@gmickel/gno 1.17.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 +18 -4
- package/assets/skill/SKILL.md +44 -1
- package/assets/skill/mcp-reference.md +21 -0
- package/package.json +3 -2
- 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/daemon.ts +69 -2
- package/src/cli/commands/get.ts +64 -3
- package/src/cli/commands/models/pull.ts +13 -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/status.ts +2 -0
- package/src/cli/commands/trace.ts +200 -0
- package/src/cli/commands/vsearch.ts +75 -53
- package/src/cli/detach.ts +37 -20
- package/src/cli/program.ts +329 -27
- package/src/config/index.ts +12 -0
- package/src/config/retrieval-traces.ts +56 -0
- package/src/config/types.ts +41 -0
- package/src/core/context-compiler.ts +11 -4
- package/src/core/job-manager.ts +19 -0
- package/src/core/mutation-generations.ts +33 -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 +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/llm/cache.ts +13 -3
- package/src/llm/nodeLlamaCpp/adapter.ts +10 -1
- package/src/llm/nodeLlamaCpp/lifecycle.ts +71 -0
- package/src/mcp/context.ts +161 -0
- package/src/mcp/http-security.ts +477 -0
- package/src/mcp/http-session.ts +272 -0
- package/src/mcp/http-transport.ts +370 -0
- package/src/mcp/resources/index.ts +141 -134
- package/src/mcp/server.ts +19 -79
- package/src/mcp/tools/add-collection.ts +3 -1
- package/src/mcp/tools/capture.ts +3 -0
- package/src/mcp/tools/clear-collection-embeddings.ts +2 -0
- package/src/mcp/tools/context.ts +68 -16
- package/src/mcp/tools/embed.ts +62 -52
- package/src/mcp/tools/get.ts +35 -1
- package/src/mcp/tools/index-cmd.ts +88 -74
- package/src/mcp/tools/index.ts +96 -2
- package/src/mcp/tools/query.ts +95 -64
- package/src/mcp/tools/remove-collection.ts +2 -0
- package/src/mcp/tools/search.ts +36 -13
- package/src/mcp/tools/status.ts +11 -0
- package/src/mcp/tools/sync.ts +16 -14
- package/src/mcp/tools/trace.ts +143 -0
- package/src/mcp/tools/vsearch.ts +71 -38
- package/src/mcp/tools/workspace-write.ts +7 -3
- 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/background-runtime.ts +12 -212
- package/src/serve/context-capsule.ts +67 -8
- package/src/serve/embed-scheduler.ts +74 -43
- package/src/serve/index.ts +9 -0
- package/src/serve/jobs.ts +78 -80
- package/src/serve/public/app.tsx +12 -1
- package/src/serve/public/components/HealthCenter.tsx +74 -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 +11 -0
- package/src/serve/public/pages/TraceHistory.tsx +478 -0
- package/src/serve/public/pages/trace-history-detail.tsx +224 -0
- package/src/serve/resident-admission.ts +159 -0
- package/src/serve/resident-background-work.ts +39 -0
- package/src/serve/resident-request.ts +55 -0
- package/src/serve/resident-runtime.ts +490 -0
- package/src/serve/resident-status.ts +96 -0
- package/src/serve/retrieval-trace.ts +28 -0
- package/src/serve/routes/api.ts +629 -239
- package/src/serve/routes/mcp.ts +69 -0
- package/src/serve/routes/traces.ts +156 -0
- package/src/serve/server.ts +276 -37
- package/src/serve/status-model.ts +51 -0
- package/src/serve/status.ts +5 -0
- 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 +179 -10
- 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
|
@@ -35,6 +35,8 @@ export interface ModelsPullOptions {
|
|
|
35
35
|
force?: boolean;
|
|
36
36
|
/** Progress callback for UI (omit to disable progress) */
|
|
37
37
|
onProgress?: (type: ModelType, progress: DownloadProgress) => void;
|
|
38
|
+
/** Stop before the next model and suppress subsequent work. */
|
|
39
|
+
signal?: AbortSignal;
|
|
38
40
|
}
|
|
39
41
|
|
|
40
42
|
export interface ModelPullResult {
|
|
@@ -52,6 +54,10 @@ export interface ModelsPullResult {
|
|
|
52
54
|
skipped: number;
|
|
53
55
|
}
|
|
54
56
|
|
|
57
|
+
export interface ModelsPullDependencies {
|
|
58
|
+
cache?: Pick<ModelCache, "download" | "getCachedPath" | "isCached">;
|
|
59
|
+
}
|
|
60
|
+
|
|
55
61
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
56
62
|
// Implementation
|
|
57
63
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
@@ -87,7 +93,8 @@ function getTypesToPull(options: ModelsPullOptions): ModelType[] {
|
|
|
87
93
|
* Execute gno models pull command.
|
|
88
94
|
*/
|
|
89
95
|
export async function modelsPull(
|
|
90
|
-
options: ModelsPullOptions = {}
|
|
96
|
+
options: ModelsPullOptions = {},
|
|
97
|
+
deps: ModelsPullDependencies = {}
|
|
91
98
|
): Promise<ModelsPullResult> {
|
|
92
99
|
// Use provided config, or load from disk (use defaults if not initialized)
|
|
93
100
|
let config = options.config;
|
|
@@ -98,7 +105,7 @@ export async function modelsPull(
|
|
|
98
105
|
}
|
|
99
106
|
|
|
100
107
|
const preset = getActivePreset(config);
|
|
101
|
-
const cache = new ModelCache(getModelsCachePath());
|
|
108
|
+
const cache = deps.cache ?? new ModelCache(getModelsCachePath());
|
|
102
109
|
const types = getTypesToPull(options);
|
|
103
110
|
|
|
104
111
|
const results: ModelPullResult[] = [];
|
|
@@ -106,6 +113,7 @@ export async function modelsPull(
|
|
|
106
113
|
let skipped = 0;
|
|
107
114
|
|
|
108
115
|
for (const type of types) {
|
|
116
|
+
if (options.signal?.aborted) break;
|
|
109
117
|
const uri =
|
|
110
118
|
type === "expand" ? (preset.expand ?? preset.gen) : preset[type];
|
|
111
119
|
|
|
@@ -133,8 +141,10 @@ export async function modelsPull(
|
|
|
133
141
|
(progress) => {
|
|
134
142
|
options.onProgress?.(type, progress);
|
|
135
143
|
},
|
|
136
|
-
options.force
|
|
144
|
+
options.force,
|
|
145
|
+
options.signal
|
|
137
146
|
);
|
|
147
|
+
if (options.signal?.aborted) break;
|
|
138
148
|
|
|
139
149
|
if (result.ok) {
|
|
140
150
|
results.push({
|
|
@@ -5,6 +5,8 @@
|
|
|
5
5
|
* @module src/cli/commands/query
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
+
import type { RetrievalTraceSurfaceMetadata } from "../../core/retrieval-trace-session";
|
|
9
|
+
import type { RetrievalTraceSession } from "../../core/retrieval-trace-session";
|
|
8
10
|
import type {
|
|
9
11
|
EmbeddingPort,
|
|
10
12
|
GenerationPort,
|
|
@@ -16,6 +18,11 @@ import {
|
|
|
16
18
|
fingerprintContentTypeRules,
|
|
17
19
|
normalizeContentTypes,
|
|
18
20
|
} from "../../config";
|
|
21
|
+
import {
|
|
22
|
+
finishRetrievalTraceAfterError,
|
|
23
|
+
retrievalTraceFilters,
|
|
24
|
+
startRetrievalTraceRequest,
|
|
25
|
+
} from "../../core/retrieval-trace-request";
|
|
19
26
|
import { LlmAdapter } from "../../llm/nodeLlamaCpp/adapter";
|
|
20
27
|
import { resolveDownloadPolicy } from "../../llm/policy";
|
|
21
28
|
import { resolveModelUri } from "../../llm/registry";
|
|
@@ -72,7 +79,11 @@ export interface QueryFormatOptions {
|
|
|
72
79
|
}
|
|
73
80
|
|
|
74
81
|
export type QueryResult =
|
|
75
|
-
| {
|
|
82
|
+
| {
|
|
83
|
+
success: true;
|
|
84
|
+
data: SearchResults;
|
|
85
|
+
metadata?: RetrievalTraceSurfaceMetadata;
|
|
86
|
+
}
|
|
76
87
|
| { success: false; error: string };
|
|
77
88
|
|
|
78
89
|
export type QueryDiagnoseCommandOptions = HybridSearchOptions & {
|
|
@@ -126,8 +137,47 @@ export async function query(
|
|
|
126
137
|
let embedPort: EmbeddingPort | null = null;
|
|
127
138
|
let expandPort: GenerationPort | null = null;
|
|
128
139
|
let rerankPort: RerankPort | null = null;
|
|
140
|
+
let traceSession: RetrievalTraceSession | undefined;
|
|
129
141
|
|
|
130
142
|
try {
|
|
143
|
+
const embedUri = resolveModelUri(
|
|
144
|
+
config,
|
|
145
|
+
"embed",
|
|
146
|
+
options.embedModel,
|
|
147
|
+
options.collection
|
|
148
|
+
);
|
|
149
|
+
const expandUri =
|
|
150
|
+
!options.noExpand && !options.queryModes?.length
|
|
151
|
+
? resolveModelUri(
|
|
152
|
+
config,
|
|
153
|
+
"expand",
|
|
154
|
+
options.expandModel ?? options.genModel,
|
|
155
|
+
options.collection
|
|
156
|
+
)
|
|
157
|
+
: undefined;
|
|
158
|
+
const rerankUri = !options.noRerank
|
|
159
|
+
? resolveModelUri(
|
|
160
|
+
config,
|
|
161
|
+
"rerank",
|
|
162
|
+
options.rerankModel,
|
|
163
|
+
options.collection
|
|
164
|
+
)
|
|
165
|
+
: undefined;
|
|
166
|
+
const traceStart = await startRetrievalTraceRequest({
|
|
167
|
+
store,
|
|
168
|
+
config,
|
|
169
|
+
query: queryText,
|
|
170
|
+
filters: retrievalTraceFilters({ ...options, limit }),
|
|
171
|
+
pipeline: "hybrid",
|
|
172
|
+
indexName: options.indexName,
|
|
173
|
+
modelUris: [embedUri, expandUri, rerankUri].filter(
|
|
174
|
+
(value): value is string => Boolean(value)
|
|
175
|
+
),
|
|
176
|
+
});
|
|
177
|
+
if (!traceStart.ok) {
|
|
178
|
+
return { success: false, error: traceStart.error.message };
|
|
179
|
+
}
|
|
180
|
+
traceSession = traceStart.value ?? undefined;
|
|
131
181
|
const llm = new LlmAdapter(config);
|
|
132
182
|
|
|
133
183
|
// Resolve download policy from env/flags
|
|
@@ -143,12 +193,6 @@ export async function query(
|
|
|
143
193
|
: undefined;
|
|
144
194
|
|
|
145
195
|
// Create embedding port (for vector search)
|
|
146
|
-
const embedUri = resolveModelUri(
|
|
147
|
-
config,
|
|
148
|
-
"embed",
|
|
149
|
-
options.embedModel,
|
|
150
|
-
options.collection
|
|
151
|
-
);
|
|
152
196
|
const embedResult = await llm.createEmbeddingPort(embedUri, {
|
|
153
197
|
policy,
|
|
154
198
|
onProgress: downloadProgress
|
|
@@ -161,13 +205,7 @@ export async function query(
|
|
|
161
205
|
|
|
162
206
|
// Create expansion port - optional.
|
|
163
207
|
// Skip when structured query modes are provided.
|
|
164
|
-
if (
|
|
165
|
-
const expandUri = resolveModelUri(
|
|
166
|
-
config,
|
|
167
|
-
"expand",
|
|
168
|
-
options.expandModel ?? options.genModel,
|
|
169
|
-
options.collection
|
|
170
|
-
);
|
|
208
|
+
if (expandUri) {
|
|
171
209
|
const genResult = await llm.createExpansionPort(expandUri, {
|
|
172
210
|
policy,
|
|
173
211
|
onProgress: downloadProgress
|
|
@@ -180,13 +218,7 @@ export async function query(
|
|
|
180
218
|
}
|
|
181
219
|
|
|
182
220
|
// Create rerank port - optional
|
|
183
|
-
if (
|
|
184
|
-
const rerankUri = resolveModelUri(
|
|
185
|
-
config,
|
|
186
|
-
"rerank",
|
|
187
|
-
options.rerankModel,
|
|
188
|
-
options.collection
|
|
189
|
-
);
|
|
221
|
+
if (rerankUri) {
|
|
190
222
|
const rerankResult = await llm.createRerankPort(rerankUri, {
|
|
191
223
|
policy,
|
|
192
224
|
onProgress: downloadProgress
|
|
@@ -228,19 +260,27 @@ export async function query(
|
|
|
228
260
|
expandPort,
|
|
229
261
|
rerankPort,
|
|
230
262
|
};
|
|
231
|
-
|
|
232
263
|
const result = await searchHybrid(deps, queryText, {
|
|
233
264
|
...options,
|
|
234
265
|
limit,
|
|
266
|
+
traceSession,
|
|
235
267
|
});
|
|
236
268
|
|
|
237
269
|
if (!result.ok) {
|
|
270
|
+
await traceSession?.finish("failed");
|
|
238
271
|
return { success: false, error: result.error.message };
|
|
239
272
|
}
|
|
240
273
|
|
|
241
274
|
return {
|
|
242
275
|
success: true,
|
|
243
276
|
data: decorateSearchResultsForIndex(result.value, options.indexName),
|
|
277
|
+
metadata: traceSession?.metadata(),
|
|
278
|
+
};
|
|
279
|
+
} catch (cause) {
|
|
280
|
+
await finishRetrievalTraceAfterError(traceSession, cause);
|
|
281
|
+
return {
|
|
282
|
+
success: false,
|
|
283
|
+
error: cause instanceof Error ? cause.message : "Hybrid query failed",
|
|
244
284
|
};
|
|
245
285
|
} finally {
|
|
246
286
|
if (embedPort) {
|
|
@@ -439,7 +479,6 @@ export function formatQuery(
|
|
|
439
479
|
})
|
|
440
480
|
: `Error: ${result.error}`;
|
|
441
481
|
}
|
|
442
|
-
|
|
443
482
|
// Output explain to stderr if present (async but best-effort)
|
|
444
483
|
outputExplainToStderr(result.data);
|
|
445
484
|
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
/** Local read-only retrieval replay command. */
|
|
2
|
+
|
|
3
|
+
import type {
|
|
4
|
+
ReplayRetrievalTraceResult,
|
|
5
|
+
RetrievalReplayCandidate,
|
|
6
|
+
} from "../../core/retrieval-trace-management";
|
|
7
|
+
import type {
|
|
8
|
+
EmbeddingPort,
|
|
9
|
+
GenerationPort,
|
|
10
|
+
RerankPort,
|
|
11
|
+
} from "../../llm/types";
|
|
12
|
+
import type { VectorIndexPort } from "../../store/vector";
|
|
13
|
+
|
|
14
|
+
import { RetrievalTraceManagementService } from "../../core/retrieval-trace-management";
|
|
15
|
+
import { LlmAdapter } from "../../llm/nodeLlamaCpp/adapter";
|
|
16
|
+
import { resolveDownloadPolicy } from "../../llm/policy";
|
|
17
|
+
import { resolveModelUri } from "../../llm/registry";
|
|
18
|
+
import { createVectorIndexPort } from "../../store/vector";
|
|
19
|
+
import { CliError } from "../errors";
|
|
20
|
+
import { initStore } from "./shared";
|
|
21
|
+
|
|
22
|
+
export interface TraceReplayCommandOptions {
|
|
23
|
+
configPath?: string;
|
|
24
|
+
indexName?: string;
|
|
25
|
+
format?: "json" | "md";
|
|
26
|
+
offline?: boolean;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const unwrap = <T>(
|
|
30
|
+
value: { ok: true; value: T } | { ok: false; error: { message: string } }
|
|
31
|
+
): T => {
|
|
32
|
+
if (value.ok) return value.value;
|
|
33
|
+
throw new CliError("RUNTIME", value.error.message);
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
const formatMarkdown = (value: ReplayRetrievalTraceResult): string => {
|
|
37
|
+
const lines = [
|
|
38
|
+
`# Retrieval replay \`${value.exportId}\``,
|
|
39
|
+
"",
|
|
40
|
+
`- Verdict: **${value.verdict}**`,
|
|
41
|
+
`- Recommendation: **${value.recommendation}**`,
|
|
42
|
+
`- Applied: **no**`,
|
|
43
|
+
];
|
|
44
|
+
if (value.reason) lines.push(`- Reason: \`${value.reason}\``);
|
|
45
|
+
lines.push(
|
|
46
|
+
"",
|
|
47
|
+
"| Case | Status | Verdict | Baseline recall | Candidate recall |",
|
|
48
|
+
"|---|---|---|---:|---:|"
|
|
49
|
+
);
|
|
50
|
+
for (const item of value.cases) {
|
|
51
|
+
lines.push(
|
|
52
|
+
`| \`${item.caseId}\` | ${item.terminalStatus} | ${item.verdict} | ${item.metrics.baseline.recallAtK.toFixed(3)} | ${item.metrics.candidate.recallAtK.toFixed(3)} |`
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
return `${lines.join("\n")}\n`;
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
export const traceReplay = async (
|
|
59
|
+
exportId: string,
|
|
60
|
+
candidate: RetrievalReplayCandidate,
|
|
61
|
+
options: TraceReplayCommandOptions = {}
|
|
62
|
+
): Promise<string> => {
|
|
63
|
+
const initialized = await initStore({
|
|
64
|
+
configPath: options.configPath,
|
|
65
|
+
indexName: options.indexName,
|
|
66
|
+
syncConfig: false,
|
|
67
|
+
allowEmptyCollections: true,
|
|
68
|
+
});
|
|
69
|
+
if (!initialized.ok) throw new CliError("RUNTIME", initialized.error);
|
|
70
|
+
const { config, store } = initialized;
|
|
71
|
+
let embedPort: EmbeddingPort | null = null;
|
|
72
|
+
let expandPort: GenerationPort | null = null;
|
|
73
|
+
let rerankPort: RerankPort | null = null;
|
|
74
|
+
let vectorIndex: VectorIndexPort | null = null;
|
|
75
|
+
try {
|
|
76
|
+
const needsSemantic = candidate.type !== "bm25";
|
|
77
|
+
const embedUri = needsSemantic
|
|
78
|
+
? resolveModelUri(config, "embed")
|
|
79
|
+
: undefined;
|
|
80
|
+
const expandUri =
|
|
81
|
+
candidate.type === "hybrid" &&
|
|
82
|
+
!candidate.noExpand &&
|
|
83
|
+
!candidate.queryModes?.length
|
|
84
|
+
? resolveModelUri(config, "expand")
|
|
85
|
+
: undefined;
|
|
86
|
+
const rerankUri =
|
|
87
|
+
candidate.type === "hybrid" && !candidate.noRerank
|
|
88
|
+
? resolveModelUri(config, "rerank")
|
|
89
|
+
: undefined;
|
|
90
|
+
const llm = new LlmAdapter(config);
|
|
91
|
+
const policy = resolveDownloadPolicy(process.env, {
|
|
92
|
+
offline: options.offline,
|
|
93
|
+
});
|
|
94
|
+
if (embedUri) {
|
|
95
|
+
const created = await llm.createEmbeddingPort(embedUri, { policy });
|
|
96
|
+
if (created.ok) embedPort = created.value;
|
|
97
|
+
}
|
|
98
|
+
if (expandUri) {
|
|
99
|
+
const created = await llm.createExpansionPort(expandUri, { policy });
|
|
100
|
+
if (created.ok) expandPort = created.value;
|
|
101
|
+
}
|
|
102
|
+
if (rerankUri) {
|
|
103
|
+
const created = await llm.createRerankPort(rerankUri, { policy });
|
|
104
|
+
if (created.ok) rerankPort = created.value;
|
|
105
|
+
}
|
|
106
|
+
if (embedPort && embedUri) {
|
|
107
|
+
const initializedEmbed = await embedPort.init();
|
|
108
|
+
if (initializedEmbed.ok) {
|
|
109
|
+
const created = await createVectorIndexPort(store.getRawDb(), {
|
|
110
|
+
model: embedUri,
|
|
111
|
+
dimensions: embedPort.dimensions(),
|
|
112
|
+
});
|
|
113
|
+
if (created.ok) vectorIndex = created.value;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
const replayed = await new RetrievalTraceManagementService(store).replay(
|
|
117
|
+
{ exportId, candidate },
|
|
118
|
+
{
|
|
119
|
+
config,
|
|
120
|
+
vectorIndex,
|
|
121
|
+
embedPort,
|
|
122
|
+
expandPort,
|
|
123
|
+
rerankPort,
|
|
124
|
+
indexName: options.indexName,
|
|
125
|
+
modelUris: [embedUri, expandUri, rerankUri].filter(
|
|
126
|
+
(value): value is string => Boolean(value)
|
|
127
|
+
),
|
|
128
|
+
}
|
|
129
|
+
);
|
|
130
|
+
const value = unwrap(replayed);
|
|
131
|
+
return options.format === "md"
|
|
132
|
+
? formatMarkdown(value)
|
|
133
|
+
: `${JSON.stringify(value, null, 2)}\n`;
|
|
134
|
+
} finally {
|
|
135
|
+
await embedPort?.dispose();
|
|
136
|
+
await expandPort?.dispose();
|
|
137
|
+
await rerankPort?.dispose();
|
|
138
|
+
await store.close();
|
|
139
|
+
}
|
|
140
|
+
};
|
|
@@ -5,8 +5,16 @@
|
|
|
5
5
|
* @module src/cli/commands/search
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
+
import type {
|
|
9
|
+
RetrievalTraceSession,
|
|
10
|
+
RetrievalTraceSurfaceMetadata,
|
|
11
|
+
} from "../../core/retrieval-trace-session";
|
|
8
12
|
import type { SearchOptions, SearchResults } from "../../pipeline/types";
|
|
9
13
|
|
|
14
|
+
import {
|
|
15
|
+
finishRetrievalTraceAfterError,
|
|
16
|
+
startRetrievalTraceRequest,
|
|
17
|
+
} from "../../core/retrieval-trace-request";
|
|
10
18
|
import { searchBm25 } from "../../pipeline/search";
|
|
11
19
|
import {
|
|
12
20
|
type FormatOptions,
|
|
@@ -38,7 +46,11 @@ export type SearchCommandOptions = SearchOptions & {
|
|
|
38
46
|
};
|
|
39
47
|
|
|
40
48
|
export type SearchResult =
|
|
41
|
-
| {
|
|
49
|
+
| {
|
|
50
|
+
success: true;
|
|
51
|
+
data: SearchResults;
|
|
52
|
+
metadata?: RetrievalTraceSurfaceMetadata;
|
|
53
|
+
}
|
|
42
54
|
| { success: false; error: string; isValidation?: boolean };
|
|
43
55
|
|
|
44
56
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
@@ -68,15 +80,42 @@ export async function search(
|
|
|
68
80
|
return { success: false, error: initResult.error };
|
|
69
81
|
}
|
|
70
82
|
|
|
71
|
-
const { store } = initResult;
|
|
83
|
+
const { store, config } = initResult;
|
|
84
|
+
let traceSession: RetrievalTraceSession | undefined;
|
|
72
85
|
|
|
73
86
|
try {
|
|
87
|
+
const started = await startRetrievalTraceRequest({
|
|
88
|
+
store,
|
|
89
|
+
config,
|
|
90
|
+
query,
|
|
91
|
+
filters: {
|
|
92
|
+
limit,
|
|
93
|
+
collection: options.collection,
|
|
94
|
+
lang: options.lang,
|
|
95
|
+
full: options.full,
|
|
96
|
+
lineNumbers: options.lineNumbers,
|
|
97
|
+
tagsAll: options.tagsAll,
|
|
98
|
+
tagsAny: options.tagsAny,
|
|
99
|
+
since: options.since,
|
|
100
|
+
until: options.until,
|
|
101
|
+
categories: options.categories,
|
|
102
|
+
author: options.author,
|
|
103
|
+
intent: options.intent,
|
|
104
|
+
exclude: options.exclude,
|
|
105
|
+
},
|
|
106
|
+
pipeline: "bm25",
|
|
107
|
+
indexName: options.indexName,
|
|
108
|
+
});
|
|
109
|
+
if (!started.ok) return { success: false, error: started.error.message };
|
|
110
|
+
traceSession = started.value ?? undefined;
|
|
74
111
|
const result = await searchBm25(store, query, {
|
|
75
112
|
...options,
|
|
76
113
|
limit,
|
|
114
|
+
traceSession,
|
|
77
115
|
});
|
|
78
116
|
|
|
79
117
|
if (!result.ok) {
|
|
118
|
+
await traceSession?.finish("failed");
|
|
80
119
|
// Map INVALID_INPUT to validation error for proper exit code
|
|
81
120
|
const isValidation = result.error.code === "INVALID_INPUT";
|
|
82
121
|
return {
|
|
@@ -89,6 +128,13 @@ export async function search(
|
|
|
89
128
|
return {
|
|
90
129
|
success: true,
|
|
91
130
|
data: decorateSearchResultsForIndex(result.value, options.indexName),
|
|
131
|
+
metadata: traceSession?.metadata(),
|
|
132
|
+
};
|
|
133
|
+
} catch (cause) {
|
|
134
|
+
await finishRetrievalTraceAfterError(traceSession, cause);
|
|
135
|
+
return {
|
|
136
|
+
success: false,
|
|
137
|
+
error: cause instanceof Error ? cause.message : "Search failed",
|
|
92
138
|
};
|
|
93
139
|
} finally {
|
|
94
140
|
await store.close();
|
|
@@ -135,7 +181,6 @@ export function formatSearch(
|
|
|
135
181
|
})
|
|
136
182
|
: `Error: ${result.error}`;
|
|
137
183
|
}
|
|
138
|
-
|
|
139
184
|
const formatOpts: FormatOptions = {
|
|
140
185
|
format: getFormatType(options),
|
|
141
186
|
full: options.full,
|
|
@@ -44,6 +44,8 @@ export interface InitStoreOptions {
|
|
|
44
44
|
collection?: string;
|
|
45
45
|
/** Sync collections/contexts from config into DB on open */
|
|
46
46
|
syncConfig?: boolean;
|
|
47
|
+
/** Permit management commands that only need an existing index database */
|
|
48
|
+
allowEmptyCollections?: boolean;
|
|
47
49
|
}
|
|
48
50
|
|
|
49
51
|
/**
|
|
@@ -81,7 +83,7 @@ export async function initStore(
|
|
|
81
83
|
}
|
|
82
84
|
}
|
|
83
85
|
|
|
84
|
-
if (collections.length === 0) {
|
|
86
|
+
if (collections.length === 0 && !options.allowEmptyCollections) {
|
|
85
87
|
return {
|
|
86
88
|
ok: false,
|
|
87
89
|
error: "No collections configured. Run: gno collection add <path>",
|
|
@@ -15,6 +15,7 @@ import { buildActivationStatus } from "../../core/activation-status";
|
|
|
15
15
|
import { ModelCache } from "../../llm/cache";
|
|
16
16
|
import { getActivePreset, resolveModelUri } from "../../llm/registry";
|
|
17
17
|
import { getConnectorVerificationTargets } from "../../serve/connectors";
|
|
18
|
+
import { createStandaloneResidentStatus } from "../../serve/resident-status";
|
|
18
19
|
import { SqliteAdapter } from "../../store/sqlite/adapter";
|
|
19
20
|
|
|
20
21
|
/**
|
|
@@ -277,6 +278,7 @@ export function formatStatus(
|
|
|
277
278
|
const s = result.status;
|
|
278
279
|
return JSON.stringify(
|
|
279
280
|
{
|
|
281
|
+
resident: createStandaloneResidentStatus("direct-cli"),
|
|
280
282
|
indexName: s.indexName,
|
|
281
283
|
configPath: s.configPath,
|
|
282
284
|
dbPath: s.dbPath,
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
/** Local retrieval-trace inspection, feedback, export, and deletion commands. */
|
|
2
|
+
|
|
3
|
+
import type {
|
|
4
|
+
RetrievalTraceDeleteResult,
|
|
5
|
+
RetrievalTraceDetail,
|
|
6
|
+
RetrievalTraceLabelRequest,
|
|
7
|
+
RetrievalTraceLabelResult,
|
|
8
|
+
RetrievalTraceListResult,
|
|
9
|
+
RetrievalTracePurgeResult,
|
|
10
|
+
} from "../../core/retrieval-trace-management";
|
|
11
|
+
import type { StoreResult } from "../../store/types";
|
|
12
|
+
|
|
13
|
+
import { atomicWrite } from "../../core/file-ops";
|
|
14
|
+
import { RetrievalTraceManagementService } from "../../core/retrieval-trace-management";
|
|
15
|
+
import { CliError } from "../errors";
|
|
16
|
+
import { initStore } from "./shared";
|
|
17
|
+
|
|
18
|
+
export type TraceOutputFormat = "json" | "md";
|
|
19
|
+
|
|
20
|
+
interface TraceCommandOptions {
|
|
21
|
+
configPath?: string;
|
|
22
|
+
indexName?: string;
|
|
23
|
+
format?: TraceOutputFormat;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
interface TraceExportOptions extends TraceCommandOptions {
|
|
27
|
+
output?: string;
|
|
28
|
+
exportFormat?: "agentic-receipt" | "qrels";
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const unwrapTraceResult = <T>(result: StoreResult<T>): T => {
|
|
32
|
+
if (result.ok) {
|
|
33
|
+
return result.value;
|
|
34
|
+
}
|
|
35
|
+
throw new CliError(
|
|
36
|
+
result.error.code === "INVALID_INPUT" ? "VALIDATION" : "RUNTIME",
|
|
37
|
+
result.error.message,
|
|
38
|
+
{ details: { traceCode: result.error.code } }
|
|
39
|
+
);
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
const withTraceService = async <T>(
|
|
43
|
+
options: TraceCommandOptions,
|
|
44
|
+
operation: (service: RetrievalTraceManagementService) => Promise<T>
|
|
45
|
+
): Promise<T> => {
|
|
46
|
+
const initialized = await initStore({
|
|
47
|
+
configPath: options.configPath,
|
|
48
|
+
indexName: options.indexName,
|
|
49
|
+
syncConfig: false,
|
|
50
|
+
allowEmptyCollections: true,
|
|
51
|
+
});
|
|
52
|
+
if (!initialized.ok) {
|
|
53
|
+
throw new CliError("RUNTIME", initialized.error);
|
|
54
|
+
}
|
|
55
|
+
try {
|
|
56
|
+
return await operation(
|
|
57
|
+
new RetrievalTraceManagementService(initialized.store)
|
|
58
|
+
);
|
|
59
|
+
} finally {
|
|
60
|
+
await initialized.store.close();
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
const json = (value: unknown): string => `${JSON.stringify(value, null, 2)}\n`;
|
|
65
|
+
|
|
66
|
+
const formatListMarkdown = (value: RetrievalTraceListResult): string => {
|
|
67
|
+
const lines = [
|
|
68
|
+
"# Retrieval traces",
|
|
69
|
+
"",
|
|
70
|
+
"| Trace | Status | Mode | Created |",
|
|
71
|
+
"|---|---|---|---|",
|
|
72
|
+
];
|
|
73
|
+
for (const trace of value.traces) {
|
|
74
|
+
lines.push(
|
|
75
|
+
`| \`${trace.traceId}\` | ${trace.status} | ${trace.redactionMode} | ${new Date(trace.createdAtMs).toISOString()} |`
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
if (value.traces.length === 0) {
|
|
79
|
+
lines.push("| — | — | — | — |");
|
|
80
|
+
}
|
|
81
|
+
if (value.nextCursor) {
|
|
82
|
+
lines.push("", `Next cursor: \`${value.nextCursor}\``);
|
|
83
|
+
}
|
|
84
|
+
return `${lines.join("\n")}\n`;
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
const formatDetailMarkdown = (value: RetrievalTraceDetail): string => {
|
|
88
|
+
const trace = value.trace;
|
|
89
|
+
const lines = [
|
|
90
|
+
`# Retrieval trace \`${trace.traceId}\``,
|
|
91
|
+
"",
|
|
92
|
+
`- Status: **${trace.status}**`,
|
|
93
|
+
`- Redaction: **${trace.redactionMode}**`,
|
|
94
|
+
`- Replay capable: **${trace.replayCapable ? "yes" : "no"}**`,
|
|
95
|
+
`- Created: ${new Date(trace.createdAtMs).toISOString()}`,
|
|
96
|
+
`- Updated: ${new Date(trace.updatedAtMs).toISOString()}`,
|
|
97
|
+
"",
|
|
98
|
+
"## Records",
|
|
99
|
+
"",
|
|
100
|
+
`- Runs: ${value.runs.length}`,
|
|
101
|
+
`- Events: ${value.events.length}`,
|
|
102
|
+
`- Judgments: ${value.judgments.length}`,
|
|
103
|
+
`- Exports: ${value.exports.length}`,
|
|
104
|
+
];
|
|
105
|
+
const truncatedSections = Object.entries(value.truncated)
|
|
106
|
+
.filter(([, truncated]) => truncated)
|
|
107
|
+
.map(([section]) => section);
|
|
108
|
+
if (truncatedSections.length > 0) {
|
|
109
|
+
lines.push(
|
|
110
|
+
"",
|
|
111
|
+
`Detail truncated: ${truncatedSections.join(", ")}. Request a larger bounded detail limit.`
|
|
112
|
+
);
|
|
113
|
+
}
|
|
114
|
+
return `${lines.join("\n")}\n`;
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
const formatMutationMarkdown = (
|
|
118
|
+
heading: string,
|
|
119
|
+
value:
|
|
120
|
+
| RetrievalTraceLabelResult
|
|
121
|
+
| RetrievalTraceDeleteResult
|
|
122
|
+
| RetrievalTracePurgeResult
|
|
123
|
+
): string =>
|
|
124
|
+
`# ${heading}\n\n\`\`\`json\n${JSON.stringify(value, null, 2)}\n\`\`\`\n`;
|
|
125
|
+
|
|
126
|
+
export const traceList = async (
|
|
127
|
+
input: { limit?: number; cursor?: string },
|
|
128
|
+
options: TraceCommandOptions = {}
|
|
129
|
+
): Promise<string> => {
|
|
130
|
+
const value = await withTraceService(options, async (service) =>
|
|
131
|
+
unwrapTraceResult(await service.list(input))
|
|
132
|
+
);
|
|
133
|
+
return options.format === "md" ? formatListMarkdown(value) : json(value);
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
export const traceShow = async (
|
|
137
|
+
traceId: string,
|
|
138
|
+
input: { detailLimit?: number },
|
|
139
|
+
options: TraceCommandOptions = {}
|
|
140
|
+
): Promise<string> => {
|
|
141
|
+
const value = await withTraceService(options, async (service) =>
|
|
142
|
+
unwrapTraceResult(await service.show(traceId, input))
|
|
143
|
+
);
|
|
144
|
+
return options.format === "md" ? formatDetailMarkdown(value) : json(value);
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
export const traceLabel = async (
|
|
148
|
+
input: RetrievalTraceLabelRequest,
|
|
149
|
+
options: TraceCommandOptions = {}
|
|
150
|
+
): Promise<string> => {
|
|
151
|
+
const value = await withTraceService(options, async (service) =>
|
|
152
|
+
unwrapTraceResult(await service.label(input))
|
|
153
|
+
);
|
|
154
|
+
return options.format === "md"
|
|
155
|
+
? formatMutationMarkdown("Retrieval trace judgment", value)
|
|
156
|
+
: json(value);
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
export const traceExport = async (
|
|
160
|
+
traceIds: string[],
|
|
161
|
+
options: TraceExportOptions = {}
|
|
162
|
+
): Promise<string> => {
|
|
163
|
+
const value = await withTraceService(options, async (service) =>
|
|
164
|
+
unwrapTraceResult(
|
|
165
|
+
await service.export({
|
|
166
|
+
traceIds,
|
|
167
|
+
format: options.exportFormat ?? "agentic-receipt",
|
|
168
|
+
})
|
|
169
|
+
)
|
|
170
|
+
);
|
|
171
|
+
const artifact = json(value.artifact);
|
|
172
|
+
if (options.output) {
|
|
173
|
+
await atomicWrite(options.output, artifact);
|
|
174
|
+
return "";
|
|
175
|
+
}
|
|
176
|
+
return json(value);
|
|
177
|
+
};
|
|
178
|
+
|
|
179
|
+
export const traceDelete = async (
|
|
180
|
+
traceId: string,
|
|
181
|
+
options: TraceCommandOptions = {}
|
|
182
|
+
): Promise<string> => {
|
|
183
|
+
const value = await withTraceService(options, async (service) =>
|
|
184
|
+
unwrapTraceResult(await service.delete(traceId))
|
|
185
|
+
);
|
|
186
|
+
return options.format === "md"
|
|
187
|
+
? formatMutationMarkdown("Retrieval trace deleted", value)
|
|
188
|
+
: json(value);
|
|
189
|
+
};
|
|
190
|
+
|
|
191
|
+
export const tracePurge = async (
|
|
192
|
+
options: TraceCommandOptions = {}
|
|
193
|
+
): Promise<string> => {
|
|
194
|
+
const value = await withTraceService(options, async (service) =>
|
|
195
|
+
unwrapTraceResult(await service.purge())
|
|
196
|
+
);
|
|
197
|
+
return options.format === "md"
|
|
198
|
+
? formatMutationMarkdown("Retrieval traces purged", value)
|
|
199
|
+
: json(value);
|
|
200
|
+
};
|