Fix Gemma 4 12B tool-call routing: chat template mismatch causes Python-prose output instead of structured tool calls#1347
Conversation
- Add `GEMMA_4_12B` catalog entry (gemma-4-12B-it-Q4_K_M.gguf, 7.4 GB) with Q8_0 KV-cache quantization so the 32K context fits alongside the weights on a 16 GB device - Log `chat_format` and `parse_tool_calls` at INFO level after `apply_chat_template` so we can confirm the C++ layer detects COMMON_CHAT_FORMAT_PEG_GEMMA4 (=3) for the 12B model at runtime - Update catalog test for the new model count (4 → 5) and add a `model_spec_for` assertion confirming the 12B Q8_0 KV-cache entry - Delete unused `dump_chat_template.rs` example (superseded by the Python GGUF parser used during investigation) The 12B GGUF's embedded Jinja template contains the `<|tool_call>call:` sentinel, so llama.cpp's `common_chat_try_specialized_template` should route it to the PEG_GEMMA4 parser. The diagnostic log will confirm `chat_format=3` at runtime; if it returns 0 (CONTENT_ONLY) the template detection is failing despite the sentinel and we'll need to investigate the C++ side. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…evel - Add RAM_THRESHOLD_MEDIUM (16 GB) so recommended_model_id_for returns GEMMA_4_12B for 16–32 GB systems instead of always falling back to E4B; update docstring and tests to accept the three valid Gemma 4 outcomes - Downgrade chat-format diagnostic log from info! to debug! — it fires on every ReAct iteration for the same model; info! was log noise in prod - Add tracing::warn! when chat_format=0 (CONTENT_ONLY) is returned with tools present — this is a silent routing failure where tool calls would be emitted as plain text; warn makes it visible without a debug build Skipped: min_memory_gb:16 for 12B — 7.4 GB + 2.5 GB Q8_0 KV = ~10 GB footprint leaving ~6 GB on a 16 GB device, comparable to E4B and Ministral 8B (both also 16 GB minimum with similar footprints). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Review address summary✅ Addressed (4/5 recommendations)🔴 Finding 1 — 12B never recommended: Added 🟡 Finding 2 — Silent chat_format=0 degradation: Added 🟢 Finding 3 — Stale docstring: Updated 🟢 Finding 4 — Log level: Downgraded the chat-format diagnostic log from ⏭️ Skipped (1/5)🟢 Finding 5 — min_memory_gb:16 tightness for 12B: The 12B footprint is ~10 GB (7.4 GB weights + 2.5 GB Q8_0 KV at 32K), leaving ~6 GB on a 16 GB device. This is comparable to E4B (~10 GB F16 KV) and Ministral 8B (both also |
Re-Review: PR #1347 — Gemma 4 12B Tool-Call RoutingReview Type: Re-Review (address-review commit: e27c244) Previous Review Summary
Requirements Check (Issue #1346)The issue acceptance criteria are runtime-validation items (observe structured tool calls in the eval matrix, non-empty
The remaining ❌ items are runtime validation criteria that require the model to be loaded — they cannot be confirmed in a static code review. The PR delivers the infrastructure required for that validation. Code Review Findings (new commit e27c244 only)No Critical IssuesAll four addressed findings are correctly resolved. Suggested ImprovementsFinding 1 — Three-tier threshold: CORRECTLY ADDRESSED
Finding 2 — chat_format=0 warning: CORRECTLY ADDRESSED The guard condition The Finding 3 — Stale docstring: CORRECTLY ADDRESSED
Finding 4 — Log level: CORRECTLY ADDRESSED
NitpicksNit:
Nit: As noted above, AssessmentThe address-review commit correctly resolves all four actionable findings. The three-tier recommendation logic is sound, boundary conditions are correct (exclusive The two nits are minor polish items. Neither affects correctness or production behavior. Recommendation: APPROVE The remaining acceptance criteria (runtime tool-call validation, eval matrix) require the model to be loaded and are appropriately post-merge work. The infrastructure to enable that validation is correct. |
Use if-let to bind the non-empty tools slice, eliminating the map_or(0,...) fallback that could never be reached (the guard already confirmed tools is Some and non-empty before the warn fires). Skipped nit: let medium computed before match — mirrors the existing let large pattern; moving it inside each arm would duplicate the call. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Review address (nit pass)Addressed (1/2 nits)Nit 2 — dead fallback in warn body: Refactored the guard to use Skipped (1/2 nits)Nit 1 — |
…on-prose output instead of structured tool calls (#1347) * Add Gemma 4 12B catalog entry and chat-format diagnostics (closes #1346) - Add `GEMMA_4_12B` catalog entry (gemma-4-12B-it-Q4_K_M.gguf, 7.4 GB) with Q8_0 KV-cache quantization so the 32K context fits alongside the weights on a 16 GB device - Log `chat_format` and `parse_tool_calls` at INFO level after `apply_chat_template` so we can confirm the C++ layer detects COMMON_CHAT_FORMAT_PEG_GEMMA4 (=3) for the 12B model at runtime - Update catalog test for the new model count (4 → 5) and add a `model_spec_for` assertion confirming the 12B Q8_0 KV-cache entry - Delete unused `dump_chat_template.rs` example (superseded by the Python GGUF parser used during investigation) The 12B GGUF's embedded Jinja template contains the `<|tool_call>call:` sentinel, so llama.cpp's `common_chat_try_specialized_template` should route it to the PEG_GEMMA4 parser. The diagnostic log will confirm `chat_format=3` at runtime; if it returns 0 (CONTENT_ONLY) the template detection is failing despite the sentinel and we'll need to investigate the C++ side. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * Address review: 3-tier Gemma4 recommendation, chat_format warn, log level - Add RAM_THRESHOLD_MEDIUM (16 GB) so recommended_model_id_for returns GEMMA_4_12B for 16–32 GB systems instead of always falling back to E4B; update docstring and tests to accept the three valid Gemma 4 outcomes - Downgrade chat-format diagnostic log from info! to debug! — it fires on every ReAct iteration for the same model; info! was log noise in prod - Add tracing::warn! when chat_format=0 (CONTENT_ONLY) is returned with tools present — this is a silent routing failure where tool calls would be emitted as plain text; warn makes it visible without a debug build Skipped: min_memory_gb:16 for 12B — 7.4 GB + 2.5 GB Q8_0 KV = ~10 GB footprint leaving ~6 GB on a 16 GB device, comparable to E4B and Ministral 8B (both also 16 GB minimum with similar footprints). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * Address review nit: bind tools slice before chat_format warn Use if-let to bind the non-empty tools slice, eliminating the map_or(0,...) fallback that could never be reached (the guard already confirmed tools is Some and non-empty before the warn fires). Skipped nit: let medium computed before match — mirrors the existing let large pattern; moving it inside each arm would duplicate the call. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> --------- Co-authored-by: Michael Libio <malibio@Michaels-Mac-mini.local> Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Closes #1346