@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,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Exakte und nahe Symbolnamen-Treffer (dritter Abrufzweig).
|
|
3
|
+
*
|
|
4
|
+
* Der Nutzerfall aus vision.md §1d ist woertlich: „wenn ich nach ‚Funktion X'
|
|
5
|
+
* suche". Ein dichter Vektor kann einen exakten Namen verfehlen, weil er
|
|
6
|
+
* Bedeutung und nicht Zeichen vergleicht; BM25 kann ihn unter langen Rumpf-
|
|
7
|
+
* texten begraben. Der Symbolzweig ist deshalb kein Bonus auf einen anderen
|
|
8
|
+
* Zweig, sondern eine eigene Rangliste mit eigener Begruendung.
|
|
9
|
+
*
|
|
10
|
+
* Vier Stufen, absichtlich grob und vollstaendig dokumentiert (jede feinere
|
|
11
|
+
* Abstufung waere ein magisches Gewicht ohne Beleg):
|
|
12
|
+
*
|
|
13
|
+
* | Staerke | Bedingung |
|
|
14
|
+
* |---|---|
|
|
15
|
+
* | 1.00 | `symbol_name` ist zeichengleich mit dem Suchtext |
|
|
16
|
+
* | 0.80 | zeichengleich bis auf Gross-/Kleinschreibung |
|
|
17
|
+
* | 0.60 | `symbol_fqn` endet auf den Suchtext (`Service.charge`) |
|
|
18
|
+
* | 0.40 | alle Teilwoerter der Anfrage stecken im Namen (`getUser` ⊂ `getUserById`) |
|
|
19
|
+
*/
|
|
20
|
+
import { compareCodepoints } from "@untangle-code/domain";
|
|
21
|
+
import { identifierParts } from "./tokenize.js";
|
|
22
|
+
/** Vergleicht den Suchtext mit den Namensfeldern eines Chunks. */
|
|
23
|
+
export function matchSymbolName(payload, queryText) {
|
|
24
|
+
const name = payload.symbol_name;
|
|
25
|
+
const query = queryText.trim();
|
|
26
|
+
if (query === "") {
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
29
|
+
if (name !== undefined && name === query) {
|
|
30
|
+
return { strength: 1, evidence: `symbol_name == "${name}"` };
|
|
31
|
+
}
|
|
32
|
+
if (name !== undefined && name.toLowerCase() === query.toLowerCase()) {
|
|
33
|
+
return { strength: 0.8, evidence: `symbol_name ~= "${name}" (case-insensitive)` };
|
|
34
|
+
}
|
|
35
|
+
const fqn = payload.symbol_fqn;
|
|
36
|
+
if (fqn !== undefined &&
|
|
37
|
+
(fqn === query || fqn.endsWith(`.${query}`) || fqn.endsWith(`::${query}`))) {
|
|
38
|
+
return { strength: 0.6, evidence: `symbol_fqn "${fqn}" ends with "${query}"` };
|
|
39
|
+
}
|
|
40
|
+
if (name === undefined) {
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
const queryParts = identifierParts(query);
|
|
44
|
+
const nameParts = new Set(identifierParts(name));
|
|
45
|
+
if (queryParts.length > 0 && queryParts.every((part) => nameParts.has(part))) {
|
|
46
|
+
return { strength: 0.4, evidence: `symbol_name "${name}" contains all query parts` };
|
|
47
|
+
}
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Kandidatenwerte fuer den Speicher-Filter auf `symbol_name`.
|
|
52
|
+
*
|
|
53
|
+
* Der Payload-Filter des Speichers kann nur exakte Werte vergleichen. Deshalb
|
|
54
|
+
* werden hier deterministisch die Schreibweisen erzeugt, die derselbe Name
|
|
55
|
+
* ueblicherweise hat: der Suchtext selbst, sein letztes Pfad-/FQN-Glied und —
|
|
56
|
+
* falls der Suchtext genau ein Wort ist — dieses Wort. Mehr NICHT: eine
|
|
57
|
+
* Kombinatorik ueber camelCase/snake_case-Varianten wuerde Treffer erfinden,
|
|
58
|
+
* die im Index gar nicht so heissen.
|
|
59
|
+
*/
|
|
60
|
+
export function symbolNameFilterCandidates(queryText) {
|
|
61
|
+
const query = queryText.trim();
|
|
62
|
+
if (query === "") {
|
|
63
|
+
return [];
|
|
64
|
+
}
|
|
65
|
+
const candidates = new Set([query]);
|
|
66
|
+
const lastSegment = query.split(/[.:#/]/u).at(-1);
|
|
67
|
+
if (lastSegment !== undefined && lastSegment !== "") {
|
|
68
|
+
candidates.add(lastSegment);
|
|
69
|
+
}
|
|
70
|
+
return [...candidates].sort(compareCodepoints);
|
|
71
|
+
}
|
|
72
|
+
//# sourceMappingURL=symbol-match.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"symbol-match.js","sourceRoot":"","sources":["../src/symbol-match.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AACH,OAAO,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAG1D,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAShD,kEAAkE;AAClE,MAAM,UAAU,eAAe,CAC7B,OAA2B,EAC3B,SAAiB;IAEjB,MAAM,IAAI,GAAG,OAAO,CAAC,WAAW,CAAC;IACjC,MAAM,KAAK,GAAG,SAAS,CAAC,IAAI,EAAE,CAAC;IAC/B,IAAI,KAAK,KAAK,EAAE,EAAE,CAAC;QACjB,OAAO,IAAI,CAAC;IACd,CAAC;IACD,IAAI,IAAI,KAAK,SAAS,IAAI,IAAI,KAAK,KAAK,EAAE,CAAC;QACzC,OAAO,EAAE,QAAQ,EAAE,CAAC,EAAE,QAAQ,EAAE,mBAAmB,IAAI,GAAG,EAAE,CAAC;IAC/D,CAAC;IACD,IAAI,IAAI,KAAK,SAAS,IAAI,IAAI,CAAC,WAAW,EAAE,KAAK,KAAK,CAAC,WAAW,EAAE,EAAE,CAAC;QACrE,OAAO,EAAE,QAAQ,EAAE,GAAG,EAAE,QAAQ,EAAE,mBAAmB,IAAI,sBAAsB,EAAE,CAAC;IACpF,CAAC;IACD,MAAM,GAAG,GAAG,OAAO,CAAC,UAAU,CAAC;IAC/B,IACE,GAAG,KAAK,SAAS;QACjB,CAAC,GAAG,KAAK,KAAK,IAAI,GAAG,CAAC,QAAQ,CAAC,IAAI,KAAK,EAAE,CAAC,IAAI,GAAG,CAAC,QAAQ,CAAC,KAAK,KAAK,EAAE,CAAC,CAAC,EAC1E,CAAC;QACD,OAAO,EAAE,QAAQ,EAAE,GAAG,EAAE,QAAQ,EAAE,eAAe,GAAG,gBAAgB,KAAK,GAAG,EAAE,CAAC;IACjF,CAAC;IACD,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;QACvB,OAAO,IAAI,CAAC;IACd,CAAC;IACD,MAAM,UAAU,GAAG,eAAe,CAAC,KAAK,CAAC,CAAC;IAC1C,MAAM,SAAS,GAAG,IAAI,GAAG,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC,CAAC;IACjD,IAAI,UAAU,CAAC,MAAM,GAAG,CAAC,IAAI,UAAU,CAAC,KAAK,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC;QAC7E,OAAO,EAAE,QAAQ,EAAE,GAAG,EAAE,QAAQ,EAAE,gBAAgB,IAAI,4BAA4B,EAAE,CAAC;IACvF,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,0BAA0B,CAAC,SAAiB;IAC1D,MAAM,KAAK,GAAG,SAAS,CAAC,IAAI,EAAE,CAAC;IAC/B,IAAI,KAAK,KAAK,EAAE,EAAE,CAAC;QACjB,OAAO,EAAE,CAAC;IACZ,CAAC;IACD,MAAM,UAAU,GAAG,IAAI,GAAG,CAAS,CAAC,KAAK,CAAC,CAAC,CAAC;IAC5C,MAAM,WAAW,GAAG,KAAK,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;IAClD,IAAI,WAAW,KAAK,SAAS,IAAI,WAAW,KAAK,EAAE,EAAE,CAAC;QACpD,UAAU,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;IAC9B,CAAC;IACD,OAAO,CAAC,GAAG,UAAU,CAAC,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAC;AACjD,CAAC"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bezeichner-bewusste Tokenisierung fuer den lexikalischen Zweig
|
|
3
|
+
* (spec.md §13.2 Schritt 3: „Query normalisieren, ohne Identifier-Zeichen zu
|
|
4
|
+
* zerstoeren").
|
|
5
|
+
*
|
|
6
|
+
* Eine reine Wortgrenzen-Tokenisierung („alles ausser Buchstaben/Ziffern ist
|
|
7
|
+
* Trenner") zerlegt `get_user_by_id` korrekt, `getUserById` aber ueberhaupt
|
|
8
|
+
* nicht — dabei ist genau das der Fall, den der Nutzer meint, wenn er nach
|
|
9
|
+
* „Funktion X" sucht. Diese Tokenisierung liefert deshalb ZWEI Ebenen:
|
|
10
|
+
*
|
|
11
|
+
* 1. den ganzen, kleingeschriebenen Bezeichner (`getuserbyid`) — damit ein
|
|
12
|
+
* exakter Namenstreffer den hoechsten lexikalischen Gewinn hat;
|
|
13
|
+
* 2. seine Teilwoerter (`get`, `user`, `by`, `id`) — damit ein Treffer auch
|
|
14
|
+
* ueber Teilbegriffe gefunden wird.
|
|
15
|
+
*
|
|
16
|
+
* Beide Ebenen landen im selben Token-Bag. Das ist bewusst: BM25 belohnt
|
|
17
|
+
* dadurch einen Volltreffer zusaetzlich zu seinen Teilen, ohne dass es dafuer
|
|
18
|
+
* ein Sonderregelwerk im Ranker braucht.
|
|
19
|
+
*
|
|
20
|
+
* Alles hier ist reine Arithmetik ueber den Eingabetext — kein Locale, keine
|
|
21
|
+
* Zeit, kein Zufall (docs/BUILD_PLAN.md §3 „Determinismus").
|
|
22
|
+
*/
|
|
23
|
+
export { identifierParts, splitIdentifierRun, tokenizeIdentifiers } from "@untangle-code/domain";
|
|
24
|
+
/**
|
|
25
|
+
* Tokenisiert eine frei formulierte Anfrage fuer den lexikalischen Rang und
|
|
26
|
+
* entfernt nur grammatische Fragehuelsen. Wiederholungen bleiben erhalten;
|
|
27
|
+
* der Aufrufer entscheidet wie beim normalen Token-Bag selbst, ob er daraus
|
|
28
|
+
* eine Menge oder Haeufigkeiten braucht.
|
|
29
|
+
*/
|
|
30
|
+
export declare function informativeQueryTokens(text: string): string[];
|
|
31
|
+
/**
|
|
32
|
+
* Erkennt die enge Form, fuer die der Benchmark einen systematischen
|
|
33
|
+
* Cross-Language-Konflikt belegt: eine deutsch formulierte Frage vor
|
|
34
|
+
* ueberwiegend englischen Codebezeichnern. Das ist bewusst KEINE allgemeine
|
|
35
|
+
* Spracherkennung. Eindeutige deutsche Fragewoerter reichen; das auch im
|
|
36
|
+
* Englischen vorkommende `was` braucht zwei weitere Grammatikmarker.
|
|
37
|
+
*/
|
|
38
|
+
export declare function isLikelyGermanQuestion(text: string): boolean;
|
|
39
|
+
/** Token-Haeufigkeiten als Map; Reihenfolge der Einfuegung ist Textreihenfolge. */
|
|
40
|
+
export declare function termFrequencies(tokens: readonly string[]): Map<string, number>;
|
|
41
|
+
//# sourceMappingURL=tokenize.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tokenize.d.ts","sourceRoot":"","sources":["../src/tokenize.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAKH,OAAO,EAAE,eAAe,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AAiJjG;;;;;GAKG;AACH,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CAI7D;AAED;;;;;;GAMG;AACH,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAa5D;AAED,mFAAmF;AACnF,wBAAgB,eAAe,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,GAAG,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAM9E"}
|
package/dist/tokenize.js
ADDED
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bezeichner-bewusste Tokenisierung fuer den lexikalischen Zweig
|
|
3
|
+
* (spec.md §13.2 Schritt 3: „Query normalisieren, ohne Identifier-Zeichen zu
|
|
4
|
+
* zerstoeren").
|
|
5
|
+
*
|
|
6
|
+
* Eine reine Wortgrenzen-Tokenisierung („alles ausser Buchstaben/Ziffern ist
|
|
7
|
+
* Trenner") zerlegt `get_user_by_id` korrekt, `getUserById` aber ueberhaupt
|
|
8
|
+
* nicht — dabei ist genau das der Fall, den der Nutzer meint, wenn er nach
|
|
9
|
+
* „Funktion X" sucht. Diese Tokenisierung liefert deshalb ZWEI Ebenen:
|
|
10
|
+
*
|
|
11
|
+
* 1. den ganzen, kleingeschriebenen Bezeichner (`getuserbyid`) — damit ein
|
|
12
|
+
* exakter Namenstreffer den hoechsten lexikalischen Gewinn hat;
|
|
13
|
+
* 2. seine Teilwoerter (`get`, `user`, `by`, `id`) — damit ein Treffer auch
|
|
14
|
+
* ueber Teilbegriffe gefunden wird.
|
|
15
|
+
*
|
|
16
|
+
* Beide Ebenen landen im selben Token-Bag. Das ist bewusst: BM25 belohnt
|
|
17
|
+
* dadurch einen Volltreffer zusaetzlich zu seinen Teilen, ohne dass es dafuer
|
|
18
|
+
* ein Sonderregelwerk im Ranker braucht.
|
|
19
|
+
*
|
|
20
|
+
* Alles hier ist reine Arithmetik ueber den Eingabetext — kein Locale, keine
|
|
21
|
+
* Zeit, kein Zufall (docs/BUILD_PLAN.md §3 „Determinismus").
|
|
22
|
+
*/
|
|
23
|
+
import { tokenizeIdentifiers } from "@untangle-code/domain";
|
|
24
|
+
// Indexing, sparse queries and reranking must use one versioned token space.
|
|
25
|
+
export { identifierParts, splitIdentifierRun, tokenizeIdentifiers } from "@untangle-code/domain";
|
|
26
|
+
/**
|
|
27
|
+
* Funktionswoerter, die in frei formulierten Suchfragen fast nur die Form der
|
|
28
|
+
* Frage beschreiben. Sie als BM25-Begriffe zu werten bevorzugt lange Prosa,
|
|
29
|
+
* die ebenfalls oft "wo wird"/"how is" enthaelt, vor dem gesuchten Code.
|
|
30
|
+
*
|
|
31
|
+
* Die Liste ist absichtlich klein und enthaelt nur geschlossene grammatische
|
|
32
|
+
* Klassen in Deutsch und Englisch. Fachwoerter, Verben und Bezeichner werden
|
|
33
|
+
* nie als Stopwoerter geraten. Wenn eine Anfrage ausschliesslich daraus
|
|
34
|
+
* besteht, faellt {@link informativeQueryTokens} auf die Originaltokens
|
|
35
|
+
* zurueck, damit beispielsweise eine Symbolsuche nach `is` nicht verschwindet.
|
|
36
|
+
*/
|
|
37
|
+
const QUERY_STOP_WORDS = new Set([
|
|
38
|
+
"a",
|
|
39
|
+
"als",
|
|
40
|
+
"an",
|
|
41
|
+
"and",
|
|
42
|
+
"are",
|
|
43
|
+
"as",
|
|
44
|
+
"at",
|
|
45
|
+
"auf",
|
|
46
|
+
"aus",
|
|
47
|
+
"be",
|
|
48
|
+
"bei",
|
|
49
|
+
"by",
|
|
50
|
+
"das",
|
|
51
|
+
"dass",
|
|
52
|
+
"dem",
|
|
53
|
+
"den",
|
|
54
|
+
"der",
|
|
55
|
+
"des",
|
|
56
|
+
"die",
|
|
57
|
+
"does",
|
|
58
|
+
"ein",
|
|
59
|
+
"eine",
|
|
60
|
+
"einem",
|
|
61
|
+
"einen",
|
|
62
|
+
"einer",
|
|
63
|
+
"eines",
|
|
64
|
+
"es",
|
|
65
|
+
"for",
|
|
66
|
+
"from",
|
|
67
|
+
"für",
|
|
68
|
+
"fuer",
|
|
69
|
+
"how",
|
|
70
|
+
"im",
|
|
71
|
+
"in",
|
|
72
|
+
"into",
|
|
73
|
+
"is",
|
|
74
|
+
"ist",
|
|
75
|
+
"it",
|
|
76
|
+
"mit",
|
|
77
|
+
"of",
|
|
78
|
+
"on",
|
|
79
|
+
"or",
|
|
80
|
+
"sich",
|
|
81
|
+
"sind",
|
|
82
|
+
"that",
|
|
83
|
+
"the",
|
|
84
|
+
"this",
|
|
85
|
+
"to",
|
|
86
|
+
"und",
|
|
87
|
+
"von",
|
|
88
|
+
"was",
|
|
89
|
+
"wann",
|
|
90
|
+
"warum",
|
|
91
|
+
"werden",
|
|
92
|
+
"what",
|
|
93
|
+
"when",
|
|
94
|
+
"where",
|
|
95
|
+
"which",
|
|
96
|
+
"wie",
|
|
97
|
+
"wird",
|
|
98
|
+
"with",
|
|
99
|
+
"wo",
|
|
100
|
+
"wofür",
|
|
101
|
+
"wofuer",
|
|
102
|
+
"who",
|
|
103
|
+
"why",
|
|
104
|
+
"welche",
|
|
105
|
+
"welchem",
|
|
106
|
+
"welchen",
|
|
107
|
+
"welcher",
|
|
108
|
+
"welches",
|
|
109
|
+
"wer",
|
|
110
|
+
"zu",
|
|
111
|
+
]);
|
|
112
|
+
const UNAMBIGUOUS_GERMAN_QUESTION_OPENERS = new Set([
|
|
113
|
+
"warum",
|
|
114
|
+
"wann",
|
|
115
|
+
"welche",
|
|
116
|
+
"welchem",
|
|
117
|
+
"welchen",
|
|
118
|
+
"welcher",
|
|
119
|
+
"welches",
|
|
120
|
+
"wer",
|
|
121
|
+
"wie",
|
|
122
|
+
"wo",
|
|
123
|
+
"wofür",
|
|
124
|
+
"wofuer",
|
|
125
|
+
]);
|
|
126
|
+
const GERMAN_WAS_PREDICATES = new Set([
|
|
127
|
+
"bedeutet",
|
|
128
|
+
"geschieht",
|
|
129
|
+
"ist",
|
|
130
|
+
"passiert",
|
|
131
|
+
"sind",
|
|
132
|
+
"werden",
|
|
133
|
+
"wird",
|
|
134
|
+
]);
|
|
135
|
+
const GERMAN_INFLECTED_QUESTION_PRONOUNS = new Set([
|
|
136
|
+
"welche",
|
|
137
|
+
"welchem",
|
|
138
|
+
"welchen",
|
|
139
|
+
"welcher",
|
|
140
|
+
"welches",
|
|
141
|
+
]);
|
|
142
|
+
const GERMAN_GRAMMAR_MARKERS = new Set([
|
|
143
|
+
"das",
|
|
144
|
+
"dass",
|
|
145
|
+
"dem",
|
|
146
|
+
"den",
|
|
147
|
+
"der",
|
|
148
|
+
"des",
|
|
149
|
+
"die",
|
|
150
|
+
"ein",
|
|
151
|
+
"eine",
|
|
152
|
+
"einem",
|
|
153
|
+
"einen",
|
|
154
|
+
"einer",
|
|
155
|
+
"eines",
|
|
156
|
+
"ist",
|
|
157
|
+
"sich",
|
|
158
|
+
"und",
|
|
159
|
+
"von",
|
|
160
|
+
"werden",
|
|
161
|
+
"wird",
|
|
162
|
+
"zu",
|
|
163
|
+
]);
|
|
164
|
+
/**
|
|
165
|
+
* Tokenisiert eine frei formulierte Anfrage fuer den lexikalischen Rang und
|
|
166
|
+
* entfernt nur grammatische Fragehuelsen. Wiederholungen bleiben erhalten;
|
|
167
|
+
* der Aufrufer entscheidet wie beim normalen Token-Bag selbst, ob er daraus
|
|
168
|
+
* eine Menge oder Haeufigkeiten braucht.
|
|
169
|
+
*/
|
|
170
|
+
export function informativeQueryTokens(text) {
|
|
171
|
+
const tokens = tokenizeIdentifiers(text);
|
|
172
|
+
const informative = tokens.filter((token) => !QUERY_STOP_WORDS.has(token));
|
|
173
|
+
return informative.length === 0 ? tokens : informative;
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Erkennt die enge Form, fuer die der Benchmark einen systematischen
|
|
177
|
+
* Cross-Language-Konflikt belegt: eine deutsch formulierte Frage vor
|
|
178
|
+
* ueberwiegend englischen Codebezeichnern. Das ist bewusst KEINE allgemeine
|
|
179
|
+
* Spracherkennung. Eindeutige deutsche Fragewoerter reichen; das auch im
|
|
180
|
+
* Englischen vorkommende `was` braucht zwei weitere Grammatikmarker.
|
|
181
|
+
*/
|
|
182
|
+
export function isLikelyGermanQuestion(text) {
|
|
183
|
+
const tokens = tokenizeIdentifiers(text);
|
|
184
|
+
const first = tokens[0];
|
|
185
|
+
if (first === undefined) {
|
|
186
|
+
return false;
|
|
187
|
+
}
|
|
188
|
+
return (UNAMBIGUOUS_GERMAN_QUESTION_OPENERS.has(first) ||
|
|
189
|
+
(first === "in" && GERMAN_INFLECTED_QUESTION_PRONOUNS.has(tokens[1] ?? "")) ||
|
|
190
|
+
(first === "was" &&
|
|
191
|
+
(GERMAN_WAS_PREDICATES.has(tokens[1] ?? "") ||
|
|
192
|
+
new Set(tokens.filter((token) => GERMAN_GRAMMAR_MARKERS.has(token))).size >= 2)));
|
|
193
|
+
}
|
|
194
|
+
/** Token-Haeufigkeiten als Map; Reihenfolge der Einfuegung ist Textreihenfolge. */
|
|
195
|
+
export function termFrequencies(tokens) {
|
|
196
|
+
const counts = new Map();
|
|
197
|
+
for (const token of tokens) {
|
|
198
|
+
counts.set(token, (counts.get(token) ?? 0) + 1);
|
|
199
|
+
}
|
|
200
|
+
return counts;
|
|
201
|
+
}
|
|
202
|
+
//# sourceMappingURL=tokenize.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tokenize.js","sourceRoot":"","sources":["../src/tokenize.ts"],"names":[],"mappings":"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"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Paket-eigene Vertraege des Abrufkerns.
|
|
3
|
+
*
|
|
4
|
+
* Grundsatz: Alles, was schon in `@untangle-code/domain` steht
|
|
5
|
+
* (`SearchRequestV1`, `SearchQueryV1`, `ScoreBreakdownV1`, `SearchHitV1`,
|
|
6
|
+
* `SearchResultSetV1`, `SkipLedgerEntryV1`, …), wird NICHT neu definiert,
|
|
7
|
+
* sondern verwendet. Hier stehen nur die Erweiterungen, die der Abrufkern
|
|
8
|
+
* zusaetzlich braucht und die die Werkzeuggrenze aus spec.md §13.1 bewusst
|
|
9
|
+
* nicht kennt:
|
|
10
|
+
*
|
|
11
|
+
* - Suchmodus (semantisch / lexikalisch / Symbolname / hybrid),
|
|
12
|
+
* - die grep-artigen Faehigkeiten (Nutzerwunsch A4),
|
|
13
|
+
* - Ausgabeformate und Sortierung,
|
|
14
|
+
* - der Komplexitaets-Bias (vision.md §1b: Komplexitaet ist ein Blickwinkel
|
|
15
|
+
* auf dieselbe Frage, kein zweites Werkzeug).
|
|
16
|
+
*/
|
|
17
|
+
import type { IndexFreshnessV1, SearchAbstention, SearchRequestV1, SkipLedgerEntryV1, ToolWarningV1 } from "@untangle-code/domain";
|
|
18
|
+
/**
|
|
19
|
+
* Welche unabhaengigen Abruf- und Rankingzweige ausgefuehrt werden.
|
|
20
|
+
*
|
|
21
|
+
* - `hybrid` (Default): dicht + lexikalisch + exakter Symbolname, fusioniert;
|
|
22
|
+
* - `semantic`: nur der dichte Zweig;
|
|
23
|
+
* - `lexical`: nur BM25;
|
|
24
|
+
* - `symbol`: nur der exakte Symbolnamen-Abrufzweig.
|
|
25
|
+
*
|
|
26
|
+
* Lexikalische und Symbolsuche erzeugen kein Queryembedding. Ein
|
|
27
|
+
* eingeschraenkter Modus laesst die anderen Abrufzweige weg; Fusion und
|
|
28
|
+
* Score-Zerlegung bleiben dieselben. Genau deshalb
|
|
29
|
+
* bleibt ein `lexical`-Ergebnis mit einem `hybrid`-Ergebnis vergleichbar
|
|
30
|
+
* lesbar — es fehlen nur Bestandteile, es sind keine anderen.
|
|
31
|
+
*/
|
|
32
|
+
export declare const SEARCH_MODES: readonly ["hybrid", "semantic", "lexical", "symbol"];
|
|
33
|
+
export type SearchMode = (typeof SEARCH_MODES)[number];
|
|
34
|
+
export { SEARCH_ABSTENTIONS } from "@untangle-code/domain";
|
|
35
|
+
export type { SearchAbstention };
|
|
36
|
+
/**
|
|
37
|
+
* Ausdrueckliche Bevorzugung einfacher oder komplexer Treffer (vision.md §1b).
|
|
38
|
+
*
|
|
39
|
+
* Default ist `none`. Das ist eine Entscheidung, keine Bequemlichkeit:
|
|
40
|
+
* Komplexitaet DARF die Rangfolge nicht stillschweigend verschieben. Wer sie
|
|
41
|
+
* einbezieht, bekommt dafuer einen sichtbaren `complexity_modifier` in der
|
|
42
|
+
* Score-Zerlegung.
|
|
43
|
+
*/
|
|
44
|
+
export declare const COMPLEXITY_BIASES: readonly ["none", "prefer-simple", "prefer-complex"];
|
|
45
|
+
export type ComplexityBias = (typeof COMPLEXITY_BIASES)[number];
|
|
46
|
+
/** Sortierschluessel der Trefferliste (Nutzerwunsch A4: „Sortierung"). */
|
|
47
|
+
export declare const SEARCH_SORTS: readonly ["relevance", "path", "complexity-desc", "complexity-asc", "findability-asc"];
|
|
48
|
+
export type SearchSort = (typeof SEARCH_SORTS)[number];
|
|
49
|
+
/** Ausgabeformate (Nutzerwunsch A4: „Text, JSON, JSONL"). */
|
|
50
|
+
export declare const OUTPUT_FORMATS: readonly ["text", "json", "jsonl"];
|
|
51
|
+
export type OutputFormat = (typeof OUTPUT_FORMATS)[number];
|
|
52
|
+
/**
|
|
53
|
+
* Suchanfrage des Abrufkerns: die Spezifikationsform aus spec.md §13.1 plus
|
|
54
|
+
* die Erweiterungen dieses Pakets. Additiv — jede gueltige `SearchRequestV1`
|
|
55
|
+
* ist auch eine gueltige `UntangleSearchRequestV1`.
|
|
56
|
+
*/
|
|
57
|
+
export interface UntangleSearchRequestV1 extends SearchRequestV1 {
|
|
58
|
+
mode?: SearchMode;
|
|
59
|
+
/**
|
|
60
|
+
* Verhalten bei unzureichender Evidenz. Fehlt es, gilt `strict` — das ist
|
|
61
|
+
* byte-identisch zum Verhalten vor Welle 14, bis in beide Fingerprints.
|
|
62
|
+
*/
|
|
63
|
+
abstention?: SearchAbstention;
|
|
64
|
+
/** Pfadmuster, die AUSGESCHLOSSEN werden (Gegenstueck zu `pathGlob`). */
|
|
65
|
+
pathExclude?: string[];
|
|
66
|
+
/** Chunk-Inhalt mitliefern; Default false (tokenarm fuer Agents). */
|
|
67
|
+
includeContent?: boolean;
|
|
68
|
+
complexityBias?: ComplexityBias;
|
|
69
|
+
sort?: SearchSort;
|
|
70
|
+
/** Nur Treffer mit Findability-Score <= diesem Wert (vision.md §1d). */
|
|
71
|
+
findabilityMaxScore?: number;
|
|
72
|
+
/** Nur Treffer mit Findability-Score >= diesem Wert. */
|
|
73
|
+
findabilityMinScore?: number;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Normalisierte Zusatzangaben — das Gegenstueck zu `SearchQueryV1` fuer die
|
|
77
|
+
* Felder, die dieses Paket ergaenzt. Alle Felder sind gesetzt (keine
|
|
78
|
+
* `undefined`-Faelle mehr), damit der Ausfuehrungspfad keine Defaults mehr
|
|
79
|
+
* erraten muss.
|
|
80
|
+
*/
|
|
81
|
+
export interface SearchQueryExtensionsV1 {
|
|
82
|
+
mode: SearchMode;
|
|
83
|
+
abstention: SearchAbstention;
|
|
84
|
+
pathExcludes: string[];
|
|
85
|
+
includeContent: boolean;
|
|
86
|
+
complexityBias: ComplexityBias;
|
|
87
|
+
sort: SearchSort;
|
|
88
|
+
}
|
|
89
|
+
/** Sortierung der grep-Treffer. */
|
|
90
|
+
export declare const GREP_SORTS: readonly ["path", "matches-desc"];
|
|
91
|
+
export type GrepSort = (typeof GREP_SORTS)[number];
|
|
92
|
+
/**
|
|
93
|
+
* Regex-/Literalsuche ueber den INDEXIERTEN Inhalt.
|
|
94
|
+
*
|
|
95
|
+
* Wichtig fuer das Verstaendnis der Ergebnisse: gesucht wird im Index, nicht
|
|
96
|
+
* auf der Platte. Ein nicht indizierter oder wegen Groesse uebersprungener
|
|
97
|
+
* Inhalt kann hier nicht gefunden werden — deshalb traegt das Ergebnis den
|
|
98
|
+
* Skip-Ledger mit, statt schlicht „nichts gefunden" zu behaupten (E4).
|
|
99
|
+
*/
|
|
100
|
+
export interface GrepRequestV1 {
|
|
101
|
+
/** Suchmuster; regulaerer Ausdruck, wenn `regex` true ist, sonst Literal. */
|
|
102
|
+
pattern: string;
|
|
103
|
+
regex?: boolean;
|
|
104
|
+
caseSensitive?: boolean;
|
|
105
|
+
/** Nur ganze Woerter (Bezeichnergrenzen). */
|
|
106
|
+
wholeWord?: boolean;
|
|
107
|
+
workspaceId?: string;
|
|
108
|
+
repositories?: string[];
|
|
109
|
+
refs?: string[];
|
|
110
|
+
languages?: string[];
|
|
111
|
+
pathGlob?: string[];
|
|
112
|
+
pathExclude?: string[];
|
|
113
|
+
/** Kontextzeilen vor bzw. nach der Trefferzeile. */
|
|
114
|
+
contextLinesBefore?: number;
|
|
115
|
+
contextLinesAfter?: number;
|
|
116
|
+
/** Obergrenze der zurueckgegebenen Treffer insgesamt. */
|
|
117
|
+
maxMatches?: number;
|
|
118
|
+
/** Obergrenze je Datei; verhindert, dass eine Datei die Liste fuellt. */
|
|
119
|
+
maxMatchesPerFile?: number;
|
|
120
|
+
/**
|
|
121
|
+
* Obergrenze der durchsuchten Chunks. Schuetzt vor pathologischen
|
|
122
|
+
* Ausdruecken und riesigen Indizes; Ueberschreitung ist ein sichtbarer
|
|
123
|
+
* Abbruchgrund, kein stilles Kuerzen.
|
|
124
|
+
*/
|
|
125
|
+
maxScannedChunks?: number;
|
|
126
|
+
sort?: GrepSort;
|
|
127
|
+
}
|
|
128
|
+
/** Eine Kontextzeile eines grep-Treffers. */
|
|
129
|
+
export interface GrepContextLineV1 {
|
|
130
|
+
/** 1-basierte Zeilennummer in der Datei. */
|
|
131
|
+
line: number;
|
|
132
|
+
text: string;
|
|
133
|
+
}
|
|
134
|
+
/** Ein einzelner grep-Treffer mit Herkunft und Kontext. */
|
|
135
|
+
export interface GrepMatchV1 {
|
|
136
|
+
repoId: string;
|
|
137
|
+
repoLabel: string;
|
|
138
|
+
refId: string;
|
|
139
|
+
refName: string;
|
|
140
|
+
commitSha?: string;
|
|
141
|
+
fileId: string;
|
|
142
|
+
/** „/"-normalisierter, repo-relativer Pfad. */
|
|
143
|
+
filePath: string;
|
|
144
|
+
language: string;
|
|
145
|
+
chunkId: string;
|
|
146
|
+
symbolName?: string;
|
|
147
|
+
/** 1-basierte Zeilennummer in der Datei. */
|
|
148
|
+
line: number;
|
|
149
|
+
/** 1-basierte Spalte des Treffers innerhalb der Zeile (UTF-16-Einheiten). */
|
|
150
|
+
column: number;
|
|
151
|
+
/** Der getroffene Text selbst. */
|
|
152
|
+
matchText: string;
|
|
153
|
+
/** Die vollstaendige Trefferzeile. */
|
|
154
|
+
lineText: string;
|
|
155
|
+
before: GrepContextLineV1[];
|
|
156
|
+
after: GrepContextLineV1[];
|
|
157
|
+
}
|
|
158
|
+
/** Ergebnis einer grep-Ausfuehrung. */
|
|
159
|
+
export interface GrepResultSetV1 {
|
|
160
|
+
schemaVersion: 1;
|
|
161
|
+
indexFreshness?: IndexFreshnessV1;
|
|
162
|
+
pattern: string;
|
|
163
|
+
isRegex: boolean;
|
|
164
|
+
matches: GrepMatchV1[];
|
|
165
|
+
totalMatches: number;
|
|
166
|
+
/** True, wenn eine Grenze griff; dann ist `truncationReason` Pflicht (E4). */
|
|
167
|
+
truncated: boolean;
|
|
168
|
+
truncationReason?: "max_matches" | "max_matches_per_file" | "max_scanned_chunks";
|
|
169
|
+
scannedChunks: number;
|
|
170
|
+
skipped: SkipLedgerEntryV1[];
|
|
171
|
+
warnings: ToolWarningV1[];
|
|
172
|
+
}
|
|
173
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Welche unabhaengigen Abruf- und Rankingzweige ausgefuehrt werden.
|
|
3
|
+
*
|
|
4
|
+
* - `hybrid` (Default): dicht + lexikalisch + exakter Symbolname, fusioniert;
|
|
5
|
+
* - `semantic`: nur der dichte Zweig;
|
|
6
|
+
* - `lexical`: nur BM25;
|
|
7
|
+
* - `symbol`: nur der exakte Symbolnamen-Abrufzweig.
|
|
8
|
+
*
|
|
9
|
+
* Lexikalische und Symbolsuche erzeugen kein Queryembedding. Ein
|
|
10
|
+
* eingeschraenkter Modus laesst die anderen Abrufzweige weg; Fusion und
|
|
11
|
+
* Score-Zerlegung bleiben dieselben. Genau deshalb
|
|
12
|
+
* bleibt ein `lexical`-Ergebnis mit einem `hybrid`-Ergebnis vergleichbar
|
|
13
|
+
* lesbar — es fehlen nur Bestandteile, es sind keine anderen.
|
|
14
|
+
*/
|
|
15
|
+
export const SEARCH_MODES = ["hybrid", "semantic", "lexical", "symbol"];
|
|
16
|
+
// Aus dem Domain re-exportiert, damit Aufrufer nur EIN Paket brauchen.
|
|
17
|
+
export { SEARCH_ABSTENTIONS } from "@untangle-code/domain";
|
|
18
|
+
/**
|
|
19
|
+
* Ausdrueckliche Bevorzugung einfacher oder komplexer Treffer (vision.md §1b).
|
|
20
|
+
*
|
|
21
|
+
* Default ist `none`. Das ist eine Entscheidung, keine Bequemlichkeit:
|
|
22
|
+
* Komplexitaet DARF die Rangfolge nicht stillschweigend verschieben. Wer sie
|
|
23
|
+
* einbezieht, bekommt dafuer einen sichtbaren `complexity_modifier` in der
|
|
24
|
+
* Score-Zerlegung.
|
|
25
|
+
*/
|
|
26
|
+
export const COMPLEXITY_BIASES = ["none", "prefer-simple", "prefer-complex"];
|
|
27
|
+
/** Sortierschluessel der Trefferliste (Nutzerwunsch A4: „Sortierung"). */
|
|
28
|
+
export const SEARCH_SORTS = [
|
|
29
|
+
"relevance",
|
|
30
|
+
"path",
|
|
31
|
+
"complexity-desc",
|
|
32
|
+
"complexity-asc",
|
|
33
|
+
"findability-asc",
|
|
34
|
+
];
|
|
35
|
+
/** Ausgabeformate (Nutzerwunsch A4: „Text, JSON, JSONL"). */
|
|
36
|
+
export const OUTPUT_FORMATS = ["text", "json", "jsonl"];
|
|
37
|
+
// ---------------------------------------------------------------------------
|
|
38
|
+
// grep-Modus (Nutzerwunsch A4)
|
|
39
|
+
// ---------------------------------------------------------------------------
|
|
40
|
+
/** Sortierung der grep-Treffer. */
|
|
41
|
+
export const GREP_SORTS = ["path", "matches-desc"];
|
|
42
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAwBA;;;;;;;;;;;;;GAaG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC,QAAQ,EAAE,UAAU,EAAE,SAAS,EAAE,QAAQ,CAAU,CAAC;AAIjF,uEAAuE;AACvE,OAAO,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAG3D;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,MAAM,EAAE,eAAe,EAAE,gBAAgB,CAAU,CAAC;AAItF,0EAA0E;AAC1E,MAAM,CAAC,MAAM,YAAY,GAAG;IAC1B,WAAW;IACX,MAAM;IACN,iBAAiB;IACjB,gBAAgB;IAChB,iBAAiB;CACT,CAAC;AAIX,6DAA6D;AAC7D,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,CAAU,CAAC;AA2CjE,8EAA8E;AAC9E,+BAA+B;AAC/B,8EAA8E;AAE9E,mCAAmC;AACnC,MAAM,CAAC,MAAM,UAAU,GAAG,CAAC,MAAM,EAAE,cAAc,CAAU,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,64 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@untangle-code/search",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
3
|
+
"version": "0.1.1",
|
|
4
|
+
"private": false,
|
|
5
|
+
"type": "module",
|
|
6
|
+
"description": "Untangle retrieval core: hybrid search (dense + deterministic BM25 + exact symbol names) with reciprocal rank fusion, explainable score breakdowns, complexity/findability filters, grep mode and deterministic output formats (spec.md §13, vision.md §1b/§1d).",
|
|
7
|
+
"license": "AGPL-3.0-only",
|
|
8
|
+
"homepage": "https://github.com/lboel/untangle",
|
|
9
|
+
"bugs": {
|
|
10
|
+
"url": "https://github.com/lboel/untangle/issues"
|
|
11
|
+
},
|
|
12
|
+
"repository": {
|
|
13
|
+
"type": "git",
|
|
14
|
+
"url": "git+https://github.com/lboel/untangle.git",
|
|
15
|
+
"directory": "packages/search"
|
|
16
|
+
},
|
|
17
|
+
"publishConfig": {
|
|
18
|
+
"access": "public"
|
|
19
|
+
},
|
|
20
|
+
"engines": {
|
|
21
|
+
"node": "^22.13.0 || >=24.0.0"
|
|
22
|
+
},
|
|
23
|
+
"exports": {
|
|
24
|
+
".": {
|
|
25
|
+
"types": "./dist/index.d.ts",
|
|
26
|
+
"default": "./dist/index.js"
|
|
27
|
+
}
|
|
28
|
+
},
|
|
29
|
+
"files": [
|
|
30
|
+
"dist",
|
|
31
|
+
"!dist/**/*.test.*",
|
|
32
|
+
"!dist/**/test-helpers.*",
|
|
33
|
+
"!dist/**/test-support.*",
|
|
34
|
+
"!dist/**/test-support/**",
|
|
35
|
+
"!dist/**/testing/**",
|
|
36
|
+
"!dist/**/__tests__/**",
|
|
37
|
+
"src",
|
|
38
|
+
"!src/**/*.test.ts",
|
|
39
|
+
"!src/**/test-helpers.ts",
|
|
40
|
+
"!src/**/test-support.ts",
|
|
41
|
+
"!src/**/test-support/**",
|
|
42
|
+
"!src/**/testing/**",
|
|
43
|
+
"LICENSE",
|
|
44
|
+
"NOTICE",
|
|
45
|
+
"README.md",
|
|
46
|
+
"THIRD_PARTY_LICENSES.md"
|
|
47
|
+
],
|
|
48
|
+
"dependencies": {
|
|
49
|
+
"@untangle-code/config": "0.1.1",
|
|
50
|
+
"@untangle-code/domain": "0.1.1"
|
|
51
|
+
},
|
|
52
|
+
"devDependencies": {
|
|
53
|
+
"@types/node": "22.19.7",
|
|
54
|
+
"typescript": "5.9.3",
|
|
55
|
+
"vitest": "3.2.7",
|
|
56
|
+
"@untangle-code/testkit": "0.1.1"
|
|
57
|
+
},
|
|
58
|
+
"scripts": {
|
|
59
|
+
"build": "node ../../scripts/tsc-build.mjs tsconfig.json",
|
|
60
|
+
"typecheck": "tsc -p tsconfig.typecheck.json",
|
|
61
|
+
"test": "vitest run",
|
|
62
|
+
"clean": "rm -rf dist *.tsbuildinfo"
|
|
63
|
+
}
|
|
6
64
|
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Harte Ehrlichkeitsgrenze fuer den Live-Benchmark.
|
|
3
|
+
*
|
|
4
|
+
* Der Search-Port degradiert ausgefallene Ziele absichtlich zu Warnungen,
|
|
5
|
+
* damit eine interaktive Suche andere Profile weiter anzeigen kann. Ein
|
|
6
|
+
* Benchmark darf diese Semantik nicht als Messwert missverstehen: ohne
|
|
7
|
+
* ausgefuehrten Abruf sind null Treffer weder Akzeptanz noch Abstention.
|
|
8
|
+
*/
|
|
9
|
+
import type { SearchResultSetV1 } from "@untangle-code/domain";
|
|
10
|
+
|
|
11
|
+
import { detectRetrievalFailure } from "./findability.js";
|
|
12
|
+
|
|
13
|
+
/** Wirft, bevor ein Benchmark eine nicht ausgefuehrte Suche serialisieren kann. */
|
|
14
|
+
export function assertBenchmarkRetrievalCompleted(
|
|
15
|
+
questionId: string,
|
|
16
|
+
result: SearchResultSetV1,
|
|
17
|
+
): void {
|
|
18
|
+
const failure = detectRetrievalFailure(result);
|
|
19
|
+
if (failure === undefined) {
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
throw new Error(
|
|
23
|
+
`benchmark row "${questionId}" is invalid because retrieval did not complete (${failure.reason}): ${failure.detail}`,
|
|
24
|
+
);
|
|
25
|
+
}
|