@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.
@@ -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
  });
@@ -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,
@@ -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 */