diff --git a/stacks/meromero-charrp/README.md b/stacks/meromero-charrp/README.md index 118a9ea..57b061c 100644 --- a/stacks/meromero-charrp/README.md +++ b/stacks/meromero-charrp/README.md @@ -7,6 +7,32 @@ The **non-thinking, multimodal** RP prose seat. Serves the LiteLLM `char-rp` ali - **Port:** :8016 → LiteLLM `char-rp`. - **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. +- **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{...}` 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>… 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` 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`. Replaces the retired **char-rp-gguf** (Magidonia-24B GGUF / llama.cpp) seat. The quant pipeline diff --git a/stacks/meromero-charrp/compose.yaml b/stacks/meromero-charrp/compose.yaml index c01abf6..9c5f81c 100644 --- a/stacks/meromero-charrp/compose.yaml +++ b/stacks/meromero-charrp/compose.yaml @@ -30,6 +30,31 @@ services: - compressed-tensors - --served-model-name - char-rp + # Tool-calling: Gemma-4 emits its OWN native syntax + # (<|tool_call>call:name{...}), 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>... + # 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 - "${MEROMERO_MAX_MODEL_LEN:-262144}" - --max-num-seqs