Skip to content

Commit 5965cdf

Browse files
committed
Prompt cache debugging
1 parent 9952953 commit 5965cdf

File tree

4 files changed

+403
-1
lines changed

4 files changed

+403
-1
lines changed
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,11 @@
11
import { endsAgentStepParam } from '@codebuff/common/tools/constants'
22

33
export const globalStopSequence = `${JSON.stringify(endsAgentStepParam)}`
4+
5+
/**
6+
* Set to `true` to log the full LLM request (system prompt, tools, messages)
7+
* to `debug/cache-debug/` on each user prompt. Use with:
8+
* bun scripts/compare-cache-debug.ts
9+
* to diff sequential requests and find what's breaking prompt caching.
10+
*/
11+
export const CACHE_DEBUG_FULL_LOGGING = false

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

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import { createHash } from 'crypto'
2+
13
import { AnalyticsEvent } from '@codebuff/common/constants/analytics-events'
24
import { supportsCacheControl } from '@codebuff/common/old-constants'
35
import { TOOLS_WHICH_WONT_FORCE_NEXT_STEP } from '@codebuff/common/tools/constants'
@@ -7,6 +9,7 @@ import { systemMessage, userMessage } from '@codebuff/common/util/messages'
79
import { APICallError, type ToolSet } from 'ai'
810
import { cloneDeep, mapValues } from 'lodash'
911

12+
import { CACHE_DEBUG_FULL_LOGGING } from './constants'
1013
import { callTokenCountAPI } from './llm-api/codebuff-web-api'
1114
import { getMCPToolData } from './mcp'
1215
import { getAgentStreamFromTemplate } from './prompt-agent-stream'
@@ -18,6 +21,7 @@ import { getAgentPrompt } from './templates/strings'
1821
import { getToolSet } from './tools/prompts'
1922
import { processStream } from './tools/stream-parser'
2023
import { getAgentOutput } from './util/agent-output'
24+
import { writeCacheDebugSnapshot } from './util/cache-debug'
2125
import {
2226
withSystemInstructionTags,
2327
withSystemTags as withSystemTags,
@@ -461,7 +465,7 @@ export async function loopAgentSteps(
461465
params: {
462466
addAgentStep: AddAgentStepFn
463467
agentState: AgentState
464-
agentType: AgentTemplateType
468+
agentType: string
465469
clearUserPromptMessagesAfterResponse?: boolean
466470
clientSessionId: string
467471
content?: Array<TextPart | ImagePart>
@@ -711,6 +715,36 @@ export async function loopAgentSteps(
711715
inputSchema: tool.inputSchema as {},
712716
}))
713717

718+
if (CACHE_DEBUG_FULL_LOGGING) {
719+
// Debug: hash the system prompt and tool definitions to detect prompt cache invalidation
720+
const systemHash = createHash('sha256').update(system).digest('hex').slice(0, 8)
721+
const sortedToolDefs = Object.keys(toolDefinitions).sort().reduce((acc, key) => {
722+
acc[key] = toolDefinitions[key]
723+
return acc
724+
}, {} as Record<string, unknown>)
725+
const toolsHash = createHash('sha256').update(JSON.stringify(sortedToolDefs)).digest('hex').slice(0, 8)
726+
logger.debug(
727+
{
728+
systemHash,
729+
toolsHash,
730+
systemLength: system.length,
731+
toolCount: Object.keys(toolDefinitions).length,
732+
toolNames: Object.keys(toolDefinitions).sort(),
733+
agentType,
734+
},
735+
`[Cache Debug] System prompt hash: ${systemHash}, Tools hash: ${toolsHash}`,
736+
)
737+
738+
writeCacheDebugSnapshot({
739+
agentType: String(agentType),
740+
system,
741+
toolDefinitions: sortedToolDefs,
742+
messages: initialMessages,
743+
logger,
744+
projectRoot: fileContext.projectRoot,
745+
})
746+
}
747+
714748
const additionalToolDefinitionsWithCache = async () => {
715749
if (!cachedAdditionalToolDefinitions) {
716750
cachedAdditionalToolDefinitions = await additionalToolDefinitions({
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import { mkdirSync, writeFileSync } from 'fs'
2+
import { join } from 'path'
3+
4+
import type { Logger } from '@codebuff/common/types/contracts/logger'
5+
import type { Message } from '@codebuff/common/types/messages/codebuff-message'
6+
7+
function getCacheDebugDir(projectRoot: string) {
8+
return join(projectRoot, 'debug', 'cache-debug')
9+
}
10+
let cacheDebugCounter = 0
11+
12+
export function writeCacheDebugSnapshot(params: {
13+
agentType: string
14+
system: string
15+
toolDefinitions: Record<string, unknown>
16+
messages: Message[]
17+
logger: Logger
18+
projectRoot: string
19+
}) {
20+
const { agentType, system, toolDefinitions, messages, logger, projectRoot } = params
21+
const cacheDebugDir = getCacheDebugDir(projectRoot)
22+
try {
23+
mkdirSync(cacheDebugDir, { recursive: true })
24+
const index = String(cacheDebugCounter++).padStart(3, '0')
25+
const filename = `${index}-${agentType}-${Date.now()}.json`
26+
const snapshot = {
27+
index: cacheDebugCounter - 1,
28+
timestamp: new Date().toISOString(),
29+
agentType,
30+
systemPrompt: system,
31+
toolDefinitions,
32+
messages: messages.map((m) => ({
33+
role: m.role,
34+
content: m.content,
35+
tags: 'tags' in m ? m.tags : undefined,
36+
timeToLive: 'timeToLive' in m ? m.timeToLive : undefined,
37+
sentAt: 'sentAt' in m ? m.sentAt : undefined,
38+
})),
39+
}
40+
writeFileSync(
41+
join(cacheDebugDir, filename),
42+
JSON.stringify(snapshot, null, 2),
43+
)
44+
logger.debug(
45+
`[Cache Debug] Wrote full snapshot to ${cacheDebugDir}/${filename}`,
46+
)
47+
} catch (err) {
48+
logger.warn({ error: err }, '[Cache Debug] Failed to write snapshot')
49+
}
50+
}

0 commit comments

Comments
 (0)