@gmickel/gno 1.18.0 → 1.19.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 +5 -3
- package/assets/skill/SKILL.md +27 -0
- 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 +2894 -0
- package/spec/db/schema.sql +442 -0
- package/spec/evals-agentic.md +510 -0
- package/spec/evals.md +1106 -0
- package/spec/mcp.md +2229 -0
- package/spec/output-schemas/activation-verification.schema.json +515 -0
- package/spec/output-schemas/ask.schema.json +366 -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/collection-list.schema.json +45 -0
- package/spec/output-schemas/context-capsule-v1.schema.json +691 -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-types.ts +3 -0
- package/src/app/context-runtime.ts +15 -1
- package/src/cli/commands/ask.ts +106 -36
- 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 +255 -0
- 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-compiler.ts +11 -4
- 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 +175 -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 +494 -0
- package/src/core/retrieval-trace.ts +472 -0
- package/src/mcp/tools/context.ts +59 -8
- package/src/mcp/tools/get.ts +35 -1
- package/src/mcp/tools/index.ts +74 -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/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 +43 -0
- package/src/pipeline/vsearch.ts +101 -38
- package/src/sdk/client.ts +380 -71
- 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/globals.built.css +1 -1
- package/src/serve/public/lib/workspace-tabs.ts +2 -0
- 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 +366 -72
- 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
package/src/serve/routes/api.ts
CHANGED
|
@@ -94,6 +94,14 @@ import {
|
|
|
94
94
|
resolveNotePreset,
|
|
95
95
|
type NotePresetId,
|
|
96
96
|
} from "../../core/note-presets";
|
|
97
|
+
import {
|
|
98
|
+
retrievalTraceFilters,
|
|
99
|
+
startRetrievalTraceRequest,
|
|
100
|
+
} from "../../core/retrieval-trace-request";
|
|
101
|
+
import {
|
|
102
|
+
evidenceFromExactDocument,
|
|
103
|
+
RetrievalTraceSession,
|
|
104
|
+
} from "../../core/retrieval-trace-session";
|
|
97
105
|
import { extractSections } from "../../core/sections";
|
|
98
106
|
import { normalizeStructuredQueryInput } from "../../core/structured-query";
|
|
99
107
|
import {
|
|
@@ -118,7 +126,7 @@ import {
|
|
|
118
126
|
} from "../../llm/registry";
|
|
119
127
|
import {
|
|
120
128
|
generateGroundedAnswer,
|
|
121
|
-
|
|
129
|
+
processAnswerResultWithTrace,
|
|
122
130
|
} from "../../pipeline/answer";
|
|
123
131
|
import { diagnoseQueryTarget } from "../../pipeline/diagnose";
|
|
124
132
|
import { searchHybrid } from "../../pipeline/hybrid";
|
|
@@ -145,6 +153,10 @@ import {
|
|
|
145
153
|
} from "../context";
|
|
146
154
|
import { analyzeImportPath } from "../import-preview";
|
|
147
155
|
import { getActiveJob, getJobStatus, startJob } from "../jobs";
|
|
156
|
+
import {
|
|
157
|
+
requestRetrievalTraceId,
|
|
158
|
+
withRetrievalTraceHeader,
|
|
159
|
+
} from "../retrieval-trace";
|
|
148
160
|
import { buildAppStatus, type StatusBuildDeps } from "../status";
|
|
149
161
|
|
|
150
162
|
/** Mutable context holder for hot-reloading presets */
|
|
@@ -460,6 +472,65 @@ function errorResponse(
|
|
|
460
472
|
);
|
|
461
473
|
}
|
|
462
474
|
|
|
475
|
+
interface RestTraceStart {
|
|
476
|
+
session: RetrievalTraceSession | null;
|
|
477
|
+
error: Response | null;
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
async function startRestTrace(
|
|
481
|
+
ctx: ServerContext,
|
|
482
|
+
input: {
|
|
483
|
+
query: string;
|
|
484
|
+
goal?: string;
|
|
485
|
+
filters?: Record<string, unknown>;
|
|
486
|
+
pipeline: string;
|
|
487
|
+
modelUris?: string[];
|
|
488
|
+
}
|
|
489
|
+
): Promise<RestTraceStart> {
|
|
490
|
+
const started = await startRetrievalTraceRequest({
|
|
491
|
+
store: ctx.store,
|
|
492
|
+
config: ctx.config,
|
|
493
|
+
query: input.query,
|
|
494
|
+
goal: input.goal,
|
|
495
|
+
filters: input.filters,
|
|
496
|
+
pipeline: input.pipeline,
|
|
497
|
+
indexName: ctx.indexName,
|
|
498
|
+
modelUris: input.modelUris,
|
|
499
|
+
});
|
|
500
|
+
return started.ok
|
|
501
|
+
? { session: started.value, error: null }
|
|
502
|
+
: {
|
|
503
|
+
session: null,
|
|
504
|
+
error: errorResponse(
|
|
505
|
+
"RUNTIME",
|
|
506
|
+
`Retrieval trace start failed: ${started.error.message}`,
|
|
507
|
+
500
|
|
508
|
+
),
|
|
509
|
+
};
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
async function finishRestTrace(
|
|
513
|
+
request: Request,
|
|
514
|
+
session: RetrievalTraceSession | null,
|
|
515
|
+
status: "completed" | "partial" | "failed",
|
|
516
|
+
response: Response
|
|
517
|
+
): Promise<Response> {
|
|
518
|
+
if (!session) return response;
|
|
519
|
+
const terminalStatus = request.signal.aborted ? "cancelled" : status;
|
|
520
|
+
const finished = await session.finish(terminalStatus);
|
|
521
|
+
if (!finished.ok) {
|
|
522
|
+
return withRetrievalTraceHeader(
|
|
523
|
+
errorResponse(
|
|
524
|
+
"RUNTIME",
|
|
525
|
+
`Retrieval trace finalization failed: ${finished.error.message}`,
|
|
526
|
+
500
|
|
527
|
+
),
|
|
528
|
+
session
|
|
529
|
+
);
|
|
530
|
+
}
|
|
531
|
+
return withRetrievalTraceHeader(response, session);
|
|
532
|
+
}
|
|
533
|
+
|
|
463
534
|
function jobConflictResponse(jobResult: StartJobError): Response {
|
|
464
535
|
return errorResponse("CONFLICT", jobResult.error, 409, {
|
|
465
536
|
activeJobId: jobResult.activeJobId,
|
|
@@ -1639,7 +1710,8 @@ export async function handleDocsAutocomplete(
|
|
|
1639
1710
|
export async function handleDoc(
|
|
1640
1711
|
store: SqliteAdapter,
|
|
1641
1712
|
config: Config,
|
|
1642
|
-
url: URL
|
|
1713
|
+
url: URL,
|
|
1714
|
+
request?: Request
|
|
1643
1715
|
): Promise<Response> {
|
|
1644
1716
|
const uri = url.searchParams.get("uri");
|
|
1645
1717
|
if (!uri) {
|
|
@@ -1679,7 +1751,7 @@ export async function handleDoc(
|
|
|
1679
1751
|
});
|
|
1680
1752
|
const source = await buildSourceMeta(config.collections, doc);
|
|
1681
1753
|
|
|
1682
|
-
|
|
1754
|
+
const responseData = {
|
|
1683
1755
|
docid: doc.docid,
|
|
1684
1756
|
uri: doc.uri,
|
|
1685
1757
|
title: doc.title,
|
|
@@ -1690,7 +1762,52 @@ export async function handleDoc(
|
|
|
1690
1762
|
tags,
|
|
1691
1763
|
source,
|
|
1692
1764
|
capabilities,
|
|
1765
|
+
};
|
|
1766
|
+
const continuationTraceId = request
|
|
1767
|
+
? requestRetrievalTraceId(request)
|
|
1768
|
+
: undefined;
|
|
1769
|
+
if (!continuationTraceId) return jsonResponse(responseData);
|
|
1770
|
+
|
|
1771
|
+
const resumed = await RetrievalTraceSession.resume({
|
|
1772
|
+
store,
|
|
1773
|
+
config: config.retrievalTraces,
|
|
1774
|
+
traceId: continuationTraceId,
|
|
1693
1775
|
});
|
|
1776
|
+
if (!resumed.ok) {
|
|
1777
|
+
return errorResponse("RUNTIME", resumed.error.message, 500);
|
|
1778
|
+
}
|
|
1779
|
+
const traceSession = resumed.value;
|
|
1780
|
+
if (!traceSession) return jsonResponse(responseData);
|
|
1781
|
+
|
|
1782
|
+
if (content !== null) {
|
|
1783
|
+
const endLine = content.split("\n").length;
|
|
1784
|
+
const evidence = evidenceFromExactDocument({
|
|
1785
|
+
docid: doc.docid,
|
|
1786
|
+
uri: doc.uri,
|
|
1787
|
+
sourceHash: doc.sourceHash,
|
|
1788
|
+
mirrorHash: doc.mirrorHash ?? undefined,
|
|
1789
|
+
content,
|
|
1790
|
+
startLine: 1,
|
|
1791
|
+
endLine,
|
|
1792
|
+
});
|
|
1793
|
+
if (evidence) {
|
|
1794
|
+
const got = await traceSession.recordEvidence("get", [evidence]);
|
|
1795
|
+
if (!got.ok) {
|
|
1796
|
+
return withRetrievalTraceHeader(
|
|
1797
|
+
errorResponse("RUNTIME", got.error.message, 500),
|
|
1798
|
+
traceSession
|
|
1799
|
+
);
|
|
1800
|
+
}
|
|
1801
|
+
const opened = await traceSession.recordEvidence("open", [evidence]);
|
|
1802
|
+
if (!opened.ok) {
|
|
1803
|
+
return withRetrievalTraceHeader(
|
|
1804
|
+
errorResponse("RUNTIME", opened.error.message, 500),
|
|
1805
|
+
traceSession
|
|
1806
|
+
);
|
|
1807
|
+
}
|
|
1808
|
+
}
|
|
1809
|
+
}
|
|
1810
|
+
return withRetrievalTraceHeader(jsonResponse(responseData), traceSession);
|
|
1694
1811
|
}
|
|
1695
1812
|
|
|
1696
1813
|
/**
|
|
@@ -3372,9 +3489,13 @@ export async function handleCreateDoc(
|
|
|
3372
3489
|
* Returns search results.
|
|
3373
3490
|
*/
|
|
3374
3491
|
export async function handleSearch(
|
|
3375
|
-
|
|
3492
|
+
contextOrStore: ServerContext | SqliteAdapter,
|
|
3376
3493
|
req: Request
|
|
3377
3494
|
): Promise<Response> {
|
|
3495
|
+
const context =
|
|
3496
|
+
"store" in contextOrStore ? contextOrStore : (null as ServerContext | null);
|
|
3497
|
+
const store =
|
|
3498
|
+
"store" in contextOrStore ? contextOrStore.store : contextOrStore;
|
|
3378
3499
|
let body: SearchRequestBody;
|
|
3379
3500
|
try {
|
|
3380
3501
|
body = (await req.json()) as SearchRequestBody;
|
|
@@ -3486,13 +3607,30 @@ export async function handleSearch(
|
|
|
3486
3607
|
author,
|
|
3487
3608
|
};
|
|
3488
3609
|
|
|
3489
|
-
const
|
|
3610
|
+
const trace = context
|
|
3611
|
+
? await startRestTrace(context, {
|
|
3612
|
+
query: rawQuery,
|
|
3613
|
+
filters: retrievalTraceFilters(options),
|
|
3614
|
+
pipeline: "bm25",
|
|
3615
|
+
})
|
|
3616
|
+
: { session: null, error: null };
|
|
3617
|
+
if (trace.error) return trace.error;
|
|
3618
|
+
|
|
3619
|
+
const result = await searchBm25(store, rawQuery, {
|
|
3620
|
+
...options,
|
|
3621
|
+
traceSession: trace.session ?? undefined,
|
|
3622
|
+
});
|
|
3490
3623
|
|
|
3491
3624
|
if (!result.ok) {
|
|
3492
|
-
return
|
|
3625
|
+
return finishRestTrace(
|
|
3626
|
+
req,
|
|
3627
|
+
trace.session,
|
|
3628
|
+
"failed",
|
|
3629
|
+
errorResponse("RUNTIME", result.error.message, 500)
|
|
3630
|
+
);
|
|
3493
3631
|
}
|
|
3494
3632
|
|
|
3495
|
-
return jsonResponse(result.value);
|
|
3633
|
+
return withRetrievalTraceHeader(jsonResponse(result.value), trace.session);
|
|
3496
3634
|
}
|
|
3497
3635
|
|
|
3498
3636
|
/**
|
|
@@ -3626,6 +3764,41 @@ export async function handleQuery(
|
|
|
3626
3764
|
: undefined;
|
|
3627
3765
|
const author = body.author?.trim() || undefined;
|
|
3628
3766
|
|
|
3767
|
+
const queryOptions = {
|
|
3768
|
+
limit: Math.min(body.limit ?? 20, 50),
|
|
3769
|
+
minScore: body.minScore,
|
|
3770
|
+
collection: body.collection,
|
|
3771
|
+
lang: body.lang,
|
|
3772
|
+
intent: body.intent?.trim() || undefined,
|
|
3773
|
+
candidateLimit:
|
|
3774
|
+
body.candidateLimit !== undefined
|
|
3775
|
+
? Math.min(body.candidateLimit, 100)
|
|
3776
|
+
: undefined,
|
|
3777
|
+
exclude,
|
|
3778
|
+
queryModes: normalizedQueryModes,
|
|
3779
|
+
noExpand: body.noExpand,
|
|
3780
|
+
noRerank: body.noRerank,
|
|
3781
|
+
graph: body.graph === true,
|
|
3782
|
+
noGraph: body.noGraph,
|
|
3783
|
+
tagsAll,
|
|
3784
|
+
tagsAny,
|
|
3785
|
+
since: body.since,
|
|
3786
|
+
until: body.until,
|
|
3787
|
+
categories,
|
|
3788
|
+
author,
|
|
3789
|
+
};
|
|
3790
|
+
const trace = await startRestTrace(ctx, {
|
|
3791
|
+
query: normalizedQuery,
|
|
3792
|
+
filters: retrievalTraceFilters(queryOptions),
|
|
3793
|
+
pipeline: "hybrid",
|
|
3794
|
+
modelUris: [
|
|
3795
|
+
ctx.embedPort?.modelUri,
|
|
3796
|
+
ctx.expandPort?.modelUri,
|
|
3797
|
+
ctx.rerankPort?.modelUri,
|
|
3798
|
+
].filter((value): value is string => Boolean(value)),
|
|
3799
|
+
});
|
|
3800
|
+
if (trace.error) return trace.error;
|
|
3801
|
+
|
|
3629
3802
|
const result = await searchHybrid(
|
|
3630
3803
|
{
|
|
3631
3804
|
store: ctx.store,
|
|
@@ -3637,35 +3810,21 @@ export async function handleQuery(
|
|
|
3637
3810
|
},
|
|
3638
3811
|
normalizedQuery,
|
|
3639
3812
|
{
|
|
3640
|
-
|
|
3641
|
-
|
|
3642
|
-
collection: body.collection,
|
|
3643
|
-
lang: body.lang,
|
|
3644
|
-
intent: body.intent?.trim() || undefined,
|
|
3645
|
-
candidateLimit:
|
|
3646
|
-
body.candidateLimit !== undefined
|
|
3647
|
-
? Math.min(body.candidateLimit, 100)
|
|
3648
|
-
: undefined,
|
|
3649
|
-
exclude,
|
|
3650
|
-
queryModes: normalizedQueryModes,
|
|
3651
|
-
noExpand: body.noExpand,
|
|
3652
|
-
noRerank: body.noRerank,
|
|
3653
|
-
graph: body.graph === true,
|
|
3654
|
-
noGraph: body.noGraph,
|
|
3655
|
-
tagsAll,
|
|
3656
|
-
tagsAny,
|
|
3657
|
-
since: body.since,
|
|
3658
|
-
until: body.until,
|
|
3659
|
-
categories,
|
|
3660
|
-
author,
|
|
3813
|
+
...queryOptions,
|
|
3814
|
+
traceSession: trace.session ?? undefined,
|
|
3661
3815
|
}
|
|
3662
3816
|
);
|
|
3663
3817
|
|
|
3664
3818
|
if (!result.ok) {
|
|
3665
|
-
return
|
|
3819
|
+
return finishRestTrace(
|
|
3820
|
+
req,
|
|
3821
|
+
trace.session,
|
|
3822
|
+
"failed",
|
|
3823
|
+
errorResponse("RUNTIME", result.error.message, 500)
|
|
3824
|
+
);
|
|
3666
3825
|
}
|
|
3667
3826
|
|
|
3668
|
-
return jsonResponse(result.value);
|
|
3827
|
+
return withRetrievalTraceHeader(jsonResponse(result.value), trace.session);
|
|
3669
3828
|
}
|
|
3670
3829
|
|
|
3671
3830
|
/**
|
|
@@ -3878,15 +4037,6 @@ export async function handleAsk(
|
|
|
3878
4037
|
return errorResponse("VALIDATION", "Query cannot be empty");
|
|
3879
4038
|
}
|
|
3880
4039
|
|
|
3881
|
-
// Check if answer generation is available
|
|
3882
|
-
if (!ctx.capabilities.answer) {
|
|
3883
|
-
return errorResponse(
|
|
3884
|
-
"UNAVAILABLE",
|
|
3885
|
-
"Answer generation not available. No answer model loaded.",
|
|
3886
|
-
503
|
|
3887
|
-
);
|
|
3888
|
-
}
|
|
3889
|
-
|
|
3890
4040
|
// Parse tag filters
|
|
3891
4041
|
let tagsAll: string[] | undefined;
|
|
3892
4042
|
let tagsAny: string[] | undefined;
|
|
@@ -3973,6 +4123,64 @@ export async function handleAsk(
|
|
|
3973
4123
|
const author = body.author?.trim() || undefined;
|
|
3974
4124
|
|
|
3975
4125
|
const limit = Math.min(body.limit ?? 5, 20);
|
|
4126
|
+
const askOptions = {
|
|
4127
|
+
limit,
|
|
4128
|
+
collection: body.collection,
|
|
4129
|
+
lang: body.lang,
|
|
4130
|
+
intent: body.intent?.trim() || undefined,
|
|
4131
|
+
noExpand: body.noExpand,
|
|
4132
|
+
noRerank: body.noRerank,
|
|
4133
|
+
candidateLimit:
|
|
4134
|
+
body.candidateLimit !== undefined
|
|
4135
|
+
? Math.min(body.candidateLimit, 100)
|
|
4136
|
+
: undefined,
|
|
4137
|
+
exclude,
|
|
4138
|
+
queryModes: normalizedQueryModes,
|
|
4139
|
+
tagsAll,
|
|
4140
|
+
tagsAny,
|
|
4141
|
+
since: body.since,
|
|
4142
|
+
until: body.until,
|
|
4143
|
+
categories,
|
|
4144
|
+
author,
|
|
4145
|
+
};
|
|
4146
|
+
const trace = await startRestTrace(ctx, {
|
|
4147
|
+
query: normalizedQuery,
|
|
4148
|
+
filters: retrievalTraceFilters(askOptions),
|
|
4149
|
+
pipeline: "ask",
|
|
4150
|
+
modelUris: [
|
|
4151
|
+
ctx.embedPort?.modelUri,
|
|
4152
|
+
ctx.expandPort?.modelUri,
|
|
4153
|
+
ctx.answerPort?.modelUri,
|
|
4154
|
+
ctx.rerankPort?.modelUri,
|
|
4155
|
+
].filter((value): value is string => Boolean(value)),
|
|
4156
|
+
});
|
|
4157
|
+
if (trace.error) return trace.error;
|
|
4158
|
+
|
|
4159
|
+
if (!ctx.capabilities.answer) {
|
|
4160
|
+
const unavailable = await trace.session?.recordCapability(
|
|
4161
|
+
"answer_generation",
|
|
4162
|
+
"unavailable",
|
|
4163
|
+
"model_unavailable"
|
|
4164
|
+
);
|
|
4165
|
+
if (unavailable && !unavailable.ok) {
|
|
4166
|
+
return finishRestTrace(
|
|
4167
|
+
req,
|
|
4168
|
+
trace.session,
|
|
4169
|
+
"failed",
|
|
4170
|
+
errorResponse("RUNTIME", unavailable.error.message, 500)
|
|
4171
|
+
);
|
|
4172
|
+
}
|
|
4173
|
+
return finishRestTrace(
|
|
4174
|
+
req,
|
|
4175
|
+
trace.session,
|
|
4176
|
+
"failed",
|
|
4177
|
+
errorResponse(
|
|
4178
|
+
"UNAVAILABLE",
|
|
4179
|
+
"Answer generation not available. No answer model loaded.",
|
|
4180
|
+
503
|
|
4181
|
+
)
|
|
4182
|
+
);
|
|
4183
|
+
}
|
|
3976
4184
|
|
|
3977
4185
|
// Run hybrid search first
|
|
3978
4186
|
const searchResult = await searchHybrid(
|
|
@@ -3986,29 +4194,18 @@ export async function handleAsk(
|
|
|
3986
4194
|
},
|
|
3987
4195
|
normalizedQuery,
|
|
3988
4196
|
{
|
|
3989
|
-
|
|
3990
|
-
|
|
3991
|
-
lang: body.lang,
|
|
3992
|
-
intent: body.intent?.trim() || undefined,
|
|
3993
|
-
noExpand: body.noExpand,
|
|
3994
|
-
noRerank: body.noRerank,
|
|
3995
|
-
candidateLimit:
|
|
3996
|
-
body.candidateLimit !== undefined
|
|
3997
|
-
? Math.min(body.candidateLimit, 100)
|
|
3998
|
-
: undefined,
|
|
3999
|
-
exclude,
|
|
4000
|
-
queryModes: normalizedQueryModes,
|
|
4001
|
-
tagsAll,
|
|
4002
|
-
tagsAny,
|
|
4003
|
-
since: body.since,
|
|
4004
|
-
until: body.until,
|
|
4005
|
-
categories,
|
|
4006
|
-
author,
|
|
4197
|
+
...askOptions,
|
|
4198
|
+
traceSession: trace.session ?? undefined,
|
|
4007
4199
|
}
|
|
4008
4200
|
);
|
|
4009
4201
|
|
|
4010
4202
|
if (!searchResult.ok) {
|
|
4011
|
-
return
|
|
4203
|
+
return finishRestTrace(
|
|
4204
|
+
req,
|
|
4205
|
+
trace.session,
|
|
4206
|
+
"failed",
|
|
4207
|
+
errorResponse("RUNTIME", searchResult.error.message, 500)
|
|
4208
|
+
);
|
|
4012
4209
|
}
|
|
4013
4210
|
|
|
4014
4211
|
const results = searchResult.value.results;
|
|
@@ -4020,20 +4217,93 @@ export async function handleAsk(
|
|
|
4020
4217
|
let answerGenerated = false;
|
|
4021
4218
|
|
|
4022
4219
|
if (ctx.answerPort) {
|
|
4023
|
-
const
|
|
4024
|
-
|
|
4025
|
-
|
|
4026
|
-
normalizedQuery,
|
|
4027
|
-
results,
|
|
4028
|
-
maxTokens
|
|
4220
|
+
const attempted = await trace.session?.recordCapability(
|
|
4221
|
+
"answer_generation",
|
|
4222
|
+
"attempted"
|
|
4029
4223
|
);
|
|
4224
|
+
if (attempted && !attempted.ok) {
|
|
4225
|
+
return finishRestTrace(
|
|
4226
|
+
req,
|
|
4227
|
+
trace.session,
|
|
4228
|
+
"failed",
|
|
4229
|
+
errorResponse("RUNTIME", attempted.error.message, 500)
|
|
4230
|
+
);
|
|
4231
|
+
}
|
|
4232
|
+
const maxTokens = body.maxAnswerTokens ?? 512;
|
|
4233
|
+
let rawResult: Awaited<ReturnType<typeof generateGroundedAnswer>>;
|
|
4234
|
+
try {
|
|
4235
|
+
rawResult = await generateGroundedAnswer(
|
|
4236
|
+
{ genPort: ctx.answerPort, store: ctx.store },
|
|
4237
|
+
normalizedQuery,
|
|
4238
|
+
results,
|
|
4239
|
+
maxTokens
|
|
4240
|
+
);
|
|
4241
|
+
} catch (error) {
|
|
4242
|
+
await trace.session?.recordCapability(
|
|
4243
|
+
"answer_generation",
|
|
4244
|
+
"failed",
|
|
4245
|
+
"generation_failed"
|
|
4246
|
+
);
|
|
4247
|
+
return finishRestTrace(
|
|
4248
|
+
req,
|
|
4249
|
+
trace.session,
|
|
4250
|
+
"failed",
|
|
4251
|
+
errorResponse(
|
|
4252
|
+
"RUNTIME",
|
|
4253
|
+
error instanceof Error ? error.message : String(error),
|
|
4254
|
+
500
|
|
4255
|
+
)
|
|
4256
|
+
);
|
|
4257
|
+
}
|
|
4030
4258
|
|
|
4031
4259
|
if (rawResult) {
|
|
4032
|
-
|
|
4033
|
-
|
|
4034
|
-
|
|
4035
|
-
|
|
4036
|
-
|
|
4260
|
+
try {
|
|
4261
|
+
const processed = await processAnswerResultWithTrace(
|
|
4262
|
+
rawResult,
|
|
4263
|
+
trace.session ?? undefined
|
|
4264
|
+
);
|
|
4265
|
+
answer = processed.answer;
|
|
4266
|
+
citations = processed.citations;
|
|
4267
|
+
answerContext = processed.answerContext;
|
|
4268
|
+
answerGenerated = true;
|
|
4269
|
+
const used = await trace.session?.recordCapability(
|
|
4270
|
+
"answer_generation",
|
|
4271
|
+
"used"
|
|
4272
|
+
);
|
|
4273
|
+
if (used && !used.ok) {
|
|
4274
|
+
throw new Error(`Trace recording failed: ${used.error.message}`);
|
|
4275
|
+
}
|
|
4276
|
+
} catch (error) {
|
|
4277
|
+
await trace.session?.recordCapability(
|
|
4278
|
+
"answer_generation",
|
|
4279
|
+
"failed",
|
|
4280
|
+
"generation_failed"
|
|
4281
|
+
);
|
|
4282
|
+
return finishRestTrace(
|
|
4283
|
+
req,
|
|
4284
|
+
trace.session,
|
|
4285
|
+
"failed",
|
|
4286
|
+
errorResponse(
|
|
4287
|
+
"RUNTIME",
|
|
4288
|
+
error instanceof Error ? error.message : String(error),
|
|
4289
|
+
500
|
|
4290
|
+
)
|
|
4291
|
+
);
|
|
4292
|
+
}
|
|
4293
|
+
} else {
|
|
4294
|
+
const failed = await trace.session?.recordCapability(
|
|
4295
|
+
"answer_generation",
|
|
4296
|
+
"failed",
|
|
4297
|
+
"generation_failed"
|
|
4298
|
+
);
|
|
4299
|
+
if (failed && !failed.ok) {
|
|
4300
|
+
return finishRestTrace(
|
|
4301
|
+
req,
|
|
4302
|
+
trace.session,
|
|
4303
|
+
"failed",
|
|
4304
|
+
errorResponse("RUNTIME", failed.error.message, 500)
|
|
4305
|
+
);
|
|
4306
|
+
}
|
|
4037
4307
|
}
|
|
4038
4308
|
}
|
|
4039
4309
|
|
|
@@ -4058,7 +4328,13 @@ export async function handleAsk(
|
|
|
4058
4328
|
},
|
|
4059
4329
|
};
|
|
4060
4330
|
|
|
4061
|
-
|
|
4331
|
+
const complete = answerGenerated && (citations?.length ?? 0) > 0;
|
|
4332
|
+
return finishRestTrace(
|
|
4333
|
+
req,
|
|
4334
|
+
trace.session,
|
|
4335
|
+
complete ? "completed" : "partial",
|
|
4336
|
+
jsonResponse(askResult)
|
|
4337
|
+
);
|
|
4062
4338
|
}
|
|
4063
4339
|
|
|
4064
4340
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
@@ -4671,7 +4947,7 @@ export async function routeApi(
|
|
|
4671
4947
|
}
|
|
4672
4948
|
|
|
4673
4949
|
if (path === "/api/doc") {
|
|
4674
|
-
return handleDoc(store, config, url);
|
|
4950
|
+
return handleDoc(store, config, url, req);
|
|
4675
4951
|
}
|
|
4676
4952
|
|
|
4677
4953
|
if (path === "/api/doc-asset") {
|
|
@@ -4679,7 +4955,25 @@ export async function routeApi(
|
|
|
4679
4955
|
}
|
|
4680
4956
|
|
|
4681
4957
|
if (path === "/api/search" && req.method === "POST") {
|
|
4682
|
-
return handleSearch(
|
|
4958
|
+
return handleSearch(
|
|
4959
|
+
{
|
|
4960
|
+
store,
|
|
4961
|
+
config,
|
|
4962
|
+
indexName: "default",
|
|
4963
|
+
vectorIndex: null,
|
|
4964
|
+
embedPort: null,
|
|
4965
|
+
expandPort: null,
|
|
4966
|
+
answerPort: null,
|
|
4967
|
+
rerankPort: null,
|
|
4968
|
+
capabilities: {
|
|
4969
|
+
bm25: true,
|
|
4970
|
+
vector: false,
|
|
4971
|
+
hybrid: false,
|
|
4972
|
+
answer: false,
|
|
4973
|
+
},
|
|
4974
|
+
},
|
|
4975
|
+
req
|
|
4976
|
+
);
|
|
4683
4977
|
}
|
|
4684
4978
|
|
|
4685
4979
|
// Unknown API route
|