mirror of
https://github.com/AgentSeal/codeburn.git
synced 2026-10-27 09:11:45 +00:00
Devin transcripts carry no committed_acu_cost, so the ACU path priced every session at zero and devin.acuUsdRate hid them all until it was set by hand. Cost now comes from each step's tokens and its generation_model through the shared pricing tables, with the cached share carved out of OpenAI-style prompt_tokens; swe-2-high stays unpriced at $0. Task text is the user turn, then the sessions.db title, then prompt_history. The menubar ACU field, its config store and the config key are gone. Separate commit: gpt-5-3-codex ids resolve to the gpt-5.3-codex pricing row instead of the base gpt-5 row.
94 lines
2.8 KiB
TypeScript
94 lines
2.8 KiB
TypeScript
import { mkdir, mkdtemp, rm, writeFile } from 'node:fs/promises'
|
|
import { tmpdir } from 'node:os'
|
|
import { join } from 'node:path'
|
|
import { spawnSync } from 'node:child_process'
|
|
|
|
import { describe, expect, it } from 'vitest'
|
|
|
|
import { calculateCost } from '../src/models.js'
|
|
|
|
function runCli(args: string[], home: string) {
|
|
return spawnSync(process.execPath, ['--import', 'tsx', 'src/cli.ts', ...args], {
|
|
cwd: process.cwd(),
|
|
env: {
|
|
...process.env,
|
|
HOME: home,
|
|
USERPROFILE: home,
|
|
HOMEPATH: home,
|
|
HOMEDRIVE: '',
|
|
CLAUDE_CONFIG_DIR: join(home, '.claude'),
|
|
CODEBURN_CACHE_DIR: join(home, '.cache', 'codeburn'),
|
|
TZ: 'UTC',
|
|
},
|
|
encoding: 'utf-8',
|
|
timeout: 30_000,
|
|
})
|
|
}
|
|
|
|
describe('codeburn report Devin model variants', () => {
|
|
it('keeps friendly Devin effort-tier names in JSON model rows and efficiency rows', async () => {
|
|
const home = await mkdtemp(join(tmpdir(), 'codeburn-cli-devin-models-'))
|
|
try {
|
|
const transcriptsDir = join(home, '.local', 'share', 'devin', 'cli', 'transcripts')
|
|
await mkdir(transcriptsDir, { recursive: true })
|
|
await writeFile(join(transcriptsDir, 'session-487.json'), JSON.stringify({
|
|
schema_version: '1.4',
|
|
session_id: 'session-487',
|
|
agent: { model_name: 'GPT-5.4' },
|
|
steps: [
|
|
{
|
|
step_id: 1,
|
|
message: 'fix the model row',
|
|
metadata: { is_user_input: true, created_at: '2026-04-10T09:00:00.000Z' },
|
|
},
|
|
{
|
|
step_id: 2,
|
|
message: 'editing',
|
|
tool_calls: [{ function_name: 'Edit' }],
|
|
metadata: {
|
|
created_at: '2026-04-10T09:01:00.000Z',
|
|
generation_model: 'gpt-5-3-codex-xhigh',
|
|
metrics: { input_tokens: 100, output_tokens: 25 },
|
|
},
|
|
},
|
|
],
|
|
}))
|
|
|
|
const result = runCli([
|
|
'report',
|
|
'--format',
|
|
'json',
|
|
'--from',
|
|
'2026-04-10',
|
|
'--to',
|
|
'2026-04-10',
|
|
'--provider',
|
|
'devin',
|
|
], home)
|
|
|
|
expect(result.status, result.stderr).toBe(0)
|
|
const report = JSON.parse(result.stdout) as {
|
|
models: Array<{
|
|
name: string
|
|
calls: number
|
|
cost: number
|
|
editTurns: number
|
|
oneShotTurns: number
|
|
costPerEdit: number | null
|
|
}>
|
|
}
|
|
|
|
expect(report.models).toHaveLength(1)
|
|
expect(report.models[0]).toMatchObject({
|
|
name: 'GPT-5.3 Codex (xhigh)',
|
|
calls: 1,
|
|
cost: calculateCost('gpt-5.3-codex-xhigh', 100, 25, 0, 0, 0),
|
|
editTurns: 1,
|
|
oneShotTurns: 1,
|
|
costPerEdit: calculateCost('gpt-5.3-codex-xhigh', 100, 25, 0, 0, 0),
|
|
})
|
|
} finally {
|
|
await rm(home, { recursive: true, force: true })
|
|
}
|
|
})
|
|
})
|