@steel-dev/atlas 0.1.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/LICENSE +21 -0
- package/README.md +219 -0
- package/dist/agent.d.ts +34 -0
- package/dist/agent.js +133 -0
- package/dist/async.d.ts +19 -0
- package/dist/async.js +172 -0
- package/dist/atlas.d.ts +19 -0
- package/dist/atlas.js +69 -0
- package/dist/budget.d.ts +64 -0
- package/dist/budget.js +336 -0
- package/dist/checklist.d.ts +115 -0
- package/dist/checklist.js +297 -0
- package/dist/cli.js +38700 -0
- package/dist/config.d.ts +80 -0
- package/dist/config.js +109 -0
- package/dist/context.d.ts +26 -0
- package/dist/context.js +250 -0
- package/dist/custom-tools.d.ts +26 -0
- package/dist/custom-tools.js +33 -0
- package/dist/defaults.d.ts +10 -0
- package/dist/defaults.js +37 -0
- package/dist/economy.d.ts +12 -0
- package/dist/economy.js +6 -0
- package/dist/env.d.ts +1 -0
- package/dist/env.js +8 -0
- package/dist/errors.d.ts +6 -0
- package/dist/errors.js +11 -0
- package/dist/event-hub.d.ts +11 -0
- package/dist/event-hub.js +83 -0
- package/dist/events.d.ts +105 -0
- package/dist/events.js +1 -0
- package/dist/html-extract.d.ts +21 -0
- package/dist/html-extract.js +459 -0
- package/dist/index.d.ts +59 -0
- package/dist/index.js +26 -0
- package/dist/memory.d.ts +2 -0
- package/dist/memory.js +38 -0
- package/dist/model.d.ts +49 -0
- package/dist/model.js +630 -0
- package/dist/orchestrate.d.ts +5 -0
- package/dist/orchestrate.js +277 -0
- package/dist/pdf-extract.d.ts +5 -0
- package/dist/pdf-extract.js +20 -0
- package/dist/prompts.d.ts +2 -0
- package/dist/prompts.js +6 -0
- package/dist/providers/domain/arxiv.d.ts +6 -0
- package/dist/providers/domain/arxiv.js +83 -0
- package/dist/providers/domain/clinicaltrials.d.ts +6 -0
- package/dist/providers/domain/clinicaltrials.js +104 -0
- package/dist/providers/domain/edgar.d.ts +10 -0
- package/dist/providers/domain/edgar.js +92 -0
- package/dist/providers/domain/index.d.ts +14 -0
- package/dist/providers/domain/index.js +7 -0
- package/dist/providers/domain/openalex.d.ts +7 -0
- package/dist/providers/domain/openalex.js +128 -0
- package/dist/providers/domain/pubmed.d.ts +8 -0
- package/dist/providers/domain/pubmed.js +123 -0
- package/dist/providers/domain/semantic-scholar.d.ts +6 -0
- package/dist/providers/domain/semantic-scholar.js +112 -0
- package/dist/providers/domain/shared.d.ts +12 -0
- package/dist/providers/domain/shared.js +39 -0
- package/dist/providers/domain/wikipedia.d.ts +6 -0
- package/dist/providers/domain/wikipedia.js +71 -0
- package/dist/providers/exa-agent.d.ts +9 -0
- package/dist/providers/exa-agent.js +67 -0
- package/dist/providers/fetch.d.ts +66 -0
- package/dist/providers/fetch.js +675 -0
- package/dist/providers/parallel-agent.d.ts +11 -0
- package/dist/providers/parallel-agent.js +100 -0
- package/dist/providers/perplexity-agent.d.ts +17 -0
- package/dist/providers/perplexity-agent.js +86 -0
- package/dist/providers/search.d.ts +65 -0
- package/dist/providers/search.js +433 -0
- package/dist/providers/store.d.ts +48 -0
- package/dist/providers/store.js +217 -0
- package/dist/researcher.d.ts +20 -0
- package/dist/researcher.js +3 -0
- package/dist/robots.d.ts +16 -0
- package/dist/robots.js +146 -0
- package/dist/roles.d.ts +6 -0
- package/dist/roles.js +4 -0
- package/dist/run.d.ts +65 -0
- package/dist/run.js +371 -0
- package/dist/safe-dispatcher.d.ts +16 -0
- package/dist/safe-dispatcher.js +32 -0
- package/dist/safety.d.ts +23 -0
- package/dist/safety.js +206 -0
- package/dist/sandbox.d.ts +22 -0
- package/dist/sandbox.js +228 -0
- package/dist/search-normalize.d.ts +2 -0
- package/dist/search-normalize.js +13 -0
- package/dist/source-documents.d.ts +77 -0
- package/dist/source-documents.js +421 -0
- package/dist/sources.d.ts +57 -0
- package/dist/sources.js +1 -0
- package/dist/spine.d.ts +19 -0
- package/dist/spine.js +722 -0
- package/dist/state.d.ts +90 -0
- package/dist/state.js +27 -0
- package/dist/structured.d.ts +7 -0
- package/dist/structured.js +18 -0
- package/dist/tools.d.ts +33 -0
- package/dist/tools.js +1187 -0
- package/dist/trace-digest.d.ts +11 -0
- package/dist/trace-digest.js +309 -0
- package/dist/trace.d.ts +225 -0
- package/dist/trace.js +278 -0
- package/dist/trail.d.ts +15 -0
- package/dist/trail.js +74 -0
- package/dist/url.d.ts +1 -0
- package/dist/url.js +25 -0
- package/package.json +107 -0
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { type RunDigest, type Span, type TraceStep } from "./trace.js";
|
|
2
|
+
export interface DigestMeta {
|
|
3
|
+
runId: string;
|
|
4
|
+
wallMs: number;
|
|
5
|
+
costUSD: number;
|
|
6
|
+
freshTokens: number;
|
|
7
|
+
replayedUSD: number;
|
|
8
|
+
gateLimitModel: number;
|
|
9
|
+
gateLimitIo: number;
|
|
10
|
+
}
|
|
11
|
+
export declare function computeDigest(spans: readonly Span[], _steps: readonly TraceStep[], meta: DigestMeta): RunDigest;
|
|
@@ -0,0 +1,309 @@
|
|
|
1
|
+
import { SITE_SOURCE, TRACE_SCHEMA_VERSION, } from "./trace.js";
|
|
2
|
+
const TOP_N = 8;
|
|
3
|
+
const HIGH_WAIT_FLOOR_MS = 750;
|
|
4
|
+
const SLOW_STEP_FLOOR_MS = 4_000;
|
|
5
|
+
const MAX_CRITICAL_STEPS = 20_000;
|
|
6
|
+
function tokensOf(span) {
|
|
7
|
+
const t = span.tokens;
|
|
8
|
+
return t ? t.input + t.output + t.cacheRead + t.cacheWrite : 0;
|
|
9
|
+
}
|
|
10
|
+
function toCritical(span) {
|
|
11
|
+
return {
|
|
12
|
+
spanId: span.id,
|
|
13
|
+
site: span.site,
|
|
14
|
+
...(span.agentId ? { agentId: span.agentId } : {}),
|
|
15
|
+
kind: span.kind,
|
|
16
|
+
durationMs: span.durationMs,
|
|
17
|
+
waitMs: span.waitMs,
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
/** Klee's algorithm: total wall time covered by a set of [t0,t1] intervals. */
|
|
21
|
+
function intervalUnionMs(spans) {
|
|
22
|
+
const intervals = spans
|
|
23
|
+
.map((s) => [s.t0, s.t1])
|
|
24
|
+
.filter(([a, b]) => b > a)
|
|
25
|
+
.sort((a, b) => a[0] - b[0]);
|
|
26
|
+
if (intervals.length === 0)
|
|
27
|
+
return 0;
|
|
28
|
+
let total = 0;
|
|
29
|
+
let [curStart, curEnd] = intervals[0];
|
|
30
|
+
for (let i = 1; i < intervals.length; i++) {
|
|
31
|
+
const [s, e] = intervals[i];
|
|
32
|
+
if (s > curEnd) {
|
|
33
|
+
total += curEnd - curStart;
|
|
34
|
+
curStart = s;
|
|
35
|
+
curEnd = e;
|
|
36
|
+
}
|
|
37
|
+
else if (e > curEnd) {
|
|
38
|
+
curEnd = e;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
return total + (curEnd - curStart);
|
|
42
|
+
}
|
|
43
|
+
/** Max number of overlapping intervals (sweep). Ties: ends before starts. */
|
|
44
|
+
function peakOverlap(spans) {
|
|
45
|
+
const points = [];
|
|
46
|
+
for (const s of spans) {
|
|
47
|
+
if (s.t1 <= s.t0)
|
|
48
|
+
continue;
|
|
49
|
+
points.push([s.t0, 1], [s.t1, -1]);
|
|
50
|
+
}
|
|
51
|
+
points.sort((a, b) => a[0] - b[0] || a[1] - b[1]);
|
|
52
|
+
let cur = 0;
|
|
53
|
+
let peak = 0;
|
|
54
|
+
for (const [, delta] of points) {
|
|
55
|
+
cur += delta;
|
|
56
|
+
if (cur > peak)
|
|
57
|
+
peak = cur;
|
|
58
|
+
}
|
|
59
|
+
return peak;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Greedy timeline cover: walk from run start, always stepping into the work
|
|
63
|
+
* span that extends furthest from the cursor. Produces the chain of spans that
|
|
64
|
+
* actually fills the wall clock — i.e. where the time went.
|
|
65
|
+
*/
|
|
66
|
+
function criticalPath(work, runStart, runEnd) {
|
|
67
|
+
const sorted = [...work].sort((a, b) => a.t0 - b.t0);
|
|
68
|
+
const path = [];
|
|
69
|
+
let cursor = runStart;
|
|
70
|
+
let covered = 0;
|
|
71
|
+
let idle = 0;
|
|
72
|
+
let guard = 0;
|
|
73
|
+
while (cursor < runEnd && guard++ < MAX_CRITICAL_STEPS) {
|
|
74
|
+
let pick;
|
|
75
|
+
let next;
|
|
76
|
+
for (const s of sorted) {
|
|
77
|
+
if (s.t1 <= cursor)
|
|
78
|
+
continue;
|
|
79
|
+
if (s.t0 <= cursor) {
|
|
80
|
+
if (!pick || s.t1 > pick.t1)
|
|
81
|
+
pick = s;
|
|
82
|
+
}
|
|
83
|
+
else if (!next || s.t0 < next.t0) {
|
|
84
|
+
next = s;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
if (pick) {
|
|
88
|
+
path.push(toCritical(pick));
|
|
89
|
+
covered += pick.t1 - cursor;
|
|
90
|
+
cursor = pick.t1;
|
|
91
|
+
}
|
|
92
|
+
else if (next) {
|
|
93
|
+
idle += next.t0 - cursor;
|
|
94
|
+
cursor = next.t0;
|
|
95
|
+
}
|
|
96
|
+
else {
|
|
97
|
+
break;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
return { path, coveredMs: covered, idleMs: idle };
|
|
101
|
+
}
|
|
102
|
+
function percentile(values, p) {
|
|
103
|
+
if (values.length === 0)
|
|
104
|
+
return 0;
|
|
105
|
+
const sorted = [...values].sort((a, b) => a - b);
|
|
106
|
+
const idx = Math.min(sorted.length - 1, Math.floor((p / 100) * sorted.length));
|
|
107
|
+
return sorted[idx];
|
|
108
|
+
}
|
|
109
|
+
function median(values) {
|
|
110
|
+
return percentile(values, 50);
|
|
111
|
+
}
|
|
112
|
+
function detectAnomalies(modelSpans, agentSpans) {
|
|
113
|
+
const anomalies = [];
|
|
114
|
+
const waits = modelSpans.map((s) => s.waitMs);
|
|
115
|
+
const computes = modelSpans
|
|
116
|
+
.map((s) => s.computeMs ?? s.durationMs)
|
|
117
|
+
.filter((v) => v > 0);
|
|
118
|
+
const waitThreshold = Math.max(HIGH_WAIT_FLOOR_MS, percentile(waits, 95));
|
|
119
|
+
const slowThreshold = Math.max(SLOW_STEP_FLOOR_MS, 3 * median(computes));
|
|
120
|
+
for (const s of modelSpans) {
|
|
121
|
+
if (s.waitMs >= waitThreshold && s.waitMs >= HIGH_WAIT_FLOOR_MS) {
|
|
122
|
+
anomalies.push({
|
|
123
|
+
kind: "high-wait",
|
|
124
|
+
spanId: s.id,
|
|
125
|
+
site: s.site,
|
|
126
|
+
...(s.agentId ? { agentId: s.agentId } : {}),
|
|
127
|
+
detail: `queued ${Math.round(s.waitMs)}ms before the model gate freed a slot`,
|
|
128
|
+
severityMs: Math.round(s.waitMs),
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
const compute = s.computeMs ?? s.durationMs;
|
|
132
|
+
if (compute >= slowThreshold) {
|
|
133
|
+
anomalies.push({
|
|
134
|
+
kind: "slow-step",
|
|
135
|
+
spanId: s.id,
|
|
136
|
+
site: s.site,
|
|
137
|
+
...(s.agentId ? { agentId: s.agentId } : {}),
|
|
138
|
+
detail: `model call took ${Math.round(compute)}ms`,
|
|
139
|
+
severityMs: Math.round(compute),
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
if (s.retryDelayMs && s.retryDelayMs > 1_000) {
|
|
143
|
+
anomalies.push({
|
|
144
|
+
kind: "retry-storm",
|
|
145
|
+
spanId: s.id,
|
|
146
|
+
site: s.site,
|
|
147
|
+
detail: `${Math.round(s.retryDelayMs)}ms lost to retries/backoff`,
|
|
148
|
+
severityMs: Math.round(s.retryDelayMs),
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
if (s.status === "error") {
|
|
152
|
+
anomalies.push({
|
|
153
|
+
kind: "error-step",
|
|
154
|
+
spanId: s.id,
|
|
155
|
+
site: s.site,
|
|
156
|
+
...(s.agentId ? { agentId: s.agentId } : {}),
|
|
157
|
+
detail: `model call failed: ${String(s.attrs?.finishReason ?? "error")}`,
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
const freshKeys = new Map();
|
|
162
|
+
for (const s of modelSpans) {
|
|
163
|
+
if (s.status !== "ok")
|
|
164
|
+
continue;
|
|
165
|
+
const key = s.attrs?.callKey;
|
|
166
|
+
if (typeof key !== "string" || !key)
|
|
167
|
+
continue;
|
|
168
|
+
freshKeys.set(key, (freshKeys.get(key) ?? 0) + 1);
|
|
169
|
+
}
|
|
170
|
+
for (const [key, count] of freshKeys) {
|
|
171
|
+
if (count > 1) {
|
|
172
|
+
anomalies.push({
|
|
173
|
+
kind: "redundant-call",
|
|
174
|
+
detail: `identical model call issued ${count}× (callKey ${key.slice(0, 8)}) — duplicated spend`,
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
const subtrees = agentSpans.map((s) => s.durationMs);
|
|
179
|
+
const tailThreshold = 3 * median(subtrees);
|
|
180
|
+
for (const s of agentSpans) {
|
|
181
|
+
if (subtrees.length > 2 &&
|
|
182
|
+
s.durationMs >= tailThreshold &&
|
|
183
|
+
s.durationMs > 0) {
|
|
184
|
+
anomalies.push({
|
|
185
|
+
kind: "tail-agent",
|
|
186
|
+
spanId: s.id,
|
|
187
|
+
...(s.agentId ? { agentId: s.agentId } : {}),
|
|
188
|
+
detail: `agent ran ${Math.round(s.durationMs)}ms — a long pole vs sibling median`,
|
|
189
|
+
severityMs: Math.round(s.durationMs),
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
return anomalies.sort((a, b) => (b.severityMs ?? 0) - (a.severityMs ?? 0));
|
|
194
|
+
}
|
|
195
|
+
export function computeDigest(spans, _steps, meta) {
|
|
196
|
+
const modelSpans = spans.filter((s) => s.kind === "model");
|
|
197
|
+
const ioSpans = spans.filter((s) => s.kind === "io");
|
|
198
|
+
const toolSpans = spans.filter((s) => s.kind === "tool");
|
|
199
|
+
const agentSpans = spans.filter((s) => s.kind === "agent");
|
|
200
|
+
const workSpans = [...modelSpans, ...ioSpans, ...toolSpans];
|
|
201
|
+
const realStart = spans.reduce((min, s) => Math.min(min, s.t0), Infinity);
|
|
202
|
+
const realEnd = spans.reduce((max, s) => Math.max(max, s.t1), 0);
|
|
203
|
+
const runStart = Number.isFinite(realStart) ? realStart : 0;
|
|
204
|
+
const runEnd = Math.max(realEnd, runStart);
|
|
205
|
+
const modelComputeMs = modelSpans.reduce((sum, s) => sum + (s.computeMs ?? s.durationMs), 0);
|
|
206
|
+
const modelWaitMs = modelSpans.reduce((sum, s) => sum + s.waitMs, 0);
|
|
207
|
+
const ioMs = intervalUnionMs(ioSpans);
|
|
208
|
+
const { path, coveredMs, idleMs } = criticalPath(workSpans, runStart, runEnd);
|
|
209
|
+
// phase rollups keyed by site (agents excluded — they wrap their children)
|
|
210
|
+
const bySite = new Map();
|
|
211
|
+
for (const s of workSpans) {
|
|
212
|
+
const arr = bySite.get(s.site) ?? [];
|
|
213
|
+
arr.push(s);
|
|
214
|
+
bySite.set(s.site, arr);
|
|
215
|
+
}
|
|
216
|
+
const phaseBreakdown = {};
|
|
217
|
+
const attribution = {};
|
|
218
|
+
for (const [site, arr] of bySite) {
|
|
219
|
+
const models = arr.filter((s) => s.kind === "model");
|
|
220
|
+
phaseBreakdown[site] = {
|
|
221
|
+
wallMs: Math.round(intervalUnionMs(arr)),
|
|
222
|
+
modelComputeMs: Math.round(models.reduce((sum, s) => sum + (s.computeMs ?? s.durationMs), 0)),
|
|
223
|
+
modelWaitMs: Math.round(models.reduce((sum, s) => sum + s.waitMs, 0)),
|
|
224
|
+
costUSD: Math.round(arr.reduce((sum, s) => sum + (s.costUSD ?? 0), 0) * 10_000) /
|
|
225
|
+
10_000,
|
|
226
|
+
tokens: arr.reduce((sum, s) => sum + tokensOf(s), 0),
|
|
227
|
+
spanCount: arr.length,
|
|
228
|
+
};
|
|
229
|
+
if (SITE_SOURCE[site])
|
|
230
|
+
attribution[site] = SITE_SOURCE[site];
|
|
231
|
+
}
|
|
232
|
+
// per-agent rollups
|
|
233
|
+
const ownByAgent = new Map();
|
|
234
|
+
for (const s of workSpans) {
|
|
235
|
+
if (!s.agentId)
|
|
236
|
+
continue;
|
|
237
|
+
const arr = ownByAgent.get(s.agentId) ?? [];
|
|
238
|
+
arr.push(s);
|
|
239
|
+
ownByAgent.set(s.agentId, arr);
|
|
240
|
+
}
|
|
241
|
+
const agentSpanById = new Map();
|
|
242
|
+
for (const s of agentSpans)
|
|
243
|
+
if (s.agentId)
|
|
244
|
+
agentSpanById.set(s.agentId, s);
|
|
245
|
+
const byAgent = [];
|
|
246
|
+
for (const [agentId, own] of ownByAgent) {
|
|
247
|
+
const wrapper = agentSpanById.get(agentId);
|
|
248
|
+
byAgent.push({
|
|
249
|
+
agentId,
|
|
250
|
+
...(wrapper?.logicalAgentId
|
|
251
|
+
? { logicalAgentId: wrapper.logicalAgentId }
|
|
252
|
+
: {}),
|
|
253
|
+
...(wrapper?.role ? { role: wrapper.role } : {}),
|
|
254
|
+
selfMs: Math.round(intervalUnionMs(own)),
|
|
255
|
+
subtreeMs: Math.round(wrapper ? wrapper.durationMs : intervalUnionMs(own)),
|
|
256
|
+
costUSD: Math.round(own.reduce((sum, s) => sum + (s.costUSD ?? 0), 0) * 10_000) /
|
|
257
|
+
10_000,
|
|
258
|
+
tokens: own.reduce((sum, s) => sum + tokensOf(s), 0),
|
|
259
|
+
spanCount: own.length,
|
|
260
|
+
});
|
|
261
|
+
}
|
|
262
|
+
byAgent.sort((a, b) => b.subtreeMs - a.subtreeMs);
|
|
263
|
+
const topByCost = [...modelSpans]
|
|
264
|
+
.sort((a, b) => (b.costUSD ?? 0) - (a.costUSD ?? 0))
|
|
265
|
+
.slice(0, TOP_N)
|
|
266
|
+
.map(toCritical);
|
|
267
|
+
const topByLatency = [...modelSpans]
|
|
268
|
+
.sort((a, b) => (b.computeMs ?? b.durationMs) - (a.computeMs ?? a.durationMs))
|
|
269
|
+
.slice(0, TOP_N)
|
|
270
|
+
.map(toCritical);
|
|
271
|
+
const topByWait = [...modelSpans]
|
|
272
|
+
.sort((a, b) => b.waitMs - a.waitMs)
|
|
273
|
+
.slice(0, TOP_N)
|
|
274
|
+
.map(toCritical);
|
|
275
|
+
return {
|
|
276
|
+
runId: meta.runId,
|
|
277
|
+
schemaVersion: TRACE_SCHEMA_VERSION,
|
|
278
|
+
wallMs: Math.round(meta.wallMs),
|
|
279
|
+
modelComputeMs: Math.round(modelComputeMs),
|
|
280
|
+
modelWaitMs: Math.round(modelWaitMs),
|
|
281
|
+
ioMs: Math.round(ioMs),
|
|
282
|
+
costUSD: meta.costUSD,
|
|
283
|
+
freshTokens: meta.freshTokens,
|
|
284
|
+
replayedUSD: Math.round(meta.replayedUSD * 10_000) / 10_000,
|
|
285
|
+
criticalPath: path,
|
|
286
|
+
criticalPathMs: Math.round(coveredMs),
|
|
287
|
+
idleMs: Math.round(idleMs),
|
|
288
|
+
phaseBreakdown,
|
|
289
|
+
byAgent,
|
|
290
|
+
topByCost,
|
|
291
|
+
topByLatency,
|
|
292
|
+
topByWait,
|
|
293
|
+
waitVsCompute: {
|
|
294
|
+
computeMs: Math.round(modelComputeMs),
|
|
295
|
+
waitMs: Math.round(modelWaitMs),
|
|
296
|
+
ratio: modelComputeMs > 0
|
|
297
|
+
? Math.round((modelWaitMs / modelComputeMs) * 100) / 100
|
|
298
|
+
: 0,
|
|
299
|
+
},
|
|
300
|
+
concurrency: {
|
|
301
|
+
peakModelInFlight: peakOverlap(modelSpans),
|
|
302
|
+
peakIoInFlight: peakOverlap(ioSpans),
|
|
303
|
+
gateLimitModel: meta.gateLimitModel,
|
|
304
|
+
gateLimitIo: meta.gateLimitIo,
|
|
305
|
+
},
|
|
306
|
+
anomalies: detectAnomalies(modelSpans, agentSpans),
|
|
307
|
+
attribution,
|
|
308
|
+
};
|
|
309
|
+
}
|
package/dist/trace.d.ts
ADDED
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
import type { LanguageModelV3CallOptions, LanguageModelV3Content } from "@ai-sdk/provider";
|
|
2
|
+
import type { TraceMode } from "./config.js";
|
|
3
|
+
export declare const TRACE_SCHEMA_VERSION = "1.0";
|
|
4
|
+
export type SpanKind = "model" | "tool" | "io" | "agent";
|
|
5
|
+
export type SpanStatus = "ok" | "error" | "aborted" | "replayed";
|
|
6
|
+
export interface TokenCounts {
|
|
7
|
+
input: number;
|
|
8
|
+
output: number;
|
|
9
|
+
cacheRead: number;
|
|
10
|
+
cacheWrite: number;
|
|
11
|
+
}
|
|
12
|
+
/** Ambient attribution used to tag spans without threading parameters through calls. */
|
|
13
|
+
export interface TraceFrame {
|
|
14
|
+
agentId?: string;
|
|
15
|
+
logicalAgentId?: string;
|
|
16
|
+
role?: string;
|
|
17
|
+
depth?: number;
|
|
18
|
+
parentSpanId?: string;
|
|
19
|
+
site?: string;
|
|
20
|
+
}
|
|
21
|
+
export interface Span {
|
|
22
|
+
id: string;
|
|
23
|
+
parentId?: string;
|
|
24
|
+
kind: SpanKind;
|
|
25
|
+
site: string;
|
|
26
|
+
agentId?: string;
|
|
27
|
+
logicalAgentId?: string;
|
|
28
|
+
role?: string;
|
|
29
|
+
t0: number;
|
|
30
|
+
t1: number;
|
|
31
|
+
durationMs: number;
|
|
32
|
+
waitMs: number;
|
|
33
|
+
computeMs?: number;
|
|
34
|
+
retryDelayMs?: number;
|
|
35
|
+
tokens?: TokenCounts;
|
|
36
|
+
costUSD?: number;
|
|
37
|
+
status: SpanStatus;
|
|
38
|
+
attrs?: Record<string, unknown>;
|
|
39
|
+
}
|
|
40
|
+
/** Verbatim per-model-call record used for trace replay and inspection. */
|
|
41
|
+
export interface TraceStep {
|
|
42
|
+
seq: number;
|
|
43
|
+
atMs: number;
|
|
44
|
+
spanId: string;
|
|
45
|
+
role: string;
|
|
46
|
+
adapter: string;
|
|
47
|
+
site: string;
|
|
48
|
+
agentId?: string;
|
|
49
|
+
durationMs: number;
|
|
50
|
+
waitMs: number;
|
|
51
|
+
system: string;
|
|
52
|
+
messages: unknown[];
|
|
53
|
+
toolNames?: string[];
|
|
54
|
+
maxTokens: number;
|
|
55
|
+
outputSchema?: string;
|
|
56
|
+
output: Array<Record<string, unknown>>;
|
|
57
|
+
inputTokens?: number;
|
|
58
|
+
costUSD?: number;
|
|
59
|
+
finishReason?: string;
|
|
60
|
+
replayed?: boolean;
|
|
61
|
+
truncated?: boolean;
|
|
62
|
+
error?: string;
|
|
63
|
+
}
|
|
64
|
+
export interface DigestAnomaly {
|
|
65
|
+
kind: "high-wait" | "slow-step" | "retry-storm" | "redundant-call" | "oversized-prompt" | "empty-output" | "error-step" | "tail-agent";
|
|
66
|
+
spanId?: string;
|
|
67
|
+
site?: string;
|
|
68
|
+
agentId?: string;
|
|
69
|
+
detail: string;
|
|
70
|
+
severityMs?: number;
|
|
71
|
+
}
|
|
72
|
+
export interface DigestPhase {
|
|
73
|
+
wallMs: number;
|
|
74
|
+
modelComputeMs: number;
|
|
75
|
+
modelWaitMs: number;
|
|
76
|
+
costUSD: number;
|
|
77
|
+
tokens: number;
|
|
78
|
+
spanCount: number;
|
|
79
|
+
}
|
|
80
|
+
export interface DigestAgent {
|
|
81
|
+
agentId: string;
|
|
82
|
+
logicalAgentId?: string;
|
|
83
|
+
role?: string;
|
|
84
|
+
selfMs: number;
|
|
85
|
+
subtreeMs: number;
|
|
86
|
+
costUSD: number;
|
|
87
|
+
tokens: number;
|
|
88
|
+
spanCount: number;
|
|
89
|
+
}
|
|
90
|
+
export interface CriticalSpan {
|
|
91
|
+
spanId: string;
|
|
92
|
+
site: string;
|
|
93
|
+
agentId?: string;
|
|
94
|
+
kind: SpanKind;
|
|
95
|
+
durationMs: number;
|
|
96
|
+
waitMs: number;
|
|
97
|
+
}
|
|
98
|
+
export interface RunDigest {
|
|
99
|
+
runId: string;
|
|
100
|
+
schemaVersion: string;
|
|
101
|
+
wallMs: number;
|
|
102
|
+
modelComputeMs: number;
|
|
103
|
+
modelWaitMs: number;
|
|
104
|
+
ioMs: number;
|
|
105
|
+
costUSD: number;
|
|
106
|
+
freshTokens: number;
|
|
107
|
+
replayedUSD: number;
|
|
108
|
+
criticalPath: CriticalSpan[];
|
|
109
|
+
criticalPathMs: number;
|
|
110
|
+
idleMs: number;
|
|
111
|
+
phaseBreakdown: Record<string, DigestPhase>;
|
|
112
|
+
byAgent: DigestAgent[];
|
|
113
|
+
topByCost: CriticalSpan[];
|
|
114
|
+
topByLatency: CriticalSpan[];
|
|
115
|
+
topByWait: CriticalSpan[];
|
|
116
|
+
waitVsCompute: {
|
|
117
|
+
computeMs: number;
|
|
118
|
+
waitMs: number;
|
|
119
|
+
ratio: number;
|
|
120
|
+
};
|
|
121
|
+
concurrency: {
|
|
122
|
+
peakModelInFlight: number;
|
|
123
|
+
peakIoInFlight: number;
|
|
124
|
+
gateLimitModel: number;
|
|
125
|
+
gateLimitIo: number;
|
|
126
|
+
};
|
|
127
|
+
anomalies: DigestAnomaly[];
|
|
128
|
+
attribution: Record<string, string>;
|
|
129
|
+
degraded?: boolean;
|
|
130
|
+
}
|
|
131
|
+
export interface RunTrace {
|
|
132
|
+
schemaVersion: string;
|
|
133
|
+
mode: TraceMode;
|
|
134
|
+
spans: Span[];
|
|
135
|
+
steps: TraceStep[];
|
|
136
|
+
digest?: RunDigest;
|
|
137
|
+
degraded: boolean;
|
|
138
|
+
}
|
|
139
|
+
/** Maps trace sites back to their source locations. */
|
|
140
|
+
export declare const SITE_SOURCE: Record<string, string>;
|
|
141
|
+
export declare function currentFrame(): TraceFrame | undefined;
|
|
142
|
+
/** Run `fn` under a trace frame merged onto the current one. */
|
|
143
|
+
export declare function withTraceFrame<T>(recorder: TraceRecorder | undefined, patch: TraceFrame, fn: () => T): T;
|
|
144
|
+
/** Normalize model content blocks into the trace viewer's block vocabulary. */
|
|
145
|
+
export declare function toAnthropicBlocks(content: readonly LanguageModelV3Content[] | undefined): Array<Record<string, unknown>>;
|
|
146
|
+
export interface ModelCallRecord {
|
|
147
|
+
callKey: string;
|
|
148
|
+
role: string;
|
|
149
|
+
provider: string;
|
|
150
|
+
modelId: string;
|
|
151
|
+
t0: number;
|
|
152
|
+
t1: number;
|
|
153
|
+
waitMs: number;
|
|
154
|
+
computeMs: number;
|
|
155
|
+
retryDelayMs?: number;
|
|
156
|
+
attempts?: number;
|
|
157
|
+
tokens?: TokenCounts;
|
|
158
|
+
costUSD?: number;
|
|
159
|
+
finishReason?: string;
|
|
160
|
+
status: SpanStatus;
|
|
161
|
+
replayed?: boolean;
|
|
162
|
+
error?: string;
|
|
163
|
+
params?: LanguageModelV3CallOptions;
|
|
164
|
+
content?: readonly LanguageModelV3Content[];
|
|
165
|
+
}
|
|
166
|
+
export interface AgentSpanRecord {
|
|
167
|
+
id: string;
|
|
168
|
+
parentId?: string;
|
|
169
|
+
site: string;
|
|
170
|
+
agentId: string;
|
|
171
|
+
logicalAgentId?: string;
|
|
172
|
+
role?: string;
|
|
173
|
+
t0: number;
|
|
174
|
+
t1: number;
|
|
175
|
+
costUSD?: number;
|
|
176
|
+
status: SpanStatus;
|
|
177
|
+
attrs?: Record<string, unknown>;
|
|
178
|
+
}
|
|
179
|
+
export interface ToolSpanRecord {
|
|
180
|
+
kind: "tool" | "io";
|
|
181
|
+
site: string;
|
|
182
|
+
agentId?: string;
|
|
183
|
+
parentId?: string;
|
|
184
|
+
t0: number;
|
|
185
|
+
t1: number;
|
|
186
|
+
waitMs: number;
|
|
187
|
+
status: SpanStatus;
|
|
188
|
+
attrs?: Record<string, unknown>;
|
|
189
|
+
}
|
|
190
|
+
export declare class TraceRecorder {
|
|
191
|
+
readonly mode: Exclude<TraceMode, "off">;
|
|
192
|
+
private readonly nowFn;
|
|
193
|
+
private readonly startedAt;
|
|
194
|
+
private readonly spanList;
|
|
195
|
+
private readonly stepList;
|
|
196
|
+
private spanSeq;
|
|
197
|
+
private totalChars;
|
|
198
|
+
private degradedFlag;
|
|
199
|
+
private digest;
|
|
200
|
+
private sink;
|
|
201
|
+
constructor(opts: {
|
|
202
|
+
mode: Exclude<TraceMode, "off">;
|
|
203
|
+
now: () => number;
|
|
204
|
+
startedAt: number;
|
|
205
|
+
});
|
|
206
|
+
now(): number;
|
|
207
|
+
get spans(): readonly Span[];
|
|
208
|
+
get steps(): readonly TraceStep[];
|
|
209
|
+
get degraded(): boolean;
|
|
210
|
+
mintSpanId(): string;
|
|
211
|
+
/** Where trace entries are streamed for plain-run journaling (P7). */
|
|
212
|
+
setSink(sink: (kind: "span" | "step" | "digest", id: string, data: unknown) => void): void;
|
|
213
|
+
private addSpan;
|
|
214
|
+
recordAgentSpan(rec: AgentSpanRecord): void;
|
|
215
|
+
recordToolSpan(rec: ToolSpanRecord): void;
|
|
216
|
+
recordModelCall(rec: ModelCallRecord, frame: TraceFrame | undefined): void;
|
|
217
|
+
private recordStep;
|
|
218
|
+
finalize(digest: RunDigest): void;
|
|
219
|
+
snapshot(): RunTrace;
|
|
220
|
+
}
|
|
221
|
+
export declare function createTraceRecorder(opts: {
|
|
222
|
+
mode: Exclude<TraceMode, "off">;
|
|
223
|
+
now: () => number;
|
|
224
|
+
startedAt: number;
|
|
225
|
+
}): TraceRecorder;
|