unsloth/studio/backend/tests/test_safetensors_capability_advertise.py
Daniel Han 187144d4e7
Reduce and tighten code comments and docstrings repo-wide (#6095)
Trim and tighten code comments and docstrings across the repository. Comment-only: every changed file verified code-identical to main via AST/token comparison.
2026-06-08 23:09:51 -07:00

441 lines
15 KiB
Python

# SPDX-License-Identifier: AGPL-3.0-only
# Copyright 2026-present the Unsloth AI Inc. team. All rights reserved. See /studio/LICENSE.AGPL-3.0
"""Capability advertisement contract: classifier honesty, worker->orchestrator
IPC hop, route-layer end-to-end. Pure helpers + fakes; no torch/transformers."""
from __future__ import annotations
import sys
from pathlib import Path
from types import SimpleNamespace
from unittest.mock import MagicMock
_backend_root = Path(__file__).resolve().parent.parent
if str(_backend_root) not in sys.path:
sys.path.insert(0, str(_backend_root))
# Qwen3 snippet covering tools, enable_thinking, preserve_thinking.
QWEN3_TEMPLATE = """
{%- if tools %}
{{- '<|im_start|>system\\nFor each function call, return a json object'
' wrapped inside <tool_call></tool_call> tags.\\n' }}
{%- for tool in tools %}
{{- tool | tojson }}
{%- endfor %}
{%- endif %}
{%- for message in messages %}
{%- if message.role == 'tool' %}
{{- '<|im_start|>tool\\n' + message.content + '<|im_end|>\\n' }}
{%- endif %}
{%- endfor %}
{%- if enable_thinking is defined and enable_thinking %}
{{- '<think>' }}
{%- endif %}
{%- if preserve_thinking %}
{{- assistant.reasoning_content }}
{%- endif %}
"""
GPT_OSS_TEMPLATE = """
<|start|>system<|message|>You are gpt-oss.
reasoning_effort: {{ reasoning_effort }}
<|end|>
"""
PLAIN_TEMPLATE = """
{%- for message in messages %}
{{- message.role + ': ' + message.content + '\\n' }}
{%- endfor %}
"""
# ── Tests: classifier honesty ────────────────────────────────────────
def test_detect_reasoning_flags_qwen3_supports_tools_and_reasoning():
from core.inference.llama_cpp import detect_reasoning_flags
flags = detect_reasoning_flags(QWEN3_TEMPLATE, "unsloth/Qwen3-0.6B")
assert flags["supports_tools"] is True
assert flags["supports_reasoning"] is True
assert flags["reasoning_style"] == "enable_thinking"
assert flags["supports_preserve_thinking"] is True
assert flags["reasoning_always_on"] is False
def test_detect_reasoning_flags_plain_template_all_false():
from core.inference.llama_cpp import detect_reasoning_flags
flags = detect_reasoning_flags(PLAIN_TEMPLATE, "some/PlainChat")
assert flags["supports_tools"] is False
assert flags["supports_reasoning"] is False
assert flags["supports_preserve_thinking"] is False
assert flags["reasoning_always_on"] is False
def test_detect_reasoning_flags_none_template_returns_all_false():
from core.inference.llama_cpp import detect_reasoning_flags
flags = detect_reasoning_flags(None)
assert flags["supports_tools"] is False
assert flags["supports_reasoning"] is False
assert flags["supports_preserve_thinking"] is False
assert flags["reasoning_always_on"] is False
assert flags["reasoning_style"] == "enable_thinking"
def test_detect_safetensors_features_passes_template_through_to_classifier():
"""Route wrapper forwards a real template to the inner classifier."""
from routes.inference import _detect_safetensors_features
backend = SimpleNamespace(active_model_name = "unsloth/Qwen3-0.6B")
flags = _detect_safetensors_features(backend, QWEN3_TEMPLATE)
assert flags["supports_tools"] is True
assert flags["supports_reasoning"] is True
def test_detect_safetensors_features_none_template_returns_all_false():
from routes.inference import _detect_safetensors_features
backend = SimpleNamespace(active_model_name = "unsloth/Qwen3-0.6B")
flags = _detect_safetensors_features(backend, None)
assert flags == {
"supports_reasoning": False,
"reasoning_style": "enable_thinking",
"reasoning_always_on": False,
"supports_preserve_thinking": False,
"supports_tools": False,
}
def test_detect_safetensors_features_gptoss_disables_tools():
"""gpt-oss Harmony: tools off even if template marks it."""
from routes.inference import _detect_safetensors_features
backend = MagicMock()
backend.active_model_name = "unsloth/gpt-oss-20b"
backend._is_gpt_oss_model.return_value = True
flags = _detect_safetensors_features(backend, QWEN3_TEMPLATE)
assert flags["supports_reasoning"] is True
assert flags["reasoning_style"] == "reasoning_effort"
assert flags["supports_tools"] is False
# Llama-3 / Mistral advertise tools but emit <|python_tag|> / [TOOL_CALLS],
# which our parser can't read. The route helper must not flip supports_tools=True
# for them, else the UI enables a pill the agentic loop can't honour.
LLAMA3_TEMPLATE = """
{%- if tools %}
{{- '<|start_header_id|>system<|end_header_id|>' }}
{{- 'You have access to the following tools.' }}
{%- for tool in tools %}
{{- tool | tojson }}
{%- endfor %}
{%- endif %}
{%- for message in messages %}
{%- if message.role == 'tool' %}
{{- '<|start_header_id|>ipython<|end_header_id|>' }}
{{- '<|python_tag|>' }}
{{- message.content }}
{%- endif %}
{%- endfor %}
"""
MISTRAL_TEMPLATE = """
{%- if tools %}
{%- for tool in tools %}
{{- tool | tojson }}
{%- endfor %}
{%- endif %}
{%- for message in messages %}
{%- if message.role == 'tool' %}
{{- '[TOOL_CALLS]' + message.content + '[/TOOL_CALLS]' }}
{%- endif %}
{%- endfor %}
"""
def test_detect_safetensors_features_llama3_template_suppresses_tools():
"""Llama-3 emits <|python_tag|>; safetensors loop cannot parse it."""
from routes.inference import _detect_safetensors_features
backend = SimpleNamespace(active_model_name = "unsloth/Llama-3.2-3B-Instruct")
flags = _detect_safetensors_features(backend, LLAMA3_TEMPLATE)
assert flags["supports_tools"] is False
def test_detect_safetensors_features_mistral_template_suppresses_tools():
"""Mistral emits [TOOL_CALLS]; safetensors loop cannot parse it."""
from routes.inference import _detect_safetensors_features
backend = SimpleNamespace(active_model_name = "unsloth/mistral-7b-instruct-v0.3")
flags = _detect_safetensors_features(backend, MISTRAL_TEMPLATE)
assert flags["supports_tools"] is False
def test_detect_safetensors_features_qwen_tool_call_keeps_tools_on():
"""Sanity check: gate only suppresses non-Qwen formats."""
from routes.inference import _detect_safetensors_features
backend = SimpleNamespace(active_model_name = "unsloth/Qwen3-0.6B")
flags = _detect_safetensors_features(backend, QWEN3_TEMPLATE)
assert flags["supports_tools"] is True
def test_detect_safetensors_features_function_xml_format_keeps_tools_on():
"""Templates emitting <function=name> XML are parser-compatible."""
from routes.inference import _detect_safetensors_features
tpl_with_function_xml = (
"{%- if tools %}<|im_start|>system\n"
"Tool call format: <function=name><parameter=k>v</parameter></function>"
"<|im_end|>{%- endif %}"
)
backend = SimpleNamespace(active_model_name = "custom/with-function-xml")
flags = _detect_safetensors_features(backend, tpl_with_function_xml)
assert flags["supports_tools"] is True
# Qwen3.5 family pin: the live GGUF + safetensors templates both wrap tool
# calls as ``<tool_call>\n<function=name>...``. Faithful slice so the
# classifier never silently regresses for this family.
QWEN35_TOOL_INSTRUCTION = (
"{%- if tools %}\n"
" <|im_start|>system\n"
" # Tools\n"
" <tools>\n"
" {%- for tool in tools %}{{ tool | tojson }}{%- endfor %}\n"
" </tools>\n"
" If you choose to call a function ONLY reply in the following format:\n"
" <tool_call>\n"
" <function=example_function_name>\n"
" <parameter=example_parameter_1>\n"
" value_1\n"
" </parameter>\n"
" </function>\n"
" </tool_call>\n"
" <|im_end|>\n"
"{%- endif %}\n"
"{%- if enable_thinking is defined and enable_thinking %}{{- '<think>' }}{%- endif %}\n"
)
def test_detect_safetensors_features_qwen35_keeps_tools_on():
"""unsloth/Qwen3.5-0.8B family must surface tools+reasoning on."""
from routes.inference import _detect_safetensors_features
backend = SimpleNamespace(active_model_name = "unsloth/Qwen3.5-0.8B")
flags = _detect_safetensors_features(backend, QWEN35_TOOL_INSTRUCTION)
assert flags["supports_tools"] is True
assert flags["supports_reasoning"] is True
assert flags["reasoning_style"] == "enable_thinking"
# ── Tests: IPC bridge contract ───────────────────────────────────────
def test_orchestrator_mirrors_chat_template_info_into_models_dict():
"""Worker → orchestrator copies chat_template_info verbatim."""
from core.inference.orchestrator import InferenceOrchestrator
orch = InferenceOrchestrator.__new__(InferenceOrchestrator)
orch.models = {}
orch.active_model_name = None
orch.loading_models = set()
model_info = {
"identifier": "unsloth/Qwen3-0.6B",
"display_name": "Qwen3-0.6B",
"is_vision": False,
"is_lora": False,
"is_gguf": False,
"is_audio": False,
"audio_type": None,
"has_audio_input": False,
"chat_template_info": {
"has_template": True,
"template": QWEN3_TEMPLATE,
"format_type": "chatml",
"template_name": "qwen3",
"special_tokens": {"bos_token": "<|im_start|>"},
},
}
# Replay orchestrator.load_model's mirror block.
orch.active_model_name = model_info["identifier"]
orch.models[orch.active_model_name] = {
"is_vision": model_info.get("is_vision", False),
"is_lora": model_info.get("is_lora", False),
"display_name": model_info.get("display_name", "x"),
"is_audio": model_info.get("is_audio", False),
"audio_type": model_info.get("audio_type"),
"has_audio_input": model_info.get("has_audio_input", False),
}
_tpl_info = model_info.get("chat_template_info")
if isinstance(_tpl_info, dict):
orch.models[orch.active_model_name]["chat_template_info"] = _tpl_info
entry = orch.models[orch.active_model_name]
tpl = entry.get("chat_template_info", {}).get("template")
assert tpl == QWEN3_TEMPLATE
from routes.inference import _detect_safetensors_features
flags = _detect_safetensors_features(
SimpleNamespace(active_model_name = orch.active_model_name), tpl
)
assert flags["supports_tools"] is True
assert flags["supports_reasoning"] is True
def test_orchestrator_missing_chat_template_info_falls_back_to_all_false():
"""Old / malformed worker reply: no crash, all flags False."""
from core.inference.orchestrator import InferenceOrchestrator
from routes.inference import _detect_safetensors_features
orch = InferenceOrchestrator.__new__(InferenceOrchestrator)
orch.models = {}
orch.active_model_name = "unsloth/Qwen3-0.6B"
model_info = {
"identifier": "unsloth/Qwen3-0.6B",
"is_vision": False,
"is_lora": False,
# NB: no chat_template_info key
}
orch.models[orch.active_model_name] = {
"is_vision": False,
"is_lora": False,
}
_tpl_info = model_info.get("chat_template_info")
if isinstance(_tpl_info, dict):
orch.models[orch.active_model_name]["chat_template_info"] = _tpl_info
entry = orch.models[orch.active_model_name]
tpl = entry.get("chat_template_info", {}).get("template")
assert tpl is None
flags = _detect_safetensors_features(
SimpleNamespace(active_model_name = orch.active_model_name), tpl
)
assert flags["supports_tools"] is False
def test_worker_load_reply_payload_includes_chat_template_info():
"""Worker IPC reply carries chat_template_info dict."""
class _StubBackend:
def __init__(self, identifier, template):
self.active_model_name = identifier
self.models = {
identifier: {
"chat_template_info": {
"has_template": True,
"template": template,
"format_type": "chatml",
"template_name": "qwen3",
"special_tokens": {"bos_token": "<|im_start|>"},
}
}
}
backend = _StubBackend("unsloth/Qwen3-0.6B", QWEN3_TEMPLATE)
mc = SimpleNamespace(
identifier = "unsloth/Qwen3-0.6B",
display_name = "Qwen3-0.6B",
is_vision = False,
is_lora = False,
)
# Replay the worker's payload-build block.
model_info = {
"identifier": mc.identifier,
"display_name": mc.display_name,
"is_vision": mc.is_vision,
"is_lora": mc.is_lora,
"is_gguf": False,
}
_bm = getattr(backend, "models", {}) or {}
_entry = _bm.get(mc.identifier) or _bm.get(getattr(backend, "active_model_name", None)) or {}
_tpl_info = _entry.get("chat_template_info")
if isinstance(_tpl_info, dict):
model_info["chat_template_info"] = {
"has_template": bool(_tpl_info.get("has_template", False)),
"template": _tpl_info.get("template"),
"format_type": _tpl_info.get("format_type", "generic"),
"template_name": _tpl_info.get("template_name"),
"special_tokens": _tpl_info.get("special_tokens", {}) or {},
}
assert "chat_template_info" in model_info
assert model_info["chat_template_info"]["template"] == QWEN3_TEMPLATE
assert model_info["chat_template_info"]["has_template"] is True
def test_worker_load_reply_payload_survives_missing_template():
"""Tokenizer with no chat_template still yields a valid reply."""
class _StubBackend:
def __init__(self):
self.active_model_name = "legacy/no-template"
self.models = {"legacy/no-template": {}} # no chat_template_info
backend = _StubBackend()
mc = SimpleNamespace(
identifier = "legacy/no-template",
display_name = "legacy",
is_vision = False,
is_lora = False,
)
model_info = {
"identifier": mc.identifier,
"display_name": mc.display_name,
"is_vision": mc.is_vision,
"is_lora": mc.is_lora,
"is_gguf": False,
}
_bm = getattr(backend, "models", {}) or {}
_entry = _bm.get(mc.identifier) or {}
_tpl_info = _entry.get("chat_template_info")
if isinstance(_tpl_info, dict):
model_info["chat_template_info"] = dict(_tpl_info)
assert "chat_template_info" not in model_info
# ── End-to-end: route layer sees the template, advertises True ───────
def test_route_layer_emits_supports_tools_true_for_qwen3_safetensors():
"""E2E: Qwen3 safetensors flips supports_tools=True."""
from routes.inference import _detect_safetensors_features
backend = SimpleNamespace(
active_model_name = "unsloth/Qwen3-0.6B",
models = {
"unsloth/Qwen3-0.6B": {
"is_vision": False,
"chat_template_info": {
"has_template": True,
"template": QWEN3_TEMPLATE,
"format_type": "chatml",
},
}
},
)
_model_info = backend.models.get(backend.active_model_name, {})
_tpl = _model_info.get("chat_template_info", {}).get("template")
flags = _detect_safetensors_features(backend, _tpl)
assert flags["supports_tools"] is True
assert flags["supports_reasoning"] is True
assert flags["supports_preserve_thinking"] is True