fix(runs): count a model run once when the terminal node carries its result on
A terminal node's result is the record it received, so after post_upgrade the run listed the same post-upgrade check twice and counted its cost twice (run view, run list and model costs). Identical calls now count once. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
d05b6c27f3
commit
bcd144fbdc
2 files changed
+25
-2
No files matched your search
@@ -27,4 +27,18 @@ describe('workflowModelCalls', () => {
|
||||
expect(calls[2]).toMatchObject({ instanceId: 'run-1', workflowId: 'update-radarr', summary: 'Clean start.', actions: ['hl ps'], at: '2026-09-22T21:35:00.000Z' });
|
||||
expect(totalCostUsd(calls)).toBeCloseTo(0.82);
|
||||
});
|
||||
|
||||
it('counts a model run once when a later node carries its result on, as the terminal node does', () => {
|
||||
const postUpgrade = { action: 'model.postUpgrade', state: 'completed', report: { outcome: 'nothing_to_do', summary: 'Clean start.', actions: ['hl ps'], model: 'sonnet', costUsd: 0.2 } };
|
||||
const notes = { verdict: 'attention', summary: 'A migration runs.', model: 'opus', costUsd: 0.12 };
|
||||
const calls = workflowModelCalls({ id: 'run-1', workflowId: 'update-radarr' }, [
|
||||
attempt(1, 'release_notes', notes),
|
||||
attempt(2, 'gate', notes),
|
||||
attempt(3, 'post_upgrade', postUpgrade),
|
||||
attempt(4, 'terminal_updated', postUpgrade),
|
||||
]);
|
||||
|
||||
expect(calls.map((call) => call.nodeId)).toEqual(['release_notes', 'post_upgrade']);
|
||||
expect(totalCostUsd(calls)).toBeCloseTo(0.32);
|
||||
});
|
||||
});
|
||||
@@ -22,9 +22,19 @@ const cost = (value: unknown): number => (typeof value === 'number' && Number.is
|
||||
/**
|
||||
* The model runs a workflow run made, read from its node attempts: a model action's result carries `report`
|
||||
* (app/privileged-action-executor-adapter), the release notes operation's result carries `verdict` and `model`.
|
||||
* A node that passes the record on (a terminal node's result is its input) repeats that result; the same call counts once.
|
||||
*/
|
||||
export function workflowModelCalls(instance: { readonly id: string; readonly workflowId: string }, attempts: readonly NodeAttempt[]): WorkflowModelCall[] {
|
||||
return attempts.flatMap((attempt): WorkflowModelCall[] => {
|
||||
const seen = new Set<string>();
|
||||
return attempts.flatMap((attempt) => modelCallOf(instance, attempt)).filter(({ nodeId: _nodeId, at: _at, ...call }) => {
|
||||
const key = JSON.stringify(call);
|
||||
if (seen.has(key)) return false;
|
||||
seen.add(key);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
function modelCallOf(instance: { readonly id: string; readonly workflowId: string }, attempt: NodeAttempt): WorkflowModelCall[] {
|
||||
const result = record(attempt.result);
|
||||
const at = attempt.finishedAt ?? attempt.startedAt;
|
||||
const base = { instanceId: instance.id, workflowId: instance.workflowId, nodeId: attempt.nodeId, at };
|
||||
@@ -47,7 +57,6 @@ export function workflowModelCalls(instance: { readonly id: string; readonly wor
|
||||
}];
|
||||
}
|
||||
return [];
|
||||
});
|
||||
}
|
||||
|
||||
export function totalCostUsd(calls: readonly WorkflowModelCall[]): number {
|
||||
|
||||
Reference in new issue
Block a user