@vellumai/assistant 0.10.7-dev.202607100313.fffdfea → 0.10.7-dev.202607100720.fba456e
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/openapi.yaml +846 -27
- package/package.json +1 -1
- package/src/__tests__/config-watcher-skill-reseed.test.ts +1 -0
- package/src/__tests__/config-watcher.test.ts +22 -4
- package/src/__tests__/credential-security-invariants.test.ts +1 -1
- package/src/__tests__/plugin-import-boundary-guard.test.ts +6 -9
- package/src/__tests__/plugin-import-boundary-reverse-guard.test.ts +0 -2
- package/src/cli/commands/__tests__/inference-models.test.ts +86 -0
- package/src/cli/commands/__tests__/inference-profiles.test.ts +254 -0
- package/src/cli/commands/__tests__/inference-providers.test.ts +171 -0
- package/src/cli/commands/__tests__/schedules.test.ts +133 -1
- package/src/cli/commands/__tests__/usage.test.ts +131 -0
- package/src/cli/commands/inference-callsites.ts +158 -0
- package/src/cli/commands/inference-models.ts +84 -0
- package/src/cli/commands/inference-profiles.ts +371 -0
- package/src/cli/commands/inference-providers-default.ts +89 -0
- package/src/cli/commands/inference-providers.ts +87 -16
- package/src/cli/commands/inference.ts +6 -0
- package/src/cli/commands/memory/nodes.ts +90 -50
- package/src/cli/commands/schedules.ts +17 -2
- package/src/cli/commands/usage.ts +59 -21
- package/src/cli/lib/__tests__/cli-output.test.ts +20 -0
- package/src/cli/lib/cli-output.ts +55 -0
- package/src/config/__tests__/loader-last-known-good.test.ts +229 -0
- package/src/config/__tests__/loader-sparse-array-cleanup.test.ts +166 -0
- package/src/config/default-profile-catalog.ts +28 -0
- package/src/config/loader.ts +276 -46
- package/src/daemon/config-watcher.ts +65 -20
- package/src/daemon/message-types/sync.ts +1 -0
- package/src/oauth/byo-connection.test.ts +1 -1
- package/src/oauth/connection-resolver.test.ts +140 -0
- package/src/oauth/connection-resolver.ts +48 -12
- package/src/plugins/defaults/memory/anisotropy.ts +1 -1
- package/src/plugins/defaults/memory/config.ts +1 -1
- package/src/plugins/defaults/memory/context-search/agent-protocol.ts +1 -1
- package/src/plugins/defaults/memory/context-search/search.ts +1 -1
- package/src/plugins/defaults/memory/graph/bootstrap.ts +1 -1
- package/src/plugins/defaults/memory/graph/consolidation.ts +1 -1
- package/src/plugins/defaults/memory/graph/conversation-graph-memory.ts +11 -1
- package/src/plugins/defaults/memory/graph/extraction.ts +1 -1
- package/src/plugins/defaults/memory/graph/narrative.ts +1 -1
- package/src/plugins/defaults/memory/graph/pattern-scan.ts +1 -1
- package/src/plugins/defaults/memory/graph/tool-handlers.ts +1 -1
- package/src/plugins/defaults/memory/graph/tools.ts +1 -1
- package/src/plugins/defaults/memory/graph-topology/build-memory-graph.ts +43 -16
- package/src/plugins/defaults/memory/hooks/user-prompt-submit.ts +1 -1
- package/src/plugins/defaults/memory/host-utils.ts +80 -0
- package/src/plugins/defaults/memory/identity-context.ts +1 -1
- package/src/plugins/defaults/memory/injectors.ts +1 -1
- package/src/plugins/defaults/memory/jobs/embed-concept-page.ts +2 -2
- package/src/plugins/defaults/memory/jobs-worker.ts +2 -9
- package/src/plugins/defaults/memory/memory-retrospective-state.ts +1 -1
- package/src/plugins/defaults/memory/paths.ts +48 -0
- package/src/plugins/defaults/memory/pkb/autoinject.ts +1 -1
- package/src/plugins/defaults/memory/pkb/context.ts +2 -2
- package/src/plugins/defaults/memory/pkb/types.ts +1 -1
- package/src/plugins/defaults/memory/pkb-schedule.ts +2 -2
- package/src/plugins/defaults/memory/rerank-local.ts +2 -3
- package/src/plugins/defaults/memory/routes/memory-eval-routes.ts +1 -1
- package/src/plugins/defaults/memory/routes/memory-item-routes.test.ts +149 -1
- package/src/plugins/defaults/memory/routes/memory-item-routes.ts +144 -1
- package/src/plugins/defaults/memory/routes/memory-v2-routes.ts +1 -1
- package/src/plugins/defaults/memory/startup.ts +1 -1
- package/src/plugins/defaults/memory/v2/backfill-jobs.ts +1 -1
- package/src/plugins/defaults/memory/v2/consolidation-job.ts +2 -2
- package/src/plugins/defaults/memory/v2/injection.ts +1 -1
- package/src/plugins/defaults/memory/v2/memory-v2-startup.ts +1 -1
- package/src/plugins/defaults/memory/v2/prompts/consolidation.ts +1 -1
- package/src/plugins/defaults/memory/v2/qdrant.ts +1 -1
- package/src/plugins/defaults/memory/v2/reranker.ts +1 -1
- package/src/plugins/defaults/memory/v2/static-context.ts +1 -1
- package/src/plugins/defaults/memory/v2/sweep-job.ts +1 -1
- package/src/plugins/defaults/memory/v3/candidate-match.ts +1 -1
- package/src/plugins/defaults/memory/v3/maintain-job.ts +1 -1
- package/src/plugins/defaults/memory/v3/page-content.ts +1 -1
- package/src/plugins/defaults/memory/v3/pool-select.ts +1 -2
- package/src/plugins/defaults/memory/v3/selection-log-store.ts +1 -1
- package/src/plugins/defaults/memory/v3/shadow-plugin.ts +2 -5
- package/src/plugins/defaults/memory/worker.ts +1 -1
- package/src/plugins/defaults/workspace/injectors.ts +17 -15
- package/src/plugins/types.ts +9 -14
- package/src/providers/inference/connection-availability.ts +193 -0
- package/src/runtime/routes/__tests__/inference-callsites-routes.test.ts +54 -0
- package/src/runtime/routes/__tests__/inference-models-routes.test.ts +54 -0
- package/src/runtime/routes/__tests__/inference-profiles-routes.test.ts +405 -0
- package/src/runtime/routes/__tests__/inference-routes-registration.test.ts +42 -0
- package/src/runtime/routes/__tests__/theme-routes.test.ts +66 -0
- package/src/runtime/routes/conversation-query-routes.ts +5 -3
- package/src/runtime/routes/default-provider-routes.ts +5 -152
- package/src/runtime/routes/index.ts +8 -0
- package/src/runtime/routes/inference-callsites-routes.ts +211 -0
- package/src/runtime/routes/inference-models-routes.ts +104 -0
- package/src/runtime/routes/inference-profiles-routes.ts +717 -0
- package/src/runtime/routes/theme-routes.ts +36 -0
- package/src/runtime/sync/resource-sync-events.ts +4 -0
- package/src/theme/workspace-theme.test.ts +269 -0
- package/src/theme/workspace-theme.ts +304 -0
- package/src/watcher/__tests__/engine-auth-notify.test.ts +316 -0
- package/src/watcher/engine.ts +87 -2
|
@@ -64,7 +64,9 @@ async function runCommand(
|
|
|
64
64
|
registerSchedulesCommand(program);
|
|
65
65
|
await program.parseAsync(["node", "assistant", ...args]);
|
|
66
66
|
} catch {
|
|
67
|
-
if (process.exitCode === 0)
|
|
67
|
+
if (process.exitCode === 0) {
|
|
68
|
+
process.exitCode = 1;
|
|
69
|
+
}
|
|
68
70
|
} finally {
|
|
69
71
|
process.stdout.write = originalStdoutWrite;
|
|
70
72
|
}
|
|
@@ -469,6 +471,136 @@ describe("schedules runs", () => {
|
|
|
469
471
|
expect(logLines.join("\n")).toContain("conversation-1");
|
|
470
472
|
});
|
|
471
473
|
|
|
474
|
+
test("renders a COST column with the shared variable-precision USD format", async () => {
|
|
475
|
+
mockIpcResult = {
|
|
476
|
+
ok: true,
|
|
477
|
+
result: {
|
|
478
|
+
runs: [
|
|
479
|
+
{
|
|
480
|
+
id: "run-1",
|
|
481
|
+
jobId: "schedule-1",
|
|
482
|
+
status: "ok",
|
|
483
|
+
startedAt: 1_778_799_000_000,
|
|
484
|
+
finishedAt: 1_778_799_002_500,
|
|
485
|
+
durationMs: 2_500,
|
|
486
|
+
output: "done",
|
|
487
|
+
error: null,
|
|
488
|
+
conversationId: "conversation-1",
|
|
489
|
+
estimatedCostUsd: 0.0123,
|
|
490
|
+
createdAt: 1_778_799_000_000,
|
|
491
|
+
},
|
|
492
|
+
{
|
|
493
|
+
id: "run-subcent",
|
|
494
|
+
jobId: "schedule-1",
|
|
495
|
+
status: "ok",
|
|
496
|
+
startedAt: 1_778_799_000_000,
|
|
497
|
+
finishedAt: 1_778_799_002_500,
|
|
498
|
+
durationMs: 2_500,
|
|
499
|
+
output: "done",
|
|
500
|
+
error: null,
|
|
501
|
+
conversationId: "conversation-1",
|
|
502
|
+
estimatedCostUsd: 0.0042,
|
|
503
|
+
createdAt: 1_778_799_000_000,
|
|
504
|
+
},
|
|
505
|
+
],
|
|
506
|
+
},
|
|
507
|
+
};
|
|
508
|
+
|
|
509
|
+
const { exitCode } = await runCommand(["schedules", "runs", "schedule-1"]);
|
|
510
|
+
|
|
511
|
+
expect(exitCode).toBe(0);
|
|
512
|
+
const output = logLines.join("\n");
|
|
513
|
+
expect(output).toContain("COST");
|
|
514
|
+
// Matches usage totals/daily/breakdown: 2dp at a cent or more, 6dp sub-cent.
|
|
515
|
+
expect(output).toContain("$0.01");
|
|
516
|
+
expect(output).toContain("$0.004200");
|
|
517
|
+
});
|
|
518
|
+
|
|
519
|
+
test("renders — for zero or unpriced run cost", async () => {
|
|
520
|
+
mockIpcResult = {
|
|
521
|
+
ok: true,
|
|
522
|
+
result: {
|
|
523
|
+
runs: [
|
|
524
|
+
{
|
|
525
|
+
id: "run-zero",
|
|
526
|
+
jobId: "schedule-1",
|
|
527
|
+
status: "ok",
|
|
528
|
+
startedAt: 1_778_799_000_000,
|
|
529
|
+
finishedAt: 1_778_799_002_500,
|
|
530
|
+
durationMs: 2_500,
|
|
531
|
+
output: "done",
|
|
532
|
+
error: null,
|
|
533
|
+
conversationId: "conversation-1",
|
|
534
|
+
estimatedCostUsd: 0,
|
|
535
|
+
createdAt: 1_778_799_000_000,
|
|
536
|
+
},
|
|
537
|
+
{
|
|
538
|
+
id: "run-unpriced",
|
|
539
|
+
jobId: "schedule-1",
|
|
540
|
+
status: "ok",
|
|
541
|
+
startedAt: 1_778_799_000_000,
|
|
542
|
+
finishedAt: 1_778_799_002_500,
|
|
543
|
+
durationMs: 2_500,
|
|
544
|
+
output: "done",
|
|
545
|
+
error: null,
|
|
546
|
+
conversationId: "conversation-1",
|
|
547
|
+
createdAt: 1_778_799_000_000,
|
|
548
|
+
},
|
|
549
|
+
],
|
|
550
|
+
},
|
|
551
|
+
};
|
|
552
|
+
|
|
553
|
+
const { exitCode } = await runCommand(["schedules", "runs", "schedule-1"]);
|
|
554
|
+
|
|
555
|
+
expect(exitCode).toBe(0);
|
|
556
|
+
const output = logLines.join("\n");
|
|
557
|
+
expect(output).toContain("COST");
|
|
558
|
+
expect(output).not.toContain("$");
|
|
559
|
+
const runZeroLine = logLines.find((line) => line.includes("run-zero"));
|
|
560
|
+
const runUnpricedLine = logLines.find((line) =>
|
|
561
|
+
line.includes("run-unpriced"),
|
|
562
|
+
);
|
|
563
|
+
expect(runZeroLine).toContain("—");
|
|
564
|
+
expect(runUnpricedLine).toContain("—");
|
|
565
|
+
});
|
|
566
|
+
|
|
567
|
+
test("includes estimatedCostUsd in --json output", async () => {
|
|
568
|
+
mockIpcResult = {
|
|
569
|
+
ok: true,
|
|
570
|
+
result: {
|
|
571
|
+
runs: [
|
|
572
|
+
{
|
|
573
|
+
id: "run-1",
|
|
574
|
+
jobId: "schedule-1",
|
|
575
|
+
status: "ok",
|
|
576
|
+
startedAt: 1_778_799_000_000,
|
|
577
|
+
finishedAt: 1_778_799_002_500,
|
|
578
|
+
durationMs: 2_500,
|
|
579
|
+
output: "done",
|
|
580
|
+
error: null,
|
|
581
|
+
conversationId: "conversation-1",
|
|
582
|
+
estimatedCostUsd: 0.0123,
|
|
583
|
+
createdAt: 1_778_799_000_000,
|
|
584
|
+
},
|
|
585
|
+
],
|
|
586
|
+
},
|
|
587
|
+
};
|
|
588
|
+
|
|
589
|
+
const { stdout, exitCode } = await runCommand([
|
|
590
|
+
"schedules",
|
|
591
|
+
"runs",
|
|
592
|
+
"schedule-1",
|
|
593
|
+
"--json",
|
|
594
|
+
]);
|
|
595
|
+
|
|
596
|
+
expect(exitCode).toBe(0);
|
|
597
|
+
expect(JSON.parse(stdout)).toEqual({
|
|
598
|
+
runs: [
|
|
599
|
+
expect.objectContaining({ id: "run-1", estimatedCostUsd: 0.0123 }),
|
|
600
|
+
],
|
|
601
|
+
});
|
|
602
|
+
});
|
|
603
|
+
|
|
472
604
|
test("formats run durations into human-friendly units", async () => {
|
|
473
605
|
const durations = [
|
|
474
606
|
{ ms: 450, expected: "450ms" },
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import { beforeEach, describe, expect, mock, test } from "bun:test";
|
|
2
|
+
|
|
3
|
+
import { Command } from "commander";
|
|
4
|
+
|
|
5
|
+
let ipcCalls: Array<{ method: string; params?: Record<string, unknown> }> = [];
|
|
6
|
+
let mockIpcResult: {
|
|
7
|
+
ok: boolean;
|
|
8
|
+
result?: unknown;
|
|
9
|
+
error?: string;
|
|
10
|
+
} = { ok: true, result: {} };
|
|
11
|
+
let exitFromIpcResultCalls: unknown[] = [];
|
|
12
|
+
|
|
13
|
+
mock.module("../../../ipc/cli-client.js", () => ({
|
|
14
|
+
cliIpcCall: async (method: string, params?: Record<string, unknown>) => {
|
|
15
|
+
ipcCalls.push({ method, params });
|
|
16
|
+
return mockIpcResult;
|
|
17
|
+
},
|
|
18
|
+
exitFromIpcResult: (result: unknown) => {
|
|
19
|
+
exitFromIpcResultCalls.push(result);
|
|
20
|
+
process.exitCode = 10;
|
|
21
|
+
throw new Error("exitFromIpcResult called");
|
|
22
|
+
},
|
|
23
|
+
}));
|
|
24
|
+
|
|
25
|
+
mock.module("../../../util/logger.js", () => ({
|
|
26
|
+
getLogger: () => ({
|
|
27
|
+
info: () => {},
|
|
28
|
+
warn: () => {},
|
|
29
|
+
error: () => {},
|
|
30
|
+
debug: () => {},
|
|
31
|
+
}),
|
|
32
|
+
getCliLogger: () => ({
|
|
33
|
+
info: () => {},
|
|
34
|
+
warn: () => {},
|
|
35
|
+
error: () => {},
|
|
36
|
+
debug: () => {},
|
|
37
|
+
}),
|
|
38
|
+
}));
|
|
39
|
+
|
|
40
|
+
const { registerUsageCommand } = await import("../usage.js");
|
|
41
|
+
|
|
42
|
+
async function runCommand(args: string[]): Promise<{ exitCode: number }> {
|
|
43
|
+
process.exitCode = 0;
|
|
44
|
+
try {
|
|
45
|
+
const program = new Command();
|
|
46
|
+
program.exitOverride();
|
|
47
|
+
program.configureOutput({
|
|
48
|
+
writeErr: () => {},
|
|
49
|
+
writeOut: () => {},
|
|
50
|
+
});
|
|
51
|
+
registerUsageCommand(program);
|
|
52
|
+
await program.parseAsync(["node", "assistant", "usage", ...args]);
|
|
53
|
+
} catch {
|
|
54
|
+
if (process.exitCode === 0) {
|
|
55
|
+
process.exitCode = 1;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
const exitCode = process.exitCode ?? 0;
|
|
59
|
+
process.exitCode = 0;
|
|
60
|
+
return { exitCode };
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function lastQueryParams(): Record<string, unknown> {
|
|
64
|
+
const call = ipcCalls.at(-1);
|
|
65
|
+
return (call?.params?.queryParams ?? {}) as Record<string, unknown>;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
describe("usage --schedule option", () => {
|
|
69
|
+
beforeEach(() => {
|
|
70
|
+
ipcCalls = [];
|
|
71
|
+
exitFromIpcResultCalls = [];
|
|
72
|
+
mockIpcResult = { ok: true, result: {} };
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
test("totals forwards --schedule as scheduleId", async () => {
|
|
76
|
+
mockIpcResult = { ok: true, result: { buckets: [] } };
|
|
77
|
+
await runCommand([
|
|
78
|
+
"totals",
|
|
79
|
+
"--range",
|
|
80
|
+
"all",
|
|
81
|
+
"--schedule",
|
|
82
|
+
"sched-abc123",
|
|
83
|
+
]);
|
|
84
|
+
expect(ipcCalls.at(-1)?.method).toBe("usage_totals");
|
|
85
|
+
expect(lastQueryParams().scheduleId).toBe("sched-abc123");
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
test("totals omits scheduleId when --schedule absent", async () => {
|
|
89
|
+
await runCommand(["totals", "--range", "all"]);
|
|
90
|
+
expect(ipcCalls.at(-1)?.method).toBe("usage_totals");
|
|
91
|
+
expect(lastQueryParams()).not.toHaveProperty("scheduleId");
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
test("daily forwards --schedule as scheduleId", async () => {
|
|
95
|
+
mockIpcResult = { ok: true, result: { buckets: [] } };
|
|
96
|
+
await runCommand(["daily", "--range", "all", "--schedule", "sched-abc123"]);
|
|
97
|
+
expect(ipcCalls.at(-1)?.method).toBe("usage_daily");
|
|
98
|
+
expect(lastQueryParams().scheduleId).toBe("sched-abc123");
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
test("daily omits scheduleId when --schedule absent", async () => {
|
|
102
|
+
mockIpcResult = { ok: true, result: { buckets: [] } };
|
|
103
|
+
await runCommand(["daily", "--range", "all"]);
|
|
104
|
+
expect(ipcCalls.at(-1)?.method).toBe("usage_daily");
|
|
105
|
+
expect(lastQueryParams()).not.toHaveProperty("scheduleId");
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
test("breakdown forwards --schedule as scheduleId alongside groupBy", async () => {
|
|
109
|
+
mockIpcResult = { ok: true, result: { breakdown: [] } };
|
|
110
|
+
await runCommand([
|
|
111
|
+
"breakdown",
|
|
112
|
+
"--range",
|
|
113
|
+
"all",
|
|
114
|
+
"--group-by",
|
|
115
|
+
"provider",
|
|
116
|
+
"--schedule",
|
|
117
|
+
"sched-abc123",
|
|
118
|
+
]);
|
|
119
|
+
expect(ipcCalls.at(-1)?.method).toBe("usage_breakdown");
|
|
120
|
+
const qp = lastQueryParams();
|
|
121
|
+
expect(qp.scheduleId).toBe("sched-abc123");
|
|
122
|
+
expect(qp.groupBy).toBe("provider");
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
test("breakdown omits scheduleId when --schedule absent", async () => {
|
|
126
|
+
mockIpcResult = { ok: true, result: { breakdown: [] } };
|
|
127
|
+
await runCommand(["breakdown", "--range", "all", "--group-by", "provider"]);
|
|
128
|
+
expect(ipcCalls.at(-1)?.method).toBe("usage_breakdown");
|
|
129
|
+
expect(lastQueryParams()).not.toHaveProperty("scheduleId");
|
|
130
|
+
});
|
|
131
|
+
});
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `assistant inference callsites` CLI namespace.
|
|
3
|
+
*
|
|
4
|
+
* callsites list — effective resolution for every call site
|
|
5
|
+
* callsites get <site> — resolution detail + chain for one call site
|
|
6
|
+
*
|
|
7
|
+
* Read-only. Delegates to the daemon (`inference_callsites_*`), which computes
|
|
8
|
+
* resolution via the shared `resolveCallSiteConfig` / `selectWinningProfile`
|
|
9
|
+
* machinery. Output is deliberately compact and aligned — it is mostly read by
|
|
10
|
+
* the assistant itself.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import type { Command } from "commander";
|
|
14
|
+
|
|
15
|
+
import { cliIpcCall } from "../../ipc/cli-client.js";
|
|
16
|
+
import { renderTable, writeCliError, writeLine } from "../lib/cli-output.js";
|
|
17
|
+
|
|
18
|
+
type Source = "override" | "active" | "call_site" | "default";
|
|
19
|
+
|
|
20
|
+
interface CallSiteSummary {
|
|
21
|
+
callSite: string;
|
|
22
|
+
profile: string | null;
|
|
23
|
+
label: string | null;
|
|
24
|
+
source: Source;
|
|
25
|
+
provider: string;
|
|
26
|
+
model: string;
|
|
27
|
+
effort: string;
|
|
28
|
+
maxTokens: number;
|
|
29
|
+
maxInputTokens?: number;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
interface CallSiteDetail {
|
|
33
|
+
callSite: string;
|
|
34
|
+
winner: { profile: string | null; label: string | null; source: Source };
|
|
35
|
+
resolved: {
|
|
36
|
+
provider: string;
|
|
37
|
+
model: string;
|
|
38
|
+
maxTokens: number;
|
|
39
|
+
effort: string;
|
|
40
|
+
temperature: number | null;
|
|
41
|
+
maxInputTokens?: number;
|
|
42
|
+
};
|
|
43
|
+
resolutionChain: { requested: string; reason: string }[];
|
|
44
|
+
shippedDefault: Record<string, unknown>;
|
|
45
|
+
userPin: Record<string, unknown> | null;
|
|
46
|
+
resolutionError?: { reason: string; message: string };
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const SOURCE_LABEL: Record<Source, string> = {
|
|
50
|
+
override: "override",
|
|
51
|
+
active: "active",
|
|
52
|
+
call_site: "pin",
|
|
53
|
+
default: "default",
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
function formatCtxIn(tokens: number | undefined): string {
|
|
57
|
+
if (tokens == null) {
|
|
58
|
+
return "-";
|
|
59
|
+
}
|
|
60
|
+
if (tokens >= 1_000_000 && tokens % 1_000_000 === 0) {
|
|
61
|
+
return `${tokens / 1_000_000}M`;
|
|
62
|
+
}
|
|
63
|
+
return tokens.toLocaleString();
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export function attachCallsitesSubcommand(inference: Command): void {
|
|
67
|
+
const callsites = inference
|
|
68
|
+
.command("callsites")
|
|
69
|
+
.description("Inspect how each LLM call site resolves to a profile");
|
|
70
|
+
|
|
71
|
+
callsites
|
|
72
|
+
.command("list")
|
|
73
|
+
.description("List the effective resolution for every call site")
|
|
74
|
+
.option("--json", "Output as machine-readable JSON")
|
|
75
|
+
.action(async (opts: { json?: boolean }) => {
|
|
76
|
+
const ipcResult = await cliIpcCall<{ callSites: CallSiteSummary[] }>(
|
|
77
|
+
"inference_callsites_list",
|
|
78
|
+
{},
|
|
79
|
+
);
|
|
80
|
+
if (!ipcResult.ok) {
|
|
81
|
+
writeCliError(ipcResult.error ?? "Unknown error", opts.json);
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
const rows = ipcResult.result!.callSites;
|
|
85
|
+
if (opts.json) {
|
|
86
|
+
process.stdout.write(
|
|
87
|
+
JSON.stringify({ ok: true, callSites: rows }) + "\n",
|
|
88
|
+
);
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
renderTable(
|
|
92
|
+
[
|
|
93
|
+
"CALL SITE",
|
|
94
|
+
"PROFILE",
|
|
95
|
+
"SRC",
|
|
96
|
+
"PROVIDER",
|
|
97
|
+
"MODEL",
|
|
98
|
+
"EFFORT",
|
|
99
|
+
"CTX-IN",
|
|
100
|
+
],
|
|
101
|
+
rows.map((r) => [
|
|
102
|
+
r.callSite,
|
|
103
|
+
r.profile ?? "(anchor)",
|
|
104
|
+
SOURCE_LABEL[r.source],
|
|
105
|
+
r.provider,
|
|
106
|
+
r.model,
|
|
107
|
+
r.effort,
|
|
108
|
+
formatCtxIn(r.maxInputTokens),
|
|
109
|
+
]),
|
|
110
|
+
);
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
callsites
|
|
114
|
+
.command("get <site>")
|
|
115
|
+
.description("Show the resolution detail and chain for one call site")
|
|
116
|
+
.option("--json", "Output as machine-readable JSON")
|
|
117
|
+
.action(async (site: string, opts: { json?: boolean }) => {
|
|
118
|
+
const ipcResult = await cliIpcCall<CallSiteDetail>(
|
|
119
|
+
"inference_callsites_get",
|
|
120
|
+
{ pathParams: { site } },
|
|
121
|
+
);
|
|
122
|
+
if (!ipcResult.ok) {
|
|
123
|
+
writeCliError(ipcResult.error ?? "Unknown error", opts.json);
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
const d = ipcResult.result!;
|
|
127
|
+
if (opts.json) {
|
|
128
|
+
process.stdout.write(JSON.stringify({ ok: true, ...d }) + "\n");
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
writeLine(`call site: ${d.callSite}`);
|
|
132
|
+
writeLine(
|
|
133
|
+
` winner: ${d.winner.profile ?? "(anchor)"} [${SOURCE_LABEL[d.winner.source]}]` +
|
|
134
|
+
(d.winner.label ? ` "${d.winner.label}"` : ""),
|
|
135
|
+
);
|
|
136
|
+
writeLine(
|
|
137
|
+
` resolved: ${d.resolved.provider}/${d.resolved.model} effort=${d.resolved.effort} maxTokens=${d.resolved.maxTokens}` +
|
|
138
|
+
(d.resolved.maxInputTokens != null
|
|
139
|
+
? ` ctxIn=${formatCtxIn(d.resolved.maxInputTokens)}`
|
|
140
|
+
: ""),
|
|
141
|
+
);
|
|
142
|
+
if (d.resolutionChain.length > 0) {
|
|
143
|
+
writeLine(" resolution chain (skipped rungs):");
|
|
144
|
+
for (const step of d.resolutionChain) {
|
|
145
|
+
writeLine(` - ${step.requested}: ${step.reason}`);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
writeLine(` shipped default: ${JSON.stringify(d.shippedDefault)}`);
|
|
149
|
+
writeLine(
|
|
150
|
+
` user pin: ${d.userPin ? JSON.stringify(d.userPin) : "(none)"}`,
|
|
151
|
+
);
|
|
152
|
+
if (d.resolutionError) {
|
|
153
|
+
writeLine(
|
|
154
|
+
` resolution error [${d.resolutionError.reason}]: ${d.resolutionError.message}`,
|
|
155
|
+
);
|
|
156
|
+
}
|
|
157
|
+
});
|
|
158
|
+
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `assistant inference models` CLI namespace.
|
|
3
|
+
*
|
|
4
|
+
* `assistant inference models list [--provider <p>] [--json]`
|
|
5
|
+
*
|
|
6
|
+
* Lists the code-owned model catalog so the assistant can discover valid
|
|
7
|
+
* model ids instead of guessing. Delegates to the daemon via IPC
|
|
8
|
+
* (`inference_models_list`).
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import type { Command } from "commander";
|
|
12
|
+
|
|
13
|
+
import { cliIpcCall } from "../../ipc/cli-client.js";
|
|
14
|
+
import { renderTable, writeCliError, writeLine } from "../lib/cli-output.js";
|
|
15
|
+
|
|
16
|
+
interface CatalogModel {
|
|
17
|
+
provider: string;
|
|
18
|
+
id: string;
|
|
19
|
+
displayName: string;
|
|
20
|
+
contextWindowTokens?: number;
|
|
21
|
+
maxOutputTokens?: number;
|
|
22
|
+
supportsThinking?: boolean;
|
|
23
|
+
supportsVision?: boolean;
|
|
24
|
+
supportsToolUse?: boolean;
|
|
25
|
+
featureFlag?: string;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function attachModelsSubcommand(inference: Command): void {
|
|
29
|
+
const models = inference
|
|
30
|
+
.command("models")
|
|
31
|
+
.description("Inspect the inference model catalog");
|
|
32
|
+
|
|
33
|
+
models
|
|
34
|
+
.command("list")
|
|
35
|
+
.description("List catalog models (optionally filtered by provider)")
|
|
36
|
+
.option("--provider <p>", "Filter by provider id")
|
|
37
|
+
.option("--json", "Output as machine-readable JSON")
|
|
38
|
+
.addHelpText(
|
|
39
|
+
"after",
|
|
40
|
+
`
|
|
41
|
+
Lists every model in the code-owned provider catalog. Use the ids here
|
|
42
|
+
when creating an inference profile:
|
|
43
|
+
|
|
44
|
+
Examples:
|
|
45
|
+
$ assistant inference models list
|
|
46
|
+
$ assistant inference models list --provider anthropic
|
|
47
|
+
$ assistant inference models list --json`,
|
|
48
|
+
)
|
|
49
|
+
.action(async (opts: { provider?: string; json?: boolean }) => {
|
|
50
|
+
const ipcResult = await cliIpcCall<{ models: CatalogModel[] }>(
|
|
51
|
+
"inference_models_list",
|
|
52
|
+
{ queryParams: opts.provider ? { provider: opts.provider } : {} },
|
|
53
|
+
);
|
|
54
|
+
|
|
55
|
+
if (!ipcResult.ok) {
|
|
56
|
+
writeCliError(ipcResult.error ?? "Unknown error", opts.json);
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const rows = ipcResult.result!.models;
|
|
61
|
+
|
|
62
|
+
if (opts.json) {
|
|
63
|
+
process.stdout.write(JSON.stringify({ ok: true, models: rows }) + "\n");
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
if (rows.length === 0) {
|
|
68
|
+
writeLine("No models found.");
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
renderTable(
|
|
73
|
+
["PROVIDER", "MODEL ID", "DISPLAY NAME", "CONTEXT"],
|
|
74
|
+
rows.map((m) => [
|
|
75
|
+
m.provider,
|
|
76
|
+
m.id,
|
|
77
|
+
m.displayName,
|
|
78
|
+
m.contextWindowTokens != null
|
|
79
|
+
? m.contextWindowTokens.toLocaleString()
|
|
80
|
+
: "-",
|
|
81
|
+
]),
|
|
82
|
+
);
|
|
83
|
+
});
|
|
84
|
+
}
|