@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
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { BENCHMARK_MAX_OBSERVATIONS_PER_SNAPSHOT, MAX_DYNAMIC_ROUTES, MODEL_AGGREGATE_MAX_ROWS } from "../constants.ts";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
2
|
+
import { type BenchmarkObservation, normalizeModelIdentity } from "./benchmark.ts";
|
|
3
|
+
import { type ModelMetricAggregate, type ModelTaskDomain, type ModelTaskType, TASK_DOMAINS, TASK_TYPES } from "./model-observation.ts";
|
|
4
4
|
|
|
5
5
|
export type ScopeAuthority = "exact-session" | "available-models";
|
|
6
6
|
export type UtilityComponentName = "quality" | "cost" | "latency" | "context" | "reliability";
|
|
@@ -85,7 +85,8 @@ function finiteBound(value: number, name: string, minimum: number, maximum: numb
|
|
|
85
85
|
|
|
86
86
|
function candidateIdentity(candidate: ModelCandidate): string {
|
|
87
87
|
const identity = normalizeModelIdentity(candidate.provider, candidate.model);
|
|
88
|
-
if (typeof candidate.thinking !== "string" || candidate.thinking.length === 0 || candidate.thinking.length > 160)
|
|
88
|
+
if (typeof candidate.thinking !== "string" || candidate.thinking.length === 0 || candidate.thinking.length > 160)
|
|
89
|
+
throw new Error("candidate thinking level is invalid");
|
|
89
90
|
return `${identity.canonical}:${candidate.thinking}`;
|
|
90
91
|
}
|
|
91
92
|
|
|
@@ -93,19 +94,48 @@ function average(values: number[]): number {
|
|
|
93
94
|
return values.reduce((sum, value) => sum + value, 0) / values.length;
|
|
94
95
|
}
|
|
95
96
|
|
|
96
|
-
function externalValues(
|
|
97
|
+
function externalValues(
|
|
98
|
+
candidate: ModelCandidate,
|
|
99
|
+
evidence: BenchmarkObservation[],
|
|
100
|
+
dimensions: string[],
|
|
101
|
+
now: number,
|
|
102
|
+
): { values: number[]; confidences: number[]; provenance: RankingProvenance[] } {
|
|
97
103
|
const identity = normalizeModelIdentity(candidate.provider, candidate.model);
|
|
98
|
-
const matching = evidence.filter(
|
|
104
|
+
const matching = evidence.filter(
|
|
105
|
+
(item) =>
|
|
106
|
+
(item.model.canonical === identity.canonical || item.model.aliases.includes(identity.canonical)) &&
|
|
107
|
+
dimensions.includes(item.dimension),
|
|
108
|
+
);
|
|
99
109
|
return {
|
|
100
110
|
values: matching.map((item) => item.value),
|
|
101
111
|
confidences: matching.map((item) => item.provenance.confidence * (now <= item.provenance.freshUntil ? 1 : 0.25)),
|
|
102
|
-
provenance: matching.map((item) => ({
|
|
112
|
+
provenance: matching.map((item) => ({
|
|
113
|
+
sourceId: item.provenance.sourceId,
|
|
114
|
+
publisher: item.provenance.publisher,
|
|
115
|
+
url: item.provenance.url,
|
|
116
|
+
revision: item.provenance.revision,
|
|
117
|
+
freshness: now <= item.provenance.freshUntil ? "fresh" : "stale",
|
|
118
|
+
})),
|
|
103
119
|
};
|
|
104
120
|
}
|
|
105
121
|
|
|
106
|
-
function localValues(
|
|
122
|
+
function localValues(
|
|
123
|
+
candidate: ModelCandidate,
|
|
124
|
+
domain: ModelTaskDomain,
|
|
125
|
+
type: ModelTaskType,
|
|
126
|
+
evidence: ModelMetricAggregate[],
|
|
127
|
+
dimension: string,
|
|
128
|
+
): ModelMetricAggregate[] {
|
|
107
129
|
const identity = normalizeModelIdentity(candidate.provider, candidate.model);
|
|
108
|
-
return evidence.filter(
|
|
130
|
+
return evidence.filter(
|
|
131
|
+
(item) =>
|
|
132
|
+
item.provider === identity.provider &&
|
|
133
|
+
item.model === identity.model &&
|
|
134
|
+
item.thinking === candidate.thinking &&
|
|
135
|
+
item.domain === domain &&
|
|
136
|
+
item.type === type &&
|
|
137
|
+
item.dimension === dimension,
|
|
138
|
+
);
|
|
109
139
|
}
|
|
110
140
|
|
|
111
141
|
/**
|
|
@@ -123,7 +153,10 @@ function qualityDimensions(domain: ModelTaskDomain, type: ModelTaskType): string
|
|
|
123
153
|
return dimensions;
|
|
124
154
|
}
|
|
125
155
|
|
|
126
|
-
function rawComponents(
|
|
156
|
+
function rawComponents(
|
|
157
|
+
candidate: ModelCandidate,
|
|
158
|
+
input: ModelRankingInput,
|
|
159
|
+
): { components: Record<UtilityComponentName, RawComponent>; provenance: RankingProvenance[] } {
|
|
127
160
|
const quality = externalValues(candidate, input.externalEvidence, qualityDimensions(input.domain, input.type), input.now);
|
|
128
161
|
const priceInput = externalValues(candidate, input.externalEvidence, ["price-input"], input.now);
|
|
129
162
|
const priceOutput = externalValues(candidate, input.externalEvidence, ["price-output"], input.now);
|
|
@@ -139,23 +172,33 @@ function rawComponents(candidate: ModelCandidate, input: ModelRankingInput): { c
|
|
|
139
172
|
const latencyValues = localLatency.length > 0 ? localLatency.map((item) => item.median) : latency.values;
|
|
140
173
|
const latencyConfidences = localLatency.length > 0 ? localLatency.map((item) => item.confidence) : latency.confidences;
|
|
141
174
|
const reliabilityValues = [...failures.map((item) => 1 - item.median), ...outcomes.map((item) => item.median)];
|
|
142
|
-
const withEvidence = (values: number[], confidences: number[], lowerIsBetter: boolean, label: string): RawComponent =>
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
175
|
+
const withEvidence = (values: number[], confidences: number[], lowerIsBetter: boolean, label: string): RawComponent =>
|
|
176
|
+
values.length === 0
|
|
177
|
+
? { value: null, confidence: 0, evidenceCount: 0, reason: `${label} evidence is missing`, lowerIsBetter }
|
|
178
|
+
: {
|
|
179
|
+
value: average(values),
|
|
180
|
+
confidence:
|
|
181
|
+
average(confidences) / (1 + (Math.max(...values) - Math.min(...values)) / Math.max(Math.abs(average(values)), Number.EPSILON)),
|
|
182
|
+
evidenceCount: values.length,
|
|
183
|
+
reason: `${values.length} ${label} observation${values.length === 1 ? "" : "s"}`,
|
|
184
|
+
lowerIsBetter,
|
|
185
|
+
};
|
|
151
186
|
const components: Record<UtilityComponentName, RawComponent> = {
|
|
152
187
|
quality: withEvidence(qualityValues, quality.confidences, false, "task quality"),
|
|
153
188
|
cost: withEvidence(prices, [...priceInput.confidences, ...priceOutput.confidences], true, "price"),
|
|
154
189
|
latency: withEvidence(latencyValues, latencyConfidences, true, "latency"),
|
|
155
190
|
context: withEvidence(context.values, context.confidences, false, "context window"),
|
|
156
|
-
reliability: withEvidence(
|
|
191
|
+
reliability: withEvidence(
|
|
192
|
+
reliabilityValues,
|
|
193
|
+
[...failures, ...outcomes].map((item) => item.confidence),
|
|
194
|
+
false,
|
|
195
|
+
"local reliability",
|
|
196
|
+
),
|
|
197
|
+
};
|
|
198
|
+
return {
|
|
199
|
+
components,
|
|
200
|
+
provenance: [...quality.provenance, ...priceInput.provenance, ...priceOutput.provenance, ...latency.provenance, ...context.provenance],
|
|
157
201
|
};
|
|
158
|
-
return { components, provenance: [...quality.provenance, ...priceInput.provenance, ...priceOutput.provenance, ...latency.provenance, ...context.provenance] };
|
|
159
202
|
}
|
|
160
203
|
|
|
161
204
|
function normalizedScore(value: number, values: number[], lowerIsBetter: boolean): number {
|
|
@@ -167,55 +210,78 @@ function normalizedScore(value: number, values: number[], lowerIsBetter: boolean
|
|
|
167
210
|
}
|
|
168
211
|
|
|
169
212
|
export function rankModelCandidates(value: ModelRankingInput): ModelRankingResult {
|
|
170
|
-
if (!Array.isArray(value.candidates) || value.candidates.length === 0 || value.candidates.length > MAX_DYNAMIC_ROUTES)
|
|
171
|
-
|
|
172
|
-
if (!Array.isArray(value.
|
|
173
|
-
|
|
213
|
+
if (!Array.isArray(value.candidates) || value.candidates.length === 0 || value.candidates.length > MAX_DYNAMIC_ROUTES)
|
|
214
|
+
throw new Error("candidate count is outside its supported range");
|
|
215
|
+
if (!Array.isArray(value.externalEvidence) || value.externalEvidence.length > BENCHMARK_MAX_OBSERVATIONS_PER_SNAPSHOT * 4)
|
|
216
|
+
throw new Error("external evidence exceeds the supported bound");
|
|
217
|
+
if (!Array.isArray(value.localEvidence) || value.localEvidence.length > MODEL_AGGREGATE_MAX_ROWS)
|
|
218
|
+
throw new Error("local evidence exceeds the supported bound");
|
|
219
|
+
if (value.scopeAuthority !== "exact-session" && value.scopeAuthority !== "available-models")
|
|
220
|
+
throw new Error("scope authority is invalid");
|
|
174
221
|
if (!TASK_DOMAINS.includes(value.domain)) throw new Error("task domain is invalid");
|
|
175
222
|
if (!TASK_TYPES.includes(value.type)) throw new Error("task type is invalid");
|
|
176
223
|
if (!Number.isSafeInteger(value.now) || value.now <= 0) throw new Error("ranking time is invalid");
|
|
177
224
|
const budgetPressure = finiteBound(value.budgetPressure, "budget pressure", 0, 2);
|
|
178
|
-
const weights = Object.fromEntries(
|
|
225
|
+
const weights = Object.fromEntries(
|
|
226
|
+
COMPONENTS.map((name) => [name, finiteBound(value.weights[name], `${name} weight`, 0, 10)]),
|
|
227
|
+
) as unknown as UtilityWeights;
|
|
179
228
|
const seen = new Set<string>();
|
|
180
|
-
const candidates = value.candidates
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
229
|
+
const candidates = value.candidates
|
|
230
|
+
.map((candidate) => ({ ...candidate }))
|
|
231
|
+
.filter((candidate) => {
|
|
232
|
+
const identity = candidateIdentity(candidate);
|
|
233
|
+
if (seen.has(identity)) return false;
|
|
234
|
+
seen.add(identity);
|
|
235
|
+
return true;
|
|
236
|
+
});
|
|
186
237
|
const raw = candidates.map((candidate) => rawComponents(candidate, value));
|
|
187
238
|
const effectiveWeights: UtilityWeights = { ...weights, cost: weights.cost * (1 + budgetPressure) };
|
|
188
|
-
const ranked = candidates
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
const
|
|
192
|
-
|
|
239
|
+
const ranked = candidates
|
|
240
|
+
.map((candidate, index): RankedModel => {
|
|
241
|
+
const source = raw[index]!;
|
|
242
|
+
const components = COMPONENTS.map((name): UtilityComponent => {
|
|
243
|
+
const component = source.components[name];
|
|
244
|
+
const comparable = raw.map((item) => item.components[name].value).filter((item): item is number => item !== null);
|
|
245
|
+
return {
|
|
246
|
+
name,
|
|
247
|
+
score: component.value === null ? null : normalizedScore(component.value, comparable, component.lowerIsBetter),
|
|
248
|
+
confidence: component.confidence,
|
|
249
|
+
weight: effectiveWeights[name],
|
|
250
|
+
evidenceCount: component.evidenceCount,
|
|
251
|
+
reason: component.reason,
|
|
252
|
+
};
|
|
253
|
+
});
|
|
254
|
+
const known = components.filter(
|
|
255
|
+
(component): component is UtilityComponent & { score: number } => component.score !== null && component.weight > 0,
|
|
256
|
+
);
|
|
257
|
+
const knownWeight = known.reduce((sum, component) => sum + component.weight, 0);
|
|
258
|
+
const totalWeight = components.reduce((sum, component) => sum + component.weight, 0);
|
|
259
|
+
const utility =
|
|
260
|
+
knownWeight === 0 ? null : known.reduce((sum, component) => sum + component.score * component.weight, 0) / knownWeight;
|
|
261
|
+
const confidence =
|
|
262
|
+
totalWeight === 0 ? 0 : known.reduce((sum, component) => sum + component.confidence * component.weight, 0) / totalWeight;
|
|
263
|
+
const provenance = [
|
|
264
|
+
...new Map(source.provenance.map((item) => [`${item.sourceId}:${item.revision}:${item.url}`, item])).values(),
|
|
265
|
+
].sort((left, right) => left.sourceId.localeCompare(right.sourceId) || left.revision.localeCompare(right.revision));
|
|
193
266
|
return {
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
267
|
+
candidate,
|
|
268
|
+
identity: candidateIdentity(candidate),
|
|
269
|
+
utility,
|
|
270
|
+
confidence,
|
|
271
|
+
components,
|
|
272
|
+
provenance,
|
|
273
|
+
trace: [
|
|
274
|
+
`domain ${value.domain}, type ${value.type}`,
|
|
275
|
+
`budget pressure ${budgetPressure.toFixed(3)} makes cost weight ${effectiveWeights.cost.toFixed(3)}`,
|
|
276
|
+
`${known.length}/${components.length} utility components have evidence`,
|
|
277
|
+
`scope authority ${value.scopeAuthority}`,
|
|
278
|
+
],
|
|
200
279
|
};
|
|
201
|
-
})
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
const confidence = totalWeight === 0 ? 0 : known.reduce((sum, component) => sum + (component.confidence * component.weight), 0) / totalWeight;
|
|
207
|
-
const provenance = [...new Map(source.provenance.map((item) => [`${item.sourceId}:${item.revision}:${item.url}`, item])).values()]
|
|
208
|
-
.sort((left, right) => left.sourceId.localeCompare(right.sourceId) || left.revision.localeCompare(right.revision));
|
|
209
|
-
return {
|
|
210
|
-
candidate,
|
|
211
|
-
identity: candidateIdentity(candidate),
|
|
212
|
-
utility,
|
|
213
|
-
confidence,
|
|
214
|
-
components,
|
|
215
|
-
provenance,
|
|
216
|
-
trace: [`domain ${value.domain}, type ${value.type}`, `budget pressure ${budgetPressure.toFixed(3)} makes cost weight ${effectiveWeights.cost.toFixed(3)}`, `${known.length}/${components.length} utility components have evidence`, `scope authority ${value.scopeAuthority}`],
|
|
217
|
-
};
|
|
218
|
-
}).sort((left, right) => (right.utility ?? -1) - (left.utility ?? -1) || right.confidence - left.confidence || left.identity.localeCompare(right.identity));
|
|
280
|
+
})
|
|
281
|
+
.sort(
|
|
282
|
+
(left, right) =>
|
|
283
|
+
(right.utility ?? -1) - (left.utility ?? -1) || right.confidence - left.confidence || left.identity.localeCompare(right.identity),
|
|
284
|
+
);
|
|
219
285
|
const knownComponents = ranked.reduce((sum, item) => sum + item.components.filter((component) => component.score !== null).length, 0);
|
|
220
286
|
const possibleComponents = ranked.length * COMPONENTS.length;
|
|
221
287
|
const completeness = knownComponents === 0 ? "insufficient-evidence" : knownComponents === possibleComponents ? "complete" : "partial";
|
package/src/domain/task-cost.ts
CHANGED
|
@@ -44,7 +44,15 @@ function attributeText(attributes: Record<string, unknown>, key: string): string
|
|
|
44
44
|
function entryFor(byTask: Map<string, TaskCostEntry>, taskId: string): TaskCostEntry {
|
|
45
45
|
const existing = byTask.get(taskId);
|
|
46
46
|
if (existing) return existing;
|
|
47
|
-
const created: TaskCostEntry = {
|
|
47
|
+
const created: TaskCostEntry = {
|
|
48
|
+
taskId,
|
|
49
|
+
costUsd: 0,
|
|
50
|
+
inputTokens: 0,
|
|
51
|
+
outputTokens: 0,
|
|
52
|
+
cacheReadTokens: 0,
|
|
53
|
+
cacheWriteTokens: 0,
|
|
54
|
+
byModel: [],
|
|
55
|
+
};
|
|
48
56
|
byTask.set(taskId, created);
|
|
49
57
|
return created;
|
|
50
58
|
}
|
|
@@ -53,7 +61,16 @@ function breakdownFor(byModel: Map<string, TaskCostBreakdown>, provider: string,
|
|
|
53
61
|
const key = `${provider}\u0000${model}\u0000${thinking}`;
|
|
54
62
|
const existing = byModel.get(key);
|
|
55
63
|
if (existing) return existing;
|
|
56
|
-
const created: TaskCostBreakdown = {
|
|
64
|
+
const created: TaskCostBreakdown = {
|
|
65
|
+
provider,
|
|
66
|
+
model,
|
|
67
|
+
thinking,
|
|
68
|
+
costUsd: 0,
|
|
69
|
+
inputTokens: 0,
|
|
70
|
+
outputTokens: 0,
|
|
71
|
+
cacheReadTokens: 0,
|
|
72
|
+
cacheWriteTokens: 0,
|
|
73
|
+
};
|
|
57
74
|
byModel.set(key, created);
|
|
58
75
|
return created;
|
|
59
76
|
}
|
|
@@ -80,25 +97,49 @@ export function buildTaskCostSummary(rows: StoredMetricObservation[], options: T
|
|
|
80
97
|
for (const row of rows) {
|
|
81
98
|
if (row.source !== "pi" || typeof row.value !== "number" || !Number.isFinite(row.value) || row.value < 0) continue;
|
|
82
99
|
if (row.observedAt < options.since || row.observedAt > options.until) continue;
|
|
83
|
-
const taskId = typeof row.attributes
|
|
100
|
+
const taskId = typeof row.attributes.taskId === "string" ? row.attributes.taskId : undefined;
|
|
84
101
|
if (row.metric === "cost" && row.unit === "usd") {
|
|
85
|
-
if (taskId === undefined) {
|
|
102
|
+
if (taskId === undefined) {
|
|
103
|
+
unattributedCostUsd += row.value;
|
|
104
|
+
continue;
|
|
105
|
+
}
|
|
86
106
|
entryFor(byTask, taskId).costUsd += row.value;
|
|
87
107
|
if (!byTaskModel.has(taskId)) byTaskModel.set(taskId, new Map());
|
|
88
|
-
breakdownFor(
|
|
108
|
+
breakdownFor(
|
|
109
|
+
byTaskModel.get(taskId)!,
|
|
110
|
+
attributeText(row.attributes, "provider"),
|
|
111
|
+
attributeText(row.attributes, "model"),
|
|
112
|
+
attributeText(row.attributes, "thinking"),
|
|
113
|
+
).costUsd += row.value;
|
|
89
114
|
continue;
|
|
90
115
|
}
|
|
91
116
|
if (taskId === undefined || row.unit !== "tokens" || !TOKEN_METRICS.has(row.metric)) continue;
|
|
92
117
|
const entry = entryFor(byTask, taskId);
|
|
93
118
|
if (!byTaskModel.has(taskId)) byTaskModel.set(taskId, new Map());
|
|
94
|
-
const breakdown = breakdownFor(
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
119
|
+
const breakdown = breakdownFor(
|
|
120
|
+
byTaskModel.get(taskId)!,
|
|
121
|
+
attributeText(row.attributes, "provider"),
|
|
122
|
+
attributeText(row.attributes, "model"),
|
|
123
|
+
attributeText(row.attributes, "thinking"),
|
|
124
|
+
);
|
|
125
|
+
if (row.metric === "input-tokens") {
|
|
126
|
+
entry.inputTokens += row.value;
|
|
127
|
+
breakdown.inputTokens += row.value;
|
|
128
|
+
} else if (row.metric === "output-tokens") {
|
|
129
|
+
entry.outputTokens += row.value;
|
|
130
|
+
breakdown.outputTokens += row.value;
|
|
131
|
+
} else if (row.metric === "cache-read-tokens") {
|
|
132
|
+
entry.cacheReadTokens += row.value;
|
|
133
|
+
breakdown.cacheReadTokens += row.value;
|
|
134
|
+
} else {
|
|
135
|
+
entry.cacheWriteTokens += row.value;
|
|
136
|
+
breakdown.cacheWriteTokens += row.value;
|
|
137
|
+
}
|
|
99
138
|
}
|
|
100
139
|
for (const [taskId, entry] of byTask) {
|
|
101
|
-
entry.byModel = [...(byTaskModel.get(taskId)?.values() ?? [])].sort(
|
|
140
|
+
entry.byModel = [...(byTaskModel.get(taskId)?.values() ?? [])].sort(
|
|
141
|
+
(left, right) => right.costUsd - left.costUsd || left.model.localeCompare(right.model),
|
|
142
|
+
);
|
|
102
143
|
}
|
|
103
144
|
const entries = [...byTask.values()].sort((left, right) => right.costUsd - left.costUsd || left.taskId.localeCompare(right.taskId));
|
|
104
145
|
return { since: options.since, until: options.until, entries, unattributedCostUsd, truncated: options.truncated === true };
|
package/src/domain/task-focus.ts
CHANGED
|
@@ -16,12 +16,14 @@ const STATUSES = new Set<string>(["focused", "paused", "unpaused", "cleared"]);
|
|
|
16
16
|
function record(value: unknown): Record<string, unknown> {
|
|
17
17
|
if (typeof value !== "object" || value === null || Array.isArray(value)) throw new Error("task-focus event must be an object");
|
|
18
18
|
const result = value as Record<string, unknown>;
|
|
19
|
-
for (const key of Object.keys(result))
|
|
19
|
+
for (const key of Object.keys(result))
|
|
20
|
+
if (!TOP_LEVEL_FIELDS.has(key)) throw new Error(`task-focus event contains unexpected field: ${key}`);
|
|
20
21
|
return result;
|
|
21
22
|
}
|
|
22
23
|
|
|
23
24
|
function boundedId(value: unknown, name: string): string {
|
|
24
|
-
if (typeof value !== "string" || value.length === 0 || value.length > TASK_FOCUS_ID_MAX_LENGTH)
|
|
25
|
+
if (typeof value !== "string" || value.length === 0 || value.length > TASK_FOCUS_ID_MAX_LENGTH)
|
|
26
|
+
throw new Error(`${name} must be a non-empty bounded string`);
|
|
25
27
|
return value;
|
|
26
28
|
}
|
|
27
29
|
|
|
@@ -34,16 +36,17 @@ function boundedId(value: unknown, name: string): string {
|
|
|
34
36
|
*/
|
|
35
37
|
export function validateTaskFocusEvent(value: unknown, now = Date.now()): TaskFocusEvent {
|
|
36
38
|
const input = record(value);
|
|
37
|
-
if (input
|
|
38
|
-
const status = input
|
|
39
|
+
if (input.schema !== PAPYRUS_TASK_FOCUS_SCHEMA) throw new Error("task-focus event schema is not supported");
|
|
40
|
+
const status = input.status;
|
|
39
41
|
if (typeof status !== "string" || !STATUSES.has(status)) throw new Error("task-focus event status is not supported");
|
|
40
|
-
const observedAt = input
|
|
41
|
-
if (typeof observedAt !== "number" || !Number.isSafeInteger(observedAt) || observedAt < 0)
|
|
42
|
+
const observedAt = input.observedAt;
|
|
43
|
+
if (typeof observedAt !== "number" || !Number.isSafeInteger(observedAt) || observedAt < 0)
|
|
44
|
+
throw new Error("task-focus event observedAt must be a non-negative integer");
|
|
42
45
|
if (Math.abs(now - observedAt) > TASK_FOCUS_EVENT_MAX_AGE_MS) throw new Error("task-focus event is stale");
|
|
43
|
-
const rawTaskId = input
|
|
46
|
+
const rawTaskId = input.taskId;
|
|
44
47
|
const taskId = rawTaskId === null ? null : boundedId(rawTaskId, "taskId");
|
|
45
48
|
if (taskId === null && status !== "cleared") throw new Error(`task-focus event of status "${status}" requires a taskId`);
|
|
46
|
-
const rawSessionId = input
|
|
49
|
+
const rawSessionId = input.sessionId;
|
|
47
50
|
const sessionId = rawSessionId === undefined ? undefined : boundedId(rawSessionId, "sessionId");
|
|
48
51
|
return {
|
|
49
52
|
schema: PAPYRUS_TASK_FOCUS_SCHEMA,
|
package/src/domain/usage.ts
CHANGED
|
@@ -7,9 +7,9 @@ export const USAGE_PERIODS = [
|
|
|
7
7
|
{ id: "monthly", label: "Monthly", windowMs: 30 * MILLISECONDS_PER_DAY, bucketCount: 30 },
|
|
8
8
|
{ id: "quarterly", label: "Quarterly", windowMs: 90 * MILLISECONDS_PER_DAY, bucketCount: 90 },
|
|
9
9
|
] as const;
|
|
10
|
-
export type UsagePeriod = typeof USAGE_PERIODS[number]["id"];
|
|
10
|
+
export type UsagePeriod = (typeof USAGE_PERIODS)[number]["id"];
|
|
11
11
|
|
|
12
|
-
export function usagePeriod(period: UsagePeriod): typeof USAGE_PERIODS[number] {
|
|
12
|
+
export function usagePeriod(period: UsagePeriod): (typeof USAGE_PERIODS)[number] {
|
|
13
13
|
return USAGE_PERIODS.find((candidate) => candidate.id === period)!;
|
|
14
14
|
}
|
|
15
15
|
|
package/src/index.ts
CHANGED
|
@@ -1,51 +1,44 @@
|
|
|
1
|
-
export * from "./constants.ts";
|
|
2
1
|
export {
|
|
2
|
+
connectJittorClient,
|
|
3
3
|
type FetchTransport,
|
|
4
4
|
JittorClient,
|
|
5
|
-
connectJittorClient,
|
|
6
5
|
} from "./client.ts";
|
|
6
|
+
export * from "./constants.ts";
|
|
7
7
|
export {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
type
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
} from "./service.ts";
|
|
18
|
-
export {
|
|
19
|
-
type CompactionDurationEstimate,
|
|
20
|
-
type CompactionStart,
|
|
21
|
-
CompactionTelemetry,
|
|
22
|
-
type ContextAssessment,
|
|
23
|
-
type PapyrusContextInjection,
|
|
24
|
-
assessContextTelemetry,
|
|
25
|
-
estimateCompactionDuration,
|
|
26
|
-
papyrusContextMetric,
|
|
27
|
-
validatePapyrusContextInjection,
|
|
28
|
-
} from "./domain/context-telemetry.ts";
|
|
29
|
-
export {
|
|
30
|
-
type TaskFocusEvent,
|
|
31
|
-
type TaskFocusStatus,
|
|
32
|
-
applyTaskFocusEvent,
|
|
33
|
-
validateTaskFocusEvent,
|
|
34
|
-
} from "./domain/task-focus.ts";
|
|
8
|
+
type CodexFailure,
|
|
9
|
+
type CodexFailureKind,
|
|
10
|
+
type CodexFailureMetadata,
|
|
11
|
+
type CodexRecoveryAttempt,
|
|
12
|
+
type CodexRecoveryOptions,
|
|
13
|
+
type CodexRecoveryPlan,
|
|
14
|
+
CodexRecoveryPolicy,
|
|
15
|
+
classifyCodexFailure,
|
|
16
|
+
} from "./domain/codex-recovery.ts";
|
|
35
17
|
export {
|
|
36
18
|
type ContextConfidenceTier,
|
|
37
19
|
type ContextContribution,
|
|
38
20
|
type ContextSegment,
|
|
39
21
|
type ContextSegmentItem,
|
|
22
|
+
computeToolSchemaLedger,
|
|
23
|
+
contextContributionMetric,
|
|
40
24
|
type ToolLedgerEntry,
|
|
41
25
|
type ToolLedgerSourceUsage,
|
|
42
26
|
type ToolLedgerToolUsage,
|
|
43
|
-
computeToolSchemaLedger,
|
|
44
|
-
contextContributionMetric,
|
|
45
27
|
toolLedgerSegment,
|
|
46
28
|
validateContextContribution,
|
|
47
29
|
validateContextSegment,
|
|
48
30
|
} from "./domain/context-hub.ts";
|
|
31
|
+
export {
|
|
32
|
+
assessContextTelemetry,
|
|
33
|
+
type CompactionDurationEstimate,
|
|
34
|
+
type CompactionStart,
|
|
35
|
+
CompactionTelemetry,
|
|
36
|
+
type ContextAssessment,
|
|
37
|
+
estimateCompactionDuration,
|
|
38
|
+
type PapyrusContextInjection,
|
|
39
|
+
papyrusContextMetric,
|
|
40
|
+
validatePapyrusContextInjection,
|
|
41
|
+
} from "./domain/context-telemetry.ts";
|
|
49
42
|
export {
|
|
50
43
|
METRIC_UNITS,
|
|
51
44
|
type MetricObservation,
|
|
@@ -55,8 +48,8 @@ export {
|
|
|
55
48
|
validateMetricObservation,
|
|
56
49
|
} from "./domain/metric.ts";
|
|
57
50
|
export {
|
|
58
|
-
|
|
59
|
-
|
|
51
|
+
aggregateModelMetrics,
|
|
52
|
+
classifyTaskFromTools,
|
|
60
53
|
type ExplicitOutcome,
|
|
61
54
|
type ModelAggregateOptions,
|
|
62
55
|
type ModelMetricAggregate,
|
|
@@ -64,9 +57,9 @@ export {
|
|
|
64
57
|
type ModelTaskClassification,
|
|
65
58
|
type ModelTaskDomain,
|
|
66
59
|
type ModelTaskType,
|
|
67
|
-
aggregateModelMetrics,
|
|
68
|
-
classifyTaskFromTools,
|
|
69
60
|
modelRunMetrics,
|
|
61
|
+
TASK_DOMAINS,
|
|
62
|
+
TASK_TYPES,
|
|
70
63
|
validateModelRunObservation,
|
|
71
64
|
} from "./domain/model-observation.ts";
|
|
72
65
|
export {
|
|
@@ -75,17 +68,27 @@ export {
|
|
|
75
68
|
type ModelRankingResult,
|
|
76
69
|
type RankedModel,
|
|
77
70
|
type RankingProvenance,
|
|
71
|
+
rankModelCandidates,
|
|
78
72
|
type ScopeAuthority,
|
|
79
73
|
type UtilityComponent,
|
|
80
74
|
type UtilityComponentName,
|
|
81
75
|
type UtilityWeights,
|
|
82
|
-
rankModelCandidates,
|
|
83
76
|
} from "./domain/model-ranking.ts";
|
|
84
77
|
export {
|
|
85
|
-
|
|
78
|
+
applyTaskFocusEvent,
|
|
79
|
+
type TaskFocusEvent,
|
|
80
|
+
type TaskFocusStatus,
|
|
81
|
+
validateTaskFocusEvent,
|
|
82
|
+
} from "./domain/task-focus.ts";
|
|
83
|
+
export {
|
|
84
|
+
buildCostGraph,
|
|
85
|
+
buildUsageGraph,
|
|
86
86
|
type CostBucket,
|
|
87
87
|
type CostGraph,
|
|
88
88
|
type CostSeries,
|
|
89
|
+
identity,
|
|
90
|
+
resolveUsageWindow,
|
|
91
|
+
USAGE_PERIODS,
|
|
89
92
|
type UsageAggregateRow,
|
|
90
93
|
type UsageBreakdown,
|
|
91
94
|
type UsageBucket,
|
|
@@ -94,43 +97,13 @@ export {
|
|
|
94
97
|
type UsageGraphOptions,
|
|
95
98
|
type UsagePeriod,
|
|
96
99
|
type UsageSeries,
|
|
97
|
-
buildCostGraph,
|
|
98
|
-
buildUsageGraph,
|
|
99
|
-
identity,
|
|
100
|
-
resolveUsageWindow,
|
|
101
100
|
usageBucketIndex,
|
|
102
101
|
usagePeriod,
|
|
103
102
|
usagePeriodStart,
|
|
104
103
|
} from "./domain/usage.ts";
|
|
105
104
|
export {
|
|
106
|
-
CodexRecoveryPolicy,
|
|
107
|
-
classifyCodexFailure,
|
|
108
|
-
type CodexFailure,
|
|
109
|
-
type CodexFailureKind,
|
|
110
|
-
type CodexFailureMetadata,
|
|
111
|
-
type CodexRecoveryAttempt,
|
|
112
|
-
type CodexRecoveryOptions,
|
|
113
|
-
type CodexRecoveryPlan,
|
|
114
|
-
} from "./domain/codex-recovery.ts";
|
|
115
|
-
export {
|
|
116
|
-
hasAnthropicRateLimitHeaders,
|
|
117
|
-
parseAnthropicRateLimitHeaders,
|
|
118
|
-
type AnthropicMetricSource,
|
|
119
|
-
type AnthropicRateLimitSnapshot,
|
|
120
|
-
type AnthropicRateLimitWindow,
|
|
121
|
-
} from "./providers/anthropic-contracts.ts";
|
|
122
|
-
export { parseCodexRateLimitHeaders } from "./providers/codex.ts";
|
|
123
|
-
export {
|
|
124
|
-
classifyGoogleVertexFailure,
|
|
125
|
-
googleVertexFailureMetrics,
|
|
126
|
-
type GoogleVertexFailure,
|
|
127
|
-
type GoogleVertexFailureKind,
|
|
128
|
-
type GoogleVertexFailureMetadata,
|
|
129
|
-
type GoogleVertexMetricSource,
|
|
130
|
-
} from "./providers/google-vertex-contracts.ts";
|
|
131
|
-
export {
|
|
132
|
-
evaluateRoutingPolicy,
|
|
133
105
|
type BudgetWindow,
|
|
106
|
+
evaluateRoutingPolicy,
|
|
134
107
|
type PolicyAction,
|
|
135
108
|
type PolicyConfig,
|
|
136
109
|
type PolicyDecision,
|
|
@@ -140,6 +113,7 @@ export {
|
|
|
140
113
|
type Route,
|
|
141
114
|
type TelemetryFreshness,
|
|
142
115
|
} from "./policy.ts";
|
|
116
|
+
export type { DistinctScopesFilter, MetricStore, UsageAggregateFilter } from "./ports/metric-store.ts";
|
|
143
117
|
export type {
|
|
144
118
|
RouteOverride,
|
|
145
119
|
RouterController,
|
|
@@ -147,5 +121,31 @@ export type {
|
|
|
147
121
|
TelemetryPollResult,
|
|
148
122
|
TelemetrySourceStatus,
|
|
149
123
|
} from "./ports/router-controller.ts";
|
|
150
|
-
export
|
|
124
|
+
export {
|
|
125
|
+
type AnthropicMetricSource,
|
|
126
|
+
type AnthropicRateLimitSnapshot,
|
|
127
|
+
type AnthropicRateLimitWindow,
|
|
128
|
+
hasAnthropicRateLimitHeaders,
|
|
129
|
+
parseAnthropicRateLimitHeaders,
|
|
130
|
+
} from "./providers/anthropic-contracts.ts";
|
|
131
|
+
export { parseCodexRateLimitHeaders } from "./providers/codex.ts";
|
|
132
|
+
export {
|
|
133
|
+
classifyGoogleVertexFailure,
|
|
134
|
+
type GoogleVertexFailure,
|
|
135
|
+
type GoogleVertexFailureKind,
|
|
136
|
+
type GoogleVertexFailureMetadata,
|
|
137
|
+
type GoogleVertexMetricSource,
|
|
138
|
+
googleVertexFailureMetrics,
|
|
139
|
+
} from "./providers/google-vertex-contracts.ts";
|
|
140
|
+
export {
|
|
141
|
+
createApp,
|
|
142
|
+
EXPECTED_OPERATION_NAMES,
|
|
143
|
+
InvalidSessionSecretError,
|
|
144
|
+
type JittorAppOptions,
|
|
145
|
+
JittorService,
|
|
146
|
+
type OperationInputs,
|
|
147
|
+
type OperationName,
|
|
148
|
+
type OperationOutputs,
|
|
149
|
+
UnknownOperationError,
|
|
150
|
+
} from "./service.ts";
|
|
151
151
|
export { VERSION as jittorVersion } from "./version.ts";
|
package/src/log.ts
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* four daemons. `component`/`level`/`timestamp` and credential-safety (callers still must pass
|
|
9
9
|
* only bounded, non-sensitive fields) are unchanged.
|
|
10
10
|
*/
|
|
11
|
-
import { createLogger, type LogLevel as VehicleLogLevel
|
|
11
|
+
import { createLogger, type Logger, type LogLevel as VehicleLogLevel } from "@danypops/vehicle-server/logging";
|
|
12
12
|
|
|
13
13
|
export type LogLevel = Extract<VehicleLogLevel, "info" | "warn" | "error">;
|
|
14
14
|
|
|
@@ -19,7 +19,12 @@ export type LogLevel = Extract<VehicleLogLevel, "info" | "warn" | "error">;
|
|
|
19
19
|
* `console.error` entirely) so existing tooling/tests that intercept `console.error` keep working.
|
|
20
20
|
*/
|
|
21
21
|
export const logger: Logger = createLogger("jittor-daemon", {
|
|
22
|
-
destination: {
|
|
22
|
+
destination: {
|
|
23
|
+
write: (chunk: string) => {
|
|
24
|
+
console.error(chunk.replace(/\n$/, ""));
|
|
25
|
+
return true;
|
|
26
|
+
},
|
|
27
|
+
},
|
|
23
28
|
});
|
|
24
29
|
|
|
25
30
|
/** Credential-safe structured daemon event. Callers must pass bounded, non-sensitive fields. */
|
|
@@ -5,7 +5,7 @@ import type { OperationHandlerMap } from "./types.ts";
|
|
|
5
5
|
/** benchmark.* -- every operation whose only collaborator is the benchmark-controller port. */
|
|
6
6
|
export function benchmarkOperations(benchmarks: BenchmarkController): OperationHandlerMap {
|
|
7
7
|
return {
|
|
8
|
-
"benchmark.refresh": (input) => benchmarks.refresh(input
|
|
8
|
+
"benchmark.refresh": (input) => benchmarks.refresh(input.force === true),
|
|
9
9
|
"benchmark.status": () => benchmarks.status(),
|
|
10
10
|
"benchmark.query": (input) => benchmarks.query(input as unknown as BenchmarkQuery),
|
|
11
11
|
};
|