@artemiskit/core 0.5.1 → 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 +8 -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/index.js +517 -390
- 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/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/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/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;
|
|
@@ -32101,6 +31916,318 @@ class Logger {
|
|
|
32101
31916
|
}
|
|
32102
31917
|
}
|
|
32103
31918
|
var logger = new Logger("artemis");
|
|
31919
|
+
// src/cost/pricing.ts
|
|
31920
|
+
var MODEL_PRICING = {
|
|
31921
|
+
"gpt-5": {
|
|
31922
|
+
promptPer1K: 0.00125,
|
|
31923
|
+
completionPer1K: 0.01,
|
|
31924
|
+
lastUpdated: "2026-01",
|
|
31925
|
+
notes: "400K context window"
|
|
31926
|
+
},
|
|
31927
|
+
"gpt-5.1": {
|
|
31928
|
+
promptPer1K: 0.00125,
|
|
31929
|
+
completionPer1K: 0.01,
|
|
31930
|
+
lastUpdated: "2026-01"
|
|
31931
|
+
},
|
|
31932
|
+
"gpt-5.2": {
|
|
31933
|
+
promptPer1K: 0.00175,
|
|
31934
|
+
completionPer1K: 0.014,
|
|
31935
|
+
lastUpdated: "2026-01"
|
|
31936
|
+
},
|
|
31937
|
+
"gpt-5-mini": {
|
|
31938
|
+
promptPer1K: 0.00025,
|
|
31939
|
+
completionPer1K: 0.002,
|
|
31940
|
+
lastUpdated: "2026-01"
|
|
31941
|
+
},
|
|
31942
|
+
"gpt-5-nano": {
|
|
31943
|
+
promptPer1K: 0.00005,
|
|
31944
|
+
completionPer1K: 0.0004,
|
|
31945
|
+
lastUpdated: "2026-01"
|
|
31946
|
+
},
|
|
31947
|
+
"gpt-4.1": {
|
|
31948
|
+
promptPer1K: 0.002,
|
|
31949
|
+
completionPer1K: 0.008,
|
|
31950
|
+
lastUpdated: "2026-01",
|
|
31951
|
+
notes: "1M context window"
|
|
31952
|
+
},
|
|
31953
|
+
"gpt-4.1-mini": {
|
|
31954
|
+
promptPer1K: 0.0004,
|
|
31955
|
+
completionPer1K: 0.0016,
|
|
31956
|
+
lastUpdated: "2026-01"
|
|
31957
|
+
},
|
|
31958
|
+
"gpt-4.1-nano": {
|
|
31959
|
+
promptPer1K: 0.0001,
|
|
31960
|
+
completionPer1K: 0.0004,
|
|
31961
|
+
lastUpdated: "2026-01"
|
|
31962
|
+
},
|
|
31963
|
+
"gpt-4o": {
|
|
31964
|
+
promptPer1K: 0.0025,
|
|
31965
|
+
completionPer1K: 0.01,
|
|
31966
|
+
lastUpdated: "2026-01",
|
|
31967
|
+
notes: "128K context window"
|
|
31968
|
+
},
|
|
31969
|
+
"gpt-4o-mini": {
|
|
31970
|
+
promptPer1K: 0.00015,
|
|
31971
|
+
completionPer1K: 0.0006,
|
|
31972
|
+
lastUpdated: "2026-01",
|
|
31973
|
+
notes: "128K context window"
|
|
31974
|
+
},
|
|
31975
|
+
o1: {
|
|
31976
|
+
promptPer1K: 0.015,
|
|
31977
|
+
completionPer1K: 0.06,
|
|
31978
|
+
lastUpdated: "2026-01",
|
|
31979
|
+
notes: "Reasoning model - internal thinking tokens billed as output"
|
|
31980
|
+
},
|
|
31981
|
+
o3: {
|
|
31982
|
+
promptPer1K: 0.002,
|
|
31983
|
+
completionPer1K: 0.008,
|
|
31984
|
+
lastUpdated: "2026-01"
|
|
31985
|
+
},
|
|
31986
|
+
"o3-mini": {
|
|
31987
|
+
promptPer1K: 0.0011,
|
|
31988
|
+
completionPer1K: 0.0044,
|
|
31989
|
+
lastUpdated: "2026-01"
|
|
31990
|
+
},
|
|
31991
|
+
"o4-mini": {
|
|
31992
|
+
promptPer1K: 0.0011,
|
|
31993
|
+
completionPer1K: 0.0044,
|
|
31994
|
+
lastUpdated: "2026-01"
|
|
31995
|
+
},
|
|
31996
|
+
"gpt-4-turbo": {
|
|
31997
|
+
promptPer1K: 0.01,
|
|
31998
|
+
completionPer1K: 0.03,
|
|
31999
|
+
lastUpdated: "2026-01"
|
|
32000
|
+
},
|
|
32001
|
+
"gpt-4": {
|
|
32002
|
+
promptPer1K: 0.03,
|
|
32003
|
+
completionPer1K: 0.06,
|
|
32004
|
+
lastUpdated: "2026-01"
|
|
32005
|
+
},
|
|
32006
|
+
"gpt-3.5-turbo": {
|
|
32007
|
+
promptPer1K: 0.0005,
|
|
32008
|
+
completionPer1K: 0.0015,
|
|
32009
|
+
lastUpdated: "2026-01"
|
|
32010
|
+
},
|
|
32011
|
+
"claude-opus-4.5": {
|
|
32012
|
+
promptPer1K: 0.005,
|
|
32013
|
+
completionPer1K: 0.025,
|
|
32014
|
+
lastUpdated: "2026-01",
|
|
32015
|
+
notes: "Most capable Claude model"
|
|
32016
|
+
},
|
|
32017
|
+
"claude-sonnet-4.5": {
|
|
32018
|
+
promptPer1K: 0.003,
|
|
32019
|
+
completionPer1K: 0.015,
|
|
32020
|
+
lastUpdated: "2026-01",
|
|
32021
|
+
notes: "Balanced performance and cost"
|
|
32022
|
+
},
|
|
32023
|
+
"claude-haiku-4.5": {
|
|
32024
|
+
promptPer1K: 0.001,
|
|
32025
|
+
completionPer1K: 0.005,
|
|
32026
|
+
lastUpdated: "2026-01",
|
|
32027
|
+
notes: "Fastest Claude model"
|
|
32028
|
+
},
|
|
32029
|
+
"claude-opus-4": {
|
|
32030
|
+
promptPer1K: 0.015,
|
|
32031
|
+
completionPer1K: 0.075,
|
|
32032
|
+
lastUpdated: "2026-01"
|
|
32033
|
+
},
|
|
32034
|
+
"claude-opus-4.1": {
|
|
32035
|
+
promptPer1K: 0.015,
|
|
32036
|
+
completionPer1K: 0.075,
|
|
32037
|
+
lastUpdated: "2026-01"
|
|
32038
|
+
},
|
|
32039
|
+
"claude-sonnet-4": {
|
|
32040
|
+
promptPer1K: 0.003,
|
|
32041
|
+
completionPer1K: 0.015,
|
|
32042
|
+
lastUpdated: "2026-01"
|
|
32043
|
+
},
|
|
32044
|
+
"claude-sonnet-3.7": {
|
|
32045
|
+
promptPer1K: 0.003,
|
|
32046
|
+
completionPer1K: 0.015,
|
|
32047
|
+
lastUpdated: "2026-01"
|
|
32048
|
+
},
|
|
32049
|
+
"claude-3-7-sonnet": {
|
|
32050
|
+
promptPer1K: 0.003,
|
|
32051
|
+
completionPer1K: 0.015,
|
|
32052
|
+
lastUpdated: "2026-01"
|
|
32053
|
+
},
|
|
32054
|
+
"claude-3-5-sonnet-20241022": {
|
|
32055
|
+
promptPer1K: 0.003,
|
|
32056
|
+
completionPer1K: 0.015,
|
|
32057
|
+
lastUpdated: "2026-01"
|
|
32058
|
+
},
|
|
32059
|
+
"claude-3-5-haiku-20241022": {
|
|
32060
|
+
promptPer1K: 0.0008,
|
|
32061
|
+
completionPer1K: 0.004,
|
|
32062
|
+
lastUpdated: "2026-01"
|
|
32063
|
+
},
|
|
32064
|
+
"claude-haiku-3.5": {
|
|
32065
|
+
promptPer1K: 0.0008,
|
|
32066
|
+
completionPer1K: 0.004,
|
|
32067
|
+
lastUpdated: "2026-01"
|
|
32068
|
+
},
|
|
32069
|
+
"claude-3-opus": {
|
|
32070
|
+
promptPer1K: 0.015,
|
|
32071
|
+
completionPer1K: 0.075,
|
|
32072
|
+
lastUpdated: "2026-01"
|
|
32073
|
+
},
|
|
32074
|
+
"claude-3-sonnet": {
|
|
32075
|
+
promptPer1K: 0.003,
|
|
32076
|
+
completionPer1K: 0.015,
|
|
32077
|
+
lastUpdated: "2026-01"
|
|
32078
|
+
},
|
|
32079
|
+
"claude-3-haiku": {
|
|
32080
|
+
promptPer1K: 0.00025,
|
|
32081
|
+
completionPer1K: 0.00125,
|
|
32082
|
+
lastUpdated: "2026-01"
|
|
32083
|
+
},
|
|
32084
|
+
"claude-3.5-sonnet": {
|
|
32085
|
+
promptPer1K: 0.003,
|
|
32086
|
+
completionPer1K: 0.015,
|
|
32087
|
+
lastUpdated: "2026-01"
|
|
32088
|
+
},
|
|
32089
|
+
"claude-3.5-haiku": {
|
|
32090
|
+
promptPer1K: 0.0008,
|
|
32091
|
+
completionPer1K: 0.004,
|
|
32092
|
+
lastUpdated: "2026-01"
|
|
32093
|
+
}
|
|
32094
|
+
};
|
|
32095
|
+
var DEFAULT_PRICING = {
|
|
32096
|
+
promptPer1K: 0.003,
|
|
32097
|
+
completionPer1K: 0.015,
|
|
32098
|
+
lastUpdated: "2026-01",
|
|
32099
|
+
notes: "Default pricing - verify with provider"
|
|
32100
|
+
};
|
|
32101
|
+
function getModelPricing(model) {
|
|
32102
|
+
if (MODEL_PRICING[model]) {
|
|
32103
|
+
return MODEL_PRICING[model];
|
|
32104
|
+
}
|
|
32105
|
+
const lowerModel = model.toLowerCase();
|
|
32106
|
+
for (const [key, pricing] of Object.entries(MODEL_PRICING)) {
|
|
32107
|
+
if (key.toLowerCase() === lowerModel) {
|
|
32108
|
+
return pricing;
|
|
32109
|
+
}
|
|
32110
|
+
}
|
|
32111
|
+
if (lowerModel.includes("gpt-5.2")) {
|
|
32112
|
+
return MODEL_PRICING["gpt-5.2"];
|
|
32113
|
+
}
|
|
32114
|
+
if (lowerModel.includes("gpt-5.1")) {
|
|
32115
|
+
return MODEL_PRICING["gpt-5.1"];
|
|
32116
|
+
}
|
|
32117
|
+
if (lowerModel.includes("gpt-5-mini")) {
|
|
32118
|
+
return MODEL_PRICING["gpt-5-mini"];
|
|
32119
|
+
}
|
|
32120
|
+
if (lowerModel.includes("gpt-5-nano")) {
|
|
32121
|
+
return MODEL_PRICING["gpt-5-nano"];
|
|
32122
|
+
}
|
|
32123
|
+
if (lowerModel.includes("gpt-5")) {
|
|
32124
|
+
return MODEL_PRICING["gpt-5"];
|
|
32125
|
+
}
|
|
32126
|
+
if (lowerModel.includes("gpt-4.1-mini")) {
|
|
32127
|
+
return MODEL_PRICING["gpt-4.1-mini"];
|
|
32128
|
+
}
|
|
32129
|
+
if (lowerModel.includes("gpt-4.1-nano")) {
|
|
32130
|
+
return MODEL_PRICING["gpt-4.1-nano"];
|
|
32131
|
+
}
|
|
32132
|
+
if (lowerModel.includes("gpt-4.1")) {
|
|
32133
|
+
return MODEL_PRICING["gpt-4.1"];
|
|
32134
|
+
}
|
|
32135
|
+
if (lowerModel.includes("gpt-4o-mini")) {
|
|
32136
|
+
return MODEL_PRICING["gpt-4o-mini"];
|
|
32137
|
+
}
|
|
32138
|
+
if (lowerModel.includes("gpt-4o")) {
|
|
32139
|
+
return MODEL_PRICING["gpt-4o"];
|
|
32140
|
+
}
|
|
32141
|
+
if (lowerModel.includes("o4-mini")) {
|
|
32142
|
+
return MODEL_PRICING["o4-mini"];
|
|
32143
|
+
}
|
|
32144
|
+
if (lowerModel.includes("o3-mini")) {
|
|
32145
|
+
return MODEL_PRICING["o3-mini"];
|
|
32146
|
+
}
|
|
32147
|
+
if (lowerModel.includes("o3")) {
|
|
32148
|
+
return MODEL_PRICING.o3;
|
|
32149
|
+
}
|
|
32150
|
+
if (lowerModel.includes("o1")) {
|
|
32151
|
+
return MODEL_PRICING.o1;
|
|
32152
|
+
}
|
|
32153
|
+
if (lowerModel.includes("gpt-4-turbo")) {
|
|
32154
|
+
return MODEL_PRICING["gpt-4-turbo"];
|
|
32155
|
+
}
|
|
32156
|
+
if (lowerModel.includes("gpt-4")) {
|
|
32157
|
+
return MODEL_PRICING["gpt-4"];
|
|
32158
|
+
}
|
|
32159
|
+
if (lowerModel.includes("gpt-3.5")) {
|
|
32160
|
+
return MODEL_PRICING["gpt-3.5-turbo"];
|
|
32161
|
+
}
|
|
32162
|
+
if (lowerModel.includes("opus-4.5") || lowerModel.includes("opus-4-5")) {
|
|
32163
|
+
return MODEL_PRICING["claude-opus-4.5"];
|
|
32164
|
+
}
|
|
32165
|
+
if (lowerModel.includes("sonnet-4.5") || lowerModel.includes("sonnet-4-5")) {
|
|
32166
|
+
return MODEL_PRICING["claude-sonnet-4.5"];
|
|
32167
|
+
}
|
|
32168
|
+
if (lowerModel.includes("haiku-4.5") || lowerModel.includes("haiku-4-5")) {
|
|
32169
|
+
return MODEL_PRICING["claude-haiku-4.5"];
|
|
32170
|
+
}
|
|
32171
|
+
if (lowerModel.includes("opus-4.1") || lowerModel.includes("opus-4-1")) {
|
|
32172
|
+
return MODEL_PRICING["claude-opus-4.1"];
|
|
32173
|
+
}
|
|
32174
|
+
if (lowerModel.includes("opus-4")) {
|
|
32175
|
+
return MODEL_PRICING["claude-opus-4"];
|
|
32176
|
+
}
|
|
32177
|
+
if (lowerModel.includes("sonnet-4")) {
|
|
32178
|
+
return MODEL_PRICING["claude-sonnet-4"];
|
|
32179
|
+
}
|
|
32180
|
+
if (lowerModel.includes("sonnet-3.7") || lowerModel.includes("sonnet-3-7")) {
|
|
32181
|
+
return MODEL_PRICING["claude-sonnet-3.7"];
|
|
32182
|
+
}
|
|
32183
|
+
if (lowerModel.includes("claude-3-5-sonnet") || lowerModel.includes("claude-3.5-sonnet")) {
|
|
32184
|
+
return MODEL_PRICING["claude-3.5-sonnet"];
|
|
32185
|
+
}
|
|
32186
|
+
if (lowerModel.includes("claude-3-5-haiku") || lowerModel.includes("claude-3.5-haiku")) {
|
|
32187
|
+
return MODEL_PRICING["claude-3.5-haiku"];
|
|
32188
|
+
}
|
|
32189
|
+
if (lowerModel.includes("claude-3-opus")) {
|
|
32190
|
+
return MODEL_PRICING["claude-3-opus"];
|
|
32191
|
+
}
|
|
32192
|
+
if (lowerModel.includes("claude-3-sonnet")) {
|
|
32193
|
+
return MODEL_PRICING["claude-3-sonnet"];
|
|
32194
|
+
}
|
|
32195
|
+
if (lowerModel.includes("claude-3-haiku")) {
|
|
32196
|
+
return MODEL_PRICING["claude-3-haiku"];
|
|
32197
|
+
}
|
|
32198
|
+
if (lowerModel.includes("claude")) {
|
|
32199
|
+
return MODEL_PRICING["claude-sonnet-4.5"];
|
|
32200
|
+
}
|
|
32201
|
+
return DEFAULT_PRICING;
|
|
32202
|
+
}
|
|
32203
|
+
function estimateCost(promptTokens, completionTokens, model) {
|
|
32204
|
+
const pricing = getModelPricing(model);
|
|
32205
|
+
const promptCostUsd = promptTokens / 1000 * pricing.promptPer1K;
|
|
32206
|
+
const completionCostUsd = completionTokens / 1000 * pricing.completionPer1K;
|
|
32207
|
+
const totalUsd = promptCostUsd + completionCostUsd;
|
|
32208
|
+
return {
|
|
32209
|
+
totalUsd,
|
|
32210
|
+
promptCostUsd,
|
|
32211
|
+
completionCostUsd,
|
|
32212
|
+
model,
|
|
32213
|
+
pricing
|
|
32214
|
+
};
|
|
32215
|
+
}
|
|
32216
|
+
function formatCost(costUsd) {
|
|
32217
|
+
if (costUsd < 0.01) {
|
|
32218
|
+
return `$${(costUsd * 100).toFixed(4)} cents`;
|
|
32219
|
+
}
|
|
32220
|
+
if (costUsd < 1) {
|
|
32221
|
+
return `$${costUsd.toFixed(4)}`;
|
|
32222
|
+
}
|
|
32223
|
+
return `$${costUsd.toFixed(2)}`;
|
|
32224
|
+
}
|
|
32225
|
+
function listKnownModels() {
|
|
32226
|
+
return Object.entries(MODEL_PRICING).map(([model, pricing]) => ({
|
|
32227
|
+
model,
|
|
32228
|
+
pricing
|
|
32229
|
+
}));
|
|
32230
|
+
}
|
|
32104
32231
|
// src/agent-evaluation/types.ts
|
|
32105
32232
|
function actionBudgetExceeded(task, trace) {
|
|
32106
32233
|
return trace.actions.length > task.maxActions;
|