@tangle-network/agent-eval 0.20.12 → 0.21.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 +76 -0
- package/README.md +39 -1
- package/dist/{chunk-75MCTH7P.js → chunk-3GN6U53I.js} +198 -3
- package/dist/chunk-3GN6U53I.js.map +1 -0
- package/dist/chunk-3IX6QTB7.js +1349 -0
- package/dist/chunk-3IX6QTB7.js.map +1 -0
- package/dist/{chunk-PKCVBYTQ.js → chunk-5IIQKMD5.js} +38 -2
- package/dist/chunk-5IIQKMD5.js.map +1 -0
- package/dist/{chunk-MCMV7DUL.js → chunk-ARZ6BEV6.js} +2 -2
- package/dist/{chunk-HKYRWNHV.js → chunk-HRZELXCR.js} +2 -2
- package/dist/{chunk-ODFINDLQ.js → chunk-KRR4VMH7.js} +11 -1
- package/dist/chunk-KRR4VMH7.js.map +1 -0
- package/dist/chunk-SNUHRBDL.js +154 -0
- package/dist/chunk-SNUHRBDL.js.map +1 -0
- package/dist/{chunk-KWUAAIHR.js → chunk-WOK2RTWG.js} +157 -1
- package/dist/chunk-WOK2RTWG.js.map +1 -0
- package/dist/{chunk-HNJLMAJ2.js → chunk-WOPGKVN4.js} +2 -2
- package/dist/cli.js +3 -2
- package/dist/cli.js.map +1 -1
- package/dist/{control-C8NKbF3w.d.ts → control-cxwMOAsy.d.ts} +3 -2
- package/dist/control.d.ts +4 -3
- package/dist/control.js +2 -2
- package/dist/emitter-B2XqDKFU.d.ts +121 -0
- package/dist/{feedback-trajectory-BGQ_ANCN.d.ts → feedback-trajectory-CB0A32o3.d.ts} +2 -1
- package/dist/index.d.ts +71 -83
- package/dist/index.js +48 -60
- package/dist/index.js.map +1 -1
- package/dist/openapi.json +1 -1
- package/dist/optimization.d.ts +3 -2
- package/dist/optimization.js +2 -2
- package/dist/reporting-Da2ihlcM.d.ts +672 -0
- package/dist/reporting.d.ts +5 -426
- package/dist/reporting.js +6 -2
- package/dist/{emitter-BYO2nSDA.d.ts → store-u47QaJ9G.d.ts} +1 -91
- package/dist/traces.d.ts +259 -3
- package/dist/traces.js +24 -4
- package/dist/wire/index.js +3 -2
- package/docs/research-report-methodology.md +155 -0
- package/package.json +10 -12
- package/dist/chunk-75MCTH7P.js.map +0 -1
- package/dist/chunk-IKFVX537.js +0 -717
- package/dist/chunk-IKFVX537.js.map +0 -1
- package/dist/chunk-KWUAAIHR.js.map +0 -1
- package/dist/chunk-ODFINDLQ.js.map +0 -1
- package/dist/chunk-PKCVBYTQ.js.map +0 -1
- /package/dist/{chunk-MCMV7DUL.js.map → chunk-ARZ6BEV6.js.map} +0 -0
- /package/dist/{chunk-HKYRWNHV.js.map → chunk-HRZELXCR.js.map} +0 -0
- /package/dist/{chunk-HNJLMAJ2.js.map → chunk-WOPGKVN4.js.map} +0 -0
package/dist/traces.d.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
|
-
import {
|
|
2
|
-
export { A as Artifact, B as BudgetLedgerEntry,
|
|
1
|
+
import { T as TraceStore, L as LlmSpan, J as JudgeSpan, a as Run, F as FailureClass, c as ToolSpan } from './store-u47QaJ9G.js';
|
|
2
|
+
export { A as Artifact, B as BudgetLedgerEntry, g as BudgetSpec, i as EventFilter, E as EventKind, j as FAILURE_CLASSES, k as FileSystemTraceStore, l as FileSystemTraceStoreOptions, G as GenericSpan, I as InMemoryTraceStore, M as Message, d as RetrievalSpan, h as RunFilter, m as RunLayer, R as RunOutcome, n as RunStatus, e as SandboxSpan, S as Span, o as SpanBase, p as SpanFilter, b as SpanKind, q as SpanStatus, r as TRACE_SCHEMA_VERSION, f as TraceEvent, s as isJudgeSpan, t as isLlmSpan, u as isRetrievalSpan, v as isSandboxSpan, w as isToolSpan } from './store-u47QaJ9G.js';
|
|
3
|
+
import { a as RunCompleteHookContext, R as RunCompleteHook } from './emitter-B2XqDKFU.js';
|
|
4
|
+
export { S as SpanHandle, T as TraceEmitter, b as TraceEmitterOptions, l as llmSpanFromProvider } from './emitter-B2XqDKFU.js';
|
|
3
5
|
import { AxAIService, AxFunction } from '@ax-llm/ax';
|
|
4
6
|
|
|
5
7
|
/**
|
|
@@ -130,6 +132,206 @@ interface OtlpExport {
|
|
|
130
132
|
/** Export a single run's spans + events in OTLP/JSON. */
|
|
131
133
|
declare function exportRunAsOtlp(store: TraceStore, runId: string, resourceAttrs?: Record<string, string | number | boolean>): Promise<OtlpExport>;
|
|
132
134
|
|
|
135
|
+
/**
|
|
136
|
+
* RawProviderSink — first-class persistence for the actual HTTP-level
|
|
137
|
+
* request/response bodies of every LLM provider call.
|
|
138
|
+
*
|
|
139
|
+
* Why this is a separate sink from the structured `LlmSpan`:
|
|
140
|
+
*
|
|
141
|
+
* - `LlmSpan` records the *intent* — model name, messages, output text,
|
|
142
|
+
* usage. It's what dashboards read; it's NOT enough for forensics.
|
|
143
|
+
* - When a downstream consumer reports "the verifier used the wrong route"
|
|
144
|
+
* or "tokens look right but reasoning was missing," the only way to
|
|
145
|
+
* answer is the raw HTTP body. Span fields can lie (a proxy can echo
|
|
146
|
+
* a different `model` value than what actually answered); the raw
|
|
147
|
+
* response is ground truth.
|
|
148
|
+
*
|
|
149
|
+
* Default behaviour: opt-in. Pass `rawSink` to `LlmClientOptions` (or the
|
|
150
|
+
* matrix runner / BuilderSession sets it up automatically) and every
|
|
151
|
+
* request, response, and error is recorded — including retries, with the
|
|
152
|
+
* attempt index attached so a flaky call's full event chain is recoverable.
|
|
153
|
+
*
|
|
154
|
+
* Redaction is enforced at sink time. The default redactor strips
|
|
155
|
+
* `Authorization`, `X-Api-Key`, `X-Auth-Token`, `Cookie` headers and any
|
|
156
|
+
* payload field whose key matches `apiKey | api_key | bearer | password |
|
|
157
|
+
* secret | token` (case-insensitive). Override via the sink constructor or
|
|
158
|
+
* the per-call `redactor`. The `redactedFields` array on the persisted
|
|
159
|
+
* event lets a reviewer see what was stripped without exposing the values.
|
|
160
|
+
*/
|
|
161
|
+
type RawProviderDirection = 'request' | 'response' | 'error';
|
|
162
|
+
interface RawProviderEvent {
|
|
163
|
+
/** Stable id. Generated by the sink if omitted. */
|
|
164
|
+
eventId: string;
|
|
165
|
+
/** Trace context populated by `LlmClient` when the call is wrapped in a span. */
|
|
166
|
+
runId?: string;
|
|
167
|
+
spanId?: string;
|
|
168
|
+
/**
|
|
169
|
+
* Logical provider name. Free-form so callers can use whatever id matches
|
|
170
|
+
* their topology (`'openai'`, `'anthropic'`, `'tangle-router'`, …). When
|
|
171
|
+
* omitted, derived from `baseUrl` in `LlmClientOptions`.
|
|
172
|
+
*/
|
|
173
|
+
provider: string;
|
|
174
|
+
model: string;
|
|
175
|
+
/** Endpoint path, e.g. `'/v1/chat/completions'`. */
|
|
176
|
+
endpoint: string;
|
|
177
|
+
/** Base URL used for the call (already-normalised — no trailing slash). */
|
|
178
|
+
baseUrl: string;
|
|
179
|
+
/** 0-indexed retry attempt. The first attempt is 0; a retried call gets 1, 2, … */
|
|
180
|
+
attemptIndex: number;
|
|
181
|
+
direction: RawProviderDirection;
|
|
182
|
+
/** Unix ms. */
|
|
183
|
+
timestamp: number;
|
|
184
|
+
/** Wall-clock duration of the call leg. Set on `response` and `error` events; null on `request`. */
|
|
185
|
+
durationMs?: number;
|
|
186
|
+
statusCode?: number;
|
|
187
|
+
requestHeaders?: Record<string, string>;
|
|
188
|
+
requestBody?: unknown;
|
|
189
|
+
responseHeaders?: Record<string, string>;
|
|
190
|
+
responseBody?: unknown;
|
|
191
|
+
/** Set on `direction: 'error'` events. */
|
|
192
|
+
errorMessage?: string;
|
|
193
|
+
/** Field paths the redactor stripped from this event ('header:Authorization', 'body.apiKey', …). */
|
|
194
|
+
redactedFields: string[];
|
|
195
|
+
}
|
|
196
|
+
interface RawProviderSinkFilter {
|
|
197
|
+
runId?: string;
|
|
198
|
+
spanId?: string;
|
|
199
|
+
direction?: RawProviderDirection;
|
|
200
|
+
attemptIndex?: number;
|
|
201
|
+
}
|
|
202
|
+
interface RawProviderSink {
|
|
203
|
+
record(event: RawProviderEvent): Promise<void>;
|
|
204
|
+
/** Optional listing — implementations that durably persist (file, db) should support this. */
|
|
205
|
+
list?(filter?: RawProviderSinkFilter): Promise<RawProviderEvent[]>;
|
|
206
|
+
/** Optional teardown for backed implementations. */
|
|
207
|
+
close?(): Promise<void>;
|
|
208
|
+
}
|
|
209
|
+
type ProviderRedactor = (event: RawProviderEvent) => RawProviderEvent;
|
|
210
|
+
/**
|
|
211
|
+
* Default redactor — strips well-known auth headers and any body field whose
|
|
212
|
+
* key matches the credential pattern. Records every redacted path on
|
|
213
|
+
* `event.redactedFields` so a downstream reviewer can see what was removed.
|
|
214
|
+
*/
|
|
215
|
+
declare function defaultProviderRedactor(event: RawProviderEvent): RawProviderEvent;
|
|
216
|
+
interface InMemoryRawProviderSinkOptions {
|
|
217
|
+
redactor?: ProviderRedactor;
|
|
218
|
+
}
|
|
219
|
+
declare class InMemoryRawProviderSink implements RawProviderSink {
|
|
220
|
+
private events;
|
|
221
|
+
private redactor;
|
|
222
|
+
constructor(opts?: InMemoryRawProviderSinkOptions);
|
|
223
|
+
record(event: RawProviderEvent): Promise<void>;
|
|
224
|
+
list(filter?: RawProviderSinkFilter): Promise<RawProviderEvent[]>;
|
|
225
|
+
size(): number;
|
|
226
|
+
}
|
|
227
|
+
declare class NoopRawProviderSink implements RawProviderSink {
|
|
228
|
+
record(): Promise<void>;
|
|
229
|
+
}
|
|
230
|
+
interface FileSystemRawProviderSinkOptions {
|
|
231
|
+
/** Directory the NDJSON file is written into. Created if missing. */
|
|
232
|
+
dir: string;
|
|
233
|
+
/** File name; default `'raw-provider-events.ndjson'`. */
|
|
234
|
+
fileName?: string;
|
|
235
|
+
/** Bytes after which the writer rolls over to a new file (default 32 MiB). */
|
|
236
|
+
rollAtBytes?: number;
|
|
237
|
+
redactor?: ProviderRedactor;
|
|
238
|
+
}
|
|
239
|
+
declare class FileSystemRawProviderSink implements RawProviderSink {
|
|
240
|
+
private dir;
|
|
241
|
+
private fileName;
|
|
242
|
+
private rollAtBytes;
|
|
243
|
+
private redactor;
|
|
244
|
+
private bytesWritten;
|
|
245
|
+
private rollIndex;
|
|
246
|
+
private initPromise;
|
|
247
|
+
constructor(opts: FileSystemRawProviderSinkOptions);
|
|
248
|
+
private ensureInit;
|
|
249
|
+
private currentPath;
|
|
250
|
+
record(event: RawProviderEvent): Promise<void>;
|
|
251
|
+
list(filter?: RawProviderSinkFilter): Promise<RawProviderEvent[]>;
|
|
252
|
+
}
|
|
253
|
+
/**
|
|
254
|
+
* Best-effort provider id from a base URL. Falls back to the URL host when
|
|
255
|
+
* none of the well-known patterns match.
|
|
256
|
+
*/
|
|
257
|
+
declare function providerFromBaseUrl(baseUrl: string): string;
|
|
258
|
+
|
|
259
|
+
/**
|
|
260
|
+
* Run-completion integrity check — at end of run, verify the expected event
|
|
261
|
+
* types were actually captured. The point is the launch-review failure mode:
|
|
262
|
+
* a run *appears* successful but the raw provider events were never written,
|
|
263
|
+
* so a downstream reviewer can't reconstruct what happened.
|
|
264
|
+
*
|
|
265
|
+
* Pattern:
|
|
266
|
+
*
|
|
267
|
+
* const report = await assertRunCaptured(store, runId, {
|
|
268
|
+
* llmSpansMin: 1,
|
|
269
|
+
* judgeSpansMin: 1,
|
|
270
|
+
* rawSink: providerSink, // must have ≥ 1 event for this run
|
|
271
|
+
* requireRawCoverageOfLlmSpans: true, // every llm span has matching raw events
|
|
272
|
+
* })
|
|
273
|
+
* if (!report.ok) throwIfRunIncomplete(report) // or mark run failed and continue
|
|
274
|
+
*
|
|
275
|
+
* The function is read-only on the store and returns a structured report;
|
|
276
|
+
* the caller chooses the failure mode (throw, mark run failed, log warning).
|
|
277
|
+
* `throwIfRunIncomplete` is the convenient strict mode.
|
|
278
|
+
*/
|
|
279
|
+
|
|
280
|
+
interface RunIntegrityExpectations {
|
|
281
|
+
/** Minimum LLM span count. Default 0 (no requirement). */
|
|
282
|
+
llmSpansMin?: number;
|
|
283
|
+
/** Minimum judge span count. Default 0. */
|
|
284
|
+
judgeSpansMin?: number;
|
|
285
|
+
/** Minimum tool span count. Default 0. */
|
|
286
|
+
toolSpansMin?: number;
|
|
287
|
+
/**
|
|
288
|
+
* Raw provider sink to consult for capture verification. When present,
|
|
289
|
+
* the check requires at least one raw event for the run.
|
|
290
|
+
*/
|
|
291
|
+
rawSink?: RawProviderSink;
|
|
292
|
+
/** Minimum raw provider event count. Default 0; ignored when `rawSink` absent. */
|
|
293
|
+
rawProviderEventsMin?: number;
|
|
294
|
+
/**
|
|
295
|
+
* Every LLM span must have at least one matching raw `request` event
|
|
296
|
+
* (matched by spanId). Catches the common bug where the structured span
|
|
297
|
+
* was emitted but the raw HTTP capture was wired to a different sink.
|
|
298
|
+
*/
|
|
299
|
+
requireRawCoverageOfLlmSpans?: boolean;
|
|
300
|
+
/** Run outcome must be set (not null/undefined). Default false. */
|
|
301
|
+
requireOutcome?: boolean;
|
|
302
|
+
}
|
|
303
|
+
type RunIntegrityIssueCode = 'no_run' | 'missing_llm_spans' | 'missing_judge_spans' | 'missing_tool_spans' | 'missing_raw_events' | 'no_raw_sink' | 'orphan_llm_span' | 'missing_outcome';
|
|
304
|
+
interface RunIntegrityIssue {
|
|
305
|
+
code: RunIntegrityIssueCode;
|
|
306
|
+
message: string;
|
|
307
|
+
detail?: Record<string, unknown>;
|
|
308
|
+
}
|
|
309
|
+
interface RunIntegrityReport {
|
|
310
|
+
ok: boolean;
|
|
311
|
+
runId: string;
|
|
312
|
+
llmSpanCount: number;
|
|
313
|
+
judgeSpanCount: number;
|
|
314
|
+
toolSpanCount: number;
|
|
315
|
+
rawProviderEventCount: number;
|
|
316
|
+
/**
|
|
317
|
+
* Coverage of LLM spans by raw provider events keyed on spanId.
|
|
318
|
+
* `total` is the number of LLM spans; `covered` is the count with at
|
|
319
|
+
* least one matching `request` raw event.
|
|
320
|
+
*/
|
|
321
|
+
rawSpanCoverage: {
|
|
322
|
+
covered: number;
|
|
323
|
+
total: number;
|
|
324
|
+
};
|
|
325
|
+
issues: RunIntegrityIssue[];
|
|
326
|
+
}
|
|
327
|
+
declare class RunIntegrityError extends Error {
|
|
328
|
+
readonly report: RunIntegrityReport;
|
|
329
|
+
constructor(report: RunIntegrityReport);
|
|
330
|
+
}
|
|
331
|
+
declare function assertRunCaptured(store: TraceStore, runId: string, expectations?: RunIntegrityExpectations): Promise<RunIntegrityReport>;
|
|
332
|
+
/** Strict mode: throws `RunIntegrityError` when the report isn't ok. */
|
|
333
|
+
declare function throwIfRunIncomplete(report: RunIntegrityReport): void;
|
|
334
|
+
|
|
133
335
|
/**
|
|
134
336
|
* Shared types for the trace-analyst module.
|
|
135
337
|
*
|
|
@@ -578,6 +780,60 @@ declare function traceAnalystFunctionGroup(opts: BuildTraceAnalystToolsOpts): {
|
|
|
578
780
|
functions: AxFunction[];
|
|
579
781
|
};
|
|
580
782
|
|
|
783
|
+
/**
|
|
784
|
+
* Trace-analyst auto-execution hook.
|
|
785
|
+
*
|
|
786
|
+
* Wires `analyzeTraces` into a `TraceEmitter`'s `onRunComplete` so a
|
|
787
|
+
* direct matrix run produces an analysis artifact without an out-of-band
|
|
788
|
+
* step. Designed for the case where the consumer reports "the analyst
|
|
789
|
+
* never ran" — the cause is almost always orchestration, not the analyst.
|
|
790
|
+
*
|
|
791
|
+
* Usage:
|
|
792
|
+
*
|
|
793
|
+
* const emitter = new TraceEmitter(store, {
|
|
794
|
+
* onRunComplete: [traceAnalystOnRunComplete({ analyze: opts, save })],
|
|
795
|
+
* })
|
|
796
|
+
*
|
|
797
|
+
* Hooks are best-effort by default — they never crash the underlying run.
|
|
798
|
+
* The caller decides whether to gate the run on the analysis result via
|
|
799
|
+
* the `gateOn` callback.
|
|
800
|
+
*/
|
|
801
|
+
|
|
802
|
+
interface TraceAnalystHookOptions {
|
|
803
|
+
/**
|
|
804
|
+
* Options forwarded to `analyzeTraces`. The hook supplies the question
|
|
805
|
+
* if you don't pass one — defaulting to a launch-grade prompt that asks
|
|
806
|
+
* for failure modes, surprising findings, and a recommendation.
|
|
807
|
+
*/
|
|
808
|
+
analyze: Omit<AnalyzeTracesOptions, 'source'> & {
|
|
809
|
+
source?: AnalyzeTracesOptions['source'];
|
|
810
|
+
};
|
|
811
|
+
/**
|
|
812
|
+
* Override the question. The default is intentionally generic:
|
|
813
|
+
* "Summarise what happened in this run, surface any failure modes,
|
|
814
|
+
* surprising findings, or evidence the verdict is wrong."
|
|
815
|
+
*/
|
|
816
|
+
question?: string;
|
|
817
|
+
/**
|
|
818
|
+
* Persist the result. The hook calls this with the analysis output and
|
|
819
|
+
* the run context. Common implementations write to a TraceAnalysisStore
|
|
820
|
+
* or append to a per-run JSONL.
|
|
821
|
+
*/
|
|
822
|
+
save?: (result: AnalyzeTracesResult, ctx: RunCompleteHookContext) => Promise<void>;
|
|
823
|
+
/**
|
|
824
|
+
* Predicate gating execution per run. Default: every completed run.
|
|
825
|
+
* Use to skip aborted runs, debug runs, or runs without LLM activity.
|
|
826
|
+
*/
|
|
827
|
+
shouldRun?: (ctx: RunCompleteHookContext) => boolean;
|
|
828
|
+
/**
|
|
829
|
+
* Optional gate: if set and returns false, the hook records the failure
|
|
830
|
+
* as a log event on the run instead of staying quiet. The caller can
|
|
831
|
+
* then trigger downstream alerts off `analyst_gate_failed` log events.
|
|
832
|
+
*/
|
|
833
|
+
gateOn?: (result: AnalyzeTracesResult, ctx: RunCompleteHookContext) => boolean;
|
|
834
|
+
}
|
|
835
|
+
declare function traceAnalystOnRunComplete(opts: TraceAnalystHookOptions): RunCompleteHook;
|
|
836
|
+
|
|
581
837
|
/** Ax RLM prompt for bounded trace discovery and evidence-backed analysis. */
|
|
582
838
|
declare const TRACE_ANALYST_ACTOR_DESCRIPTION = "You answer questions about an OTLP-shaped JSONL trace dataset using the trace tools provided in the `traces` namespace.\n\nDISCOVERY \u2192 NARROW \u2192 DEEP-READ protocol \u2014 follow exactly:\n\n1. ALWAYS call `traces.getDatasetOverview({})` FIRST without a regex_pattern. The result tells you total_traces, raw_jsonl_bytes, services, agents, models, and sample_trace_ids (real ids \u2014 never fabricate one).\n\n2. Use raw_jsonl_bytes to gauge how expensive raw scans will be. `filters.regex_pattern` is the one scan-heavy filter on getDatasetOverview / queryTraces / countTraces \u2014 narrow with indexed fields (has_errors, model_names, service_names, agent_names, time bounds) BEFORE adding a regex on a large dataset.\n\n3. To list more traces than the sample, call `traces.queryTraces({ filters?, limit, offset? })`. Each summary carries raw_jsonl_bytes \u2014 use it to choose between viewTrace and searchTrace BEFORE calling either.\n\n4. Per-trace inspection:\n - SMALL trace (raw_jsonl_bytes well under 150_000): call `traces.viewTrace({ trace_id })`. Returns all spans. Per-attribute payloads are head-capped at ~4KB; large `input.value` / `output.value` / `llm.input_messages` will show a `[trace-analyst truncated: N bytes]` marker.\n - LARGE trace (raw_jsonl_bytes near or above 150_000, or you saw an `oversized` response): use `traces.searchTrace({ trace_id, regex_pattern })` to get bounded SpanMatchRecords (span metadata + matched text + surrounding context). Then call `traces.viewSpans({ trace_id, span_ids: [...] })` for surgical reads (~16KB cap, 4\u00D7 higher than discovery), or `traces.searchSpan({ trace_id, span_id, regex_pattern })` for one large span. Stays bounded regardless of trace size.\n - Useful regex patterns: `STATUS_CODE_ERROR` (failures), tool names like `grep` or `view_trace`, error strings like `MaxTurnsExceeded`, model names, attribute keys.\n\n5. ONLY call viewTrace / viewSpans / searchTrace / searchSpan with trace/span ids you have already seen in sample_trace_ids, a queryTraces page, or a previous search result. Never invent ids.\n\n5a. **Result-shape contract** \u2014 searchTrace and searchSpan return `{ trace_id, hits, total_matches, has_more }`. Iterate `result.hits` (NOT result.matches). Each hit has `{ span_id, span_name, span_kind, attribute_path, matched_text, context_before, context_after, match_offset }`. viewTrace returns `{ trace_id, spans }` (or `oversized`). viewSpans returns `{ trace_id, spans, missing_span_ids, truncated_attribute_count }`. Never assume a field name \u2014 log the result shape first if unsure.\n\n6. If viewTrace returns an `oversized` summary instead of `spans`, DO NOT retry the same call. Read the summary's top_span_names, span_count, span_response_bytes_max, error_span_count to plan a follow-up: switch to searchTrace (or searchSpan for one large span), then viewSpans on a smaller, surgical span_ids set.\n\n7. If searchTrace or searchSpan returns has_more=true, REFINE the regex to be more specific rather than blindly raising max_matches.\n\n8. If a tool errors (invalid regex, range error), STOP and reconsider \u2014 don't retry with a guessed id or argument. Use the discovery tools above to recover.\n\n9. If a ~4KB-truncated payload from viewTrace / searchTrace matters for your answer, first try viewSpans on that span id (~16KB cap). If a 16KB-truncated payload from viewSpans still matters, narrow further with searchSpan against a more specific regex rather than asking for the full payload again.\n\n10. If maxDepth > 0 and the question splits into independent semantic branches, delegate well-defined subtasks to subagents using `await llmQuery(...)`. Pass narrow context and a focused query. Examples:\n\n const reviews = await llmQuery([\n { query: 'Drill into trace abc123 \u2014 what tool calls preceded the failure?', context: { trace_id: 'abc123' } },\n { query: 'Drill into trace def456 \u2014 same failure mode?', context: { trace_id: 'def456' } },\n ]);\n\nOBSERVABILITY rules:\n- Each non-final actor turn must emit at least one `console.log(...)` for evidence. Up to 3 logs per turn is fine when correlating multiple data sources (e.g. one log for findings list, one for source-file content, one for derived analysis).\n- Do NOT combine `console.log` with `final(...)` or `askClarification(...)` in the same turn \u2014 finish gathering data first, then call final on its own turn.\n- Reuse runtime variables across turns; don't recompute.\n- When done, call `await final(answer)` with the fully-formed report. The responder rewrites the answer into output fields; if you only pass a vague summary string the responder has nothing concrete to format.\n\nCRITICAL \u2014 `final()` payload contract for evidence-grounded analysis tasks:\n- Pass a STRUCTURED object as the second arg with the actual data the responder needs to format the answer. Do NOT pass abstract instructions; pass evidence.\n- Example for per-item verdict tasks:\n ```js\n await final(\"Format the per-item verdict report from the evidence below.\", {\n findings: [\n { id: 'sub-1-finding-1', claim: '...', verdict: 'TRUE-POSITIVE', evidence: 'lines 42-45 of contracts/X.sol show ...' },\n ...all items\n ],\n systemic_summary: '3 sentences I wrote based on the evidence above'\n });\n ```\n- Calling `final(\"answer\", {})` with no evidence is a failure mode \u2014 the responder will hallucinate or echo back the field names. Always include the gathered data.\n- Premature final after a single viewSpans call is INSUFFICIENT for per-finding analysis tasks. Read the requested attributes (e.g. `spans[i].attributes['redteam.finding.title']`), and for each one perform the requested cross-reference (e.g. read the source SPAN's `attributes['source.content']`).\n\nOUTPUT contract \u2014 your final answer must include:\n- A clear prose conclusion answering the user's question.\n- Trace ids and span ids cited as evidence for each claim.\n- Failure modes named in the user's domain language, with frequency and concrete examples.\n\nDo NOT invent trace ids, span ids, error messages, or model names. Every fact must be traceable to a tool result.";
|
|
583
839
|
declare const TRACE_ANALYST_ACTOR_DESCRIPTION_VERSION = "trace-analyst-actor-v5-2026-05-06";
|
|
@@ -655,4 +911,4 @@ declare function scoreTraceInsightReadiness(context: TraceInsightContext): Trace
|
|
|
655
911
|
declare function defaultTraceInsightPanel(): TraceInsightPanelRole[];
|
|
656
912
|
declare function buildTraceInsightPrompt(input: TraceInsightPromptInput): string;
|
|
657
913
|
|
|
658
|
-
export { type AnalyzeTracesInput, type AnalyzeTracesOptions, type AnalyzeTracesResult, type AnalyzeTracesTurnSnapshot, DEFAULT_REDACTION_RULES, DEFAULT_TRACE_ANALYST_BUDGETS, type DatasetOverview, FailureClass, JudgeSpan, LlmSpan, OTEL_AGENT_EVAL_SCOPE, type OtlpExport, OtlpFileTraceStore, type OtlpFileTraceStoreOptions, type OtlpResourceSpans, type OtlpSpan, type QueryTracesPage, REDACTION_VERSION, type RedactionReport, type RedactionRule, Run, type SearchSpanResult, type SearchTraceResult, type SpanMatchRecord, SpanNotFoundError, TRACE_ANALYST_ACTOR_DESCRIPTION, TRACE_ANALYST_ACTOR_DESCRIPTION_VERSION, TRACE_ANALYST_SUBAGENT_DESCRIPTION, TRACE_ANALYST_TRUNCATION_MARKER_PREFIX, ToolSpan, type TraceAnalysisStore, type TraceAnalystByteBudgets, type TraceAnalystFilters, type TraceAnalystSpan, type TraceAnalystSpanKind, type TraceAnalystSpanStatus, type TraceAnalystTraceSummary, TraceFileMissingError, type TraceInsightContext, type TraceInsightFinding, type TraceInsightPanelRole, type TraceInsightPromptInput, type TraceInsightQualityGate, type TraceInsightQuestion, type TraceInsightReadiness, type TraceInsightSuite, type TraceInsightTask, TraceNotFoundError, TraceStore, type ViewSpansResult, type ViewTraceOversized, type ViewTraceResult, aggregateLlm, analyzeTraces, argHash, buildTraceAnalystTools, buildTraceInsightContext, buildTraceInsightPrompt, defaultTraceInsightPanel, describeTraceInsightScope, domainEvidencePattern, exportRunAsOtlp, groupBy, inferDomainKeywords, judgeSpans, llmSpans, planTraceInsightQuestions, redactString, redactValue, runFailureClass, runsForScenario, scoreTraceInsightReadiness, tokenizeDomainWords, toolSpans, traceAnalystFunctionGroup };
|
|
914
|
+
export { type AnalyzeTracesInput, type AnalyzeTracesOptions, type AnalyzeTracesResult, type AnalyzeTracesTurnSnapshot, DEFAULT_REDACTION_RULES, DEFAULT_TRACE_ANALYST_BUDGETS, type DatasetOverview, FailureClass, FileSystemRawProviderSink, type FileSystemRawProviderSinkOptions, InMemoryRawProviderSink, type InMemoryRawProviderSinkOptions, JudgeSpan, LlmSpan, NoopRawProviderSink, OTEL_AGENT_EVAL_SCOPE, type OtlpExport, OtlpFileTraceStore, type OtlpFileTraceStoreOptions, type OtlpResourceSpans, type OtlpSpan, type ProviderRedactor, type QueryTracesPage, REDACTION_VERSION, type RawProviderDirection, type RawProviderEvent, type RawProviderSink, type RawProviderSinkFilter, type RedactionReport, type RedactionRule, Run, RunCompleteHook, RunCompleteHookContext, RunIntegrityError, type RunIntegrityExpectations, type RunIntegrityIssue, type RunIntegrityIssueCode, type RunIntegrityReport, type SearchSpanResult, type SearchTraceResult, type SpanMatchRecord, SpanNotFoundError, TRACE_ANALYST_ACTOR_DESCRIPTION, TRACE_ANALYST_ACTOR_DESCRIPTION_VERSION, TRACE_ANALYST_SUBAGENT_DESCRIPTION, TRACE_ANALYST_TRUNCATION_MARKER_PREFIX, ToolSpan, type TraceAnalysisStore, type TraceAnalystByteBudgets, type TraceAnalystFilters, type TraceAnalystHookOptions, type TraceAnalystSpan, type TraceAnalystSpanKind, type TraceAnalystSpanStatus, type TraceAnalystTraceSummary, TraceFileMissingError, type TraceInsightContext, type TraceInsightFinding, type TraceInsightPanelRole, type TraceInsightPromptInput, type TraceInsightQualityGate, type TraceInsightQuestion, type TraceInsightReadiness, type TraceInsightSuite, type TraceInsightTask, TraceNotFoundError, TraceStore, type ViewSpansResult, type ViewTraceOversized, type ViewTraceResult, aggregateLlm, analyzeTraces, argHash, assertRunCaptured, buildTraceAnalystTools, buildTraceInsightContext, buildTraceInsightPrompt, defaultProviderRedactor, defaultTraceInsightPanel, describeTraceInsightScope, domainEvidencePattern, exportRunAsOtlp, groupBy, inferDomainKeywords, judgeSpans, llmSpans, planTraceInsightQuestions, providerFromBaseUrl, redactString, redactValue, runFailureClass, runsForScenario, scoreTraceInsightReadiness, throwIfRunIncomplete, tokenizeDomainWords, toolSpans, traceAnalystFunctionGroup, traceAnalystOnRunComplete };
|
package/dist/traces.js
CHANGED
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
OTEL_AGENT_EVAL_SCOPE,
|
|
8
8
|
OtlpFileTraceStore,
|
|
9
9
|
REDACTION_VERSION,
|
|
10
|
+
RunIntegrityError,
|
|
10
11
|
SpanNotFoundError,
|
|
11
12
|
TRACE_ANALYST_ACTOR_DESCRIPTION,
|
|
12
13
|
TRACE_ANALYST_ACTOR_DESCRIPTION_VERSION,
|
|
@@ -18,6 +19,7 @@ import {
|
|
|
18
19
|
aggregateLlm,
|
|
19
20
|
analyzeTraces,
|
|
20
21
|
argHash,
|
|
22
|
+
assertRunCaptured,
|
|
21
23
|
buildTraceAnalystTools,
|
|
22
24
|
buildTraceInsightContext,
|
|
23
25
|
buildTraceInsightPrompt,
|
|
@@ -40,24 +42,37 @@ import {
|
|
|
40
42
|
runFailureClass,
|
|
41
43
|
runsForScenario,
|
|
42
44
|
scoreTraceInsightReadiness,
|
|
45
|
+
throwIfRunIncomplete,
|
|
43
46
|
tokenizeDomainWords,
|
|
44
47
|
toolSpans,
|
|
45
|
-
traceAnalystFunctionGroup
|
|
46
|
-
|
|
48
|
+
traceAnalystFunctionGroup,
|
|
49
|
+
traceAnalystOnRunComplete
|
|
50
|
+
} from "./chunk-WOK2RTWG.js";
|
|
47
51
|
import {
|
|
48
52
|
TraceEmitter,
|
|
49
53
|
llmSpanFromProvider
|
|
50
|
-
} from "./chunk-
|
|
54
|
+
} from "./chunk-5IIQKMD5.js";
|
|
55
|
+
import {
|
|
56
|
+
FileSystemRawProviderSink,
|
|
57
|
+
InMemoryRawProviderSink,
|
|
58
|
+
NoopRawProviderSink,
|
|
59
|
+
defaultProviderRedactor,
|
|
60
|
+
providerFromBaseUrl
|
|
61
|
+
} from "./chunk-SNUHRBDL.js";
|
|
51
62
|
import "./chunk-PZ5AY32C.js";
|
|
52
63
|
export {
|
|
53
64
|
DEFAULT_REDACTION_RULES,
|
|
54
65
|
DEFAULT_TRACE_ANALYST_BUDGETS,
|
|
55
66
|
FAILURE_CLASSES,
|
|
67
|
+
FileSystemRawProviderSink,
|
|
56
68
|
FileSystemTraceStore,
|
|
69
|
+
InMemoryRawProviderSink,
|
|
57
70
|
InMemoryTraceStore,
|
|
71
|
+
NoopRawProviderSink,
|
|
58
72
|
OTEL_AGENT_EVAL_SCOPE,
|
|
59
73
|
OtlpFileTraceStore,
|
|
60
74
|
REDACTION_VERSION,
|
|
75
|
+
RunIntegrityError,
|
|
61
76
|
SpanNotFoundError,
|
|
62
77
|
TRACE_ANALYST_ACTOR_DESCRIPTION,
|
|
63
78
|
TRACE_ANALYST_ACTOR_DESCRIPTION_VERSION,
|
|
@@ -70,9 +85,11 @@ export {
|
|
|
70
85
|
aggregateLlm,
|
|
71
86
|
analyzeTraces,
|
|
72
87
|
argHash,
|
|
88
|
+
assertRunCaptured,
|
|
73
89
|
buildTraceAnalystTools,
|
|
74
90
|
buildTraceInsightContext,
|
|
75
91
|
buildTraceInsightPrompt,
|
|
92
|
+
defaultProviderRedactor,
|
|
76
93
|
defaultTraceInsightPanel,
|
|
77
94
|
describeTraceInsightScope,
|
|
78
95
|
domainEvidencePattern,
|
|
@@ -88,13 +105,16 @@ export {
|
|
|
88
105
|
llmSpanFromProvider,
|
|
89
106
|
llmSpans,
|
|
90
107
|
planTraceInsightQuestions,
|
|
108
|
+
providerFromBaseUrl,
|
|
91
109
|
redactString,
|
|
92
110
|
redactValue,
|
|
93
111
|
runFailureClass,
|
|
94
112
|
runsForScenario,
|
|
95
113
|
scoreTraceInsightReadiness,
|
|
114
|
+
throwIfRunIncomplete,
|
|
96
115
|
tokenizeDomainWords,
|
|
97
116
|
toolSpans,
|
|
98
|
-
traceAnalystFunctionGroup
|
|
117
|
+
traceAnalystFunctionGroup,
|
|
118
|
+
traceAnalystOnRunComplete
|
|
99
119
|
};
|
|
100
120
|
//# sourceMappingURL=traces.js.map
|
package/dist/wire/index.js
CHANGED
|
@@ -24,8 +24,9 @@ import {
|
|
|
24
24
|
runRpcBatch,
|
|
25
25
|
runRpcOnce,
|
|
26
26
|
startServer
|
|
27
|
-
} from "../chunk-
|
|
28
|
-
import "../chunk-
|
|
27
|
+
} from "../chunk-WOPGKVN4.js";
|
|
28
|
+
import "../chunk-3GN6U53I.js";
|
|
29
|
+
import "../chunk-SNUHRBDL.js";
|
|
29
30
|
import "../chunk-PZ5AY32C.js";
|
|
30
31
|
export {
|
|
31
32
|
BUILTIN_RUBRICS,
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
# researchReport — methodology
|
|
2
|
+
|
|
3
|
+
This document is the methodological brief for `researchReport` (exported from
|
|
4
|
+
`@tangle-network/agent-eval` and `@tangle-network/agent-eval/reporting`). It
|
|
5
|
+
exists so a launch reviewer, peer reviewer, or auditor can quickly verify that
|
|
6
|
+
the verdict embedded in any rendered report is defensible, reproducible, and
|
|
7
|
+
appropriate to the data.
|
|
8
|
+
|
|
9
|
+
The companion code is `src/summary-report.ts`. Each item below names the
|
|
10
|
+
corresponding function or option so the doc and the code don't drift.
|
|
11
|
+
|
|
12
|
+
## Inputs
|
|
13
|
+
|
|
14
|
+
- `runs: RunRecord[]` — every record carries `runId`, `candidateId`, `seed`,
|
|
15
|
+
`experimentId`, `splitTag`, and an `outcome` with the configured score.
|
|
16
|
+
- `comparator: string` — the candidate id treated as the null reference. Must
|
|
17
|
+
be selected before data inspection; `preregistrationHash` should pin this.
|
|
18
|
+
- `split: 'search' | 'holdout'` — defaults to `holdout`. Decisions on `search`
|
|
19
|
+
are descriptive only; promotion calls require the holdout.
|
|
20
|
+
- `rope: { low, high }` — Region of Practical Equivalence on the paired delta,
|
|
21
|
+
in score units. Must come from the domain owner — there is no
|
|
22
|
+
statistically-defensible default.
|
|
23
|
+
- `minPairs` (soft floor, default 20) and `RESEARCH_REPORT_HARD_PAIR_FLOOR`
|
|
24
|
+
(hard floor, 6). Below the soft floor, the verdict is `needs_more_data` and
|
|
25
|
+
the report carries the MDE at the current N.
|
|
26
|
+
- `fdr` (default 0.05), `confidence` (default 0.95), `mdePower` (default 0.8),
|
|
27
|
+
`mdeAlpha` (default = `fdr`).
|
|
28
|
+
|
|
29
|
+
## Pairing
|
|
30
|
+
|
|
31
|
+
Pairs are joined by `(experimentId, seed)` so the comparator and candidate
|
|
32
|
+
share scenario *and* seed. This is the same join `gainHistogram` uses; see
|
|
33
|
+
`pairScoresByKey` in `src/summary-report.ts`. Records on the wrong split or
|
|
34
|
+
with non-finite scores are dropped before pairing.
|
|
35
|
+
|
|
36
|
+
## Decision rule
|
|
37
|
+
|
|
38
|
+
In order — first match wins:
|
|
39
|
+
|
|
40
|
+
1. `comparator` itself → `hold` (baseline).
|
|
41
|
+
2. No comparator → `hold` if on the cost/quality Pareto frontier, else
|
|
42
|
+
`needs_more_data`. The verdict is descriptive, not causal.
|
|
43
|
+
3. Held-out gate verdict ≠ `promote` → `reject`. The gate is *necessary but
|
|
44
|
+
not sufficient*; even a `promote` gate must clear the paired test below.
|
|
45
|
+
4. Paired N < `RESEARCH_REPORT_HARD_PAIR_FLOOR` → `needs_more_data` with a
|
|
46
|
+
"below hard floor" reason. Bootstrap CIs degenerate at this size.
|
|
47
|
+
5. ROPE configured AND paired-delta CI ⊂ ROPE → `equivalent`.
|
|
48
|
+
6. Paired-delta CI upper bound < 0 → `reject` (CI excludes a non-negative
|
|
49
|
+
effect). Note: this uses **paired delta only** — not the marginal mean.
|
|
50
|
+
7. Paired N < `minPairs` (soft floor) → `needs_more_data` with the MDE at
|
|
51
|
+
current N attached so the verdict is actionable.
|
|
52
|
+
8. BH-adjusted q ≤ `fdr` AND CI lower bound > 0 → `promote`. The BH q-value
|
|
53
|
+
controls FDR across all candidates in the same sweep; the bootstrap CI
|
|
54
|
+
provides an effect-size guarantee independent of the test.
|
|
55
|
+
9. Otherwise → `hold`.
|
|
56
|
+
|
|
57
|
+
## Statistical primitives used
|
|
58
|
+
|
|
59
|
+
| Quantity | Function | Source file |
|
|
60
|
+
|---|---|---|
|
|
61
|
+
| Marginal CI on score mean | `confidenceInterval` | `statistics.ts` |
|
|
62
|
+
| Cohen's d vs comparator | `cohensD` | `statistics.ts` |
|
|
63
|
+
| Wilcoxon signed-rank (paired) | `wilcoxonSignedRank` | `statistics.ts` |
|
|
64
|
+
| BH-FDR q-values | `benjaminiHochberg` | `power-analysis.ts` |
|
|
65
|
+
| Paired bootstrap CI on median delta | `pairedBootstrap` | `paired-stats.ts` |
|
|
66
|
+
| Bayesian-bootstrap-style Pr(Δ>0), Pr(Δ∈ROPE) | `bootstrapMeanSamples` | `summary-report.ts` (private) |
|
|
67
|
+
| Minimum detectable paired effect | `pairedMde` | `power-analysis.ts` |
|
|
68
|
+
| Run fingerprint | `hashJson(canonicalize(...))` | `pre-registration.ts` |
|
|
69
|
+
|
|
70
|
+
The Pr(Δ>0) and Pr(Δ∈ROPE) summaries use the bootstrap-prior duality of
|
|
71
|
+
[Rubin 1981]: under a non-informative Dirichlet prior, the bootstrap
|
|
72
|
+
distribution of a sample statistic is its posterior. We expose these as
|
|
73
|
+
posterior summaries on the **mean** delta and the bootstrap CI on the
|
|
74
|
+
**median** delta — the median is more robust to the heavy-tailed score
|
|
75
|
+
distributions seen in agent benchmarks; the mean lets us read off the
|
|
76
|
+
Bayesian-style probability of superiority in a single number.
|
|
77
|
+
|
|
78
|
+
## MDE
|
|
79
|
+
|
|
80
|
+
The minimum detectable paired effect at N pairs, two-sided α, and power β:
|
|
81
|
+
|
|
82
|
+
$$d_\text{min} = \frac{z_{1-\alpha/2} + z_\beta}{\sqrt{n}}$$
|
|
83
|
+
|
|
84
|
+
reported on the standardised scale, then multiplied by the observed paired-
|
|
85
|
+
delta SD to get the MDE in score units. Consumers reading a `needs_more_data`
|
|
86
|
+
verdict can use the MDE to budget the next round of runs:
|
|
87
|
+
|
|
88
|
+
- Observed paired SD = 0.10 score units, paired N = 20, α = 0.05, β = 0.8 →
|
|
89
|
+
d_min ≈ 0.63 standardised → MDE ≈ 0.063 score units. If the smallest
|
|
90
|
+
effect that would change a launch decision is below this, run more pairs.
|
|
91
|
+
|
|
92
|
+
## Provenance
|
|
93
|
+
|
|
94
|
+
Every report carries:
|
|
95
|
+
|
|
96
|
+
- `runFingerprint`: SHA-256 over the canonicalised list of
|
|
97
|
+
`(runId, candidateId, splitTag)` triples (sorted by runId), plus the
|
|
98
|
+
comparator id and split. Same `(runs, comparator, split)` produces the same
|
|
99
|
+
fingerprint regardless of input order.
|
|
100
|
+
- `preregistrationHash`: the caller passes the hash of a signed
|
|
101
|
+
`HypothesisManifest` (see `pre-registration.ts`). The fingerprint and the
|
|
102
|
+
preregistration hash together let a reader verify both *what data the
|
|
103
|
+
report saw* and *what protocol it was supposed to run.*
|
|
104
|
+
|
|
105
|
+
Reports without a `preregistrationHash` carry a "post-hoc" warning in the
|
|
106
|
+
risks list and the executive summary. Treat them as descriptive only.
|
|
107
|
+
|
|
108
|
+
## Alternatives considered
|
|
109
|
+
|
|
110
|
+
- **Paired t-test instead of Wilcoxon + bootstrap.** Rejected: agent score
|
|
111
|
+
distributions are heavy-tailed (judges saturate near 0 and 1) and the t
|
|
112
|
+
approximation breaks down with the small N typical of holdouts.
|
|
113
|
+
- **Unpaired Mann–Whitney.** Rejected: matched scenarios make pairing free,
|
|
114
|
+
and unpaired tests throw away the variance reduction. Use the paired test
|
|
115
|
+
by default.
|
|
116
|
+
- **Sequential / always-valid inference (e-values, mSPRT, alpha-spending).**
|
|
117
|
+
Out of scope for a single-look report. If users iterate, wrap this report
|
|
118
|
+
in an alpha-spending schedule, or commit to one preregistered look.
|
|
119
|
+
- **Hierarchical Bayesian shrinkage across many candidates.** Future work.
|
|
120
|
+
The current ranking is on raw paired statistics and over-credits the top
|
|
121
|
+
candidate when many are tested.
|
|
122
|
+
- **Calibration / coverage simulation on the bootstrap CI.** Future work; we
|
|
123
|
+
rely on the asymptotic guarantee plus the hard pair floor to keep coverage
|
|
124
|
+
reasonable.
|
|
125
|
+
|
|
126
|
+
## When NOT to apply
|
|
127
|
+
|
|
128
|
+
- Paired N below the hard floor (6) on any candidate.
|
|
129
|
+
- Comparator chosen by inspecting the data (post-hoc selection inflates
|
|
130
|
+
false-discovery rates beyond the BH guarantee).
|
|
131
|
+
- Mid-run distribution shift: judge model swap, rubric change, infrastructure
|
|
132
|
+
outage. Pair exchangeability is violated and the bootstrap is not valid.
|
|
133
|
+
- Scenarios drawn non-randomly from a stream the candidate can influence
|
|
134
|
+
(data-leak across runs). The pairing is no longer ignorable.
|
|
135
|
+
- Highly skewed cost distributions: the Pareto frontier still works but the
|
|
136
|
+
marginal CI on cost may be misleading.
|
|
137
|
+
|
|
138
|
+
## Citations
|
|
139
|
+
|
|
140
|
+
- Benjamini, Y. & Hochberg, Y. (1995). Controlling the false discovery rate:
|
|
141
|
+
a practical and powerful approach to multiple testing. *JRSS B*,
|
|
142
|
+
57(1), 289–300.
|
|
143
|
+
- Wilcoxon, F. (1945). Individual comparisons by ranking methods.
|
|
144
|
+
*Biometrics Bulletin*, 1(6), 80–83.
|
|
145
|
+
- Efron, B. (1979). Bootstrap methods: another look at the jackknife.
|
|
146
|
+
*Annals of Statistics*, 7(1), 1–26.
|
|
147
|
+
- Rubin, D. B. (1981). The Bayesian bootstrap.
|
|
148
|
+
*Annals of Statistics*, 9(1), 130–134.
|
|
149
|
+
- Kruschke, J. K. (2018). Rejecting or accepting parameter values in
|
|
150
|
+
Bayesian estimation. *Advances in Methods and Practices in
|
|
151
|
+
Psychological Science*, 1(2), 270–280. (ROPE.)
|
|
152
|
+
- Howard, S. R., Ramdas, A., McAuliffe, J., Sekhon, J. (2021).
|
|
153
|
+
Time-uniform, nonparametric, nonasymptotic confidence sequences.
|
|
154
|
+
*Annals of Statistics*, 49(2), 1055–1080. (Background reading on
|
|
155
|
+
always-valid inference for sequential extensions.)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tangle-network/agent-eval",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.21.0",
|
|
4
4
|
"description": "Trace-first evaluation infrastructure for agent systems: traces, harnesses, verifier pipelines, judges, datasets, gates, optimization, and reporting.",
|
|
5
5
|
"homepage": "https://github.com/tangle-network/agent-eval#readme",
|
|
6
6
|
"repository": {
|
|
@@ -74,15 +74,6 @@
|
|
|
74
74
|
"publishConfig": {
|
|
75
75
|
"access": "public"
|
|
76
76
|
},
|
|
77
|
-
"scripts": {
|
|
78
|
-
"build": "tsup && pnpm openapi",
|
|
79
|
-
"dev": "tsup --watch",
|
|
80
|
-
"prepare": "pnpm build",
|
|
81
|
-
"test": "vitest run",
|
|
82
|
-
"test:watch": "vitest",
|
|
83
|
-
"typecheck": "tsc --noEmit",
|
|
84
|
-
"openapi": "node dist/cli.js openapi --out dist/openapi.json"
|
|
85
|
-
},
|
|
86
77
|
"dependencies": {
|
|
87
78
|
"@asteasolutions/zod-to-openapi": "^8.5.0",
|
|
88
79
|
"@ax-llm/ax": "^19.0.25",
|
|
@@ -102,5 +93,12 @@
|
|
|
102
93
|
"node": ">=20"
|
|
103
94
|
},
|
|
104
95
|
"license": "MIT",
|
|
105
|
-
"
|
|
106
|
-
|
|
96
|
+
"scripts": {
|
|
97
|
+
"build": "tsup && pnpm openapi",
|
|
98
|
+
"dev": "tsup --watch",
|
|
99
|
+
"test": "vitest run",
|
|
100
|
+
"test:watch": "vitest",
|
|
101
|
+
"typecheck": "tsc --noEmit",
|
|
102
|
+
"openapi": "node dist/cli.js openapi --out dist/openapi.json"
|
|
103
|
+
}
|
|
104
|
+
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/llm-client.ts"],"sourcesContent":["/**\n * LLM client with graceful degrade.\n *\n * OpenAI-compatible `/v1/chat/completions` client with:\n * - Exponential-backoff retry on 429 + 5xx gateway errors (502/503/504).\n * - Retry on transient network errors (fetch failed, AbortError, ECONNRESET).\n * - Graceful json_schema → json_object degrade on 400 with schema-reject body.\n * - Fenced-JSON stripping (```json ... ```) for models that wrap structured output.\n * - Configurable base URL + api key / bearer, works with LiteLLM proxies, OpenAI\n * directly, cli-bridge subscriptions, and any router that speaks the spec.\n *\n * Usage:\n * const { value, result } = await callLlmJson<MyType>(\n * { model: 'gpt-4o', messages: [...], jsonSchema: { name: 'x', schema: {...} } },\n * { baseUrl: 'https://router.tangle.tools/v1', apiKey: process.env.KEY },\n * )\n *\n * This is THE llm-calling seam for agent-eval primitives that need structured\n * output (semantic concept judge, reviewer directives, critic scores). Primitives\n * that need free-form text use `callLlm` and parse output themselves.\n */\n\n// ─── Types ──────────────────────────────────────────────────────────────\n\nexport interface LlmMessage {\n role: 'system' | 'user' | 'assistant'\n /**\n * Either a plain text content string OR a multimodal content array\n * (text + image_url parts) for vision-capable models.\n */\n content:\n | string\n | Array<\n | { type: 'text'; text: string }\n | { type: 'image_url'; image_url: { url: string; detail?: 'auto' | 'low' | 'high' } }\n >\n}\n\nexport interface LlmCallRequest {\n model: string\n messages: LlmMessage[]\n /** Optional JSON-mode response format (response_format: json_object). */\n jsonMode?: boolean\n /** Optional structured output via JSON Schema. Falls back to json_object on 400. */\n jsonSchema?: { name: string; schema: Record<string, unknown> }\n temperature?: number\n maxTokens?: number\n /** Per-call timeout, default 60s. */\n timeoutMs?: number\n}\n\nexport interface LlmUsage {\n promptTokens: number\n completionTokens: number\n totalTokens: number\n /** Proxies populate this when prompt caching is on. */\n cachedPromptTokens?: number\n}\n\nexport interface LlmCallResult {\n /** The text content of the first choice. Empty string if none. */\n content: string\n usage: LlmUsage\n /**\n * Cost in USD. Pulled from proxy's `_response_cost` field when present;\n * `null` when neither the proxy nor the caller can derive it.\n */\n costUsd: number | null\n /** Model name actually used (echoed from response). */\n model: string\n /** Wall-clock duration of the HTTP call (last attempt, if retried). */\n durationMs: number\n /** Raw response body. */\n raw: Record<string, unknown>\n}\n\nexport class LlmCallError extends Error {\n constructor(\n message: string,\n public readonly status: number,\n public readonly body: string,\n public readonly model: string,\n ) {\n super(message)\n this.name = 'LlmCallError'\n }\n}\n\nexport interface LlmClientOptions {\n /** Base URL (without trailing slash). Must end at the `/v1` prefix. */\n baseUrl?: string\n /** Bearer token — either `apiKey` or `bearer` populates `Authorization: Bearer ...`. */\n apiKey?: string\n bearer?: string\n /** Override for the `Authorization` header (e.g. `X-Auth: ...`). Takes precedence over apiKey/bearer. */\n authHeader?: { name: string; value: string }\n /** Default timeout in ms. Per-call can override. */\n defaultTimeoutMs?: number\n /** Max retry attempts on retriable errors. Default 3 (1 initial + 2 retries). */\n maxRetries?: number\n /** Fetch implementation — defaults to global `fetch`. Override for custom transport (e.g. tests). */\n fetch?: typeof fetch\n}\n\n// ─── Internals ──────────────────────────────────────────────────────────\n\nconst DEFAULT_BASE_URL = 'https://router.tangle.tools/v1'\nconst DEFAULT_TIMEOUT_MS = 60_000\nconst DEFAULT_MAX_RETRIES = 3\n\nconst RETRYABLE_STATUS = new Set([429, 502, 503, 504])\n\nfunction isRetryableError(err: unknown): boolean {\n if (err instanceof LlmCallError) return RETRYABLE_STATUS.has(err.status)\n if (err instanceof Error) {\n return (\n err.name === 'AbortError' ||\n err.name === 'TimeoutError' ||\n /fetch failed|ECONNRESET|ETIMEDOUT|EAI_AGAIN/i.test(err.message)\n )\n }\n return false\n}\n\nfunction parseRetryAfter(headers: Headers): number | null {\n const h = headers.get('retry-after')\n if (!h) return null\n const asNumber = Number(h)\n if (Number.isFinite(asNumber) && asNumber > 0) return asNumber * 1000\n const asDate = Date.parse(h)\n if (Number.isFinite(asDate)) return Math.max(0, asDate - Date.now())\n return null\n}\n\nfunction backoffMs(attempt: number): number {\n // 500ms, 1s, 2s, 4s, ...\n return Math.min(500 * Math.pow(2, attempt), 16_000)\n}\n\nfunction buildHeaders(opts: LlmClientOptions): Record<string, string> {\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n Accept: 'application/json',\n }\n if (opts.authHeader) {\n headers[opts.authHeader.name] = opts.authHeader.value\n } else if (opts.bearer || opts.apiKey) {\n headers.Authorization = `Bearer ${opts.bearer ?? opts.apiKey}`\n }\n return headers\n}\n\nfunction isSchemaRejection(status: number, body: string): boolean {\n if (status !== 400) return false\n const lower = body.toLowerCase()\n return (\n lower.includes('response_format') ||\n lower.includes('json_schema') ||\n lower.includes('is unavailable') ||\n lower.includes('not supported')\n )\n}\n\nfunction buildBody(req: LlmCallRequest, forceJsonObject: boolean): Record<string, unknown> {\n const body: Record<string, unknown> = {\n model: req.model,\n messages: req.messages,\n temperature: req.temperature ?? 0,\n }\n if (req.maxTokens != null) {\n if (usesMaxCompletionTokens(req.model)) body.max_completion_tokens = req.maxTokens\n else body.max_tokens = req.maxTokens\n }\n\n if (req.jsonSchema && !forceJsonObject) {\n body.response_format = {\n type: 'json_schema',\n json_schema: { name: req.jsonSchema.name, schema: req.jsonSchema.schema, strict: true },\n }\n } else if (req.jsonMode || req.jsonSchema) {\n body.response_format = { type: 'json_object' }\n }\n\n return body\n}\n\nfunction usesMaxCompletionTokens(model: string): boolean {\n return /^gpt-5(?:[.\\-]|$)/i.test(model)\n}\n\nasync function sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms))\n}\n\n// ─── Public API ─────────────────────────────────────────────────────────\n\n/**\n * Strip a ```json / ``` code fence if the model emitted one.\n * Idempotent for naked JSON. Some models (claude-code via router, certain\n * deepseek models) wrap output even under json_object.\n */\nexport function stripFencedJson(raw: string): string {\n const trimmed = raw.trim()\n const m = trimmed.match(/^```(?:json)?\\s*\\n?([\\s\\S]*?)\\n?```\\s*$/)\n return m ? m[1]!.trim() : trimmed\n}\n\nexport function extractJsonPayload(raw: string): string {\n const stripped = stripFencedJson(raw)\n try {\n JSON.parse(stripped)\n return stripped\n } catch {\n // Continue with balanced extraction below.\n }\n\n const starts = [...stripped.matchAll(/[\\[{]/g)].map((match) => match.index).filter((index) => index != null)\n for (const start of starts) {\n const candidate = extractBalancedJson(stripped, start)\n if (!candidate) continue\n try {\n JSON.parse(candidate)\n return candidate\n } catch {\n // Keep scanning; earlier braces may belong to prose.\n }\n }\n\n return stripped\n}\n\nfunction extractBalancedJson(input: string, start: number): string | null {\n const opener = input[start]\n const closer = opener === '{' ? '}' : opener === '[' ? ']' : null\n if (!closer) return null\n\n const stack: string[] = [closer]\n let isInString = false\n let isEscaped = false\n\n for (let i = start + 1; i < input.length; i++) {\n const char = input[i]!\n if (isEscaped) {\n isEscaped = false\n continue\n }\n if (char === '\\\\') {\n isEscaped = isInString\n continue\n }\n if (char === '\"') {\n isInString = !isInString\n continue\n }\n if (isInString) continue\n\n if (char === '{') stack.push('}')\n else if (char === '[') stack.push(']')\n else if (char === stack[stack.length - 1]) {\n stack.pop()\n if (stack.length === 0) return input.slice(start, i + 1)\n }\n }\n\n return null\n}\n\n/**\n * Low-level call. Returns raw content + usage + cost. Retries on transient\n * failures; does NOT degrade schema here — callers that want graceful\n * degrade use `callLlmJson`.\n */\nexport async function callLlm(\n req: LlmCallRequest,\n opts: LlmClientOptions = {},\n): Promise<LlmCallResult> {\n const baseUrl = (opts.baseUrl ?? DEFAULT_BASE_URL).replace(/\\/+$/, '')\n const url = `${baseUrl}/chat/completions`\n const timeoutMs = req.timeoutMs ?? opts.defaultTimeoutMs ?? DEFAULT_TIMEOUT_MS\n const maxRetries = opts.maxRetries ?? DEFAULT_MAX_RETRIES\n const fetchFn = opts.fetch ?? globalThis.fetch\n const headers = buildHeaders(opts)\n\n let lastErr: unknown\n for (let attempt = 0; attempt < maxRetries; attempt++) {\n const controller = new AbortController()\n const timeoutHandle = setTimeout(() => controller.abort(), timeoutMs)\n const started = Date.now()\n\n try {\n const res = await fetchFn(url, {\n method: 'POST',\n headers,\n body: JSON.stringify(buildBody(req, false)),\n signal: controller.signal,\n })\n clearTimeout(timeoutHandle)\n\n if (!res.ok) {\n const body = await res.text()\n const err = new LlmCallError(\n `LLM call ${res.status}: ${body.slice(0, 300)}`,\n res.status,\n body,\n req.model,\n )\n if (RETRYABLE_STATUS.has(res.status) && attempt < maxRetries - 1) {\n lastErr = err\n const retryAfter = parseRetryAfter(res.headers)\n await sleep(retryAfter ?? backoffMs(attempt))\n continue\n }\n throw err\n }\n\n const json = (await res.json()) as Record<string, unknown>\n const choice = (json.choices as Array<{ message?: { content?: string } }> | undefined)?.[0]\n const usageRaw = (json.usage as Record<string, unknown> | undefined) ?? {}\n const costFromProxy = (json._response_cost ?? json.cost_usd) as number | undefined\n\n return {\n content: choice?.message?.content ?? '',\n usage: {\n promptTokens: Number(usageRaw.prompt_tokens ?? 0),\n completionTokens: Number(usageRaw.completion_tokens ?? 0),\n totalTokens: Number(usageRaw.total_tokens ?? 0),\n cachedPromptTokens:\n usageRaw.prompt_tokens_details &&\n typeof usageRaw.prompt_tokens_details === 'object'\n ? Number(\n (usageRaw.prompt_tokens_details as Record<string, unknown>).cached_tokens ?? 0,\n )\n : undefined,\n },\n costUsd: typeof costFromProxy === 'number' ? costFromProxy : null,\n model: (json.model as string) ?? req.model,\n durationMs: Date.now() - started,\n raw: json,\n }\n } catch (err) {\n clearTimeout(timeoutHandle)\n lastErr = err\n if (attempt < maxRetries - 1 && isRetryableError(err)) {\n await sleep(backoffMs(attempt))\n continue\n }\n throw err\n }\n }\n throw lastErr instanceof Error ? lastErr : new Error(String(lastErr))\n}\n\n/**\n * Structured-output call. Returns parsed JSON plus the raw result envelope.\n * Degrades `jsonSchema` → `jsonMode` on a 400 that names the schema param —\n * critical for deepseek-v3/v4, kimi-k2.6, and other models that don't accept\n * the `response_format.json_schema` shape but DO accept `json_object`.\n */\nexport async function callLlmJson<T = unknown>(\n req: LlmCallRequest,\n opts: LlmClientOptions = {},\n): Promise<{ value: T; result: LlmCallResult }> {\n try {\n const result = await callLlm({ ...req, jsonMode: req.jsonMode ?? !req.jsonSchema }, opts)\n const value = parseJsonSafely<T>(result.content, result.model)\n return { value, result }\n } catch (err) {\n if (err instanceof LlmCallError && isSchemaRejection(err.status, err.body) && req.jsonSchema) {\n // Degrade to json_object + retry.\n const degradedReq: LlmCallRequest = { ...req, jsonMode: true, jsonSchema: undefined }\n const result = await callLlm(degradedReq, opts)\n const value = parseJsonSafely<T>(result.content, result.model)\n return { value, result }\n }\n throw err\n }\n}\n\nfunction parseJsonSafely<T>(content: string, model: string): T {\n const stripped = extractJsonPayload(content)\n try {\n return JSON.parse(stripped) as T\n } catch (err) {\n throw new Error(\n `LLM returned non-JSON content (model=${model}): ${\n err instanceof Error ? err.message : String(err)\n }\\n--- raw content ---\\n${content.slice(0, 800)}`,\n )\n }\n}\n\n/**\n * Probe whether a model is reachable. Returns latency + null error on\n * success; `ok=false` + error message on any failure (HTTP, timeout,\n * network, parse). Designed for sweep preflights — fail loud at the\n * boundary before burning a 30-leaf run on a misconfigured router.\n *\n * Sends a tiny `ping` message with `maxTokens=64`. Reasoning models\n * (glm-5.1, deepseek-v4) can burn the entire budget on internal reasoning\n * for short prompts, so don't tighten this further. We don't validate\n * content; HTTP 200 means reachable.\n */\nexport async function probeLlm(\n model: string,\n opts: LlmClientOptions & { timeoutMs?: number } = {},\n): Promise<{ ok: boolean; latencyMs: number; error: string | null }> {\n const start = Date.now()\n try {\n await callLlm(\n {\n model,\n messages: [{ role: 'user', content: 'ping' }],\n maxTokens: 64,\n timeoutMs: opts.timeoutMs ?? 30_000,\n },\n opts,\n )\n return { ok: true, latencyMs: Date.now() - start, error: null }\n } catch (err) {\n return {\n ok: false,\n latencyMs: Date.now() - start,\n error: err instanceof Error ? err.message : String(err),\n }\n }\n}\n\n/**\n * Stateful client — construct once with defaults, call many times.\n * Thin wrapper around the free functions; exists for callers that want\n * to inject a single configured instance into multiple primitives.\n */\nexport class LlmClient {\n constructor(private readonly opts: LlmClientOptions = {}) {}\n\n call(req: LlmCallRequest, per?: LlmClientOptions): Promise<LlmCallResult> {\n return callLlm(req, { ...this.opts, ...per })\n }\n\n callJson<T = unknown>(\n req: LlmCallRequest,\n per?: LlmClientOptions,\n ): Promise<{ value: T; result: LlmCallResult }> {\n return callLlmJson<T>(req, { ...this.opts, ...per })\n }\n}\n"],"mappings":"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","names":[]}
|