@untangle-code/search 0.0.0-stage → 0.1.1
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/LICENSE +661 -0
- package/NOTICE +90 -0
- package/README.md +433 -2
- package/THIRD_PARTY_LICENSES.md +29 -0
- package/dist/benchmark-integrity.d.ts +12 -0
- package/dist/benchmark-integrity.d.ts.map +1 -0
- package/dist/benchmark-integrity.js +10 -0
- package/dist/benchmark-integrity.js.map +1 -0
- package/dist/bm25.d.ts +46 -0
- package/dist/bm25.d.ts.map +1 -0
- package/dist/bm25.js +105 -0
- package/dist/bm25.js.map +1 -0
- package/dist/deadline.d.ts +4 -0
- package/dist/deadline.d.ts.map +1 -0
- package/dist/deadline.js +54 -0
- package/dist/deadline.js.map +1 -0
- package/dist/dedupe.d.ts +48 -0
- package/dist/dedupe.d.ts.map +1 -0
- package/dist/dedupe.js +58 -0
- package/dist/dedupe.js.map +1 -0
- package/dist/diversify.d.ts +45 -0
- package/dist/diversify.d.ts.map +1 -0
- package/dist/diversify.js +30 -0
- package/dist/diversify.js.map +1 -0
- package/dist/engine.d.ts +157 -0
- package/dist/engine.d.ts.map +1 -0
- package/dist/engine.js +1498 -0
- package/dist/engine.js.map +1 -0
- package/dist/enrichment.d.ts +16 -0
- package/dist/enrichment.d.ts.map +1 -0
- package/dist/enrichment.js +92 -0
- package/dist/enrichment.js.map +1 -0
- package/dist/filter.d.ts +86 -0
- package/dist/filter.d.ts.map +1 -0
- package/dist/filter.js +166 -0
- package/dist/filter.js.map +1 -0
- package/dist/findability.d.ts +144 -0
- package/dist/findability.d.ts.map +1 -0
- package/dist/findability.js +393 -0
- package/dist/findability.js.map +1 -0
- package/dist/format.d.ts +28 -0
- package/dist/format.d.ts.map +1 -0
- package/dist/format.js +172 -0
- package/dist/format.js.map +1 -0
- package/dist/fusion.d.ts +47 -0
- package/dist/fusion.d.ts.map +1 -0
- package/dist/fusion.js +84 -0
- package/dist/fusion.js.map +1 -0
- package/dist/glob.d.ts +50 -0
- package/dist/glob.d.ts.map +1 -0
- package/dist/glob.js +144 -0
- package/dist/glob.js.map +1 -0
- package/dist/grep.d.ts +76 -0
- package/dist/grep.d.ts.map +1 -0
- package/dist/grep.js +475 -0
- package/dist/grep.js.map +1 -0
- package/dist/index.d.ts +42 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +63 -0
- package/dist/index.js.map +1 -0
- package/dist/ledger.d.ts +45 -0
- package/dist/ledger.d.ts.map +1 -0
- package/dist/ledger.js +39 -0
- package/dist/ledger.js.map +1 -0
- package/dist/payload.d.ts +108 -0
- package/dist/payload.d.ts.map +1 -0
- package/dist/payload.js +263 -0
- package/dist/payload.js.map +1 -0
- package/dist/port.d.ts +112 -0
- package/dist/port.d.ts.map +1 -0
- package/dist/port.js +2 -0
- package/dist/port.js.map +1 -0
- package/dist/product-complexity.d.ts +89 -0
- package/dist/product-complexity.d.ts.map +1 -0
- package/dist/product-complexity.js +1367 -0
- package/dist/product-complexity.js.map +1 -0
- package/dist/regex-safety.d.ts +75 -0
- package/dist/regex-safety.d.ts.map +1 -0
- package/dist/regex-safety.js +902 -0
- package/dist/regex-safety.js.map +1 -0
- package/dist/relevance-prerequisites.d.ts +21 -0
- package/dist/relevance-prerequisites.d.ts.map +1 -0
- package/dist/relevance-prerequisites.js +47 -0
- package/dist/relevance-prerequisites.js.map +1 -0
- package/dist/relevance.d.ts +107 -0
- package/dist/relevance.d.ts.map +1 -0
- package/dist/relevance.js +199 -0
- package/dist/relevance.js.map +1 -0
- package/dist/retrieval.d.ts +46 -0
- package/dist/retrieval.d.ts.map +1 -0
- package/dist/retrieval.js +182 -0
- package/dist/retrieval.js.map +1 -0
- package/dist/scoring.d.ts +102 -0
- package/dist/scoring.d.ts.map +1 -0
- package/dist/scoring.js +175 -0
- package/dist/scoring.js.map +1 -0
- package/dist/sort.d.ts +21 -0
- package/dist/sort.d.ts.map +1 -0
- package/dist/sort.js +53 -0
- package/dist/sort.js.map +1 -0
- package/dist/symbol-match.d.ts +21 -0
- package/dist/symbol-match.d.ts.map +1 -0
- package/dist/symbol-match.js +72 -0
- package/dist/symbol-match.js.map +1 -0
- package/dist/tokenize.d.ts +41 -0
- package/dist/tokenize.d.ts.map +1 -0
- package/dist/tokenize.js +202 -0
- package/dist/tokenize.js.map +1 -0
- package/dist/types.d.ts +173 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +42 -0
- package/dist/types.js.map +1 -0
- package/package.json +61 -3
- package/src/benchmark-integrity.ts +25 -0
- package/src/bm25.ts +135 -0
- package/src/deadline.ts +59 -0
- package/src/dedupe.ts +99 -0
- package/src/diversify.ts +71 -0
- package/src/engine.ts +1957 -0
- package/src/enrichment.ts +115 -0
- package/src/filter.ts +267 -0
- package/src/findability.ts +580 -0
- package/src/format.ts +228 -0
- package/src/fusion.ts +121 -0
- package/src/glob.ts +151 -0
- package/src/grep.ts +618 -0
- package/src/index.ts +83 -0
- package/src/ledger.ts +72 -0
- package/src/payload.ts +382 -0
- package/src/port.ts +133 -0
- package/src/product-complexity.ts +2168 -0
- package/src/regex-safety.ts +1002 -0
- package/src/relevance-prerequisites.ts +83 -0
- package/src/relevance.ts +328 -0
- package/src/retrieval.ts +299 -0
- package/src/scoring.ts +321 -0
- package/src/sort.ts +86 -0
- package/src/symbol-match.ts +87 -0
- package/src/tokenize.ts +213 -0
- package/src/types.ts +206 -0
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
/** Independent recall branches, bounded provider calls and deterministic failure diagnostics. */
|
|
2
|
+
import { UntangleError, compareCodepoints, isUntangleError, } from "@untangle-code/domain";
|
|
3
|
+
import { withSearchBranchDeadline } from "./deadline.js";
|
|
4
|
+
import { buildSymbolNameFilter } from "./filter.js";
|
|
5
|
+
import { symbolNameFilterCandidates } from "./symbol-match.js";
|
|
6
|
+
/** The mode controls both recall and ranking, so it has one shared definition. */
|
|
7
|
+
export function branchesOfMode(mode) {
|
|
8
|
+
switch (mode) {
|
|
9
|
+
case "semantic":
|
|
10
|
+
return { dense: true, lexical: false, symbol: false };
|
|
11
|
+
case "lexical":
|
|
12
|
+
return { dense: false, lexical: true, symbol: false };
|
|
13
|
+
case "symbol":
|
|
14
|
+
return { dense: false, lexical: false, symbol: true };
|
|
15
|
+
case "hybrid":
|
|
16
|
+
return { dense: true, lexical: true, symbol: true };
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
/** Cancellation is control flow, never a degraded retrieval result. */
|
|
20
|
+
export function rethrowCancellation(error, options) {
|
|
21
|
+
options?.signal?.throwIfAborted();
|
|
22
|
+
if ((isUntangleError(error) && error.code === "JOB_CANCELLED") ||
|
|
23
|
+
(error instanceof Error && error.name === "AbortError")) {
|
|
24
|
+
throw error;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
async function retrieveDense(request, options) {
|
|
28
|
+
const { target } = request;
|
|
29
|
+
const preparedQuery = `${target.embedder.profile.queryPrefix}${request.text}`;
|
|
30
|
+
const dense = await target.embedder.embedQuery(preparedQuery, options);
|
|
31
|
+
options.signal?.throwIfAborted();
|
|
32
|
+
return request.vectorStore.hybridQuery(target.collection, {
|
|
33
|
+
dense,
|
|
34
|
+
filter: request.filter,
|
|
35
|
+
limit: request.candidateLimit,
|
|
36
|
+
candidateLimit: request.candidateLimit,
|
|
37
|
+
withPayload: true,
|
|
38
|
+
}, options);
|
|
39
|
+
}
|
|
40
|
+
async function retrieveSparse(request, options) {
|
|
41
|
+
if (request.sparseVectorizer === undefined) {
|
|
42
|
+
throw new UntangleError("INDEX_INCOMPLETE", "no sparse vectorizer is wired", {
|
|
43
|
+
operation: "search_sparse_retrieval",
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
return request.vectorStore.hybridQuery(request.target.collection, {
|
|
47
|
+
sparse: request.sparseVectorizer(request.text),
|
|
48
|
+
filter: request.filter,
|
|
49
|
+
limit: request.candidateLimit,
|
|
50
|
+
candidateLimit: request.candidateLimit,
|
|
51
|
+
withPayload: true,
|
|
52
|
+
}, options);
|
|
53
|
+
}
|
|
54
|
+
async function retrieveSymbol(request, options) {
|
|
55
|
+
const filter = buildSymbolNameFilter(request.filter, symbolNameFilterCandidates(request.text));
|
|
56
|
+
if (filter === undefined)
|
|
57
|
+
return { points: [] };
|
|
58
|
+
return request.vectorStore.scroll(request.target.collection, { filter, limit: request.candidateLimit }, options);
|
|
59
|
+
}
|
|
60
|
+
/** Each branch produces an explicit outcome; a provider failure does not cancel its peers. */
|
|
61
|
+
async function executeBranch(branch, enabled, request, retrieve, options) {
|
|
62
|
+
if (!enabled)
|
|
63
|
+
return { branch, status: "not_requested" };
|
|
64
|
+
try {
|
|
65
|
+
const result = await withSearchBranchDeadline(branch, request.mode === "hybrid" ? request.branchTimeoutMs : undefined, retrieve.bind(undefined, request), options, request.vectorStore.backend === "lancedb" ? "LANCEDB_UNAVAILABLE" : "QDRANT_UNAVAILABLE");
|
|
66
|
+
options.signal?.throwIfAborted();
|
|
67
|
+
return { branch, status: "succeeded", result };
|
|
68
|
+
}
|
|
69
|
+
catch (error) {
|
|
70
|
+
rethrowCancellation(error, options);
|
|
71
|
+
return {
|
|
72
|
+
branch,
|
|
73
|
+
status: "failed",
|
|
74
|
+
error: isUntangleError(error) || branch === "dense" || request.vectorStore.backend !== "lancedb"
|
|
75
|
+
? error
|
|
76
|
+
: new UntangleError("LANCEDB_UNAVAILABLE", error instanceof Error ? error.message : String(error), { operation: "search_retrieval", retryable: true, cause: error }),
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
function branchResult(outcome) {
|
|
81
|
+
return outcome.status === "succeeded" ? outcome.result : undefined;
|
|
82
|
+
}
|
|
83
|
+
function isFailedBranch(outcome) {
|
|
84
|
+
return outcome.status === "failed";
|
|
85
|
+
}
|
|
86
|
+
function branchFailureCode(failure) {
|
|
87
|
+
if (failure !== undefined && isUntangleError(failure.error))
|
|
88
|
+
return failure.error.code;
|
|
89
|
+
return failure?.branch === "dense" ? "EMBEDDING_UNAVAILABLE" : "QDRANT_UNAVAILABLE";
|
|
90
|
+
}
|
|
91
|
+
function timeoutDetails(error) {
|
|
92
|
+
if (!isUntangleError(error) || error.context["timedOut"] !== true)
|
|
93
|
+
return {};
|
|
94
|
+
return { timedOut: true, timeoutMs: error.context["timeoutMs"] };
|
|
95
|
+
}
|
|
96
|
+
/** Build evidence once in branch-name order, independent of completion timing. */
|
|
97
|
+
function failureDiagnostics(target, failures) {
|
|
98
|
+
const warnings = [];
|
|
99
|
+
const caveats = [];
|
|
100
|
+
const skipped = [];
|
|
101
|
+
for (const failure of failures) {
|
|
102
|
+
const { branch, error } = failure;
|
|
103
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
104
|
+
const detail = `profile "${target.embeddingProfileId}" retrieval branch "${branch}" failed: ${message}`;
|
|
105
|
+
const timeout = timeoutDetails(error);
|
|
106
|
+
caveats.push(detail);
|
|
107
|
+
warnings.push({
|
|
108
|
+
code: "SEARCH_BRANCH_UNAVAILABLE",
|
|
109
|
+
message: detail,
|
|
110
|
+
details: {
|
|
111
|
+
embeddingProfileId: target.embeddingProfileId,
|
|
112
|
+
collection: target.collection,
|
|
113
|
+
branch,
|
|
114
|
+
causeCode: branchFailureCode(failure),
|
|
115
|
+
...timeout,
|
|
116
|
+
},
|
|
117
|
+
});
|
|
118
|
+
if (timeout["timedOut"] === true) {
|
|
119
|
+
warnings.push({
|
|
120
|
+
code: "SEARCH_BRANCH_TIMEOUT",
|
|
121
|
+
message: detail,
|
|
122
|
+
details: { branch, timeoutMs: timeout["timeoutMs"] },
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
skipped.push({
|
|
126
|
+
subjectKind: "search_candidate",
|
|
127
|
+
subjectId: `${target.embeddingProfileId}:${branch}`,
|
|
128
|
+
reason: "provider_unavailable",
|
|
129
|
+
detail,
|
|
130
|
+
recoverable: true,
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
return { warnings, caveats, skipped };
|
|
134
|
+
}
|
|
135
|
+
/** Return all requested branch outcomes in fixed dense/sparse/symbol order. */
|
|
136
|
+
export async function retrieveIndependentBranches(request, options) {
|
|
137
|
+
options?.signal?.throwIfAborted();
|
|
138
|
+
const active = branchesOfMode(request.mode);
|
|
139
|
+
const controller = new AbortController();
|
|
140
|
+
const linkedOptions = {
|
|
141
|
+
...options,
|
|
142
|
+
signal: options?.signal === undefined
|
|
143
|
+
? controller.signal
|
|
144
|
+
: AbortSignal.any([options.signal, controller.signal]),
|
|
145
|
+
};
|
|
146
|
+
const [dense, sparse, symbol] = await Promise.all([
|
|
147
|
+
executeBranch("dense", active.dense, request, retrieveDense, linkedOptions),
|
|
148
|
+
executeBranch("sparse", active.lexical, request, retrieveSparse, linkedOptions),
|
|
149
|
+
executeBranch("symbol", active.symbol, request, retrieveSymbol, linkedOptions),
|
|
150
|
+
]).finally(() => controller.abort());
|
|
151
|
+
options?.signal?.throwIfAborted();
|
|
152
|
+
const outcomes = [dense, sparse, symbol];
|
|
153
|
+
const failures = outcomes
|
|
154
|
+
.filter(isFailedBranch)
|
|
155
|
+
.sort((left, right) => compareCodepoints(left.branch, right.branch));
|
|
156
|
+
const completedBranches = new Set(outcomes.filter((outcome) => outcome.status === "succeeded").map((outcome) => outcome.branch));
|
|
157
|
+
const symbolPage = branchResult(symbol);
|
|
158
|
+
return {
|
|
159
|
+
densePoints: branchResult(dense) ?? [],
|
|
160
|
+
sparsePoints: branchResult(sparse) ?? [],
|
|
161
|
+
...(symbolPage === undefined ? {} : { symbolPage }),
|
|
162
|
+
completedBranches,
|
|
163
|
+
failures,
|
|
164
|
+
...failureDiagnostics(request.target, failures),
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
/** The caller first records diagnostics, then distinguishes total failure from empty success. */
|
|
168
|
+
export function assertCompletedRetrieval(result, target) {
|
|
169
|
+
if (result.completedBranches.size > 0)
|
|
170
|
+
return;
|
|
171
|
+
const first = result.failures[0];
|
|
172
|
+
throw new UntangleError(branchFailureCode(first), `all requested retrieval branches failed for profile "${target.embeddingProfileId}": ${result.warnings.map((warning) => warning.message).join("; ")}`, {
|
|
173
|
+
operation: "search_retrieval",
|
|
174
|
+
retryable: result.failures.some(({ error }) => !isUntangleError(error) || error.retryable),
|
|
175
|
+
context: {
|
|
176
|
+
collection: target.collection,
|
|
177
|
+
failedBranches: result.failures.map(({ branch }) => branch),
|
|
178
|
+
},
|
|
179
|
+
cause: first?.error,
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
//# sourceMappingURL=retrieval.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"retrieval.js","sourceRoot":"","sources":["../src/retrieval.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Score-Zerlegung (spec.md §13.3).
|
|
3
|
+
*
|
|
4
|
+
* Das ist keine Debug-Ausgabe. Die Zerlegung ist oeffentliche API und die
|
|
5
|
+
* Grundlage der Findability-Diagnose (vision.md §1d): ohne sie laesst sich
|
|
6
|
+
* „der richtige Treffer rankt niedrig" nicht von „es gibt keinen Treffer"
|
|
7
|
+
* unterscheiden, und genau diese Unterscheidung ist die Aussage ueber den
|
|
8
|
+
* Code, nicht ueber die Suche.
|
|
9
|
+
*
|
|
10
|
+
* Aufbau:
|
|
11
|
+
*
|
|
12
|
+
* final = base_hybrid
|
|
13
|
+
* + exact_symbol_boost + exact_path_boost + repo_priority_boost
|
|
14
|
+
* + graph_proximity_boost + recency_boost
|
|
15
|
+
* + complexity_modifier
|
|
16
|
+
* - duplicate_penalty
|
|
17
|
+
*
|
|
18
|
+
* Jeder Bestandteil traegt `rawValue` (das normalisierte Signal), `weight`
|
|
19
|
+
* (das Gewicht aus der Konfiguration) und `contribution = rawValue * weight`.
|
|
20
|
+
* `validateScoreBreakdown()` aus der Domain prueft genau diese Identitaet und
|
|
21
|
+
* die Summenprobe — die Tests dieses Pakets rufen sie auf jedem erzeugten
|
|
22
|
+
* Treffer auf.
|
|
23
|
+
*
|
|
24
|
+
* BEGRENZUNG DER BOOSTS (spec.md §13.3: „Heuristische Boosts DUERFEN
|
|
25
|
+
* Dense/BM25 nicht vollstaendig ueberstimmen"): Uebersteigt die Summe der
|
|
26
|
+
* Betraege aller Nicht-Basis-Bestandteile den Anteil {@link MAX_BOOST_SHARE}
|
|
27
|
+
* des Basisbetrags, werden ALLE Nicht-Basis-Gewichte mit demselben Faktor
|
|
28
|
+
* verkleinert und `clamped`/`clampReason` gesetzt. Verkleinert wird das
|
|
29
|
+
* GEWICHT, nicht der Beitrag: nur so bleibt `contribution = rawValue * weight`
|
|
30
|
+
* wahr und das Signal (`rawValue`) sichtbar.
|
|
31
|
+
*/
|
|
32
|
+
import type { CodeChunkPayloadV1, ScoreBreakdownV1, ScoreComponentV1 } from "@untangle-code/domain";
|
|
33
|
+
import type { FusionResult } from "./fusion.js";
|
|
34
|
+
import type { SearchCandidate } from "./payload.js";
|
|
35
|
+
import type { ComplexityBias } from "./types.js";
|
|
36
|
+
/**
|
|
37
|
+
* Hoechster zulaessiger Anteil aller Boosts/Strafen am Basisscore.
|
|
38
|
+
*
|
|
39
|
+
* 0.5 heisst: die Heuristik darf einen Treffer um hoechstens die Haelfte
|
|
40
|
+
* seines Basisscores verschieben. Damit bleibt der Basisanteil (`baseShare`
|
|
41
|
+
* aus `validateScoreBreakdown`) immer bei mindestens 1/1.5 ≈ 0.667 — der
|
|
42
|
+
* Basiszweig behaelt also strukturell die Mehrheit. Der Wert ist eine
|
|
43
|
+
* Politik-Entscheidung dieses Pakets und hier bewusst als benannte Konstante
|
|
44
|
+
* sichtbar statt als Zahl im Code verstreut.
|
|
45
|
+
*/
|
|
46
|
+
export declare const MAX_BOOST_SHARE = 0.5;
|
|
47
|
+
/**
|
|
48
|
+
* Referenzwert der kognitiven Komplexitaet fuer den Komplexitaets-Modifikator.
|
|
49
|
+
* 25 ist der Punkt, ab dem der Modifikator seinen vollen Ausschlag erreicht;
|
|
50
|
+
* er ist eine Skalierungsgroesse fuer die Rangfolge und ausdruecklich KEINE
|
|
51
|
+
* Qualitaetsschwelle (Schwellen gehoeren in die Quality Gate, spec.md §18).
|
|
52
|
+
*/
|
|
53
|
+
export declare const COMPLEXITY_REFERENCE_COGNITIVE = 25;
|
|
54
|
+
/** Gewichte der additiven Score-Bestandteile (Form aus `SearchConfig`). */
|
|
55
|
+
export interface BoostWeights {
|
|
56
|
+
exactSymbol: number;
|
|
57
|
+
exactPath: number;
|
|
58
|
+
repoPriority: number;
|
|
59
|
+
graphProximity: number;
|
|
60
|
+
recency: number;
|
|
61
|
+
duplicatePenalty: number;
|
|
62
|
+
}
|
|
63
|
+
/** Gewicht des Komplexitaets-Modifikators, wenn die Anfrage ihn anfordert. */
|
|
64
|
+
export declare const COMPLEXITY_MODIFIER_WEIGHT = 0.1;
|
|
65
|
+
/** Alles, was zur Bewertung eines einzelnen Kandidaten gebraucht wird. */
|
|
66
|
+
export interface ScoringContext {
|
|
67
|
+
queryText: string;
|
|
68
|
+
weights: BoostWeights;
|
|
69
|
+
complexityBias: ComplexityBias;
|
|
70
|
+
/** Repo-Prioritaet in [0, 1] je `repo_id`; fehlt = kein Bestandteil. */
|
|
71
|
+
repoPriorities: Readonly<Record<string, number>>;
|
|
72
|
+
/** Graph-Naehe in [0, 1] je `symbol_id`; fehlt = kein Bestandteil. */
|
|
73
|
+
graphProximity: ReadonlyMap<string, number>;
|
|
74
|
+
/** „Jetzt" in Epoch-Millisekunden, ueber den Clock-Port bezogen. */
|
|
75
|
+
nowEpochMs?: number;
|
|
76
|
+
/** Halbwertszeit des Recency-Boosts in Tagen. */
|
|
77
|
+
recencyHalfLifeDays: number;
|
|
78
|
+
}
|
|
79
|
+
/** Zeitzerfall in [0, 1]: 1 heisst „gerade eben", 0.5 nach einer Halbwertszeit. */
|
|
80
|
+
export declare function recencyDecay(ageMs: number, halfLifeDays: number): number;
|
|
81
|
+
/**
|
|
82
|
+
* Komplexitaets-Modifikator in [-1, 1]. Positiv, wenn der Treffer der
|
|
83
|
+
* angeforderten Richtung entspricht.
|
|
84
|
+
*/
|
|
85
|
+
export declare function complexityModifierRawValue(payload: CodeChunkPayloadV1, bias: ComplexityBias): number | null;
|
|
86
|
+
/**
|
|
87
|
+
* Baut die vollstaendige Score-Zerlegung eines Kandidaten.
|
|
88
|
+
*
|
|
89
|
+
* `duplicatePenaltyRawValue` kommt aus der Diversifizierung (siehe
|
|
90
|
+
* `diversify.ts`) und ist bereits in [0, 1]; das Vorzeichen setzt diese
|
|
91
|
+
* Funktion, damit die Strafe an genau EINER Stelle negativ wird.
|
|
92
|
+
*/
|
|
93
|
+
export declare function assembleScoreBreakdown(candidate: SearchCandidate, fusion: FusionResult, context: ScoringContext, duplicatePenaltyRawValue?: number): ScoreBreakdownV1;
|
|
94
|
+
interface ClampResult {
|
|
95
|
+
boosts: ScoreComponentV1[];
|
|
96
|
+
clamped: boolean;
|
|
97
|
+
reason?: string;
|
|
98
|
+
}
|
|
99
|
+
/** Setzt {@link MAX_BOOST_SHARE} durch (siehe Modulkommentar). */
|
|
100
|
+
export declare function clampBoosts(base: ScoreComponentV1, boosts: readonly ScoreComponentV1[]): ClampResult;
|
|
101
|
+
export {};
|
|
102
|
+
//# sourceMappingURL=scoring.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scoring.d.ts","sourceRoot":"","sources":["../src/scoring.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,OAAO,KAAK,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAGpG,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAGpD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAEjD;;;;;;;;;GASG;AACH,eAAO,MAAM,eAAe,MAAM,CAAC;AAEnC;;;;;GAKG;AACH,eAAO,MAAM,8BAA8B,KAAK,CAAC;AAEjD,2EAA2E;AAC3E,MAAM,WAAW,YAAY;IAC3B,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC;IACrB,cAAc,EAAE,MAAM,CAAC;IACvB,OAAO,EAAE,MAAM,CAAC;IAChB,gBAAgB,EAAE,MAAM,CAAC;CAC1B;AAED,8EAA8E;AAC9E,eAAO,MAAM,0BAA0B,MAAM,CAAC;AAE9C,0EAA0E;AAC1E,MAAM,WAAW,cAAc;IAC7B,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,YAAY,CAAC;IACtB,cAAc,EAAE,cAAc,CAAC;IAC/B,wEAAwE;IACxE,cAAc,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IACjD,sEAAsE;IACtE,cAAc,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC5C,oEAAoE;IACpE,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,iDAAiD;IACjD,mBAAmB,EAAE,MAAM,CAAC;CAC7B;AA2BD,mFAAmF;AACnF,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,MAAM,CASxE;AAED;;;GAGG;AACH,wBAAgB,0BAA0B,CACxC,OAAO,EAAE,kBAAkB,EAC3B,IAAI,EAAE,cAAc,GACnB,MAAM,GAAG,IAAI,CAUf;AAED;;;;;;GAMG;AACH,wBAAgB,sBAAsB,CACpC,SAAS,EAAE,eAAe,EAC1B,MAAM,EAAE,YAAY,EACpB,OAAO,EAAE,cAAc,EACvB,wBAAwB,SAAI,GAC3B,gBAAgB,CAkHlB;AAoBD,UAAU,WAAW;IACnB,MAAM,EAAE,gBAAgB,EAAE,CAAC;IAC3B,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,kEAAkE;AAClE,wBAAgB,WAAW,CACzB,IAAI,EAAE,gBAAgB,EACtB,MAAM,EAAE,SAAS,gBAAgB,EAAE,GAClC,WAAW,CAoBb"}
|
package/dist/scoring.js
ADDED
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
import { RANKER_VERSION } from "@untangle-code/domain";
|
|
2
|
+
import { readRawNumber } from "./payload.js";
|
|
3
|
+
import { matchSymbolName } from "./symbol-match.js";
|
|
4
|
+
/**
|
|
5
|
+
* Hoechster zulaessiger Anteil aller Boosts/Strafen am Basisscore.
|
|
6
|
+
*
|
|
7
|
+
* 0.5 heisst: die Heuristik darf einen Treffer um hoechstens die Haelfte
|
|
8
|
+
* seines Basisscores verschieben. Damit bleibt der Basisanteil (`baseShare`
|
|
9
|
+
* aus `validateScoreBreakdown`) immer bei mindestens 1/1.5 ≈ 0.667 — der
|
|
10
|
+
* Basiszweig behaelt also strukturell die Mehrheit. Der Wert ist eine
|
|
11
|
+
* Politik-Entscheidung dieses Pakets und hier bewusst als benannte Konstante
|
|
12
|
+
* sichtbar statt als Zahl im Code verstreut.
|
|
13
|
+
*/
|
|
14
|
+
export const MAX_BOOST_SHARE = 0.5;
|
|
15
|
+
/**
|
|
16
|
+
* Referenzwert der kognitiven Komplexitaet fuer den Komplexitaets-Modifikator.
|
|
17
|
+
* 25 ist der Punkt, ab dem der Modifikator seinen vollen Ausschlag erreicht;
|
|
18
|
+
* er ist eine Skalierungsgroesse fuer die Rangfolge und ausdruecklich KEINE
|
|
19
|
+
* Qualitaetsschwelle (Schwellen gehoeren in die Quality Gate, spec.md §18).
|
|
20
|
+
*/
|
|
21
|
+
export const COMPLEXITY_REFERENCE_COGNITIVE = 25;
|
|
22
|
+
/** Gewicht des Komplexitaets-Modifikators, wenn die Anfrage ihn anfordert. */
|
|
23
|
+
export const COMPLEXITY_MODIFIER_WEIGHT = 0.1;
|
|
24
|
+
function pathBoost(payload, queryText) {
|
|
25
|
+
const query = queryText.trim();
|
|
26
|
+
if (query === "") {
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
29
|
+
const path = payload.file_path;
|
|
30
|
+
const segments = path.split("/");
|
|
31
|
+
if (path === query) {
|
|
32
|
+
return raw("exact_path_boost", 1, `file_path == "${path}"`);
|
|
33
|
+
}
|
|
34
|
+
if (segments.includes(query)) {
|
|
35
|
+
return raw("exact_path_boost", 0.75, `path segment == "${query}"`);
|
|
36
|
+
}
|
|
37
|
+
if (path.includes(query)) {
|
|
38
|
+
return raw("exact_path_boost", 0.5, `file_path contains "${query}"`);
|
|
39
|
+
}
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
function raw(kind, rawValue, evidence) {
|
|
43
|
+
// `weight` wird erst in `assembleScoreBreakdown` gesetzt; hier steht das
|
|
44
|
+
// rohe Signal mit seinem Beleg.
|
|
45
|
+
return { kind, rawValue, weight: 0, contribution: 0, evidence };
|
|
46
|
+
}
|
|
47
|
+
/** Zeitzerfall in [0, 1]: 1 heisst „gerade eben", 0.5 nach einer Halbwertszeit. */
|
|
48
|
+
export function recencyDecay(ageMs, halfLifeDays) {
|
|
49
|
+
if (ageMs <= 0) {
|
|
50
|
+
return 1;
|
|
51
|
+
}
|
|
52
|
+
const halfLifeMs = halfLifeDays * 24 * 60 * 60 * 1000;
|
|
53
|
+
if (halfLifeMs <= 0) {
|
|
54
|
+
return 0;
|
|
55
|
+
}
|
|
56
|
+
return Math.pow(0.5, ageMs / halfLifeMs);
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Komplexitaets-Modifikator in [-1, 1]. Positiv, wenn der Treffer der
|
|
60
|
+
* angeforderten Richtung entspricht.
|
|
61
|
+
*/
|
|
62
|
+
export function complexityModifierRawValue(payload, bias) {
|
|
63
|
+
if (bias === "none") {
|
|
64
|
+
return null;
|
|
65
|
+
}
|
|
66
|
+
const cognitive = payload.cognitive;
|
|
67
|
+
if (cognitive === undefined) {
|
|
68
|
+
return null;
|
|
69
|
+
}
|
|
70
|
+
const normalized = Math.min(cognitive / COMPLEXITY_REFERENCE_COGNITIVE, 1);
|
|
71
|
+
return bias === "prefer-simple" ? 1 - 2 * normalized : 2 * normalized - 1;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Baut die vollstaendige Score-Zerlegung eines Kandidaten.
|
|
75
|
+
*
|
|
76
|
+
* `duplicatePenaltyRawValue` kommt aus der Diversifizierung (siehe
|
|
77
|
+
* `diversify.ts`) und ist bereits in [0, 1]; das Vorzeichen setzt diese
|
|
78
|
+
* Funktion, damit die Strafe an genau EINER Stelle negativ wird.
|
|
79
|
+
*/
|
|
80
|
+
export function assembleScoreBreakdown(candidate, fusion, context, duplicatePenaltyRawValue = 0) {
|
|
81
|
+
const { payload } = candidate;
|
|
82
|
+
const base = {
|
|
83
|
+
kind: "base_hybrid",
|
|
84
|
+
rawValue: fusion.normalized,
|
|
85
|
+
weight: 1,
|
|
86
|
+
contribution: fusion.normalized,
|
|
87
|
+
evidence: describeBranches(fusion),
|
|
88
|
+
};
|
|
89
|
+
const boosts = [];
|
|
90
|
+
const symbolMatch = matchSymbolName(payload, context.queryText);
|
|
91
|
+
if (symbolMatch !== null) {
|
|
92
|
+
boosts.push(withWeight(raw("exact_symbol_boost", symbolMatch.strength, symbolMatch.evidence), context.weights.exactSymbol));
|
|
93
|
+
}
|
|
94
|
+
const pathComponent = pathBoost(payload, context.queryText);
|
|
95
|
+
if (pathComponent !== null) {
|
|
96
|
+
boosts.push(withWeight(pathComponent, context.weights.exactPath));
|
|
97
|
+
}
|
|
98
|
+
const priority = context.repoPriorities[payload.repo_id];
|
|
99
|
+
if (priority !== undefined && priority !== 0) {
|
|
100
|
+
boosts.push(withWeight(raw("repo_priority_boost", priority, `configured priority of repo "${payload.repo_id}"`), context.weights.repoPriority));
|
|
101
|
+
}
|
|
102
|
+
const proximity = payload.symbol_id === undefined ? undefined : context.graphProximity.get(payload.symbol_id);
|
|
103
|
+
if (proximity !== undefined && proximity !== 0) {
|
|
104
|
+
boosts.push(withWeight(raw("graph_proximity_boost", proximity, `graph proximity of symbol "${payload.symbol_id ?? ""}"`), context.weights.graphProximity));
|
|
105
|
+
}
|
|
106
|
+
const changedAt = readRawNumber(candidate.raw, "changed_at_epoch_ms");
|
|
107
|
+
if (changedAt !== undefined && context.nowEpochMs !== undefined) {
|
|
108
|
+
const decay = recencyDecay(context.nowEpochMs - changedAt, context.recencyHalfLifeDays);
|
|
109
|
+
if (decay > 0) {
|
|
110
|
+
boosts.push(withWeight(raw("recency_boost", decay, `changed_at_epoch_ms = ${changedAt}`), context.weights.recency));
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
const complexityRaw = complexityModifierRawValue(payload, context.complexityBias);
|
|
114
|
+
if (complexityRaw !== null) {
|
|
115
|
+
boosts.push(withWeight(raw("complexity_modifier", complexityRaw, `cognitive = ${String(payload.cognitive)} (bias "${context.complexityBias}", reference ${COMPLEXITY_REFERENCE_COGNITIVE})`), COMPLEXITY_MODIFIER_WEIGHT));
|
|
116
|
+
}
|
|
117
|
+
if (duplicatePenaltyRawValue > 0) {
|
|
118
|
+
boosts.push(withWeight(raw("duplicate_penalty", duplicatePenaltyRawValue, `file "${payload.file_path}" is already represented higher in the ranking`), -context.weights.duplicatePenalty));
|
|
119
|
+
}
|
|
120
|
+
const clamp = clampBoosts(base, boosts);
|
|
121
|
+
const components = [base, ...clamp.boosts];
|
|
122
|
+
const final = components.reduce((sum, component) => sum + component.contribution, 0);
|
|
123
|
+
return {
|
|
124
|
+
schemaVersion: 1,
|
|
125
|
+
final,
|
|
126
|
+
components,
|
|
127
|
+
inputs: {
|
|
128
|
+
...(candidate.denseScore === undefined ? {} : { dense: candidate.denseScore }),
|
|
129
|
+
...(candidate.denseRank === undefined ? {} : { denseRank: candidate.denseRank }),
|
|
130
|
+
...(candidate.lexicalRank === undefined
|
|
131
|
+
? {}
|
|
132
|
+
: { sparseRank: candidate.lexicalRank, lexicalRank: candidate.lexicalRank }),
|
|
133
|
+
...(candidate.lexicalScore === undefined ? {} : { lexicalScore: candidate.lexicalScore }),
|
|
134
|
+
...(candidate.symbolRank === undefined ? {} : { symbolRank: candidate.symbolRank }),
|
|
135
|
+
rrf: fusion.rrf,
|
|
136
|
+
},
|
|
137
|
+
rankerVersion: RANKER_VERSION,
|
|
138
|
+
clamped: clamp.clamped,
|
|
139
|
+
...(clamp.reason === undefined ? {} : { clampReason: clamp.reason }),
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
function withWeight(component, weight) {
|
|
143
|
+
return { ...component, weight, contribution: component.rawValue * weight };
|
|
144
|
+
}
|
|
145
|
+
function describeBranches(fusion) {
|
|
146
|
+
const parts = [];
|
|
147
|
+
for (const branch of ["dense", "lexical", "symbol"]) {
|
|
148
|
+
const rank = fusion.ranks[branch];
|
|
149
|
+
if (rank !== undefined) {
|
|
150
|
+
const weight = fusion.weights?.[branch];
|
|
151
|
+
parts.push(weight === undefined ? `${branch}#${rank}` : `${branch}#${rank} x weight ${weight}`);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
return parts.length === 0 ? "no branch contributed" : `RRF over ${parts.join(", ")}`;
|
|
155
|
+
}
|
|
156
|
+
/** Setzt {@link MAX_BOOST_SHARE} durch (siehe Modulkommentar). */
|
|
157
|
+
export function clampBoosts(base, boosts) {
|
|
158
|
+
const boostMagnitude = boosts.reduce((sum, component) => sum + Math.abs(component.contribution), 0);
|
|
159
|
+
const baseMagnitude = Math.abs(base.contribution);
|
|
160
|
+
const budget = baseMagnitude * MAX_BOOST_SHARE;
|
|
161
|
+
if (boostMagnitude <= budget || boostMagnitude === 0) {
|
|
162
|
+
return { boosts: [...boosts], clamped: false };
|
|
163
|
+
}
|
|
164
|
+
const factor = budget / boostMagnitude;
|
|
165
|
+
return {
|
|
166
|
+
boosts: boosts.map((component) => ({
|
|
167
|
+
...component,
|
|
168
|
+
weight: component.weight * factor,
|
|
169
|
+
contribution: component.contribution * factor,
|
|
170
|
+
})),
|
|
171
|
+
clamped: true,
|
|
172
|
+
reason: `boost magnitude ${boostMagnitude.toFixed(6)} exceeded ${MAX_BOOST_SHARE} of the base score ${baseMagnitude.toFixed(6)}; all non-base weights scaled by ${factor.toFixed(6)} (spec.md §13.3)`,
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
//# sourceMappingURL=scoring.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scoring.js","sourceRoot":"","sources":["../src/scoring.ts"],"names":[],"mappings":"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"}
|
package/dist/sort.d.ts
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sortierung der Trefferliste (Nutzerwunsch A4: „Sortierung").
|
|
3
|
+
*
|
|
4
|
+
* Jede Sortierung ist zweistufig: erst das gewaehlte Kriterium, dann — IMMER —
|
|
5
|
+
* der Dedupe-Schluessel nach Codepoints. Der zweite Schritt ist nicht Kosmetik:
|
|
6
|
+
* ohne ihn haengt die Reihenfolge gleichbewerteter Treffer an der
|
|
7
|
+
* Antwortreihenfolge des Speichers, und dieselbe Anfrage liefert auf zwei
|
|
8
|
+
* Maschinen zwei Listen. `String#localeCompare` kommt dabei nie zum Einsatz
|
|
9
|
+
* (docs/BUILD_PLAN.md §3).
|
|
10
|
+
*
|
|
11
|
+
* Treffer ohne den jeweiligen Wert (keine Komplexitaet gemessen, keine
|
|
12
|
+
* Findability berechnet) landen ans ENDE der Liste, nicht an den Anfang: ein
|
|
13
|
+
* fehlender Wert ist kein Bestwert.
|
|
14
|
+
*/
|
|
15
|
+
import type { SearchHitV1 } from "@untangle-code/domain";
|
|
16
|
+
import type { SearchSort } from "./types.js";
|
|
17
|
+
/** Sortiert eine Kopie der Treffer nach dem gewaehlten Kriterium. */
|
|
18
|
+
export declare function sortHits(hits: readonly SearchHitV1[], sort: SearchSort): SearchHitV1[];
|
|
19
|
+
/** Weist die 1-basierten Raenge INNERHALB einer Profilgruppe zu. */
|
|
20
|
+
export declare function assignRanks(hits: readonly SearchHitV1[]): SearchHitV1[];
|
|
21
|
+
//# sourceMappingURL=sort.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sort.d.ts","sourceRoot":"","sources":["../src/sort.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAGzD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAuB7C,qEAAqE;AACrE,wBAAgB,QAAQ,CAAC,IAAI,EAAE,SAAS,WAAW,EAAE,EAAE,IAAI,EAAE,UAAU,GAAG,WAAW,EAAE,CAuCtF;AAED,oEAAoE;AACpE,wBAAgB,WAAW,CAAC,IAAI,EAAE,SAAS,WAAW,EAAE,GAAG,WAAW,EAAE,CAEvE"}
|
package/dist/sort.js
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { compareCodepoints } from "@untangle-code/domain";
|
|
2
|
+
function tiebreak(a, b) {
|
|
3
|
+
return compareCodepoints(a.dedupeKey, b.dedupeKey);
|
|
4
|
+
}
|
|
5
|
+
function compareOptionalNumber(a, b, direction) {
|
|
6
|
+
if (a === undefined && b === undefined) {
|
|
7
|
+
return 0;
|
|
8
|
+
}
|
|
9
|
+
if (a === undefined) {
|
|
10
|
+
return 1;
|
|
11
|
+
}
|
|
12
|
+
if (b === undefined) {
|
|
13
|
+
return -1;
|
|
14
|
+
}
|
|
15
|
+
return direction === "asc" ? a - b : b - a;
|
|
16
|
+
}
|
|
17
|
+
/** Sortiert eine Kopie der Treffer nach dem gewaehlten Kriterium. */
|
|
18
|
+
export function sortHits(hits, sort) {
|
|
19
|
+
const copy = [...hits];
|
|
20
|
+
switch (sort) {
|
|
21
|
+
case "relevance":
|
|
22
|
+
copy.sort((a, b) => a.score.final === b.score.final ? tiebreak(a, b) : b.score.final - a.score.final);
|
|
23
|
+
break;
|
|
24
|
+
case "path":
|
|
25
|
+
copy.sort((a, b) => compareCodepoints(a.repoId, b.repoId) ||
|
|
26
|
+
compareCodepoints(a.filePath, b.filePath) ||
|
|
27
|
+
(a.range.startLine !== b.range.startLine
|
|
28
|
+
? a.range.startLine - b.range.startLine
|
|
29
|
+
: tiebreak(a, b)));
|
|
30
|
+
break;
|
|
31
|
+
case "complexity-desc":
|
|
32
|
+
case "complexity-asc": {
|
|
33
|
+
const direction = sort === "complexity-asc" ? "asc" : "desc";
|
|
34
|
+
copy.sort((a, b) => {
|
|
35
|
+
const result = compareOptionalNumber(a.complexity?.cognitive, b.complexity?.cognitive, direction);
|
|
36
|
+
return result === 0 ? tiebreak(a, b) : result;
|
|
37
|
+
});
|
|
38
|
+
break;
|
|
39
|
+
}
|
|
40
|
+
case "findability-asc":
|
|
41
|
+
copy.sort((a, b) => {
|
|
42
|
+
const result = compareOptionalNumber(a.findability?.score, b.findability?.score, "asc");
|
|
43
|
+
return result === 0 ? tiebreak(a, b) : result;
|
|
44
|
+
});
|
|
45
|
+
break;
|
|
46
|
+
}
|
|
47
|
+
return copy;
|
|
48
|
+
}
|
|
49
|
+
/** Weist die 1-basierten Raenge INNERHALB einer Profilgruppe zu. */
|
|
50
|
+
export function assignRanks(hits) {
|
|
51
|
+
return hits.map((hit, index) => ({ ...hit, rank: index + 1 }));
|
|
52
|
+
}
|
|
53
|
+
//# sourceMappingURL=sort.js.map
|
package/dist/sort.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sort.js","sourceRoot":"","sources":["../src/sort.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { CodeChunkPayloadV1 } from "@untangle-code/domain";
|
|
2
|
+
/** Ergebnis eines Namensvergleichs. */
|
|
3
|
+
export interface SymbolMatch {
|
|
4
|
+
strength: number;
|
|
5
|
+
/** Beleg fuer die Score-Zerlegung, z. B. `symbol_name == "charge"`. */
|
|
6
|
+
evidence: string;
|
|
7
|
+
}
|
|
8
|
+
/** Vergleicht den Suchtext mit den Namensfeldern eines Chunks. */
|
|
9
|
+
export declare function matchSymbolName(payload: CodeChunkPayloadV1, queryText: string): SymbolMatch | null;
|
|
10
|
+
/**
|
|
11
|
+
* Kandidatenwerte fuer den Speicher-Filter auf `symbol_name`.
|
|
12
|
+
*
|
|
13
|
+
* Der Payload-Filter des Speichers kann nur exakte Werte vergleichen. Deshalb
|
|
14
|
+
* werden hier deterministisch die Schreibweisen erzeugt, die derselbe Name
|
|
15
|
+
* ueblicherweise hat: der Suchtext selbst, sein letztes Pfad-/FQN-Glied und —
|
|
16
|
+
* falls der Suchtext genau ein Wort ist — dieses Wort. Mehr NICHT: eine
|
|
17
|
+
* Kombinatorik ueber camelCase/snake_case-Varianten wuerde Treffer erfinden,
|
|
18
|
+
* die im Index gar nicht so heissen.
|
|
19
|
+
*/
|
|
20
|
+
export declare function symbolNameFilterCandidates(queryText: string): string[];
|
|
21
|
+
//# sourceMappingURL=symbol-match.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"symbol-match.d.ts","sourceRoot":"","sources":["../src/symbol-match.ts"],"names":[],"mappings":"AAoBA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAIhE,uCAAuC;AACvC,MAAM,WAAW,WAAW;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,uEAAuE;IACvE,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,kEAAkE;AAClE,wBAAgB,eAAe,CAC7B,OAAO,EAAE,kBAAkB,EAC3B,SAAS,EAAE,MAAM,GAChB,WAAW,GAAG,IAAI,CA4BpB;AAED;;;;;;;;;GASG;AACH,wBAAgB,0BAA0B,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,EAAE,CAWtE"}
|