ROCm / ROCm/FastFlowLM

[Issue]: `usage.prompt_tokens` on the OpenAI-compatible endpoints reports only the tokens prefilled on the current turn

Open Beginner friendly
#730 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
C++
Stars
1.9k
Forks
152
Avg merge
4h 14m
Merged PRs (30d)
11

Description

Problem Description

usage.prompt_tokens on the OpenAI-compatible endpoints reports only the tokens
prefilled on the current turn, not the size of the prompt. In any multi-turn
conversation the reported context usage collapses back to roughly the size of the
newest message on every request after the first.

Operating System

Ubuntu 26.04.1 LTS (kernel 7.0.0-31-generic)

CPU

AMD Ryzen™ AI 7 445

GPU

AMD Radeon™ 840M × 12

ROCm Version

1.0.1

Installation Method

Package manager

Installed ROCm Packages / Versions
Installed ROCm packages / versions

N/A

ROCm Component

No response

Steps to Reproduce
Command that fails
flm serve llama3.2:3b

then two sequential requests on the same conversation:

# turn 1
curl -s localhost:52625/v1/chat/completions -H 'Content-Type: application/json' -d '{
  "model": "llama3.2:3b",
  "messages": [{"role": "user", "content": "say alpha"}],
  "max_tokens": 16
}' | jq .usage

# turn 2 -- same conversation, with turn 1's reply appended
curl -s localhost:52625/v1/chat/completions -H 'Content-Type: application/json' -d '{
  "model": "llama3.2:3b",
  "messages": [
    {"role": "user", "content": "say alpha"},
    {"role": "assistant", "content": "alpha"},
    {"role": "user", "content": "say beta"}
  ],
  "max_tokens": 16
}' | jq .usage
Expected

prompt_tokens grows with the conversation, because it is defined as the number
of tokens in the input prompt. Under the OpenAI contract any cached portion is
counted inside prompt_tokens and reported separately in
prompt_tokens_details.cached_tokens.

Actual

Turn 2 reports a prompt_tokens roughly the size of the new message alone, so the
figure drops instead of growing.

Cause

The prompt cache matches the incoming prompt against token_history and erases the
matching prefix so only the new tail is prefilled. The count is then taken from the
already-trimmed vector, in AutoModel::_shared_insert
(src/common/AutoModel/automodel.cpp):

tokens.erase(tokens.begin(), tokens.begin() + skip_count);   // cached prefix dropped
...
meta_info.prompt_tokens = tokens.size();                      // only the NEW tokens

Qwen3_5_Omni::insert has its own copy of the same pattern.

The line was correct when written — at that point no prompt cache existed and
tokens really was the whole prompt. It was invalidated by 996d80e
("refactor: rewrite prompt cache management logic", 2026-05-20), which introduced
skip_count and the erase in the same hunk where it edited this line to drop a
stale + 1, without adding the skipped prefix back.

(Optional for Linux users) Output of rocminfo --support
rocminfo --support output

N/A

Additional Information
Impact

Agent front-ends that size the context from usage never see it fill up. OpenCode
in particular decides when to compact from this figure, so it never compacts, and
the session degrades into a loop of the context appearing to reset and being refed.
Every tool-call result is another turn, so the effect compounds quickly.

Related
  • #533 — request to expose current KV cache length in the JSON output. Adjacent:
    prompt_tokens_details.cached_tokens partly answers that need.
Fix

Proposed in #729.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start in src/common/AutoModel/automodel.cpp at AutoModel::_shared_insert, then compare the duplicated logic in Qwen3_5_Omni::insert. Reproduce the two sequential curl requests from the issue and inspect usage, including prompt_tokens_details.cached_tokens. Done means prompt_tokens reflects the full conversation while cached tokens remain reported separately; check #729 for the proposed fix context.

Written by the indexing model from the issue text.

Assessment

Tech stack
cpp
Domain
api, backend
Issue type
Bug
Difficulty
2/5
Estimated time
1-3 hours
Activity status
Active
Clarity
Clearly specified
Newbie friendliness
75/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.