mirror of
https://github.com/unslothai/unsloth.git
synced 2026-07-10 00:08:58 +00:00
* fix(studio/llama_cpp): disable trust_env on the loopback health probe _wait_for_health() polls http://127.0.0.1:<port>/health with the default httpx trust_env=True, so an ambient HTTP(S)_PROXY in the environment is applied to the loopback request. A proxy that returns 503 for 127.0.0.1 makes every probe fail, so the loop runs until timeout and Studio load hangs (trust_env=False returns 200 immediately). Pass trust_env=False so the local readiness probe never goes through a proxy. This mirrors the existing trust_env=False handling in the sibling llama_http / external_provider HTTP clients. * test(offline_gguf_cache): accept trust_env kwarg in fake_get mock _wait_for_health now calls httpx.get(..., trust_env=False); update the retry test's fake_get to accept the kwarg so it doesn't raise TypeError. * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * fix(studio/llama_cpp): bypass proxies for loopback clients * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * fix(studio/routes): bypass proxies for llama streams * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: wasimysaid <wasimysdev@gmail.com>
260 lines
8.5 KiB
Python
260 lines
8.5 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
|
|
|
|
"""Tests for the post-launch /props context readback.
|
|
|
|
llama-server's memory-fit step or --parallel slot split can allocate less
|
|
context than the requested -c while Studio keeps advertising the requested
|
|
value; clients sized to it then die on exceed_context_size_error 400s.
|
|
``_reconcile_effective_ctx_with_server`` must adopt the server's real
|
|
``default_generation_settings.n_ctx`` whenever it is smaller.
|
|
|
|
Stubbed httpx; no subprocess, GPU, or network. Cross-platform.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
import sys
|
|
import types as _types
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Stub heavy/unavailable deps before importing the module under test.
|
|
# Mirrors test_llama_cpp_context_fit.py.
|
|
# ---------------------------------------------------------------------------
|
|
|
|
_BACKEND_DIR = str(Path(__file__).resolve().parent.parent)
|
|
if _BACKEND_DIR not in sys.path:
|
|
sys.path.insert(0, _BACKEND_DIR)
|
|
|
|
# Prefer the real modules so importing this file first cannot poison later
|
|
# test modules with stubs; only stub what the environment genuinely lacks.
|
|
try:
|
|
import loggers # noqa: F401
|
|
except ImportError:
|
|
_loggers_stub = _types.ModuleType("loggers")
|
|
_loggers_stub.get_logger = lambda name: __import__("logging").getLogger(name)
|
|
sys.modules.setdefault("loggers", _loggers_stub)
|
|
|
|
try:
|
|
import structlog # noqa: F401
|
|
except ImportError:
|
|
sys.modules.setdefault("structlog", _types.ModuleType("structlog"))
|
|
|
|
try:
|
|
import httpx # noqa: F401
|
|
except ImportError:
|
|
_httpx_stub = _types.ModuleType("httpx")
|
|
for _exc_name in (
|
|
"ConnectError",
|
|
"TimeoutException",
|
|
"ReadTimeout",
|
|
"ReadError",
|
|
"RemoteProtocolError",
|
|
"CloseError",
|
|
"WriteError",
|
|
"HTTPError",
|
|
):
|
|
setattr(_httpx_stub, _exc_name, type(_exc_name, (Exception,), {}))
|
|
|
|
class _FakeTimeout:
|
|
def __init__(self, *a, **kw):
|
|
pass
|
|
|
|
_httpx_stub.Timeout = _FakeTimeout
|
|
_httpx_stub.Client = type(
|
|
"Client",
|
|
(),
|
|
{
|
|
"__init__": lambda self, **kw: None,
|
|
"__enter__": lambda self: self,
|
|
"__exit__": lambda self, *a: None,
|
|
},
|
|
)
|
|
_httpx_stub.get = lambda *a, **kw: (_ for _ in ()).throw(RuntimeError("unstubbed httpx.get"))
|
|
sys.modules.setdefault("httpx", _httpx_stub)
|
|
|
|
from core.inference.llama_cpp import LlamaCppBackend
|
|
import core.inference.llama_cpp as llama_cpp_mod
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Helpers
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
class _FakeResponse:
|
|
def __init__(
|
|
self,
|
|
status_code = 200,
|
|
body = None,
|
|
):
|
|
self.status_code = status_code
|
|
self._body = body or {}
|
|
|
|
def json(self):
|
|
return self._body
|
|
|
|
|
|
def _make_backend(effective_ctx = 98304, port = 51234):
|
|
inst = LlamaCppBackend.__new__(LlamaCppBackend)
|
|
inst._port = port
|
|
inst._effective_context_length = effective_ctx
|
|
inst._context_length = 262144
|
|
return inst
|
|
|
|
|
|
def _stub_props(
|
|
monkeypatch,
|
|
status_code = 200,
|
|
body = None,
|
|
exc = None,
|
|
):
|
|
def fake_get(
|
|
url,
|
|
timeout = None,
|
|
trust_env = None,
|
|
):
|
|
assert url.endswith("/props")
|
|
|
|
assert trust_env is False
|
|
if exc is not None:
|
|
raise exc
|
|
return _FakeResponse(status_code, body)
|
|
|
|
monkeypatch.setattr(llama_cpp_mod.httpx, "get", fake_get, raising = False)
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# _query_server_n_ctx parsing
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
def test_query_n_ctx_reads_default_generation_settings(monkeypatch):
|
|
_stub_props(
|
|
monkeypatch,
|
|
body = {"default_generation_settings": {"n_ctx": 67584}},
|
|
)
|
|
assert _make_backend()._query_server_n_ctx() == 67584
|
|
|
|
|
|
def test_query_n_ctx_non_200_returns_none(monkeypatch):
|
|
_stub_props(monkeypatch, status_code = 503)
|
|
assert _make_backend()._query_server_n_ctx() is None
|
|
|
|
|
|
def test_query_n_ctx_missing_key_returns_none(monkeypatch):
|
|
_stub_props(monkeypatch, body = {"default_generation_settings": {}})
|
|
assert _make_backend()._query_server_n_ctx() is None
|
|
|
|
|
|
def test_query_n_ctx_swallows_transport_errors(monkeypatch):
|
|
_stub_props(monkeypatch, exc = RuntimeError("connection refused"))
|
|
assert _make_backend()._query_server_n_ctx() is None
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# _reconcile_effective_ctx_with_server decisions
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
def test_fit_shrunk_ctx_overwrites_advertised_value(monkeypatch):
|
|
"""The Nick repro: requested/advertised 98304, server really at 67584."""
|
|
inst = _make_backend(effective_ctx = 98304)
|
|
_stub_props(
|
|
monkeypatch,
|
|
body = {"default_generation_settings": {"n_ctx": 67584}},
|
|
)
|
|
inst._reconcile_effective_ctx_with_server()
|
|
assert inst._effective_context_length == 67584
|
|
assert inst.context_length == 67584
|
|
|
|
|
|
def test_matching_ctx_is_left_alone(monkeypatch):
|
|
inst = _make_backend(effective_ctx = 98304)
|
|
_stub_props(
|
|
monkeypatch,
|
|
body = {"default_generation_settings": {"n_ctx": 98304}},
|
|
)
|
|
inst._reconcile_effective_ctx_with_server()
|
|
assert inst._effective_context_length == 98304
|
|
|
|
|
|
def test_larger_server_ctx_does_not_inflate_advertised_value(monkeypatch):
|
|
"""Never advertise more than the user asked for, even if the server could."""
|
|
inst = _make_backend(effective_ctx = 32768)
|
|
_stub_props(
|
|
monkeypatch,
|
|
body = {"default_generation_settings": {"n_ctx": 65536}},
|
|
)
|
|
inst._reconcile_effective_ctx_with_server()
|
|
assert inst._effective_context_length == 32768
|
|
|
|
|
|
def test_unset_effective_ctx_adopts_server_value(monkeypatch):
|
|
inst = _make_backend(effective_ctx = None)
|
|
inst._context_length = None
|
|
_stub_props(
|
|
monkeypatch,
|
|
body = {"default_generation_settings": {"n_ctx": 40960}},
|
|
)
|
|
inst._reconcile_effective_ctx_with_server()
|
|
assert inst._effective_context_length == 40960
|
|
|
|
|
|
def test_props_failure_keeps_studio_value(monkeypatch):
|
|
"""A flaky /props must never wipe the computed context."""
|
|
inst = _make_backend(effective_ctx = 98304)
|
|
_stub_props(monkeypatch, exc = RuntimeError("boom"))
|
|
inst._reconcile_effective_ctx_with_server()
|
|
assert inst._effective_context_length == 98304
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# _ctx_integrity_flags: keep the per-request window equal to the advertised ctx
|
|
# ---------------------------------------------------------------------------
|
|
|
|
_CAPS_ALL = {"supports_kv_unified": True, "supports_fit_ctx": True}
|
|
_CAPS_NONE = {"supports_kv_unified": False, "supports_fit_ctx": False}
|
|
|
|
|
|
def test_kv_unified_added_for_multi_slot():
|
|
"""Explicit --parallel N disables llama-server's auto-slots kv-unified
|
|
default, splitting -c into per-slot windows of -c/N; Studio must restore
|
|
the shared pool so one request can use the full advertised context."""
|
|
flags = LlamaCppBackend._ctx_integrity_flags(4, False, 98304, 98304, _CAPS_ALL)
|
|
assert "--kv-unified" in flags
|
|
|
|
|
|
def test_kv_unified_skipped_for_single_slot_or_old_build():
|
|
assert "--kv-unified" not in LlamaCppBackend._ctx_integrity_flags(
|
|
1, False, 98304, 98304, _CAPS_ALL
|
|
)
|
|
assert "--kv-unified" not in LlamaCppBackend._ctx_integrity_flags(
|
|
4, False, 98304, 98304, _CAPS_NONE
|
|
)
|
|
|
|
|
|
def test_fit_ctx_floors_explicit_request_under_fit():
|
|
flags = LlamaCppBackend._ctx_integrity_flags(1, True, 98304, 98304, _CAPS_ALL)
|
|
assert flags[flags.index("--fit-ctx") + 1] == "98304"
|
|
|
|
|
|
def test_fit_ctx_skipped_without_fit_or_explicit_ctx_or_support():
|
|
assert "--fit-ctx" not in LlamaCppBackend._ctx_integrity_flags(
|
|
1, False, 98304, 98304, _CAPS_ALL
|
|
)
|
|
assert "--fit-ctx" not in LlamaCppBackend._ctx_integrity_flags(1, True, 0, 262144, _CAPS_ALL)
|
|
assert "--fit-ctx" not in LlamaCppBackend._ctx_integrity_flags(
|
|
1, True, 98304, 98304, _CAPS_NONE
|
|
)
|
|
|
|
|
|
def test_probe_missing_binary_reports_new_capabilities_false():
|
|
info = LlamaCppBackend.probe_server_capabilities(binary = "/nonexistent/llama-server")
|
|
assert info["found"] is False
|
|
assert info["supports_kv_unified"] is False
|
|
assert info["supports_fit_ctx"] is False
|