@artemiskit/core 0.5.0 → 0.5.2
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 +16 -0
- package/dist/artifacts/manifest.d.ts +5 -1
- package/dist/artifacts/manifest.d.ts.map +1 -1
- package/dist/artifacts/types.d.ts +87 -0
- package/dist/artifacts/types.d.ts.map +1 -1
- package/dist/index.js +622 -400
- package/dist/provenance/execution-provenance.d.ts +11 -0
- package/dist/provenance/execution-provenance.d.ts.map +1 -0
- package/dist/provenance/index.d.ts +1 -0
- package/dist/provenance/index.d.ts.map +1 -1
- package/dist/runner/executor.d.ts.map +1 -1
- package/dist/runner/runner.d.ts.map +1 -1
- package/dist/runner/types.d.ts +15 -1
- package/dist/runner/types.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/artifacts/manifest.test.ts +123 -1
- package/src/artifacts/manifest.ts +25 -29
- package/src/artifacts/types.ts +262 -0
- package/src/provenance/execution-provenance.test.ts +59 -0
- package/src/provenance/execution-provenance.ts +53 -0
- package/src/provenance/index.ts +1 -0
- package/src/runner/executor.test.ts +60 -0
- package/src/runner/executor.ts +145 -11
- package/src/runner/release-validation.test.ts +36 -0
- package/src/runner/runner.ts +31 -1
- package/src/runner/types.ts +9 -1
- package/src/scenario/schema.ts +1 -1
package/src/runner/executor.ts
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
import type {
|
|
6
|
+
CaseAttemptEvidence,
|
|
6
7
|
CaseEvaluationEvidence,
|
|
7
8
|
CaseEvaluationStatus,
|
|
8
9
|
CaseRedactionInfo,
|
|
@@ -99,17 +100,50 @@ export async function executeCase(
|
|
|
99
100
|
context: ExecutorContext
|
|
100
101
|
): Promise<CaseResult> {
|
|
101
102
|
const { timeout, retries = 0 } = context;
|
|
103
|
+
if (!Number.isSafeInteger(retries) || retries < 0 || retries > 99) {
|
|
104
|
+
throw new RangeError('retries must be a whole number between 0 and 99');
|
|
105
|
+
}
|
|
102
106
|
const caseStartTime = Date.now();
|
|
107
|
+
const requestedModel = testCase.model || context.requestedModel || context.scenario.model;
|
|
108
|
+
const retryChainId = `${context.runId ?? 'untracked'}:${testCase.id}`;
|
|
109
|
+
const repetitionIndex = context.repetition?.index ?? 1;
|
|
110
|
+
const attemptEvidence: CaseAttemptEvidence[] = [];
|
|
103
111
|
|
|
104
112
|
let lastError: Error | null = null;
|
|
105
113
|
|
|
106
114
|
for (let attempt = 0; attempt <= retries; attempt++) {
|
|
115
|
+
const attemptStartTime = Date.now();
|
|
107
116
|
try {
|
|
108
117
|
const result = await executeCaseAttempt(testCase, context, timeout);
|
|
109
|
-
return
|
|
118
|
+
return withAttemptEvidence(result, attemptEvidence, {
|
|
119
|
+
retryChainId,
|
|
120
|
+
repetitionIndex,
|
|
121
|
+
attemptNumber: attempt + 1,
|
|
122
|
+
includedInOutcome: true,
|
|
123
|
+
latencyMs: result.latencyMs,
|
|
124
|
+
});
|
|
110
125
|
} catch (error) {
|
|
111
126
|
lastError = error as Error;
|
|
112
|
-
if (error instanceof ToolLoopError)
|
|
127
|
+
if (error instanceof ToolLoopError) {
|
|
128
|
+
return withAttemptEvidence(error.caseResult, attemptEvidence, {
|
|
129
|
+
retryChainId,
|
|
130
|
+
repetitionIndex,
|
|
131
|
+
attemptNumber: attempt + 1,
|
|
132
|
+
includedInOutcome: true,
|
|
133
|
+
latencyMs: error.caseResult.latencyMs,
|
|
134
|
+
errorCode: 'tool_error',
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
attemptEvidence.push({
|
|
138
|
+
attempt_id: `${retryChainId}:${attempt + 1}`,
|
|
139
|
+
retry_chain_id: retryChainId,
|
|
140
|
+
repetition_index: repetitionIndex,
|
|
141
|
+
attempt_number: attempt + 1,
|
|
142
|
+
status: 'error',
|
|
143
|
+
included_in_outcome: false,
|
|
144
|
+
latency_ms: Date.now() - attemptStartTime,
|
|
145
|
+
error_code: error instanceof TimeoutError ? 'timeout' : 'target_error',
|
|
146
|
+
});
|
|
113
147
|
if (attempt < retries) {
|
|
114
148
|
// Wait before retry with exponential backoff
|
|
115
149
|
await sleep(2 ** attempt * 1000);
|
|
@@ -125,6 +159,10 @@ export async function executeCase(
|
|
|
125
159
|
ok: false,
|
|
126
160
|
status: 'error',
|
|
127
161
|
attempts: retries + 1,
|
|
162
|
+
attempt_evidence: attemptEvidence.map((entry, index) => ({
|
|
163
|
+
...entry,
|
|
164
|
+
included_in_outcome: index === attemptEvidence.length - 1,
|
|
165
|
+
})),
|
|
128
166
|
score: 0,
|
|
129
167
|
matcherType: testCase.expected.type,
|
|
130
168
|
reason: `Failed after ${retries + 1} attempts: ${lastError?.message}`,
|
|
@@ -135,15 +173,51 @@ export async function executeCase(
|
|
|
135
173
|
expected: testCase.expected,
|
|
136
174
|
tags: testCase.tags,
|
|
137
175
|
error: lastError?.message,
|
|
176
|
+
target: targetEvidence(context.client.provider, requestedModel),
|
|
138
177
|
};
|
|
139
178
|
}
|
|
140
179
|
|
|
180
|
+
function withAttemptEvidence(
|
|
181
|
+
result: CaseResult,
|
|
182
|
+
priorAttempts: CaseAttemptEvidence[],
|
|
183
|
+
input: {
|
|
184
|
+
retryChainId: string;
|
|
185
|
+
repetitionIndex: number;
|
|
186
|
+
attemptNumber: number;
|
|
187
|
+
includedInOutcome: boolean;
|
|
188
|
+
latencyMs: number;
|
|
189
|
+
errorCode?: CaseAttemptEvidence['error_code'];
|
|
190
|
+
}
|
|
191
|
+
): CaseResult {
|
|
192
|
+
return {
|
|
193
|
+
...result,
|
|
194
|
+
attempts: input.attemptNumber,
|
|
195
|
+
attempt_evidence: [
|
|
196
|
+
...priorAttempts,
|
|
197
|
+
{
|
|
198
|
+
attempt_id: `${input.retryChainId}:${input.attemptNumber}`,
|
|
199
|
+
retry_chain_id: input.retryChainId,
|
|
200
|
+
repetition_index: input.repetitionIndex,
|
|
201
|
+
attempt_number: input.attemptNumber,
|
|
202
|
+
status: getTerminalStatus(result),
|
|
203
|
+
included_in_outcome: input.includedInOutcome,
|
|
204
|
+
latency_ms: input.latencyMs,
|
|
205
|
+
...(input.errorCode ? { error_code: input.errorCode } : {}),
|
|
206
|
+
},
|
|
207
|
+
],
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
function getTerminalStatus(result: CaseResult): CaseEvaluationStatus {
|
|
212
|
+
return result.status ?? (result.ok ? 'passed' : result.error ? 'error' : 'failed');
|
|
213
|
+
}
|
|
214
|
+
|
|
141
215
|
async function executeCaseAttempt(
|
|
142
216
|
testCase: TestCase,
|
|
143
217
|
context: ExecutorContext,
|
|
144
218
|
timeout?: number
|
|
145
219
|
): Promise<CaseResult> {
|
|
146
|
-
const { client, scenario, redaction: cliRedaction, toolExecutor } = context;
|
|
220
|
+
const { client, scenario, requestedModel, redaction: cliRedaction, toolExecutor } = context;
|
|
147
221
|
|
|
148
222
|
// Merge scenario-level and case-level variables (case overrides scenario)
|
|
149
223
|
const variables = mergeVariables(scenario.variables, testCase.variables);
|
|
@@ -171,7 +245,7 @@ async function executeCaseAttempt(
|
|
|
171
245
|
const generate = () =>
|
|
172
246
|
client.generate({
|
|
173
247
|
prompt: loopPrompt,
|
|
174
|
-
model: testCase.model || scenario.model,
|
|
248
|
+
model: testCase.model || requestedModel || scenario.model,
|
|
175
249
|
temperature: scenario.temperature,
|
|
176
250
|
maxTokens: scenario.maxTokens,
|
|
177
251
|
seed: scenario.seed,
|
|
@@ -182,6 +256,7 @@ async function executeCaseAttempt(
|
|
|
182
256
|
let result = timeout
|
|
183
257
|
? await Promise.race([generatePromise, createTimeout(timeout)])
|
|
184
258
|
: await generatePromise;
|
|
259
|
+
const observedModels = [result.model];
|
|
185
260
|
const generationMetrics = {
|
|
186
261
|
latencyMs: result.latencyMs,
|
|
187
262
|
tokens: { ...result.tokens },
|
|
@@ -198,7 +273,12 @@ async function executeCaseAttempt(
|
|
|
198
273
|
terminationReason: 'tool_error',
|
|
199
274
|
},
|
|
200
275
|
generationMetrics,
|
|
201
|
-
'TOOL_EXECUTOR_REQUIRED'
|
|
276
|
+
'TOOL_EXECUTOR_REQUIRED',
|
|
277
|
+
targetEvidence(
|
|
278
|
+
client.provider,
|
|
279
|
+
testCase.model || requestedModel || scenario.model,
|
|
280
|
+
observedModels
|
|
281
|
+
)
|
|
202
282
|
);
|
|
203
283
|
}
|
|
204
284
|
const executor =
|
|
@@ -225,7 +305,12 @@ async function executeCaseAttempt(
|
|
|
225
305
|
terminationReason: 'duplicate_call',
|
|
226
306
|
},
|
|
227
307
|
generationMetrics,
|
|
228
|
-
'TOOL_DUPLICATE_CALL'
|
|
308
|
+
'TOOL_DUPLICATE_CALL',
|
|
309
|
+
targetEvidence(
|
|
310
|
+
client.provider,
|
|
311
|
+
testCase.model || requestedModel || scenario.model,
|
|
312
|
+
observedModels
|
|
313
|
+
)
|
|
229
314
|
);
|
|
230
315
|
}
|
|
231
316
|
seenCalls.add(fingerprint);
|
|
@@ -263,7 +348,12 @@ async function executeCaseAttempt(
|
|
|
263
348
|
: 'tool_error',
|
|
264
349
|
},
|
|
265
350
|
generationMetrics,
|
|
266
|
-
execution.error?.code ?? 'TOOL_EXECUTION_FAILED'
|
|
351
|
+
execution.error?.code ?? 'TOOL_EXECUTION_FAILED',
|
|
352
|
+
targetEvidence(
|
|
353
|
+
client.provider,
|
|
354
|
+
testCase.model || requestedModel || scenario.model,
|
|
355
|
+
observedModels
|
|
356
|
+
)
|
|
267
357
|
);
|
|
268
358
|
}
|
|
269
359
|
const content = JSON.stringify(execution.result ?? {});
|
|
@@ -281,7 +371,12 @@ async function executeCaseAttempt(
|
|
|
281
371
|
toolTrace,
|
|
282
372
|
{ status: 'error', steps: step + 1, terminationReason: 'timeout' },
|
|
283
373
|
generationMetrics,
|
|
284
|
-
'TOOL_LOOP_TIMEOUT'
|
|
374
|
+
'TOOL_LOOP_TIMEOUT',
|
|
375
|
+
targetEvidence(
|
|
376
|
+
client.provider,
|
|
377
|
+
testCase.model || requestedModel || scenario.model,
|
|
378
|
+
observedModels
|
|
379
|
+
)
|
|
285
380
|
);
|
|
286
381
|
}
|
|
287
382
|
const requestTimeout = timeout ? Math.min(timeout, remainingLoopTime) : remainingLoopTime;
|
|
@@ -298,9 +393,15 @@ async function executeCaseAttempt(
|
|
|
298
393
|
terminationReason: timedOut ? 'timeout' : 'tool_error',
|
|
299
394
|
},
|
|
300
395
|
generationMetrics,
|
|
301
|
-
timedOut ? 'TOOL_LOOP_TIMEOUT' : 'TOOL_GENERATION_FAILED'
|
|
396
|
+
timedOut ? 'TOOL_LOOP_TIMEOUT' : 'TOOL_GENERATION_FAILED',
|
|
397
|
+
targetEvidence(
|
|
398
|
+
client.provider,
|
|
399
|
+
testCase.model || requestedModel || scenario.model,
|
|
400
|
+
observedModels
|
|
401
|
+
)
|
|
302
402
|
);
|
|
303
403
|
}
|
|
404
|
+
observedModels.push(result.model);
|
|
304
405
|
generationMetrics.latencyMs += result.latencyMs;
|
|
305
406
|
generationMetrics.tokens.prompt += result.tokens.prompt;
|
|
306
407
|
generationMetrics.tokens.completion += result.tokens.completion;
|
|
@@ -316,7 +417,12 @@ async function executeCaseAttempt(
|
|
|
316
417
|
terminationReason: 'max_steps',
|
|
317
418
|
},
|
|
318
419
|
generationMetrics,
|
|
319
|
-
'TOOL_LOOP_MAX_STEPS'
|
|
420
|
+
'TOOL_LOOP_MAX_STEPS',
|
|
421
|
+
targetEvidence(
|
|
422
|
+
client.provider,
|
|
423
|
+
testCase.model || requestedModel || scenario.model,
|
|
424
|
+
observedModels
|
|
425
|
+
)
|
|
320
426
|
);
|
|
321
427
|
}
|
|
322
428
|
toolLoop = { status: 'completed', steps: toolTrace.length, terminationReason: 'completed' };
|
|
@@ -435,17 +541,44 @@ async function executeCaseAttempt(
|
|
|
435
541
|
tags: testCase.tags,
|
|
436
542
|
redaction: redactionInfo,
|
|
437
543
|
evidence: finalEvidence,
|
|
544
|
+
target: targetEvidence(
|
|
545
|
+
client.provider,
|
|
546
|
+
testCase.model || requestedModel || scenario.model,
|
|
547
|
+
observedModels
|
|
548
|
+
),
|
|
438
549
|
toolTrace: toolTrace.length ? toolTrace : undefined,
|
|
439
550
|
toolLoop,
|
|
440
551
|
};
|
|
441
552
|
}
|
|
442
553
|
|
|
554
|
+
function targetEvidence(
|
|
555
|
+
provider: string,
|
|
556
|
+
requestedModel?: string,
|
|
557
|
+
observedModels?: unknown[]
|
|
558
|
+
): NonNullable<CaseResult['target']> {
|
|
559
|
+
const observed = [
|
|
560
|
+
...new Set(
|
|
561
|
+
(observedModels ?? []).filter(
|
|
562
|
+
(model): model is string => typeof model === 'string' && model.length > 0
|
|
563
|
+
)
|
|
564
|
+
),
|
|
565
|
+
]
|
|
566
|
+
.map((model) => sanitizeArtifactText(model, 200))
|
|
567
|
+
.filter((model): model is string => Boolean(model));
|
|
568
|
+
return {
|
|
569
|
+
provider: sanitizeArtifactText(provider, 100) ?? 'unknown',
|
|
570
|
+
...(requestedModel ? { requested_model: sanitizeArtifactText(requestedModel, 200) } : {}),
|
|
571
|
+
...(observed.length ? { observed_models: observed } : {}),
|
|
572
|
+
};
|
|
573
|
+
}
|
|
574
|
+
|
|
443
575
|
function createToolLoopError(
|
|
444
576
|
testCase: TestCase,
|
|
445
577
|
toolTrace: ToolTraceEntry[],
|
|
446
578
|
toolLoop: ToolLoopSummary,
|
|
447
579
|
generationMetrics: Pick<CaseResult, 'latencyMs' | 'tokens'>,
|
|
448
|
-
code: string
|
|
580
|
+
code: string,
|
|
581
|
+
target?: CaseResult['target']
|
|
449
582
|
): ToolLoopError {
|
|
450
583
|
return new ToolLoopError(code, {
|
|
451
584
|
id: testCase.id,
|
|
@@ -462,6 +595,7 @@ function createToolLoopError(
|
|
|
462
595
|
expected: testCase.expected,
|
|
463
596
|
tags: testCase.tags,
|
|
464
597
|
error: code,
|
|
598
|
+
target,
|
|
465
599
|
toolTrace,
|
|
466
600
|
toolLoop,
|
|
467
601
|
});
|
|
@@ -96,6 +96,13 @@ describe('release validation: fixture-backed workflow cases', () => {
|
|
|
96
96
|
workload: { algorithm: 'sha256' },
|
|
97
97
|
rubric: { algorithm: 'sha256' },
|
|
98
98
|
});
|
|
99
|
+
expect(result.manifest.execution_provenance).toMatchObject({
|
|
100
|
+
schema_version: '1',
|
|
101
|
+
target: {
|
|
102
|
+
provider: 'fixture',
|
|
103
|
+
observed_models: ['fixture-model'],
|
|
104
|
+
},
|
|
105
|
+
});
|
|
99
106
|
});
|
|
100
107
|
|
|
101
108
|
test('RV-02 retains independent logistics tool evidence through a fixture-backed workflow', async () => {
|
|
@@ -171,4 +178,33 @@ describe('release validation: fixture-backed workflow cases', () => {
|
|
|
171
178
|
toolTrace: [{ toolCall: { id: 'capacity-1' }, result: { available: true } }],
|
|
172
179
|
});
|
|
173
180
|
});
|
|
181
|
+
|
|
182
|
+
test('records a CLI-resolved model for each case when the scenario does not declare one', async () => {
|
|
183
|
+
const scenario = ScenarioSchema.parse({
|
|
184
|
+
name: 'resolved model evidence',
|
|
185
|
+
cases: [
|
|
186
|
+
{
|
|
187
|
+
id: 'cli-model-override',
|
|
188
|
+
prompt: 'Respond with assured',
|
|
189
|
+
expected: { type: 'exact', value: 'assured' },
|
|
190
|
+
},
|
|
191
|
+
],
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
const result = await runScenario({
|
|
195
|
+
scenario,
|
|
196
|
+
client: fixtureClient(['assured']),
|
|
197
|
+
resolvedConfig: {
|
|
198
|
+
provider: 'fixture',
|
|
199
|
+
model: 'fixture-cli-model',
|
|
200
|
+
source: { provider: 'cli', model: 'cli' },
|
|
201
|
+
},
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
expect(result.cases[0].target).toEqual({
|
|
205
|
+
provider: 'fixture',
|
|
206
|
+
requested_model: 'fixture-cli-model',
|
|
207
|
+
observed_models: ['fixture-model'],
|
|
208
|
+
});
|
|
209
|
+
});
|
|
174
210
|
});
|
package/src/runner/runner.ts
CHANGED
|
@@ -2,9 +2,10 @@
|
|
|
2
2
|
* Scenario runner - main entry point for running test scenarios
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
+
import { nanoid } from 'nanoid';
|
|
5
6
|
import { createRunManifest } from '../artifacts/manifest';
|
|
6
7
|
import type { CaseResult, ManifestRedactionInfo } from '../artifacts/types';
|
|
7
|
-
import { createWorkloadIdentity } from '../provenance';
|
|
8
|
+
import { createExecutionProvenance, createWorkloadIdentity } from '../provenance';
|
|
8
9
|
import { Redactor } from '../redaction';
|
|
9
10
|
import { executeCase } from './executor';
|
|
10
11
|
import type { RunOptions, RunResult } from './types';
|
|
@@ -22,6 +23,8 @@ export async function runScenario(options: RunOptions): Promise<RunResult> {
|
|
|
22
23
|
concurrency = 1,
|
|
23
24
|
timeout,
|
|
24
25
|
retries,
|
|
26
|
+
repetition = { index: 1, total: 1 },
|
|
27
|
+
costProvenance,
|
|
25
28
|
redaction,
|
|
26
29
|
toolExecutor,
|
|
27
30
|
onCaseComplete,
|
|
@@ -42,6 +45,7 @@ export async function runScenario(options: RunOptions): Promise<RunResult> {
|
|
|
42
45
|
onProgress?.(`Running ${cases.length} test cases...`);
|
|
43
46
|
|
|
44
47
|
const startTime = new Date();
|
|
48
|
+
const runId = nanoid(12);
|
|
45
49
|
const results: CaseResult[] = [];
|
|
46
50
|
|
|
47
51
|
if (concurrency === 1) {
|
|
@@ -51,8 +55,11 @@ export async function runScenario(options: RunOptions): Promise<RunResult> {
|
|
|
51
55
|
const result = await executeCase(testCase, {
|
|
52
56
|
client,
|
|
53
57
|
scenario,
|
|
58
|
+
requestedModel: resolvedConfig?.model,
|
|
54
59
|
timeout: testCase.timeout || timeout,
|
|
55
60
|
retries: testCase.retries ?? retries,
|
|
61
|
+
runId,
|
|
62
|
+
repetition,
|
|
56
63
|
redaction,
|
|
57
64
|
toolExecutor,
|
|
58
65
|
});
|
|
@@ -70,8 +77,11 @@ export async function runScenario(options: RunOptions): Promise<RunResult> {
|
|
|
70
77
|
const result = await executeCase(testCase, {
|
|
71
78
|
client,
|
|
72
79
|
scenario,
|
|
80
|
+
requestedModel: resolvedConfig?.model,
|
|
73
81
|
timeout: testCase.timeout || timeout,
|
|
74
82
|
retries: testCase.retries ?? retries,
|
|
83
|
+
runId,
|
|
84
|
+
repetition,
|
|
75
85
|
redaction,
|
|
76
86
|
toolExecutor,
|
|
77
87
|
});
|
|
@@ -122,9 +132,29 @@ export async function runScenario(options: RunOptions): Promise<RunResult> {
|
|
|
122
132
|
},
|
|
123
133
|
resolvedConfig,
|
|
124
134
|
workloadIdentity: createWorkloadIdentity(scenario),
|
|
135
|
+
executionProvenance: createExecutionProvenance({
|
|
136
|
+
provider: client.provider,
|
|
137
|
+
requestedModel: resolvedConfig?.model || scenario.model,
|
|
138
|
+
temperature: resolvedConfig?.temperature ?? scenario.temperature,
|
|
139
|
+
maxTokens: resolvedConfig?.max_tokens ?? scenario.maxTokens,
|
|
140
|
+
seed: scenario.seed,
|
|
141
|
+
cases: results,
|
|
142
|
+
}),
|
|
143
|
+
attemptEvidence: {
|
|
144
|
+
schema_version: '1',
|
|
145
|
+
repetition,
|
|
146
|
+
retry_policy: {
|
|
147
|
+
default_max_retries: retries ?? 0,
|
|
148
|
+
backoff: 'exponential',
|
|
149
|
+
initial_delay_ms: 1000,
|
|
150
|
+
},
|
|
151
|
+
...(timeout ? { timeout: { default_ms: timeout } } : {}),
|
|
152
|
+
},
|
|
153
|
+
costProvenance,
|
|
125
154
|
cases: results,
|
|
126
155
|
startTime,
|
|
127
156
|
endTime,
|
|
157
|
+
runId,
|
|
128
158
|
redaction: redactionInfo,
|
|
129
159
|
});
|
|
130
160
|
|
package/src/runner/types.ts
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
import type { ModelClient } from '../adapters/types';
|
|
6
|
-
import type { CaseResult, ResolvedConfig, RunManifest } from '../artifacts/types';
|
|
6
|
+
import type { CaseResult, CostProvenance, ResolvedConfig, RunManifest } from '../artifacts/types';
|
|
7
7
|
import type { RedactionConfig } from '../redaction/types';
|
|
8
8
|
import type { Scenario } from '../scenario/schema';
|
|
9
9
|
import type { ToolExecutor } from '../tools';
|
|
@@ -28,6 +28,10 @@ export interface RunOptions {
|
|
|
28
28
|
timeout?: number;
|
|
29
29
|
/** Number of retries per case */
|
|
30
30
|
retries?: number;
|
|
31
|
+
/** One-based coordinate for an independently planned repetition. */
|
|
32
|
+
repetition?: { index: number; total: number };
|
|
33
|
+
/** Attested or operator-supplied monetary evidence; omitted means unavailable. */
|
|
34
|
+
costProvenance?: CostProvenance;
|
|
31
35
|
/** Redaction configuration (CLI overrides scenario) */
|
|
32
36
|
redaction?: RedactionConfig;
|
|
33
37
|
/** SDK-only executor for explicitly supplied real tools. */
|
|
@@ -56,8 +60,12 @@ export interface RunResult {
|
|
|
56
60
|
export interface ExecutorContext {
|
|
57
61
|
client: ModelClient;
|
|
58
62
|
scenario: Scenario;
|
|
63
|
+
/** Effective model selected outside the scenario, such as a CLI override. */
|
|
64
|
+
requestedModel?: string;
|
|
59
65
|
timeout?: number;
|
|
60
66
|
retries?: number;
|
|
67
|
+
runId?: string;
|
|
68
|
+
repetition?: { index: number; total: number };
|
|
61
69
|
/** Redaction configuration for this execution */
|
|
62
70
|
redaction?: RedactionConfig;
|
|
63
71
|
toolExecutor?: ToolExecutor;
|
package/src/scenario/schema.ts
CHANGED
|
@@ -225,7 +225,7 @@ export const TestCaseSchema = z.object({
|
|
|
225
225
|
tags: z.array(z.string()).optional().default([]),
|
|
226
226
|
metadata: z.record(z.unknown()).optional().default({}),
|
|
227
227
|
timeout: z.number().optional(),
|
|
228
|
-
retries: z.number().optional().default(0),
|
|
228
|
+
retries: z.number().int().min(0).max(99).optional().default(0),
|
|
229
229
|
provider: ProviderSchema.optional(),
|
|
230
230
|
model: z.string().optional(),
|
|
231
231
|
variables: VariablesSchema.optional(),
|