feat(llm): enable Anthropic prompt caching and fix token accounting

Add cache_control ephemeral breakpoints on system prompt and tool
definitions to activate Anthropic prompt caching for multi-turn
agent loops. Fix token double-counting by removing the redundant
totalTokensUsed accumulator and computing it from sub-items. Surface
cache read/write statistics in both text and JSON summary output.
This commit is contained in:
kite 2026-06-06 19:25:08 +08:00
parent 828fc6cae7
commit 08bb28ced5
6 changed files with 190 additions and 46 deletions

View file

@ -163,12 +163,14 @@ func buildDiffLines(comment model.LlmComment) []suggestdiff.DiffLine {
}
type jsonSummary struct {
FilesReviewed int64 `json:"files_reviewed"`
Comments int64 `json:"comments"`
TotalTokens int64 `json:"total_tokens"`
InputTokens int64 `json:"input_tokens"`
OutputTokens int64 `json:"output_tokens"`
Elapsed string `json:"elapsed"`
FilesReviewed int64 `json:"files_reviewed"`
Comments int64 `json:"comments"`
TotalTokens int64 `json:"total_tokens"`
InputTokens int64 `json:"input_tokens"`
OutputTokens int64 `json:"output_tokens"`
CacheReadTokens int64 `json:"cache_read_tokens,omitempty"`
CacheWriteTokens int64 `json:"cache_write_tokens,omitempty"`
Elapsed string `json:"elapsed"`
}
type jsonOutput struct {
@ -193,17 +195,19 @@ func outputJSON(comments []model.LlmComment) error {
}
func outputJSONWithWarnings(comments []model.LlmComment, warnings []agent.AgentWarning,
filesReviewed, inputTokens, outputTokens, totalTokens int64, duration time.Duration) error {
filesReviewed, inputTokens, outputTokens, totalTokens, cacheReadTokens, cacheWriteTokens int64, duration time.Duration) error {
out := jsonOutput{
Status: "success",
Comments: comments,
Summary: &jsonSummary{
FilesReviewed: filesReviewed,
Comments: int64(len(comments)),
TotalTokens: totalTokens,
InputTokens: inputTokens,
OutputTokens: outputTokens,
Elapsed: duration.Round(time.Second).String(),
FilesReviewed: filesReviewed,
Comments: int64(len(comments)),
TotalTokens: totalTokens,
InputTokens: inputTokens,
OutputTokens: outputTokens,
CacheReadTokens: cacheReadTokens,
CacheWriteTokens: cacheWriteTokens,
Elapsed: duration.Round(time.Second).String(),
},
}
if len(comments) == 0 {

View file

@ -166,11 +166,11 @@ func runReview(args []string) error {
}
if opts.outputFormat != "json" {
telemetry.PrintTraceSummary(ag.FilesReviewed(), int64(len(comments)), ag.TotalInputTokens(), ag.TotalOutputTokens(), ag.TotalTokensUsed(), duration)
telemetry.PrintTraceSummary(ag.FilesReviewed(), int64(len(comments)), ag.TotalInputTokens(), ag.TotalOutputTokens(), ag.TotalTokensUsed(), ag.TotalCacheReadTokens(), ag.TotalCacheWriteTokens(), duration)
}
if opts.outputFormat == "json" {
return outputJSONWithWarnings(comments, ag.Warnings(), ag.FilesReviewed(), ag.TotalInputTokens(), ag.TotalOutputTokens(), ag.TotalTokensUsed(), duration)
return outputJSONWithWarnings(comments, ag.Warnings(), ag.FilesReviewed(), ag.TotalInputTokens(), ag.TotalOutputTokens(), ag.TotalTokensUsed(), ag.TotalCacheReadTokens(), ag.TotalCacheWriteTokens(), duration)
}
if opts.audience == "agent" {
outputTextWithWarnings(comments, ag.Warnings())