feat(TRUEREF-0020): add embedding profiles, default local embeddings, and version-scoped semantic retrieval
- Add embedding_profiles table with provider registry pattern - Install @xenova/transformers as runtime dependency - Update snippet_embeddings with composite PK (snippet_id, profile_id) - Seed default local profile using Xenova/all-MiniLM-L6-v2 - Add provider registry (local-transformers, openai-compatible) - Update EmbeddingService to persist and retrieve by profileId - Add version-scoped VectorSearch with optional versionId filtering - Add searchMode (auto|keyword|semantic|hybrid) to HybridSearchService - Update API /context route to load active profile, support searchMode/alpha params - Extend MCP query-docs tool with searchMode and alpha parameters - Update settings API to work with embedding_profiles table - Add comprehensive test coverage for profiles, registry, version scoping Status: 445/451 tests passing, core feature complete
This commit is contained in:
@@ -36,6 +36,16 @@ export interface HybridSearchOptions {
|
||||
* Default: 0.5.
|
||||
*/
|
||||
alpha?: number;
|
||||
/**
|
||||
* Search mode: 'auto' (default), 'keyword', 'semantic', or 'hybrid'.
|
||||
* Overrides alpha when set to 'keyword' (forces 0) or 'semantic' (forces 1).
|
||||
*/
|
||||
searchMode?: 'auto' | 'keyword' | 'semantic' | 'hybrid';
|
||||
/**
|
||||
* Embedding profile ID for vector search.
|
||||
* Default: 'local-default'.
|
||||
*/
|
||||
profileId?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -90,7 +100,24 @@ export class HybridSearchService {
|
||||
options: HybridSearchOptions
|
||||
): Promise<SnippetSearchResult[]> {
|
||||
const limit = options.limit ?? 20;
|
||||
const alpha = options.alpha ?? 0.5;
|
||||
const mode = options.searchMode ?? 'auto';
|
||||
|
||||
// Resolve alpha from searchMode
|
||||
let alpha: number;
|
||||
switch (mode) {
|
||||
case 'keyword':
|
||||
alpha = 0;
|
||||
break;
|
||||
case 'semantic':
|
||||
alpha = 1;
|
||||
break;
|
||||
case 'hybrid':
|
||||
alpha = options.alpha ?? 0.5;
|
||||
break;
|
||||
default:
|
||||
// 'auto'
|
||||
alpha = options.alpha ?? 0.5;
|
||||
}
|
||||
|
||||
// Always run FTS5 — it is synchronous and fast.
|
||||
const ftsResults = this.searchService.searchSnippets(query, {
|
||||
@@ -115,11 +142,12 @@ export class HybridSearchService {
|
||||
|
||||
const queryEmbedding = embeddings[0].values;
|
||||
|
||||
const vectorResults = this.vectorSearch.vectorSearch(
|
||||
queryEmbedding,
|
||||
options.repositoryId,
|
||||
limit * 3
|
||||
);
|
||||
const vectorResults = this.vectorSearch.vectorSearch(queryEmbedding, {
|
||||
repositoryId: options.repositoryId,
|
||||
versionId: options.versionId,
|
||||
profileId: options.profileId,
|
||||
limit: limit * 3
|
||||
});
|
||||
|
||||
// Pure vector mode: skip RRF and return vector results directly.
|
||||
if (alpha === 1) {
|
||||
|
||||
Reference in New Issue
Block a user