@0xsarwagya/ontoly-core 0.1.0-alpha.13 → 0.1.0-alpha.14
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/README.md +1 -1
- package/dist/index.js +444 -47
- package/dist/index.js.map +1 -1
- package/dist/semantic-index.d.ts +1 -1
- package/dist/semantic-index.d.ts.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -12,7 +12,7 @@ pnpm add @0xsarwagya/ontoly-core
|
|
|
12
12
|
|
|
13
13
|
## Status
|
|
14
14
|
|
|
15
|
-
Alpha package for Ontoly v0.1.0-alpha.
|
|
15
|
+
Alpha package for Ontoly v0.1.0-alpha.14. The public API is versioned with the Software Graph and RFC process.
|
|
16
16
|
|
|
17
17
|
## Links
|
|
18
18
|
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// src/semantic-index.ts
|
|
2
|
-
var SEMANTIC_INDEX_VERSION = "1.0.
|
|
2
|
+
var SEMANTIC_INDEX_VERSION = "1.0.1";
|
|
3
3
|
var STOP_WORDS = /* @__PURE__ */ new Set([
|
|
4
4
|
"a",
|
|
5
5
|
"an",
|
|
@@ -63,7 +63,7 @@ var FRAMEWORK_SUFFIXES = /* @__PURE__ */ new Set([
|
|
|
63
63
|
var CATEGORY_TYPES = {
|
|
64
64
|
concept: [],
|
|
65
65
|
symbol: ["Function", "Method", "Class", "Interface", "TypeAlias", "Enum", "Field", "Service", "Provider", "Controller", "Repository", "Resource", "Model", "Route"],
|
|
66
|
-
feature: ["Route", "Controller", "Service", "Provider", "Module", "Package", "Repository", "Model", "Resource", "Class", "Interface", "TypeAlias", "Function", "Method", "Operation"],
|
|
66
|
+
feature: ["Route", "Controller", "Service", "Provider", "Module", "Package", "Repository", "Model", "Resource", "Class", "Interface", "TypeAlias", "Function", "Method", "Operation", "Configuration", "EnvironmentVariable", "Guard", "Permission", "Middleware"],
|
|
67
67
|
configuration: ["Configuration", "EnvironmentVariable", "BuildTarget", "Script", "Task"],
|
|
68
68
|
environment: ["EnvironmentVariable"],
|
|
69
69
|
route: ["Route", "Operation", "Controller"],
|
|
@@ -133,8 +133,67 @@ var SEMANTIC_EXPANSIONS = {
|
|
|
133
133
|
var SEMANTIC_METADATA_DEPTH_LIMIT = 3;
|
|
134
134
|
var SEMANTIC_METADATA_ARRAY_LIMIT = 12;
|
|
135
135
|
var SEMANTIC_METADATA_ENTRY_LIMIT = 24;
|
|
136
|
-
var SEMANTIC_METADATA_VALUES_LIMIT =
|
|
136
|
+
var SEMANTIC_METADATA_VALUES_LIMIT = 48;
|
|
137
137
|
var SEMANTIC_TEXT_LIMIT = 240;
|
|
138
|
+
var SEMANTIC_ALIAS_SOURCE_LIMIT = 16;
|
|
139
|
+
var SEMANTIC_ALIAS_LIMIT = 20;
|
|
140
|
+
var SEMANTIC_KEYWORD_LIMIT = 24;
|
|
141
|
+
var SEMANTIC_INVERTED_IDS_LIMIT = 160;
|
|
142
|
+
var SEMANTIC_VOCABULARY_IDS_LIMIT = 50;
|
|
143
|
+
var SEMANTIC_NOISE_TERMS = /* @__PURE__ */ new Set([
|
|
144
|
+
"0",
|
|
145
|
+
"0s",
|
|
146
|
+
"1",
|
|
147
|
+
"1s",
|
|
148
|
+
"2",
|
|
149
|
+
"2s",
|
|
150
|
+
"3",
|
|
151
|
+
"3s",
|
|
152
|
+
"4",
|
|
153
|
+
"4s",
|
|
154
|
+
"abstract",
|
|
155
|
+
"abstracts",
|
|
156
|
+
"active",
|
|
157
|
+
"actives",
|
|
158
|
+
"anies",
|
|
159
|
+
"any",
|
|
160
|
+
"async",
|
|
161
|
+
"asyncs",
|
|
162
|
+
"boolean",
|
|
163
|
+
"booleans",
|
|
164
|
+
"false",
|
|
165
|
+
"falses",
|
|
166
|
+
"number",
|
|
167
|
+
"numbers",
|
|
168
|
+
"parameter",
|
|
169
|
+
"parameters",
|
|
170
|
+
"static",
|
|
171
|
+
"statics",
|
|
172
|
+
"string",
|
|
173
|
+
"strings",
|
|
174
|
+
"true",
|
|
175
|
+
"trues",
|
|
176
|
+
"typeparameter",
|
|
177
|
+
"typeparameters"
|
|
178
|
+
]);
|
|
179
|
+
var REPOSITORY_LOCAL_KIND_BOOSTS = {
|
|
180
|
+
Service: 112,
|
|
181
|
+
Controller: 92,
|
|
182
|
+
Repository: 72,
|
|
183
|
+
Route: 68,
|
|
184
|
+
Operation: 64,
|
|
185
|
+
Method: 62,
|
|
186
|
+
Module: 58,
|
|
187
|
+
Configuration: 58,
|
|
188
|
+
Model: 56,
|
|
189
|
+
Interface: 54,
|
|
190
|
+
TypeAlias: 54,
|
|
191
|
+
Class: 50,
|
|
192
|
+
Function: 48,
|
|
193
|
+
EnvironmentVariable: 44,
|
|
194
|
+
Provider: 42
|
|
195
|
+
};
|
|
196
|
+
var FRAMEWORK_PACKAGE_PATTERN = /@nestjs\/|@medplum\/|next\/dist|react\/|typescript\/lib|@types\/|@babel\/|@typescript-eslint\/|tslib|rxjs|zone\.js/i;
|
|
138
197
|
function createSemanticIndex(graph) {
|
|
139
198
|
const nodeById = new Map(graph.nodes.map((node) => [node.id, node]));
|
|
140
199
|
const incoming = groupEdges(graph.edges, "to");
|
|
@@ -352,12 +411,12 @@ function aliasSourceValues(graph, node, neighbors, parentChain) {
|
|
|
352
411
|
node.file ?? "",
|
|
353
412
|
node.package ?? "",
|
|
354
413
|
...parentChain,
|
|
355
|
-
...metadataValues,
|
|
356
414
|
...routeValues,
|
|
357
415
|
...envValues,
|
|
358
|
-
...neighbors.map((neighbor) => neighbor.name),
|
|
359
|
-
...neighbors.map((neighbor) => neighbor.id)
|
|
360
|
-
|
|
416
|
+
...neighbors.slice(0, 16).map((neighbor) => neighbor.name),
|
|
417
|
+
...neighbors.slice(0, 16).map((neighbor) => neighbor.id),
|
|
418
|
+
...metadataValues
|
|
419
|
+
].filter(Boolean)).slice(0, SEMANTIC_ALIAS_SOURCE_LIMIT);
|
|
361
420
|
}
|
|
362
421
|
function generateAliases(values, node) {
|
|
363
422
|
const aliases = /* @__PURE__ */ new Set();
|
|
@@ -379,6 +438,9 @@ function generateAliases(values, node) {
|
|
|
379
438
|
aliases.add(withoutSuffix.join(""));
|
|
380
439
|
}
|
|
381
440
|
for (const token of tokens) {
|
|
441
|
+
if (isSemanticNoiseTerm(token)) {
|
|
442
|
+
continue;
|
|
443
|
+
}
|
|
382
444
|
aliases.add(token);
|
|
383
445
|
aliases.add(singularize(token));
|
|
384
446
|
aliases.add(pluralize(token));
|
|
@@ -408,12 +470,15 @@ function generateAliases(values, node) {
|
|
|
408
470
|
aliases.add("feature");
|
|
409
471
|
aliases.add("feature module");
|
|
410
472
|
}
|
|
411
|
-
return uniqueStrings([...aliases].filter((alias) => alias.length > 1));
|
|
473
|
+
return uniqueStrings([...aliases].filter((alias) => alias.length > 1).filter((alias) => !isNoisySemanticAlias(alias))).slice(0, SEMANTIC_ALIAS_LIMIT);
|
|
412
474
|
}
|
|
413
475
|
function generateKeywords(values, node) {
|
|
414
476
|
const keywords = /* @__PURE__ */ new Set();
|
|
415
477
|
for (const value of values) {
|
|
416
478
|
for (const token of tokenize(value)) {
|
|
479
|
+
if (isSemanticNoiseTerm(token)) {
|
|
480
|
+
continue;
|
|
481
|
+
}
|
|
417
482
|
keywords.add(token);
|
|
418
483
|
keywords.add(singularize(token));
|
|
419
484
|
}
|
|
@@ -434,7 +499,7 @@ function generateKeywords(values, node) {
|
|
|
434
499
|
if (isFeatureModuleNode(node)) {
|
|
435
500
|
keywords.add("feature");
|
|
436
501
|
}
|
|
437
|
-
return uniqueStrings([...keywords].filter((keyword) => keyword.length > 1));
|
|
502
|
+
return uniqueStrings([...keywords].filter((keyword) => keyword.length > 1).filter((keyword) => !isSemanticNoiseTerm(keyword))).slice(0, SEMANTIC_KEYWORD_LIMIT);
|
|
438
503
|
}
|
|
439
504
|
function buildInvertedIndex(entries) {
|
|
440
505
|
const inverted = /* @__PURE__ */ new Map();
|
|
@@ -457,9 +522,14 @@ function buildVocabulary(entries) {
|
|
|
457
522
|
if (term.length < 2 || STOP_WORDS.has(term)) {
|
|
458
523
|
continue;
|
|
459
524
|
}
|
|
525
|
+
if (isSemanticNoiseTerm(term)) {
|
|
526
|
+
continue;
|
|
527
|
+
}
|
|
460
528
|
const current = terms.get(term) ?? { count: 0, nodeIds: /* @__PURE__ */ new Set(), kinds: /* @__PURE__ */ new Set() };
|
|
461
529
|
current.count += 1;
|
|
462
|
-
current.nodeIds.
|
|
530
|
+
if (current.nodeIds.size < SEMANTIC_VOCABULARY_IDS_LIMIT) {
|
|
531
|
+
current.nodeIds.add(entry.stableId);
|
|
532
|
+
}
|
|
463
533
|
current.kinds.add(entry.kind);
|
|
464
534
|
terms.set(term, current);
|
|
465
535
|
}
|
|
@@ -476,7 +546,7 @@ function rankCandidates(index, intent, options) {
|
|
|
476
546
|
const entryById = new Map(index.entries.map((entry) => [entry.stableId, entry]));
|
|
477
547
|
const kinds = options.kinds?.length ? new Set(options.kinds) : categoryTypeSet(options.category);
|
|
478
548
|
const candidates = candidateIds.map((id) => entryById.get(id)).filter(isEntry).filter((entry) => includeEntryForCategory(entry, intent, options.category)).filter((entry) => !kinds || kinds.has(entry.kind)).filter((entry) => !options.packageScope || entry.package === options.packageScope || entry.filePath?.startsWith(options.packageScope)).map((entry) => scoreEntry(entry, intent, options.category)).filter((candidate) => candidate.score > 0).sort(compareCandidates);
|
|
479
|
-
return candidates.slice(0, options.limit ?? 20);
|
|
549
|
+
return calibrateCandidateConfidences(candidates, intent, options.category).slice(0, options.limit ?? 20);
|
|
480
550
|
}
|
|
481
551
|
function candidateIdsFor(index, intent) {
|
|
482
552
|
const ids = /* @__PURE__ */ new Set();
|
|
@@ -486,14 +556,59 @@ function candidateIdsFor(index, intent) {
|
|
|
486
556
|
ids.add(id);
|
|
487
557
|
}
|
|
488
558
|
}
|
|
559
|
+
for (const id of repositoryVocabularyCandidateIds(index, terms)) {
|
|
560
|
+
ids.add(id);
|
|
561
|
+
}
|
|
489
562
|
const fuzzyIds = index.entries.filter((entry) => fuzzyEntryMatch(entry, intent)).map((entry) => entry.stableId).sort();
|
|
490
563
|
if (ids.size > 0) {
|
|
491
564
|
for (const id of fuzzyIds.slice(0, 250)) {
|
|
492
565
|
ids.add(id);
|
|
493
566
|
}
|
|
494
|
-
return [...ids].sort();
|
|
567
|
+
return [...expandCandidateIds(index, intent, [...ids])].sort();
|
|
495
568
|
}
|
|
496
|
-
return fuzzyIds;
|
|
569
|
+
return [...expandCandidateIds(index, intent, fuzzyIds)].sort();
|
|
570
|
+
}
|
|
571
|
+
function repositoryVocabularyCandidateIds(index, terms) {
|
|
572
|
+
const termSet = new Set(terms);
|
|
573
|
+
const ids = /* @__PURE__ */ new Set();
|
|
574
|
+
for (const vocabularyTerm of index.vocabulary) {
|
|
575
|
+
if (termSet.has(vocabularyTerm.term)) {
|
|
576
|
+
for (const id of vocabularyTerm.nodeIds) {
|
|
577
|
+
ids.add(id);
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
for (const vocabularyTerm of index.vocabulary) {
|
|
582
|
+
if (ids.size >= 300) {
|
|
583
|
+
break;
|
|
584
|
+
}
|
|
585
|
+
if (!termSet.has(vocabularyTerm.term) && vocabularyTerm.term.length > 3 && terms.some((term) => term.length > 3 && (term.includes(vocabularyTerm.term) || vocabularyTerm.term.includes(term)))) {
|
|
586
|
+
for (const id of vocabularyTerm.nodeIds.slice(0, 20)) {
|
|
587
|
+
ids.add(id);
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
}
|
|
591
|
+
return [...ids].sort();
|
|
592
|
+
}
|
|
593
|
+
function expandCandidateIds(index, intent, seedIds) {
|
|
594
|
+
const ids = new Set(seedIds);
|
|
595
|
+
const entryById = new Map(index.entries.map((entry) => [entry.stableId, entry]));
|
|
596
|
+
for (const id of seedIds.slice(0, 300)) {
|
|
597
|
+
const entry = entryById.get(id);
|
|
598
|
+
if (!entry || semanticFeatureAgreement(entry, intent) < 0.35) {
|
|
599
|
+
continue;
|
|
600
|
+
}
|
|
601
|
+
for (const neighborId of entry.relationships.neighborIds) {
|
|
602
|
+
const neighbor = entryById.get(neighborId);
|
|
603
|
+
if (!neighbor) {
|
|
604
|
+
continue;
|
|
605
|
+
}
|
|
606
|
+
if (semanticFeatureAgreement(neighbor, intent) >= 0.25 || architectureAgreementConfidence(neighbor, "feature") >= 0.75) {
|
|
607
|
+
ids.add(neighborId);
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
return [...ids];
|
|
497
612
|
}
|
|
498
613
|
function scoreEntry(entry, intent, category) {
|
|
499
614
|
const reasons = [];
|
|
@@ -540,94 +655,347 @@ function scoreEntry(entry, intent, category) {
|
|
|
540
655
|
addScore(reasons, "intent-expansion", expandedMatches.length * 32, expandedMatches.join(", "));
|
|
541
656
|
const relationshipMatches = intent.expandedTerms.filter((term) => entry.relationships.neighborNames.some((name) => tokenize(name).includes(term)));
|
|
542
657
|
addScore(reasons, "relationship-context", Math.min(120, relationshipMatches.length * 18), relationshipMatches.join(", "));
|
|
658
|
+
const semanticSimilarity = semanticSimilarityConfidence(entry, intent);
|
|
659
|
+
addScore(reasons, "semantic-similarity", semanticSimilarity * 140, `${Math.round(semanticSimilarity * 100)}% lexical/semantic coverage`);
|
|
660
|
+
const featureAgreement = semanticFeatureAgreement(entry, intent);
|
|
661
|
+
addScore(reasons, "feature-agreement", featureAgreement * 120, `${Math.round(featureAgreement * 100)}% repository feature agreement`);
|
|
662
|
+
const queryCoverage = originalQueryCoverage(entry, intent);
|
|
663
|
+
addScore(reasons, "query-token-coverage", queryCoverage.ratio * 110, `${Math.round(queryCoverage.ratio * 100)}% original query token coverage`);
|
|
664
|
+
const architectureAgreement = architectureAgreementConfidence(entry, category);
|
|
665
|
+
addScore(reasons, "architecture-agreement", architectureAgreement * 90, `${entry.kind} agreement for ${category}`);
|
|
543
666
|
const categoryBoost = categoryBoostFor(entry, category);
|
|
544
667
|
addScore(reasons, "architecture-layer", categoryBoost, `${entry.kind} in ${entry.architectureLayer}`);
|
|
668
|
+
addScore(reasons, "feature-owner", featureOwnerBoostFor(entry, category), `${entry.displayName} owns repository feature behavior`);
|
|
545
669
|
addScore(reasons, "graph-importance", Math.min(90, entry.importance * 18), `degree ${entry.relationships.degree}`);
|
|
546
670
|
addScore(reasons, "usage-frequency", Math.min(60, entry.usageFrequency * 8), `${entry.usageFrequency} incoming edges`);
|
|
547
671
|
addScore(reasons, "repository-locality", repositoryLocalityBoost(entry), repositoryLocalityEvidence(entry));
|
|
548
|
-
const penalty = repositoryRankingPenalty(entry);
|
|
672
|
+
const penalty = repositoryRankingPenalty(entry) + queryCoverage.missing.length * 120 + semanticFeaturePenalty(entry, intent);
|
|
549
673
|
const score = Math.max(0, reasons.reduce((total, reason) => total + reason.score, 0) - penalty);
|
|
550
|
-
const returnedReasons = penalty > 0 ? [...reasons, { factor: "repository-noise-demotion", score: -penalty, evidence: repositoryNoiseEvidence(entry) }] : reasons;
|
|
674
|
+
const returnedReasons = penalty > 0 ? [...reasons, { factor: "repository-noise-demotion", score: -penalty, evidence: repositoryNoiseEvidence(entry, queryCoverage.missing) }] : reasons;
|
|
551
675
|
return {
|
|
552
676
|
nodeId: entry.stableId,
|
|
553
677
|
stableId: entry.stableId,
|
|
554
678
|
displayName: entry.displayName,
|
|
555
679
|
kind: entry.kind,
|
|
556
680
|
score: round(score, 3),
|
|
557
|
-
confidence: round(
|
|
681
|
+
confidence: round(confidenceFromCandidateSignals(entry, intent, category, score), 3),
|
|
558
682
|
matchedTerms: uniqueStrings([...matchedTerms]),
|
|
559
683
|
reasons: returnedReasons.filter((reason) => reason.score !== 0).sort(compareReasons),
|
|
560
684
|
entry
|
|
561
685
|
};
|
|
562
686
|
}
|
|
563
|
-
function
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
score += 32;
|
|
687
|
+
function calibrateCandidateConfidences(candidates, intent, category) {
|
|
688
|
+
if (candidates.length === 0) {
|
|
689
|
+
return candidates;
|
|
567
690
|
}
|
|
568
|
-
|
|
569
|
-
|
|
691
|
+
const topScore = candidates[0]?.score ?? 0;
|
|
692
|
+
const secondScore = candidates[1]?.score ?? 0;
|
|
693
|
+
const closeAlternatives = candidates.filter(
|
|
694
|
+
(candidate, index) => index > 0 && isPlausibleAlternative(candidate, topScore, secondScore)
|
|
695
|
+
);
|
|
696
|
+
return candidates.map((candidate, index) => {
|
|
697
|
+
const baseConfidence = confidenceFromCandidateSignals(candidate.entry, intent, category, candidate.score);
|
|
698
|
+
const ratio = topScore > 0 ? Math.min(1, candidate.score / topScore) : 0;
|
|
699
|
+
const confidence = round(calibratedConfidenceCap(candidate, baseConfidence * (index === 0 ? 1 : ratio), {
|
|
700
|
+
index,
|
|
701
|
+
topScore,
|
|
702
|
+
secondScore,
|
|
703
|
+
closeAlternatives,
|
|
704
|
+
intent,
|
|
705
|
+
category
|
|
706
|
+
}), 3);
|
|
707
|
+
const reasons = [
|
|
708
|
+
...candidate.reasons,
|
|
709
|
+
{
|
|
710
|
+
factor: "confidence-calibration",
|
|
711
|
+
score: round(confidence * 100),
|
|
712
|
+
evidence: confidenceCalibrationEvidence(candidate.entry, confidence, closeAlternatives.length)
|
|
713
|
+
}
|
|
714
|
+
];
|
|
715
|
+
if (index === 0 && closeAlternatives.length > 0) {
|
|
716
|
+
reasons.push({
|
|
717
|
+
factor: "ambiguity-calibration",
|
|
718
|
+
score: -Math.min(30, closeAlternatives.length * 8),
|
|
719
|
+
evidence: `${closeAlternatives.length} plausible alternative seed(s) were retained.`
|
|
720
|
+
});
|
|
721
|
+
}
|
|
722
|
+
return {
|
|
723
|
+
...candidate,
|
|
724
|
+
confidence,
|
|
725
|
+
reasons: reasons.filter((reason) => reason.score !== 0).sort(compareReasons)
|
|
726
|
+
};
|
|
727
|
+
});
|
|
728
|
+
}
|
|
729
|
+
function calibratedConfidenceCap(candidate, confidence, context) {
|
|
730
|
+
let capped = Math.max(0, Math.min(1, confidence));
|
|
731
|
+
const isExact = candidate.reasons.some((reason) => reason.factor === "exact-symbol" || reason.factor === "exact-normalized-name");
|
|
732
|
+
const scoreGap = context.topScore - context.secondScore;
|
|
733
|
+
if (isExternalOrFrameworkEntry(candidate.entry)) {
|
|
734
|
+
capped = Math.min(capped, hasRepositoryLocalAlternative(context.closeAlternatives) ? 0.52 : 0.58);
|
|
570
735
|
}
|
|
571
|
-
if (
|
|
572
|
-
|
|
736
|
+
if (isGenericUtilityEntry(candidate.entry) && !isExact) {
|
|
737
|
+
capped = Math.min(capped, 0.72);
|
|
573
738
|
}
|
|
574
|
-
if (
|
|
575
|
-
|
|
739
|
+
if (context.closeAlternatives.length > 0 && !isExact) {
|
|
740
|
+
capped = Math.min(capped, Math.max(0.72, 0.9 - context.closeAlternatives.length * 0.06));
|
|
576
741
|
}
|
|
577
|
-
if (
|
|
578
|
-
|
|
742
|
+
if (context.index === 0 && scoreGap > 0 && scoreGap < 45 && !isExact) {
|
|
743
|
+
capped = Math.min(capped, 0.78);
|
|
744
|
+
} else if (context.index === 0 && scoreGap > 0 && scoreGap < 90 && !isExact) {
|
|
745
|
+
capped = Math.min(capped, 0.86);
|
|
579
746
|
}
|
|
580
|
-
if (
|
|
581
|
-
|
|
747
|
+
if (context.intent.tokens.length >= 2 && originalTokenCoverage(candidate.entry, context.intent) < 0.5 && !isExact) {
|
|
748
|
+
capped = Math.min(capped, 0.62);
|
|
749
|
+
}
|
|
750
|
+
if (context.index > 0) {
|
|
751
|
+
capped = Math.min(capped, 0.84);
|
|
752
|
+
}
|
|
753
|
+
if (context.category === "feature" && architectureAgreementConfidence(candidate.entry, context.category) < 0.4 && !isExact) {
|
|
754
|
+
capped = Math.min(capped, 0.6);
|
|
755
|
+
}
|
|
756
|
+
return capped;
|
|
757
|
+
}
|
|
758
|
+
function isPlausibleAlternative(candidate, topScore, secondScore) {
|
|
759
|
+
return candidate.score >= Math.max(secondScore, topScore - 90) || candidate.score >= topScore * 0.84;
|
|
760
|
+
}
|
|
761
|
+
function hasRepositoryLocalAlternative(candidates) {
|
|
762
|
+
return candidates.some((candidate) => isRepositoryLocalEntry(candidate.entry));
|
|
763
|
+
}
|
|
764
|
+
function confidenceCalibrationEvidence(entry, confidence, alternativeCount) {
|
|
765
|
+
const locality = isRepositoryLocalEntry(entry) ? "repository-local" : "external/framework";
|
|
766
|
+
const ambiguity = alternativeCount > 0 ? ` with ${alternativeCount} plausible alternative(s)` : "";
|
|
767
|
+
return `${locality} ${entry.kind} calibrated to ${confidence.toFixed(3)}${ambiguity}`;
|
|
768
|
+
}
|
|
769
|
+
function confidenceFromCandidateSignals(entry, intent, category, score) {
|
|
770
|
+
const semantic = semanticSimilarityConfidence(entry, intent);
|
|
771
|
+
const locality = repositoryLocalityConfidence(entry);
|
|
772
|
+
const architecture = architectureAgreementConfidence(entry, category);
|
|
773
|
+
const feature = semanticFeatureAgreement(entry, intent);
|
|
774
|
+
const connectivity = graphConnectivityConfidence(entry);
|
|
775
|
+
const scoreConfidence = Math.min(1, score / 760);
|
|
776
|
+
const confidence = semantic * 0.32 + locality * 0.22 + architecture * 0.16 + feature * 0.16 + connectivity * 0.08 + scoreConfidence * 0.06;
|
|
777
|
+
const exact = entry.normalizedName === intent.normalized || normalizePhrase(entry.stableId) === intent.normalized || entry.aliases.includes(intent.normalized);
|
|
778
|
+
return exact ? Math.max(confidence, isRepositoryLocalEntry(entry) ? 0.78 : 0.55) : confidence;
|
|
779
|
+
}
|
|
780
|
+
function semanticSimilarityConfidence(entry, intent) {
|
|
781
|
+
const entryTerms = /* @__PURE__ */ new Set([entry.normalizedName, ...entry.aliases, ...entry.keywords]);
|
|
782
|
+
if (!intent.normalized) {
|
|
783
|
+
return 0;
|
|
582
784
|
}
|
|
583
|
-
if (
|
|
584
|
-
|
|
785
|
+
if (entry.normalizedName === intent.normalized || entry.aliases.includes(intent.normalized) || normalizePhrase(entry.stableId) === intent.normalized) {
|
|
786
|
+
return 1;
|
|
787
|
+
}
|
|
788
|
+
const originalCoverage = originalTokenCoverage(entry, intent);
|
|
789
|
+
const expandedTerms = intent.expandedTerms.filter((term) => !intent.tokens.includes(term));
|
|
790
|
+
const expandedCoverage = expandedTerms.length === 0 ? 0 : expandedTerms.filter((term) => entryTerms.has(term)).length / expandedTerms.length;
|
|
791
|
+
const phraseCoverage = intent.phrases.some((phrase) => entryTerms.has(phrase)) ? 0.9 : intent.phrases.some((phrase) => [...entryTerms].some((term) => term.includes(phrase) || phrase.includes(term))) ? 0.55 : 0;
|
|
792
|
+
return Math.min(1, Math.max(phraseCoverage, originalCoverage * 0.78 + expandedCoverage * 0.22));
|
|
793
|
+
}
|
|
794
|
+
function originalTokenCoverage(entry, intent) {
|
|
795
|
+
if (intent.tokens.length === 0) {
|
|
796
|
+
return 0;
|
|
797
|
+
}
|
|
798
|
+
const entryTerms = /* @__PURE__ */ new Set([entry.normalizedName, ...entry.aliases, ...entry.keywords]);
|
|
799
|
+
const searchableText = [
|
|
800
|
+
entry.stableId,
|
|
801
|
+
entry.displayName,
|
|
802
|
+
entry.filePath ?? "",
|
|
803
|
+
entry.folderPath ?? "",
|
|
804
|
+
entry.parentChain.join(" "),
|
|
805
|
+
entry.relationships.neighborNames.join(" "),
|
|
806
|
+
entry.documentation ?? "",
|
|
807
|
+
entry.comments ?? ""
|
|
808
|
+
].join(" ").toLowerCase();
|
|
809
|
+
const matches = intent.tokens.filter(
|
|
810
|
+
(token) => entryTerms.has(token) || entryTerms.has(singularize(token)) || searchableText.includes(token)
|
|
811
|
+
);
|
|
812
|
+
return matches.length / intent.tokens.length;
|
|
813
|
+
}
|
|
814
|
+
function originalQueryCoverage(entry, intent) {
|
|
815
|
+
const required = uniqueStrings(intent.tokens.map((token) => singularize(token)).filter((token) => token.length > 2));
|
|
816
|
+
if (required.length === 0) {
|
|
817
|
+
return { matched: [], missing: [], ratio: 1 };
|
|
818
|
+
}
|
|
819
|
+
const entryTerms = new Set([entry.normalizedName, ...entry.aliases, ...entry.keywords].flatMap((term) => tokenize(term).map(singularize)));
|
|
820
|
+
const searchableTerms = new Set(tokenize([
|
|
821
|
+
entry.stableId,
|
|
822
|
+
entry.displayName,
|
|
823
|
+
entry.filePath ?? "",
|
|
824
|
+
entry.folderPath ?? "",
|
|
825
|
+
entry.parentChain.join(" "),
|
|
826
|
+
entry.relationships.neighborNames.join(" "),
|
|
827
|
+
entry.documentation ?? "",
|
|
828
|
+
entry.comments ?? ""
|
|
829
|
+
].join(" ")).map(singularize));
|
|
830
|
+
const matched = required.filter((term) => entryTerms.has(term) || searchableTerms.has(term));
|
|
831
|
+
const missing = required.filter((term) => !matched.includes(term));
|
|
832
|
+
return { matched, missing, ratio: matched.length / required.length };
|
|
833
|
+
}
|
|
834
|
+
function semanticFeatureAgreement(entry, intent) {
|
|
835
|
+
if (intent.tokens.length === 0) {
|
|
836
|
+
return 0;
|
|
585
837
|
}
|
|
586
|
-
|
|
838
|
+
const featureText = tokenize([
|
|
839
|
+
entry.stableId,
|
|
840
|
+
entry.displayName,
|
|
841
|
+
entry.filePath ?? "",
|
|
842
|
+
entry.folderPath ?? "",
|
|
843
|
+
entry.parentChain.join(" "),
|
|
844
|
+
entry.relationships.neighborNames.join(" "),
|
|
845
|
+
entry.documentation ?? "",
|
|
846
|
+
entry.comments ?? ""
|
|
847
|
+
].join(" "));
|
|
848
|
+
const featureTerms = new Set(featureText);
|
|
849
|
+
const original = intent.tokens.filter((token) => featureTerms.has(token) || featureTerms.has(singularize(token))).length / intent.tokens.length;
|
|
850
|
+
const expanded = intent.expandedTerms.length === 0 ? 0 : intent.expandedTerms.filter((term) => featureTerms.has(term)).length / intent.expandedTerms.length;
|
|
851
|
+
return Math.min(1, original * 0.82 + expanded * 0.18);
|
|
852
|
+
}
|
|
853
|
+
function semanticFeaturePenalty(entry, intent) {
|
|
854
|
+
const required = new Set(intent.tokens.map(singularize));
|
|
855
|
+
const terms = new Set(tokenize([
|
|
856
|
+
entry.stableId,
|
|
857
|
+
entry.displayName,
|
|
858
|
+
entry.filePath ?? "",
|
|
859
|
+
entry.folderPath ?? "",
|
|
860
|
+
entry.parentChain.join(" "),
|
|
861
|
+
entry.relationships.neighborNames.join(" "),
|
|
862
|
+
entry.documentation ?? "",
|
|
863
|
+
entry.comments ?? ""
|
|
864
|
+
].join(" ")).map(singularize));
|
|
865
|
+
let penalty = 0;
|
|
866
|
+
if (required.has("threshold") && !terms.has("threshold")) {
|
|
867
|
+
penalty += 130;
|
|
868
|
+
}
|
|
869
|
+
if (required.has("threshold") && (terms.has("statistic") || terms.has("observation")) && !terms.has("threshold")) {
|
|
870
|
+
penalty += 220;
|
|
871
|
+
}
|
|
872
|
+
if (required.has("duration") && required.has("sleep") && !terms.has("duration")) {
|
|
873
|
+
penalty += 80;
|
|
874
|
+
}
|
|
875
|
+
return penalty;
|
|
876
|
+
}
|
|
877
|
+
function repositoryLocalityConfidence(entry) {
|
|
878
|
+
if (!isRepositoryLocalEntry(entry)) {
|
|
879
|
+
return 0.08;
|
|
880
|
+
}
|
|
881
|
+
if (isGenericUtilityEntry(entry)) {
|
|
882
|
+
return 0.72;
|
|
883
|
+
}
|
|
884
|
+
if (!entry.filePath && (entry.kind === "Dependency" || entry.kind === "Framework")) {
|
|
885
|
+
return 0.2;
|
|
886
|
+
}
|
|
887
|
+
return 1;
|
|
888
|
+
}
|
|
889
|
+
function architectureAgreementConfidence(entry, category) {
|
|
890
|
+
if (isExternalOrFrameworkEntry(entry)) {
|
|
891
|
+
return 0.12;
|
|
892
|
+
}
|
|
893
|
+
if (isPreferredRepositorySeedEntry(entry)) {
|
|
894
|
+
return 1;
|
|
895
|
+
}
|
|
896
|
+
if (categoryTypeSet(category)?.has(entry.kind)) {
|
|
897
|
+
return 0.82;
|
|
898
|
+
}
|
|
899
|
+
if (entry.architectureLayer === "architecture" && category === "feature") {
|
|
900
|
+
return 0.4;
|
|
901
|
+
}
|
|
902
|
+
return entry.architectureLayer === "language" ? 0.55 : 0.65;
|
|
903
|
+
}
|
|
904
|
+
function graphConnectivityConfidence(entry) {
|
|
905
|
+
return Math.min(1, 0.25 + Math.log2(entry.relationships.degree + 1) / 4);
|
|
906
|
+
}
|
|
907
|
+
function repositoryLocalityBoost(entry) {
|
|
908
|
+
let score = 0;
|
|
909
|
+
if (isRepositoryLocalEntry(entry)) {
|
|
910
|
+
score += 56;
|
|
911
|
+
score += REPOSITORY_LOCAL_KIND_BOOSTS[entry.kind] ?? 0;
|
|
912
|
+
}
|
|
913
|
+
if (isDtoLikeEntry(entry)) {
|
|
914
|
+
score += 46;
|
|
915
|
+
}
|
|
916
|
+
if (isMapperEntry(entry)) {
|
|
917
|
+
score += 34;
|
|
918
|
+
}
|
|
919
|
+
if (isFeatureModuleEntry(entry)) {
|
|
587
920
|
score += 22;
|
|
588
921
|
}
|
|
922
|
+
if (isTestEntry(entry)) {
|
|
923
|
+
score += 28;
|
|
924
|
+
}
|
|
589
925
|
return score;
|
|
590
926
|
}
|
|
591
927
|
function repositoryRankingPenalty(entry) {
|
|
592
928
|
let penalty = 0;
|
|
593
929
|
const text = `${entry.stableId} ${entry.filePath ?? ""} ${entry.package ?? ""} ${entry.displayName}`.toLowerCase();
|
|
594
930
|
if (text.includes("node_modules") || entry.stableId.startsWith("dep:")) {
|
|
595
|
-
penalty +=
|
|
931
|
+
penalty += 260;
|
|
596
932
|
}
|
|
597
933
|
if (/(\bdto\b|dto$|schema$|type$|types$|generated|__generated__)/i.test(entry.displayName) && !isRepositoryLocalEntry(entry)) {
|
|
598
|
-
penalty +=
|
|
934
|
+
penalty += 90;
|
|
599
935
|
}
|
|
600
|
-
if (/node_modules|@nestjs\/|next\/dist|react\/|typescript\/lib
|
|
601
|
-
penalty +=
|
|
936
|
+
if (/node_modules|@nestjs\/|@medplum\/|next\/dist|react\/|typescript\/lib|@types\//i.test(text)) {
|
|
937
|
+
penalty += 180;
|
|
938
|
+
}
|
|
939
|
+
if (isExternalOrFrameworkEntry(entry)) {
|
|
940
|
+
penalty += 120;
|
|
602
941
|
}
|
|
603
942
|
if (/^framework:|framework|adapter|platform|runtime/.test(entry.stableId.toLowerCase()) && !isRepositoryLocalEntry(entry)) {
|
|
604
|
-
penalty +=
|
|
943
|
+
penalty += 90;
|
|
605
944
|
}
|
|
606
945
|
if (isGenericUtilityEntry(entry)) {
|
|
607
|
-
penalty +=
|
|
946
|
+
penalty += 55;
|
|
608
947
|
}
|
|
609
948
|
return penalty;
|
|
610
949
|
}
|
|
611
950
|
function repositoryLocalityEvidence(entry) {
|
|
612
951
|
return isRepositoryLocalEntry(entry) ? `${entry.kind} from repository-local graph evidence` : `${entry.kind} from external or generated boundary`;
|
|
613
952
|
}
|
|
614
|
-
function repositoryNoiseEvidence(entry) {
|
|
615
|
-
|
|
953
|
+
function repositoryNoiseEvidence(entry, missingTerms = []) {
|
|
954
|
+
const missing = missingTerms.length > 0 ? `; missing query term(s): ${missingTerms.join(", ")}` : "";
|
|
955
|
+
return `${entry.displayName} is external, generated, generic framework-adjacent, or lower-coverage evidence${missing}`;
|
|
616
956
|
}
|
|
617
957
|
function isRepositoryLocalEntry(entry) {
|
|
618
958
|
const text = `${entry.stableId} ${entry.filePath ?? ""} ${entry.package ?? ""}`.toLowerCase();
|
|
619
|
-
return !text.includes("node_modules") && !entry.stableId.startsWith("dep:") && !entry.stableId.startsWith("framework:");
|
|
620
|
-
}
|
|
621
|
-
function isRepositorySymbolEntry(entry) {
|
|
622
|
-
return ["Function", "Method", "Class", "Interface", "TypeAlias", "Enum", "Field", "Model", "Resource"].includes(entry.kind);
|
|
959
|
+
return !text.includes("node_modules") && !entry.stableId.startsWith("dep:") && !entry.stableId.startsWith("framework:") && !isKnownExternalPackageEntry(entry);
|
|
623
960
|
}
|
|
624
961
|
function isDtoLikeEntry(entry) {
|
|
625
962
|
const text = `${entry.displayName} ${entry.filePath ?? ""} ${entry.keywords.join(" ")}`;
|
|
626
963
|
return ["Model", "Interface", "TypeAlias", "Class"].includes(entry.kind) && /\bdto\b|dto$|data transfer|payload|request|response/i.test(text);
|
|
627
964
|
}
|
|
965
|
+
function featureOwnerBoostFor(entry, category) {
|
|
966
|
+
if (category !== "feature" && category !== "concept" || !isRepositoryLocalEntry(entry)) {
|
|
967
|
+
return 0;
|
|
968
|
+
}
|
|
969
|
+
if (entry.kind === "Service" || entry.kind === "Provider") {
|
|
970
|
+
return 170;
|
|
971
|
+
}
|
|
972
|
+
if (entry.kind === "Class" && /service$/i.test(entry.displayName)) {
|
|
973
|
+
return 170;
|
|
974
|
+
}
|
|
975
|
+
if (entry.kind === "Controller") {
|
|
976
|
+
return 90;
|
|
977
|
+
}
|
|
978
|
+
return 0;
|
|
979
|
+
}
|
|
628
980
|
function isFeatureModuleEntry(entry) {
|
|
629
981
|
return entry.kind === "Module" && isRepositoryLocalEntry(entry) && !isGenericUtilityEntry(entry);
|
|
630
982
|
}
|
|
983
|
+
function isPreferredRepositorySeedEntry(entry) {
|
|
984
|
+
return isRepositoryLocalEntry(entry) && (["Service", "Controller", "Repository", "Method", "Route", "Operation", "Configuration", "EnvironmentVariable", "Guard", "Permission", "Middleware"].includes(entry.kind) || isDtoLikeEntry(entry) || isFeatureModuleEntry(entry) || isTestEntry(entry) || isMapperEntry(entry));
|
|
985
|
+
}
|
|
986
|
+
function isMapperEntry(entry) {
|
|
987
|
+
return isRepositoryLocalEntry(entry) && /\bmapper\b|mapper$/i.test(`${entry.displayName} ${entry.filePath ?? ""}`);
|
|
988
|
+
}
|
|
989
|
+
function isTestEntry(entry) {
|
|
990
|
+
return /(^|[/.])(test|tests|spec|__tests__)([/.]|$)|\.(test|spec)\./i.test(`${entry.filePath ?? ""} ${entry.displayName}`);
|
|
991
|
+
}
|
|
992
|
+
function isExternalOrFrameworkEntry(entry) {
|
|
993
|
+
return !isRepositoryLocalEntry(entry) || entry.kind === "Dependency" || entry.kind === "Framework" || isKnownExternalPackageEntry(entry);
|
|
994
|
+
}
|
|
995
|
+
function isKnownExternalPackageEntry(entry) {
|
|
996
|
+
const text = `${entry.stableId} ${entry.displayName} ${entry.package ?? ""} ${entry.filePath ?? ""}`.toLowerCase();
|
|
997
|
+
return FRAMEWORK_PACKAGE_PATTERN.test(text) || /(^|[/:])(@nestjs|@medplum|@types|@angular|next|react|typescript|express|fastify)([/:-]|$)/.test(text) || /(^|[/:])(node|npm):/.test(text);
|
|
998
|
+
}
|
|
631
999
|
function isGenericUtilityEntry(entry) {
|
|
632
1000
|
const text = `${entry.stableId} ${entry.displayName} ${entry.filePath ?? ""}`.toLowerCase();
|
|
633
1001
|
return /(^|[/.:-])(common|shared|utils?|helpers?|internal|misc|core|index)([/.:-]|$)/.test(text) || /\b(base|abstract|generic|utility|utils?|helpers?)\b/.test(entry.displayName);
|
|
@@ -900,7 +1268,20 @@ function collectMetadataText(value, values, depth, seen) {
|
|
|
900
1268
|
}
|
|
901
1269
|
}
|
|
902
1270
|
function isNoisyMetadataKey(key) {
|
|
903
|
-
return [
|
|
1271
|
+
return [
|
|
1272
|
+
"abstract",
|
|
1273
|
+
"async",
|
|
1274
|
+
"declarationKind",
|
|
1275
|
+
"language",
|
|
1276
|
+
"parameters",
|
|
1277
|
+
"parser",
|
|
1278
|
+
"parserVersion",
|
|
1279
|
+
"passId",
|
|
1280
|
+
"provenance",
|
|
1281
|
+
"source",
|
|
1282
|
+
"static",
|
|
1283
|
+
"typeParameters"
|
|
1284
|
+
].includes(key);
|
|
904
1285
|
}
|
|
905
1286
|
function routeTextValues(node) {
|
|
906
1287
|
if (node.type !== "Route" && node.type !== "Operation") {
|
|
@@ -962,10 +1343,13 @@ function groupEdges(edges, side) {
|
|
|
962
1343
|
return new Map([...grouped.entries()].map(([key, values]) => [key, values.sort(compareEdges)]));
|
|
963
1344
|
}
|
|
964
1345
|
function addInverted(map, term, id) {
|
|
965
|
-
if (!term || STOP_WORDS.has(term)) {
|
|
1346
|
+
if (!term || STOP_WORDS.has(term) || isSemanticNoiseTerm(term) || isNoisySemanticAlias(term)) {
|
|
966
1347
|
return;
|
|
967
1348
|
}
|
|
968
1349
|
const current = map.get(term) ?? /* @__PURE__ */ new Set();
|
|
1350
|
+
if (current.size >= SEMANTIC_INVERTED_IDS_LIMIT) {
|
|
1351
|
+
return;
|
|
1352
|
+
}
|
|
969
1353
|
current.add(id);
|
|
970
1354
|
map.set(term, current);
|
|
971
1355
|
}
|
|
@@ -990,6 +1374,19 @@ function compareEdges(left, right) {
|
|
|
990
1374
|
function uniqueStrings(values) {
|
|
991
1375
|
return [...new Set(values.map((value) => value.trim()).filter(Boolean))].sort((left, right) => left.localeCompare(right));
|
|
992
1376
|
}
|
|
1377
|
+
function isSemanticNoiseTerm(term) {
|
|
1378
|
+
return SEMANTIC_NOISE_TERMS.has(term) || /^\d+[a-z]*$/i.test(term);
|
|
1379
|
+
}
|
|
1380
|
+
function isNoisySemanticAlias(alias) {
|
|
1381
|
+
const tokens = tokenize(alias);
|
|
1382
|
+
if (tokens.length === 0) {
|
|
1383
|
+
return true;
|
|
1384
|
+
}
|
|
1385
|
+
if (tokens.length > 14) {
|
|
1386
|
+
return true;
|
|
1387
|
+
}
|
|
1388
|
+
return tokens.every(isSemanticNoiseTerm);
|
|
1389
|
+
}
|
|
993
1390
|
function sum(values) {
|
|
994
1391
|
return values.reduce((total, value) => total + value, 0);
|
|
995
1392
|
}
|