Skip to content

fix: fast mode pricing for anthropic - #4267

Merged
akshaydeo merged 1 commit into
devfrom
06-10-fix_fast_mode_pricing_for_anthropic
Jun 11, 2026
Merged

fix: fast mode pricing for anthropic#4267
akshaydeo merged 1 commit into
devfrom
06-10-fix_fast_mode_pricing_for_anthropic

Conversation

@TejasGhatte

@TejasGhatte TejasGhatte commented Jun 10, 2026

Copy link
Copy Markdown
Collaborator

Summary

Adds support for Anthropic's fast mode (research preview), where usage.speed is returned as "fast" or "standard" to indicate which speed was actually served. This field drives fast-mode billing, which uses a flat per-token rate across the full context window rather than the tiered 128k/200k/272k rates.

Changes

  • Added Speed *string field to AnthropicUsage, BifrostChatResponse, BifrostResponsesResponse, and BifrostPassthroughUsage to carry the usage.speed value from Anthropic responses through the full response pipeline.
  • Propagated Speed through all relevant conversion paths: chat, responses, passthrough (including streaming).
  • Added InputCostPerTokenFast and OutputCostPerTokenFast pricing columns to TableModelPricing and the Options/Entry types, with a corresponding DB migration (add_fast_mode_pricing_columns).
  • Extended tieredInputRate and tieredOutputRate to check tier.isFast first, applying the flat fast-mode rate and bypassing all token-count tiers when fast mode was served and the columns are populated.
  • Added isFastSpeed helper and wired tier.isFast into all extractCostInput call sites (chat, responses, responses stream, passthrough).
  • Fast-mode pricing falls back gracefully to standard rates when the fast columns are not configured (e.g. non-Opus models).
  • Cache tokens on fast requests continue to bill at standard cache rates; only non-cached input and output tokens use the fast rate.
  • Added pricingSyncUpdateColumns entries so fast-mode pricing syncs correctly from the catalog.
  • Added unit tests covering fast vs. standard rate selection, flat-rate behavior above the 200k tier, fallback when unconfigured, cache token billing at standard rates, and the isFastSpeed helper.

Type of change

  • Bug fix
  • Feature
  • Refactor
  • Documentation
  • Chore/CI

Affected areas

  • Core (Go)
  • Transports (HTTP)
  • Providers/Integrations
  • Plugins
  • UI (React)
  • Docs

How to test

go test ./core/providers/anthropic/...
go test ./framework/modelcatalog/datasheet/...
go test ./...

To validate end-to-end:

  1. Configure a model with input_cost_per_token_fast and output_cost_per_token_fast (e.g. Anthropic Opus 4.x).
  2. Send a request with fast mode enabled.
  3. Confirm the response includes "speed": "fast" and that the computed cost reflects the fast-mode flat rate rather than the standard or tiered rate.
  4. Send a request without fast mode and confirm "speed": "standard" and standard rates are applied.

Breaking changes

  • Yes
  • No

Related issues

Security considerations

None. No new auth, secrets, or PII handling introduced.

Checklist

  • I read docs/contributing/README.md and followed the guidelines
  • I added/updated tests where appropriate
  • I updated documentation where needed
  • I verified builds succeed (Go and UI)
  • I verified the CI pipeline passes locally if applicable

Summary by CodeRabbit

  • New Features

    • Speed indicators now propagate across chat, responses, and passthrough usage, exposing whether replies used "fast" or "standard" mode.
    • Fast-mode headers are added whenever a speed is present for supported models.
    • Pricing override fields for fast-mode token rates added to the UI.
  • Database Schema

    • Added columns to store separate input/output fast-token pricing rates.
  • Tests

    • Added unit tests validating fast-mode pricing behavior and speed parsing.

@coderabbitai

coderabbitai Bot commented Jun 10, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

Captures Anthropic response speed, threads it through conversion and passthrough paths, persists fast-mode pricing columns, extends pricing models with fast-mode fields, and applies flat fast-token rates during cost calculation with unit tests.

Changes

Anthropic Fast-Mode Implementation

Layer / File(s) Summary
Response Schema Extensions
core/providers/anthropic/types.go, core/schemas/chatcompletions.go, core/schemas/responses.go, core/schemas/passthrough.go
AnthropicUsage, BifrostChatResponse, BifrostResponsesResponse, and BifrostPassthroughUsage now include optional Speed fields to carry served speed information.
Anthropic Provider Speed Forwarding
core/providers/anthropic/chat.go, core/providers/anthropic/responses.go, core/providers/anthropic/passthrough_usage.go, core/providers/anthropic/utils.go
ToBifrostChatResponse, ToAnthropicChatResponse, ToBifrostResponsesResponse, ToAnthropicResponsesResponse, buildAnthropicPassthroughUsage, and AnthropicPassthroughStreamUsage.ObserveEvent now propagate Speed bidirectionally; beta header injection now checks for a non-nil req.Speed.
Database Schema and Migration
framework/configstore/migrations.go, framework/configstore/tables/modelpricing.go, framework/configstore/rdb.go
Adds migrationAddFastModePricingColumns to add/remove input_cost_per_token_fast and output_cost_per_token_fast; updates TableModelPricing with fast-mode fields and extends pricingSyncUpdateColumns.
Pricing Data Model Extensions
framework/modelcatalog/datasheet/types.go, ui/app/workspace/custom-pricing/overrides/pricingOverrideSheet.tsx, ui/lib/types/governance.ts
Options gains InputCostPerTokenFast and OutputCostPerTokenFast; serviceTier gains isFast; conversions map these fields to/from TableModelPricing; UI pricing fields and governance patch types updated.
Cost Calculation with Fast-Mode Billing
framework/modelcatalog/datasheet/cost.go, framework/modelcatalog/datasheet/overrides.go, framework/modelcatalog/datasheet/cost_test.go
extractCostInput and passthroughUsageToCostInput set isFast from Speed; tieredInputRate/tieredOutputRate short-circuit to use flat fast rates when configured; patchPricing applies fast override fields; tests validate fast-mode behaviors and parsing.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • maximhq/bifrost#3941: Prior passthrough usage plumbing that propagates Speed into BifrostPassthroughUsage.

Suggested reviewers

  • danpiths
  • akshaydeo

Poem

🐰 I nibble bytes where speeds are stored,
Fast or standard, tagged and scored.
Schemas, DB, and billing sing,
Tokens priced for the speedy spring.
Hooray — the rabbit hops, all synced and floored!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 60.71% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title 'fix: fast mode pricing for anthropic' clearly and concisely describes the main feature—adding fast mode pricing support for Anthropic—which aligns with the core changes throughout the changeset.
Description check ✅ Passed The description comprehensively covers the purpose, changes, affected areas, testing approach, and includes appropriate checklist items; it follows the template structure and provides sufficient detail for understanding the feature.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch 06-10-fix_fast_mode_pricing_for_anthropic

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 golangci-lint (2.12.2)

level=error msg="[linters_context] typechecking error: pattern ./...: directory prefix . does not contain main module or its selected dependencies"


Comment @coderabbitai help to get the list of available commands and usage tips.

@CLAassistant

CLAassistant commented Jun 10, 2026

Copy link
Copy Markdown

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you sign our Contributor License Agreement before we can accept your contribution.


tejas ghatte seems not to be a GitHub user. You need a GitHub account to be able to sign the CLA. If you have already a GitHub account, please add the email address used for this commit to your account.
You have signed the CLA already but the status is still pending? Let us recheck it.

TejasGhatte commented Jun 10, 2026

Copy link
Copy Markdown
Collaborator Author

@TejasGhatte
TejasGhatte marked this pull request as ready for review June 10, 2026 19:58
@TejasGhatte
TejasGhatte force-pushed the 06-10-fix_fast_mode_pricing_for_anthropic branch from 546ce22 to a8dbd79 Compare June 10, 2026 19:59

@coderabbitai coderabbitai Bot 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.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@core/providers/anthropic/chat.go`:
- Around line 1038-1041: The Anthropic stream serializer
(ToAnthropicChatStreamResponse) never sets streamResp.Usage.Speed, so streaming
clients lose usage.speed; update ToAnthropicChatStreamResponse to copy bifrost
chunk Speed into streamResp.Usage.Speed whenever the incoming Bifrost
response/chunk has Speed (same logic used in the non-stream path that sets
anthropicResp.Usage.Speed from bifrostResp.Speed). Locate
ToAnthropicChatStreamResponse and add assignment(s) to set
streamResp.Usage.Speed = bifrostChunk.Speed (or equivalent field) on the
final/appropriate chunk(s) so the streamed Anthropic response includes
usage.speed.

In `@core/schemas/responses.go`:
- Line 132: The WithDefaults() implementation is not copying the new Speed
*string field, so any provider-populated Speed gets dropped during
normalization; update the WithDefaults() method (the function named WithDefaults
on the response struct that currently copies SearchResults, PromptCacheKey,
etc.) to also copy the Speed pointer into the returned struct (i.e., set
dst.Speed = src.Speed or otherwise preserve the pointer) so that downstream
callers like BifrostResponsesStreamResponse.WithDefaults() retain the fast-mode
billing value.

In `@framework/modelcatalog/datasheet/cost.go`:
- Around line 762-766: The bug is that fast-mode pricing (tier.isFast == true /
pricing.InputCostPerTokenFast) is being applied to cached-token fallback rates;
modify the cache-rate lookup so it never queries helpers with tier.isFast set —
i.e., when calling the cache helpers from computeTextCost (and the similar block
around the other occurrence), ensure you clear or override tier.isFast to false
before passing the tier to tieredInputRate/tieredOutputRate or any cache-rate
helper so cached tokens fall back to the normal tiered rates rather than
*_per_token_fast.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 53cf960c-4dd5-46b0-bf4e-e924eac82229

📥 Commits

Reviewing files that changed from the base of the PR and between 9908b7c and a8dbd79.

📒 Files selected for processing (14)
  • core/providers/anthropic/chat.go
  • core/providers/anthropic/passthrough_usage.go
  • core/providers/anthropic/responses.go
  • core/providers/anthropic/types.go
  • core/schemas/chatcompletions.go
  • core/schemas/passthrough.go
  • core/schemas/responses.go
  • framework/configstore/migrations.go
  • framework/configstore/rdb.go
  • framework/configstore/tables/modelpricing.go
  • framework/modelcatalog/datasheet/cost.go
  • framework/modelcatalog/datasheet/cost_test.go
  • framework/modelcatalog/datasheet/overrides.go
  • framework/modelcatalog/datasheet/types.go

Comment thread core/providers/anthropic/chat.go
Comment thread core/schemas/responses.go
Comment thread framework/modelcatalog/datasheet/cost.go
@greptile-apps

greptile-apps Bot commented Jun 10, 2026

Copy link
Copy Markdown
Contributor

Confidence Score: 3/5

Three correctness bugs remain unfixed: the beta header fires for any non-nil speed (including 'standard'), the streaming Responses path never sets Speed so fast requests bill at standard rates, and the cache-token fallback applies the fast rate when no explicit cache rate is configured.

All three bugs affect billing correctness on live request paths. The beta-header bug could cause unwanted routing for callers explicitly opting out of fast mode. The streaming Speed omission means fast-mode billing is completely bypassed on the streaming Responses API. The cache-token fallback means cache reads/writes on fast requests silently apply the fast rate instead of the standard cache rate when no explicit cache cost is set.

core/providers/anthropic/utils.go, core/providers/anthropic/responses.go, and framework/modelcatalog/datasheet/cost.go all need fixes before merge.

Important Files Changed

Filename Overview
core/providers/anthropic/utils.go Beta header guard widened to req.Speed != nil, sending fast-mode header even for explicit speed:"standard" requests — still unresolved from prior review.
core/providers/anthropic/responses.go message_delta streaming handler omits Speed from the emitted BifrostResponsesResponse, causing fast-mode billing to be silently skipped on the streaming Responses API path — still unresolved from prior review.
framework/modelcatalog/datasheet/cost.go Cache-token fallback in tieredCacheReadInputTokenRate and tieredCacheCreationInputTokenRate passes tier through to tieredInputRate, returning the fast rate when no explicit cache cost is configured — still unresolved from prior review.
core/providers/anthropic/types.go Adds Speed *string to AnthropicUsage with correct JSON tag and documentation comment.
core/providers/anthropic/chat.go Propagates Speed from Anthropic usage into BifrostChatResponse and back; implementation is correct.
core/providers/anthropic/passthrough_usage.go Propagates Speed through passthrough and streaming passthrough usage paths correctly.
framework/configstore/migrations.go Adds migrationAddFastModePricingColumns migration with add/rollback paths and column existence checks; follows existing patterns correctly.
framework/modelcatalog/datasheet/cost_test.go Good coverage for fast vs standard rate selection, flat-rate behavior, fallback, and tierFromResponse; does not cover the cache-token fallback path (no explicit cache rate configured) which is the open bug.
framework/modelcatalog/datasheet/types.go Adds isFast to serviceTier and InputCostPerTokenFast/OutputCostPerTokenFast to Options/Entry; correctly propagated through convertEntryToTablePricing and back.

Comments Outside Diff (3)

  1. core/providers/anthropic/utils.go, line 1082-1086 (link)

    P1 The guard was widened from *req.Speed == "fast" to req.Speed != nil, so the fast-mode beta header is now sent when a caller explicitly sets speed:"standard" to force non-fast behaviour on a capable model. Sending the header on a "standard" request is semantically wrong and could route the request to fast infrastructure against the caller's intent.

  2. core/providers/anthropic/responses.go, line 1406-1412 (link)

    P1 The message_delta handler builds a BifrostResponsesResponse with usage and stop-reason but never copies chunk.Usage.Speed into response.Speed. When this chunk reaches extractCostInput, isFastSpeed(result.ResponsesStreamResponse.Response.Speed) returns false and fast-mode billing is silently skipped for the entire streaming Responses API path — a fast request would be billed at standard rates.

  3. framework/modelcatalog/datasheet/cost.go, line 920-937 (link)

    P1 tieredCacheReadInputTokenRate falls back to tieredInputRate(pricing, totalTokens, tier) when no explicit cache-read cost is configured. Because the same tier with isFast=true is passed through, that fallback returns *pricing.InputCostPerTokenFast instead of the standard base rate. The same problem exists in tieredCacheCreationInputTokenRate (and tieredCacheCreationInputAbove1hrTokenRate which chains through it). The PR description explicitly states cache tokens bill at standard rates; the existing unit test only covers the path where both cache rates are explicitly configured.

Reviews (5): Last reviewed commit: "fix: fast mode pricing for anthropic" | Re-trigger Greptile

@TejasGhatte
TejasGhatte force-pushed the 06-10-fix_fast_mode_pricing_for_anthropic branch from a8dbd79 to d1887c5 Compare June 11, 2026 07:07

@coderabbitai coderabbitai Bot 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.

Actionable comments posted: 1

♻️ Duplicate comments (1)
core/schemas/responses.go (1)

132-132: ⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

Speed field is not copied in WithDefaults() — fast-mode billing will be silently dropped.

The new Speed field must be copied in the WithDefaults() method (lines 191–326) alongside other optional fields like PromptCacheKey, PromptCacheRetention, and SafetyIdentifier. Without this copy, when WithDefaults() is called during response normalization (e.g., from BifrostResponsesStreamResponse.WithDefaults() at line 2692), the Speed value populated by the provider converter will be lost, and the fast-mode billing logic in framework/modelcatalog/datasheet/cost.go:195 will never see it.

🩹 Proposed fix

Add the copy after line 223 in the WithDefaults() method:

 result.PromptCacheRetention = resp.PromptCacheRetention
 result.SafetyIdentifier = resp.SafetyIdentifier
+result.Speed = resp.Speed
 result.MaxToolCalls = resp.MaxToolCalls
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@core/schemas/responses.go` at line 132, The Speed field is not being
preserved by WithDefaults(), so add logic in the WithDefaults() method to copy
the Speed pointer into the returned struct alongside the other optional fields
(e.g., PromptCacheKey, PromptCacheRetention, SafetyIdentifier) so
provider-populated Speed isn't dropped; this ensures
BifrostResponsesStreamResponse.WithDefaults() will retain Speed for downstream
billing logic that inspects Speed.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@framework/modelcatalog/datasheet/cost_test.go`:
- Line 229: Remove or correct the misleading inline computation comment that
claims the input cost equals 0.0019; update it to either remove the leftover "(?
recompute)" comment or replace it with the correct computation/result
(300*0.00001 + 1500*0.0000005 + 200*0.00000625 = 0.005) so it no longer
contradicts the existing expected value (0.03) asserted later in the test;
locate the comment in cost_test.go near the test asserting the 0.03 expected
value and edit that comment accordingly.

---

Duplicate comments:
In `@core/schemas/responses.go`:
- Line 132: The Speed field is not being preserved by WithDefaults(), so add
logic in the WithDefaults() method to copy the Speed pointer into the returned
struct alongside the other optional fields (e.g., PromptCacheKey,
PromptCacheRetention, SafetyIdentifier) so provider-populated Speed isn't
dropped; this ensures BifrostResponsesStreamResponse.WithDefaults() will retain
Speed for downstream billing logic that inspects Speed.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: f6fb7c87-c860-4511-b855-b46a3cc9a82b

📥 Commits

Reviewing files that changed from the base of the PR and between a8dbd79 and d1887c5.

📒 Files selected for processing (15)
  • core/providers/anthropic/chat.go
  • core/providers/anthropic/passthrough_usage.go
  • core/providers/anthropic/responses.go
  • core/providers/anthropic/types.go
  • core/providers/anthropic/utils.go
  • core/schemas/chatcompletions.go
  • core/schemas/passthrough.go
  • core/schemas/responses.go
  • framework/configstore/migrations.go
  • framework/configstore/rdb.go
  • framework/configstore/tables/modelpricing.go
  • framework/modelcatalog/datasheet/cost.go
  • framework/modelcatalog/datasheet/cost_test.go
  • framework/modelcatalog/datasheet/overrides.go
  • framework/modelcatalog/datasheet/types.go

Comment thread framework/modelcatalog/datasheet/cost_test.go
@TejasGhatte
TejasGhatte changed the base branch from dev to graphite-base/4267 June 11, 2026 07:42
@TejasGhatte
TejasGhatte force-pushed the 06-10-fix_fast_mode_pricing_for_anthropic branch from d1887c5 to fc3120d Compare June 11, 2026 07:42
@TejasGhatte
TejasGhatte changed the base branch from graphite-base/4267 to 06-11-fix_vertex_batch_api_tests June 11, 2026 07:42
Comment thread framework/modelcatalog/datasheet/cost.go Outdated
Comment thread framework/modelcatalog/datasheet/types.go

@coderabbitai coderabbitai Bot 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.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
core/providers/anthropic/utils.go (1)

1082-1086: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Incorrect beta header injection for non-fast speed values.

The beta header AnthropicFastModeBetaHeader (fast-mode-2026-02-01) should only be injected when req.Speed is explicitly "fast", not for any non-nil value such as "standard". The removed check *req.Speed == "fast" was correct.

Evidence:

  • Line 1079 comment: "Check for fast mode" (not "check for speed field")
  • SupportsFastMode doc (line 796-799): specifically about speed:"fast" requests; other models reject with 400
  • stripUnsupportedAnthropicFields (line 211): gates Speed on features.FastMode, tying it to fast mode specifically
  • Header name: "FastModeBetaHeader" indicates the beta feature is fast mode, not the speed parameter in general

If req.Speed == "standard", sending the fast-mode beta header is incorrect—standard mode doesn't require the beta preview header.

🔧 Restore the value check
-	if req.Speed != nil {
+	if req.Speed != nil && *req.Speed == "fast" {
 		if (!hasProvider || features.FastMode) && SupportsFastMode(req.Model) {
 			headers = appendUniqueHeader(headers, AnthropicFastModeBetaHeader)
 		}
 	}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@core/providers/anthropic/utils.go` around lines 1082 - 1086, The header
injection currently adds AnthropicFastModeBetaHeader whenever req.Speed is
non-nil; change it to only add the header when req.Speed is explicitly "fast".
In the block that checks req.Speed (the code using SupportsFastMode,
appendUniqueHeader and AnthropicFastModeBetaHeader), restore the removed value
check so it verifies *req.Speed == "fast" (and still respects features.FastMode
and SupportsFastMode(req.Model)) before calling appendUniqueHeader; this aligns
with SupportsFastMode and stripUnsupportedAnthropicFields behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@core/providers/anthropic/utils.go`:
- Around line 1082-1086: The header injection currently adds
AnthropicFastModeBetaHeader whenever req.Speed is non-nil; change it to only add
the header when req.Speed is explicitly "fast". In the block that checks
req.Speed (the code using SupportsFastMode, appendUniqueHeader and
AnthropicFastModeBetaHeader), restore the removed value check so it verifies
*req.Speed == "fast" (and still respects features.FastMode and
SupportsFastMode(req.Model)) before calling appendUniqueHeader; this aligns with
SupportsFastMode and stripUnsupportedAnthropicFields behavior.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: e6e5f06d-f386-4cbd-9250-e208c1709286

📥 Commits

Reviewing files that changed from the base of the PR and between d1887c5 and fc3120d.

📒 Files selected for processing (15)
  • core/providers/anthropic/chat.go
  • core/providers/anthropic/passthrough_usage.go
  • core/providers/anthropic/responses.go
  • core/providers/anthropic/types.go
  • core/providers/anthropic/utils.go
  • core/schemas/chatcompletions.go
  • core/schemas/passthrough.go
  • core/schemas/responses.go
  • framework/configstore/migrations.go
  • framework/configstore/rdb.go
  • framework/configstore/tables/modelpricing.go
  • framework/modelcatalog/datasheet/cost.go
  • framework/modelcatalog/datasheet/cost_test.go
  • framework/modelcatalog/datasheet/overrides.go
  • framework/modelcatalog/datasheet/types.go

@Pratham-Mishra04
Pratham-Mishra04 changed the base branch from 06-11-fix_vertex_batch_api_tests to graphite-base/4267 June 11, 2026 07:51
@Pratham-Mishra04
Pratham-Mishra04 force-pushed the 06-10-fix_fast_mode_pricing_for_anthropic branch from fc3120d to c22e11f Compare June 11, 2026 07:53
@graphite-app
graphite-app Bot changed the base branch from graphite-base/4267 to dev June 11, 2026 07:53
@Pratham-Mishra04
Pratham-Mishra04 force-pushed the 06-10-fix_fast_mode_pricing_for_anthropic branch from c22e11f to 45ac0fb Compare June 11, 2026 07:53
@TejasGhatte
TejasGhatte force-pushed the 06-10-fix_fast_mode_pricing_for_anthropic branch from 45ac0fb to 2f34a9f Compare June 11, 2026 08:05

@coderabbitai coderabbitai Bot 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.

♻️ Duplicate comments (2)
core/providers/anthropic/chat.go (1)

1450-1459: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Streaming path still missing speed forwarding (per prior review).

ToAnthropicChatStreamResponse() builds streamResp.Usage but never copies bifrostResp.Speed into it, so Anthropic-compatible streaming clients lose usage.speed even when the final Bifrost chunk carries it. This breaks parity with the non-streaming path fixed in this PR.

Suggested fix
 	if bifrostResp.Usage != nil {
 		if streamResp.Type == "" {
 			streamResp.Type = "message_delta"
 		}
 		streamResp.Usage = &AnthropicUsage{
 			InputTokens:  bifrostResp.Usage.PromptTokens,
 			OutputTokens: bifrostResp.Usage.CompletionTokens,
 		}
+		if bifrostResp.Speed != nil {
+			streamResp.Usage.Speed = bifrostResp.Speed
+		}
 	}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@core/providers/anthropic/chat.go` around lines 1450 - 1459,
ToAnthropicChatStreamResponse currently sets streamResp.Usage from
bifrostResp.Usage but never forwards bifrostResp.Speed, so update the function
(ToAnthropicChatStreamResponse) to copy bifrostResp.Speed into the Anthropic
usage struct: after constructing streamResp.Usage (the AnthropicUsage instance),
set its Speed field from bifrostResp.Speed (or set streamResp.Usage.Speed =
bifrostResp.Speed) so streaming responses include usage.speed just like the
non-streaming path.
framework/modelcatalog/datasheet/cost.go (1)

755-759: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Prevent fast-mode leakage into cached-token fallback rates.

Fast-mode precedence here makes cached-token fallback incorrect: when cache-specific rates are unset, Line 923 and Line 936 fall back via tieredInputRate(..., tier), so tier.isFast=true causes cached tokens to be billed at fast non-cached rates.

💡 Suggested fix
func tieredCacheReadInputTokenRate(pricing *configstoreTables.TableModelPricing, totalTokens int, tier serviceTier) float64 {
+	standardTier := tier
+	standardTier.isFast = false
 	if tier.isFlex && pricing.CacheReadInputTokenCostFlex != nil {
 		return *pricing.CacheReadInputTokenCostFlex
 	}
@@
 	if pricing.CacheReadInputTokenCost != nil {
 		return *pricing.CacheReadInputTokenCost
 	}
-	return tieredInputRate(pricing, totalTokens, tier)
+	return tieredInputRate(pricing, totalTokens, standardTier)
}
@@
func tieredCacheCreationInputTokenRate(pricing *configstoreTables.TableModelPricing, totalTokens int, tier serviceTier) float64 {
+	standardTier := tier
+	standardTier.isFast = false
 	if totalTokens > TokenTierAbove200K && pricing.CacheCreationInputTokenCostAbove200kTokens != nil {
 		return *pricing.CacheCreationInputTokenCostAbove200kTokens
 	}
 	if pricing.CacheCreationInputTokenCost != nil {
 		return *pricing.CacheCreationInputTokenCost
 	}
-	return tieredInputRate(pricing, totalTokens, tier)
+	return tieredInputRate(pricing, totalTokens, standardTier)
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@framework/modelcatalog/datasheet/cost.go` around lines 755 - 759, The
fast-mode check (tier.isFast && pricing.InputCostPerTokenFast != nil) is leaking
into cached-token fallbacks; adjust the fast-rate precedence so it only applies
for normal (non-cached) rate calculation. Add a boolean flag to tieredInputRate
(or an equivalent parameter) to indicate a cached-token fallback and change the
fast-mode branch to: if tier.isFast && pricing.InputCostPerTokenFast != nil &&
!isCachedFallback { return *pricing.InputCostPerTokenFast }, then update callers
that compute cached rates (the sites that invoke tieredInputRate for
cached-token fallbacks) to pass isCachedFallback=true so cached-token logic
never returns the fast non-cached rate.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Duplicate comments:
In `@core/providers/anthropic/chat.go`:
- Around line 1450-1459: ToAnthropicChatStreamResponse currently sets
streamResp.Usage from bifrostResp.Usage but never forwards bifrostResp.Speed, so
update the function (ToAnthropicChatStreamResponse) to copy bifrostResp.Speed
into the Anthropic usage struct: after constructing streamResp.Usage (the
AnthropicUsage instance), set its Speed field from bifrostResp.Speed (or set
streamResp.Usage.Speed = bifrostResp.Speed) so streaming responses include
usage.speed just like the non-streaming path.

In `@framework/modelcatalog/datasheet/cost.go`:
- Around line 755-759: The fast-mode check (tier.isFast &&
pricing.InputCostPerTokenFast != nil) is leaking into cached-token fallbacks;
adjust the fast-rate precedence so it only applies for normal (non-cached) rate
calculation. Add a boolean flag to tieredInputRate (or an equivalent parameter)
to indicate a cached-token fallback and change the fast-mode branch to: if
tier.isFast && pricing.InputCostPerTokenFast != nil && !isCachedFallback {
return *pricing.InputCostPerTokenFast }, then update callers that compute cached
rates (the sites that invoke tieredInputRate for cached-token fallbacks) to pass
isCachedFallback=true so cached-token logic never returns the fast non-cached
rate.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 9dcd7855-bdb2-4ef1-b477-0790abe13111

📥 Commits

Reviewing files that changed from the base of the PR and between fc3120d and 2f34a9f.

📒 Files selected for processing (17)
  • core/providers/anthropic/chat.go
  • core/providers/anthropic/passthrough_usage.go
  • core/providers/anthropic/responses.go
  • core/providers/anthropic/types.go
  • core/providers/anthropic/utils.go
  • core/schemas/chatcompletions.go
  • core/schemas/passthrough.go
  • core/schemas/responses.go
  • framework/configstore/migrations.go
  • framework/configstore/rdb.go
  • framework/configstore/tables/modelpricing.go
  • framework/modelcatalog/datasheet/cost.go
  • framework/modelcatalog/datasheet/cost_test.go
  • framework/modelcatalog/datasheet/overrides.go
  • framework/modelcatalog/datasheet/types.go
  • ui/app/workspace/custom-pricing/overrides/pricingOverrideSheet.tsx
  • ui/lib/types/governance.ts

akshaydeo commented Jun 11, 2026

Copy link
Copy Markdown
Contributor

Merge activity

  • Jun 11, 1:02 PM UTC: A user started a stack merge that includes this pull request via Graphite.
  • Jun 11, 1:03 PM UTC: @akshaydeo merged this pull request with Graphite.

@akshaydeo
akshaydeo merged commit 575ff8d into dev Jun 11, 2026
15 of 16 checks passed
@akshaydeo
akshaydeo deleted the 06-10-fix_fast_mode_pricing_for_anthropic branch June 11, 2026 13:03
akshaydeo pushed a commit that referenced this pull request Jun 12, 2026
## Summary

Adds support for Anthropic's fast mode (research preview), where `usage.speed` is returned as `"fast"` or `"standard"` to indicate which speed was actually served. This field drives fast-mode billing, which uses a flat per-token rate across the full context window rather than the tiered 128k/200k/272k rates.

## Changes

- Added `Speed *string` field to `AnthropicUsage`, `BifrostChatResponse`, `BifrostResponsesResponse`, and `BifrostPassthroughUsage` to carry the `usage.speed` value from Anthropic responses through the full response pipeline.
- Propagated `Speed` through all relevant conversion paths: chat, responses, passthrough (including streaming).
- Added `InputCostPerTokenFast` and `OutputCostPerTokenFast` pricing columns to `TableModelPricing` and the `Options`/`Entry` types, with a corresponding DB migration (`add_fast_mode_pricing_columns`).
- Extended `tieredInputRate` and `tieredOutputRate` to check `tier.isFast` first, applying the flat fast-mode rate and bypassing all token-count tiers when fast mode was served and the columns are populated.
- Added `isFastSpeed` helper and wired `tier.isFast` into all `extractCostInput` call sites (chat, responses, responses stream, passthrough).
- Fast-mode pricing falls back gracefully to standard rates when the fast columns are not configured (e.g. non-Opus models).
- Cache tokens on fast requests continue to bill at standard cache rates; only non-cached input and output tokens use the fast rate.
- Added `pricingSyncUpdateColumns` entries so fast-mode pricing syncs correctly from the catalog.
- Added unit tests covering fast vs. standard rate selection, flat-rate behavior above the 200k tier, fallback when unconfigured, cache token billing at standard rates, and the `isFastSpeed` helper.

## Type of change

- [ ] Bug fix
- [x] Feature
- [ ] Refactor
- [ ] Documentation
- [ ] Chore/CI

## Affected areas

- [x] Core (Go)
- [ ] Transports (HTTP)
- [x] Providers/Integrations
- [ ] Plugins
- [ ] UI (React)
- [ ] Docs

## How to test

```sh
go test ./core/providers/anthropic/...
go test ./framework/modelcatalog/datasheet/...
go test ./...
```

To validate end-to-end:
1. Configure a model with `input_cost_per_token_fast` and `output_cost_per_token_fast` (e.g. Anthropic Opus 4.x).
2. Send a request with fast mode enabled.
3. Confirm the response includes `"speed": "fast"` and that the computed cost reflects the fast-mode flat rate rather than the standard or tiered rate.
4. Send a request without fast mode and confirm `"speed": "standard"` and standard rates are applied.

## Breaking changes

- [ ] Yes
- [x] No

## Related issues

## Security considerations

None. No new auth, secrets, or PII handling introduced.

## Checklist

- [ ] I read `docs/contributing/README.md` and followed the guidelines
- [x] I added/updated tests where appropriate
- [ ] I updated documentation where needed
- [x] I verified builds succeed (Go and UI)
- [ ] I verified the CI pipeline passes locally if applicable

<!-- This is an auto-generated comment: release notes by coderabbit.ai -->
## Summary by CodeRabbit

* **New Features**
  * Speed indicators now propagate across chat, responses, and passthrough usage, exposing whether replies used "fast" or "standard" mode.
  * Fast-mode headers are added whenever a speed is present for supported models.
  * Pricing override fields for fast-mode token rates added to the UI.

* **Database Schema**
  * Added columns to store separate input/output fast-token pricing rates.

* **Tests**
  * Added unit tests validating fast-mode pricing behavior and speed parsing.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants