@artemiskit/core 0.4.2 → 0.5.1
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 +3 -1
- package/dist/artifacts/manifest.d.ts.map +1 -1
- package/dist/artifacts/types.d.ts +48 -0
- package/dist/artifacts/types.d.ts.map +1 -1
- package/dist/index.js +186 -12
- 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 +2 -0
- package/dist/provenance/index.d.ts.map +1 -1
- package/dist/provenance/workload-identity.d.ts +12 -0
- package/dist/provenance/workload-identity.d.ts.map +1 -0
- package/dist/runner/executor.d.ts.map +1 -1
- package/dist/runner/runner.d.ts.map +1 -1
- package/dist/runner/types.d.ts +2 -0
- package/dist/runner/types.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/artifacts/manifest.test.ts +67 -1
- package/src/artifacts/manifest.ts +9 -1
- package/src/artifacts/types.ts +139 -0
- package/src/provenance/execution-provenance.test.ts +59 -0
- package/src/provenance/execution-provenance.ts +53 -0
- package/src/provenance/index.ts +2 -0
- package/src/provenance/workload-identity.test.ts +69 -0
- package/src/provenance/workload-identity.ts +92 -0
- package/src/runner/executor.test.ts +10 -0
- package/src/runner/executor.ts +71 -9
- package/src/runner/release-validation.test.ts +41 -0
- package/src/runner/runner.ts +12 -0
- package/src/runner/types.ts +2 -0
package/src/runner/executor.ts
CHANGED
|
@@ -100,6 +100,7 @@ export async function executeCase(
|
|
|
100
100
|
): Promise<CaseResult> {
|
|
101
101
|
const { timeout, retries = 0 } = context;
|
|
102
102
|
const caseStartTime = Date.now();
|
|
103
|
+
const requestedModel = testCase.model || context.requestedModel || context.scenario.model;
|
|
103
104
|
|
|
104
105
|
let lastError: Error | null = null;
|
|
105
106
|
|
|
@@ -135,6 +136,7 @@ export async function executeCase(
|
|
|
135
136
|
expected: testCase.expected,
|
|
136
137
|
tags: testCase.tags,
|
|
137
138
|
error: lastError?.message,
|
|
139
|
+
target: targetEvidence(context.client.provider, requestedModel),
|
|
138
140
|
};
|
|
139
141
|
}
|
|
140
142
|
|
|
@@ -143,7 +145,7 @@ async function executeCaseAttempt(
|
|
|
143
145
|
context: ExecutorContext,
|
|
144
146
|
timeout?: number
|
|
145
147
|
): Promise<CaseResult> {
|
|
146
|
-
const { client, scenario, redaction: cliRedaction, toolExecutor } = context;
|
|
148
|
+
const { client, scenario, requestedModel, redaction: cliRedaction, toolExecutor } = context;
|
|
147
149
|
|
|
148
150
|
// Merge scenario-level and case-level variables (case overrides scenario)
|
|
149
151
|
const variables = mergeVariables(scenario.variables, testCase.variables);
|
|
@@ -171,7 +173,7 @@ async function executeCaseAttempt(
|
|
|
171
173
|
const generate = () =>
|
|
172
174
|
client.generate({
|
|
173
175
|
prompt: loopPrompt,
|
|
174
|
-
model: testCase.model || scenario.model,
|
|
176
|
+
model: testCase.model || requestedModel || scenario.model,
|
|
175
177
|
temperature: scenario.temperature,
|
|
176
178
|
maxTokens: scenario.maxTokens,
|
|
177
179
|
seed: scenario.seed,
|
|
@@ -182,6 +184,7 @@ async function executeCaseAttempt(
|
|
|
182
184
|
let result = timeout
|
|
183
185
|
? await Promise.race([generatePromise, createTimeout(timeout)])
|
|
184
186
|
: await generatePromise;
|
|
187
|
+
const observedModels = [result.model];
|
|
185
188
|
const generationMetrics = {
|
|
186
189
|
latencyMs: result.latencyMs,
|
|
187
190
|
tokens: { ...result.tokens },
|
|
@@ -198,7 +201,12 @@ async function executeCaseAttempt(
|
|
|
198
201
|
terminationReason: 'tool_error',
|
|
199
202
|
},
|
|
200
203
|
generationMetrics,
|
|
201
|
-
'TOOL_EXECUTOR_REQUIRED'
|
|
204
|
+
'TOOL_EXECUTOR_REQUIRED',
|
|
205
|
+
targetEvidence(
|
|
206
|
+
client.provider,
|
|
207
|
+
testCase.model || requestedModel || scenario.model,
|
|
208
|
+
observedModels
|
|
209
|
+
)
|
|
202
210
|
);
|
|
203
211
|
}
|
|
204
212
|
const executor =
|
|
@@ -225,7 +233,12 @@ async function executeCaseAttempt(
|
|
|
225
233
|
terminationReason: 'duplicate_call',
|
|
226
234
|
},
|
|
227
235
|
generationMetrics,
|
|
228
|
-
'TOOL_DUPLICATE_CALL'
|
|
236
|
+
'TOOL_DUPLICATE_CALL',
|
|
237
|
+
targetEvidence(
|
|
238
|
+
client.provider,
|
|
239
|
+
testCase.model || requestedModel || scenario.model,
|
|
240
|
+
observedModels
|
|
241
|
+
)
|
|
229
242
|
);
|
|
230
243
|
}
|
|
231
244
|
seenCalls.add(fingerprint);
|
|
@@ -263,7 +276,12 @@ async function executeCaseAttempt(
|
|
|
263
276
|
: 'tool_error',
|
|
264
277
|
},
|
|
265
278
|
generationMetrics,
|
|
266
|
-
execution.error?.code ?? 'TOOL_EXECUTION_FAILED'
|
|
279
|
+
execution.error?.code ?? 'TOOL_EXECUTION_FAILED',
|
|
280
|
+
targetEvidence(
|
|
281
|
+
client.provider,
|
|
282
|
+
testCase.model || requestedModel || scenario.model,
|
|
283
|
+
observedModels
|
|
284
|
+
)
|
|
267
285
|
);
|
|
268
286
|
}
|
|
269
287
|
const content = JSON.stringify(execution.result ?? {});
|
|
@@ -281,7 +299,12 @@ async function executeCaseAttempt(
|
|
|
281
299
|
toolTrace,
|
|
282
300
|
{ status: 'error', steps: step + 1, terminationReason: 'timeout' },
|
|
283
301
|
generationMetrics,
|
|
284
|
-
'TOOL_LOOP_TIMEOUT'
|
|
302
|
+
'TOOL_LOOP_TIMEOUT',
|
|
303
|
+
targetEvidence(
|
|
304
|
+
client.provider,
|
|
305
|
+
testCase.model || requestedModel || scenario.model,
|
|
306
|
+
observedModels
|
|
307
|
+
)
|
|
285
308
|
);
|
|
286
309
|
}
|
|
287
310
|
const requestTimeout = timeout ? Math.min(timeout, remainingLoopTime) : remainingLoopTime;
|
|
@@ -298,9 +321,15 @@ async function executeCaseAttempt(
|
|
|
298
321
|
terminationReason: timedOut ? 'timeout' : 'tool_error',
|
|
299
322
|
},
|
|
300
323
|
generationMetrics,
|
|
301
|
-
timedOut ? 'TOOL_LOOP_TIMEOUT' : 'TOOL_GENERATION_FAILED'
|
|
324
|
+
timedOut ? 'TOOL_LOOP_TIMEOUT' : 'TOOL_GENERATION_FAILED',
|
|
325
|
+
targetEvidence(
|
|
326
|
+
client.provider,
|
|
327
|
+
testCase.model || requestedModel || scenario.model,
|
|
328
|
+
observedModels
|
|
329
|
+
)
|
|
302
330
|
);
|
|
303
331
|
}
|
|
332
|
+
observedModels.push(result.model);
|
|
304
333
|
generationMetrics.latencyMs += result.latencyMs;
|
|
305
334
|
generationMetrics.tokens.prompt += result.tokens.prompt;
|
|
306
335
|
generationMetrics.tokens.completion += result.tokens.completion;
|
|
@@ -316,7 +345,12 @@ async function executeCaseAttempt(
|
|
|
316
345
|
terminationReason: 'max_steps',
|
|
317
346
|
},
|
|
318
347
|
generationMetrics,
|
|
319
|
-
'TOOL_LOOP_MAX_STEPS'
|
|
348
|
+
'TOOL_LOOP_MAX_STEPS',
|
|
349
|
+
targetEvidence(
|
|
350
|
+
client.provider,
|
|
351
|
+
testCase.model || requestedModel || scenario.model,
|
|
352
|
+
observedModels
|
|
353
|
+
)
|
|
320
354
|
);
|
|
321
355
|
}
|
|
322
356
|
toolLoop = { status: 'completed', steps: toolTrace.length, terminationReason: 'completed' };
|
|
@@ -435,17 +469,44 @@ async function executeCaseAttempt(
|
|
|
435
469
|
tags: testCase.tags,
|
|
436
470
|
redaction: redactionInfo,
|
|
437
471
|
evidence: finalEvidence,
|
|
472
|
+
target: targetEvidence(
|
|
473
|
+
client.provider,
|
|
474
|
+
testCase.model || requestedModel || scenario.model,
|
|
475
|
+
observedModels
|
|
476
|
+
),
|
|
438
477
|
toolTrace: toolTrace.length ? toolTrace : undefined,
|
|
439
478
|
toolLoop,
|
|
440
479
|
};
|
|
441
480
|
}
|
|
442
481
|
|
|
482
|
+
function targetEvidence(
|
|
483
|
+
provider: string,
|
|
484
|
+
requestedModel?: string,
|
|
485
|
+
observedModels?: unknown[]
|
|
486
|
+
): NonNullable<CaseResult['target']> {
|
|
487
|
+
const observed = [
|
|
488
|
+
...new Set(
|
|
489
|
+
(observedModels ?? []).filter(
|
|
490
|
+
(model): model is string => typeof model === 'string' && model.length > 0
|
|
491
|
+
)
|
|
492
|
+
),
|
|
493
|
+
]
|
|
494
|
+
.map((model) => sanitizeArtifactText(model, 200))
|
|
495
|
+
.filter((model): model is string => Boolean(model));
|
|
496
|
+
return {
|
|
497
|
+
provider: sanitizeArtifactText(provider, 100) ?? 'unknown',
|
|
498
|
+
...(requestedModel ? { requested_model: sanitizeArtifactText(requestedModel, 200) } : {}),
|
|
499
|
+
...(observed.length ? { observed_models: observed } : {}),
|
|
500
|
+
};
|
|
501
|
+
}
|
|
502
|
+
|
|
443
503
|
function createToolLoopError(
|
|
444
504
|
testCase: TestCase,
|
|
445
505
|
toolTrace: ToolTraceEntry[],
|
|
446
506
|
toolLoop: ToolLoopSummary,
|
|
447
507
|
generationMetrics: Pick<CaseResult, 'latencyMs' | 'tokens'>,
|
|
448
|
-
code: string
|
|
508
|
+
code: string,
|
|
509
|
+
target?: CaseResult['target']
|
|
449
510
|
): ToolLoopError {
|
|
450
511
|
return new ToolLoopError(code, {
|
|
451
512
|
id: testCase.id,
|
|
@@ -462,6 +523,7 @@ function createToolLoopError(
|
|
|
462
523
|
expected: testCase.expected,
|
|
463
524
|
tags: testCase.tags,
|
|
464
525
|
error: code,
|
|
526
|
+
target,
|
|
465
527
|
toolTrace,
|
|
466
528
|
toolLoop,
|
|
467
529
|
});
|
|
@@ -91,6 +91,18 @@ describe('release validation: fixture-backed workflow cases', () => {
|
|
|
91
91
|
failed_cases: 2,
|
|
92
92
|
success_rate: 0.6,
|
|
93
93
|
});
|
|
94
|
+
expect(result.manifest.workload_identity).toMatchObject({
|
|
95
|
+
schema_version: '1',
|
|
96
|
+
workload: { algorithm: 'sha256' },
|
|
97
|
+
rubric: { algorithm: 'sha256' },
|
|
98
|
+
});
|
|
99
|
+
expect(result.manifest.execution_provenance).toMatchObject({
|
|
100
|
+
schema_version: '1',
|
|
101
|
+
target: {
|
|
102
|
+
provider: 'fixture',
|
|
103
|
+
observed_models: ['fixture-model'],
|
|
104
|
+
},
|
|
105
|
+
});
|
|
94
106
|
});
|
|
95
107
|
|
|
96
108
|
test('RV-02 retains independent logistics tool evidence through a fixture-backed workflow', async () => {
|
|
@@ -166,4 +178,33 @@ describe('release validation: fixture-backed workflow cases', () => {
|
|
|
166
178
|
toolTrace: [{ toolCall: { id: 'capacity-1' }, result: { available: true } }],
|
|
167
179
|
});
|
|
168
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
|
+
});
|
|
169
210
|
});
|
package/src/runner/runner.ts
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
import { createRunManifest } from '../artifacts/manifest';
|
|
6
6
|
import type { CaseResult, ManifestRedactionInfo } from '../artifacts/types';
|
|
7
|
+
import { createExecutionProvenance, createWorkloadIdentity } from '../provenance';
|
|
7
8
|
import { Redactor } from '../redaction';
|
|
8
9
|
import { executeCase } from './executor';
|
|
9
10
|
import type { RunOptions, RunResult } from './types';
|
|
@@ -50,6 +51,7 @@ export async function runScenario(options: RunOptions): Promise<RunResult> {
|
|
|
50
51
|
const result = await executeCase(testCase, {
|
|
51
52
|
client,
|
|
52
53
|
scenario,
|
|
54
|
+
requestedModel: resolvedConfig?.model,
|
|
53
55
|
timeout: testCase.timeout || timeout,
|
|
54
56
|
retries: testCase.retries ?? retries,
|
|
55
57
|
redaction,
|
|
@@ -69,6 +71,7 @@ export async function runScenario(options: RunOptions): Promise<RunResult> {
|
|
|
69
71
|
const result = await executeCase(testCase, {
|
|
70
72
|
client,
|
|
71
73
|
scenario,
|
|
74
|
+
requestedModel: resolvedConfig?.model,
|
|
72
75
|
timeout: testCase.timeout || timeout,
|
|
73
76
|
retries: testCase.retries ?? retries,
|
|
74
77
|
redaction,
|
|
@@ -120,6 +123,15 @@ export async function runScenario(options: RunOptions): Promise<RunResult> {
|
|
|
120
123
|
seed: scenario.seed,
|
|
121
124
|
},
|
|
122
125
|
resolvedConfig,
|
|
126
|
+
workloadIdentity: createWorkloadIdentity(scenario),
|
|
127
|
+
executionProvenance: createExecutionProvenance({
|
|
128
|
+
provider: client.provider,
|
|
129
|
+
requestedModel: resolvedConfig?.model || scenario.model,
|
|
130
|
+
temperature: resolvedConfig?.temperature ?? scenario.temperature,
|
|
131
|
+
maxTokens: resolvedConfig?.max_tokens ?? scenario.maxTokens,
|
|
132
|
+
seed: scenario.seed,
|
|
133
|
+
cases: results,
|
|
134
|
+
}),
|
|
123
135
|
cases: results,
|
|
124
136
|
startTime,
|
|
125
137
|
endTime,
|
package/src/runner/types.ts
CHANGED
|
@@ -56,6 +56,8 @@ export interface RunResult {
|
|
|
56
56
|
export interface ExecutorContext {
|
|
57
57
|
client: ModelClient;
|
|
58
58
|
scenario: Scenario;
|
|
59
|
+
/** Effective model selected outside the scenario, such as a CLI override. */
|
|
60
|
+
requestedModel?: string;
|
|
59
61
|
timeout?: number;
|
|
60
62
|
retries?: number;
|
|
61
63
|
/** Redaction configuration for this execution */
|