Summary
chat_template.jinja in peculiar-ragdoll/Tiel-Coder-35B-A3B-MLX-oQ4e (the "Sharp chat template" from peculiar-ragdoll/Qwen-Sharp-Chat-Templates) throws Cannot convert value of type Optional<Any> to Jinja Value on any real agentic request that includes a tools array — but the model serves fine on --info / startup and answers plain (tool-less) requests correctly. The #142 startup probe doesn't catch this because it only renders a bare one-turn prompt with no tools, so the failure only surfaces at request time, in production, against real coding-agent clients (opencode, pi).
Repro
SwiftLM --model <path>/Tiel-Coder-35B-A3B-MLX-oQ4e --port 5413 --stream-experts --ssd-prefetch --prefill-size 2048 --ctx-size 32768 --max-tokens 4096
-
Plain request, no tools: works.
curl http://127.0.0.1:5413/v1/chat/completions -d '{"model":"tiel","messages":[{"role":"user","content":"hi"}]}'
→ 200, coherent response
-
Real agentic client request (opencode run, pi --print), which includes a tools array in the payload: every attempt fails —
AI_APICallError: The chat template shipped with .../tiel-oq4e is not valid Jinja and could not be
parsed: runtime('Cannot convert value of type Optional<Any> to Jinja Value'). This is a defect in
the model's chat_template.jinja (or the chat_template field of its tokenizer_config.json), not in
the request — report it to whoever publishes the checkpoint. Pinning to an earlier revision of the
model is the usual workaround.
What I ruled out
- Not a recent regression. Pinned to the checkpoint's original template revision (commit
943cfc8, Sharp v22.3.2, before any tool-protocol-specific patches) — identical crash.
- Not fixed by removing the template entirely to force the "simple text format" fallback (works for other checkpoints shipping no template at all, e.g. DeepSeek-V4-Flash) — with
chat_template.jinja and the embedded tokenizer_config.json chat_template key both removed, the request instead hangs indefinitely (never reaches slot_launch in the server log) rather than crashing. Worse, not better.
- Confirmed the startup probe passes cleanly —
[SwiftLM] ✅ Ready — because it never exercises a tools-bearing turn.
Secondary finding: the opt-in heartbeat (#75) also breaks against real opencode
Enabling X-SwiftLM-Prefill-Progress: true does deliver the heartbeat payload, but opencode
1.18.30's strict client rejects it outright rather than skipping it:
AI_TypeValidationError: Type validation failed: Value: {"n_past":0,"status":"processing",
"n_prompt_tokens":1016,"fraction":0,"elapsed_seconds":2}.
Error message: [{"code":"invalid_union","errors":[[{"expected":"array","code":"invalid_type",
"path":["choices"],...}],[{"expected":"object","code":"invalid_type","path":["error"],...}]],...}]
#75's own e2e test only validates this against gemma-4-e4b-it-4bit with no tools involved —
it doesn't cover a model with a tools array in play, where a title-generation sub-call
(agent=title) hits this before the main turn even starts. Worth its own regression test
alongside a tools-bearing request.
Suggested fix
Extend the #142 startup probe to render one additional case with a minimal non-empty tools array (in addition to the existing bare one-turn prompt), so a checkpoint whose template only breaks under tool-calling fails fast at load time with the same clear MalformedChatTemplate diagnostic, instead of failing silently in production on the first real agentic request. Separately, add a tools-bearing request to #75's opencode e2e test so the heartbeat validation gap above gets caught too.
Environment
- SwiftLM b711 (
SwiftLM-b711-c70957c, macOS arm64)
- Checkpoint:
peculiar-ragdoll/Tiel-Coder-35B-A3B-MLX-oQ4e (qwen3_5_moe, Qwen35MoEModel, oQ4e quant)
- Clients tested: opencode 1.18.30, pi 0.85.1 — both reproduce identically, ruling out a client-side cause
Summary
chat_template.jinjainpeculiar-ragdoll/Tiel-Coder-35B-A3B-MLX-oQ4e(the "Sharp chat template" frompeculiar-ragdoll/Qwen-Sharp-Chat-Templates) throwsCannot convert value of type Optional<Any> to Jinja Valueon any real agentic request that includes atoolsarray — but the model serves fine on--info/ startup and answers plain (tool-less) requests correctly. The #142 startup probe doesn't catch this because it only renders a bare one-turn prompt with no tools, so the failure only surfaces at request time, in production, against real coding-agent clients (opencode, pi).Repro
Plain request, no
tools: works.Real agentic client request (opencode
run, pi--print), which includes atoolsarray in the payload: every attempt fails —What I ruled out
943cfc8, Sharp v22.3.2, before any tool-protocol-specific patches) — identical crash.chat_template.jinjaand the embeddedtokenizer_config.jsonchat_templatekey both removed, the request instead hangs indefinitely (never reachesslot_launchin the server log) rather than crashing. Worse, not better.[SwiftLM] ✅ Ready— because it never exercises a tools-bearing turn.Secondary finding: the opt-in heartbeat (#75) also breaks against real opencode
Enabling
X-SwiftLM-Prefill-Progress: truedoes deliver the heartbeat payload, but opencode1.18.30's strict client rejects it outright rather than skipping it:
#75's own e2e test only validates this against
gemma-4-e4b-it-4bitwith no tools involved —it doesn't cover a model with a
toolsarray in play, where a title-generation sub-call(
agent=title) hits this before the main turn even starts. Worth its own regression testalongside a tools-bearing request.
Suggested fix
Extend the #142 startup probe to render one additional case with a minimal non-empty
toolsarray (in addition to the existing bare one-turn prompt), so a checkpoint whose template only breaks under tool-calling fails fast at load time with the same clearMalformedChatTemplatediagnostic, instead of failing silently in production on the first real agentic request. Separately, add atools-bearing request to #75's opencode e2e test so the heartbeat validation gap above gets caught too.Environment
SwiftLM-b711-c70957c, macOS arm64)peculiar-ragdoll/Tiel-Coder-35B-A3B-MLX-oQ4e(qwen3_5_moe,Qwen35MoEModel, oQ4e quant)