@zuvia-software-solutions/code-mapper 1.4.0 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/ai-context.js +1 -1
- package/dist/cli/analyze.d.ts +1 -0
- package/dist/cli/analyze.js +73 -82
- package/dist/cli/augment.js +0 -2
- package/dist/cli/eval-server.d.ts +2 -2
- package/dist/cli/eval-server.js +6 -6
- package/dist/cli/index.js +6 -10
- package/dist/cli/mcp.d.ts +1 -3
- package/dist/cli/mcp.js +3 -3
- package/dist/cli/refresh.d.ts +2 -2
- package/dist/cli/refresh.js +24 -29
- package/dist/cli/status.js +4 -13
- package/dist/cli/tool.d.ts +5 -4
- package/dist/cli/tool.js +8 -10
- package/dist/config/ignore-service.js +14 -34
- package/dist/core/augmentation/engine.js +53 -83
- package/dist/core/db/adapter.d.ts +99 -0
- package/dist/core/db/adapter.js +402 -0
- package/dist/core/db/graph-loader.d.ts +27 -0
- package/dist/core/db/graph-loader.js +148 -0
- package/dist/core/db/queries.d.ts +160 -0
- package/dist/core/db/queries.js +441 -0
- package/dist/core/db/schema.d.ts +108 -0
- package/dist/core/db/schema.js +136 -0
- package/dist/core/embeddings/embedder.d.ts +21 -12
- package/dist/core/embeddings/embedder.js +104 -50
- package/dist/core/embeddings/embedding-pipeline.d.ts +48 -22
- package/dist/core/embeddings/embedding-pipeline.js +220 -262
- package/dist/core/embeddings/text-generator.js +4 -19
- package/dist/core/embeddings/types.d.ts +1 -1
- package/dist/core/graph/graph.d.ts +1 -1
- package/dist/core/graph/graph.js +1 -0
- package/dist/core/graph/types.d.ts +11 -9
- package/dist/core/graph/types.js +4 -1
- package/dist/core/incremental/refresh.d.ts +46 -0
- package/dist/core/incremental/refresh.js +464 -0
- package/dist/core/incremental/types.d.ts +2 -1
- package/dist/core/incremental/types.js +42 -44
- package/dist/core/ingestion/ast-cache.js +1 -0
- package/dist/core/ingestion/call-processor.d.ts +15 -3
- package/dist/core/ingestion/call-processor.js +448 -60
- package/dist/core/ingestion/cluster-enricher.d.ts +1 -1
- package/dist/core/ingestion/cluster-enricher.js +2 -0
- package/dist/core/ingestion/community-processor.d.ts +1 -1
- package/dist/core/ingestion/community-processor.js +8 -3
- package/dist/core/ingestion/export-detection.d.ts +1 -1
- package/dist/core/ingestion/export-detection.js +1 -1
- package/dist/core/ingestion/filesystem-walker.js +1 -1
- package/dist/core/ingestion/heritage-processor.d.ts +2 -2
- package/dist/core/ingestion/heritage-processor.js +22 -11
- package/dist/core/ingestion/import-processor.d.ts +2 -2
- package/dist/core/ingestion/import-processor.js +24 -9
- package/dist/core/ingestion/language-config.js +7 -4
- package/dist/core/ingestion/mro-processor.d.ts +1 -1
- package/dist/core/ingestion/mro-processor.js +23 -11
- package/dist/core/ingestion/named-binding-extraction.js +5 -5
- package/dist/core/ingestion/parsing-processor.d.ts +4 -4
- package/dist/core/ingestion/parsing-processor.js +26 -18
- package/dist/core/ingestion/pipeline.d.ts +4 -2
- package/dist/core/ingestion/pipeline.js +50 -20
- package/dist/core/ingestion/process-processor.d.ts +2 -2
- package/dist/core/ingestion/process-processor.js +28 -14
- package/dist/core/ingestion/resolution-context.d.ts +1 -1
- package/dist/core/ingestion/resolution-context.js +14 -4
- package/dist/core/ingestion/resolvers/csharp.js +4 -3
- package/dist/core/ingestion/resolvers/go.js +3 -1
- package/dist/core/ingestion/resolvers/jvm.js +13 -4
- package/dist/core/ingestion/resolvers/standard.js +2 -2
- package/dist/core/ingestion/resolvers/utils.js +6 -2
- package/dist/core/ingestion/route-stitcher.d.ts +15 -0
- package/dist/core/ingestion/route-stitcher.js +92 -0
- package/dist/core/ingestion/structure-processor.d.ts +1 -1
- package/dist/core/ingestion/structure-processor.js +3 -2
- package/dist/core/ingestion/symbol-table.d.ts +2 -0
- package/dist/core/ingestion/symbol-table.js +5 -1
- package/dist/core/ingestion/tree-sitter-queries.d.ts +2 -2
- package/dist/core/ingestion/tree-sitter-queries.js +177 -0
- package/dist/core/ingestion/type-env.js +20 -0
- package/dist/core/ingestion/type-extractors/csharp.js +4 -3
- package/dist/core/ingestion/type-extractors/go.js +23 -12
- package/dist/core/ingestion/type-extractors/php.js +18 -10
- package/dist/core/ingestion/type-extractors/ruby.js +15 -3
- package/dist/core/ingestion/type-extractors/rust.js +3 -2
- package/dist/core/ingestion/type-extractors/shared.js +3 -2
- package/dist/core/ingestion/type-extractors/typescript.js +11 -5
- package/dist/core/ingestion/utils.d.ts +27 -4
- package/dist/core/ingestion/utils.js +145 -100
- package/dist/core/ingestion/workers/parse-worker.d.ts +1 -0
- package/dist/core/ingestion/workers/parse-worker.js +97 -29
- package/dist/core/ingestion/workers/worker-pool.js +3 -0
- package/dist/core/search/bm25-index.d.ts +15 -8
- package/dist/core/search/bm25-index.js +48 -98
- package/dist/core/search/hybrid-search.d.ts +9 -3
- package/dist/core/search/hybrid-search.js +30 -25
- package/dist/core/search/reranker.js +9 -7
- package/dist/core/search/types.d.ts +0 -4
- package/dist/core/semantic/tsgo-service.d.ts +5 -1
- package/dist/core/semantic/tsgo-service.js +161 -66
- package/dist/lib/tsgo-test.d.ts +2 -0
- package/dist/lib/tsgo-test.js +6 -0
- package/dist/lib/type-utils.d.ts +25 -0
- package/dist/lib/type-utils.js +22 -0
- package/dist/lib/utils.d.ts +3 -2
- package/dist/lib/utils.js +3 -2
- package/dist/mcp/compatible-stdio-transport.js +1 -1
- package/dist/mcp/local/local-backend.d.ts +29 -56
- package/dist/mcp/local/local-backend.js +808 -1118
- package/dist/mcp/resources.js +35 -25
- package/dist/mcp/server.d.ts +1 -1
- package/dist/mcp/server.js +5 -5
- package/dist/mcp/tools.js +24 -25
- package/dist/storage/repo-manager.d.ts +2 -12
- package/dist/storage/repo-manager.js +1 -47
- package/dist/types/pipeline.d.ts +8 -5
- package/dist/types/pipeline.js +5 -0
- package/package.json +18 -11
- package/dist/cli/serve.d.ts +0 -5
- package/dist/cli/serve.js +0 -8
- package/dist/core/incremental/child-process.d.ts +0 -8
- package/dist/core/incremental/child-process.js +0 -649
- package/dist/core/incremental/refresh-coordinator.d.ts +0 -32
- package/dist/core/incremental/refresh-coordinator.js +0 -147
- package/dist/core/lbug/csv-generator.d.ts +0 -28
- package/dist/core/lbug/csv-generator.js +0 -355
- package/dist/core/lbug/lbug-adapter.d.ts +0 -96
- package/dist/core/lbug/lbug-adapter.js +0 -753
- package/dist/core/lbug/schema.d.ts +0 -46
- package/dist/core/lbug/schema.js +0 -402
- package/dist/mcp/core/embedder.d.ts +0 -24
- package/dist/mcp/core/embedder.js +0 -168
- package/dist/mcp/core/lbug-adapter.d.ts +0 -29
- package/dist/mcp/core/lbug-adapter.js +0 -330
- package/dist/server/api.d.ts +0 -5
- package/dist/server/api.js +0 -340
- package/dist/server/mcp-http.d.ts +0 -7
- package/dist/server/mcp-http.js +0 -95
- package/models/mlx-embedder.py +0 -185
|
@@ -1,109 +1,65 @@
|
|
|
1
1
|
// code-mapper/src/core/search/bm25-index.ts
|
|
2
2
|
/**
|
|
3
3
|
* @file bm25-index.ts
|
|
4
|
-
* @description Symbol-level full-text search via
|
|
4
|
+
* @description Symbol-level full-text search via SQLite FTS5 index.
|
|
5
5
|
*
|
|
6
6
|
* Returns individual symbols (functions, classes, methods, interfaces, files)
|
|
7
|
-
* with their nodeId, name, type
|
|
7
|
+
* with their nodeId, name, type -- NOT just filePaths.
|
|
8
8
|
* This gives the RRF merge symbol-level granularity for accurate ranking.
|
|
9
9
|
*
|
|
10
|
-
*
|
|
10
|
+
* Backed by the unified FTS5 index on the `nodes` table (see adapter.searchFTS).
|
|
11
11
|
*/
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
*/
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
const cypher = `
|
|
26
|
-
CALL QUERY_FTS_INDEX('${tableName}', '${indexName}', '${escapedQuery}', conjunctive := false)
|
|
27
|
-
RETURN ${returnFields}
|
|
28
|
-
ORDER BY score DESC
|
|
29
|
-
LIMIT ${limit}
|
|
30
|
-
`;
|
|
31
|
-
try {
|
|
32
|
-
const rows = await executor(cypher);
|
|
33
|
-
return rows.map((row) => {
|
|
34
|
-
const r = row;
|
|
35
|
-
const rawScore = r.score ?? 0;
|
|
36
|
-
const score = typeof rawScore === 'number' ? rawScore : parseFloat(String(rawScore)) || 0;
|
|
37
|
-
return {
|
|
38
|
-
nodeId: String(r.nodeId ?? ''),
|
|
39
|
-
name: String(r.name ?? ''),
|
|
40
|
-
type: String(r.type ?? tableName),
|
|
41
|
-
filePath: String(r.filePath ?? ''),
|
|
42
|
-
score,
|
|
43
|
-
...(!isFile && r.startLine != null ? { startLine: Number(r.startLine) } : {}),
|
|
44
|
-
...(!isFile && r.endLine != null ? { endLine: Number(r.endLine) } : {}),
|
|
45
|
-
};
|
|
46
|
-
});
|
|
47
|
-
}
|
|
48
|
-
catch {
|
|
49
|
-
return [];
|
|
50
|
-
}
|
|
51
|
-
}
|
|
12
|
+
import { searchFTS } from '../db/adapter.js';
|
|
13
|
+
import {} from './types.js';
|
|
14
|
+
// ============================================================================
|
|
15
|
+
// SYMBOL-TYPE WEIGHT MULTIPLIERS
|
|
16
|
+
// ============================================================================
|
|
17
|
+
/** Functions/methods are more actionable than files in search results */
|
|
18
|
+
const TYPE_WEIGHTS = {
|
|
19
|
+
Function: 1.5,
|
|
20
|
+
Method: 1.5,
|
|
21
|
+
Class: 1.3,
|
|
22
|
+
Interface: 1.3,
|
|
23
|
+
File: 1.0,
|
|
24
|
+
};
|
|
52
25
|
// ============================================================================
|
|
53
26
|
// MAIN SEARCH FUNCTION
|
|
54
27
|
// ============================================================================
|
|
55
28
|
/**
|
|
56
|
-
* Symbol-level BM25 search via
|
|
29
|
+
* Symbol-level BM25 search via SQLite FTS5.
|
|
57
30
|
*
|
|
58
|
-
* Queries
|
|
59
|
-
*
|
|
60
|
-
*
|
|
31
|
+
* Queries the unified `nodes_fts` virtual table, deduplicates by nodeId,
|
|
32
|
+
* applies type-based weighting, and returns ranked results.
|
|
33
|
+
*
|
|
34
|
+
* Synchronous under the hood (better-sqlite3), but the function is sync --
|
|
35
|
+
* callers that `await` it still work (await on a non-Promise is a no-op).
|
|
36
|
+
*
|
|
37
|
+
* @param query - Search text
|
|
38
|
+
* @param limit - Max results to return (default 20)
|
|
39
|
+
* @param db - Open SQLite database instance (required; returns [] if undefined)
|
|
61
40
|
*/
|
|
62
|
-
export
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
for (const { table, index } of FTS_TABLES) {
|
|
68
|
-
queryAll.push(queryFTSSymbols(executor, table, index, query, limit));
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
else {
|
|
72
|
-
for (const { table, index } of FTS_TABLES) {
|
|
73
|
-
queryAll.push(queryFTS(table, index, query, limit, false)
|
|
74
|
-
.then((rows) => rows.map(r => ({
|
|
75
|
-
nodeId: String(r.nodeId ?? r.id ?? ''),
|
|
76
|
-
name: String(r.name ?? ''),
|
|
77
|
-
type: table,
|
|
78
|
-
filePath: String(r.filePath ?? ''),
|
|
79
|
-
score: typeof r.score === 'number' ? r.score : 0,
|
|
80
|
-
...(r.startLine != null ? { startLine: Number(r.startLine) } : {}),
|
|
81
|
-
...(r.endLine != null ? { endLine: Number(r.endLine) } : {}),
|
|
82
|
-
})))
|
|
83
|
-
.catch(() => []));
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
const allResults = await Promise.all(queryAll);
|
|
87
|
-
// Symbol-type weight multipliers: functions/methods are more actionable than files
|
|
88
|
-
const TYPE_WEIGHTS = {
|
|
89
|
-
Function: 1.5,
|
|
90
|
-
Method: 1.5,
|
|
91
|
-
Class: 1.3,
|
|
92
|
-
Interface: 1.3,
|
|
93
|
-
File: 1.0,
|
|
94
|
-
};
|
|
95
|
-
// Deduplicate by nodeId — keep highest score per symbol, apply type weighting
|
|
41
|
+
export function searchFTSSymbols(query, limit = 20, db) {
|
|
42
|
+
if (!db)
|
|
43
|
+
return [];
|
|
44
|
+
const ftsResults = searchFTS(db, query, limit * 2); // over-fetch for dedup headroom
|
|
45
|
+
// Deduplicate by node ID, apply type weighting, keep highest score per symbol
|
|
96
46
|
const byNodeId = new Map();
|
|
97
|
-
for (const
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
47
|
+
for (const r of ftsResults) {
|
|
48
|
+
const id = r.id;
|
|
49
|
+
if (!id)
|
|
50
|
+
continue;
|
|
51
|
+
const weight = TYPE_WEIGHTS[r.label] ?? 1.0;
|
|
52
|
+
const weightedScore = r.score * weight;
|
|
53
|
+
const existing = byNodeId.get(id);
|
|
54
|
+
if (!existing || weightedScore > existing.score) {
|
|
55
|
+
byNodeId.set(id, {
|
|
56
|
+
nodeId: id,
|
|
57
|
+
name: r.name,
|
|
58
|
+
type: r.label,
|
|
59
|
+
filePath: r.filePath,
|
|
60
|
+
score: weightedScore,
|
|
61
|
+
rank: 0, // assigned below after sorting
|
|
62
|
+
});
|
|
107
63
|
}
|
|
108
64
|
}
|
|
109
65
|
// Sort by score descending, assign rank
|
|
@@ -111,13 +67,7 @@ export async function searchFTSFromLbug(query, limit = 20, repoId) {
|
|
|
111
67
|
.sort((a, b) => b.score - a.score)
|
|
112
68
|
.slice(0, limit);
|
|
113
69
|
return sorted.map((r, index) => ({
|
|
114
|
-
|
|
115
|
-
name: r.name,
|
|
116
|
-
type: r.type,
|
|
117
|
-
filePath: r.filePath,
|
|
118
|
-
score: r.score,
|
|
70
|
+
...r,
|
|
119
71
|
rank: index + 1,
|
|
120
|
-
...(r.startLine != null ? { startLine: r.startLine } : {}),
|
|
121
|
-
...(r.endLine != null ? { endLine: r.endLine } : {}),
|
|
122
72
|
}));
|
|
123
73
|
}
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
* All constants come from types.ts (single source of truth)
|
|
7
7
|
* This is the ONLY RRF implementation — local-backend.ts must import this, never reimplement
|
|
8
8
|
*/
|
|
9
|
+
import type Database from 'better-sqlite3';
|
|
9
10
|
import { type BM25SearchResult, type SemanticSearchResult, type HybridSearchResult, type RRFConfig } from './types.js';
|
|
10
11
|
export type { HybridSearchResult, RRFConfig } from './types.js';
|
|
11
12
|
/**
|
|
@@ -26,7 +27,12 @@ export declare function formatHybridResults(results: readonly HybridSearchResult
|
|
|
26
27
|
/**
|
|
27
28
|
* Execute BM25 + semantic search and merge with weighted RRF
|
|
28
29
|
*
|
|
29
|
-
* Uses
|
|
30
|
-
* The semanticSearch function is injected to keep this module environment-agnostic
|
|
30
|
+
* Uses SQLite FTS5 for always-fresh BM25 results.
|
|
31
|
+
* The semanticSearch function is injected to keep this module environment-agnostic.
|
|
32
|
+
*
|
|
33
|
+
* @param db - Open SQLite database instance
|
|
34
|
+
* @param query - Search text
|
|
35
|
+
* @param limit - Max results to return
|
|
36
|
+
* @param semanticSearchFn - Async function that performs vector search given a db + query
|
|
31
37
|
*/
|
|
32
|
-
export declare function hybridSearch(query: string, limit: number,
|
|
38
|
+
export declare function hybridSearch(db: Database.Database, query: string, limit: number, semanticSearchFn: (db: Database.Database, query: string, k?: number) => Promise<SemanticSearchResult[]>): Promise<HybridSearchResult[]>;
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* All constants come from types.ts (single source of truth)
|
|
8
8
|
* This is the ONLY RRF implementation — local-backend.ts must import this, never reimplement
|
|
9
9
|
*/
|
|
10
|
-
import {
|
|
10
|
+
import { searchFTSSymbols } from './bm25-index.js';
|
|
11
11
|
import { DEFAULT_RRF_CONFIG, } from './types.js';
|
|
12
12
|
/**
|
|
13
13
|
* Merge BM25 and semantic results using weighted Reciprocal Rank Fusion
|
|
@@ -24,9 +24,10 @@ export function mergeWithRRF(bm25Results, semanticResults, config = {}) {
|
|
|
24
24
|
// Mutable accumulator for building results
|
|
25
25
|
const merged = new Map();
|
|
26
26
|
// Score BM25 results — key by nodeId for symbol-level merging
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
27
|
+
let bm25Rank = 0;
|
|
28
|
+
for (const r of bm25Results) {
|
|
29
|
+
bm25Rank++;
|
|
30
|
+
const rrfScore = bm25Weight * (1 / (k + bm25Rank)); // rank is 1-indexed
|
|
30
31
|
const key = r.nodeId || r.filePath; // symbol-level key when available
|
|
31
32
|
merged.set(key, {
|
|
32
33
|
filePath: r.filePath,
|
|
@@ -36,14 +37,15 @@ export function mergeWithRRF(bm25Results, semanticResults, config = {}) {
|
|
|
36
37
|
nodeId: r.nodeId,
|
|
37
38
|
name: r.name,
|
|
38
39
|
label: r.type,
|
|
39
|
-
startLine: r.startLine,
|
|
40
|
-
endLine: r.endLine,
|
|
40
|
+
...(r.startLine != null ? { startLine: r.startLine } : {}),
|
|
41
|
+
...(r.endLine != null ? { endLine: r.endLine } : {}),
|
|
41
42
|
});
|
|
42
43
|
}
|
|
43
44
|
// Score and merge semantic results — same symbol-level keying
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
45
|
+
let semRank = 0;
|
|
46
|
+
for (const r of semanticResults) {
|
|
47
|
+
semRank++;
|
|
48
|
+
const rrfScore = semanticWeight * (1 / (k + semRank));
|
|
47
49
|
const key = r.nodeId || r.filePath;
|
|
48
50
|
const existing = merged.get(key);
|
|
49
51
|
if (existing) {
|
|
@@ -72,8 +74,8 @@ export function mergeWithRRF(bm25Results, semanticResults, config = {}) {
|
|
|
72
74
|
nodeId: r.nodeId,
|
|
73
75
|
name: r.name,
|
|
74
76
|
label: r.label,
|
|
75
|
-
startLine: r.startLine,
|
|
76
|
-
endLine: r.endLine,
|
|
77
|
+
...(r.startLine != null ? { startLine: r.startLine } : {}),
|
|
78
|
+
...(r.endLine != null ? { endLine: r.endLine } : {}),
|
|
77
79
|
});
|
|
78
80
|
}
|
|
79
81
|
}
|
|
@@ -86,13 +88,13 @@ export function mergeWithRRF(bm25Results, semanticResults, config = {}) {
|
|
|
86
88
|
score: r.score,
|
|
87
89
|
rank: i + 1,
|
|
88
90
|
sources: r.sources,
|
|
89
|
-
bm25Score: r.bm25Score,
|
|
90
|
-
semanticScore: r.semanticScore,
|
|
91
|
-
nodeId: r.nodeId,
|
|
92
|
-
name: r.name,
|
|
93
|
-
label: r.label,
|
|
94
|
-
startLine: r.startLine,
|
|
95
|
-
endLine: r.endLine,
|
|
91
|
+
...(r.bm25Score != null ? { bm25Score: r.bm25Score } : {}),
|
|
92
|
+
...(r.semanticScore != null ? { semanticScore: r.semanticScore } : {}),
|
|
93
|
+
...(r.nodeId != null ? { nodeId: r.nodeId } : {}),
|
|
94
|
+
...(r.name != null ? { name: r.name } : {}),
|
|
95
|
+
...(r.label != null ? { label: r.label } : {}),
|
|
96
|
+
...(r.startLine != null ? { startLine: r.startLine } : {}),
|
|
97
|
+
...(r.endLine != null ? { endLine: r.endLine } : {}),
|
|
96
98
|
}));
|
|
97
99
|
}
|
|
98
100
|
// Hybrid search convenience functions
|
|
@@ -119,13 +121,16 @@ export function formatHybridResults(results) {
|
|
|
119
121
|
/**
|
|
120
122
|
* Execute BM25 + semantic search and merge with weighted RRF
|
|
121
123
|
*
|
|
122
|
-
* Uses
|
|
123
|
-
* The semanticSearch function is injected to keep this module environment-agnostic
|
|
124
|
+
* Uses SQLite FTS5 for always-fresh BM25 results.
|
|
125
|
+
* The semanticSearch function is injected to keep this module environment-agnostic.
|
|
126
|
+
*
|
|
127
|
+
* @param db - Open SQLite database instance
|
|
128
|
+
* @param query - Search text
|
|
129
|
+
* @param limit - Max results to return
|
|
130
|
+
* @param semanticSearchFn - Async function that performs vector search given a db + query
|
|
124
131
|
*/
|
|
125
|
-
export async function hybridSearch(query, limit,
|
|
126
|
-
const
|
|
127
|
-
|
|
128
|
-
semanticSearchFn(executeQuery, query, limit),
|
|
129
|
-
]);
|
|
132
|
+
export async function hybridSearch(db, query, limit, semanticSearchFn) {
|
|
133
|
+
const bm25Results = searchFTSSymbols(query, limit, db);
|
|
134
|
+
const semanticResults = await semanticSearchFn(db, query, limit);
|
|
130
135
|
return mergeWithRRF(bm25Results, semanticResults, { limit });
|
|
131
136
|
}
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
// ============================================================================
|
|
16
16
|
// BGE reranker base — cross-encoder that understands code structure.
|
|
17
17
|
// 278M params, ONNX, runs on CPU via transformers.js.
|
|
18
|
-
// Tested: correctly ranks "
|
|
18
|
+
// Tested: correctly ranks "openDb" > "checkout" > "parseCode" > "README" for "database connection pool".
|
|
19
19
|
const RERANKER_MODEL_ID = 'Xenova/bge-reranker-base';
|
|
20
20
|
let rerankerState = null;
|
|
21
21
|
let initPromise = null;
|
|
@@ -69,7 +69,10 @@ export async function rerank(query, passages) {
|
|
|
69
69
|
if (passages.length === 0)
|
|
70
70
|
return [];
|
|
71
71
|
if (passages.length === 1) {
|
|
72
|
-
|
|
72
|
+
const first = passages[0];
|
|
73
|
+
if (first === undefined)
|
|
74
|
+
return [];
|
|
75
|
+
return [{ id: first.id, score: 1, originalRank: 1 }];
|
|
73
76
|
}
|
|
74
77
|
let state;
|
|
75
78
|
try {
|
|
@@ -86,11 +89,10 @@ export async function rerank(query, passages) {
|
|
|
86
89
|
}
|
|
87
90
|
const { tokenizer, model } = state;
|
|
88
91
|
const tok = tokenizer;
|
|
89
|
-
const mod = model;
|
|
90
92
|
// Tokenize all (query, passage) pairs
|
|
91
93
|
const queries = passages.map(() => query);
|
|
92
94
|
const texts = passages.map(p => p.text);
|
|
93
|
-
const inputs = tok
|
|
95
|
+
const inputs = tok['call']
|
|
94
96
|
? await tokenizer(queries, { text_pair: texts, padding: true, truncation: true })
|
|
95
97
|
: await tok.__call__(queries, { text_pair: texts, padding: true, truncation: true });
|
|
96
98
|
const output = await model(inputs);
|
|
@@ -100,7 +102,7 @@ export async function rerank(query, passages) {
|
|
|
100
102
|
return passages
|
|
101
103
|
.map((p, i) => ({
|
|
102
104
|
id: p.id,
|
|
103
|
-
score: scores[i],
|
|
105
|
+
score: scores[i] ?? 0,
|
|
104
106
|
originalRank: i + 1,
|
|
105
107
|
}))
|
|
106
108
|
.sort((a, b) => b.score - a.score);
|
|
@@ -113,8 +115,8 @@ export function isRerankerReady() {
|
|
|
113
115
|
export async function disposeReranker() {
|
|
114
116
|
if (rerankerState) {
|
|
115
117
|
const model = rerankerState.model;
|
|
116
|
-
if (typeof model
|
|
117
|
-
await model
|
|
118
|
+
if (typeof model['dispose'] === 'function') {
|
|
119
|
+
await model['dispose']();
|
|
118
120
|
}
|
|
119
121
|
rerankerState = null;
|
|
120
122
|
initPromise = null;
|
|
@@ -139,10 +139,6 @@ export interface RerankResult {
|
|
|
139
139
|
readonly score: number;
|
|
140
140
|
readonly originalRank: number;
|
|
141
141
|
}
|
|
142
|
-
/** Execute a raw Cypher query and return rows */
|
|
143
|
-
export type CypherExecutor = (cypher: string) => Promise<readonly Record<string, unknown>[]>;
|
|
144
|
-
/** Execute a parameterized Cypher query */
|
|
145
|
-
export type ParameterizedCypherExecutor = (repoId: string, cypher: string, params: Record<string, unknown>) => Promise<readonly Record<string, unknown>[]>;
|
|
146
142
|
export interface SearchPipelineItem {
|
|
147
143
|
readonly nodeId?: string;
|
|
148
144
|
readonly name: string;
|
|
@@ -24,8 +24,9 @@ export interface TsgoReference {
|
|
|
24
24
|
}
|
|
25
25
|
export declare class TsgoService {
|
|
26
26
|
private process;
|
|
27
|
-
private buf;
|
|
28
27
|
private responses;
|
|
28
|
+
/** Pending resolve callbacks keyed by request ID — event-driven, no polling */
|
|
29
|
+
private pending;
|
|
29
30
|
private nextId;
|
|
30
31
|
private projectRoot;
|
|
31
32
|
private tsgoPath;
|
|
@@ -50,10 +51,13 @@ export declare class TsgoService {
|
|
|
50
51
|
notifyFileChanged(absFilePath: string): Promise<void>;
|
|
51
52
|
/** Notify tsgo that a file was deleted */
|
|
52
53
|
notifyFileDeleted(absFilePath: string): Promise<void>;
|
|
54
|
+
/** Pre-open multiple files in one batch (fire-and-forget, no await per file) */
|
|
55
|
+
preOpenFiles(absFilePaths: readonly string[]): void;
|
|
53
56
|
/** Stop the tsgo process */
|
|
54
57
|
stop(): void;
|
|
55
58
|
private doStart;
|
|
56
59
|
private findTsgoBinary;
|
|
60
|
+
private rawBuf;
|
|
57
61
|
private onData;
|
|
58
62
|
private send;
|
|
59
63
|
private request;
|