@artemiskit/core 0.5.1 → 0.5.3
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 +4 -1
- package/dist/artifacts/manifest.d.ts.map +1 -1
- package/dist/artifacts/types.d.ts +58 -0
- package/dist/artifacts/types.d.ts.map +1 -1
- package/dist/comparison/eligibility.d.ts +26 -0
- package/dist/comparison/eligibility.d.ts.map +1 -0
- package/dist/comparison/index.d.ts +2 -0
- package/dist/comparison/index.d.ts.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +590 -392
- package/dist/runner/executor.d.ts.map +1 -1
- package/dist/runner/runner.d.ts.map +1 -1
- package/dist/runner/types.d.ts +13 -1
- package/dist/runner/types.d.ts.map +1 -1
- package/dist/storage/local.d.ts.map +1 -1
- package/dist/storage/supabase.d.ts.map +1 -1
- package/dist/storage/types.d.ts +5 -1
- package/dist/storage/types.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/artifacts/manifest.test.ts +87 -1
- package/src/artifacts/manifest.ts +21 -29
- package/src/artifacts/types.ts +173 -0
- package/src/comparison/eligibility.test.ts +101 -0
- package/src/comparison/eligibility.ts +116 -0
- package/src/comparison/index.ts +8 -0
- package/src/index.ts +3 -0
- package/src/runner/executor.test.ts +50 -0
- package/src/runner/executor.ts +74 -2
- package/src/runner/runner.ts +20 -0
- package/src/runner/types.ts +7 -1
- package/src/scenario/schema.ts +1 -1
- package/src/storage/local.test.ts +39 -0
- package/src/storage/local.ts +9 -1
- package/src/storage/supabase.ts +9 -1
- package/src/storage/types.ts +5 -1
package/dist/index.js
CHANGED
|
@@ -19824,7 +19824,7 @@ var TestCaseSchema = objectType({
|
|
|
19824
19824
|
tags: arrayType(stringType()).optional().default([]),
|
|
19825
19825
|
metadata: recordType(unknownType()).optional().default({}),
|
|
19826
19826
|
timeout: numberType().optional(),
|
|
19827
|
-
retries: numberType().optional().default(0),
|
|
19827
|
+
retries: numberType().int().min(0).max(99).optional().default(0),
|
|
19828
19828
|
provider: ProviderSchema.optional(),
|
|
19829
19829
|
model: stringType().optional(),
|
|
19830
19830
|
variables: VariablesSchema.optional(),
|
|
@@ -20524,17 +20524,48 @@ function mergeRedactionConfig(scenarioConfig, caseConfig, cliConfig) {
|
|
|
20524
20524
|
}
|
|
20525
20525
|
async function executeCase(testCase, context) {
|
|
20526
20526
|
const { timeout, retries = 0 } = context;
|
|
20527
|
+
if (!Number.isSafeInteger(retries) || retries < 0 || retries > 99) {
|
|
20528
|
+
throw new RangeError("retries must be a whole number between 0 and 99");
|
|
20529
|
+
}
|
|
20527
20530
|
const caseStartTime = Date.now();
|
|
20528
20531
|
const requestedModel = testCase.model || context.requestedModel || context.scenario.model;
|
|
20532
|
+
const retryChainId = `${context.runId ?? "untracked"}:${testCase.id}`;
|
|
20533
|
+
const repetitionIndex = context.repetition?.index ?? 1;
|
|
20534
|
+
const attemptEvidence = [];
|
|
20529
20535
|
let lastError = null;
|
|
20530
20536
|
for (let attempt = 0;attempt <= retries; attempt++) {
|
|
20537
|
+
const attemptStartTime = Date.now();
|
|
20531
20538
|
try {
|
|
20532
20539
|
const result = await executeCaseAttempt(testCase, context, timeout);
|
|
20533
|
-
return
|
|
20540
|
+
return withAttemptEvidence(result, attemptEvidence, {
|
|
20541
|
+
retryChainId,
|
|
20542
|
+
repetitionIndex,
|
|
20543
|
+
attemptNumber: attempt + 1,
|
|
20544
|
+
includedInOutcome: true,
|
|
20545
|
+
latencyMs: result.latencyMs
|
|
20546
|
+
});
|
|
20534
20547
|
} catch (error) {
|
|
20535
20548
|
lastError = error;
|
|
20536
|
-
if (error instanceof ToolLoopError)
|
|
20537
|
-
return
|
|
20549
|
+
if (error instanceof ToolLoopError) {
|
|
20550
|
+
return withAttemptEvidence(error.caseResult, attemptEvidence, {
|
|
20551
|
+
retryChainId,
|
|
20552
|
+
repetitionIndex,
|
|
20553
|
+
attemptNumber: attempt + 1,
|
|
20554
|
+
includedInOutcome: true,
|
|
20555
|
+
latencyMs: error.caseResult.latencyMs,
|
|
20556
|
+
errorCode: "tool_error"
|
|
20557
|
+
});
|
|
20558
|
+
}
|
|
20559
|
+
attemptEvidence.push({
|
|
20560
|
+
attempt_id: `${retryChainId}:${attempt + 1}`,
|
|
20561
|
+
retry_chain_id: retryChainId,
|
|
20562
|
+
repetition_index: repetitionIndex,
|
|
20563
|
+
attempt_number: attempt + 1,
|
|
20564
|
+
status: "error",
|
|
20565
|
+
included_in_outcome: false,
|
|
20566
|
+
latency_ms: Date.now() - attemptStartTime,
|
|
20567
|
+
error_code: error instanceof TimeoutError ? "timeout" : "target_error"
|
|
20568
|
+
});
|
|
20538
20569
|
if (attempt < retries) {
|
|
20539
20570
|
await sleep(2 ** attempt * 1000);
|
|
20540
20571
|
}
|
|
@@ -20547,6 +20578,10 @@ async function executeCase(testCase, context) {
|
|
|
20547
20578
|
ok: false,
|
|
20548
20579
|
status: "error",
|
|
20549
20580
|
attempts: retries + 1,
|
|
20581
|
+
attempt_evidence: attemptEvidence.map((entry, index) => ({
|
|
20582
|
+
...entry,
|
|
20583
|
+
included_in_outcome: index === attemptEvidence.length - 1
|
|
20584
|
+
})),
|
|
20550
20585
|
score: 0,
|
|
20551
20586
|
matcherType: testCase.expected.type,
|
|
20552
20587
|
reason: `Failed after ${retries + 1} attempts: ${lastError?.message}`,
|
|
@@ -20560,6 +20595,28 @@ async function executeCase(testCase, context) {
|
|
|
20560
20595
|
target: targetEvidence(context.client.provider, requestedModel)
|
|
20561
20596
|
};
|
|
20562
20597
|
}
|
|
20598
|
+
function withAttemptEvidence(result, priorAttempts, input) {
|
|
20599
|
+
return {
|
|
20600
|
+
...result,
|
|
20601
|
+
attempts: input.attemptNumber,
|
|
20602
|
+
attempt_evidence: [
|
|
20603
|
+
...priorAttempts,
|
|
20604
|
+
{
|
|
20605
|
+
attempt_id: `${input.retryChainId}:${input.attemptNumber}`,
|
|
20606
|
+
retry_chain_id: input.retryChainId,
|
|
20607
|
+
repetition_index: input.repetitionIndex,
|
|
20608
|
+
attempt_number: input.attemptNumber,
|
|
20609
|
+
status: getTerminalStatus(result),
|
|
20610
|
+
included_in_outcome: input.includedInOutcome,
|
|
20611
|
+
latency_ms: input.latencyMs,
|
|
20612
|
+
...input.errorCode ? { error_code: input.errorCode } : {}
|
|
20613
|
+
}
|
|
20614
|
+
]
|
|
20615
|
+
};
|
|
20616
|
+
}
|
|
20617
|
+
function getTerminalStatus(result) {
|
|
20618
|
+
return result.status ?? (result.ok ? "passed" : result.error ? "error" : "failed");
|
|
20619
|
+
}
|
|
20563
20620
|
async function executeCaseAttempt(testCase, context, timeout) {
|
|
20564
20621
|
const { client, scenario, requestedModel, redaction: cliRedaction, toolExecutor } = context;
|
|
20565
20622
|
const variables = mergeVariables(scenario.variables, testCase.variables);
|
|
@@ -20928,374 +20985,61 @@ function nanoid(size = 21) {
|
|
|
20928
20985
|
return id;
|
|
20929
20986
|
}
|
|
20930
20987
|
|
|
20931
|
-
// src/
|
|
20932
|
-
|
|
20933
|
-
|
|
20934
|
-
|
|
20935
|
-
|
|
20936
|
-
|
|
20937
|
-
|
|
20938
|
-
|
|
20939
|
-
|
|
20940
|
-
|
|
20941
|
-
|
|
20942
|
-
|
|
20943
|
-
|
|
20944
|
-
|
|
20945
|
-
|
|
20946
|
-
|
|
20947
|
-
|
|
20948
|
-
|
|
20949
|
-
|
|
20950
|
-
|
|
20951
|
-
|
|
20952
|
-
|
|
20953
|
-
|
|
20954
|
-
|
|
20955
|
-
|
|
20956
|
-
|
|
20957
|
-
|
|
20958
|
-
|
|
20959
|
-
|
|
20960
|
-
promptPer1K: 0.002,
|
|
20961
|
-
completionPer1K: 0.008,
|
|
20962
|
-
lastUpdated: "2026-01",
|
|
20963
|
-
notes: "1M context window"
|
|
20964
|
-
},
|
|
20965
|
-
"gpt-4.1-mini": {
|
|
20966
|
-
promptPer1K: 0.0004,
|
|
20967
|
-
completionPer1K: 0.0016,
|
|
20968
|
-
lastUpdated: "2026-01"
|
|
20969
|
-
},
|
|
20970
|
-
"gpt-4.1-nano": {
|
|
20971
|
-
promptPer1K: 0.0001,
|
|
20972
|
-
completionPer1K: 0.0004,
|
|
20973
|
-
lastUpdated: "2026-01"
|
|
20974
|
-
},
|
|
20975
|
-
"gpt-4o": {
|
|
20976
|
-
promptPer1K: 0.0025,
|
|
20977
|
-
completionPer1K: 0.01,
|
|
20978
|
-
lastUpdated: "2026-01",
|
|
20979
|
-
notes: "128K context window"
|
|
20980
|
-
},
|
|
20981
|
-
"gpt-4o-mini": {
|
|
20982
|
-
promptPer1K: 0.00015,
|
|
20983
|
-
completionPer1K: 0.0006,
|
|
20984
|
-
lastUpdated: "2026-01",
|
|
20985
|
-
notes: "128K context window"
|
|
20986
|
-
},
|
|
20987
|
-
o1: {
|
|
20988
|
-
promptPer1K: 0.015,
|
|
20989
|
-
completionPer1K: 0.06,
|
|
20990
|
-
lastUpdated: "2026-01",
|
|
20991
|
-
notes: "Reasoning model - internal thinking tokens billed as output"
|
|
20992
|
-
},
|
|
20993
|
-
o3: {
|
|
20994
|
-
promptPer1K: 0.002,
|
|
20995
|
-
completionPer1K: 0.008,
|
|
20996
|
-
lastUpdated: "2026-01"
|
|
20997
|
-
},
|
|
20998
|
-
"o3-mini": {
|
|
20999
|
-
promptPer1K: 0.0011,
|
|
21000
|
-
completionPer1K: 0.0044,
|
|
21001
|
-
lastUpdated: "2026-01"
|
|
21002
|
-
},
|
|
21003
|
-
"o4-mini": {
|
|
21004
|
-
promptPer1K: 0.0011,
|
|
21005
|
-
completionPer1K: 0.0044,
|
|
21006
|
-
lastUpdated: "2026-01"
|
|
21007
|
-
},
|
|
21008
|
-
"gpt-4-turbo": {
|
|
21009
|
-
promptPer1K: 0.01,
|
|
21010
|
-
completionPer1K: 0.03,
|
|
21011
|
-
lastUpdated: "2026-01"
|
|
21012
|
-
},
|
|
21013
|
-
"gpt-4": {
|
|
21014
|
-
promptPer1K: 0.03,
|
|
21015
|
-
completionPer1K: 0.06,
|
|
21016
|
-
lastUpdated: "2026-01"
|
|
21017
|
-
},
|
|
21018
|
-
"gpt-3.5-turbo": {
|
|
21019
|
-
promptPer1K: 0.0005,
|
|
21020
|
-
completionPer1K: 0.0015,
|
|
21021
|
-
lastUpdated: "2026-01"
|
|
21022
|
-
},
|
|
21023
|
-
"claude-opus-4.5": {
|
|
21024
|
-
promptPer1K: 0.005,
|
|
21025
|
-
completionPer1K: 0.025,
|
|
21026
|
-
lastUpdated: "2026-01",
|
|
21027
|
-
notes: "Most capable Claude model"
|
|
21028
|
-
},
|
|
21029
|
-
"claude-sonnet-4.5": {
|
|
21030
|
-
promptPer1K: 0.003,
|
|
21031
|
-
completionPer1K: 0.015,
|
|
21032
|
-
lastUpdated: "2026-01",
|
|
21033
|
-
notes: "Balanced performance and cost"
|
|
21034
|
-
},
|
|
21035
|
-
"claude-haiku-4.5": {
|
|
21036
|
-
promptPer1K: 0.001,
|
|
21037
|
-
completionPer1K: 0.005,
|
|
21038
|
-
lastUpdated: "2026-01",
|
|
21039
|
-
notes: "Fastest Claude model"
|
|
21040
|
-
},
|
|
21041
|
-
"claude-opus-4": {
|
|
21042
|
-
promptPer1K: 0.015,
|
|
21043
|
-
completionPer1K: 0.075,
|
|
21044
|
-
lastUpdated: "2026-01"
|
|
21045
|
-
},
|
|
21046
|
-
"claude-opus-4.1": {
|
|
21047
|
-
promptPer1K: 0.015,
|
|
21048
|
-
completionPer1K: 0.075,
|
|
21049
|
-
lastUpdated: "2026-01"
|
|
21050
|
-
},
|
|
21051
|
-
"claude-sonnet-4": {
|
|
21052
|
-
promptPer1K: 0.003,
|
|
21053
|
-
completionPer1K: 0.015,
|
|
21054
|
-
lastUpdated: "2026-01"
|
|
21055
|
-
},
|
|
21056
|
-
"claude-sonnet-3.7": {
|
|
21057
|
-
promptPer1K: 0.003,
|
|
21058
|
-
completionPer1K: 0.015,
|
|
21059
|
-
lastUpdated: "2026-01"
|
|
21060
|
-
},
|
|
21061
|
-
"claude-3-7-sonnet": {
|
|
21062
|
-
promptPer1K: 0.003,
|
|
21063
|
-
completionPer1K: 0.015,
|
|
21064
|
-
lastUpdated: "2026-01"
|
|
21065
|
-
},
|
|
21066
|
-
"claude-3-5-sonnet-20241022": {
|
|
21067
|
-
promptPer1K: 0.003,
|
|
21068
|
-
completionPer1K: 0.015,
|
|
21069
|
-
lastUpdated: "2026-01"
|
|
21070
|
-
},
|
|
21071
|
-
"claude-3-5-haiku-20241022": {
|
|
21072
|
-
promptPer1K: 0.0008,
|
|
21073
|
-
completionPer1K: 0.004,
|
|
21074
|
-
lastUpdated: "2026-01"
|
|
21075
|
-
},
|
|
21076
|
-
"claude-haiku-3.5": {
|
|
21077
|
-
promptPer1K: 0.0008,
|
|
21078
|
-
completionPer1K: 0.004,
|
|
21079
|
-
lastUpdated: "2026-01"
|
|
21080
|
-
},
|
|
21081
|
-
"claude-3-opus": {
|
|
21082
|
-
promptPer1K: 0.015,
|
|
21083
|
-
completionPer1K: 0.075,
|
|
21084
|
-
lastUpdated: "2026-01"
|
|
21085
|
-
},
|
|
21086
|
-
"claude-3-sonnet": {
|
|
21087
|
-
promptPer1K: 0.003,
|
|
21088
|
-
completionPer1K: 0.015,
|
|
21089
|
-
lastUpdated: "2026-01"
|
|
21090
|
-
},
|
|
21091
|
-
"claude-3-haiku": {
|
|
21092
|
-
promptPer1K: 0.00025,
|
|
21093
|
-
completionPer1K: 0.00125,
|
|
21094
|
-
lastUpdated: "2026-01"
|
|
21095
|
-
},
|
|
21096
|
-
"claude-3.5-sonnet": {
|
|
21097
|
-
promptPer1K: 0.003,
|
|
21098
|
-
completionPer1K: 0.015,
|
|
21099
|
-
lastUpdated: "2026-01"
|
|
21100
|
-
},
|
|
21101
|
-
"claude-3.5-haiku": {
|
|
21102
|
-
promptPer1K: 0.0008,
|
|
21103
|
-
completionPer1K: 0.004,
|
|
21104
|
-
lastUpdated: "2026-01"
|
|
21105
|
-
}
|
|
21106
|
-
};
|
|
21107
|
-
var DEFAULT_PRICING = {
|
|
21108
|
-
promptPer1K: 0.003,
|
|
21109
|
-
completionPer1K: 0.015,
|
|
21110
|
-
lastUpdated: "2026-01",
|
|
21111
|
-
notes: "Default pricing - verify with provider"
|
|
21112
|
-
};
|
|
21113
|
-
function getModelPricing(model) {
|
|
21114
|
-
if (MODEL_PRICING[model]) {
|
|
21115
|
-
return MODEL_PRICING[model];
|
|
20988
|
+
// src/provenance/environment.ts
|
|
20989
|
+
function getEnvironmentInfo() {
|
|
20990
|
+
return {
|
|
20991
|
+
node_version: process.version,
|
|
20992
|
+
platform: process.platform,
|
|
20993
|
+
arch: process.arch
|
|
20994
|
+
};
|
|
20995
|
+
}
|
|
20996
|
+
|
|
20997
|
+
// src/provenance/git.ts
|
|
20998
|
+
import { execSync } from "child_process";
|
|
20999
|
+
function getGitInfo() {
|
|
21000
|
+
try {
|
|
21001
|
+
const commit = execGit("rev-parse HEAD");
|
|
21002
|
+
const branch = execGit("rev-parse --abbrev-ref HEAD");
|
|
21003
|
+
const dirty = execGit("status --porcelain").length > 0;
|
|
21004
|
+
const remote = execGit("remote get-url origin", true);
|
|
21005
|
+
return {
|
|
21006
|
+
commit,
|
|
21007
|
+
branch,
|
|
21008
|
+
dirty,
|
|
21009
|
+
remote: remote || undefined
|
|
21010
|
+
};
|
|
21011
|
+
} catch {
|
|
21012
|
+
return {
|
|
21013
|
+
commit: "unknown",
|
|
21014
|
+
branch: "unknown",
|
|
21015
|
+
dirty: false
|
|
21016
|
+
};
|
|
21116
21017
|
}
|
|
21117
|
-
|
|
21118
|
-
|
|
21119
|
-
|
|
21120
|
-
|
|
21018
|
+
}
|
|
21019
|
+
function execGit(command, allowFailure = false) {
|
|
21020
|
+
try {
|
|
21021
|
+
return execSync(`git ${command}`, {
|
|
21022
|
+
encoding: "utf-8",
|
|
21023
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
21024
|
+
}).trim();
|
|
21025
|
+
} catch {
|
|
21026
|
+
if (allowFailure) {
|
|
21027
|
+
return "";
|
|
21121
21028
|
}
|
|
21029
|
+
throw new Error(`Git command failed: ${command}`);
|
|
21122
21030
|
}
|
|
21123
|
-
|
|
21124
|
-
|
|
21125
|
-
|
|
21126
|
-
|
|
21127
|
-
|
|
21128
|
-
|
|
21129
|
-
|
|
21130
|
-
|
|
21131
|
-
|
|
21132
|
-
|
|
21133
|
-
|
|
21134
|
-
|
|
21135
|
-
if (lowerModel.includes("gpt-5")) {
|
|
21136
|
-
return MODEL_PRICING["gpt-5"];
|
|
21137
|
-
}
|
|
21138
|
-
if (lowerModel.includes("gpt-4.1-mini")) {
|
|
21139
|
-
return MODEL_PRICING["gpt-4.1-mini"];
|
|
21140
|
-
}
|
|
21141
|
-
if (lowerModel.includes("gpt-4.1-nano")) {
|
|
21142
|
-
return MODEL_PRICING["gpt-4.1-nano"];
|
|
21143
|
-
}
|
|
21144
|
-
if (lowerModel.includes("gpt-4.1")) {
|
|
21145
|
-
return MODEL_PRICING["gpt-4.1"];
|
|
21146
|
-
}
|
|
21147
|
-
if (lowerModel.includes("gpt-4o-mini")) {
|
|
21148
|
-
return MODEL_PRICING["gpt-4o-mini"];
|
|
21149
|
-
}
|
|
21150
|
-
if (lowerModel.includes("gpt-4o")) {
|
|
21151
|
-
return MODEL_PRICING["gpt-4o"];
|
|
21152
|
-
}
|
|
21153
|
-
if (lowerModel.includes("o4-mini")) {
|
|
21154
|
-
return MODEL_PRICING["o4-mini"];
|
|
21155
|
-
}
|
|
21156
|
-
if (lowerModel.includes("o3-mini")) {
|
|
21157
|
-
return MODEL_PRICING["o3-mini"];
|
|
21158
|
-
}
|
|
21159
|
-
if (lowerModel.includes("o3")) {
|
|
21160
|
-
return MODEL_PRICING.o3;
|
|
21161
|
-
}
|
|
21162
|
-
if (lowerModel.includes("o1")) {
|
|
21163
|
-
return MODEL_PRICING.o1;
|
|
21164
|
-
}
|
|
21165
|
-
if (lowerModel.includes("gpt-4-turbo")) {
|
|
21166
|
-
return MODEL_PRICING["gpt-4-turbo"];
|
|
21167
|
-
}
|
|
21168
|
-
if (lowerModel.includes("gpt-4")) {
|
|
21169
|
-
return MODEL_PRICING["gpt-4"];
|
|
21170
|
-
}
|
|
21171
|
-
if (lowerModel.includes("gpt-3.5")) {
|
|
21172
|
-
return MODEL_PRICING["gpt-3.5-turbo"];
|
|
21173
|
-
}
|
|
21174
|
-
if (lowerModel.includes("opus-4.5") || lowerModel.includes("opus-4-5")) {
|
|
21175
|
-
return MODEL_PRICING["claude-opus-4.5"];
|
|
21176
|
-
}
|
|
21177
|
-
if (lowerModel.includes("sonnet-4.5") || lowerModel.includes("sonnet-4-5")) {
|
|
21178
|
-
return MODEL_PRICING["claude-sonnet-4.5"];
|
|
21179
|
-
}
|
|
21180
|
-
if (lowerModel.includes("haiku-4.5") || lowerModel.includes("haiku-4-5")) {
|
|
21181
|
-
return MODEL_PRICING["claude-haiku-4.5"];
|
|
21182
|
-
}
|
|
21183
|
-
if (lowerModel.includes("opus-4.1") || lowerModel.includes("opus-4-1")) {
|
|
21184
|
-
return MODEL_PRICING["claude-opus-4.1"];
|
|
21185
|
-
}
|
|
21186
|
-
if (lowerModel.includes("opus-4")) {
|
|
21187
|
-
return MODEL_PRICING["claude-opus-4"];
|
|
21188
|
-
}
|
|
21189
|
-
if (lowerModel.includes("sonnet-4")) {
|
|
21190
|
-
return MODEL_PRICING["claude-sonnet-4"];
|
|
21191
|
-
}
|
|
21192
|
-
if (lowerModel.includes("sonnet-3.7") || lowerModel.includes("sonnet-3-7")) {
|
|
21193
|
-
return MODEL_PRICING["claude-sonnet-3.7"];
|
|
21194
|
-
}
|
|
21195
|
-
if (lowerModel.includes("claude-3-5-sonnet") || lowerModel.includes("claude-3.5-sonnet")) {
|
|
21196
|
-
return MODEL_PRICING["claude-3.5-sonnet"];
|
|
21197
|
-
}
|
|
21198
|
-
if (lowerModel.includes("claude-3-5-haiku") || lowerModel.includes("claude-3.5-haiku")) {
|
|
21199
|
-
return MODEL_PRICING["claude-3.5-haiku"];
|
|
21200
|
-
}
|
|
21201
|
-
if (lowerModel.includes("claude-3-opus")) {
|
|
21202
|
-
return MODEL_PRICING["claude-3-opus"];
|
|
21203
|
-
}
|
|
21204
|
-
if (lowerModel.includes("claude-3-sonnet")) {
|
|
21205
|
-
return MODEL_PRICING["claude-3-sonnet"];
|
|
21206
|
-
}
|
|
21207
|
-
if (lowerModel.includes("claude-3-haiku")) {
|
|
21208
|
-
return MODEL_PRICING["claude-3-haiku"];
|
|
21209
|
-
}
|
|
21210
|
-
if (lowerModel.includes("claude")) {
|
|
21211
|
-
return MODEL_PRICING["claude-sonnet-4.5"];
|
|
21212
|
-
}
|
|
21213
|
-
return DEFAULT_PRICING;
|
|
21214
|
-
}
|
|
21215
|
-
function estimateCost(promptTokens, completionTokens, model) {
|
|
21216
|
-
const pricing = getModelPricing(model);
|
|
21217
|
-
const promptCostUsd = promptTokens / 1000 * pricing.promptPer1K;
|
|
21218
|
-
const completionCostUsd = completionTokens / 1000 * pricing.completionPer1K;
|
|
21219
|
-
const totalUsd = promptCostUsd + completionCostUsd;
|
|
21220
|
-
return {
|
|
21221
|
-
totalUsd,
|
|
21222
|
-
promptCostUsd,
|
|
21223
|
-
completionCostUsd,
|
|
21224
|
-
model,
|
|
21225
|
-
pricing
|
|
21226
|
-
};
|
|
21227
|
-
}
|
|
21228
|
-
function formatCost(costUsd) {
|
|
21229
|
-
if (costUsd < 0.01) {
|
|
21230
|
-
return `$${(costUsd * 100).toFixed(4)} cents`;
|
|
21231
|
-
}
|
|
21232
|
-
if (costUsd < 1) {
|
|
21233
|
-
return `$${costUsd.toFixed(4)}`;
|
|
21234
|
-
}
|
|
21235
|
-
return `$${costUsd.toFixed(2)}`;
|
|
21236
|
-
}
|
|
21237
|
-
function listKnownModels() {
|
|
21238
|
-
return Object.entries(MODEL_PRICING).map(([model, pricing]) => ({
|
|
21239
|
-
model,
|
|
21240
|
-
pricing
|
|
21241
|
-
}));
|
|
21242
|
-
}
|
|
21243
|
-
|
|
21244
|
-
// src/provenance/environment.ts
|
|
21245
|
-
function getEnvironmentInfo() {
|
|
21246
|
-
return {
|
|
21247
|
-
node_version: process.version,
|
|
21248
|
-
platform: process.platform,
|
|
21249
|
-
arch: process.arch
|
|
21250
|
-
};
|
|
21251
|
-
}
|
|
21252
|
-
|
|
21253
|
-
// src/provenance/git.ts
|
|
21254
|
-
import { execSync } from "child_process";
|
|
21255
|
-
function getGitInfo() {
|
|
21256
|
-
try {
|
|
21257
|
-
const commit = execGit("rev-parse HEAD");
|
|
21258
|
-
const branch = execGit("rev-parse --abbrev-ref HEAD");
|
|
21259
|
-
const dirty = execGit("status --porcelain").length > 0;
|
|
21260
|
-
const remote = execGit("remote get-url origin", true);
|
|
21261
|
-
return {
|
|
21262
|
-
commit,
|
|
21263
|
-
branch,
|
|
21264
|
-
dirty,
|
|
21265
|
-
remote: remote || undefined
|
|
21266
|
-
};
|
|
21267
|
-
} catch {
|
|
21268
|
-
return {
|
|
21269
|
-
commit: "unknown",
|
|
21270
|
-
branch: "unknown",
|
|
21271
|
-
dirty: false
|
|
21272
|
-
};
|
|
21273
|
-
}
|
|
21274
|
-
}
|
|
21275
|
-
function execGit(command, allowFailure = false) {
|
|
21276
|
-
try {
|
|
21277
|
-
return execSync(`git ${command}`, {
|
|
21278
|
-
encoding: "utf-8",
|
|
21279
|
-
stdio: ["pipe", "pipe", "pipe"]
|
|
21280
|
-
}).trim();
|
|
21281
|
-
} catch {
|
|
21282
|
-
if (allowFailure) {
|
|
21283
|
-
return "";
|
|
21284
|
-
}
|
|
21285
|
-
throw new Error(`Git command failed: ${command}`);
|
|
21286
|
-
}
|
|
21287
|
-
}
|
|
21288
|
-
|
|
21289
|
-
// src/artifacts/types.ts
|
|
21290
|
-
var CASE_EVALUATION_STATUS_LABELS = {
|
|
21291
|
-
passed: "Passed",
|
|
21292
|
-
failed: "Failed criteria",
|
|
21293
|
-
invalid: "Invalid measurement",
|
|
21294
|
-
error: "Execution error"
|
|
21295
|
-
};
|
|
21296
|
-
function getCaseEvaluationStatus(caseResult) {
|
|
21297
|
-
if (caseResult.status === "passed" || caseResult.status === "failed" || caseResult.status === "invalid" || caseResult.status === "error") {
|
|
21298
|
-
return caseResult.status;
|
|
21031
|
+
}
|
|
21032
|
+
|
|
21033
|
+
// src/artifacts/types.ts
|
|
21034
|
+
var CASE_EVALUATION_STATUS_LABELS = {
|
|
21035
|
+
passed: "Passed",
|
|
21036
|
+
failed: "Failed criteria",
|
|
21037
|
+
invalid: "Invalid measurement",
|
|
21038
|
+
error: "Execution error"
|
|
21039
|
+
};
|
|
21040
|
+
function getCaseEvaluationStatus(caseResult) {
|
|
21041
|
+
if (caseResult.status === "passed" || caseResult.status === "failed" || caseResult.status === "invalid" || caseResult.status === "error") {
|
|
21042
|
+
return caseResult.status;
|
|
21299
21043
|
}
|
|
21300
21044
|
if (caseResult.ok)
|
|
21301
21045
|
return "passed";
|
|
@@ -21314,6 +21058,12 @@ function assertRunManifestIntegrity(manifest) {
|
|
|
21314
21058
|
if (manifest.execution_provenance !== undefined) {
|
|
21315
21059
|
assertExecutionProvenance(manifest.execution_provenance);
|
|
21316
21060
|
}
|
|
21061
|
+
if (manifest.attempt_evidence !== undefined) {
|
|
21062
|
+
assertRunAttemptEvidence(manifest.attempt_evidence);
|
|
21063
|
+
}
|
|
21064
|
+
if (isRecord2(manifest.metrics) && manifest.metrics.cost_provenance !== undefined) {
|
|
21065
|
+
assertCostProvenance(manifest.metrics.cost_provenance);
|
|
21066
|
+
}
|
|
21317
21067
|
for (const [index, caseResult] of manifest.cases.entries()) {
|
|
21318
21068
|
if (!isRecord2(caseResult)) {
|
|
21319
21069
|
throw new Error(`Invalid run manifest: case ${index} is not an object`);
|
|
@@ -21327,8 +21077,61 @@ function assertRunManifestIntegrity(manifest) {
|
|
|
21327
21077
|
if (caseResult.target !== undefined) {
|
|
21328
21078
|
assertCaseTargetEvidence(caseResult.target);
|
|
21329
21079
|
}
|
|
21080
|
+
if (caseResult.attempt_evidence !== undefined) {
|
|
21081
|
+
assertCaseAttemptEvidence(caseResult.attempt_evidence, index);
|
|
21082
|
+
}
|
|
21083
|
+
}
|
|
21084
|
+
}
|
|
21085
|
+
function assertRunAttemptEvidence(evidence) {
|
|
21086
|
+
if (!isRecord2(evidence) || evidence.schema_version !== "1" || !isRecord2(evidence.repetition) || !isPositiveSafeInteger(evidence.repetition.index) || !isPositiveSafeInteger(evidence.repetition.total) || evidence.repetition.index > evidence.repetition.total || !isRecord2(evidence.retry_policy) || !isNonnegativeSafeInteger(evidence.retry_policy.default_max_retries) || evidence.retry_policy.backoff !== "exponential" || !isNonnegativeFiniteNumber(evidence.retry_policy.initial_delay_ms) || evidence.timeout !== undefined && (!isRecord2(evidence.timeout) || !isPositiveFiniteNumber(evidence.timeout.default_ms))) {
|
|
21087
|
+
throw new Error("Invalid run manifest: malformed attempt evidence");
|
|
21088
|
+
}
|
|
21089
|
+
}
|
|
21090
|
+
function assertCaseAttemptEvidence(evidence, index) {
|
|
21091
|
+
if (!Array.isArray(evidence) || evidence.length === 0 || evidence.length > 100) {
|
|
21092
|
+
throw new Error(`Invalid run manifest: case ${index} has malformed attempt evidence`);
|
|
21093
|
+
}
|
|
21094
|
+
for (const attempt of evidence) {
|
|
21095
|
+
if (!isRecord2(attempt) || !isBoundedNonemptyString(attempt.attempt_id, 200) || !isBoundedNonemptyString(attempt.retry_chain_id, 200) || !isPositiveSafeInteger(attempt.repetition_index) || !isPositiveSafeInteger(attempt.attempt_number) || !isCaseEvaluationStatus(attempt.status) || typeof attempt.included_in_outcome !== "boolean" || !isNonnegativeFiniteNumber(attempt.latency_ms) || attempt.error_code !== undefined && attempt.error_code !== "timeout" && attempt.error_code !== "target_error" && attempt.error_code !== "tool_error") {
|
|
21096
|
+
throw new Error(`Invalid run manifest: case ${index} has malformed attempt evidence`);
|
|
21097
|
+
}
|
|
21098
|
+
}
|
|
21099
|
+
}
|
|
21100
|
+
function assertCostProvenance(cost) {
|
|
21101
|
+
if (!isRecord2(cost) || cost.schema_version !== "1") {
|
|
21102
|
+
throw new Error("Invalid run manifest: malformed cost provenance");
|
|
21103
|
+
}
|
|
21104
|
+
if (cost.status === "unavailable") {
|
|
21105
|
+
if (cost.amount !== undefined || cost.currency !== undefined || cost.source !== undefined || cost.recorded_at !== undefined || cost.unavailable_reason !== "provider_billing_not_recorded" && cost.unavailable_reason !== "unsupported_provider" && cost.unavailable_reason !== "not_requested") {
|
|
21106
|
+
throw new Error("Invalid run manifest: malformed cost provenance");
|
|
21107
|
+
}
|
|
21108
|
+
return;
|
|
21109
|
+
}
|
|
21110
|
+
if (cost.status !== "known" && cost.status !== "user_supplied" || !isNonnegativeFiniteNumber(cost.amount) || !isBoundedNonemptyString(cost.currency, 3) || cost.status === "known" && cost.source !== "provider_billing" || cost.status === "user_supplied" && cost.source !== "operator_input" || !isIsoTimestamp(cost.recorded_at) || cost.unavailable_reason !== undefined) {
|
|
21111
|
+
throw new Error("Invalid run manifest: malformed cost provenance");
|
|
21330
21112
|
}
|
|
21331
21113
|
}
|
|
21114
|
+
function isCaseEvaluationStatus(value) {
|
|
21115
|
+
return value === "passed" || value === "failed" || value === "invalid" || value === "error";
|
|
21116
|
+
}
|
|
21117
|
+
function isBoundedNonemptyString(value, maxLength) {
|
|
21118
|
+
return typeof value === "string" && value.length > 0 && value.length <= maxLength;
|
|
21119
|
+
}
|
|
21120
|
+
function isNonnegativeFiniteNumber(value) {
|
|
21121
|
+
return typeof value === "number" && Number.isFinite(value) && value >= 0;
|
|
21122
|
+
}
|
|
21123
|
+
function isPositiveFiniteNumber(value) {
|
|
21124
|
+
return isNonnegativeFiniteNumber(value) && value > 0;
|
|
21125
|
+
}
|
|
21126
|
+
function isNonnegativeSafeInteger(value) {
|
|
21127
|
+
return typeof value === "number" && Number.isSafeInteger(value) && value >= 0;
|
|
21128
|
+
}
|
|
21129
|
+
function isPositiveSafeInteger(value) {
|
|
21130
|
+
return isNonnegativeSafeInteger(value) && value > 0;
|
|
21131
|
+
}
|
|
21132
|
+
function isIsoTimestamp(value) {
|
|
21133
|
+
return typeof value === "string" && Number.isFinite(Date.parse(value));
|
|
21134
|
+
}
|
|
21332
21135
|
function assertCaseTargetEvidence(target) {
|
|
21333
21136
|
if (!isRecord2(target) || typeof target.provider !== "string" || target.provider.length === 0 || target.provider.length > 100 || target.requested_model !== undefined && (typeof target.requested_model !== "string" || target.requested_model.length > 200) || !isBoundedStringList(target.observed_models)) {
|
|
21334
21137
|
throw new Error("Invalid run manifest: malformed target evidence");
|
|
@@ -21407,6 +21210,9 @@ function createRunManifest(options) {
|
|
|
21407
21210
|
resolvedConfig,
|
|
21408
21211
|
workloadIdentity,
|
|
21409
21212
|
executionProvenance,
|
|
21213
|
+
attemptEvidence,
|
|
21214
|
+
costProvenance,
|
|
21215
|
+
runId,
|
|
21410
21216
|
cases,
|
|
21411
21217
|
startTime,
|
|
21412
21218
|
endTime,
|
|
@@ -21414,13 +21220,12 @@ function createRunManifest(options) {
|
|
|
21414
21220
|
runReason,
|
|
21415
21221
|
redaction
|
|
21416
21222
|
} = options;
|
|
21417
|
-
const
|
|
21418
|
-
const metrics = calculateMetrics(cases, modelForCost);
|
|
21223
|
+
const metrics = calculateMetrics(cases, costProvenance);
|
|
21419
21224
|
const git = getGitInfo();
|
|
21420
21225
|
const environment = getEnvironmentInfo();
|
|
21421
21226
|
return {
|
|
21422
|
-
version: "1.
|
|
21423
|
-
run_id: nanoid(12),
|
|
21227
|
+
version: "1.4",
|
|
21228
|
+
run_id: runId ?? nanoid(12),
|
|
21424
21229
|
project,
|
|
21425
21230
|
start_time: startTime.toISOString(),
|
|
21426
21231
|
end_time: endTime.toISOString(),
|
|
@@ -21429,6 +21234,7 @@ function createRunManifest(options) {
|
|
|
21429
21234
|
resolved_config: resolvedConfig,
|
|
21430
21235
|
workload_identity: workloadIdentity,
|
|
21431
21236
|
execution_provenance: executionProvenance,
|
|
21237
|
+
attempt_evidence: attemptEvidence,
|
|
21432
21238
|
metrics,
|
|
21433
21239
|
git,
|
|
21434
21240
|
provenance: {
|
|
@@ -21441,7 +21247,7 @@ function createRunManifest(options) {
|
|
|
21441
21247
|
redaction
|
|
21442
21248
|
};
|
|
21443
21249
|
}
|
|
21444
|
-
function calculateMetrics(cases,
|
|
21250
|
+
function calculateMetrics(cases, costProvenance) {
|
|
21445
21251
|
const passedCases = cases.filter((c) => getCaseEvaluationStatus(c) === "passed");
|
|
21446
21252
|
const validCases = cases.filter((c) => {
|
|
21447
21253
|
const status = getCaseEvaluationStatus(c);
|
|
@@ -21453,21 +21259,11 @@ function calculateMetrics(cases, model) {
|
|
|
21453
21259
|
const p95Latency = latencies.length > 0 ? latencies[p95Index] : 0;
|
|
21454
21260
|
const totalPromptTokens = cases.reduce((sum, c) => sum + c.tokens.prompt, 0);
|
|
21455
21261
|
const totalCompletionTokens = cases.reduce((sum, c) => sum + c.tokens.completion, 0);
|
|
21456
|
-
|
|
21457
|
-
|
|
21458
|
-
|
|
21459
|
-
|
|
21460
|
-
|
|
21461
|
-
total_usd: costEstimate.totalUsd,
|
|
21462
|
-
prompt_cost_usd: costEstimate.promptCostUsd,
|
|
21463
|
-
completion_cost_usd: costEstimate.completionCostUsd,
|
|
21464
|
-
model: costEstimate.model,
|
|
21465
|
-
pricing: {
|
|
21466
|
-
prompt_per_1k: pricing.promptPer1K,
|
|
21467
|
-
completion_per_1k: pricing.completionPer1K
|
|
21468
|
-
}
|
|
21469
|
-
};
|
|
21470
|
-
}
|
|
21262
|
+
const cost_provenance = costProvenance ?? {
|
|
21263
|
+
schema_version: "1",
|
|
21264
|
+
status: "unavailable",
|
|
21265
|
+
unavailable_reason: "provider_billing_not_recorded"
|
|
21266
|
+
};
|
|
21471
21267
|
return {
|
|
21472
21268
|
success_rate: validCases.length > 0 ? passedCases.length / validCases.length : 0,
|
|
21473
21269
|
total_attempts: cases.reduce((sum, c) => sum + (c.attempts ?? 1), 0),
|
|
@@ -21482,7 +21278,7 @@ function calculateMetrics(cases, model) {
|
|
|
21482
21278
|
total_tokens: totalPromptTokens + totalCompletionTokens,
|
|
21483
21279
|
total_prompt_tokens: totalPromptTokens,
|
|
21484
21280
|
total_completion_tokens: totalCompletionTokens,
|
|
21485
|
-
|
|
21281
|
+
cost_provenance
|
|
21486
21282
|
};
|
|
21487
21283
|
}
|
|
21488
21284
|
function detectCIEnvironment() {
|
|
@@ -21625,6 +21421,8 @@ async function runScenario(options) {
|
|
|
21625
21421
|
concurrency = 1,
|
|
21626
21422
|
timeout,
|
|
21627
21423
|
retries,
|
|
21424
|
+
repetition = { index: 1, total: 1 },
|
|
21425
|
+
costProvenance,
|
|
21628
21426
|
redaction,
|
|
21629
21427
|
toolExecutor,
|
|
21630
21428
|
onCaseComplete,
|
|
@@ -21640,6 +21438,7 @@ async function runScenario(options) {
|
|
|
21640
21438
|
}
|
|
21641
21439
|
onProgress?.(`Running ${cases.length} test cases...`);
|
|
21642
21440
|
const startTime = new Date;
|
|
21441
|
+
const runId = nanoid(12);
|
|
21643
21442
|
const results = [];
|
|
21644
21443
|
if (concurrency === 1) {
|
|
21645
21444
|
for (let i = 0;i < cases.length; i++) {
|
|
@@ -21650,6 +21449,8 @@ async function runScenario(options) {
|
|
|
21650
21449
|
requestedModel: resolvedConfig?.model,
|
|
21651
21450
|
timeout: testCase.timeout || timeout,
|
|
21652
21451
|
retries: testCase.retries ?? retries,
|
|
21452
|
+
runId,
|
|
21453
|
+
repetition,
|
|
21653
21454
|
redaction,
|
|
21654
21455
|
toolExecutor
|
|
21655
21456
|
});
|
|
@@ -21667,6 +21468,8 @@ async function runScenario(options) {
|
|
|
21667
21468
|
requestedModel: resolvedConfig?.model,
|
|
21668
21469
|
timeout: testCase.timeout || timeout,
|
|
21669
21470
|
retries: testCase.retries ?? retries,
|
|
21471
|
+
runId,
|
|
21472
|
+
repetition,
|
|
21670
21473
|
redaction,
|
|
21671
21474
|
toolExecutor
|
|
21672
21475
|
});
|
|
@@ -21717,9 +21520,21 @@ async function runScenario(options) {
|
|
|
21717
21520
|
seed: scenario.seed,
|
|
21718
21521
|
cases: results
|
|
21719
21522
|
}),
|
|
21523
|
+
attemptEvidence: {
|
|
21524
|
+
schema_version: "1",
|
|
21525
|
+
repetition,
|
|
21526
|
+
retry_policy: {
|
|
21527
|
+
default_max_retries: retries ?? 0,
|
|
21528
|
+
backoff: "exponential",
|
|
21529
|
+
initial_delay_ms: 1000
|
|
21530
|
+
},
|
|
21531
|
+
...timeout ? { timeout: { default_ms: timeout } } : {}
|
|
21532
|
+
},
|
|
21533
|
+
costProvenance,
|
|
21720
21534
|
cases: results,
|
|
21721
21535
|
startTime,
|
|
21722
21536
|
endTime,
|
|
21537
|
+
runId,
|
|
21723
21538
|
redaction: redactionInfo
|
|
21724
21539
|
});
|
|
21725
21540
|
const success = manifest.metrics.failed_cases === 0 && (manifest.metrics.invalid_evaluations ?? 0) === 0;
|
|
@@ -21747,6 +21562,65 @@ function chunkArray(array, size) {
|
|
|
21747
21562
|
// src/storage/local.ts
|
|
21748
21563
|
import { mkdir, readFile as readFile2, readdir as readdir2, unlink, writeFile } from "fs/promises";
|
|
21749
21564
|
import { join as join2, resolve as resolve2 } from "path";
|
|
21565
|
+
|
|
21566
|
+
// src/comparison/eligibility.ts
|
|
21567
|
+
function assessComparisonEligibility(baseline, current) {
|
|
21568
|
+
const reasons = [];
|
|
21569
|
+
if (baseline.config.scenario !== current.config.scenario) {
|
|
21570
|
+
reasons.push({ code: "scenario_mismatch" });
|
|
21571
|
+
}
|
|
21572
|
+
const baselineIdentity = baseline.workload_identity;
|
|
21573
|
+
const currentIdentity = current.workload_identity;
|
|
21574
|
+
if (!baselineIdentity || !currentIdentity) {
|
|
21575
|
+
if (!baselineIdentity || !currentIdentity)
|
|
21576
|
+
reasons.push({ code: "workload_identity_missing" });
|
|
21577
|
+
if (!baselineIdentity || !currentIdentity)
|
|
21578
|
+
reasons.push({ code: "rubric_identity_missing" });
|
|
21579
|
+
} else {
|
|
21580
|
+
if (baselineIdentity.workload.digest !== currentIdentity.workload.digest) {
|
|
21581
|
+
reasons.push({ code: "workload_mismatch" });
|
|
21582
|
+
}
|
|
21583
|
+
if (baselineIdentity.rubric.digest !== currentIdentity.rubric.digest) {
|
|
21584
|
+
reasons.push({ code: "rubric_mismatch" });
|
|
21585
|
+
}
|
|
21586
|
+
}
|
|
21587
|
+
if (reasons.some((reason) => isIncomparableReason(reason.code))) {
|
|
21588
|
+
return { schema_version: "1", status: "incomparable", reasons };
|
|
21589
|
+
}
|
|
21590
|
+
const baselineExecution = baseline.execution_provenance;
|
|
21591
|
+
const currentExecution = current.execution_provenance;
|
|
21592
|
+
if (!baselineExecution || !currentExecution) {
|
|
21593
|
+
reasons.push({ code: "execution_provenance_missing" });
|
|
21594
|
+
} else {
|
|
21595
|
+
if (baselineExecution.target.provider !== currentExecution.target.provider) {
|
|
21596
|
+
reasons.push({ code: "target_provider_changed" });
|
|
21597
|
+
}
|
|
21598
|
+
if (!sameStringSet(baselineExecution.target.requested_models, currentExecution.target.requested_models)) {
|
|
21599
|
+
reasons.push({ code: "target_model_changed" });
|
|
21600
|
+
}
|
|
21601
|
+
if (!sameGeneration(baselineExecution.target.generation, currentExecution.target.generation)) {
|
|
21602
|
+
reasons.push({ code: "generation_settings_changed" });
|
|
21603
|
+
}
|
|
21604
|
+
}
|
|
21605
|
+
return {
|
|
21606
|
+
schema_version: "1",
|
|
21607
|
+
status: reasons.length === 0 ? "compatible" : "qualified",
|
|
21608
|
+
reasons
|
|
21609
|
+
};
|
|
21610
|
+
}
|
|
21611
|
+
function isComparisonAvailable(eligibility) {
|
|
21612
|
+
return eligibility.status !== "incomparable";
|
|
21613
|
+
}
|
|
21614
|
+
function isIncomparableReason(code) {
|
|
21615
|
+
return code === "scenario_mismatch" || code === "workload_mismatch" || code === "rubric_mismatch";
|
|
21616
|
+
}
|
|
21617
|
+
function sameStringSet(left, right) {
|
|
21618
|
+
return JSON.stringify([...left ?? []].sort()) === JSON.stringify([...right ?? []].sort());
|
|
21619
|
+
}
|
|
21620
|
+
function sameGeneration(left, right) {
|
|
21621
|
+
return left?.temperature === right?.temperature && left?.max_tokens === right?.max_tokens && left?.seed === right?.seed;
|
|
21622
|
+
}
|
|
21623
|
+
// src/storage/local.ts
|
|
21750
21624
|
function getManifestType(manifest) {
|
|
21751
21625
|
if ("type" in manifest) {
|
|
21752
21626
|
if (manifest.type === "redteam")
|
|
@@ -21886,9 +21760,14 @@ class LocalStorageAdapter {
|
|
|
21886
21760
|
this.loadRun(baselineId),
|
|
21887
21761
|
this.loadRun(currentId)
|
|
21888
21762
|
]);
|
|
21763
|
+
const eligibility = assessComparisonEligibility(baseline, current);
|
|
21764
|
+
if (!isComparisonAvailable(eligibility)) {
|
|
21765
|
+
return { baseline, current, eligibility };
|
|
21766
|
+
}
|
|
21889
21767
|
return {
|
|
21890
21768
|
baseline,
|
|
21891
21769
|
current,
|
|
21770
|
+
eligibility,
|
|
21892
21771
|
delta: {
|
|
21893
21772
|
successRate: current.metrics.success_rate - baseline.metrics.success_rate,
|
|
21894
21773
|
latency: current.metrics.median_latency_ms - baseline.metrics.median_latency_ms,
|
|
@@ -21987,7 +21866,7 @@ class LocalStorageAdapter {
|
|
|
21987
21866
|
return null;
|
|
21988
21867
|
}
|
|
21989
21868
|
const comparison = await this.compare(baseline.runId, runId);
|
|
21990
|
-
const hasRegression = comparison.delta.successRate < -regressionThreshold;
|
|
21869
|
+
const hasRegression = comparison.delta !== undefined && comparison.delta.successRate < -regressionThreshold;
|
|
21991
21870
|
return {
|
|
21992
21871
|
baseline,
|
|
21993
21872
|
comparison,
|
|
@@ -30633,9 +30512,14 @@ class SupabaseStorageAdapter {
|
|
|
30633
30512
|
}
|
|
30634
30513
|
async compare(baselineId, currentId) {
|
|
30635
30514
|
const [baseline, current] = await Promise.all([this.load(baselineId), this.load(currentId)]);
|
|
30515
|
+
const eligibility = assessComparisonEligibility(baseline, current);
|
|
30516
|
+
if (!isComparisonAvailable(eligibility)) {
|
|
30517
|
+
return { baseline, current, eligibility };
|
|
30518
|
+
}
|
|
30636
30519
|
return {
|
|
30637
30520
|
baseline,
|
|
30638
30521
|
current,
|
|
30522
|
+
eligibility,
|
|
30639
30523
|
delta: {
|
|
30640
30524
|
successRate: current.metrics.success_rate - baseline.metrics.success_rate,
|
|
30641
30525
|
latency: current.metrics.median_latency_ms - baseline.metrics.median_latency_ms,
|
|
@@ -30766,7 +30650,7 @@ class SupabaseStorageAdapter {
|
|
|
30766
30650
|
return null;
|
|
30767
30651
|
}
|
|
30768
30652
|
const comparison = await this.compare(baseline.runId, runId);
|
|
30769
|
-
const hasRegression = comparison.delta.successRate < -regressionThreshold;
|
|
30653
|
+
const hasRegression = comparison.delta !== undefined && comparison.delta.successRate < -regressionThreshold;
|
|
30770
30654
|
return {
|
|
30771
30655
|
baseline,
|
|
30772
30656
|
comparison,
|
|
@@ -32101,6 +31985,318 @@ class Logger {
|
|
|
32101
31985
|
}
|
|
32102
31986
|
}
|
|
32103
31987
|
var logger = new Logger("artemis");
|
|
31988
|
+
// src/cost/pricing.ts
|
|
31989
|
+
var MODEL_PRICING = {
|
|
31990
|
+
"gpt-5": {
|
|
31991
|
+
promptPer1K: 0.00125,
|
|
31992
|
+
completionPer1K: 0.01,
|
|
31993
|
+
lastUpdated: "2026-01",
|
|
31994
|
+
notes: "400K context window"
|
|
31995
|
+
},
|
|
31996
|
+
"gpt-5.1": {
|
|
31997
|
+
promptPer1K: 0.00125,
|
|
31998
|
+
completionPer1K: 0.01,
|
|
31999
|
+
lastUpdated: "2026-01"
|
|
32000
|
+
},
|
|
32001
|
+
"gpt-5.2": {
|
|
32002
|
+
promptPer1K: 0.00175,
|
|
32003
|
+
completionPer1K: 0.014,
|
|
32004
|
+
lastUpdated: "2026-01"
|
|
32005
|
+
},
|
|
32006
|
+
"gpt-5-mini": {
|
|
32007
|
+
promptPer1K: 0.00025,
|
|
32008
|
+
completionPer1K: 0.002,
|
|
32009
|
+
lastUpdated: "2026-01"
|
|
32010
|
+
},
|
|
32011
|
+
"gpt-5-nano": {
|
|
32012
|
+
promptPer1K: 0.00005,
|
|
32013
|
+
completionPer1K: 0.0004,
|
|
32014
|
+
lastUpdated: "2026-01"
|
|
32015
|
+
},
|
|
32016
|
+
"gpt-4.1": {
|
|
32017
|
+
promptPer1K: 0.002,
|
|
32018
|
+
completionPer1K: 0.008,
|
|
32019
|
+
lastUpdated: "2026-01",
|
|
32020
|
+
notes: "1M context window"
|
|
32021
|
+
},
|
|
32022
|
+
"gpt-4.1-mini": {
|
|
32023
|
+
promptPer1K: 0.0004,
|
|
32024
|
+
completionPer1K: 0.0016,
|
|
32025
|
+
lastUpdated: "2026-01"
|
|
32026
|
+
},
|
|
32027
|
+
"gpt-4.1-nano": {
|
|
32028
|
+
promptPer1K: 0.0001,
|
|
32029
|
+
completionPer1K: 0.0004,
|
|
32030
|
+
lastUpdated: "2026-01"
|
|
32031
|
+
},
|
|
32032
|
+
"gpt-4o": {
|
|
32033
|
+
promptPer1K: 0.0025,
|
|
32034
|
+
completionPer1K: 0.01,
|
|
32035
|
+
lastUpdated: "2026-01",
|
|
32036
|
+
notes: "128K context window"
|
|
32037
|
+
},
|
|
32038
|
+
"gpt-4o-mini": {
|
|
32039
|
+
promptPer1K: 0.00015,
|
|
32040
|
+
completionPer1K: 0.0006,
|
|
32041
|
+
lastUpdated: "2026-01",
|
|
32042
|
+
notes: "128K context window"
|
|
32043
|
+
},
|
|
32044
|
+
o1: {
|
|
32045
|
+
promptPer1K: 0.015,
|
|
32046
|
+
completionPer1K: 0.06,
|
|
32047
|
+
lastUpdated: "2026-01",
|
|
32048
|
+
notes: "Reasoning model - internal thinking tokens billed as output"
|
|
32049
|
+
},
|
|
32050
|
+
o3: {
|
|
32051
|
+
promptPer1K: 0.002,
|
|
32052
|
+
completionPer1K: 0.008,
|
|
32053
|
+
lastUpdated: "2026-01"
|
|
32054
|
+
},
|
|
32055
|
+
"o3-mini": {
|
|
32056
|
+
promptPer1K: 0.0011,
|
|
32057
|
+
completionPer1K: 0.0044,
|
|
32058
|
+
lastUpdated: "2026-01"
|
|
32059
|
+
},
|
|
32060
|
+
"o4-mini": {
|
|
32061
|
+
promptPer1K: 0.0011,
|
|
32062
|
+
completionPer1K: 0.0044,
|
|
32063
|
+
lastUpdated: "2026-01"
|
|
32064
|
+
},
|
|
32065
|
+
"gpt-4-turbo": {
|
|
32066
|
+
promptPer1K: 0.01,
|
|
32067
|
+
completionPer1K: 0.03,
|
|
32068
|
+
lastUpdated: "2026-01"
|
|
32069
|
+
},
|
|
32070
|
+
"gpt-4": {
|
|
32071
|
+
promptPer1K: 0.03,
|
|
32072
|
+
completionPer1K: 0.06,
|
|
32073
|
+
lastUpdated: "2026-01"
|
|
32074
|
+
},
|
|
32075
|
+
"gpt-3.5-turbo": {
|
|
32076
|
+
promptPer1K: 0.0005,
|
|
32077
|
+
completionPer1K: 0.0015,
|
|
32078
|
+
lastUpdated: "2026-01"
|
|
32079
|
+
},
|
|
32080
|
+
"claude-opus-4.5": {
|
|
32081
|
+
promptPer1K: 0.005,
|
|
32082
|
+
completionPer1K: 0.025,
|
|
32083
|
+
lastUpdated: "2026-01",
|
|
32084
|
+
notes: "Most capable Claude model"
|
|
32085
|
+
},
|
|
32086
|
+
"claude-sonnet-4.5": {
|
|
32087
|
+
promptPer1K: 0.003,
|
|
32088
|
+
completionPer1K: 0.015,
|
|
32089
|
+
lastUpdated: "2026-01",
|
|
32090
|
+
notes: "Balanced performance and cost"
|
|
32091
|
+
},
|
|
32092
|
+
"claude-haiku-4.5": {
|
|
32093
|
+
promptPer1K: 0.001,
|
|
32094
|
+
completionPer1K: 0.005,
|
|
32095
|
+
lastUpdated: "2026-01",
|
|
32096
|
+
notes: "Fastest Claude model"
|
|
32097
|
+
},
|
|
32098
|
+
"claude-opus-4": {
|
|
32099
|
+
promptPer1K: 0.015,
|
|
32100
|
+
completionPer1K: 0.075,
|
|
32101
|
+
lastUpdated: "2026-01"
|
|
32102
|
+
},
|
|
32103
|
+
"claude-opus-4.1": {
|
|
32104
|
+
promptPer1K: 0.015,
|
|
32105
|
+
completionPer1K: 0.075,
|
|
32106
|
+
lastUpdated: "2026-01"
|
|
32107
|
+
},
|
|
32108
|
+
"claude-sonnet-4": {
|
|
32109
|
+
promptPer1K: 0.003,
|
|
32110
|
+
completionPer1K: 0.015,
|
|
32111
|
+
lastUpdated: "2026-01"
|
|
32112
|
+
},
|
|
32113
|
+
"claude-sonnet-3.7": {
|
|
32114
|
+
promptPer1K: 0.003,
|
|
32115
|
+
completionPer1K: 0.015,
|
|
32116
|
+
lastUpdated: "2026-01"
|
|
32117
|
+
},
|
|
32118
|
+
"claude-3-7-sonnet": {
|
|
32119
|
+
promptPer1K: 0.003,
|
|
32120
|
+
completionPer1K: 0.015,
|
|
32121
|
+
lastUpdated: "2026-01"
|
|
32122
|
+
},
|
|
32123
|
+
"claude-3-5-sonnet-20241022": {
|
|
32124
|
+
promptPer1K: 0.003,
|
|
32125
|
+
completionPer1K: 0.015,
|
|
32126
|
+
lastUpdated: "2026-01"
|
|
32127
|
+
},
|
|
32128
|
+
"claude-3-5-haiku-20241022": {
|
|
32129
|
+
promptPer1K: 0.0008,
|
|
32130
|
+
completionPer1K: 0.004,
|
|
32131
|
+
lastUpdated: "2026-01"
|
|
32132
|
+
},
|
|
32133
|
+
"claude-haiku-3.5": {
|
|
32134
|
+
promptPer1K: 0.0008,
|
|
32135
|
+
completionPer1K: 0.004,
|
|
32136
|
+
lastUpdated: "2026-01"
|
|
32137
|
+
},
|
|
32138
|
+
"claude-3-opus": {
|
|
32139
|
+
promptPer1K: 0.015,
|
|
32140
|
+
completionPer1K: 0.075,
|
|
32141
|
+
lastUpdated: "2026-01"
|
|
32142
|
+
},
|
|
32143
|
+
"claude-3-sonnet": {
|
|
32144
|
+
promptPer1K: 0.003,
|
|
32145
|
+
completionPer1K: 0.015,
|
|
32146
|
+
lastUpdated: "2026-01"
|
|
32147
|
+
},
|
|
32148
|
+
"claude-3-haiku": {
|
|
32149
|
+
promptPer1K: 0.00025,
|
|
32150
|
+
completionPer1K: 0.00125,
|
|
32151
|
+
lastUpdated: "2026-01"
|
|
32152
|
+
},
|
|
32153
|
+
"claude-3.5-sonnet": {
|
|
32154
|
+
promptPer1K: 0.003,
|
|
32155
|
+
completionPer1K: 0.015,
|
|
32156
|
+
lastUpdated: "2026-01"
|
|
32157
|
+
},
|
|
32158
|
+
"claude-3.5-haiku": {
|
|
32159
|
+
promptPer1K: 0.0008,
|
|
32160
|
+
completionPer1K: 0.004,
|
|
32161
|
+
lastUpdated: "2026-01"
|
|
32162
|
+
}
|
|
32163
|
+
};
|
|
32164
|
+
var DEFAULT_PRICING = {
|
|
32165
|
+
promptPer1K: 0.003,
|
|
32166
|
+
completionPer1K: 0.015,
|
|
32167
|
+
lastUpdated: "2026-01",
|
|
32168
|
+
notes: "Default pricing - verify with provider"
|
|
32169
|
+
};
|
|
32170
|
+
function getModelPricing(model) {
|
|
32171
|
+
if (MODEL_PRICING[model]) {
|
|
32172
|
+
return MODEL_PRICING[model];
|
|
32173
|
+
}
|
|
32174
|
+
const lowerModel = model.toLowerCase();
|
|
32175
|
+
for (const [key, pricing] of Object.entries(MODEL_PRICING)) {
|
|
32176
|
+
if (key.toLowerCase() === lowerModel) {
|
|
32177
|
+
return pricing;
|
|
32178
|
+
}
|
|
32179
|
+
}
|
|
32180
|
+
if (lowerModel.includes("gpt-5.2")) {
|
|
32181
|
+
return MODEL_PRICING["gpt-5.2"];
|
|
32182
|
+
}
|
|
32183
|
+
if (lowerModel.includes("gpt-5.1")) {
|
|
32184
|
+
return MODEL_PRICING["gpt-5.1"];
|
|
32185
|
+
}
|
|
32186
|
+
if (lowerModel.includes("gpt-5-mini")) {
|
|
32187
|
+
return MODEL_PRICING["gpt-5-mini"];
|
|
32188
|
+
}
|
|
32189
|
+
if (lowerModel.includes("gpt-5-nano")) {
|
|
32190
|
+
return MODEL_PRICING["gpt-5-nano"];
|
|
32191
|
+
}
|
|
32192
|
+
if (lowerModel.includes("gpt-5")) {
|
|
32193
|
+
return MODEL_PRICING["gpt-5"];
|
|
32194
|
+
}
|
|
32195
|
+
if (lowerModel.includes("gpt-4.1-mini")) {
|
|
32196
|
+
return MODEL_PRICING["gpt-4.1-mini"];
|
|
32197
|
+
}
|
|
32198
|
+
if (lowerModel.includes("gpt-4.1-nano")) {
|
|
32199
|
+
return MODEL_PRICING["gpt-4.1-nano"];
|
|
32200
|
+
}
|
|
32201
|
+
if (lowerModel.includes("gpt-4.1")) {
|
|
32202
|
+
return MODEL_PRICING["gpt-4.1"];
|
|
32203
|
+
}
|
|
32204
|
+
if (lowerModel.includes("gpt-4o-mini")) {
|
|
32205
|
+
return MODEL_PRICING["gpt-4o-mini"];
|
|
32206
|
+
}
|
|
32207
|
+
if (lowerModel.includes("gpt-4o")) {
|
|
32208
|
+
return MODEL_PRICING["gpt-4o"];
|
|
32209
|
+
}
|
|
32210
|
+
if (lowerModel.includes("o4-mini")) {
|
|
32211
|
+
return MODEL_PRICING["o4-mini"];
|
|
32212
|
+
}
|
|
32213
|
+
if (lowerModel.includes("o3-mini")) {
|
|
32214
|
+
return MODEL_PRICING["o3-mini"];
|
|
32215
|
+
}
|
|
32216
|
+
if (lowerModel.includes("o3")) {
|
|
32217
|
+
return MODEL_PRICING.o3;
|
|
32218
|
+
}
|
|
32219
|
+
if (lowerModel.includes("o1")) {
|
|
32220
|
+
return MODEL_PRICING.o1;
|
|
32221
|
+
}
|
|
32222
|
+
if (lowerModel.includes("gpt-4-turbo")) {
|
|
32223
|
+
return MODEL_PRICING["gpt-4-turbo"];
|
|
32224
|
+
}
|
|
32225
|
+
if (lowerModel.includes("gpt-4")) {
|
|
32226
|
+
return MODEL_PRICING["gpt-4"];
|
|
32227
|
+
}
|
|
32228
|
+
if (lowerModel.includes("gpt-3.5")) {
|
|
32229
|
+
return MODEL_PRICING["gpt-3.5-turbo"];
|
|
32230
|
+
}
|
|
32231
|
+
if (lowerModel.includes("opus-4.5") || lowerModel.includes("opus-4-5")) {
|
|
32232
|
+
return MODEL_PRICING["claude-opus-4.5"];
|
|
32233
|
+
}
|
|
32234
|
+
if (lowerModel.includes("sonnet-4.5") || lowerModel.includes("sonnet-4-5")) {
|
|
32235
|
+
return MODEL_PRICING["claude-sonnet-4.5"];
|
|
32236
|
+
}
|
|
32237
|
+
if (lowerModel.includes("haiku-4.5") || lowerModel.includes("haiku-4-5")) {
|
|
32238
|
+
return MODEL_PRICING["claude-haiku-4.5"];
|
|
32239
|
+
}
|
|
32240
|
+
if (lowerModel.includes("opus-4.1") || lowerModel.includes("opus-4-1")) {
|
|
32241
|
+
return MODEL_PRICING["claude-opus-4.1"];
|
|
32242
|
+
}
|
|
32243
|
+
if (lowerModel.includes("opus-4")) {
|
|
32244
|
+
return MODEL_PRICING["claude-opus-4"];
|
|
32245
|
+
}
|
|
32246
|
+
if (lowerModel.includes("sonnet-4")) {
|
|
32247
|
+
return MODEL_PRICING["claude-sonnet-4"];
|
|
32248
|
+
}
|
|
32249
|
+
if (lowerModel.includes("sonnet-3.7") || lowerModel.includes("sonnet-3-7")) {
|
|
32250
|
+
return MODEL_PRICING["claude-sonnet-3.7"];
|
|
32251
|
+
}
|
|
32252
|
+
if (lowerModel.includes("claude-3-5-sonnet") || lowerModel.includes("claude-3.5-sonnet")) {
|
|
32253
|
+
return MODEL_PRICING["claude-3.5-sonnet"];
|
|
32254
|
+
}
|
|
32255
|
+
if (lowerModel.includes("claude-3-5-haiku") || lowerModel.includes("claude-3.5-haiku")) {
|
|
32256
|
+
return MODEL_PRICING["claude-3.5-haiku"];
|
|
32257
|
+
}
|
|
32258
|
+
if (lowerModel.includes("claude-3-opus")) {
|
|
32259
|
+
return MODEL_PRICING["claude-3-opus"];
|
|
32260
|
+
}
|
|
32261
|
+
if (lowerModel.includes("claude-3-sonnet")) {
|
|
32262
|
+
return MODEL_PRICING["claude-3-sonnet"];
|
|
32263
|
+
}
|
|
32264
|
+
if (lowerModel.includes("claude-3-haiku")) {
|
|
32265
|
+
return MODEL_PRICING["claude-3-haiku"];
|
|
32266
|
+
}
|
|
32267
|
+
if (lowerModel.includes("claude")) {
|
|
32268
|
+
return MODEL_PRICING["claude-sonnet-4.5"];
|
|
32269
|
+
}
|
|
32270
|
+
return DEFAULT_PRICING;
|
|
32271
|
+
}
|
|
32272
|
+
function estimateCost(promptTokens, completionTokens, model) {
|
|
32273
|
+
const pricing = getModelPricing(model);
|
|
32274
|
+
const promptCostUsd = promptTokens / 1000 * pricing.promptPer1K;
|
|
32275
|
+
const completionCostUsd = completionTokens / 1000 * pricing.completionPer1K;
|
|
32276
|
+
const totalUsd = promptCostUsd + completionCostUsd;
|
|
32277
|
+
return {
|
|
32278
|
+
totalUsd,
|
|
32279
|
+
promptCostUsd,
|
|
32280
|
+
completionCostUsd,
|
|
32281
|
+
model,
|
|
32282
|
+
pricing
|
|
32283
|
+
};
|
|
32284
|
+
}
|
|
32285
|
+
function formatCost(costUsd) {
|
|
32286
|
+
if (costUsd < 0.01) {
|
|
32287
|
+
return `$${(costUsd * 100).toFixed(4)} cents`;
|
|
32288
|
+
}
|
|
32289
|
+
if (costUsd < 1) {
|
|
32290
|
+
return `$${costUsd.toFixed(4)}`;
|
|
32291
|
+
}
|
|
32292
|
+
return `$${costUsd.toFixed(2)}`;
|
|
32293
|
+
}
|
|
32294
|
+
function listKnownModels() {
|
|
32295
|
+
return Object.entries(MODEL_PRICING).map(([model, pricing]) => ({
|
|
32296
|
+
model,
|
|
32297
|
+
pricing
|
|
32298
|
+
}));
|
|
32299
|
+
}
|
|
32104
32300
|
// src/agent-evaluation/types.ts
|
|
32105
32301
|
function actionBudgetExceeded(task, trace) {
|
|
32106
32302
|
return trace.actions.length > task.maxActions;
|
|
@@ -32797,6 +32993,7 @@ export {
|
|
|
32797
32993
|
actionBudgetExceeded,
|
|
32798
32994
|
adapterRegistry,
|
|
32799
32995
|
assertRunManifestIntegrity,
|
|
32996
|
+
assessComparisonEligibility,
|
|
32800
32997
|
createAdapter,
|
|
32801
32998
|
createDefaultRedactor,
|
|
32802
32999
|
createExecutionProvenance,
|
|
@@ -32818,6 +33015,7 @@ export {
|
|
|
32818
33015
|
getModelPricing,
|
|
32819
33016
|
hashText,
|
|
32820
33017
|
isArtemisError,
|
|
33018
|
+
isComparisonAvailable,
|
|
32821
33019
|
isRedTeamManifest,
|
|
32822
33020
|
isRunManifest,
|
|
32823
33021
|
isStressManifest,
|