retrieval fusion
This commit is contained in:
@@ -168,17 +168,26 @@ function getRecentEpisodes(sessionId, limit = EPISODIC.DEFAULT_RECENT_LIMIT) {
|
|||||||
|
|
||||||
|
|
||||||
// Searches episodes using FTS5 full-text search, ordered by relevance, with a limit
|
// Searches episodes using FTS5 full-text search, ordered by relevance, with a limit
|
||||||
function searchEpisodes(query, limit = EPISODIC.DEFAULT_SEARCH_LIMIT) {
|
function searchEpisodes(query, limit = EPISODIC.DEFAULT_SEARCH_LIMIT, sessionIds = null) {
|
||||||
// FTS5 full-text search across all episodes
|
|
||||||
const db = getDB();
|
const db = getDB();
|
||||||
const stmt = db.prepare(`
|
if (sessionIds && sessionIds.length > 0) {
|
||||||
|
const ph = sessionIds.map(() => '?').join(',');
|
||||||
|
return db.prepare(`
|
||||||
|
SELECT e.* FROM episodes e
|
||||||
|
JOIN episodes_fts fts ON e.id = fts.rowid
|
||||||
|
WHERE episodes_fts MATCH ?
|
||||||
|
AND e.session_id IN (${ph})
|
||||||
|
ORDER BY rank
|
||||||
|
LIMIT ?
|
||||||
|
`).all(query, ...sessionIds, limit).map(parseRow);
|
||||||
|
}
|
||||||
|
return db.prepare(`
|
||||||
SELECT e.* FROM episodes e
|
SELECT e.* FROM episodes e
|
||||||
JOIN episodes_fts fts ON e.id = fts.rowid
|
JOIN episodes_fts fts ON e.id = fts.rowid
|
||||||
WHERE episodes_fts MATCH ?
|
WHERE episodes_fts MATCH ?
|
||||||
ORDER BY rank
|
ORDER BY rank
|
||||||
LIMIT ?
|
LIMIT ?
|
||||||
`);
|
`).all(query, limit).map(parseRow);
|
||||||
return stmt.all(query, limit).map(parseRow);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Deletes an episode by its ID
|
// Deletes an episode by its ID
|
||||||
|
|||||||
@@ -131,10 +131,12 @@ app.get('/episodes', (req, res) => {
|
|||||||
|
|
||||||
// Search MUST come before /:id — otherwise 'search' gets captured as an id
|
// Search MUST come before /:id — otherwise 'search' gets captured as an id
|
||||||
app.get('/episodes/search', (req, res) => {
|
app.get('/episodes/search', (req, res) => {
|
||||||
const { q, limit = EPISODIC.DEFAULT_PAGE_SIZE } = req.query;
|
const { q, limit = EPISODIC.DEFAULT_PAGE_SIZE, sessionIds } = req.query;
|
||||||
if (!q) return res.status(400).json({ error: 'q (query) parameter is required' });
|
if (!q) return res.status(400).json({ error: 'q (query) parameter is required' });
|
||||||
const results = episodic.searchEpisodes(q, Number(limit));
|
const parsedSessionIds = sessionIds
|
||||||
res.json(results);
|
? sessionIds.split(',').map(Number).filter(Boolean)
|
||||||
|
: null;
|
||||||
|
res.json(episodic.searchEpisodes(q, Number(limit), parsedSessionIds));
|
||||||
});
|
});
|
||||||
|
|
||||||
app.get('/episodes/:id', (req, res) => {
|
app.get('/episodes/:id', (req, res) => {
|
||||||
|
|||||||
@@ -96,6 +96,12 @@ const ENTITIES = {
|
|||||||
],
|
],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const RETRIEVAL = {
|
||||||
|
RRF_K: 60, // Reciprocal Rank Fusion smoothing constant, softens rank-1 advantage, not exposed in settings
|
||||||
|
SEMANTIC_WEIGHT: 1.0, // Weight applied to semantic (QDrant) results
|
||||||
|
KEYWORD_WEIGHT: 0, // Weight applied to keyword (SQLite) results, 0 = disables, set >0 to enable and tune balance between semantic vs keyword matches
|
||||||
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
QDRANT,
|
QDRANT,
|
||||||
COLLECTIONS,
|
COLLECTIONS,
|
||||||
@@ -108,5 +114,6 @@ module.exports = {
|
|||||||
SQLITE,
|
SQLITE,
|
||||||
ORCHESTRATION,
|
ORCHESTRATION,
|
||||||
SUMMARIES,
|
SUMMARIES,
|
||||||
ENTITIES
|
ENTITIES,
|
||||||
|
RETRIEVAL,
|
||||||
};
|
};
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
const {getEnv} = require('./config/env');
|
const {getEnv} = require('./config/env');
|
||||||
const {QDRANT, COLLECTIONS, EPISODIC, SERVICES, OLLAMA, PORTS, LLAMACPP, INFERENCE_DEFAULTS, SQLITE, ORCHESTRATION, SUMMARIES, ENTITIES } = require('./config/constants');
|
const {QDRANT, COLLECTIONS, EPISODIC, SERVICES, OLLAMA, PORTS, LLAMACPP, INFERENCE_DEFAULTS, SQLITE, ORCHESTRATION, SUMMARIES, ENTITIES, RETRIEVAL } = require('./config/constants');
|
||||||
const {parseRow, formatEpisodeText} = require('./utils')
|
const {parseRow, formatEpisodeText} = require('./utils')
|
||||||
const logger = require('./utils/logger');
|
const logger = require('./utils/logger');
|
||||||
|
|
||||||
@@ -20,4 +20,5 @@ module.exports = {
|
|||||||
SUMMARIES,
|
SUMMARIES,
|
||||||
ENTITIES,
|
ENTITIES,
|
||||||
logger,
|
logger,
|
||||||
|
RETRIEVAL,
|
||||||
};
|
};
|
||||||
Reference in New Issue
Block a user