unsloth/studio/backend/tests/test_inference_model_validation.py
Daniel Han a9e5a80654 Address the round of Codex review findings on the merged diffusion phases
Memory planning and dense-quant path: size a local diffusers base's
resident companions from its on-disk VAE and text-encoder weights instead
of folding them to zero, feed the distilled variant hint into the runtime
headroom estimate so turbo and schnell models are not over-reserved, place
group-offload companions resident before attaching the transformer hooks
so a failed placement falls back to whole-module offload instead of
crashing, and bail out of the dense transformer download before it starts
when the requested quant scheme is unsupported so the load falls back to
GGUF cleanly.

sd.cpp stack: scrub the native path lease secret from sd-cli child env,
redact native load-progress errors, forward the resolved accelerator when
auto-installing a forced-native binary, release stale diffusion GPU
ownership on CPU-native loads, and remove the sd.cpp install tree on
uninstall.

Prequant and scripts: reject prequant artifacts missing base_model_id
when a base is requested, expanduser before checkpoint existence checks,
record and validate the int8 exclusion filter and fp8 fast-accum in
checkpoint metadata, make verify_prequant_backend allowlist its local
checkpoint and fail on missing or bad LPIPS and on load-peak regressions,
average only finite PSNR values in diffusion_quality, and reset the
process-wide attention backend between perf probe variants.

API and UI: normalize attention_backend casing before Literal validation,
close hidden popovers when leaving the Images page, and clear the stale
quant label when loading a direct local GGUF file.
2026-07-02 03:29:18 +00:00

250 lines
7.8 KiB
Python

# SPDX-License-Identifier: AGPL-3.0-only
# Copyright 2026-present the Unsloth AI Inc. team. All rights reserved.
import os
import sys
_backend = os.path.join(os.path.dirname(__file__), "..")
sys.path.insert(0, _backend)
from models.inference import LoadRequest
def _base_load_request(**overrides):
data = {
"model_path": "unsloth/test-model-GGUF",
"hf_token": None,
"max_seq_length": 4096,
"load_in_4bit": True,
"is_lora": False,
"gguf_variant": "Q4_K_M",
}
data.update(overrides)
return LoadRequest.model_validate(data)
def test_blank_chat_template_override_normalizes_to_none():
req = _base_load_request(chat_template_override = " \n\t")
assert req.chat_template_override is None
def test_nonblank_chat_template_override_is_preserved_verbatim():
template = " {{ messages }} "
req = _base_load_request(chat_template_override = template)
assert req.chat_template_override == template
# ---------- ChatCompletionRequest tool_call_id walkback ----------
from models.inference import ChatCompletionRequest
def _req(messages, **overrides):
payload = {"model": "x", "messages": messages, **overrides}
return ChatCompletionRequest.model_validate(payload)
def test_tool_message_inherits_id_from_prior_assistant_tool_call():
req = _req(
[
{"role": "user", "content": "what is 2+2"},
{
"role": "assistant",
"content": None,
"tool_calls": [
{
"id": "call_real123",
"type": "function",
"function": {"name": "calc", "arguments": "{}"},
}
],
},
{"role": "tool", "name": "calc", "content": "4"}, # no tool_call_id
]
)
assert req.messages[-1].tool_call_id == "call_real123"
def test_tool_message_with_explicit_id_unchanged():
req = _req(
[
{
"role": "assistant",
"content": None,
"tool_calls": [
{
"id": "call_a",
"type": "function",
"function": {"name": "search", "arguments": "{}"},
}
],
},
{"role": "tool", "tool_call_id": "call_user_supplied", "content": "ok"},
]
)
assert req.messages[-1].tool_call_id == "call_user_supplied"
def test_walkback_prefers_function_name_match():
req = _req(
[
{
"role": "assistant",
"content": None,
"tool_calls": [
{
"id": "call_x",
"type": "function",
"function": {"name": "search", "arguments": "{}"},
},
{
"id": "call_y",
"type": "function",
"function": {"name": "calc", "arguments": "{}"},
},
],
},
{"role": "tool", "name": "calc", "content": "4"},
]
)
assert req.messages[-1].tool_call_id == "call_y"
def test_walkback_takes_first_unconsumed_when_no_name():
req = _req(
[
{
"role": "assistant",
"content": None,
"tool_calls": [
{
"id": "call_a",
"type": "function",
"function": {"name": "calc", "arguments": "{}"},
},
{
"id": "call_b",
"type": "function",
"function": {"name": "search", "arguments": "{}"},
},
],
},
{"role": "tool", "content": "first result"},
{"role": "tool", "content": "second result"},
]
)
assert req.messages[-2].tool_call_id == "call_a"
assert req.messages[-1].tool_call_id == "call_b"
def test_walkback_falls_back_to_synth_when_no_assistant_turn():
req = _req(
[
{"role": "user", "content": "hi"},
{"role": "tool", "content": "orphan"},
]
)
tcid = req.messages[-1].tool_call_id
assert tcid is not None and tcid.startswith("call_") and len(tcid) > 5
def test_walkback_does_not_cross_user_turn():
req = _req(
[
{
"role": "assistant",
"content": None,
"tool_calls": [
{
"id": "old_call",
"type": "function",
"function": {"name": "calc", "arguments": "{}"},
}
],
},
{"role": "tool", "tool_call_id": "old_call", "content": "4"},
{"role": "user", "content": "next turn"},
{"role": "tool", "content": "no parent in this turn"},
]
)
last = req.messages[-1].tool_call_id
# Walkback must NOT pick old_call across a user turn; falls back to synth.
assert last is not None
assert last != "old_call"
assert last.startswith("call_")
def test_walkback_skips_explicitly_consumed_tool_call_id():
"""An explicit-id tool result reserves its assistant slot so a
follow-up missing-id result picks the OTHER tool call."""
req = _req(
[
{
"role": "assistant",
"content": None,
"tool_calls": [
{
"id": "call_a",
"type": "function",
"function": {"name": "calc", "arguments": "{}"},
},
{
"id": "call_b",
"type": "function",
"function": {"name": "search", "arguments": "{}"},
},
],
},
{"role": "tool", "tool_call_id": "call_a", "content": "4"},
{"role": "tool", "content": "second result"},
]
)
assert [m.tool_call_id for m in req.messages if m.role == "tool"] == ["call_a", "call_b"]
def test_walkback_handles_malformed_function_string():
"""A tool_call with ``function`` as a string (provider quirk) must not
raise; resolution falls back to id selection."""
req = _req(
[
{
"role": "assistant",
"content": None,
"tool_calls": [
{"id": "call_a", "type": "function", "function": "calc"},
],
},
{"role": "tool", "name": "calc", "content": "4"},
]
)
assert req.messages[-1].tool_call_id == "call_a"
# ── DiffusionLoadRequest.attention_backend casing (Literal validated before normalizer) ──
import pytest
from pydantic import ValidationError
from models.inference import DiffusionLoadRequest
def _diff_load(**kw):
return DiffusionLoadRequest(model_path = "repo", gguf_filename = "m.gguf", **kw)
def test_attention_backend_casing_and_whitespace_normalized():
# The dispatcher accepts case/whitespace variants; the before-validator must fold them so
# the lowercase Literal does not 422 an otherwise-valid request.
assert _diff_load(attention_backend = "CuDNN").attention_backend == "cudnn"
assert _diff_load(attention_backend = " sage ").attention_backend == "sage"
def test_attention_backend_none_preserved():
assert _diff_load(attention_backend = None).attention_backend is None
assert _diff_load().attention_backend is None
def test_attention_backend_unknown_still_rejected():
with pytest.raises(ValidationError):
_diff_load(attention_backend = "bogus")