Skip to content

[OPIK-7392] [BE] fix: split cipx cache-creation tokens by TTL (5m vs 1h) - #7565

Merged
BorisTkachenko merged 3 commits into
mainfrom
boryst/OPIK-7392-persist-cache-creation-split
Jul 22, 2026
Merged

[OPIK-7392] [BE] fix: split cipx cache-creation tokens by TTL (5m vs 1h)#7565
BorisTkachenko merged 3 commits into
mainfrom
boryst/OPIK-7392-persist-cache-creation-split

Conversation

@BorisTkachenko

Copy link
Copy Markdown
Contributor

Details

Persists the 5-minute vs 1-hour cache-creation split so AI-spend can price 1h cache writes at 2× input. Previously cipx_spends collapsed the split into a single u_cache_creation lump, so cost was computed at the 5m rate (1.25× input) for all cache-creation tokens — a ~18% understatement, since Claude Code writes its cache with a 1-hour TTL (2× input).

  • migration 000109: add u_cache_creation_5m / u_cache_creation_1h to cipx_spends
  • CipxSpendDAO: parse and persist the split from usage.cache_creation
  • CipxSpendBlockDAO: a cipx span is a single LLM call with one cache TTL, so each write block inherits the span's TTL into its tier (cache_creation_5m / cache_creation_1h); legacy rows keep cache_creation and are read as 1h downstream

Change checklist

  • User facing
  • Documentation update

Issues

  • Resolves #
  • OPIK-7392

AI-WATERMARK

AI-WATERMARK: yes

  • Tools: Claude Code
  • Model(s): Claude Opus 4.8
  • Scope: implementation, tests, and analysis
  • Human verification: author reviewed the diff, compiles/tests the Java backend, and validated the fix end-to-end against Claude Code /usage

Testing

  • Java backend compiled/tested by the author. CostIntelligenceIngestionTest extended with 5m/1h split assertions and a parameterized write-block TTL-inheritance test.
  • E2E: fresh Claude Code session through the local cipx proxy → local Opik; cipx_spends.u_cache_creation_1h and cipx_spend_blocks.tier='cache_creation_1h' populate correctly, and the AI-spend UI total matches /usage to the cent.

Paired cost-api change that applies the pricing: comet-ml/ai-cost-backend#36

Important

Deploy order: this migration (000109) must be deployed before the cost-api change, which reads the new columns.

Documentation

N/A

Cache writes are billed by TTL (5-minute = 1.25x input, 1-hour = 2x input) and
Claude Code writes its cache with a 1-hour TTL, but cipx_spends collapsed the
split into a single u_cache_creation lump, so AI-spend priced everything at the
5m rate (~18% cost understatement).

- migration 000109: add u_cache_creation_5m / u_cache_creation_1h to cipx_spends
- CipxSpendDAO: parse and persist the split from usage.cache_creation
- CipxSpendBlockDAO: a span is a single LLM call with one TTL, so each write
  block inherits the span's TTL into its tier (cache_creation_5m / _1h)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@github-actions github-actions Bot added java Pull requests that update Java code Backend tests Including test files, or tests related like configuration. labels Jul 22, 2026
@github-actions

github-actions Bot commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

⏱️ pre-commit per-hook timing

Hook Description Result Duration
☕ spotless — java backend Format Java code 9.28s
Total (1 ran) 9.28s
⏭️ 40 skipped (no matching files changed)
Hook Description Result
🐍 trim trailing whitespace — python sdk Strip trailing whitespace ⏭️
🐍 fix end of files — python sdk Ensure files end in a newline ⏭️
🐍 ruff — python sdk Lint + autofix Python (ruff) ⏭️
🐍 ruff-format — python sdk Format Python code (ruff) ⏭️
🐍 mypy — python sdk Static type check ⏭️
🤖 trim trailing whitespace — optimizer Strip trailing whitespace ⏭️
🤖 fix end of files — optimizer Ensure files end in a newline ⏭️
🤖 check yaml — optimizer Validate YAML syntax ⏭️
🤖 check json — optimizer Validate JSON syntax ⏭️
🤖 check toml — optimizer Validate TOML syntax ⏭️
🤖 check for added large files — optimizer Block large files (>1MB) ⏭️
🔐 detect private key — optimizer Block committed private keys ⏭️
🤖 check for merge conflicts — optimizer Block merge-conflict markers ⏭️
🤖 check for case conflicts — optimizer Block case-only name clashes ⏭️
🤖 pyupgrade — optimizer Modernize Python syntax ⏭️
🤖 ruff — optimizer Lint + autofix Python (ruff) ⏭️
🤖 ruff-format — optimizer Format Python code (ruff) ⏭️
🤖 mypy — optimizer Static type check ⏭️
📓 nbstripout — optimizer notebooks Strip notebook output ⏭️
📝 markdownlint — optimizer Lint Markdown ⏭️
🔤 codespell — optimizer Fix common misspellings ⏭️
📊 radon cc — optimizer Cyclomatic-complexity gate ⏭️
📊 radon raw — optimizer Raw size metrics gate ⏭️
📊 xenon — optimizer Fail on complexity thresholds ⏭️
📊 lizard — optimizer Cyclomatic-complexity gate ⏭️
🧹 vulture — optimizer Find dead code ⏭️
🛡️ trim trailing whitespace — guardrails Strip trailing whitespace ⏭️
🛡️ fix end of files — guardrails Ensure files end in a newline ⏭️
🛡️ ruff — guardrails Lint + autofix Python (ruff) ⏭️
🛡️ ruff-format — guardrails Format Python code (ruff) ⏭️
🛡️ mypy — guardrails Static type check ⏭️
⚓ helm-docs Regenerate Helm chart README ⏭️
block non-public FE plugins Block non-public FE plugins ⏭️
🧪 pre-commit wrapper smoke tests Self-test the wrapper scripts ⏭️
🌐 eslint — frontend Lint + autofix JS/TS ⏭️
🌐 typecheck — frontend Whole-project tsc type check ⏭️
📘 eslint — typescript sdk Lint + autofix JS/TS ⏭️
📘 typecheck — typescript sdk Whole-project tsc type check ⏭️
⚙️ actionlint — github workflows Lint GitHub Actions workflows ⏭️
🐳 hadolint — dockerfiles Lint Dockerfiles ⏭️

@BorisTkachenko
BorisTkachenko marked this pull request as ready for review July 22, 2026 11:16
@BorisTkachenko
BorisTkachenko requested a review from a team as a code owner July 22, 2026 11:16
Comment thread apps/opik-backend/src/main/java/com/comet/opik/domain/CipxSpendBlockDAO.java Outdated
Address PR #7565 review: correct the @link in CipxSpendBlockDAO to the
nested BlockRow#tierName(int, String) signature, and end migration 000109
with a trailing blank line per migrations.md.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

-- Anthropic bills cache writes by TTL: 5-minute at 1.25x input, 1-hour at 2x input. Claude Code
-- writes its stable prefix with a 1-hour TTL, so pricing every cache-creation token at the 5m rate
-- understates cost (~18% in practice). The proxy already emits the split on

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doubt that these kind of comments are valid for a DAO object in Opik repo :)
"understates cost (~18% in practice)"
Moreover IMO in OSS repo we should try to be as mean with cost explaining comments as possible

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in a44dc78 — trimmed the migration comment to the technical schema change (what the split columns are and that the proxy emits them), dropping the pricing/cost rationale. That business context lives in the cost-api repo where the pricing actually happens. Good call keeping the OSS side lean.

🤖 Reply posted via /address-github-pr-comments

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Commit a44dc78 addressed this comment by removing the detailed cost/pricing explanation from the DAO migration comment while retaining only the relevant technical context.

Address PR #7565 review: keep the OSS migration comment to the technical
schema change (what the columns are and their source), dropping the
pricing/cost explanation, which lives in the cost-api repo.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

@LifeXplorer LifeXplorer left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good!

@BorisTkachenko
BorisTkachenko merged commit 2735c6d into main Jul 22, 2026
63 checks passed
@BorisTkachenko
BorisTkachenko deleted the boryst/OPIK-7392-persist-cache-creation-split branch July 22, 2026 13:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Backend java Pull requests that update Java code 🟡 size/M tests Including test files, or tests related like configuration.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants