fix(heretic2-nvfp4): parse tool_call arguments string->dict for Qwen3.6 template
render-verify caught it: Dvalin's calib tool_calls carry OpenAI wire-form JSON string arguments, but the Qwen3.6 chat template does .items() on arguments (needs a dict) → jinja TypeError. Parse string->dict in render_verify + the quant's load_calib_chat. Confirmed: renders the exact qwen3_coder XML the seat emits (prefixed bifrost.soong-lab.*, v0.3.13 generate_portrait, <think>, <tool_response>).
This commit is contained in:
@@ -61,6 +61,14 @@ def load_calib_chat(path, tokenizer, seqlen):
|
||||
rows = [json.loads(l) for l in open(path) if l.strip()]
|
||||
out = []
|
||||
for r in rows:
|
||||
# Tool_call arguments arrive as OpenAI wire-form JSON *strings*; the Qwen3.6
|
||||
# template does .items() on them → needs a dict. Parse string→dict so the
|
||||
# forward-pass sees the qwen3_coder XML the seat emits. (render_verify finding.)
|
||||
for m in r["messages"]:
|
||||
for tc in (m.get("tool_calls") or []):
|
||||
a = tc.get("function", {}).get("arguments")
|
||||
if isinstance(a, str):
|
||||
tc["function"]["arguments"] = json.loads(a)
|
||||
text = tokenizer.apply_chat_template(
|
||||
r["messages"], tools=r.get("tools"),
|
||||
tokenize=False, add_generation_prompt=False, enable_thinking=True,
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Render-verify a tool-call-XML calib row through the Qwen3.6 chat template.
|
||||
|
||||
brokkr's last gate before the production calib enters the quant: confirm the
|
||||
assistant tool_calls serialize to the qwen3_coder XML the LIVE seat emits
|
||||
(`<tool_call>\n<function=NAME>\n<parameter=KEY>...`), so the calibration
|
||||
forward-pass sees the seat's REAL tool-call activations (#355-preservation).
|
||||
|
||||
Usage: python3 render_verify.py <tokenizer_dir> <calib.jsonl> [row_index]
|
||||
"""
|
||||
import json
|
||||
import sys
|
||||
|
||||
from transformers import AutoTokenizer
|
||||
|
||||
tok_dir, calib = sys.argv[1], sys.argv[2]
|
||||
idx = int(sys.argv[3]) if len(sys.argv) > 3 else 0
|
||||
|
||||
tok = AutoTokenizer.from_pretrained(tok_dir, trust_remote_code=True)
|
||||
with open(calib) as f:
|
||||
row = json.loads([l for l in f if l.strip()][idx])
|
||||
|
||||
# Tool_call arguments arrive in OpenAI wire form (a JSON *string*), but the Qwen3.6
|
||||
# chat template does .items() on them → needs a dict. Parse string→dict so the
|
||||
# template renders the qwen3_coder <parameter=...> XML. (render-verify finding.)
|
||||
for m in row["messages"]:
|
||||
for tc in (m.get("tool_calls") or []):
|
||||
a = tc.get("function", {}).get("arguments")
|
||||
if isinstance(a, str):
|
||||
tc["function"]["arguments"] = json.loads(a)
|
||||
|
||||
tools = row.get("tools", [])
|
||||
print("=== tools:", [t.get("function", {}).get("name") for t in tools])
|
||||
print("=== message roles:", [m.get("role") for m in row["messages"]])
|
||||
|
||||
text = tok.apply_chat_template(
|
||||
row["messages"], tools=tools,
|
||||
tokenize=False, add_generation_prompt=False, enable_thinking=True,
|
||||
)
|
||||
print(f"=== rendered ({len(text)} chars) ===")
|
||||
print(text)
|
||||
print("=== qwen3_coder XML markers present? ===")
|
||||
for m in ("<tool_call>", "<function=", "<parameter=", "</think>", "bifrost.soong-lab."):
|
||||
print(f" {m!r}: {m in text}")
|
||||
Reference in New Issue
Block a user