@gmickel/gno 1.18.0 → 1.20.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 +14 -7
- package/assets/skill/SKILL.md +54 -12
- package/assets/skill/mcp-reference.md +7 -2
- package/assets/skill/recipes/citation-and-provenance.md +32 -9
- package/package.json +2 -1
- package/spec/AGENTS.md +83 -0
- package/spec/CLAUDE.md +83 -0
- package/spec/bench-fixture.schema.json +137 -0
- package/spec/cli.md +2919 -0
- package/spec/db/schema.sql +442 -0
- package/spec/evals-agentic.md +592 -0
- package/spec/evals.md +1106 -0
- package/spec/mcp.md +2279 -0
- package/spec/output-schemas/activation-verification.schema.json +515 -0
- package/spec/output-schemas/ask.schema.json +564 -0
- package/spec/output-schemas/backlinks.schema.json +131 -0
- package/spec/output-schemas/bench-result.schema.json +120 -0
- package/spec/output-schemas/capture-receipt.schema.json +143 -0
- package/spec/output-schemas/claim-verification.schema.json +291 -0
- package/spec/output-schemas/collection-list.schema.json +45 -0
- package/spec/output-schemas/context-capsule-v1.schema.json +726 -0
- package/spec/output-schemas/context-capsule-verification.schema.json +1338 -0
- package/spec/output-schemas/context-list.schema.json +21 -0
- package/spec/output-schemas/doctor.schema.json +313 -0
- package/spec/output-schemas/error.schema.json +30 -0
- package/spec/output-schemas/expansion.schema.json +37 -0
- package/spec/output-schemas/get.schema.json +140 -0
- package/spec/output-schemas/graph-query.schema.json +99 -0
- package/spec/output-schemas/graph.schema.json +371 -0
- package/spec/output-schemas/links-list.schema.json +186 -0
- package/spec/output-schemas/mcp-add-collection-result.schema.json +23 -0
- package/spec/output-schemas/mcp-capture-result.schema.json +152 -0
- package/spec/output-schemas/mcp-http-error.schema.json +30 -0
- package/spec/output-schemas/mcp-job-list.schema.json +58 -0
- package/spec/output-schemas/mcp-job-status.schema.json +224 -0
- package/spec/output-schemas/mcp-remove-result.schema.json +39 -0
- package/spec/output-schemas/mcp-sync-result.schema.json +41 -0
- package/spec/output-schemas/mcp-tag-result.schema.json +33 -0
- package/spec/output-schemas/models-list.schema.json +93 -0
- package/spec/output-schemas/multi-get.schema.json +103 -0
- package/spec/output-schemas/process-status.schema.json +119 -0
- package/spec/output-schemas/query-diagnose.schema.json +123 -0
- package/spec/output-schemas/resident-status.schema.json +154 -0
- package/spec/output-schemas/retrieval-trace-common.schema.json +492 -0
- package/spec/output-schemas/retrieval-trace-delete.schema.json +16 -0
- package/spec/output-schemas/retrieval-trace-export.schema.json +61 -0
- package/spec/output-schemas/retrieval-trace-filters.schema.json +139 -0
- package/spec/output-schemas/retrieval-trace-judgment.schema.json +15 -0
- package/spec/output-schemas/retrieval-trace-list.schema.json +18 -0
- package/spec/output-schemas/retrieval-trace-payloads.schema.json +178 -0
- package/spec/output-schemas/retrieval-trace-purge.schema.json +31 -0
- package/spec/output-schemas/retrieval-trace-qrels.schema.json +303 -0
- package/spec/output-schemas/retrieval-trace-replay.schema.json +286 -0
- package/spec/output-schemas/retrieval-trace-show.schema.json +69 -0
- package/spec/output-schemas/retrieval-trace-summary.schema.json +65 -0
- package/spec/output-schemas/search-result.schema.json +154 -0
- package/spec/output-schemas/search-results.schema.json +338 -0
- package/spec/output-schemas/similar.schema.json +84 -0
- package/spec/output-schemas/status.schema.json +676 -0
- package/spec/output-schemas/tags-list.schema.json +48 -0
- package/src/app/context-runtime-contract.ts +10 -5
- package/src/app/context-runtime-input.ts +29 -1
- package/src/app/context-runtime-types.ts +7 -0
- package/src/app/context-runtime.ts +20 -2
- package/src/app/context-surface.ts +4 -0
- package/src/app/verified-ask.ts +291 -0
- package/src/cli/commands/ask-format.ts +255 -0
- package/src/cli/commands/ask.ts +144 -183
- package/src/cli/commands/context-build.ts +56 -9
- package/src/cli/commands/get.ts +64 -3
- package/src/cli/commands/query.ts +62 -23
- package/src/cli/commands/replay.ts +140 -0
- package/src/cli/commands/search.ts +48 -3
- package/src/cli/commands/shared.ts +3 -1
- package/src/cli/commands/trace.ts +200 -0
- package/src/cli/commands/vsearch.ts +75 -53
- package/src/cli/program.ts +287 -1
- package/src/config/index.ts +9 -0
- package/src/config/retrieval-traces.ts +56 -0
- package/src/config/types.ts +4 -0
- package/src/core/context-budget.ts +6 -0
- package/src/core/context-capsule-retrieval-schema.ts +4 -0
- package/src/core/context-capsule-schema.ts +17 -0
- package/src/core/context-capsule-validation.ts +3 -2
- package/src/core/context-capsule.ts +18 -0
- package/src/core/context-compiler.ts +44 -25
- package/src/core/context-evidence.ts +6 -0
- package/src/core/retrieval-qrels.ts +405 -0
- package/src/core/retrieval-replay-candidate.ts +368 -0
- package/src/core/retrieval-replay-types.ts +109 -0
- package/src/core/retrieval-replay-validation.ts +89 -0
- package/src/core/retrieval-replay.ts +441 -0
- package/src/core/retrieval-trace-evidence-origin.ts +178 -0
- package/src/core/retrieval-trace-export.ts +113 -0
- package/src/core/retrieval-trace-filter-normalization.ts +27 -0
- package/src/core/retrieval-trace-filters.ts +19 -0
- package/src/core/retrieval-trace-management-helpers.ts +247 -0
- package/src/core/retrieval-trace-management-types.ts +132 -0
- package/src/core/retrieval-trace-management.ts +422 -0
- package/src/core/retrieval-trace-request.ts +141 -0
- package/src/core/retrieval-trace-session.ts +507 -0
- package/src/core/retrieval-trace.ts +472 -0
- package/src/llm/errors.ts +10 -1
- package/src/llm/httpGeneration.ts +11 -1
- package/src/llm/nodeLlamaCpp/generation.ts +54 -10
- package/src/llm/types.ts +6 -0
- package/src/mcp/tools/ask.ts +228 -0
- package/src/mcp/tools/context.ts +87 -15
- package/src/mcp/tools/get.ts +35 -1
- package/src/mcp/tools/index.ts +83 -0
- package/src/mcp/tools/query.ts +95 -64
- package/src/mcp/tools/search.ts +36 -13
- package/src/mcp/tools/trace.ts +143 -0
- package/src/mcp/tools/vsearch.ts +71 -38
- package/src/pipeline/answer.ts +167 -26
- package/src/pipeline/claim-verification-schema.ts +235 -0
- package/src/pipeline/claim-verification.ts +487 -0
- package/src/pipeline/claim-verifier.ts +474 -0
- package/src/pipeline/graph-retrieval.ts +15 -1
- package/src/pipeline/hybrid.ts +151 -43
- package/src/pipeline/search.ts +36 -3
- package/src/pipeline/trace-metadata.ts +47 -0
- package/src/pipeline/types.ts +68 -0
- package/src/pipeline/vsearch.ts +101 -38
- package/src/sdk/client.ts +415 -73
- package/src/sdk/documents.ts +48 -1
- package/src/sdk/index.ts +17 -0
- package/src/sdk/types.ts +28 -0
- package/src/serve/context-capsule.ts +67 -8
- package/src/serve/public/app.tsx +12 -1
- package/src/serve/public/components/AskVerificationPanel.tsx +189 -0
- package/src/serve/public/globals.built.css +1 -1
- package/src/serve/public/lib/workspace-tabs.ts +2 -0
- package/src/serve/public/pages/Ask.tsx +42 -4
- package/src/serve/public/pages/Dashboard.tsx +10 -0
- package/src/serve/public/pages/TraceHistory.tsx +478 -0
- package/src/serve/public/pages/trace-history-detail.tsx +224 -0
- package/src/serve/retrieval-trace.ts +28 -0
- package/src/serve/routes/api.ts +508 -68
- package/src/serve/routes/traces.ts +156 -0
- package/src/serve/server.ts +87 -2
- package/src/store/index.ts +31 -0
- package/src/store/migrations/014-retrieval-traces.ts +303 -0
- package/src/store/migrations/index.ts +2 -0
- package/src/store/retrieval-trace-codec.ts +384 -0
- package/src/store/sqlite/adapter.ts +153 -1
- package/src/store/sqlite/retrieval-trace-management-store.ts +341 -0
- package/src/store/sqlite/retrieval-trace-retention.ts +349 -0
- package/src/store/sqlite/retrieval-trace-rows.ts +267 -0
- package/src/store/sqlite/retrieval-trace-store.ts +515 -0
- package/src/store/types.ts +297 -0
- package/src/store/vector/sqlite-vec.ts +76 -1
- package/src/store/vector/types.ts +1 -1
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
|
+
"$id": "gno://schemas/tags-list@1.0",
|
|
4
|
+
"title": "GNO Tags List",
|
|
5
|
+
"description": "List of tags with document counts",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"required": ["tags", "meta"],
|
|
8
|
+
"properties": {
|
|
9
|
+
"tags": {
|
|
10
|
+
"type": "array",
|
|
11
|
+
"description": "List of tags with counts",
|
|
12
|
+
"items": {
|
|
13
|
+
"type": "object",
|
|
14
|
+
"required": ["tag", "count"],
|
|
15
|
+
"properties": {
|
|
16
|
+
"tag": {
|
|
17
|
+
"type": "string",
|
|
18
|
+
"description": "Tag name (lowercase, alphanumeric with hyphens/dots/slashes)"
|
|
19
|
+
},
|
|
20
|
+
"count": {
|
|
21
|
+
"type": "integer",
|
|
22
|
+
"description": "Number of documents with this tag",
|
|
23
|
+
"minimum": 1
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
},
|
|
28
|
+
"meta": {
|
|
29
|
+
"type": "object",
|
|
30
|
+
"required": ["totalTags"],
|
|
31
|
+
"properties": {
|
|
32
|
+
"totalTags": {
|
|
33
|
+
"type": "integer",
|
|
34
|
+
"description": "Total number of unique tags",
|
|
35
|
+
"minimum": 0
|
|
36
|
+
},
|
|
37
|
+
"collection": {
|
|
38
|
+
"type": "string",
|
|
39
|
+
"description": "Collection filter applied"
|
|
40
|
+
},
|
|
41
|
+
"prefix": {
|
|
42
|
+
"type": "string",
|
|
43
|
+
"description": "Prefix filter applied"
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
@@ -24,8 +24,9 @@ import { resolveModelUri } from "../llm/registry";
|
|
|
24
24
|
const fingerprint = (value: unknown): string =>
|
|
25
25
|
sha256Text(canonicalVerifierJson(value));
|
|
26
26
|
|
|
27
|
-
const
|
|
28
|
-
|
|
27
|
+
export const contextRuntimeConfigFingerprint = (
|
|
28
|
+
deps: Pick<ContextCapsuleRuntimeDeps, "config">
|
|
29
|
+
): string => fingerprint(deps.config);
|
|
29
30
|
|
|
30
31
|
const configuredContextFingerprint = (
|
|
31
32
|
deps: ContextCapsuleRuntimeDeps
|
|
@@ -75,7 +76,7 @@ const capsuleCapabilityStates = (
|
|
|
75
76
|
["embedding_unavailable"]
|
|
76
77
|
),
|
|
77
78
|
reranking: capabilityState(
|
|
78
|
-
input.depthPolicy !== "fast",
|
|
79
|
+
input.depthPolicy !== "fast" && !input.noRerank,
|
|
79
80
|
draft.retrieval.reranked,
|
|
80
81
|
["reranking_unavailable"]
|
|
81
82
|
),
|
|
@@ -191,10 +192,14 @@ export const projectContextCapsule = (
|
|
|
191
192
|
request: {
|
|
192
193
|
author: input.author,
|
|
193
194
|
lang: input.lang,
|
|
195
|
+
intent: input.intent,
|
|
196
|
+
exclude: input.exclude,
|
|
197
|
+
minScore: input.minScore,
|
|
194
198
|
queryModes: input.queryModes,
|
|
195
199
|
limit: input.limit,
|
|
196
200
|
candidateLimit: input.candidateLimit,
|
|
197
201
|
graphRequested: input.graph,
|
|
202
|
+
rerankRequested: input.depthPolicy !== "fast" && !input.noRerank,
|
|
198
203
|
},
|
|
199
204
|
capabilityStates,
|
|
200
205
|
indexSnapshot: {
|
|
@@ -221,7 +226,7 @@ export const projectContextCapsule = (
|
|
|
221
226
|
: null,
|
|
222
227
|
},
|
|
223
228
|
fingerprints: {
|
|
224
|
-
config:
|
|
229
|
+
config: contextRuntimeConfigFingerprint(deps),
|
|
225
230
|
retrieval: retrievalFingerprint(base, snapshots.contextFingerprint),
|
|
226
231
|
embeddingModel: capabilities.semanticSearch
|
|
227
232
|
? sha256Text(deps.embedPort?.modelUri ?? "")
|
|
@@ -292,7 +297,7 @@ export const currentContextFingerprints = (
|
|
|
292
297
|
capsule: ContextCapsuleV1,
|
|
293
298
|
deps: ContextCapsuleRuntimeDeps
|
|
294
299
|
) => ({
|
|
295
|
-
config:
|
|
300
|
+
config: contextRuntimeConfigFingerprint(deps),
|
|
296
301
|
retrieval: retrievalFingerprint(capsule, configuredContextFingerprint(deps)),
|
|
297
302
|
embeddingModel: capsule.capabilities.semanticSearch
|
|
298
303
|
? sha256Text(
|
|
@@ -315,16 +315,40 @@ export const normalizeContextBuildInput = (
|
|
|
315
315
|
"Context graph flag must be boolean"
|
|
316
316
|
);
|
|
317
317
|
}
|
|
318
|
+
if (input.noRerank !== undefined && typeof input.noRerank !== "boolean") {
|
|
319
|
+
throw new ContextRuntimeError(
|
|
320
|
+
"invalid_filter",
|
|
321
|
+
"Context noRerank flag must be boolean"
|
|
322
|
+
);
|
|
323
|
+
}
|
|
324
|
+
if (
|
|
325
|
+
input.minScore !== undefined &&
|
|
326
|
+
(typeof input.minScore !== "number" ||
|
|
327
|
+
!Number.isFinite(input.minScore) ||
|
|
328
|
+
input.minScore < 0 ||
|
|
329
|
+
input.minScore > 1)
|
|
330
|
+
) {
|
|
331
|
+
throw new ContextRuntimeError(
|
|
332
|
+
"invalid_filter",
|
|
333
|
+
"Context minimum score must be between 0 and 1"
|
|
334
|
+
);
|
|
335
|
+
}
|
|
318
336
|
const author =
|
|
319
337
|
typeof input.author === "string"
|
|
320
338
|
? input.author.normalize("NFC").trim()
|
|
321
339
|
: null;
|
|
322
340
|
const lang =
|
|
323
341
|
typeof input.lang === "string" ? input.lang.normalize("NFC").trim() : null;
|
|
342
|
+
const intent =
|
|
343
|
+
typeof input.intent === "string"
|
|
344
|
+
? input.intent.normalize("NFC").trim()
|
|
345
|
+
: null;
|
|
324
346
|
if (
|
|
325
347
|
(input.author !== undefined &&
|
|
326
348
|
(!author || author.length > MAX_FILTER_LENGTH)) ||
|
|
327
|
-
(input.lang !== undefined && (!lang || !isValidLanguageHint(lang)))
|
|
349
|
+
(input.lang !== undefined && (!lang || !isValidLanguageHint(lang))) ||
|
|
350
|
+
(input.intent !== undefined &&
|
|
351
|
+
(!intent || intent.length > MAX_TEXT_LENGTH || intent.includes("\r")))
|
|
328
352
|
) {
|
|
329
353
|
throw new ContextRuntimeError(
|
|
330
354
|
"invalid_filter",
|
|
@@ -344,9 +368,13 @@ export const normalizeContextBuildInput = (
|
|
|
344
368
|
categories: canonicalFilters(input.categories, "categories"),
|
|
345
369
|
author,
|
|
346
370
|
lang,
|
|
371
|
+
intent,
|
|
372
|
+
exclude: canonicalFilters(input.exclude, "exclude"),
|
|
373
|
+
minScore: input.minScore ?? null,
|
|
347
374
|
since: temporalRange.since,
|
|
348
375
|
until: temporalRange.until,
|
|
349
376
|
graph: input.graph ?? false,
|
|
377
|
+
noRerank: input.noRerank ?? false,
|
|
350
378
|
limit,
|
|
351
379
|
candidateLimit,
|
|
352
380
|
budgetTokens,
|
|
@@ -2,6 +2,7 @@ import type { Config } from "../config/types";
|
|
|
2
2
|
import type { ContextCapsuleV1 } from "../core/context-capsule";
|
|
3
3
|
import type { ContextEvidenceCompilerDeps } from "../core/context-evidence";
|
|
4
4
|
import type { ContextVerifierDeps } from "../core/context-verifier";
|
|
5
|
+
import type { RetrievalTraceSession } from "../core/retrieval-trace-session";
|
|
5
6
|
import type { EmbeddingPort, RerankPort } from "../llm/types";
|
|
6
7
|
import type { QueryModeInput } from "../pipeline/types";
|
|
7
8
|
import type { StorePort } from "../store/types";
|
|
@@ -21,9 +22,13 @@ export interface ContextCapsuleBuildInput {
|
|
|
21
22
|
categories?: string[];
|
|
22
23
|
author?: string;
|
|
23
24
|
lang?: string;
|
|
25
|
+
intent?: string;
|
|
26
|
+
exclude?: string[];
|
|
27
|
+
minScore?: number;
|
|
24
28
|
since?: string;
|
|
25
29
|
until?: string;
|
|
26
30
|
graph?: boolean;
|
|
31
|
+
noRerank?: boolean;
|
|
27
32
|
limit?: number;
|
|
28
33
|
candidateLimit?: number;
|
|
29
34
|
budgetTokens: number;
|
|
@@ -45,6 +50,8 @@ export interface ContextCapsuleRuntimeDeps {
|
|
|
45
50
|
countTokens?: (accountingJson: string) => number;
|
|
46
51
|
tokenizerFingerprint?: string | null;
|
|
47
52
|
resolveCurrentRanks?: ContextVerifierDeps["resolveCurrentRanks"];
|
|
53
|
+
/** Optional non-canonical receipt session owned by the calling surface. */
|
|
54
|
+
traceSession?: RetrievalTraceSession;
|
|
48
55
|
}
|
|
49
56
|
|
|
50
57
|
export type ContextRuntimeErrorCode =
|
|
@@ -48,7 +48,7 @@ export const buildContextCapsule = async (
|
|
|
48
48
|
now,
|
|
49
49
|
deps.config.collections.map((collection) => collection.name)
|
|
50
50
|
);
|
|
51
|
-
const noRerank = normalized.depthPolicy === "fast";
|
|
51
|
+
const noRerank = normalized.depthPolicy === "fast" || normalized.noRerank;
|
|
52
52
|
const plan = await compileContextEvidence<ContextCapsuleV1>(
|
|
53
53
|
{
|
|
54
54
|
goal: normalized.goal,
|
|
@@ -62,9 +62,13 @@ export const buildContextCapsule = async (
|
|
|
62
62
|
categories: normalized.categories,
|
|
63
63
|
author: normalized.author ?? undefined,
|
|
64
64
|
lang: normalized.lang ?? undefined,
|
|
65
|
+
intent: normalized.intent ?? undefined,
|
|
66
|
+
exclude: normalized.exclude,
|
|
67
|
+
minScore: normalized.minScore ?? undefined,
|
|
65
68
|
since: normalized.since,
|
|
66
69
|
until: normalized.until,
|
|
67
70
|
graph: normalized.graph,
|
|
71
|
+
noRerank: normalized.noRerank,
|
|
68
72
|
limit: normalized.limit,
|
|
69
73
|
candidateLimit: normalized.candidateLimit,
|
|
70
74
|
temporalNow: now,
|
|
@@ -89,7 +93,11 @@ export const buildContextCapsule = async (
|
|
|
89
93
|
rerankPort: requestNoRerank ? null : (deps.rerankPort ?? null),
|
|
90
94
|
},
|
|
91
95
|
request.query,
|
|
92
|
-
{
|
|
96
|
+
{
|
|
97
|
+
...request,
|
|
98
|
+
noRerank: requestNoRerank,
|
|
99
|
+
traceSession: deps.traceSession,
|
|
100
|
+
}
|
|
93
101
|
);
|
|
94
102
|
if (!result.ok) {
|
|
95
103
|
throw new ContextRuntimeError(
|
|
@@ -115,6 +123,16 @@ export const buildContextCapsule = async (
|
|
|
115
123
|
: "No in-scope evidence was available for the Context Capsule"
|
|
116
124
|
);
|
|
117
125
|
}
|
|
126
|
+
const traceResult = await deps.traceSession?.recordContext(
|
|
127
|
+
plan.projection.value
|
|
128
|
+
);
|
|
129
|
+
if (traceResult && !traceResult.ok) {
|
|
130
|
+
throw new ContextRuntimeError(
|
|
131
|
+
"retrieval_failed",
|
|
132
|
+
`Trace recording failed: ${traceResult.error.message}`,
|
|
133
|
+
traceResult.error.cause
|
|
134
|
+
);
|
|
135
|
+
}
|
|
118
136
|
return plan.projection.value;
|
|
119
137
|
};
|
|
120
138
|
|
|
@@ -30,9 +30,13 @@ export const contextBuildSurfaceSchema = z
|
|
|
30
30
|
categories: stringList.optional(),
|
|
31
31
|
author: z.string().optional(),
|
|
32
32
|
lang: z.string().optional(),
|
|
33
|
+
intent: z.string().optional(),
|
|
34
|
+
exclude: stringList.optional(),
|
|
35
|
+
minScore: z.number().min(0).max(1).optional(),
|
|
33
36
|
since: z.string().optional(),
|
|
34
37
|
until: z.string().optional(),
|
|
35
38
|
graph: z.boolean().optional(),
|
|
39
|
+
noRerank: z.boolean().optional(),
|
|
36
40
|
limit: positiveInteger.optional(),
|
|
37
41
|
candidateLimit: positiveInteger.optional(),
|
|
38
42
|
budgetTokens: positiveInteger,
|
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
/** Shared closed-evidence Ask synthesis boundary. */
|
|
2
|
+
|
|
3
|
+
import type {
|
|
4
|
+
ContextCapsuleV1,
|
|
5
|
+
ContextCapsuleVerification,
|
|
6
|
+
} from "../core/context-capsule";
|
|
7
|
+
import type { RetrievalTraceSession } from "../core/retrieval-trace-session";
|
|
8
|
+
import type { GenerationPort } from "../llm/types";
|
|
9
|
+
import type { AskOptions, AskResult, Citation } from "../pipeline/types";
|
|
10
|
+
import type { ContextCapsuleRuntimeDeps } from "./context-runtime";
|
|
11
|
+
|
|
12
|
+
import { buildAnswerPrompt } from "../pipeline/answer-prompt";
|
|
13
|
+
import { CLAIM_ABSTENTION_TEXT } from "../pipeline/claim-verification";
|
|
14
|
+
import { verifyClaimsSemantically } from "../pipeline/claim-verifier";
|
|
15
|
+
import { attachCitationTraceMetadata } from "../pipeline/trace-metadata";
|
|
16
|
+
import { CITATION_TRACE_METADATA } from "../pipeline/types";
|
|
17
|
+
import {
|
|
18
|
+
buildContextCapsule,
|
|
19
|
+
verifyContextCapsuleRuntime,
|
|
20
|
+
} from "./context-runtime";
|
|
21
|
+
import { contextRuntimeConfigFingerprint } from "./context-runtime-contract";
|
|
22
|
+
|
|
23
|
+
const DEFAULT_CONTEXT_BUDGET_TOKENS = 12_000;
|
|
24
|
+
const DEFAULT_MAX_ANSWER_TOKENS = 512;
|
|
25
|
+
const DEFAULT_VERIFIED_ASK_LIMIT = 5;
|
|
26
|
+
const NUMERIC_CITATION_PATTERN = /\[(\d+)\]/g;
|
|
27
|
+
|
|
28
|
+
export interface VerifiedAskDeps extends ContextCapsuleRuntimeDeps {
|
|
29
|
+
genPort: GenerationPort;
|
|
30
|
+
traceSession?: RetrievalTraceSession;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const generationSources = (capsule: ContextCapsuleV1) => {
|
|
34
|
+
const guidanceById = new Map(
|
|
35
|
+
capsule.guidance.configuredContexts.map((item) => [
|
|
36
|
+
item.contextId,
|
|
37
|
+
item.text,
|
|
38
|
+
])
|
|
39
|
+
);
|
|
40
|
+
return capsule.evidence.map((evidence, index) => ({
|
|
41
|
+
index: index + 1,
|
|
42
|
+
docid: evidence.docid,
|
|
43
|
+
uri: evidence.uri,
|
|
44
|
+
content: evidence.text,
|
|
45
|
+
guidance: evidence.contextIds
|
|
46
|
+
.flatMap((contextId) => {
|
|
47
|
+
const text = guidanceById.get(contextId);
|
|
48
|
+
return text ? [text] : [];
|
|
49
|
+
})
|
|
50
|
+
.join("\n"),
|
|
51
|
+
}));
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
/** Map model-facing numeric citations to immutable Capsule evidence IDs. */
|
|
55
|
+
export const mapAnswerCitationsToEvidence = (
|
|
56
|
+
answer: string,
|
|
57
|
+
capsule: ContextCapsuleV1
|
|
58
|
+
): string =>
|
|
59
|
+
answer
|
|
60
|
+
.replace(NUMERIC_CITATION_PATTERN, (_marker, rawIndex: string) => {
|
|
61
|
+
const evidence = capsule.evidence[Number(rawIndex) - 1];
|
|
62
|
+
return evidence ? `[evidence:${evidence.evidenceId}]` : "";
|
|
63
|
+
})
|
|
64
|
+
.replace(/ {2,}/g, " ")
|
|
65
|
+
.trim();
|
|
66
|
+
|
|
67
|
+
const retainedCitations = (
|
|
68
|
+
capsule: ContextCapsuleV1,
|
|
69
|
+
evidenceIds: ReadonlySet<string>
|
|
70
|
+
): Citation[] =>
|
|
71
|
+
capsule.evidence.flatMap((evidence) => {
|
|
72
|
+
if (!evidenceIds.has(evidence.evidenceId)) return [];
|
|
73
|
+
return [
|
|
74
|
+
attachCitationTraceMetadata(
|
|
75
|
+
{
|
|
76
|
+
evidenceId: evidence.evidenceId,
|
|
77
|
+
docid: evidence.docid,
|
|
78
|
+
uri: evidence.uri,
|
|
79
|
+
startLine: evidence.startLine,
|
|
80
|
+
endLine: evidence.endLine,
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
sourceHash: evidence.sourceHash,
|
|
84
|
+
mirrorHash: evidence.mirrorHash,
|
|
85
|
+
passageHash: evidence.passageHash,
|
|
86
|
+
rank: evidence.selectionRank,
|
|
87
|
+
plannerRank: evidence.retrievalRank,
|
|
88
|
+
...(evidence.retrievalSources === undefined
|
|
89
|
+
? {}
|
|
90
|
+
: { sources: evidence.retrievalSources }),
|
|
91
|
+
...(evidence.graphExpanded === undefined
|
|
92
|
+
? {}
|
|
93
|
+
: { graphExpanded: evidence.graphExpanded }),
|
|
94
|
+
}
|
|
95
|
+
),
|
|
96
|
+
];
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
const recordRetainedCitations = async (
|
|
100
|
+
traceSession: RetrievalTraceSession | undefined,
|
|
101
|
+
citations: readonly Citation[]
|
|
102
|
+
): Promise<void> => {
|
|
103
|
+
if (!traceSession || citations.length === 0) return;
|
|
104
|
+
const evidence = citations.flatMap((citation) => {
|
|
105
|
+
const metadata = citation[CITATION_TRACE_METADATA];
|
|
106
|
+
if (
|
|
107
|
+
!metadata ||
|
|
108
|
+
citation.startLine === undefined ||
|
|
109
|
+
citation.endLine === undefined
|
|
110
|
+
) {
|
|
111
|
+
return [];
|
|
112
|
+
}
|
|
113
|
+
return [
|
|
114
|
+
{
|
|
115
|
+
docid: citation.docid,
|
|
116
|
+
uri: citation.uri,
|
|
117
|
+
sourceHash: metadata.sourceHash,
|
|
118
|
+
mirrorHash: metadata.mirrorHash,
|
|
119
|
+
passageHash: metadata.passageHash,
|
|
120
|
+
startLine: citation.startLine,
|
|
121
|
+
endLine: citation.endLine,
|
|
122
|
+
rank: metadata.rank,
|
|
123
|
+
...(metadata.plannerRank === undefined
|
|
124
|
+
? {}
|
|
125
|
+
: { plannerRank: metadata.plannerRank }),
|
|
126
|
+
...(metadata.sources === undefined
|
|
127
|
+
? {}
|
|
128
|
+
: { sources: metadata.sources }),
|
|
129
|
+
...(metadata.graphExpanded === undefined
|
|
130
|
+
? {}
|
|
131
|
+
: { graphExpanded: metadata.graphExpanded }),
|
|
132
|
+
},
|
|
133
|
+
];
|
|
134
|
+
});
|
|
135
|
+
if (evidence.length === 0) return;
|
|
136
|
+
const recorded = await traceSession.recordEvidence("cite", evidence);
|
|
137
|
+
if (!recorded.ok) {
|
|
138
|
+
throw new Error(`Trace recording failed: ${recorded.error.message}`);
|
|
139
|
+
}
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
const citationEvidenceIds = (
|
|
143
|
+
verification: Awaited<ReturnType<typeof verifyClaimsSemantically>>,
|
|
144
|
+
statuses: ReadonlySet<"supported" | "contradicted">
|
|
145
|
+
): Set<string> =>
|
|
146
|
+
new Set(
|
|
147
|
+
verification.verification.claims.flatMap((claim) =>
|
|
148
|
+
statuses.has(claim.status as "supported" | "contradicted")
|
|
149
|
+
? claim.evidence.map((evidence) => evidence.evidenceId)
|
|
150
|
+
: []
|
|
151
|
+
)
|
|
152
|
+
);
|
|
153
|
+
|
|
154
|
+
const recordCapability = async (
|
|
155
|
+
traceSession: RetrievalTraceSession | undefined,
|
|
156
|
+
capability: string,
|
|
157
|
+
status: "attempted" | "used" | "unavailable" | "failed",
|
|
158
|
+
reasonCode?: string
|
|
159
|
+
): Promise<void> => {
|
|
160
|
+
const recorded = await traceSession?.recordCapability(
|
|
161
|
+
capability,
|
|
162
|
+
status,
|
|
163
|
+
reasonCode
|
|
164
|
+
);
|
|
165
|
+
if (recorded && !recorded.ok) {
|
|
166
|
+
throw new Error(`Trace recording failed: ${recorded.error.message}`);
|
|
167
|
+
}
|
|
168
|
+
};
|
|
169
|
+
|
|
170
|
+
export const buildVerifiedAsk = async (
|
|
171
|
+
query: string,
|
|
172
|
+
options: AskOptions,
|
|
173
|
+
deps: VerifiedAskDeps
|
|
174
|
+
): Promise<AskResult> => {
|
|
175
|
+
const collection = options.collection;
|
|
176
|
+
const capsule = await buildContextCapsule(
|
|
177
|
+
{
|
|
178
|
+
goal: query,
|
|
179
|
+
query,
|
|
180
|
+
indexName: deps.indexName,
|
|
181
|
+
collections: collection ? [collection] : [],
|
|
182
|
+
queryModes: options.queryModes,
|
|
183
|
+
tagsAll: options.tagsAll,
|
|
184
|
+
tagsAny: options.tagsAny,
|
|
185
|
+
categories: options.categories,
|
|
186
|
+
author: options.author,
|
|
187
|
+
lang: options.lang,
|
|
188
|
+
intent: options.intent,
|
|
189
|
+
exclude: options.exclude,
|
|
190
|
+
minScore: options.minScore,
|
|
191
|
+
since: options.since,
|
|
192
|
+
until: options.until,
|
|
193
|
+
graph: Boolean(options.graph && !options.noGraph),
|
|
194
|
+
noRerank: options.noRerank,
|
|
195
|
+
limit: options.limit ?? DEFAULT_VERIFIED_ASK_LIMIT,
|
|
196
|
+
candidateLimit: options.candidateLimit,
|
|
197
|
+
budgetTokens:
|
|
198
|
+
options.contextBudgetTokens ?? DEFAULT_CONTEXT_BUDGET_TOKENS,
|
|
199
|
+
budgetBytes: options.contextBudgetBytes,
|
|
200
|
+
depthPolicy: "balanced",
|
|
201
|
+
},
|
|
202
|
+
deps
|
|
203
|
+
);
|
|
204
|
+
const freshness = await verifyContextCapsuleRuntime(capsule, deps);
|
|
205
|
+
return synthesizeVerifiedAsk(query, options, capsule, freshness, deps);
|
|
206
|
+
};
|
|
207
|
+
|
|
208
|
+
export const synthesizeVerifiedAsk = async (
|
|
209
|
+
query: string,
|
|
210
|
+
options: AskOptions,
|
|
211
|
+
capsule: ContextCapsuleV1,
|
|
212
|
+
freshness: ContextCapsuleVerification,
|
|
213
|
+
deps: Pick<
|
|
214
|
+
VerifiedAskDeps,
|
|
215
|
+
"config" | "genPort" | "indexName" | "traceSession"
|
|
216
|
+
>
|
|
217
|
+
): Promise<AskResult> => {
|
|
218
|
+
await recordCapability(deps.traceSession, "answer_generation", "attempted");
|
|
219
|
+
const generated = await deps.genPort.generate(
|
|
220
|
+
buildAnswerPrompt(query, generationSources(capsule)),
|
|
221
|
+
{
|
|
222
|
+
temperature: 0,
|
|
223
|
+
maxTokens: options.maxAnswerTokens ?? DEFAULT_MAX_ANSWER_TOKENS,
|
|
224
|
+
}
|
|
225
|
+
);
|
|
226
|
+
await recordCapability(
|
|
227
|
+
deps.traceSession,
|
|
228
|
+
"answer_generation",
|
|
229
|
+
generated.ok ? "used" : "failed",
|
|
230
|
+
generated.ok ? undefined : "generation_failed"
|
|
231
|
+
);
|
|
232
|
+
const draftAnswer = generated.ok
|
|
233
|
+
? mapAnswerCitationsToEvidence(generated.value, capsule)
|
|
234
|
+
: CLAIM_ABSTENTION_TEXT;
|
|
235
|
+
const verification = await verifyClaimsSemantically({
|
|
236
|
+
answer: draftAnswer,
|
|
237
|
+
capsule,
|
|
238
|
+
freshness,
|
|
239
|
+
genPort: generated.ok ? deps.genPort : null,
|
|
240
|
+
configFingerprint: contextRuntimeConfigFingerprint(deps),
|
|
241
|
+
});
|
|
242
|
+
await recordCapability(
|
|
243
|
+
deps.traceSession,
|
|
244
|
+
"claim_verification",
|
|
245
|
+
verification.semanticVerification.status === "completed"
|
|
246
|
+
? "used"
|
|
247
|
+
: verification.semanticVerification.status,
|
|
248
|
+
verification.semanticVerification.reason
|
|
249
|
+
);
|
|
250
|
+
const citations = verification.verification.abstained
|
|
251
|
+
? []
|
|
252
|
+
: retainedCitations(
|
|
253
|
+
capsule,
|
|
254
|
+
citationEvidenceIds(verification, new Set(["supported"]))
|
|
255
|
+
);
|
|
256
|
+
const traceCitations = retainedCitations(
|
|
257
|
+
capsule,
|
|
258
|
+
citationEvidenceIds(verification, new Set(["supported", "contradicted"]))
|
|
259
|
+
);
|
|
260
|
+
await recordRetainedCitations(deps.traceSession, traceCitations);
|
|
261
|
+
return {
|
|
262
|
+
query,
|
|
263
|
+
mode: capsule.capabilities.semanticSearch ? "hybrid" : "bm25_only",
|
|
264
|
+
queryLanguage: capsule.retrieval.request.lang ?? "und",
|
|
265
|
+
answer: verification.verification.abstained
|
|
266
|
+
? (verification.verification.abstentionText ?? CLAIM_ABSTENTION_TEXT)
|
|
267
|
+
: draftAnswer,
|
|
268
|
+
citations,
|
|
269
|
+
results: [],
|
|
270
|
+
meta: {
|
|
271
|
+
expanded: false,
|
|
272
|
+
reranked: capsule.capabilities.reranking,
|
|
273
|
+
vectorsUsed: capsule.capabilities.semanticSearch,
|
|
274
|
+
intent: capsule.retrieval.request.intent ?? undefined,
|
|
275
|
+
candidateLimit: capsule.retrieval.request.candidateLimit,
|
|
276
|
+
exclude: capsule.retrieval.request.exclude,
|
|
277
|
+
answerGenerated: generated.ok,
|
|
278
|
+
totalResults: capsule.evidence.length,
|
|
279
|
+
verificationRequested: true,
|
|
280
|
+
abstained: verification.verification.abstained,
|
|
281
|
+
},
|
|
282
|
+
verification: {
|
|
283
|
+
schemaVersion: "1.0",
|
|
284
|
+
mode: "closed_capsule",
|
|
285
|
+
capsule,
|
|
286
|
+
freshness,
|
|
287
|
+
claims: verification.verification,
|
|
288
|
+
semantic: verification.semanticVerification,
|
|
289
|
+
},
|
|
290
|
+
};
|
|
291
|
+
};
|