modelrig 0.11.0 → 0.12.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/CHANGELOG.md +82 -0
- package/dist/adapters/anthropic.d.ts +4 -4
- package/dist/adapters/anthropic.d.ts.map +1 -1
- package/dist/adapters/anthropic.js +8 -1
- package/dist/adapters/deepseek.d.ts +2 -4
- package/dist/adapters/deepseek.d.ts.map +1 -1
- package/dist/adapters/deepseek.js +9 -5
- package/dist/adapters/gemini.d.ts +4 -1
- package/dist/adapters/gemini.d.ts.map +1 -1
- package/dist/adapters/gemini.js +16 -1
- package/dist/adapters/grok.d.ts +2 -4
- package/dist/adapters/grok.d.ts.map +1 -1
- package/dist/adapters/grok.js +8 -4
- package/dist/adapters/history-mapping.d.ts +126 -0
- package/dist/adapters/history-mapping.d.ts.map +1 -0
- package/dist/adapters/history-mapping.js +230 -0
- package/dist/adapters/openai-compat.d.ts +2 -4
- package/dist/adapters/openai-compat.d.ts.map +1 -1
- package/dist/adapters/openai-compat.js +9 -5
- package/dist/adapters/openai.d.ts +2 -4
- package/dist/adapters/openai.d.ts.map +1 -1
- package/dist/adapters/openai.js +8 -4
- package/dist/adapters/responses-api.d.ts.map +1 -1
- package/dist/adapters/responses-api.js +9 -4
- package/dist/artifacts/artifact.d.ts.map +1 -1
- package/dist/artifacts/artifact.js +79 -0
- package/dist/artifacts/conversation.d.ts +128 -0
- package/dist/artifacts/conversation.d.ts.map +1 -0
- package/dist/artifacts/conversation.js +172 -0
- package/dist/artifacts/index.d.ts +1 -1
- package/dist/artifacts/index.d.ts.map +1 -1
- package/dist/artifacts/media.d.ts +46 -0
- package/dist/artifacts/media.d.ts.map +1 -0
- package/dist/artifacts/media.js +88 -0
- package/dist/artifacts/run-context.d.ts +61 -2
- package/dist/artifacts/run-context.d.ts.map +1 -1
- package/dist/artifacts/run-context.js +183 -12
- package/dist/artifacts/scrub.d.ts +3 -0
- package/dist/artifacts/scrub.d.ts.map +1 -1
- package/dist/bakeoff/engine.d.ts.map +1 -1
- package/dist/bakeoff/engine.js +34 -7
- package/dist/bakeoff/eval-cases.d.ts +5 -0
- package/dist/bakeoff/eval-cases.d.ts.map +1 -1
- package/dist/bakeoff/eval-cases.js +10 -1
- package/dist/bakeoff/replay.d.ts +15 -0
- package/dist/bakeoff/replay.d.ts.map +1 -1
- package/dist/bakeoff/replay.js +19 -1
- package/dist/bakeoff/report.d.ts.map +1 -1
- package/dist/bakeoff/report.js +9 -2
- package/dist/bakeoff/turn-replay.d.ts +93 -0
- package/dist/bakeoff/turn-replay.d.ts.map +1 -0
- package/dist/bakeoff/turn-replay.js +150 -0
- package/dist/bakeoff/types.d.ts +12 -0
- package/dist/bakeoff/types.d.ts.map +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +1 -0
- package/dist/config.d.ts +18 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +23 -3
- package/dist/doctor-preflight.d.ts.map +1 -1
- package/dist/doctor-preflight.js +6 -1
- package/dist/feedback/grade.d.ts +7 -2
- package/dist/feedback/grade.d.ts.map +1 -1
- package/dist/feedback/grade.js +14 -0
- package/dist/feedback/outcome.d.ts +103 -0
- package/dist/feedback/outcome.d.ts.map +1 -0
- package/dist/feedback/outcome.js +175 -0
- package/dist/gateway/history-bridge.d.ts +32 -0
- package/dist/gateway/history-bridge.d.ts.map +1 -0
- package/dist/gateway/history-bridge.js +64 -0
- package/dist/gateway/serve-client.d.ts.map +1 -1
- package/dist/gateway/serve-client.js +6 -0
- package/dist/index.d.ts +44 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +110 -9
- package/dist/init/init-cli.d.ts.map +1 -1
- package/dist/init/init-cli.js +21 -4
- package/dist/init/observe-cli.js +1 -1
- package/dist/init/observe.d.ts +7 -1
- package/dist/init/observe.d.ts.map +1 -1
- package/dist/init/observe.js +74 -22
- package/dist/init/starter.d.ts +36 -0
- package/dist/init/starter.d.ts.map +1 -0
- package/dist/init/starter.js +85 -0
- package/dist/registry/build.d.ts +4 -0
- package/dist/registry/build.d.ts.map +1 -1
- package/dist/registry/generation-models.d.ts +68 -0
- package/dist/registry/generation-models.d.ts.map +1 -0
- package/dist/registry/generation-models.js +168 -0
- package/dist/registry/model-kind.d.ts +34 -0
- package/dist/registry/model-kind.d.ts.map +1 -0
- package/dist/registry/model-kind.js +40 -0
- package/dist/registry/pricing-shape.d.ts +29 -1
- package/dist/registry/pricing-shape.d.ts.map +1 -1
- package/dist/registry/pricing-shape.js +9 -0
- package/dist/registry/pricing.d.ts +26 -0
- package/dist/registry/pricing.d.ts.map +1 -1
- package/dist/registry/pricing.js +29 -0
- package/dist/registry/rows.d.ts.map +1 -1
- package/dist/registry/rows.js +3 -1
- package/dist/registry/unit-pricing.d.ts +32 -0
- package/dist/registry/unit-pricing.d.ts.map +1 -0
- package/dist/registry/unit-pricing.js +62 -0
- package/dist/routes/input-loader.d.ts +38 -0
- package/dist/routes/input-loader.d.ts.map +1 -0
- package/dist/routes/input-loader.js +80 -0
- package/dist/routes/loader.d.ts.map +1 -1
- package/dist/routes/loader.js +3 -0
- package/dist/routes/resolved-bundle.d.ts +2 -0
- package/dist/routes/resolved-bundle.d.ts.map +1 -1
- package/dist/routes/resolved-bundle.js +3 -0
- package/dist/run/cost.d.ts +28 -0
- package/dist/run/cost.d.ts.map +1 -1
- package/dist/run/cost.js +74 -0
- package/dist/run/dispatch.d.ts +4 -1
- package/dist/run/dispatch.d.ts.map +1 -1
- package/dist/run/dispatch.js +3 -0
- package/dist/run/limits.d.ts +88 -0
- package/dist/run/limits.d.ts.map +1 -0
- package/dist/run/limits.js +155 -0
- package/dist/run/messages.d.ts +199 -0
- package/dist/run/messages.d.ts.map +1 -0
- package/dist/run/messages.js +430 -0
- package/dist/run/raw-finalize.d.ts +5 -2
- package/dist/run/raw-finalize.d.ts.map +1 -1
- package/dist/run/raw-finalize.js +8 -5
- package/dist/run/raw-request.d.ts.map +1 -1
- package/dist/run/raw-request.js +2 -1
- package/dist/run/raw.d.ts +10 -4
- package/dist/run/raw.d.ts.map +1 -1
- package/dist/run/raw.js +11 -3
- package/dist/run/rawonly.d.ts +29 -5
- package/dist/run/rawonly.d.ts.map +1 -1
- package/dist/run/rawonly.js +54 -19
- package/dist/run/record-attempt.d.ts.map +1 -1
- package/dist/run/record-attempt.js +16 -6
- package/dist/run/record-call.d.ts +13 -2
- package/dist/run/record-call.d.ts.map +1 -1
- package/dist/run/record-call.js +50 -7
- package/dist/run/run.d.ts.map +1 -1
- package/dist/run/run.js +12 -8
- package/dist/telemetry/attempt-meta.d.ts +49 -1
- package/dist/telemetry/attempt-meta.d.ts.map +1 -1
- package/dist/telemetry/attempt-meta.js +45 -1
- package/dist/telemetry/exporter.d.ts.map +1 -1
- package/dist/telemetry/exporter.js +2 -1
- package/dist/telemetry/grade-mappers.d.ts +1 -1
- package/dist/telemetry/grade-mappers.d.ts.map +1 -1
- package/dist/telemetry/grade-mappers.js +7 -1
- package/dist/telemetry/grade-task.d.ts +49 -0
- package/dist/telemetry/grade-task.d.ts.map +1 -0
- package/dist/telemetry/grade-task.js +85 -0
- package/dist/telemetry/grades-sql.d.ts +16 -1
- package/dist/telemetry/grades-sql.d.ts.map +1 -1
- package/dist/telemetry/grades-sql.js +64 -4
- package/dist/telemetry/ingest-protocol.d.ts +24 -1
- package/dist/telemetry/ingest-protocol.d.ts.map +1 -1
- package/dist/telemetry/ingest-protocol.js +72 -43
- package/dist/telemetry/otel-content.d.ts +31 -0
- package/dist/telemetry/otel-content.d.ts.map +1 -0
- package/dist/telemetry/otel-content.js +106 -0
- package/dist/telemetry/otel-hierarchy.d.ts +57 -0
- package/dist/telemetry/otel-hierarchy.d.ts.map +1 -0
- package/dist/telemetry/otel-hierarchy.js +155 -0
- package/dist/telemetry/otel-normalize.d.ts +15 -1
- package/dist/telemetry/otel-normalize.d.ts.map +1 -1
- package/dist/telemetry/otel-normalize.js +24 -39
- package/dist/telemetry/sinks.d.ts +4 -1
- package/dist/telemetry/sinks.d.ts.map +1 -1
- package/dist/telemetry/sinks.js +53 -34
- package/dist/telemetry/store-migrations.d.ts.map +1 -1
- package/dist/telemetry/store-migrations.js +8 -0
- package/dist/telemetry/store.d.ts +9 -0
- package/dist/telemetry/store.d.ts.map +1 -1
- package/dist/telemetry/store.js +10 -0
- package/dist/telemetry/uniform-rows.d.ts +43 -0
- package/dist/telemetry/uniform-rows.d.ts.map +1 -0
- package/dist/telemetry/uniform-rows.js +139 -0
- package/dist/types/failures-teaching.d.ts.map +1 -1
- package/dist/types/failures-teaching.js +11 -0
- package/dist/types/failures.d.ts +1 -1
- package/dist/types/failures.d.ts.map +1 -1
- package/dist/types/failures.js +2 -0
- package/dist/types/provider.d.ts +12 -0
- package/dist/types/provider.d.ts.map +1 -1
- package/dist/types/route.d.ts +13 -0
- package/dist/types/route.d.ts.map +1 -1
- package/dist/types/telemetry.d.ts +14 -1
- package/dist/types/telemetry.d.ts.map +1 -1
- package/docs/agent-skill/SKILL.md +122 -12
- package/docs/how-it-fits.md +4 -4
- package/docs/instrumentation.md +148 -44
- package/docs/llms.txt +143 -13
- package/docs/mcp-oracle.md +15 -4
- package/docs/quickstart.md +129 -16
- package/package.json +4 -2
- package/registry/registry.json +456 -0
- package/templates/creative-approval.json +81 -0
- package/templates/support-conversation.json +82 -0
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
27
27
|
exports.normalizeOtlpTraces = normalizeOtlpTraces;
|
|
28
28
|
exports.looksLikeLogsPayload = looksLikeLogsPayload;
|
|
29
|
-
const
|
|
29
|
+
const otel_hierarchy_1 = require("./otel-hierarchy");
|
|
30
30
|
// ── value coercion (learnings §1.2, ported fresh) ────────────────────────────
|
|
31
31
|
/** OTLP AnyValue → plain JS. Numeric strings coerce EXCEPT NaN/±Infinity which
|
|
32
32
|
* stay as their string form (invalid JSON must never enter the envelope). int64
|
|
@@ -296,22 +296,6 @@ function classify(map) {
|
|
|
296
296
|
return "chat";
|
|
297
297
|
return "span";
|
|
298
298
|
}
|
|
299
|
-
// ── structure helpers ────────────────────────────────────────────────────────
|
|
300
|
-
const WS_RE = /\s+/g;
|
|
301
|
-
/** span name → step_key: trimmed, whitespace-collapsed, length-capped, never
|
|
302
|
-
* empty (the step PK forbids it). */
|
|
303
|
-
function toStepKey(name) {
|
|
304
|
-
const raw = typeof name === "string" ? name.trim().replace(WS_RE, " ") : "";
|
|
305
|
-
if (raw === "")
|
|
306
|
-
return "span";
|
|
307
|
-
return raw.slice(0, 200);
|
|
308
|
-
}
|
|
309
|
-
/** 32-hex trace id → canonical UUID string (runs.id is uuid; 16 bytes ≡ a UUID).
|
|
310
|
-
* A trace id that is not 16 bytes gets a deterministic UUID derived from it. */
|
|
311
|
-
function traceIdToRunUuid(traceHex) {
|
|
312
|
-
const hex = traceHex.length === 32 ? traceHex : (0, node_crypto_1.createHash)("sha256").update(traceHex).digest("hex").slice(0, 32);
|
|
313
|
-
return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20, 32)}`;
|
|
314
|
-
}
|
|
315
299
|
function bump(drops, reason, by = 1) {
|
|
316
300
|
drops[reason] = (drops[reason] ?? 0) + by;
|
|
317
301
|
}
|
|
@@ -374,7 +358,7 @@ function normalizeOtlpTraces(data, nowMs = Date.now()) {
|
|
|
374
358
|
}
|
|
375
359
|
const { map, keys } = attrMap(span.attributes);
|
|
376
360
|
const timing = resolveTiming(span, nowMs, drops);
|
|
377
|
-
const runId = traceIdToRunUuid(traceHex);
|
|
361
|
+
const runId = (0, otel_hierarchy_1.traceIdToRunUuid)(traceHex);
|
|
378
362
|
// ── envelope (identity/structure/usage ONLY; NO content) ──
|
|
379
363
|
const operation = classify(map);
|
|
380
364
|
const isAgentAggregate = AGENT_OPERATIONS.has(operation);
|
|
@@ -383,10 +367,14 @@ function normalizeOtlpTraces(data, nowMs = Date.now()) {
|
|
|
383
367
|
? "unknown"
|
|
384
368
|
: (firstAttr(map, PROVIDER_CHAIN) ?? "unknown");
|
|
385
369
|
const usage = isAgentAggregate ? undefined : extractUsage(map);
|
|
370
|
+
const parentHex = toHexId(span.parentSpanId ?? span.parent_span_id, 8);
|
|
371
|
+
const operationClass = (0, otel_hierarchy_1.operationClassOf)(operation);
|
|
386
372
|
const envelope = {
|
|
387
373
|
model,
|
|
388
374
|
provider,
|
|
389
375
|
operation,
|
|
376
|
+
operation_class: operationClass,
|
|
377
|
+
parent_span_id: parentHex,
|
|
390
378
|
...(usage !== undefined ? { usage } : {}),
|
|
391
379
|
scope: { name: scopeName, version: scopeVersion },
|
|
392
380
|
};
|
|
@@ -397,7 +385,7 @@ function normalizeOtlpTraces(data, nowMs = Date.now()) {
|
|
|
397
385
|
const failureClass = statusCode === 2 ? "attested_error" : null;
|
|
398
386
|
const step = {
|
|
399
387
|
run_id: runId,
|
|
400
|
-
step_key: toStepKey(span.name),
|
|
388
|
+
step_key: (0, otel_hierarchy_1.toStepKey)(span.name),
|
|
401
389
|
attempt_no: 1, // assigned per (trace, step_key) below
|
|
402
390
|
task: null,
|
|
403
391
|
provenance: "attested",
|
|
@@ -413,6 +401,13 @@ function normalizeOtlpTraces(data, nowMs = Date.now()) {
|
|
|
413
401
|
const acc = runs.get(runId);
|
|
414
402
|
const endMs = timing.endedAt !== null ? Date.parse(timing.endedAt) : timing.startMs;
|
|
415
403
|
const episodeKey = firstAttr(map, EPISODE_CHAIN) ?? null;
|
|
404
|
+
const conversation = firstAttr(map, ["gen_ai.conversation.id"]) !== undefined;
|
|
405
|
+
const pendingStep = {
|
|
406
|
+
step,
|
|
407
|
+
stepKey: step.step_key,
|
|
408
|
+
startMs: timing.startMs,
|
|
409
|
+
span: { id: spanHex, parent: parentHex, operation, operationClass },
|
|
410
|
+
};
|
|
416
411
|
if (acc === undefined) {
|
|
417
412
|
runs.set(runId, {
|
|
418
413
|
runId,
|
|
@@ -423,7 +418,8 @@ function normalizeOtlpTraces(data, nowMs = Date.now()) {
|
|
|
423
418
|
minStartMs: timing.startMs,
|
|
424
419
|
maxEndMs: endMs,
|
|
425
420
|
anyError: failureClass !== null,
|
|
426
|
-
|
|
421
|
+
conversation,
|
|
422
|
+
pending: [pendingStep],
|
|
427
423
|
});
|
|
428
424
|
}
|
|
429
425
|
else {
|
|
@@ -432,13 +428,17 @@ function normalizeOtlpTraces(data, nowMs = Date.now()) {
|
|
|
432
428
|
acc.anyError = acc.anyError || failureClass !== null;
|
|
433
429
|
if (acc.episodeKey === null && episodeKey !== null)
|
|
434
430
|
acc.episodeKey = episodeKey;
|
|
435
|
-
acc.
|
|
431
|
+
acc.conversation = acc.conversation || conversation;
|
|
432
|
+
acc.pending.push(pendingStep);
|
|
436
433
|
}
|
|
437
434
|
}
|
|
438
435
|
}
|
|
439
436
|
}
|
|
440
437
|
const runRows = [];
|
|
441
438
|
for (const acc of runs.values()) {
|
|
439
|
+
// ARC-6: a conversation trace (gen_ai.conversation.id + invoke_agent spans)
|
|
440
|
+
// numbers its generations into ARC-α turn steps and marks the run.
|
|
441
|
+
const turned = acc.conversation ? (0, otel_hierarchy_1.numberTurns)(acc.pending) : { pending: acc.pending, turns: 0 };
|
|
442
442
|
runRows.push({
|
|
443
443
|
id: acc.runId,
|
|
444
444
|
pipeline: acc.pipeline,
|
|
@@ -449,25 +449,10 @@ function normalizeOtlpTraces(data, nowMs = Date.now()) {
|
|
|
449
449
|
ended_at: new Date(acc.maxEndMs).toISOString(),
|
|
450
450
|
// The trace has completed (we only see finished spans); status is honest.
|
|
451
451
|
status: acc.anyError ? "failed" : "succeeded",
|
|
452
|
+
...(turned.turns > 0 ? { meta: { v: 1, trigger: "conversation" } } : {}),
|
|
452
453
|
});
|
|
453
|
-
//
|
|
454
|
-
|
|
455
|
-
// trace. v0 limitation: a trace split across batches is not deduped (no
|
|
456
|
-
// seen-store); one trace = one batch is the common, correct case.
|
|
457
|
-
const perKey = new Map();
|
|
458
|
-
for (const p of acc.pending) {
|
|
459
|
-
const list = perKey.get(p.stepKey) ?? [];
|
|
460
|
-
list.push({ step: p.step, startMs: p.startMs });
|
|
461
|
-
perKey.set(p.stepKey, list);
|
|
462
|
-
}
|
|
463
|
-
for (const list of perKey.values()) {
|
|
464
|
-
list.sort((a, b) => a.startMs !== b.startMs
|
|
465
|
-
? a.startMs - b.startMs
|
|
466
|
-
: (a.step.span_id ?? "") < (b.step.span_id ?? "")
|
|
467
|
-
? -1
|
|
468
|
-
: 1);
|
|
469
|
-
list.forEach(({ step }, i) => steps.push({ ...step, attempt_no: i + 1 }));
|
|
470
|
-
}
|
|
454
|
+
// attempt_no per (run, step_key) — the hierarchy module (otel-hierarchy.ts).
|
|
455
|
+
steps.push(...(0, otel_hierarchy_1.assignAttempts)(turned.pending));
|
|
471
456
|
}
|
|
472
457
|
return { runs: runRows, steps, drops, spanCount, scopeNames: [...scopeNames] };
|
|
473
458
|
}
|
|
@@ -57,7 +57,10 @@ export declare class SinkSendError extends Error {
|
|
|
57
57
|
* poison-skip candidate). Derived once, from the status, so the classification
|
|
58
58
|
* lives in exactly one place. */
|
|
59
59
|
readonly retriable: boolean;
|
|
60
|
-
|
|
60
|
+
/** COV-1c: the server's 422 `reason_code` — a PostgREST/PG error CODE
|
|
61
|
+
* (`PGRST102`, `23514`) and nothing else, shape-checked; null when absent. */
|
|
62
|
+
readonly reasonCode: string | null;
|
|
63
|
+
constructor(message: string, status: number | undefined, reasonCode?: string | null);
|
|
61
64
|
}
|
|
62
65
|
export interface DirectSinkConfig {
|
|
63
66
|
readonly supabaseUrl: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sinks.d.ts","sourceRoot":"","sources":["../../src/telemetry/sinks.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAIH,OAAO,KAAK,EAAE,WAAW,EAAE,aAAa,
|
|
1
|
+
{"version":3,"file":"sinks.d.ts","sourceRoot":"","sources":["../../src/telemetry/sinks.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAIH,OAAO,KAAK,EAAE,WAAW,EAAE,aAAa,EAAc,OAAO,EAAE,MAAM,mBAAmB,CAAC;AACzF,OAAO,KAAK,EAAE,eAAe,EAAE,kBAAkB,EAAE,MAAM,WAAW,CAAC;AAmBrE;6CAC6C;AAC7C,wBAAgB,6BAA6B,IAAI,IAAI,CAEpD;AAED,MAAM,WAAW,aAAa;IAC5B;;yDAEqD;IACrD,QAAQ,CAAC,IAAI,EAAE,QAAQ,GAAG,KAAK,CAAC;IAChC;;6EAEyE;IACzE,IAAI,CAAC,KAAK,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACzC;AAED;;;;;;;;;;;;;;;GAeG;AACH,qBAAa,aAAc,SAAQ,KAAK;IACtC,wEAAwE;IACxE,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB;;qCAEiC;IACjC,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;IAC5B;kFAC8E;IAC9E,QAAQ,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;gBAEvB,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,SAAS,EAAE,UAAU,GAAE,MAAM,GAAG,IAAW;CAO1F;AAcD,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,oEAAoE;IACpE,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,yEAAyE;IACzE,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,KAAK,CAAC;CACjC;AAED,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,gBAAgB,GAAG,aAAa,CAkDxE;AAMD,MAAM,WAAW,aAAa;IAC5B,mEAAmE;IACnE,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B;4EACwE;IACxE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,KAAK,CAAC;IAChC;;;wDAGoD;IACpD,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC,OAAO,EAAE,aAAa,KAAK,IAAI,CAAC;IACtD;;oDAEgD;IAChD,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;CAC7B;AAKD,wBAAgB,aAAa,CAAC,MAAM,EAAE,aAAa,GAAG,aAAa,CA4ElE;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAChC,YAAY,EAAE,eAAe,GAAG,kBAAkB,EAClD,QAAQ,EAAE,MAAM,EAChB,IAAI,GAAE;IAAE,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAO,GACzC,aAAa,CA2Bf"}
|
package/dist/telemetry/sinks.js
CHANGED
|
@@ -66,55 +66,72 @@ function __resetApiSinkWarningsForTest() {
|
|
|
66
66
|
* gate), so the body a failed write returns is logged nowhere.
|
|
67
67
|
*/
|
|
68
68
|
class SinkSendError extends Error {
|
|
69
|
-
constructor(message, status) {
|
|
69
|
+
constructor(message, status, reasonCode = null) {
|
|
70
70
|
super(message);
|
|
71
71
|
this.name = "SinkSendError";
|
|
72
72
|
if (status !== undefined)
|
|
73
73
|
this.status = status;
|
|
74
74
|
this.retriable = status === undefined || status >= 500 || status === 429;
|
|
75
|
+
this.reasonCode = reasonCode;
|
|
75
76
|
}
|
|
76
77
|
}
|
|
77
78
|
exports.SinkSendError = SinkSendError;
|
|
79
|
+
/** The 422 body's `reason_code`, iff it is code-shaped — never any other body text. */
|
|
80
|
+
async function reasonCodeOf(response) {
|
|
81
|
+
if (response.status !== 422)
|
|
82
|
+
return null;
|
|
83
|
+
const body = (await response.json().catch(() => null));
|
|
84
|
+
const code = body?.reason_code;
|
|
85
|
+
return typeof code === "string" && /^(PGRST\d{3}|[0-9A-Z]{5})$/.test(code) ? code : null;
|
|
86
|
+
}
|
|
78
87
|
function createDirectSink(config) {
|
|
79
88
|
const base = config.supabaseUrl.replace(/\/$/, "");
|
|
80
89
|
const fetchFn = config.fetchFn ?? fetch;
|
|
81
90
|
return {
|
|
82
91
|
mode: "direct",
|
|
83
92
|
async send(batch) {
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
`${base}/rest/v1/${write.table}?on_conflict=${encodeURIComponent(write.onConflict)}`, {
|
|
91
|
-
method: "POST",
|
|
92
|
-
headers: {
|
|
93
|
-
"content-type": "application/json",
|
|
94
|
-
apikey: config.serviceRoleKey,
|
|
95
|
-
authorization: `Bearer ${config.serviceRoleKey}`,
|
|
96
|
-
prefer: write.merge
|
|
97
|
-
? "resolution=merge-duplicates,return=minimal"
|
|
98
|
-
: "resolution=ignore-duplicates,return=minimal",
|
|
99
|
-
},
|
|
100
|
-
body: JSON.stringify(write.rows),
|
|
101
|
-
});
|
|
102
|
-
}
|
|
103
|
-
catch {
|
|
104
|
-
// Network-level throw — no status, so TRANSIENT (the exporter retries
|
|
105
|
-
// forever). The raw error can carry the control-plane host, so it is
|
|
106
|
-
// NOT echoed (C2); the table + "network error" is the whole message.
|
|
107
|
-
throw new SinkSendError(`sink POST ${write.table} → network error`, undefined);
|
|
108
|
-
}
|
|
109
|
-
if (!response.ok) {
|
|
110
|
-
// Status + table only — never the PostgREST body (it carries the
|
|
111
|
-
// control-plane host and, on some failures, header fragments; C2).
|
|
112
|
-
// The status is what the exporter classifies on (spec §2.3.1).
|
|
113
|
-
throw new SinkSendError(`sink POST ${write.table} → ${response.status}`, response.status);
|
|
114
|
-
}
|
|
115
|
-
}
|
|
93
|
+
await sendWrites((0, ingest_protocol_1.stampBatch)(batch, config.tenancy));
|
|
94
|
+
// GRADE-RAW S2-1: step-task registration is best-effort and AFTER the
|
|
95
|
+
// batch landed — a refused registration never fails (or re-sends) it.
|
|
96
|
+
const registration = (0, ingest_protocol_1.stepTaskRegistration)(batch, config.tenancy);
|
|
97
|
+
if (registration !== null)
|
|
98
|
+
await sendWrites([registration]).catch(() => undefined);
|
|
116
99
|
},
|
|
117
100
|
};
|
|
101
|
+
async function sendWrites(writes) {
|
|
102
|
+
for (const write of writes) {
|
|
103
|
+
let response;
|
|
104
|
+
try {
|
|
105
|
+
response = await fetchFn(
|
|
106
|
+
// S5: the upsert names a real unique constraint. A single-argument
|
|
107
|
+
// upsert targets the primary key and 23505s later.
|
|
108
|
+
`${base}/rest/v1/${write.table}?on_conflict=${encodeURIComponent(write.onConflict)}`, {
|
|
109
|
+
method: "POST",
|
|
110
|
+
headers: {
|
|
111
|
+
"content-type": "application/json",
|
|
112
|
+
apikey: config.serviceRoleKey,
|
|
113
|
+
authorization: `Bearer ${config.serviceRoleKey}`,
|
|
114
|
+
prefer: write.merge
|
|
115
|
+
? "resolution=merge-duplicates,return=minimal"
|
|
116
|
+
: "resolution=ignore-duplicates,return=minimal",
|
|
117
|
+
},
|
|
118
|
+
body: JSON.stringify(write.rows),
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
catch {
|
|
122
|
+
// Network-level throw — no status, so TRANSIENT (the exporter retries
|
|
123
|
+
// forever). The raw error can carry the control-plane host, so it is
|
|
124
|
+
// NOT echoed (C2); the table + "network error" is the whole message.
|
|
125
|
+
throw new SinkSendError(`sink POST ${write.table} → network error`, undefined);
|
|
126
|
+
}
|
|
127
|
+
if (!response.ok) {
|
|
128
|
+
// Status + table only — never the PostgREST body (it carries the
|
|
129
|
+
// control-plane host and, on some failures, header fragments; C2).
|
|
130
|
+
// The status is what the exporter classifies on (spec §2.3.1).
|
|
131
|
+
throw new SinkSendError(`sink POST ${write.table} → ${response.status}`, response.status);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
118
135
|
}
|
|
119
136
|
/** Below this, gzip costs more than it saves. */
|
|
120
137
|
const GZIP_MIN_BYTES = 1024;
|
|
@@ -166,7 +183,9 @@ function createApiSink(config) {
|
|
|
166
183
|
// someone else's log aggregator. The status is the exporter's transient-
|
|
167
184
|
// vs-poison signal: the server sends 422 for a deterministic control-plane
|
|
168
185
|
// rejection, 503 for a transient one (spec §2.2/§2.3.1).
|
|
169
|
-
|
|
186
|
+
// COV-1c: a 422 may name the PostgREST/PG code (shape-checked, code only).
|
|
187
|
+
const reasonCode = await reasonCodeOf(response);
|
|
188
|
+
throw new SinkSendError(`ingest POST → ${response.status}${reasonCode !== null ? ` (${reasonCode})` : ""}`, response.status, reasonCode);
|
|
170
189
|
}
|
|
171
190
|
// A good send clears the auth-failure latch, so a later key breakage
|
|
172
191
|
// re-alerts instead of staying silent behind a once-per-process guard.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"store-migrations.d.ts","sourceRoot":"","sources":["../../src/telemetry/store-migrations.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,QAAQ,MAAM,gBAAgB,CAAC;AAG3C;sCACsC;AACtC,wBAAgB,kBAAkB,CAAC,EAAE,EAAE,QAAQ,CAAC,QAAQ,GAAG,IAAI,
|
|
1
|
+
{"version":3,"file":"store-migrations.d.ts","sourceRoot":"","sources":["../../src/telemetry/store-migrations.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,QAAQ,MAAM,gBAAgB,CAAC;AAG3C;sCACsC;AACtC,wBAAgB,kBAAkB,CAAC,EAAE,EAAE,QAAQ,CAAC,QAAQ,GAAG,IAAI,CAwM9D"}
|
|
@@ -206,4 +206,12 @@ function runStoreMigrations(db) {
|
|
|
206
206
|
}
|
|
207
207
|
db.pragma("user_version = 16");
|
|
208
208
|
}
|
|
209
|
+
// v17 (ARC-3, mirrors 00089): `grades.kind` gains 'outcome' (table rebuild —
|
|
210
|
+
// SQLite cannot ALTER a CHECK) and the nullable `value` column. The rebuild
|
|
211
|
+
// copies each row's ROWID too, so the exporter's `:grades` rowid watermark is
|
|
212
|
+
// exactly as valid after the rung as before. See migrateGradesToV17.
|
|
213
|
+
if (db.pragma("user_version", { simple: true }) < 17) {
|
|
214
|
+
(0, grades_sql_1.migrateGradesToV17)(db);
|
|
215
|
+
db.pragma("user_version = 17");
|
|
216
|
+
}
|
|
209
217
|
}
|
|
@@ -52,6 +52,15 @@ export interface ExportableTelemetryStore extends TelemetryStore {
|
|
|
52
52
|
* non-null inference_id, or null (E4 chunk A: bake-off rehydration resolves an
|
|
53
53
|
* eval case's source step to the inference that keys its captured input). */
|
|
54
54
|
getStepInferenceId(runId: string, stepKey: string): string | null;
|
|
55
|
+
/** ARC-1 conversation re-entry: the newest `running` run for (pipeline,
|
|
56
|
+
* episode_key) in THIS local store, with its recorded step keys (the caller
|
|
57
|
+
* derives the turn counter from `turn:<n>.<k>` keys), or null. Local only —
|
|
58
|
+
* cross-process re-entry is out of scope (duplicate runs per episode are
|
|
59
|
+
* tolerated, 00031). */
|
|
60
|
+
findOpenRun?(pipeline: string, episodeKey: string): {
|
|
61
|
+
readonly row: RunRow;
|
|
62
|
+
readonly stepKeys: readonly string[];
|
|
63
|
+
} | null;
|
|
55
64
|
listEnvelopes(): EnvelopeRow[];
|
|
56
65
|
/** Local exporter watermark (per source instance), 0 when unset. */
|
|
57
66
|
getExportWatermark(sourceId: string): number;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../../src/telemetry/store.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAKH,OAAO,KAAK,EACV,WAAW,EACX,QAAQ,EACR,MAAM,EACN,OAAO,EACP,YAAY,EACZ,cAAc,EACf,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EAA2B,KAAK,cAAc,EAAuB,MAAM,UAAU,CAAC;AAU7F;;;;;GAKG;AACH,MAAM,WAAW,wBAAyB,SAAQ,cAAc;IAC9D,qEAAqE;IACrE,mBAAmB,CAAC,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,KAAK,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,YAAY,CAAA;KAAE,CAAC,CAAC;IACpG;kEAC8D;IAC9D,WAAW,CAAC,GAAG,EAAE,QAAQ,GAAG,IAAI,CAAC;IACjC;;wDAEoD;IACpD,eAAe,CAAC,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,KAAK,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,QAAQ,CAAA;KAAE,CAAC,CAAC;IAC5F;mEAC+D;IAC/D,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,iFAAiF;IACjF,UAAU,CAAC,GAAG,EAAE,OAAO,GAAG,IAAI,CAAC;IAC/B,kEAAkE;IAClE,aAAa,CAAC,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,KAAK,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACxF,mEAAmE;IACnE,cAAc,CAAC,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,KAAK,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,OAAO,CAAA;KAAE,CAAC,CAAC;IAC1F;;iFAE6E;IAC7E,kBAAkB,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC;IAClE,aAAa,IAAI,WAAW,EAAE,CAAC;IAC/B,oEAAoE;IACpE,kBAAkB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAAC;IAC7C,kBAAkB,CAAC,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9D;;;;;OAKG;IACH,kBAAkB,CAAC,aAAa,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,cAAc,EAAE,CAAC;CAC3E;AAgSD,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,MAAM,GAAG,wBAAwB,
|
|
1
|
+
{"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../../src/telemetry/store.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAKH,OAAO,KAAK,EACV,WAAW,EACX,QAAQ,EACR,MAAM,EACN,OAAO,EACP,YAAY,EACZ,cAAc,EACf,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EAA2B,KAAK,cAAc,EAAuB,MAAM,UAAU,CAAC;AAU7F;;;;;GAKG;AACH,MAAM,WAAW,wBAAyB,SAAQ,cAAc;IAC9D,qEAAqE;IACrE,mBAAmB,CAAC,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,KAAK,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,YAAY,CAAA;KAAE,CAAC,CAAC;IACpG;kEAC8D;IAC9D,WAAW,CAAC,GAAG,EAAE,QAAQ,GAAG,IAAI,CAAC;IACjC;;wDAEoD;IACpD,eAAe,CAAC,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,KAAK,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,QAAQ,CAAA;KAAE,CAAC,CAAC;IAC5F;mEAC+D;IAC/D,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,iFAAiF;IACjF,UAAU,CAAC,GAAG,EAAE,OAAO,GAAG,IAAI,CAAC;IAC/B,kEAAkE;IAClE,aAAa,CAAC,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,KAAK,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACxF,mEAAmE;IACnE,cAAc,CAAC,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,KAAK,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,OAAO,CAAA;KAAE,CAAC,CAAC;IAC1F;;iFAE6E;IAC7E,kBAAkB,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC;IAClE;;;;4BAIwB;IACxB,WAAW,CAAC,CAAC,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG;QAAE,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,QAAQ,EAAE,SAAS,MAAM,EAAE,CAAA;KAAE,GAAG,IAAI,CAAC;IAC1H,aAAa,IAAI,WAAW,EAAE,CAAC;IAC/B,oEAAoE;IACpE,kBAAkB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAAC;IAC7C,kBAAkB,CAAC,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9D;;;;;OAKG;IACH,kBAAkB,CAAC,aAAa,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,cAAc,EAAE,CAAC;CAC3E;AAgSD,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,MAAM,GAAG,wBAAwB,CAyQ3E"}
|
package/dist/telemetry/store.js
CHANGED
|
@@ -403,6 +403,16 @@ function openTelemetryStore(dbPath) {
|
|
|
403
403
|
.all(afterRowid, limit);
|
|
404
404
|
return rows.map((r) => ({ rowid: r.__rowid, row: toStepRow(r) }));
|
|
405
405
|
},
|
|
406
|
+
findOpenRun(pipeline, episodeKey) {
|
|
407
|
+
const r = db
|
|
408
|
+
.prepare("SELECT * FROM runs WHERE pipeline = ? AND episode_key = ? AND status = 'running' " +
|
|
409
|
+
"ORDER BY started_at DESC LIMIT 1")
|
|
410
|
+
.get(pipeline, episodeKey);
|
|
411
|
+
if (r === undefined)
|
|
412
|
+
return null;
|
|
413
|
+
const keys = db.prepare("SELECT DISTINCT step_key FROM steps WHERE run_id = ?").all(r.id);
|
|
414
|
+
return { row: toRunRow(r), stepKeys: keys.map((k) => k.step_key) };
|
|
415
|
+
},
|
|
406
416
|
getStepInferenceId(runId, stepKey) {
|
|
407
417
|
const r = db
|
|
408
418
|
.prepare("SELECT inference_id FROM steps WHERE run_id = ? AND step_key = ? AND inference_id IS NOT NULL " +
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Uniform key sets for `stampBatch`'s bulk writes (COV-1c). Extracted from
|
|
3
|
+
* ingest-protocol.ts so stampBatch stays under the LOC cap (P1).
|
|
4
|
+
*
|
|
5
|
+
* A PostgREST bulk insert requires every row in the array to carry the SAME
|
|
6
|
+
* keys — one row that omits a key the others carry fails the whole POST with
|
|
7
|
+
* PGRST102 ("All object keys must match") before any SQL runs. The wire is
|
|
8
|
+
* spread-iff-present by design (an older SDK's payload stays byte-identical),
|
|
9
|
+
* so rows in one batch legitimately differ: a web-search surcharge row carries
|
|
10
|
+
* `meta: null` (no `meta` key after validation) beside metered rows that carry
|
|
11
|
+
* one. Two remedies, chosen by the write's conflict policy:
|
|
12
|
+
*
|
|
13
|
+
* - merge=false (an immutable fact: inferences, steps, artifacts) — FILL every
|
|
14
|
+
* absent optional column with its DB default (null, or the NOT NULL column's
|
|
15
|
+
* default). Safe because ignore-duplicates never rewrites a landed row.
|
|
16
|
+
* - merge=true (a mutable projection: tasks, runs) — SPLIT into one write per
|
|
17
|
+
* key set (the routes precedent). Filling would null out a column a merge
|
|
18
|
+
* upsert must leave untouched.
|
|
19
|
+
*
|
|
20
|
+
* `undefined` values count as absent: JSON.stringify drops the key, so a row
|
|
21
|
+
* holding `{ meta: undefined }` reaches PostgREST without `meta`.
|
|
22
|
+
*/
|
|
23
|
+
import type { Tenancy, WireInference } from "./ingest-protocol";
|
|
24
|
+
/** Every `inferences` column the wire can carry, plus the stamped tenancy. ONE
|
|
25
|
+
* list — the uniform key set of every inferences write. */
|
|
26
|
+
export declare const INFERENCE_WRITE_COLUMNS: readonly ["org_id", "project_id", "source_id", "id", "ts", "route", "route_version", "provider", "model", "requested_tier", "served_tier", "tokens_in", "tokens_out", "tokens_cached", "cost_estimate_usd", "latency_ms", "ttfb_ms", "failure_class", "attempts", "tags", "fingerprint_cell", "served_variant", "repaired_by", "pricing_missing", "tokens_cache_write", "tokens_cache_write_1h", "cache_key", "prefix_fingerprint", "call_id", "attempt_seq", "candidate_index", "meta", "mask"];
|
|
27
|
+
export type InferenceWriteColumn = (typeof INFERENCE_WRITE_COLUMNS)[number];
|
|
28
|
+
/** A wire inference → its `inferences` row with EXACTLY `INFERENCE_WRITE_COLUMNS`
|
|
29
|
+
* as keys. Tenancy is stamped from the principal and never read from the row
|
|
30
|
+
* (T3) — the list order puts it first, and the wire type cannot carry it. */
|
|
31
|
+
export declare function normalizeInferenceRow(row: WireInference, tenancy: Tenancy): Record<string, unknown>;
|
|
32
|
+
/** merge=false fill: for each of `optional` that ANY row in the write carries,
|
|
33
|
+
* give the rows lacking it an explicit null, so rows that differ only in those
|
|
34
|
+
* spread-iff-present columns share one key set. A write where no row carries
|
|
35
|
+
* the column is returned key-for-key unchanged (byte-identical to pre-COV-1c). */
|
|
36
|
+
export declare function fillNulls(rows: readonly Record<string, unknown>[], optional: readonly string[]): Record<string, unknown>[];
|
|
37
|
+
/** The key set a row reaches PostgREST with (undefined-valued keys dropped, as
|
|
38
|
+
* JSON.stringify drops them), sorted — the PGRST102 comparison. */
|
|
39
|
+
export declare function wireKeySet(row: Record<string, unknown>): string;
|
|
40
|
+
/** merge=true split: group rows by key set, first-seen order, so each bulk POST
|
|
41
|
+
* is uniform without writing a null a merge upsert would apply. */
|
|
42
|
+
export declare function splitByKeySet(rows: readonly Record<string, unknown>[]): Record<string, unknown>[][];
|
|
43
|
+
//# sourceMappingURL=uniform-rows.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"uniform-rows.d.ts","sourceRoot":"","sources":["../../src/telemetry/uniform-rows.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAEhE;2DAC2D;AAC3D,eAAO,MAAM,uBAAuB,keAkC1B,CAAC;AAEX,MAAM,MAAM,oBAAoB,GAAG,CAAC,OAAO,uBAAuB,CAAC,CAAC,MAAM,CAAC,CAAC;AAwB5E;;6EAE6E;AAC7E,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,aAAa,EAAE,OAAO,EAAE,OAAO,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAanG;AAED;;;kFAGkF;AAClF,wBAAgB,SAAS,CACvB,IAAI,EAAE,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,EACxC,QAAQ,EAAE,SAAS,MAAM,EAAE,GAC1B,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAO3B;AAED;mEACmE;AACnE,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAK/D;AAED;mEACmE;AACnE,wBAAgB,aAAa,CAAC,IAAI,EAAE,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,EAAE,CASnG"}
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Uniform key sets for `stampBatch`'s bulk writes (COV-1c). Extracted from
|
|
4
|
+
* ingest-protocol.ts so stampBatch stays under the LOC cap (P1).
|
|
5
|
+
*
|
|
6
|
+
* A PostgREST bulk insert requires every row in the array to carry the SAME
|
|
7
|
+
* keys — one row that omits a key the others carry fails the whole POST with
|
|
8
|
+
* PGRST102 ("All object keys must match") before any SQL runs. The wire is
|
|
9
|
+
* spread-iff-present by design (an older SDK's payload stays byte-identical),
|
|
10
|
+
* so rows in one batch legitimately differ: a web-search surcharge row carries
|
|
11
|
+
* `meta: null` (no `meta` key after validation) beside metered rows that carry
|
|
12
|
+
* one. Two remedies, chosen by the write's conflict policy:
|
|
13
|
+
*
|
|
14
|
+
* - merge=false (an immutable fact: inferences, steps, artifacts) — FILL every
|
|
15
|
+
* absent optional column with its DB default (null, or the NOT NULL column's
|
|
16
|
+
* default). Safe because ignore-duplicates never rewrites a landed row.
|
|
17
|
+
* - merge=true (a mutable projection: tasks, runs) — SPLIT into one write per
|
|
18
|
+
* key set (the routes precedent). Filling would null out a column a merge
|
|
19
|
+
* upsert must leave untouched.
|
|
20
|
+
*
|
|
21
|
+
* `undefined` values count as absent: JSON.stringify drops the key, so a row
|
|
22
|
+
* holding `{ meta: undefined }` reaches PostgREST without `meta`.
|
|
23
|
+
*/
|
|
24
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
25
|
+
exports.INFERENCE_WRITE_COLUMNS = void 0;
|
|
26
|
+
exports.normalizeInferenceRow = normalizeInferenceRow;
|
|
27
|
+
exports.fillNulls = fillNulls;
|
|
28
|
+
exports.wireKeySet = wireKeySet;
|
|
29
|
+
exports.splitByKeySet = splitByKeySet;
|
|
30
|
+
/** Every `inferences` column the wire can carry, plus the stamped tenancy. ONE
|
|
31
|
+
* list — the uniform key set of every inferences write. */
|
|
32
|
+
exports.INFERENCE_WRITE_COLUMNS = [
|
|
33
|
+
"org_id",
|
|
34
|
+
"project_id",
|
|
35
|
+
"source_id",
|
|
36
|
+
"id",
|
|
37
|
+
"ts",
|
|
38
|
+
"route",
|
|
39
|
+
"route_version",
|
|
40
|
+
"provider",
|
|
41
|
+
"model",
|
|
42
|
+
"requested_tier",
|
|
43
|
+
"served_tier",
|
|
44
|
+
"tokens_in",
|
|
45
|
+
"tokens_out",
|
|
46
|
+
"tokens_cached",
|
|
47
|
+
"cost_estimate_usd",
|
|
48
|
+
"latency_ms",
|
|
49
|
+
"ttfb_ms",
|
|
50
|
+
"failure_class",
|
|
51
|
+
"attempts",
|
|
52
|
+
"tags",
|
|
53
|
+
"fingerprint_cell",
|
|
54
|
+
"served_variant",
|
|
55
|
+
"repaired_by",
|
|
56
|
+
"pricing_missing",
|
|
57
|
+
"tokens_cache_write",
|
|
58
|
+
"tokens_cache_write_1h",
|
|
59
|
+
"cache_key",
|
|
60
|
+
"prefix_fingerprint",
|
|
61
|
+
"call_id",
|
|
62
|
+
"attempt_seq",
|
|
63
|
+
"candidate_index",
|
|
64
|
+
"meta",
|
|
65
|
+
"mask",
|
|
66
|
+
];
|
|
67
|
+
// Compile-time drift lock: a WireInference field missing from the list above
|
|
68
|
+
// fails tsc here, rather than being silently dropped from the write.
|
|
69
|
+
const _everyWireFieldListed = true;
|
|
70
|
+
void _everyWireFieldListed;
|
|
71
|
+
/** The value written for an ABSENT column: the NOT NULL columns' DB defaults
|
|
72
|
+
* (00001 / 00005 / 00049), null for every nullable one (00002/00003/00007/
|
|
73
|
+
* 00049/00054/00056/00062 add them nullable with no default). Writing the
|
|
74
|
+
* default explicitly is what the omitted key produced before. */
|
|
75
|
+
const ABSENT_DEFAULTS = {
|
|
76
|
+
tokens_in: 0,
|
|
77
|
+
tokens_out: 0,
|
|
78
|
+
tokens_cached: 0,
|
|
79
|
+
cost_estimate_usd: 0,
|
|
80
|
+
latency_ms: 0,
|
|
81
|
+
attempts: {},
|
|
82
|
+
tags: {},
|
|
83
|
+
pricing_missing: false,
|
|
84
|
+
tokens_cache_write: 0,
|
|
85
|
+
tokens_cache_write_1h: 0,
|
|
86
|
+
};
|
|
87
|
+
/** A wire inference → its `inferences` row with EXACTLY `INFERENCE_WRITE_COLUMNS`
|
|
88
|
+
* as keys. Tenancy is stamped from the principal and never read from the row
|
|
89
|
+
* (T3) — the list order puts it first, and the wire type cannot carry it. */
|
|
90
|
+
function normalizeInferenceRow(row, tenancy) {
|
|
91
|
+
const source = {
|
|
92
|
+
...row,
|
|
93
|
+
org_id: tenancy.org_id,
|
|
94
|
+
project_id: tenancy.project_id,
|
|
95
|
+
source_id: tenancy.source_id,
|
|
96
|
+
};
|
|
97
|
+
const out = {};
|
|
98
|
+
for (const col of exports.INFERENCE_WRITE_COLUMNS) {
|
|
99
|
+
const v = source[col];
|
|
100
|
+
out[col] = v !== undefined ? v : (ABSENT_DEFAULTS[col] ?? null);
|
|
101
|
+
}
|
|
102
|
+
return out;
|
|
103
|
+
}
|
|
104
|
+
/** merge=false fill: for each of `optional` that ANY row in the write carries,
|
|
105
|
+
* give the rows lacking it an explicit null, so rows that differ only in those
|
|
106
|
+
* spread-iff-present columns share one key set. A write where no row carries
|
|
107
|
+
* the column is returned key-for-key unchanged (byte-identical to pre-COV-1c). */
|
|
108
|
+
function fillNulls(rows, optional) {
|
|
109
|
+
const carried = optional.filter((k) => rows.some((r) => r[k] !== undefined));
|
|
110
|
+
return rows.map((r) => {
|
|
111
|
+
const out = { ...r };
|
|
112
|
+
for (const k of carried)
|
|
113
|
+
if (out[k] === undefined)
|
|
114
|
+
out[k] = null;
|
|
115
|
+
return out;
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
/** The key set a row reaches PostgREST with (undefined-valued keys dropped, as
|
|
119
|
+
* JSON.stringify drops them), sorted — the PGRST102 comparison. */
|
|
120
|
+
function wireKeySet(row) {
|
|
121
|
+
return Object.keys(row)
|
|
122
|
+
.filter((k) => row[k] !== undefined)
|
|
123
|
+
.sort()
|
|
124
|
+
.join(",");
|
|
125
|
+
}
|
|
126
|
+
/** merge=true split: group rows by key set, first-seen order, so each bulk POST
|
|
127
|
+
* is uniform without writing a null a merge upsert would apply. */
|
|
128
|
+
function splitByKeySet(rows) {
|
|
129
|
+
const groups = new Map();
|
|
130
|
+
for (const r of rows) {
|
|
131
|
+
const key = wireKeySet(r);
|
|
132
|
+
const g = groups.get(key);
|
|
133
|
+
if (g === undefined)
|
|
134
|
+
groups.set(key, [r]);
|
|
135
|
+
else
|
|
136
|
+
g.push(r);
|
|
137
|
+
}
|
|
138
|
+
return [...groups.values()];
|
|
139
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"failures-teaching.d.ts","sourceRoot":"","sources":["../../src/types/failures-teaching.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAE3D,2EAA2E;AAC3E,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,EAAE,EAAE,KAAK,GAAG,SAAS,GAAG,QAAQ,CAAC;IAC1C,+EAA+E;IAC/E,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,CAAC;IACzB,8DAA8D;IAC9D,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,KAAK,EAAE,YAAY,CAAC;IAC7B,kEAAkE;IAClE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB;4EACwE;IACxE,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,iEAAiE;IACjE,QAAQ,CAAC,eAAe,CAAC,EAAE,CAAC,OAAO,EAAE,UAAU,KAAK,eAAe,GAAG,IAAI,CAAC;CAC5E;AAID,eAAO,MAAM,gBAAgB,EAAE,QAAQ,CAAC,MAAM,CAAC,YAAY,EAAE,eAAe,CAAC,
|
|
1
|
+
{"version":3,"file":"failures-teaching.d.ts","sourceRoot":"","sources":["../../src/types/failures-teaching.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAE3D,2EAA2E;AAC3E,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,EAAE,EAAE,KAAK,GAAG,SAAS,GAAG,QAAQ,CAAC;IAC1C,+EAA+E;IAC/E,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,CAAC;IACzB,8DAA8D;IAC9D,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,KAAK,EAAE,YAAY,CAAC;IAC7B,kEAAkE;IAClE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB;4EACwE;IACxE,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,iEAAiE;IACjE,QAAQ,CAAC,eAAe,CAAC,EAAE,CAAC,OAAO,EAAE,UAAU,KAAK,eAAe,GAAG,IAAI,CAAC;CAC5E;AAID,eAAO,MAAM,gBAAgB,EAAE,QAAQ,CAAC,MAAM,CAAC,YAAY,EAAE,eAAe,CAAC,CAqL5E,CAAC;AAiBF;6EAC6E;AAC7E,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,KAAK,EAAE,YAAY,CAAC;IAC7B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,eAAe,EAAE,eAAe,GAAG,IAAI,CAAC;CAClD;AAED,wBAAgB,KAAK,CAAC,OAAO,EAAE,UAAU,GAAG,gBAAgB,CAU3D;AAED;gEACgE;AAChE,wBAAgB,wBAAwB,CAAC,OAAO,EAAE,UAAU,GAAG,MAAM,CAapE"}
|
|
@@ -157,6 +157,17 @@ exports.FAILURE_TEACHING = {
|
|
|
157
157
|
"falling through, at the cost of one extra call.",
|
|
158
158
|
}),
|
|
159
159
|
},
|
|
160
|
+
limit_exceeded: {
|
|
161
|
+
class: "limit_exceeded",
|
|
162
|
+
what: "The run reached one of the limits you set on it (maxUsd, maxCalls, maxWallMs or " +
|
|
163
|
+
"maxRepeatToolCall), so ModelRig refused the next call in-process and ended the run as failed " +
|
|
164
|
+
"with failure_reason `limit:<which>` (e.g. `limit:max_usd`). Nothing was sent to a provider for the refused call.",
|
|
165
|
+
fixHint: "Look at the run before raising the limit: a stuck tool loop (the same tool with the same " +
|
|
166
|
+
"arguments again and again) or a runaway agent looks exactly like an under-sized limit. If the " +
|
|
167
|
+
"run was legitimately bigger, raise that one limit on run.start({ limits }) or " +
|
|
168
|
+
"rig.conversation(id, { limits }).",
|
|
169
|
+
docsUrl: `${DOCS_BASE}#limit_exceeded`,
|
|
170
|
+
},
|
|
160
171
|
};
|
|
161
172
|
/** Provider → the env var that keys it. Mirrors config.ts's PROVIDER_KEY_ENV;
|
|
162
173
|
* kept as a local lookup so this table has no import cycle with config. */
|
package/dist/types/failures.d.ts
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* errors never cross the adapter boundary. Retry budgets are per-class and
|
|
5
5
|
* non-fungible (see run/retry.ts).
|
|
6
6
|
*/
|
|
7
|
-
export type FailureClass = "content_invalid" | "capacity_shed" | "network" | "refusal" | "cache_invalid" | "timeout" | "config_auth" | "budget_exhausted" | "invariant_violation" | "quality_rejected";
|
|
7
|
+
export type FailureClass = "content_invalid" | "capacity_shed" | "network" | "refusal" | "cache_invalid" | "timeout" | "config_auth" | "budget_exhausted" | "invariant_violation" | "quality_rejected" | "limit_exceeded";
|
|
8
8
|
export interface RigFailure {
|
|
9
9
|
readonly class: FailureClass;
|
|
10
10
|
readonly message: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"failures.d.ts","sourceRoot":"","sources":["../../src/types/failures.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,MAAM,MAAM,YAAY,GACpB,iBAAiB,GACjB,eAAe,GACf,SAAS,GACT,SAAS,GACT,eAAe,GACf,SAAS,GACT,aAAa,GACb,kBAAkB,GAClB,qBAAqB,GACrB,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"failures.d.ts","sourceRoot":"","sources":["../../src/types/failures.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,MAAM,MAAM,YAAY,GACpB,iBAAiB,GACjB,eAAe,GACf,SAAS,GACT,SAAS,GACT,eAAe,GACf,SAAS,GACT,aAAa,GACb,kBAAkB,GAClB,qBAAqB,GACrB,kBAAkB,GAClB,gBAAgB,CAAC;AAErB,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,KAAK,EAAE,YAAY,CAAC;IAC7B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,iEAAiE;IACjE,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,yEAAyE;IACzE,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,CAAC;IACzB,+EAA+E;IAC/E,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED,eAAO,MAAM,gBAAgB,EAAE,WAAW,CAAC,YAAY,CAIrD,CAAC;AAgBH;kFACkF;AAClF,qBAAa,eAAgB,SAAQ,KAAK;IACxC,QAAQ,CAAC,OAAO,EAAE,UAAU,CAAC;gBAEjB,OAAO,EAAE,UAAU;CAKhC;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,UAAU,CAIhE"}
|
package/dist/types/failures.js
CHANGED
|
@@ -11,6 +11,7 @@ exports.isRigFailure = isRigFailure;
|
|
|
11
11
|
exports.TERMINAL_CLASSES = new Set([
|
|
12
12
|
"budget_exhausted",
|
|
13
13
|
"invariant_violation",
|
|
14
|
+
"limit_exceeded",
|
|
14
15
|
]);
|
|
15
16
|
const FAILURE_CLASSES = new Set([
|
|
16
17
|
"content_invalid",
|
|
@@ -23,6 +24,7 @@ const FAILURE_CLASSES = new Set([
|
|
|
23
24
|
"budget_exhausted",
|
|
24
25
|
"invariant_violation",
|
|
25
26
|
"quality_rejected",
|
|
27
|
+
"limit_exceeded",
|
|
26
28
|
]);
|
|
27
29
|
/** Error subclass for the rare paths that must throw (config errors, terminal
|
|
28
30
|
* failures surfacing to the caller). The failure travels intact on `.failure`. */
|
package/dist/types/provider.d.ts
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
*/
|
|
6
6
|
import type { RigFailure } from "./failures";
|
|
7
7
|
import type { AttemptMetaV1 } from "../telemetry/attempt-meta";
|
|
8
|
+
import type { HistoryMessage } from "../run/messages";
|
|
8
9
|
export type ProviderId = "gemini" | "openai" | "deepseek" | "anthropic" | "grok" | "deepinfra" | "fireworks" | "zai";
|
|
9
10
|
/** Runtime enumeration of every ProviderId — the single list a raw-model lane
|
|
10
11
|
* (`provider/model`) validates against (http gateway spec §3.3). Kept in sync
|
|
@@ -171,6 +172,17 @@ export interface AdapterRequest {
|
|
|
171
172
|
readonly tools?: readonly ToolDefinition[];
|
|
172
173
|
/** Tool-selection directive; only meaningful alongside `tools`. */
|
|
173
174
|
readonly toolChoice?: ToolChoice;
|
|
175
|
+
/**
|
|
176
|
+
* ARC-β messages lane (agent-archetypes spec §4) — the conversation history,
|
|
177
|
+
* validated by `run/messages.ts`. Identical semantics in every adapter:
|
|
178
|
+
* - ABSENT ⇒ today's single-turn code path, BYTE-IDENTICAL request (pinned by
|
|
179
|
+
* adapters/one-turn-parity.test.ts).
|
|
180
|
+
* - PRESENT ⇒ provider messages = system(`systemPrompt`) + map(history), and a
|
|
181
|
+
* NON-EMPTY `userPrompt` is appended as a final user message (a route
|
|
182
|
+
* template's final instruction); an empty `userPrompt` appends nothing.
|
|
183
|
+
* Per-family mapping lives in adapters/history-mapping.ts.
|
|
184
|
+
*/
|
|
185
|
+
readonly history?: readonly HistoryMessage[];
|
|
174
186
|
}
|
|
175
187
|
export interface AdapterUsage {
|
|
176
188
|
/**
|