Skip to content

5.6 Luna on opencode zen tui showing half the price being shown on web version #39891

Closed as not planned

Description

@tejush1998

Description

Ive attached screenshots for both, and the one on web seem more correct. Its misleading, makes costs in tui appear much less than they actually are. Findings from my triage are below.

Plugins

No response

OpenCode version

1.18.10

Steps to reproduce

No response

Screenshot and/or share link

Image Image

Operating System

MacOS 26.5.2

Terminal

terminal app on mac

Activity

  1. tejush1998 commented on Jul 31, 2026

    @tejush1998
    Author

    Logged this in opencode in packages/opencode/src/session/llm.ts :
    Response from openai luna :

    {
      "inputTokens": 82307,
      "inputTokenDetails": {
        "noCacheTokens": 3,
        "cacheReadTokens": 79354,
        "cacheWriteTokens": 2950
      },
    ....
    }
    

    Meanwhile opencode usage shows:

    Total input: 85257 
    Input: 2953
    Cached input: 79394
    Cached write: 2950 (leftover)
    

    As you can see above inputTokens: 82307 vs 85257
    Diff = cache_write_tokens = 2950 (inputTokens from openai is exclusive of cacheWriteTokens)

    Problem?

    the getUsage function in packages/opencode/src/session/session.ts :
    assumes cacheWriteInputTokens to be part of inputTokens

      const adjustedInputTokens = safe(inputTokens - cacheReadInputTokens - cacheWriteInputTokens)
    

    This shows in tui as low billing than it actually is, because cacheWrites are eating into the freshTokens total

    Anthropic works properly:

    {
      "inputTokens": 23335,
      "inputTokenDetails": {
        "noCacheTokens": 2,
        "cacheReadTokens": 23028,
        "cacheWriteTokens": 305
      },
    ...
    }
    

    Opencode usage correctly showing totalInput : 23335, which matches the above inputTokens

    Question: So this is an upstream issue need to be reported to OpenAI? Or should we handle it? Would love to contribute

  2. Zaczero commented on Aug 18, 2026

    @Zaczero

    The TUI is not “about half by rounding.” It is subtracting Luna’s cache-write tokens from a total that never contained them as extra, and the web is not doing that subtraction.

    Your own capture is enough:

    OpenAI Luna
      inputTokens:              82,307
        noCacheTokens:               3
        cacheReadTokens:        79,354
        cacheWriteTokens:        2,950
    
      3 + 79,354 + 2,950 = 82,307
    

    So inputTokens is already the whole prompt: fresh + read + write. It is not exclusive of the write.

    TUI then shows:

    Total input:    85,257     = 82,307 + 2,950   (write added again)
    Input:           2,953     = 82,307 − 79,354  (write still inside)
    Cached write:    2,950
    

    getUsage then does the Anthropic split:

    fresh = inputTokens − cacheRead − cacheWrite
          = 82,307 − 79,354 − 2,950
          = 3
    

    Those 2,950 write tokens were already inside 82,307. Subtracting them again leaves 3 tokens at the input rate instead of 2,953. Anthropic looks right because its inputTokens is exclusive of cache, so the same minus does not eat a second copy.

    The web path does not subtract the write. Zen only removes the cache read:

    input billed = prompt_tokens − cached_tokens   → 82,307 − 79,354 = 2,953
    

    plus 2,950 at the write rate. Same tokens, two bills on the web, one bill (write only) on the TUI.

    On this request that is 2,950 tokens at Luna’s input price that the TUI never charges:

    low tier   2,950 × $0.20 / 1M = $0.0006
    high tier  2,950 × $0.40 / 1M = $0.0012
    

    One row is small. A long session is thousands of those writes, which is the “TUI looks like half the web” screenshot.

    This is not an OpenAI bug. 3 + 79,354 + 2,950 = 82,307 is internally consistent. The bug is applying the exclusive split (input − read − write) to a provider whose inputTokens is already inclusive. Handle it on the OpenAI / Luna mapper: if noCache + read + write == inputTokens, take noCache as fresh and do not subtract write a second time.

    const mapUsage = (usage: OpenAIChatEvent["usage"]): Usage | undefined => {
    if (!usage) return undefined
    const input = usage.prompt_tokens ?? undefined
    const output = usage.completion_tokens ?? undefined
    const cached = usage.prompt_tokens_details?.cached_tokens ?? undefined
    const cacheWrite = usage.prompt_tokens_details?.cache_write_tokens ?? undefined
    const reasoning = usage.completion_tokens_details?.reasoning_tokens ?? undefined
    const nonCached = ProviderShared.subtractTokens(input, ProviderShared.sumTokens(cached, cacheWrite))

  3. thdxr commented on Oct 11, 2026

    @thdxr
    Member

    Thanks for the report! OpenCode 2.0 is now out and development is focused on V2, so we're closing issues filed against OpenCode 1. Please upgrade (opencode upgrade --major, details: https://anoma.ly/notes/opencode-2-0), and if this is still relevant on 2.0, reopen this issue or open a new one with your 2.0 version.

    — from 𝕺𝖕𝖊𝖓𝕮𝖔𝖉𝖊

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions