oxyle's picture
download
raw
8.21 kB
#!/usr/bin/env python3
"""Verify the Sharp template: minimal diff from upstream, correct rendering, minify round-trip.
python3 scripts/verify_template.py
Run this before publishing, and after any edit to chat_template.jinja. It fetches current
upstream (v22.3) fresh, so it also catches the case where froggeric ships a new version and our
splice silently sits on top of a template we haven't looked at.
The checks exist because each of them has a real failure mode:
* diff-vs-upstream -- guards against accidentally shipping edits we didn't intend to make
* terseness-once -- a double-splice would send the instruction twice and waste context
* system-preserved -- the else-branch is the whole reason a user's own prompt survives
* think-retained -- froggeric's retention fix is the thing we must NOT have broken
* minify round-trip -- the minifier collapses newlines; our {% set %} body must survive it
"""
from __future__ import annotations
import difflib
import re
import pathlib
import sys
import urllib.request
from jinja2 import Environment
HERE = pathlib.Path(__file__).resolve().parent.parent
UPSTREAM = ("https://huggingface.co/froggeric/Qwen-Fixed-Chat-Templates/"
"resolve/main/chat_template.jinja")
MARKER = "Never: open with preamble"
BASE = "qwen3.8-froggeric-v22.3" # the upstream release this fork is rebased onto
fails: list[str] = []
def check(ok: bool, label: str) -> None:
print(f" {'PASS' if ok else 'FAIL'} {label}")
if not ok:
fails.append(label)
def render(src: str, msgs: list[dict], **kw) -> str:
return Environment().from_string(src).render(
messages=msgs, add_generation_prompt=True, **kw)
def main() -> int:
full = (HERE / "chat_template.jinja").read_text()
mini = (HERE / "chat_template_oneline.txt").read_text()
print("=== diff vs upstream v22.3")
with urllib.request.urlopen(UPSTREAM, timeout=60) as r:
up = r.read().decode()
diff = [l for l in difflib.unified_diff(up.splitlines(), full.splitlines(), lineterm="")
if l.startswith(("+", "-")) and not l.startswith(("+++", "---"))]
# v22.3.1 is froggeric v22.3 + the terseness append + the fast-mode fixes (Fix 3 + the
# thinking-on/off terseness-lead split), so it is NO LONGER a pure-insertion diff on v22.3:
# the fixes conditionalize a few upstream tool-call lines. We therefore assert INTENT, not an
# exact insertion count -- the append must be present, the fast-mode fixes must be in, and the
# thinking-ON path must remain byte-identical to "upstream v22.3 + the terseness append".
up_ver = re.search(r'template_version = "([^"]+)"', up)
up_ver = up_ver.group(1) if up_ver else "?"
check(up_ver == BASE, f"upstream is still the rebase base {BASE} (live: {up_ver})")
ins = [l for l in diff if l.startswith("+")]
check(len(ins) >= 11, f"terseness + fixes inserted (>=11 added lines, got {len(ins)})")
check("Answer directly and concisely." in full,
"fast-mode terseness lead present (v22.1.1 split)")
check('template_version = "qwen3.8-froggeric-v22.3.1"' in full, "version is v22.3.1")
check("qwen3.8-froggeric-v22.3" in full, "built on the froggeric v22.3 base")
check("Nail" not in full and "Dagger" not in full, "no model-specific identity in template")
print("\n=== rendering")
cases = {
"no system prompt": [{"role": "user", "content": "hi"}],
"with system prompt": [{"role": "system", "content": "Be a pirate."},
{"role": "user", "content": "hi"}],
"multi-turn w/ think": [{"role": "user", "content": "Q1"},
{"role": "assistant", "content": "<think>PRIORTHOUGHT</think>A1"},
{"role": "user", "content": "Q2"}],
}
for name, msgs in cases.items():
out = render(full, msgs)
check(out.count(MARKER) == 1, f"{name}: terseness appears exactly once")
check("Be a pirate." in render(full, cases["with system prompt"]),
"user's own system prompt is preserved")
# v22.2+ extracts in-content reasoning into a canonical block instead of passing the
# tags through, so match the thought itself inside a think block, not a tag layout.
mt = render(full, cases["multi-turn w/ think"])
check(any("PRIORTHOUGHT" in b for b in re.findall(r"<think>(.*?)</think>", mt, re.S)),
"prior thinking is retained across turns")
# v22.1 defaults reasoning_effort to MEDIUM, which injects no steering line -- so out-of-the-box
# behavior is terseness-only with no forced effort, matching tuned v1, WITHOUT us suppressing
# anything (upstream fixed the old forced-xhigh default). An EXPLICIT effort still injects steering,
# so froggeric's feature is honored as opt-in.
STEER = "Reasoning effort is set to"
check(STEER not in render(full, cases["no system prompt"]),
"default (no reasoning_effort): no steering line (upstream medium default)")
check(STEER not in render(full, cases["with system prompt"]),
"default with system prompt: still no steering line")
check("Reasoning effort is set to low" in render(full, cases["no system prompt"],
reasoning_effort="low"), "explicit reasoning_effort=low is still honored (opt-in)")
check(MARKER in render(full, cases["no system prompt"], reasoning_effort="low"),
"terseness still present when an explicit effort is requested")
tools = [{"type": "function", "function": {"name": "get_weather", "description": "w",
"parameters": {"type": "object", "properties": {"city": {"type": "string"}}}}}]
out = render(full, [{"role": "user", "content": "weather?"}], tools=tools)
check("get_weather" in out and out.count(MARKER) == 1, "tool definitions still render")
# The fast-mode fixes: with thinking off, nothing in the prompt may ask for a <think> block
# the generation prompt has already closed. Assert both directions -- the instructions must
# vanish when thinking is off AND survive when it is on, so a future edit can't "fix" this by
# deleting them outright. Tool-call FORMAT rules are not part of the fix and must stay.
print("\n=== fast mode (thinking off)")
weather = [{"role": "user", "content": "weather?"}]
for fmt in ("xml", "json"):
fast = render(full, weather, tools=tools, enable_thinking=False, tool_call_format=fmt)
slow = render(full, weather, tools=tools, tool_call_format=fmt)
body = fast.split("<|im_start|>assistant")[0] # ignore the empty-think prefill
check("think" not in body.lower(), f"{fmt}: no thinking reference anywhere in fast prompt")
check("IMMEDIATELY, with NO conversational" in fast,
f"{fmt}: tool-call immediacy rule rephrased without 'after thinking'")
check("IMMEDIATELY after thinking" in slow,
f"{fmt}: thinking-on keeps the original 'after thinking' wording")
check("Brief explanation of tool call" in slow,
f"{fmt}: thinking-on keeps the <think> stanza")
check("Function calls MUST follow the specified format" in fast,
f"{fmt}: fast mode still carries the tool-call format rules")
check("Answer directly and concisely" in fast, f"{fmt}: fast-mode terseness lead")
print("\n=== minified round-trip")
for name, msgs in cases.items():
check(render(full, msgs) == render(mini, msgs), f"{name}: oneline == full")
sysblk = render(mini, cases["no system prompt"]).split("<|im_start|>system")[1] \
.split("<|im_end|>")[0].strip()
# the terseness tail starts with "Answer directly," (at medium default no steering precedes it);
# isolate it and confirm ITS newlines survived minification as 4 lines.
terse = "Answer directly," + sysblk.split("Answer directly,", 1)[1]
check(len(terse.splitlines()) == 4, "terseness survives minification as 4 lines")
print(f"\n{'ALL CHECKS PASSED' if not fails else str(len(fails)) + ' FAILED: ' + '; '.join(fails)}")
return 1 if fails else 0
if __name__ == "__main__":
sys.exit(main())

Xet Storage Details

Size:
8.21 kB
·
Xet hash:
c1f77ac6b304c1f7a6fd7e752c910d2688a0ccca41cba2bb3405a5dc8b239232

Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.