@gmickel/gno 1.26.0 → 1.27.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -1
- package/assets/skill/SKILL.md +8 -0
- package/browser-extension/artifacts/{gno-browser-clipper-v1.26.0.zip → gno-browser-clipper-v1.27.0.zip} +0 -0
- package/browser-extension/artifacts/gno-browser-clipper-v1.27.0.zip.sha256 +1 -0
- package/browser-extension/dist/manifest.json +1 -1
- package/package.json +1 -1
- package/spec/cli.md +15 -2
- package/spec/evals-agentic.md +17 -0
- package/spec/mcp.md +25 -6
- package/spec/output-schemas/ask.schema.json +3 -0
- package/spec/output-schemas/query-diagnose.schema.json +61 -4
- package/spec/output-schemas/search-results.schema.json +87 -1
- package/spec/output-schemas/status.schema.json +24 -0
- package/spec/project-profile.schema.json +3 -1
- package/src/app/context-runtime-contract.ts +4 -1
- package/src/app/context-runtime-types.ts +2 -0
- package/src/app/context-runtime.ts +26 -0
- package/src/app/verified-ask.ts +6 -1
- package/src/cli/commands/ask.ts +8 -1
- package/src/cli/commands/query.ts +6 -3
- package/src/cli/commands/search.ts +6 -1
- package/src/cli/commands/status.ts +43 -7
- package/src/cli/program.ts +2 -0
- package/src/config/content-types.ts +82 -0
- package/src/config/index.ts +8 -0
- package/src/config/project-profile.ts +8 -1
- package/src/config/types.ts +11 -2
- package/src/core/context-compiler.ts +38 -1
- package/src/core/retrieval-replay-candidate.ts +6 -1
- package/src/ingestion/sync-options.ts +6 -2
- package/src/ingestion/sync.ts +21 -29
- package/src/ingestion/types.ts +1 -1
- package/src/mcp/tools/ask.ts +1 -0
- package/src/mcp/tools/index.ts +4 -0
- package/src/mcp/tools/query.ts +4 -2
- package/src/mcp/tools/search.ts +3 -0
- package/src/mcp/tools/status.ts +4 -0
- package/src/pipeline/content-type-boost.ts +264 -0
- package/src/pipeline/diagnose.ts +46 -19
- package/src/pipeline/explain.ts +15 -2
- package/src/pipeline/hybrid.ts +170 -74
- package/src/pipeline/rerank.ts +45 -15
- package/src/pipeline/search.ts +29 -11
- package/src/pipeline/types.ts +13 -4
- package/src/pipeline/vsearch.ts +30 -10
- package/src/sdk/client.ts +19 -3
- package/src/sdk/index.ts +1 -0
- package/src/sdk/types.ts +21 -5
- package/src/serve/routes/api.ts +17 -3
- package/src/serve/status-model.ts +2 -0
- package/src/serve/status.ts +4 -0
- package/src/store/sqlite/adapter.ts +3 -0
- package/src/store/types.ts +3 -2
- package/browser-extension/artifacts/gno-browser-clipper-v1.26.0.zip.sha256 +0 -1
package/src/mcp/tools/status.ts
CHANGED
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
import type { IndexStatus } from "../../store/types";
|
|
8
8
|
import type { ToolContext } from "../server";
|
|
9
9
|
|
|
10
|
+
import { buildContentTypeBoostStatus } from "../../config/content-types";
|
|
10
11
|
import { resolveModelUri } from "../../llm/registry";
|
|
11
12
|
import { createStandaloneResidentStatus } from "../../serve/resident-status";
|
|
12
13
|
import { runTool, type ToolResult } from "./index";
|
|
@@ -87,6 +88,9 @@ export function handleStatus(
|
|
|
87
88
|
return {
|
|
88
89
|
...result.value,
|
|
89
90
|
configPath: ctx.actualConfigPath,
|
|
91
|
+
contentTypeBoost: buildContentTypeBoostStatus(
|
|
92
|
+
ctx.config.contentTypes ?? []
|
|
93
|
+
),
|
|
90
94
|
resident:
|
|
91
95
|
ctx.getResidentStatus?.() ?? createStandaloneResidentStatus("stdio"),
|
|
92
96
|
};
|
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bounded content-type scoring composed with trusted project affinity.
|
|
3
|
+
*
|
|
4
|
+
* @module src/pipeline/content-type-boost
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import type { NormalizedContentTypeRule } from "../config/content-types";
|
|
8
|
+
import type { ProjectAffinityScoringInput } from "./project-affinity";
|
|
9
|
+
import type { SearchResult } from "./types";
|
|
10
|
+
|
|
11
|
+
import {
|
|
12
|
+
fingerprintContentTypeRules,
|
|
13
|
+
resolveContentTypeRule,
|
|
14
|
+
} from "../config/content-types";
|
|
15
|
+
import {
|
|
16
|
+
CONTENT_TYPE_SEARCH_BOOST_MAX,
|
|
17
|
+
CONTENT_TYPE_SEARCH_BOOST_MIN,
|
|
18
|
+
CONTENT_TYPE_SEARCH_BOOST_NEUTRAL,
|
|
19
|
+
} from "../config/types";
|
|
20
|
+
import {
|
|
21
|
+
applyAuxiliaryScore,
|
|
22
|
+
hasProjectAffinity,
|
|
23
|
+
SEARCH_RESULT_AFFINITY_METADATA,
|
|
24
|
+
scoreProjectAffinity,
|
|
25
|
+
type ProjectAffinityScoreMetadata,
|
|
26
|
+
} from "./project-affinity";
|
|
27
|
+
|
|
28
|
+
export const CONTENT_TYPE_MAX_CONTRIBUTION = 0.05;
|
|
29
|
+
|
|
30
|
+
export interface ContentTypeBoostScoreMetadata {
|
|
31
|
+
baseScore: number;
|
|
32
|
+
cappedContribution: number;
|
|
33
|
+
combinedAuxiliaryApplied: number;
|
|
34
|
+
combinedAuxiliaryCap: number;
|
|
35
|
+
combinedAuxiliaryRequested: number;
|
|
36
|
+
configuredFactor: number;
|
|
37
|
+
contentType: string;
|
|
38
|
+
finalScore: number;
|
|
39
|
+
rawContribution: number;
|
|
40
|
+
rawScore: number;
|
|
41
|
+
rawScoreKind: ProjectAffinityScoreMetadata["rawScoreKind"];
|
|
42
|
+
ruleSource: "configured-id" | "prefix";
|
|
43
|
+
rulesFingerprint: string;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export interface AuxiliaryScoreResult {
|
|
47
|
+
contentTypeBoost?: ContentTypeBoostScoreMetadata;
|
|
48
|
+
projectAffinity: ProjectAffinityScoreMetadata;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export const SEARCH_RESULT_CONTENT_TYPE_BOOST_METADATA = Symbol(
|
|
52
|
+
"gno.searchResultContentTypeBoostMetadata"
|
|
53
|
+
);
|
|
54
|
+
|
|
55
|
+
const clamp = (value: number, min: number, max: number): number =>
|
|
56
|
+
Math.min(max, Math.max(min, value));
|
|
57
|
+
|
|
58
|
+
const rankingFingerprints = new WeakMap<
|
|
59
|
+
readonly NormalizedContentTypeRule[],
|
|
60
|
+
string
|
|
61
|
+
>();
|
|
62
|
+
|
|
63
|
+
const rankingFingerprint = (
|
|
64
|
+
rules: readonly NormalizedContentTypeRule[] | undefined
|
|
65
|
+
): string => {
|
|
66
|
+
if (!rules) return fingerprintContentTypeRules([]);
|
|
67
|
+
const cached = rankingFingerprints.get(rules);
|
|
68
|
+
if (cached) return cached;
|
|
69
|
+
const fingerprint = fingerprintContentTypeRules([...rules]);
|
|
70
|
+
rankingFingerprints.set(rules, fingerprint);
|
|
71
|
+
return fingerprint;
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
/** Map the supported factor range continuously onto the contribution range. */
|
|
75
|
+
export function contentTypeBoostContribution(factor: number): {
|
|
76
|
+
raw: number;
|
|
77
|
+
capped: number;
|
|
78
|
+
} {
|
|
79
|
+
const raw =
|
|
80
|
+
factor >= CONTENT_TYPE_SEARCH_BOOST_NEUTRAL
|
|
81
|
+
? ((factor - CONTENT_TYPE_SEARCH_BOOST_NEUTRAL) /
|
|
82
|
+
(CONTENT_TYPE_SEARCH_BOOST_MAX - CONTENT_TYPE_SEARCH_BOOST_NEUTRAL)) *
|
|
83
|
+
CONTENT_TYPE_MAX_CONTRIBUTION
|
|
84
|
+
: ((factor - CONTENT_TYPE_SEARCH_BOOST_NEUTRAL) /
|
|
85
|
+
(CONTENT_TYPE_SEARCH_BOOST_NEUTRAL - CONTENT_TYPE_SEARCH_BOOST_MIN)) *
|
|
86
|
+
CONTENT_TYPE_MAX_CONTRIBUTION;
|
|
87
|
+
return {
|
|
88
|
+
raw,
|
|
89
|
+
capped: clamp(
|
|
90
|
+
raw,
|
|
91
|
+
-CONTENT_TYPE_MAX_CONTRIBUTION,
|
|
92
|
+
CONTENT_TYPE_MAX_CONTRIBUTION
|
|
93
|
+
),
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export function hasContentTypeBoost(
|
|
98
|
+
rules: readonly NormalizedContentTypeRule[] | undefined
|
|
99
|
+
): boolean {
|
|
100
|
+
return Boolean(
|
|
101
|
+
rules?.some(
|
|
102
|
+
(rule) => rule.searchBoost !== CONTENT_TYPE_SEARCH_BOOST_NEUTRAL
|
|
103
|
+
)
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export function hasAuxiliaryRanking(
|
|
108
|
+
projectAffinity: ProjectAffinityScoringInput | undefined,
|
|
109
|
+
rules: readonly NormalizedContentTypeRule[] | undefined
|
|
110
|
+
): boolean {
|
|
111
|
+
return hasProjectAffinity(projectAffinity) || hasContentTypeBoost(rules);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
export function scoreContentTypeBoost(
|
|
115
|
+
baseScore: number,
|
|
116
|
+
contentType: string | undefined,
|
|
117
|
+
contentTypeSource: string | null | undefined,
|
|
118
|
+
relativePath: string,
|
|
119
|
+
collection: string,
|
|
120
|
+
rules: readonly NormalizedContentTypeRule[] | undefined,
|
|
121
|
+
projectAffinity: ProjectAffinityScoringInput | undefined,
|
|
122
|
+
raw: {
|
|
123
|
+
kind: ProjectAffinityScoreMetadata["rawScoreKind"];
|
|
124
|
+
score: number;
|
|
125
|
+
} = { kind: "normalized", score: baseScore }
|
|
126
|
+
): AuxiliaryScoreResult {
|
|
127
|
+
const configuredId =
|
|
128
|
+
contentTypeSource === "frontmatter-type" ||
|
|
129
|
+
contentTypeSource === "frontmatter"
|
|
130
|
+
? contentType
|
|
131
|
+
: undefined;
|
|
132
|
+
const resolution = resolveContentTypeRule(
|
|
133
|
+
configuredId,
|
|
134
|
+
relativePath,
|
|
135
|
+
rules ? [...rules] : []
|
|
136
|
+
);
|
|
137
|
+
const factor =
|
|
138
|
+
resolution?.rule.searchBoost ?? CONTENT_TYPE_SEARCH_BOOST_NEUTRAL;
|
|
139
|
+
const contribution = contentTypeBoostContribution(factor);
|
|
140
|
+
const projectScore = scoreProjectAffinity(
|
|
141
|
+
baseScore,
|
|
142
|
+
collection,
|
|
143
|
+
projectAffinity,
|
|
144
|
+
raw
|
|
145
|
+
);
|
|
146
|
+
const combined = applyAuxiliaryScore(baseScore, [
|
|
147
|
+
projectScore.affinityRequested,
|
|
148
|
+
contribution.capped,
|
|
149
|
+
]);
|
|
150
|
+
const compositeProjectScore: ProjectAffinityScoreMetadata = {
|
|
151
|
+
...projectScore,
|
|
152
|
+
combinedAuxiliaryApplied: combined.applied,
|
|
153
|
+
combinedAuxiliaryRequested: combined.requested,
|
|
154
|
+
finalBlendedScore: combined.finalScore,
|
|
155
|
+
finalScore: combined.finalScore,
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
if (!resolution || factor === CONTENT_TYPE_SEARCH_BOOST_NEUTRAL) {
|
|
159
|
+
return { projectAffinity: compositeProjectScore };
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
return {
|
|
163
|
+
projectAffinity: compositeProjectScore,
|
|
164
|
+
contentTypeBoost: {
|
|
165
|
+
baseScore,
|
|
166
|
+
cappedContribution: contribution.capped,
|
|
167
|
+
combinedAuxiliaryApplied: compositeProjectScore.combinedAuxiliaryApplied,
|
|
168
|
+
combinedAuxiliaryCap: compositeProjectScore.combinedAuxiliaryCap,
|
|
169
|
+
combinedAuxiliaryRequested:
|
|
170
|
+
compositeProjectScore.combinedAuxiliaryRequested,
|
|
171
|
+
configuredFactor: factor,
|
|
172
|
+
contentType: resolution.rule.id,
|
|
173
|
+
finalScore: compositeProjectScore.finalScore,
|
|
174
|
+
rawContribution: contribution.raw,
|
|
175
|
+
rawScore: raw.score,
|
|
176
|
+
rawScoreKind: raw.kind,
|
|
177
|
+
ruleSource: resolution.source,
|
|
178
|
+
rulesFingerprint: rankingFingerprint(rules),
|
|
179
|
+
},
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
export function attachAuxiliaryScoreMetadata(
|
|
184
|
+
result: SearchResult,
|
|
185
|
+
scored: AuxiliaryScoreResult,
|
|
186
|
+
finalScore: number,
|
|
187
|
+
includeProjectAffinity = false
|
|
188
|
+
): SearchResult {
|
|
189
|
+
const projectAffinity = {
|
|
190
|
+
...scored.projectAffinity,
|
|
191
|
+
finalBlendedScore: finalScore,
|
|
192
|
+
finalScore,
|
|
193
|
+
};
|
|
194
|
+
result.score = finalScore;
|
|
195
|
+
if (includeProjectAffinity) {
|
|
196
|
+
Object.defineProperty(result, SEARCH_RESULT_AFFINITY_METADATA, {
|
|
197
|
+
configurable: true,
|
|
198
|
+
enumerable: false,
|
|
199
|
+
value: projectAffinity,
|
|
200
|
+
writable: true,
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
if (scored.contentTypeBoost) {
|
|
204
|
+
Object.defineProperty(result, SEARCH_RESULT_CONTENT_TYPE_BOOST_METADATA, {
|
|
205
|
+
configurable: true,
|
|
206
|
+
enumerable: false,
|
|
207
|
+
value: { ...scored.contentTypeBoost, finalScore },
|
|
208
|
+
writable: true,
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
return result;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
export function applyContentTypeBoost(
|
|
215
|
+
result: SearchResult,
|
|
216
|
+
collection: string,
|
|
217
|
+
rules: readonly NormalizedContentTypeRule[] | undefined,
|
|
218
|
+
projectAffinity: ProjectAffinityScoringInput | undefined,
|
|
219
|
+
contentTypeSource?: string | null,
|
|
220
|
+
raw?: {
|
|
221
|
+
kind: ProjectAffinityScoreMetadata["rawScoreKind"];
|
|
222
|
+
score: number;
|
|
223
|
+
}
|
|
224
|
+
): SearchResult {
|
|
225
|
+
const scored = scoreContentTypeBoost(
|
|
226
|
+
result.score,
|
|
227
|
+
result.contentType,
|
|
228
|
+
contentTypeSource,
|
|
229
|
+
result.source.relPath,
|
|
230
|
+
collection,
|
|
231
|
+
rules,
|
|
232
|
+
projectAffinity,
|
|
233
|
+
raw
|
|
234
|
+
);
|
|
235
|
+
const affinityActive = hasProjectAffinity(projectAffinity);
|
|
236
|
+
if (!(scored.contentTypeBoost || affinityActive)) return result;
|
|
237
|
+
return attachAuxiliaryScoreMetadata(
|
|
238
|
+
result,
|
|
239
|
+
scored,
|
|
240
|
+
scored.projectAffinity.finalScore,
|
|
241
|
+
affinityActive
|
|
242
|
+
);
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
export function getContentTypeBoostMetadata(
|
|
246
|
+
result: SearchResult
|
|
247
|
+
): ContentTypeBoostScoreMetadata | undefined {
|
|
248
|
+
return (
|
|
249
|
+
result as SearchResult & {
|
|
250
|
+
[SEARCH_RESULT_CONTENT_TYPE_BOOST_METADATA]?: ContentTypeBoostScoreMetadata;
|
|
251
|
+
}
|
|
252
|
+
)[SEARCH_RESULT_CONTENT_TYPE_BOOST_METADATA];
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
export function sortByFinalScoreStable(results: SearchResult[]): void {
|
|
256
|
+
const originalRank = new Map(
|
|
257
|
+
results.map((result, index) => [result, index] as const)
|
|
258
|
+
);
|
|
259
|
+
results.sort(
|
|
260
|
+
(left, right) =>
|
|
261
|
+
right.score - left.score ||
|
|
262
|
+
(originalRank.get(left) ?? 0) - (originalRank.get(right) ?? 0)
|
|
263
|
+
);
|
|
264
|
+
}
|
package/src/pipeline/diagnose.ts
CHANGED
|
@@ -14,16 +14,23 @@ import type {
|
|
|
14
14
|
QueryDiagnoseTraceCandidate,
|
|
15
15
|
} from "./types";
|
|
16
16
|
|
|
17
|
-
import {
|
|
17
|
+
import {
|
|
18
|
+
fingerprintContentTypeMetadataRules,
|
|
19
|
+
normalizeContentTypes,
|
|
20
|
+
} from "../config";
|
|
18
21
|
import { resolveDocRef } from "../core/ref-parser";
|
|
19
22
|
import { err, ok } from "../store/types";
|
|
23
|
+
import {
|
|
24
|
+
getContentTypeBoostMetadata,
|
|
25
|
+
scoreContentTypeBoost,
|
|
26
|
+
type ContentTypeBoostScoreMetadata,
|
|
27
|
+
} from "./content-type-boost";
|
|
20
28
|
import { evaluateQueryTargetFilters } from "./filters";
|
|
21
29
|
import { searchHybrid } from "./hybrid";
|
|
22
30
|
import {
|
|
23
31
|
getProjectAffinityMetadata,
|
|
24
32
|
type ProjectAffinityScoringInput,
|
|
25
33
|
type ProjectAffinityScoreMetadata,
|
|
26
|
-
scoreProjectAffinity,
|
|
27
34
|
} from "./project-affinity";
|
|
28
35
|
|
|
29
36
|
export type QueryDiagnoseTargetStatus =
|
|
@@ -52,7 +59,7 @@ export interface QueryDiagnoseStage {
|
|
|
52
59
|
}
|
|
53
60
|
|
|
54
61
|
export interface QueryDiagnoseResult {
|
|
55
|
-
schemaVersion: "1.0" | "1.1";
|
|
62
|
+
schemaVersion: "1.0" | "1.1" | "1.2";
|
|
56
63
|
query: string;
|
|
57
64
|
target: {
|
|
58
65
|
ref: string;
|
|
@@ -72,6 +79,7 @@ export interface QueryDiagnoseResult {
|
|
|
72
79
|
};
|
|
73
80
|
stages: QueryDiagnoseStage[];
|
|
74
81
|
affinity?: ProjectAffinityScoreMetadata;
|
|
82
|
+
contentTypeBoost?: ContentTypeBoostScoreMetadata;
|
|
75
83
|
chunk: {
|
|
76
84
|
seq: number | null;
|
|
77
85
|
startLine: number | null;
|
|
@@ -182,9 +190,12 @@ export async function diagnoseQueryTarget(
|
|
|
182
190
|
}
|
|
183
191
|
|
|
184
192
|
const doc = resolved.doc;
|
|
185
|
-
const rules =
|
|
193
|
+
const rules =
|
|
194
|
+
options.contentTypeRules ??
|
|
195
|
+
normalizeContentTypes(deps.config.contentTypes ?? []).rules;
|
|
186
196
|
const expectedFingerprint =
|
|
187
|
-
options.contentTypeRulesFingerprint ??
|
|
197
|
+
options.contentTypeRulesFingerprint ??
|
|
198
|
+
fingerprintContentTypeMetadataRules(rules);
|
|
188
199
|
const fingerprintMatches = doc.contentTypeRulesFingerprint
|
|
189
200
|
? doc.contentTypeRulesFingerprint === expectedFingerprint
|
|
190
201
|
: null;
|
|
@@ -302,16 +313,24 @@ export async function diagnoseQueryTarget(
|
|
|
302
313
|
(candidate) =>
|
|
303
314
|
candidate.mirrorHash === doc.mirrorHash && targetSeqs.has(candidate.seq)
|
|
304
315
|
);
|
|
316
|
+
const fallbackScore = lastMatched
|
|
317
|
+
? scoreContentTypeBoost(
|
|
318
|
+
lastMatched.score,
|
|
319
|
+
doc.contentType ?? undefined,
|
|
320
|
+
doc.contentTypeSource,
|
|
321
|
+
doc.relPath,
|
|
322
|
+
doc.collection,
|
|
323
|
+
rules,
|
|
324
|
+
options.projectAffinity,
|
|
325
|
+
{ kind: "hybrid_blended", score: lastMatched.score }
|
|
326
|
+
)
|
|
327
|
+
: undefined;
|
|
305
328
|
const affinity =
|
|
306
329
|
(targetResult ? getProjectAffinityMetadata(targetResult) : undefined) ??
|
|
307
|
-
(
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
options.projectAffinity,
|
|
312
|
-
{ kind: "hybrid_blended", score: lastMatched.score }
|
|
313
|
-
)
|
|
314
|
-
: null);
|
|
330
|
+
(options.projectAffinity ? fallbackScore?.projectAffinity : undefined);
|
|
331
|
+
const contentTypeBoost =
|
|
332
|
+
(targetResult ? getContentTypeBoostMetadata(targetResult) : undefined) ??
|
|
333
|
+
fallbackScore?.contentTypeBoost;
|
|
315
334
|
|
|
316
335
|
const baseResult: QueryDiagnoseResult = {
|
|
317
336
|
...buildBaseResult(query, options.target, "diagnosed", doc, {
|
|
@@ -335,13 +354,21 @@ export async function diagnoseQueryTarget(
|
|
|
335
354
|
queryModes: searchResult.value.meta.queryModes,
|
|
336
355
|
},
|
|
337
356
|
};
|
|
338
|
-
|
|
357
|
+
const trustedAffinity =
|
|
339
358
|
affinity && hasTrustedProjectAffinityInput(options.projectAffinity)
|
|
340
|
-
?
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
359
|
+
? affinity
|
|
360
|
+
: undefined;
|
|
361
|
+
if (contentTypeBoost) {
|
|
362
|
+
return ok({
|
|
363
|
+
...baseResult,
|
|
364
|
+
schemaVersion: "1.2",
|
|
365
|
+
...(trustedAffinity ? { affinity: trustedAffinity } : {}),
|
|
366
|
+
contentTypeBoost,
|
|
367
|
+
});
|
|
368
|
+
}
|
|
369
|
+
return ok(
|
|
370
|
+
trustedAffinity
|
|
371
|
+
? { ...baseResult, schemaVersion: "1.1", affinity: trustedAffinity }
|
|
345
372
|
: baseResult
|
|
346
373
|
);
|
|
347
374
|
}
|
package/src/pipeline/explain.ts
CHANGED
|
@@ -14,6 +14,8 @@ import type {
|
|
|
14
14
|
SearchResult,
|
|
15
15
|
} from "./types";
|
|
16
16
|
|
|
17
|
+
import { getContentTypeBoostMetadata } from "./content-type-boost";
|
|
18
|
+
import { getProjectAffinityMetadata } from "./project-affinity";
|
|
17
19
|
import { SEARCH_RESULT_PLANNER_METADATA } from "./types";
|
|
18
20
|
|
|
19
21
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
@@ -39,7 +41,8 @@ export function formatResultExplain(results: ExplainResult[]): string {
|
|
|
39
41
|
r.bm25Score !== undefined ||
|
|
40
42
|
r.vecScore !== undefined ||
|
|
41
43
|
r.rerankScore !== undefined ||
|
|
42
|
-
r.projectAffinity !== undefined
|
|
44
|
+
r.projectAffinity !== undefined ||
|
|
45
|
+
r.contentTypeBoost !== undefined
|
|
43
46
|
) {
|
|
44
47
|
msg += " (";
|
|
45
48
|
if (r.fusionScore !== undefined) {
|
|
@@ -69,6 +72,12 @@ export function formatResultExplain(results: ExplainResult[]): string {
|
|
|
69
72
|
}
|
|
70
73
|
msg += `raw=${r.projectAffinity.rawScoreKind}:${r.projectAffinity.rawScore.toFixed(3)}, base=${r.projectAffinity.baseScore.toFixed(3)}, affinity=${r.projectAffinity.affinityApplied.toFixed(3)}/${r.projectAffinity.affinityRequested.toFixed(3)}, auxiliary=${r.projectAffinity.combinedAuxiliaryApplied.toFixed(3)}/${r.projectAffinity.combinedAuxiliaryCap.toFixed(3)}, collection=${r.projectAffinity.collectionAlias}, root=${r.projectAffinity.rootAlias}, source=${r.projectAffinity.source}, final=${r.projectAffinity.finalScore.toFixed(3)}`;
|
|
71
74
|
}
|
|
75
|
+
if (r.contentTypeBoost) {
|
|
76
|
+
if (msg.at(-1) !== "(") {
|
|
77
|
+
msg += ", ";
|
|
78
|
+
}
|
|
79
|
+
msg += `contentType=${r.contentTypeBoost.contentType}, factor=${r.contentTypeBoost.configuredFactor.toFixed(3)}, rawBoost=${r.contentTypeBoost.rawContribution.toFixed(3)}, cappedBoost=${r.contentTypeBoost.cappedContribution.toFixed(3)}, auxiliary=${r.contentTypeBoost.combinedAuxiliaryApplied.toFixed(3)}/${r.contentTypeBoost.combinedAuxiliaryCap.toFixed(3)}, source=${r.contentTypeBoost.ruleSource}, final=${r.contentTypeBoost.finalScore.toFixed(3)}`;
|
|
80
|
+
}
|
|
72
81
|
msg += ")";
|
|
73
82
|
}
|
|
74
83
|
lines.push(`[explain] result ${r.rank}: ${r.docid} ${msg}`);
|
|
@@ -227,7 +236,11 @@ export function buildExplainResults(
|
|
|
227
236
|
entry.mirrorHash === result.conversion?.mirrorHash &&
|
|
228
237
|
entry.seq === (planner?.retrievalSeq ?? planner?.seq)
|
|
229
238
|
);
|
|
230
|
-
return
|
|
239
|
+
return {
|
|
240
|
+
...buildExplainResult(result.docid, result.score, index, candidate),
|
|
241
|
+
projectAffinity: getProjectAffinityMetadata(result),
|
|
242
|
+
contentTypeBoost: getContentTypeBoostMetadata(result),
|
|
243
|
+
};
|
|
231
244
|
});
|
|
232
245
|
}
|
|
233
246
|
return candidates.slice(0, 20).map((candidate, index) => {
|