modelrig 0.11.1 → 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 +34 -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/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 +106 -6
- package/dist/init/observe.d.ts.map +1 -1
- package/dist/init/observe.js +3 -3
- 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/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 +16 -3
- package/docs/how-it-fits.md +3 -3
- package/docs/instrumentation.md +15 -6
- package/docs/llms.txt +55 -7
- package/docs/mcp-oracle.md +15 -4
- package/docs/quickstart.md +6 -5
- 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
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OTLP-in — the trace HIERARCHY half of the normalizer, extracted from
|
|
3
|
+
* otel-normalize.ts (ARC-γ: the normalizer sat at 662 LOC, near the 700 cap).
|
|
4
|
+
* PURE: span identity → run/step identity, and step ordering within a run.
|
|
5
|
+
* No attribute is read here and no content ever reaches this module.
|
|
6
|
+
*/
|
|
7
|
+
import type { WireStep } from "./ingest-protocol";
|
|
8
|
+
/** ARC-6: the classified operation kept in the attested envelope — the five
|
|
9
|
+
* classes OTLP-in v1 uses (turn numbering + tool counts), or `span` for
|
|
10
|
+
* anything else (honest: never guessed into a class). */
|
|
11
|
+
export type OperationClass = "generation" | "tool" | "agent" | "retriever" | "embedding" | "span";
|
|
12
|
+
/** Map the normalizer's raw operation string to its class. */
|
|
13
|
+
export declare function operationClassOf(operation: string): OperationClass;
|
|
14
|
+
/** span name → step_key: trimmed, whitespace-collapsed, length-capped, never
|
|
15
|
+
* empty (the step PK forbids it). */
|
|
16
|
+
export declare function toStepKey(name: unknown): string;
|
|
17
|
+
/** 32-hex trace id → canonical UUID string (runs.id is uuid; 16 bytes ≡ a UUID).
|
|
18
|
+
* A trace id that is not 16 bytes gets a deterministic UUID derived from it. */
|
|
19
|
+
export declare function traceIdToRunUuid(traceHex: string): string;
|
|
20
|
+
/** The structural identity of the span a pending step came from (ARC-6). */
|
|
21
|
+
export interface SpanShape {
|
|
22
|
+
readonly id: string;
|
|
23
|
+
readonly parent: string | null;
|
|
24
|
+
/** The normalizer's raw operation (`invoke_agent`, `chat`, …). */
|
|
25
|
+
readonly operation: string;
|
|
26
|
+
readonly operationClass: OperationClass;
|
|
27
|
+
}
|
|
28
|
+
/** One normalized span awaiting its attempt number. */
|
|
29
|
+
export interface PendingStep {
|
|
30
|
+
readonly step: WireStep;
|
|
31
|
+
readonly stepKey: string;
|
|
32
|
+
readonly startMs: number;
|
|
33
|
+
readonly span?: SpanShape;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Assign attempt_no per (run, step_key): repeated same-named spans become
|
|
37
|
+
* attempts in start-time (then span-id) order — deterministic within a trace.
|
|
38
|
+
* v0 limitation: a trace split across batches is not deduped (no seen-store);
|
|
39
|
+
* one trace = one batch is the common, correct case.
|
|
40
|
+
*/
|
|
41
|
+
export declare function assignAttempts(pending: readonly PendingStep[]): WireStep[];
|
|
42
|
+
/**
|
|
43
|
+
* ARC-6 turn numbering for ONE trace that is a conversation (it carries
|
|
44
|
+
* `gen_ai.conversation.id` and has `invoke_agent` spans). Each TOP-LEVEL
|
|
45
|
+
* `invoke_agent` span (no `invoke_agent` ancestor), in start order, is turn n;
|
|
46
|
+
* every generation span under it (any depth) becomes step `turn:<n>.<k>` in
|
|
47
|
+
* start order — ARC-α's step-key convention exactly (`turnStepKey`; a repeated
|
|
48
|
+
* key would read as a retry). The turn number and, on the agent span, the
|
|
49
|
+
* count of tool spans in that turn ride the attested envelope (`turn`,
|
|
50
|
+
* `turn_tool_calls`, `turn_generations`). Spans outside every turn are left
|
|
51
|
+
* as they were. Returns the rewritten list and the number of turns.
|
|
52
|
+
*/
|
|
53
|
+
export declare function numberTurns(pending: readonly PendingStep[]): {
|
|
54
|
+
pending: PendingStep[];
|
|
55
|
+
turns: number;
|
|
56
|
+
};
|
|
57
|
+
//# sourceMappingURL=otel-hierarchy.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"otel-hierarchy.d.ts","sourceRoot":"","sources":["../../src/telemetry/otel-hierarchy.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAElD;;yDAEyD;AACzD,MAAM,MAAM,cAAc,GAAG,YAAY,GAAG,MAAM,GAAG,OAAO,GAAG,WAAW,GAAG,WAAW,GAAG,MAAM,CAAC;AAuBlG,8DAA8D;AAC9D,wBAAgB,gBAAgB,CAAC,SAAS,EAAE,MAAM,GAAG,cAAc,CAElE;AAID;qCACqC;AACrC,wBAAgB,SAAS,CAAC,IAAI,EAAE,OAAO,GAAG,MAAM,CAI/C;AAED;gFACgF;AAChF,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAGzD;AAED,4EAA4E;AAC5E,MAAM,WAAW,SAAS;IACxB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,kEAAkE;IAClE,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,cAAc,EAAE,cAAc,CAAC;CACzC;AAED,uDAAuD;AACvD,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC;IACxB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,IAAI,CAAC,EAAE,SAAS,CAAC;CAC3B;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,SAAS,WAAW,EAAE,GAAG,QAAQ,EAAE,CAmB1E;AAUD;;;;;;;;;;GAUG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,SAAS,WAAW,EAAE,GAAG;IAAE,OAAO,EAAE,WAAW,EAAE,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAgDtG"}
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* OTLP-in — the trace HIERARCHY half of the normalizer, extracted from
|
|
4
|
+
* otel-normalize.ts (ARC-γ: the normalizer sat at 662 LOC, near the 700 cap).
|
|
5
|
+
* PURE: span identity → run/step identity, and step ordering within a run.
|
|
6
|
+
* No attribute is read here and no content ever reaches this module.
|
|
7
|
+
*/
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.operationClassOf = operationClassOf;
|
|
10
|
+
exports.toStepKey = toStepKey;
|
|
11
|
+
exports.traceIdToRunUuid = traceIdToRunUuid;
|
|
12
|
+
exports.assignAttempts = assignAttempts;
|
|
13
|
+
exports.numberTurns = numberTurns;
|
|
14
|
+
const node_crypto_1 = require("node:crypto");
|
|
15
|
+
const conversation_1 = require("../artifacts/conversation");
|
|
16
|
+
const OPERATION_CLASSES = {
|
|
17
|
+
// gen_ai.operation.name (semconv) + the normalizer's model-presence fallback
|
|
18
|
+
chat: "generation",
|
|
19
|
+
text_completion: "generation",
|
|
20
|
+
generate_content: "generation",
|
|
21
|
+
completion: "generation",
|
|
22
|
+
// openinference.span.kind (lower-cased by the normalizer)
|
|
23
|
+
llm: "generation",
|
|
24
|
+
generation: "generation",
|
|
25
|
+
execute_tool: "tool",
|
|
26
|
+
tool: "tool",
|
|
27
|
+
invoke_agent: "agent",
|
|
28
|
+
create_agent: "agent",
|
|
29
|
+
agent_step: "agent",
|
|
30
|
+
agent: "agent",
|
|
31
|
+
retriever: "retriever",
|
|
32
|
+
retrieval: "retriever",
|
|
33
|
+
embeddings: "embedding",
|
|
34
|
+
embedding: "embedding",
|
|
35
|
+
};
|
|
36
|
+
/** Map the normalizer's raw operation string to its class. */
|
|
37
|
+
function operationClassOf(operation) {
|
|
38
|
+
return OPERATION_CLASSES[operation] ?? "span";
|
|
39
|
+
}
|
|
40
|
+
const WS_RE = /\s+/g;
|
|
41
|
+
/** span name → step_key: trimmed, whitespace-collapsed, length-capped, never
|
|
42
|
+
* empty (the step PK forbids it). */
|
|
43
|
+
function toStepKey(name) {
|
|
44
|
+
const raw = typeof name === "string" ? name.trim().replace(WS_RE, " ") : "";
|
|
45
|
+
if (raw === "")
|
|
46
|
+
return "span";
|
|
47
|
+
return raw.slice(0, 200);
|
|
48
|
+
}
|
|
49
|
+
/** 32-hex trace id → canonical UUID string (runs.id is uuid; 16 bytes ≡ a UUID).
|
|
50
|
+
* A trace id that is not 16 bytes gets a deterministic UUID derived from it. */
|
|
51
|
+
function traceIdToRunUuid(traceHex) {
|
|
52
|
+
const hex = traceHex.length === 32 ? traceHex : (0, node_crypto_1.createHash)("sha256").update(traceHex).digest("hex").slice(0, 32);
|
|
53
|
+
return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20, 32)}`;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Assign attempt_no per (run, step_key): repeated same-named spans become
|
|
57
|
+
* attempts in start-time (then span-id) order — deterministic within a trace.
|
|
58
|
+
* v0 limitation: a trace split across batches is not deduped (no seen-store);
|
|
59
|
+
* one trace = one batch is the common, correct case.
|
|
60
|
+
*/
|
|
61
|
+
function assignAttempts(pending) {
|
|
62
|
+
const out = [];
|
|
63
|
+
const perKey = new Map();
|
|
64
|
+
for (const p of pending) {
|
|
65
|
+
const list = perKey.get(p.stepKey) ?? [];
|
|
66
|
+
list.push({ step: p.step, startMs: p.startMs });
|
|
67
|
+
perKey.set(p.stepKey, list);
|
|
68
|
+
}
|
|
69
|
+
for (const list of perKey.values()) {
|
|
70
|
+
list.sort((a, b) => a.startMs !== b.startMs
|
|
71
|
+
? a.startMs - b.startMs
|
|
72
|
+
: (a.step.span_id ?? "") < (b.step.span_id ?? "")
|
|
73
|
+
? -1
|
|
74
|
+
: 1);
|
|
75
|
+
list.forEach(({ step }, i) => out.push({ ...step, attempt_no: i + 1 }));
|
|
76
|
+
}
|
|
77
|
+
return out;
|
|
78
|
+
}
|
|
79
|
+
/** Parent-chain walk bound (a malformed trace can carry a cycle). */
|
|
80
|
+
const MAX_DEPTH = 64;
|
|
81
|
+
function byStart(a, b) {
|
|
82
|
+
if (a.startMs !== b.startMs)
|
|
83
|
+
return a.startMs - b.startMs;
|
|
84
|
+
return (a.span?.id ?? "") < (b.span?.id ?? "") ? -1 : 1;
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* ARC-6 turn numbering for ONE trace that is a conversation (it carries
|
|
88
|
+
* `gen_ai.conversation.id` and has `invoke_agent` spans). Each TOP-LEVEL
|
|
89
|
+
* `invoke_agent` span (no `invoke_agent` ancestor), in start order, is turn n;
|
|
90
|
+
* every generation span under it (any depth) becomes step `turn:<n>.<k>` in
|
|
91
|
+
* start order — ARC-α's step-key convention exactly (`turnStepKey`; a repeated
|
|
92
|
+
* key would read as a retry). The turn number and, on the agent span, the
|
|
93
|
+
* count of tool spans in that turn ride the attested envelope (`turn`,
|
|
94
|
+
* `turn_tool_calls`, `turn_generations`). Spans outside every turn are left
|
|
95
|
+
* as they were. Returns the rewritten list and the number of turns.
|
|
96
|
+
*/
|
|
97
|
+
function numberTurns(pending) {
|
|
98
|
+
const byId = new Map();
|
|
99
|
+
for (const p of pending)
|
|
100
|
+
if (p.span !== undefined)
|
|
101
|
+
byId.set(p.span.id, p);
|
|
102
|
+
const isAgent = (p) => p?.span?.operation === "invoke_agent";
|
|
103
|
+
/** The top-level invoke_agent ancestor of a span (itself when it is one), or null. */
|
|
104
|
+
const topAgentOf = (p) => {
|
|
105
|
+
let top = isAgent(p) ? p : null;
|
|
106
|
+
let cur = p.span?.parent != null ? byId.get(p.span.parent) : undefined;
|
|
107
|
+
const seen = new Set([p.span?.id ?? ""]);
|
|
108
|
+
for (let d = 0; cur !== undefined && d < MAX_DEPTH; d++) {
|
|
109
|
+
const id = cur.span?.id ?? "";
|
|
110
|
+
if (seen.has(id))
|
|
111
|
+
break;
|
|
112
|
+
seen.add(id);
|
|
113
|
+
if (isAgent(cur))
|
|
114
|
+
top = cur;
|
|
115
|
+
cur = cur.span?.parent != null ? byId.get(cur.span.parent) : undefined;
|
|
116
|
+
}
|
|
117
|
+
return top;
|
|
118
|
+
};
|
|
119
|
+
const agents = pending.filter((p) => isAgent(p) && topAgentOf(p) === p).sort(byStart);
|
|
120
|
+
if (agents.length === 0)
|
|
121
|
+
return { pending: [...pending], turns: 0 };
|
|
122
|
+
const turnOf = new Map(agents.map((a, i) => [a, i + 1]));
|
|
123
|
+
const generations = new Map();
|
|
124
|
+
const tools = new Map();
|
|
125
|
+
const turnBySpan = new Map();
|
|
126
|
+
for (const p of pending) {
|
|
127
|
+
const top = topAgentOf(p);
|
|
128
|
+
const n = top !== null ? turnOf.get(top) : undefined;
|
|
129
|
+
if (n === undefined)
|
|
130
|
+
continue;
|
|
131
|
+
turnBySpan.set(p, n);
|
|
132
|
+
if (p.span?.operationClass === "generation")
|
|
133
|
+
generations.set(n, [...(generations.get(n) ?? []), p]);
|
|
134
|
+
if (p.span?.operationClass === "tool")
|
|
135
|
+
tools.set(n, (tools.get(n) ?? 0) + 1);
|
|
136
|
+
}
|
|
137
|
+
const keyOf = new Map();
|
|
138
|
+
for (const [n, list] of generations)
|
|
139
|
+
list.sort(byStart).forEach((p, i) => keyOf.set(p, (0, conversation_1.turnStepKey)(n, i + 1)));
|
|
140
|
+
const out = pending.map((p) => {
|
|
141
|
+
const n = turnBySpan.get(p);
|
|
142
|
+
if (n === undefined)
|
|
143
|
+
return p;
|
|
144
|
+
const env = (typeof p.step.attested === "object" && p.step.attested !== null ? p.step.attested : {});
|
|
145
|
+
const isTop = turnOf.has(p);
|
|
146
|
+
const attested = {
|
|
147
|
+
...env,
|
|
148
|
+
turn: n,
|
|
149
|
+
...(isTop ? { turn_tool_calls: tools.get(n) ?? 0, turn_generations: generations.get(n)?.length ?? 0 } : {}),
|
|
150
|
+
};
|
|
151
|
+
const stepKey = keyOf.get(p) ?? p.stepKey;
|
|
152
|
+
return { ...p, stepKey, step: { ...p.step, step_key: stepKey, attested } };
|
|
153
|
+
});
|
|
154
|
+
return { pending: out, turns: agents.length };
|
|
155
|
+
}
|
|
@@ -23,6 +23,7 @@
|
|
|
23
23
|
* pattern per §5.3 — implemented fresh here, no bodies copied).
|
|
24
24
|
*/
|
|
25
25
|
import type { WireRun, WireStep } from "./ingest-protocol";
|
|
26
|
+
import { type OperationClass } from "./otel-hierarchy";
|
|
26
27
|
export interface AttestedUsage {
|
|
27
28
|
/** Non-cached input tokens (cache buckets subtracted, floored 0). */
|
|
28
29
|
readonly input?: number;
|
|
@@ -41,8 +42,21 @@ export interface AttestedEnvelope {
|
|
|
41
42
|
readonly model: string | null;
|
|
42
43
|
/** Resolved provider — honest, ending at "unknown", NEVER guessed. */
|
|
43
44
|
readonly provider: string;
|
|
44
|
-
/**
|
|
45
|
+
/** The classifier's raw operation (`invoke_agent`, `chat`, `retriever`, …). */
|
|
45
46
|
readonly operation: string;
|
|
47
|
+
/** ARC-6: the operation's class — generation | tool | agent | retriever |
|
|
48
|
+
* embedding, or `span` (unclassified). Additive. */
|
|
49
|
+
readonly operation_class?: OperationClass;
|
|
50
|
+
/** ARC-6: the parent span id (lowercase hex), or null for a root span.
|
|
51
|
+
* Kept for turn numbering + per-turn tool counts only — never rendered as a
|
|
52
|
+
* tree (spec §9). Additive. */
|
|
53
|
+
readonly parent_span_id?: string | null;
|
|
54
|
+
/** ARC-6: in a conversation trace, the turn this span belongs to. */
|
|
55
|
+
readonly turn?: number;
|
|
56
|
+
/** ARC-6: on a turn's top-level `invoke_agent` span — tool / generation
|
|
57
|
+
* spans counted in that turn. */
|
|
58
|
+
readonly turn_tool_calls?: number;
|
|
59
|
+
readonly turn_generations?: number;
|
|
46
60
|
/** De-double-counted usage; omitted entirely on agent-aggregate spans. */
|
|
47
61
|
readonly usage?: AttestedUsage;
|
|
48
62
|
/** The emitting instrumentation scope (name + optional version). */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"otel-normalize.d.ts","sourceRoot":"","sources":["../../src/telemetry/otel-normalize.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;
|
|
1
|
+
{"version":3,"file":"otel-normalize.d.ts","sourceRoot":"","sources":["../../src/telemetry/otel-normalize.ts"],"names":[],"mappings":"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"}
|
|
@@ -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 " +
|