Skip to content

Commit d343bc1

Browse files
committed
Further cache debugging code to track usage
1 parent 8c81553 commit d343bc1

File tree

6 files changed

+118
-2
lines changed

6 files changed

+118
-2
lines changed

common/src/types/contracts/llm.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,13 @@ export type StreamChunk =
2525
>
2626
| { type: 'error'; message: string }
2727

28+
export type CacheDebugUsageData = {
29+
inputTokens: number
30+
outputTokens: number
31+
cachedInputTokens: number
32+
totalTokens: number
33+
}
34+
2835
export type PromptAiSdkStreamFn = (
2936
params: {
3037
apiKey: string
@@ -45,6 +52,7 @@ export type PromptAiSdkStreamFn = (
4552
rawBody: unknown
4653
normalizedBody?: unknown
4754
}) => void
55+
onCacheDebugUsageReceived?: (usage: CacheDebugUsageData) => void
4856
includeCacheControl?: boolean
4957
cacheDebugCorrelation?: string
5058
agentProviderOptions?: OpenRouterProviderRoutingOptions
@@ -79,6 +87,7 @@ export type PromptAiSdkFn = (
7987
rawBody: unknown
8088
normalizedBody?: unknown
8189
}) => void
90+
onCacheDebugUsageReceived?: (usage: CacheDebugUsageData) => void
8291
includeCacheControl?: boolean
8392
cacheDebugCorrelation?: string
8493
agentProviderOptions?: OpenRouterProviderRoutingOptions
@@ -114,6 +123,7 @@ export type PromptAiSdkStructuredInput<T> = {
114123
rawBody: unknown
115124
normalizedBody?: unknown
116125
}) => void
126+
onCacheDebugUsageReceived?: (usage: CacheDebugUsageData) => void
117127
includeCacheControl?: boolean
118128
cacheDebugCorrelation?: string
119129
agentProviderOptions?: OpenRouterProviderRoutingOptions

packages/agent-runtime/src/prompt-agent-stream.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { globalStopSequence } from './constants'
33
import type { AgentTemplate } from './templates/types'
44
import type { TrackEventFn } from '@codebuff/common/types/contracts/analytics'
55
import type { SendActionFn } from '@codebuff/common/types/contracts/client'
6-
import type { PromptAiSdkStreamFn } from '@codebuff/common/types/contracts/llm'
6+
import type { CacheDebugUsageData, PromptAiSdkStreamFn } from '@codebuff/common/types/contracts/llm'
77
import type { Logger } from '@codebuff/common/types/contracts/logger'
88
import type { ParamsOf } from '@codebuff/common/types/function-params'
99
import type { Message } from '@codebuff/common/types/messages/codebuff-message'
@@ -32,6 +32,7 @@ export const getAgentStreamFromTemplate = (params: {
3232
rawBody: unknown
3333
normalizedBody?: unknown
3434
}) => void
35+
onCacheDebugUsageReceived?: (usage: CacheDebugUsageData) => void
3536

3637
onCostCalculated?: (credits: number) => Promise<void>
3738
promptAiSdkStream: PromptAiSdkStreamFn
@@ -55,6 +56,7 @@ export const getAgentStreamFromTemplate = (params: {
5556
userInputId,
5657
cacheDebugCorrelation,
5758
onCacheDebugProviderRequestBuilt,
59+
onCacheDebugUsageReceived,
5860

5961
sendAction,
6062
onCostCalculated,
@@ -90,6 +92,7 @@ export const getAgentStreamFromTemplate = (params: {
9092
userInputId,
9193
cacheDebugCorrelation,
9294
onCacheDebugProviderRequestBuilt,
95+
onCacheDebugUsageReceived,
9396

9497
onCostCalculated,
9598
sendAction,

packages/agent-runtime/src/run-agent-step.ts

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import { getAgentOutput } from './util/agent-output'
2323
import {
2424
createCacheDebugSnapshot,
2525
enrichCacheDebugSnapshotWithProviderRequest,
26+
enrichCacheDebugSnapshotWithUsage,
2627
} from './util/cache-debug'
2728
import {
2829
withSystemInstructionTags,
@@ -39,7 +40,7 @@ import type {
3940
FinishAgentRunFn,
4041
StartAgentRunFn,
4142
} from '@codebuff/common/types/contracts/database'
42-
import type { PromptAiSdkFn } from '@codebuff/common/types/contracts/llm'
43+
import type { CacheDebugUsageData, PromptAiSdkFn } from '@codebuff/common/types/contracts/llm'
4344
import type { Logger } from '@codebuff/common/types/contracts/logger'
4445
import type {
4546
ParamsExcluding,
@@ -312,6 +313,17 @@ export const runAgentStep = async (
312313
}
313314
: undefined
314315

316+
const onCacheDebugUsageReceived =
317+
cacheDebugCorrelation
318+
? (usage: CacheDebugUsageData) => {
319+
enrichCacheDebugSnapshotWithUsage({
320+
correlation: cacheDebugCorrelation,
321+
usage,
322+
logger,
323+
})
324+
}
325+
: undefined
326+
315327
logger.debug(
316328
{
317329
iteration: iterationNum,
@@ -343,6 +355,7 @@ export const runAgentStep = async (
343355
? serializeCacheDebugCorrelation(cacheDebugCorrelation)
344356
: undefined,
345357
onCacheDebugProviderRequestBuilt,
358+
onCacheDebugUsageReceived,
346359
})
347360

348361
if (result.aborted) {
@@ -399,6 +412,7 @@ export const runAgentStep = async (
399412
includeCacheControl: supportsCacheControl(agentTemplate.model),
400413
messages: [systemMessage(system), ...agentState.messageHistory],
401414
onCacheDebugProviderRequestBuilt,
415+
onCacheDebugUsageReceived,
402416
template: agentTemplate,
403417
onCostCalculated,
404418
})

packages/agent-runtime/src/util/cache-debug.ts

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { dirname, join } from 'path'
55
import {
66
type CacheDebugCorrelation,
77
} from '@codebuff/common/util/cache-debug'
8+
import type { CacheDebugUsageData } from '@codebuff/common/types/contracts/llm'
89
import type { Logger } from '@codebuff/common/types/contracts/logger'
910
import type { Message } from '@codebuff/common/types/messages/codebuff-message'
1011
import type { ProviderMetadata } from '@codebuff/common/types/messages/provider-metadata'
@@ -50,6 +51,7 @@ export type CacheDebugSnapshot = {
5051
toolsHash?: string
5152
preConversion: CacheDebugPreConversionSnapshot
5253
providerRequest?: CacheDebugProviderRequestSnapshot
54+
usage?: CacheDebugUsageData
5355
}
5456

5557
function getCacheDebugDir(projectRoot: string) {
@@ -241,6 +243,42 @@ export function createCacheDebugSnapshot(params: {
241243
return { snapshotId, filename, projectRoot }
242244
}
243245

246+
export function enrichCacheDebugSnapshotWithUsage(params: {
247+
correlation: CacheDebugCorrelation
248+
usage: CacheDebugUsageData
249+
logger: Logger
250+
}) {
251+
const { correlation, usage, logger } = params
252+
try {
253+
const existing = loadSnapshot({
254+
projectRoot: correlation.projectRoot,
255+
filename: correlation.filename,
256+
})
257+
if (!existing) {
258+
logger.warn(
259+
`[Cache Debug] Could not find snapshot ${correlation.filename} to enrich with usage`,
260+
)
261+
return
262+
}
263+
264+
if (existing.id !== correlation.snapshotId) {
265+
logger.warn(
266+
`[Cache Debug] Snapshot ID mismatch while enriching ${correlation.filename} with usage`,
267+
)
268+
return
269+
}
270+
271+
const updated: CacheDebugSnapshot = {
272+
...existing,
273+
usage,
274+
}
275+
276+
writeSnapshot({ snapshot: updated, logger })
277+
} catch (err) {
278+
logger.warn({ error: err }, '[Cache Debug] Failed to enrich snapshot with usage')
279+
}
280+
}
281+
244282
export function enrichCacheDebugSnapshotWithProviderRequest(params: {
245283
correlation: CacheDebugCorrelation
246284
provider: string

scripts/compare-cache-debug.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,12 @@ interface Snapshot {
5454
rawBody: unknown
5555
normalized: unknown
5656
}
57+
usage?: {
58+
inputTokens: number
59+
outputTokens: number
60+
cachedInputTokens: number
61+
totalTokens: number
62+
}
5763
}
5864

5965
function findFirstDifference(
@@ -261,6 +267,13 @@ function comparePair(prev: Snapshot, curr: Snapshot, prevFile: string, currFile:
261267
if (prev.systemHash || curr.systemHash) {
262268
console.log(` Hashes: system=${prev.systemHash ?? '?'}${curr.systemHash ?? '?'} tools=${prev.toolsHash ?? '?'}${curr.toolsHash ?? '?'}`)
263269
}
270+
for (const snap of [{ label: 'A', data: prev }, { label: 'B', data: curr }]) {
271+
if (snap.data.usage) {
272+
const u = snap.data.usage
273+
const hitRate = u.inputTokens > 0 ? ((u.cachedInputTokens / u.inputTokens) * 100).toFixed(1) : '0.0'
274+
console.log(` Usage ${snap.label}: ${u.inputTokens} in, ${u.outputTokens} out, ${u.cachedInputTokens} cached (${hitRate}% cache hit)`)
275+
}
276+
}
264277
if (prev.runId !== curr.runId) {
265278
console.log(` ⚠️ Different runs: ${prev.runId ?? '?'}${curr.runId ?? '?'}`)
266279
}

sdk/src/impl/llm.ts

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -216,6 +216,30 @@ function emitCacheDebugProviderRequest(params: {
216216
})
217217
}
218218

219+
function emitCacheDebugUsage(params: {
220+
callback?: (usage: {
221+
inputTokens: number
222+
outputTokens: number
223+
cachedInputTokens: number
224+
totalTokens: number
225+
}) => void
226+
usage: {
227+
inputTokens?: number
228+
outputTokens?: number
229+
totalTokens?: number
230+
cachedInputTokens?: number
231+
}
232+
}) {
233+
if (!params.callback) return
234+
235+
params.callback({
236+
inputTokens: params.usage.inputTokens ?? 0,
237+
outputTokens: params.usage.outputTokens ?? 0,
238+
cachedInputTokens: params.usage.cachedInputTokens ?? 0,
239+
totalTokens: params.usage.totalTokens ?? 0,
240+
})
241+
}
242+
219243
export async function* promptAiSdkStream(
220244
params: ParamsOf<PromptAiSdkStreamFn> & {
221245
skipClaudeOAuth?: boolean
@@ -587,6 +611,12 @@ export async function* promptAiSdkStream(
587611
rawBody: requestMetadata.body,
588612
})
589613

614+
const usageResult = await response.usage
615+
emitCacheDebugUsage({
616+
callback: params.onCacheDebugUsageReceived,
617+
usage: usageResult,
618+
})
619+
590620
// Skip cost tracking for Claude OAuth (user is on their own subscription)
591621
if (!isClaudeOAuth) {
592622
const providerMetadataResult = await response.providerMetadata
@@ -654,6 +684,10 @@ export async function promptAiSdk(
654684
provider: getModelProvider(aiSDKModel),
655685
rawBody: response.request?.body,
656686
})
687+
emitCacheDebugUsage({
688+
callback: params.onCacheDebugUsageReceived,
689+
usage: response.usage,
690+
})
657691
const content = response.text
658692

659693
const providerMetadata = response.providerMetadata ?? {}
@@ -719,6 +753,10 @@ export async function promptAiSdkStructured<T>(
719753
provider: getModelProvider(aiSDKModel),
720754
rawBody: response.request?.body,
721755
})
756+
emitCacheDebugUsage({
757+
callback: params.onCacheDebugUsageReceived,
758+
usage: response.usage,
759+
})
722760

723761
const content = response.object
724762

0 commit comments

Comments
 (0)