Skip to content

feat(billing): map gpt-image-2 quality (low/medium/high) to 1K/2K/4K tier#2269

Open
yingcheng1026 wants to merge 1 commit intoWei-Shaw:mainfrom
yingcheng1026:feat/image-billing-quality-tier-upstream
Open

feat(billing): map gpt-image-2 quality (low/medium/high) to 1K/2K/4K tier#2269
yingcheng1026 wants to merge 1 commit intoWei-Shaw:mainfrom
yingcheng1026:feat/image-billing-quality-tier-upstream

Conversation

@yingcheng1026
Copy link
Copy Markdown

Summary

The billing layer already reads Group.ImagePrice1K/2K/4K via BillingService.CalculateImageCost, but normalizeOpenAIImageSizeTier classified strictly by pixel size, so the 4K price slot was unreachable for typical 1024x1024 requests even when callers asked for quality=high.

For gpt-image-2, image_tokens scale primarily with quality, not pixel dimensions:

$ curl … -d '{"size":"1024x1024","quality":"low"}'
  → output_tokens ≈ 196   (≈ 1K tier)

$ curl … -d '{"size":"1024x1024","quality":"high"}'
  → output_tokens ≈ 1756  (≈ 4K tier)

This PR makes normalizeOpenAIImageSizeTier accept (size, quality) and prefer quality-based classification. When quality is empty, "auto", or unrecognized it falls back to the existing size-dimension classification, preserving behaviour for older gpt-image-1 / dall-e flows and the OpenAI Responses API image_generation tool (image_generation_intent.go now also threads tool[\"quality\"] through).

  • quality=low1K
  • quality=medium / auto / empty → 2K (or size-based if size matches)
  • quality=high4K

Why now

A user reported that requesting quality=high on gpt-image-2 (which costs 4× the tokens upstream) was billed at 1K tier in their group's ImagePrice1K, undercharging by 3×. Inspecting usage_logs confirmed image_size = "1K" for those rows, so the 4K column on groups was effectively dead code.

Test plan

  • go build ./...
  • go test -tags unit ./internal/service/... — all green
  • Existing JSON parse test (size=1024x1024 + quality=high) updated to expect "4K" (the previous "1K" was the bug)
  • Live deployment smoke test:
    • quality=lowusage_logs.image_size = "1K", total_cost = 0.20 (group ImagePrice1K = 0.2)
    • quality=highusage_logs.image_size = "4K", total_cost = 0.60 (group ImagePrice4K = 0.6)

🤖 Generated with Claude Code

…tier

The billing layer already reads Group.ImagePrice1K/2K/4K via
BillingService.CalculateImageCost, but normalizeOpenAIImageSizeTier
classified strictly by pixel size, so the 4K price slot was unreachable
for typical 1024x1024 requests even when callers asked for high quality.

For gpt-image-2, image_tokens scale primarily with `quality`
(low ≈ 1k tokens, medium ≈ 2k, high ≈ 4k), not pixel dimensions:

  $ curl … -d '{"size":"1024x1024","quality":"low"}'
    → output_tokens=196   (≈ 1K tier)
  $ curl … -d '{"size":"1024x1024","quality":"high"}'
    → output_tokens=1756  (≈ 4K tier)

This change makes normalizeOpenAIImageSizeTier accept (size, quality)
and prefer quality-based classification. When quality is empty,
"auto", or unrecognized it falls back to the existing size-dimension
classification, preserving behaviour for older gpt-image-1 / dall-e
flows and for the OpenAI Responses API image_generation tool path
(image_generation_intent.go now also threads tool["quality"] through).

Tests:
  - go build ./...
  - go test -tags unit ./internal/service/...
  - existing JSON parse case (size=1024x1024 + quality=high) updated
    to expect "4K" — its previous "1K" was the billing bug

Verified end-to-end against a live deployment (HTTP 200, usage_logs
shows image_size="4K" total_cost=0.6 for quality=high; image_size="1K"
total_cost=0.2 for quality=low when Group.ImagePrice1K/2K/4K are set).
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 7, 2026

Thank you for your contribution! Before we can merge this PR, we need you to sign our Contributor License Agreement (CLA).

To sign, please reply with the following comment:

I have read the CLA Document and I hereby sign the CLA

You only need to sign once — it will be valid for all your future contributions to this project.


I have read the CLA Document and I hereby sign the CLA


You can retrigger this bot by commenting recheck in this Pull Request. Posted by the CLA Assistant Lite bot.

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.

1 participant