You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Parse and surface Anthropic's extended-cache TTL breakdown — the 5-minute vs 1-hour ephemeral split — on both the llm_call audit event and the OTel LLM span, extending the flat cache-token capture from #431/#432 (audit) and #441 (span).
Background
Forge currently captures only the flat prompt-cache counts (forge-core/llm/providers/anthropic.go:348-349):
Anthropic also returns a nested breakdown of the creation tokens by cache TTL (the extended ttl: "1h" cache-control option splits writes into 5-minute and 1-hour tiers, billed differently):
cache_creation.* is not parsed at all today, so the 5m/1h split is invisible in both audit and traces. cache_creation_input_tokens (the flat total) equals ephemeral_5m_input_tokens + ephemeral_1h_input_tokens, but the tiers bill at different rates, so the split matters for cost attribution.
Scope
Mirror the flat-cache plumbing established by #431/#432/#441, one layer at a time:
Parse (forge-core/llm/providers/anthropic.go): add a nested struct to anthropicResponse.Usage:
Accumulator (forge-core/runtime/usage_accumulator.go): if it's useful to aggregate per-run, sum the tiers; otherwise leave the run aggregate to the flat total. Decide during implementation.
Consistency invariant
cache_creation_5m_input_tokens + cache_creation_1h_input_tokens == cache_creation_input_tokens. Keep the flat field as the source of truth for totals; the tiers are additive detail. Providers other than Anthropic (and Anthropic without the ttl:"1h" option) leave all cache_creation tiers zero → omitempty keeps the pre-existing JSON/span shape.
Acceptance
A response with cache_creation.ephemeral_5m_input_tokens / ephemeral_1h_input_tokens populates the new UsageInfo fields.
Goal
Parse and surface Anthropic's extended-cache TTL breakdown — the 5-minute vs 1-hour ephemeral split — on both the
llm_callaudit event and the OTel LLM span, extending the flat cache-token capture from #431/#432 (audit) and #441 (span).Background
Forge currently captures only the flat prompt-cache counts (
forge-core/llm/providers/anthropic.go:348-349):Anthropic also returns a nested breakdown of the creation tokens by cache TTL (the extended
ttl: "1h"cache-control option splits writes into 5-minute and 1-hour tiers, billed differently):cache_creation.*is not parsed at all today, so the 5m/1h split is invisible in both audit and traces.cache_creation_input_tokens(the flat total) equalsephemeral_5m_input_tokens + ephemeral_1h_input_tokens, but the tiers bill at different rates, so the split matters for cost attribution.Scope
Mirror the flat-cache plumbing established by #431/#432/#441, one layer at a time:
forge-core/llm/providers/anthropic.go): add a nested struct toanthropicResponse.Usage:forge-core/llm/types.goUsageInfo,forge-core/runtime/audit.goLLMUsage): addCacheCreation5mInputTokens/CacheCreation1hInputTokensfields (omitempty), threaded like the existing flat cache fields.forge-core/runtime/audit.goEmitLLMCall+AuditEvent): emitcache_creation_5m_input_tokens/cache_creation_1h_input_tokens(omitempty when zero), alongside the existingcache_creation_input_tokens.forge-core/runtime/loop.go+forge-core/observability/attrs.go): addgen_ai.usage.cache_creation_5m_input_tokens/gen_ai.usage.cache_creation_1h_input_tokensattribute constants and stamp them when non-zero (following the LLM span drops prompt-cache token attributes (cache_read/creation not on gen_ai span; diverges from llm_call audit) #441 pattern).forge-core/runtime/usage_accumulator.go): if it's useful to aggregate per-run, sum the tiers; otherwise leave the run aggregate to the flat total. Decide during implementation.Consistency invariant
cache_creation_5m_input_tokens + cache_creation_1h_input_tokens == cache_creation_input_tokens. Keep the flat field as the source of truth for totals; the tiers are additive detail. Providers other than Anthropic (and Anthropic without thettl:"1h"option) leave all cache_creation tiers zero → omitempty keeps the pre-existing JSON/span shape.Acceptance
cache_creation.ephemeral_5m_input_tokens/ephemeral_1h_input_tokenspopulates the newUsageInfofields.llm_callaudit event carriescache_creation_5m_input_tokens/cache_creation_1h_input_tokens(omitempty).gen_ai.usage.cache_creation_5m_input_tokens/..._1h_input_tokens(non-zero only).docs/security/audit-logging.mdtoken table +docs/core-concepts/observability-tracing.mdspan table.References
UsageInfo+llm_callaudit +total_input_tokens.