mirror of
https://github.com/AgentSeal/codeburn.git
synced 2026-08-10 17:14:48 +00:00
refactor(pricing): fan-out batch 2 — zed, gemini, kimi, pi/omp
This commit is contained in:
parent
7bc5f3b044
commit
a3380b2453
8 changed files with 29 additions and 42 deletions
|
|
@ -3,7 +3,6 @@ import { join } from 'path'
|
|||
import { homedir } from 'os'
|
||||
|
||||
import { readSessionFile } from '../fs-utils.js'
|
||||
import { calculateCost } from '../models.js'
|
||||
import { extractBashCommands } from '../bash-utils.js'
|
||||
import type { Provider, SessionSource, SessionParser, ParsedProviderCall } from './types.js'
|
||||
|
||||
|
|
@ -119,11 +118,6 @@ function parseSession(data: GeminiSession, seenKeys: Set<string>): ParsedProvide
|
|||
|
||||
seenKeys.add(dedupKey)
|
||||
|
||||
// Gemini bills thoughts at the output token rate; calculateCost does not
|
||||
// accept a reasoning parameter, so fold thoughts into the output count for
|
||||
// pricing while keeping outputTokens / reasoningTokens reported separately.
|
||||
const costUSD = calculateCost(msg.model, freshInput, totalOutput + totalThoughts, 0, totalCached, 0)
|
||||
|
||||
results.push({
|
||||
provider: 'gemini',
|
||||
model: msg.model,
|
||||
|
|
@ -134,7 +128,7 @@ function parseSession(data: GeminiSession, seenKeys: Set<string>): ParsedProvide
|
|||
cachedInputTokens: totalCached,
|
||||
reasoningTokens: totalThoughts,
|
||||
webSearchRequests: 0,
|
||||
costUSD,
|
||||
costBasis: 'estimated',
|
||||
tools: [...new Set(tools)],
|
||||
bashCommands: [...new Set(bashCommands)],
|
||||
timestamp: tsDate.toISOString(),
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import { homedir } from 'os'
|
|||
|
||||
import { extractBashCommands } from '../bash-utils.js'
|
||||
import { readSessionLines } from '../fs-utils.js'
|
||||
import { calculateCost, getShortModelName } from '../models.js'
|
||||
import { getShortModelName } from '../models.js'
|
||||
import type { ParsedProviderCall, Provider, SessionParser, SessionSource } from './types.js'
|
||||
|
||||
type JsonObject = Record<string, unknown>
|
||||
|
|
@ -297,14 +297,6 @@ function createParser(source: SessionSource, shareDir: string, seenKeys: Set<str
|
|||
seenKeys.add(dedupKey)
|
||||
|
||||
const model = stringField(envelope.payload, 'model') ?? stringField(envelope.payload, 'model_name') ?? configuredModel
|
||||
const costUSD = calculateCost(
|
||||
model,
|
||||
usage.inputTokens,
|
||||
usage.outputTokens,
|
||||
usage.cacheCreationInputTokens,
|
||||
usage.cacheReadInputTokens,
|
||||
0,
|
||||
)
|
||||
|
||||
yield {
|
||||
provider: 'kimi',
|
||||
|
|
@ -316,7 +308,7 @@ function createParser(source: SessionSource, shareDir: string, seenKeys: Set<str
|
|||
cachedInputTokens: usage.cacheReadInputTokens,
|
||||
reasoningTokens: 0,
|
||||
webSearchRequests: 0,
|
||||
costUSD,
|
||||
costBasis: 'estimated',
|
||||
tools: [...tools],
|
||||
bashCommands: [...bashCommands],
|
||||
timestamp: envelope.timestamp,
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@ import { basename, join } from 'path'
|
|||
import { homedir } from 'os'
|
||||
|
||||
import { readSessionFile } from '../fs-utils.js'
|
||||
import { calculateCost } from '../models.js'
|
||||
import { extractBashCommands } from '../bash-utils.js'
|
||||
import { normalizeContentBlocks } from '../content-utils.js'
|
||||
import type { Provider, SessionSource, SessionParser, ParsedProviderCall } from './types.js'
|
||||
|
|
@ -223,7 +222,6 @@ function createParser(source: SessionSource, seenKeys: Set<string>): SessionPars
|
|||
return typeof cmd === 'string' ? extractBashCommands(cmd) : []
|
||||
})
|
||||
|
||||
const costUSD = calculateCost(model, input, output, cacheWrite, cacheRead, 0)
|
||||
const timestamp = entry.timestamp ?? ''
|
||||
|
||||
yield {
|
||||
|
|
@ -236,7 +234,7 @@ function createParser(source: SessionSource, seenKeys: Set<string>): SessionPars
|
|||
cachedInputTokens: cacheRead,
|
||||
reasoningTokens: 0,
|
||||
webSearchRequests: 0,
|
||||
costUSD,
|
||||
costBasis: 'estimated',
|
||||
tools,
|
||||
bashCommands,
|
||||
skills,
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@ import { join } from 'path'
|
|||
import { homedir } from 'os'
|
||||
import zlib from 'zlib'
|
||||
|
||||
import { calculateCost } from '../models.js'
|
||||
import { getSqliteLoadError, isSqliteAvailable, openDatabase, type SqliteDatabase } from '../sqlite.js'
|
||||
import type { ParsedProviderCall, Provider, SessionParser, SessionSource } from './types.js'
|
||||
|
||||
|
|
@ -88,7 +87,7 @@ function buildCall(opts: {
|
|||
cachedInputTokens: cacheRead,
|
||||
reasoningTokens: 0,
|
||||
webSearchRequests: 0,
|
||||
costUSD: calculateCost(opts.model, input, output, cacheWrite, cacheRead, 0),
|
||||
costBasis: 'estimated',
|
||||
tools: [],
|
||||
bashCommands: [],
|
||||
timestamp: opts.timestamp,
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import { join } from 'path'
|
|||
import { tmpdir } from 'os'
|
||||
|
||||
import { createKimiProvider } from '../../src/providers/kimi.js'
|
||||
import { priceProviderCall } from '../../src/pricing-pass.js'
|
||||
import type { ParsedProviderCall } from '../../src/providers/types.js'
|
||||
|
||||
let tmpDir: string
|
||||
|
|
@ -44,7 +45,7 @@ async function writeSession(workDir: string, sessionId: string, lines: string[])
|
|||
async function collect(provider: ReturnType<typeof createKimiProvider>, path: string, seen = new Set<string>()): Promise<ParsedProviderCall[]> {
|
||||
const parser = provider.createSessionParser({ path, project: 'app', provider: 'kimi' }, seen)
|
||||
const calls: ParsedProviderCall[] = []
|
||||
for await (const call of parser.parse()) calls.push(call)
|
||||
for await (const call of parser.parse()) calls.push(priceProviderCall(call))
|
||||
return calls
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import { join } from 'path'
|
|||
import { tmpdir } from 'os'
|
||||
|
||||
import { createOmpProvider } from '../../src/providers/pi.js'
|
||||
import { priceProviderCall } from '../../src/pricing-pass.js'
|
||||
import type { ParsedProviderCall } from '../../src/providers/types.js'
|
||||
|
||||
let tmpDir: string
|
||||
|
|
@ -149,7 +150,7 @@ describe('omp provider - JSONL parsing', () => {
|
|||
const source = { path: filePath, project: 'myproject', provider: 'omp' }
|
||||
const calls: ParsedProviderCall[] = []
|
||||
for await (const call of provider.createSessionParser(source, new Set()).parse()) {
|
||||
calls.push(call)
|
||||
calls.push(priceProviderCall(call))
|
||||
}
|
||||
|
||||
expect(calls).toHaveLength(1)
|
||||
|
|
@ -179,7 +180,7 @@ describe('omp provider - JSONL parsing', () => {
|
|||
const source = { path: filePath, project: 'myproject', provider: 'omp' }
|
||||
const calls: ParsedProviderCall[] = []
|
||||
for await (const call of provider.createSessionParser(source, new Set()).parse()) {
|
||||
calls.push(call)
|
||||
calls.push(priceProviderCall(call))
|
||||
}
|
||||
|
||||
// cost must be calculated by codeburn, not taken from usage.cost (which is zeroed in fixture)
|
||||
|
|
@ -199,7 +200,7 @@ describe('omp provider - JSONL parsing', () => {
|
|||
const source = { path: filePath, project: 'myproject', provider: 'omp' }
|
||||
const calls: ParsedProviderCall[] = []
|
||||
for await (const call of provider.createSessionParser(source, new Set()).parse()) {
|
||||
calls.push(call)
|
||||
calls.push(priceProviderCall(call))
|
||||
}
|
||||
|
||||
expect(calls[0]!.tools).toEqual(['Read', 'Edit', 'Bash'])
|
||||
|
|
@ -217,7 +218,7 @@ describe('omp provider - JSONL parsing', () => {
|
|||
const source = { path: filePath, project: 'myproject', provider: 'omp' }
|
||||
const calls: ParsedProviderCall[] = []
|
||||
for await (const call of provider.createSessionParser(source, new Set()).parse()) {
|
||||
calls.push(call)
|
||||
calls.push(priceProviderCall(call))
|
||||
}
|
||||
|
||||
expect(calls).toHaveLength(0)
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import { join } from 'path'
|
|||
import { tmpdir } from 'os'
|
||||
|
||||
import { createPiProvider } from '../../src/providers/pi.js'
|
||||
import { priceProviderCall } from '../../src/pricing-pass.js'
|
||||
import type { ParsedProviderCall } from '../../src/providers/types.js'
|
||||
import { classifyTurn } from '../../src/classifier.js'
|
||||
import type { ParsedApiCall, ParsedTurn } from '../../src/types.js'
|
||||
|
|
@ -208,7 +209,7 @@ describe('pi provider - JSONL parsing', () => {
|
|||
const source = { path: filePath, project: 'myproject', provider: 'pi' }
|
||||
const calls: ParsedProviderCall[] = []
|
||||
for await (const call of provider.createSessionParser(source, new Set()).parse()) {
|
||||
calls.push(call)
|
||||
calls.push(priceProviderCall(call))
|
||||
}
|
||||
|
||||
expect(calls).toHaveLength(1)
|
||||
|
|
@ -249,7 +250,7 @@ describe('pi provider - JSONL parsing', () => {
|
|||
const source = { path: filePath, project: 'myproject', provider: 'pi' }
|
||||
const calls: ParsedProviderCall[] = []
|
||||
for await (const call of provider.createSessionParser(source, new Set()).parse()) {
|
||||
calls.push(call)
|
||||
calls.push(priceProviderCall(call))
|
||||
}
|
||||
|
||||
// The assistant turn is still parsed, and the string user content is paired.
|
||||
|
|
@ -275,7 +276,7 @@ describe('pi provider - JSONL parsing', () => {
|
|||
const source = { path: filePath, project: 'myproject', provider: 'pi' }
|
||||
const calls: ParsedProviderCall[] = []
|
||||
for await (const call of provider.createSessionParser(source, new Set()).parse()) {
|
||||
calls.push(call)
|
||||
calls.push(priceProviderCall(call))
|
||||
}
|
||||
|
||||
expect(calls[0]!.tools).toEqual(['Read', 'Edit', 'Bash'])
|
||||
|
|
@ -296,7 +297,7 @@ describe('pi provider - JSONL parsing', () => {
|
|||
const source = { path: filePath, project: 'myproject', provider: 'pi' }
|
||||
const calls: ParsedProviderCall[] = []
|
||||
for await (const call of provider.createSessionParser(source, new Set()).parse()) {
|
||||
calls.push(call)
|
||||
calls.push(priceProviderCall(call))
|
||||
}
|
||||
|
||||
expect(calls[0]!.bashCommands).toEqual(['git', 'bun'])
|
||||
|
|
@ -319,7 +320,7 @@ describe('pi provider - JSONL parsing', () => {
|
|||
const source = { path: filePath, project: 'myproject', provider: 'pi' }
|
||||
const calls: ParsedProviderCall[] = []
|
||||
for await (const call of provider.createSessionParser(source, new Set()).parse()) {
|
||||
calls.push(call)
|
||||
calls.push(priceProviderCall(call))
|
||||
}
|
||||
|
||||
// The SKILL.md read is surfaced as the Skill tool (not Read); the plain
|
||||
|
|
@ -341,7 +342,7 @@ describe('pi provider - JSONL parsing', () => {
|
|||
const source = { path: filePath, project: 'myproject', provider: 'pi' }
|
||||
const calls: ParsedProviderCall[] = []
|
||||
for await (const call of provider.createSessionParser(source, new Set()).parse()) {
|
||||
calls.push(call)
|
||||
calls.push(priceProviderCall(call))
|
||||
}
|
||||
|
||||
expect(calls[0]!.skills).toEqual(['commit-workflow'])
|
||||
|
|
@ -361,7 +362,7 @@ describe('pi provider - JSONL parsing', () => {
|
|||
const source = { path: filePath, project: 'myproject', provider: 'pi' }
|
||||
const calls: ParsedProviderCall[] = []
|
||||
for await (const call of provider.createSessionParser(source, new Set()).parse()) {
|
||||
calls.push(call)
|
||||
calls.push(priceProviderCall(call))
|
||||
}
|
||||
|
||||
expect(calls[0]!.skills).toEqual(['deep-research'])
|
||||
|
|
@ -381,7 +382,7 @@ describe('pi provider - JSONL parsing', () => {
|
|||
const source = { path: filePath, project: 'myproject', provider: 'pi' }
|
||||
const calls: ParsedProviderCall[] = []
|
||||
for await (const call of provider.createSessionParser(source, new Set()).parse()) {
|
||||
calls.push(call)
|
||||
calls.push(priceProviderCall(call))
|
||||
}
|
||||
|
||||
expect(calls[0]!.skills).toEqual([])
|
||||
|
|
@ -401,7 +402,7 @@ describe('pi provider - JSONL parsing', () => {
|
|||
const source = { path: filePath, project: 'myproject', provider: 'pi' }
|
||||
const calls: ParsedProviderCall[] = []
|
||||
for await (const call of provider.createSessionParser(source, new Set()).parse()) {
|
||||
calls.push(call)
|
||||
calls.push(priceProviderCall(call))
|
||||
}
|
||||
|
||||
// End to end: the parsed skill load must reach `subCategory`, or the
|
||||
|
|
@ -422,7 +423,7 @@ describe('pi provider - JSONL parsing', () => {
|
|||
const source = { path: filePath, project: 'myproject', provider: 'pi' }
|
||||
const calls: ParsedProviderCall[] = []
|
||||
for await (const call of provider.createSessionParser(source, new Set()).parse()) {
|
||||
calls.push(call)
|
||||
calls.push(priceProviderCall(call))
|
||||
}
|
||||
|
||||
expect(calls).toHaveLength(0)
|
||||
|
|
@ -441,12 +442,12 @@ describe('pi provider - JSONL parsing', () => {
|
|||
|
||||
const firstRun: ParsedProviderCall[] = []
|
||||
for await (const call of provider.createSessionParser(source, seenKeys).parse()) {
|
||||
firstRun.push(call)
|
||||
firstRun.push(priceProviderCall(call))
|
||||
}
|
||||
|
||||
const secondRun: ParsedProviderCall[] = []
|
||||
for await (const call of provider.createSessionParser(source, seenKeys).parse()) {
|
||||
secondRun.push(call)
|
||||
secondRun.push(priceProviderCall(call))
|
||||
}
|
||||
|
||||
expect(firstRun).toHaveLength(1)
|
||||
|
|
@ -467,7 +468,7 @@ describe('pi provider - JSONL parsing', () => {
|
|||
const source = { path: filePath, project: 'myproject', provider: 'pi' }
|
||||
const calls: ParsedProviderCall[] = []
|
||||
for await (const call of provider.createSessionParser(source, new Set()).parse()) {
|
||||
calls.push(call)
|
||||
calls.push(priceProviderCall(call))
|
||||
}
|
||||
|
||||
expect(calls).toHaveLength(2)
|
||||
|
|
@ -482,7 +483,7 @@ describe('pi provider - JSONL parsing', () => {
|
|||
const source = { path: '/nonexistent/session.jsonl', project: 'test', provider: 'pi' }
|
||||
const calls: ParsedProviderCall[] = []
|
||||
for await (const call of provider.createSessionParser(source, new Set()).parse()) {
|
||||
calls.push(call)
|
||||
calls.push(priceProviderCall(call))
|
||||
}
|
||||
expect(calls).toHaveLength(0)
|
||||
})
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import zlib from 'zlib'
|
|||
|
||||
import { createZedProvider } from '../../src/providers/zed.js'
|
||||
import { isSqliteAvailable } from '../../src/sqlite.js'
|
||||
import { priceProviderCall } from '../../src/pricing-pass.js'
|
||||
import type { ParsedProviderCall } from '../../src/providers/types.js'
|
||||
|
||||
const requireForTest = createRequire(import.meta.url)
|
||||
|
|
@ -76,7 +77,7 @@ async function collectCalls(dbPath: string, seenKeys = new Set<string>()): Promi
|
|||
const calls: ParsedProviderCall[] = []
|
||||
for (const source of sources) {
|
||||
for await (const call of provider.createSessionParser(source, seenKeys).parse()) {
|
||||
calls.push(call)
|
||||
calls.push(priceProviderCall(call))
|
||||
}
|
||||
}
|
||||
return calls
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue