74 lines
2.0 KiB
TypeScript
74 lines
2.0 KiB
TypeScript
import type { SessionStore } from './store.js';
|
|
import { tokenize } from './indexer.js';
|
|
|
|
export interface HistorySearchResult {
|
|
sessionId: string;
|
|
messageId: number;
|
|
role: 'user' | 'assistant';
|
|
content: string;
|
|
score: number;
|
|
createdAt: number;
|
|
}
|
|
|
|
export interface HistorySearchConfig {
|
|
limit: number;
|
|
minScore: number;
|
|
}
|
|
|
|
export class SessionSearch {
|
|
private readonly store: SessionStore;
|
|
private readonly config: HistorySearchConfig;
|
|
|
|
constructor(store: SessionStore, config: HistorySearchConfig) {
|
|
this.store = store;
|
|
this.config = config;
|
|
}
|
|
|
|
search(query: string, opts?: { limit?: number; sessionId?: string }): HistorySearchResult[] {
|
|
const queryTokens = new Set(tokenize(query));
|
|
if (queryTokens.size === 0) {
|
|
return [];
|
|
}
|
|
|
|
const rows = opts?.sessionId
|
|
? this.store.getMessagesWithMetadata(opts.sessionId)
|
|
: this.store.getAllMessagesWithMetadata();
|
|
|
|
const now = Math.floor(Date.now() / 1000);
|
|
const results: HistorySearchResult[] = [];
|
|
|
|
for (const row of rows) {
|
|
const keywords = row.metadata?.keywords ?? [];
|
|
if (keywords.length === 0) {
|
|
continue;
|
|
}
|
|
|
|
const overlapCount = keywords.filter(keyword => queryTokens.has(keyword)).length;
|
|
if (overlapCount === 0) {
|
|
continue;
|
|
}
|
|
|
|
const overlapScore = overlapCount / queryTokens.size;
|
|
const ageSeconds = Math.max(0, now - row.createdAt);
|
|
const recencyScore = Math.max(0, 1 - ageSeconds / (30 * 24 * 3600)) * 0.2;
|
|
const totalScore = overlapScore + recencyScore;
|
|
|
|
if (totalScore < this.config.minScore) {
|
|
continue;
|
|
}
|
|
|
|
results.push({
|
|
sessionId: row.sessionId,
|
|
messageId: row.id,
|
|
role: row.role,
|
|
content: row.content,
|
|
score: totalScore,
|
|
createdAt: row.createdAt,
|
|
});
|
|
}
|
|
|
|
results.sort((a, b) => b.score - a.score || b.createdAt - a.createdAt);
|
|
return results.slice(0, opts?.limit ?? this.config.limit);
|
|
}
|
|
}
|