@lanefoundry/gatelane-engine 0.0.0-canary-b3f6165

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.
Files changed (54) hide show
  1. package/LICENSE +190 -0
  2. package/dist/attack.d.ts +53 -0
  3. package/dist/attack.d.ts.map +1 -0
  4. package/dist/attack.js +75 -0
  5. package/dist/attack.js.map +1 -0
  6. package/dist/audit.d.ts +56 -0
  7. package/dist/audit.d.ts.map +1 -0
  8. package/dist/audit.js +115 -0
  9. package/dist/audit.js.map +1 -0
  10. package/dist/compare.d.ts +45 -0
  11. package/dist/compare.d.ts.map +1 -0
  12. package/dist/compare.js +118 -0
  13. package/dist/compare.js.map +1 -0
  14. package/dist/evaluate.d.ts +40 -0
  15. package/dist/evaluate.d.ts.map +1 -0
  16. package/dist/evaluate.js +87 -0
  17. package/dist/evaluate.js.map +1 -0
  18. package/dist/index.d.ts +19 -0
  19. package/dist/index.d.ts.map +1 -0
  20. package/dist/index.js +19 -0
  21. package/dist/index.js.map +1 -0
  22. package/dist/judge.d.ts +68 -0
  23. package/dist/judge.d.ts.map +1 -0
  24. package/dist/judge.js +102 -0
  25. package/dist/judge.js.map +1 -0
  26. package/dist/llm.d.ts +158 -0
  27. package/dist/llm.d.ts.map +1 -0
  28. package/dist/llm.js +540 -0
  29. package/dist/llm.js.map +1 -0
  30. package/dist/otel-export.d.ts +36 -0
  31. package/dist/otel-export.d.ts.map +1 -0
  32. package/dist/otel-export.js +162 -0
  33. package/dist/otel-export.js.map +1 -0
  34. package/dist/replay.d.ts +51 -0
  35. package/dist/replay.d.ts.map +1 -0
  36. package/dist/replay.js +260 -0
  37. package/dist/replay.js.map +1 -0
  38. package/dist/runner.d.ts +46 -0
  39. package/dist/runner.d.ts.map +1 -0
  40. package/dist/runner.js +250 -0
  41. package/dist/runner.js.map +1 -0
  42. package/dist/scan.d.ts +172 -0
  43. package/dist/scan.d.ts.map +1 -0
  44. package/dist/scan.js +260 -0
  45. package/dist/scan.js.map +1 -0
  46. package/dist/sign.d.ts +20 -0
  47. package/dist/sign.d.ts.map +1 -0
  48. package/dist/sign.js +64 -0
  49. package/dist/sign.js.map +1 -0
  50. package/dist/tracing.d.ts +20 -0
  51. package/dist/tracing.d.ts.map +1 -0
  52. package/dist/tracing.js +65 -0
  53. package/dist/tracing.js.map +1 -0
  54. package/package.json +91 -0
@@ -0,0 +1,162 @@
1
+ /**
2
+ * OTel export — convert Turn[] traces to OpenTelemetry spans and send
3
+ * to any OTLP-compatible backend (Langfuse, Jaeger, Datadog, Grafana Tempo, etc.).
4
+ *
5
+ * Uses GenAI semantic conventions for LLM-specific attributes.
6
+ *
7
+ * @example
8
+ * ```ts
9
+ * import { exportTurnsToOTel } from '@lanefoundry/gatelane-engine';
10
+ *
11
+ * await exportTurnsToOTel(turns, {
12
+ * endpoint: 'https://cloud.langfuse.com/api/public/otel',
13
+ * headers: { Authorization: 'Basic <base64(publicKey:secretKey)>' },
14
+ * });
15
+ * ```
16
+ */
17
+ import { SpanKind, SpanStatusCode } from '@opentelemetry/api';
18
+ import { NodeTracerProvider, SimpleSpanProcessor } from '@opentelemetry/sdk-trace-node';
19
+ import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http';
20
+ import { resourceFromAttributes } from '@opentelemetry/resources';
21
+ const SPAN_KIND_MAP = {
22
+ llm: SpanKind.CLIENT,
23
+ tool: SpanKind.INTERNAL,
24
+ agent: SpanKind.INTERNAL,
25
+ retriever: SpanKind.CLIENT,
26
+ guardrail: SpanKind.INTERNAL,
27
+ };
28
+ /**
29
+ * Export Turn[] traces to an OTLP-compatible backend.
30
+ *
31
+ * Creates a temporary TracerProvider with an OTLP/HTTP exporter, converts
32
+ * each Turn with a span_id into an OTel span (using GenAI semantic
33
+ * conventions), exports them, and shuts down.
34
+ */
35
+ export async function exportTurnsToOTel(turns, config) {
36
+ const spannedTurns = turns.filter((t) => t.span_id);
37
+ if (spannedTurns.length === 0) {
38
+ return { exported: 0 };
39
+ }
40
+ const exporter = new OTLPTraceExporter({
41
+ url: config.endpoint.replace(/\/$/, '') + '/v1/traces',
42
+ headers: config.headers,
43
+ });
44
+ const provider = new NodeTracerProvider({
45
+ resource: resourceFromAttributes({
46
+ 'service.name': config.service_name ?? 'gatelane',
47
+ }),
48
+ spanProcessors: [new SimpleSpanProcessor(exporter)],
49
+ });
50
+ const tracer = provider.getTracer('gatelane-otel-export');
51
+ // Build a map of span_id → Turn for parent lookup
52
+ const spanMap = new Map();
53
+ for (const turn of spannedTurns) {
54
+ spanMap.set(turn.span_id, turn);
55
+ }
56
+ // Process root spans first, then children, to maintain parent-child relationships.
57
+ // OTel SDK handles span linking via context, so we process in order and
58
+ // create spans with explicit start/end times.
59
+ const roots = spannedTurns.filter((t) => !t.parent_span_id);
60
+ const children = spannedTurns.filter((t) => t.parent_span_id);
61
+ let exported = 0;
62
+ for (const turn of [...roots, ...children]) {
63
+ const spanKind = SPAN_KIND_MAP[turn.span_kind ?? 'agent'] ?? SpanKind.INTERNAL;
64
+ const spanName = buildSpanName(turn);
65
+ const startTime = turn.started_at ? new Date(turn.started_at) : new Date();
66
+ const span = tracer.startSpan(spanName, {
67
+ kind: spanKind,
68
+ startTime,
69
+ });
70
+ // GenAI semantic conventions
71
+ if (turn.model) {
72
+ span.setAttribute('gen_ai.request.model', turn.model);
73
+ }
74
+ if (turn.tokens_in !== undefined) {
75
+ span.setAttribute('gen_ai.usage.input_tokens', turn.tokens_in);
76
+ }
77
+ if (turn.tokens_out !== undefined) {
78
+ span.setAttribute('gen_ai.usage.output_tokens', turn.tokens_out);
79
+ }
80
+ if (turn.cost_usd !== undefined) {
81
+ span.setAttribute('gen_ai.usage.cost', turn.cost_usd);
82
+ }
83
+ // gatelane-specific attributes
84
+ span.setAttribute('gatelane.role', turn.role);
85
+ if (turn.span_kind) {
86
+ span.setAttribute('gatelane.span_kind', turn.span_kind);
87
+ }
88
+ if (turn.span_id) {
89
+ span.setAttribute('gatelane.span_id', turn.span_id);
90
+ }
91
+ if (turn.parent_span_id) {
92
+ span.setAttribute('gatelane.parent_span_id', turn.parent_span_id);
93
+ }
94
+ if (turn.latency_ms !== undefined) {
95
+ span.setAttribute('gatelane.latency_ms', turn.latency_ms);
96
+ }
97
+ // Content (opt-in via GenAI conventions — content is a span event)
98
+ if (turn.content) {
99
+ span.addEvent(turn.role === 'assistant' ? 'gen_ai.completion' : 'gen_ai.prompt', {
100
+ 'gen_ai.content': turn.content.slice(0, 4096),
101
+ });
102
+ }
103
+ // Tool calls
104
+ if (turn.tool_calls) {
105
+ for (const tc of turn.tool_calls) {
106
+ span.addEvent('gen_ai.tool_call', {
107
+ 'gen_ai.tool.name': tc.name,
108
+ 'gen_ai.tool.call_id': tc.id,
109
+ 'gen_ai.tool.arguments': tc.arguments.slice(0, 4096),
110
+ });
111
+ }
112
+ }
113
+ // Tool result (for role=tool turns)
114
+ if (turn.role === 'tool' && turn.name) {
115
+ span.setAttribute('gen_ai.tool.name', turn.name);
116
+ if (turn.tool_call_id) {
117
+ span.setAttribute('gen_ai.tool.call_id', turn.tool_call_id);
118
+ }
119
+ }
120
+ // Error status
121
+ if (turn.status === 'error' || turn.status === 'timeout') {
122
+ span.setStatus({
123
+ code: SpanStatusCode.ERROR,
124
+ message: turn.error?.message ?? turn.status,
125
+ });
126
+ if (turn.error) {
127
+ span.recordException({
128
+ name: turn.error.type,
129
+ message: turn.error.message,
130
+ stack: turn.error.stack,
131
+ });
132
+ }
133
+ }
134
+ else {
135
+ span.setStatus({ code: SpanStatusCode.OK });
136
+ }
137
+ const endTime = turn.completed_at
138
+ ? new Date(turn.completed_at)
139
+ : turn.latency_ms !== undefined
140
+ ? new Date(startTime.getTime() + turn.latency_ms)
141
+ : new Date();
142
+ span.end(endTime);
143
+ exported++;
144
+ }
145
+ // Flush and shutdown
146
+ await provider.forceFlush();
147
+ await provider.shutdown();
148
+ return { exported };
149
+ }
150
+ function buildSpanName(turn) {
151
+ if (turn.span_kind === 'llm' && turn.model) {
152
+ return `llm.${turn.model}`;
153
+ }
154
+ if (turn.span_kind === 'tool' && turn.name) {
155
+ return `tool.${turn.name}`;
156
+ }
157
+ if (turn.span_kind) {
158
+ return turn.span_kind;
159
+ }
160
+ return `${turn.role}`;
161
+ }
162
+ //# sourceMappingURL=otel-export.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"otel-export.js","sourceRoot":"","sources":["../src/otel-export.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,51 @@
1
+ /**
2
+ * Replay engine — runs each dataset item against each candidate via LLMCaller.
3
+ *
4
+ * Two modes:
5
+ * - Single-turn: one prompt → one response (legacy, item.input only)
6
+ * - Multi-turn: replay full agent trace with mocked tool results (item.turns)
7
+ *
8
+ * Multi-turn replay feeds user/system turns to the candidate, lets the model
9
+ * decide tool calls, injects the ORIGINAL tool results from the captured trace,
10
+ * and records the complete new trace. This isolates model behavior changes
11
+ * from tool behavior changes.
12
+ *
13
+ * Determinism: a stable seed per (candidate, item) produces reproducible output.
14
+ *
15
+ * @see docs/prd.md §5.1 — The gate
16
+ */
17
+ import type { CapturedCall, Turn } from '@lanefoundry/gatelane-sdk/capture';
18
+ import type { DatasetItem, FrozenDataset } from '@lanefoundry/gatelane-sdk/dataset';
19
+ import type { LLMCaller, ChatMessage, LLMResponse } from './llm.js';
20
+ export type ReplayRow = {
21
+ item_id: string;
22
+ candidate_ref: string;
23
+ response: LLMResponse;
24
+ captured: CapturedCall;
25
+ /** Full multi-turn trace when replaying an agent trace. */
26
+ turns?: Turn[];
27
+ };
28
+ export type ReplayResult = {
29
+ rows: ReplayRow[];
30
+ /** 0.0–1.0 across all (candidate, item) pairs. Higher = more reproducible. */
31
+ determinism_score: number;
32
+ total_cost_usd: number;
33
+ total_latency_ms: number;
34
+ };
35
+ export type ReplayArgs = {
36
+ dataset: FrozenDataset;
37
+ candidates: ReadonlyArray<{
38
+ ref: string;
39
+ model: string;
40
+ messages_from_item?: (item: DatasetItem) => Array<ChatMessage>;
41
+ }>;
42
+ caller: LLMCaller;
43
+ /** Master seed; per (candidate, item) seed is derived deterministically. */
44
+ seed?: number;
45
+ /** If true, replay twice to measure determinism. Doubles cost. */
46
+ measureDeterminism?: boolean;
47
+ /** Max LLM round-trips per multi-turn replay (prevents infinite loops). */
48
+ maxTurns?: number;
49
+ };
50
+ export declare function replay(args: ReplayArgs): Promise<ReplayResult>;
51
+ //# sourceMappingURL=replay.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"replay.d.ts","sourceRoot":"","sources":["../src/replay.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,IAAI,EAAE,MAAM,mCAAmC,CAAC;AAC5E,OAAO,KAAK,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,mCAAmC,CAAC;AAEpF,OAAO,KAAK,EAAE,SAAS,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAEpE,MAAM,MAAM,SAAS,GAAG;IACtB,OAAO,EAAE,MAAM,CAAC;IAChB,aAAa,EAAE,MAAM,CAAC;IACtB,QAAQ,EAAE,WAAW,CAAC;IACtB,QAAQ,EAAE,YAAY,CAAC;IACvB,2DAA2D;IAC3D,KAAK,CAAC,EAAE,IAAI,EAAE,CAAC;CAChB,CAAC;AAEF,MAAM,MAAM,YAAY,GAAG;IACzB,IAAI,EAAE,SAAS,EAAE,CAAC;IAClB,8EAA8E;IAC9E,iBAAiB,EAAE,MAAM,CAAC;IAC1B,cAAc,EAAE,MAAM,CAAC;IACvB,gBAAgB,EAAE,MAAM,CAAC;CAC1B,CAAC;AAEF,MAAM,MAAM,UAAU,GAAG;IACvB,OAAO,EAAE,aAAa,CAAC;IACvB,UAAU,EAAE,aAAa,CAAC;QACxB,GAAG,EAAE,MAAM,CAAC;QACZ,KAAK,EAAE,MAAM,CAAC;QACd,kBAAkB,CAAC,EAAE,CAAC,IAAI,EAAE,WAAW,KAAK,KAAK,CAAC,WAAW,CAAC,CAAC;KAChE,CAAC,CAAC;IACH,MAAM,EAAE,SAAS,CAAC;IAClB,4EAA4E;IAC5E,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,kEAAkE;IAClE,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,2EAA2E;IAC3E,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,CAAC;AA0KF,wBAAsB,MAAM,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,YAAY,CAAC,CAsFpE"}
package/dist/replay.js ADDED
@@ -0,0 +1,260 @@
1
+ /**
2
+ * Replay engine — runs each dataset item against each candidate via LLMCaller.
3
+ *
4
+ * Two modes:
5
+ * - Single-turn: one prompt → one response (legacy, item.input only)
6
+ * - Multi-turn: replay full agent trace with mocked tool results (item.turns)
7
+ *
8
+ * Multi-turn replay feeds user/system turns to the candidate, lets the model
9
+ * decide tool calls, injects the ORIGINAL tool results from the captured trace,
10
+ * and records the complete new trace. This isolates model behavior changes
11
+ * from tool behavior changes.
12
+ *
13
+ * Determinism: a stable seed per (candidate, item) produces reproducible output.
14
+ *
15
+ * @see docs/prd.md §5.1 — The gate
16
+ */
17
+ function deriveSeed(master, candidateRef, itemId) {
18
+ let h = 0x811c9dc5 ^ master;
19
+ for (const ch of `${candidateRef}|${itemId}`) {
20
+ h ^= ch.charCodeAt(0);
21
+ h = Math.imul(h, 0x01000193);
22
+ }
23
+ return h >>> 0;
24
+ }
25
+ function itemId(item, idx) {
26
+ return item.id ?? `<item-${idx}>`;
27
+ }
28
+ /** Build a tool-result lookup from original turns: tool_call_id → tool content. */
29
+ function buildToolResultMap(turns) {
30
+ const map = new Map();
31
+ for (const t of turns) {
32
+ if (t.role === 'tool' && t.tool_call_id) {
33
+ map.set(t.tool_call_id, t);
34
+ }
35
+ }
36
+ return map;
37
+ }
38
+ /** Convert Turn[] into ChatMessage[] for the LLMCaller (which only takes role+content). */
39
+ function turnsToMessages(turns) {
40
+ return turns
41
+ .filter((t) => t.content !== null)
42
+ .map((t) => ({
43
+ role: t.role === 'tool' ? 'user' : t.role,
44
+ content: t.role === 'tool'
45
+ ? `[Tool result from ${t.name ?? 'unknown'}]: ${t.content}`
46
+ : t.content,
47
+ }));
48
+ }
49
+ /**
50
+ * Multi-turn replay: replay a full agent trace with mocked tool results.
51
+ *
52
+ * The model makes fresh decisions (may call different tools), but tool results
53
+ * come from the original trace. If the model calls a tool that wasn't in the
54
+ * original trace, the tool result is "Tool not available in replay".
55
+ */
56
+ async function replayMultiTurn(args) {
57
+ const { itemTurns, candidateRef, model, caller, seed, maxTurns } = args;
58
+ const toolResultMap = buildToolResultMap(itemTurns);
59
+ const replayTurns = [];
60
+ let totalCost = 0;
61
+ let totalLatency = 0;
62
+ let lastResponse = null;
63
+ // Start with system and user turns from the original trace
64
+ const initialTurns = itemTurns.filter((t) => t.role === 'system' || t.role === 'user');
65
+ for (const t of initialTurns) {
66
+ replayTurns.push({ ...t });
67
+ }
68
+ let roundTrips = 0;
69
+ while (roundTrips < maxTurns) {
70
+ roundTrips++;
71
+ const messages = turnsToMessages(replayTurns);
72
+ const startedAt = new Date().toISOString();
73
+ const response = await caller.call({
74
+ candidate_ref: candidateRef,
75
+ model,
76
+ messages,
77
+ seed,
78
+ });
79
+ const completedAt = new Date().toISOString();
80
+ totalCost += response.cost_usd;
81
+ totalLatency += response.latency_ms;
82
+ lastResponse = response;
83
+ // Parse tool_calls from the raw response if the provider returned them
84
+ const rawToolCalls = extractToolCalls(response);
85
+ if (rawToolCalls.length > 0) {
86
+ // Model wants to call tools
87
+ replayTurns.push({
88
+ role: 'assistant',
89
+ content: response.content || null,
90
+ tool_calls: rawToolCalls,
91
+ status: 'ok',
92
+ span_kind: 'llm',
93
+ model,
94
+ started_at: startedAt,
95
+ completed_at: completedAt,
96
+ cost_usd: response.cost_usd,
97
+ latency_ms: response.latency_ms,
98
+ tokens_in: response.tokens_in,
99
+ tokens_out: response.tokens_out,
100
+ });
101
+ // Inject original tool results (mocked)
102
+ for (const tc of rawToolCalls) {
103
+ const originalResult = toolResultMap.get(tc.id);
104
+ if (originalResult) {
105
+ replayTurns.push({
106
+ ...originalResult,
107
+ status: originalResult.status ?? 'ok',
108
+ });
109
+ }
110
+ else {
111
+ replayTurns.push({
112
+ role: 'tool',
113
+ tool_call_id: tc.id,
114
+ name: tc.name,
115
+ content: JSON.stringify({ error: 'Tool not available in replay', tool: tc.name }),
116
+ status: 'error',
117
+ error: { type: 'ReplayError', message: `Tool "${tc.name}" was not called in the original trace` },
118
+ });
119
+ }
120
+ }
121
+ }
122
+ else {
123
+ // Model gave a final text response — done
124
+ replayTurns.push({
125
+ role: 'assistant',
126
+ content: response.content,
127
+ status: response.finish_reason === 'error' ? 'error' : 'ok',
128
+ span_kind: 'llm',
129
+ model,
130
+ started_at: startedAt,
131
+ completed_at: completedAt,
132
+ cost_usd: response.cost_usd,
133
+ latency_ms: response.latency_ms,
134
+ tokens_in: response.tokens_in,
135
+ tokens_out: response.tokens_out,
136
+ ...(response.finish_reason === 'error' ? {
137
+ error: { type: 'LLMError', message: 'Model returned error finish_reason' },
138
+ } : {}),
139
+ });
140
+ break;
141
+ }
142
+ }
143
+ if (!lastResponse) {
144
+ throw new Error(`Multi-turn replay produced no response for ${candidateRef}`);
145
+ }
146
+ return { response: lastResponse, replayTurns, totalCost, totalLatency };
147
+ }
148
+ /** Extract tool_calls from a raw LLM response (OpenAI format). */
149
+ function extractToolCalls(response) {
150
+ if (response.finish_reason !== 'tool_calls')
151
+ return [];
152
+ const raw = response.raw;
153
+ if (!raw)
154
+ return [];
155
+ // OpenAI format: choices[0].message.tool_calls
156
+ const choices = raw.choices;
157
+ const toolCalls = choices?.[0]?.message?.tool_calls;
158
+ if (!toolCalls || !Array.isArray(toolCalls))
159
+ return [];
160
+ return toolCalls.map((tc) => ({
161
+ id: tc.id,
162
+ name: tc.function.name,
163
+ arguments: tc.function.arguments,
164
+ }));
165
+ }
166
+ export async function replay(args) {
167
+ const items = args.dataset.items ?? [];
168
+ if (items.length === 0) {
169
+ return { rows: [], determinism_score: 1, total_cost_usd: 0, total_latency_ms: 0 };
170
+ }
171
+ const master = args.seed ?? 0xC0FFEE;
172
+ const maxTurns = args.maxTurns ?? 10;
173
+ const rows = [];
174
+ let totalCost = 0;
175
+ let totalLatency = 0;
176
+ let matches = 0;
177
+ let attempts = 0;
178
+ for (let i = 0; i < items.length; i++) {
179
+ const item = items[i];
180
+ if (!item)
181
+ continue;
182
+ const id = itemId(item, i);
183
+ for (const cand of args.candidates) {
184
+ const seed = deriveSeed(master, cand.ref, id);
185
+ // Multi-turn replay when item has turns
186
+ if (item.turns && item.turns.length > 0) {
187
+ const result = await replayMultiTurn({
188
+ item,
189
+ itemTurns: item.turns,
190
+ candidateRef: cand.ref,
191
+ model: cand.model,
192
+ caller: args.caller,
193
+ seed,
194
+ maxTurns,
195
+ });
196
+ const captured = result.response.toCapturedCall({
197
+ id: `r-${id}-${cand.ref}`,
198
+ started_at: new Date().toISOString(),
199
+ completed_at: new Date().toISOString(),
200
+ span_kind: 'gate.replay',
201
+ });
202
+ rows.push({
203
+ item_id: id,
204
+ candidate_ref: cand.ref,
205
+ response: result.response,
206
+ captured,
207
+ turns: result.replayTurns,
208
+ });
209
+ totalCost += result.totalCost;
210
+ totalLatency += result.totalLatency;
211
+ }
212
+ else {
213
+ // Single-turn replay (legacy path)
214
+ const messages = cand.messages_from_item?.(item) ?? deriveMessages(item);
215
+ const response = await args.caller.call({
216
+ candidate_ref: cand.ref,
217
+ model: cand.model,
218
+ messages,
219
+ seed,
220
+ });
221
+ const captured = response.toCapturedCall({
222
+ id: `r-${id}-${cand.ref}`,
223
+ started_at: new Date().toISOString(),
224
+ completed_at: new Date().toISOString(),
225
+ span_kind: 'gate.replay',
226
+ });
227
+ rows.push({ item_id: id, candidate_ref: cand.ref, response, captured });
228
+ totalCost += response.cost_usd;
229
+ totalLatency += response.latency_ms;
230
+ if (args.measureDeterminism === true) {
231
+ const second = await args.caller.call({
232
+ candidate_ref: cand.ref,
233
+ model: cand.model,
234
+ messages,
235
+ seed,
236
+ });
237
+ attempts += 1;
238
+ if (second.content === response.content)
239
+ matches += 1;
240
+ }
241
+ }
242
+ }
243
+ }
244
+ const determinism_score = attempts === 0 ? 1 : matches / attempts;
245
+ return {
246
+ rows,
247
+ determinism_score,
248
+ total_cost_usd: totalCost,
249
+ total_latency_ms: totalLatency,
250
+ };
251
+ }
252
+ function deriveMessages(item) {
253
+ if (Array.isArray(item.input)) {
254
+ return item.input
255
+ .filter((m) => typeof m === 'object' && m !== null && 'role' in m && 'content' in m)
256
+ .map((m) => ({ role: m.role, content: m.content }));
257
+ }
258
+ return [{ role: 'user', content: String(item.input ?? '') }];
259
+ }
260
+ //# sourceMappingURL=replay.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"replay.js","sourceRoot":"","sources":["../src/replay.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,46 @@
1
+ /**
2
+ * Real runGate runner — orchestrates replay + judge + compare + sign + evaluate.
3
+ *
4
+ * This is what gets installed into the SDK via `setGateRunner()`. Decoupled
5
+ * from the SDK shape so the engine can evolve without breaking SDK consumers.
6
+ *
7
+ * Flow:
8
+ * 1. Resolve each candidate ref → { ref, model, messages_from_item }.
9
+ * 2. Replay each (candidate, item) via LLMCaller.
10
+ * 3. For each (candidate, item, judge), call LLMJudge and parse verdict.
11
+ * 4. Compare → per-candidate metrics + judge matrix.
12
+ * 5. Sign the resulting GateReport via HMAC-SHA256.
13
+ * 6. Evaluate policy → GateDecision.
14
+ *
15
+ * @see docs/prd.md §5.1 — The gate
16
+ */
17
+ import type { DatasetItem } from '@lanefoundry/gatelane-sdk/dataset';
18
+ import { type GateRunner } from '@lanefoundry/gatelane-sdk/gate';
19
+ import { type LLMCaller, type LLMRequest } from './llm.js';
20
+ import type { D1DatabaseLike } from '@lanefoundry/gatelane-sdk';
21
+ export type RunnerOptions = {
22
+ /** Caller used for both candidate invocations and judge calls. */
23
+ caller: LLMCaller;
24
+ /** Per-judge LLMCallers (optional; defaults to `caller`). */
25
+ judge_callers?: Record<string, LLMCaller>;
26
+ /** HMAC signing key. Required. */
27
+ signing_key: string;
28
+ /** Master seed for replay determinism. */
29
+ seed?: number;
30
+ /** Replay twice to compute determinism_score. Doubles cost. */
31
+ measureDeterminism?: boolean;
32
+ /** How to extract chat messages from a dataset item. */
33
+ messages_from_item?: (item: DatasetItem) => Array<{
34
+ role: 'system' | 'user' | 'assistant';
35
+ content: string;
36
+ }>;
37
+ /** Optional D1 database for audit log persistence. */
38
+ audit_db?: D1DatabaseLike;
39
+ /** Service name for OTel tracing. */
40
+ tracing_service_name?: string;
41
+ /** Max concurrent LLM calls during judge stage (default: 5). */
42
+ concurrency?: number;
43
+ };
44
+ export declare function createRunner(opts: RunnerOptions): GateRunner;
45
+ export type { LLMRequest };
46
+ //# sourceMappingURL=runner.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runner.d.ts","sourceRoot":"","sources":["../src/runner.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AACH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mCAAmC,CAAC;AACrE,OAAO,EAGL,KAAK,UAAU,EAChB,MAAM,gCAAgC,CAAC;AAKxC,OAAO,EAAsB,KAAK,SAAS,EAAE,KAAK,UAAU,EAAE,MAAM,UAAU,CAAC;AAQ/E,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAEhE,MAAM,MAAM,aAAa,GAAG;IAC1B,kEAAkE;IAClE,MAAM,EAAE,SAAS,CAAC;IAClB,6DAA6D;IAC7D,aAAa,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;IAC1C,kCAAkC;IAClC,WAAW,EAAE,MAAM,CAAC;IACpB,0CAA0C;IAC1C,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,+DAA+D;IAC/D,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,wDAAwD;IACxD,kBAAkB,CAAC,EAAE,CAAC,IAAI,EAAE,WAAW,KAAK,KAAK,CAAC;QAAE,IAAI,EAAE,QAAQ,GAAG,MAAM,GAAG,WAAW,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC9G,sDAAsD;IACtD,QAAQ,CAAC,EAAE,cAAc,CAAC;IAC1B,qCAAqC;IACrC,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,gEAAgE;IAChE,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB,CAAC;AAEF,wBAAgB,YAAY,CAAC,IAAI,EAAE,aAAa,GAAG,UAAU,CAuQ5D;AAED,YAAY,EAAE,UAAU,EAAE,CAAC"}