@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,113 @@
|
|
|
1
|
+
/** Aggregate retrieval trace export with manifest-bound verification. */
|
|
2
|
+
|
|
3
|
+
import type {
|
|
4
|
+
RetrievalTraceBundle,
|
|
5
|
+
RetrievalTraceExportFormat,
|
|
6
|
+
StorePort,
|
|
7
|
+
StoreResult,
|
|
8
|
+
} from "../store/types";
|
|
9
|
+
import type {
|
|
10
|
+
ExportRetrievalTracesInput,
|
|
11
|
+
ExportRetrievalTracesResult,
|
|
12
|
+
RetrievalTraceArtifact,
|
|
13
|
+
} from "./retrieval-trace-management-types";
|
|
14
|
+
|
|
15
|
+
import { hashTraceCanonical } from "../store/retrieval-trace-codec";
|
|
16
|
+
import { err, ok } from "../store/types";
|
|
17
|
+
import { buildRetrievalQrelsArtifact } from "./retrieval-qrels";
|
|
18
|
+
|
|
19
|
+
const validateTraceIds = (traceIds: unknown): StoreResult<string[]> => {
|
|
20
|
+
if (
|
|
21
|
+
!Array.isArray(traceIds) ||
|
|
22
|
+
traceIds.some(
|
|
23
|
+
(traceId) => typeof traceId !== "string" || traceId.length < 1
|
|
24
|
+
)
|
|
25
|
+
) {
|
|
26
|
+
return err("INVALID_INPUT", "Export trace IDs must be a string array");
|
|
27
|
+
}
|
|
28
|
+
const normalized = [...new Set(traceIds)].sort((left, right) =>
|
|
29
|
+
left.localeCompare(right)
|
|
30
|
+
);
|
|
31
|
+
return normalized.length < 1 || normalized.length > 10_000
|
|
32
|
+
? err("INVALID_INPUT", "Export requires from 1 to 10000 trace IDs")
|
|
33
|
+
: ok(normalized);
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
const agenticArtifact = (
|
|
37
|
+
bundles: RetrievalTraceBundle[]
|
|
38
|
+
): RetrievalTraceArtifact => ({
|
|
39
|
+
schemaVersion: "1.0",
|
|
40
|
+
format: "agentic-receipt",
|
|
41
|
+
traces: bundles.map(({ exports: _exports, ...trace }) => trace),
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
const buildArtifact = (
|
|
45
|
+
format: "agentic-receipt" | "qrels",
|
|
46
|
+
bundles: RetrievalTraceBundle[]
|
|
47
|
+
): StoreResult<RetrievalTraceArtifact> => {
|
|
48
|
+
if (format === "agentic-receipt") return ok(agenticArtifact(bundles));
|
|
49
|
+
return buildRetrievalQrelsArtifact(bundles);
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
export const exportRetrievalTraces = async <
|
|
53
|
+
Format extends RetrievalTraceExportFormat,
|
|
54
|
+
>(
|
|
55
|
+
store: StorePort,
|
|
56
|
+
clock: () => number,
|
|
57
|
+
input: ExportRetrievalTracesInput<Format>
|
|
58
|
+
): Promise<StoreResult<ExportRetrievalTracesResult<Format>>> => {
|
|
59
|
+
const format = input.format ?? "agentic-receipt";
|
|
60
|
+
if (!["agentic-receipt", "qrels"].includes(format)) {
|
|
61
|
+
return err("INVALID_INPUT", "Invalid retrieval trace export format");
|
|
62
|
+
}
|
|
63
|
+
const traceIds = validateTraceIds(input.traceIds);
|
|
64
|
+
if (!traceIds.ok) return traceIds;
|
|
65
|
+
const bundles: RetrievalTraceBundle[] = [];
|
|
66
|
+
for (const traceId of traceIds.value) {
|
|
67
|
+
const stored = await store.getRetrievalTrace(traceId);
|
|
68
|
+
if (!stored.ok) return stored;
|
|
69
|
+
if (!stored.value) return err("NOT_FOUND", "Retrieval trace not found");
|
|
70
|
+
if (stored.value.trace.status === "open") {
|
|
71
|
+
return err(
|
|
72
|
+
"CONSTRAINT_VIOLATION",
|
|
73
|
+
"Open retrieval traces cannot be exported"
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
bundles.push(stored.value);
|
|
77
|
+
}
|
|
78
|
+
const built = buildArtifact(format, bundles);
|
|
79
|
+
if (!built.ok) return built;
|
|
80
|
+
const artifactHash = hashTraceCanonical(built.value);
|
|
81
|
+
const exportId = `trace-export-${artifactHash.slice(0, 40)}`;
|
|
82
|
+
const appended = await store.appendRetrievalTraceExportManifest({
|
|
83
|
+
exportId,
|
|
84
|
+
traceIds: traceIds.value,
|
|
85
|
+
format,
|
|
86
|
+
artifactHash,
|
|
87
|
+
createdAtMs: clock(),
|
|
88
|
+
});
|
|
89
|
+
if (!appended.ok) return appended;
|
|
90
|
+
const complete = await store.getRetrievalTraceExportBundle(exportId);
|
|
91
|
+
if (!complete.ok) return complete;
|
|
92
|
+
if (!complete.value) {
|
|
93
|
+
return err("QUERY_FAILED", "Stored retrieval trace export is unavailable");
|
|
94
|
+
}
|
|
95
|
+
const reconstructed = buildArtifact(format, complete.value.traces);
|
|
96
|
+
if (!reconstructed.ok) return reconstructed;
|
|
97
|
+
if (
|
|
98
|
+
complete.value.manifest.traceIds.join("\0") !== traceIds.value.join("\0") ||
|
|
99
|
+
hashTraceCanonical(reconstructed.value) !==
|
|
100
|
+
complete.value.manifest.artifactHash
|
|
101
|
+
) {
|
|
102
|
+
return err(
|
|
103
|
+
"CONSTRAINT_VIOLATION",
|
|
104
|
+
"manifest_hash_mismatch: stored retrieval trace export changed"
|
|
105
|
+
);
|
|
106
|
+
}
|
|
107
|
+
return ok({
|
|
108
|
+
schemaVersion: "1.0",
|
|
109
|
+
result: appended.value,
|
|
110
|
+
manifest: complete.value.manifest,
|
|
111
|
+
artifact: reconstructed.value,
|
|
112
|
+
} as ExportRetrievalTracesResult<Format>);
|
|
113
|
+
};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/** Deterministic set-like retrieval-filter normalization before persistence. */
|
|
2
|
+
|
|
3
|
+
const SET_ARRAY_KEYS = [
|
|
4
|
+
"categories",
|
|
5
|
+
"collections",
|
|
6
|
+
"exclude",
|
|
7
|
+
"tagsAll",
|
|
8
|
+
"tagsAny",
|
|
9
|
+
] as const;
|
|
10
|
+
|
|
11
|
+
const compareCodeUnits = (left: string, right: string): number =>
|
|
12
|
+
left < right ? -1 : left > right ? 1 : 0;
|
|
13
|
+
|
|
14
|
+
export const canonicalizeRetrievalTraceFilters = <
|
|
15
|
+
Filters extends Record<string, unknown>,
|
|
16
|
+
>(
|
|
17
|
+
filters: Filters
|
|
18
|
+
): Filters => {
|
|
19
|
+
const normalized: Record<string, unknown> = { ...filters };
|
|
20
|
+
for (const key of SET_ARRAY_KEYS) {
|
|
21
|
+
const values = filters[key];
|
|
22
|
+
if (Array.isArray(values)) {
|
|
23
|
+
normalized[key] = [...new Set(values)].sort(compareCodeUnits);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
return normalized as Filters;
|
|
27
|
+
};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/** Canonical replay-filter parsing shared by qrels export and replay. */
|
|
2
|
+
|
|
3
|
+
import type { StoreResult } from "../store/types";
|
|
4
|
+
|
|
5
|
+
import { err, ok } from "../store/types";
|
|
6
|
+
import { traceFiltersSchema } from "./retrieval-trace";
|
|
7
|
+
import { canonicalizeRetrievalTraceFilters } from "./retrieval-trace-filter-normalization";
|
|
8
|
+
|
|
9
|
+
export type RetrievalTraceFilters = Record<string, unknown>;
|
|
10
|
+
|
|
11
|
+
export const parseRetrievalTraceFilters = (
|
|
12
|
+
value: unknown
|
|
13
|
+
): StoreResult<RetrievalTraceFilters> => {
|
|
14
|
+
const parsed = traceFiltersSchema.safeParse(value);
|
|
15
|
+
if (!parsed.success) {
|
|
16
|
+
return err("INVALID_INPUT", `filters_incomplete: ${parsed.error.message}`);
|
|
17
|
+
}
|
|
18
|
+
return ok(canonicalizeRetrievalTraceFilters(parsed.data));
|
|
19
|
+
};
|
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
RetrievalTraceBundle,
|
|
3
|
+
RetrievalTraceCursor,
|
|
4
|
+
RetrievalTraceJudgmentRow,
|
|
5
|
+
StoreResult,
|
|
6
|
+
} from "../store/types";
|
|
7
|
+
import type {
|
|
8
|
+
LabelRetrievalTraceInput,
|
|
9
|
+
RetrievalTraceSummary,
|
|
10
|
+
} from "./retrieval-trace-management-types";
|
|
11
|
+
|
|
12
|
+
import { hashTraceCanonical } from "../store/retrieval-trace-codec";
|
|
13
|
+
import { err, ok } from "../store/types";
|
|
14
|
+
|
|
15
|
+
export type EvidenceTarget = {
|
|
16
|
+
docid?: string;
|
|
17
|
+
sourceHash?: string;
|
|
18
|
+
mirrorHash?: string;
|
|
19
|
+
uri?: string;
|
|
20
|
+
seq?: number;
|
|
21
|
+
startLine?: number;
|
|
22
|
+
endLine?: number;
|
|
23
|
+
passageHash?: string;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
export interface EvidenceMatch {
|
|
27
|
+
target: EvidenceTarget;
|
|
28
|
+
runId: string | null;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export const compactTarget = (target: EvidenceTarget): EvidenceTarget =>
|
|
32
|
+
Object.fromEntries(
|
|
33
|
+
Object.entries(target).filter(([, value]) => value !== undefined)
|
|
34
|
+
) as EvidenceTarget;
|
|
35
|
+
|
|
36
|
+
export const summaryOf = (
|
|
37
|
+
trace: RetrievalTraceBundle["trace"]
|
|
38
|
+
): RetrievalTraceSummary => ({
|
|
39
|
+
traceId: trace.traceId,
|
|
40
|
+
schemaVersion: trace.schemaVersion,
|
|
41
|
+
redactionMode: trace.redactionMode,
|
|
42
|
+
replayCapable: trace.replayCapable,
|
|
43
|
+
status: trace.status,
|
|
44
|
+
queryShape: trace.queryShape,
|
|
45
|
+
goalShape: trace.goalShape,
|
|
46
|
+
fingerprints: trace.fingerprints,
|
|
47
|
+
createdAtMs: trace.createdAtMs,
|
|
48
|
+
updatedAtMs: trace.updatedAtMs,
|
|
49
|
+
expiresAtMs: trace.expiresAtMs,
|
|
50
|
+
byteSize: trace.byteSize,
|
|
51
|
+
creationDigest: trace.creationDigest,
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
const TRACE_CURSOR_PREFIX = "gno-trace-v1.";
|
|
55
|
+
const TRACE_CURSOR_KEYS = ["createdAtMs", "traceId"] as const;
|
|
56
|
+
|
|
57
|
+
export const encodeCursor = (cursor: RetrievalTraceCursor): string => {
|
|
58
|
+
const payload = new TextEncoder().encode(
|
|
59
|
+
JSON.stringify({
|
|
60
|
+
createdAtMs: cursor.createdAtMs,
|
|
61
|
+
traceId: cursor.traceId,
|
|
62
|
+
})
|
|
63
|
+
);
|
|
64
|
+
return `${TRACE_CURSOR_PREFIX}${payload.toBase64({
|
|
65
|
+
alphabet: "base64url",
|
|
66
|
+
omitPadding: true,
|
|
67
|
+
})}`;
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
export const decodeCursor = (
|
|
71
|
+
value: string | undefined
|
|
72
|
+
): StoreResult<RetrievalTraceCursor | undefined> => {
|
|
73
|
+
if (value === undefined) return ok(undefined);
|
|
74
|
+
if (!value.startsWith(TRACE_CURSOR_PREFIX)) {
|
|
75
|
+
return err("INVALID_INPUT", "Invalid trace cursor");
|
|
76
|
+
}
|
|
77
|
+
let decoded: unknown;
|
|
78
|
+
try {
|
|
79
|
+
const bytes = Uint8Array.fromBase64(
|
|
80
|
+
value.slice(TRACE_CURSOR_PREFIX.length),
|
|
81
|
+
{
|
|
82
|
+
alphabet: "base64url",
|
|
83
|
+
}
|
|
84
|
+
);
|
|
85
|
+
decoded = JSON.parse(
|
|
86
|
+
new TextDecoder("utf-8", { fatal: true }).decode(bytes)
|
|
87
|
+
);
|
|
88
|
+
} catch {
|
|
89
|
+
return err("INVALID_INPUT", "Invalid trace cursor");
|
|
90
|
+
}
|
|
91
|
+
if (!decoded || typeof decoded !== "object" || Array.isArray(decoded)) {
|
|
92
|
+
return err("INVALID_INPUT", "Invalid trace cursor");
|
|
93
|
+
}
|
|
94
|
+
const record = decoded as Record<string, unknown>;
|
|
95
|
+
if (
|
|
96
|
+
Object.keys(record).length !== TRACE_CURSOR_KEYS.length ||
|
|
97
|
+
TRACE_CURSOR_KEYS.some((key) => !(key in record))
|
|
98
|
+
) {
|
|
99
|
+
return err("INVALID_INPUT", "Invalid trace cursor");
|
|
100
|
+
}
|
|
101
|
+
const { createdAtMs, traceId } = record;
|
|
102
|
+
if (
|
|
103
|
+
!Number.isSafeInteger(createdAtMs) ||
|
|
104
|
+
(createdAtMs as number) < 0 ||
|
|
105
|
+
typeof traceId !== "string" ||
|
|
106
|
+
traceId.length < 1 ||
|
|
107
|
+
traceId.length > 128
|
|
108
|
+
) {
|
|
109
|
+
return err("INVALID_INPUT", "Invalid trace cursor");
|
|
110
|
+
}
|
|
111
|
+
return ok({ createdAtMs: createdAtMs as number, traceId });
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
export const stableTarget = (value: unknown): EvidenceTarget | null => {
|
|
115
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return null;
|
|
116
|
+
const record = value as Record<string, unknown>;
|
|
117
|
+
const target: EvidenceTarget = {};
|
|
118
|
+
for (const key of [
|
|
119
|
+
"docid",
|
|
120
|
+
"sourceHash",
|
|
121
|
+
"mirrorHash",
|
|
122
|
+
"uri",
|
|
123
|
+
"passageHash",
|
|
124
|
+
] as const) {
|
|
125
|
+
if (typeof record[key] === "string") target[key] = record[key];
|
|
126
|
+
}
|
|
127
|
+
for (const key of ["seq", "startLine", "endLine"] as const) {
|
|
128
|
+
if (typeof record[key] === "number") target[key] = record[key];
|
|
129
|
+
}
|
|
130
|
+
return target.docid || target.sourceHash || target.mirrorHash || target.uri
|
|
131
|
+
? target
|
|
132
|
+
: null;
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
const payloadEvidence = (
|
|
136
|
+
payload: Record<string, unknown>
|
|
137
|
+
): EvidenceTarget[] => {
|
|
138
|
+
const found: EvidenceTarget[] = [];
|
|
139
|
+
for (const key of ["ranked", "evidence"] as const) {
|
|
140
|
+
const values = payload[key];
|
|
141
|
+
if (!Array.isArray(values)) continue;
|
|
142
|
+
for (const value of values) {
|
|
143
|
+
const target = stableTarget(value);
|
|
144
|
+
if (target) found.push(target);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
return found;
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
export const allEvidence = (bundle: RetrievalTraceBundle): EvidenceMatch[] => [
|
|
151
|
+
...bundle.runs.flatMap((run) =>
|
|
152
|
+
payloadEvidence(run.payload).map((target) => ({
|
|
153
|
+
target,
|
|
154
|
+
runId: run.runId,
|
|
155
|
+
}))
|
|
156
|
+
),
|
|
157
|
+
...bundle.events.flatMap((event) =>
|
|
158
|
+
payloadEvidence(event.payload).map((target) => ({
|
|
159
|
+
target,
|
|
160
|
+
runId: event.runId,
|
|
161
|
+
}))
|
|
162
|
+
),
|
|
163
|
+
];
|
|
164
|
+
|
|
165
|
+
export const refMatches = (target: EvidenceTarget, ref: string): boolean =>
|
|
166
|
+
[
|
|
167
|
+
target.uri,
|
|
168
|
+
target.docid,
|
|
169
|
+
target.sourceHash,
|
|
170
|
+
target.mirrorHash,
|
|
171
|
+
target.passageHash,
|
|
172
|
+
].includes(ref);
|
|
173
|
+
|
|
174
|
+
export const applyTargetKind = (
|
|
175
|
+
target: EvidenceTarget,
|
|
176
|
+
input: LabelRetrievalTraceInput
|
|
177
|
+
): StoreResult<EvidenceTarget> => {
|
|
178
|
+
const targetKind =
|
|
179
|
+
input.targetKind ??
|
|
180
|
+
(input.startLine === undefined
|
|
181
|
+
? target.seq === undefined
|
|
182
|
+
? "document"
|
|
183
|
+
: "chunk"
|
|
184
|
+
: "span");
|
|
185
|
+
if ((input.startLine === undefined) !== (input.endLine === undefined)) {
|
|
186
|
+
return err("INVALID_INPUT", "Trace span labels require both line bounds");
|
|
187
|
+
}
|
|
188
|
+
if (
|
|
189
|
+
input.startLine !== undefined &&
|
|
190
|
+
(input.startLine < 1 ||
|
|
191
|
+
input.endLine! < input.startLine ||
|
|
192
|
+
target.startLine !== input.startLine ||
|
|
193
|
+
target.endLine !== input.endLine)
|
|
194
|
+
) {
|
|
195
|
+
return err(
|
|
196
|
+
"INVALID_INPUT",
|
|
197
|
+
"Trace label does not match an exact evidence span"
|
|
198
|
+
);
|
|
199
|
+
}
|
|
200
|
+
if (input.sourceHash && target.sourceHash !== input.sourceHash) {
|
|
201
|
+
return err(
|
|
202
|
+
"INVALID_INPUT",
|
|
203
|
+
"Trace label source hash does not match evidence"
|
|
204
|
+
);
|
|
205
|
+
}
|
|
206
|
+
if (input.docid && target.docid !== input.docid) {
|
|
207
|
+
return err("INVALID_INPUT", "Trace label docid does not match evidence");
|
|
208
|
+
}
|
|
209
|
+
if (targetKind === "span" && target.startLine === undefined) {
|
|
210
|
+
return err(
|
|
211
|
+
"INVALID_INPUT",
|
|
212
|
+
"Span labels require exact recorded evidence lines"
|
|
213
|
+
);
|
|
214
|
+
}
|
|
215
|
+
if (targetKind === "chunk" && target.seq === undefined) {
|
|
216
|
+
return err(
|
|
217
|
+
"INVALID_INPUT",
|
|
218
|
+
"Chunk labels require a recorded chunk sequence"
|
|
219
|
+
);
|
|
220
|
+
}
|
|
221
|
+
if (targetKind === "document") {
|
|
222
|
+
const { docid, sourceHash, mirrorHash, uri } = target;
|
|
223
|
+
return ok(compactTarget({ docid, sourceHash, mirrorHash, uri }));
|
|
224
|
+
}
|
|
225
|
+
return ok(target);
|
|
226
|
+
};
|
|
227
|
+
|
|
228
|
+
export const targetKey = (
|
|
229
|
+
targetKind: NonNullable<LabelRetrievalTraceInput["targetKind"]>,
|
|
230
|
+
target: EvidenceTarget
|
|
231
|
+
): string => hashTraceCanonical({ targetKind, target });
|
|
232
|
+
|
|
233
|
+
export const latestForTarget = (
|
|
234
|
+
judgments: RetrievalTraceJudgmentRow[],
|
|
235
|
+
key: string
|
|
236
|
+
): RetrievalTraceJudgmentRow | undefined =>
|
|
237
|
+
judgments
|
|
238
|
+
.filter(
|
|
239
|
+
(judgment) =>
|
|
240
|
+
targetKey(
|
|
241
|
+
judgment.targetKind as NonNullable<
|
|
242
|
+
LabelRetrievalTraceInput["targetKind"]
|
|
243
|
+
>,
|
|
244
|
+
stableTarget(judgment.target) ?? {}
|
|
245
|
+
) === key
|
|
246
|
+
)
|
|
247
|
+
.at(-1);
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
RetrievalTraceAppendResult,
|
|
3
|
+
RetrievalTraceBundle,
|
|
4
|
+
RetrievalTraceDeleteCounts,
|
|
5
|
+
RetrievalTraceExportFormat,
|
|
6
|
+
RetrievalTraceExportManifestRow,
|
|
7
|
+
RetrievalTraceJudgmentLabel,
|
|
8
|
+
RetrievalTraceJudgmentRow,
|
|
9
|
+
RetrievalTraceJudgmentTargetKind,
|
|
10
|
+
RetrievalTracePurgeResult as StoredRetrievalTracePurgeResult,
|
|
11
|
+
RetrievalTraceRow,
|
|
12
|
+
} from "../store/types";
|
|
13
|
+
import type { RetrievalTraceQrelsArtifact } from "./retrieval-qrels";
|
|
14
|
+
|
|
15
|
+
export interface RetrievalTraceSummary {
|
|
16
|
+
traceId: string;
|
|
17
|
+
schemaVersion: "1.0";
|
|
18
|
+
redactionMode: RetrievalTraceRow["redactionMode"];
|
|
19
|
+
replayCapable: boolean;
|
|
20
|
+
status: RetrievalTraceRow["status"];
|
|
21
|
+
queryShape: RetrievalTraceRow["queryShape"];
|
|
22
|
+
goalShape: RetrievalTraceRow["goalShape"];
|
|
23
|
+
fingerprints: RetrievalTraceRow["fingerprints"];
|
|
24
|
+
createdAtMs: number;
|
|
25
|
+
updatedAtMs: number;
|
|
26
|
+
expiresAtMs: number;
|
|
27
|
+
byteSize: number;
|
|
28
|
+
creationDigest: string;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export interface RetrievalTraceListOptions {
|
|
32
|
+
limit?: number;
|
|
33
|
+
cursor?: string;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export interface RetrievalTraceListResult {
|
|
37
|
+
schemaVersion: "1.0";
|
|
38
|
+
traces: RetrievalTraceSummary[];
|
|
39
|
+
nextCursor: string | null;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export interface RetrievalTraceDetailOptions {
|
|
43
|
+
detailLimit?: number;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export interface RetrievalTraceDetail extends Omit<
|
|
47
|
+
RetrievalTraceBundle,
|
|
48
|
+
"runs" | "events" | "judgments" | "exports"
|
|
49
|
+
> {
|
|
50
|
+
schemaVersion: "1.0";
|
|
51
|
+
runs: RetrievalTraceBundle["runs"];
|
|
52
|
+
events: RetrievalTraceBundle["events"];
|
|
53
|
+
judgments: RetrievalTraceBundle["judgments"];
|
|
54
|
+
exports: RetrievalTraceBundle["exports"];
|
|
55
|
+
totals: {
|
|
56
|
+
runs: number;
|
|
57
|
+
events: number;
|
|
58
|
+
judgments: number;
|
|
59
|
+
exports: number;
|
|
60
|
+
};
|
|
61
|
+
truncated: {
|
|
62
|
+
runs: boolean;
|
|
63
|
+
events: boolean;
|
|
64
|
+
judgments: boolean;
|
|
65
|
+
exports: boolean;
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export interface LabelRetrievalTraceInput {
|
|
70
|
+
traceId: string;
|
|
71
|
+
label: RetrievalTraceJudgmentLabel;
|
|
72
|
+
targetRef: string;
|
|
73
|
+
targetKind?: Exclude<RetrievalTraceJudgmentTargetKind, "query">;
|
|
74
|
+
startLine?: number;
|
|
75
|
+
endLine?: number;
|
|
76
|
+
sourceHash?: string;
|
|
77
|
+
docid?: string;
|
|
78
|
+
idempotencyKey?: string;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export interface LabelRetrievalTraceResult {
|
|
82
|
+
schemaVersion: "1.0";
|
|
83
|
+
result: RetrievalTraceAppendResult;
|
|
84
|
+
judgment: RetrievalTraceJudgmentRow;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export interface RetrievalTraceAgenticArtifact {
|
|
88
|
+
schemaVersion: "1.0";
|
|
89
|
+
format: "agentic-receipt";
|
|
90
|
+
traces: Array<Omit<RetrievalTraceBundle, "exports">>;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export type RetrievalTraceArtifact =
|
|
94
|
+
| RetrievalTraceAgenticArtifact
|
|
95
|
+
| RetrievalTraceQrelsArtifact;
|
|
96
|
+
|
|
97
|
+
export interface ExportRetrievalTracesInput<
|
|
98
|
+
Format extends RetrievalTraceExportFormat = "agentic-receipt",
|
|
99
|
+
> {
|
|
100
|
+
traceIds: string[];
|
|
101
|
+
format?: Format;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export interface ExportRetrievalTracesResult<
|
|
105
|
+
Format extends RetrievalTraceExportFormat = "agentic-receipt",
|
|
106
|
+
> {
|
|
107
|
+
schemaVersion: "1.0";
|
|
108
|
+
result: RetrievalTraceAppendResult;
|
|
109
|
+
manifest: RetrievalTraceExportManifestRow;
|
|
110
|
+
artifact: Format extends "qrels"
|
|
111
|
+
? RetrievalTraceQrelsArtifact
|
|
112
|
+
: RetrievalTraceAgenticArtifact;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
export interface DeleteRetrievalTraceResult {
|
|
116
|
+
schemaVersion: "1.0";
|
|
117
|
+
traceId: string;
|
|
118
|
+
deleted: boolean;
|
|
119
|
+
counts: RetrievalTraceDeleteCounts;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
export interface PurgeRetrievalTracesResult extends StoredRetrievalTracePurgeResult {
|
|
123
|
+
schemaVersion: "1.0";
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
export type RetrievalTraceListRequest = RetrievalTraceListOptions;
|
|
127
|
+
export type RetrievalTraceLabelRequest = LabelRetrievalTraceInput;
|
|
128
|
+
export type RetrievalTraceExportRequest = ExportRetrievalTracesInput;
|
|
129
|
+
export type RetrievalTraceLabelResult = LabelRetrievalTraceResult;
|
|
130
|
+
export type RetrievalTraceExportResult = ExportRetrievalTracesResult;
|
|
131
|
+
export type RetrievalTraceDeleteResult = DeleteRetrievalTraceResult;
|
|
132
|
+
export type RetrievalTracePurgeResult = PurgeRetrievalTracesResult;
|