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
|
||||
function searchEpisodes(query, limit = EPISODIC.DEFAULT_SEARCH_LIMIT) {
|
||||
// FTS5 full-text search across all episodes
|
||||
function searchEpisodes(query, limit = EPISODIC.DEFAULT_SEARCH_LIMIT, sessionIds = null) {
|
||||
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
|
||||
JOIN episodes_fts fts ON e.id = fts.rowid
|
||||
WHERE episodes_fts MATCH ?
|
||||
ORDER BY rank
|
||||
LIMIT ?
|
||||
`);
|
||||
return stmt.all(query, limit).map(parseRow);
|
||||
`).all(query, limit).map(parseRow);
|
||||
}
|
||||
|
||||
// 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
|
||||
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' });
|
||||
const results = episodic.searchEpisodes(q, Number(limit));
|
||||
res.json(results);
|
||||
const parsedSessionIds = sessionIds
|
||||
? sessionIds.split(',').map(Number).filter(Boolean)
|
||||
: null;
|
||||
res.json(episodic.searchEpisodes(q, Number(limit), parsedSessionIds));
|
||||
});
|
||||
|
||||
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 = {
|
||||
QDRANT,
|
||||
COLLECTIONS,
|
||||
@@ -108,5 +114,6 @@ module.exports = {
|
||||
SQLITE,
|
||||
ORCHESTRATION,
|
||||
SUMMARIES,
|
||||
ENTITIES
|
||||
ENTITIES,
|
||||
RETRIEVAL,
|
||||
};
|
||||
@@ -1,5 +1,5 @@
|
||||
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 logger = require('./utils/logger');
|
||||
|
||||
@@ -20,4 +20,5 @@ module.exports = {
|
||||
SUMMARIES,
|
||||
ENTITIES,
|
||||
logger,
|
||||
RETRIEVAL,
|
||||
};
|
||||
Reference in New Issue
Block a user