fix(char-rp): enable Gemma-4 tool-calling on the MeroMero seat
The char-rp seat shipped with no tool-call parser at all, so every
tools-bearing request was rejected outright:
400 "auto" tool choice requires --enable-auto-tool-choice and
--tool-call-parser to be set
MeroMero-v2 is Gemma-4, which emits its own native
`<|tool_call>call:name{...}<tool_call|>` syntax rather than the
qwen3_coder XML the Qwen-family seats use. vLLM 0.24 ships a matching
`gemma4` parser whose TOOL_CALL_START/END, CHANNEL_START/END and escape
token constants line up with this tokenizer's etc/eoc/escape tokens
exactly.
Three flags, and they are a set:
- --tool-call-parser gemma4 + --enable-auto-tool-choice: the fix proper.
- --reasoning-parser gemma4: without it the post-tool-response turn
leaks a literal `<|channel>thought\n<channel|>` prefix into content
(upstream vllm #45834 — the chat template leaves the prompt inside an
open channel block).
- --default-chat-template-kwargs '{"enable_thinking": false}': MANDATORY
companion to the reasoning parser. The parser reads enable_thinking
from chat_template_kwargs and defaults it to True
(vllm/parser/gemma4.py:439); True makes is_reasoning_end() return
False at a new turn, pre-initialising the engine to REASONING, which
routes ALL plain RP prose into reasoning_content and returns a null
content — breaking every char-rp consumer. This template already
defaults enable_thinking to false (chat_template.jinja:350), so
passing it explicitly renders a byte-identical prompt (verified across
plain / tools / post-tool-response / system-prompt shapes). It changes
generation not at all; it only corrects the parser state machine.
Verified green on the live seat after deploy: tool call streaming and
non-streaming, tool-result round-trip (leak gone), plain prose in
content with reasoning null, vision unchanged.
This commit is contained in:
@@ -7,6 +7,32 @@ The **non-thinking, multimodal** RP prose seat. Serves the LiteLLM `char-rp` ali
|
|||||||
- **Port:** :8016 → LiteLLM `char-rp`.
|
- **Port:** :8016 → LiteLLM `char-rp`.
|
||||||
- **Context:** 256K (`--max-model-len 262144`). Gemma-4 uses sliding-window attention → KV-efficient, ~2× concurrency at full context.
|
- **Context:** 256K (`--max-model-len 262144`). Gemma-4 uses sliding-window attention → KV-efficient, ~2× concurrency at full context.
|
||||||
- **Vision:** enabled (image + text). `preprocessor_config.json` was materialized from the model's own `processor_config.json` (`Gemma4ImageProcessor`); audio is config-declared but weightless.
|
- **Vision:** enabled (image + text). `preprocessor_config.json` was materialized from the model's own `processor_config.json` (`Gemma4ImageProcessor`); audio is config-declared but weightless.
|
||||||
|
- **Tool-calling:** enabled via the `gemma4` parser (**not** `qwen3_coder` — that's the Qwen-family XML the other seats use). Gemma-4 emits its own native `<|tool_call>call:name{...}<tool_call|>` syntax.
|
||||||
|
|
||||||
|
## Tool-calling — the three flags are a set, don't split them
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
- --tool-call-parser gemma4 # native <|tool_call> syntax; without it ANY tools request 400s
|
||||||
|
- --enable-auto-tool-choice
|
||||||
|
- --reasoning-parser gemma4 # absorbs the <|channel>…<channel|> thought markers
|
||||||
|
- --default-chat-template-kwargs '{"enable_thinking": false}' # MANDATORY, see below
|
||||||
|
```
|
||||||
|
|
||||||
|
Why the last one is mandatory: the gemma4 parser reads `enable_thinking` out of
|
||||||
|
`chat_template_kwargs` and **defaults it to `True`** (`vllm/parser/gemma4.py:439`). With `True`,
|
||||||
|
`is_reasoning_end()` returns `False` at a new turn, which pre-initialises the parser engine to
|
||||||
|
`REASONING` — so **all plain RP prose lands in `reasoning_content` and `content` comes back
|
||||||
|
`null`**, breaking every `char-rp` consumer. This model's `chat_template.jinja:350` already
|
||||||
|
defaults `enable_thinking` to `false`, so passing it explicitly renders a **byte-identical
|
||||||
|
prompt** (verified across plain / tools / post-tool-response / system-prompt shapes) — it changes
|
||||||
|
nothing about generation, it only corrects the parser's state machine.
|
||||||
|
|
||||||
|
Without `--reasoning-parser gemma4`, the post-tool-response turn leaks a literal
|
||||||
|
`<|channel>thought\n<channel|>` prefix into `content` (upstream vllm #45834 — the chat template
|
||||||
|
leaves the prompt sitting inside an open channel block).
|
||||||
|
|
||||||
|
Verified green after the fix: tool call (streaming + non-streaming), tool-result round-trip,
|
||||||
|
plain prose in `content`, vision.
|
||||||
- **Tuning:** `.env` — `MEROMERO_GPU_MEM_UTIL=0.52` (leaves ~4.6 GB GPU0 headroom), `MEROMERO_MAX_MODEL_LEN=262144`, `MEROMERO_GPU_ID=0`.
|
- **Tuning:** `.env` — `MEROMERO_GPU_MEM_UTIL=0.52` (leaves ~4.6 GB GPU0 headroom), `MEROMERO_MAX_MODEL_LEN=262144`, `MEROMERO_GPU_ID=0`.
|
||||||
|
|
||||||
Replaces the retired **char-rp-gguf** (Magidonia-24B GGUF / llama.cpp) seat. The quant pipeline
|
Replaces the retired **char-rp-gguf** (Magidonia-24B GGUF / llama.cpp) seat. The quant pipeline
|
||||||
|
|||||||
@@ -30,6 +30,31 @@ services:
|
|||||||
- compressed-tensors
|
- compressed-tensors
|
||||||
- --served-model-name
|
- --served-model-name
|
||||||
- char-rp
|
- char-rp
|
||||||
|
# Tool-calling: Gemma-4 emits its OWN native syntax
|
||||||
|
# (<|tool_call>call:name{...}<tool_call|>), NOT the qwen3_coder XML the
|
||||||
|
# other seats use. vLLM 0.24 ships a matching `gemma4` parser whose token
|
||||||
|
# constants line up with this tokenizer's etc/eoc/escape tokens exactly.
|
||||||
|
# Without these two flags any tools-bearing request 400s outright.
|
||||||
|
- --tool-call-parser
|
||||||
|
- gemma4
|
||||||
|
- --enable-auto-tool-choice
|
||||||
|
# The gemma4 REASONING parser is what absorbs the <|channel>...<channel|>
|
||||||
|
# thought markers; without it they leak into `content` verbatim on the
|
||||||
|
# post-tool-response turn (upstream vllm #45834 — the chat template leaves
|
||||||
|
# the prompt inside an open channel block).
|
||||||
|
- --reasoning-parser
|
||||||
|
- gemma4
|
||||||
|
# MANDATORY companion to the reasoning parser on this seat. The parser
|
||||||
|
# reads enable_thinking from chat_template_kwargs and DEFAULTS IT TO TRUE
|
||||||
|
# (vllm/parser/gemma4.py:439). True makes is_reasoning_end() return False
|
||||||
|
# at a new turn, which pre-initialises the engine to REASONING -> ALL plain
|
||||||
|
# RP prose lands in reasoning_content with a NULL content, breaking every
|
||||||
|
# char-rp consumer. This template already defaults enable_thinking to false
|
||||||
|
# (chat_template.jinja:350), so passing it explicitly renders a BYTE-
|
||||||
|
# IDENTICAL prompt (verified across plain/tools/post-tool/system shapes) --
|
||||||
|
# it only corrects the parser's state machine. Do not remove.
|
||||||
|
- --default-chat-template-kwargs
|
||||||
|
- '{"enable_thinking": false}'
|
||||||
- --max-model-len
|
- --max-model-len
|
||||||
- "${MEROMERO_MAX_MODEL_LEN:-262144}"
|
- "${MEROMERO_MAX_MODEL_LEN:-262144}"
|
||||||
- --max-num-seqs
|
- --max-num-seqs
|
||||||
|
|||||||
Reference in New Issue
Block a user