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.
250 lines
7.8 KiB
Python
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")
|