Skip to content

fix(gemma4): put audio after the text in the prompt - #979

Open
NVolcz wants to merge 1 commit into
ROCm:mainfrom
NVolcz:gemma4-audio-after-text
Open

NVolcz wants to merge 1 commit into
ROCm:mainfrom
NVolcz:gemma4-audio-after-text

Conversation

@NVolcz

@NVolcz NVolcz commented Nov 7, 2026

Copy link
Copy Markdown

The Gemma 4 model card ("Best Practices", "Modality order") recommends putting image content before the text and audio content after it. FastFlowLM puts both images and audio before the text. Images stay where they are; audio now follows the text, in both the server and the CLI prompt builders of gemma4-it (E2B, E4B and their flash and dflash variants) and gemma4-it:12b.

Before:

<|turn>user
<|audio|>Transcribe the audio.<turn|>

After:

<|turn>user
Transcribe the audio.<|audio|><turn|>

The media payload reaches the engine with the first prefill chunk only, and that chunk was widened to reach the last image. With audio after the text, a long text would push the audio into a later chunk without its payload. The first chunk now reaches the last image or audio clip (plan.media_end), as for Qwen3.5-Omni, and the per-kind media_end_by_kind that only Gemma 4 used is removed.

How to reproduce

The order isn't visible through the API, so this shows the prefill chunking and the transcript. Make a short speech clip and send it after a long text (about 7,800 tokens):

espeak-ng -w speech.wav "The quick brown fox jumps over the lazy dog."
python3 - <<'EOF' > request.json
import base64, json
filler = " ".join(f"Item {i}: a note about nothing in particular." for i in range(600))
audio = base64.b64encode(open("speech.wav", "rb").read()).decode()
print(json.dumps({"model": "gemma4-it:e4b", "temperature": 0, "think": False, "max_tokens": 40,
    "messages": [{"role": "user", "content": [
        {"type": "text", "text": "Ignore this list.\n" + filler + "\nNow transcribe the audio."},
        {"type": "input_audio", "input_audio": {"data": audio, "format": "wav"}}]}]}))
EOF
curl -s http://localhost:52625/v1/chat/completions -H 'Content-Type: application/json' \
  -d @request.json | jq -r '.choices[0].message.content'
Server log Transcript
main Prefill chunk 1/2 with 4096 tokens, Prefill chunk 2/2 with 3708 tokens A quick brown fox jumps over the lazy dog.
This branch Prefill chunk 1/1 with 7804 tokens The quick brown fox jumps over the lazy dog.

The single chunk is the payload fix: the audio now comes after the text, so the first chunk is widened to reach it. The transcript difference is one example on this clip, not a measurement.

I found this while comparing the prompts flm renders with Google's template, using a small debug branch that logs every prompt to a file (log-prompt, FLM_LOG_PROMPT=<file>).

The Gemma 4 model card recommends image content before the text and audio content after it.
Both prompt builders put images and audio before the text; audio now follows it, for
gemma4-it (E2B, E4B and their flash and dflash variants) and gemma4-it:12b, from the server
and the CLI.

The media payload reaches the engine with the first prefill chunk only, which was widened to
reach the last image. With audio after the text, a long text would push the audio into a later
chunk without its payload. The first chunk now reaches the last image or audio clip
(plan.media_end), as for Qwen3.5-Omni, and media_end_by_kind, which only Gemma 4 read, is gone.
@tawei-amd
tawei-amd requested a review from zaneni6 November 7, 2026 21:55

This branch has not been deployed

No deployments
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