@danypops/jittor 0.14.0 → 0.16.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/package.json +4 -3
- package/src/adapters/artificial-analysis-direct-source.ts +42 -11
- package/src/adapters/lmarena-hf-source.ts +37 -15
- package/src/adapters/metric-benchmark-store.ts +29 -18
- package/src/adapters/openrouter-benchmark-source.ts +75 -19
- package/src/adapters/openrouter-design-arena-source.ts +36 -19
- package/src/adapters/sqlite-metric-store.ts +48 -25
- package/src/adapters/sqlite-session-identity-store.ts +13 -7
- package/src/cli-commands/benchmarks.ts +87 -22
- package/src/cli-commands/compaction.ts +7 -2
- package/src/cli-commands/context.ts +10 -2
- package/src/cli-commands/metrics.ts +128 -31
- package/src/cli-commands/op.ts +6 -1
- package/src/cli-commands/route-args.ts +5 -1
- package/src/cli-commands/router.ts +61 -18
- package/src/cli-commands/service-daemon.ts +31 -13
- package/src/cli-commands/session.ts +15 -4
- package/src/cli-commands/support.ts +1 -1
- package/src/cli.ts +30 -23
- package/src/client.ts +1 -1
- package/src/constants.ts +1 -1
- package/src/daemon.ts +44 -24
- package/src/domain/benchmark.ts +72 -40
- package/src/domain/codex-recovery.ts +34 -25
- package/src/domain/context-hub.ts +35 -26
- package/src/domain/context-telemetry.ts +106 -35
- package/src/domain/metric.ts +11 -11
- package/src/domain/model-observation.ts +139 -55
- package/src/domain/model-ranking-service.ts +13 -3
- package/src/domain/model-ranking.ts +126 -60
- package/src/domain/task-cost.ts +52 -11
- package/src/domain/task-focus.ts +11 -8
- package/src/domain/usage.ts +2 -2
- package/src/index.ts +69 -69
- package/src/log.ts +7 -2
- package/src/operations/benchmark-operations.ts +1 -1
- package/src/operations/context-operations.ts +15 -6
- package/src/operations/metrics-operations.ts +63 -28
- package/src/operations/model-ranking-operations.ts +9 -2
- package/src/operations/router-operations.ts +8 -4
- package/src/operations/session-identity-operations.ts +1 -1
- package/src/operations/session-scope.ts +5 -3
- package/src/policy.ts +22 -17
- package/src/ports/benchmark-controller.ts +1 -5
- package/src/ports/metric-store.ts +1 -1
- package/src/providers/anthropic-contracts.ts +13 -3
- package/src/providers/codex-contracts.ts +60 -52
- package/src/providers/codex.ts +16 -19
- package/src/providers/google-vertex-budget-contracts.ts +24 -14
- package/src/providers/google-vertex-budget.ts +15 -13
- package/src/providers/google-vertex-contracts.ts +36 -24
- package/src/providers/openrouter-contracts.ts +49 -51
- package/src/providers/openrouter.ts +21 -15
- package/src/providers/telemetry-sources.ts +13 -10
- package/src/router.ts +92 -43
- package/src/service.ts +93 -32
- package/src/session-identity-service.ts +10 -2
- package/src/state.ts +4 -10
- package/src/vehicle-registration.ts +154 -0
|
@@ -6,20 +6,24 @@ export class SQLiteSessionIdentityStore implements SessionIdentityStore {
|
|
|
6
6
|
constructor(private readonly db: Database) {}
|
|
7
7
|
|
|
8
8
|
find(sessionId: string): SessionIdentityRecord | undefined {
|
|
9
|
-
const row = this.db
|
|
10
|
-
|
|
11
|
-
| null;
|
|
12
|
-
return row
|
|
9
|
+
const row = this.db
|
|
10
|
+
.query("SELECT session_id, secret_hash, registered_at, last_seen_at FROM session_identities WHERE session_id = ?")
|
|
11
|
+
.get(sessionId) as { session_id: string; secret_hash: string; registered_at: string; last_seen_at: string } | null;
|
|
12
|
+
return row
|
|
13
|
+
? { sessionId: row.session_id, secretHash: row.secret_hash, registeredAt: row.registered_at, lastSeenAt: row.last_seen_at }
|
|
14
|
+
: undefined;
|
|
13
15
|
}
|
|
14
16
|
|
|
15
17
|
upsert(record: SessionIdentityRecord): void {
|
|
16
18
|
this.db.transaction(() => {
|
|
17
19
|
this.evictOldestBeyondCap(record.sessionId);
|
|
18
|
-
this.db
|
|
20
|
+
this.db
|
|
21
|
+
.query(`
|
|
19
22
|
INSERT INTO session_identities (session_id, secret_hash, registered_at, last_seen_at)
|
|
20
23
|
VALUES (?, ?, ?, ?)
|
|
21
24
|
ON CONFLICT(session_id) DO UPDATE SET secret_hash = excluded.secret_hash, registered_at = excluded.registered_at, last_seen_at = excluded.last_seen_at
|
|
22
|
-
`)
|
|
25
|
+
`)
|
|
26
|
+
.run(record.sessionId, record.secretHash, record.registeredAt, record.lastSeenAt);
|
|
23
27
|
})();
|
|
24
28
|
}
|
|
25
29
|
|
|
@@ -40,6 +44,8 @@ export class SQLiteSessionIdentityStore implements SessionIdentityStore {
|
|
|
40
44
|
const exists = this.db.query("SELECT 1 FROM session_identities WHERE session_id = ?").get(sessionId);
|
|
41
45
|
if (exists) return;
|
|
42
46
|
if (this.count() < SESSION_IDENTITY_MAX_ROWS) return;
|
|
43
|
-
this.db.exec(
|
|
47
|
+
this.db.exec(
|
|
48
|
+
"DELETE FROM session_identities WHERE session_id = (SELECT session_id FROM session_identities ORDER BY last_seen_at ASC LIMIT 1)",
|
|
49
|
+
);
|
|
44
50
|
}
|
|
45
51
|
}
|
|
@@ -8,11 +8,11 @@ import {
|
|
|
8
8
|
MODEL_RANKING_MAX_SOURCES,
|
|
9
9
|
} from "../constants.ts";
|
|
10
10
|
import type { BenchmarkQuery, BenchmarkQueryResult, BenchmarkRefreshResult } from "../domain/benchmark.ts";
|
|
11
|
-
import type
|
|
11
|
+
import { type ModelTaskDomain, type ModelTaskType, TASK_DOMAINS, TASK_TYPES } from "../domain/model-observation.ts";
|
|
12
12
|
import type { ModelCandidate, ModelRankingResult, ScopeAuthority, UtilityWeights } from "../domain/model-ranking.ts";
|
|
13
|
-
import
|
|
14
|
-
import { humanField, type CliDependencies } from "./support.ts";
|
|
13
|
+
import type { ModelRecommendationInput } from "../domain/model-ranking-service.ts";
|
|
15
14
|
import { parseCandidate } from "./route-args.ts";
|
|
15
|
+
import { type CliDependencies, humanField } from "./support.ts";
|
|
16
16
|
|
|
17
17
|
export const BENCHMARKS_USAGE_LINES = [" benchmarks <status|refresh|list|rank> [options] [--json]"];
|
|
18
18
|
|
|
@@ -38,16 +38,42 @@ function parseBenchmarkArgs(action: string | undefined, args: string[]): Benchma
|
|
|
38
38
|
let sessionId: string | undefined;
|
|
39
39
|
let sessionSecret: string | undefined;
|
|
40
40
|
const weights: UtilityWeights = {
|
|
41
|
-
quality: MODEL_RANKING_DEFAULT_QUALITY_WEIGHT,
|
|
42
|
-
|
|
41
|
+
quality: MODEL_RANKING_DEFAULT_QUALITY_WEIGHT,
|
|
42
|
+
cost: MODEL_RANKING_DEFAULT_COST_WEIGHT,
|
|
43
|
+
latency: MODEL_RANKING_DEFAULT_LATENCY_WEIGHT,
|
|
44
|
+
context: MODEL_RANKING_DEFAULT_CONTEXT_WEIGHT,
|
|
43
45
|
reliability: MODEL_RANKING_DEFAULT_RELIABILITY_WEIGHT,
|
|
44
46
|
};
|
|
45
47
|
for (let index = 0; index < args.length; index += 1) {
|
|
46
48
|
const argument = args[index];
|
|
47
|
-
if (argument === "--json") {
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
49
|
+
if (argument === "--json") {
|
|
50
|
+
json = true;
|
|
51
|
+
continue;
|
|
52
|
+
}
|
|
53
|
+
if (argument === "--force" && action === "refresh") {
|
|
54
|
+
force = true;
|
|
55
|
+
continue;
|
|
56
|
+
}
|
|
57
|
+
const allowed =
|
|
58
|
+
action === "list"
|
|
59
|
+
? ["--source", "--model", "--dimension", "--limit"]
|
|
60
|
+
: action === "rank"
|
|
61
|
+
? [
|
|
62
|
+
"--candidate",
|
|
63
|
+
"--source",
|
|
64
|
+
"--domain",
|
|
65
|
+
"--type",
|
|
66
|
+
"--scope",
|
|
67
|
+
"--budget",
|
|
68
|
+
"--weight-quality",
|
|
69
|
+
"--weight-cost",
|
|
70
|
+
"--weight-latency",
|
|
71
|
+
"--weight-context",
|
|
72
|
+
"--weight-reliability",
|
|
73
|
+
"--session-id",
|
|
74
|
+
"--session-secret",
|
|
75
|
+
]
|
|
76
|
+
: [];
|
|
51
77
|
if (!allowed.includes(argument ?? "")) return null;
|
|
52
78
|
const raw = args[++index];
|
|
53
79
|
if (raw === undefined || raw.length === 0) return null;
|
|
@@ -85,38 +111,76 @@ function parseBenchmarkArgs(action: string | undefined, args: string[]): Benchma
|
|
|
85
111
|
}
|
|
86
112
|
}
|
|
87
113
|
if (action === "list" && query.sourceId === undefined) return null;
|
|
88
|
-
if (
|
|
114
|
+
if (
|
|
115
|
+
action === "rank" &&
|
|
116
|
+
(candidates.length === 0 ||
|
|
117
|
+
sourceIds.length > MODEL_RANKING_MAX_SOURCES ||
|
|
118
|
+
!Number.isFinite(budgetPressure) ||
|
|
119
|
+
budgetPressure < 0 ||
|
|
120
|
+
budgetPressure > 2)
|
|
121
|
+
)
|
|
122
|
+
return null;
|
|
89
123
|
return {
|
|
90
|
-
action,
|
|
124
|
+
action,
|
|
125
|
+
json,
|
|
126
|
+
force,
|
|
91
127
|
...(action === "list" ? { query: query as BenchmarkQuery } : {}),
|
|
92
|
-
...(action === "rank"
|
|
128
|
+
...(action === "rank"
|
|
129
|
+
? {
|
|
130
|
+
recommendation: {
|
|
131
|
+
candidates,
|
|
132
|
+
sourceIds: [...new Set(sourceIds)],
|
|
133
|
+
scopeAuthority,
|
|
134
|
+
domain,
|
|
135
|
+
type,
|
|
136
|
+
budgetPressure,
|
|
137
|
+
weights,
|
|
138
|
+
...(sessionId ? { session_id: sessionId } : {}),
|
|
139
|
+
...(sessionSecret ? { session_secret: sessionSecret } : {}),
|
|
140
|
+
},
|
|
141
|
+
}
|
|
142
|
+
: {}),
|
|
93
143
|
};
|
|
94
144
|
}
|
|
95
145
|
|
|
96
146
|
export function formatBenchmarkStatus(result: BenchmarkRefreshResult): string {
|
|
97
147
|
if (result.sources.length === 0) return "Benchmark sources: none configured";
|
|
98
|
-
return [
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
148
|
+
return [
|
|
149
|
+
"Benchmark sources:",
|
|
150
|
+
...result.sources.map((source) => {
|
|
151
|
+
const state = source.ok === null ? "not refreshed" : source.ok ? "ready" : "refresh failed";
|
|
152
|
+
return `- ${source.id}: ${state} · ${source.observations.toLocaleString()} observations · ${source.hasEvidence ? "evidence retained" : "no evidence"}`;
|
|
153
|
+
}),
|
|
154
|
+
].join("\n");
|
|
102
155
|
}
|
|
103
156
|
|
|
104
157
|
export function formatBenchmarkQuery(result: BenchmarkQueryResult): string {
|
|
105
158
|
return [
|
|
106
159
|
`Benchmark evidence: ${humanField(result.sourceId)} · ${result.completeness} · ${result.freshness} · ${result.observations.length.toLocaleString()} observations`,
|
|
107
|
-
...result.observations.map(
|
|
160
|
+
...result.observations.map(
|
|
161
|
+
(observation) =>
|
|
162
|
+
`- ${humanField(observation.model.canonical)} · ${humanField(observation.dimension)} ${observation.value.toLocaleString()} ${observation.unit} · ${humanField(observation.provenance.publisher)} · confidence ${(observation.provenance.confidence * 100).toFixed(0)}%`,
|
|
163
|
+
),
|
|
108
164
|
].join("\n");
|
|
109
165
|
}
|
|
110
166
|
|
|
111
167
|
export function formatModelRanking(result: ModelRankingResult): string {
|
|
112
168
|
return [
|
|
113
169
|
`Model ranking: ${result.completeness} · scope ${result.scopeAuthority}${result.scopeWarning ? " · advisory only" : ""}`,
|
|
114
|
-
...result.ranked.map(
|
|
170
|
+
...result.ranked.map(
|
|
171
|
+
(item, index) =>
|
|
172
|
+
`${index + 1}. ${humanField(item.identity)} · utility ${item.utility === null ? "unknown" : item.utility.toFixed(3)} · confidence ${(item.confidence * 100).toFixed(0)}%`,
|
|
173
|
+
),
|
|
115
174
|
...(result.scopeWarning ? [result.scopeWarning] : []),
|
|
116
175
|
].join("\n");
|
|
117
176
|
}
|
|
118
177
|
|
|
119
|
-
export async function runBenchmarksCommand(
|
|
178
|
+
export async function runBenchmarksCommand(
|
|
179
|
+
action: string | undefined,
|
|
180
|
+
rest: string[],
|
|
181
|
+
deps: CliDependencies,
|
|
182
|
+
usage: () => number,
|
|
183
|
+
): Promise<number> {
|
|
120
184
|
const parsed = parseBenchmarkArgs(action, rest);
|
|
121
185
|
if (!parsed) return usage();
|
|
122
186
|
try {
|
|
@@ -127,9 +191,10 @@ export async function runBenchmarksCommand(action: string | undefined, rest: str
|
|
|
127
191
|
const result = await deps.client.call("models.rank", parsed.recommendation!);
|
|
128
192
|
deps.stdout(parsed.json ? JSON.stringify(result) : formatModelRanking(result));
|
|
129
193
|
} else {
|
|
130
|
-
const result =
|
|
131
|
-
|
|
132
|
-
|
|
194
|
+
const result =
|
|
195
|
+
parsed.action === "refresh"
|
|
196
|
+
? await deps.client.call("benchmark.refresh", { force: parsed.force })
|
|
197
|
+
: await deps.client.call("benchmark.status", {});
|
|
133
198
|
deps.stdout(parsed.json ? JSON.stringify(result) : formatBenchmarkStatus(result));
|
|
134
199
|
}
|
|
135
200
|
return 0;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { CompactionDurationEstimate } from "../domain/context-telemetry.ts";
|
|
2
|
-
import { callAndPrint, type CliDependencies } from "./support.ts";
|
|
3
2
|
import { parseJsonOnlyArgs } from "./router.ts";
|
|
3
|
+
import { type CliDependencies, callAndPrint } from "./support.ts";
|
|
4
4
|
|
|
5
5
|
export function formatCompactionEstimate(estimate: CompactionDurationEstimate): string {
|
|
6
6
|
if (estimate.confidence === "cold-start" || estimate.ms === null) {
|
|
@@ -9,7 +9,12 @@ export function formatCompactionEstimate(estimate: CompactionDurationEstimate):
|
|
|
9
9
|
return `Compaction duration: ~${estimate.ms.toLocaleString()}ms learned from ${estimate.sampleSize.toLocaleString()} sample(s)`;
|
|
10
10
|
}
|
|
11
11
|
|
|
12
|
-
export async function runCompactionCommand(
|
|
12
|
+
export async function runCompactionCommand(
|
|
13
|
+
action: string | undefined,
|
|
14
|
+
rest: string[],
|
|
15
|
+
deps: CliDependencies,
|
|
16
|
+
usage: () => number,
|
|
17
|
+
): Promise<number> {
|
|
13
18
|
if (action !== "estimate") return usage();
|
|
14
19
|
const parsed = parseJsonOnlyArgs(rest);
|
|
15
20
|
if (!parsed) return usage();
|
|
@@ -8,7 +8,10 @@ function parseContextArgs(args: string[]): { input: { since?: number; until?: nu
|
|
|
8
8
|
let json = false;
|
|
9
9
|
for (let index = 0; index < args.length; index += 1) {
|
|
10
10
|
const argument = args[index];
|
|
11
|
-
if (argument === "--json") {
|
|
11
|
+
if (argument === "--json") {
|
|
12
|
+
json = true;
|
|
13
|
+
continue;
|
|
14
|
+
}
|
|
12
15
|
if (argument !== "--since" && argument !== "--until") return null;
|
|
13
16
|
const raw = args[++index];
|
|
14
17
|
const value = raw === undefined ? Number.NaN : Number(raw);
|
|
@@ -35,7 +38,12 @@ export function formatContextAssessment(summary: ContextAssessment): string {
|
|
|
35
38
|
].join("\n");
|
|
36
39
|
}
|
|
37
40
|
|
|
38
|
-
export async function runContextCommand(
|
|
41
|
+
export async function runContextCommand(
|
|
42
|
+
action: string | undefined,
|
|
43
|
+
rest: string[],
|
|
44
|
+
deps: CliDependencies,
|
|
45
|
+
usage: () => number,
|
|
46
|
+
): Promise<number> {
|
|
39
47
|
const parsed = parseContextArgs([...(action === undefined ? [] : [action]), ...rest]);
|
|
40
48
|
if (!parsed) return usage();
|
|
41
49
|
try {
|
|
@@ -1,7 +1,13 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
CLI_METRICS_HUMAN_MAX_ROWS,
|
|
3
|
+
MAX_QUERY_LIMIT,
|
|
4
|
+
MAX_USAGE_BUCKETS,
|
|
5
|
+
METRIC_BATCH_MAX_OBSERVATIONS,
|
|
6
|
+
USAGE_MAX_DISTINCT_SCOPES,
|
|
7
|
+
} from "../constants.ts";
|
|
2
8
|
import { METRIC_UNITS, type MetricObservation, type MetricQuery, type MetricUnit, type StoredMetricObservation } from "../domain/metric.ts";
|
|
3
9
|
import type { TaskCostSummary } from "../domain/task-cost.ts";
|
|
4
|
-
import { callAndPrint, humanField
|
|
10
|
+
import { type CliDependencies, callAndPrint, humanField } from "./support.ts";
|
|
5
11
|
|
|
6
12
|
export const METRICS_USAGE_LINES = [
|
|
7
13
|
" metrics record --source <s> --scope <s> --metric <s> --value <number|null> --unit <unit> [--observed-at <ms>] [--attributes <json>] [--json]",
|
|
@@ -13,7 +19,10 @@ export const METRICS_USAGE_LINES = [
|
|
|
13
19
|
" metrics cost-by-task --since <ms> --until <ms> [--json]",
|
|
14
20
|
];
|
|
15
21
|
|
|
16
|
-
interface MetricsRecordArgs {
|
|
22
|
+
interface MetricsRecordArgs {
|
|
23
|
+
input: MetricObservation;
|
|
24
|
+
json: boolean;
|
|
25
|
+
}
|
|
17
26
|
|
|
18
27
|
function parseMetricsRecordArgs(args: string[]): MetricsRecordArgs | null {
|
|
19
28
|
let json = false;
|
|
@@ -26,7 +35,10 @@ function parseMetricsRecordArgs(args: string[]): MetricsRecordArgs | null {
|
|
|
26
35
|
let attributes: Record<string, unknown> | undefined;
|
|
27
36
|
for (let index = 0; index < args.length; index += 1) {
|
|
28
37
|
const argument = args[index];
|
|
29
|
-
if (argument === "--json") {
|
|
38
|
+
if (argument === "--json") {
|
|
39
|
+
json = true;
|
|
40
|
+
continue;
|
|
41
|
+
}
|
|
30
42
|
if (!["--source", "--scope", "--metric", "--value", "--unit", "--observed-at", "--attributes"].includes(argument ?? "")) return null;
|
|
31
43
|
const raw = args[++index];
|
|
32
44
|
if (raw === undefined || raw.length === 0) return null;
|
|
@@ -61,21 +73,31 @@ function parseMetricsRecordArgs(args: string[]): MetricsRecordArgs | null {
|
|
|
61
73
|
return {
|
|
62
74
|
json,
|
|
63
75
|
input: {
|
|
64
|
-
source,
|
|
76
|
+
source,
|
|
77
|
+
scope,
|
|
78
|
+
metric,
|
|
79
|
+
value,
|
|
80
|
+
unit,
|
|
65
81
|
observedAt: observedAt ?? Date.now(),
|
|
66
82
|
...(attributes ? { attributes } : {}),
|
|
67
83
|
},
|
|
68
84
|
};
|
|
69
85
|
}
|
|
70
86
|
|
|
71
|
-
interface MetricsRecordBatchArgs {
|
|
87
|
+
interface MetricsRecordBatchArgs {
|
|
88
|
+
input: { observations: MetricObservation[] };
|
|
89
|
+
json: boolean;
|
|
90
|
+
}
|
|
72
91
|
|
|
73
92
|
function parseMetricsRecordBatchArgs(args: string[]): MetricsRecordBatchArgs | null {
|
|
74
93
|
let json = false;
|
|
75
94
|
let observations: unknown;
|
|
76
95
|
for (let index = 0; index < args.length; index += 1) {
|
|
77
96
|
const argument = args[index];
|
|
78
|
-
if (argument === "--json") {
|
|
97
|
+
if (argument === "--json") {
|
|
98
|
+
json = true;
|
|
99
|
+
continue;
|
|
100
|
+
}
|
|
79
101
|
if (argument !== "--observations") return null;
|
|
80
102
|
const raw = args[++index];
|
|
81
103
|
if (raw === undefined || raw.length === 0) return null;
|
|
@@ -89,14 +111,20 @@ function parseMetricsRecordBatchArgs(args: string[]): MetricsRecordBatchArgs | n
|
|
|
89
111
|
return { input: { observations: observations as MetricObservation[] }, json };
|
|
90
112
|
}
|
|
91
113
|
|
|
92
|
-
interface MetricsQueryArgs {
|
|
114
|
+
interface MetricsQueryArgs {
|
|
115
|
+
input: MetricQuery;
|
|
116
|
+
json: boolean;
|
|
117
|
+
}
|
|
93
118
|
|
|
94
119
|
function parseMetricsQueryArgs(args: string[]): MetricsQueryArgs | null {
|
|
95
120
|
let json = false;
|
|
96
121
|
const input: MetricQuery = {};
|
|
97
122
|
for (let index = 0; index < args.length; index += 1) {
|
|
98
123
|
const argument = args[index];
|
|
99
|
-
if (argument === "--json") {
|
|
124
|
+
if (argument === "--json") {
|
|
125
|
+
json = true;
|
|
126
|
+
continue;
|
|
127
|
+
}
|
|
100
128
|
if (!["--source", "--scope", "--metric", "--since", "--until", "--limit", "--order"].includes(argument ?? "")) return null;
|
|
101
129
|
const raw = args[++index];
|
|
102
130
|
if (raw === undefined || raw.length === 0) return null;
|
|
@@ -121,7 +149,10 @@ function parseMetricsQueryArgs(args: string[]): MetricsQueryArgs | null {
|
|
|
121
149
|
return { input, json };
|
|
122
150
|
}
|
|
123
151
|
|
|
124
|
-
interface MetricsDistinctScopesArgs {
|
|
152
|
+
interface MetricsDistinctScopesArgs {
|
|
153
|
+
input: { source: string; since: number; until: number; limit?: number };
|
|
154
|
+
json: boolean;
|
|
155
|
+
}
|
|
125
156
|
|
|
126
157
|
function parseMetricsDistinctScopesArgs(args: string[]): MetricsDistinctScopesArgs | null {
|
|
127
158
|
let json = false;
|
|
@@ -131,11 +162,17 @@ function parseMetricsDistinctScopesArgs(args: string[]): MetricsDistinctScopesAr
|
|
|
131
162
|
let limit: number | undefined;
|
|
132
163
|
for (let index = 0; index < args.length; index += 1) {
|
|
133
164
|
const argument = args[index];
|
|
134
|
-
if (argument === "--json") {
|
|
165
|
+
if (argument === "--json") {
|
|
166
|
+
json = true;
|
|
167
|
+
continue;
|
|
168
|
+
}
|
|
135
169
|
if (!["--source", "--since", "--until", "--limit"].includes(argument ?? "")) return null;
|
|
136
170
|
const raw = args[++index];
|
|
137
171
|
if (raw === undefined || raw.length === 0) return null;
|
|
138
|
-
if (argument === "--source") {
|
|
172
|
+
if (argument === "--source") {
|
|
173
|
+
source = raw;
|
|
174
|
+
continue;
|
|
175
|
+
}
|
|
139
176
|
const parsed = Number(raw);
|
|
140
177
|
if (!Number.isSafeInteger(parsed) || parsed < 0) return null;
|
|
141
178
|
if (argument === "--since") since = parsed;
|
|
@@ -149,7 +186,10 @@ function parseMetricsDistinctScopesArgs(args: string[]): MetricsDistinctScopesAr
|
|
|
149
186
|
return { input: { source, since, until, ...(limit === undefined ? {} : { limit }) }, json };
|
|
150
187
|
}
|
|
151
188
|
|
|
152
|
-
interface MetricsUsageSeriesArgs {
|
|
189
|
+
interface MetricsUsageSeriesArgs {
|
|
190
|
+
input: { source: string; since: number; until: number; bucketSizeMs: number; bucketCount: number; scopeLimit?: number };
|
|
191
|
+
json: boolean;
|
|
192
|
+
}
|
|
153
193
|
|
|
154
194
|
function parseMetricsUsageSeriesArgs(args: string[]): MetricsUsageSeriesArgs | null {
|
|
155
195
|
let json = false;
|
|
@@ -161,24 +201,48 @@ function parseMetricsUsageSeriesArgs(args: string[]): MetricsUsageSeriesArgs | n
|
|
|
161
201
|
let scopeLimit: number | undefined;
|
|
162
202
|
for (let index = 0; index < args.length; index += 1) {
|
|
163
203
|
const argument = args[index];
|
|
164
|
-
if (argument === "--json") {
|
|
165
|
-
|
|
204
|
+
if (argument === "--json") {
|
|
205
|
+
json = true;
|
|
206
|
+
continue;
|
|
207
|
+
}
|
|
208
|
+
if (!["--source", "--since", "--until", "--bucket-size-ms", "--bucket-count", "--scope-limit"].includes(argument ?? "")) return null;
|
|
166
209
|
const raw = args[++index];
|
|
167
210
|
if (raw === undefined || raw.length === 0) return null;
|
|
168
|
-
if (argument === "--source") {
|
|
211
|
+
if (argument === "--source") {
|
|
212
|
+
source = raw;
|
|
213
|
+
continue;
|
|
214
|
+
}
|
|
169
215
|
const parsed = Number(raw);
|
|
170
216
|
if (!Number.isSafeInteger(parsed) || parsed < 0) return null;
|
|
171
217
|
if (argument === "--since") since = parsed;
|
|
172
218
|
else if (argument === "--until") until = parsed;
|
|
173
|
-
else if (argument === "--bucket-size-ms") {
|
|
174
|
-
|
|
175
|
-
|
|
219
|
+
else if (argument === "--bucket-size-ms") {
|
|
220
|
+
if (parsed < 1) return null;
|
|
221
|
+
bucketSizeMs = parsed;
|
|
222
|
+
} else if (argument === "--bucket-count") {
|
|
223
|
+
if (parsed < 1 || parsed > MAX_USAGE_BUCKETS) return null;
|
|
224
|
+
bucketCount = parsed;
|
|
225
|
+
} else {
|
|
226
|
+
if (parsed < 1 || parsed > USAGE_MAX_DISTINCT_SCOPES) return null;
|
|
227
|
+
scopeLimit = parsed;
|
|
228
|
+
}
|
|
176
229
|
}
|
|
177
|
-
if (
|
|
230
|
+
if (
|
|
231
|
+
source === undefined ||
|
|
232
|
+
since === undefined ||
|
|
233
|
+
until === undefined ||
|
|
234
|
+
until < since ||
|
|
235
|
+
bucketSizeMs === undefined ||
|
|
236
|
+
bucketCount === undefined
|
|
237
|
+
)
|
|
238
|
+
return null;
|
|
178
239
|
return { input: { source, since, until, bucketSizeMs, bucketCount, ...(scopeLimit === undefined ? {} : { scopeLimit }) }, json };
|
|
179
240
|
}
|
|
180
241
|
|
|
181
|
-
interface CostByTaskArgs {
|
|
242
|
+
interface CostByTaskArgs {
|
|
243
|
+
input: { since: number; until: number };
|
|
244
|
+
json: boolean;
|
|
245
|
+
}
|
|
182
246
|
|
|
183
247
|
function parseCostByTaskArgs(args: string[]): CostByTaskArgs | null {
|
|
184
248
|
let json = false;
|
|
@@ -186,7 +250,10 @@ function parseCostByTaskArgs(args: string[]): CostByTaskArgs | null {
|
|
|
186
250
|
let until: number | undefined;
|
|
187
251
|
for (let index = 0; index < args.length; index += 1) {
|
|
188
252
|
const argument = args[index];
|
|
189
|
-
if (argument === "--json") {
|
|
253
|
+
if (argument === "--json") {
|
|
254
|
+
json = true;
|
|
255
|
+
continue;
|
|
256
|
+
}
|
|
190
257
|
if (!["--since", "--until"].includes(argument ?? "")) return null;
|
|
191
258
|
const raw = args[++index];
|
|
192
259
|
const parsed = Number(raw);
|
|
@@ -198,7 +265,10 @@ function parseCostByTaskArgs(args: string[]): CostByTaskArgs | null {
|
|
|
198
265
|
return { input: { since, until }, json };
|
|
199
266
|
}
|
|
200
267
|
|
|
201
|
-
interface MetricsPruneArgs {
|
|
268
|
+
interface MetricsPruneArgs {
|
|
269
|
+
input: { before: number; force?: boolean };
|
|
270
|
+
json: boolean;
|
|
271
|
+
}
|
|
202
272
|
|
|
203
273
|
function parseMetricsPruneArgs(args: string[]): MetricsPruneArgs | null {
|
|
204
274
|
let json = false;
|
|
@@ -206,8 +276,14 @@ function parseMetricsPruneArgs(args: string[]): MetricsPruneArgs | null {
|
|
|
206
276
|
let before: number | undefined;
|
|
207
277
|
for (let index = 0; index < args.length; index += 1) {
|
|
208
278
|
const argument = args[index];
|
|
209
|
-
if (argument === "--json") {
|
|
210
|
-
|
|
279
|
+
if (argument === "--json") {
|
|
280
|
+
json = true;
|
|
281
|
+
continue;
|
|
282
|
+
}
|
|
283
|
+
if (argument === "--force") {
|
|
284
|
+
force = true;
|
|
285
|
+
continue;
|
|
286
|
+
}
|
|
211
287
|
if (argument !== "--before") return null;
|
|
212
288
|
const raw = args[++index];
|
|
213
289
|
const parsed = Number(raw);
|
|
@@ -223,7 +299,10 @@ export function formatMetricsQuery(rows: StoredMetricObservation[]): string {
|
|
|
223
299
|
const shown = rows.slice(0, CLI_METRICS_HUMAN_MAX_ROWS);
|
|
224
300
|
const lines = [
|
|
225
301
|
`Metrics: ${rows.length.toLocaleString()} observation(s)${rows.length > shown.length ? ` (showing first ${shown.length})` : ""}`,
|
|
226
|
-
...shown.map(
|
|
302
|
+
...shown.map(
|
|
303
|
+
(row) =>
|
|
304
|
+
`- ${humanField(row.source)}/${humanField(row.scope)}/${humanField(row.metric)} = ${row.value === null ? "null" : row.value} ${row.unit} @ ${new Date(row.observedAt).toISOString()}`,
|
|
305
|
+
),
|
|
227
306
|
];
|
|
228
307
|
return lines.join("\n");
|
|
229
308
|
}
|
|
@@ -233,10 +312,14 @@ export function formatMetricsDistinctScopes(scopes: string[]): string {
|
|
|
233
312
|
return [`Scopes: ${scopes.length.toLocaleString()}`, ...scopes.map((scope) => `- ${humanField(scope)}`)].join("\n");
|
|
234
313
|
}
|
|
235
314
|
|
|
236
|
-
export function formatMetricsUsageSeries(result: {
|
|
315
|
+
export function formatMetricsUsageSeries(result: {
|
|
316
|
+
rows: Array<{ scope: string; metric: string; bucketIndex: number; sum: number }>;
|
|
317
|
+
truncated: boolean;
|
|
318
|
+
}): string {
|
|
237
319
|
if (result.rows.length === 0) return `Usage series: no data${result.truncated ? " (scope limit reached)" : ""}`;
|
|
238
320
|
const lines = [`Usage series: ${result.rows.length.toLocaleString()} bucket(s)${result.truncated ? " (scope limit reached)" : ""}`];
|
|
239
|
-
for (const row of result.rows)
|
|
321
|
+
for (const row of result.rows)
|
|
322
|
+
lines.push(`- ${humanField(row.scope)}/${humanField(row.metric)} bucket ${row.bucketIndex}: ${row.sum.toLocaleString()}`);
|
|
240
323
|
return lines.join("\n");
|
|
241
324
|
}
|
|
242
325
|
|
|
@@ -249,14 +332,22 @@ export function formatCostByTask(summary: TaskCostSummary): string {
|
|
|
249
332
|
`Cost by task: ${summary.entries.length.toLocaleString()} task(s)${summary.truncated ? " (query limit reached; totals are a lower bound)" : ""}`,
|
|
250
333
|
...summary.entries.flatMap((entry) => [
|
|
251
334
|
`- ${humanField(entry.taskId)}: ${formatUsdAmount(entry.costUsd)} · ↑${entry.inputTokens.toLocaleString()} ↓${entry.outputTokens.toLocaleString()} R${entry.cacheReadTokens.toLocaleString()} W${entry.cacheWriteTokens.toLocaleString()}`,
|
|
252
|
-
...entry.byModel.map(
|
|
335
|
+
...entry.byModel.map(
|
|
336
|
+
(model) =>
|
|
337
|
+
` · ${humanField(model.provider)}/${humanField(model.model)} (${humanField(model.thinking)}): ${formatUsdAmount(model.costUsd)} · ↑${model.inputTokens.toLocaleString()} ↓${model.outputTokens.toLocaleString()} R${model.cacheReadTokens.toLocaleString()} W${model.cacheWriteTokens.toLocaleString()}`,
|
|
338
|
+
),
|
|
253
339
|
]),
|
|
254
340
|
`Unattributed spend (no task was focused): ${formatUsdAmount(summary.unattributedCostUsd)}`,
|
|
255
341
|
];
|
|
256
342
|
return lines.join("\n");
|
|
257
343
|
}
|
|
258
344
|
|
|
259
|
-
export async function runMetricsCommand(
|
|
345
|
+
export async function runMetricsCommand(
|
|
346
|
+
action: string | undefined,
|
|
347
|
+
rest: string[],
|
|
348
|
+
deps: CliDependencies,
|
|
349
|
+
usage: () => number,
|
|
350
|
+
): Promise<number> {
|
|
260
351
|
if (action === "record") {
|
|
261
352
|
const parsed = parseMetricsRecordArgs(rest);
|
|
262
353
|
if (!parsed) return usage();
|
|
@@ -275,7 +366,13 @@ export async function runMetricsCommand(action: string | undefined, rest: string
|
|
|
275
366
|
if (action === "prune") {
|
|
276
367
|
const parsed = parseMetricsPruneArgs(rest);
|
|
277
368
|
if (!parsed) return usage();
|
|
278
|
-
return callAndPrint(
|
|
369
|
+
return callAndPrint(
|
|
370
|
+
deps,
|
|
371
|
+
"metrics.prune",
|
|
372
|
+
parsed.input,
|
|
373
|
+
parsed.json,
|
|
374
|
+
(result) => `Pruned ${result.deleted.toLocaleString()} observation(s)`,
|
|
375
|
+
);
|
|
279
376
|
}
|
|
280
377
|
if (action === "distinct-scopes") {
|
|
281
378
|
const parsed = parseMetricsDistinctScopesArgs(rest);
|
package/src/cli-commands/op.ts
CHANGED
|
@@ -22,7 +22,12 @@ function parseOpArgs(args: string[]): { operation: OperationName; input: Record<
|
|
|
22
22
|
return { operation: operation as OperationName, input };
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
-
export async function runOpCommand(
|
|
25
|
+
export async function runOpCommand(
|
|
26
|
+
action: string | undefined,
|
|
27
|
+
rest: string[],
|
|
28
|
+
deps: CliDependencies,
|
|
29
|
+
usage: () => number,
|
|
30
|
+
): Promise<number> {
|
|
26
31
|
const parsed = parseOpArgs(action === undefined ? [] : [action, ...rest]);
|
|
27
32
|
if (!parsed) return usage();
|
|
28
33
|
try {
|
|
@@ -6,7 +6,11 @@ export function parseCandidate(raw: string): ModelCandidate | null {
|
|
|
6
6
|
const separator = raw.indexOf("/");
|
|
7
7
|
const thinkingSeparator = raw.lastIndexOf("@");
|
|
8
8
|
if (separator <= 0 || thinkingSeparator <= separator + 1 || thinkingSeparator === raw.length - 1) return null;
|
|
9
|
-
return {
|
|
9
|
+
return {
|
|
10
|
+
provider: raw.slice(0, separator),
|
|
11
|
+
model: raw.slice(separator + 1, thinkingSeparator),
|
|
12
|
+
thinking: raw.slice(thinkingSeparator + 1),
|
|
13
|
+
};
|
|
10
14
|
}
|
|
11
15
|
|
|
12
16
|
export function parseRoute(raw: string | undefined): Route | null {
|