refactor(pricing): fan-out batch 1 — goose, mux, open-design, lingtai-tui, droid

This commit is contained in:
iamtoruk 2026-07-26 08:06:53 -07:00
parent 34905db773
commit 0eb0c6f2da
7 changed files with 23 additions and 58 deletions

View file

@ -3,7 +3,7 @@ import { join } from 'path'
import { homedir } from 'os'
import { readSessionFile, readSessionLines } from '../fs-utils.js'
import { calculateCost, getShortModelName } from '../models.js'
import { getShortModelName } from '../models.js'
import { extractBashCommands } from '../bash-utils.js'
import { normalizeContentBlocks } from '../content-utils.js'
import type {
@ -255,15 +255,6 @@ function createParser(
if (seenKeys.has(dedupKey)) continue
seenKeys.add(dedupKey)
const costUSD = calculateCost(
sessionModelDisplay.toLowerCase(),
inputTokens,
outputTokens + thinkingTokens,
cacheCreationTokens,
cacheReadTokens,
0,
)
// Use the call's timestamp, or session_start timestamp
const timestamp = call.timestamp || ''
@ -277,7 +268,7 @@ function createParser(
cachedInputTokens: cacheReadTokens,
reasoningTokens: thinkingTokens,
webSearchRequests: 0,
costUSD,
costBasis: 'estimated',
tools: call.tools,
bashCommands: call.bashCommands,
timestamp,

View file

@ -1,7 +1,7 @@
import { join } from 'path'
import { homedir, platform } from 'os'
import { calculateCost, getShortModelName } from '../models.js'
import { getShortModelName } from '../models.js'
import { extractBashCommands } from '../bash-utils.js'
import { isSqliteAvailable, getSqliteLoadError, openDatabase, blobToText, type SqliteDatabase } from '../sqlite.js'
import type { ToolCall } from '../types.js'
@ -190,7 +190,6 @@ function createParser(source: SessionSource, seenKeys: Set<string>): SessionPars
const config = parseModelConfig(blobToText(session.model_config_json))
const model = config.model_name ?? 'unknown'
const costUSD = calculateCost(model, inputTokens, outputTokens, 0, 0, 0)
const { tools, bashCommands, toolSequence } = extractToolsFromMessages(db, sessionId)
const userMessage = getFirstUserMessage(db, sessionId)
@ -210,7 +209,7 @@ function createParser(source: SessionSource, seenKeys: Set<string>): SessionPars
cachedInputTokens: 0,
reasoningTokens: 0,
webSearchRequests: 0,
costUSD,
costBasis: 'estimated',
tools,
bashCommands,
toolSequence: toolSequence.length > 1 ? toolSequence : undefined,

View file

@ -3,7 +3,7 @@ import { basename, delimiter, dirname, join, resolve } from 'path'
import { homedir } from 'os'
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>
@ -366,15 +366,6 @@ function createParser(source: SessionSource): SessionParser {
cachedInputTokens,
].join(':')
const costUSD = calculateCost(
model,
inputTokens,
outputTokens + reasoningTokens,
0,
cachedInputTokens,
0,
)
yield {
provider: 'lingtai-tui',
model,
@ -385,7 +376,7 @@ function createParser(source: SessionSource): SessionParser {
cachedInputTokens,
reasoningTokens,
webSearchRequests: 0,
costUSD,
costBasis: 'estimated',
tools: activity.tools,
bashCommands: [],
subagentTypes: activity.subagentTypes,

View file

@ -3,7 +3,7 @@ import { basename, dirname, join, resolve } from 'path'
import { homedir } from 'os'
import { readSessionLines } from '../fs-utils.js'
import { calculateCost, getShortModelName } from '../models.js'
import { getShortModelName } from '../models.js'
import { extractBashCommands } from '../bash-utils.js'
import type { Provider, SessionSource, SessionParser, ParsedProviderCall } from './types.js'
import { safeNumber } from '../parser.js'
@ -217,15 +217,6 @@ function createParser(source: SessionSource, seenKeys: Set<string>): SessionPars
return typeof script === 'string' ? extractBashCommands(script) : []
})
const costUSD = calculateCost(
model,
inputTokens,
outputTokens + reasoning,
cacheCreate,
cacheRead,
0,
)
const timestamp = toIsoTimestamp(meta.timestamp, msg.createdAt)
yield {
@ -238,7 +229,7 @@ function createParser(source: SessionSource, seenKeys: Set<string>): SessionPars
cachedInputTokens: cacheRead,
reasoningTokens: reasoning,
webSearchRequests: 0,
costUSD,
costBasis: 'estimated',
tools,
bashCommands,
timestamp,

View file

@ -3,7 +3,6 @@ import { basename, dirname, join } from 'path'
import { homedir, platform } from 'os'
import { readSessionLines } from '../fs-utils.js'
import { calculateCost } from '../models.js'
import type { Provider, SessionSource, SessionParser, ParsedProviderCall } from './types.js'
const PROVIDER_NAME = 'open-design'
@ -199,14 +198,6 @@ function createParser(source: SessionSource, seenKeys: Set<string>): SessionPars
seenKeys.add(dedupKey)
const uncachedInputTokens = Math.max(0, usage.inputTokens - usage.cacheReadTokens)
const costUSD = calculateCost(
currentModel,
uncachedInputTokens,
usage.outputTokens + usage.reasoningTokens,
0,
usage.cacheReadTokens,
0,
)
yield {
provider: PROVIDER_NAME,
@ -220,7 +211,7 @@ function createParser(source: SessionSource, seenKeys: Set<string>): SessionPars
cachedInputTokens: usage.cacheReadTokens,
reasoningTokens: usage.reasoningTokens,
webSearchRequests: 0,
costUSD,
costBasis: 'estimated',
tools: [],
bashCommands: [],
timestamp: timestampValue(entry.timestamp),

View file

@ -5,6 +5,7 @@ import { tmpdir } from 'os'
import { createMuxProvider } from '../../src/providers/mux.js'
import type { ParsedProviderCall } from '../../src/providers/types.js'
import { priceProviderCall } from '../../src/pricing-pass.js'
let tmpDir: string
@ -187,7 +188,7 @@ describe('mux provider - session discovery', () => {
const sessions = await provider.discoverSessions()
const calls: ParsedProviderCall[] = []
for (const source of sessions) {
for await (const call of provider.createSessionParser(source, seenKeys).parse()) calls.push(call)
for await (const call of provider.createSessionParser(source, seenKeys).parse()) calls.push(priceProviderCall(call))
}
expect(calls).toHaveLength(2)
@ -217,7 +218,7 @@ describe('mux provider - chat.jsonl parsing', () => {
const source = { path: filePath, project: 'myproject', provider: 'mux' }
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)
@ -253,7 +254,7 @@ describe('mux provider - chat.jsonl parsing', () => {
const source = { path: filePath, project: 'myproject', provider: 'mux' }
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'])
@ -274,7 +275,7 @@ describe('mux provider - chat.jsonl parsing', () => {
const source = { path: filePath, project: 'myproject', provider: 'mux' }
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)
})
@ -288,7 +289,7 @@ describe('mux provider - chat.jsonl parsing', () => {
const source = { path: filePath, project: 'myproject', provider: 'mux' }
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)
})
@ -301,9 +302,9 @@ describe('mux provider - chat.jsonl parsing', () => {
const seenKeys = new Set<string>()
const first: ParsedProviderCall[] = []
for await (const call of provider.createSessionParser(source, seenKeys).parse()) first.push(call)
for await (const call of provider.createSessionParser(source, seenKeys).parse()) first.push(priceProviderCall(call))
const second: ParsedProviderCall[] = []
for await (const call of provider.createSessionParser(source, seenKeys).parse()) second.push(call)
for await (const call of provider.createSessionParser(source, seenKeys).parse()) second.push(priceProviderCall(call))
expect(first).toHaveLength(1)
expect(second).toHaveLength(0)
@ -320,7 +321,7 @@ describe('mux provider - chat.jsonl parsing', () => {
const provider = createMuxProvider(tmpDir)
const source = { path: filePath, project: 'myproject', provider: 'mux' }
const calls: ParsedProviderCall[] = []
for await (const call of provider.createSessionParser(source, new Set()).parse()) calls.push(call)
for await (const call of provider.createSessionParser(source, new Set()).parse()) calls.push(priceProviderCall(call))
expect(calls).toHaveLength(2)
expect(calls[0]!.userMessage).toBe('first question')
@ -333,7 +334,7 @@ describe('mux provider - chat.jsonl parsing', () => {
const provider = createMuxProvider(tmpDir)
const source = { path: join(tmpDir, 'sessions', 'nope', 'chat.jsonl'), project: 'x', provider: 'mux' }
const calls: ParsedProviderCall[] = []
for await (const call of provider.createSessionParser(source, new Set()).parse()) calls.push(call)
for await (const call of provider.createSessionParser(source, new Set()).parse()) calls.push(priceProviderCall(call))
expect(calls).toHaveLength(0)
})
@ -350,7 +351,7 @@ describe('mux provider - chat.jsonl parsing', () => {
const provider = createMuxProvider(tmpDir)
const source = { path: filePath, project: 'p', provider: 'mux' }
const calls: ParsedProviderCall[] = []
for await (const call of provider.createSessionParser(source, new Set()).parse()) calls.push(call)
for await (const call of provider.createSessionParser(source, new Set()).parse()) calls.push(priceProviderCall(call))
expect(calls).toHaveLength(2)
expect(new Set(calls.map(c => c.deduplicationKey)).size).toBe(2)
@ -368,7 +369,7 @@ describe('mux provider - chat.jsonl parsing', () => {
const provider = createMuxProvider(tmpDir)
const source = { path: filePath, project: 'p', provider: 'mux' }
const calls: ParsedProviderCall[] = []
for await (const call of provider.createSessionParser(source, new Set()).parse()) calls.push(call)
for await (const call of provider.createSessionParser(source, new Set()).parse()) calls.push(priceProviderCall(call))
expect(calls).toHaveLength(3) // none of the malformed lines aborted the parse
expect(calls.find(c => c.deduplicationKey === 'mux:ws-abc:good')?.inputTokens).toBe(100)

View file

@ -8,6 +8,7 @@ import { clearSessionCache, filterProjectsByDateRange, parseAllSessions } from '
import { allProviderNames } from '../../src/providers/index.js'
import { createOpenDesignProvider } from '../../src/providers/open-design.js'
import type { ParsedProviderCall, SessionSource } from '../../src/providers/types.js'
import { priceProviderCall } from '../../src/pricing-pass.js'
const fixtureRoot = join(import.meta.dirname, '../fixtures/open-design')
const dataDir = join(fixtureRoot, 'namespaces', 'release-stable', 'data')
@ -19,7 +20,7 @@ let cacheDir: string | undefined
async function collect(source: SessionSource, seenKeys = new Set<string>()): Promise<ParsedProviderCall[]> {
const provider = createOpenDesignProvider()
const calls: ParsedProviderCall[] = []
for await (const call of provider.createSessionParser(source, seenKeys).parse()) calls.push(call)
for await (const call of provider.createSessionParser(source, seenKeys).parse()) calls.push(priceProviderCall(call))
return calls
}