tag"),
+ self._chunk(finish_reason = "stop"),
+ ],
)
texts = [
e["delta"]["text"]
for e in events
- if e.get("type") == "content_block_delta" and e["delta"]["type"] == "text_delta"
+ if e.get("type") == "content_block_delta"
+ and e["delta"]["type"] == "text_delta"
]
assert "".join(texts) == "use the
tag"
(message_delta,) = [e for e in events if e.get("type") == "message_delta"]
@@ -1066,7 +1118,8 @@ class TestAnthropicEmitterHealing:
texts = [
e
for e in events
- if e.get("type") == "content_block_delta" and e["delta"]["type"] == "text_delta"
+ if e.get("type") == "content_block_delta"
+ and e["delta"]["type"] == "text_delta"
]
assert texts == []
@@ -1097,14 +1150,18 @@ class TestAnthropicEmitterHealing:
texts = [
e["delta"]["text"]
for e in events
- if e.get("type") == "content_block_delta" and e["delta"]["type"] == "text_delta"
+ if e.get("type") == "content_block_delta"
+ and e["delta"]["type"] == "text_delta"
]
assert "".join(texts) == "held {"name":"lookup","arguments":{"q":"y"}}'
+ two = (
+ LOOKUP_XML
+ + '{"name":"lookup","arguments":{"q":"y"}}'
+ )
events = self._events(
self._emitter(disable_parallel_tool_use = True),
[self._chunk(content = two), self._chunk(finish_reason = "stop")],
@@ -1112,7 +1169,8 @@ class TestAnthropicEmitterHealing:
starts = [
e
for e in events
- if e.get("type") == "content_block_start" and e["content_block"]["type"] == "tool_use"
+ if e.get("type") == "content_block_start"
+ and e["content_block"]["type"] == "tool_use"
]
assert len(starts) == 1
@@ -1138,7 +1196,8 @@ class TestAnthropicEmitterHealing:
starts = [
e
for e in events
- if e.get("type") == "content_block_start" and e["content_block"]["type"] == "tool_use"
+ if e.get("type") == "content_block_start"
+ and e["content_block"]["type"] == "tool_use"
]
assert len(starts) == 1
@@ -1153,7 +1212,8 @@ class TestAnthropicEmitterHealing:
texts = [
e["delta"]["text"]
for e in events
- if e.get("type") == "content_block_delta" and e["delta"]["type"] == "text_delta"
+ if e.get("type") == "content_block_delta"
+ and e["delta"]["type"] == "text_delta"
]
assert "".join(texts) == LOOKUP_XML
@@ -1229,7 +1289,9 @@ class TestAnthropicNonStreamingRoute:
# stays in the text block (the legacy strip must not run after a
# span-exact heal), matching the OpenAI passthrough.
rogue = '{"name":"rogue","arguments":{}}'
- _, data = await self._drive(monkeypatch, [_upstream_message(f"{LOOKUP_XML} {rogue}")])
+ _, data = await self._drive(
+ monkeypatch, [_upstream_message(f"{LOOKUP_XML} {rogue}")]
+ )
(tool_block,) = [b for b in data["content"] if b["type"] == "tool_use"]
assert tool_block["name"] == "lookup"
(text_block,) = [b for b in data["content"] if b["type"] == "text"]
@@ -1269,7 +1331,12 @@ class TestAnthropicNonStreamingRoute:
class TestOpenaiStreamingRoute:
def test_heals_streamed_xml(self, monkeypatch):
async def _run():
- pieces = ["", '{"name":"lookup",', '"arguments":{"q":"x"}}', ""]
+ pieces = [
+ "",
+ '{"name":"lookup",',
+ '"arguments":{"q":"x"}}',
+ "",
+ ]
lines = [
'data: {"id":"c1","model":"gguf","created":1,"choices":[{"index":0,"delta":{"content":%s}}]}'
% json.dumps(p)
@@ -1326,7 +1393,9 @@ class TestOpenaiStreamingRoute:
'data: {"id":"c1","choices":[{"index":0,"delta":{},"finish_reason":"tool_calls"}]}',
"data: [DONE]",
]
- chunks = await _drive_stream(monkeypatch, _payload(parallel_tool_calls = False), lines)
+ chunks = await _drive_stream(
+ monkeypatch, _payload(parallel_tool_calls = False), lines
+ )
payloads = _stream_payloads(chunks)
tool_deltas = [
tc
@@ -1443,7 +1512,10 @@ class TestHealerSignalAlignment:
def test_bracket_tool_calls_still_promote_in_stream(self):
healer = StreamToolCallHealer({"web_search"})
- events = healer.feed('[TOOL_CALLS]web_search{"query": "unsloth docs"}') + healer.finalize()
+ events = (
+ healer.feed('[TOOL_CALLS]web_search{"query": "unsloth docs"}')
+ + healer.finalize()
+ )
(call,) = _events_calls(events)
assert call["function"]["name"] == "web_search"
assert healer.healed
diff --git a/studio/backend/tests/test_password_prompt.py b/studio/backend/tests/test_password_prompt.py
index 372d6a2aa4..21012d2a1d 100644
--- a/studio/backend/tests/test_password_prompt.py
+++ b/studio/backend/tests/test_password_prompt.py
@@ -177,7 +177,9 @@ def test_loop_success_applies_once(monkeypatch):
def test_loop_short_password_reprompts(monkeypatch):
- ok, applied, out = _run_loop(monkeypatch, _keys("short", "long-enough-pw", "long-enough-pw"))
+ ok, applied, out = _run_loop(
+ monkeypatch, _keys("short", "long-enough-pw", "long-enough-pw")
+ )
assert ok is True
assert applied == ["long-enough-pw"]
assert "at least 8 characters" in out
@@ -306,7 +308,9 @@ def test_resolve_supplied_password_env(monkeypatch):
def test_resolve_supplied_password_literal_beats_env(monkeypatch):
import io
monkeypatch.setenv(tp.SUPPLIED_PASSWORD_ENV, "env-secret-pw")
- assert tp.resolve_supplied_password("cli-wins-pw", out = io.StringIO()) == "cli-wins-pw"
+ assert (
+ tp.resolve_supplied_password("cli-wins-pw", out = io.StringIO()) == "cli-wins-pw"
+ )
def test_resolve_supplied_password_stdin_beats_env(monkeypatch):
diff --git a/studio/backend/tests/test_password_prompt_backstop.py b/studio/backend/tests/test_password_prompt_backstop.py
index 3c2c1956f9..095ce81983 100644
--- a/studio/backend/tests/test_password_prompt_backstop.py
+++ b/studio/backend/tests/test_password_prompt_backstop.py
@@ -90,7 +90,9 @@ def _patch_seeded_admin(monkeypatch, *, requires_change: bool) -> None:
# The gate seeds the admin row itself (it can run before lifespan startup);
# tests fake both the seeding no-op and the flag.
monkeypatch.setattr(auth_storage, "ensure_default_admin", lambda: False)
- monkeypatch.setattr(auth_storage, "requires_password_change", lambda u: requires_change)
+ monkeypatch.setattr(
+ auth_storage, "requires_password_change", lambda u: requires_change
+ )
def test_gate_skips_when_tunnel_off(monkeypatch):
@@ -100,7 +102,10 @@ def test_gate_skips_when_tunnel_off(monkeypatch):
monkeypatch.setattr(auth_storage, "requires_password_change", _boom)
monkeypatch.setattr(auth_storage, "ensure_default_admin", _boom)
- assert run._terminal_password_gate(tunnel_will_start = False, **_GATE_KWARGS) == (True, False)
+ assert run._terminal_password_gate(tunnel_will_start = False, **_GATE_KWARGS) == (
+ True,
+ False,
+ )
def test_gate_skips_when_password_already_changed(monkeypatch):
@@ -111,7 +116,10 @@ def test_gate_skips_when_password_already_changed(monkeypatch):
"prompt_for_password_change",
lambda **k: pytest.fail("prompt must not run when no change is required"),
)
- assert run._terminal_password_gate(tunnel_will_start = True, **_GATE_KWARGS) == (True, False)
+ assert run._terminal_password_gate(tunnel_will_start = True, **_GATE_KWARGS) == (
+ True,
+ False,
+ )
def test_gate_warns_and_proceeds_without_tty_when_deadline_arms(monkeypatch):
@@ -124,7 +132,10 @@ def test_gate_warns_and_proceeds_without_tty_when_deadline_arms(monkeypatch):
lambda **k: pytest.fail("prompt must not run without a tty"),
)
# Proceeds, but the public HTML must not auto-fill the default credential.
- assert run._terminal_password_gate(tunnel_will_start = True, **_GATE_KWARGS) == (True, True)
+ assert run._terminal_password_gate(tunnel_will_start = True, **_GATE_KWARGS) == (
+ True,
+ True,
+ )
out = stderr.getvalue()
assert "default admin password is still active" in out
assert "UNSLOTH_STUDIO_BOOTSTRAP_TIMEOUT" in out
@@ -144,7 +155,10 @@ def test_gate_fails_closed_without_tty_when_deadline_cannot_arm(monkeypatch):
kwargs = dict(_GATE_KWARGS)
kwargs["api_only"] = True
kwargs["frontend_served"] = False
- assert run._terminal_password_gate(tunnel_will_start = True, **kwargs) == (False, False)
+ assert run._terminal_password_gate(tunnel_will_start = True, **kwargs) == (
+ False,
+ False,
+ )
assert "Refusing to publish" in stderr.getvalue()
@@ -152,7 +166,10 @@ def test_gate_fails_closed_without_tty_when_deadline_disabled(monkeypatch):
stderr = _patch_streams(monkeypatch, tty = False)
_patch_seeded_admin(monkeypatch, requires_change = True)
monkeypatch.setenv("UNSLOTH_STUDIO_BOOTSTRAP_TIMEOUT", "0")
- assert run._terminal_password_gate(tunnel_will_start = True, **_GATE_KWARGS) == (False, False)
+ assert run._terminal_password_gate(tunnel_will_start = True, **_GATE_KWARGS) == (
+ False,
+ False,
+ )
assert "Refusing to publish" in stderr.getvalue()
@@ -163,14 +180,22 @@ def test_gate_treats_broken_streams_as_non_interactive(monkeypatch):
monkeypatch.setattr(sys, "stderr", stderr)
_patch_seeded_admin(monkeypatch, requires_change = True)
monkeypatch.delenv("UNSLOTH_STUDIO_BOOTSTRAP_TIMEOUT", raising = False)
- assert run._terminal_password_gate(tunnel_will_start = True, **_GATE_KWARGS) == (True, True)
+ assert run._terminal_password_gate(tunnel_will_start = True, **_GATE_KWARGS) == (
+ True,
+ True,
+ )
def test_gate_refusal_fails_closed(monkeypatch):
_patch_streams(monkeypatch, tty = True)
_patch_seeded_admin(monkeypatch, requires_change = True)
- monkeypatch.setattr(terminal_prompt, "prompt_for_password_change", lambda **k: False)
- assert run._terminal_password_gate(tunnel_will_start = True, **_GATE_KWARGS) == (False, False)
+ monkeypatch.setattr(
+ terminal_prompt, "prompt_for_password_change", lambda **k: False
+ )
+ assert run._terminal_password_gate(tunnel_will_start = True, **_GATE_KWARGS) == (
+ False,
+ False,
+ )
def test_gate_success_applies_route_equivalent_change(monkeypatch):
@@ -197,12 +222,17 @@ def test_gate_success_applies_route_equivalent_change(monkeypatch):
return True
monkeypatch.setattr(terminal_prompt, "prompt_for_password_change", _fake_prompt)
- assert run._terminal_password_gate(tunnel_will_start = True, **_GATE_KWARGS) == (True, True)
+ assert run._terminal_password_gate(tunnel_will_start = True, **_GATE_KWARGS) == (
+ True,
+ True,
+ )
admin = auth_storage.DEFAULT_ADMIN_USERNAME
# One atomic call: refresh tokens revoked in the same transaction as the
# password commit (a separable follow-up delete can fail and leave a
# pre-change refresh token able to mint access tokens).
- assert calls == [("update", admin, "brand-new-password", {"revoke_refresh_tokens": True})]
+ assert calls == [
+ ("update", admin, "brand-new-password", {"revoke_refresh_tokens": True})
+ ]
# ── ordering inside run_server (source-level, repo convention) ───────
@@ -276,7 +306,9 @@ def test_clear_bootstrap_password_truncates_when_unlink_fails(monkeypatch, tmp_p
assert auth_storage._load_bootstrap_password() is None
-def test_clear_bootstrap_password_warns_truthfully_when_not_cleared(monkeypatch, tmp_path, capsys):
+def test_clear_bootstrap_password_warns_truthfully_when_not_cleared(
+ monkeypatch, tmp_path, capsys
+):
# If the file can be neither unlinked NOR truncated, the stale plaintext stays
# on disk. The warning must NOT claim it was made unreusable (Codex 3571888584):
# it must say it could not be cleared and ask the user to remove it manually.
@@ -331,9 +363,13 @@ def _seed_stub_admin(
salt, pwd_hash = hashing.hash_password(bootstrap_pw)
monkeypatch.setattr(auth_storage, "ensure_default_admin", lambda: False)
- monkeypatch.setattr(auth_storage, "requires_password_change", lambda u: requires_change)
monkeypatch.setattr(
- auth_storage, "get_user_and_secret", lambda u: (salt, pwd_hash, "jwt", requires_change)
+ auth_storage, "requires_password_change", lambda u: requires_change
+ )
+ monkeypatch.setattr(
+ auth_storage,
+ "get_user_and_secret",
+ lambda u: (salt, pwd_hash, "jwt", requires_change),
)
calls = []
monkeypatch.setattr(
@@ -377,7 +413,9 @@ def test_apply_supplied_password_too_short_fails_closed(monkeypatch):
def test_apply_supplied_password_must_differ_fails_closed(monkeypatch):
- calls = _seed_stub_admin(monkeypatch, requires_change = True, bootstrap_pw = "bootstrap-secret")
+ calls = _seed_stub_admin(
+ monkeypatch, requires_change = True, bootstrap_pw = "bootstrap-secret"
+ )
monkeypatch.setenv(terminal_prompt.SUPPLIED_PASSWORD_ENV, "bootstrap-secret")
with pytest.raises(SystemExit) as exc:
run._apply_supplied_password(None)
diff --git a/studio/backend/tests/test_permission_mode.py b/studio/backend/tests/test_permission_mode.py
index 4fc64a6291..533bded092 100644
--- a/studio/backend/tests/test_permission_mode.py
+++ b/studio/backend/tests/test_permission_mode.py
@@ -233,7 +233,10 @@ def _clear_pending():
("grep -R TOKEN ~/logs", True), # tilde-home recursive root escapes
("cat /etc/pass{w,}d", True), # brace expansion builds /etc/passwd
("cat report{1,2}.txt", False), # benign brace stays safe
- ("cat /e{t,}c/pass?d", True), # brace-expanded candidate then a glob resolves it
+ (
+ "cat /e{t,}c/pass?d",
+ True,
+ ), # brace-expanded candidate then a glob resolves it
("cat /et{c,}/pass?d", True), # brace + glob in the tail
("cat repo/d{1,2}/f?.txt", False), # benign brace + glob stays safe
("cat /etc/pass${x:-wd}", True), # default param expansion builds path
@@ -269,7 +272,10 @@ def _clear_pending():
("g=abc; cat /$g/readme", False), # benign assigned path stays safe
("cat /etc/pass[[:lower:]]d", True), # POSIX class glob builds /etc/passwd
("x=passwd; p=x; cat /etc/${!p}", True), # indirect expansion builds path
- ("x=notes; p=x; cat /home/${!p}", False), # benign indirect expansion stays safe
+ (
+ "x=notes; p=x; cat /home/${!p}",
+ False,
+ ), # benign indirect expansion stays safe
("cat Report
Summary
") is False
assert (
- rh("") is False
+ rh(
+ ""
+ )
+ is False
)
assert rh("") is False
assert rh("") is False
@@ -974,10 +1142,16 @@ def test_render_html_gated_only_when_networked():
# see (a module worker from a CORS CDN, or a blob/same-origin worker that
# fetches/importScripts) under worker-src http: https: blob:, so they ask.
assert rh("") is True
- assert rh("") is True
+ assert (
+ rh("") is True
+ )
assert rh("") is True
- assert rh("") is False # not a ctor
- assert rh("") is False # unrelated class, not a real Worker
+ assert (
+ rh("") is False
+ ) # not a ctor
+ assert (
+ rh("") is False
+ ) # unrelated class, not a real Worker
# Resource-loading forms beyond a direct fetch also reach the network.
assert rh("") is True
assert rh("") is True
@@ -1004,7 +1178,9 @@ def test_render_html_gated_only_when_networked():
# A meta-refresh with a url navigates the frame to an external origin.
assert rh('') is True
assert rh("") is True
- assert rh('') is False # self-reload, no url
+ assert (
+ rh('') is False
+ ) # self-reload, no url
assert rh('
Hi
') is False # ordinary meta stays safe
@@ -1066,7 +1242,10 @@ def test_is_always_safe_tool():
("get_primary_key", False), # a schema key is not a credential
("search_keyboard_shortcuts", False), # 'key' inside another word stays safe
("list_bookmarks", False), # 'mark' substring in a token stays safe
- ("list_notifications", False), # 'notify' is a different token than 'notifications'
+ (
+ "list_notifications",
+ False,
+ ), # 'notify' is a different token than 'notifications'
],
)
def test_mcp_classifier(tool, unsafe):
@@ -1084,7 +1263,9 @@ def test_mcp_classifier(tool, unsafe):
({"name": "AWS_SECRET_ACCESS_KEY"}, True),
({"key": "DATABASE_PASSWORD"}, True),
(
- {"url": "http://169.254.169.254/latest/meta-data/iam/security-credentials/"},
+ {
+ "url": "http://169.254.169.254/latest/meta-data/iam/security-credentials/"
+ },
True,
), # AWS instance-metadata host
(
@@ -1112,74 +1293,161 @@ def test_mcp_sensitive_arguments(args, unsafe):
({"query": "UPDATE t SET x=1"}, True),
({"query": "INSERT INTO t VALUES (1)"}, True),
({"query": "SELECT * FROM runs"}, False), # read query stays safe
- ({"query": "how to delete old files"}, False), # NL text with 'delete' stays safe
- ({"query": "find the created_at column"}, False), # 'created' substring stays safe
+ (
+ {"query": "how to delete old files"},
+ False,
+ ), # NL text with 'delete' stays safe
+ (
+ {"query": "find the created_at column"},
+ False,
+ ), # 'created' substring stays safe
({"query": "DELETE/**/FROM runs"}, True), # inline SQL comment as whitespace
({"query": "UPDATE/**/t SET x=1"}, True),
({"query": "DROP/**/TABLE users"}, True),
- ({"query": "SELECT * FROM runs -- delete later"}, False), # trailing comment stays safe
+ (
+ {"query": "SELECT * FROM runs -- delete later"},
+ False,
+ ), # trailing comment stays safe
({"query": "COPY users FROM '/tmp/u.csv'"}, True), # bulk load writes the table
({"query": "COPY users (id, name)\nFROM STDIN"}, True), # multiline COPY FROM
- ({"query": "COPY (SELECT 1) TO '/tmp/o.csv'"}, True), # COPY TO writes a server file
- ({"query": "SELECT copy_count FROM t"}, False), # 'copy' substring column stays safe
+ (
+ {"query": "COPY (SELECT 1) TO '/tmp/o.csv'"},
+ True,
+ ), # COPY TO writes a server file
+ (
+ {"query": "SELECT copy_count FROM t"},
+ False,
+ ), # 'copy' substring column stays safe
({"query": "mutation { deleteIssue(id: 1) }"}, True), # GraphQL mutation
- ({"query": "mutation DelIssue { deleteIssue(id: 1) }"}, True), # named GraphQL mutation
- ({"query": "mutation # note\n { deleteIssue(id: 1) }"}, True), # comment before body
- ({"query": "mutation # c\n Del { deleteIssue(id: 1) }"}, True), # comment before name
- ({"query": "query { issue(id: 1) { title } }"}, False), # GraphQL read query stays safe
- ({"query": "{ issue(id: 1) { title } }"}, False), # shorthand GraphQL query stays safe
- ({"query": "query # note\n { issue(id: 1) }"}, False), # commented read query stays safe
- ({"query": "CREATE OR REPLACE VIEW v AS SELECT 1"}, True), # DDL with a modifier
+ (
+ {"query": "mutation DelIssue { deleteIssue(id: 1) }"},
+ True,
+ ), # named GraphQL mutation
+ (
+ {"query": "mutation # note\n { deleteIssue(id: 1) }"},
+ True,
+ ), # comment before body
+ (
+ {"query": "mutation # c\n Del { deleteIssue(id: 1) }"},
+ True,
+ ), # comment before name
+ (
+ {"query": "query { issue(id: 1) { title } }"},
+ False,
+ ), # GraphQL read query stays safe
+ (
+ {"query": "{ issue(id: 1) { title } }"},
+ False,
+ ), # shorthand GraphQL query stays safe
+ (
+ {"query": "query # note\n { issue(id: 1) }"},
+ False,
+ ), # commented read query stays safe
+ (
+ {"query": "CREATE OR REPLACE VIEW v AS SELECT 1"},
+ True,
+ ), # DDL with a modifier
({"query": "CREATE UNIQUE INDEX idx ON t(x)"}, True), # DDL with UNIQUE
({"query": "CREATE TEMP TABLE t (id int)"}, True), # DDL with TEMP
- ({"query": "CREATE MATERIALIZED VIEW mv AS SELECT 1"}, True), # materialized view DDL
+ (
+ {"query": "CREATE MATERIALIZED VIEW mv AS SELECT 1"},
+ True,
+ ), # materialized view DDL
({"query": "CREATE FUNCTION f() RETURNS int AS $$ $$"}, True), # function DDL
- ({"query": "ALTER SYSTEM SET work_mem = '1GB'"}, True), # persists server config
+ (
+ {"query": "ALTER SYSTEM SET work_mem = '1GB'"},
+ True,
+ ), # persists server config
({"query": "alter system reset all"}, True), # ALTER SYSTEM RESET
- ({"query": "SELECT * FROM system_logs"}, False), # 'system' as a table name stays safe
- ({"query": "SELECT * FROM created_view"}, False), # 'create' substring stays safe
+ (
+ {"query": "SELECT * FROM system_logs"},
+ False,
+ ), # 'system' as a table name stays safe
+ (
+ {"query": "SELECT * FROM created_view"},
+ False,
+ ), # 'create' substring stays safe
({"query": "CALL delete_all_users()"}, True), # stored procedure invocation
({"query": "EXEC purge_queue"}, True), # EXEC procedure
({"query": "EXECUTE sp_drop"}, True), # EXECUTE procedure
({"query": "VACUUM INTO 'backup.db'"}, True), # VACUUM rewrites the database
({"query": "please call me back later"}, False), # NL 'call' stays safe
- ({"query": "ATTACH DATABASE '/tmp/x.db' AS x"}, True), # attaches a database file
+ (
+ {"query": "ATTACH DATABASE '/tmp/x.db' AS x"},
+ True,
+ ), # attaches a database file
({"query": "DETACH DATABASE x"}, True), # detaches a database
({"query": "PRAGMA user_version = 42"}, True), # write-form PRAGMA
({"query": "PRAGMA journal_mode=WAL"}, True), # write-form PRAGMA (no spaces)
({"query": "PRAGMA foreign_keys(0)"}, True), # call-form PRAGMA write
({"query": "SELECT load_extension('/tmp/evil.so')"}, True), # loads native code
({"query": "PRAGMA journal_mode"}, False), # read-form PRAGMA stays safe
- ({"query": "can you attach the report to the email"}, False), # NL 'attach' stays safe
+ (
+ {"query": "can you attach the report to the email"},
+ False,
+ ), # NL 'attach' stays safe
({"query": "ATTACH '/tmp/x.db' AS x"}, True), # ATTACH without DATABASE keyword
- ({"query": "PRAGMA main.user_version = 1"}, True), # schema-qualified write PRAGMA
+ (
+ {"query": "PRAGMA main.user_version = 1"},
+ True,
+ ), # schema-qualified write PRAGMA
({"query": "attach it as draft"}, False), # NL 'attach ... as' stays safe
({"query": "DROP FUNCTION f()"}, True), # DROP of a non-table object
- ({"query": "ALTER INDEX idx RENAME TO idx2"}, True), # ALTER of a non-table object
+ (
+ {"query": "ALTER INDEX idx RENAME TO idx2"},
+ True,
+ ), # ALTER of a non-table object
({"query": "DROP MATERIALIZED VIEW mv"}, True), # DROP with a modifier
({"query": "ALTER USER bob WITH PASSWORD 'x'"}, True), # ALTER USER mutates
- ({"query": "SELECT dropped_at FROM t"}, False), # 'drop' substring column stays safe
- ({"query": "mutation M @audit { deleteIssue(id: 1) }"}, True), # directive GraphQL mutation
+ (
+ {"query": "SELECT dropped_at FROM t"},
+ False,
+ ), # 'drop' substring column stays safe
+ (
+ {"query": "mutation M @audit { deleteIssue(id: 1) }"},
+ True,
+ ), # directive GraphQL mutation
(
{"query": "query Q @cached { issue(id: 1) { title } }"},
False,
), # directive GraphQL read stays safe
({"query": 'UPDATE "users" SET admin=1'}, True), # double-quoted UPDATE target
({"query": "UPDATE public.users SET admin=1"}, True), # schema-qualified UPDATE
- ({"query": "UPDATE ONLY public.users SET admin=1"}, True), # ONLY-qualified UPDATE
+ (
+ {"query": "UPDATE ONLY public.users SET admin=1"},
+ True,
+ ), # ONLY-qualified UPDATE
({"query": "UPDATE `users` SET admin=1"}, True), # backtick-quoted UPDATE
({"query": "UPDATE [users] SET admin=1"}, True), # bracket-quoted UPDATE
- ({"query": "please update the documentation set"}, False), # NL 'update ... set' stays safe
- ({"query": "SELECT pg_terminate_backend(123)"}, True), # state-changing SQL function
+ (
+ {"query": "please update the documentation set"},
+ False,
+ ), # NL 'update ... set' stays safe
+ (
+ {"query": "SELECT pg_terminate_backend(123)"},
+ True,
+ ), # state-changing SQL function
({"query": "SELECT setval('s', 1)"}, True), # sequence mutation function
- ({"query": "SELECT pg_write_file('/tmp/p', 'x')"}, True), # server-side file write
- ({"query": "SELECT lo_export(123, '/tmp/p')"}, True), # large-object export to a file
- ({"query": "SELECT setval_col FROM t"}, False), # 'setval' column prefix stays safe
+ (
+ {"query": "SELECT pg_write_file('/tmp/p', 'x')"},
+ True,
+ ), # server-side file write
+ (
+ {"query": "SELECT lo_export(123, '/tmp/p')"},
+ True,
+ ), # large-object export to a file
+ (
+ {"query": "SELECT setval_col FROM t"},
+ False,
+ ), # 'setval' column prefix stays safe
(
{"query": "SELECT secret INTO OUTFILE '/tmp/leak' FROM users"},
True,
), # INTO OUTFILE write
- ({"query": "SELECT x INTO DUMPFILE '/tmp/d' FROM t"}, True), # INTO DUMPFILE write
+ (
+ {"query": "SELECT x INTO DUMPFILE '/tmp/d' FROM t"},
+ True,
+ ), # INTO DUMPFILE write
(
{"query": "SELECT count(*) INTO cnt FROM t"},
False,
@@ -1187,29 +1455,62 @@ def test_mcp_sensitive_arguments(args, unsafe):
({"query": "REFRESH MATERIALIZED VIEW mv"}, True), # materialized view rewrite
({"query": "REINDEX INDEX idx"}, True), # index rebuild
({"query": "REINDEX TABLE t"}, True), # table reindex
- ({"query": "SELECT refresh_count FROM t"}, False), # 'refresh' column stays safe
+ (
+ {"query": "SELECT refresh_count FROM t"},
+ False,
+ ), # 'refresh' column stays safe
({"query": "please refresh the page"}, False), # NL 'refresh' stays safe
- ({"query": "COMMENT ON TABLE users IS 'owned'"}, True), # catalog metadata write
+ (
+ {"query": "COMMENT ON TABLE users IS 'owned'"},
+ True,
+ ), # catalog metadata write
({"query": "LOCK TABLE users IN ACCESS EXCLUSIVE MODE"}, True), # explicit lock
- ({"query": "SECURITY LABEL FOR x ON TABLE t IS 'z'"}, True), # security label write
- ({"query": "CREATE POLICY p ON accounts USING (true)"}, True), # row-security policy DDL
+ (
+ {"query": "SECURITY LABEL FOR x ON TABLE t IS 'z'"},
+ True,
+ ), # security label write
+ (
+ {"query": "CREATE POLICY p ON accounts USING (true)"},
+ True,
+ ), # row-security policy DDL
({"query": "SELECT comment FROM t"}, False), # 'comment' column stays safe
({"query": "SELECT * FROM locks"}, False), # 'locks' table stays safe
({"query": "SELECT nextval('billing_seq')"}, True), # sequence advance mutates
({"query": "SELECT pg_advisory_lock(42)"}, True), # advisory lock changes state
- ({"query": "SELECT pg_notify('jobs', 'wake')"}, True), # server-side notification
+ (
+ {"query": "SELECT pg_notify('jobs', 'wake')"},
+ True,
+ ), # server-side notification
({"query": "SELECT set_config('x', 'y', false)"}, True), # session config write
- ({"query": "SELECT nextval_col FROM t"}, False), # 'nextval' column prefix stays safe
+ (
+ {"query": "SELECT nextval_col FROM t"},
+ False,
+ ), # 'nextval' column prefix stays safe
({"query": "TRUNCATE users"}, True), # multi-char table name (bare TRUNCATE)
({"query": "TRUNCATE TABLE accounts"}, True), # multi-char TRUNCATE TABLE
({"query": 'TRUNCATE TABLE "users"'}, True), # quoted TRUNCATE target
- ({"query": "TRUNCATE accounts RESTART IDENTITY"}, True), # TRUNCATE with options
- ({"query": "SELECT truncate_log FROM t"}, False), # 'truncate' column stays safe
- ({"query": "UPDATE users AS u SET admin=1"}, True), # aliased UPDATE target (AS)
+ (
+ {"query": "TRUNCATE accounts RESTART IDENTITY"},
+ True,
+ ), # TRUNCATE with options
+ (
+ {"query": "SELECT truncate_log FROM t"},
+ False,
+ ), # 'truncate' column stays safe
+ (
+ {"query": "UPDATE users AS u SET admin=1"},
+ True,
+ ), # aliased UPDATE target (AS)
({"query": 'UPDATE "users" AS u SET x=1'}, True), # quoted+aliased UPDATE
- ({"query": "UPDATE public.users AS u SET x=1"}, True), # schema-qualified aliased UPDATE
+ (
+ {"query": "UPDATE public.users AS u SET x=1"},
+ True,
+ ), # schema-qualified aliased UPDATE
({"query": "SELECT * FROM users AS u"}, False), # aliased SELECT stays safe
- ({"query": "please update the documentation set"}, False), # NL, no AS, stays safe
+ (
+ {"query": "please update the documentation set"},
+ False,
+ ), # NL, no AS, stays safe
({"query": "GRANT SELECT ON t TO u"}, True), # privilege grant (multi-word)
({"query": "REVOKE ALL ON t FROM u"}, True), # privilege revoke (multi-word)
({"query": "SELECT * FROM grants"}, False), # 'grants' table stays safe
@@ -1290,7 +1591,9 @@ def _drive(turns, decisions, **loop_kwargs):
for ev in gen:
events.append(ev)
if ev["type"] == "tool_start" and ev.get("awaiting_confirmation"):
- resolve_tool_decision(ev["approval_id"], next(decision_iter), session_id = session)
+ resolve_tool_decision(
+ ev["approval_id"], next(decision_iter), session_id = session
+ )
return events, exec_fn
@@ -1316,7 +1619,9 @@ def test_auto_mode_does_not_gate_safe_calls():
permission_mode = "auto",
)
starts = _tool_starts(events)
- assert starts and starts[0]["awaiting_confirmation"] is False, _diag(events, exec_fn)
+ assert starts and starts[0]["awaiting_confirmation"] is False, _diag(
+ events, exec_fn
+ )
assert starts[0]["approval_id"] == ""
assert exec_fn.calls == [("python", {"code": "print(1)"})], _diag(events, exec_fn)
assert exec_fn.disable_sandbox_seen == [False], _diag(
@@ -1368,7 +1673,9 @@ def test_off_mode_never_gates_and_keeps_sandbox():
permission_mode = "off",
)
starts = _tool_starts(events)
- assert starts and starts[0]["awaiting_confirmation"] is False, _diag(events, exec_fn)
+ assert starts and starts[0]["awaiting_confirmation"] is False, _diag(
+ events, exec_fn
+ )
assert starts[0]["approval_id"] == ""
assert exec_fn.disable_sandbox_seen == [False], _diag(events, exec_fn)
@@ -1381,7 +1688,9 @@ def test_full_mode_never_gates_and_drops_sandbox():
permission_mode = "full",
)
starts = _tool_starts(events)
- assert starts and starts[0]["awaiting_confirmation"] is False, _diag(events, exec_fn)
+ assert starts and starts[0]["awaiting_confirmation"] is False, _diag(
+ events, exec_fn
+ )
assert exec_fn.disable_sandbox_seen == [True], _diag(events, exec_fn)
@@ -1394,7 +1703,9 @@ def test_bypass_flag_implies_full_mode():
bypass_permissions = True,
)
starts = _tool_starts(events)
- assert starts and starts[0]["awaiting_confirmation"] is False, _diag(events, exec_fn)
+ assert starts and starts[0]["awaiting_confirmation"] is False, _diag(
+ events, exec_fn
+ )
assert exec_fn.disable_sandbox_seen == [True], _diag(events, exec_fn)
@@ -1424,7 +1735,9 @@ def test_unknown_permission_mode_normalizes_to_ask_on_request_models():
)
assert req.permission_mode == "ask", (cls.__name__, unknown)
assert (
- cls(messages = [{"role": "user", "content": "hi"}], permission_mode = None).permission_mode
+ cls(
+ messages = [{"role": "user", "content": "hi"}], permission_mode = None
+ ).permission_mode
is None
)
for known in ("ask", "auto", "off", "full"):
@@ -1524,7 +1837,10 @@ def test_permission_mode_confirm_derivation():
# An explicit confirm flag always wins (True gates, False opts out).
assert _permission_mode_confirm(req(confirm_tool_calls = True, stream = False)) is True
- assert _permission_mode_confirm(req(confirm_tool_calls = False, permission_mode = "ask")) is False
+ assert (
+ _permission_mode_confirm(req(confirm_tool_calls = False, permission_mode = "ask"))
+ is False
+ )
# Explicit ask/auto always engage the gate (a non-streaming one is rejected
# by the guard that reads this).
assert _permission_mode_confirm(req(permission_mode = "ask", stream = False)) is True
@@ -1549,9 +1865,14 @@ def test_confirm_gate_needs_stream():
safe = ["web_search", "search_knowledge_base"]
# auto + a safe-only selection never prompts -> no stream needed.
- assert _confirm_gate_needs_stream(req(permission_mode = "auto", enabled_tools = safe)) is False
assert (
- _confirm_gate_needs_stream(req(permission_mode = "auto", enabled_tools = ["web_search"]))
+ _confirm_gate_needs_stream(req(permission_mode = "auto", enabled_tools = safe))
+ is False
+ )
+ assert (
+ _confirm_gate_needs_stream(
+ req(permission_mode = "auto", enabled_tools = ["web_search"])
+ )
is False
)
# render_html can prompt when its canvas reaches the network, so a selection
@@ -1565,9 +1886,15 @@ def test_confirm_gate_needs_stream():
# But a selectable unsafe tool, an unrestricted (omitted) selection, MCP, or an
# explicit confirm flag all still require streaming under auto.
assert (
- _confirm_gate_needs_stream(req(permission_mode = "auto", enabled_tools = ["terminal"])) is True
+ _confirm_gate_needs_stream(
+ req(permission_mode = "auto", enabled_tools = ["terminal"])
+ )
+ is True
+ )
+ assert (
+ _confirm_gate_needs_stream(req(permission_mode = "auto", enable_tools = True))
+ is True
)
- assert _confirm_gate_needs_stream(req(permission_mode = "auto", enable_tools = True)) is True
assert (
_confirm_gate_needs_stream(
req(permission_mode = "auto", enabled_tools = ["web_search"], mcp_enabled = True)
@@ -1576,19 +1903,34 @@ def test_confirm_gate_needs_stream():
)
assert (
_confirm_gate_needs_stream(
- req(permission_mode = "auto", enabled_tools = ["web_search"], confirm_tool_calls = True)
+ req(
+ permission_mode = "auto",
+ enabled_tools = ["web_search"],
+ confirm_tool_calls = True,
+ )
)
is True
)
# An explicit empty selection runs no built-in tool, so nothing can prompt and
# no stream is needed (distinct from an omitted list, which means all tools).
assert (
- _confirm_gate_needs_stream(req(permission_mode = "auto", enable_tools = True, enabled_tools = []))
+ _confirm_gate_needs_stream(
+ req(permission_mode = "auto", enable_tools = True, enabled_tools = [])
+ )
is False
)
# ask prompts for every call, so even a safe-only selection needs streaming.
- assert _confirm_gate_needs_stream(req(permission_mode = "ask", enabled_tools = safe)) is True
+ assert (
+ _confirm_gate_needs_stream(req(permission_mode = "ask", enabled_tools = safe))
+ is True
+ )
# off/full never prompt; unset non-streaming keeps the legacy run-without-gate.
- assert _confirm_gate_needs_stream(req(permission_mode = "off", enabled_tools = safe)) is False
- assert _confirm_gate_needs_stream(req(permission_mode = "full", enabled_tools = safe)) is False
+ assert (
+ _confirm_gate_needs_stream(req(permission_mode = "off", enabled_tools = safe))
+ is False
+ )
+ assert (
+ _confirm_gate_needs_stream(req(permission_mode = "full", enabled_tools = safe))
+ is False
+ )
assert _confirm_gate_needs_stream(req(enabled_tools = safe, stream = False)) is False
diff --git a/studio/backend/tests/test_personalization_settings.py b/studio/backend/tests/test_personalization_settings.py
index 7b5e70decc..0c8ea6d052 100644
--- a/studio/backend/tests/test_personalization_settings.py
+++ b/studio/backend/tests/test_personalization_settings.py
@@ -79,9 +79,13 @@ def test_customization_invalid_values_rejected():
{"appearance": {"customization": {"colors": {"light": {"accent": "red"}}}}}
)
with pytest.raises(ValidationError):
- PersonalizationPayload.model_validate({"appearance": {"customization": {"uiFontSize": 99}}})
+ PersonalizationPayload.model_validate(
+ {"appearance": {"customization": {"uiFontSize": 99}}}
+ )
with pytest.raises(ValidationError):
- PersonalizationPayload.model_validate({"appearance": {"customization": {"contrast": 500}}})
+ PersonalizationPayload.model_validate(
+ {"appearance": {"customization": {"contrast": 500}}}
+ )
with pytest.raises(ValidationError):
PersonalizationPayload.model_validate(
{"appearance": {"customization": {"reduceMotion": "sometimes"}}}
@@ -149,7 +153,9 @@ def test_customization_imported_fonts_validated():
{
"appearance": {
"customization": {
- "importedFonts": [{"name": "My Font", "dataUrl": "data:font/woff2;base64,AAAA"}]
+ "importedFonts": [
+ {"name": "My Font", "dataUrl": "data:font/woff2;base64,AAAA"}
+ ]
}
}
}
@@ -161,7 +167,10 @@ def test_customization_imported_fonts_validated():
"appearance": {
"customization": {
"importedFonts": [
- {"name": "Evil", "dataUrl": "https://example.com/font.woff2"}
+ {
+ "name": "Evil",
+ "dataUrl": "https://example.com/font.woff2",
+ }
]
}
}
@@ -173,7 +182,10 @@ def test_customization_imported_fonts_validated():
"appearance": {
"customization": {
"importedFonts": [
- {"name": f"Font {i}", "dataUrl": "data:font/ttf;base64,AAAA"}
+ {
+ "name": f"Font {i}",
+ "dataUrl": "data:font/ttf;base64,AAAA",
+ }
for i in range(4)
]
}
@@ -189,7 +201,17 @@ def _imported(fonts):
def test_imported_font_name_rejects_css_characters():
# Includes backslash (escapes the quoted family), comma/slash (extra
# fallbacks / comment start), and a control character.
- for bad in ['Ev"il', "Ev;il", "Ev{il", "Ev" "<|tool▁call▁begin|>get_time" "<|tool▁sep|>" '{"city":"Tokyo"}'
+ "<|tool▁calls▁begin|>"
+ "<|tool▁call▁begin|>get_time"
+ "<|tool▁sep|>"
+ '{"city":"Tokyo"}'
# neither <|tool▁call▁end|> nor <|tool▁calls▁end|>
)
calls = parse_tool_calls_from_text(text)
@@ -511,7 +517,9 @@ def test_glm_value_containing_literal_arg_value_close_is_preserved():
)
calls = parse_tool_calls_from_text(content)
assert len(calls) == 1, calls
- assert json.loads(calls[0]["function"]["arguments"]) == {"code": 'print("")'}
+ assert json.loads(calls[0]["function"]["arguments"]) == {
+ "code": 'print("")'
+ }
def test_attribute_form_function_with_embedded_marker_runs_outer_call():
@@ -530,7 +538,9 @@ def test_attribute_form_function_with_embedded_marker_runs_outer_call():
def test_wrapperless_gemma_call_gated_by_enabled_tools():
# Once skip_special_tokens removes the <|tool_call> wrapper, call:NAME{...} is
# indistinguishable from prose documenting the Gemma syntax.
- prose = "Here is an example of the syntax: call:foo{x:1}. That shows how tools work."
+ prose = (
+ "Here is an example of the syntax: call:foo{x:1}. That shows how tools work."
+ )
assert parse_tool_calls_from_text(prose, enabled_tool_names = {"web_search"}) == []
# The display strip is gated the same way, so the example survives in the answer.
assert "call:foo{x:1}" in strip_tool_markup(
@@ -572,9 +582,7 @@ def test_closed_envelope_before_deepseek_block_owns_turn():
"```"
"<|tool▁call▁end|><|tool▁calls▁end|>"
)
- prose = (
- 'A Qwen call looks like {"name":"example_tool","arguments":{}}.\n'
- )
+ prose = 'A Qwen call looks like {"name":"example_tool","arguments":{}}.\n'
calls = parse_tool_calls_from_text(prose + deepseek)
assert [c["function"]["name"] for c in calls] == ["example_tool"], calls
@@ -582,15 +590,15 @@ def test_closed_envelope_before_deepseek_block_owns_turn():
"<|tool_calls_section_begin|><|tool_call_begin|>functions.lookup:0"
'<|tool_call_argument_begin|>{"id":7}<|tool_call_end|><|tool_calls_section_end|>'
)
- calls_k = parse_tool_calls_from_text("Example: {} and now:\n" + kimi)
+ calls_k = parse_tool_calls_from_text(
+ "Example: {} and now:\n" + kimi
+ )
assert [c["function"]["name"] for c in calls_k] == ["demo"], calls_k
def test_marker_inside_closed_outer_envelope_still_runs_outer_call():
# The guard must fire when the marker sits INSIDE a closed outer / envelope's arguments: the OUTER call wins.
- outer = (
- "what does <|tool▁calls▁begin|> mean"
- )
+ outer = "what does <|tool▁calls▁begin|> mean"
calls = parse_tool_calls_from_text(outer)
# The outer envelope is the real call; the embedded DeepSeek marker must not
# hijack the parse into a spurious tool.
@@ -691,7 +699,8 @@ def test_r1_heal_keeps_later_call_when_first_omits_close_fence():
assert "get_time" in heal, heal
# Strict keeps the later well-formed call; heal must be a superset.
strict = [
- c["function"]["name"] for c in parse_tool_calls_from_text(text, allow_incomplete = False)
+ c["function"]["name"]
+ for c in parse_tool_calls_from_text(text, allow_incomplete = False)
]
assert set(strict) <= set(heal), (strict, heal)
@@ -699,7 +708,9 @@ def test_r1_heal_keeps_later_call_when_first_omits_close_fence():
def test_wrapperless_gemma_nested_call_in_arg_is_not_a_second_call():
# A wrapper-less Gemma call whose quoted argument mentions another enabled tool must not execute that nested name.
text = 'call:web_search{query:"explain call:delete_all{target:files}"}'
- calls = parse_tool_calls_from_text(text, enabled_tool_names = {"web_search", "delete_all"})
+ calls = parse_tool_calls_from_text(
+ text, enabled_tool_names = {"web_search", "delete_all"}
+ )
assert [c["function"]["name"] for c in calls] == ["web_search"], calls
assert json.loads(calls[0]["function"]["arguments"]) == {
"query": "explain call:delete_all{target:files}"
@@ -708,7 +719,9 @@ def test_wrapperless_gemma_nested_call_in_arg_is_not_a_second_call():
two = "call:web_search{query:hi}call:get_time{tz:UTC}"
assert [
c["function"]["name"]
- for c in parse_tool_calls_from_text(two, enabled_tool_names = {"web_search", "get_time"})
+ for c in parse_tool_calls_from_text(
+ two, enabled_tool_names = {"web_search", "get_time"}
+ )
] == ["web_search", "get_time"]
@@ -718,7 +731,9 @@ def test_leading_bare_json_call_owns_quoted_gemma_snippet():
'{"name":"lookup","parameters":{"note":"use call:web_search{query:cats} for this"}}\n'
"That is the call I would make."
)
- calls = parse_tool_calls_from_text(text, enabled_tool_names = {"lookup", "web_search"})
+ calls = parse_tool_calls_from_text(
+ text, enabled_tool_names = {"lookup", "web_search"}
+ )
assert [c["function"]["name"] for c in calls] == ["lookup"], calls
assert json.loads(calls[0]["function"]["arguments"]) == {
"note": "use call:web_search{query:cats} for this"
@@ -730,14 +745,20 @@ def test_leading_bare_json_call_owns_quoted_gemma_snippet():
'{"name":"lookup","parameters":{"note":"see call:web_search{query:cats}"}};'
'{"name":"lookup","parameters":{"q":"second"}}'
)
- calls_two = parse_tool_calls_from_text(two, enabled_tool_names = {"lookup", "web_search"})
+ calls_two = parse_tool_calls_from_text(
+ two, enabled_tool_names = {"lookup", "web_search"}
+ )
assert [c["function"]["name"] for c in calls_two] == ["lookup", "lookup"], calls_two
def test_leading_gemma_call_still_wins_over_trailing_json_example():
# Reverse control: a real leading Gemma call followed by a bare-JSON example keeps the Gemma call (bare JSON matches only a LEADING object).
- text = 'call:web_search{query:cats} Example JSON: {"name":"demo_tool","parameters":{}}'
- calls = parse_tool_calls_from_text(text, enabled_tool_names = {"web_search", "demo_tool"})
+ text = (
+ 'call:web_search{query:cats} Example JSON: {"name":"demo_tool","parameters":{}}'
+ )
+ calls = parse_tool_calls_from_text(
+ text, enabled_tool_names = {"web_search", "demo_tool"}
+ )
assert [c["function"]["name"] for c in calls] == ["web_search"], calls
# And prose-only enabled Gemma syntax (no leading JSON) still promotes: the
@@ -750,7 +771,9 @@ def test_leading_gemma_call_still_wins_over_trailing_json_example():
def test_leading_gemma_call_owns_quoted_mistral_trigger():
# A leading wrapper-less Gemma call whose argument quotes a Mistral trigger must win: the [TOOL_CALLS] literal is data.
text = 'call:web_search{query:"docs say [TOOL_CALLS]delete_all{}"}'
- calls = parse_tool_calls_from_text(text, enabled_tool_names = {"web_search", "delete_all"})
+ calls = parse_tool_calls_from_text(
+ text, enabled_tool_names = {"web_search", "delete_all"}
+ )
assert [c["function"]["name"] for c in calls] == ["web_search"], calls
assert json.loads(calls[0]["function"]["arguments"]) == {
"query": "docs say [TOOL_CALLS]delete_all{}"
@@ -758,7 +781,9 @@ def test_leading_gemma_call_owns_quoted_mistral_trigger():
# Reverse control: a real leading Mistral call still parses normally.
real = '[TOOL_CALLS]delete_all{"x":1}'
- calls_m = parse_tool_calls_from_text(real, enabled_tool_names = {"web_search", "delete_all"})
+ calls_m = parse_tool_calls_from_text(
+ real, enabled_tool_names = {"web_search", "delete_all"}
+ )
assert [c["function"]["name"] for c in calls_m] == ["delete_all"], calls_m
# A DISABLED Gemma example quoting the trigger is dropped as prose and a
@@ -767,7 +792,9 @@ def test_leading_gemma_call_owns_quoted_mistral_trigger():
'Example: call:demo{note:"see [TOOL_CALLS]delete_all{}"}\n'
'[TOOL_CALLS]web_search{"q":"real"}'
)
- calls_d = parse_tool_calls_from_text(mixed, enabled_tool_names = {"web_search", "delete_all"})
+ calls_d = parse_tool_calls_from_text(
+ mixed, enabled_tool_names = {"web_search", "delete_all"}
+ )
assert [c["function"]["name"] for c in calls_d] == ["web_search"], calls_d
@@ -785,14 +812,22 @@ def test_chained_bare_json_owns_kimi_marker_in_later_call():
assert [c["function"]["name"] for c in calls] == ["lookup", "lookup"], calls
# Reverse control: prose followed by a real Kimi block still parses.
- real = "Let me check.\n<|tool_calls_section_begin|>" + kimi + "<|tool_calls_section_end|>"
- calls_k = parse_tool_calls_from_text(real, enabled_tool_names = {"lookup", "delete_all"})
+ real = (
+ "Let me check.\n<|tool_calls_section_begin|>"
+ + kimi
+ + "<|tool_calls_section_end|>"
+ )
+ calls_k = parse_tool_calls_from_text(
+ real, enabled_tool_names = {"lookup", "delete_all"}
+ )
assert [c["function"]["name"] for c in calls_k] == ["delete_all"], calls_k
# A closed leading Mistral call preceding a trailing Kimi example owns the
# turn too (same closed-call-precedes-marker rule).
mistral = '[TOOL_CALLS]lookup{"q":"first"} then example ' + kimi
- calls_m = parse_tool_calls_from_text(mistral, enabled_tool_names = {"lookup", "delete_all"})
+ calls_m = parse_tool_calls_from_text(
+ mistral, enabled_tool_names = {"lookup", "delete_all"}
+ )
assert [c["function"]["name"] for c in calls_m] == ["lookup"], calls_m
@@ -809,12 +844,16 @@ def test_nested_gemma_values_keep_commas_and_parens():
arr = parse_tool_calls_from_text(
"call:python{opts:[1,2,{a:f(1,2)}]}", enabled_tool_names = {"python"}
)
- assert json.loads(arr[0]["function"]["arguments"]) == {"opts": [1, 2, {"a": "f(1,2)"}]}
+ assert json.loads(arr[0]["function"]["arguments"]) == {
+ "opts": [1, 2, {"a": "f(1,2)"}]
+ }
prose_comma = parse_tool_calls_from_text(
"call:python{opts:{note:hello, world}}", enabled_tool_names = {"python"}
)
- assert json.loads(prose_comma[0]["function"]["arguments"]) == {"opts": {"note": "hello, world"}}
+ assert json.loads(prose_comma[0]["function"]["arguments"]) == {
+ "opts": {"note": "hello, world"}
+ }
quoted = parse_tool_calls_from_text(
'call:python{opts:{q:say "a, b" now,n:3}}', enabled_tool_names = {"python"}
@@ -828,11 +867,16 @@ def test_nested_gemma_values_keep_commas_and_parens():
nested_q = parse_tool_calls_from_text(
'call:python{loc:{city:"New York"}}', enabled_tool_names = {"python"}
)
- assert json.loads(nested_q[0]["function"]["arguments"]) == {"loc": {"city": "New York"}}
+ assert json.loads(nested_q[0]["function"]["arguments"]) == {
+ "loc": {"city": "New York"}
+ }
multi = parse_tool_calls_from_text(
"call:python{opts:{a:1,b:2},n:3}", enabled_tool_names = {"python"}
)
- assert json.loads(multi[0]["function"]["arguments"]) == {"opts": {"a": 1, "b": 2}, "n": 3}
+ assert json.loads(multi[0]["function"]["arguments"]) == {
+ "opts": {"a": 1, "b": 2},
+ "n": 3,
+ }
trunc = parse_tool_calls_from_text(
"call:python{opts:{code:print(1,2}}", enabled_tool_names = {"python"}
)
@@ -907,7 +951,8 @@ def test_disabled_leading_bare_json_does_not_hide_later_marker_call():
'```json\n{"q":"cats"}\n```<|tool▁call▁end|><|tool▁calls▁end|>'
)
calls_ds = parse_tool_calls_from_text(
- '{"name":"draft","parameters":{}} ' + deepseek, enabled_tool_names = {"web_search"}
+ '{"name":"draft","parameters":{}} ' + deepseek,
+ enabled_tool_names = {"web_search"},
)
assert [c["function"]["name"] for c in calls_ds] == ["web_search"], calls_ds
@@ -938,7 +983,9 @@ def test_disabled_leading_bare_json_ownership_controls():
)
assert [c["function"]["name"] for c in nameless] == ["delete_all"], nameless
# Name-agnostic path unchanged: the leading object is the call.
- agnostic = parse_tool_calls_from_text('{"name":"draft","parameters":{}} ' + kimi_delete)
+ agnostic = parse_tool_calls_from_text(
+ '{"name":"draft","parameters":{}} ' + kimi_delete
+ )
assert [c["function"]["name"] for c in agnostic] == ["draft"], agnostic
@@ -987,7 +1034,9 @@ def test_glm_heal_bounds_unclosed_value_at_tool_call_close():
'print("")'
)
calls_lit = parse_tool_calls_from_text(lit, allow_incomplete = True)
- assert json.loads(calls_lit[0]["function"]["arguments"]) == {"city": 'print("")'}
+ assert json.loads(calls_lit[0]["function"]["arguments"]) == {
+ "city": 'print("")'
+ }
def test_prose_mentioning_ds_kimi_markers_survives_final_strip():
@@ -1008,4 +1057,6 @@ def test_prose_mentioning_ds_kimi_markers_survives_final_strip():
'<|tool_call_argument_begin|>{"q'
)
assert strip_tool_markup(truncated_kimi, final = True) == ""
- assert strip_tool_markup("prefix <|tool_calls_section_begin|>", final = True) == "prefix"
+ assert (
+ strip_tool_markup("prefix <|tool_calls_section_begin|>", final = True) == "prefix"
+ )
diff --git a/studio/backend/tests/test_presence_penalty.py b/studio/backend/tests/test_presence_penalty.py
index 030ddb6011..331513b5ce 100644
--- a/studio/backend/tests/test_presence_penalty.py
+++ b/studio/backend/tests/test_presence_penalty.py
@@ -249,4 +249,6 @@ def test_worker_forwards_all_sampling_params_to_backend():
assert backend.received is not None
for key, val in _SAMPLING.items():
- assert backend.received[key] == val, f"{key} dropped/altered in worker gen_kwargs"
+ assert (
+ backend.received[key] == val
+ ), f"{key} dropped/altered in worker gen_kwargs"
diff --git a/studio/backend/tests/test_preview_routes.py b/studio/backend/tests/test_preview_routes.py
index 8fa3093d04..b3de1cedd6 100644
--- a/studio/backend/tests/test_preview_routes.py
+++ b/studio/backend/tests/test_preview_routes.py
@@ -45,7 +45,9 @@ _TEST_SECRET = b"unit-test-preview-secret-0123456789"
def _use_test_secret(monkeypatch) -> None:
- monkeypatch.setattr(preview_token, "get_or_create_preview_link_secret", lambda: _TEST_SECRET)
+ monkeypatch.setattr(
+ preview_token, "get_or_create_preview_link_secret", lambda: _TEST_SECRET
+ )
def _sig(ref: str) -> str:
@@ -229,7 +231,9 @@ def test_chat_payload_sanitized(client, captured):
f"/p/demorun/v1/chat/completions?k={_sig('demorun')}",
json = {
"messages": [{"role": "user", "content": "hi"}],
- "tools": [{"type": "function", "function": {"name": "rm", "parameters": {}}}],
+ "tools": [
+ {"type": "function", "function": {"name": "rm", "parameters": {}}}
+ ],
"enable_tools": True,
"enabled_tools": ["python"],
"mcp_enabled": True,
@@ -452,7 +456,10 @@ def test_generation_clamp_honors_lower_legacy_max_tokens(client, captured):
def test_generation_clamp_honors_lower_completion_tokens(client, captured):
r = client.post(
f"/p/demorun/v1/chat/completions?k={_sig('demorun')}",
- json = {"messages": [{"role": "user", "content": "hi"}], "max_completion_tokens": 32},
+ json = {
+ "messages": [{"role": "user", "content": "hi"}],
+ "max_completion_tokens": 32,
+ },
)
assert r.status_code == 200
p = captured["payload"]
diff --git a/studio/backend/tests/test_preview_sharing_settings.py b/studio/backend/tests/test_preview_sharing_settings.py
index abadaf483c..12c403fc47 100644
--- a/studio/backend/tests/test_preview_sharing_settings.py
+++ b/studio/backend/tests/test_preview_sharing_settings.py
@@ -33,10 +33,14 @@ def client(monkeypatch):
calls["enabled"] = bool(value)
return bool(value)
- monkeypatch.setattr(settings, "get_preview_sharing_enabled", lambda: calls["enabled"])
+ monkeypatch.setattr(
+ settings, "get_preview_sharing_enabled", lambda: calls["enabled"]
+ )
monkeypatch.setattr(settings, "set_preview_sharing_enabled", _set)
monkeypatch.setattr(
- settings, "rotate_preview_link_secret", lambda: calls.__setitem__("rotated", True)
+ settings,
+ "rotate_preview_link_secret",
+ lambda: calls.__setitem__("rotated", True),
)
app = FastAPI()
diff --git a/studio/backend/tests/test_preview_token.py b/studio/backend/tests/test_preview_token.py
index 6b0e802864..0352676a1d 100644
--- a/studio/backend/tests/test_preview_token.py
+++ b/studio/backend/tests/test_preview_token.py
@@ -72,4 +72,6 @@ def test_rotation_revokes_links(tmp_path, monkeypatch):
storage.rotate_preview_link_secret()
# Old shared link is revoked; a freshly minted one works.
assert not preview_token.verify_preview_ref("demorun", token)
- assert preview_token.verify_preview_ref("demorun", preview_token.sign_preview_ref("demorun"))
+ assert preview_token.verify_preview_ref(
+ "demorun", preview_token.sign_preview_ref("demorun")
+ )
diff --git a/studio/backend/tests/test_pricing.py b/studio/backend/tests/test_pricing.py
index 8cd7796f14..198b9d453f 100644
--- a/studio/backend/tests/test_pricing.py
+++ b/studio/backend/tests/test_pricing.py
@@ -245,7 +245,9 @@ def test_openai_cache_read_subtracted_from_input_at_discount():
)
# 20k charged at full price, 80k charged at 0.1x
assert _isclose(out["input_usd"], 20_000 / 1_000_000.0 * base)
- assert _isclose(out["cache_read_usd"], 80_000 / 1_000_000.0 * base * OPENAI_CACHE_READ_MULT)
+ assert _isclose(
+ out["cache_read_usd"], 80_000 / 1_000_000.0 * base * OPENAI_CACHE_READ_MULT
+ )
def test_openai_billable_input_tokens_does_not_double_count_cache_read():
@@ -392,7 +394,9 @@ def test_openai_web_search_charged_per_thousand():
"openai_tool_use": {"web_search_requests": 250},
},
)
- assert _isclose(out["server_tools_usd"], 250 / 1_000.0 * OPENAI_WEB_SEARCH_USD_PER_1K)
+ assert _isclose(
+ out["server_tools_usd"], 250 / 1_000.0 * OPENAI_WEB_SEARCH_USD_PER_1K
+ )
assert _isclose(out["total_usd"], 250 / 1_000.0 * OPENAI_WEB_SEARCH_USD_PER_1K)
@@ -426,7 +430,8 @@ def test_openai_tool_surcharges_added_to_total():
expected_input = 100_000 / 1_000_000.0 * 5.0
expected_output = 5_000 / 1_000_000.0 * 30.0
expected_tools = (
- 3 / 1_000.0 * OPENAI_WEB_SEARCH_USD_PER_1K + 0.25 * OPENAI_CONTAINER_USD_PER_HOUR
+ 3 / 1_000.0 * OPENAI_WEB_SEARCH_USD_PER_1K
+ + 0.25 * OPENAI_CONTAINER_USD_PER_HOUR
)
assert _isclose(
out["total_usd"],
@@ -599,7 +604,10 @@ def test_openai_chat_style_envelope_reads_cache_from_prompt_tokens_details():
)
# Both envelopes must price identically.
assert _isclose(chat_style["input_usd"], raw["input_usd"]), (chat_style, raw)
- assert _isclose(chat_style["cache_read_usd"], raw["cache_read_usd"]), (chat_style, raw)
+ assert _isclose(chat_style["cache_read_usd"], raw["cache_read_usd"]), (
+ chat_style,
+ raw,
+ )
# 80k at 0.1x base, 20k at full.
assert _isclose(
chat_style["cache_read_usd"],
diff --git a/studio/backend/tests/test_pricing_edge.py b/studio/backend/tests/test_pricing_edge.py
index 1fcc428f90..6c7f4038f1 100644
--- a/studio/backend/tests/test_pricing_edge.py
+++ b/studio/backend/tests/test_pricing_edge.py
@@ -191,7 +191,9 @@ def test_anthropic_chat_cache_read_exceeds_prompt_no_negative_billable():
assert out["billable_input_tokens"] == 500 # 0 uncached + 500 cache_read
# cache_read still priced at the discount rate.
base = ANTHROPIC_PRICING["claude-opus-4-7"]["input_per_mtok"]
- assert _isclose(out["cache_read_usd"], 500 / 1_000_000.0 * base * ANTHROPIC_CACHE_READ_MULT)
+ assert _isclose(
+ out["cache_read_usd"], 500 / 1_000_000.0 * base * ANTHROPIC_CACHE_READ_MULT
+ )
def test_openai_raw_cached_tokens_exceeds_input_clamp_non_cached():
@@ -208,7 +210,9 @@ def test_openai_raw_cached_tokens_exceeds_input_clamp_non_cached():
)
assert out["input_usd"] == 0.0
# Cache read still priced (the 0.1x bucket).
- assert _isclose(out["cache_read_usd"], 500 / 1_000_000.0 * base * OPENAI_CACHE_READ_MULT)
+ assert _isclose(
+ out["cache_read_usd"], 500 / 1_000_000.0 * base * OPENAI_CACHE_READ_MULT
+ )
# ── long-context tier crosses on billable, including cache_creation ──
diff --git a/studio/backend/tests/test_process_lifetime.py b/studio/backend/tests/test_process_lifetime.py
index c36cd75405..05d8fac0e6 100644
--- a/studio/backend/tests/test_process_lifetime.py
+++ b/studio/backend/tests/test_process_lifetime.py
@@ -124,7 +124,9 @@ def test_pdeathsig_child_dies_when_parent_sigkilled(tmp_path):
"print(p.pid, flush = True)\n"
"time.sleep(300)\n"
)
- proc = subprocess.Popen([sys.executable, str(mid)], stdout = subprocess.PIPE, text = True)
+ proc = subprocess.Popen(
+ [sys.executable, str(mid)], stdout = subprocess.PIPE, text = True
+ )
try:
sleeper_pid = int(proc.stdout.readline().strip())
assert _alive(sleeper_pid)
@@ -150,7 +152,9 @@ def test_windows_job_kills_child_when_parent_dies(tmp_path):
"print(p.pid, int(pl._win_job_handle is not None), flush = True)\n"
"time.sleep(300)\n"
)
- proc = subprocess.Popen([sys.executable, str(mid)], stdout = subprocess.PIPE, text = True)
+ proc = subprocess.Popen(
+ [sys.executable, str(mid)], stdout = subprocess.PIPE, text = True
+ )
try:
first = proc.stdout.readline().split()
child_pid, installed = int(first[0]), first[1] == "1"
@@ -250,7 +254,9 @@ def test_bind_kills_multiprocessing_child_on_parent_death(tmp_path):
" print(p.pid, flush = True)\n"
" time.sleep(300)\n"
)
- proc = subprocess.Popen([sys.executable, str(mid)], stdout = subprocess.PIPE, text = True)
+ proc = subprocess.Popen(
+ [sys.executable, str(mid)], stdout = subprocess.PIPE, text = True
+ )
try:
child_pid = int(proc.stdout.readline().strip())
assert _alive(child_pid)
diff --git a/studio/backend/tests/test_providers_api.py b/studio/backend/tests/test_providers_api.py
index 7cac3a9e99..3fb8158dc5 100644
--- a/studio/backend/tests/test_providers_api.py
+++ b/studio/backend/tests/test_providers_api.py
@@ -211,7 +211,9 @@ class TestAuth:
json = {"username": USERNAME, "password": PASSWORD},
timeout = 10,
)
- assert resp.status_code == 200, f"Login failed ({resp.status_code}): {resp.text}"
+ assert (
+ resp.status_code == 200
+ ), f"Login failed ({resp.status_code}): {resp.text}"
body = resp.json()
assert body.get("access_token"), "access_token is missing or empty"
assert body.get("token_type") == "bearer"
@@ -221,7 +223,9 @@ class TestAuth:
class TestPublicKey:
- def test_public_key_is_valid_pem(self, auth_headers: dict[str, str], public_key_pem: str):
+ def test_public_key_is_valid_pem(
+ self, auth_headers: dict[str, str], public_key_pem: str
+ ):
"""GET /api/providers/public-key returns an importable RSA PEM key."""
pem_bytes = public_key_pem.encode("utf-8")
key = serialization.load_pem_public_key(pem_bytes)
@@ -243,7 +247,9 @@ class TestRegistry:
)
assert resp.status_code == 200, f"Registry failed: {resp.text}"
providers = resp.json()
- assert len(providers) == 9, f"Expected 9 providers, got {len(providers)}: {providers}"
+ assert (
+ len(providers) == 9
+ ), f"Expected 9 providers, got {len(providers)}: {providers}"
print(f"\n {'Provider':<12} {'Base URL'}")
print(f" {'-'*12} {'-'*45}")
for p in providers:
@@ -263,7 +269,9 @@ class TestRegistry:
def test_registry_entries_have_required_fields(self, auth_headers: dict[str, str]):
"""Each registry entry has provider_type, display_name, base_url, default_models."""
- resp = requests.get(_url("/api/providers/registry"), headers = auth_headers, timeout = 10)
+ resp = requests.get(
+ _url("/api/providers/registry"), headers = auth_headers, timeout = 10
+ )
assert resp.status_code == 200
for entry in resp.json():
for field in (
@@ -298,7 +306,9 @@ class TestProviderCRUD:
json = {"provider_type": "openai", "display_name": "Test OpenAI (pytest)"},
timeout = 10,
)
- assert resp.status_code == 201, f"Create failed ({resp.status_code}): {resp.text}"
+ assert (
+ resp.status_code == 201
+ ), f"Create failed ({resp.status_code}): {resp.text}"
body = resp.json()
assert body.get("id"), "No id in response"
assert body["provider_type"] == "openai"
@@ -309,7 +319,9 @@ class TestProviderCRUD:
def test_list_includes_created(self, auth_headers: dict[str, str]):
"""GET /api/providers/ includes the newly created config."""
- assert TestProviderCRUD._created_id, "No created_id (run test_create_provider first)"
+ assert (
+ TestProviderCRUD._created_id
+ ), "No created_id (run test_create_provider first)"
resp = requests.get(_url("/api/providers/"), headers = auth_headers, timeout = 10)
assert resp.status_code == 200
ids = [p["id"] for p in resp.json()]
@@ -328,7 +340,9 @@ class TestProviderCRUD:
json = {"display_name": new_name},
timeout = 10,
)
- assert resp.status_code == 200, f"Update failed ({resp.status_code}): {resp.text}"
+ assert (
+ resp.status_code == 200
+ ), f"Update failed ({resp.status_code}): {resp.text}"
assert resp.json()["display_name"] == new_name
print(f"\n updated display_name to '{new_name}'")
@@ -340,10 +354,14 @@ class TestProviderCRUD:
headers = auth_headers,
timeout = 10,
)
- assert resp.status_code == 204, f"Delete failed ({resp.status_code}): {resp.text}"
+ assert (
+ resp.status_code == 204
+ ), f"Delete failed ({resp.status_code}): {resp.text}"
# Confirm gone
- list_resp = requests.get(_url("/api/providers/"), headers = auth_headers, timeout = 10)
+ list_resp = requests.get(
+ _url("/api/providers/"), headers = auth_headers, timeout = 10
+ )
ids = [p["id"] for p in list_resp.json()]
assert TestProviderCRUD._created_id not in ids, "Deleted provider still in list"
print(f"\n deleted id={TestProviderCRUD._created_id} confirmed gone")
@@ -391,7 +409,9 @@ class TestProviderInference:
json = {"provider_type": provider_type, "encrypted_api_key": encrypted},
timeout = 30,
)
- assert resp.status_code == 200, f"Request failed ({resp.status_code}): {resp.text}"
+ assert (
+ resp.status_code == 200
+ ), f"Request failed ({resp.status_code}): {resp.text}"
body = resp.json()
assert (
body["success"] is True
@@ -415,7 +435,9 @@ class TestProviderInference:
json = {"provider_type": provider_type, "encrypted_api_key": encrypted},
timeout = 30,
)
- assert resp.status_code == 200, f"Request failed ({resp.status_code}): {resp.text}"
+ assert (
+ resp.status_code == 200
+ ), f"Request failed ({resp.status_code}): {resp.text}"
models = resp.json()
assert isinstance(models, list), f"Expected list, got {type(models)}"
assert len(models) > 0, f"No models returned for {provider_type}"
@@ -462,7 +484,9 @@ class TestProviderInference:
# ── TestVisionInference ─────────────────────────────────────────────
# Sloth photo for testing vision routing across providers
-_VISION_IMAGE_URL = "https://www.travelexcellence.com/images/where-to-see-sloths-in-costa-rica.jpg"
+_VISION_IMAGE_URL = (
+ "https://www.travelexcellence.com/images/where-to-see-sloths-in-costa-rica.jpg"
+)
_VISION_PARAMS = [
pytest.param(
@@ -562,6 +586,8 @@ class TestLocalInferenceUnaffected:
f"This likely means the provider fields broke the base request schema."
)
status_label = (
- "local model responded" if resp.status_code == 200 else "no model loaded (expected)"
+ "local model responded"
+ if resp.status_code == 200
+ else "no model loaded (expected)"
)
print(f"\n status={resp.status_code} ({status_label}) — local path unaffected")
diff --git a/studio/backend/tests/test_rag_captioning.py b/studio/backend/tests/test_rag_captioning.py
index 5ae0926990..eadaf5f696 100644
--- a/studio/backend/tests/test_rag_captioning.py
+++ b/studio/backend/tests/test_rag_captioning.py
@@ -23,16 +23,27 @@ def test_caption_images_runs_when_images_present(monkeypatch):
def test_caption_images_groups_by_page(monkeypatch):
monkeypatch.setattr(captioner.config, "CAPTION_MAX_IMAGES", 8)
- monkeypatch.setattr(captioner, "_caption_one", lambda base, model, b, t: "a chart of results")
- out = captioner.caption_images([_img(1), _img(1), _img(3)], endpoint = ("http://x", "local"))
- assert out == {1: ["a chart of results", "a chart of results"], 3: ["a chart of results"]}
+ monkeypatch.setattr(
+ captioner, "_caption_one", lambda base, model, b, t: "a chart of results"
+ )
+ out = captioner.caption_images(
+ [_img(1), _img(1), _img(3)], endpoint = ("http://x", "local")
+ )
+ assert out == {
+ 1: ["a chart of results", "a chart of results"],
+ 3: ["a chart of results"],
+ }
def test_caption_images_respects_cap(monkeypatch):
monkeypatch.setattr(captioner.config, "CAPTION_MAX_IMAGES", 2)
calls = []
- monkeypatch.setattr(captioner, "_caption_one", lambda *a: (calls.append(1) or "cap"))
- captioner.caption_images([_img(i) for i in range(5)], endpoint = ("http://x", "local"))
+ monkeypatch.setattr(
+ captioner, "_caption_one", lambda *a: (calls.append(1) or "cap")
+ )
+ captioner.caption_images(
+ [_img(i) for i in range(5)], endpoint = ("http://x", "local")
+ )
assert len(calls) == 2
@@ -52,7 +63,9 @@ def test_caption_prompt_and_token_budget(monkeypatch):
# Caption and OCR keep separate prompts + token caps over the shared _vision_complete.
captured: dict = {}
- def fake_vision_complete(base_url, model, image_bytes, *, prompt, timeout, max_tokens):
+ def fake_vision_complete(
+ base_url, model, image_bytes, *, prompt, timeout, max_tokens
+ ):
captured.update(prompt = prompt, timeout = timeout, max_tokens = max_tokens)
return "ok"
@@ -82,9 +95,13 @@ def test_pages_with_figures_and_tiles(tmp_path):
_figure_pdf(pdf)
pgs = parsers.pages_with_figures(str(pdf), max_pages = 4)
assert pgs == [1]
- tiles = parsers.render_pdf_figure_tiles(str(pdf), pgs, rows = 2, cols = 2, fullpage = True)
+ tiles = parsers.render_pdf_figure_tiles(
+ str(pdf), pgs, rows = 2, cols = 2, fullpage = True
+ )
assert len(tiles) == 5 # full page + 2x2 grid
- assert all(t.image_bytes[:8] == b"\x89PNG\r\n\x1a\n" and t.page_number == 1 for t in tiles)
+ assert all(
+ t.image_bytes[:8] == b"\x89PNG\r\n\x1a\n" and t.page_number == 1 for t in tiles
+ )
capped = parsers.render_pdf_figure_tiles(
str(pdf), pgs, rows = 2, cols = 2, fullpage = True, max_tiles = 3
)
@@ -147,7 +164,9 @@ def test_run_skips_figure_work_without_vision_model(
parsers, "pages_with_figures", lambda *a, **k: touched.append("detect") or []
)
monkeypatch.setattr(
- parsers, "render_pdf_figure_tiles", lambda *a, **k: touched.append("render") or []
+ parsers,
+ "render_pdf_figure_tiles",
+ lambda *a, **k: touched.append("render") or [],
)
pdf = tmp_path / "fig.pdf"
@@ -205,7 +224,9 @@ def test_vision_complete_omits_header_when_unauthenticated(monkeypatch):
return _Resp()
monkeypatch.setattr(httpx, "post", fake_post)
- captioner._vision_complete("http://x", "local", b"i", prompt = "p", timeout = 5.0, max_tokens = 8)
+ captioner._vision_complete(
+ "http://x", "local", b"i", prompt = "p", timeout = 5.0, max_tokens = 8
+ )
assert captured["headers"] is None
assert captured["trust_env"] is False
@@ -213,7 +234,9 @@ def test_vision_complete_omits_header_when_unauthenticated(monkeypatch):
def test_merge_page_captions_dedups():
out = captioner.merge_page_captions({1: ["MatMul\nScale", "Scale\nSoftMax"]})
text = out[1][0]
- assert text.lower().count("scale") == 1 # repeated label from overlapping tiles dropped
+ assert (
+ text.lower().count("scale") == 1
+ ) # repeated label from overlapping tiles dropped
assert "MatMul" in text and "SoftMax" in text
@@ -310,7 +333,9 @@ def test_caption_override_true_runs_when_config_off(
monkeypatch.setattr(captioner.config, "CAPTION_IMAGES", False)
monkeypatch.setattr(captioner, "vision_endpoint", lambda: ("http://x", "local"))
- monkeypatch.setattr(captioner, "_caption_one", lambda *a: "bar chart of revenue wombat-7")
+ monkeypatch.setattr(
+ captioner, "_caption_one", lambda *a: "bar chart of revenue wombat-7"
+ )
pdf = tmp_path / "fig.pdf"
_figure_pdf(pdf)
@@ -327,7 +352,9 @@ def test_caption_override_false_skips_when_config_on(
monkeypatch.setattr(captioner.config, "CAPTION_IMAGES", True)
monkeypatch.setattr(captioner, "vision_endpoint", lambda: ("http://x", "local"))
called = []
- monkeypatch.setattr(captioner, "_caption_one", lambda *a: called.append(1) or "should not run")
+ monkeypatch.setattr(
+ captioner, "_caption_one", lambda *a: called.append(1) or "should not run"
+ )
pdf = tmp_path / "fig.pdf"
_figure_pdf(pdf)
@@ -340,7 +367,9 @@ def test_caption_none_follows_config(rag_conn, stub_embeddings, monkeypatch, tmp
# Omitted override (None) falls back to config.CAPTION_IMAGES.
monkeypatch.setattr(captioner, "vision_endpoint", lambda: ("http://x", "local"))
seen = []
- monkeypatch.setattr(captioner, "_caption_one", lambda *a: seen.append(1) or "chart caption")
+ monkeypatch.setattr(
+ captioner, "_caption_one", lambda *a: seen.append(1) or "chart caption"
+ )
monkeypatch.setattr(captioner.config, "CAPTION_IMAGES", False)
pdf_off = tmp_path / "off.pdf"
diff --git a/studio/backend/tests/test_rag_chunking.py b/studio/backend/tests/test_rag_chunking.py
index 3d3c9eedd8..e562a9a59f 100644
--- a/studio/backend/tests/test_rag_chunking.py
+++ b/studio/backend/tests/test_rag_chunking.py
@@ -27,12 +27,16 @@ def test_chunk_never_exceeds_max_with_overlap_carry():
"""Overlap carry is trimmed so no chunk exceeds max_tokens (else the embedder overflows)."""
s1 = " ".join("a" for _ in range(10))
s2 = " ".join("b" for _ in range(95)) # near max
- chunks = chunk_pages([_page(f"{s1}. {s2}")], max_tokens = 100, overlap = 24, count = WORDS)
+ chunks = chunk_pages(
+ [_page(f"{s1}. {s2}")], max_tokens = 100, overlap = 24, count = WORDS
+ )
assert all(c.token_count <= 100 for c in chunks), [c.token_count for c in chunks]
def test_chunk_indices_are_sequential():
- chunks = chunk_pages([_page("alpha. " * 200)], max_tokens = 32, overlap = 0, count = WORDS)
+ chunks = chunk_pages(
+ [_page("alpha. " * 200)], max_tokens = 32, overlap = 0, count = WORDS
+ )
assert [c.chunk_index for c in chunks] == list(range(len(chunks)))
diff --git a/studio/backend/tests/test_rag_embed_llama_server.py b/studio/backend/tests/test_rag_embed_llama_server.py
index 3a332ee19b..ffc1d5fa83 100644
--- a/studio/backend/tests/test_rag_embed_llama_server.py
+++ b/studio/backend/tests/test_rag_embed_llama_server.py
@@ -52,8 +52,12 @@ def _mock_auto(monkeypatch, *, gpus, binary):
from core.inference.llama_cpp import LlamaCppBackend
monkeypatch.setattr(config, "EMBED_BACKEND", "auto")
- monkeypatch.setattr(LlamaCppBackend, "_get_gpu_free_memory", staticmethod(lambda: gpus))
- monkeypatch.setattr(LlamaCppBackend, "_find_llama_server_binary", staticmethod(lambda: binary))
+ monkeypatch.setattr(
+ LlamaCppBackend, "_get_gpu_free_memory", staticmethod(lambda: gpus)
+ )
+ monkeypatch.setattr(
+ LlamaCppBackend, "_find_llama_server_binary", staticmethod(lambda: binary)
+ )
def _stub_st_load(monkeypatch):
@@ -117,7 +121,9 @@ def test_llama_backend_imports_no_torch():
"RAG_EMBED_BACKEND": "llama-server",
"PYTHONPATH": str(backend_dir),
}
- proc = subprocess.run([sys.executable, "-c", code], capture_output = True, text = True, env = env)
+ proc = subprocess.run(
+ [sys.executable, "-c", code], capture_output = True, text = True, env = env
+ )
assert proc.returncode == 0, proc.stderr
assert "OK" in proc.stdout
@@ -163,16 +169,22 @@ def test_use_gpu_explicit_modes(monkeypatch):
def test_use_gpu_auto_follows_probe(monkeypatch):
b = LlamaServerBackend()
monkeypatch.setattr(config, "EMBED_DEVICE", "auto")
- monkeypatch.setattr(LlamaServerBackend, "_gpu_available", staticmethod(lambda: True))
+ monkeypatch.setattr(
+ LlamaServerBackend, "_gpu_available", staticmethod(lambda: True)
+ )
assert b._use_gpu() is True
- monkeypatch.setattr(LlamaServerBackend, "_gpu_available", staticmethod(lambda: False))
+ monkeypatch.setattr(
+ LlamaServerBackend, "_gpu_available", staticmethod(lambda: False)
+ )
assert b._use_gpu() is False
def test_use_gpu_sticky_cpu_fallback(monkeypatch):
b = LlamaServerBackend()
monkeypatch.setattr(config, "EMBED_DEVICE", "auto")
- monkeypatch.setattr(LlamaServerBackend, "_gpu_available", staticmethod(lambda: True))
+ monkeypatch.setattr(
+ LlamaServerBackend, "_gpu_available", staticmethod(lambda: True)
+ )
b._force_cpu = True # a prior GPU start failed
assert b._use_gpu() is False
@@ -183,11 +195,17 @@ def test_gpu_available_reuses_studio_probe(monkeypatch):
monkeypatch.setattr(uh, "is_apple_silicon", lambda: False)
# Ample free VRAM -> GPU; nearly full -> CPU; none -> CPU.
- monkeypatch.setattr(LlamaCppBackend, "_get_gpu_free_memory", staticmethod(lambda: [(0, 40000)]))
+ monkeypatch.setattr(
+ LlamaCppBackend, "_get_gpu_free_memory", staticmethod(lambda: [(0, 40000)])
+ )
assert LlamaServerBackend._gpu_available() is True
- monkeypatch.setattr(LlamaCppBackend, "_get_gpu_free_memory", staticmethod(lambda: [(0, 100)]))
+ monkeypatch.setattr(
+ LlamaCppBackend, "_get_gpu_free_memory", staticmethod(lambda: [(0, 100)])
+ )
assert LlamaServerBackend._gpu_available() is False
- monkeypatch.setattr(LlamaCppBackend, "_get_gpu_free_memory", staticmethod(lambda: []))
+ monkeypatch.setattr(
+ LlamaCppBackend, "_get_gpu_free_memory", staticmethod(lambda: [])
+ )
assert LlamaServerBackend._gpu_available() is False
@@ -205,9 +223,15 @@ def _patch_spawn_deps(
):
# Force CPU so spawn never depends on a host GPU.
monkeypatch.setattr(config, "EMBED_DEVICE", "cpu")
- monkeypatch.setattr(LlamaServerBackend, "_resolve_binary", lambda self: "/bin/llama-server")
- monkeypatch.setattr(LlamaServerBackend, "_resolve_model_path", lambda self: "/m/bge.gguf")
- monkeypatch.setattr(LlamaServerBackend, "_find_free_port", staticmethod(lambda: free_port))
+ monkeypatch.setattr(
+ LlamaServerBackend, "_resolve_binary", lambda self: "/bin/llama-server"
+ )
+ monkeypatch.setattr(
+ LlamaServerBackend, "_resolve_model_path", lambda self: "/m/bge.gguf"
+ )
+ monkeypatch.setattr(
+ LlamaServerBackend, "_find_free_port", staticmethod(lambda: free_port)
+ )
monkeypatch.setattr(mod.subprocess, "Popen", lambda *a, **k: proc)
@@ -239,7 +263,9 @@ def test_spawn_fails_loud_on_early_exit(monkeypatch):
def test_spawn_auto_falls_back_to_cpu_on_gpu_failure(monkeypatch):
monkeypatch.setattr(config, "EMBED_DEVICE", "auto")
- monkeypatch.setattr(LlamaServerBackend, "_gpu_available", staticmethod(lambda: True))
+ monkeypatch.setattr(
+ LlamaServerBackend, "_gpu_available", staticmethod(lambda: True)
+ )
b = LlamaServerBackend()
calls = []
@@ -311,7 +337,9 @@ def test_encode_empty_returns_zero_rows(monkeypatch):
def test_encode_rejects_count_mismatch(monkeypatch):
b = LlamaServerBackend()
monkeypatch.setattr(b, "_ensure_ready", lambda: None)
- monkeypatch.setattr(b, "_post", lambda p, pl: {"data": [{"index": 0, "embedding": [1.0]}]})
+ monkeypatch.setattr(
+ b, "_post", lambda p, pl: {"data": [{"index": 0, "embedding": [1.0]}]}
+ )
with pytest.raises(RuntimeError, match = "vectors for"):
b.encode(["a", "b"], normalize = False)
@@ -392,7 +420,9 @@ def test_post_restarts_once_on_connect_error(monkeypatch):
b._port = 9000
monkeypatch.setattr(b, "_ensure_ready", lambda: None)
restarts = {"n": 0}
- monkeypatch.setattr(b, "_restart", lambda: restarts.__setitem__("n", restarts["n"] + 1))
+ monkeypatch.setattr(
+ b, "_restart", lambda: restarts.__setitem__("n", restarts["n"] + 1)
+ )
attempts = {"n": 0}
@@ -425,7 +455,9 @@ def test_post_restarts_once_on_read_timeout(monkeypatch):
b._port = 9000
monkeypatch.setattr(b, "_ensure_ready", lambda: None)
restarts = {"n": 0}
- monkeypatch.setattr(b, "_restart", lambda: restarts.__setitem__("n", restarts["n"] + 1))
+ monkeypatch.setattr(
+ b, "_restart", lambda: restarts.__setitem__("n", restarts["n"] + 1)
+ )
attempts = {"n": 0}
diff --git a/studio/backend/tests/test_rag_embeddings.py b/studio/backend/tests/test_rag_embeddings.py
index 28a2f69426..0d22b6cd54 100644
--- a/studio/backend/tests/test_rag_embeddings.py
+++ b/studio/backend/tests/test_rag_embeddings.py
@@ -147,7 +147,9 @@ def _patch_llama_backend(monkeypatch, *, binary):
from core.inference.llama_cpp import LlamaCppBackend
from core.rag import embed_llama_server
- monkeypatch.setattr(LlamaCppBackend, "_find_llama_server_binary", staticmethod(lambda: binary))
+ monkeypatch.setattr(
+ LlamaCppBackend, "_find_llama_server_binary", staticmethod(lambda: binary)
+ )
monkeypatch.setattr(embed_llama_server, "LlamaServerBackend", _SentinelLlamaBackend)
@@ -189,7 +191,9 @@ class _BoomOnEncodeModel:
def test_st_encode_runtime_failure_switches_to_llama(monkeypatch):
# encode() blows up mid-run -> switch to llama-server and stay switched.
- monkeypatch.setattr(embeddings, "_get", lambda model_name = None: _BoomOnEncodeModel())
+ monkeypatch.setattr(
+ embeddings, "_get", lambda model_name = None: _BoomOnEncodeModel()
+ )
_patch_llama_backend(monkeypatch, binary = "/fake/llama-server")
calls = {}
@@ -203,7 +207,9 @@ def test_st_encode_runtime_failure_switches_to_llama(monkeypatch):
calls["used"] = True
return np.zeros((len(texts), 4), dtype = np.float32)
- monkeypatch.setattr(_SentinelLlamaBackend, "encode", _sentinel_encode, raising = False)
+ monkeypatch.setattr(
+ _SentinelLlamaBackend, "encode", _sentinel_encode, raising = False
+ )
embeddings._reset_backend()
out = embeddings.encode(["alpha", "beta"])
@@ -215,7 +221,9 @@ def test_st_encode_runtime_failure_switches_to_llama(monkeypatch):
def test_st_encode_failure_without_llama_binary_reraises(monkeypatch):
# No llama-server binary -> surface the encode error.
- monkeypatch.setattr(embeddings, "_get", lambda model_name = None: _BoomOnEncodeModel())
+ monkeypatch.setattr(
+ embeddings, "_get", lambda model_name = None: _BoomOnEncodeModel()
+ )
_patch_llama_backend(monkeypatch, binary = None)
embeddings._reset_backend()
with pytest.raises(RuntimeError, match = "CUDA error during encode"):
diff --git a/studio/backend/tests/test_rag_ingestion.py b/studio/backend/tests/test_rag_ingestion.py
index 7e9e803687..35aa3671c8 100644
--- a/studio/backend/tests/test_rag_ingestion.py
+++ b/studio/backend/tests/test_rag_ingestion.py
@@ -39,7 +39,11 @@ def test_ingestion_lifecycle_pending_to_completed(rag_home, stub_embeddings, tmp
conn = rag_db.get_connection()
try:
- assert store.get_document(conn, doc_id)["status"] in {"pending", "running", "completed"}
+ assert store.get_document(conn, doc_id)["status"] in {
+ "pending",
+ "running",
+ "completed",
+ }
finally:
conn.close()
@@ -83,7 +87,9 @@ def test_ingestion_dedupe_by_hash(rag_home, stub_embeddings, tmp_path):
conn.close()
-def test_ingestion_reingests_when_existing_has_zero_chunks(rag_home, stub_embeddings, tmp_path):
+def test_ingestion_reingests_when_existing_has_zero_chunks(
+ rag_home, stub_embeddings, tmp_path
+):
# A prior ingest of identical bytes that yielded no chunks (e.g. a scanned PDF
# before a vision model loaded) must re-ingest, not dedupe to the empty record.
path = _write(tmp_path, "doc.txt", "alpha bravo charlie " * 50)
@@ -91,7 +97,9 @@ def test_ingestion_reingests_when_existing_has_zero_chunks(rag_home, stub_embedd
scope = store.kb_scope("K1")
conn = rag_db.get_connection()
try:
- empty_id = store.create_document(conn, scope = scope, filename = "old.txt", sha256 = sha)
+ empty_id = store.create_document(
+ conn, scope = scope, filename = "old.txt", sha256 = sha
+ )
store.set_document_status(conn, empty_id, "completed", num_chunks = 0)
finally:
conn.close()
@@ -296,7 +304,9 @@ def test_ingestion_rejects_unsupported_ext(rag_home, stub_embeddings, tmp_path):
ingestion.start_ingestion(store.kb_scope("K1"), "K1", None, "doc.xyz", path)
-def test_ingestion_empty_doc_completes_with_zero_chunks(rag_home, stub_embeddings, tmp_path):
+def test_ingestion_empty_doc_completes_with_zero_chunks(
+ rag_home, stub_embeddings, tmp_path
+):
path = _write(tmp_path, "empty.txt", " \n ")
scope = store.kb_scope("K1")
doc_id, job_id = ingestion.start_ingestion(scope, "K1", None, "empty.txt", path)
@@ -312,7 +322,9 @@ def test_ingestion_empty_doc_completes_with_zero_chunks(rag_home, stub_embedding
reason = "set RAG_REAL_EMBEDDER=1 to run the real sentence-transformers test",
)
def test_ingestion_with_real_embedder(rag_home, tmp_path):
- path = _write(tmp_path, "doc.txt", "The Kestrel-9 turbine is rated at 9.5 megawatts.")
+ path = _write(
+ tmp_path, "doc.txt", "The Kestrel-9 turbine is rated at 9.5 megawatts."
+ )
scope = store.kb_scope("K1")
doc_id, job_id = ingestion.start_ingestion(scope, "K1", None, "doc.txt", path)
_drain(job_id)
@@ -323,7 +335,9 @@ def test_ingestion_with_real_embedder(rag_home, tmp_path):
conn = rag_db.get_connection()
try:
- hits = retrieval.retrieve_hybrid(conn, scope, "how much power does the turbine make?", k = 5)
+ hits = retrieval.retrieve_hybrid(
+ conn, scope, "how much power does the turbine make?", k = 5
+ )
assert hits and hits[0].chunk_id == f"{doc_id}:0"
finally:
conn.close()
diff --git a/studio/backend/tests/test_rag_job_events_queue_lifecycle.py b/studio/backend/tests/test_rag_job_events_queue_lifecycle.py
index 0eb115c562..e21ea93fe5 100644
--- a/studio/backend/tests/test_rag_job_events_queue_lifecycle.py
+++ b/studio/backend/tests/test_rag_job_events_queue_lifecycle.py
@@ -93,9 +93,13 @@ def test_transient_status_read_failure_does_not_end_stream(monkeypatch):
ing._jobs[jid] = queue.Queue()
try:
gen = ing.job_events(jid)
- assert next(gen) == {"type": "heartbeat"} # transient error -> heartbeat, no raise
+ assert next(gen) == {
+ "type": "heartbeat"
+ } # transient error -> heartbeat, no raise
gen.close()
- assert jid in ing._jobs, "an unconfirmed (transient-error) status must keep the queue"
+ assert (
+ jid in ing._jobs
+ ), "an unconfirmed (transient-error) status must keep the queue"
finally:
ing._jobs.pop(jid, None)
diff --git a/studio/backend/tests/test_rag_loopback_trust_env.py b/studio/backend/tests/test_rag_loopback_trust_env.py
index 1945e09982..4f60c607f2 100644
--- a/studio/backend/tests/test_rag_loopback_trust_env.py
+++ b/studio/backend/tests/test_rag_loopback_trust_env.py
@@ -4,7 +4,9 @@ package (all target the local 127.0.0.1 llama-server) must set trust_env=False."
import ast
import os
-RAG_DIR = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "core", "rag")
+RAG_DIR = os.path.join(
+ os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "core", "rag"
+)
HTTPX_CALLEES = {"get", "post", "stream", "request", "Client", "AsyncClient"}
@@ -28,7 +30,11 @@ def _httpx_calls(path):
def _sets_trust_env_false(call):
for kw in call.keywords:
- if kw.arg == "trust_env" and isinstance(kw.value, ast.Constant) and kw.value.value is False:
+ if (
+ kw.arg == "trust_env"
+ and isinstance(kw.value, ast.Constant)
+ and kw.value.value is False
+ ):
return True
return False
diff --git a/studio/backend/tests/test_rag_ocr_fallback.py b/studio/backend/tests/test_rag_ocr_fallback.py
index c7be1fe60b..654eb909b3 100644
--- a/studio/backend/tests/test_rag_ocr_fallback.py
+++ b/studio/backend/tests/test_rag_ocr_fallback.py
@@ -128,7 +128,9 @@ def test_ocr_scanned_pages_merges_short_text_layer(rag_conn, monkeypatch):
# Near-empty pages can still have meaningful extractable text; OCR augments it
# rather than replacing it with a fallible vision transcription.
scope = store.thread_scope("t1")
- document_id = store.create_document(rag_conn, scope = scope, filename = "scan.pdf", sha256 = "h")
+ document_id = store.create_document(
+ rag_conn, scope = scope, filename = "scan.pdf", sha256 = "h"
+ )
job_id = ingestion._new_job(rag_conn, document_id, scope)
pages = [parsers.Page("ID-42", 1, 5)]
@@ -146,11 +148,15 @@ def test_ocr_scanned_pages_merges_short_text_layer(rag_conn, monkeypatch):
# ── end-to-end ingestion ─────────────────────────────────────────────
-def test_scanned_pdf_is_ocred_into_chunks(rag_conn, stub_embeddings, monkeypatch, tmp_path):
+def test_scanned_pdf_is_ocred_into_chunks(
+ rag_conn, stub_embeddings, monkeypatch, tmp_path
+):
monkeypatch.setattr(captioner.config, "OCR_SCANNED", True)
monkeypatch.setattr(captioner, "vision_endpoint", lambda: ("http://x", "local"))
monkeypatch.setattr(
- captioner, "_ocr_one", lambda base, model, b, t: "Invoice total is zebra-42 due Friday"
+ captioner,
+ "_ocr_one",
+ lambda base, model, b, t: "Invoice total is zebra-42 due Friday",
)
pdf = tmp_path / "scan.pdf"
@@ -184,13 +190,17 @@ def test_scanned_page_past_ocr_cap_is_still_captioned(
assert doc["status"] == "completed"
text, _ = tool.whole_document_context(scope_thread_id = "t1", max_tokens = 6000)
assert "scanned page alpha" in text # page 1 OCR'd, within the cap
- assert "figure caption bravo" in text # page 2 past the cap -> captioned, not dropped
+ assert (
+ "figure caption bravo" in text
+ ) # page 2 past the cap -> captioned, not dropped
def test_born_digital_pdf_skips_ocr(rag_conn, stub_embeddings, monkeypatch, tmp_path):
called = []
monkeypatch.setattr(captioner.config, "OCR_SCANNED", True)
- monkeypatch.setattr(captioner, "_ocr_one", lambda *a: called.append(1) or "should not run")
+ monkeypatch.setattr(
+ captioner, "_ocr_one", lambda *a: called.append(1) or "should not run"
+ )
pdf = tmp_path / "digital.pdf"
_text_pdf(pdf, "Real born digital body text. " * 30 + "marker-quokka")
@@ -246,7 +256,9 @@ def test_ocr_override_true_runs_ocr_when_config_off(
assert "quokka" in text
-def test_ocr_disabled_leaves_scanned_pdf_empty(rag_conn, stub_embeddings, monkeypatch, tmp_path):
+def test_ocr_disabled_leaves_scanned_pdf_empty(
+ rag_conn, stub_embeddings, monkeypatch, tmp_path
+):
monkeypatch.setattr(captioner.config, "OCR_SCANNED", False)
pdf = tmp_path / "scan.pdf"
diff --git a/studio/backend/tests/test_rag_parsing.py b/studio/backend/tests/test_rag_parsing.py
index 3e259f6bd0..af63e1e259 100644
--- a/studio/backend/tests/test_rag_parsing.py
+++ b/studio/backend/tests/test_rag_parsing.py
@@ -16,7 +16,11 @@ def _table_pdf(path):
doc = pymupdf.open()
page = doc.new_page()
page.insert_textbox(pymupdf.Rect(40, 40, 550, 70), "Quarterly Results", fontsize = 16)
- rows = [("Quarter", "Revenue", "Growth"), ("Q1", "$1.2M", "12%"), ("Q2", "$1.5M", "25%")]
+ rows = [
+ ("Quarter", "Revenue", "Growth"),
+ ("Q1", "$1.2M", "12%"),
+ ("Q2", "$1.5M", "25%"),
+ ]
y = 90
for r in rows:
page.insert_textbox(pymupdf.Rect(40, y, 250, y + 20), r[0], fontsize = 11)
@@ -51,7 +55,9 @@ def test_pdf_markdown_off_uses_plain_text(tmp_path, monkeypatch):
_table_pdf(pdf)
text = "\n".join(p.text for p in parsers.parse(str(pdf)))
assert "Q2" in text and "$1.5M" in text
- assert "#" not in text and "|" not in text # plain text path emits no Markdown markup
+ assert (
+ "#" not in text and "|" not in text
+ ) # plain text path emits no Markdown markup
def test_pdf_bytes_use_same_extraction_path(tmp_path, monkeypatch):
@@ -173,7 +179,9 @@ def test_pdf_markdown_incomplete_falls_back_to_plain(tmp_path, monkeypatch):
pdf = tmp_path / "long.pdf"
_long_text_pdf(pdf)
text = "\n".join(p.text for p in parsers.parse(str(pdf)))
- assert "quick brown fox" in text # fuller raw layer used, not the near-empty Markdown
+ assert (
+ "quick brown fox" in text
+ ) # fuller raw layer used, not the near-empty Markdown
def _docx_with_table(path):
@@ -252,7 +260,9 @@ def test_docx_table_merged_cell_keeps_grid_alignment(tmp_path):
text = "\n".join(p.text for p in parsers.parse(str(path)))
assert text.count("WIDE") == 1 # merged cell not duplicated across spanned columns
- assert "WIDE | | END" in text # placeholder keeps 3 fields, aligned with "a | b | c"
+ assert (
+ "WIDE | | END" in text
+ ) # placeholder keeps 3 fields, aligned with "a | b | c"
assert "a | b | c" in text
diff --git a/studio/backend/tests/test_rag_preview.py b/studio/backend/tests/test_rag_preview.py
index 0ff27897bd..671d5e358c 100644
--- a/studio/backend/tests/test_rag_preview.py
+++ b/studio/backend/tests/test_rag_preview.py
@@ -112,7 +112,9 @@ def test_preview_routes_and_signed_file(rag_home, stub_embeddings):
assert res
chunk_id = res[0]["chunkId"]
- pt = c.get(f"/api/rag/documents/{doc_id}/preview-target", params = {"chunk_id": chunk_id}).json()
+ pt = c.get(
+ f"/api/rag/documents/{doc_id}/preview-target", params = {"chunk_id": chunk_id}
+ ).json()
assert pt["mediaKind"] == "pdf"
assert pt["text"]
@@ -148,7 +150,9 @@ def test_locator_handles_midword_anchor_and_locates_line():
doc = pymupdf.open()
page = doc.new_page()
- page.insert_text((72, 200), "alpha beta gamma delta epsilon zeta eta theta", fontsize = 12)
+ page.insert_text(
+ (72, 200), "alpha beta gamma delta epsilon zeta eta theta", fontsize = 12
+ )
page_text = doc[0].get_text("text") # mirrors what the parser stores
start = page_text.index("lpha")
end = page_text.index("theta") + 3
@@ -174,7 +178,9 @@ def test_locator_anchors_through_markdown_table_pipes():
doc = pymupdf.open()
page = doc.new_page()
- page.insert_text((72, 200), "Quarter Revenue Growth Q1 sales strong here", fontsize = 12)
+ page.insert_text(
+ (72, 200), "Quarter Revenue Growth Q1 sales strong here", fontsize = 12
+ )
# What the Markdown parser stores for the row (cells joined by pipes, no spaces).
page_text = "|Quarter|Revenue|Growth|Q1|sales|strong|here|"
match = LocatorMatch(page_index = 0, page_number = 1, start = 0, end = len(page_text))
@@ -189,5 +195,7 @@ def test_sign_verify_roundtrip(rag_home):
tok = rag_routes._sign_document("doc-123")
assert rag_routes._verify_document_token(tok) == "doc-123"
- assert rag_routes._verify_document_token("doc-123.0.deadbeef") is None # expired/bad
+ assert (
+ rag_routes._verify_document_token("doc-123.0.deadbeef") is None
+ ) # expired/bad
assert rag_routes._verify_document_token("garbage") is None
diff --git a/studio/backend/tests/test_rag_reconcile_orphaned.py b/studio/backend/tests/test_rag_reconcile_orphaned.py
index c6932e4588..f6007bffff 100644
--- a/studio/backend/tests/test_rag_reconcile_orphaned.py
+++ b/studio/backend/tests/test_rag_reconcile_orphaned.py
@@ -43,7 +43,11 @@ def _add_doc(conn, scope, doc_id, status, texts):
conn, scope = scope, filename = f"{doc_id}.txt", sha256 = doc_id, document_id = doc_id
)
store.add_chunks(
- conn, scope, doc_id, [_chunk(t, i) for i, t in enumerate(texts)], [_embed(t) for t in texts]
+ conn,
+ scope,
+ doc_id,
+ [_chunk(t, i) for i, t in enumerate(texts)],
+ [_embed(t) for t in texts],
)
store.set_document_status(conn, doc_id, status, num_chunks = len(texts))
@@ -63,7 +67,9 @@ def _orphan_job(
def _chunk_count(conn, doc_id):
- return conn.execute("SELECT COUNT(*) FROM chunks WHERE document_id=?", (doc_id,)).fetchone()[0]
+ return conn.execute(
+ "SELECT COUNT(*) FROM chunks WHERE document_id=?", (doc_id,)
+ ).fetchone()[0]
def _job_status(conn, doc_id):
diff --git a/studio/backend/tests/test_rag_retrieval.py b/studio/backend/tests/test_rag_retrieval.py
index 69d9e90871..6cad207bb5 100644
--- a/studio/backend/tests/test_rag_retrieval.py
+++ b/studio/backend/tests/test_rag_retrieval.py
@@ -57,7 +57,9 @@ def _add_doc(
text,
page = None,
):
- store.create_document(conn, scope = scope, filename = filename, sha256 = sha, document_id = doc_id)
+ store.create_document(
+ conn, scope = scope, filename = filename, sha256 = sha, document_id = doc_id
+ )
store.add_chunks(conn, scope, doc_id, [_chunk(text, 0, page)], [_embed(text)])
@@ -150,7 +152,9 @@ def test_tool_formats_chunks_and_sources(rag_conn, bow_embeddings, monkeypatch):
def test_tool_kb_scope_retrieves_from_db(rag_conn, bow_embeddings):
# End-to-end (no retrieve stub): doc found via its scope_kb_id (#8).
_add_doc(rag_conn, "kb_K", "d1", "kb.pdf", "h1", "alpha bravo charlie", page = 1)
- text, sources = tool.search_knowledge_base_with_sources(query = "alpha bravo", scope_kb_id = "K")
+ text, sources = tool.search_knowledge_base_with_sources(
+ query = "alpha bravo", scope_kb_id = "K"
+ )
assert "No matching chunks" not in text
assert sources and sources[0]["chunkId"] == "d1:0"
assert sources[0]["filename"] == "kb.pdf"
@@ -192,11 +196,15 @@ def test_dispatcher_no_sentinel_when_no_hits(rag_home, monkeypatch):
assert tools.RAG_SOURCES_SENTINEL not in out
-def test_search_for_autoinject_gates_on_dense_score(rag_conn, bow_embeddings, monkeypatch):
+def test_search_for_autoinject_gates_on_dense_score(
+ rag_conn, bow_embeddings, monkeypatch
+):
_add_doc(rag_conn, "kb_a", "d1", "paper.pdf", "h1", "body text here", page = 3)
def _hits(score, **kw):
- return lambda conn, scope, q, **k: [retrieval.Hit("d1:0", 1.0, **{kw["key"]: score})]
+ return lambda conn, scope, q, **k: [
+ retrieval.Hit("d1:0", 1.0, **{kw["key"]: score})
+ ]
# Strong dense hit -> injected.
monkeypatch.setattr(retrieval, "retrieve_hybrid", _hits(0.8, key = "dense_score"))
@@ -207,14 +215,22 @@ def test_search_for_autoinject_gates_on_dense_score(rag_conn, bow_embeddings, mo
# Dense below floor -> nothing injected.
monkeypatch.setattr(retrieval, "retrieve_hybrid", _hits(0.30, key = "dense_score"))
- assert tool.search_for_autoinject(query = "q", scope_kb_id = "a", min_dense_score = 0.55) is None
+ assert (
+ tool.search_for_autoinject(query = "q", scope_kb_id = "a", min_dense_score = 0.55)
+ is None
+ )
# Lexical-only hit (no dense score) does not auto-inject.
monkeypatch.setattr(retrieval, "retrieve_hybrid", _hits(1.0, key = "lexical_score"))
- assert tool.search_for_autoinject(query = "q", scope_kb_id = "a", min_dense_score = 0.55) is None
+ assert (
+ tool.search_for_autoinject(query = "q", scope_kb_id = "a", min_dense_score = 0.55)
+ is None
+ )
-def test_search_for_autoinject_bm25_gates_on_dense_probe(rag_conn, bow_embeddings, monkeypatch):
+def test_search_for_autoinject_bm25_gates_on_dense_probe(
+ rag_conn, bow_embeddings, monkeypatch
+):
# BM25 hits carry no cosine, so the gate uses a dense 1-NN probe (#5).
_add_doc(rag_conn, "kb_a", "d1", "paper.pdf", "h1", "body text here", page = 3)
monkeypatch.setattr(
@@ -226,7 +242,9 @@ def test_search_for_autoinject_bm25_gates_on_dense_probe(rag_conn, bow_embedding
monkeypatch.setattr(
retrieval,
"retrieve_dense",
- lambda conn, scope, q, k = None, **kw: [retrieval.Hit("d1:0", 0.82, dense_score = 0.82)],
+ lambda conn, scope, q, k = None, **kw: [
+ retrieval.Hit("d1:0", 0.82, dense_score = 0.82)
+ ],
)
found = tool.search_for_autoinject(
query = "q", scope_kb_id = "a", mode = "lexical", min_dense_score = 0.70
@@ -236,10 +254,14 @@ def test_search_for_autoinject_bm25_gates_on_dense_probe(rag_conn, bow_embedding
monkeypatch.setattr(
retrieval,
"retrieve_dense",
- lambda conn, scope, q, k = None, **kw: [retrieval.Hit("d1:0", 0.40, dense_score = 0.40)],
+ lambda conn, scope, q, k = None, **kw: [
+ retrieval.Hit("d1:0", 0.40, dense_score = 0.40)
+ ],
)
assert (
- tool.search_for_autoinject(query = "q", scope_kb_id = "a", mode = "lexical", min_dense_score = 0.70)
+ tool.search_for_autoinject(
+ query = "q", scope_kb_id = "a", mode = "lexical", min_dense_score = 0.70
+ )
is None
)
@@ -271,7 +293,10 @@ def test_build_rag_autoinject_emits_pipeline(monkeypatch):
te = next(e for e in out["events"] if e["type"] == "tool_end")
assert te["tool_name"] == "search_knowledge_base"
assert tools.RAG_SOURCES_SENTINEL in te["result"]
- assert out["messages"][0]["tool_calls"][0]["function"]["name"] == "search_knowledge_base"
+ assert (
+ out["messages"][0]["tool_calls"][0]["function"]["name"]
+ == "search_knowledge_base"
+ )
assert "__RAG_SOURCES__" not in out["messages"][1]["content"]
@@ -282,7 +307,10 @@ def test_build_rag_autoinject_skips_without_hit(monkeypatch):
monkeypatch.setattr(rag_db, "RAG_AVAILABLE", True, raising = False)
monkeypatch.setattr(tool, "search_for_autoinject", lambda **k: None)
assert (
- tools.build_rag_autoinject([{"role": "user", "content": "hi"}], {"thread_id": "t1"}) is None
+ tools.build_rag_autoinject(
+ [{"role": "user", "content": "hi"}], {"thread_id": "t1"}
+ )
+ is None
)
@@ -300,7 +328,9 @@ def test_build_rag_autoinject_enabled_by_default(monkeypatch):
return ("x", [{"citationId": 1}])
monkeypatch.setattr(tool, "search_for_autoinject", fake)
- out = tools.build_rag_autoinject([{"role": "user", "content": "hi"}], {"thread_id": "t1"})
+ out = tools.build_rag_autoinject(
+ [{"role": "user", "content": "hi"}], {"thread_id": "t1"}
+ )
assert out is not None
assert seen["min_dense_score"] == 0.70 # high-precision floor by default
@@ -331,7 +361,10 @@ def test_build_rag_autoinject_disabled_by_env(monkeypatch):
monkeypatch.setenv("RAG_AUTOINJECT", "0")
assert (
- tools.build_rag_autoinject([{"role": "user", "content": "hi"}], {"thread_id": "t1"}) is None
+ tools.build_rag_autoinject(
+ [{"role": "user", "content": "hi"}], {"thread_id": "t1"}
+ )
+ is None
)
# No scope -> also a no-op.
monkeypatch.delenv("RAG_AUTOINJECT", raising = False)
@@ -429,4 +462,7 @@ def test_build_rag_autoinject_scope_overrides_env(monkeypatch):
# Explicit False disables even with the env default on.
monkeypatch.setenv("RAG_AUTOINJECT", "1")
- assert tools.build_rag_autoinject(conv, {"thread_id": "t1", "autoinject": False}) is None
+ assert (
+ tools.build_rag_autoinject(conv, {"thread_id": "t1", "autoinject": False})
+ is None
+ )
diff --git a/studio/backend/tests/test_rag_store.py b/studio/backend/tests/test_rag_store.py
index 4c54b02ea7..5d216af228 100644
--- a/studio/backend/tests/test_rag_store.py
+++ b/studio/backend/tests/test_rag_store.py
@@ -36,7 +36,9 @@ def _chunk(
def _add_doc(conn, scope, doc_id, filename, sha, texts):
chunks = [_chunk(t, i) for i, t in enumerate(texts)]
vectors = [embed(t) for t in texts]
- store.create_document(conn, scope = scope, filename = filename, sha256 = sha, document_id = doc_id)
+ store.create_document(
+ conn, scope = scope, filename = filename, sha256 = sha, document_id = doc_id
+ )
store.add_chunks(conn, scope, doc_id, chunks, vectors)
@@ -50,7 +52,9 @@ def test_lexical_returns_only_matching_docs(rag_conn):
def test_scope_isolation(rag_conn):
_add_doc(rag_conn, "kb_a", "d1", "f", "h1", ["alpha bravo"])
_add_doc(rag_conn, "kb_b", "d2", "f", "h2", ["alpha bravo"])
- assert [cid for cid, _ in store.search_lexical(rag_conn, "kb_b", "alpha", 10)] == ["d2:0"]
+ assert [cid for cid, _ in store.search_lexical(rag_conn, "kb_b", "alpha", 10)] == [
+ "d2:0"
+ ]
def test_match_query_sanitizes_special_chars():
@@ -100,7 +104,9 @@ def test_incremental_add_is_flat(rag_conn):
after = rag_conn.execute(
"SELECT rowid, chunk_id FROM chunks_fts WHERE scope='kb_a' AND chunk_id LIKE 'd1:%'"
).fetchall()
- before_d1 = [(r["rowid"], r["chunk_id"]) for r in before if r["chunk_id"].startswith("d1:")]
+ before_d1 = [
+ (r["rowid"], r["chunk_id"]) for r in before if r["chunk_id"].startswith("d1:")
+ ]
after_d1 = [(r["rowid"], r["chunk_id"]) for r in after]
assert before_d1 == after_d1
diff --git a/studio/backend/tests/test_rag_whole_document.py b/studio/backend/tests/test_rag_whole_document.py
index 545d731fd2..e8738a5967 100644
--- a/studio/backend/tests/test_rag_whole_document.py
+++ b/studio/backend/tests/test_rag_whole_document.py
@@ -56,7 +56,9 @@ def _add_doc(
for i, t in enumerate(texts)
]
vectors = [list(_VEC) for _ in texts]
- store.create_document(conn, scope = scope, filename = filename, sha256 = sha, document_id = doc_id)
+ store.create_document(
+ conn, scope = scope, filename = filename, sha256 = sha, document_id = doc_id
+ )
store.add_chunks(conn, scope, doc_id, chunks, vectors)
store.set_document_status(conn, doc_id, status, num_chunks = len(texts))
@@ -109,7 +111,9 @@ def test_scope_token_estimate_sums_without_hydrating(rag_conn):
_add_doc(rag_conn, scope, "d1", "a.pdf", "h1", ["alpha", "bravo"], tokens = [10, 20])
# token_count 0 -> length/4 fallback: a 40-char chunk estimates to 10 tokens.
_add_doc(rag_conn, scope, "d2", "b.pdf", "h2", ["x" * 40], tokens = [0])
- _add_doc(rag_conn, scope, "d3", "c.pdf", "h3", ["pending"], status = "pending", tokens = [99])
+ _add_doc(
+ rag_conn, scope, "d3", "c.pdf", "h3", ["pending"], status = "pending", tokens = [99]
+ )
assert store.scope_token_estimate(rag_conn, scope) == 10 + 20 + 10
assert store.scope_token_estimate(rag_conn, store.thread_scope("none")) == 0
@@ -121,10 +125,18 @@ def test_scope_token_estimate_matches_row_sum(rag_conn):
scope = store.thread_scope("t1")
_add_doc(
- rag_conn, scope, "d1", "a.pdf", "h1", ["a long-ish chunk body here", "tail"], tokens = [0, 5]
+ rag_conn,
+ scope,
+ "d1",
+ "a.pdf",
+ "h1",
+ ["a long-ish chunk body here", "tail"],
+ tokens = [0, 5],
)
rows = store.all_chunks_for_scope(rag_conn, scope)
- assert store.scope_token_estimate(rag_conn, scope) == sum(_row_token_count(r) for r in rows)
+ assert store.scope_token_estimate(rag_conn, scope) == sum(
+ _row_token_count(r) for r in rows
+ )
# ── tool.whole_document_context ──────────────────────────────────────
@@ -163,7 +175,10 @@ def test_whole_document_context_none_over_budget(rag_conn):
_add_doc(rag_conn, scope, "d1", "big.pdf", "h1", ["huge"], tokens = [50_000])
assert tool.whole_document_context(scope_thread_id = "t1", max_tokens = 6000) is None
# Same doc fits under a larger budget.
- assert tool.whole_document_context(scope_thread_id = "t1", max_tokens = 100_000) is not None
+ assert (
+ tool.whole_document_context(scope_thread_id = "t1", max_tokens = 100_000)
+ is not None
+ )
def test_whole_document_context_none_when_empty(rag_conn):
@@ -187,9 +202,14 @@ def test_whole_document_context_none_without_scope(rag_conn):
def test_whole_document_context_null_token_count_enforces_budget(rag_conn):
# A missing token_count must not bypass the budget; fall back to a length estimate.
big = "word " * 20_000 # ~20k tokens by length estimate
- _add_doc(rag_conn, store.thread_scope("t1"), "d1", "big.pdf", "h1", [big], tokens = [None])
+ _add_doc(
+ rag_conn, store.thread_scope("t1"), "d1", "big.pdf", "h1", [big], tokens = [None]
+ )
assert tool.whole_document_context(scope_thread_id = "t1", max_tokens = 6000) is None
- assert tool.whole_document_context(scope_thread_id = "t1", max_tokens = 1_000_000) is not None
+ assert (
+ tool.whole_document_context(scope_thread_id = "t1", max_tokens = 1_000_000)
+ is not None
+ )
def test_whole_document_context_spans_multiple_docs(rag_conn):
@@ -210,7 +230,9 @@ def _convo(text = "summarize the whole document"):
def test_build_rag_autoinject_uses_whole_doc(rag_conn):
scope = store.thread_scope("t1")
- _add_doc(rag_conn, scope, "d1", "doc.pdf", "h1", ["whole alpha part", "whole bravo part"])
+ _add_doc(
+ rag_conn, scope, "d1", "doc.pdf", "h1", ["whole alpha part", "whole bravo part"]
+ )
result = inf_tools.build_rag_autoinject(_convo(), {"thread_id": "t1"})
assert result is not None
injected = _injected_text(result)
@@ -221,16 +243,22 @@ def test_build_rag_autoinject_uses_whole_doc(rag_conn):
assert inf_tools.RAG_SOURCES_SENTINEL not in injected
-def test_build_rag_autoinject_whole_doc_runs_when_autoinject_false(rag_conn, monkeypatch):
+def test_build_rag_autoinject_whole_doc_runs_when_autoinject_false(
+ rag_conn, monkeypatch
+):
# Large-model Auto sets autoinject=False, but whole-doc is a separate thread-doc
# context mode and should still inject a fitting attachment.
- _add_doc(rag_conn, store.thread_scope("t1"), "d1", "doc.pdf", "h1", ["entire file body"])
+ _add_doc(
+ rag_conn, store.thread_scope("t1"), "d1", "doc.pdf", "h1", ["entire file body"]
+ )
monkeypatch.setattr(
tool,
"search_for_autoinject",
lambda **kw: (_ for _ in ()).throw(AssertionError("retrieval should not run")),
)
- result = inf_tools.build_rag_autoinject(_convo(), {"thread_id": "t1", "autoinject": False})
+ result = inf_tools.build_rag_autoinject(
+ _convo(), {"thread_id": "t1", "autoinject": False}
+ )
assert result is not None
assert "entire file body" in _injected_text(result)
@@ -255,7 +283,10 @@ def test_build_rag_autoinject_falls_back_over_budget(rag_conn, monkeypatch):
scope = store.thread_scope("t1")
_add_doc(rag_conn, scope, "d1", "big.pdf", "h1", ["overflow"], tokens = [50_000])
- sentinel = ("TOPK_FALLBACK_TEXT", [{"citationId": 1, "filename": "big.pdf", "text": "x"}])
+ sentinel = (
+ "TOPK_FALLBACK_TEXT",
+ [{"citationId": 1, "filename": "big.pdf", "text": "x"}],
+ )
monkeypatch.setattr(tool, "search_for_autoinject", lambda **kw: sentinel)
result = inf_tools.build_rag_autoinject(_convo(), {"thread_id": "t1"})
@@ -267,9 +298,18 @@ def test_build_rag_autoinject_context_budget_falls_back(rag_conn, monkeypatch):
# Runtime context can be smaller than RAG_WHOLE_DOC_MAX_TOKENS; cap whole-doc to
# the active context and fall back to retrieval when it would overflow.
_add_doc(
- rag_conn, store.thread_scope("t1"), "d1", "small.pdf", "h1", ["fits global"], tokens = [900]
+ rag_conn,
+ store.thread_scope("t1"),
+ "d1",
+ "small.pdf",
+ "h1",
+ ["fits global"],
+ tokens = [900],
+ )
+ sentinel = (
+ "TOPK_CONTEXT_FALLBACK",
+ [{"citationId": 1, "filename": "small.pdf", "text": "x"}],
)
- sentinel = ("TOPK_CONTEXT_FALLBACK", [{"citationId": 1, "filename": "small.pdf", "text": "x"}])
monkeypatch.setattr(tool, "search_for_autoinject", lambda **kw: sentinel)
result = inf_tools.build_rag_autoinject(
_convo(), {"thread_id": "t1", "context_length": 1200, "whole_doc": True}
@@ -289,7 +329,10 @@ def test_whole_doc_budget_reserves_image_parts(monkeypatch):
"role": "user",
"content": [
{"type": "text", "text": "summarize"},
- {"type": "image_url", "image_url": {"url": "data:image/png;base64,abc"}},
+ {
+ "type": "image_url",
+ "image_url": {"url": "data:image/png;base64,abc"},
+ },
],
}
]
@@ -301,7 +344,9 @@ def test_whole_doc_budget_reserves_image_parts(monkeypatch):
)
-def test_build_rag_autoinject_server_kill_switch_blocks_whole_doc(rag_conn, monkeypatch):
+def test_build_rag_autoinject_server_kill_switch_blocks_whole_doc(
+ rag_conn, monkeypatch
+):
# RAG_THREAD_WHOLE_DOC=0 stays authoritative; browser requests should not
# turn it back on by default.
from core.rag import config
@@ -314,7 +359,10 @@ def test_build_rag_autoinject_server_kill_switch_blocks_whole_doc(rag_conn, monk
lambda **kw: (_ for _ in ()).throw(AssertionError("retrieval should not run")),
)
assert (
- inf_tools.build_rag_autoinject(_convo(), {"thread_id": "t1", "autoinject": False}) is None
+ inf_tools.build_rag_autoinject(
+ _convo(), {"thread_id": "t1", "autoinject": False}
+ )
+ is None
)
@@ -342,7 +390,9 @@ def test_build_rag_autoinject_whole_doc_disabled_via_override(rag_conn, monkeypa
monkeypatch.setattr(tool, "search_for_autoinject", lambda **kw: sentinel)
# whole_doc=False forces retrieval even though the doc fits.
- result = inf_tools.build_rag_autoinject(_convo(), {"thread_id": "t1", "whole_doc": False})
+ result = inf_tools.build_rag_autoinject(
+ _convo(), {"thread_id": "t1", "whole_doc": False}
+ )
assert result is not None
assert _injected_text(result) == "TOPK_TEXT"
@@ -352,7 +402,10 @@ def test_build_rag_autoinject_kb_scope_never_whole_doc(rag_conn, monkeypatch):
kb_scope = store.kb_scope("K1")
_add_doc(rag_conn, kb_scope, "d1", "kb.pdf", "h1", ["kb body one", "kb body two"])
- sentinel = ("KB_RETRIEVAL_TEXT", [{"citationId": 1, "filename": "kb.pdf", "text": "x"}])
+ sentinel = (
+ "KB_RETRIEVAL_TEXT",
+ [{"citationId": 1, "filename": "kb.pdf", "text": "x"}],
+ )
monkeypatch.setattr(tool, "search_for_autoinject", lambda **kw: sentinel)
result = inf_tools.build_rag_autoinject(_convo(), {"kb_id": "K1"})
@@ -362,8 +415,22 @@ def test_build_rag_autoinject_kb_scope_never_whole_doc(rag_conn, monkeypatch):
def test_whole_document_context_thread_scope_only(rag_conn):
# A project corpus chunk is never whole-doc injected, even with a thread attachment.
- _add_doc(rag_conn, store.thread_scope("t1"), "td", "thread.txt", "h1", ["thread attachment"])
- _add_doc(rag_conn, store.project_scope("p1"), "pd", "project.txt", "h2", ["project corpus"])
+ _add_doc(
+ rag_conn,
+ store.thread_scope("t1"),
+ "td",
+ "thread.txt",
+ "h1",
+ ["thread attachment"],
+ )
+ _add_doc(
+ rag_conn,
+ store.project_scope("p1"),
+ "pd",
+ "project.txt",
+ "h2",
+ ["project corpus"],
+ )
text, sources = tool.whole_document_context(scope_thread_id = "t1", max_tokens = 6000)
assert "thread attachment" in text
assert "project corpus" not in text
@@ -401,7 +468,9 @@ def test_build_rag_autoinject_appends_project_retrieval(rag_conn, monkeypatch):
return proj
monkeypatch.setattr(tool, "search_for_autoinject", fake_search)
- result = inf_tools.build_rag_autoinject(_convo(), {"thread_id": "t1", "project_id": "p1"})
+ result = inf_tools.build_rag_autoinject(
+ _convo(), {"thread_id": "t1", "project_id": "p1"}
+ )
injected = _injected_text(result)
# Whole thread attachment AND the project passage are both injected.
assert "thread chunk one" in injected
@@ -417,8 +486,12 @@ def test_build_rag_autoinject_appends_project_retrieval(rag_conn, monkeypatch):
assert '[INST]", "response": "[/INST]"},
- "starling": {"instruction": "GPT4 Correct User:", "response": "GPT4 Correct Assistant:"},
+ "starling": {
+ "instruction": "GPT4 Correct User:",
+ "response": "GPT4 Correct Assistant:",
+ },
"glm": {"instruction": "<|user|>", "response": "<|assistant|>"},
- "qwen3-thinking": {"instruction": "<|im_start|>user\n", "response": "<|im_start|>assistant\n"},
+ "qwen3-thinking": {
+ "instruction": "<|im_start|>user\n",
+ "response": "<|im_start|>assistant\n",
+ },
"zephyr": {"instruction": "\n<|user|>\n", "response": "\n<|assistant|>\n"},
}
# Spot-pin some known-good entries so a refactor cannot silently change them.
EXPECTED_UNCHANGED = {
- "qwen3": {"instruction": "<|im_start|>user\n", "response": "<|im_start|>assistant\n"},
+ "qwen3": {
+ "instruction": "<|im_start|>user\n",
+ "response": "<|im_start|>assistant\n",
+ },
"llama-3.1": {
"instruction": "<|start_header_id|>user<|end_header_id|>\n\n",
"response": "<|start_header_id|>assistant<|end_header_id|>\n\n",
@@ -76,7 +85,10 @@ EXPECTED_UNCHANGED = {
"instruction": "<|im_start|>user<|im_sep|>",
"response": "<|im_start|>assistant<|im_sep|>",
},
- "gemma-3": {"instruction": "user\n", "response": "model\n"},
+ "gemma-3": {
+ "instruction": "user\n",
+ "response": "model\n",
+ },
"gpt-oss": {
"instruction": "<|start|>user<|message|>",
"response": "<|start|>assistant<|channel|>final<|message|>",
@@ -137,7 +149,9 @@ def _load_tokenizer(repo):
from huggingface_hub import hf_hub_download
from transformers import PreTrainedTokenizerFast
- with open(hf_hub_download(repo, "tokenizer_config.json"), encoding = "utf-8") as f:
+ with open(
+ hf_hub_download(repo, "tokenizer_config.json"), encoding = "utf-8"
+ ) as f:
cfg = _json.load(f)
tok_file = hf_hub_download(repo, "tokenizer.json")
@@ -180,7 +194,9 @@ def test_fixed_markers_token_level(template, repo):
if hasattr(ids, "keys"):
ids = ids["input_ids"] # transformers 5.x returns a BatchEncoding
except Exception:
- ids = tok.apply_chat_template(FIXTURE, tokenize = True, add_generation_prompt = False)
+ ids = tok.apply_chat_template(
+ FIXTURE, tokenize = True, add_generation_prompt = False
+ )
if hasattr(ids, "keys"):
ids = ids["input_ids"]
@@ -209,7 +225,9 @@ def test_fixed_markers_token_level(template, repo):
i = n - 1
while i > 0 and tok.decode([ids[i]]).strip() == "":
i -= 1
- assert labels[i] != -100, f"final token {tok.convert_ids_to_tokens(int(ids[i]))!r} is masked"
+ assert (
+ labels[i] != -100
+ ), f"final token {tok.convert_ids_to_tokens(int(ids[i]))!r} is masked"
if __name__ == "__main__":
diff --git a/studio/backend/tests/test_responses_api.py b/studio/backend/tests/test_responses_api.py
index 693e832113..2ad5aeab94 100644
--- a/studio/backend/tests/test_responses_api.py
+++ b/studio/backend/tests/test_responses_api.py
@@ -168,7 +168,9 @@ class TestResponsesResponse:
resp = ResponsesResponse(
model = "test-model",
output = [
- ResponsesOutputMessage(content = [ResponsesOutputTextContent(text = "Hello!")]),
+ ResponsesOutputMessage(
+ content = [ResponsesOutputTextContent(text = "Hello!")]
+ ),
],
usage = ResponsesUsage(input_tokens = 10, output_tokens = 5, total_tokens = 15),
)
diff --git a/studio/backend/tests/test_responses_tool_passthrough.py b/studio/backend/tests/test_responses_tool_passthrough.py
index 69715649b7..3377e3a682 100644
--- a/studio/backend/tests/test_responses_tool_passthrough.py
+++ b/studio/backend/tests/test_responses_tool_passthrough.py
@@ -174,7 +174,9 @@ class TestResponsesMultiTurnInput:
def test_function_call_output_missing_call_id_rejected(self):
with pytest.raises(ValidationError):
- ResponsesFunctionCallOutputInputItem(type = "function_call_output", output = "x")
+ ResponsesFunctionCallOutputInputItem(
+ type = "function_call_output", output = "x"
+ )
def test_function_call_output_accepts_content_array(self):
item = ResponsesFunctionCallOutputInputItem(
@@ -234,7 +236,9 @@ class TestToolsTranslation:
assert _translate_responses_tools_to_chat([]) is None
def test_only_builtin_tools_returns_none(self):
- assert _translate_responses_tools_to_chat([{"type": "web_search_preview"}]) is None
+ assert (
+ _translate_responses_tools_to_chat([{"type": "web_search_preview"}]) is None
+ )
def test_description_optional(self):
out = _translate_responses_tools_to_chat(
@@ -266,7 +270,9 @@ class TestToolChoiceTranslation:
"""A client sending the Chat Completions nested shape isn't
double-wrapped."""
already_nested = {"type": "function", "function": {"name": "get_weather"}}
- assert _translate_responses_tool_choice_to_chat(already_nested) == already_nested
+ assert (
+ _translate_responses_tool_choice_to_chat(already_nested) == already_nested
+ )
def test_unknown_shape_passes_through(self):
obj = {"type": "allowed_tools", "tools": [{"type": "function", "name": "x"}]}
@@ -779,7 +785,9 @@ class TestResponsesNonStreamingAdapter:
)
assert [item["type"] for item in body["output"]] == ["reasoning", "message"]
- assert body["output"][0]["content"] == [{"type": "reasoning_text", "text": "plan"}]
+ assert body["output"][0]["content"] == [
+ {"type": "reasoning_text", "text": "plan"}
+ ]
assert body["output"][0]["summary"] == []
assert body["output"][1]["content"][0]["text"] == "33"
assert "" not in body["output"][1]["content"][0]["text"]
@@ -825,7 +833,9 @@ class TestResponsesNonStreamingAdapter:
body = asyncio.run(run())
- assert body["output"][0]["content"] == [{"type": "reasoning_text", "text": "plan"}]
+ assert body["output"][0]["content"] == [
+ {"type": "reasoning_text", "text": "plan"}
+ ]
assert body["output"][1]["content"][0]["text"] == "answer"
[entry] = monitor.snapshot()
assert entry["status"] == "completed"
@@ -918,8 +928,12 @@ class TestResponsesNonStreamingAdapter:
assert monitor.active_count() == 0
assert request.state.skip_api_monitor is False
- def test_literal_think_tags_remain_visible_without_reasoning_request(self, monkeypatch):
- body = self._run_with_message(monkeypatch, {"content": "show x tags"})
+ def test_literal_think_tags_remain_visible_without_reasoning_request(
+ self, monkeypatch
+ ):
+ body = self._run_with_message(
+ monkeypatch, {"content": "show x tags"}
+ )
assert [item["type"] for item in body["output"]] == ["message"]
assert body["output"][0]["content"][0]["text"] == "show x tags"
@@ -952,10 +966,14 @@ class TestResponsesNonStreamingAdapter:
)
assert [item["type"] for item in body["output"]] == ["reasoning", "message"]
- assert body["output"][0]["content"] == [{"type": "reasoning_text", "text": "plan"}]
+ assert body["output"][0]["content"] == [
+ {"type": "reasoning_text", "text": "plan"}
+ ]
assert body["output"][1]["content"][0]["text"] == "answer"
- def test_reasoning_capable_gguf_sanitizes_think_tags_when_disabled(self, monkeypatch):
+ def test_reasoning_capable_gguf_sanitizes_think_tags_when_disabled(
+ self, monkeypatch
+ ):
payload = ResponsesRequest(input = "hi", reasoning = {"effort": "none"})
body = self._run_with_message(
monkeypatch,
@@ -969,7 +987,9 @@ class TestResponsesNonStreamingAdapter:
)
assert [item["type"] for item in body["output"]] == ["reasoning", "message"]
- assert body["output"][0]["content"] == [{"type": "reasoning_text", "text": "leaked"}]
+ assert body["output"][0]["content"] == [
+ {"type": "reasoning_text", "text": "leaked"}
+ ]
assert body["output"][1]["content"][0]["text"] == "answer"
def test_structured_reasoning_content_extracts_text_parts(self, monkeypatch):
@@ -985,7 +1005,9 @@ class TestResponsesNonStreamingAdapter:
)
assert [item["type"] for item in body["output"]] == ["reasoning", "message"]
- assert body["output"][0]["content"] == [{"type": "reasoning_text", "text": "plan next"}]
+ assert body["output"][0]["content"] == [
+ {"type": "reasoning_text", "text": "plan next"}
+ ]
assert body["output"][1]["content"][0]["text"] == "33"
def test_plain_content_remains_message_only(self, monkeypatch):
@@ -1072,7 +1094,9 @@ class TestResponsesStreamAdapter:
supports_reasoning = supports_reasoning,
reasoning_always_on = reasoning_always_on,
_request_reasoning_kwargs = (
- lambda enable_thinking = None, reasoning_effort = None, preserve_thinking = None: None
+ lambda enable_thinking = None,
+ reasoning_effort = None,
+ preserve_thinking = None: None
),
),
)
@@ -1092,12 +1116,16 @@ class TestResponsesStreamAdapter:
sent = []
async def receive():
- raise AssertionError("Responses streams poll disconnects in the generator")
+ raise AssertionError(
+ "Responses streams poll disconnects in the generator"
+ )
async def send(message):
sent.append(message)
- await response({"type": "http", "asgi": {"spec_version": "2.3"}}, receive, send)
+ await response(
+ {"type": "http", "asgi": {"spec_version": "2.3"}}, receive, send
+ )
return sent
sent = asyncio.run(run())
@@ -1107,7 +1135,9 @@ class TestResponsesStreamAdapter:
assert "response.output_text.delta" in body
assert '"delta":"33"' in body.replace(" ", "")
- def test_split_think_markers_stream_as_reasoning_and_visible_text(self, monkeypatch):
+ def test_split_think_markers_stream_as_reasoning_and_visible_text(
+ self, monkeypatch
+ ):
chunks = [
{"choices": [{"delta": {"content": "pla"}}]},
@@ -1116,7 +1146,9 @@ class TestResponsesStreamAdapter:
{"choices": [], "usage": {"prompt_tokens": 2, "completion_tokens": 3}},
]
self._install_stream_mock(monkeypatch, chunks)
- payload = ResponsesRequest(input = "hi", stream = True, reasoning = {"effort": "high"})
+ payload = ResponsesRequest(
+ input = "hi", stream = True, reasoning = {"effort": "high"}
+ )
messages = [ChatMessage(role = "user", content = "hi")]
async def run():
@@ -1221,7 +1253,10 @@ class TestResponsesStreamAdapter:
lines = asyncio.run(run())
- assert self._payloads(lines, "response.output_item.done")[-1]["item"]["name"] == "lookup"
+ assert (
+ self._payloads(lines, "response.output_item.done")[-1]["item"]["name"]
+ == "lookup"
+ )
[entry] = monitor.snapshot()
assert entry["status"] == "completed"
assert entry["reply"] == 'Tool call: lookup({"query":"weather"})'
@@ -1275,7 +1310,9 @@ class TestResponsesStreamAdapter:
self._install_stream_mock(monkeypatch, [])
monitor = ApiMonitor(max_entries = 3)
monkeypatch.setattr(inf_mod, "api_monitor", monitor)
- monkeypatch.setattr(inf_mod, "_send_stream_with_preheader_cancel", fake_send)
+ monkeypatch.setattr(
+ inf_mod, "_send_stream_with_preheader_cancel", fake_send
+ )
monkeypatch.setattr(inf_mod, "_aiter_llama_stream_items", fake_items)
monitor_id = monitor.start(
endpoint = "/v1/responses",
@@ -1331,7 +1368,9 @@ class TestResponsesStreamAdapter:
def finish(self):
return "", "tail"
- self._install_stream_mock(monkeypatch, [{"choices": [{"delta": {"content": ""}}]}])
+ self._install_stream_mock(
+ monkeypatch, [{"choices": [{"delta": {"content": ""}}]}]
+ )
monitor = ApiMonitor(max_entries = 3)
monkeypatch.setattr(inf_mod, "api_monitor", monitor)
monkeypatch.setattr(inf_mod, "_ResponsesReasoningExtractor", FakeExtractor)
@@ -1402,12 +1447,17 @@ class TestResponsesStreamAdapter:
lines = asyncio.run(run())
assert self._payloads(lines, "response.output_text.delta") == []
- assert self._payloads(lines, "response.reasoning_text.delta")[-1]["delta"] == "plan"
+ assert (
+ self._payloads(lines, "response.reasoning_text.delta")[-1]["delta"]
+ == "plan"
+ )
[entry] = monitor.snapshot()
assert entry["status"] == "completed"
assert entry["reply"] == ""
- def test_reasoning_capable_gguf_stream_parses_think_tags_by_default(self, monkeypatch):
+ def test_reasoning_capable_gguf_stream_parses_think_tags_by_default(
+ self, monkeypatch
+ ):
chunks = [
{"choices": [{"delta": {"content": "plananswer"}}]},
@@ -1435,14 +1485,18 @@ class TestResponsesStreamAdapter:
assert completed["response"]["output"][0]["content"][0]["text"] == "plan"
assert completed["response"]["output"][1]["content"][0]["text"] == "answer"
- def test_non_reasoning_gguf_stream_keeps_literal_think_tags_visible(self, monkeypatch):
+ def test_non_reasoning_gguf_stream_keeps_literal_think_tags_visible(
+ self, monkeypatch
+ ):
chunks = [
{"choices": [{"delta": {"content": "show x tags"}}]},
{"choices": [], "usage": {"prompt_tokens": 2, "completion_tokens": 3}},
]
self._install_stream_mock(monkeypatch, chunks, supports_reasoning = False)
- payload = ResponsesRequest(input = "hi", stream = True, reasoning = {"effort": "high"})
+ payload = ResponsesRequest(
+ input = "hi", stream = True, reasoning = {"effort": "high"}
+ )
messages = [ChatMessage(role = "user", content = "hi")]
async def run():
@@ -1454,7 +1508,10 @@ class TestResponsesStreamAdapter:
reasoning_deltas = self._payloads(lines, "response.reasoning_text.delta")
text_deltas = self._payloads(lines, "response.output_text.delta")
assert reasoning_deltas == []
- assert "".join(event["delta"] for event in text_deltas) == "show x tags"
+ assert (
+ "".join(event["delta"] for event in text_deltas)
+ == "show x tags"
+ )
completed = self._payloads(lines, "response.completed")[0]
assert [item["type"] for item in completed["response"]["output"]] == ["message"]
assert completed["response"]["output"][0]["content"][0]["text"] == (
@@ -1467,7 +1524,9 @@ class TestResponsesStreamAdapter:
{"choices": [], "usage": {"prompt_tokens": 2, "completion_tokens": 3}},
]
self._install_stream_mock(monkeypatch, chunks)
- payload = ResponsesRequest(input = "hi", stream = True, reasoning = {"effort": "high"})
+ payload = ResponsesRequest(
+ input = "hi", stream = True, reasoning = {"effort": "high"}
+ )
messages = [ChatMessage(role = "user", content = "hi")]
async def run():
@@ -1481,7 +1540,9 @@ class TestResponsesStreamAdapter:
assert "".join(event["delta"] for event in reasoning_deltas) == "plan"
assert text_deltas == []
completed = self._payloads(lines, "response.completed")[0]
- assert [item["type"] for item in completed["response"]["output"]] == ["reasoning"]
+ assert [item["type"] for item in completed["response"]["output"]] == [
+ "reasoning"
+ ]
assert completed["response"]["output"][0]["content"][0]["text"] == "plan"
def test_unclosed_think_stream_stays_out_of_visible_message_text(self, monkeypatch):
@@ -1491,7 +1552,9 @@ class TestResponsesStreamAdapter:
{"choices": [], "usage": {"prompt_tokens": 2, "completion_tokens": 3}},
]
self._install_stream_mock(monkeypatch, chunks)
- payload = ResponsesRequest(input = "hi", stream = True, reasoning = {"effort": "high"})
+ payload = ResponsesRequest(
+ input = "hi", stream = True, reasoning = {"effort": "high"}
+ )
messages = [ChatMessage(role = "user", content = "hi")]
async def run():
@@ -1505,7 +1568,9 @@ class TestResponsesStreamAdapter:
assert "".join(event["delta"] for event in reasoning_deltas) == "plan"
assert text_deltas == []
completed = self._payloads(lines, "response.completed")[0]
- assert [item["type"] for item in completed["response"]["output"]] == ["reasoning"]
+ assert [item["type"] for item in completed["response"]["output"]] == [
+ "reasoning"
+ ]
assert completed["response"]["output"][0]["content"][0]["text"] == "plan"
def test_structured_reasoning_content_streams_as_reasoning(self, monkeypatch):
@@ -1565,7 +1630,9 @@ class TestResponsesStreamAdapter:
text_deltas = self._payloads(lines, "response.output_text.delta")
assert "".join(event["delta"] for event in reasoning_deltas) == "plan next"
assert "".join(event["delta"] for event in text_deltas) == "33"
- assert "reasoning_text" not in "".join(event["delta"] for event in reasoning_deltas)
+ assert "reasoning_text" not in "".join(
+ event["delta"] for event in reasoning_deltas
+ )
completed = self._payloads(lines, "response.completed")[0]
assert completed["response"]["output"][0]["content"][0]["text"] == "plan next"
assert completed["response"]["output"][1]["content"][0]["text"] == "33"
@@ -1603,7 +1670,10 @@ class TestResponsesStreamAdapter:
done_events = self._payloads(lines, "response.output_item.done")
assert [event["output_index"] for event in done_events] == [0, 1]
- assert [event["item"]["type"] for event in done_events] == ["function_call", "message"]
+ assert [event["item"]["type"] for event in done_events] == [
+ "function_call",
+ "message",
+ ]
completed = self._payloads(lines, "response.completed")[0]
assert [item["type"] for item in completed["response"]["output"]] == [
"function_call",
@@ -1627,13 +1697,19 @@ class TestResponsesStreamAdapter:
"index": 0,
"id": "call_0",
"type": "function",
- "function": {"name": "first", "arguments": "{}"},
+ "function": {
+ "name": "first",
+ "arguments": "{}",
+ },
},
{
"index": 1,
"id": "call_1",
"type": "function",
- "function": {"name": "second", "arguments": "{}"},
+ "function": {
+ "name": "second",
+ "arguments": "{}",
+ },
},
]
}
@@ -1670,7 +1746,9 @@ class TestResponsesStreamAdapter:
base_url = "http://llama.test",
# Non-reasoning template: the real backend returns None here.
_request_reasoning_kwargs = (
- lambda enable_thinking = None, reasoning_effort = None, preserve_thinking = None: None
+ lambda enable_thinking = None,
+ reasoning_effort = None,
+ preserve_thinking = None: None
),
),
)
@@ -1719,7 +1797,9 @@ class TestResponsesStreamAdapter:
class TestResponsesOutputFunctionCall:
def test_reasoning_output_item_serialises_full_reasoning_content(self):
- item = ResponsesOutputReasoning(content = [{"type": "reasoning_text", "text": "plan"}])
+ item = ResponsesOutputReasoning(
+ content = [{"type": "reasoning_text", "text": "plan"}]
+ )
d = item.model_dump()
assert d["type"] == "reasoning"
assert d["id"].startswith("rs_")
@@ -1845,7 +1925,9 @@ class TestCodexStyleRequestShapes:
msgs = _normalise_responses_input(payload)
assert [m.role for m in msgs] == ["user", "assistant", "user"]
- assert all("plan" not in (m.content or "") for m in msgs if isinstance(m.content, str))
+ assert all(
+ "plan" not in (m.content or "") for m in msgs if isinstance(m.content, str)
+ )
def test_unknown_content_part_type_accepted(self):
"""Unknown content-part types (e.g. future input_audio) validate as
@@ -1936,7 +2018,9 @@ class TestCodexStyleRequestShapes:
input = [
{
"role": "assistant",
- "content": [{"type": "output_text", "text": "ok", "annotations": []}],
+ "content": [
+ {"type": "output_text", "text": "ok", "annotations": []}
+ ],
},
{"role": "user", "content": "next"},
],
@@ -2015,7 +2099,9 @@ class TestReasoningPrefilledExtractor:
def test_prefilled_close_split_across_feeds(self):
# T3: straddles two feed() calls; holdback resolves it.
- ex = _ResponsesReasoningExtractor(parse_think_markers = True, reasoning_prefilled = True)
+ ex = _ResponsesReasoningExtractor(
+ parse_think_markers = True, reasoning_prefilled = True
+ )
r1, v1 = ex.feed("planans")
fr, fv = ex.finish()
@@ -2024,7 +2110,9 @@ class TestReasoningPrefilledExtractor:
def test_prefilled_close_split_one_char_per_feed(self):
# T4: every char in its own feed still splits correctly.
- ex = _ResponsesReasoningExtractor(parse_think_markers = True, reasoning_prefilled = True)
+ ex = _ResponsesReasoningExtractor(
+ parse_think_markers = True, reasoning_prefilled = True
+ )
reasoning, visible = "", ""
for ch in "planx":
r, v = ex.feed(ch)
@@ -2136,7 +2224,9 @@ class TestResponsesStreamHealing:
TestResponsesStreamAdapter._install_stream_mock(
monkeypatch, [{"choices": [{"delta": {"content": content}}]}]
)
- payload = ResponsesRequest(input = "hi", stream = True, tools = [self._TOOL], **payload_kwargs)
+ payload = ResponsesRequest(
+ input = "hi", stream = True, tools = [self._TOOL], **payload_kwargs
+ )
messages = [ChatMessage(role = "user", content = "hi")]
async def run():
@@ -2169,7 +2259,9 @@ class TestResponsesStreamHealing:
def test_call_before_trailing_text_claims_lower_output_index(self, monkeypatch):
events = self._run_stream(monkeypatch, f"{self._XML} done.")
item_added = [
- (name, payload) for name, payload in events if name == "response.output_item.added"
+ (name, payload)
+ for name, payload in events
+ if name == "response.output_item.added"
]
# The call came first in the model output, so its item is added first
# and claims the lower output_index; the trailing text's message item
@@ -2182,7 +2274,9 @@ class TestResponsesStreamHealing:
msg_idx = item_added[1][1]["output_index"]
assert call_idx < msg_idx
text = "".join(
- payload["delta"] for name, payload in events if name == "response.output_text.delta"
+ payload["delta"]
+ for name, payload in events
+ if name == "response.output_text.delta"
)
assert "done." in text
assert "" not in text
@@ -2195,7 +2289,9 @@ class TestResponsesStreamHealing:
if name == "response.output_item.added"
)
text = "".join(
- payload["delta"] for name, payload in events if name == "response.output_text.delta"
+ payload["delta"]
+ for name, payload in events
+ if name == "response.output_text.delta"
)
assert text == self._XML
@@ -2205,7 +2301,11 @@ class TestResponsesStreamHealing:
# one with a later output index (native Responses stream shape).
events = self._run_stream(monkeypatch, f"before {self._XML} after.")
added = [
- (payload["output_index"], payload["item"]["type"], payload["item"].get("id"))
+ (
+ payload["output_index"],
+ payload["item"]["type"],
+ payload["item"].get("id"),
+ )
for name, payload in events
if name == "response.output_item.added"
]
@@ -2225,9 +2325,15 @@ class TestResponsesStreamHealing:
assert [d for i, d in deltas if i == added[0][2]] == ["before "]
assert [d for i, d in deltas if i == added[2][2]] == [" after."]
# The completed snapshot lists all three items with per-item text.
- completed = [payload for name, payload in events if name == "response.completed"]
+ completed = [
+ payload for name, payload in events if name == "response.completed"
+ ]
output = completed[0]["response"]["output"]
- assert [item["type"] for item in output] == ["message", "function_call", "message"]
+ assert [item["type"] for item in output] == [
+ "message",
+ "function_call",
+ "message",
+ ]
assert output[0]["content"][0]["text"] == "before "
assert output[2]["content"][0]["text"] == " after."
@@ -2248,7 +2354,10 @@ class TestResponsesStreamHealing:
{
"index": 0,
"id": "call_up",
- "function": {"name": "lookup", "arguments": "{}"},
+ "function": {
+ "name": "lookup",
+ "arguments": "{}",
+ },
}
]
}
@@ -2275,7 +2384,8 @@ class TestResponsesStreamHealing:
calls = [
payload
for name, payload in events
- if name == "response.output_item.added" and payload["item"]["type"] == "function_call"
+ if name == "response.output_item.added"
+ and payload["item"]["type"] == "function_call"
]
assert len(calls) == 1
assert calls[0]["item"]["name"] == "lookup"
diff --git a/studio/backend/tests/test_rocm_oom_guard.py b/studio/backend/tests/test_rocm_oom_guard.py
index 699d0b74f5..bd48bd1c3b 100644
--- a/studio/backend/tests/test_rocm_oom_guard.py
+++ b/studio/backend/tests/test_rocm_oom_guard.py
@@ -172,7 +172,9 @@ class TestDeviceNameFallback:
props = _props(name = device_name)
gcn, is_unified = _rocm_classify_unified_memory(props)
assert gcn == "", f"expected empty gcn_arch, got {gcn!r}"
- assert is_unified is True, f"device {device_name!r} should be classified as unified-memory"
+ assert (
+ is_unified is True
+ ), f"device {device_name!r} should be classified as unified-memory"
# --- discrete devices that must NOT be mis-classified ---
diff --git a/studio/backend/tests/test_rocm_windows_vram_7072.py b/studio/backend/tests/test_rocm_windows_vram_7072.py
index b4079831b7..6241b51553 100644
--- a/studio/backend/tests/test_rocm_windows_vram_7072.py
+++ b/studio/backend/tests/test_rocm_windows_vram_7072.py
@@ -92,7 +92,9 @@ def _subprocess_run(*, adapter_output = "__NONE__\n", util_output = "12.0\n"):
out = util_output
else:
out = "-1\n"
- return subprocess.CompletedProcess(args = cmd, returncode = 0, stdout = out, stderr = "")
+ return subprocess.CompletedProcess(
+ args = cmd, returncode = 0, stdout = out, stderr = ""
+ )
return fake_run
@@ -124,9 +126,13 @@ DEVICES = [("AMD Radeon PRO W7900", 48 * GB), ("AMD Radeon PRO W7500", 8 * GB)]
# System tab (get_visible_gpu_utilization) -- the reporter's screenshot
# ----------------------------------------------------------------------------- #
def test_system_tab_shows_per_gpu_used(win_rocm, monkeypatch):
- monkeypatch.setitem(sys.modules, "torch", _fake_torch(DEVICES, free_equals_total = True))
+ monkeypatch.setitem(
+ sys.modules, "torch", _fake_torch(DEVICES, free_equals_total = True)
+ )
monkeypatch.setattr(
- hw.subprocess, "run", _subprocess_run(adapter_output = _adapter_output(REPORTER_ADAPTERS))
+ hw.subprocess,
+ "run",
+ _subprocess_run(adapter_output = _adapter_output(REPORTER_ADAPTERS)),
)
devices = hw.get_visible_gpu_utilization()["devices"]
@@ -140,14 +146,20 @@ def test_system_tab_shows_per_gpu_used(win_rocm, monkeypatch):
assert by_idx[1]["vram_used_gb"] is None
assert by_idx[1]["vram_utilization_pct"] is None
assert all(
- d["vram_used_gb"] <= d["vram_total_gb"] for d in devices if d["vram_used_gb"] is not None
+ d["vram_used_gb"] <= d["vram_total_gb"]
+ for d in devices
+ if d["vram_used_gb"] is not None
)
def test_gpu_utilization_does_not_collapse(win_rocm, monkeypatch):
- monkeypatch.setitem(sys.modules, "torch", _fake_torch(DEVICES, free_equals_total = True))
+ monkeypatch.setitem(
+ sys.modules, "torch", _fake_torch(DEVICES, free_equals_total = True)
+ )
monkeypatch.setattr(
- hw.subprocess, "run", _subprocess_run(adapter_output = _adapter_output(REPORTER_ADAPTERS))
+ hw.subprocess,
+ "run",
+ _subprocess_run(adapter_output = _adapter_output(REPORTER_ADAPTERS)),
)
result = hw.get_gpu_utilization()
@@ -158,8 +170,12 @@ def test_gpu_utilization_does_not_collapse(win_rocm, monkeypatch):
def test_localized_counter_reports_unknown_not_zero(win_rocm, monkeypatch):
- monkeypatch.setitem(sys.modules, "torch", _fake_torch(DEVICES, free_equals_total = True))
- monkeypatch.setattr(hw.subprocess, "run", _subprocess_run(adapter_output = "__NONE__\n"))
+ monkeypatch.setitem(
+ sys.modules, "torch", _fake_torch(DEVICES, free_equals_total = True)
+ )
+ monkeypatch.setattr(
+ hw.subprocess, "run", _subprocess_run(adapter_output = "__NONE__\n")
+ )
devices = hw.get_visible_gpu_utilization()["devices"]
assert len(devices) == 2 # both still shown with correct totals
@@ -197,12 +213,19 @@ def test_mem_get_info_guard_scopes_to_windows_rocm(monkeypatch):
# Per-adapter attribution helpers (pure unit)
# ----------------------------------------------------------------------------- #
def test_match_adapter_pairs_and_clamps():
- assert hw._match_adapter_used_to_devices([40 * GB, 0.5 * GB], [48 * GB, 8 * GB]) == [
+ assert hw._match_adapter_used_to_devices(
+ [40 * GB, 0.5 * GB], [48 * GB, 8 * GB]
+ ) == [
40 * GB,
0.5 * GB,
]
- assert hw._match_adapter_used_to_devices([100 * GB], [48 * GB]) == [48 * GB] # clamp
- assert hw._match_adapter_used_to_devices([40 * GB], [48 * GB, 8 * GB]) == [40 * GB, None]
+ assert hw._match_adapter_used_to_devices([100 * GB], [48 * GB]) == [
+ 48 * GB
+ ] # clamp
+ assert hw._match_adapter_used_to_devices([40 * GB], [48 * GB, 8 * GB]) == [
+ 40 * GB,
+ None,
+ ]
def test_match_adapter_reports_unknown_when_more_active_than_visible():
@@ -227,7 +250,9 @@ def test_match_adapter_reports_unknown_for_placeholder_fallback():
# Order of the counters must not matter.
assert hw._match_adapter_used_to_devices([10 * MiB, 50 * MiB], [8 * GB]) == [None]
# Two idle visible GPUs plus a placeholder: all three counters below the floor.
- assert hw._match_adapter_used_to_devices([50 * MiB, 10 * MiB, 5 * MiB], [48 * GB, 8 * GB]) == [
+ assert hw._match_adapter_used_to_devices(
+ [50 * MiB, 10 * MiB, 5 * MiB], [48 * GB, 8 * GB]
+ ) == [
None,
None,
]
@@ -236,28 +261,47 @@ def test_match_adapter_reports_unknown_for_placeholder_fallback():
def test_match_adapter_reports_unknown_when_usage_not_capacity_ordered():
# 8 GiB card at 7 GiB beside a 48 GiB card at 5 GiB: the bigger usage still fits
# the smaller card, so both pairings are feasible -> unknown.
- assert hw._match_adapter_used_to_devices([7 * GB, 5 * GB], [8 * GB, 48 * GB]) == [None, None]
+ assert hw._match_adapter_used_to_devices([7 * GB, 5 * GB], [8 * GB, 48 * GB]) == [
+ None,
+ None,
+ ]
# Device order must not matter (same physical situation, ordinals flipped).
- assert hw._match_adapter_used_to_devices([7 * GB, 5 * GB], [48 * GB, 8 * GB]) == [None, None]
+ assert hw._match_adapter_used_to_devices([7 * GB, 5 * GB], [48 * GB, 8 * GB]) == [
+ None,
+ None,
+ ]
# Same-capacity cards with unequal usage are equally unattributable.
- assert hw._match_adapter_used_to_devices([12 * GB, 8 * GB], [24 * GB, 24 * GB]) == [None, None]
+ assert hw._match_adapter_used_to_devices([12 * GB, 8 * GB], [24 * GB, 24 * GB]) == [
+ None,
+ None,
+ ]
# A single usage that fits both cards can sit on either -> unknown.
- assert hw._match_adapter_used_to_devices([5 * GB], [48 * GB, 8 * GB]) == [None, None]
+ assert hw._match_adapter_used_to_devices([5 * GB], [48 * GB, 8 * GB]) == [
+ None,
+ None,
+ ]
# But a capacity-forced assignment (usage exceeds the smaller card) is kept:
# 40 GiB can only be the 48 GiB card, so it is not fabrication.
- assert hw._match_adapter_used_to_devices([40 * GB], [48 * GB, 8 * GB]) == [40 * GB, None]
+ assert hw._match_adapter_used_to_devices([40 * GB], [48 * GB, 8 * GB]) == [
+ 40 * GB,
+ None,
+ ]
def test_match_adapter_reports_unknown_when_hidden_usage_fits_visible_card():
# A survivor that merely *fits* a visible card must not be pinned onto it. Two
# cards (48/8 GiB) at 40 GiB / 10 MiB beside a hidden 6 GiB adapter: the 6 GiB
# fits the idle 8 GiB card but isn't forced -> Unknown; only 40 GiB is forced.
- assert hw._match_adapter_used_to_devices([40 * GB, 10 * MiB, 6 * GB], [48 * GB, 8 * GB]) == [
+ assert hw._match_adapter_used_to_devices(
+ [40 * GB, 10 * MiB, 6 * GB], [48 * GB, 8 * GB]
+ ) == [
40 * GB,
None,
]
# Counter order must not matter.
- assert hw._match_adapter_used_to_devices([6 * GB, 40 * GB, 10 * MiB], [48 * GB, 8 * GB]) == [
+ assert hw._match_adapter_used_to_devices(
+ [6 * GB, 40 * GB, 10 * MiB], [48 * GB, 8 * GB]
+ ) == [
40 * GB,
None,
]
@@ -307,7 +351,10 @@ def test_match_adapter_capacity_forced_matrix():
assert m([48 * GB, 3 * MiB, 3 * MiB], [24 * GB, 8 * GB]) == [None, None]
# -- more active adapters than visible cards -> all unknown --------------- #
assert m([40 * GB, 7 * GB, 6 * GB, 3 * MiB], [48 * GB, 8 * GB]) == [None, None]
- assert m([40 * GB, 7 * GB, 6 * GB, 3 * MiB, 3 * MiB], [48 * GB, 8 * GB]) == [None, None]
+ assert m([40 * GB, 7 * GB, 6 * GB, 3 * MiB, 3 * MiB], [48 * GB, 8 * GB]) == [
+ None,
+ None,
+ ]
# -- every counter below the noise floor (placeholder fallback) -> unknown - #
assert m([50 * MiB, 10 * MiB], [8 * GB]) == [None]
assert m([50 * MiB, 10 * MiB, 5 * MiB], [48 * GB, 8 * GB]) == [None, None]
@@ -319,12 +366,16 @@ def test_match_adapter_capacity_forced_matrix():
def test_perf_counter_parser_and_sentinel(monkeypatch):
monkeypatch.setattr(hw.platform, "system", lambda: "Windows")
monkeypatch.setattr(
- hw.subprocess, "run", _subprocess_run(adapter_output = _adapter_output(REPORTER_ADAPTERS))
+ hw.subprocess,
+ "run",
+ _subprocess_run(adapter_output = _adapter_output(REPORTER_ADAPTERS)),
)
parsed = hw._rocm_windows_perf_counter_vram_by_adapter()
assert parsed is not None and len(parsed) == 3
assert parsed[0][0].startswith("luid_")
- monkeypatch.setattr(hw.subprocess, "run", _subprocess_run(adapter_output = "__NONE__\n"))
+ monkeypatch.setattr(
+ hw.subprocess, "run", _subprocess_run(adapter_output = "__NONE__\n")
+ )
assert hw._rocm_windows_perf_counter_vram_by_adapter() is None
@@ -336,8 +387,12 @@ def test_unified_memory_adopts_torch_total_even_when_used_unknown():
GTT pool) is authoritative. The correction must still adopt the larger total;
used stays at amd-smi's figure when torch's is unknown."""
metrics = {"vram_total_gb": 8.0, "vram_used_gb": 2.0, "vram_utilization_pct": 25.0}
- hw._apply_unified_memory_correction(metrics, {"total_gb": 124.0, "used_gb": None, "index": 0})
- assert metrics["vram_total_gb"] == 124.0 # full unified pool, not the 8 GB carve-out
+ hw._apply_unified_memory_correction(
+ metrics, {"total_gb": 124.0, "used_gb": None, "index": 0}
+ )
+ assert (
+ metrics["vram_total_gb"] == 124.0
+ ) # full unified pool, not the 8 GB carve-out
assert metrics["vram_used_gb"] == 2.0 # amd-smi used preserved (torch's was None)
assert metrics["vram_utilization_pct"] == pytest.approx(round(2.0 / 124.0 * 100, 1))
@@ -346,16 +401,26 @@ def test_unified_memory_overwrites_used_when_torch_used_known():
"""When torch reports both a larger total and a known used, both are adopted
and utilization is recomputed against the corrected total (unchanged path)."""
metrics = {"vram_total_gb": 8.0, "vram_used_gb": 2.0, "vram_utilization_pct": 25.0}
- hw._apply_unified_memory_correction(metrics, {"total_gb": 124.0, "used_gb": 40.0, "index": 0})
+ hw._apply_unified_memory_correction(
+ metrics, {"total_gb": 124.0, "used_gb": 40.0, "index": 0}
+ )
assert metrics["vram_total_gb"] == 124.0
assert metrics["vram_used_gb"] == 40.0
- assert metrics["vram_utilization_pct"] == pytest.approx(round(40.0 / 124.0 * 100, 1))
+ assert metrics["vram_utilization_pct"] == pytest.approx(
+ round(40.0 / 124.0 * 100, 1)
+ )
def test_unified_memory_no_op_when_torch_total_not_larger():
"""A discrete GPU where torch total does not exceed amd-smi's is left untouched."""
- metrics = {"vram_total_gb": 48.0, "vram_used_gb": 10.0, "vram_utilization_pct": 20.8}
- hw._apply_unified_memory_correction(metrics, {"total_gb": 48.0, "used_gb": None, "index": 0})
+ metrics = {
+ "vram_total_gb": 48.0,
+ "vram_used_gb": 10.0,
+ "vram_utilization_pct": 20.8,
+ }
+ hw._apply_unified_memory_correction(
+ metrics, {"total_gb": 48.0, "used_gb": None, "index": 0}
+ )
assert metrics["vram_total_gb"] == 48.0
assert metrics["vram_used_gb"] == 10.0
assert metrics["vram_utilization_pct"] == 20.8
diff --git a/studio/backend/tests/test_s3_dataset.py b/studio/backend/tests/test_s3_dataset.py
index f47db565ff..791954b8f4 100644
--- a/studio/backend/tests/test_s3_dataset.py
+++ b/studio/backend/tests/test_s3_dataset.py
@@ -37,7 +37,9 @@ class _FakePaginator:
def paginate(self, **kwargs):
prefix = kwargs.get("Prefix")
- contents = [{"Key": k} for k in self._keys if prefix is None or k.startswith(prefix)]
+ contents = [
+ {"Key": k} for k in self._keys if prefix is None or k.startswith(prefix)
+ ]
# Emit in two pages to exercise pagination handling.
mid = len(contents) // 2
yield {"Contents": contents[:mid]}
diff --git a/studio/backend/tests/test_safetensors_capability_advertise.py b/studio/backend/tests/test_safetensors_capability_advertise.py
index bd3d8d16b9..1ada081f3e 100644
--- a/studio/backend/tests/test_safetensors_capability_advertise.py
+++ b/studio/backend/tests/test_safetensors_capability_advertise.py
@@ -111,7 +111,9 @@ def test_detect_reasoning_flags_deepseek_v4_exposes_none_high_max():
though the template only branches on 'max'."""
from core.inference.llama_cpp import detect_reasoning_flags
- flags = detect_reasoning_flags(DEEPSEEK_V4_TEMPLATE, "unsloth/DeepSeek-V4-Flash-GGUF")
+ flags = detect_reasoning_flags(
+ DEEPSEEK_V4_TEMPLATE, "unsloth/DeepSeek-V4-Flash-GGUF"
+ )
assert flags["supports_reasoning"] is True
assert flags["reasoning_style"] == "enable_thinking_effort"
assert flags["reasoning_effort_levels"] == ["high", "max"]
@@ -419,9 +421,14 @@ def test_detect_safetensors_features_gemma_native_tool_call_keeps_tools_on():
def test_detect_safetensors_features_gemma_native_reasoning_is_parseable_not_prefilled():
"""Native Gemma channels are normalized to , then split by the route."""
- from routes.inference import _detect_safetensors_features, _sf_reasoning_prefill_mode
+ from routes.inference import (
+ _detect_safetensors_features,
+ _sf_reasoning_prefill_mode,
+ )
- tpl_with_gemma_native = "{% if add_generation_prompt %}<|channel>thought\n{% endif %}"
+ tpl_with_gemma_native = (
+ "{% if add_generation_prompt %}<|channel>thought\n{% endif %}"
+ )
backend = SimpleNamespace(
active_model_name = "unsloth/gemma-4-E2B-it",
models = {
@@ -453,7 +460,9 @@ def test_detect_safetensors_features_selects_native_reasoning_from_tool_template
models = {
"custom/named-native-reasoning": {
"native_chat_template": named_template,
- "chat_template_info": {"template": "{% if tools %}{% endif %}"},
+ "chat_template_info": {
+ "template": "{% if tools %}{% endif %}"
+ },
}
},
)
@@ -631,7 +640,11 @@ def test_worker_load_reply_payload_includes_chat_template_info():
"is_gguf": False,
}
_bm = getattr(backend, "models", {}) or {}
- _entry = _bm.get(mc.identifier) or _bm.get(getattr(backend, "active_model_name", None)) 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"] = {
@@ -804,7 +817,9 @@ class TestSafetensorsReasoningPrefillGate:
# A minimal Qwen3-style template with the standard / markers.
_QWEN_TPL = "{% if enable_thinking %}{% endif %}......"
# gemma-style bespoke reasoning channel -- no standard markers.
- _GEMMA_TPL = "{% if enable_thinking %}<|think|>{% endif %}<|channel>thought"
+ _GEMMA_TPL = (
+ "{% if enable_thinking %}<|think|>{% endif %}<|channel>thought"
+ )
# always-on template whose GENERATION PROMPT opens an unclosed (DeepSeek-R1 / QwQ /
# Qwen3-Thinking shape): the model emits only the closing , so prefill.
_ALWAYS_ON_OPEN_TPL = (
@@ -834,17 +849,23 @@ class TestSafetensorsReasoningPrefillGate:
def test_g1_enable_thinking_true(self):
# G1: Qwen3.5 template + explicit enable_thinking=True -> prefilled.
from routes.inference import _sf_reasoning_prefill_mode
- assert _sf_reasoning_prefill_mode(self._features(), True, self._QWEN_TPL) is True
+ assert (
+ _sf_reasoning_prefill_mode(self._features(), True, self._QWEN_TPL) is True
+ )
def test_g2_enable_thinking_none_defaults_on(self):
# G2: default request (None) -> prefilled (Qwen3/GLM templates default on).
from routes.inference import _sf_reasoning_prefill_mode
- assert _sf_reasoning_prefill_mode(self._features(), None, self._QWEN_TPL) is True
+ assert (
+ _sf_reasoning_prefill_mode(self._features(), None, self._QWEN_TPL) is True
+ )
def test_g3_enable_thinking_false(self):
# G3: thinking explicitly off -> not prefilled.
from routes.inference import _sf_reasoning_prefill_mode
- assert _sf_reasoning_prefill_mode(self._features(), False, self._QWEN_TPL) is False
+ assert (
+ _sf_reasoning_prefill_mode(self._features(), False, self._QWEN_TPL) is False
+ )
def test_g4_gpt_oss_reasoning_effort_excluded(self):
# G4: gpt-oss uses explicit tags via HarmonyTextStreamer -> normal mode.
@@ -868,7 +889,9 @@ class TestSafetensorsReasoningPrefillGate:
# G7: always-on template whose generation prompt opens -> prefilled regardless of the flag.
from routes.inference import _sf_reasoning_prefill_mode
feats = self._features(reasoning_always_on = True)
- assert _sf_reasoning_prefill_mode(feats, False, self._ALWAYS_ON_OPEN_TPL) is True
+ assert (
+ _sf_reasoning_prefill_mode(feats, False, self._ALWAYS_ON_OPEN_TPL) is True
+ )
def test_g7b_reasoning_always_on_history_only_not_prefilled(self):
# G7b (#5704): always-on classification from rendered assistant HISTORY
@@ -876,13 +899,18 @@ class TestSafetensorsReasoningPrefillGate:
# normal answer entirely as reasoning_content and blank the visible answer, so it must be off.
from routes.inference import _sf_reasoning_prefill_mode
feats = self._features(reasoning_always_on = True)
- assert _sf_reasoning_prefill_mode(feats, None, self._ALWAYS_ON_HISTORY_TPL) is False
+ assert (
+ _sf_reasoning_prefill_mode(feats, None, self._ALWAYS_ON_HISTORY_TPL)
+ is False
+ )
def test_g8_gemma_bespoke_channel_excluded(self):
# G8: gemma's <|think|>/<|channel> format has no -> NOT prefilled
# (would otherwise swallow the whole answer as reasoning). Regression guard.
from routes.inference import _sf_reasoning_prefill_mode
- assert _sf_reasoning_prefill_mode(self._features(), True, self._GEMMA_TPL) is False
+ assert (
+ _sf_reasoning_prefill_mode(self._features(), True, self._GEMMA_TPL) is False
+ )
def test_g9_missing_template_not_prefilled(self):
# G9: no template available -> conservative (not prefilled).
diff --git a/studio/backend/tests/test_safetensors_reasoning_stream.py b/studio/backend/tests/test_safetensors_reasoning_stream.py
index af5a05d266..565687b680 100644
--- a/studio/backend/tests/test_safetensors_reasoning_stream.py
+++ b/studio/backend/tests/test_safetensors_reasoning_stream.py
@@ -28,7 +28,10 @@ from routes.inference import (
_THINK_TPL = "........."
_ETHINK = {"reasoning_style": "enable_thinking", "supports_reasoning": True}
-_ETHINK_EFFORT = {"reasoning_style": "enable_thinking_effort", "supports_reasoning": True}
+_ETHINK_EFFORT = {
+ "reasoning_style": "enable_thinking_effort",
+ "supports_reasoning": True,
+}
def test_prefill_mode_on_for_enable_thinking_default():
@@ -43,11 +46,15 @@ def test_prefill_mode_off_for_reasoning_effort_none():
# enable_thinking_effort turns thinking off via reasoning_effort="none"; prefilled mode
# would capture the whole answer as reasoning_content.
assert (
- _sf_reasoning_prefill_mode(_ETHINK_EFFORT, None, _THINK_TPL, reasoning_effort = "none")
+ _sf_reasoning_prefill_mode(
+ _ETHINK_EFFORT, None, _THINK_TPL, reasoning_effort = "none"
+ )
is False
)
assert (
- _sf_reasoning_prefill_mode(_ETHINK_EFFORT, None, _THINK_TPL, reasoning_effort = "high")
+ _sf_reasoning_prefill_mode(
+ _ETHINK_EFFORT, None, _THINK_TPL, reasoning_effort = "high"
+ )
is True
)
@@ -100,7 +107,9 @@ def _replay_sf_reasoning_stream(events: list[dict], *, prefilled: bool) -> dict:
tool_starts.append(event)
order.append("tool_start")
continue
- clean = _strip_tool_xml_for_display(event.get("text", ""), auto_heal_tool_calls = True)
+ clean = _strip_tool_xml_for_display(
+ event.get("text", ""), auto_heal_tool_calls = True
+ )
new_text = clean[len(prev_text) :]
prev_text = clean
if not new_text:
@@ -127,7 +136,10 @@ def test_s1_plain_stream_splits_prefilled_reasoning():
# S1: plain/MLX single turn -> reasoning delta + visible delta; monitor visible-only.
events = [
{"type": "content", "text": "Let me compute 17*23"},
- {"type": "content", "text": "Let me compute 17*23 = 391The answer is 391."},
+ {
+ "type": "content",
+ "text": "Let me compute 17*23 = 391The answer is 391.",
+ },
]
out = _replay_sf_reasoning_stream(events, prefilled = True)
assert out["reasoning"] == "Let me compute 17*23 = 391"
@@ -170,7 +182,9 @@ def test_s3_extractor_resets_each_turn():
def test_s4_harmony_full_tags_normal_mode():
# S4: gpt-oss / explicit-tag models use normal mode (prefilled=False).
- events = [{"type": "content", "text": "reasoning herevisible answer"}]
+ events = [
+ {"type": "content", "text": "reasoning herevisible answer"}
+ ]
out = _replay_sf_reasoning_stream(events, prefilled = False)
assert out["reasoning"] == "reasoning here"
assert out["visible"] == "visible answer"
@@ -264,7 +278,10 @@ def test_native_reasoning_streamer_selected_and_errors_raise():
backend._generation_lock = threading.Lock()
backend.models = {"gemma-test": {"model": Model(), "tokenizer": Tok()}}
- assert list(backend.generate_stream("prompt", max_new_tokens = 4))[-1] == "ra"
+ assert (
+ list(backend.generate_stream("prompt", max_new_tokens = 4))[-1]
+ == "ra"
+ )
backend.models["gemma-test"]["model"] = Model(fail = True)
diff --git a/studio/backend/tests/test_safetensors_tool_loop.py b/studio/backend/tests/test_safetensors_tool_loop.py
index 31c728afca..7a3dbe6f6e 100644
--- a/studio/backend/tests/test_safetensors_tool_loop.py
+++ b/studio/backend/tests/test_safetensors_tool_loop.py
@@ -41,7 +41,9 @@ from utils.datasets import is_gpt_oss_model_name
class TestParser:
def test_json_tool_call(self):
- text = '{"name":"web_search","arguments":{"query":"hello"}}'
+ text = (
+ '{"name":"web_search","arguments":{"query":"hello"}}'
+ )
result = parse_tool_calls_from_text(text)
assert len(result) == 1
tc = result[0]
@@ -76,28 +78,36 @@ class TestParser:
result = parse_tool_calls_from_text(text)
assert len(result) == 1
assert result[0]["function"]["name"] == "web_search"
- assert json.loads(result[0]["function"]["arguments"]) == {"query": "openai news"}
+ assert json.loads(result[0]["function"]["arguments"]) == {
+ "query": "openai news"
+ }
def test_gemma_native_tool_call_template_quotes_escape_backslashes(self):
text = r'<|tool_call>call:ls{path:<|"|>C:\Users\wasim\repo<|"|>}'
result = parse_tool_calls_from_text(text)
assert len(result) == 1
assert result[0]["function"]["name"] == "ls"
- assert json.loads(result[0]["function"]["arguments"]) == {"path": r"C:\Users\wasim\repo"}
+ assert json.loads(result[0]["function"]["arguments"]) == {
+ "path": r"C:\Users\wasim\repo"
+ }
def test_gemma_native_tool_call_hyphenated_argument_name(self):
text = '<|tool_call>call:mcp__srv__create-issue{issue-title:"Bug report"}'
result = parse_tool_calls_from_text(text)
assert len(result) == 1
assert result[0]["function"]["name"] == "mcp__srv__create-issue"
- assert json.loads(result[0]["function"]["arguments"]) == {"issue-title": "Bug report"}
+ assert json.loads(result[0]["function"]["arguments"]) == {
+ "issue-title": "Bug report"
+ }
def test_gemma_native_tool_call_keeps_braces_inside_string_value(self):
text = '<|tool_call>call:terminal{command:"echo {foo:bar}"}'
result = parse_tool_calls_from_text(text)
assert len(result) == 1
assert result[0]["function"]["name"] == "terminal"
- assert json.loads(result[0]["function"]["arguments"]) == {"command": "echo {foo:bar}"}
+ assert json.loads(result[0]["function"]["arguments"]) == {
+ "command": "echo {foo:bar}"
+ }
def test_gemma_native_tool_call_bare_string_values(self):
text = "<|tool_call>call:get_weather{location:Tokyo,unit:celsius}"
@@ -147,9 +157,7 @@ class TestParser:
def test_code_with_embedded_xml(self):
# A code parameter with a literal must not truncate: the
# parser uses end-of-body as the only boundary for single-param calls.
- text = (
- "html = ''\nprint('hi')"
- )
+ text = "html = ''\nprint('hi')"
result = parse_tool_calls_from_text(text)
assert len(result) == 1
assert "print('hi')" in result[0]["function"]["arguments"]
@@ -217,8 +225,12 @@ class TestParser:
def test_render_html_start_detector_covers_mistral_and_rehearsal_forms(self):
# The provisional render-html card must fire for bracket-tag forms too, not only XML.
- assert _detect_render_html_tool_start('[TOOL_CALLS]render_html{"code":""}')
- assert _detect_render_html_tool_start('[TOOL_CALLS]render_html[ARGS]{"code":"x"}')
+ assert _detect_render_html_tool_start(
+ '[TOOL_CALLS]render_html{"code":""}'
+ )
+ assert _detect_render_html_tool_start(
+ '[TOOL_CALLS]render_html[ARGS]{"code":"x"}'
+ )
assert _detect_render_html_tool_start(
'[TOOL_CALLS] [{"name":"render_html","arguments":{}}]'
)
@@ -226,7 +238,9 @@ class TestParser:
# A different first tool (or a prose mention with no JSON body) must not fire.
assert not _detect_render_html_tool_start('[TOOL_CALLS]web_search{"q":"x"}')
assert not _detect_render_html_tool_start('web_search[ARGS]{"q":"x"}')
- assert not _detect_render_html_tool_start('python[ARGS]{"code":"render_html[ARGS]{}"}')
+ assert not _detect_render_html_tool_start(
+ 'python[ARGS]{"code":"render_html[ARGS]{}"}'
+ )
assert not _detect_render_html_tool_start("use render_html[ARGS] to render")
def test_render_html_start_detector_skips_think_block_rehearsal(self):
@@ -242,7 +256,9 @@ class TestParser:
'web_search[ARGS]{"q":"x"}render_html[ARGS]{"code":""}'
)
# A render_html rehearsed inside think with no real call after does not fire.
- assert not _detect_render_html_tool_start('render_html[ARGS]{"code":"x"}')
+ assert not _detect_render_html_tool_start(
+ 'render_html[ARGS]{"code":"x"}'
+ )
def test_render_html_start_detector_reads_top_level_array_name(self):
# Array form: the name is the object's top-level ``"name"``, not an argument key.
@@ -274,7 +290,10 @@ class TestParser:
assert strip_tool_markup(text, final = True) == "before"
# Without final=True the unclosed run is preserved.
assert "partial" in strip_tool_markup(text)
- assert strip_tool_markup("before <|tool_call>call:terminal{", final = True) == "before"
+ assert (
+ strip_tool_markup("before <|tool_call>call:terminal{", final = True)
+ == "before"
+ )
def test_streaming_strip_respects_disabled_healing(self):
raw = 'before {"name":"web_search"'
@@ -311,7 +330,8 @@ class TestParser:
end-of-string as a terminator. Regression for the Gemini
high-severity flag on this PR."""
text = (
- "I should call web_search[ARGS]" '{"query":"weather"} next to find the answer.'
+ "I should call web_search[ARGS]"
+ '{"query":"weather"} next to find the answer.'
)
result = parse_tool_calls_from_text(text)
# Inside an unclosed think block no calls are yielded.
@@ -365,7 +385,9 @@ class TestParser:
def test_mistral_bracket_nested_json(self):
# Brace-balance scan handles nested objects and braces inside string literals.
- text = "[TOOL_CALLS]web_search" '{"query":"a {nested} brace","opts":{"limit":5}}'
+ text = (
+ "[TOOL_CALLS]web_search" '{"query":"a {nested} brace","opts":{"limit":5}}'
+ )
result = parse_tool_calls_from_text(text)
assert len(result) == 1
import json as _json
@@ -408,7 +430,9 @@ class TestParser:
assert "print(1)" in result[0]["function"]["arguments"]
def test_rehearsal_with_prose(self):
- text = "I should call the python tool. Like this: " 'python[ARGS]{"code":"x = 1"}'
+ text = (
+ "I should call the python tool. Like this: " 'python[ARGS]{"code":"x = 1"}'
+ )
result = parse_tool_calls_from_text(text)
assert len(result) == 1
assert result[0]["function"]["name"] == "python"
@@ -433,7 +457,9 @@ class TestParser:
def test_streaming_strip_removes_partial_bracket_marker(self):
# A bracket tag streamed before its opening brace must strip on the final pass, not leak.
- assert strip_tool_markup("answer [TOOL_CALLS]web_search", final = True) == "answer"
+ assert (
+ strip_tool_markup("answer [TOOL_CALLS]web_search", final = True) == "answer"
+ )
assert strip_tool_markup("text python[ARGS]", final = True) == "text"
# Non-final must keep the in-progress tag buffered (not yet stripped).
partial = "answer [TOOL_CALLS]web_search"
@@ -473,7 +499,9 @@ class TestParser:
# [CALL_ID]/[ARGS] metadata (aligned with the parser).
raw = 'before [TOOL_CALLS]web_search[CALL_ID]abc123[ARGS]{"q":"x"} after'
out = strip_tool_markup_streaming(raw)
- assert "[TOOL_CALLS]" not in out and "[CALL_ID]" not in out and "[ARGS]" not in out
+ assert (
+ "[TOOL_CALLS]" not in out and "[CALL_ID]" not in out and "[ARGS]" not in out
+ )
assert "before" in out and "after" in out
# pre-strip.
@@ -490,7 +518,8 @@ class TestParser:
def test_think_block_stripped_before_bracket_tag(self):
text = (
- "Let me search for that.\n" '[TOOL_CALLS]web_search{"query":"weather"}'
+ "Let me search for that.\n"
+ '[TOOL_CALLS]web_search{"query":"weather"}'
)
result = parse_tool_calls_from_text(text)
assert len(result) == 1
@@ -498,7 +527,10 @@ class TestParser:
def test_uppercase_think_tag_stripped(self):
# Some templates use [THINK]...[/THINK] instead of .
- text = "[THINK]planning my next call[/THINK]" '[TOOL_CALLS]python{"code":"print(1)"}'
+ text = (
+ "[THINK]planning my next call[/THINK]"
+ '[TOOL_CALLS]python{"code":"print(1)"}'
+ )
result = parse_tool_calls_from_text(text)
assert len(result) == 1
assert result[0]["function"]["name"] == "python"
@@ -530,7 +562,10 @@ class TestParser:
text = '[TOOL_CALLS]search{"q":"explain [THINK] blocks"}'
result = parse_tool_calls_from_text(text)
assert len(result) == 1
- assert json.loads(result[0]["function"]["arguments"])["q"] == "explain [THINK] blocks"
+ assert (
+ json.loads(result[0]["function"]["arguments"])["q"]
+ == "explain [THINK] blocks"
+ )
def test_real_call_after_think_with_rehearsal_inside(self):
# A rehearsal inside is skipped, but the real call after the close tag parses.
@@ -601,7 +636,11 @@ class TestParser:
xml = parse_tool_calls_from_text(
'{"name":"web_search","arguments":"weather"}'
)
- assert array[0]["function"]["arguments"] == xml[0]["function"]["arguments"] == "weather"
+ assert (
+ array[0]["function"]["arguments"]
+ == xml[0]["function"]["arguments"]
+ == "weather"
+ )
healed = _coerce_arguments(
array[0]["function"]["arguments"], heal = True, tool_name = "web_search"
)
@@ -620,7 +659,9 @@ class TestParser:
def test_mistral_v11_call_id_is_not_the_function_name(self):
# v11 shape: the function name is ``name``, never the opaque call-id token.
- result = parse_tool_calls_from_text('[TOOL_CALLS]get_weather[CALL_ID]abc123[ARGS]{"q":"x"}')
+ result = parse_tool_calls_from_text(
+ '[TOOL_CALLS]get_weather[CALL_ID]abc123[ARGS]{"q":"x"}'
+ )
assert len(result) == 1
assert result[0]["function"]["name"] == "get_weather"
assert json.loads(result[0]["function"]["arguments"]) == {"q": "x"}
@@ -643,7 +684,9 @@ class TestParser:
assert strip_tool_markup_streaming(text, tool_protocol_active = True) == text
# An unclosed block during streaming is preserved too (the parser keeps it).
partial = 'plan: search[ARGS]{"q":"x"}'
- assert strip_tool_markup_streaming(partial, tool_protocol_active = True) == partial
+ assert (
+ strip_tool_markup_streaming(partial, tool_protocol_active = True) == partial
+ )
def test_streaming_strip_still_removes_real_call_outside_think(self):
# The think guard must not stop the streaming strip removing a call outside the block.
@@ -699,7 +742,9 @@ class TestParser:
# safetensors content; GGUF routes it to reasoning_content natively.
closed = "[THINK]Let me think. 2+2 is 4.[/THINK]The answer is 4."
assert strip_tool_markup_streaming(closed) == "The answer is 4."
- assert strip_tool_markup_streaming(closed) == strip_tool_markup(closed, final = True)
+ assert strip_tool_markup_streaming(closed) == strip_tool_markup(
+ closed, final = True
+ )
# Unclosed mid-stream reasoning is held from the marker on (nothing leaks, and
# the cleaned text only grows as the answer streams in after ``[/THINK]``).
assert strip_tool_markup_streaming("[THINK]still thinking") == ""
@@ -728,7 +773,10 @@ class TestParserMultiFormat:
def test_llama3_python_tag_dot_call_multi_arg(self):
import json
- text = "<|python_tag|>get_weather.call(" 'location="Tokyo", units="celsius", days=5)'
+ text = (
+ "<|python_tag|>get_weather.call("
+ 'location="Tokyo", units="celsius", days=5)'
+ )
result = parse_tool_calls_from_text(text)
assert len(result) == 1
args = json.loads(result[0]["function"]["arguments"])
@@ -998,7 +1046,9 @@ class TestParserMultiFormat:
text = '[TOOL_CALLS]search[ARGS]{"q":"explain the [THINK] token"}'
result = parse_tool_calls_from_text(text)
assert len(result) == 1
- assert json.loads(result[0]["function"]["arguments"]) == {"q": "explain the [THINK] token"}
+ assert json.loads(result[0]["function"]["arguments"]) == {
+ "q": "explain the [THINK] token"
+ }
# Gemma 4
@@ -1024,7 +1074,12 @@ class TestParserMultiFormat:
)
result = parse_tool_calls_from_text(text)
args = json.loads(result[0]["function"]["arguments"])
- assert args == {"enabled": True, "attempts": 5, "threshold": 1.5, "nickname": None}
+ assert args == {
+ "enabled": True,
+ "attempts": 5,
+ "threshold": 1.5,
+ "nickname": None,
+ }
def test_gemma4_nested_args(self):
# Gemma 4 nests dicts / lists with bare keys and ``<|"|>`` strings.
@@ -1128,7 +1183,9 @@ class TestParserMultiFormat:
"[TOOL_CALLS]",
"<|tool_call>",
):
- assert marker in TOOL_XML_SIGNALS, f"streaming loop would not wake on {marker!r}"
+ assert (
+ marker in TOOL_XML_SIGNALS
+ ), f"streaming loop would not wake on {marker!r}"
def test_has_tool_signal_for_all_formats(self):
assert has_tool_signal('<|python_tag|>brave_search.call(q="x")')
@@ -1766,7 +1823,8 @@ class TestParserCrossFormatRouting:
result = parse_tool_calls_from_text(text)
assert len(result) == 1, f"{label}: parser missed the call"
assert result[0]["function"]["name"] == expected_name, (
- f"{label}: got {result[0]['function']['name']!r}, " f"expected {expected_name!r}"
+ f"{label}: got {result[0]['function']['name']!r}, "
+ f"expected {expected_name!r}"
)
def test_all_new_markers_in_tool_xml_signals(self):
@@ -1779,7 +1837,9 @@ class TestParserCrossFormatRouting:
"<|tool_calls_section_begin|>",
"<|tool_call_begin|>",
):
- assert marker in TOOL_XML_SIGNALS, f"streaming loop would not wake on {marker!r}"
+ assert (
+ marker in TOOL_XML_SIGNALS
+ ), f"streaming loop would not wake on {marker!r}"
def test_active_tools_are_passed_to_single_turn_after_render_html_success():
@@ -1814,7 +1874,10 @@ def test_active_tools_are_passed_to_single_turn_after_render_html_success():
assert exec_fn.calls == [("render_html", {"code": "one"})]
assert captured_tool_names == [["render_html", "web_search"], ["web_search"]]
- assert any(event.get("type") == "content" and event.get("text") == "Done." for event in events)
+ assert any(
+ event.get("type") == "content" and event.get("text") == "Done."
+ for event in events
+ )
def test_spent_one_shot_rehearsal_repeat_is_detected_not_blank_continuation():
@@ -1827,7 +1890,9 @@ def test_spent_one_shot_rehearsal_repeat_is_detected_not_blank_continuation():
[
'{"name":"render_html","arguments":{"code":"one"}}'
],
- ['render_html[ARGS]{"code":"two"}'], # spent one-shot rehearsal
+ [
+ 'render_html[ARGS]{"code":"two"}'
+ ], # spent one-shot rehearsal
["The chart is above."],
]
)
@@ -1856,7 +1921,9 @@ def test_spent_one_shot_rehearsal_repeat_is_detected_not_blank_continuation():
)
contents = [e["text"] for e in events if e["type"] == "content"]
# render_html ran exactly once; the repeat was a no-op, not a second execution.
- assert exec_fn.calls == [("render_html", {"code": "one"})], exec_fn.calls
+ assert exec_fn.calls == [
+ ("render_html", {"code": "one"})
+ ], exec_fn.calls
# The loop continued past the repeat to the real answer (not a blank continuation).
assert any("The chart is above." in t for t in contents), contents
# The raw rehearsal markup never leaked as visible content.
@@ -1905,7 +1972,12 @@ def test_rehearsal_name_after_prose_in_streaming_is_not_streamed():
loop, exec_fn = _make_loop(
turns = [
# _make_loop accumulates these deltas into cumulative snapshots.
- ["Let me think. ", "I will search ", "web_search", '[ARGS]{"query":"cats"}'],
+ [
+ "Let me think. ",
+ "I will search ",
+ "web_search",
+ '[ARGS]{"query":"cats"}',
+ ],
["Found."],
],
exec_results = ["RESULT"],
@@ -2069,7 +2141,9 @@ def test_safety_net_honors_disabled_auto_heal_for_late_incomplete_call():
# A late call caught by the safety net: an unclosed ```` heals only with Auto-Heal on;
# off, the safety net must not pass ``allow_incomplete=True`` and execute a truncated call.
prose = "Sure, let me look that up for you right now. "
- incomplete = '{"name":"web_search","arguments":{"query":"weather in Sydney"}}'
+ incomplete = (
+ '{"name":"web_search","arguments":{"query":"weather in Sydney"}}'
+ )
loop_off, exec_off = _make_loop(
turns = [[prose, incomplete], ["Final answer."]],
@@ -2078,7 +2152,9 @@ def test_safety_net_honors_disabled_auto_heal_for_late_incomplete_call():
max_tool_iterations = 3,
)
events_off = _collect_events(loop_off)
- assert exec_off.calls == [], "disabled Auto-Heal must not execute a healed incomplete call"
+ assert (
+ exec_off.calls == []
+ ), "disabled Auto-Heal must not execute a healed incomplete call"
assert not [e for e in events_off if e.get("type") == "tool_start"]
loop_on, exec_on = _make_loop(
@@ -2088,7 +2164,9 @@ def test_safety_net_honors_disabled_auto_heal_for_late_incomplete_call():
max_tool_iterations = 3,
)
_collect_events(loop_on)
- assert exec_on.calls == [("web_search", {"query": "weather in Sydney"})], exec_on.calls
+ assert exec_on.calls == [
+ ("web_search", {"query": "weather in Sydney"})
+ ], exec_on.calls
def test_bare_json_tool_call_is_not_streamed_as_content():
@@ -2504,14 +2582,22 @@ class TestLoopBasic:
assert exec_fn.calls[0][0] == "render_html"
assert "" in exec_fn.calls[0][1]["code"]
- def test_render_html_confirmation_gate_suppresses_early_provisional(self, monkeypatch):
+ def test_render_html_confirmation_gate_suppresses_early_provisional(
+ self, monkeypatch
+ ):
"""When a human confirmation gate is active, render_html must not surface
an early provisional tool_start: that card (keyed by tool_call_id, no
approval) would show the tool 'running' before the user approves. The
gated real tool_start is the first signal the UI receives instead."""
- monkeypatch.setattr(safetensors_agentic, "new_approval_id", lambda: "approval-rh")
- monkeypatch.setattr(safetensors_agentic, "begin_tool_decision", lambda *_a, **_k: object())
- monkeypatch.setattr(safetensors_agentic, "wait_tool_decision", lambda *_a, **_k: "allow")
+ monkeypatch.setattr(
+ safetensors_agentic, "new_approval_id", lambda: "approval-rh"
+ )
+ monkeypatch.setattr(
+ safetensors_agentic, "begin_tool_decision", lambda *_a, **_k: object()
+ )
+ monkeypatch.setattr(
+ safetensors_agentic, "wait_tool_decision", lambda *_a, **_k: "allow"
+ )
exec_fn = FakeExecuteTool(["Rendered HTML canvas."])
turn_iter = iter(
@@ -2644,7 +2730,10 @@ class TestLoopBasic:
def _gen(_messages):
acc = ""
- for chunk in ["", ""]:
+ for chunk in [
+ "",
+ "",
+ ]:
acc += chunk
yield acc
raise RuntimeError("model pipeline exploded")
@@ -2667,7 +2756,9 @@ class TestLoopBasic:
assert raised
provisional = [
- e for e in collected if e["type"] == "tool_start" and e.get("arguments") == {}
+ e
+ for e in collected
+ if e["type"] == "tool_start" and e.get("arguments") == {}
]
assert len(provisional) == 1
# The provisional card is closed (as an error) before the exception
@@ -2675,12 +2766,15 @@ class TestLoopBasic:
closing = [
e
for e in collected
- if e["type"] == "tool_end" and e.get("tool_call_id") == provisional[0]["tool_call_id"]
+ if e["type"] == "tool_end"
+ and e.get("tool_call_id") == provisional[0]["tool_call_id"]
]
assert len(closing) == 1
assert "Error" in (closing[0].get("result") or "")
- def test_python_tool_containing_render_html_signal_does_not_emit_provisional_start(self):
+ def test_python_tool_containing_render_html_signal_does_not_emit_provisional_start(
+ self,
+ ):
loop, exec_fn = _make_loop(
turns = [
[
@@ -2697,7 +2791,9 @@ class TestLoopBasic:
assert len(tool_starts) == 1
assert tool_starts[0]["tool_name"] == "python"
- assert exec_fn.calls == [("python", {"code": "print('')"})]
+ assert exec_fn.calls == [
+ ("python", {"code": "print('')"})
+ ]
def test_render_html_rehearsed_in_think_block_emits_no_provisional_start(self):
# BUG B: a render_html rehearsed inside think before a real python call must not emit a
@@ -2768,7 +2864,10 @@ class TestLoopBasic:
tool_starts = [e for e in events if e["type"] == "tool_start"]
assert exec_fn.calls == [("render_html", {"code": "one"})]
- assert [e["arguments"] for e in tool_starts] == [{}, {"code": "one"}]
+ assert [e["arguments"] for e in tool_starts] == [
+ {},
+ {"code": "one"},
+ ]
def test_truncated_unclosed_tool_call(self):
loop, exec_fn = _make_loop(
@@ -2788,7 +2887,9 @@ class TestLoopBasic:
loop, exec_fn = _make_loop(
turns = [
# ``arguments`` is a string _coerce_arguments can't parse, so heal runs.
- ['{"name":"web_search","arguments":"hello world"}'],
+ [
+ '{"name":"web_search","arguments":"hello world"}'
+ ],
["ok"],
],
exec_results = ["..."],
@@ -2803,8 +2904,12 @@ class TestLoopBehaviour:
captured_messages: list[list[dict]] = []
turns = iter(
[
- ['{"name":"web_search","arguments":{"query":"x"}}'],
- ['{"name":"web_search","arguments":{"query":"x"}}'],
+ [
+ '{"name":"web_search","arguments":{"query":"x"}}'
+ ],
+ [
+ '{"name":"web_search","arguments":{"query":"x"}}'
+ ],
["final"],
]
)
@@ -2829,11 +2934,14 @@ class TestLoopBehaviour:
)
assert exec_fn.calls == [("web_search", {"query": "x"})]
- assert [e["tool_call_id"] for e in events if e["type"] == "tool_end"] == ["call_0"]
+ assert [e["tool_call_id"] for e in events if e["type"] == "tool_end"] == [
+ "call_0"
+ ]
assert not [
e
for e in events
- if e.get("tool_call_id") == "call_1" and e.get("type") in {"tool_start", "tool_end"}
+ if e.get("tool_call_id") == "call_1"
+ and e.get("type") in {"tool_start", "tool_end"}
]
duplicate_nudges = [
message
@@ -2850,7 +2958,9 @@ class TestLoopBehaviour:
captured_messages: list[list[dict]] = []
turns = iter(
[
- ['{"name":"web_search","arguments":{"query":"x"}}'],
+ [
+ '{"name":"web_search","arguments":{"query":"x"}}'
+ ],
[
'{"name":"web_search","arguments":{"query":"x"}}'
'{"name":"python","arguments":{"code":"print(1)"}}'
@@ -2888,7 +2998,9 @@ class TestLoopBehaviour:
]
conv = captured_messages[-1]
- turn2 = [m for m in conv if m.get("role") == "assistant" and m.get("tool_calls")][-1]
+ turn2 = [
+ m for m in conv if m.get("role") == "assistant" and m.get("tool_calls")
+ ][-1]
assert [tc["function"]["name"] for tc in turn2["tool_calls"]] == ["python"]
after = conv[conv.index(turn2) + 1 :]
assert after[0]["role"] == "tool" and after[0]["content"] == "py-result"
@@ -2903,9 +3015,15 @@ class TestLoopBehaviour:
captured_tool_names: list[list[str]] = []
turns = iter(
[
- ['{"name":"web_search","arguments":{"query":"x"}}'],
- ['{"name":"web_search","arguments":{"query":"x"}}'],
- ['{"name":"python","arguments":{"code":"print(1)"}}'],
+ [
+ '{"name":"web_search","arguments":{"query":"x"}}'
+ ],
+ [
+ '{"name":"web_search","arguments":{"query":"x"}}'
+ ],
+ [
+ '{"name":"python","arguments":{"code":"print(1)"}}'
+ ],
["final"],
]
)
@@ -2950,7 +3068,8 @@ class TestLoopBehaviour:
assert not [
e
for e in events
- if e.get("tool_call_id") == "call_1" and e.get("type") in {"tool_start", "tool_end"}
+ if e.get("tool_call_id") == "call_1"
+ and e.get("type") in {"tool_start", "tool_end"}
]
duplicate_nudges = [
message
@@ -2971,9 +3090,15 @@ class TestLoopBehaviour:
captured_tool_names: list[list[str]] = []
turns = iter(
[
- ['{"name":"web_search","arguments":{"query":"x"}}'],
- ['{"name":"web_search","arguments":{"query":"x"}}'],
- ['{"name":"python","arguments":{"code":"print(1)"}}'],
+ [
+ '{"name":"web_search","arguments":{"query":"x"}}'
+ ],
+ [
+ '{"name":"web_search","arguments":{"query":"x"}}'
+ ],
+ [
+ '{"name":"python","arguments":{"code":"print(1)"}}'
+ ],
["final"],
]
)
@@ -3018,9 +3143,15 @@ class TestLoopBehaviour:
captured_tool_names: list[list[str]] = []
turns = iter(
[
- ['{"name":"web_search","arguments":{"query":"x"}}'],
- ['{"name":"web_search","arguments":{"query":"x"}}'],
- ['{"name":"web_search","arguments":{"query":"x"}}'],
+ [
+ '{"name":"web_search","arguments":{"query":"x"}}'
+ ],
+ [
+ '{"name":"web_search","arguments":{"query":"x"}}'
+ ],
+ [
+ '{"name":"web_search","arguments":{"query":"x"}}'
+ ],
["final from first result"],
]
)
@@ -3052,11 +3183,14 @@ class TestLoopBehaviour:
assert exec_fn.calls == [("web_search", {"query": "x"})]
assert [
- event.get("tool_call_id") for event in events if event.get("type") == "tool_end"
+ event.get("tool_call_id")
+ for event in events
+ if event.get("type") == "tool_end"
] == ["call_0"]
assert captured_tool_names[-1] == []
assert any(
- event.get("type") == "content" and "final from first result" in event.get("text", "")
+ event.get("type") == "content"
+ and "final from first result" in event.get("text", "")
for event in events
)
@@ -3103,7 +3237,9 @@ class TestLoopBehaviour:
# carries the raw result for the UI.
loop, exec_fn = _make_loop(
turns = [
- ['{"name":"python","arguments":{"code":"plot()"}}'],
+ [
+ '{"name":"python","arguments":{"code":"plot()"}}'
+ ],
["see chart"],
],
exec_results = ["chart\n__IMAGES__:/tmp/chart.png"],
@@ -3141,7 +3277,9 @@ class TestLoopBehaviour:
tool_msgs = [m for m in captured[1] if m.get("role") == "tool"]
assert tool_msgs, "no tool message reached the model"
for tm in tool_msgs:
- assert "__IMAGES__" not in tm["content"], f"sentinel leaked to model: {tm['content']!r}"
+ assert (
+ "__IMAGES__" not in tm["content"]
+ ), f"sentinel leaked to model: {tm['content']!r}"
def test_image_sentinel_stripped_with_multiple_markers(self):
# Consecutive sentinels: cut at the first, nothing leaks.
@@ -3171,13 +3309,19 @@ class TestLoopBehaviour:
tool_msgs = [m for m in captured[1] if m.get("role") == "tool"]
assert tool_msgs
for tm in tool_msgs:
- assert "__IMAGES__" not in tm["content"], f"second sentinel leaked: {tm['content']!r}"
- assert tm["content"] == "panel", f"expected payload-only 'panel', got {tm['content']!r}"
+ assert (
+ "__IMAGES__" not in tm["content"]
+ ), f"second sentinel leaked: {tm['content']!r}"
+ assert (
+ tm["content"] == "panel"
+ ), f"expected payload-only 'panel', got {tm['content']!r}"
def test_tool_execution_error_is_emitted_but_loop_continues(self):
loop, exec_fn = _make_loop(
turns = [
- ['{"name":"web_search","arguments":{"query":"x"}}'],
+ [
+ '{"name":"web_search","arguments":{"query":"x"}}'
+ ],
["sorry, that failed"],
],
exec_results = ["Error: network unreachable"],
@@ -3192,7 +3336,9 @@ class TestLoopBehaviour:
def test_exception_in_executor_does_not_raise(self):
loop, exec_fn = _make_loop(
turns = [
- ['{"name":"web_search","arguments":{"query":"x"}}'],
+ [
+ '{"name":"web_search","arguments":{"query":"x"}}'
+ ],
["recovered"],
],
exec_results = [RuntimeError("boom")],
@@ -3318,7 +3464,9 @@ class TestLoopRePrompt:
loop, exec_fn = _make_loop(
turns = [
["Let me search for that."],
- ['{"name":"web_search","arguments":{"query":"cats"}}'],
+ [
+ '{"name":"web_search","arguments":{"query":"cats"}}'
+ ],
["Here is the answer."],
],
exec_results = ["result"],
@@ -3335,7 +3483,9 @@ class TestLoopRePrompt:
loop, exec_fn = _make_loop(
turns = [
["I need more context.Let me search for that."],
- ['{"name":"web_search","arguments":{"query":"cats"}}'],
+ [
+ '{"name":"web_search","arguments":{"query":"cats"}}'
+ ],
["Here is the answer."],
],
exec_results = ["result"],
@@ -3353,7 +3503,9 @@ class TestLoopRePrompt:
loop, exec_fn = _make_loop(
turns = [
["Let me search for that.checking details"],
- ['{"name":"web_search","arguments":{"query":"cats"}}'],
+ [
+ '{"name":"web_search","arguments":{"query":"cats"}}'
+ ],
["Here is the answer."],
],
exec_results = ["result"],
@@ -3391,7 +3543,10 @@ class TestLoopRePrompt:
)
assert exec_fn.calls == [("web_search", {"query": "cats"})]
- assert captured[1][1] == {"role": "assistant", "content": "Let me search for that."}
+ assert captured[1][1] == {
+ "role": "assistant",
+ "content": "Let me search for that.",
+ }
contents = [e["text"] for e in events if e["type"] == "content"]
assert contents[-1] == "Here is the answer."
@@ -3475,7 +3630,9 @@ class TestLoopRePrompt:
loop, exec_fn = _make_loop(
turns = [
["Let me check."],
- ['{"name":"web_search","arguments":{"query":"x"}}'],
+ [
+ '{"name":"web_search","arguments":{"query":"x"}}'
+ ],
["found"],
],
exec_results = ["..."],
@@ -3493,7 +3650,9 @@ class TestLoopRePrompt:
# 1. Intent stall (re-prompt).
["Let me search for that."],
# 2. Real tool call (uses the budget slot).
- ['{"name":"web_search","arguments":{"query":"weather"}}'],
+ [
+ '{"name":"web_search","arguments":{"query":"weather"}}'
+ ],
# 3. Budget exhausted -> nudged final answer.
["Final: it is sunny"],
],
@@ -3591,15 +3750,15 @@ class TestGGUFSafetensorsHealingParity:
assert _CANONICAL_HEAL_ARG["python"] == "code"
assert _CANONICAL_HEAL_ARG["terminal"] == "command"
- assert coerce_tool_arguments("print(1)", heal = True, tool_name = "python").arguments == {
- "code": "print(1)"
- }
- assert coerce_tool_arguments("ls -la", heal = True, tool_name = "terminal").arguments == {
- "command": "ls -la"
- }
- assert coerce_tool_arguments("weather", heal = True, tool_name = "web_search").arguments == {
- "query": "weather"
- }
+ assert coerce_tool_arguments(
+ "print(1)", heal = True, tool_name = "python"
+ ).arguments == {"code": "print(1)"}
+ assert coerce_tool_arguments(
+ "ls -la", heal = True, tool_name = "terminal"
+ ).arguments == {"command": "ls -la"}
+ assert coerce_tool_arguments(
+ "weather", heal = True, tool_name = "web_search"
+ ).arguments == {"query": "weather"}
def test_intent_regex_matches_same_phrases_as_gguf(self):
# The intent re-prompt regex is now a single shared source of truth
@@ -3681,7 +3840,9 @@ class TestLoopControl:
loop, exec_fn = _make_loop(
turns = [
# Tool call (executes once).
- ['{"name":"web_search","arguments":{"query":"a"}}'],
+ [
+ '{"name":"web_search","arguments":{"query":"a"}}'
+ ],
# Model gives a final answer when nudged.
["here is the final answer"],
],
@@ -3698,19 +3859,24 @@ class TestStatusFormatting:
def test_status_for_known_tools(self):
# Call the private helper directly to verify status formatting.
assert (
- safetensors_agentic._status_for_tool("web_search", {"query": "abc"}) == "Searching: abc"
+ safetensors_agentic._status_for_tool("web_search", {"query": "abc"})
+ == "Searching: abc"
)
assert (
- safetensors_agentic._status_for_tool("web_search", {"url": "https://www.example.com/x"})
+ safetensors_agentic._status_for_tool(
+ "web_search", {"url": "https://www.example.com/x"}
+ )
== "Reading: example.com"
)
- assert safetensors_agentic._status_for_tool("python", {"code": "x = 1"}).startswith(
- "Running Python:"
+ assert safetensors_agentic._status_for_tool(
+ "python", {"code": "x = 1"}
+ ).startswith("Running Python:")
+ assert safetensors_agentic._status_for_tool(
+ "terminal", {"command": "ls"}
+ ).startswith("Running:")
+ assert safetensors_agentic._status_for_tool("unknown_tool", {}).startswith(
+ "Calling:"
)
- assert safetensors_agentic._status_for_tool("terminal", {"command": "ls"}).startswith(
- "Running:"
- )
- assert safetensors_agentic._status_for_tool("unknown_tool", {}).startswith("Calling:")
class TestProseMentioningToolCall:
@@ -3720,7 +3886,9 @@ class TestProseMentioningToolCall:
loop, exec_fn = _make_loop(
turns = [
# A real tool call so the loop advances a turn.
- ['{"name":"web_search","arguments":{"query":"x"}}'],
+ [
+ '{"name":"web_search","arguments":{"query":"x"}}'
+ ],
# Prose that mentions the literal text.
["the docs say means an LLM tool call wrapper"],
],
@@ -3739,7 +3907,9 @@ class TestProseMentioningToolCall:
# loop parses only model output, so exactly one call.
loop, exec_fn = _make_loop(
turns = [
- ['{"name":"web_search","arguments":{"query":"x"}}'],
+ [
+ '{"name":"web_search","arguments":{"query":"x"}}'
+ ],
["the docs mention wrappers"],
],
exec_results = ["Page text: appears here in the docs"],
@@ -3840,18 +4010,23 @@ class TestGuardrails:
)
assert exec_fn.calls == []
- assert not [event for event in events if event.get("type") in {"tool_start", "tool_end"}]
+ assert not [
+ event for event in events if event.get("type") in {"tool_start", "tool_end"}
+ ]
disabled_nudges = [
message
for message in captured_messages[-1]
- if message.get("role") == "user" and "not enabled" in message.get("content", "")
+ if message.get("role") == "user"
+ and "not enabled" in message.get("content", "")
]
assert len(disabled_nudges) == 1
def test_empty_tools_list_means_allow_all_in_core_loop(self):
turns = iter(
[
- ['{"name":"python","arguments":{"code":"print(1)"}}'],
+ [
+ '{"name":"python","arguments":{"code":"print(1)"}}'
+ ],
["done"],
]
)
@@ -3878,7 +4053,11 @@ class TestGuardrails:
def test_max_iterations_zero_executes_no_tools(self):
loop, exec_fn = _make_loop(
- turns = [['{"name":"web_search","arguments":{"query":"x"}}']],
+ turns = [
+ [
+ '{"name":"web_search","arguments":{"query":"x"}}'
+ ]
+ ],
exec_results = ["OK"],
max_tool_iterations = 0,
)
@@ -3909,7 +4088,9 @@ class TestGuardrails:
def test_auto_heal_disabled_still_parses_valid_tool_call(self):
loop, exec_fn = _make_loop(
turns = [
- ['{"name":"web_search","arguments":{"query":"x"}}'],
+ [
+ '{"name":"web_search","arguments":{"query":"x"}}'
+ ],
["done"],
],
exec_results = ["OK"],
@@ -3924,7 +4105,11 @@ class TestGuardrails:
monkeypatch.setattr(safetensors_agentic, "new_approval_id", lambda: approval_id)
loop, exec_fn = _make_loop(
- turns = [['{"name":"python","arguments":{"code":"print(1)"}}']],
+ turns = [
+ [
+ '{"name":"python","arguments":{"code":"print(1)"}}'
+ ]
+ ],
exec_results = ["OK"],
confirm_tool_calls = True,
session_id = "sess",
@@ -3953,14 +4138,19 @@ class TestGuardrails:
def fail_autoinject(*_args, **_kwargs):
raise AssertionError("RAG autoinject must not run before approval")
- monkeypatch.setattr("core.inference.tools.build_rag_autoinject", fail_autoinject)
+ monkeypatch.setattr(
+ "core.inference.tools.build_rag_autoinject", fail_autoinject
+ )
loop, exec_fn = _make_loop(
turns = [["plain answer"]],
confirm_tool_calls = True,
rag_scope = {"thread_id": "t1"},
)
events = _collect_events(loop)
- assert any(e.get("type") == "content" and e.get("text") == "plain answer" for e in events)
+ assert any(
+ e.get("type") == "content" and e.get("text") == "plain answer"
+ for e in events
+ )
assert exec_fn.calls == []
def test_auto_mode_still_runs_rag_autoinject(self, monkeypatch):
@@ -3973,7 +4163,9 @@ class TestGuardrails:
ran["called"] = True
return None
- monkeypatch.setattr("core.inference.tools.build_rag_autoinject", fake_autoinject)
+ monkeypatch.setattr(
+ "core.inference.tools.build_rag_autoinject", fake_autoinject
+ )
loop, _exec_fn = _make_loop(
turns = [["plain answer"]],
confirm_tool_calls = True,
@@ -3986,8 +4178,12 @@ class TestGuardrails:
def test_auto_heal_disabled_preserves_xml_on_final_no_tools_pass(self):
turns = iter(
[
- ['{"name":"web_search","arguments":{"query":"x"}}'],
- ['{"name":"web_search","arguments":{"query":"literal"}}'],
+ [
+ '{"name":"web_search","arguments":{"query":"x"}}'
+ ],
+ [
+ '{"name":"web_search","arguments":{"query":"literal"}}'
+ ],
]
)
@@ -4047,18 +4243,29 @@ class TestGuardrails:
def test_non_consecutive_duplicate_is_short_circuited(self):
loop, exec_fn = _make_loop(
turns = [
- ['{"name":"web_search","arguments":{"query":"A"}}'],
- ['{"name":"web_search","arguments":{"query":"B"}}'],
- ['{"name":"web_search","arguments":{"query":"A"}}'],
+ [
+ '{"name":"web_search","arguments":{"query":"A"}}'
+ ],
+ [
+ '{"name":"web_search","arguments":{"query":"B"}}'
+ ],
+ [
+ '{"name":"web_search","arguments":{"query":"A"}}'
+ ],
["final"],
],
exec_results = ["res-A", "res-B"],
max_tool_iterations = 4,
)
events = _collect_events(loop)
- assert exec_fn.calls == [("web_search", {"query": "A"}), ("web_search", {"query": "B"})]
+ assert exec_fn.calls == [
+ ("web_search", {"query": "A"}),
+ ("web_search", {"query": "B"}),
+ ]
assert [
- event.get("tool_call_id") for event in events if event.get("type") == "tool_end"
+ event.get("tool_call_id")
+ for event in events
+ if event.get("type") == "tool_end"
] == ["call_0", "call_1"]
assert not [
event
@@ -4082,7 +4289,9 @@ class TestGuardrails:
events = _collect_events(loop)
assert exec_fn.calls == [("web_search", {"query": "A"})]
assert [
- event.get("tool_call_id") for event in events if event.get("type") == "tool_end"
+ event.get("tool_call_id")
+ for event in events
+ if event.get("type") == "tool_end"
] == ["call_0"]
assert not [
event
@@ -4098,7 +4307,8 @@ class TestGuardrails:
n = _MAX_TOOL_CALLS_PER_TURN + 4
turn = "".join(
- '{"name":"web_search","arguments":{"query":"q%d"}}' % i
+ '{"name":"web_search","arguments":{"query":"q%d"}}'
+ % i
for i in range(n)
)
loop, exec_fn = _make_loop(
@@ -4114,16 +4324,24 @@ class TestGuardrails:
]
def test_coerce_string_args_python_uses_code_key(self):
- assert _coerce_arguments("print(1)", heal = True, tool_name = "python") == {"code": "print(1)"}
+ assert _coerce_arguments("print(1)", heal = True, tool_name = "python") == {
+ "code": "print(1)"
+ }
def test_coerce_string_args_terminal_uses_command_key(self):
- assert _coerce_arguments("ls -la", heal = True, tool_name = "terminal") == {"command": "ls -la"}
+ assert _coerce_arguments("ls -la", heal = True, tool_name = "terminal") == {
+ "command": "ls -la"
+ }
def test_tool_call_ids_unique_across_loop_iterations(self):
loop, _exec = _make_loop(
turns = [
- ['{"name":"web_search","arguments":{"query":"A"}}'],
- ['{"name":"web_search","arguments":{"query":"B"}}'],
+ [
+ '{"name":"web_search","arguments":{"query":"A"}}'
+ ],
+ [
+ '{"name":"web_search","arguments":{"query":"B"}}'
+ ],
["done"],
],
exec_results = ["A", "B"],
@@ -4161,7 +4379,9 @@ class TestPlanWithoutActionReprompt:
loop, exec_fn = _make_loop(
turns = [
["I'll search the web for that."],
- ['{"name":"web_search","arguments":{"query":"cats"}}'],
+ [
+ '{"name":"web_search","arguments":{"query":"cats"}}'
+ ],
["Here is the final answer."],
],
exec_results = ["result-1"],
@@ -4304,11 +4524,17 @@ class TestPlanWithoutActionReprompt:
# An explicit user denial must not be answered with a nudge to call
# the tool again (which would raise another confirmation prompt).
monkeypatch.setattr(safetensors_agentic, "new_approval_id", lambda: "appr-1")
- monkeypatch.setattr(safetensors_agentic, "begin_tool_decision", lambda *_a, **_k: object())
- monkeypatch.setattr(safetensors_agentic, "wait_tool_decision", lambda *_a, **_k: "deny")
+ monkeypatch.setattr(
+ safetensors_agentic, "begin_tool_decision", lambda *_a, **_k: object()
+ )
+ monkeypatch.setattr(
+ safetensors_agentic, "wait_tool_decision", lambda *_a, **_k: "deny"
+ )
loop, exec_fn = _make_loop(
turns = [
- ['{"name":"web_search","arguments":{"query":"cats"}}'],
+ [
+ '{"name":"web_search","arguments":{"query":"cats"}}'
+ ],
["I'll search again."],
["SHOULD NOT APPEAR"],
],
@@ -4325,7 +4551,9 @@ class TestPlanWithoutActionReprompt:
def test_no_reprompt_after_a_tool_already_executed(self):
loop, exec_fn = _make_loop(
turns = [
- ['{"name":"web_search","arguments":{"query":"cats"}}'],
+ [
+ '{"name":"web_search","arguments":{"query":"cats"}}'
+ ],
["Now I'll refine the search."],
["SHOULD NOT APPEAR"],
],
@@ -4376,7 +4604,10 @@ class TestRoutesPythonTagStrip:
def test_python_tag_stops_at_eom_sentinel(self):
# Strip stops at the next Llama-3 ``<|`` sentinel so any
# trailing assistant content survives.
- text = '<|python_tag|>python.call(code="multi\nline")' "<|eom_id|>final answer text"
+ text = (
+ '<|python_tag|>python.call(code="multi\nline")'
+ "<|eom_id|>final answer text"
+ )
assert self._strip(text) == "<|eom_id|>final answer text"
def test_python_tag_stops_at_eot_sentinel(self):
@@ -4410,7 +4641,11 @@ class TestParserRobustness:
# too. Was extracting name only and silently dropping the args.
import json
- text = "\n" '{"name": "search", "parameters": {"q": "ramen"}}\n' ""
+ text = (
+ "\n"
+ '{"name": "search", "parameters": {"q": "ramen"}}\n'
+ ""
+ )
result = parse_tool_calls_from_text(text)
assert len(result) == 1
assert result[0]["function"]["name"] == "search"
@@ -4421,7 +4656,11 @@ class TestParserRobustness:
# ``v``.
import json
- text = '' 'Tokyo' ""
+ text = (
+ ''
+ 'Tokyo'
+ ""
+ )
result = parse_tool_calls_from_text(text)
assert len(result) == 1
assert result[0]["function"]["name"] == "get_weather"
@@ -4553,7 +4792,9 @@ def test_render_with_native_template_returns_render_only_when_tools_emitted():
def emitting(tokenizer, msgs, *, tools, **_kw):
body = "".join(m["content"] for m in msgs)
- return body + ("|TOOLS=" + ",".join(t["function"]["name"] for t in tools) if tools else "")
+ return body + (
+ "|TOOLS=" + ",".join(t["function"]["name"] for t in tools) if tools else ""
+ )
def ignoring(tokenizer, msgs, *, tools, **_kw):
return "".join(m["content"] for m in msgs) # never reflects tools
@@ -4639,7 +4880,9 @@ def test_native_template_loads_from_base_model_for_lora(monkeypatch):
captured["source"] = name
return SimpleNamespace(chat_template = "BASE_TPL")
- monkeypatch.setattr(transformers.AutoTokenizer, "from_pretrained", fake_from_pretrained)
+ monkeypatch.setattr(
+ transformers.AutoTokenizer, "from_pretrained", fake_from_pretrained
+ )
def emitting(tokenizer, msgs, *, tools, **_kw):
body = "".join(m["content"] for m in msgs)
@@ -4665,7 +4908,9 @@ def test_render_with_native_template_fallback_swaps_when_override_drops_tools():
# identical with and without tools, re-render with the native template and return it.
from types import SimpleNamespace
- from core.inference.chat_template_helpers import render_with_native_template_fallback
+ from core.inference.chat_template_helpers import (
+ render_with_native_template_fallback,
+ )
messages = [{"role": "user", "content": "hi"}]
tools = [{"type": "function", "function": {"name": "web_search"}}]
@@ -4705,7 +4950,9 @@ def test_render_with_native_template_fallback_keeps_prompt_when_tools_emitted():
# unchanged. Also a no-tools call is a passthrough.
from types import SimpleNamespace
- from core.inference.chat_template_helpers import render_with_native_template_fallback
+ from core.inference.chat_template_helpers import (
+ render_with_native_template_fallback,
+ )
messages = [{"role": "user", "content": "hi"}]
tools = [{"type": "function", "function": {"name": "web_search"}}]
@@ -4742,7 +4989,9 @@ def test_render_with_native_template_fallback_keeps_prompt_when_no_tools_probe_r
# A template that REQUIRES tools can raise on the no-tools probe.
from types import SimpleNamespace
- from core.inference.chat_template_helpers import render_with_native_template_fallback
+ from core.inference.chat_template_helpers import (
+ render_with_native_template_fallback,
+ )
messages = [{"role": "user", "content": "hi"}]
tools = [{"type": "function", "function": {"name": "web_search"}}]
@@ -4785,7 +5034,9 @@ def test_oversized_bare_json_call_is_not_leaked_and_executes():
big = "A" * (_MAX_BARE_JSON_BUFFER + 5000)
full = '{"name":"python","parameters":{"code":"' + big + '"}}'
chunks = [full[i : i + 2000] for i in range(0, len(full), 2000)]
- loop, exec_fn = _make_loop(turns = [chunks, ["done"]], exec_results = ["OK"], max_tool_iterations = 2)
+ loop, exec_fn = _make_loop(
+ turns = [chunks, ["done"]], exec_results = ["OK"], max_tool_iterations = 2
+ )
events = _collect_events(loop)
contents = [e["text"] for e in events if e["type"] == "content"]
assert not any(t.lstrip().startswith('{"name') for t in contents), contents[:1]
@@ -4964,12 +5215,17 @@ class TestEnabledToolNameGate:
def test_parse_inactive_rehearsal_alone_is_prose(self):
assert (
- parse_tool_calls_from_text('foo[ARGS]{"a":1}', enabled_tool_names = {"web_search"}) == []
+ parse_tool_calls_from_text(
+ 'foo[ARGS]{"a":1}', enabled_tool_names = {"web_search"}
+ )
+ == []
)
def test_streaming_strip_keeps_inactive_rehearsal(self):
raw = 'answer foo[ARGS]{"x":1} tail'
- assert strip_tool_markup_streaming(raw, enabled_tool_names = {"web_search"}) == raw
+ assert (
+ strip_tool_markup_streaming(raw, enabled_tool_names = {"web_search"}) == raw
+ )
def test_streaming_strip_removes_active_rehearsal(self):
raw = 'answer web_search[ARGS]{"q":1} tail'
@@ -4979,7 +5235,10 @@ class TestEnabledToolNameGate:
def test_final_strip_keeps_inactive_rehearsal(self):
text = 'foo[ARGS]{"x":1} is just syntax.'
- assert strip_tool_markup(text, final = True, enabled_tool_names = {"web_search"}) == text
+ assert (
+ strip_tool_markup(text, final = True, enabled_tool_names = {"web_search"})
+ == text
+ )
def test_gate_none_preserves_legacy_strip_and_parse(self):
text = 'foo[ARGS]{"x":1} tail'
@@ -4993,13 +5252,17 @@ def test_drain_truncated_enabled_name_json_preserved_when_auto_heal_disabled():
# preserved), matching the XML strip in the same drain branch. With Auto-Heal ON
# the same fragment is suppressed.
trunc = '{"name":"web_search","parameters":{"query":"weather'
- off, exec_off = _make_loop(turns = [[trunc]], max_tool_iterations = 1, auto_heal_tool_calls = False)
+ off, exec_off = _make_loop(
+ turns = [[trunc]], max_tool_iterations = 1, auto_heal_tool_calls = False
+ )
events_off = _collect_events(off)
assert exec_off.calls == [], exec_off.calls
contents_off = "".join(e["text"] for e in events_off if e["type"] == "content")
assert "web_search" in contents_off, contents_off
- on, exec_on = _make_loop(turns = [[trunc]], max_tool_iterations = 1, auto_heal_tool_calls = True)
+ on, exec_on = _make_loop(
+ turns = [[trunc]], max_tool_iterations = 1, auto_heal_tool_calls = True
+ )
events_on = _collect_events(on)
assert exec_on.calls == [], exec_on.calls
contents_on = "".join(e["text"] for e in events_on if e["type"] == "content")
@@ -5017,7 +5280,9 @@ def test_looks_like_enabled_bare_json_accepts_function_alias():
'{"function":"web_search","parameters":{"q":"x"}}', enabled
)
# A non-tool "function" value is an ordinary JSON answer -> not gated.
- assert not _looks_like_enabled_bare_json('{"function":"Alice","parameters":{}}', enabled)
+ assert not _looks_like_enabled_bare_json(
+ '{"function":"Alice","parameters":{}}', enabled
+ )
class TestFalseAlarmMarkerProse:
diff --git a/studio/backend/tests/test_safetensors_toolcall_wiring.py b/studio/backend/tests/test_safetensors_toolcall_wiring.py
index 8909e0c0c5..034bbbed48 100644
--- a/studio/backend/tests/test_safetensors_toolcall_wiring.py
+++ b/studio/backend/tests/test_safetensors_toolcall_wiring.py
@@ -42,7 +42,9 @@ FAKE_TOOL = {
},
}
# Full parser matrix lives in test_safetensors_tool_loop.py.
-TOOL_CALL_TEXT = '{"name": "get_weather", "arguments": {"city": "Paris"}}'
+TOOL_CALL_TEXT = (
+ '{"name": "get_weather", "arguments": {"city": "Paris"}}'
+)
FINAL_ANSWER = "The weather in Paris is sunny and 22C."
TOOL_RESULT = "Paris: sunny, 22C"
@@ -164,11 +166,15 @@ def test_backend_seam_injects_tools_and_drives_full_tool_loop():
assert contents and FINAL_ANSWER in contents[-1]["text"]
last_tool_end_idx = max(i for i, e in enumerate(events) if e["type"] == "tool_end")
last_content_idx = max(i for i, e in enumerate(events) if e["type"] == "content")
- assert last_content_idx > last_tool_end_idx, "final answer must stream after the tool result"
+ assert (
+ last_content_idx > last_tool_end_idx
+ ), "final answer must stream after the tool result"
# 6b. Tool result fed back into the conversation before the final turn (6 alone misses this:
# the fake generation ignores the conversation).
- assert len(conversations_seen) >= 2, "loop did not re-enter generation after the tool call"
+ assert (
+ len(conversations_seen) >= 2
+ ), "loop did not re-enter generation after the tool call"
final_turn_convo = conversations_seen[1]
assert any(
TOOL_RESULT in str(m.get("content", "")) for m in final_turn_convo
diff --git a/studio/backend/tests/test_sandbox_sitecustomize.py b/studio/backend/tests/test_sandbox_sitecustomize.py
index 3ac427f9f1..7136d16949 100644
--- a/studio/backend/tests/test_sandbox_sitecustomize.py
+++ b/studio/backend/tests/test_sandbox_sitecustomize.py
@@ -53,7 +53,9 @@ def _save_patch_targets():
def _restore_patch_targets(saved):
"""Undo _save_patch_targets so the test process stays clean."""
globals_tuple, accessor, accessor_open = saved
- (builtins.open, io.open, os.open, os.makedirs, os.mkdir, pathlib.Path.mkdir) = globals_tuple
+ (builtins.open, io.open, os.open, os.makedirs, os.mkdir, pathlib.Path.mkdir) = (
+ globals_tuple
+ )
if accessor is not None:
accessor.open = accessor_open
@@ -61,7 +63,9 @@ def _restore_patch_targets(saved):
def _load_shim():
"""Import the shim without leaving its open()/mkdir patches installed."""
saved = _save_patch_targets()
- spec = importlib.util.spec_from_file_location("_sandbox_sitecustomize_under_test", _SHIM)
+ spec = importlib.util.spec_from_file_location(
+ "_sandbox_sitecustomize_under_test", _SHIM
+ )
mod = importlib.util.module_from_spec(spec)
try:
spec.loader.exec_module(mod) # runs _install(), patching the globals
@@ -134,9 +138,13 @@ def test_write_fallback_remaps_hallucinated_absolute_path(monkeypatch, tmp_path)
cwd = os.getcwd()
hallucinated = "/home/ubuntu/Sandbox/flappy_bird.html"
for mode in ("w", "a", "x", "w+"):
- assert mod._remap_open(hallucinated, mode) == os.path.join(cwd, "flappy_bird.html")
+ assert mod._remap_open(hallucinated, mode) == os.path.join(
+ cwd, "flappy_bird.html"
+ )
# A nested missing tree collapses to just the basename in the CWD.
- assert mod._remap_open("/no/such/tree/report.txt", "w") == os.path.join(cwd, "report.txt")
+ assert mod._remap_open("/no/such/tree/report.txt", "w") == os.path.join(
+ cwd, "report.txt"
+ )
def test_write_fallback_never_touches_read_modes(monkeypatch, tmp_path):
@@ -219,7 +227,9 @@ def test_write_fallback_reserves_same_target_on_repeated_writes(monkeypatch, tmp
assert mod._remap_open(other, "w") == other
-def test_write_fallback_reserves_healed_target_across_separate_runs(monkeypatch, tmp_path):
+def test_write_fallback_reserves_healed_target_across_separate_runs(
+ monkeypatch, tmp_path
+):
# Each tool call is a FRESH subprocess, so the in-process remap map is empty on
# the next run while the healed file persists in the working directory. A second
# run overwriting the SAME invented path (whose healed basename now exists) must
@@ -263,7 +273,9 @@ def test_write_fallback_reserves_healed_target_across_separate_runs(monkeypatch,
@pytest.mark.parametrize("mode", ["r+", "rb+"])
-def test_read_update_modes_never_redirected_even_with_missing_parent(monkeypatch, tmp_path, mode):
+def test_read_update_modes_never_redirected_even_with_missing_parent(
+ monkeypatch, tmp_path, mode
+):
# r+ / rb+ REQUIRE the target to exist and never create; a "+" must not qualify
# as creation, or a missing absolute path would be redirected onto a same-basename
# workspace file and corrupt it. The parent is missing, so only the mode predicate
@@ -318,7 +330,9 @@ def test_os_open_and_path_touch_remap_convention_path(monkeypatch, tmp_path):
# Keep the shim's patches installed under a chdir into tmp_path so os.open is
# patched, and confirm a convention path is healed into the CWD instead of raising.
saved = _save_patch_targets()
- spec = importlib.util.spec_from_file_location("_sandbox_sitecustomize_osopen", _SHIM)
+ spec = importlib.util.spec_from_file_location(
+ "_sandbox_sitecustomize_osopen", _SHIM
+ )
mod = importlib.util.module_from_spec(spec)
monkeypatch.chdir(tmp_path)
cwd = os.getcwd()
@@ -341,7 +355,9 @@ def test_path_write_read_text_remap_convention_path(monkeypatch, tmp_path):
# and confirm a convention path is healed into the CWD on every version. This is
# the hermetic guard for the 3.10 accessor path a plain io.open patch misses.
saved = _save_patch_targets()
- spec = importlib.util.spec_from_file_location("_sandbox_sitecustomize_writetext", _SHIM)
+ spec = importlib.util.spec_from_file_location(
+ "_sandbox_sitecustomize_writetext", _SHIM
+ )
mod = importlib.util.module_from_spec(spec)
monkeypatch.chdir(tmp_path)
cwd = os.getcwd()
@@ -375,7 +391,9 @@ def test_remap_open_still_applies_prefix_remaps(monkeypatch, tmp_path):
mod = _load_shim()
monkeypatch.chdir(tmp_path)
cwd = os.getcwd()
- assert mod._remap_open("/mnt/data/sub/out.txt", "w") == os.path.join(cwd, "sub", "out.txt")
+ assert mod._remap_open("/mnt/data/sub/out.txt", "w") == os.path.join(
+ cwd, "sub", "out.txt"
+ )
# A read whose mapped target does NOT exist keeps the original path: a missing
# input stays truthful, not silently redirected into the CWD.
assert mod._remap_open("/mnt/data/sub/out.txt", "r") == "/mnt/data/sub/out.txt"
@@ -498,7 +516,9 @@ def test_read_of_missing_prefix_path_emits_no_notice(monkeypatch, tmp_path, caps
assert mod._notified is False
assert "does not exist" not in capsys.readouterr().err
# A committed write then heals and fires the notice exactly once.
- assert mod._remap_open("/mnt/data/out.txt", "w") == os.path.join(os.getcwd(), "out.txt")
+ assert mod._remap_open("/mnt/data/out.txt", "w") == os.path.join(
+ os.getcwd(), "out.txt"
+ )
assert mod._notified is True
assert "/mnt/data does not exist in this sandbox" in capsys.readouterr().err
diff --git a/studio/backend/tests/test_sandbox_tools.py b/studio/backend/tests/test_sandbox_tools.py
index 2970b1a6bb..d19d5b0aa8 100644
--- a/studio/backend/tests/test_sandbox_tools.py
+++ b/studio/backend/tests/test_sandbox_tools.py
@@ -88,7 +88,9 @@ class TestTrustedHostAllowlist:
_ok(f"import requests; requests.get({url!r})")
def test_wikipedia_subdomain_passes(self):
- _ok('import urllib.request; urllib.request.urlopen("https://m.en.wikipedia.org/wiki/Foo")')
+ _ok(
+ 'import urllib.request; urllib.request.urlopen("https://m.en.wikipedia.org/wiki/Foo")'
+ )
def test_hf_co_short_form_passes(self):
_ok('import requests; requests.get("https://hf.co/unsloth/Qwen3.5-4B-GGUF")')
@@ -219,7 +221,10 @@ class TestUploadDenylist:
)
def test_plain_post_json_not_blocked(self):
- _ok("import requests\n" 'requests.post("https://api.weather.gov/lookup", json={"k": "v"})')
+ _ok(
+ "import requests\n"
+ 'requests.post("https://api.weather.gov/lookup", json={"k": "v"})'
+ )
class TestSandboxEnvIsolation:
@@ -327,7 +332,9 @@ class TestSandboxEnvIsolation:
env = _build_bypass_env(str(tmp_path))
assert _SANDBOX_SITE_DIR in env["PYTHONPATH"].split(os.pathsep)
- def test_bypass_env_prepends_shim_and_keeps_inherited_pythonpath(self, monkeypatch, tmp_path):
+ def test_bypass_env_prepends_shim_and_keeps_inherited_pythonpath(
+ self, monkeypatch, tmp_path
+ ):
from core.inference.tools import _SANDBOX_SITE_DIR, _build_bypass_env
monkeypatch.setenv("PYTHONPATH", "/operator/libs")
diff --git a/studio/backend/tests/test_secure_tools_execute.py b/studio/backend/tests/test_secure_tools_execute.py
index d8c76091e4..b3f63ed46e 100644
--- a/studio/backend/tests/test_secure_tools_execute.py
+++ b/studio/backend/tests/test_secure_tools_execute.py
@@ -50,7 +50,10 @@ def _tool_call_stream(tool_name: str, arguments: dict, call_id: str) -> list[str
"index": 0,
"id": call_id,
"type": "function",
- "function": {"name": tool_name, "arguments": json.dumps(arguments)},
+ "function": {
+ "name": tool_name,
+ "arguments": json.dumps(arguments),
+ },
}
]
}
@@ -123,9 +126,13 @@ def _run_one_tool(monkeypatch, tool_name: str, arguments: dict) -> str:
)
)
tool_ends = [
- e for e in events if e.get("type") == "tool_end" and e.get("tool_name") == tool_name
+ e
+ for e in events
+ if e.get("type") == "tool_end" and e.get("tool_name") == tool_name
]
- assert tool_ends, f"loop never executed {tool_name}; events={[e.get('type') for e in events]}"
+ assert (
+ tool_ends
+ ), f"loop never executed {tool_name}; events={[e.get('type') for e in events]}"
return tool_ends[0]["result"]
@@ -143,7 +150,9 @@ def test_python_tool_counts_to_100(monkeypatch):
# "Use the python tool to count from 1 to 100."
expected = " ".join(str(i) for i in range(1, 101))
result = _run_one_tool(
- monkeypatch, "python", {"code": "print(' '.join(str(i) for i in range(1, 101)))"}
+ monkeypatch,
+ "python",
+ {"code": "print(' '.join(str(i) for i in range(1, 101)))"},
)
assert expected in result, result # real subprocess produced the full sequence
@@ -152,12 +161,16 @@ def test_bash_tool_returns_current_datetime(monkeypatch):
# "Use the bash tool to provide today's datetime." Bound the parsed UTC time
# to the call window rather than a hard-coded date (survives midnight/TZ).
before = datetime.now(timezone.utc) - timedelta(seconds = 5)
- result = _run_one_tool(monkeypatch, "terminal", {"command": "date -u +%Y-%m-%dT%H:%M:%SZ"})
+ result = _run_one_tool(
+ monkeypatch, "terminal", {"command": "date -u +%Y-%m-%dT%H:%M:%SZ"}
+ )
after = datetime.now(timezone.utc) + timedelta(seconds = 5)
match = re.search(r"\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z", result)
assert match, f"no UTC datetime in terminal result: {result!r}"
- parsed = datetime.strptime(match.group(), "%Y-%m-%dT%H:%M:%SZ").replace(tzinfo = timezone.utc)
+ parsed = datetime.strptime(match.group(), "%Y-%m-%dT%H:%M:%SZ").replace(
+ tzinfo = timezone.utc
+ )
assert before <= parsed <= after, f"{parsed} not in [{before}, {after}]"
@@ -182,7 +195,9 @@ def test_web_search_tool_runs_with_mocked_fetch(monkeypatch):
]
monkeypatch.setattr("ddgs.DDGS", _FakeDDGS)
- result = _run_one_tool(monkeypatch, "web_search", {"query": "weather in San Francisco"})
+ result = _run_one_tool(
+ monkeypatch, "web_search", {"query": "weather in San Francisco"}
+ )
assert "San Francisco: sunny, 68F." in result, result
assert "https://example.test/sf" in result
diff --git a/studio/backend/tests/test_secure_tunnel_gate.py b/studio/backend/tests/test_secure_tunnel_gate.py
index a8c0c2305f..a6084ff4d9 100644
--- a/studio/backend/tests/test_secure_tunnel_gate.py
+++ b/studio/backend/tests/test_secure_tunnel_gate.py
@@ -143,7 +143,9 @@ def test_startup_output_emits_tool_notice_on_network_bind(capsys, monkeypatch):
monkeypatch.setattr(run, "_print_cloudflare_line", lambda *a, **k: None)
monkeypatch.setattr(run, "_localhost_ipv6_mismatch_url", lambda *a, **k: None)
- run._emit_startup_output("0.0.0.0", 8000, "0.0.0.0", secure = False, enable_tools = None)
+ run._emit_startup_output(
+ "0.0.0.0", 8000, "0.0.0.0", secure = False, enable_tools = None
+ )
out = capsys.readouterr().out
assert "Server-side tools" in out
assert "network-reachable" in out
@@ -153,7 +155,9 @@ def test_startup_output_emits_disabled_notice(capsys, monkeypatch):
import run
monkeypatch.setattr(run, "_localhost_ipv6_mismatch_url", lambda *a, **k: None)
- run._emit_startup_output("127.0.0.1", 8000, "127.0.0.1", secure = False, enable_tools = False)
+ run._emit_startup_output(
+ "127.0.0.1", 8000, "127.0.0.1", secure = False, enable_tools = False
+ )
out = capsys.readouterr().out
assert "Server-side tools are DISABLED" in out
diff --git a/studio/backend/tests/test_security_gate_consistency.py b/studio/backend/tests/test_security_gate_consistency.py
index b5f1069f12..ea54bc6024 100644
--- a/studio/backend/tests/test_security_gate_consistency.py
+++ b/studio/backend/tests/test_security_gate_consistency.py
@@ -27,7 +27,9 @@ def _iter_caller_files():
def _passes_token(call: ast.Call) -> bool:
"""True if the call passes an hf_token (keyword, or the 2nd positional slot)."""
- if any(kw.arg in ("hf_token", "token") for kw in call.keywords if kw.arg is not None):
+ if any(
+ kw.arg in ("hf_token", "token") for kw in call.keywords if kw.arg is not None
+ ):
return True
return len(call.args) >= 2
@@ -50,7 +52,9 @@ def test_capability_probes_thread_the_hf_token():
if isinstance(node, ast.Call) and _call_name(node) in _PROBE_FUNCS:
if not _passes_token(node):
rel = path.relative_to(_BACKEND)
- offenders.append(f"{rel}:{node.lineno} {_call_name(node)}() drops the hf_token")
+ offenders.append(
+ f"{rel}:{node.lineno} {_call_name(node)}() drops the hf_token"
+ )
assert not offenders, (
"A capability probe must pass the hf_token so gated/private models classify "
"correctly:\n " + "\n ".join(offenders)
@@ -94,8 +98,12 @@ def test_malware_and_consent_gates_cover_the_lora_base():
offenders = []
for rel in gated_workers:
src = (_BACKEND / rel).read_text()
- runs_gate = "evaluate_file_security(" in src or "evaluate_remote_code_consent" in src
- resolves_base = "get_base_model_from_lora_identifier(" in src or "base_model" in src
+ runs_gate = (
+ "evaluate_file_security(" in src or "evaluate_remote_code_consent" in src
+ )
+ resolves_base = (
+ "get_base_model_from_lora_identifier(" in src or "base_model" in src
+ )
if runs_gate and not resolves_base:
offenders.append(f"{rel} runs a load gate but never resolves the LoRA base")
assert not offenders, "\n".join(offenders)
diff --git a/studio/backend/tests/test_server_disk_logging.py b/studio/backend/tests/test_server_disk_logging.py
index ce733c2aaa..6702b1d891 100644
--- a/studio/backend/tests/test_server_disk_logging.py
+++ b/studio/backend/tests/test_server_disk_logging.py
@@ -91,7 +91,9 @@ class TestSetupServerDiskLogging:
def test_run_server_wires_logging_before_main_import(self):
src = (Path(_BACKEND_DIR) / "run.py").read_text(encoding = "utf-8")
- call_idx = src.index("_setup_server_disk_logging()", src.index("def run_server"))
+ call_idx = src.index(
+ "_setup_server_disk_logging()", src.index("def run_server")
+ )
main_import_idx = src.index("from main import app", src.index("def run_server"))
assert call_idx < main_import_idx, (
"disk logging must be armed before importing main so import-time "
diff --git a/studio/backend/tests/test_setup_cache_env_hf_home.py b/studio/backend/tests/test_setup_cache_env_hf_home.py
index 4520c93a51..3f999031e6 100644
--- a/studio/backend/tests/test_setup_cache_env_hf_home.py
+++ b/studio/backend/tests/test_setup_cache_env_hf_home.py
@@ -18,7 +18,9 @@ _BACKEND_DIR = str(Path(__file__).resolve().parent.parent)
if _BACKEND_DIR not in sys.path:
sys.path.insert(0, _BACKEND_DIR)
-_STORAGE_ROOTS_PATH = Path(__file__).resolve().parent.parent / "utils/paths/storage_roots.py"
+_STORAGE_ROOTS_PATH = (
+ Path(__file__).resolve().parent.parent / "utils/paths/storage_roots.py"
+)
@pytest.fixture(autouse = True)
@@ -28,7 +30,9 @@ def _isolate_studio_home(monkeypatch, tmp_path):
def _load_storage_roots():
- spec = importlib.util.spec_from_file_location("storage_roots_under_test", _STORAGE_ROOTS_PATH)
+ spec = importlib.util.spec_from_file_location(
+ "storage_roots_under_test", _STORAGE_ROOTS_PATH
+ )
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)
return module
diff --git a/studio/backend/tests/test_setup_llama_cpp_backend.py b/studio/backend/tests/test_setup_llama_cpp_backend.py
index 36928c680c..70ca6374ea 100644
--- a/studio/backend/tests/test_setup_llama_cpp_backend.py
+++ b/studio/backend/tests/test_setup_llama_cpp_backend.py
@@ -20,8 +20,12 @@ import pytest
_STUDIO = Path(__file__).resolve().parents[2]
_SETUP_SH = _STUDIO / "setup.sh"
_SETUP_PS1 = _STUDIO / "setup.ps1"
-_SKIP_NO_BASH = pytest.mark.skipif(shutil.which("bash") is None, reason = "bash unavailable")
-_SKIP_NO_PWSH = pytest.mark.skipif(shutil.which("pwsh") is None, reason = "pwsh unavailable")
+_SKIP_NO_BASH = pytest.mark.skipif(
+ shutil.which("bash") is None, reason = "bash unavailable"
+)
+_SKIP_NO_PWSH = pytest.mark.skipif(
+ shutil.which("pwsh") is None, reason = "pwsh unavailable"
+)
def _backend_block() -> str:
diff --git a/studio/backend/tests/test_sf_client_tools_passthrough.py b/studio/backend/tests/test_sf_client_tools_passthrough.py
index f91eec9817..3bafe7c01e 100644
--- a/studio/backend/tests/test_sf_client_tools_passthrough.py
+++ b/studio/backend/tests/test_sf_client_tools_passthrough.py
@@ -43,7 +43,9 @@ SEARCH_TOOL = {
}
_CALL_XML = '{"name": "lookup", "arguments": {"q": "cats"}}'
-_SEARCH_XML = '{"name": "search", "arguments": {"query": "dogs"}}'
+_SEARCH_XML = (
+ '{"name": "search", "arguments": {"query": "dogs"}}'
+)
class _Request:
@@ -145,7 +147,9 @@ def _call(payload, monkeypatch, backend, **install_kwargs):
_install(monkeypatch, backend, **install_kwargs)
async def _run():
- return await openai_chat_completions(payload, request = _Request(), current_subject = "u")
+ return await openai_chat_completions(
+ payload, request = _Request(), current_subject = "u"
+ )
return asyncio.run(_run())
@@ -340,7 +344,9 @@ def test_forced_tool_choice_narrows_promotion(monkeypatch):
def test_parallel_cap_non_streaming(monkeypatch):
backend = _ScriptedBackend(_fixed(_CALL_XML + _SEARCH_XML))
- payload = _request(tools = [LOOKUP_TOOL, SEARCH_TOOL], stream = False, parallel_tool_calls = False)
+ payload = _request(
+ tools = [LOOKUP_TOOL, SEARCH_TOOL], stream = False, parallel_tool_calls = False
+ )
body = _json_body(_call(payload, monkeypatch, backend))
calls = body["choices"][0]["message"]["tool_calls"]
assert len(calls) == 1
@@ -354,7 +360,9 @@ def test_usage_recorded_when_stats_present(monkeypatch):
monitor = _install(monkeypatch, backend)
async def _run():
- return await openai_chat_completions(payload, request = _Request(), current_subject = "u")
+ return await openai_chat_completions(
+ payload, request = _Request(), current_subject = "u"
+ )
asyncio.run(_run())
[entry] = monitor.snapshot()
@@ -410,7 +418,11 @@ def test_nudge_double_failure_relays_original(monkeypatch):
def test_streaming_heals_split_call_into_one_delta(monkeypatch):
# Cumulative snapshots that build the call across many increments.
- pieces = ["{"name": "loo', '{"name": "lookup", "argum']
+ pieces = [
+ "{"name": "loo',
+ '{"name": "lookup", "argum',
+ ]
cumulative = pieces + [_CALL_XML]
backend = _ScriptedBackend(_fixed(*cumulative))
payload = _request(tools = [LOOKUP_TOOL], stream = True)
@@ -419,7 +431,10 @@ def test_streaming_heals_split_call_into_one_delta(monkeypatch):
tool_deltas = [
tc
for o in objs
- for tc in (o.get("choices", [{}])[0].get("delta", {}) or {}).get("tool_calls", []) or []
+ for tc in (o.get("choices", [{}])[0].get("delta", {}) or {}).get(
+ "tool_calls", []
+ )
+ or []
]
assert len(tool_deltas) == 1
assert tool_deltas[0]["function"]["name"] == "lookup"
@@ -464,7 +479,10 @@ def test_streaming_cancel_does_not_finalize_tool_call(monkeypatch):
tool_deltas = [
tc
for o in objs
- for tc in (o.get("choices", [{}])[0].get("delta", {}) or {}).get("tool_calls", []) or []
+ for tc in (o.get("choices", [{}])[0].get("delta", {}) or {}).get(
+ "tool_calls", []
+ )
+ or []
]
assert tool_deltas == [] # no tool promoted after cancel
finishes = [
@@ -511,7 +529,9 @@ def test_streaming_gen_stream_error_is_not_model_text(monkeypatch):
chunks = _collect_sse(response)
objs = _sse_objects(chunks)
- deltas = [o.get("choices", [{}])[0].get("delta", {}) for o in objs if o.get("choices")]
+ deltas = [
+ o.get("choices", [{}])[0].get("delta", {}) for o in objs if o.get("choices")
+ ]
assert any("partial" in json.dumps(delta) for delta in deltas)
assert not any("/tmp/secret" in json.dumps(delta) for delta in deltas)
errors = [o["error"]["message"] for o in objs if "error" in o]
@@ -549,20 +569,28 @@ def test_streaming_repeated_snapshot_no_duplicate_call(monkeypatch):
tool_deltas = [
tc
for o in objs
- for tc in (o.get("choices", [{}])[0].get("delta", {}) or {}).get("tool_calls", []) or []
+ for tc in (o.get("choices", [{}])[0].get("delta", {}) or {}).get(
+ "tool_calls", []
+ )
+ or []
]
assert len(tool_deltas) == 1
def test_streaming_parallel_cap(monkeypatch):
backend = _ScriptedBackend(_fixed(_CALL_XML + _SEARCH_XML))
- payload = _request(tools = [LOOKUP_TOOL, SEARCH_TOOL], stream = True, parallel_tool_calls = False)
+ payload = _request(
+ tools = [LOOKUP_TOOL, SEARCH_TOOL], stream = True, parallel_tool_calls = False
+ )
response = _call(payload, monkeypatch, backend)
objs = _sse_objects(_collect_sse(response))
tool_deltas = [
tc
for o in objs
- for tc in (o.get("choices", [{}])[0].get("delta", {}) or {}).get("tool_calls", []) or []
+ for tc in (o.get("choices", [{}])[0].get("delta", {}) or {}).get(
+ "tool_calls", []
+ )
+ or []
]
assert len(tool_deltas) == 1
assert tool_deltas[0]["function"]["name"] == "lookup"
@@ -658,8 +686,12 @@ def test_discarded_nudge_retry_reports_first_attempt_usage(monkeypatch):
# Double-failure nudge: the first response is delivered, but the retry's
# generate() overwrites stats_holder. The monitor must record the FIRST
# attempt's usage, not the discarded retry's.
- first_stats = {"usage": {"prompt_tokens": 7, "completion_tokens": 3, "total_tokens": 10}}
- retry_stats = {"usage": {"prompt_tokens": 99, "completion_tokens": 99, "total_tokens": 198}}
+ first_stats = {
+ "usage": {"prompt_tokens": 7, "completion_tokens": 3, "total_tokens": 10}
+ }
+ retry_stats = {
+ "usage": {"prompt_tokens": 99, "completion_tokens": 99, "total_tokens": 198}
+ }
class _PerCallStatsBackend(_ScriptedBackend):
def __init__(self):
@@ -687,7 +719,9 @@ def test_discarded_nudge_retry_reports_first_attempt_usage(monkeypatch):
monitor = _install(monkeypatch, backend)
async def _run():
- return await openai_chat_completions(payload, request = _Request(), current_subject = "u")
+ return await openai_chat_completions(
+ payload, request = _Request(), current_subject = "u"
+ )
asyncio.run(_run())
assert len(backend.calls) == 2 # first attempt + one discarded retry
@@ -703,7 +737,9 @@ def test_monitor_records_healed_call_not_raw_xml(monkeypatch):
monitor = _install(monkeypatch, backend)
async def _run():
- return await openai_chat_completions(payload, request = _Request(), current_subject = "u")
+ return await openai_chat_completions(
+ payload, request = _Request(), current_subject = "u"
+ )
asyncio.run(_run())
snap = monitor.snapshot(include_details = True)
@@ -721,7 +757,9 @@ def test_streaming_monitor_records_healed_call_not_raw_xml(monkeypatch):
monitor = _install(monkeypatch, backend)
async def _run():
- return await openai_chat_completions(payload, request = _Request(), current_subject = "u")
+ return await openai_chat_completions(
+ payload, request = _Request(), current_subject = "u"
+ )
response = asyncio.run(_run())
_collect_sse(response)
@@ -798,7 +836,9 @@ def test_string_arguments_history_deserialized_for_template(monkeypatch):
],
)
_json_body(_call(payload, monkeypatch, backend))
- assistant = next(m for m in backend.calls[0]["messages"] if m["role"] == "assistant")
+ assistant = next(
+ m for m in backend.calls[0]["messages"] if m["role"] == "assistant"
+ )
assert assistant["tool_calls"][0]["function"]["arguments"] == {"q": "weather"}
@@ -825,7 +865,9 @@ def test_unparseable_arguments_string_left_untouched(monkeypatch):
)
body = _json_body(_call(payload, monkeypatch, backend))
assert body["choices"][0]["message"]["content"] == "ok"
- assistant = next(m for m in backend.calls[0]["messages"] if m["role"] == "assistant")
+ assistant = next(
+ m for m in backend.calls[0]["messages"] if m["role"] == "assistant"
+ )
assert assistant["tool_calls"][0]["function"]["arguments"] == "not json {"
diff --git a/studio/backend/tests/test_shutdown_preserves_live_worker.py b/studio/backend/tests/test_shutdown_preserves_live_worker.py
index faf273411c..fec90c2054 100644
--- a/studio/backend/tests/test_shutdown_preserves_live_worker.py
+++ b/studio/backend/tests/test_shutdown_preserves_live_worker.py
@@ -133,7 +133,9 @@ class TestSpawnPathsHonorFailedShutdown:
o._export_active = False
o._ensure_subprocess_alive = lambda: True
o._shutdown_subprocess = lambda *a, **k: False
- o._spawn_subprocess = lambda cfg: pytest.fail("must not spawn over a live survivor")
+ o._spawn_subprocess = lambda cfg: pytest.fail(
+ "must not spawn over a live survivor"
+ )
o._record_op_finished = lambda *a, **k: None
monkeypatch.setattr(tv, "sidecar_swap_in_progress", lambda: False)
diff --git a/studio/backend/tests/test_slot_offload_fit.py b/studio/backend/tests/test_slot_offload_fit.py
index d354c7e113..4acf82733d 100644
--- a/studio/backend/tests/test_slot_offload_fit.py
+++ b/studio/backend/tests/test_slot_offload_fit.py
@@ -111,5 +111,7 @@ class TestSlotsThatFitOnGpu:
def test_kv_counted_per_candidate(self):
# A non-zero (slot-independent) KV shifts the threshold: with 3000 MiB KV and
# base 19500 (= 22500 total at par-independent terms) the same par3 fit holds.
- gi, use_fit, slots = _run(_backend(kv_fixed_mib = 3000), 4, 19500, [(0, 24576)], {0: 24576})
+ gi, use_fit, slots = _run(
+ _backend(kv_fixed_mib = 3000), 4, 19500, [(0, 24576)], {0: 24576}
+ )
assert use_fit is False and slots == 3
diff --git a/studio/backend/tests/test_ssm_runtime.py b/studio/backend/tests/test_ssm_runtime.py
index bb0caa2887..f61070a153 100644
--- a/studio/backend/tests/test_ssm_runtime.py
+++ b/studio/backend/tests/test_ssm_runtime.py
@@ -85,7 +85,9 @@ def test_non_ssm_models_not_detected(name):
def test_probe_lora_uses_base_not_adapter_name():
# A plain-Llama LoRA whose adapter id contains an SSM substring is not SSM.
- probe = ssm_runtime.ssm_probe_identifier("user/falcon-h1-lora", "meta-llama/Llama-3-8B")
+ probe = ssm_runtime.ssm_probe_identifier(
+ "user/falcon-h1-lora", "meta-llama/Llama-3-8B"
+ )
assert probe == "meta-llama/Llama-3-8B"
assert ssm_runtime.model_is_ssm(probe) is False
@@ -96,7 +98,10 @@ def test_probe_lora_on_ssm_base_detected():
def test_probe_plain_hf_id_unchanged():
- assert ssm_runtime.ssm_probe_identifier("nvidia/Nemotron-H-8B") == "nvidia/Nemotron-H-8B"
+ assert (
+ ssm_runtime.ssm_probe_identifier("nvidia/Nemotron-H-8B")
+ == "nvidia/Nemotron-H-8B"
+ )
def test_probe_local_path_uses_basename(tmp_path):
@@ -119,8 +124,12 @@ def test_probe_local_ssm_checkpoint_basename_detected(tmp_path):
def test_noop_for_non_ssm_model(monkeypatch):
calls = []
- monkeypatch.setattr(ssm_runtime, "_install_kernel", lambda **k: calls.append(k) or True)
- ssm_runtime.ensure_ssm_runtime("unsloth/Llama-3.2-1B-Instruct", run = lambda *a, **k: _Result())
+ monkeypatch.setattr(
+ ssm_runtime, "_install_kernel", lambda **k: calls.append(k) or True
+ )
+ ssm_runtime.ensure_ssm_runtime(
+ "unsloth/Llama-3.2-1B-Instruct", run = lambda *a, **k: _Result()
+ )
assert calls == [] # nothing installed for a plain transformer
@@ -165,7 +174,9 @@ def test_causal_only_install_failure_is_not_fatal(monkeypatch):
def test_ssm_causal_failure_nonfatal_when_mamba_ok(monkeypatch):
# causal-conv1d is best-effort even for a true SSM model; only mamba-ssm is fatal.
monkeypatch.setattr(
- ssm_runtime, "_install_kernel", lambda *, import_name, **_: import_name == "mamba_ssm"
+ ssm_runtime,
+ "_install_kernel",
+ lambda *, import_name, **_: import_name == "mamba_ssm",
)
ssm_runtime.ensure_ssm_runtime("unsloth/NVIDIA-Nemotron-3-Nano-4B") # no raise
@@ -173,7 +184,9 @@ def test_ssm_causal_failure_nonfatal_when_mamba_ok(monkeypatch):
def test_install_kernel_idempotent_when_present(monkeypatch):
monkeypatch.setattr(ssm_runtime, "_is_importable", lambda name: True)
called = []
- monkeypatch.setattr(ssm_runtime, "url_exists", lambda u: called.append("url") or True)
+ monkeypatch.setattr(
+ ssm_runtime, "url_exists", lambda u: called.append("url") or True
+ )
ok = ssm_runtime._install_kernel(
import_name = "mamba_ssm",
display_name = "mamba-ssm",
@@ -192,7 +205,9 @@ def test_install_kernel_uses_prebuilt_wheel(monkeypatch):
# not importable before install, importable after the wheel lands
states = iter([False, True])
monkeypatch.setattr(ssm_runtime, "_is_importable", lambda name: next(states))
- monkeypatch.setattr(ssm_runtime, "probe_torch_wheel_env", lambda timeout = 30: {"x": "y"})
+ monkeypatch.setattr(
+ ssm_runtime, "probe_torch_wheel_env", lambda timeout = 30: {"x": "y"}
+ )
seen = {}
monkeypatch.setattr(
ssm_runtime,
@@ -250,7 +265,9 @@ def test_install_kernel_falls_back_to_source(monkeypatch):
def test_is_importable_invalidates_caches(monkeypatch):
calls = []
- monkeypatch.setattr(ssm_runtime.importlib, "invalidate_caches", lambda: calls.append(1))
+ monkeypatch.setattr(
+ ssm_runtime.importlib, "invalidate_caches", lambda: calls.append(1)
+ )
assert ssm_runtime._is_importable("sys") is True
assert calls # caches invalidated before attempting the import
@@ -299,7 +316,9 @@ def test_ssm_model_on_windows_still_installs_mamba(monkeypatch):
lambda *, import_name, **_: installed.append(import_name) or True,
)
ssm_runtime.ensure_ssm_runtime("unsloth/NVIDIA-Nemotron-3-Nano-4B")
- assert installed == ["mamba_ssm"] # causal-conv1d skipped, mamba-ssm still attempted
+ assert installed == [
+ "mamba_ssm"
+ ] # causal-conv1d skipped, mamba-ssm still attempted
def test_wheel_installed_but_not_importable_falls_back_to_source(monkeypatch):
@@ -335,7 +354,9 @@ def test_hip_source_build_requires_hipcc(monkeypatch):
ssm_runtime, "probe_torch_wheel_env", lambda timeout = 30: {"hip_version": "6.2"}
)
monkeypatch.setattr(ssm_runtime, "direct_wheel_url", lambda **k: None)
- monkeypatch.setattr(ssm_runtime.shutil, "which", lambda name: None) # no uv, no hipcc
+ monkeypatch.setattr(
+ ssm_runtime.shutil, "which", lambda name: None
+ ) # no uv, no hipcc
ran = []
ok = ssm_runtime._install_kernel(
import_name = "causal_conv1d",
@@ -380,7 +401,9 @@ def test_hip_uv_source_build_uses_no_cache(monkeypatch):
ssm_runtime, "probe_torch_wheel_env", lambda timeout = 30: {"hip_version": "6.2"}
)
monkeypatch.setattr(ssm_runtime, "direct_wheel_url", lambda **k: None)
- monkeypatch.setattr(ssm_runtime.shutil, "which", lambda name: "/usr/bin/" + name) # uv + hipcc
+ monkeypatch.setattr(
+ ssm_runtime.shutil, "which", lambda name: "/usr/bin/" + name
+ ) # uv + hipcc
monkeypatch.setattr(ssm_runtime, "_hipcc_gcc_install_dir", lambda: None)
cmds = []
ssm_runtime._install_kernel(
@@ -464,7 +487,9 @@ def test_pre_import_gate_is_transformers_free():
with patch.object(fs, "_fetch_security_status", return_value = None):
fs.evaluate_file_security("nvidia/Nemotron-H-8B", load_subdirs = ())
with patch.object(
- consent, "_load_remote_code_configs", return_value = [{"model_type": "nemotron_h"}]
+ consent,
+ "_load_remote_code_configs",
+ return_value = [{"model_type": "nemotron_h"}],
):
from utils.security import evaluate_remote_code_consent_for_targets
evaluate_remote_code_consent_for_targets(
@@ -476,7 +501,9 @@ def test_pre_import_gate_is_transformers_free():
finally:
# Drop anything the gate imported, then rebind the original module objects so later
# tests see the same instances they captured at import time.
- for m in [m for m in list(_sys.modules) if _is_gated_module(m) and m not in _saved]:
+ for m in [
+ m for m in list(_sys.modules) if _is_gated_module(m) and m not in _saved
+ ]:
_sys.modules.pop(m, None)
_sys.modules.update(_saved)
@@ -527,7 +554,9 @@ def test_constants_match_training_worker():
assert set(ssm_runtime.SSM_MODEL_SUBSTRINGS) == set(tw._SSM_MODEL_SUBSTRINGS)
assert ssm_runtime.MAMBA_SSM_PACKAGE_VERSION == tw._MAMBA_SSM_PACKAGE_VERSION
assert ssm_runtime.MAMBA_SSM_RELEASE_TAG == tw._MAMBA_SSM_RELEASE_TAG
- assert ssm_runtime.CAUSAL_CONV1D_PACKAGE_VERSION == tw._CAUSAL_CONV1D_PACKAGE_VERSION
+ assert (
+ ssm_runtime.CAUSAL_CONV1D_PACKAGE_VERSION == tw._CAUSAL_CONV1D_PACKAGE_VERSION
+ )
assert ssm_runtime.CAUSAL_CONV1D_RELEASE_TAG == tw._CAUSAL_CONV1D_RELEASE_TAG
# detection must agree with the training worker across SSM + non-SSM names
@@ -541,6 +570,6 @@ def test_constants_match_training_worker():
"unsloth/Llama-3.2-1B-Instruct",
"unsloth/Qwen2.5-7B",
):
- assert ssm_runtime.model_wants_causal_conv1d(name) == tw._model_wants_causal_conv1d(
+ assert ssm_runtime.model_wants_causal_conv1d(
name
- ), name
+ ) == tw._model_wants_causal_conv1d(name), name
diff --git a/studio/backend/tests/test_startup_banner_loopback.py b/studio/backend/tests/test_startup_banner_loopback.py
index c8875bf5db..8735449c50 100644
--- a/studio/backend/tests/test_startup_banner_loopback.py
+++ b/studio/backend/tests/test_startup_banner_loopback.py
@@ -15,7 +15,9 @@ from startup_banner import print_studio_access_banner
def test_non_alias_loopback_shows_real_address(capsys):
# A server bound to 127.0.0.2 does not listen on 127.0.0.1.
- print_studio_access_banner(port = 8891, bind_host = "127.0.0.2", display_host = "127.0.0.2")
+ print_studio_access_banner(
+ port = 8891, bind_host = "127.0.0.2", display_host = "127.0.0.2"
+ )
out = capsys.readouterr().out
assert "http://127.0.0.2:8891" in out
assert "http://127.0.0.1" not in out
@@ -32,7 +34,9 @@ def test_banner_prints_on_strict_cp1252_stdout(monkeypatch):
stdout = io.TextIOWrapper(buf, encoding = "cp1252", errors = "strict")
monkeypatch.setattr(sys, "stdout", stdout)
- print_studio_access_banner(port = 8891, bind_host = "127.0.0.1", display_host = "127.0.0.1")
+ print_studio_access_banner(
+ port = 8891, bind_host = "127.0.0.1", display_host = "127.0.0.1"
+ )
stdout.flush()
out = buf.getvalue().decode("cp1252")
@@ -60,6 +64,8 @@ def test_banner_print_fallback_handles_unknown_stdout_encoding(monkeypatch):
stdout = InvalidEncodingStdout()
monkeypatch.setattr(sys, "stdout", stdout)
- print_studio_access_banner(port = 8891, bind_host = "127.0.0.1", display_host = "127.0.0.1")
+ print_studio_access_banner(
+ port = 8891, bind_host = "127.0.0.1", display_host = "127.0.0.1"
+ )
assert "? Unsloth Studio is running" in stdout.getvalue()
diff --git a/studio/backend/tests/test_studio_api.py b/studio/backend/tests/test_studio_api.py
index 087c00b648..c4d46621c3 100644
--- a/studio/backend/tests/test_studio_api.py
+++ b/studio/backend/tests/test_studio_api.py
@@ -72,7 +72,11 @@ DEFAULT_VARIANT = "UD-Q4_K_XL"
PORT = 18222 # high port unlikely to collide
HOST = "127.0.0.1"
STARTUP_TIMEOUT = 120 # seconds
-LOG_FILE = Path(__file__).resolve().parent.parent.parent.parent / "temp" / "test_studio_api.log"
+LOG_FILE = (
+ Path(__file__).resolve().parent.parent.parent.parent
+ / "temp"
+ / "test_studio_api.log"
+)
# Helpers
@@ -216,7 +220,9 @@ def test_openai_sdk(base_url: str, api_key: str):
client = OpenAI(base_url = f"{base_url}/v1", api_key = api_key)
response = client.chat.completions.create(
model = "current",
- messages = [{"role": "user", "content": "What is 2+2? Answer with just the number."}],
+ messages = [
+ {"role": "user", "content": "What is 2+2? Answer with just the number."}
+ ],
stream = True,
)
content_parts = []
@@ -379,7 +385,9 @@ def test_openai_tools_nonstream(base_url: str, api_key: str):
assert "city" in parsed, f"Tool call missing required 'city' arg: {parsed}"
# Usage must be non-zero (was 0 before the fix)
usage = data.get("usage") or {}
- assert usage.get("prompt_tokens", 0) > 0, f"Expected non-zero prompt_tokens; got {usage}"
+ assert (
+ usage.get("prompt_tokens", 0) > 0
+ ), f"Expected non-zero prompt_tokens; got {usage}"
assert data.get("id"), "Missing response id"
print(
f" PASS openai tools non-stream: "
@@ -404,7 +412,8 @@ def test_openai_tools_stream(base_url: str, api_key: str):
assert status == 200, f"Expected 200, got {status}"
assert len(chunks) > 0, "No SSE chunks received"
assert _final_finish_reason(chunks) == "tool_calls", (
- f"Expected final finish_reason='tool_calls', got " f"{_final_finish_reason(chunks)!r}"
+ f"Expected final finish_reason='tool_calls', got "
+ f"{_final_finish_reason(chunks)!r}"
)
assembled = _collect_streamed_tool_calls(chunks)
assert len(assembled) >= 1, "No tool_calls reassembled from stream"
@@ -487,7 +496,8 @@ def test_openai_sdk_tool_calling(base_url: str, api_key: str):
stream = False,
)
assert resp.choices[0].finish_reason == "tool_calls", (
- f"Expected finish_reason='tool_calls', got " f"{resp.choices[0].finish_reason!r}"
+ f"Expected finish_reason='tool_calls', got "
+ f"{resp.choices[0].finish_reason!r}"
)
tool_calls = resp.choices[0].message.tool_calls
assert tool_calls and len(tool_calls) >= 1, "No tool_calls from SDK"
@@ -495,7 +505,9 @@ def test_openai_sdk_tool_calling(base_url: str, api_key: str):
assert tc.function.name == "get_weather"
parsed = json.loads(tc.function.arguments)
assert "city" in parsed
- print(f" PASS openai SDK tool calling: " f"tool={tc.function.name}, args={parsed}")
+ print(
+ f" PASS openai SDK tool calling: " f"tool={tc.function.name}, args={parsed}"
+ )
def test_invalid_key_rejected(base_url: str):
@@ -638,7 +650,9 @@ def test_anthropic_sdk(base_url: str, api_key: str):
message = client.messages.create(
model = "default",
max_tokens = 100,
- messages = [{"role": "user", "content": "What is 2+2? Answer with just the number."}],
+ messages = [
+ {"role": "user", "content": "What is 2+2? Answer with just the number."}
+ ],
)
assert message.role == "assistant"
assert len(message.content) > 0, "Empty content"
@@ -689,7 +703,9 @@ def test_anthropic_with_tools(base_url: str, api_key: str):
assert "message_stop" in event_types, "Missing message_stop"
full = _collect_anthropic_text(events)
- print(f" PASS anthropic with tools: {len(events)} events, {len(full)} chars content")
+ print(
+ f" PASS anthropic with tools: {len(events)} events, {len(full)} chars content"
+ )
def test_anthropic_tool_choice_any(base_url: str, api_key: str):
@@ -749,7 +765,8 @@ def test_anthropic_tool_choice_any(base_url: str, api_key: str):
tool_use_starts = [
e
for e in events
- if e[0] == "content_block_start" and e[1].get("content_block", {}).get("type") == "tool_use"
+ if e[0] == "content_block_start"
+ and e[1].get("content_block", {}).get("type") == "tool_use"
]
assert len(tool_use_starts) >= 1, "No tool_use content block emitted"
print(
@@ -799,7 +816,9 @@ def _start_server(model: str, variant: str | None) -> tuple[subprocess.Popen, st
if proc.poll() is not None:
log_fh.flush()
log_text = LOG_FILE.read_text()
- raise RuntimeError(f"Server exited early (code {proc.returncode}):\n{log_text[-2000:]}")
+ raise RuntimeError(
+ f"Server exited early (code {proc.returncode}):\n{log_text[-2000:]}"
+ )
log_text = LOG_FILE.read_text()
m = re.search(r"API Key:\s+(sk-unsloth-[a-f0-9]+)", log_text)
if m:
@@ -809,7 +828,9 @@ def _start_server(model: str, variant: str | None) -> tuple[subprocess.Popen, st
if not api_key:
log_text = LOG_FILE.read_text()
_kill_server(proc)
- raise RuntimeError(f"Timed out waiting for API key in server output:\n{log_text[-2000:]}")
+ raise RuntimeError(
+ f"Timed out waiting for API key in server output:\n{log_text[-2000:]}"
+ )
# Wait a moment for the model to be fully loaded
time.sleep(2)
@@ -836,7 +857,9 @@ def _kill_server(proc: subprocess.Popen):
def main():
- parser = argparse.ArgumentParser(description = "End-to-end tests for unsloth studio run")
+ parser = argparse.ArgumentParser(
+ description = "End-to-end tests for unsloth studio run"
+ )
parser.add_argument(
"--model",
default = DEFAULT_MODEL,
@@ -870,7 +893,9 @@ def main():
run_test(test_help_output)
# 2-16. Start server and run API tests
- print(f"\nStarting server: {args.model} (variant={args.gguf_variant}) on port {PORT}...")
+ print(
+ f"\nStarting server: {args.model} (variant={args.gguf_variant}) on port {PORT}..."
+ )
proc = None
try:
proc, api_key = _start_server(args.model, args.gguf_variant)
diff --git a/studio/backend/tests/test_tensor_parallel.py b/studio/backend/tests/test_tensor_parallel.py
index 00c7aeac69..36d22b2a48 100644
--- a/studio/backend/tests/test_tensor_parallel.py
+++ b/studio/backend/tests/test_tensor_parallel.py
@@ -92,7 +92,9 @@ def test_load_request_accepts_tensor_parallel():
def test_load_request_round_trips_json_key():
# The frontend sends the snake_case key verbatim.
- req = LoadRequest.model_validate({"model_path": "owner/repo", "tensor_parallel": True})
+ req = LoadRequest.model_validate(
+ {"model_path": "owner/repo", "tensor_parallel": True}
+ )
assert req.tensor_parallel is True
assert req.model_dump()["tensor_parallel"] is True
@@ -266,7 +268,9 @@ def test_proportional_tensor_split_is_emitted_in_tensor_mode():
# --tensor-split earlier in the source from the user's per-GPU shares.
ts = src.find('"--tensor-split"', gate)
nxt_else = src.find("self._tensor_parallel = False")
- assert 0 <= gate < ts < nxt_else, "--tensor-split must be emitted under `if tensor_parallel:`"
+ assert (
+ 0 <= gate < ts < nxt_else
+ ), "--tensor-split must be emitted under `if tensor_parallel:`"
assert "tp_tensor_split" in src[gate:nxt_else]
@@ -301,7 +305,9 @@ def test_probe_mtp_decode_returns_false_on_crash(monkeypatch):
self.status_code = code
backend._process = None # liveness check skipped; exercise the HTTP result
- monkeypatch.setattr(llama_cpp_module.httpx, "post", lambda *a, **k: _Resp(200), raising = False)
+ monkeypatch.setattr(
+ llama_cpp_module.httpx, "post", lambda *a, **k: _Resp(200), raising = False
+ )
assert backend._probe_mtp_decode(timeout = 1.0) is True
def _drop(*a, **k):
@@ -310,12 +316,16 @@ def test_probe_mtp_decode_returns_false_on_crash(monkeypatch):
monkeypatch.setattr(llama_cpp_module.httpx, "post", _drop, raising = False)
assert backend._probe_mtp_decode(timeout = 1.0) is False
- monkeypatch.setattr(llama_cpp_module.httpx, "post", lambda *a, **k: _Resp(500), raising = False)
+ monkeypatch.setattr(
+ llama_cpp_module.httpx, "post", lambda *a, **k: _Resp(500), raising = False
+ )
assert backend._probe_mtp_decode(timeout = 1.0) is False
# 200 but the server aborted right after (poll() returns an exit code).
backend._process = _FakeProcess()
- monkeypatch.setattr(llama_cpp_module.httpx, "post", lambda *a, **k: _Resp(200), raising = False)
+ monkeypatch.setattr(
+ llama_cpp_module.httpx, "post", lambda *a, **k: _Resp(200), raising = False
+ )
assert backend._probe_mtp_decode(timeout = 1.0) is False
@@ -442,7 +452,9 @@ def test_runtime_recovery_strips_user_mtp_extra_args(monkeypatch):
# A user --spec-type draft-mtp in extra_args must be neutralised on the reload
# (append a last-wins --spec-default) so MTP can't re-engage and loop.
b = _recovery_backend()
- b._last_load_kwargs = dict(b._last_load_kwargs, extra_args = ["--spec-type", "draft-mtp"])
+ b._last_load_kwargs = dict(
+ b._last_load_kwargs, extra_args = ["--spec-type", "draft-mtp"]
+ )
done = threading.Event()
captured = {}
@@ -691,11 +703,15 @@ def test_fit_context_budget_frac_override_is_tighter():
pool_mib = 24 * 1024 # tight enough that KV capping bites
fit_default = backend._fit_context_to_vram(131072, pool_mib, model_size, "f16")
- fit_tp = backend._fit_context_to_vram(131072, pool_mib, model_size, "f16", budget_frac = 0.80)
+ fit_tp = backend._fit_context_to_vram(
+ 131072, pool_mib, model_size, "f16", budget_frac = 0.80
+ )
assert fit_tp < 131072, "expected the context to be capped at this VRAM tier"
assert fit_tp <= fit_default, "a tighter budget must not allow MORE context"
# Omitting the override must reproduce the default budget exactly.
- assert backend._fit_context_to_vram(131072, pool_mib, model_size, "f16") == fit_default
+ assert (
+ backend._fit_context_to_vram(131072, pool_mib, model_size, "f16") == fit_default
+ )
# ── unsupported-arch load failure -> clean message ───────────────────
@@ -735,7 +751,9 @@ def _plan(
mtp = False,
):
b = _kv_seeded_backend()
- return b, b._plan_tensor_parallel(gpus, int(model_gb * _GB), target, mtp_engaged = mtp)
+ return b, b._plan_tensor_parallel(
+ gpus, int(model_gb * _GB), target, mtp_engaged = mtp
+ )
def _kv_budget_b(model_gb, gpus = _ASYM):
@@ -814,7 +832,9 @@ def test_tp_plan_max_available_ctx_reports_native_not_explicit_ctx():
# An explicit small ctx caps effective_ctx but the UI ceiling
# (max_available_ctx) must reflect the native/hardware cap, not the request.
b = _kv_seeded_backend()
- ec, mac, _gi, _ts = b._plan_tensor_parallel(_ASYM, int(50 * _GB), 8192, max_target_ctx = 131072)
+ ec, mac, _gi, _ts = b._plan_tensor_parallel(
+ _ASYM, int(50 * _GB), 8192, max_target_ctx = 131072
+ )
_, native_mac, *_ = b._plan_tensor_parallel(_ASYM, int(50 * _GB), 131072)
assert ec == 8192 # explicit request honored for the load
assert mac == native_mac > ec # ceiling reflects the hardware cap
@@ -865,7 +885,9 @@ def test_tp_plan_soft_overhead_reserved_against_budget():
# the replicated context compute, so the real footprint stays within the pool.
b = _kv_seeded_backend()
soft = 2 * _GB
- ec, *_r = b._plan_tensor_parallel(_ASYM, int(50 * _GB), 131072, soft_overhead_bytes = soft)
+ ec, *_r = b._plan_tensor_parallel(
+ _ASYM, int(50 * _GB), 131072, soft_overhead_bytes = soft
+ )
cc = len(_ASYM) * b._compute_buffer_ctx_bytes(ec, None, None)
assert b._estimate_kv_cache_bytes(ec) + cc + soft <= _kv_budget_b(50)
@@ -892,7 +914,10 @@ def test_tp_plan_weighted_split_keeps_small_gpu_within_budget():
# card was placed over its budget; the cc term is what pulls it back.
old_adj = [int(free_by_idx[i] * _CTX_FIT_VRAM_FRACTION - reserve) for i in gi]
old_small_placed = split_content_mib * old_adj[1] / sum(old_adj)
- assert old_small_placed + reserve + cc_per_dev > free_by_idx[1] * _CTX_FIT_VRAM_FRACTION
+ assert (
+ old_small_placed + reserve + cc_per_dev
+ > free_by_idx[1] * _CTX_FIT_VRAM_FRACTION
+ )
def test_tp_plan_no_kv_metadata_floors_context():
@@ -923,7 +948,9 @@ def test_tp_plan_drops_gpu_below_buffer_reserve():
# split (and gpu_indices reflects only the usable device).
b = _kv_seeded_backend()
reserve = LlamaCppBackend._TENSOR_PARALLEL_BUFFER_RESERVE_MIB
- ec, mac, gi, ts = b._plan_tensor_parallel([(0, 48000), (1, reserve - 1)], int(8 * _GB), 8192)
+ ec, mac, gi, ts = b._plan_tensor_parallel(
+ [(0, 48000), (1, reserve - 1)], int(8 * _GB), 8192
+ )
assert gi == [0]
assert ts is None
@@ -943,7 +970,9 @@ class _RecordingLoader:
self.calls: list[tuple] = []
async def __call__(self, tensor_parallel, extra_args):
- self.calls.append((tensor_parallel, list(extra_args) if extra_args else extra_args))
+ self.calls.append(
+ (tensor_parallel, list(extra_args) if extra_args else extra_args)
+ )
if resolve_tensor_parallel(extra_args, tensor_parallel):
raise RuntimeError("llama-server failed to start")
return True
@@ -952,7 +981,9 @@ class _RecordingLoader:
def test_tensor_fallback_retries_layer_on_crash():
loader = _RecordingLoader()
ok = asyncio.run(
- load_with_tensor_fallback(loader, requested_tensor = True, extra_args = None, label = "m")
+ load_with_tensor_fallback(
+ loader, requested_tensor = True, extra_args = None, label = "m"
+ )
)
assert ok is True
# tensor first (crashes), then layer split.
@@ -967,7 +998,9 @@ def test_tensor_fallback_no_retry_on_success():
return True
ok = asyncio.run(
- load_with_tensor_fallback(_ok, requested_tensor = True, extra_args = None, label = "m")
+ load_with_tensor_fallback(
+ _ok, requested_tensor = True, extra_args = None, label = "m"
+ )
)
assert ok is True
assert calls == [True] # no fallback when the tensor load succeeds
@@ -1001,7 +1034,9 @@ def test_tensor_fallback_returns_false_when_both_attempts_fail():
return False
ok = asyncio.run(
- load_with_tensor_fallback(_always_false, requested_tensor = True, extra_args = None, label = "m")
+ load_with_tensor_fallback(
+ _always_false, requested_tensor = True, extra_args = None, label = "m"
+ )
)
assert ok is False
assert calls == [True, False] # tried tensor, then layer split
@@ -1044,7 +1079,9 @@ def test_tensor_fallback_strips_split_mode_from_extras_on_retry(extras):
# other flags, else tensor is re-enabled and relaunches the crash.
loader = _RecordingLoader()
ok = asyncio.run(
- load_with_tensor_fallback(loader, requested_tensor = False, extra_args = extras, label = "m")
+ load_with_tensor_fallback(
+ loader, requested_tensor = False, extra_args = extras, label = "m"
+ )
)
assert ok is True
assert len(loader.calls) == 2
@@ -1109,10 +1146,16 @@ def test_tensor_caps_context_to_total_vram_budget():
assert with_total < without # total cap tightens the chosen context
MIB = 1024 * 1024
- reserve = LlamaCppBackend._TENSOR_PARALLEL_BUFFER_RESERVE_MIB # flat (no vocab dims)
+ reserve = (
+ LlamaCppBackend._TENSOR_PARALLEL_BUFFER_RESERVE_MIB
+ ) # flat (no vocab dims)
pool_usable = sum(f - (1.0 - _CTX_FIT_VRAM_FRACTION) * totals[i] for i, f in gpus)
- foot_total = (model + b._estimate_kv_cache_bytes(with_total, None)) / MIB + len(gpus) * reserve
- foot_free = (model + b._estimate_kv_cache_bytes(without, None)) / MIB + len(gpus) * reserve
+ foot_total = (model + b._estimate_kv_cache_bytes(with_total, None)) / MIB + len(
+ gpus
+ ) * reserve
+ foot_free = (model + b._estimate_kv_cache_bytes(without, None)) / MIB + len(
+ gpus
+ ) * reserve
assert foot_total <= pool_usable + 2 # fix: fits the total-based budget
assert foot_free > pool_usable # old behavior over-spent the cushion
@@ -1126,12 +1169,18 @@ def test_tensor_unknown_total_keeps_fraction_cushion():
MIB = 1024 * 1024
reserve = LlamaCppBackend._TENSOR_PARALLEL_BUFFER_RESERVE_MIB
model = int(18 * _GB)
- ec_zero, *_ = b._plan_tensor_parallel(gpus, model, 131072, total_by_idx = {0: 0, 1: 0})
+ ec_zero, *_ = b._plan_tensor_parallel(
+ gpus, model, 131072, total_by_idx = {0: 0, 1: 0}
+ )
ec_none, *_ = b._plan_tensor_parallel(gpus, model, 131072)
assert ec_zero == ec_none # total 0 == total absent: both use free*frac
pool_free = sum(f for _, f in gpus)
- foot = (model + b._estimate_kv_cache_bytes(ec_zero, None)) / MIB + len(gpus) * reserve
- assert foot <= pool_free * _CTX_FIT_VRAM_FRACTION + 2 # within free*frac, not raw free
+ foot = (model + b._estimate_kv_cache_bytes(ec_zero, None)) / MIB + len(
+ gpus
+ ) * reserve
+ assert (
+ foot <= pool_free * _CTX_FIT_VRAM_FRACTION + 2
+ ) # within free*frac, not raw free
def test_tensor_reserve_scales_with_ubatch():
@@ -1181,7 +1230,9 @@ def test_tensor_admission_drops_gpu_below_usable_budget():
b = _kv_seeded_backend()
gpus = [(0, 6000), (1, 40000)]
totals = {0: 81920, 1: 81920}
- _ec, _mac, gi, ts = b._plan_tensor_parallel(gpus, int(8 * _GB), 8192, total_by_idx = totals)
+ _ec, _mac, gi, ts = b._plan_tensor_parallel(
+ gpus, int(8 * _GB), 8192, total_by_idx = totals
+ )
assert gi == [1] and ts is None # GPU 0 excluded on usable budget
_ec2, _mac2, gi_raw, _ts2 = b._plan_tensor_parallel(gpus, int(8 * _GB), 8192)
assert gi_raw == [0, 1] # raw free would have admitted both
@@ -1232,6 +1283,12 @@ def test_load_model_restores_quantized_kv_on_tensor_downgrade():
compact = "".join(inspect.getsource(LlamaCppBackend.load_model).split())
assert "_tensor_dropped_cache_type_kv=cache_type_kv" in compact # captured pre-null
# Restore is shared in one closure, called at every tensor->layer downgrade.
- assert "cache_type_kv=_tensor_dropped_cache_type_kv" in compact # restored in the closure
- assert "def_restore_after_tensor_downgrade():" in compact # one shared restore helper
- assert compact.count("_restore_after_tensor_downgrade()") >= 3 # called at each downgrade
+ assert (
+ "cache_type_kv=_tensor_dropped_cache_type_kv" in compact
+ ) # restored in the closure
+ assert (
+ "def_restore_after_tensor_downgrade():" in compact
+ ) # one shared restore helper
+ assert (
+ compact.count("_restore_after_tensor_downgrade()") >= 3
+ ) # called at each downgrade
diff --git a/studio/backend/tests/test_think_prefill_reemit.py b/studio/backend/tests/test_think_prefill_reemit.py
index 346399c3b2..07a2df7ae1 100644
--- a/studio/backend/tests/test_think_prefill_reemit.py
+++ b/studio/backend/tests/test_think_prefill_reemit.py
@@ -162,7 +162,11 @@ def test_native_template_fallback_returns_selected_reasoning_metadata():
def render(tokenizer, msgs, *, tools, **_kw):
body = "".join(message["content"] for message in msgs)
suffix = "|TOOLS" if tools else ""
- return body + suffix if tokenizer.chat_template == "NATIVE <|channel>thought\n" else body
+ return (
+ body + suffix
+ if tokenizer.chat_template == "NATIVE <|channel>thought\n"
+ else body
+ )
result = render_with_native_template_fallback(
formatted_prompt = "hi",
@@ -185,7 +189,9 @@ def test_native_template_fallback_returns_selected_reasoning_metadata():
def test_cached_native_template_metadata_recovers_reasoning_markers_without_tools():
from types import SimpleNamespace
- model_info = {"chat_template_info": {"template": "native <|channel>thought\n"}}
+ model_info = {
+ "chat_template_info": {"template": "native <|channel>thought\n"}
+ }
assert detect_reasoning_channel_markers_from_model_info(
SimpleNamespace(chat_template = "override has no native markers"),
diff --git a/studio/backend/tests/test_tool_approvals.py b/studio/backend/tests/test_tool_approvals.py
index af792e652c..9a5af18893 100644
--- a/studio/backend/tests/test_tool_approvals.py
+++ b/studio/backend/tests/test_tool_approvals.py
@@ -246,7 +246,9 @@ def test_concurrent_distinct_calls_route_their_own_decisions():
for i in range(n):
aid = new_approval_id()
waiters[aid] = _Waiter(f"s{i}", aid).start()
- expected = {aid: ("allow" if i % 2 == 0 else "deny") for i, aid in enumerate(waiters)}
+ expected = {
+ aid: ("allow" if i % 2 == 0 else "deny") for i, aid in enumerate(waiters)
+ }
for aid, decision in expected.items():
assert resolve_tool_decision(aid, decision) is True
for aid, w in waiters.items():
diff --git a/studio/backend/tests/test_tool_call_parser_strict.py b/studio/backend/tests/test_tool_call_parser_strict.py
index 02f63c41a2..b2d3cf7a3b 100644
--- a/studio/backend/tests/test_tool_call_parser_strict.py
+++ b/studio/backend/tests/test_tool_call_parser_strict.py
@@ -40,7 +40,9 @@ class TestFunctionStyleTrailingText:
assert call == {"name": "web_search", "arguments": {"query": "weather london"}}
def test_closed_function_with_trailing_whitespace_is_accepted(self):
- text = "cats \n\n"
+ text = (
+ "cats \n\n"
+ )
call = _only(text)
assert call == {"name": "web_search", "arguments": {"query": "cats"}}
@@ -114,9 +116,15 @@ class TestFunctionStyleTrailingText:
def test_closed_zero_param_attribute_call_is_accepted_in_strict_mode(self):
# A closed call with no parameters is a valid zero-argument call; strict
# mode must not treat the empty parameter list as a truncated call.
- assert _only('') == {"name": "ping", "arguments": {}}
+ assert _only('') == {
+ "name": "ping",
+ "arguments": {},
+ }
# A no-arg call that never closes is still rejected as truncated.
- assert parse_tool_calls_from_text('', allow_incomplete = False) == []
+ assert (
+ parse_tool_calls_from_text('', allow_incomplete = False)
+ == []
+ )
class TestParityWithJsonStyle:
@@ -147,7 +155,8 @@ class TestParityWithJsonStyle:
class TestGemmaNativeStyle:
def test_closed_native_call_with_trailing_prose_is_accepted(self):
text = (
- '<|tool_call>call:terminal{command:"ls -la",workdir:"."}' " running it now"
+ '<|tool_call>call:terminal{command:"ls -la",workdir:"."}'
+ " running it now"
)
calls = parse_tool_calls_from_text(text, allow_incomplete = False)
assert len(calls) == 1
@@ -169,19 +178,21 @@ class TestGemmaNativeStyle:
calls = parse_tool_calls_from_text(text, allow_incomplete = False)
assert len(calls) == 1
assert calls[0]["function"]["name"] == "mcp__srv__create-issue"
- assert json.loads(calls[0]["function"]["arguments"]) == {"issue-title": "Bug report"}
+ assert json.loads(calls[0]["function"]["arguments"]) == {
+ "issue-title": "Bug report"
+ }
def test_native_template_quotes_preserve_windows_path(self):
text = r'<|tool_call>call:ls{path:<|"|>C:\Users\wasim\repo<|"|>}'
calls = parse_tool_calls_from_text(text, allow_incomplete = False)
assert len(calls) == 1
- assert json.loads(calls[0]["function"]["arguments"]) == {"path": r"C:\Users\wasim\repo"}
+ assert json.loads(calls[0]["function"]["arguments"]) == {
+ "path": r"C:\Users\wasim\repo"
+ }
def test_bare_unquoted_string_values_are_accepted(self):
# Gemma can emit enum/string args unquoted; bare JSON scalars stay typed.
- text = (
- "<|tool_call>call:get_weather{location:Tokyo,unit:celsius,days:3,live:true}"
- )
+ text = "<|tool_call>call:get_weather{location:Tokyo,unit:celsius,days:3,live:true}"
calls = parse_tool_calls_from_text(text, allow_incomplete = False)
assert len(calls) == 1
assert json.loads(calls[0]["function"]["arguments"]) == {
@@ -235,7 +246,9 @@ class TestHealingPathUnaffected:
# out of the last parameter and the removal span.
from core.tool_healing import parse_tool_calls_from_text as parse_with_spans
- text = "cats trailing"
+ text = (
+ "cats trailing"
+ )
calls, spans = parse_with_spans(text, allow_incomplete = True, with_spans = True)
(call,) = calls
assert json.loads(call["function"]["arguments"]) == {"query": "cats"}
@@ -295,7 +308,9 @@ class TestEnabledToolNameGate:
# Without a gate every ``NAME[ARGS]{...}`` is parsed, as before the gate landed.
text = 'foo[ARGS]{"a":1} web_search[ARGS]{"query":"cats"}'
assert self._names(parse_tool_calls_from_text(text)) == ["foo", "web_search"]
- assert self._names(parse_tool_calls_from_text(text, enabled_tool_names = None)) == [
+ assert self._names(
+ parse_tool_calls_from_text(text, enabled_tool_names = None)
+ ) == [
"foo",
"web_search",
]
@@ -389,7 +404,9 @@ class TestMistralArrayHealing:
def test_mistral_array_null_arguments_normalized_to_empty_object(self):
# ``"arguments": null`` is a no-arg call; it must become {} (as the
# path does), not the string "null" that auto-heal turns into {"query":"null"}.
- calls = parse_tool_calls_from_text('[TOOL_CALLS][{"name":"get_time","arguments":null}]')
+ calls = parse_tool_calls_from_text(
+ '[TOOL_CALLS][{"name":"get_time","arguments":null}]'
+ )
assert calls[0]["function"]["arguments"] == "{}"
@@ -419,7 +436,15 @@ class TestKimiStrict:
_SE = "<|tool_calls_section_end|>"
def test_full_kimi_call_is_accepted(self):
- text = self._SB + self._KB + "functions.x:0" + self._AB + '{"a":1}' + self._KE + self._SE
+ text = (
+ self._SB
+ + self._KB
+ + "functions.x:0"
+ + self._AB
+ + '{"a":1}'
+ + self._KE
+ + self._SE
+ )
calls = parse_tool_calls_from_text(text, allow_incomplete = False)
assert len(calls) == 1
assert calls[0]["function"]["name"] == "x"
@@ -443,7 +468,9 @@ class TestParserLinearity:
def test_llama3_unterminated_call_arg_is_linear(self):
import time
- text = '<|python_tag|>upload.call(data="' + "A" * 200_000 # no closing quote/paren
+ text = (
+ '<|python_tag|>upload.call(data="' + "A" * 200_000
+ ) # no closing quote/paren
t0 = time.perf_counter()
parse_tool_calls_from_text(text, allow_incomplete = True)
assert time.perf_counter() - t0 < 2.0
@@ -478,7 +505,9 @@ class TestParserLinearity:
t0 = time.perf_counter()
calls = parse_tool_calls_from_text(text)
best = min(best, time.perf_counter() - t0)
- assert calls and json.loads(calls[0]["function"]["arguments"]), "nested args dropped"
+ assert calls and json.loads(
+ calls[0]["function"]["arguments"]
+ ), "nested args dropped"
return best
t200 = best_ms(200)
@@ -585,9 +614,15 @@ def test_strip_leading_bare_json_call_drops_complete_call():
from core.inference.tool_call_parser import strip_leading_bare_json_call
# A complete Llama-3.2 bare-JSON call is removed; trailing prose is kept.
- assert strip_leading_bare_json_call('{"name":"web_search","parameters":{"query":"cats"}}') == ""
assert (
- strip_leading_bare_json_call('{"name":"python","parameters":{"code":"x"}} done') == "done"
+ strip_leading_bare_json_call(
+ '{"name":"web_search","parameters":{"query":"cats"}}'
+ )
+ == ""
+ )
+ assert (
+ strip_leading_bare_json_call('{"name":"python","parameters":{"code":"x"}} done')
+ == "done"
)
@@ -596,7 +631,9 @@ def test_strip_leading_bare_json_call_drops_truncated_call():
# A truncated call (no closing brace) collapses to "" -- nothing recoverable.
assert (
- strip_leading_bare_json_call('{"name":"web_search","parameters":{"query":"weather in S')
+ strip_leading_bare_json_call(
+ '{"name":"web_search","parameters":{"query":"weather in S'
+ )
== ""
)
@@ -606,10 +643,13 @@ def test_strip_leading_bare_json_call_preserves_plain_json_and_prose():
# No "name" key -> plain JSON answer, left untouched.
assert (
- strip_leading_bare_json_call('{"result": 42, "ok": true}') == '{"result": 42, "ok": true}'
+ strip_leading_bare_json_call('{"result": 42, "ok": true}')
+ == '{"result": 42, "ok": true}'
)
# Prose before the brace -> not a leading bare call, untouched.
- assert strip_leading_bare_json_call('here is {"name":"x"}') == 'here is {"name":"x"}'
+ assert (
+ strip_leading_bare_json_call('here is {"name":"x"}') == 'here is {"name":"x"}'
+ )
# Ordinary text untouched.
assert strip_leading_bare_json_call("just a sentence.") == "just a sentence."
@@ -676,7 +716,9 @@ def test_bare_json_gated_on_enabled_tool_names():
got = parse_tool_calls_from_text(real, enabled_tool_names = {"web_search"})
assert [c["function"]["name"] for c in got] == ["web_search"]
# No enabled set (None) keeps the name-agnostic behaviour for direct callers.
- assert [c["function"]["name"] for c in parse_tool_calls_from_text(alice)] == ["Alice"]
+ assert [c["function"]["name"] for c in parse_tool_calls_from_text(alice)] == [
+ "Alice"
+ ]
# Marker-based forms are NOT gated (an explicit signal is a real call attempt).
xml = '{"name":"Alice","arguments":{}}'
assert parse_tool_calls_from_text(xml, enabled_tool_names = {"web_search"})
@@ -712,7 +754,10 @@ def test_function_xml_strip_keeps_literal_close_tag_in_param_value():
def test_function_xml_strip_keeps_trailing_text_after_literal_open_tag():
- from core.inference.tool_call_parser import parse_tool_calls_from_text, strip_tool_markup
+ from core.inference.tool_call_parser import (
+ parse_tool_calls_from_text,
+ strip_tool_markup,
+ )
# A literal ```` opener inside a parameter value is data, not a call: the scan-based
# strip keeps " done" (the old negative-lookahead regex ate the trailing prose).
@@ -779,11 +824,18 @@ def test_mistral_single_object_call_is_stripped_for_display():
# The parser accepts the single-object [TOOL_CALLS]{...} shape, so the display
# strip must remove it too (asymmetry would leak the raw object).
- text = '[TOOL_CALLS]{"name":"web_search","arguments":{"filters":{"date":"2024"}}} tail'
- assert [c["function"]["name"] for c in parse_tool_calls_from_text(text)] == ["web_search"]
+ text = (
+ '[TOOL_CALLS]{"name":"web_search","arguments":{"filters":{"date":"2024"}}} tail'
+ )
+ assert [c["function"]["name"] for c in parse_tool_calls_from_text(text)] == [
+ "web_search"
+ ]
assert _strip_mistral_closed_calls(text) == " tail"
# A literal [TOOL_CALLS] in prose (no following object) is left untouched.
- assert _strip_mistral_closed_calls("See the [TOOL_CALLS] docs") == "See the [TOOL_CALLS] docs"
+ assert (
+ _strip_mistral_closed_calls("See the [TOOL_CALLS] docs")
+ == "See the [TOOL_CALLS] docs"
+ )
def test_tool_call_parser_declares_future_annotations_for_py39_import():
@@ -791,7 +843,10 @@ def test_tool_call_parser_declares_future_annotations_for_py39_import():
# annotations need ``from __future__ import annotations``; guard that the import stays.
from pathlib import Path
src = (
- Path(__file__).resolve().parent.parent / "core" / "inference" / "tool_call_parser.py"
+ Path(__file__).resolve().parent.parent
+ / "core"
+ / "inference"
+ / "tool_call_parser.py"
).read_text()
assert "from __future__ import annotations" in src
@@ -807,7 +862,9 @@ def test_glm_strip_treats_literal_close_tag_in_arg_value_as_data():
assert strip_tool_markup(text, final = True) == "tail"
calls = parse_tool_calls_from_text(text)
assert [c["function"]["name"] for c in calls] == ["web_search"]
- assert json.loads(calls[0]["function"]["arguments"]) == {"query": "see tag"}
+ assert json.loads(calls[0]["function"]["arguments"]) == {
+ "query": "see tag"
+ }
def test_bare_json_function_alias_parses_and_strips_symmetrically():
@@ -826,12 +883,17 @@ def test_bare_json_function_alias_parses_and_strips_symmetrically():
assert strip_leading_bare_json_call(text, enabled) == ""
# "name" still takes precedence when both are present; nested aliases are data.
- assert _top_level_bare_json_name('{"function":"foo","name":"web_search"}') == "web_search"
+ assert (
+ _top_level_bare_json_name('{"function":"foo","name":"web_search"}')
+ == "web_search"
+ )
assert _top_level_bare_json_name('{"function":"web_search"}') == "web_search"
assert _top_level_bare_json_name('{"result":{"function":"web_search"}}') is None
# A non-enabled function-alias object is ordinary content and is preserved.
assert (
- strip_leading_bare_json_call('{"function":"not_a_tool","parameters":{}}', enabled)
+ strip_leading_bare_json_call(
+ '{"function":"not_a_tool","parameters":{}}', enabled
+ )
== '{"function":"not_a_tool","parameters":{}}'
)
@@ -847,7 +909,10 @@ class TestMistralOuterOverXmlLiteral:
for strict in (True, False):
calls = parse_tool_calls_from_text(text, allow_incomplete = not strict)
assert [c["function"]["name"] for c in calls] == ["web_search"]
- assert "" in json.loads(calls[0]["function"]["arguments"])["query"]
+ assert (
+ ""
+ in json.loads(calls[0]["function"]["arguments"])["query"]
+ )
def test_mistral_array_arg_quoting_tool_call_json(self):
text = (
@@ -887,13 +952,20 @@ class TestHealerSignalAlignment:
healer = StreamToolCallHealer(
{"web_search"},
- [{"type": "function", "function": {"name": "web_search", "parameters": {}}}],
+ [
+ {
+ "type": "function",
+ "function": {"name": "web_search", "parameters": {}},
+ }
+ ],
)
# Llama <|python_tag|> is not a healer-promotable format, so it streams through as text.
events = list(healer.feed('<|python_tag|>web_search.call(query="cats")'))
text_out = "".join(v for k, v in events if k == "text")
assert "<|python_tag|>" in text_out # streamed through, not buffered
- assert not list(healer.finalize()) or all(k == "text" for k, _v in healer.finalize())
+ assert not list(healer.finalize()) or all(
+ k == "text" for k, _v in healer.finalize()
+ )
class TestGemmaWrapperlessLiteralMarkers:
@@ -1011,7 +1083,10 @@ class TestPythonTagOuterOverXmlLiteral:
calls = parse_tool_calls_from_text(text)
assert [c["function"]["name"] for c in calls] == ["python"]
args = json.loads(calls[0]["function"]["arguments"])
- assert args["code"] == "1"
+ assert (
+ args["code"]
+ == "1"
+ )
def test_call_arg_quoting_bare_function_tag_in_query(self):
# A query mentioning must search, not execute a phantom tool.
@@ -1140,7 +1215,9 @@ class TestGemmaUnquotedApostrophes:
from core.inference.tool_call_parser import strip_tool_markup
text = "call:web_search{query:what's the weather} Done."
- stripped = strip_tool_markup(text, final = True, enabled_tool_names = {"web_search"})
+ stripped = strip_tool_markup(
+ text, final = True, enabled_tool_names = {"web_search"}
+ )
assert "call:web_search" not in stripped
assert stripped.strip() == "Done."
@@ -1230,7 +1307,9 @@ class TestMistralLiteralInsideLeadingJson:
def test_outer_json_call_wins_over_mistral_literal(self):
text = '{"name": "python", "arguments": {"code": "[TOOL_CALLS]web_search{}"}}'
- calls = parse_tool_calls_from_text(text, enabled_tool_names = {"python", "web_search"})
+ calls = parse_tool_calls_from_text(
+ text, enabled_tool_names = {"python", "web_search"}
+ )
assert [c["function"]["name"] for c in calls] == ["python"]
args = json.loads(calls[0]["function"]["arguments"])
assert args["code"] == "[TOOL_CALLS]web_search{}"
@@ -1303,7 +1382,9 @@ class TestLeadingWrapperlessGemmaOverEmbeddedMarkers:
'call:web_search{query:"explain '
'{"name":"evil","arguments":{}}"}'
)
- calls = parse_tool_calls_from_text(text, enabled_tool_names = {"web_search", "evil"})
+ calls = parse_tool_calls_from_text(
+ text, enabled_tool_names = {"web_search", "evil"}
+ )
assert [c["function"]["name"] for c in calls] == ["web_search"]
def test_xml_leading_keeps_normal_order(self):
@@ -1311,7 +1392,9 @@ class TestLeadingWrapperlessGemmaOverEmbeddedMarkers:
'{"name":"web_search","arguments":'
'{"query":"call:evil{x:1} example"}}'
)
- calls = parse_tool_calls_from_text(text, enabled_tool_names = {"web_search", "evil"})
+ calls = parse_tool_calls_from_text(
+ text, enabled_tool_names = {"web_search", "evil"}
+ )
assert [c["function"]["name"] for c in calls] == ["web_search"]
@@ -1356,7 +1439,10 @@ class TestJsonAnswersAreDataForMarkerlessScans:
def test_gemma_example_inside_json_answer_not_stripped(self):
from core.inference.tool_call_parser import strip_tool_markup
text = '{"answer":"Gemma syntax is call:web_search{query:hi}"}'
- assert strip_tool_markup(text, final = True, enabled_tool_names = {"web_search"}) == text
+ assert (
+ strip_tool_markup(text, final = True, enabled_tool_names = {"web_search"})
+ == text
+ )
def test_kimi_marker_inside_json_answer_not_promoted(self):
text = (
@@ -1434,7 +1520,9 @@ class TestClosedCallPrecedesMarkerPrePass:
+ self._KIMI_EVIL
+ '<|"|>}'
)
- calls = parse_tool_calls_from_text(text, enabled_tool_names = {"web_search", "evil"})
+ calls = parse_tool_calls_from_text(
+ text, enabled_tool_names = {"web_search", "evil"}
+ )
assert [c["function"]["name"] for c in calls] == ["web_search"]
def test_leading_xml_call_wins_over_trailing_kimi_example(self):
@@ -1442,7 +1530,9 @@ class TestClosedCallPrecedesMarkerPrePass:
'{"name":"web_search","arguments":{"query":"cats"}}'
" For reference: " + self._KIMI_EVIL
)
- calls = parse_tool_calls_from_text(text, enabled_tool_names = {"web_search", "evil"})
+ calls = parse_tool_calls_from_text(
+ text, enabled_tool_names = {"web_search", "evil"}
+ )
assert [c["function"]["name"] for c in calls] == ["web_search"]
def test_standalone_kimi_call_still_parses(self):
@@ -1453,7 +1543,12 @@ class TestClosedCallPrecedesMarkerPrePass:
class TestTruncatedWrapperlessGemmaStopsScan:
def test_call_quoted_inside_truncated_arg_not_promoted(self):
text = 'call:python{code:example("call:web_search{query:hi}") and then it cut'
- assert parse_tool_calls_from_text(text, enabled_tool_names = {"python", "web_search"}) == []
+ assert (
+ parse_tool_calls_from_text(
+ text, enabled_tool_names = {"python", "web_search"}
+ )
+ == []
+ )
class TestGemmaQuotedNestedDelimiters:
@@ -1494,9 +1589,7 @@ class TestGlmStrictRefusesInQuoteFallback:
literal must reject in strict mode instead of executing truncated
arguments; Auto-Heal keeps the lenient partial value."""
- _TRUNC = (
- 'python\ncode\nprint("")'
- )
+ _TRUNC = 'python\ncode\nprint("")'
def test_strict_rejects_truncated_in_string_close(self):
assert parse_tool_calls_from_text(self._TRUNC, allow_incomplete = False) == []
@@ -1512,7 +1605,9 @@ class TestGemmaGuardCoversPreambles:
"Sure, searching now. call:web_search{query:"
'"explain {"name":"evil","arguments":{}}"}'
)
- calls = parse_tool_calls_from_text(text, enabled_tool_names = {"web_search", "evil"})
+ calls = parse_tool_calls_from_text(
+ text, enabled_tool_names = {"web_search", "evil"}
+ )
assert [c["function"]["name"] for c in calls] == ["web_search"]
@@ -1531,14 +1626,21 @@ class TestGlmStrictAcceptsApostrophes:
class TestDisabledGemmaCallLiteralsAreData:
def test_literal_inside_disabled_call_not_promoted(self):
text = 'call:foo{query:"x"}'
- assert parse_tool_calls_from_text(text, enabled_tool_names = {"python", "web_search"}) == []
+ assert (
+ parse_tool_calls_from_text(
+ text, enabled_tool_names = {"python", "web_search"}
+ )
+ == []
+ )
def test_real_call_after_disabled_example_still_parses(self):
text = (
'call:foo{query:"x"}'
" call:web_search{query:hi}"
)
- calls = parse_tool_calls_from_text(text, enabled_tool_names = {"python", "web_search"})
+ calls = parse_tool_calls_from_text(
+ text, enabled_tool_names = {"python", "web_search"}
+ )
assert [c["function"]["name"] for c in calls] == ["web_search"]
@@ -1560,7 +1662,9 @@ class TestLeadingBareJsonOwnsTurnOverTrailingXml:
'{"name":"lookup","parameters":{"q":"first"}} Example: '
'{"name":"delete_all","arguments":{}}'
)
- calls = parse_tool_calls_from_text(text, enabled_tool_names = {"lookup", "delete_all"})
+ calls = parse_tool_calls_from_text(
+ text, enabled_tool_names = {"lookup", "delete_all"}
+ )
assert [c["function"]["name"] for c in calls] == ["lookup"], calls
assert json.loads(calls[0]["function"]["arguments"]) == {"q": "first"}
@@ -1570,7 +1674,9 @@ class TestLeadingBareJsonOwnsTurnOverTrailingXml:
'{"name":"lookup","parameters":{"q":"second"}} '
'{"name":"delete_all","arguments":{}}'
)
- calls = parse_tool_calls_from_text(text, enabled_tool_names = {"lookup", "delete_all"})
+ calls = parse_tool_calls_from_text(
+ text, enabled_tool_names = {"lookup", "delete_all"}
+ )
assert [c["function"]["name"] for c in calls] == ["lookup", "lookup"], calls
def test_non_call_leading_object_defers_to_trailing_real_call(self):
@@ -1579,14 +1685,19 @@ class TestLeadingBareJsonOwnsTurnOverTrailingXml:
for lead in ('{"answer": 42}', '{"name":"draft","parameters":{}}'):
text = lead + ' {"name":"delete_all","arguments":{}}'
calls = parse_tool_calls_from_text(text, enabled_tool_names = {"delete_all"})
- assert [c["function"]["name"] for c in calls] == ["delete_all"], (lead, calls)
+ assert [c["function"]["name"] for c in calls] == ["delete_all"], (
+ lead,
+ calls,
+ )
def test_leading_xml_call_still_wins_over_trailing_bare_json(self):
text = (
'{"name":"delete_all","arguments":{}} '
'Example: {"name":"lookup","parameters":{"q":"x"}}'
)
- calls = parse_tool_calls_from_text(text, enabled_tool_names = {"lookup", "delete_all"})
+ calls = parse_tool_calls_from_text(
+ text, enabled_tool_names = {"lookup", "delete_all"}
+ )
assert [c["function"]["name"] for c in calls] == ["delete_all"], calls
@@ -1608,7 +1719,9 @@ class TestProseCloseTagAfterClosedFunctionCall:
text = 'print("")'
calls = parse_tool_calls_from_text(text, enabled_tool_names = {"python"})
assert [c["function"]["name"] for c in calls] == ["python"], calls
- assert json.loads(calls[0]["function"]["arguments"]) == {"code": 'print("")'}
+ assert json.loads(calls[0]["function"]["arguments"]) == {
+ "code": 'print("")'
+ }
def test_attribute_form_arguments_do_not_swallow_prose(self):
# The attribute form shares the first-balanced-close
@@ -1624,14 +1737,18 @@ class TestProseCloseTagAfterClosedFunctionCall:
def test_attribute_form_literal_close_in_open_parameter_stays_data(self):
text = 'print("")'
calls = parse_tool_calls_from_text(text, enabled_tool_names = {"python"})
- assert json.loads(calls[0]["function"]["arguments"]) == {"code": 'print("")'}
+ assert json.loads(calls[0]["function"]["arguments"]) == {
+ "code": 'print("")'
+ }
def test_attribute_form_two_calls_both_parse(self):
text = (
'cats'
'x=1'
)
- calls = parse_tool_calls_from_text(text, enabled_tool_names = {"web_search", "python"})
+ calls = parse_tool_calls_from_text(
+ text, enabled_tool_names = {"web_search", "python"}
+ )
assert [c["function"]["name"] for c in calls] == ["web_search", "python"], calls
@@ -1676,7 +1793,9 @@ class TestAttributeFormLeadingContainment:
'find '
'{"name":"delete","arguments":{}}'
)
- calls = parse_tool_calls_from_text(text, enabled_tool_names = {"web_search", "delete"})
+ calls = parse_tool_calls_from_text(
+ text, enabled_tool_names = {"web_search", "delete"}
+ )
assert [c["function"]["name"] for c in calls] == ["web_search"]
assert "delete" in json.loads(calls[0]["function"]["arguments"])["query"]
@@ -1687,7 +1806,9 @@ class TestAttributeFormLeadingContainment:
'{"name":"delete","arguments":{}} Example: '
'x'
)
- calls = parse_tool_calls_from_text(text, enabled_tool_names = {"web_search", "delete"})
+ calls = parse_tool_calls_from_text(
+ text, enabled_tool_names = {"web_search", "delete"}
+ )
assert calls[0]["function"]["name"] == "delete"
@@ -1736,7 +1857,9 @@ class TestMistralPreambleOwnership:
'pref [TOOL_CALLS]web_search[ARGS]{"query":"cats"} Note '
"1"
)
- calls = parse_tool_calls_from_text(text, enabled_tool_names = {"web_search", "evil"})
+ calls = parse_tool_calls_from_text(
+ text, enabled_tool_names = {"web_search", "evil"}
+ )
assert [c["function"]["name"] for c in calls] == ["web_search"]
def test_array_form_after_preface(self):
@@ -1746,7 +1869,9 @@ class TestMistralPreambleOwnership:
'pref [TOOL_CALLS][{"name":"web_search","arguments":{"query":"cats"}}] Note '
"1"
)
- calls = parse_tool_calls_from_text(text, enabled_tool_names = {"web_search", "evil"})
+ calls = parse_tool_calls_from_text(
+ text, enabled_tool_names = {"web_search", "evil"}
+ )
assert [c["function"]["name"] for c in calls] == ["web_search"]
def test_xml_call_before_trigger_keeps_order(self):
@@ -1756,7 +1881,9 @@ class TestMistralPreambleOwnership:
"1 then "
'[TOOL_CALLS][{"name":"web_search","arguments":{}}]'
)
- calls = parse_tool_calls_from_text(text, enabled_tool_names = {"web_search", "evil"})
+ calls = parse_tool_calls_from_text(
+ text, enabled_tool_names = {"web_search", "evil"}
+ )
assert calls[0]["function"]["name"] == "evil"
def test_prose_mention_without_call_shape_keeps_order(self):
@@ -1783,7 +1910,10 @@ class TestBareJsonStripRequiresTopLevelName:
def test_real_call_still_strips_name_agnostic(self):
from core.inference.tool_call_parser import strip_leading_bare_json_call
- assert strip_leading_bare_json_call('{"name":"web_search","parameters":{"q":"x"}}') == ""
+ assert (
+ strip_leading_bare_json_call('{"name":"web_search","parameters":{"q":"x"}}')
+ == ""
+ )
class TestGemmaAwareClosedBlockPrePass:
@@ -1813,9 +1943,7 @@ class TestGemmaAwareClosedBlockPrePass:
def test_gemma_opener_inside_json_arg_still_strips_block(self):
from core.tool_healing import strip_tool_call_markup
- text = (
- '{"name":"t","arguments":{"code":"<|tool_call>call:x{"}} after'
- )
+ text = '{"name":"t","arguments":{"code":"<|tool_call>call:x{"}} after'
assert strip_tool_call_markup(text, final = True) == "after"
def test_gemma_opener_inside_function_param_still_strips_block(self):
diff --git a/studio/backend/tests/test_tool_confirm_loop.py b/studio/backend/tests/test_tool_confirm_loop.py
index 3db591f542..18dba68c14 100644
--- a/studio/backend/tests/test_tool_confirm_loop.py
+++ b/studio/backend/tests/test_tool_confirm_loop.py
@@ -101,7 +101,9 @@ def _drive(
if ev["type"] == "tool_start" and ev.get("awaiting_confirmation"):
# Slot is already registered (begin ran before this yield), so
# the decision lands before the loop enters its blocking wait.
- resolve_tool_decision(ev["approval_id"], next(decision_iter), session_id = _SESSION)
+ resolve_tool_decision(
+ ev["approval_id"], next(decision_iter), session_id = _SESSION
+ )
return events, exec_fn.calls
diff --git a/studio/backend/tests/test_tool_confirm_stream.py b/studio/backend/tests/test_tool_confirm_stream.py
index 0813f6b68d..0a90ae0f9b 100644
--- a/studio/backend/tests/test_tool_confirm_stream.py
+++ b/studio/backend/tests/test_tool_confirm_stream.py
@@ -69,7 +69,9 @@ def _build_app() -> FastAPI:
"approval_id": approval_id,
"awaiting_confirmation": True,
}
- denied = wait_tool_decision(slot, approval_id, cancel_event = cancel_event) == "deny"
+ denied = (
+ wait_tool_decision(slot, approval_id, cancel_event = cancel_event) == "deny"
+ )
result = TOOL_REJECTED_MESSAGE if denied else _EXECUTED_RESULT
yield {"type": "tool_end", "tool_name": "python", "result": result}
@@ -116,7 +118,9 @@ class _Server:
def __init__(self, app):
self.port = _free_port()
- config = uvicorn.Config(app, host = "127.0.0.1", port = self.port, log_level = "warning")
+ config = uvicorn.Config(
+ app, host = "127.0.0.1", port = self.port, log_level = "warning"
+ )
self.server = uvicorn.Server(config)
self._thread = threading.Thread(target = self.server.run, daemon = True)
@@ -159,7 +163,9 @@ async def _drive(base_url, session_id, decision):
resolved = None
timeout = httpx.Timeout(10.0)
async with httpx.AsyncClient(base_url = base_url, timeout = timeout) as client:
- async with client.stream("POST", "/stream", json = {"session_id": session_id}) as resp:
+ async with client.stream(
+ "POST", "/stream", json = {"session_id": session_id}
+ ) as resp:
assert resp.status_code == 200
async for line in resp.aiter_lines():
if not line.startswith("data: "):
diff --git a/studio/backend/tests/test_tool_loop_controller.py b/studio/backend/tests/test_tool_loop_controller.py
index 496c30ac13..42fb75f8ba 100644
--- a/studio/backend/tests/test_tool_loop_controller.py
+++ b/studio/backend/tests/test_tool_loop_controller.py
@@ -23,7 +23,10 @@ from core.inference.tool_loop_controller import (
def test_append_deferred_nudges_merges_deduped_into_one_message():
- conversation = [{"role": "assistant", "tool_calls": [1]}, {"role": "tool", "content": "r"}]
+ conversation = [
+ {"role": "assistant", "tool_calls": [1]},
+ {"role": "tool", "content": "r"},
+ ]
nudges = [
{"role": "user", "content": "duplicate"},
{"role": "user", "content": "duplicate"}, # dropped: same content
@@ -31,7 +34,9 @@ def test_append_deferred_nudges_merges_deduped_into_one_message():
]
append_deferred_nudges(conversation, nudges)
# One user message, after the results, with distinct contents joined.
- assert conversation[2:] == [{"role": "user", "content": "duplicate\n\ndisabled foo"}]
+ assert conversation[2:] == [
+ {"role": "user", "content": "duplicate\n\ndisabled foo"}
+ ]
# Empty is a no-op.
before = list(conversation)
append_deferred_nudges(conversation, [])
@@ -86,7 +91,10 @@ def test_status_and_provenance_match_local_event_conventions():
status_for_tool("web_search", {"url": "https://www.example.com/a"})
== "Reading: example.com"
)
- assert status_for_tool("python", {"code": "print(1)\nprint(2)"}) == "Running Python: print(1)"
+ assert (
+ status_for_tool("python", {"code": "print(1)\nprint(2)"})
+ == "Running Python: print(1)"
+ )
assert tool_event_provenance(healed = True, forced = False, provisional = None) == {
"source": "local",
"healed": True,
@@ -102,7 +110,10 @@ def test_prepare_execute_builds_visible_events_and_model_tool_message():
assert decision.status_text == "Searching: gpu prices"
assert decision.tool_start_payload()["arguments"] == {"query": "gpu prices"}
assert decision.tool_start_event()["type"] == "tool_start"
- assert decision.as_assistant_tool_call()["function"]["arguments"] == '{"query":"gpu prices"}'
+ assert (
+ decision.as_assistant_tool_call()["function"]["arguments"]
+ == '{"query":"gpu prices"}'
+ )
completion = controller.record_result(decision, "Search result\n__IMAGES__:{...}")
@@ -118,10 +129,14 @@ def test_prepare_execute_builds_visible_events_and_model_tool_message():
def test_successful_duplicate_is_internal_noop_and_keeps_remaining_tools():
controller = ToolLoopController(tools = [_tool("web_search"), _tool("python")])
- first = controller.prepare_call(_call("web_search", {"query": "gpu prices"}, "call_a"))
+ first = controller.prepare_call(
+ _call("web_search", {"query": "gpu prices"}, "call_a")
+ )
controller.record_result(first, "ok")
- duplicate = controller.prepare_call(_call("web_search", {"query": "gpu prices"}, "call_b"))
+ duplicate = controller.prepare_call(
+ _call("web_search", {"query": "gpu prices"}, "call_b")
+ )
completion = controller.record_noop(duplicate)
assert duplicate.action == "duplicate"
@@ -144,10 +159,14 @@ def test_successful_duplicate_is_internal_noop_and_keeps_remaining_tools():
def test_repeated_successful_duplicate_becomes_terminal_after_one_recovery_nudge():
controller = ToolLoopController(tools = [_tool("web_search"), _tool("python")])
- first = controller.prepare_call(_call("web_search", {"query": "gpu prices"}, "call_a"))
+ first = controller.prepare_call(
+ _call("web_search", {"query": "gpu prices"}, "call_a")
+ )
controller.record_result(first, "ok")
- duplicate_one = controller.prepare_call(_call("web_search", {"query": "gpu prices"}, "call_b"))
+ duplicate_one = controller.prepare_call(
+ _call("web_search", {"query": "gpu prices"}, "call_b")
+ )
completion_one = controller.record_noop(duplicate_one)
assert duplicate_one.action == "duplicate"
@@ -158,7 +177,9 @@ def test_repeated_successful_duplicate_becomes_terminal_after_one_recovery_nudge
"python",
]
- duplicate_two = controller.prepare_call(_call("web_search", {"query": "gpu prices"}, "call_c"))
+ duplicate_two = controller.prepare_call(
+ _call("web_search", {"query": "gpu prices"}, "call_c")
+ )
completion_two = controller.record_noop(duplicate_two)
assert duplicate_two.action == "duplicate"
@@ -216,12 +237,16 @@ def test_render_html_success_filters_active_tools_and_repeat_is_internal():
"web_search",
]
- first = controller.prepare_call(_call("render_html", {"code": ""}, "call_html_1"))
+ first = controller.prepare_call(
+ _call("render_html", {"code": ""}, "call_html_1")
+ )
controller.record_result(first, "Rendered HTML canvas: Demo")
assert [t["function"]["name"] for t in controller.active_tools()] == ["web_search"]
- repeat = controller.prepare_call(_call("render_html", {"code": ""}, "call_html_2"))
+ repeat = controller.prepare_call(
+ _call("render_html", {"code": ""}, "call_html_2")
+ )
completion = controller.record_noop(repeat)
assert repeat.action == "render_html_repeat"
diff --git a/studio/backend/tests/test_tool_output_streaming.py b/studio/backend/tests/test_tool_output_streaming.py
index 28bd79cc6e..171f203d00 100644
--- a/studio/backend/tests/test_tool_output_streaming.py
+++ b/studio/backend/tests/test_tool_output_streaming.py
@@ -556,7 +556,9 @@ def test_bash_exec_nonstreaming_timeout_kills_grandchild(tmp_path):
result = _bash_exec(command, timeout = 1) # no output_callback -> communicate path
assert "timed out" in result
time.sleep(4.0)
- assert not sentinel.exists(), "non-streaming timeout leaked a stdout-holding grandchild"
+ assert (
+ not sentinel.exists()
+ ), "non-streaming timeout leaked a stdout-holding grandchild"
@pytest.mark.skipif(sys.platform == "win32", reason = "POSIX process groups")
@@ -571,7 +573,9 @@ def test_python_exec_nonstreaming_timeout_kills_grandchild(tmp_path):
result = _python_exec(code, timeout = 1) # no output_callback -> communicate path
assert "timed out" in result
time.sleep(4.0)
- assert not sentinel.exists(), "non-streaming timeout leaked a stdout-holding grandchild"
+ assert (
+ not sentinel.exists()
+ ), "non-streaming timeout leaked a stdout-holding grandchild"
def test_drain_process_output_without_posix_process_group_apis(monkeypatch):
@@ -662,18 +666,24 @@ def test_finite_drain_honors_cancel_after_leader_exit(tmp_path):
started = time.monotonic()
# Large finite timeout (30s); without the cancel poll the drain keeps reading
# the grandchild until the pipe closes ~20s later.
- output, timed_out = _drain_process_output(proc, 30, lambda _t: None, cancel_event, pgid = pgid)
+ output, timed_out = _drain_process_output(
+ proc, 30, lambda _t: None, cancel_event, pgid = pgid
+ )
elapsed = time.monotonic() - started
assert elapsed < 5.0, f"finite drain ignored cancel_event (took {elapsed:.1f}s)"
# Cancellation is not a timeout: the budget never elapsed.
assert not timed_out
assert "parent-done" in output
time.sleep(11.0) # past the grandchild's 10s sentinel write
- assert not sentinel.exists(), "cancel did not kill the stdout-holding grandchild group"
+ assert (
+ not sentinel.exists()
+ ), "cancel did not kill the stdout-holding grandchild group"
@pytest.mark.skipif(sys.platform == "win32", reason = "POSIX process groups")
-def test_streamed_wait_timeout_kills_grandchild_when_leader_reaped(tmp_path, monkeypatch):
+def test_streamed_wait_timeout_kills_grandchild_when_leader_reaped(
+ tmp_path, monkeypatch
+):
# The proc.wait() timeout branch normally kills the group via _kill_process_tree.
# But the leader can exit before _kill_process_tree samples its pgid, which then
# short-circuits on the reaped leader and leaves a stdout-holding grandchild.
@@ -761,11 +771,16 @@ def test_gguf_loop_final_tool_message_unchanged_by_streaming(monkeypatch):
return result_text
events_plain, payloads_plain = _run_gguf_tool_turn(monkeypatch, plain_tool)
- events_streaming, payloads_streaming = _run_gguf_tool_turn(monkeypatch, streaming_tool)
+ events_streaming, payloads_streaming = _run_gguf_tool_turn(
+ monkeypatch, streaming_tool
+ )
def _tool_messages(payloads):
return [
- msg for payload in payloads for msg in payload["messages"] if msg.get("role") == "tool"
+ msg
+ for payload in payloads
+ for msg in payload["messages"]
+ if msg.get("role") == "tool"
]
# The role=tool message fed to the model is byte-identical: streaming is purely
@@ -918,7 +933,9 @@ def test_missing_path_hint_respects_project_workdir():
# Against the real project workdir it is local -> no hint.
assert _missing_path_hint(output, workdir) == ""
# A path genuinely outside the project workdir still earns the hint.
- outside_err = "FileNotFoundError: [Errno 2] No such file or directory: '/srv/other/x.html'"
+ outside_err = (
+ "FileNotFoundError: [Errno 2] No such file or directory: '/srv/other/x.html'"
+ )
assert "working directory is writable" in _missing_path_hint(outside_err, workdir)
@@ -939,7 +956,9 @@ def test_missing_path_hint_project_workdir_under_convention_prefix():
assert _missing_path_hint(root_output, workdir) == ""
# A convention path genuinely outside the project workdir still earns the
# hint (e.g. a /mnt/data habit path with a /workspace-rooted project).
- outside = "FileNotFoundError: [Errno 2] No such file or directory: '/mnt/data/x.html'"
+ outside = (
+ "FileNotFoundError: [Errno 2] No such file or directory: '/mnt/data/x.html'"
+ )
assert "'x.html', not '/mnt/data/x.html'" in _missing_path_hint(outside, workdir)
# Without an explicit workdir the default sandbox root applies, so a
# /workspace path is out of sandbox and keeps the habit-path hint.
@@ -963,7 +982,9 @@ def test_missing_path_hint_convention_scoped_to_failing_line():
)
assert _missing_path_hint(printed_err) == ""
# But a convention path ON the error line still earns the hint.
- on_line = "FileNotFoundError: [Errno 2] No such file or directory: '/mnt/data/x.html'"
+ on_line = (
+ "FileNotFoundError: [Errno 2] No such file or directory: '/mnt/data/x.html'"
+ )
assert "'x.html', not '/mnt/data/x.html'" in _missing_path_hint(on_line)
@@ -1082,7 +1103,9 @@ def test_bash_exec_missing_path_hint():
assert "No such file or directory" in baseline
assert "working directory is writable" in baseline
streamed = _bash_exec(
- "cat /mnt/data/definitely_missing.txt", timeout = 60, output_callback = lambda _t: None
+ "cat /mnt/data/definitely_missing.txt",
+ timeout = 60,
+ output_callback = lambda _t: None,
)
assert streamed == baseline
@@ -1209,7 +1232,9 @@ def test_bash_exec_nonstreaming_cancel_kills_grandchild_after_leader_exit(tmp_pa
assert time.monotonic() - started < 2.5
assert result == "Execution cancelled."
time.sleep(3.5)
- assert not sentinel.exists(), "non-streaming cancel leaked a stdout-holding grandchild"
+ assert (
+ not sentinel.exists()
+ ), "non-streaming cancel leaked a stdout-holding grandchild"
@pytest.mark.skipif(sys.platform == "win32", reason = "POSIX process groups")
@@ -1231,4 +1256,6 @@ def test_python_exec_nonstreaming_cancel_kills_grandchild_after_leader_exit(tmp_
assert time.monotonic() - started < 2.5
assert result == "Execution cancelled."
time.sleep(3.5)
- assert not sentinel.exists(), "non-streaming cancel leaked a stdout-holding grandchild"
+ assert (
+ not sentinel.exists()
+ ), "non-streaming cancel leaked a stdout-holding grandchild"
diff --git a/studio/backend/tests/test_tool_strip_guard.py b/studio/backend/tests/test_tool_strip_guard.py
index dfa3101882..1cb206ad19 100644
--- a/studio/backend/tests/test_tool_strip_guard.py
+++ b/studio/backend/tests/test_tool_strip_guard.py
@@ -56,12 +56,18 @@ def test_guard_matches_plain_loop_on_fuzz():
for patterns in (_TOOL_ALL_PATS, _TOOL_CLOSED_PATS):
for _ in range(20000):
s = "".join(rng.choice(_TOKENS) for _ in range(rng.randint(0, 10)))
- assert strip_tool_patterns(s, patterns) == _naive(s, patterns), (s, patterns)
+ assert strip_tool_patterns(s, patterns) == _naive(s, patterns), (
+ s,
+ patterns,
+ )
def test_strip_markup_representative_cases_unchanged():
assert strip_tool_call_markup("a {} b") == "a b"
- assert strip_tool_call_markup("a 1 b") == "a b"
+ assert (
+ strip_tool_call_markup("a 1 b")
+ == "a b"
+ )
# Non-final keeps an unclosed block; final strips it to EOF.
assert strip_tool_call_markup("a {partial") == "a {partial"
assert strip_tool_call_markup("a {partial", final = True) == "a"
diff --git a/studio/backend/tests/test_tool_xml_strip.py b/studio/backend/tests/test_tool_xml_strip.py
index f7792a2a71..650ed54926 100644
--- a/studio/backend/tests/test_tool_xml_strip.py
+++ b/studio/backend/tests/test_tool_xml_strip.py
@@ -100,7 +100,9 @@ _gemma_strip_gate = _ns["_gemma_strip_gate"]
def test_route_display_strip_respects_disabled_auto_heal_contract():
text = 'literal {"name":"web_search"} survives'
assert _strip_tool_xml_for_display(text, auto_heal_tool_calls = False) == text
- assert "" not in _strip_tool_xml_for_display(text, auto_heal_tool_calls = True)
+ assert "" not in _strip_tool_xml_for_display(
+ text, auto_heal_tool_calls = True
+ )
def test_route_display_strip_preserves_rehearsal_inside_think():
@@ -207,7 +209,9 @@ def test_strips_function_attribute_form():
# Auto-Heal-disabled display contract still preserves literal markup.
assert _strip_tool_xml_for_display(text, auto_heal_tool_calls = False) == text
- assert "\n\nprint(1)\n"
+ text = (
+ "I'll call python:\n\n\nprint(1)\n"
+ )
cleaned = _TOOL_XML_RE.sub("", text)
assert "real done", final = True) == "answer real done"
+ _strip("answer real done", final = True)
+ == "answer real done"
)
# A complete call followed by a real reasoning block: call stripped, block kept.
- mixed = '{"name":"a","arguments":{}} mid r end'
+ mixed = (
+ '{"name":"a","arguments":{}} mid r end'
+ )
assert _strip(mixed, final = True) == "mid r end"
@@ -574,7 +583,9 @@ def test_route_display_strip_keeps_inactive_rehearsal_when_gated():
gate = {"web_search"}
text = 'foo[ARGS]{"x":1} is just syntax.'
assert (
- _strip_tool_xml_for_display(text, auto_heal_tool_calls = True, enabled_tool_names = gate)
+ _strip_tool_xml_for_display(
+ text, auto_heal_tool_calls = True, enabled_tool_names = gate
+ )
== text
)
# A bare marker with no JSON body is likewise prose when inactive.
@@ -590,7 +601,9 @@ def test_route_display_strip_removes_active_rehearsal_when_gated():
# Mirror case: an active tool name is a real rehearsal and still strips.
gate = {"web_search"}
out = _strip_tool_xml_for_display(
- 'web_search[ARGS]{"query":"x"} done', auto_heal_tool_calls = True, enabled_tool_names = gate
+ 'web_search[ARGS]{"query":"x"} done',
+ auto_heal_tool_calls = True,
+ enabled_tool_names = gate,
)
assert "web_search[ARGS]" not in out
assert out.strip() == "done"
@@ -599,7 +612,10 @@ def test_route_display_strip_removes_active_rehearsal_when_gated():
def test_route_display_strip_ungated_strips_all_rehearsal_unchanged():
# Backwards-compat: with no gate (None) the bare rehearsal strips as before.
text = 'foo[ARGS]{"x":1} is just syntax.'
- assert _strip_tool_xml_for_display(text, auto_heal_tool_calls = True).strip() == "is just syntax."
+ assert (
+ _strip_tool_xml_for_display(text, auto_heal_tool_calls = True).strip()
+ == "is just syntax."
+ )
assert (
_strip_tool_xml_for_display(
text, auto_heal_tool_calls = True, enabled_tool_names = None
@@ -612,7 +628,9 @@ def test_route_display_strip_control_token_stripped_regardless_of_gate():
# [TOOL_CALLS] is a control token: stripped even when its NAME is not in the gate.
gate = {"web_search"}
out = _strip_tool_xml_for_display(
- '[TOOL_CALLS]foo[ARGS]{"x":1} keep', auto_heal_tool_calls = True, enabled_tool_names = gate
+ '[TOOL_CALLS]foo[ARGS]{"x":1} keep',
+ auto_heal_tool_calls = True,
+ enabled_tool_names = gate,
)
assert "[TOOL_CALLS]" not in out and "foo[ARGS]" not in out
assert out.strip() == "keep"
@@ -626,11 +644,17 @@ def test_core_strip_gates_bare_rehearsal_on_enabled_tools():
text = 'foo[ARGS]{"x":1} is just syntax.'
assert _strip(text, final = True, enabled_tool_names = {"web_search"}) == text
assert (
- _strip('web_search[ARGS]{"q":1} done', final = True, enabled_tool_names = {"web_search"})
+ _strip(
+ 'web_search[ARGS]{"q":1} done',
+ final = True,
+ enabled_tool_names = {"web_search"},
+ )
== "done"
)
assert _strip(text, final = True).strip() == "is just syntax."
- assert _strip(text, final = True, enabled_tool_names = None).strip() == "is just syntax."
+ assert (
+ _strip(text, final = True, enabled_tool_names = None).strip() == "is just syntax."
+ )
def test_route_display_strip_gate_preserves_inactive_history_rehearsal():
@@ -643,10 +667,14 @@ def test_route_display_strip_gate_preserves_inactive_history_rehearsal():
)
# An ACTIVE name is still stripped as a real rehearsed call.
assert "web_search[ARGS]" not in _strip_tool_xml_for_display(
- 'Result web_search[ARGS]{"q":"x"} done', auto_heal_tool_calls = True, enabled_tool_names = gate
+ 'Result web_search[ARGS]{"q":"x"} done',
+ auto_heal_tool_calls = True,
+ enabled_tool_names = gate,
)
# No gate (legacy) strips every NAME[ARGS]{...}.
- assert "foo[ARGS]" not in _strip_tool_xml_for_display(text, auto_heal_tool_calls = True)
+ assert "foo[ARGS]" not in _strip_tool_xml_for_display(
+ text, auto_heal_tool_calls = True
+ )
def test_gguf_history_sanitizer_forwards_enabled_tool_names_gate():
@@ -657,8 +685,8 @@ def test_gguf_history_sanitizer_forwards_enabled_tool_names_gate():
_re.DOTALL,
)
assert block, "could not locate GGUF history sanitizer block"
- assert "enabled_tool_names" in block.group(
- 0
+ assert (
+ "enabled_tool_names" in block.group(0)
), "GGUF history sanitizer must pass enabled_tool_names to _strip_tool_xml_for_display"
@@ -806,15 +834,21 @@ def test_glm_normal_and_qwen_calls_still_stripped_by_route():
glm = "get_time\ntz\nUTC\n ok"
assert _strip_tool_xml_for_display(glm, auto_heal_tool_calls = True).strip() == "ok"
qwen = '{"name":"web_search","arguments":{"q":"x"}} after'
- assert _strip_tool_xml_for_display(qwen, auto_heal_tool_calls = True).strip() == "after"
+ assert (
+ _strip_tool_xml_for_display(qwen, auto_heal_tool_calls = True).strip() == "after"
+ )
def test_route_strip_removes_param_alias_close_tag():
# The parser accepts the ... attribute-form alias of
# ; the route tail cleanup must strip an orphan close too.
- assert _strip_tool_xml_for_display("answer ", auto_heal_tool_calls = True) == "answer "
assert (
- _strip_tool_xml_for_display("answer ", auto_heal_tool_calls = True) == "answer "
+ _strip_tool_xml_for_display("answer ", auto_heal_tool_calls = True)
+ == "answer "
+ )
+ assert (
+ _strip_tool_xml_for_display("answer ", auto_heal_tool_calls = True)
+ == "answer "
)
@@ -822,13 +856,17 @@ def test_route_strip_uses_guarded_function_scan_for_literal_nested_markup():
# A literal in a value must not truncate the strip: the route runs the
# parser's guarded function-XML scan before the regex, matching the core strip.
text = " tail"
- assert _strip_tool_xml_for_display(text, auto_heal_tool_calls = True).strip() == "tail"
+ assert (
+ _strip_tool_xml_for_display(text, auto_heal_tool_calls = True).strip() == "tail"
+ )
def test_route_strip_gates_wrapperless_gemma_by_enabled_tools():
# The route strip must gate the markerless Gemma call:NAME{...} form on the enabled tool names,
# like the parser/loop, so a disabled/example name in prose is preserved in ...
- prose = "To document syntax you write call:foo{query:example}. That shows the format."
+ prose = (
+ "To document syntax you write call:foo{query:example}. That shows the format."
+ )
assert "call:foo{query:example}" in _strip_tool_xml(prose, {"web_search"})
# An enabled name is still a real call and stripped.
assert "call:web_search" not in _strip_tool_xml(
@@ -844,7 +882,9 @@ def test_gemma_strip_gate_empty_tools_preserves_prose():
assert _gemma_strip_gate([]) == set()
assert _gemma_strip_gate(None) == set()
assert _gemma_strip_gate([{"function": {"name": "web_search"}}]) == {"web_search"}
- prose = "To document syntax you write call:foo{query:example}. That shows the format."
+ prose = (
+ "To document syntax you write call:foo{query:example}. That shows the format."
+ )
assert "call:foo{query:example}" in _strip_tool_xml(prose, _gemma_strip_gate([]))
assert "call:foo{query:example}" in _strip_tool_xml(prose, _gemma_strip_gate(None))
# An enabled tool's real call is still stripped.
@@ -862,7 +902,10 @@ def test_strip_keeps_prose_after_closed_function_call_with_literal_close():
"cats"
" Done. The tag closes a call."
)
- assert strip_tool_markup(text, final = True) == "Done. The tag closes a call."
+ assert (
+ strip_tool_markup(text, final = True)
+ == "Done. The tag closes a call."
+ )
def test_final_strip_keeps_prose_mentioning_bare_markers():
@@ -903,13 +946,13 @@ def test_chained_bare_json_strip_consumes_all_calls():
)
assert strip_leading_bare_json_call(chained, enabled_tool_names = enabled) == ""
assert (
- strip_leading_bare_json_call(chained + " trailing prose", enabled_tool_names = enabled)
+ strip_leading_bare_json_call(
+ chained + " trailing prose", enabled_tool_names = enabled
+ )
== "trailing prose"
)
# The chain stops at a non-call answer object, which stays visible.
- call_then_answer = (
- '{"name":"web_search","parameters":{"q":"x"}};{"name":"web_search","result":"data"}'
- )
+ call_then_answer = '{"name":"web_search","parameters":{"q":"x"}};{"name":"web_search","result":"data"}'
assert (
strip_leading_bare_json_call(call_then_answer, enabled_tool_names = enabled)
== '{"name":"web_search","result":"data"}'
diff --git a/studio/backend/tests/test_torchao_select.py b/studio/backend/tests/test_torchao_select.py
index e4775a10a6..c07bcfc4f3 100644
--- a/studio/backend/tests/test_torchao_select.py
+++ b/studio/backend/tests/test_torchao_select.py
@@ -114,7 +114,9 @@ def test_skips_torchao_on_windows_rocm(
monkeypatch.setattr(mod, "IS_MACOS", False)
monkeypatch.setattr(mod, "IS_MAC_ARM", False)
monkeypatch.setattr(mod, "NO_TORCH", False)
- monkeypatch.setattr(mod, "_rocm_windows_torch_installed", rocm_windows_torch_installed)
+ monkeypatch.setattr(
+ mod, "_rocm_windows_torch_installed", rocm_windows_torch_installed
+ )
monkeypatch.setattr(
mod, "_installed_torch_is_windows_rocm", lambda: installed_torch_is_windows_rocm
)
@@ -128,7 +130,9 @@ def test_skips_torchao_on_windows_rocm(
monkeypatch.setattr(mod, "_progress", lambda label: progress_labels.append(label))
monkeypatch.setattr(mod, "LOCAL_DD_UNSTRUCTURED_PLUGIN", unstructured_plugin)
monkeypatch.setattr(mod, "LOCAL_DD_GITHUB_PLUGIN", github_plugin)
- monkeypatch.setattr(mod.subprocess, "run", lambda *args, **kwargs: subprocess_result)
+ monkeypatch.setattr(
+ mod.subprocess, "run", lambda *args, **kwargs: subprocess_result
+ )
assert mod.install_python_stack() == 0
diff --git a/studio/backend/tests/test_torchao_stub_worker_parity.py b/studio/backend/tests/test_torchao_stub_worker_parity.py
index bb743385f1..f2f65431f7 100644
--- a/studio/backend/tests/test_torchao_stub_worker_parity.py
+++ b/studio/backend/tests/test_torchao_stub_worker_parity.py
@@ -37,7 +37,9 @@ def _stub_call_linenos(node) -> list[int]:
return [
c.lineno
for c in ast.walk(node)
- if isinstance(c, ast.Call) and isinstance(c.func, ast.Name) and c.func.id == _STUB
+ if isinstance(c, ast.Call)
+ and isinstance(c.func, ast.Name)
+ and c.func.id == _STUB
]
@@ -82,7 +84,10 @@ def _imports_transformers(node) -> bool:
module.split(".")[0] == "transformers"
or module == _INFERENCE_MOD
or module.startswith(_INFERENCE_MOD + ".")
- or (module == "core.inference" and any(a.name == "inference" for a in node.names))
+ or (
+ module == "core.inference"
+ and any(a.name == "inference" for a in node.names)
+ )
)
# Relative forms inside core/inference/worker.py: ``from .inference import X`` and
# ``from . import inference`` both resolve to core.inference.inference.
diff --git a/studio/backend/tests/test_tp_vision_regression.py b/studio/backend/tests/test_tp_vision_regression.py
index d1372ca415..8757dfbc87 100644
--- a/studio/backend/tests/test_tp_vision_regression.py
+++ b/studio/backend/tests/test_tp_vision_regression.py
@@ -106,7 +106,10 @@ def _tensor_parallel_false_drop_guards() -> list[str]:
for n in body:
if (
isinstance(n, ast.Assign)
- and any(isinstance(t, ast.Name) and t.id == "tensor_parallel" for t in n.targets)
+ and any(
+ isinstance(t, ast.Name) and t.id == "tensor_parallel"
+ for t in n.targets
+ )
and isinstance(n.value, ast.Constant)
and n.value.value is False
):
@@ -165,7 +168,9 @@ def test_every_tp_drop_is_logged_not_silent():
def _body_drops_tp(body):
return any(
isinstance(n, ast.Assign)
- and any(isinstance(t, ast.Name) and t.id == "tensor_parallel" for t in n.targets)
+ and any(
+ isinstance(t, ast.Name) and t.id == "tensor_parallel" for t in n.targets
+ )
and isinstance(n.value, ast.Constant)
and n.value.value is False
for n in body
@@ -221,7 +226,9 @@ def test_tensor_split_abort_recorded_early_on_first_spawn():
), "record must be gated on the marker-plus-hard-crash decision helper"
# Recorded before the flash-attn-off retry, not after the full ladder.
fa_off = src.find("_with_flash_attn_off")
- assert 0 <= idx < fa_off, "recording must latch on the first spawn, before flash-off"
+ assert (
+ 0 <= idx < fa_off
+ ), "recording must latch on the first spawn, before flash-off"
def test_vision_downgrade_preserves_multi_gpu_intent():
@@ -240,7 +247,9 @@ def test_vision_downgrade_preserves_multi_gpu_intent():
def test_tensor_attempted_by_default_for_unknown_binary():
"""A (binary, model) not seen to abort -> tensor is attempted (not skipped)."""
- assert LlamaCppBackend._tensor_split_aborts("/never/seen/llama-server", "m") is False
+ assert (
+ LlamaCppBackend._tensor_split_aborts("/never/seen/llama-server", "m") is False
+ )
assert LlamaCppBackend._tensor_split_aborts(None, "m") is False
assert LlamaCppBackend._tensor_split_aborts("/x", None) is False
@@ -457,11 +466,13 @@ def test_fallback_hint_uses_effective_tensor_request_not_just_toggle():
assert "extra_llama_args, request.tensor_parallel" in block
pres = src.find("preserve_multi_gpu_on_layer = bool(")
assert (
- "_effective_tensor_parallel(attempt_extra_args, tensor_parallel)" in src[pres : pres + 200]
+ "_effective_tensor_parallel(attempt_extra_args, tensor_parallel)"
+ in src[pres : pres + 200]
)
# not the toggle-only form this replaced
assert (
- "bool(\n request.tensor_parallel and not tensor_parallel" not in src
+ "bool(\n request.tensor_parallel and not tensor_parallel"
+ not in src
)
@@ -472,9 +483,15 @@ def test_carry_preserved_tensor_intent_truth_table():
inference_routes = _load_inference_routes_module()
f = inference_routes._carry_preserved_tensor_intent
assert f(preserved = True, same_model = True, explicit_drop = False) is True
- assert f(preserved = True, same_model = True, explicit_drop = True) is False # explicit drop
- assert f(preserved = True, same_model = False, explicit_drop = False) is False # model switch
- assert f(preserved = False, same_model = True, explicit_drop = False) is False # not a fallback
+ assert (
+ f(preserved = True, same_model = True, explicit_drop = True) is False
+ ) # explicit drop
+ assert (
+ f(preserved = True, same_model = False, explicit_drop = False) is False
+ ) # model switch
+ assert (
+ f(preserved = False, same_model = True, explicit_drop = False) is False
+ ) # not a fallback
def test_preserved_fallback_carried_across_non_drop_reload():
@@ -670,7 +687,9 @@ def test_explicit_split_mode_layer_extras_reloads_after_multi_gpu_fallback():
inference_routes = _load_inference_routes_module()
- req = LoadRequest(model_path = "owner/repo", llama_extra_args = ["--split-mode", "layer"])
+ req = LoadRequest(
+ model_path = "owner/repo", llama_extra_args = ["--split-mode", "layer"]
+ )
assert "llama_extra_args" in req.model_fields_set
assert (
inference_routes._request_matches_loaded_settings(
@@ -727,18 +746,30 @@ def test_is_explicit_tensor_drop_truth_table():
f = _load_inference_routes_module()._is_explicit_tensor_drop
# A non-tensor split-mode override is the one deliberate departure -> drop.
assert (
- f(LoadRequest(model_path = "owner/repo", llama_extra_args = ["--split-mode", "layer"])) is True
+ f(
+ LoadRequest(
+ model_path = "owner/repo", llama_extra_args = ["--split-mode", "layer"]
+ )
+ )
+ is True
)
# tensor / retry re-engages, never a drop.
assert (
- f(LoadRequest(model_path = "owner/repo", llama_extra_args = ["--split-mode", "tensor"]))
+ f(
+ LoadRequest(
+ model_path = "owner/repo", llama_extra_args = ["--split-mode", "tensor"]
+ )
+ )
is False
)
# A bare tensor_parallel field is the UI echo, not a drop (would collapse on reload).
assert f(LoadRequest(model_path = "owner/repo", tensor_parallel = False)) is False
assert f(LoadRequest(model_path = "owner/repo", tensor_parallel = True)) is False
# Unrelated extra / empty clear / inherit all keep the preserved placement.
- assert f(LoadRequest(model_path = "owner/repo", llama_extra_args = ["--top-k", "20"])) is False
+ assert (
+ f(LoadRequest(model_path = "owner/repo", llama_extra_args = ["--top-k", "20"]))
+ is False
+ )
assert f(LoadRequest(model_path = "owner/repo", llama_extra_args = [])) is False
assert f(LoadRequest(model_path = "owner/repo")) is False
@@ -763,7 +794,10 @@ def test_layer_preserves_tensor_intent_set_only_on_preserved_downgrade():
off = src.find("self._tensor_parallel = False")
assert 0 <= on and 0 <= off
assert "self._layer_preserves_tensor_intent = False" in src[on : on + 120]
- assert "self._layer_preserves_tensor_intent = _layer_min_gpus > 1" in src[off : off + 400]
+ assert (
+ "self._layer_preserves_tensor_intent = _layer_min_gpus > 1"
+ in src[off : off + 400]
+ )
def test_layer_min_gpus_bound_before_gpu_selection_try():
@@ -810,7 +844,10 @@ def test_already_in_target_state_reloads_on_tensor_off_after_fallback():
# Same preserved fallback but an implicit reload that carries the intent forward
# (HF auto-pick / local-dir flows skip the route guard and reach here) -> dedupe.
assert (
- _backend(True)._already_in_target_state(**kwargs, preserve_multi_gpu_on_layer = True) is True
+ _backend(True)._already_in_target_state(
+ **kwargs, preserve_multi_gpu_on_layer = True
+ )
+ is True
)
# A genuine layer load (no preserved intent) -> dedupe, no churn.
assert _backend(False)._already_in_target_state(**kwargs) is True
diff --git a/studio/backend/tests/test_trained_model_scan.py b/studio/backend/tests/test_trained_model_scan.py
index 5d74bb7d28..5284c90526 100644
--- a/studio/backend/tests/test_trained_model_scan.py
+++ b/studio/backend/tests/test_trained_model_scan.py
@@ -29,7 +29,9 @@ from utils.models.model_config import (
)
-def test_scan_trained_models_includes_lora_and_full_finetune_outputs(tmp_path: Path, monkeypatch):
+def test_scan_trained_models_includes_lora_and_full_finetune_outputs(
+ tmp_path: Path, monkeypatch
+):
# resolve_output_dir refuses absolutes outside outputs_root; point it at tmp_path.
from utils.models import model_config as _mc
from utils.paths import storage_roots as _sr
@@ -52,14 +54,17 @@ def test_scan_trained_models_includes_lora_and_full_finetune_outputs(tmp_path: P
(full_dir / "model.safetensors").write_bytes(b"")
found = {
- name: (path, model_type) for name, path, model_type in scan_trained_models(str(tmp_path))
+ name: (path, model_type)
+ for name, path, model_type in scan_trained_models(str(tmp_path))
}
assert found[lora_dir.name] == (str(lora_dir), "lora")
assert found[full_dir.name] == (str(full_dir), "merged")
-def test_get_base_model_from_checkpoint_falls_back_to_full_finetune_config(tmp_path: Path):
+def test_get_base_model_from_checkpoint_falls_back_to_full_finetune_config(
+ tmp_path: Path,
+):
(tmp_path / "config.json").write_text(
json.dumps({"_name_or_path": "HuggingFaceTB/SmolLM-135M"})
)
@@ -83,15 +88,22 @@ def test_lora_identifier_resolves_local_dir_like_the_local_helper(tmp_path: Path
json.dumps({"base_model_name_or_path": "HuggingFaceTB/SmolLM-135M"})
)
(tmp_path / "adapter_model.safetensors").write_bytes(b"")
- with patch("huggingface_hub.hf_hub_download", side_effect = AssertionError("no Hub call")):
- assert get_base_model_from_lora_identifier(str(tmp_path)) == "HuggingFaceTB/SmolLM-135M"
+ with patch(
+ "huggingface_hub.hf_hub_download", side_effect = AssertionError("no Hub call")
+ ):
+ assert (
+ get_base_model_from_lora_identifier(str(tmp_path))
+ == "HuggingFaceTB/SmolLM-135M"
+ )
def test_lora_identifier_resolves_remote_adapter_base(tmp_path: Path):
# Remote adapter: the identifier helper fetches adapter_config.json from the Hub so
# the gate can scan the base, where the local helper returns None.
cfg = tmp_path / "adapter_config.json"
- cfg.write_text(json.dumps({"base_model_name_or_path": "unsloth/Llama-3.2-1B-Instruct"}))
+ cfg.write_text(
+ json.dumps({"base_model_name_or_path": "unsloth/Llama-3.2-1B-Instruct"})
+ )
def _dl(
repo,
@@ -102,7 +114,9 @@ def test_lora_identifier_resolves_remote_adapter_base(tmp_path: Path):
assert fn == "adapter_config.json"
return str(cfg)
- assert get_base_model_from_lora("someone/my-remote-lora") is None # local-only: misses it
+ assert (
+ get_base_model_from_lora("someone/my-remote-lora") is None
+ ) # local-only: misses it
with patch("huggingface_hub.hf_hub_download", side_effect = _dl):
base = get_base_model_from_lora_identifier("someone/my-remote-lora")
assert base == "unsloth/Llama-3.2-1B-Instruct"
@@ -112,16 +126,22 @@ def test_lora_identifier_returns_none_for_non_adapter_remote_repo():
# Non-LoRA remote repo: a 404 on adapter_config.json returns None without retrying.
from huggingface_hub.utils import EntryNotFoundError
- mock = patch("huggingface_hub.hf_hub_download", side_effect = EntryNotFoundError("404"))
+ mock = patch(
+ "huggingface_hub.hf_hub_download", side_effect = EntryNotFoundError("404")
+ )
with mock as m:
- assert get_base_model_from_lora_identifier("unsloth/Llama-3.2-1B-Instruct") is None
+ assert (
+ get_base_model_from_lora_identifier("unsloth/Llama-3.2-1B-Instruct") is None
+ )
assert m.call_count == 1 # 404 is definitive -> no retry
def test_lora_identifier_retries_transient_then_resolves(tmp_path: Path):
# A transient error is retried (not treated as "not a LoRA"); the retry resolves the base.
cfg = tmp_path / "adapter_config.json"
- cfg.write_text(json.dumps({"base_model_name_or_path": "unsloth/Llama-3.2-1B-Instruct"}))
+ cfg.write_text(
+ json.dumps({"base_model_name_or_path": "unsloth/Llama-3.2-1B-Instruct"})
+ )
calls = {"n": 0}
def _dl(
@@ -150,7 +170,8 @@ def test_lora_identifier_persistent_transient_returns_none():
):
assert get_base_model_from_lora_identifier("someone/remote-lora") is None
assert any(
- "Could not resolve remote LoRA base" in str(c.args[0]) for c in mock_warn.call_args_list
+ "Could not resolve remote LoRA base" in str(c.args[0])
+ for c in mock_warn.call_args_list
)
@@ -160,7 +181,9 @@ def test_lora_identifier_persistent_transient_returns_none():
def test_model_config_full_finetune_local_path_is_not_lora(
_mock_vision, _mock_audio_type, _mock_audio_input, tmp_path: Path
):
- (tmp_path / "config.json").write_text(json.dumps({"_name_or_path": "unsloth/Qwen3-4B"}))
+ (tmp_path / "config.json").write_text(
+ json.dumps({"_name_or_path": "unsloth/Qwen3-4B"})
+ )
(tmp_path / "model.safetensors").write_bytes(b"")
config = ModelConfig.from_identifier(str(tmp_path))
diff --git a/studio/backend/tests/test_training_before_spawn.py b/studio/backend/tests/test_training_before_spawn.py
index efd96aeda3..a8e764d39e 100644
--- a/studio/backend/tests/test_training_before_spawn.py
+++ b/studio/backend/tests/test_training_before_spawn.py
@@ -31,7 +31,9 @@ def _start(backend, hook):
dummy_queue = object()
with (
patch("core.training.training.prepare_gpu_selection", return_value = ([0], {})),
- patch("core.training.training._CTX.Queue", side_effect = [dummy_queue, dummy_queue]),
+ patch(
+ "core.training.training._CTX.Queue", side_effect = [dummy_queue, dummy_queue]
+ ),
patch("core.training.training._CTX.Process", return_value = _DummyProcess()),
patch("core.training.training.threading.Thread", return_value = _DummyThread()),
):
@@ -116,10 +118,16 @@ class TestBeforeSpawnHook(unittest.TestCase):
with (
patch("utils.hardware.hardware.DEVICE", DeviceType.CUDA),
- patch("core.training.training.prepare_gpu_selection", side_effect = _placement),
- patch("core.training.training._CTX.Queue", side_effect = [object(), object()]),
+ patch(
+ "core.training.training.prepare_gpu_selection", side_effect = _placement
+ ),
+ patch(
+ "core.training.training._CTX.Queue", side_effect = [object(), object()]
+ ),
patch("core.training.training._CTX.Process", return_value = _DummyProcess()),
- patch("core.training.training.threading.Thread", return_value = _DummyThread()),
+ patch(
+ "core.training.training.threading.Thread", return_value = _DummyThread()
+ ),
):
ok = backend.start_training(
job_id = "before-spawn-test",
@@ -143,10 +151,16 @@ class TestBeforeSpawnHook(unittest.TestCase):
with (
patch("utils.hardware.hardware.DEVICE", DeviceType.CUDA),
- patch("core.training.training.prepare_gpu_selection", side_effect = _placement),
- patch("core.training.training._CTX.Queue", side_effect = [object(), object()]),
+ patch(
+ "core.training.training.prepare_gpu_selection", side_effect = _placement
+ ),
+ patch(
+ "core.training.training._CTX.Queue", side_effect = [object(), object()]
+ ),
patch("core.training.training._CTX.Process", return_value = _DummyProcess()),
- patch("core.training.training.threading.Thread", return_value = _DummyThread()),
+ patch(
+ "core.training.training.threading.Thread", return_value = _DummyThread()
+ ),
):
ok = backend.start_training(
job_id = "before-spawn-test",
diff --git a/studio/backend/tests/test_training_config_popover_source.py b/studio/backend/tests/test_training_config_popover_source.py
index 4263b012eb..033d973d4f 100644
--- a/studio/backend/tests/test_training_config_popover_source.py
+++ b/studio/backend/tests/test_training_config_popover_source.py
@@ -17,7 +17,9 @@ from __future__ import annotations
from pathlib import Path
-_STUDIO_FRONTEND = Path(__file__).resolve().parents[2] / "frontend" / "src" / "features" / "studio"
+_STUDIO_FRONTEND = (
+ Path(__file__).resolve().parents[2] / "frontend" / "src" / "features" / "studio"
+)
def _read(rel: str) -> str:
diff --git a/studio/backend/tests/test_training_preflight.py b/studio/backend/tests/test_training_preflight.py
index 47c6669f8f..9f2e51d2e4 100644
--- a/studio/backend/tests/test_training_preflight.py
+++ b/studio/backend/tests/test_training_preflight.py
@@ -47,7 +47,9 @@ def _stub_if_missing(name, attrs):
setattr(sys.modules[parent], child, mod)
-_stub_if_missing("unsloth", ("FastLanguageModel", "FastVisionModel", "is_bfloat16_supported"))
+_stub_if_missing(
+ "unsloth", ("FastLanguageModel", "FastVisionModel", "is_bfloat16_supported")
+)
_stub_if_missing("unsloth.chat_templates", ("get_chat_template",))
_stub_if_missing("trl", ("SFTTrainer", "SFTConfig"))
@@ -110,13 +112,17 @@ class _RealTemplateTokenizer:
class TestPreflightFirstBatch(unittest.TestCase):
def test_float_input_ids_with_empty_template_suggests_instruct(self):
- ds = [{"messages": [{"role": "user", "content": [{"type": "text", "text": "x"}]}]}]
+ ds = [
+ {"messages": [{"role": "user", "content": [{"type": "text", "text": "x"}]}]}
+ ]
inner = _FakeInnerTrainer(
batch = {"input_ids": torch.zeros((1, 0), dtype = torch.float32)},
train_dataset = ds,
)
s = _fake_self(
- inner = inner, model_name = "Qwen/Qwen2-VL-7B", tokenizer = _EmptyTemplateTokenizer()
+ inner = inner,
+ model_name = "Qwen/Qwen2-VL-7B",
+ tokenizer = _EmptyTemplateTokenizer(),
)
msg = s._preflight_first_batch()
self.assertIsNotNone(msg)
@@ -125,13 +131,17 @@ class TestPreflightFirstBatch(unittest.TestCase):
self.assertIn("base (pretrained) model", msg)
def test_no_instruct_hint_when_model_already_instruct(self):
- ds = [{"messages": [{"role": "user", "content": [{"type": "text", "text": "x"}]}]}]
+ ds = [
+ {"messages": [{"role": "user", "content": [{"type": "text", "text": "x"}]}]}
+ ]
inner = _FakeInnerTrainer(
batch = {"input_ids": torch.zeros((1, 0), dtype = torch.float32)},
train_dataset = ds,
)
s = _fake_self(
- inner = inner, model_name = "org/Foo-Instruct", tokenizer = _EmptyTemplateTokenizer()
+ inner = inner,
+ model_name = "org/Foo-Instruct",
+ tokenizer = _EmptyTemplateTokenizer(),
)
msg = s._preflight_first_batch()
self.assertIsNotNone(msg)
@@ -176,17 +186,23 @@ class TestChatTemplateRendersEmpty(unittest.TestCase):
return _fake_self(inner = inner, tokenizer = tokenizer)
def test_empty_render_detected(self):
- ds = [{"messages": [{"role": "user", "content": [{"type": "text", "text": "x"}]}]}]
+ ds = [
+ {"messages": [{"role": "user", "content": [{"type": "text", "text": "x"}]}]}
+ ]
s = self._self(train_dataset = ds, tokenizer = _EmptyTemplateTokenizer())
self.assertTrue(s._chat_template_renders_empty())
def test_nonempty_render_not_flagged(self):
- ds = [{"messages": [{"role": "user", "content": [{"type": "text", "text": "x"}]}]}]
+ ds = [
+ {"messages": [{"role": "user", "content": [{"type": "text", "text": "x"}]}]}
+ ]
s = self._self(train_dataset = ds, tokenizer = _RealTemplateTokenizer())
self.assertFalse(s._chat_template_renders_empty())
def test_no_messages_key_not_flagged(self):
- s = self._self(train_dataset = [{"text": "raw"}], tokenizer = _EmptyTemplateTokenizer())
+ s = self._self(
+ train_dataset = [{"text": "raw"}], tokenizer = _EmptyTemplateTokenizer()
+ )
self.assertFalse(s._chat_template_renders_empty())
@@ -299,7 +315,11 @@ print(json.dumps({
"""
env = os.environ.copy()
env["PYTHONPATH"] = os.pathsep.join(
- [str(repo_root), str(repo_root / "studio" / "backend"), env.get("PYTHONPATH", "")]
+ [
+ str(repo_root),
+ str(repo_root / "studio" / "backend"),
+ env.get("PYTHONPATH", ""),
+ ]
)
result = subprocess.run(
[sys.executable, "-c", script],
@@ -326,7 +346,11 @@ def test_mlx_adapter_builds_config_and_reports_completion(tmp_path, monkeypatch)
captured["config"] = config
event_queue.put({"type": "progress", "step": 1, "total_steps": 1, "loss": 0.25})
event_queue.put(
- {"type": "complete", "status_message": "done", "output_dir": config["output_dir"]}
+ {
+ "type": "complete",
+ "status_message": "done",
+ "output_dir": config["output_dir"],
+ }
)
trainer = trainer_mod.UnslothTrainer()
@@ -382,7 +406,9 @@ def test_mlx_worker_helpers_cover_cli_paths(tmp_path, monkeypatch):
) == str((tmp_path / "cli-out").resolve())
-def test_run_mlx_training_process_applies_side_effects_before_hardware_detection(monkeypatch):
+def test_run_mlx_training_process_applies_side_effects_before_hardware_detection(
+ monkeypatch,
+):
_load_trainer_module(monkeypatch, "mlx")
from core.training import worker
from utils.hardware import hardware as hw
diff --git a/studio/backend/tests/test_training_progress_prep_timeout.py b/studio/backend/tests/test_training_progress_prep_timeout.py
index 28e2ee37b9..5c44a4cac6 100644
--- a/studio/backend/tests/test_training_progress_prep_timeout.py
+++ b/studio/backend/tests/test_training_progress_prep_timeout.py
@@ -61,7 +61,9 @@ class _Backend:
self.eval_enabled = False
self._active_calls = 0
self._active_polls = active_polls
- self.trainer = types.SimpleNamespace(training_progress = _Progress(step = live_step))
+ self.trainer = types.SimpleNamespace(
+ training_progress = _Progress(step = live_step)
+ )
def is_training_active(self):
self._active_calls += 1
@@ -104,19 +106,27 @@ def _fast_short_timeout(monkeypatch):
monkeypatch.setattr(rt, "_PROGRESS_STALL_TIMEOUT_POLLS", 3)
-def test_prep_phase_does_not_time_out_before_first_step(monkeypatch, _fast_short_timeout):
+def test_prep_phase_does_not_time_out_before_first_step(
+ monkeypatch, _fast_short_timeout
+):
# Step 0 for many polls (far past the timeout), then the run ends. Pre-step
# this is preparation, not a stall: no error event may be emitted.
backend = _Backend(active_polls = 20, step_history = [], live_step = 0)
monkeypatch.setattr(rt, "get_training_backend", lambda: backend)
- raw = _raw(asyncio.run(rt.stream_training_progress(_FakeRequest(), current_subject = "tester")))
+ raw = _raw(
+ asyncio.run(
+ rt.stream_training_progress(_FakeRequest(), current_subject = "tester")
+ )
+ )
assert (
backend._active_calls > rt._PROGRESS_STALL_TIMEOUT_POLLS + 1
), "the loop must have run past the stall threshold for this test to be meaningful"
assert "event: heartbeat" in raw, "prep heartbeats should still flow"
- assert "event: error" not in raw, "a still-preparing run must not be timed out as a stall"
+ assert (
+ "event: error" not in raw
+ ), "a still-preparing run must not be timed out as a stall"
def test_stall_after_first_step_still_times_out(monkeypatch, _fast_short_timeout):
@@ -125,7 +135,11 @@ def test_stall_after_first_step_still_times_out(monkeypatch, _fast_short_timeout
backend = _Backend(active_polls = 100, step_history = [1, 2], live_step = 5)
monkeypatch.setattr(rt, "get_training_backend", lambda: backend)
- raw = _raw(asyncio.run(rt.stream_training_progress(_FakeRequest(), current_subject = "tester")))
+ raw = _raw(
+ asyncio.run(
+ rt.stream_training_progress(_FakeRequest(), current_subject = "tester")
+ )
+ )
assert "event: error" in raw, "a real post-step stall should still time out"
@@ -139,7 +153,9 @@ def test_reconnect_to_stepped_run_still_times_out(monkeypatch, _fast_short_timeo
monkeypatch.setattr(rt, "get_training_backend", lambda: backend)
raw = _raw(
- asyncio.run(rt.stream_training_progress(_ReconnectRequest(), current_subject = "tester"))
+ asyncio.run(
+ rt.stream_training_progress(_ReconnectRequest(), current_subject = "tester")
+ )
)
assert (
diff --git a/studio/backend/tests/test_training_progress_stream_nan.py b/studio/backend/tests/test_training_progress_stream_nan.py
index 5cd84bbca5..68dc2a8b63 100644
--- a/studio/backend/tests/test_training_progress_stream_nan.py
+++ b/studio/backend/tests/test_training_progress_stream_nan.py
@@ -97,7 +97,9 @@ def test_stream_reports_live_step_with_null_loss_during_nan(monkeypatch):
backend = _FakeBackend(active_polls = 2)
monkeypatch.setattr(rt, "get_training_backend", lambda: backend)
- response = asyncio.run(rt.stream_training_progress(_FakeRequest(), current_subject = "tester"))
+ response = asyncio.run(
+ rt.stream_training_progress(_FakeRequest(), current_subject = "tester")
+ )
raw = _collect_events(response)
payloads = _progress_payloads(raw)
assert payloads, f"no SSE payloads parsed from: {raw!r}"
@@ -119,7 +121,9 @@ def test_inactive_stream_completes_with_live_step_and_null_loss(monkeypatch):
backend = _FakeBackend(active_polls = 0)
monkeypatch.setattr(rt, "get_training_backend", lambda: backend)
- response = asyncio.run(rt.stream_training_progress(_FakeRequest(), current_subject = "tester"))
+ response = asyncio.run(
+ rt.stream_training_progress(_FakeRequest(), current_subject = "tester")
+ )
payloads = _progress_payloads(_collect_events(response))
final = payloads[-1]
assert final["step"] == 5
@@ -147,7 +151,9 @@ def test_stream_uses_finite_history_when_progress_in_sync(monkeypatch):
backend.trainer.training_progress.loss = 1.5
monkeypatch.setattr(rt, "get_training_backend", lambda: backend)
- response = asyncio.run(rt.stream_training_progress(_FakeRequest(), current_subject = "tester"))
+ response = asyncio.run(
+ rt.stream_training_progress(_FakeRequest(), current_subject = "tester")
+ )
payloads = _progress_payloads(_collect_events(response))
finite = [p for p in payloads if p.get("step") == 2]
assert finite and finite[0]["loss"] == 1.5
diff --git a/studio/backend/tests/test_training_pump_resilience.py b/studio/backend/tests/test_training_pump_resilience.py
index d75b205f35..603ad9afb4 100644
--- a/studio/backend/tests/test_training_pump_resilience.py
+++ b/studio/backend/tests/test_training_pump_resilience.py
@@ -304,7 +304,9 @@ def test_pump_finalizes_when_read_keeps_raising_on_dead_worker(monkeypatch):
pump = threading.Thread(target = b._pump_loop, daemon = True)
pump.start()
pump.join(timeout = 5)
- assert not pump.is_alive(), "pump must finalize a dead worker even when reads keep raising"
+ assert (
+ not pump.is_alive()
+ ), "pump must finalize a dead worker even when reads keep raising"
assert b._progress.is_training is False
assert finalized.get("status") == "error"
assert b._pump_running is False
@@ -453,7 +455,9 @@ def _stub_spawn(monkeypatch):
hw = _types.ModuleType("utils.hardware")
hw.prepare_gpu_selection = lambda *a, **k: (None, None)
- hw.hardware = type("HW", (), {"DEVICE": "cuda", "DeviceType": type("D", (), {"MLX": "mlx"})})()
+ hw.hardware = type(
+ "HW", (), {"DEVICE": "cuda", "DeviceType": type("D", (), {"MLX": "mlx"})}
+ )()
monkeypatch.setitem(sys.modules, "utils.hardware", hw)
pl = _types.ModuleType("utils.process_lifetime")
diff --git a/studio/backend/tests/test_training_raw_support.py b/studio/backend/tests/test_training_raw_support.py
index fb3cffc91e..816a564f60 100644
--- a/studio/backend/tests/test_training_raw_support.py
+++ b/studio/backend/tests/test_training_raw_support.py
@@ -257,7 +257,9 @@ class TestTrainingRawSupport(unittest.TestCase):
'getattr(MLXTrainingConfig, "__dataclass_fields__", {})',
source,
)
- self.assertIn('if "cast_norm_output_to_input_dtype" in _supported_fields:', source)
+ self.assertIn(
+ 'if "cast_norm_output_to_input_dtype" in _supported_fields:', source
+ )
self.assertIn('if "dataset_order" in _supported_fields:', source)
self.assertIn('if "max_grad_leaf_norm" in _supported_fields:', source)
self.assertIn(
@@ -395,7 +397,10 @@ class TestTrainingRawSupport(unittest.TestCase):
self.assertEqual(result.dataset[0]["text"], "hello")
self.assertEqual(result.dataset[1]["text"], "world")
self.assertTrue(
- any("null or non-string 'text' values" in notice.message for notice in result.notices)
+ any(
+ "null or non-string 'text' values" in notice.message
+ for notice in result.notices
+ )
)
diff --git a/studio/backend/tests/test_training_resume.py b/studio/backend/tests/test_training_resume.py
index 91fdac9961..40df3c8f4b 100644
--- a/studio/backend/tests/test_training_resume.py
+++ b/studio/backend/tests/test_training_resume.py
@@ -67,7 +67,9 @@ def test_can_resume_run_rejects_s3_dataset_source(monkeypatch):
def test_can_resume_run_rejects_s3_metadata_marker(monkeypatch):
monkeypatch.setattr(resume, "has_resume_state", lambda _path: True)
- run = _stopped_run(config_json = json.dumps({"s3_dataset": {"bucket": "training-data"}}))
+ run = _stopped_run(
+ config_json = json.dumps({"s3_dataset": {"bucket": "training-data"}})
+ )
assert resume.can_resume_run(run) is False
@@ -77,7 +79,9 @@ def test_list_runs_includes_config_json_for_resume_policy(monkeypatch, tmp_path)
monkeypatch.setenv("UNSLOTH_STUDIO_HOME", str(tmp_path))
monkeypatch.setattr(studio_db, "_schema_ready", False)
- config_json = json.dumps({"dataset_source": "s3", "s3_dataset": {"bucket": "training-data"}})
+ config_json = json.dumps(
+ {"dataset_source": "s3", "s3_dataset": {"bucket": "training-data"}}
+ )
studio_db.create_run(
id = "run-s3",
diff --git a/studio/backend/tests/test_training_runs.py b/studio/backend/tests/test_training_runs.py
index fd0d6d380f..1a977bbcbc 100644
--- a/studio/backend/tests/test_training_runs.py
+++ b/studio/backend/tests/test_training_runs.py
@@ -13,7 +13,10 @@ from utils.training_runs import (
def test_normalize_project_name_trims_and_collapses_whitespace():
- assert normalize_project_name(" Customer Support LoRA ") == "Customer Support LoRA"
+ assert (
+ normalize_project_name(" Customer Support LoRA ")
+ == "Customer Support LoRA"
+ )
def test_normalize_project_name_returns_none_for_empty_or_invalid_values():
@@ -22,7 +25,9 @@ def test_normalize_project_name_returns_none_for_empty_or_invalid_values():
def test_slugify_project_name_makes_safe_suffix():
- assert slugify_project_name("Customer Support / LoRA v2") == "customer-support-lora-v2"
+ assert (
+ slugify_project_name("Customer Support / LoRA v2") == "customer-support-lora-v2"
+ )
def test_slugify_project_name_rejects_path_only_or_separator_only_values():
@@ -37,7 +42,10 @@ def test_build_default_output_dir_name_appends_project_slug():
timestamp = 1771227800,
)
- assert output_dir == "unsloth_Llama-3.2-3B-Instruct__project-customer-support_1771227800"
+ assert (
+ output_dir
+ == "unsloth_Llama-3.2-3B-Instruct__project-customer-support_1771227800"
+ )
def test_build_default_output_dir_name_caps_final_component(tmp_path):
@@ -77,7 +85,9 @@ def test_model_segment_preserves_project_marker_text_in_model_name():
)
assert output_dir == "org_foo__project--bar_1771227800"
- assert model_segment_from_default_output_dir_name(output_dir) == "org_foo__project-bar"
+ assert (
+ model_segment_from_default_output_dir_name(output_dir) == "org_foo__project-bar"
+ )
def test_model_segment_strips_project_slug_after_escaped_model_marker():
@@ -88,7 +98,9 @@ def test_model_segment_strips_project_slug_after_escaped_model_marker():
)
assert output_dir == "org_foo__project--bar__project-customer-support_1771227800"
- assert model_segment_from_default_output_dir_name(output_dir) == "org_foo__project-bar"
+ assert (
+ model_segment_from_default_output_dir_name(output_dir) == "org_foo__project-bar"
+ )
def test_extract_project_name_from_config_json_returns_normalized_name():
@@ -100,4 +112,7 @@ def test_extract_project_name_from_config_json_returns_normalized_name():
def test_extract_project_name_from_config_json_handles_missing_or_invalid_payload():
assert _extract_project_name_from_config_json(None) is None
assert _extract_project_name_from_config_json("not-json") is None
- assert _extract_project_name_from_config_json(json.dumps({"project_name": " "})) is None
+ assert (
+ _extract_project_name_from_config_json(json.dumps({"project_name": " "}))
+ is None
+ )
diff --git a/studio/backend/tests/test_training_stop_watchdog.py b/studio/backend/tests/test_training_stop_watchdog.py
index 0cd702bce2..9fba4802c2 100644
--- a/studio/backend/tests/test_training_stop_watchdog.py
+++ b/studio/backend/tests/test_training_stop_watchdog.py
@@ -122,7 +122,9 @@ def _wait_until(predicate, timeout = 5.0):
def _record_force_terminate(monkeypatch, b):
"""Replace force_terminate + escalation finalize with recorders (no DB/OS)."""
calls: list = []
- monkeypatch.setattr(b, "force_terminate", lambda target_proc = None: calls.append("force"))
+ monkeypatch.setattr(
+ b, "force_terminate", lambda target_proc = None: calls.append("force")
+ )
monkeypatch.setattr(
b,
"_finalize_stopped_after_escalation",
@@ -138,7 +140,9 @@ def _record_force_terminate(monkeypatch, b):
def test_watchdog_escalates_after_grace_once_complete_seen(monkeypatch):
monkeypatch.setitem(_G, "_STOP_GRACE_S", 0.05)
- monkeypatch.setitem(_G, "_STOP_TIMEOUT_S", 100.0) # ensure grace, not timeout, fires
+ monkeypatch.setitem(
+ _G, "_STOP_TIMEOUT_S", 100.0
+ ) # ensure grace, not timeout, fires
b = TrainingBackend()
calls = _record_force_terminate(monkeypatch, b)
@@ -171,7 +175,9 @@ def test_watchdog_does_not_kill_save_still_saving_within_window(monkeypatch):
b._start_stop_watchdog(cancel = False)
time.sleep(0.3)
- assert calls == [], "an in-progress save must not be killed within the absolute window"
+ assert (
+ calls == []
+ ), "an in-progress save must not be killed within the absolute window"
assert b._stop_watchdog.is_alive()
proc._alive = False
@@ -306,7 +312,9 @@ def test_force_terminate_targets_only_captured_proc():
b._proc = new_proc
b.force_terminate(target_proc = old_proc)
assert new_proc.terminated is False, "must not terminate the new run's worker"
- assert old_proc.terminated is False, "must not terminate a handle that is not current"
+ assert (
+ old_proc.terminated is False
+ ), "must not terminate a handle that is not current"
# Matching: the captured handle is the current worker, so it is terminated.
p = _FakeProc(alive = True)
@@ -363,10 +371,14 @@ def test_finalize_after_escalation_clears_state(monkeypatch):
b._finalize_stopped_after_escalation(watched_job_id = "job_c")
- assert b._proc is None, "the wedged handle must be dropped so is_training_active clears"
+ assert (
+ b._proc is None
+ ), "the wedged handle must be dropped so is_training_active clears"
assert b._progress.is_training is False
assert b._progress.status_message == "Training stopped."
- assert finstop and finstop[0][0] == "job_c", "the captured run must be finalized by id"
+ assert (
+ finstop and finstop[0][0] == "job_c"
+ ), "the captured run must be finalized by id"
assert b.is_training_active() is False
@@ -499,7 +511,13 @@ def test_later_cancel_tightens_watchdog_timeout(monkeypatch):
def _install_fake_db(monkeypatch):
"""Stub storage.studio_db + utils.downsample so the real DB helpers run without
SQLite. Returns the recorder dict."""
- recs = {"created": [], "finished": [], "inserted": [], "insert_ids": [], "progress_ids": []}
+ recs = {
+ "created": [],
+ "finished": [],
+ "inserted": [],
+ "insert_ids": [],
+ "progress_ids": [],
+ }
fake_storage = _types.ModuleType("storage")
fake_db = _types.ModuleType("storage.studio_db")
fake_db.create_run = lambda **kw: recs["created"].append(kw)
@@ -538,7 +556,9 @@ def test_finalize_run_in_db_single_winner_under_concurrency(monkeypatch):
for t in threads:
t.join(timeout = 5)
- assert len(recs["finished"]) == 1, f"finalize must run once, got {len(recs['finished'])}"
+ assert (
+ len(recs["finished"]) == 1
+ ), f"finalize must run once, got {len(recs['finished'])}"
assert b._run_finalized is True
@@ -552,7 +572,9 @@ def test_finalize_run_in_db_no_ops_on_job_mismatch(monkeypatch):
b._finalize_run_in_db(status = "stopped", expected_job_id = "job_old")
- assert recs["finished"] == [], "a superseded job id must not finalize the current run"
+ assert (
+ recs["finished"] == []
+ ), "a superseded job id must not finalize the current run"
assert b._run_finalized is False
@@ -595,7 +617,9 @@ def test_flush_pins_to_passed_run_id(monkeypatch):
b._flush_metrics_to_db(run_id = "job_old")
- assert recs["insert_ids"] == ["job_old"], "metrics must go to the captured run, not the new one"
+ assert recs["insert_ids"] == [
+ "job_old"
+ ], "metrics must go to the captured run, not the new one"
assert recs["progress_ids"] == ["job_old"]
@@ -648,7 +672,9 @@ def test_ensure_db_run_created_publishes_only_after_insert(monkeypatch):
b._ensure_db_run_created()
- assert observed["flag_during_create"] is False, "flag must not be published before insert"
+ assert (
+ observed["flag_during_create"] is False
+ ), "flag must not be published before insert"
assert observed["in_progress_during_create"] is True
assert b._db_run_created is True, "flag must be published after a successful insert"
assert b._db_create_in_progress is False
@@ -673,8 +699,12 @@ def test_ensure_db_run_created_stays_unpublished_on_failure(monkeypatch):
b._ensure_db_run_created()
- assert b._db_run_created is False, "a failed insert must not publish the row as created"
- assert b._db_create_in_progress is False, "the in-progress flag must be cleared on failure"
+ assert (
+ b._db_run_created is False
+ ), "a failed insert must not publish the row as created"
+ assert (
+ b._db_create_in_progress is False
+ ), "the in-progress flag must be cleared on failure"
def test_ensure_db_run_created_does_not_publish_for_a_new_run(monkeypatch):
@@ -700,9 +730,13 @@ def test_ensure_db_run_created_does_not_publish_for_a_new_run(monkeypatch):
b._ensure_db_run_created()
- assert b._db_run_created is False, "must not publish the created flag against the new run"
+ assert (
+ b._db_run_created is False
+ ), "must not publish the created flag against the new run"
# The stale claim is left for start_training to reset, not satisfied for the new run.
- assert b._db_create_in_progress is True, "must not clear the claim once the run is not current"
+ assert (
+ b._db_create_in_progress is True
+ ), "must not clear the claim once the run is not current"
# ----------------------------------------------------------------------------
@@ -725,9 +759,13 @@ def test_escalation_finalizes_watched_run_by_id_end_to_end(monkeypatch):
b._finalize_stopped_after_escalation(target_proc = b._proc, watched_job_id = "job_old")
- assert [f["id"] for f in recs["finished"]] == ["job_old"], "must finish the captured run by id"
+ assert [f["id"] for f in recs["finished"]] == [
+ "job_old"
+ ], "must finish the captured run by id"
assert recs["finished"][0]["status"] == "stopped"
- assert recs["insert_ids"] == ["job_old"], "buffered metrics must land on the captured run"
+ assert recs["insert_ids"] == [
+ "job_old"
+ ], "buffered metrics must land on the captured run"
assert b._metric_buffer == [], "the captured batch must be drained"
@@ -750,7 +788,9 @@ def test_escalation_defers_when_row_cannot_be_created_here(monkeypatch):
assert called == [], "must not finalize when the row can't be established here"
assert b._run_finalized is False, "must not claim the finalize the pump still owes"
- assert b._progress.is_training is False, "parent state must still clear so the UI unsticks"
+ assert (
+ b._progress.is_training is False
+ ), "parent state must still clear so the UI unsticks"
assert b._proc is None
@@ -769,8 +809,12 @@ def test_escalation_creates_row_then_finalizes_when_start_create_failed(monkeypa
b._finalize_stopped_after_escalation(target_proc = b._proc, watched_job_id = "job_s")
- assert [c["id"] for c in recs["created"]] == ["job_s"], "must create the missing row"
- assert [f["id"] for f in recs["finished"]] == ["job_s"], "must finish the created row by id"
+ assert [c["id"] for c in recs["created"]] == [
+ "job_s"
+ ], "must create the missing row"
+ assert [f["id"] for f in recs["finished"]] == [
+ "job_s"
+ ], "must finish the created row by id"
assert b._proc is None, "handle dropped only after the terminal state is recorded"
assert b._db_run_created is True
@@ -792,7 +836,9 @@ def test_escalation_does_not_drop_a_new_runs_handle(monkeypatch):
b._finalize_stopped_after_escalation(target_proc = old_proc, watched_job_id = "job_old")
- assert b._proc is new_proc, "must not drop the handle a new run installed during finalize"
+ assert (
+ b._proc is new_proc
+ ), "must not drop the handle a new run installed during finalize"
def _make_finish_raise(monkeypatch, calls):
diff --git a/studio/backend/tests/test_training_streaming.py b/studio/backend/tests/test_training_streaming.py
index 70b2d6fdcc..446c091b8f 100644
--- a/studio/backend/tests/test_training_streaming.py
+++ b/studio/backend/tests/test_training_streaming.py
@@ -43,7 +43,9 @@ class _Tokenizer:
):
assert tokenize is False
assert add_generation_prompt is False
- return "\n".join(f"{message['role']}: {message['content']}" for message in conversation)
+ return "\n".join(
+ f"{message['role']}: {message['content']}" for message in conversation
+ )
def _iterable_dataset(rows):
@@ -231,7 +233,9 @@ def test_streaming_start_rejects_train_on_completions_before_backend_start():
with patch.object(training_route, "get_training_backend", return_value = backend):
with pytest.raises(HTTPException) as exc_info:
- asyncio.run(training_route.start_training(request, current_subject = "test-user"))
+ asyncio.run(
+ training_route.start_training(request, current_subject = "test-user")
+ )
assert exc_info.value.status_code == 422
assert "train_on_completions" in exc_info.value.detail
@@ -263,7 +267,9 @@ def test_streaming_start_requires_separate_eval_split(eval_split):
with patch.object(training_route, "get_training_backend", return_value = backend):
with pytest.raises(HTTPException) as exc_info:
- asyncio.run(training_route.start_training(request, current_subject = "test-user"))
+ asyncio.run(
+ training_route.start_training(request, current_subject = "test-user")
+ )
assert exc_info.value.status_code == 422
assert "separate eval_split" in exc_info.value.detail
@@ -291,7 +297,9 @@ def test_streaming_start_rejects_missing_max_steps():
with patch.object(training_route, "get_training_backend", return_value = backend):
with pytest.raises(HTTPException) as exc_info:
- asyncio.run(training_route.start_training(request, current_subject = "test-user"))
+ asyncio.run(
+ training_route.start_training(request, current_subject = "test-user")
+ )
assert exc_info.value.status_code == 422
assert "max_steps" in exc_info.value.detail
@@ -323,7 +331,9 @@ def test_streaming_start_rejects_embedding_models():
with patch.object(training_route, "get_training_backend", return_value = backend):
with pytest.raises(HTTPException) as exc_info:
- asyncio.run(training_route.start_training(request, current_subject = "test-user"))
+ asyncio.run(
+ training_route.start_training(request, current_subject = "test-user")
+ )
assert exc_info.value.status_code == 400
assert "embedding" in exc_info.value.detail
@@ -473,10 +483,15 @@ def test_streaming_start_rejects_local_datasets():
with patch.object(training_route, "get_training_backend", return_value = backend):
with pytest.raises(HTTPException) as exc_info:
- asyncio.run(training_route.start_training(request, current_subject = "test-user"))
+ asyncio.run(
+ training_route.start_training(request, current_subject = "test-user")
+ )
assert exc_info.value.status_code == 400
- assert "local" in exc_info.value.detail.lower() or "hf-only" in exc_info.value.detail.lower()
+ assert (
+ "local" in exc_info.value.detail.lower()
+ or "hf-only" in exc_info.value.detail.lower()
+ )
# _drop_invalid_text_rows handles from_generator with column_names=None
@@ -533,7 +548,9 @@ def test_preflight_first_batch_returns_error_on_empty_stream():
trainer_mod = importlib.util.module_from_spec(spec)
# Provide a minimal sys.modules shim so top-level imports in trainer.py don't
# crash when optional heavy deps (torch, unsloth) are absent.
- _orig_import = __builtins__.__import__ if hasattr(__builtins__, "__import__") else __import__
+ _orig_import = (
+ __builtins__.__import__ if hasattr(__builtins__, "__import__") else __import__
+ )
try:
spec.loader.exec_module(trainer_mod)
@@ -552,7 +569,9 @@ def test_preflight_first_batch_returns_error_on_empty_stream():
break
if trainer_cls is None:
- pytest.skip("Could not load trainer module (missing optional deps: torch/unsloth).")
+ pytest.skip(
+ "Could not load trainer module (missing optional deps: torch/unsloth)."
+ )
# Build a bare instance without calling __init__ (avoids needing real deps).
instance = object.__new__(trainer_cls)
@@ -567,4 +586,6 @@ def test_preflight_first_batch_returns_error_on_empty_stream():
)
assert isinstance(result, str)
# The message should indicate there are no training rows / empty dataset.
- assert any(kw in result.lower() for kw in ("empty", "no training", "no rows", "stream"))
+ assert any(
+ kw in result.lower() for kw in ("empty", "no training", "no rows", "stream")
+ )
diff --git a/studio/backend/tests/test_training_vram_coexistence.py b/studio/backend/tests/test_training_vram_coexistence.py
index 2bedc46d1f..18f6b1f95b 100644
--- a/studio/backend/tests/test_training_vram_coexistence.py
+++ b/studio/backend/tests/test_training_vram_coexistence.py
@@ -79,7 +79,9 @@ def _patch_backends(inf, llama):
core_inf.get_inference_backend = lambda: inf
routes_inf = types.ModuleType("routes.inference")
routes_inf.get_llama_cpp_backend = lambda: llama
- return patch.dict(sys.modules, {"core.inference": core_inf, "routes.inference": routes_inf})
+ return patch.dict(
+ sys.modules, {"core.inference": core_inf, "routes.inference": routes_inf}
+ )
# ── summarize_resident_chat ──────────────────────────────────────────────────
@@ -87,7 +89,9 @@ def _patch_backends(inf, llama):
class TestSummarizeResidentChat(_GpuCacheResetMixin, unittest.TestCase):
def test_nothing_resident(self):
- with _patch_backends(_fake_inference_backend(), _fake_llama_backend(active = False)):
+ with _patch_backends(
+ _fake_inference_backend(), _fake_llama_backend(active = False)
+ ):
self.assertEqual(
tv.summarize_resident_chat(),
{"hf": None, "gguf": None, "loading": False, "any": False},
@@ -95,7 +99,8 @@ class TestSummarizeResidentChat(_GpuCacheResetMixin, unittest.TestCase):
def test_hf_resident_via_active_model(self):
with _patch_backends(
- _fake_inference_backend(active = "unsloth/Qwen3-4B"), _fake_llama_backend(active = False)
+ _fake_inference_backend(active = "unsloth/Qwen3-4B"),
+ _fake_llama_backend(active = False),
):
out = tv.summarize_resident_chat()
self.assertEqual(out["hf"], "unsloth/Qwen3-4B")
@@ -146,7 +151,8 @@ class TestSummarizeResidentChat(_GpuCacheResetMixin, unittest.TestCase):
def test_bare_alive_subprocess_without_model_is_not_resident(self):
# Bare-alive subprocess (no model, only CUDA context) must NOT count.
with _patch_backends(
- _fake_inference_backend(active = None, alive = True), _fake_llama_backend(active = False)
+ _fake_inference_backend(active = None, alive = True),
+ _fake_llama_backend(active = False),
):
out = tv.summarize_resident_chat()
self.assertIsNone(out["hf"])
@@ -154,7 +160,8 @@ class TestSummarizeResidentChat(_GpuCacheResetMixin, unittest.TestCase):
def test_gguf_resident(self):
with _patch_backends(
- _fake_inference_backend(), _fake_llama_backend(active = True, identifier = "gemma.gguf")
+ _fake_inference_backend(),
+ _fake_llama_backend(active = True, identifier = "gemma.gguf"),
):
out = tv.summarize_resident_chat()
self.assertEqual(out["gguf"], "gemma.gguf")
@@ -198,7 +205,9 @@ class TestCanKeepAuto(_GpuCacheResetMixin, unittest.TestCase):
kw = {**_BASE_KW, **overrides}
with (
patch("utils.hardware.get_device", return_value = device),
- patch("utils.hardware.auto_select_gpu_ids", return_value = auto_return) as auto_mock,
+ patch(
+ "utils.hardware.auto_select_gpu_ids", return_value = auto_return
+ ) as auto_mock,
):
keep, info = tv.can_keep_chat_during_training(**kw)
return keep, info, auto_mock
@@ -217,7 +226,11 @@ class TestCanKeepAuto(_GpuCacheResetMixin, unittest.TestCase):
self.assertFalse(keep)
def test_unload_on_fallback_all(self):
- meta = {"selection_mode": "fallback_all", "required_gb": 10.0, "usable_gb": 100.0}
+ meta = {
+ "selection_mode": "fallback_all",
+ "required_gb": 10.0,
+ "usable_gb": 100.0,
+ }
keep, _, _ = self._run(([0, 1], meta))
self.assertFalse(keep)
@@ -248,7 +261,9 @@ class TestCanKeepAuto(_GpuCacheResetMixin, unittest.TestCase):
kw = {**_BASE_KW}
with (
patch("utils.hardware.get_device", return_value = DeviceType.CUDA),
- patch("utils.hardware.auto_select_gpu_ids", side_effect = RuntimeError("boom")),
+ patch(
+ "utils.hardware.auto_select_gpu_ids", side_effect = RuntimeError("boom")
+ ),
):
keep, info = tv.can_keep_chat_during_training(**kw)
self.assertFalse(keep)
@@ -293,7 +308,9 @@ class TestCanKeepExplicit(_GpuCacheResetMixin, unittest.TestCase):
def test_keep_when_chosen_gpu_has_room(self):
devices = [{"index": 0, "vram_total_gb": 80.0, "vram_used_gb": 20.0}]
- keep, info, auto_mock = self._run(required = 30.0, devices = devices, resolved = [0], gpu_ids = [0])
+ keep, info, auto_mock = self._run(
+ required = 30.0, devices = devices, resolved = [0], gpu_ids = [0]
+ )
# free 60 >= 30*1.15+4 = 38.5
self.assertTrue(keep)
self.assertEqual(info["mode"], "explicit")
@@ -301,7 +318,9 @@ class TestCanKeepExplicit(_GpuCacheResetMixin, unittest.TestCase):
def test_unload_when_chosen_gpu_too_tight(self):
devices = [{"index": 0, "vram_total_gb": 24.0, "vram_used_gb": 20.0}]
- keep, _, _ = self._run(required = 10.0, devices = devices, resolved = [0], gpu_ids = [0])
+ keep, _, _ = self._run(
+ required = 10.0, devices = devices, resolved = [0], gpu_ids = [0]
+ )
# free 4 < 10*1.15+4 = 15.5
self.assertFalse(keep)
@@ -313,7 +332,9 @@ class TestCanKeepExplicit(_GpuCacheResetMixin, unittest.TestCase):
{"index": 0, "vram_total_gb": 24.0, "vram_used_gb": 4.0},
{"index": 1, "vram_total_gb": 24.0, "vram_used_gb": 14.0},
]
- keep, info, _ = self._run(required = 22.0, devices = devices, resolved = [0, 1], gpu_ids = [0, 1])
+ keep, info, _ = self._run(
+ required = 22.0, devices = devices, resolved = [0, 1], gpu_ids = [0, 1]
+ )
self.assertFalse(keep)
self.assertAlmostEqual(info["usable_gb"], 28.5, places = 3)
@@ -326,10 +347,15 @@ class TestCanKeepExplicit(_GpuCacheResetMixin, unittest.TestCase):
def test_unload_when_estimate_none(self):
with (
patch("utils.hardware.get_device", return_value = DeviceType.CUDA),
- patch("utils.hardware.estimate_required_model_memory_gb", return_value = (None, {})),
+ patch(
+ "utils.hardware.estimate_required_model_memory_gb",
+ return_value = (None, {}),
+ ),
patch("utils.hardware.resolve_requested_gpu_ids", return_value = [0]),
):
- keep, info = tv.can_keep_chat_during_training(**{**_BASE_KW, "gpu_ids": [0]})
+ keep, info = tv.can_keep_chat_during_training(
+ **{**_BASE_KW, "gpu_ids": [0]}
+ )
self.assertFalse(keep)
self.assertEqual(info["reason"], "estimate_unavailable")
@@ -408,7 +434,9 @@ class TestFreeChatModels(_GpuCacheResetMixin, unittest.TestCase):
def test_leaves_cpu_only_gguf_alone(self):
# Killing a CPU-only llama-server cannot reclaim VRAM, so don't.
inf = _fake_inference_backend()
- llama = _fake_llama_backend(active = True, identifier = "cpu.gguf", gpu_offload = False)
+ llama = _fake_llama_backend(
+ active = True, identifier = "cpu.gguf", gpu_offload = False
+ )
with _patch_backends(inf, llama):
freed = tv.free_chat_models_for_training(reason = "test")
llama.unload_model.assert_not_called()
diff --git a/studio/backend/tests/test_training_worker_flash_attn.py b/studio/backend/tests/test_training_worker_flash_attn.py
index 7e7fc1af48..dfc98410a4 100644
--- a/studio/backend/tests/test_training_worker_flash_attn.py
+++ b/studio/backend/tests/test_training_worker_flash_attn.py
@@ -49,7 +49,9 @@ def _missing_module_import(missing: str):
def test_should_try_runtime_flash_attn_install_threshold_and_skip(monkeypatch):
monkeypatch.delenv(worker._FLASH_ATTN_SKIP_ENV, raising = False)
assert worker._should_try_runtime_flash_attn_install(32767) is False
- assert worker._should_try_runtime_flash_attn_install(32768) is sys.platform.startswith("linux")
+ assert worker._should_try_runtime_flash_attn_install(
+ 32768
+ ) is sys.platform.startswith("linux")
monkeypatch.setenv(worker._FLASH_ATTN_SKIP_ENV, "1")
assert worker._should_try_runtime_flash_attn_install(32768) is False
@@ -489,10 +491,14 @@ def test_tilelang_backend_reinstalls_when_tvm_ffi_is_broken(monkeypatch):
# Repair: --force-reinstall --no-deps, apache-tvm-ffi ONLY.
assert "--force-reinstall" in repair_args
- assert "--no-deps" in repair_args, "Repair MUST use --no-deps to avoid replacing torch / CUDA"
+ assert (
+ "--no-deps" in repair_args
+ ), "Repair MUST use --no-deps to avoid replacing torch / CUDA"
assert "--only-binary=:all:" in repair_args
assert f"apache-tvm-ffi=={worker._APACHE_TVM_FFI_PACKAGE_VERSION}" in repair_args
- assert all("tilelang" not in a for a in repair_args), "Repair MUST only touch apache-tvm-ffi"
+ assert all(
+ "tilelang" not in a for a in repair_args
+ ), "Repair MUST only touch apache-tvm-ffi"
# Install: regular dep-resolving install, no --force-reinstall.
assert "--force-reinstall" not in install_args
@@ -666,12 +672,16 @@ def test_hook_installs_when_gate_returns_false(monkeypatch):
conv_install = mock.Mock(side_effect = _conv_install_side_effect)
- monkeypatch.setattr(worker, "_ensure_flash_linear_attention_unconditional", fla_install)
+ monkeypatch.setattr(
+ worker, "_ensure_flash_linear_attention_unconditional", fla_install
+ )
monkeypatch.setattr(worker, "_ensure_tilelang_backend_unconditional", tile_install)
monkeypatch.setattr(worker, "_install_package_wheel_first", conv_install)
monkeypatch.delenv(worker._FAST_PATH_HOOKS_SKIP_ENV, raising = False)
- worker._install_fast_path_hooks(event_queue = _FakeQueue(), model_name = "unsloth/Qwen3.5-2B")
+ worker._install_fast_path_hooks(
+ event_queue = _FakeQueue(), model_name = "unsloth/Qwen3.5-2B"
+ )
from transformers.utils import import_utils as _iu
@@ -695,7 +705,9 @@ def test_hook_skips_install_when_gate_already_true(monkeypatch):
fla_install = mock.Mock()
tile_install = mock.Mock()
conv_install = mock.Mock()
- monkeypatch.setattr(worker, "_ensure_flash_linear_attention_unconditional", fla_install)
+ monkeypatch.setattr(
+ worker, "_ensure_flash_linear_attention_unconditional", fla_install
+ )
monkeypatch.setattr(worker, "_ensure_tilelang_backend_unconditional", tile_install)
monkeypatch.setattr(worker, "_install_package_wheel_first", conv_install)
# Tilelang healthy -> post_available path is a no-op (otherwise it
@@ -704,7 +716,9 @@ def test_hook_skips_install_when_gate_already_true(monkeypatch):
monkeypatch.setattr(worker, "_installed_tvm_ffi_version", lambda: "0.1.9")
monkeypatch.delenv(worker._FAST_PATH_HOOKS_SKIP_ENV, raising = False)
- worker._install_fast_path_hooks(event_queue = _FakeQueue(), model_name = "unsloth/Qwen3.5-2B")
+ worker._install_fast_path_hooks(
+ event_queue = _FakeQueue(), model_name = "unsloth/Qwen3.5-2B"
+ )
from transformers.utils import import_utils as _iu
@@ -732,12 +746,16 @@ def test_hook_idempotent_on_repeat_call(monkeypatch):
return True
conv_install = mock.Mock(side_effect = _conv_install_side_effect)
- monkeypatch.setattr(worker, "_ensure_flash_linear_attention_unconditional", fla_install)
+ monkeypatch.setattr(
+ worker, "_ensure_flash_linear_attention_unconditional", fla_install
+ )
monkeypatch.setattr(worker, "_ensure_tilelang_backend_unconditional", tile_install)
monkeypatch.setattr(worker, "_install_package_wheel_first", conv_install)
monkeypatch.delenv(worker._FAST_PATH_HOOKS_SKIP_ENV, raising = False)
- worker._install_fast_path_hooks(event_queue = _FakeQueue(), model_name = "unsloth/Qwen3.5-2B")
+ worker._install_fast_path_hooks(
+ event_queue = _FakeQueue(), model_name = "unsloth/Qwen3.5-2B"
+ )
from transformers.utils import import_utils as _iu
@@ -758,12 +776,18 @@ def test_hook_handles_install_failure_gracefully(monkeypatch):
def raising_install(eq):
raise RuntimeError("pip failed to fetch wheel")
- monkeypatch.setattr(worker, "_ensure_flash_linear_attention_unconditional", raising_install)
- monkeypatch.setattr(worker, "_ensure_tilelang_backend_unconditional", lambda eq: None)
+ monkeypatch.setattr(
+ worker, "_ensure_flash_linear_attention_unconditional", raising_install
+ )
+ monkeypatch.setattr(
+ worker, "_ensure_tilelang_backend_unconditional", lambda eq: None
+ )
monkeypatch.setattr(worker, "_install_package_wheel_first", lambda **kw: None)
monkeypatch.delenv(worker._FAST_PATH_HOOKS_SKIP_ENV, raising = False)
- worker._install_fast_path_hooks(event_queue = _FakeQueue(), model_name = "unsloth/Qwen3.5-2B")
+ worker._install_fast_path_hooks(
+ event_queue = _FakeQueue(), model_name = "unsloth/Qwen3.5-2B"
+ )
from transformers.utils import import_utils as _iu
@@ -777,10 +801,14 @@ def test_hook_can_be_disabled_via_env(monkeypatch):
_patch_iu_gates(monkeypatch, fla_gate, conv_gate)
fla_install = mock.Mock()
- monkeypatch.setattr(worker, "_ensure_flash_linear_attention_unconditional", fla_install)
+ monkeypatch.setattr(
+ worker, "_ensure_flash_linear_attention_unconditional", fla_install
+ )
monkeypatch.setenv(worker._FAST_PATH_HOOKS_SKIP_ENV, "1")
- worker._install_fast_path_hooks(event_queue = _FakeQueue(), model_name = "unsloth/Qwen3.5-2B")
+ worker._install_fast_path_hooks(
+ event_queue = _FakeQueue(), model_name = "unsloth/Qwen3.5-2B"
+ )
from transformers.utils import import_utils as _iu
@@ -795,12 +823,18 @@ def test_hook_clears_lru_cache_before_first_check(monkeypatch):
conv_gate = _make_fake_gate(initial_return = True)
_patch_iu_gates(monkeypatch, fla_gate, conv_gate)
- monkeypatch.setattr(worker, "_ensure_flash_linear_attention_unconditional", lambda eq: None)
- monkeypatch.setattr(worker, "_ensure_tilelang_backend_unconditional", lambda eq: None)
+ monkeypatch.setattr(
+ worker, "_ensure_flash_linear_attention_unconditional", lambda eq: None
+ )
+ monkeypatch.setattr(
+ worker, "_ensure_tilelang_backend_unconditional", lambda eq: None
+ )
monkeypatch.setattr(worker, "_install_package_wheel_first", lambda **kw: None)
monkeypatch.delenv(worker._FAST_PATH_HOOKS_SKIP_ENV, raising = False)
- worker._install_fast_path_hooks(event_queue = _FakeQueue(), model_name = "unsloth/Qwen3.5-2B")
+ worker._install_fast_path_hooks(
+ event_queue = _FakeQueue(), model_name = "unsloth/Qwen3.5-2B"
+ )
from transformers.utils import import_utils as _iu
_iu.is_flash_linear_attention_available()
@@ -827,12 +861,18 @@ def test_hook_rewrites_previously_imported_module_bindings(monkeypatch):
fla_gate.next_return = True
return True
- monkeypatch.setattr(worker, "_ensure_flash_linear_attention_unconditional", fake_install)
- monkeypatch.setattr(worker, "_ensure_tilelang_backend_unconditional", lambda eq: True)
+ monkeypatch.setattr(
+ worker, "_ensure_flash_linear_attention_unconditional", fake_install
+ )
+ monkeypatch.setattr(
+ worker, "_ensure_tilelang_backend_unconditional", lambda eq: True
+ )
monkeypatch.setattr(worker, "_install_package_wheel_first", lambda **kw: True)
monkeypatch.delenv(worker._FAST_PATH_HOOKS_SKIP_ENV, raising = False)
- worker._install_fast_path_hooks(event_queue = _FakeQueue(), model_name = "unsloth/Qwen3.5-2B")
+ worker._install_fast_path_hooks(
+ event_queue = _FakeQueue(), model_name = "unsloth/Qwen3.5-2B"
+ )
# The fake module's local binding is rewritten to the wrapper.
assert fake_mod.is_flash_linear_attention_available is not fla_gate
@@ -856,20 +896,30 @@ def test_hook_skips_when_import_utils_unavailable(monkeypatch):
monkeypatch.delenv(worker._FAST_PATH_HOOKS_SKIP_ENV, raising = False)
# Should not raise.
- worker._install_fast_path_hooks(event_queue = _FakeQueue(), model_name = "unsloth/Qwen3.5-2B")
+ worker._install_fast_path_hooks(
+ event_queue = _FakeQueue(), model_name = "unsloth/Qwen3.5-2B"
+ )
def test_substring_fallback_unchanged_when_hook_skipped(monkeypatch):
"""Hook disabled -> legacy gate falls back to auto-discovered types."""
install_mock = mock.Mock()
- monkeypatch.setattr(worker, "_ensure_flash_linear_attention_unconditional", install_mock)
- monkeypatch.setattr(worker, "_discover_fla_model_types", lambda: frozenset({"qwen3_5"}))
+ monkeypatch.setattr(
+ worker, "_ensure_flash_linear_attention_unconditional", install_mock
+ )
+ monkeypatch.setattr(
+ worker, "_discover_fla_model_types", lambda: frozenset({"qwen3_5"})
+ )
monkeypatch.setenv(worker._FAST_PATH_HOOKS_SKIP_ENV, "1")
- worker._ensure_flash_linear_attention(event_queue = [], model_name = "unsloth/Qwen3.5-2B")
+ worker._ensure_flash_linear_attention(
+ event_queue = [], model_name = "unsloth/Qwen3.5-2B"
+ )
assert install_mock.call_count == 1
- worker._ensure_flash_linear_attention(event_queue = [], model_name = "meta-llama/Llama-3.1-8B")
+ worker._ensure_flash_linear_attention(
+ event_queue = [], model_name = "meta-llama/Llama-3.1-8B"
+ )
assert install_mock.call_count == 1
@@ -897,9 +947,13 @@ def test_hook_does_not_install_tilelang_for_model_outside_allowlist(monkeypatch)
fla_install = mock.Mock(side_effect = _fla_install)
tile_install = mock.Mock(return_value = True)
- monkeypatch.setattr(worker, "_ensure_flash_linear_attention_unconditional", fla_install)
+ monkeypatch.setattr(
+ worker, "_ensure_flash_linear_attention_unconditional", fla_install
+ )
monkeypatch.setattr(worker, "_ensure_tilelang_backend_unconditional", tile_install)
- monkeypatch.setattr(worker, "_install_package_wheel_first", mock.Mock(return_value = True))
+ monkeypatch.setattr(
+ worker, "_install_package_wheel_first", mock.Mock(return_value = True)
+ )
monkeypatch.delenv(worker._FAST_PATH_HOOKS_SKIP_ENV, raising = False)
# Hermetize the auto-discovered set so the test stays valid as new
# transformers releases add FLA-using model_types (eg olmo_hybrid in
@@ -934,12 +988,18 @@ def test_hook_does_install_tilelang_for_qwen35(monkeypatch):
fla_install = mock.Mock(side_effect = _fla_install)
tile_install = mock.Mock(return_value = True)
- monkeypatch.setattr(worker, "_ensure_flash_linear_attention_unconditional", fla_install)
+ monkeypatch.setattr(
+ worker, "_ensure_flash_linear_attention_unconditional", fla_install
+ )
monkeypatch.setattr(worker, "_ensure_tilelang_backend_unconditional", tile_install)
- monkeypatch.setattr(worker, "_install_package_wheel_first", mock.Mock(return_value = True))
+ monkeypatch.setattr(
+ worker, "_install_package_wheel_first", mock.Mock(return_value = True)
+ )
monkeypatch.delenv(worker._FAST_PATH_HOOKS_SKIP_ENV, raising = False)
- worker._install_fast_path_hooks(event_queue = _FakeQueue(), model_name = "unsloth/Qwen3.5-2B")
+ worker._install_fast_path_hooks(
+ event_queue = _FakeQueue(), model_name = "unsloth/Qwen3.5-2B"
+ )
from transformers.utils import import_utils as _iu
@@ -988,14 +1048,20 @@ def test_hook_trusts_installer_bool_not_metadata(monkeypatch):
return False # but deep import is broken
fake_fla_install = mock.Mock(side_effect = _bad_install)
- monkeypatch.setattr(worker, "_ensure_flash_linear_attention_unconditional", fake_fla_install)
+ monkeypatch.setattr(
+ worker, "_ensure_flash_linear_attention_unconditional", fake_fla_install
+ )
monkeypatch.setattr(
worker, "_ensure_tilelang_backend_unconditional", mock.Mock(return_value = True)
)
- monkeypatch.setattr(worker, "_install_package_wheel_first", mock.Mock(return_value = True))
+ monkeypatch.setattr(
+ worker, "_install_package_wheel_first", mock.Mock(return_value = True)
+ )
monkeypatch.delenv(worker._FAST_PATH_HOOKS_SKIP_ENV, raising = False)
- worker._install_fast_path_hooks(event_queue = _FakeQueue(), model_name = "unsloth/Qwen3.5-2B")
+ worker._install_fast_path_hooks(
+ event_queue = _FakeQueue(), model_name = "unsloth/Qwen3.5-2B"
+ )
from transformers.utils import import_utils as _iu
@@ -1048,10 +1114,14 @@ def test_hook_skips_tilelang_when_fla_install_is_skipped(monkeypatch):
monkeypatch.setenv(worker._FLA_SKIP_ENV, "1")
tile_install = mock.Mock(return_value = True)
monkeypatch.setattr(worker, "_ensure_tilelang_backend_unconditional", tile_install)
- monkeypatch.setattr(worker, "_install_package_wheel_first", mock.Mock(return_value = True))
+ monkeypatch.setattr(
+ worker, "_install_package_wheel_first", mock.Mock(return_value = True)
+ )
monkeypatch.delenv(worker._FAST_PATH_HOOKS_SKIP_ENV, raising = False)
- worker._install_fast_path_hooks(event_queue = _FakeQueue(), model_name = "unsloth/Qwen3.5-2B")
+ worker._install_fast_path_hooks(
+ event_queue = _FakeQueue(), model_name = "unsloth/Qwen3.5-2B"
+ )
from transformers.utils import import_utils as _iu
@@ -1071,15 +1141,21 @@ def test_hook_runs_tilelang_repair_when_fla_already_true(monkeypatch):
fla_install = mock.Mock(return_value = True)
tile_install = mock.Mock(return_value = True)
- monkeypatch.setattr(worker, "_ensure_flash_linear_attention_unconditional", fla_install)
+ monkeypatch.setattr(
+ worker, "_ensure_flash_linear_attention_unconditional", fla_install
+ )
monkeypatch.setattr(worker, "_ensure_tilelang_backend_unconditional", tile_install)
- monkeypatch.setattr(worker, "_install_package_wheel_first", mock.Mock(return_value = True))
+ monkeypatch.setattr(
+ worker, "_install_package_wheel_first", mock.Mock(return_value = True)
+ )
# tilelang missing AND tvm-ffi on broken list — both trigger repair.
monkeypatch.setattr(worker, "_tilelang_importable", lambda: False)
monkeypatch.setattr(worker, "_installed_tvm_ffi_version", lambda: "0.1.11")
monkeypatch.delenv(worker._FAST_PATH_HOOKS_SKIP_ENV, raising = False)
- worker._install_fast_path_hooks(event_queue = _FakeQueue(), model_name = "unsloth/Qwen3.5-2B")
+ worker._install_fast_path_hooks(
+ event_queue = _FakeQueue(), model_name = "unsloth/Qwen3.5-2B"
+ )
from transformers.utils import import_utils as _iu
@@ -1176,11 +1252,17 @@ def test_install_fast_path_hooks_sets_fla_tilelang_zero_on_hip(monkeypatch):
monkeypatch.delenv("FLA_TILELANG", raising = False)
monkeypatch.delenv(worker._FAST_PATH_HOOKS_SKIP_ENV, raising = False)
monkeypatch.setattr(worker, "_torch_has_hip", lambda: True)
- monkeypatch.setattr(worker, "_ensure_flash_linear_attention_unconditional", lambda eq: True)
- monkeypatch.setattr(worker, "_ensure_tilelang_backend_unconditional", lambda eq: True)
+ monkeypatch.setattr(
+ worker, "_ensure_flash_linear_attention_unconditional", lambda eq: True
+ )
+ monkeypatch.setattr(
+ worker, "_ensure_tilelang_backend_unconditional", lambda eq: True
+ )
monkeypatch.setattr(worker, "_install_package_wheel_first", lambda **kw: True)
- worker._install_fast_path_hooks(event_queue = _FakeQueue(), model_name = "unsloth/Qwen3.5-2B")
+ worker._install_fast_path_hooks(
+ event_queue = _FakeQueue(), model_name = "unsloth/Qwen3.5-2B"
+ )
assert _os.environ.get("FLA_TILELANG") == "0"
@@ -1194,11 +1276,17 @@ def test_install_fast_path_hooks_respects_user_fla_tilelang_override(monkeypatch
monkeypatch.setenv("FLA_TILELANG", "1")
monkeypatch.delenv(worker._FAST_PATH_HOOKS_SKIP_ENV, raising = False)
monkeypatch.setattr(worker, "_torch_has_hip", lambda: True)
- monkeypatch.setattr(worker, "_ensure_flash_linear_attention_unconditional", lambda eq: True)
- monkeypatch.setattr(worker, "_ensure_tilelang_backend_unconditional", lambda eq: True)
+ monkeypatch.setattr(
+ worker, "_ensure_flash_linear_attention_unconditional", lambda eq: True
+ )
+ monkeypatch.setattr(
+ worker, "_ensure_tilelang_backend_unconditional", lambda eq: True
+ )
monkeypatch.setattr(worker, "_install_package_wheel_first", lambda **kw: True)
- worker._install_fast_path_hooks(event_queue = _FakeQueue(), model_name = "unsloth/Qwen3.5-2B")
+ worker._install_fast_path_hooks(
+ event_queue = _FakeQueue(), model_name = "unsloth/Qwen3.5-2B"
+ )
assert _os.environ["FLA_TILELANG"] == "1"
@@ -1210,11 +1298,17 @@ def test_install_fast_path_hooks_does_not_set_fla_tilelang_on_cuda(monkeypatch):
monkeypatch.delenv("FLA_TILELANG", raising = False)
monkeypatch.delenv(worker._FAST_PATH_HOOKS_SKIP_ENV, raising = False)
monkeypatch.setattr(worker, "_torch_has_hip", lambda: False)
- monkeypatch.setattr(worker, "_ensure_flash_linear_attention_unconditional", lambda eq: True)
- monkeypatch.setattr(worker, "_ensure_tilelang_backend_unconditional", lambda eq: True)
+ monkeypatch.setattr(
+ worker, "_ensure_flash_linear_attention_unconditional", lambda eq: True
+ )
+ monkeypatch.setattr(
+ worker, "_ensure_tilelang_backend_unconditional", lambda eq: True
+ )
monkeypatch.setattr(worker, "_install_package_wheel_first", lambda **kw: True)
- worker._install_fast_path_hooks(event_queue = _FakeQueue(), model_name = "unsloth/Qwen3.5-2B")
+ worker._install_fast_path_hooks(
+ event_queue = _FakeQueue(), model_name = "unsloth/Qwen3.5-2B"
+ )
assert _os.environ.get("FLA_TILELANG") is None
@@ -1224,7 +1318,9 @@ def test_install_fast_path_hooks_does_not_set_fla_tilelang_on_cuda(monkeypatch):
# ───────────────────────────────────────────────────────────────────
-def _make_fake_transformers_tree(tmp_path, fla_types: list[str], non_fla_types: list[str]):
+def _make_fake_transformers_tree(
+ tmp_path, fla_types: list[str], non_fla_types: list[str]
+):
"""Lay out tmp dir as `transformers/models/{type}/modeling_{type}.py`."""
pkg = tmp_path / "transformers"
models = pkg / "models"
@@ -1267,7 +1363,9 @@ def test_discover_fla_model_types_returns_only_fla_users(tmp_path, monkeypatch):
def test_discover_fla_model_types_caches_across_calls(tmp_path, monkeypatch):
- pkg = _make_fake_transformers_tree(tmp_path, fla_types = ["qwen3_5"], non_fla_types = [])
+ pkg = _make_fake_transformers_tree(
+ tmp_path, fla_types = ["qwen3_5"], non_fla_types = []
+ )
fake = mock.MagicMock(__file__ = str(pkg / "__init__.py"))
monkeypatch.setitem(sys.modules, "transformers", fake)
_reset_fla_cache(monkeypatch)
@@ -1313,7 +1411,9 @@ def test_discover_fla_model_types_handles_missing_transformers(monkeypatch):
def test_discover_fla_model_types_handles_unreadable_file(tmp_path, monkeypatch):
- pkg = _make_fake_transformers_tree(tmp_path, fla_types = ["qwen3_5"], non_fla_types = [])
+ pkg = _make_fake_transformers_tree(
+ tmp_path, fla_types = ["qwen3_5"], non_fla_types = []
+ )
fake = mock.MagicMock(__file__ = str(pkg / "__init__.py"))
monkeypatch.setitem(sys.modules, "transformers", fake)
_reset_fla_cache(monkeypatch)
@@ -1359,7 +1459,9 @@ def test_model_wants_tilelang_empty_when_transformers_has_no_fla(monkeypatch):
def test_model_wants_tilelang_normalizes_separators(monkeypatch):
- monkeypatch.setattr(worker, "_discover_fla_model_types", lambda: frozenset({"qwen3_next"}))
+ monkeypatch.setattr(
+ worker, "_discover_fla_model_types", lambda: frozenset({"qwen3_next"})
+ )
for variant in (
"qwen3-next",
"Qwen3.Next",
diff --git a/studio/backend/tests/test_training_xet_fallback.py b/studio/backend/tests/test_training_xet_fallback.py
index b4a3864334..d85d8c7acc 100644
--- a/studio/backend/tests/test_training_xet_fallback.py
+++ b/studio/backend/tests/test_training_xet_fallback.py
@@ -134,7 +134,11 @@ class _FakeCtx:
def _backend_mid_load():
b = TrainingBackend()
- b._last_full_config = {"model_name": "org/model", "disable_xet": False, "hf_token": "tok"}
+ b._last_full_config = {
+ "model_name": "org/model",
+ "disable_xet": False,
+ "hf_token": "tok",
+ }
b._in_model_load = True
b._xet_fallback_used = False
proc = _FakeProc()
@@ -163,7 +167,9 @@ def test_respawn_uses_disable_xet_and_preserves_run_row(monkeypatch):
b, "_ensure_db_run_created", lambda: created.__setitem__("n", created["n"] + 1)
)
monkeypatch.setattr(
- b, "_finalize_run_in_db", lambda **k: finalized.__setitem__("n", finalized["n"] + 1)
+ b,
+ "_finalize_run_in_db",
+ lambda **k: finalized.__setitem__("n", finalized["n"] + 1),
)
b._respawn_worker_disable_xet()
@@ -173,7 +179,9 @@ def test_respawn_uses_disable_xet_and_preserves_run_row(monkeypatch):
assert cfg["disable_xet"] is True, "respawned worker must run with Xet disabled"
assert cfg["model_name"] == "org/model"
assert created["n"] == 0, "respawn must not recreate the DB run row"
- assert finalized["n"] == 0, "a successful respawn must not finalize the run as error"
+ assert (
+ finalized["n"] == 0
+ ), "a successful respawn must not finalize the run as error"
def test_second_stall_surfaces_error_without_respawn():
diff --git a/studio/backend/tests/test_transformers_latest.py b/studio/backend/tests/test_transformers_latest.py
index af48d674cc..7c6f6a010b 100644
--- a/studio/backend/tests/test_transformers_latest.py
+++ b/studio/backend/tests/test_transformers_latest.py
@@ -114,7 +114,9 @@ def _fake_urlopen_factory(counter: dict):
def _isolated_caches(tmp_path: Path, monkeypatch):
"""Fresh in-memory + on-disk caches per test; no accidental real studio_root writes."""
tl.clear_caches()
- monkeypatch.setattr(tl, "_cache_file", lambda: tmp_path / "transformers_latest_check.json")
+ monkeypatch.setattr(
+ tl, "_cache_file", lambda: tmp_path / "transformers_latest_check.json"
+ )
# The sidecar swap reservation writes a lock file next to the venv dir;
# point it at tmp so tests never touch the real studio root.
monkeypatch.setattr(tv, "_VENV_T5_LATEST_DIR", str(tmp_path / "venv_t5_latest"))
@@ -232,7 +234,10 @@ class TestLatestTransformersSupports:
def test_unknown_everywhere(self, monkeypatch):
monkeypatch.setattr("urllib.request.urlopen", _fake_urlopen_factory({}))
result = latest_transformers_supports("no_such_arch")
- assert result["supported_in_pypi"] is False and result["supported_in_main"] is False
+ assert (
+ result["supported_in_pypi"] is False
+ and result["supported_in_main"] is False
+ )
def test_network_failure_returns_none(self, monkeypatch):
_no_network(monkeypatch, exc = OSError("down"))
@@ -272,7 +277,9 @@ class TestLatestTransformersSupports:
counter = {}
monkeypatch.setattr("urllib.request.urlopen", _fake_urlopen_factory(counter))
latest_transformers_supports("brandnew_arch")
- stale = dict(tl._memory_snapshot, fetched_at = time.time() - tl._CACHE_TTL_SECONDS - 1)
+ stale = dict(
+ tl._memory_snapshot, fetched_at = time.time() - tl._CACHE_TTL_SECONDS - 1
+ )
tl.clear_caches()
tl._save_snapshot_file(stale)
first_total = counter["__total__"]
@@ -342,7 +349,9 @@ class TestCheckUpgradeForModel:
_fake_overlays(monkeypatch)
monkeypatch.setattr("urllib.request.urlopen", _fake_urlopen_factory({}))
result = check_upgrade_for_model(_local_model(tmp_path, "dev_only_arch"))
- assert result["supported_in_pypi"] is False and result["supported_in_main"] is True
+ assert (
+ result["supported_in_pypi"] is False and result["supported_in_main"] is True
+ )
def test_unknown_everywhere_falls_through(self, tmp_path: Path, monkeypatch):
_fake_overlays(monkeypatch)
@@ -404,13 +413,17 @@ class TestCheckUpgradeForModel:
monkeypatch.setattr("urllib.request.urlopen", _fake_urlopen_factory({}))
d = tmp_path / "nested"
d.mkdir()
- (d / "config.json").write_text(json.dumps({"text_config": {"model_type": "brandnew_arch"}}))
+ (d / "config.json").write_text(
+ json.dumps({"text_config": {"model_type": "brandnew_arch"}})
+ )
result = check_upgrade_for_model(str(d))
assert result is not None and result["model_type"] == "brandnew_arch"
def test_never_raises_on_internal_error(self, monkeypatch):
monkeypatch.setattr(
- tl, "_load_config_json", lambda *a, **k: (_ for _ in ()).throw(RuntimeError("boom"))
+ tl,
+ "_load_config_json",
+ lambda *a, **k: (_ for _ in ()).throw(RuntimeError("boom")),
)
assert check_upgrade_for_model("some/model") is None
@@ -432,7 +445,9 @@ class TestNestedModelTypeExtraction:
class TestRoutingParity:
- def test_all_overlay_types_route_identically_and_never_check(self, tmp_path: Path, monkeypatch):
+ def test_all_overlay_types_route_identically_and_never_check(
+ self, tmp_path: Path, monkeypatch
+ ):
_fake_overlays(monkeypatch)
calls = _no_network(monkeypatch)
expected_tier = {
@@ -450,7 +465,9 @@ class TestRoutingParity:
assert check_upgrade_for_model(_local_model(tmp_path, model_type)) is None
assert calls["n"] == 0
- def test_real_installed_mappings_route_without_checker(self, monkeypatch, tmp_path: Path):
+ def test_real_installed_mappings_route_without_checker(
+ self, monkeypatch, tmp_path: Path
+ ):
"""Parity over the REAL installed overlays (base + any provisioned sidecar):
every shipped model_type resolves statically, so the remote checker never
fires and routing is byte-identical with the feature enabled."""
@@ -493,7 +510,9 @@ class TestLatestVenvProvisioning:
assert pkgs[0] == "transformers==5.13.0"
assert any(p.startswith("huggingface_hub==") for p in pkgs)
- def test_ensure_latest_writes_pin_and_invalidates_cache(self, tmp_path: Path, monkeypatch):
+ def test_ensure_latest_writes_pin_and_invalidates_cache(
+ self, tmp_path: Path, monkeypatch
+ ):
venv_dir = tmp_path / ".venv_t5_latest"
monkeypatch.setattr(tv, "_VENV_T5_LATEST_DIR", str(venv_dir))
recorded = {}
@@ -515,7 +534,9 @@ class TestLatestVenvProvisioning:
assert latest_venv_pinned_version() == "5.13.0"
assert "latest" not in _config_mapping_cache
- def test_ensure_latest_upgrade_failure_keeps_old_sidecar(self, tmp_path: Path, monkeypatch):
+ def test_ensure_latest_upgrade_failure_keeps_old_sidecar(
+ self, tmp_path: Path, monkeypatch
+ ):
venv_dir = tmp_path / ".venv_t5_latest"
monkeypatch.setattr(tv, "_VENV_T5_LATEST_DIR", str(venv_dir))
venv_dir.mkdir(parents = True)
@@ -532,7 +553,9 @@ class TestLatestVenvProvisioning:
assert not Path(str(venv_dir) + ".staging").exists()
def test_ensure_latest_rejects_bad_version(self, tmp_path: Path, monkeypatch):
- monkeypatch.setattr(tv, "_VENV_T5_LATEST_DIR", str(tmp_path / ".venv_t5_latest"))
+ monkeypatch.setattr(
+ tv, "_VENV_T5_LATEST_DIR", str(tmp_path / ".venv_t5_latest")
+ )
monkeypatch.setattr(
tv,
"_ensure_venv_dir",
@@ -541,7 +564,9 @@ class TestLatestVenvProvisioning:
assert ensure_latest_transformers_venv("5.13.0 && curl evil") is False
def test_ensure_latest_offline_refuses(self, tmp_path: Path, monkeypatch):
- monkeypatch.setattr(tv, "_VENV_T5_LATEST_DIR", str(tmp_path / ".venv_t5_latest"))
+ monkeypatch.setattr(
+ tv, "_VENV_T5_LATEST_DIR", str(tmp_path / ".venv_t5_latest")
+ )
monkeypatch.setenv("HF_HUB_OFFLINE", "1")
monkeypatch.setattr(
tv,
@@ -551,7 +576,9 @@ class TestLatestVenvProvisioning:
assert ensure_latest_transformers_venv("5.13.0") is False
def test_unpinned_sidecar_never_installs(self, tmp_path: Path, monkeypatch):
- monkeypatch.setattr(tv, "_VENV_T5_LATEST_DIR", str(tmp_path / ".venv_t5_latest"))
+ monkeypatch.setattr(
+ tv, "_VENV_T5_LATEST_DIR", str(tmp_path / ".venv_t5_latest")
+ )
monkeypatch.setattr(
tv,
"_ensure_venv_dir",
@@ -559,7 +586,9 @@ class TestLatestVenvProvisioning:
)
assert tv._ensure_venv_t5_latest_exists() is False
- def test_pinned_sidecar_repairs_with_same_version(self, tmp_path: Path, monkeypatch):
+ def test_pinned_sidecar_repairs_with_same_version(
+ self, tmp_path: Path, monkeypatch
+ ):
venv_dir = tmp_path / ".venv_t5_latest"
venv_dir.mkdir()
(venv_dir / tv._LATEST_PIN_MARKER).write_text("5.13.0")
@@ -603,8 +632,12 @@ class TestLatestTierRouting:
(venv_dir / tv._LATEST_PIN_MARKER).write_text("5.13.0")
assert tv._overlay_transformers_dir("latest") == str(venv_dir / "transformers")
- def test_probe_order_excludes_unprovisioned_latest(self, tmp_path: Path, monkeypatch):
- monkeypatch.setattr(tv, "_VENV_T5_LATEST_DIR", str(tmp_path / ".venv_t5_latest"))
+ def test_probe_order_excludes_unprovisioned_latest(
+ self, tmp_path: Path, monkeypatch
+ ):
+ monkeypatch.setattr(
+ tv, "_VENV_T5_LATEST_DIR", str(tmp_path / ".venv_t5_latest")
+ )
assert tv._probe_tier_order() == tv._PROBE_TIER_ORDER
def test_probe_order_includes_provisioned_latest(self, tmp_path: Path, monkeypatch):
@@ -635,7 +668,9 @@ class TestLatestTierRouting:
os.environ["PYTHONPATH"] = old_pp
def test_activation_raises_when_latest_missing(self, tmp_path: Path, monkeypatch):
- monkeypatch.setattr(tv, "_VENV_T5_LATEST_DIR", str(tmp_path / ".venv_t5_latest"))
+ monkeypatch.setattr(
+ tv, "_VENV_T5_LATEST_DIR", str(tmp_path / ".venv_t5_latest")
+ )
monkeypatch.setattr(tv, "get_transformers_tier", lambda *a, **k: "latest")
with pytest.raises(RuntimeError, match = "venv_t5_latest"):
activate_transformers_for_subprocess("some/brand-new-model")
@@ -669,7 +704,9 @@ class TestInstallLatestTransformers:
monkeypatch.setattr(
tl,
"ensure_latest_transformers_venv",
- lambda v, extra_packages = (): (_ for _ in ()).throw(AssertionError("must not install")),
+ lambda v, extra_packages = (): (_ for _ in ()).throw(
+ AssertionError("must not install")
+ ),
)
result = install_latest_transformers("4.99.0")
assert result["success"] is False and "not the latest" in result["message"]
@@ -703,7 +740,9 @@ class TestInstallLatestTransformers:
monkeypatch.setattr(
tl,
"ensure_latest_transformers_venv",
- lambda v, extra_packages = (): (_ for _ in ()).throw(AssertionError("must not install")),
+ lambda v, extra_packages = (): (_ for _ in ()).throw(
+ AssertionError("must not install")
+ ),
)
result = install_latest_transformers("5.13.0")
assert result["success"] is False and "numpy>=99.0" in result["message"]
@@ -772,12 +811,16 @@ class TestCompatPlan:
assert extras == () and blockers == []
def test_sidecar_provided_hub_checked_against_recipe_pin(self, monkeypatch):
- self._patch_env(monkeypatch, ["huggingface-hub<2.0,>=1.5.0"], {"huggingface-hub": "0.36.2"})
+ self._patch_env(
+ monkeypatch, ["huggingface-hub<2.0,>=1.5.0"], {"huggingface-hub": "0.36.2"}
+ )
extras, blockers = tl.compat_plan("5.13.0")
assert extras == () and blockers == [] # 1.8.0 sidecar pin satisfies it
def test_sidecar_provided_hub_out_of_range_blocks(self, monkeypatch):
- self._patch_env(monkeypatch, ["huggingface-hub>=2.1"], {"huggingface-hub": "0.36.2"})
+ self._patch_env(
+ monkeypatch, ["huggingface-hub>=2.1"], {"huggingface-hub": "0.36.2"}
+ )
extras, blockers = tl.compat_plan("5.99.0")
assert blockers == ["huggingface-hub>=2.1"]
@@ -868,7 +911,9 @@ def test_upgrade_check_ignores_nested_known_types(monkeypatch):
}
monkeypatch.setattr(tl, "_load_config_json", lambda *a, **k: cfg)
calls = []
- monkeypatch.setattr(tl, "latest_transformers_supports", lambda mt: calls.append(mt) or None)
+ monkeypatch.setattr(
+ tl, "latest_transformers_supports", lambda mt: calls.append(mt) or None
+ )
assert tl.check_upgrade_for_model("some-org/normal-vlm") is None
assert calls == []
@@ -934,7 +979,9 @@ def test_install_success_invalidates_capability_caches(monkeypatch):
monkeypatch.setattr("urllib.request.urlopen", _fake_urlopen_factory({}))
monkeypatch.setattr(tl, "compat_plan", lambda v: ((), []))
monkeypatch.setattr(
- tl, "ensure_latest_transformers_venv", lambda v, extra_packages = (), before_swap = None: True
+ tl,
+ "ensure_latest_transformers_venv",
+ lambda v, extra_packages = (), before_swap = None: True,
)
monkeypatch.setattr(tl, "latest_venv_pinned_version", lambda: "5.13.0")
diff --git a/studio/backend/tests/test_transformers_version.py b/studio/backend/tests/test_transformers_version.py
index a6e6803a5c..9ecc13c2f3 100644
--- a/studio/backend/tests/test_transformers_version.py
+++ b/studio/backend/tests/test_transformers_version.py
@@ -179,7 +179,8 @@ class TestRemoteLoraBase:
cfg = {"base_model_name_or_path": "nvidia/NVIDIA-Nemotron-3-Nano-4B"}
with patch("urllib.request.urlopen", return_value = self._resp(cfg)):
assert (
- _remote_lora_base("someuser/my-nemotron-lora") == "nvidia/NVIDIA-Nemotron-3-Nano-4B"
+ _remote_lora_base("someuser/my-nemotron-lora")
+ == "nvidia/NVIDIA-Nemotron-3-Nano-4B"
)
def test_local_or_noncanonical_returns_none(self):
@@ -198,7 +199,9 @@ class TestRemoteLoraBase:
with patch("urllib.request.urlopen", side_effect = fake_urlopen):
assert _remote_lora_base("user/adapter") == "org/base"
- assert seen["url"].startswith("https://hf.mirror.internal/user/adapter/raw/main/")
+ assert seen["url"].startswith(
+ "https://hf.mirror.internal/user/adapter/raw/main/"
+ )
@staticmethod
def _seed_adapter_cache(
@@ -210,7 +213,9 @@ class TestRemoteLoraBase:
repo = hub / ("models--" + repo_id.replace("/", "--"))
snap = repo / "snapshots" / commit
snap.mkdir(parents = True)
- (snap / "adapter_config.json").write_text(json.dumps({"base_model_name_or_path": base}))
+ (snap / "adapter_config.json").write_text(
+ json.dumps({"base_model_name_or_path": base})
+ )
(repo / "refs").mkdir(parents = True)
(repo / "refs" / "main").write_text(commit)
@@ -263,7 +268,9 @@ class TestRemoteLoraBase:
with patch("urllib.request.urlopen", side_effect = err):
assert _remote_lora_base("user/was-a-lora") is None
- def test_transient_http_error_falls_back_to_cache(self, tmp_path: Path, monkeypatch):
+ def test_transient_http_error_falls_back_to_cache(
+ self, tmp_path: Path, monkeypatch
+ ):
import urllib.error
self._seed_adapter_cache(tmp_path, "user/cached-lora", "nvidia/Nemotron-H-8B")
@@ -351,9 +358,13 @@ class TestCheckTokenizerConfigNeedsV5:
monkeypatch.setattr("urllib.request.urlopen", fake_urlopen)
assert _check_tokenizer_config_needs_v5("org/gated") is False # unauth miss
- assert _check_tokenizer_config_needs_v5("org/gated", "tok") is True # authed hit
+ assert (
+ _check_tokenizer_config_needs_v5("org/gated", "tok") is True
+ ) # authed hit
assert seen_auth == [None, "Bearer tok"]
- assert _tokenizer_class_cache[("org/gated", None)] is False # miss not poisoning
+ assert (
+ _tokenizer_class_cache[("org/gated", None)] is False
+ ) # miss not poisoning
# ---------------------------------------------------------------------------
@@ -605,7 +616,10 @@ class TestNemotronHNeedsMlpSupport:
# VL wrapper (e.g. NemotronH_Nano_VL_V2): dense LM is under llm_config.
cfg = {
"model_type": "NemotronH_Nano_VL_V2",
- "llm_config": {"model_type": "nemotron_h", "hybrid_override_pattern": "M-M*-"},
+ "llm_config": {
+ "model_type": "nemotron_h",
+ "hybrid_override_pattern": "M-M*-",
+ },
}
assert _nemotron_h_needs_mlp_support(cfg) is True
assert _config_needs_510(cfg) is True
@@ -613,7 +627,10 @@ class TestNemotronHNeedsMlpSupport:
def test_nested_text_config_with_mlp(self):
cfg = {
"model_type": "wrapper",
- "text_config": {"model_type": "nemotron_h", "layers_block_type": ["mamba", "mlp"]},
+ "text_config": {
+ "model_type": "nemotron_h",
+ "layers_block_type": ["mamba", "mlp"],
+ },
}
assert _nemotron_h_needs_mlp_support(cfg) is True
@@ -623,7 +640,10 @@ class TestNemotronHNeedsMlpSupport:
def test_non_dict_and_missing_nested_do_not_raise(self):
assert _nemotron_h_needs_mlp_support(None) is False
- assert _nemotron_h_needs_mlp_support({"model_type": "wrapper", "llm_config": None}) is False
+ assert (
+ _nemotron_h_needs_mlp_support({"model_type": "wrapper", "llm_config": None})
+ is False
+ )
def _hf_response(cfg: dict):
@@ -679,7 +699,9 @@ class TestConfigJsonHfCacheFallback:
monkeypatch.delenv("HF_HUB_OFFLINE", raising = False)
monkeypatch.delenv("TRANSFORMERS_OFFLINE", raising = False)
with patch("urllib.request.urlopen", return_value = _hf_response(fresh)):
- assert _load_config_json("org/model") == fresh # network wins, not stale cache
+ assert (
+ _load_config_json("org/model") == fresh
+ ) # network wins, not stale cache
def test_network_failure_falls_back_to_cache(self, tmp_path: Path, monkeypatch):
cfg = {"model_type": "nemotron_h", "hybrid_override_pattern": "M-M*-"}
@@ -716,7 +738,9 @@ class TestConfigJsonHfCacheFallback:
monkeypatch.setenv("HF_HUB_CACHE", str(tmp_path))
assert _config_json_from_hf_cache("org/model") == {"model_type": "fresh"}
- def test_transient_failure_does_not_cache_fallback(self, tmp_path: Path, monkeypatch):
+ def test_transient_failure_does_not_cache_fallback(
+ self, tmp_path: Path, monkeypatch
+ ):
stale = {"model_type": "nemotron_h", "hybrid_override_pattern": "MMMM"}
fresh = {"model_type": "nemotron_h", "hybrid_override_pattern": "M-M*-"}
self._seed_cache(tmp_path, "org/model", stale)
@@ -779,9 +803,13 @@ class TestTierCheckTransientRetry:
(repo / "refs").mkdir(parents = True)
(repo / "refs" / "main").write_text(commit)
- def test_transient_fallback_not_memoized_then_retries(self, tmp_path: Path, monkeypatch):
+ def test_transient_fallback_not_memoized_then_retries(
+ self, tmp_path: Path, monkeypatch
+ ):
stale = {"model_type": "llama"} # does not need 510
- fresh = {"architectures": ["Gemma4UnifiedForConditionalGeneration"]} # needs 510
+ fresh = {
+ "architectures": ["Gemma4UnifiedForConditionalGeneration"]
+ } # needs 510
self._seed_cache(tmp_path, "org/model", stale)
monkeypatch.setenv("HF_HUB_CACHE", str(tmp_path))
monkeypatch.delenv("HF_HUB_OFFLINE", raising = False)
@@ -792,13 +820,17 @@ class TestTierCheckTransientRetry:
# Connectivity returns: the next call re-fetches and sees the higher tier.
with patch("urllib.request.urlopen", return_value = _hf_response(fresh)):
assert _check_config_needs_510("org/model") is True
- assert _config_needs_510_cache[("org/model", None)] is True # definitive read memoized
+ assert (
+ _config_needs_510_cache[("org/model", None)] is True
+ ) # definitive read memoized
def test_definitive_network_read_is_memoized(self, tmp_path: Path, monkeypatch):
fresh = {"architectures": ["Gemma4ForConditionalGeneration"]} # needs 550
monkeypatch.setenv("HF_HUB_CACHE", str(tmp_path))
monkeypatch.delenv("HF_HUB_OFFLINE", raising = False)
- with patch("urllib.request.urlopen", return_value = _hf_response(fresh)) as mock_url:
+ with patch(
+ "urllib.request.urlopen", return_value = _hf_response(fresh)
+ ) as mock_url:
assert _check_config_needs_550("org/model") is True
assert _check_config_needs_550("org/model") is True
assert mock_url.call_count == 1 # second call served from the tier cache
@@ -972,7 +1004,9 @@ class TestGetTransformersTier:
return_value = False,
),
):
- assert get_transformers_tier("mistralai/Ministral-3-8B-Instruct-2512") == "530"
+ assert (
+ get_transformers_tier("mistralai/Ministral-3-8B-Instruct-2512") == "530"
+ )
def test_llama_returns_default(self):
with (
@@ -1036,12 +1070,17 @@ class TestGetTransformersTier:
assert "default" in text, f"tier selection not logged: {text!r}"
def test_local_config_json_selection_is_logged(self, tmp_path: Path, caplog):
- cfg = {"architectures": ["Gemma4ForConditionalGeneration"], "model_type": "gemma4"}
+ cfg = {
+ "architectures": ["Gemma4ForConditionalGeneration"],
+ "model_type": "gemma4",
+ }
(tmp_path / "config.json").write_text(json.dumps(cfg))
caplog.set_level(logging.INFO)
assert get_transformers_tier(str(tmp_path)) == "550"
text = " ".join(r.getMessage() for r in caplog.records).lower()
- assert "550" in text and "local config.json" in text, f"local tier not logged: {text!r}"
+ assert (
+ "550" in text and "local config.json" in text
+ ), f"local tier not logged: {text!r}"
def test_needs_transformers_5_compat(self):
"""needs_transformers_5 should return True for 510, 530, and 550 models."""
@@ -1147,7 +1186,9 @@ class TestProbeTier:
monkeypatch.delenv("UNSLOTH_DISABLE_TIER_PROBE", raising = False)
for fn in ("_ensure_venv_t5_530_exists", "_ensure_venv_t5_550_exists"):
monkeypatch.setattr(f"utils.transformers_version.{fn}", lambda: True)
- monkeypatch.setattr("utils.transformers_version._ensure_venv_t5_510_exists", lambda: False)
+ monkeypatch.setattr(
+ "utils.transformers_version._ensure_venv_t5_510_exists", lambda: False
+ )
monkeypatch.setattr(
"utils.transformers_version.subprocess.run",
lambda cmd, **k: _proc(1, "KeyError: '-'"),
@@ -1159,16 +1200,22 @@ class TestProbeTier:
# 530 sidecar unavailable but 550 parses: return 550 (best effort now) but do NOT
# cache it, since once 530 is installed it may be the lowest valid tier.
monkeypatch.delenv("UNSLOTH_DISABLE_TIER_PROBE", raising = False)
- monkeypatch.setattr("utils.transformers_version._ensure_venv_t5_530_exists", lambda: False)
+ monkeypatch.setattr(
+ "utils.transformers_version._ensure_venv_t5_530_exists", lambda: False
+ )
for fn in ("_ensure_venv_t5_550_exists", "_ensure_venv_t5_510_exists"):
monkeypatch.setattr(f"utils.transformers_version.{fn}", lambda: True)
- monkeypatch.setattr("utils.transformers_version.subprocess.run", lambda cmd, **k: _proc(0))
+ monkeypatch.setattr(
+ "utils.transformers_version.subprocess.run", lambda cmd, **k: _proc(0)
+ )
assert _probe_tier("org/m", None, "x") == "550"
assert "org/m" not in _probe_tier_cache # skipped a lower tier -> not pinned
def test_cache_hit_skips_subprocess(self, monkeypatch):
self._patch_common(monkeypatch)
- monkeypatch.setattr("utils.transformers_version.subprocess.run", lambda cmd, **k: _proc(0))
+ monkeypatch.setattr(
+ "utils.transformers_version.subprocess.run", lambda cmd, **k: _proc(0)
+ )
assert _probe_tier("org/m", None, "x") == "530"
def boom(cmd, **k):
@@ -1217,7 +1264,9 @@ class TestProbeTier:
# The probe must not import huggingface_hub: that would land before the sidecar is on
# sys.path (activation never purges), pinning the default-env hub. So no in-process sha.
self._patch_common(monkeypatch)
- monkeypatch.setattr("utils.transformers_version.subprocess.run", lambda cmd, **k: _proc(0))
+ monkeypatch.setattr(
+ "utils.transformers_version.subprocess.run", lambda cmd, **k: _proc(0)
+ )
sys.modules.pop("huggingface_hub", None)
_probe_tier("org/m", None, "x")
assert "huggingface_hub" not in sys.modules
@@ -1234,15 +1283,20 @@ class TestProbeTier:
def test_get_tier_uses_probe_for_remote_tokenizer_signal(self, monkeypatch):
# tokenizer says 5.x but no architecture/substring match -> probe (not a 530 guess).
monkeypatch.setattr(
- "utils.transformers_version._check_config_needs_510", lambda m, t = None: False
+ "utils.transformers_version._check_config_needs_510",
+ lambda m, t = None: False,
)
monkeypatch.setattr(
- "utils.transformers_version._check_config_needs_550", lambda m, t = None: False
+ "utils.transformers_version._check_config_needs_550",
+ lambda m, t = None: False,
)
monkeypatch.setattr(
- "utils.transformers_version._check_tokenizer_config_needs_v5", lambda m, t = None: True
+ "utils.transformers_version._check_tokenizer_config_needs_v5",
+ lambda m, t = None: True,
+ )
+ monkeypatch.setattr(
+ "utils.transformers_version._probe_tier", lambda m, t, reason: "510"
)
- monkeypatch.setattr("utils.transformers_version._probe_tier", lambda m, t, reason: "510")
assert get_transformers_tier("org/unknown-5x-arch") == "510"
def test_stderr_is_transient(self):
@@ -1272,13 +1326,20 @@ class TestProbeTier:
lambda m, t, reason: seen.update({"probe": t}) or "510",
)
assert get_transformers_tier("org/gated-5x", "hf_abc") == "510"
- assert seen == {"510": "hf_abc", "550": "hf_abc", "tok": "hf_abc", "probe": "hf_abc"}
+ assert seen == {
+ "510": "hf_abc",
+ "550": "hf_abc",
+ "tok": "hf_abc",
+ "probe": "hf_abc",
+ }
def test_activate_threads_token_to_tier(self, monkeypatch):
# activate_transformers_for_subprocess must forward hf_token to tier detection, or
# the gated-model checks above run unauthenticated and the fix is unreachable.
seen = {}
- monkeypatch.setattr("utils.transformers_version._resolve_base_model", lambda m: m)
+ monkeypatch.setattr(
+ "utils.transformers_version._resolve_base_model", lambda m: m
+ )
monkeypatch.setattr(
"utils.transformers_version.get_transformers_tier",
lambda m, t = None: seen.update({"model": m, "token": t}) or "default",
@@ -1345,13 +1406,16 @@ class TestProbeGating:
def _patch_checks_to_tokenizer(self, monkeypatch):
monkeypatch.setattr(
- "utils.transformers_version._check_config_needs_510", lambda m, t = None: False
+ "utils.transformers_version._check_config_needs_510",
+ lambda m, t = None: False,
)
monkeypatch.setattr(
- "utils.transformers_version._check_config_needs_550", lambda m, t = None: False
+ "utils.transformers_version._check_config_needs_550",
+ lambda m, t = None: False,
)
monkeypatch.setattr(
- "utils.transformers_version._check_tokenizer_config_needs_v5", lambda m, t = None: True
+ "utils.transformers_version._check_tokenizer_config_needs_v5",
+ lambda m, t = None: True,
)
# ---- needs_transformers_5 / probe=False must not spawn probes --------------
@@ -1380,7 +1444,8 @@ class TestProbeGating:
def test_version_field_probe_stays_default_when_default_parses(self, monkeypatch):
self._patch_venvs(monkeypatch)
monkeypatch.setattr(
- "utils.transformers_version._check_tokenizer_config_needs_v5", lambda m, t = None: False
+ "utils.transformers_version._check_tokenizer_config_needs_v5",
+ lambda m, t = None: False,
)
_config_json_cache[("org/new", None)] = {
"model_type": "brandnew",
@@ -1392,14 +1457,17 @@ class TestProbeGating:
lambda cmd, **k: seen.append(cmd[3]) or _proc(0),
)
assert get_transformers_tier("org/new") == "default"
- assert seen == [""] # probed the ambient default tier first, it parsed -> stayed default
+ assert seen == [
+ ""
+ ] # probed the ambient default tier first, it parsed -> stayed default
def test_version_field_probe_escalates_when_default_fails(self, monkeypatch):
import utils.transformers_version as tv
self._patch_venvs(monkeypatch)
monkeypatch.setattr(
- "utils.transformers_version._check_tokenizer_config_needs_v5", lambda m, t = None: False
+ "utils.transformers_version._check_tokenizer_config_needs_v5",
+ lambda m, t = None: False,
)
_config_json_cache[("org/new", None)] = {
"model_type": "brandnew",
@@ -1417,7 +1485,8 @@ class TestProbeGating:
def test_ordinary_4x_config_does_not_probe(self, monkeypatch):
self._patch_venvs(monkeypatch)
monkeypatch.setattr(
- "utils.transformers_version._check_tokenizer_config_needs_v5", lambda m, t = None: False
+ "utils.transformers_version._check_tokenizer_config_needs_v5",
+ lambda m, t = None: False,
)
_config_json_cache[("org/llama", None)] = {
"model_type": "llama",
@@ -1434,13 +1503,16 @@ class TestProbeGating:
# A 5.x-saved standard-tokenizer model must report as 5.x (for vision routing)
# without spawning a probe.
monkeypatch.setattr(
- "utils.transformers_version._check_config_needs_510", lambda m, t = None: False
+ "utils.transformers_version._check_config_needs_510",
+ lambda m, t = None: False,
)
monkeypatch.setattr(
- "utils.transformers_version._check_config_needs_550", lambda m, t = None: False
+ "utils.transformers_version._check_config_needs_550",
+ lambda m, t = None: False,
)
monkeypatch.setattr(
- "utils.transformers_version._check_tokenizer_config_needs_v5", lambda m, t = None: False
+ "utils.transformers_version._check_tokenizer_config_needs_v5",
+ lambda m, t = None: False,
)
_config_json_cache[("org/new", None)] = {
"model_type": "brandnew",
@@ -1453,7 +1525,9 @@ class TestProbeGating:
monkeypatch.setattr("utils.transformers_version.subprocess.run", boom)
assert needs_transformers_5("org/new") is True
- def test_default_first_result_not_reused_for_tokenizer_path(self, monkeypatch, tmp_path):
+ def test_default_first_result_not_reused_for_tokenizer_path(
+ self, monkeypatch, tmp_path
+ ):
# A default-first probe can cache "default"; a later tokenizer/known-5.x call
# (floor=530) must re-probe, not reuse that "default".
self._patch_venvs(monkeypatch)
@@ -1461,9 +1535,12 @@ class TestProbeGating:
json.dumps({"model_type": "brandnew", "transformers_version": "5.0.0"})
)
local = str(tmp_path)
- monkeypatch.setattr("utils.transformers_version.subprocess.run", lambda cmd, **k: _proc(0))
+ monkeypatch.setattr(
+ "utils.transformers_version.subprocess.run", lambda cmd, **k: _proc(0)
+ )
assert (
- _probe_tier(local, None, "version", include_default = True, floor = "default") == "default"
+ _probe_tier(local, None, "version", include_default = True, floor = "default")
+ == "default"
)
seen = []
monkeypatch.setattr(
@@ -1472,7 +1549,9 @@ class TestProbeGating:
)
# Tokenizer/known-5.x mode (floor=530): must re-probe and never reuse "default".
assert _probe_tier(local, None, "tokenizer needs 5.x") == "530"
- assert seen, "tokenizer path reused the cached default result instead of re-probing"
+ assert (
+ seen
+ ), "tokenizer path reused the cached default result instead of re-probing"
class TestLocalCheckpointFilesAppear:
@@ -1484,7 +1563,9 @@ class TestLocalCheckpointFilesAppear:
_tokenizer_class_cache.clear()
_config_json_cache.clear()
- def test_tokenizer_config_appearing_later_is_read(self, tmp_path: Path, monkeypatch):
+ def test_tokenizer_config_appearing_later_is_read(
+ self, tmp_path: Path, monkeypatch
+ ):
local = str(tmp_path)
def boom(*a, **k):
@@ -1589,7 +1670,9 @@ class TestActivateLoggingClarity:
"sys.path" in text or "path only" in text
), f"early activation log does not clarify it is path-prepend only: {text!r}"
- def test_activate_prefers_local_checkpoint_tier_over_resolved_base(self, caplog, tmp_path):
+ def test_activate_prefers_local_checkpoint_tier_over_resolved_base(
+ self, caplog, tmp_path
+ ):
# Base resolves to an offline/private id (default tier); the local config.json wins.
(tmp_path / "config.json").write_text(json.dumps({"model_type": "llama"}))
local = str(tmp_path)
@@ -1618,7 +1701,9 @@ class TestActivateLoggingClarity:
text = " ".join(r.getMessage() for r in caplog.records).lower()
assert "5.10.2" in text, f"local checkpoint tier did not win: {text!r}"
- def test_activate_adapter_without_config_skips_path_name_recheck(self, caplog, tmp_path):
+ def test_activate_adapter_without_config_skips_path_name_recheck(
+ self, caplog, tmp_path
+ ):
# LoRA adapter in a dir named 'gemma-4' (base resolves elsewhere): the resolved
# base drives the tier; the path name must not re-check or upgrade it.
adapter = tmp_path / "gemma-4-experiment" / "llama-lora"
@@ -1650,7 +1735,9 @@ class TestActivateLoggingClarity:
finally:
self._restore_env(snap)
- assert seen == ["meta/llama"], f"adapter path was re-checked via substrings: {seen!r}"
+ assert seen == [
+ "meta/llama"
+ ], f"adapter path was re-checked via substrings: {seen!r}"
text = " ".join(r.getMessage() for r in caplog.records).lower()
assert "default transformers" in text, f"adapter wrongly upgraded: {text!r}"
@@ -1832,7 +1919,10 @@ class TestLocalConfig530Tier:
assert _config_needs_530({"model_type": "qwen3_5"}) is True
def test_config_needs_530_qwen3_5_conditional_generation(self):
- assert _config_needs_530({"architectures": ["Qwen3_5ForConditionalGeneration"]}) is True
+ assert (
+ _config_needs_530({"architectures": ["Qwen3_5ForConditionalGeneration"]})
+ is True
+ )
def test_config_needs_530_qwen3_moe(self):
assert _config_needs_530({"model_type": "qwen3_moe"}) is True
@@ -1884,7 +1974,9 @@ class TestLocalConfig530Tier:
d = tmp_path / "my-qwen3-moe"
d.mkdir()
(d / "config.json").write_text(
- json.dumps({"model_type": "qwen3_moe", "architectures": ["Qwen3MoeForCausalLM"]})
+ json.dumps(
+ {"model_type": "qwen3_moe", "architectures": ["Qwen3MoeForCausalLM"]}
+ )
)
assert get_transformers_tier(str(d)) == "530"
@@ -1893,7 +1985,12 @@ class TestLocalConfig530Tier:
d = tmp_path / "my-glm-model"
d.mkdir()
(d / "config.json").write_text(
- json.dumps({"model_type": "glm4_moe_lite", "architectures": ["Glm4MoeLiteForCausalLM"]})
+ json.dumps(
+ {
+ "model_type": "glm4_moe_lite",
+ "architectures": ["Glm4MoeLiteForCausalLM"],
+ }
+ )
)
assert get_transformers_tier(str(d)) == "530"
@@ -1903,7 +2000,10 @@ class TestLocalConfig530Tier:
d.mkdir()
(d / "config.json").write_text(
json.dumps(
- {"model_type": "lfm2_vl", "architectures": ["Lfm2VlForConditionalGeneration"]}
+ {
+ "model_type": "lfm2_vl",
+ "architectures": ["Lfm2VlForConditionalGeneration"],
+ }
)
)
assert get_transformers_tier(str(d)) == "530"
@@ -1930,7 +2030,10 @@ class TestLocalConfig530Tier:
d.mkdir()
(d / "config.json").write_text(
json.dumps(
- {"model_type": "qwen3_5", "architectures": ["Qwen3_5ForConditionalGeneration"]}
+ {
+ "model_type": "qwen3_5",
+ "architectures": ["Qwen3_5ForConditionalGeneration"],
+ }
)
)
assert get_transformers_tier(str(d)) == "550"
@@ -1955,10 +2058,13 @@ class TestLocalConfig530Tier:
d = tmp_path / "my-llama-ckpt"
d.mkdir()
(d / "config.json").write_text(
- json.dumps({"model_type": "llama", "_name_or_path": "/old/run/qwen3.5-source"})
+ json.dumps(
+ {"model_type": "llama", "_name_or_path": "/old/run/qwen3.5-source"}
+ )
)
with patch(
- "utils.transformers_version._check_tokenizer_config_needs_v5", return_value = False
+ "utils.transformers_version._check_tokenizer_config_needs_v5",
+ return_value = False,
):
assert get_transformers_tier(str(d)) == "default"
@@ -2009,13 +2115,16 @@ class TestLocalConfig530Tier:
)
)
with patch(
- "utils.transformers_version._check_tokenizer_config_needs_v5", return_value = False
+ "utils.transformers_version._check_tokenizer_config_needs_v5",
+ return_value = False,
):
# "qwen3.5" is in the path but config says llama and _name_or_path
# is self-referencing — must not be promoted to 530.
assert get_transformers_tier(str(d)) == "default"
- def test_hf_id_fallback_not_triggered_when_name_or_path_is_absolute_self(self, tmp_path: Path):
+ def test_hf_id_fallback_not_triggered_when_name_or_path_is_absolute_self(
+ self, tmp_path: Path
+ ):
"""_name_or_path == absolute path of the same checkpoint while model_name
is a relative path: the two strings differ, but both point to the same
directory. The absolute path must not be scanned for tier substrings."""
@@ -2031,7 +2140,8 @@ class TestLocalConfig530Tier:
)
)
with patch(
- "utils.transformers_version._check_tokenizer_config_needs_v5", return_value = False
+ "utils.transformers_version._check_tokenizer_config_needs_v5",
+ return_value = False,
):
# Even though str(d) contains "qwen3.5", the local-dir branch recurses
# into config checks on the resolved path, which returns default.
@@ -2204,7 +2314,9 @@ class TestResolveBaseModelNameOrPathFallback:
# model_name is not the local path, so it wins
assert _resolve_base_model(str(d)) == "unsloth/Qwen3.5-7B-bnb-4bit"
- def test_tier_resolved_via_name_or_path_when_model_name_self_refs(self, tmp_path: Path):
+ def test_tier_resolved_via_name_or_path_when_model_name_self_refs(
+ self, tmp_path: Path
+ ):
"""End-to-end: get_transformers_tier picks up the sidecar tier from
_name_or_path even when model_name is set to the checkpoint's own path."""
d = tmp_path / "my-custom-finetune"
@@ -2220,7 +2332,9 @@ class TestResolveBaseModelNameOrPathFallback:
)
assert get_transformers_tier(str(d)) == "530"
- def test_local_config_tier_not_bypassed_by_private_name_or_path(self, tmp_path: Path):
+ def test_local_config_tier_not_bypassed_by_private_name_or_path(
+ self, tmp_path: Path
+ ):
"""Full checkpoint with model_type: qwen3_5 must still route to 530 even
when _name_or_path is a private HF ID with no recognisable tier substring.
@@ -2419,7 +2533,8 @@ class TestMalformedInputRobustness:
json.dumps({"model_type": ["qwen3_5"], "_name_or_path": {"x": 1}})
)
with patch(
- "utils.transformers_version._check_tokenizer_config_needs_v5", return_value = False
+ "utils.transformers_version._check_tokenizer_config_needs_v5",
+ return_value = False,
):
assert get_transformers_tier(str(d)) == "default"
@@ -2514,7 +2629,9 @@ class TestHfEndpointUnreachable:
import urllib.error
def _405(*a, **k):
- raise urllib.error.HTTPError("http://x", 405, "Method Not Allowed", {}, None)
+ raise urllib.error.HTTPError(
+ "http://x", 405, "Method Not Allowed", {}, None
+ )
monkeypatch.setattr("urllib.request.urlopen", _405)
assert hf_endpoint_unreachable(timeout = 2) is False
@@ -2535,7 +2652,9 @@ class TestHfEndpointUnreachable:
import urllib.error
def _dns(*a, **k):
- raise urllib.error.URLError(socket.gaierror(-2, "Name or service not known"))
+ raise urllib.error.URLError(
+ socket.gaierror(-2, "Name or service not known")
+ )
monkeypatch.setattr("urllib.request.urlopen", _dns)
assert hf_endpoint_unreachable(timeout = 2) is True
@@ -2575,7 +2694,9 @@ class TestLatestTierActiveFor:
import utils.transformers_version as tv
self._pin(monkeypatch, tv)
for tier in ("default", "530", "550", "510"):
- monkeypatch.setattr(tv, "get_transformers_tier", lambda *a, _t = tier, **k: _t)
+ monkeypatch.setattr(
+ tv, "get_transformers_tier", lambda *a, _t = tier, **k: _t
+ )
assert tv.latest_tier_active_for("some/model") is False
def test_false_without_pin_and_no_resolution(self, monkeypatch):
@@ -2605,10 +2726,14 @@ class TestLatestTierActiveFor:
import utils.transformers_version as tv
monkeypatch.setattr(tv, "latest_venv_pinned_version", lambda: "5.13.1")
- monkeypatch.setattr(tv, "_remote_lora_base", lambda name, hf_token = None: "Zyphra/ZAYA1-8B")
+ monkeypatch.setattr(
+ tv, "_remote_lora_base", lambda name, hf_token = None: "Zyphra/ZAYA1-8B"
+ )
tiers = {"Zyphra/ZAYA1-8B": "latest"}
monkeypatch.setattr(
- tv, "get_transformers_tier", lambda name, *a, **k: tiers.get(name, "default")
+ tv,
+ "get_transformers_tier",
+ lambda name, *a, **k: tiers.get(name, "default"),
)
assert tv.latest_tier_active_for("someuser/zaya-lora") is True
@@ -2625,7 +2750,9 @@ class TestLatestTierActiveFor:
monkeypatch.setattr(tv, "_resolve_base_model", lambda name: "base/model")
tiers = {"base/model": "default", str(adapter): "latest"}
monkeypatch.setattr(
- tv, "get_transformers_tier", lambda name, *a, **k: tiers.get(name, "default")
+ tv,
+ "get_transformers_tier",
+ lambda name, *a, **k: tiers.get(name, "default"),
)
assert tv.latest_tier_active_for(str(adapter)) is True
@@ -2739,12 +2866,16 @@ class TestLatestTierForces16Bit:
def test_start_routes_refuse_during_install(self):
# A worker spawned mid-swap could activate a half-replaced sidecar.
training = self._read("routes/training.py")
- start = training.split("async def start_training", 1)[1].split("\nasync def ", 1)[0]
+ start = training.split("async def start_training", 1)[1].split(
+ "\nasync def ", 1
+ )[0]
assert (
"is_install_in_progress" in start
), "training /start must refuse while a transformers install is in progress"
export = self._read("routes/export.py")
- helper = export.split("def _ensure_export_supported", 1)[1].split("\ndef ", 1)[0]
+ helper = export.split("def _ensure_export_supported", 1)[1].split("\ndef ", 1)[
+ 0
+ ]
assert (
"is_install_in_progress" in helper
), "mutating export routes must refuse while a transformers install is in progress"
@@ -2766,15 +2897,21 @@ class TestLatestTierForces16Bit:
assert training.index("self._spawn_in_progress = True") < training.index(
"if sidecar_swap_in_progress():"
)
- active = training.split("def is_training_active", 1)[1].split("\n def ", 1)[0]
+ active = training.split("def is_training_active", 1)[1].split("\n def ", 1)[
+ 0
+ ]
assert "_spawn_in_progress" in active
# Export load-checkpoint refuses BEFORE tearing down the old worker, so a
# lost race against an install keeps the loaded checkpoint (no bare 500).
loadck = export.split("def load_checkpoint", 1)[1].split("\n def ", 1)[0]
- assert loadck.index("sidecar_swap_in_progress()") < loadck.index("_shutdown_subprocess()")
+ assert loadck.index("sidecar_swap_in_progress()") < loadck.index(
+ "_shutdown_subprocess()"
+ )
# The training handshake precedes the VRAM-freeing before_spawn hook, so
# losing the race never tears down chat/export for a run that won't spawn.
- assert training.index("self._spawn_in_progress = True") < training.index("before_spawn()")
+ assert training.index("self._spawn_in_progress = True") < training.index(
+ "before_spawn()"
+ )
# The spawn-time export check is op-aware for installs (the install side
# aborts on is_export_active) but always refuses for repairs, which have
# no such abort and can be rebuilding the sidecar right now.
@@ -2876,7 +3013,9 @@ class TestSidecarSwapReservation:
os.utime(lock, (old_ts, old_ts))
assert tv.sidecar_swap_in_progress() is False
- def test_repair_refused_while_install_holds_reservation(self, monkeypatch, tmp_path):
+ def test_repair_refused_while_install_holds_reservation(
+ self, monkeypatch, tmp_path
+ ):
tv = self._repair_setup(monkeypatch, tmp_path)
def _must_not_run(*a, **k):
@@ -2938,7 +3077,9 @@ class TestCachedLatestMappingRevalidated:
def test_broken_sidecar_drops_cached_latest_mapping(self, monkeypatch):
import utils.transformers_version as tv
- monkeypatch.setattr(tv, "_config_mapping_cache", {"latest": frozenset({"brandnew"})})
+ monkeypatch.setattr(
+ tv, "_config_mapping_cache", {"latest": frozenset({"brandnew"})}
+ )
monkeypatch.setattr(tv, "_latest_sidecar_intact", lambda: False)
seen = {"n": 0}
@@ -2954,12 +3095,16 @@ class TestCachedLatestMappingRevalidated:
def test_intact_sidecar_serves_cached_latest_mapping(self, monkeypatch):
import utils.transformers_version as tv
- monkeypatch.setattr(tv, "_config_mapping_cache", {"latest": frozenset({"brandnew"})})
+ monkeypatch.setattr(
+ tv, "_config_mapping_cache", {"latest": frozenset({"brandnew"})}
+ )
monkeypatch.setattr(tv, "_latest_sidecar_intact", lambda: True)
monkeypatch.setattr(
tv,
"_overlay_transformers_dir",
- lambda tier: pytest.fail("intact sidecar must serve the cache without re-resolving"),
+ lambda tier: pytest.fail(
+ "intact sidecar must serve the cache without re-resolving"
+ ),
)
assert tv._config_model_types("latest") == frozenset({"brandnew"})
@@ -2970,7 +3115,9 @@ class TestCachedLatestMappingRevalidated:
monkeypatch.setattr(
tv,
"_latest_sidecar_intact",
- lambda: pytest.fail("non-latest tiers must not pay the sidecar-intact check"),
+ lambda: pytest.fail(
+ "non-latest tiers must not pay the sidecar-intact check"
+ ),
)
assert tv._config_model_types("530") == frozenset({"gemma3"})
@@ -2982,7 +3129,9 @@ class TestCachedLatestMappingRevalidated:
monkeypatch.setattr(tv, "_VENV_T5_LATEST_DIR", str(tmp_path / "venv_t5_latest"))
monkeypatch.setattr(tv, "_latest_tier_disabled", lambda: False)
- monkeypatch.setattr(tv, "_config_mapping_cache", {"latest": frozenset({"brandnew"})})
+ monkeypatch.setattr(
+ tv, "_config_mapping_cache", {"latest": frozenset({"brandnew"})}
+ )
# No pin marker on disk -> _latest_pin_data() is None -> not intact.
assert tv._latest_sidecar_intact() is False
assert tv._config_model_types("latest") == frozenset()
@@ -3005,7 +3154,10 @@ class TestOverlayRepairsIncompleteSidecar:
monkeypatch.setattr(
tv,
"_latest_pin_data",
- lambda: {"version": "5.99.0", "packages": ["transformers==5.99.0", "tiktoken"]},
+ lambda: {
+ "version": "5.99.0",
+ "packages": ["transformers==5.99.0", "tiktoken"],
+ },
)
monkeypatch.setattr(tv, "_venv_dir_is_valid", lambda d, p: valid)
monkeypatch.setattr(tv, "_latest_repair_failed_at", 0.0)
@@ -3139,7 +3291,9 @@ class TestRaiseTierForNested:
def test_nested_latest_only_type_raises(self, monkeypatch):
import utils.transformers_version as tv
- self._patch_types(monkeypatch, {"550": {"gemma4"}, "latest": {"gemma4", "brandnew_arch"}})
+ self._patch_types(
+ monkeypatch, {"550": {"gemma4"}, "latest": {"gemma4", "brandnew_arch"}}
+ )
cfg = {"model_type": "gemma4", "text_config": {"model_type": "brandnew_arch"}}
assert tv._raise_tier_for_nested(cfg, "550") == "latest"
@@ -3170,7 +3324,9 @@ class TestRaiseTierForNested:
self._patch_types(monkeypatch, {"550": {"gemma4"}, "latest": {"brandnew_arch"}})
monkeypatch.setattr(tv, "_tier_from_name", lambda name: ("550", "gemma-4"))
monkeypatch.setattr(
- tv, "_load_config_json", lambda name, tok = None: {"model_type": "brandnew_arch"}
+ tv,
+ "_load_config_json",
+ lambda name, tok = None: {"model_type": "brandnew_arch"},
)
monkeypatch.setattr(tv, "latest_venv_pinned_version", lambda: "5.99.0")
assert tv.get_transformers_tier("org/gemma-4-new", probe = False) == "latest"
@@ -3178,7 +3334,9 @@ class TestRaiseTierForNested:
monkeypatch.setattr(
tv,
"_load_config_json",
- lambda name, tok = None: (_ for _ in ()).throw(AssertionError("no I/O without a pin")),
+ lambda name, tok = None: (_ for _ in ()).throw(
+ AssertionError("no I/O without a pin")
+ ),
)
assert tv.get_transformers_tier("org/gemma-4-new", probe = False) == "550"
@@ -3190,9 +3348,13 @@ class TestRaiseTierForNested:
ckpt = tmp_path / "wrapper"
ckpt.mkdir()
(ckpt / "config.json").write_text(
- json.dumps({"model_type": "gemma4", "text_config": {"model_type": "brandnew_arch"}})
+ json.dumps(
+ {"model_type": "gemma4", "text_config": {"model_type": "brandnew_arch"}}
+ )
+ )
+ self._patch_types(
+ monkeypatch, {"550": {"gemma4"}, "latest": {"gemma4", "brandnew_arch"}}
)
- self._patch_types(monkeypatch, {"550": {"gemma4"}, "latest": {"gemma4", "brandnew_arch"}})
monkeypatch.setattr(tv, "_config_needs_510", lambda cfg: False)
monkeypatch.setattr(tv, "_config_needs_550", lambda cfg: True)
assert tv.get_transformers_tier(str(ckpt), probe = False) == "latest"
diff --git a/studio/backend/tests/test_trc_approval_cache.py b/studio/backend/tests/test_trc_approval_cache.py
index f4a85fee5d..ddd177e076 100644
--- a/studio/backend/tests/test_trc_approval_cache.py
+++ b/studio/backend/tests/test_trc_approval_cache.py
@@ -22,7 +22,9 @@ _HIGH = {
)
}
_HIGH2 = { # a different HIGH payload -> different fingerprint
- "modeling_persist.py": ("open('/etc/cron.d/x', 'w').write('* * * * * root sh -c id')\n")
+ "modeling_persist.py": (
+ "open('/etc/cron.d/x', 'w').write('* * * * * root sh -c id')\n"
+ )
}
_CRITICAL = {
"modeling_evil.py": (
@@ -93,7 +95,12 @@ def _approve(
def test_store_roundtrip_and_forget():
approvals.record(
- "u", "k", commit_sha = "s", fingerprint = "f", max_severity = "HIGH", scanner_version = 1
+ "u",
+ "k",
+ commit_sha = "s",
+ fingerprint = "f",
+ max_severity = "HIGH",
+ scanner_version = 1,
)
got = approvals.lookup("u", "k")
assert got is not None and got.fingerprint == "f" and got.scanner_version == 1
@@ -115,7 +122,9 @@ def test_concurrent_records_do_not_lose_entries():
import threading
def rec(i):
- approvals.record("u", f"k{i}", commit_sha = "s", fingerprint = f"f{i}", max_severity = "HIGH")
+ approvals.record(
+ "u", f"k{i}", commit_sha = "s", fingerprint = f"f{i}", max_severity = "HIGH"
+ )
threads = [threading.Thread(target = rec, args = (i,)) for i in range(20)]
for t in threads:
@@ -128,7 +137,9 @@ def test_concurrent_records_do_not_lose_entries():
def test_combined_sha_none_when_any_unresolvable(monkeypatch):
monkeypatch.setattr(
- approvals, "resolve_commit_sha", lambda t, hf = None: None if t == "org/base" else "s"
+ approvals,
+ "resolve_commit_sha",
+ lambda t, hf = None: None if t == "org/base" else "s",
)
assert approvals.resolve_combined_sha(["org/a", "org/base"]) is None
assert approvals.resolve_combined_sha(["org/a"]) is not None
@@ -155,7 +166,10 @@ def test_malformed_store_shape_fails_safe():
# never crash lookup/record/forget.
store = approvals._store_path()
store.parent.mkdir(parents = True, exist_ok = True)
- for bad in ('{"version": 1, "subjects": []}', '{"version": 1, "subjects": {"u": []}}'):
+ for bad in (
+ '{"version": 1, "subjects": []}',
+ '{"version": 1, "subjects": {"u": []}}',
+ ):
store.write_text(bad)
assert approvals.lookup("u", "k") is None # no raise
approvals.forget("u", "k") # no raise
@@ -176,7 +190,9 @@ def test_cache_miss_prompts(monkeypatch):
def test_unchanged_repo_skips_prompt_but_still_scans(monkeypatch):
st, _ = _approve(monkeypatch)
before = st["scans"]
- d = _gate("org/m") # SHA + fingerprint match -> auto-approve, but the scan still runs
+ d = _gate(
+ "org/m"
+ ) # SHA + fingerprint match -> auto-approve, but the scan still runs
assert d.blocked is False and d.reason == "approved by fingerprint"
assert st["scans"] == before + 1 # cache never skips the scan
@@ -184,7 +200,9 @@ def test_unchanged_repo_skips_prompt_but_still_scans(monkeypatch):
def test_sha_moved_forces_reprompt(monkeypatch):
_approve(monkeypatch, sha = "sha1")
monkeypatch.setattr(approvals, "resolve_commit_sha", lambda t, hf = None: "sha2")
- d = _gate("org/m") # SHA moved -> seed withheld -> re-prompt even though code is identical
+ d = _gate(
+ "org/m"
+ ) # SHA moved -> seed withheld -> re-prompt even though code is identical
assert d.blocked is True
@@ -200,7 +218,9 @@ def test_changed_code_same_sha_reprompts(monkeypatch):
# Even with the primary SHA unchanged, changed executable code (e.g. an external
# auto_map repo) changes the fingerprint, so the dialog returns.
_approve(monkeypatch, files = _HIGH, sha = "sha1")
- monkeypatch.setattr(consent, "repo_remote_code_files", lambda t, hf_token = None: dict(_HIGH2))
+ monkeypatch.setattr(
+ consent, "repo_remote_code_files", lambda t, hf_token = None: dict(_HIGH2)
+ )
d = _gate("org/m")
assert d.blocked is True
@@ -281,7 +301,9 @@ def test_disable_flag_bypasses_cache(monkeypatch):
def test_subject_isolation(monkeypatch):
_approve(monkeypatch, subject = "user-a")
- assert _gate("org/m", subject = "user-a").blocked is False # a: seeded -> auto-approve
+ assert (
+ _gate("org/m", subject = "user-a").blocked is False
+ ) # a: seeded -> auto-approve
assert _gate("org/m", subject = "user-b").blocked is True # b: still prompted
diff --git a/studio/backend/tests/test_utils.py b/studio/backend/tests/test_utils.py
index 741f19c67a..eba697826b 100644
--- a/studio/backend/tests/test_utils.py
+++ b/studio/backend/tests/test_utils.py
@@ -96,7 +96,9 @@ class TestGetDevice:
patch("utils.hardware.hardware._has_torch", return_value = True),
patch("torch.cuda.is_available", return_value = True),
patch("torch.cuda.device_count", return_value = 1),
- patch("torch.cuda.get_device_properties", side_effect = RuntimeError("probe")),
+ patch(
+ "torch.cuda.get_device_properties", side_effect = RuntimeError("probe")
+ ),
):
assert _reset_and_detect() == DeviceType.CUDA
assert "" in capsys.readouterr().out
@@ -201,7 +203,9 @@ class TestGetGpuMemoryInfo:
# --- When a GPU IS available ---
- @pytest.mark.skipif(_actual_device() == "cpu", reason = "No GPU available on this machine")
+ @pytest.mark.skipif(
+ _actual_device() == "cpu", reason = "No GPU available on this machine"
+ )
def test_gpu_available_fields(self):
result = get_gpu_memory_info()
assert result["available"] is True
@@ -299,7 +303,9 @@ class TestLogGpuMemory:
"free_gb": 14.0,
}
- with patch("utils.hardware.hardware.get_gpu_memory_info", return_value = fake_info):
+ with patch(
+ "utils.hardware.hardware.get_gpu_memory_info", return_value = fake_info
+ ):
log_gpu_memory("unit-test")
captured = capfd.readouterr()
@@ -310,7 +316,9 @@ class TestLogGpuMemory:
def test_logs_cpu_fallback_when_no_gpu(self, capfd):
fake_info = {"available": False, "backend": "cpu"}
- with patch("utils.hardware.hardware.get_gpu_memory_info", return_value = fake_info):
+ with patch(
+ "utils.hardware.hardware.get_gpu_memory_info", return_value = fake_info
+ ):
log_gpu_memory("cpu-test")
captured = capfd.readouterr()
diff --git a/studio/backend/tests/test_validate_gguf_runtime_message.py b/studio/backend/tests/test_validate_gguf_runtime_message.py
index f612cc4a03..d27f9eec11 100644
--- a/studio/backend/tests/test_validate_gguf_runtime_message.py
+++ b/studio/backend/tests/test_validate_gguf_runtime_message.py
@@ -50,7 +50,9 @@ class TestValidateGgufRuntimeMessage(unittest.TestCase):
def test_missing_llama_server_returns_actionable_message(self):
route = _load_route_module("inf_route_runtime_msg_1", "routes/inference.py")
- err = self._validate(route, "unsloth/Qwen3-1.7B-GGUF", LlamaServerNotFoundError(_GGUF_MSG))
+ err = self._validate(
+ route, "unsloth/Qwen3-1.7B-GGUF", LlamaServerNotFoundError(_GGUF_MSG)
+ )
self.assertEqual(err.status_code, 400)
self.assertIn("unsloth studio setup", err.detail)
self.assertIn("llama.cpp runtime", err.detail)
@@ -61,7 +63,9 @@ class TestValidateGgufRuntimeMessage(unittest.TestCase):
# routed to the GGUF "install the runtime" message. validate_model surfaces a RuntimeError's
# own message (#6398), so assert the GGUF install text is absent and the message is intact.
route = _load_route_module("inf_route_runtime_msg_2", "routes/inference.py")
- err = self._validate(route, "not/a-real-model", RuntimeError("totally different failure"))
+ err = self._validate(
+ route, "not/a-real-model", RuntimeError("totally different failure")
+ )
self.assertEqual(err.status_code, 400)
self.assertNotIn("unsloth studio setup", err.detail)
self.assertNotIn("llama.cpp runtime", err.detail)
@@ -73,32 +77,46 @@ class TestLoadGgufRuntimeMessage(unittest.TestCase):
def _load(self, route, model_path, side_effect):
request = LoadRequest(model_path = model_path)
- backend = MagicMock(active_model_name = None) # no resident model -> reach from_identifier
+ backend = MagicMock(
+ active_model_name = None
+ ) # no resident model -> reach from_identifier
with (
patch.object(
route,
"_resolve_model_identifier_for_request",
return_value = (model_path, model_path, False),
),
- patch.object(route, "resolve_effective_chat_template_override", return_value = None),
+ patch.object(
+ route, "resolve_effective_chat_template_override", return_value = None
+ ),
patch.object(route, "get_inference_backend", return_value = backend),
patch.object(route, "get_llama_cpp_backend", return_value = MagicMock()),
patch.object(route.ModelConfig, "from_identifier", side_effect = side_effect),
):
with self.assertRaises(HTTPException) as exc:
- asyncio.run(route.load_model(request, MagicMock(), current_subject = "test-user"))
+ asyncio.run(
+ route.load_model(request, MagicMock(), current_subject = "test-user")
+ )
return exc.exception
def test_missing_llama_server_returns_actionable_message(self):
- route = _load_route_module("inf_route_load_runtime_msg_1", "routes/inference.py")
- err = self._load(route, "unsloth/Qwen3-1.7B-GGUF", LlamaServerNotFoundError(_GGUF_MSG))
+ route = _load_route_module(
+ "inf_route_load_runtime_msg_1", "routes/inference.py"
+ )
+ err = self._load(
+ route, "unsloth/Qwen3-1.7B-GGUF", LlamaServerNotFoundError(_GGUF_MSG)
+ )
self.assertEqual(err.status_code, 400)
self.assertIn("unsloth studio setup", err.detail)
self.assertIn("llama.cpp runtime", err.detail)
def test_other_load_errors_still_500(self):
- route = _load_route_module("inf_route_load_runtime_msg_2", "routes/inference.py")
- err = self._load(route, "unsloth/some-model", RuntimeError("totally different failure"))
+ route = _load_route_module(
+ "inf_route_load_runtime_msg_2", "routes/inference.py"
+ )
+ err = self._load(
+ route, "unsloth/some-model", RuntimeError("totally different failure")
+ )
self.assertEqual(err.status_code, 500)
@@ -114,7 +132,9 @@ class TestLoadPathPropagatesRuntimeError(unittest.TestCase):
with self.assertRaises(LlamaServerNotFoundError):
asyncio.run(
- load_with_tensor_fallback(_attempt, requested_tensor = False, extra_args = None)
+ load_with_tensor_fallback(
+ _attempt, requested_tensor = False, extra_args = None
+ )
)
diff --git a/studio/backend/tests/test_validate_model_error.py b/studio/backend/tests/test_validate_model_error.py
index 16edd43f93..6018acde04 100644
--- a/studio/backend/tests/test_validate_model_error.py
+++ b/studio/backend/tests/test_validate_model_error.py
@@ -105,12 +105,20 @@ def _drive_validate(monkeypatch, *, is_gguf: bool):
is_vision = False,
gguf_file = None,
)
- monkeypatch.setattr(inf.ModelConfig, "from_identifier", staticmethod(lambda **_kw: config))
+ monkeypatch.setattr(
+ inf.ModelConfig, "from_identifier", staticmethod(lambda **_kw: config)
+ )
# No LoRA base to resolve; keep it offline.
- monkeypatch.setattr(mc, "get_base_model_from_lora_identifier", lambda *_a, **_k: None)
+ monkeypatch.setattr(
+ mc, "get_base_model_from_lora_identifier", lambda *_a, **_k: None
+ )
# Both gates WOULD flag this repo (mixed repo with auto_map + an unsafe pickle).
- monkeypatch.setattr(inf, "_requires_trust_remote_code_for_model", lambda *_a, **_k: True)
- monkeypatch.setattr(inf, "_requires_security_review_for_model", lambda *_a, **_k: True)
+ monkeypatch.setattr(
+ inf, "_requires_trust_remote_code_for_model", lambda *_a, **_k: True
+ )
+ monkeypatch.setattr(
+ inf, "_requires_security_review_for_model", lambda *_a, **_k: True
+ )
req = ValidateModelRequest(model_path = "org/mixed-repo")
return asyncio.run(inf.validate_model(req, current_subject = "tester"))
@@ -135,7 +143,9 @@ def test_non_gguf_load_still_runs_trc_and_security_review(monkeypatch):
def test_resolve_loaded_trc_prefers_stored_value():
# A value stored at load time wins, so a status refresh does not re-derive it.
assert (
- inf._resolve_loaded_trust_remote_code("org/m", {"requires_trust_remote_code": True}, {})
+ inf._resolve_loaded_trust_remote_code(
+ "org/m", {"requires_trust_remote_code": True}, {}
+ )
is True
)
assert (
@@ -149,16 +159,26 @@ def test_resolve_loaded_trc_prefers_stored_value():
def test_resolve_loaded_trc_uses_runtime_and_yaml():
# No stored value: the trust_remote_code the load used, then the YAML default.
assert (
- inf._resolve_loaded_trust_remote_code("org/m", {}, {}, trust_remote_code_used = True) is True
+ inf._resolve_loaded_trust_remote_code(
+ "org/m", {}, {}, trust_remote_code_used = True
+ )
+ is True
+ )
+ assert (
+ inf._resolve_loaded_trust_remote_code("org/m", {}, {"trust_remote_code": True})
+ is True
)
- assert inf._resolve_loaded_trust_remote_code("org/m", {}, {"trust_remote_code": True}) is True
def test_resolve_loaded_trc_falls_back_to_raw_auto_map(monkeypatch):
# No stored value or runtime/YAML signal: fall back to the raw auto_map check.
- monkeypatch.setattr(inf, "_requires_trust_remote_code_for_model", lambda *_a, **_k: True)
+ monkeypatch.setattr(
+ inf, "_requires_trust_remote_code_for_model", lambda *_a, **_k: True
+ )
assert inf._resolve_loaded_trust_remote_code("org/custom", {}, {}) is True
- monkeypatch.setattr(inf, "_requires_trust_remote_code_for_model", lambda *_a, **_k: False)
+ monkeypatch.setattr(
+ inf, "_requires_trust_remote_code_for_model", lambda *_a, **_k: False
+ )
assert inf._resolve_loaded_trust_remote_code("org/plain", {}, {}) is False
@@ -183,31 +203,43 @@ def _drive_validate_lora(monkeypatch, *, adapter_needs_trc, base_needs_trc):
is_vision = False,
gguf_file = None,
)
- monkeypatch.setattr(inf.ModelConfig, "from_identifier", staticmethod(lambda **_kw: config))
- monkeypatch.setattr(mc, "get_base_model_from_lora_identifier", lambda *_a, **_k: base)
+ monkeypatch.setattr(
+ inf.ModelConfig, "from_identifier", staticmethod(lambda **_kw: config)
+ )
+ monkeypatch.setattr(
+ mc, "get_base_model_from_lora_identifier", lambda *_a, **_k: base
+ )
trc = {adapter: adapter_needs_trc, base: base_needs_trc}
monkeypatch.setattr(
inf,
"_requires_trust_remote_code_for_model",
lambda target, *_a, **_k: trc.get(target, False),
)
- monkeypatch.setattr(inf, "_requires_security_review_for_model", lambda *_a, **_k: False)
+ monkeypatch.setattr(
+ inf, "_requires_security_review_for_model", lambda *_a, **_k: False
+ )
req = ValidateModelRequest(model_path = adapter)
return asyncio.run(inf.validate_model(req, current_subject = "tester"))
def test_validate_lora_flags_trc_from_adapter_only(monkeypatch):
# Adapter ships auto_map, base does not: the requirement follows either repo.
- resp = _drive_validate_lora(monkeypatch, adapter_needs_trc = True, base_needs_trc = False)
+ resp = _drive_validate_lora(
+ monkeypatch, adapter_needs_trc = True, base_needs_trc = False
+ )
assert resp.requires_trust_remote_code is True
def test_validate_lora_flags_trc_from_base_only(monkeypatch):
# The classic case: the base ships custom code, the adapter does not.
- resp = _drive_validate_lora(monkeypatch, adapter_needs_trc = False, base_needs_trc = True)
+ resp = _drive_validate_lora(
+ monkeypatch, adapter_needs_trc = False, base_needs_trc = True
+ )
assert resp.requires_trust_remote_code is True
def test_validate_lora_clean_when_neither_needs_trc(monkeypatch):
- resp = _drive_validate_lora(monkeypatch, adapter_needs_trc = False, base_needs_trc = False)
+ resp = _drive_validate_lora(
+ monkeypatch, adapter_needs_trc = False, base_needs_trc = False
+ )
assert resp.requires_trust_remote_code is False
diff --git a/studio/backend/tests/test_vision_cache.py b/studio/backend/tests/test_vision_cache.py
index 18b532cc9b..a76e43a6cb 100644
--- a/studio/backend/tests/test_vision_cache.py
+++ b/studio/backend/tests/test_vision_cache.py
@@ -71,7 +71,9 @@ class TestVisionCacheHitMiss:
"""Two calls for the same model invoke the uncached fn once."""
assert is_vision_model("org/my-vlm") is True
assert is_vision_model("org/my-vlm") is True
- mock_uncached.assert_called_once_with("org/my-vlm", None, local_files_only = False)
+ mock_uncached.assert_called_once_with(
+ "org/my-vlm", None, local_files_only = False
+ )
@patch("utils.models.model_config._is_vision_model_uncached", return_value = False)
def test_different_models_each_detected(self, mock_uncached):
@@ -111,7 +113,9 @@ class TestVisionCacheSubprocessPath:
@patch("utils.models.model_config._raw_config_has_vision_config", return_value = None)
@patch("utils.models.model_config._is_vision_model_subprocess", return_value = True)
@patch("utils.transformers_version.needs_transformers_5", return_value = True)
- def test_subprocess_called_once_with_cache(self, mock_needs_t5, mock_subprocess, mock_raw):
+ def test_subprocess_called_once_with_cache(
+ self, mock_needs_t5, mock_subprocess, mock_raw
+ ):
"""When the raw-config reader is inconclusive (None), the transformers
5.x subprocess fires only on the first call; the second is cached."""
# First call: raw None -> subprocess
@@ -149,7 +153,9 @@ class TestLocalGgufVisionDetection:
"utils.models.model_config._is_vision_model_subprocess",
side_effect = AssertionError("GGUF must not use Transformers vision detection"),
)
- def test_qwen36_gguf_with_mmproj_skips_transformers(self, mock_subprocess, tmp_path):
+ def test_qwen36_gguf_with_mmproj_skips_transformers(
+ self, mock_subprocess, tmp_path
+ ):
model = tmp_path / "Qwen3.6-27B-UD-Q4_K_XL-MTP.gguf"
model.write_bytes(b"")
(tmp_path / "mmproj-F32.gguf").write_bytes(b"")
@@ -161,7 +167,9 @@ class TestLocalGgufVisionDetection:
"utils.models.model_config._is_vision_model_subprocess",
side_effect = AssertionError("GGUF must not use Transformers vision detection"),
)
- def test_direct_gguf_in_variant_subdir_finds_snapshot_mmproj(self, mock_subprocess, tmp_path):
+ def test_direct_gguf_in_variant_subdir_finds_snapshot_mmproj(
+ self, mock_subprocess, tmp_path
+ ):
variant_dir = tmp_path / "BF16"
variant_dir.mkdir()
model = variant_dir / "Qwen3.6-27B-UD-Q4_K_XL-MTP.gguf"
@@ -175,7 +183,9 @@ class TestLocalGgufVisionDetection:
"utils.models.model_config._is_vision_model_subprocess",
side_effect = AssertionError("GGUF must not use Transformers vision detection"),
)
- def test_qwen36_gguf_without_mmproj_skips_transformers(self, mock_subprocess, tmp_path):
+ def test_qwen36_gguf_without_mmproj_skips_transformers(
+ self, mock_subprocess, tmp_path
+ ):
model = tmp_path / "Qwen3.6-27B-UD-Q4_K_XL-MTP.gguf"
model.write_bytes(b"")
@@ -280,7 +290,9 @@ class TestVisionCacheDirectPath:
@patch("utils.models.model_config._raw_config_has_vision_config", return_value = None)
@patch("utils.transformers_version.needs_transformers_5", return_value = False)
@patch("utils.models.model_config.load_model_config")
- def test_direct_vlm_detection_cached(self, mock_load_config, mock_needs_t5, mock_raw):
+ def test_direct_vlm_detection_cached(
+ self, mock_load_config, mock_needs_t5, mock_raw
+ ):
"""A standard VLM detected via architecture suffix should be cached."""
cfg = MagicMock(spec = []) # strict: only explicitly set attrs exist
cfg.model_type = "gemma3"
@@ -295,7 +307,9 @@ class TestVisionCacheDirectPath:
@patch("utils.models.model_config._raw_config_has_vision_config", return_value = None)
@patch("utils.transformers_version.needs_transformers_5", return_value = False)
@patch("utils.models.model_config.load_model_config")
- def test_direct_non_vlm_detection_cached(self, mock_load_config, mock_needs_t5, mock_raw):
+ def test_direct_non_vlm_detection_cached(
+ self, mock_load_config, mock_needs_t5, mock_raw
+ ):
"""A standard text model (no VLM indicators) should cache False."""
cfg = MagicMock(spec = []) # spec=[] means no attributes at all
cfg.model_type = "llama"
@@ -327,7 +341,9 @@ class TestVisionCacheDirectPath:
@patch("utils.models.model_config._raw_config_has_vision_config", return_value = None)
@patch("utils.transformers_version.needs_transformers_5", return_value = False)
@patch("utils.models.model_config.load_model_config")
- def test_gemma4_model_type_detected_and_cached(self, mock_load_config, mock_needs_t5, mock_raw):
+ def test_gemma4_model_type_detected_and_cached(
+ self, mock_load_config, mock_needs_t5, mock_raw
+ ):
cfg = MagicMock(spec = [])
cfg.model_type = "gemma4"
cfg.architectures = ["Gemma4ForConditionalGeneration"]
@@ -370,7 +386,9 @@ class TestVisionCacheDirectPath:
@patch("utils.models.model_config._raw_config_has_vision_config", return_value = None)
@patch("utils.transformers_version.needs_transformers_5", return_value = False)
@patch("utils.models.model_config.load_model_config")
- def test_audio_model_excluded_and_cached(self, mock_load_config, mock_needs_t5, mock_raw):
+ def test_audio_model_excluded_and_cached(
+ self, mock_load_config, mock_needs_t5, mock_raw
+ ):
"""Audio-only models (csm, whisper) with ForConditionalGeneration
should be excluded from VLM detection and cached as False."""
cfg = MagicMock(spec = []) # strict: only explicitly set attrs exist
@@ -540,10 +558,15 @@ class TestSubprocessScript:
is True
)
assert (
- inline_is_vlm(_C(model_type = "gemma4_text", architectures = ["Gemma4ForCausalLM"]))
+ inline_is_vlm(
+ _C(model_type = "gemma4_text", architectures = ["Gemma4ForCausalLM"])
+ )
+ is False
+ )
+ assert (
+ inline_is_vlm(_C(model_type = "llama", architectures = ["LlamaForCausalLM"]))
is False
)
- assert inline_is_vlm(_C(model_type = "llama", architectures = ["LlamaForCausalLM"])) is False
# ---------------------------------------------------------------------------
@@ -702,10 +725,14 @@ class TestAudioDetectionCacheTokenAware:
# Offline probe caches None under a local-only key.
assert mc.detect_audio_type("some/audio-model", local_files_only = True) is None
# A later online probe must re-run (different key) and detect the audio model.
- assert mc.detect_audio_type("some/audio-model", local_files_only = False) == "snac"
+ assert (
+ mc.detect_audio_type("some/audio-model", local_files_only = False) == "snac"
+ )
assert seen == [True, False]
# The online positive is then cached for subsequent online callers.
- assert mc.detect_audio_type("some/audio-model", local_files_only = False) == "snac"
+ assert (
+ mc.detect_audio_type("some/audio-model", local_files_only = False) == "snac"
+ )
assert seen == [True, False]
mc._audio_detection_cache.clear()
@@ -750,7 +777,18 @@ class TestEnvOfflineParsing:
def test_truthy_values_recognized(self, monkeypatch):
import utils.models.model_config as mc
for var in ("HF_HUB_OFFLINE", "TRANSFORMERS_OFFLINE"):
- for val in ("1", "true", "TRUE", "yes", "Yes", "on", "ON", " 1 ", " on ", "\ttrue\n"):
+ for val in (
+ "1",
+ "true",
+ "TRUE",
+ "yes",
+ "Yes",
+ "on",
+ "ON",
+ " 1 ",
+ " on ",
+ "\ttrue\n",
+ ):
monkeypatch.delenv("HF_HUB_OFFLINE", raising = False)
monkeypatch.delenv("TRANSFORMERS_OFFLINE", raising = False)
monkeypatch.setenv(var, val)
@@ -764,4 +802,6 @@ class TestEnvOfflineParsing:
assert mc._env_offline() is False
for val in ("", "0", "false", "no", "off", "2", "onn"):
monkeypatch.setenv("HF_HUB_OFFLINE", val)
- assert mc._env_offline() is False, f"HF_HUB_OFFLINE={val!r} should not be offline"
+ assert (
+ mc._env_offline() is False
+ ), f"HF_HUB_OFFLINE={val!r} should not be offline"
diff --git a/studio/backend/tests/test_vram_estimation.py b/studio/backend/tests/test_vram_estimation.py
index 2def8738e2..d0e2b03623 100644
--- a/studio/backend/tests/test_vram_estimation.py
+++ b/studio/backend/tests/test_vram_estimation.py
@@ -316,8 +316,12 @@ class TestLoraParams(unittest.TestCase):
self.assertLess(qv_only, all_mods)
def test_moe_mlp_modules_scale_with_experts(self):
- dense_lora = compute_lora_params(LLAMA_8B, 16, ["gate_proj", "up_proj", "down_proj"])
- moe_lora = compute_lora_params(MOE_CONFIG, 16, ["gate_proj", "up_proj", "down_proj"])
+ dense_lora = compute_lora_params(
+ LLAMA_8B, 16, ["gate_proj", "up_proj", "down_proj"]
+ )
+ moe_lora = compute_lora_params(
+ MOE_CONFIG, 16, ["gate_proj", "up_proj", "down_proj"]
+ )
ratio = moe_lora / dense_lora
self.assertAlmostEqual(ratio, 8.0, delta = 0.5)
@@ -334,8 +338,12 @@ class TestLoraParams(unittest.TestCase):
self.assertGreater(moe_lora, dense_lora * 20)
def test_attention_modules_same_for_moe(self):
- dense_attn = compute_lora_params(LLAMA_8B, 16, ["q_proj", "k_proj", "v_proj", "o_proj"])
- moe_attn = compute_lora_params(MOE_CONFIG, 16, ["q_proj", "k_proj", "v_proj", "o_proj"])
+ dense_attn = compute_lora_params(
+ LLAMA_8B, 16, ["q_proj", "k_proj", "v_proj", "o_proj"]
+ )
+ moe_attn = compute_lora_params(
+ MOE_CONFIG, 16, ["q_proj", "k_proj", "v_proj", "o_proj"]
+ )
self.assertEqual(dense_attn, moe_attn)
def test_all_linear_uses_default_text_modules(self):
@@ -458,7 +466,9 @@ class TestActivationBytes(unittest.TestCase):
def test_non_flash_attention_uses_quadratic_path(self):
seq_len = 4096
- expected_quadratic = 1 * STRUCTURED_MIXED.num_attention_heads * seq_len * seq_len * 2 * 12.0
+ expected_quadratic = (
+ 1 * STRUCTURED_MIXED.num_attention_heads * seq_len * seq_len * 2 * 12.0
+ )
for attention_implementation in ("eager", "unknown_impl", None):
with self.subTest(attention_implementation = attention_implementation):
non_flash = compute_activation_bytes(
@@ -473,7 +483,9 @@ class TestActivationBytes(unittest.TestCase):
def test_non_flash_attention_without_gc_scales_quadratic_path_by_layers(self):
seq_len = 4096
- one_layer = 1 * STRUCTURED_MIXED.num_attention_heads * seq_len * seq_len * 2 * 12.0
+ one_layer = (
+ 1 * STRUCTURED_MIXED.num_attention_heads * seq_len * seq_len * 2 * 12.0
+ )
non_flash = compute_activation_bytes(
STRUCTURED_MIXED,
1,
@@ -705,7 +717,9 @@ class TestEstimateTrainingVram(unittest.TestCase):
)
v8 = estimate_training_vram(LLAMA_8B, opt8)
v32 = estimate_training_vram(LLAMA_8B, opt32)
- self.assertAlmostEqual(v32.optimizer_states / v8.optimizer_states, 1.5, delta = 0.1)
+ self.assertAlmostEqual(
+ v32.optimizer_states / v8.optimizer_states, 1.5, delta = 0.1
+ )
def test_min_gpu_vram_treats_activations_as_per_gpu_fixed(self):
config = TrainingVramConfig(training_method = "qlora", load_in_4bit = True)
@@ -755,7 +769,9 @@ class TestEstimateTrainingVram(unittest.TestCase):
optimizer = "adamw_8bit",
load_in_4bit = False,
)
- expected_floor = int(compute_model_weights_bytes(LLAMA_8B, "full", False) * 0.15)
+ expected_floor = int(
+ compute_model_weights_bytes(LLAMA_8B, "full", False) * 0.15
+ )
with patch(
"utils.hardware.vram_estimation.compute_gradient_bytes",
return_value = 1,
@@ -1275,7 +1291,9 @@ class TestSharedExperts(unittest.TestCase):
delta_per_layer = 4096 * 1407 * 3 * 2
expected_delta = delta_per_layer * 32 * 2
actual_delta = w_yes - w_no
- self.assertAlmostEqual(actual_delta, expected_delta, delta = expected_delta * 0.01)
+ self.assertAlmostEqual(
+ actual_delta, expected_delta, delta = expected_delta * 0.01
+ )
def test_deepseek_v3_params_in_range(self):
total = compute_total_params(DEEPSEEK_V3)
@@ -1391,7 +1409,9 @@ class TestDenseMoEMix(unittest.TestCase):
moe_intermediate_size = 1024,
num_dense_layers = 5,
)
- lora_all = compute_lora_params(all_moe, 16, ["gate_proj", "up_proj", "down_proj"])
+ lora_all = compute_lora_params(
+ all_moe, 16, ["gate_proj", "up_proj", "down_proj"]
+ )
lora_mix = compute_lora_params(mixed, 16, ["gate_proj", "up_proj", "down_proj"])
self.assertNotEqual(lora_all, lora_mix)
@@ -1475,7 +1495,9 @@ class TestPerLayerInputSkipAlias(unittest.TestCase):
delta = _compute_skipped_quantizable_elements(arch)
self.assertEqual(
delta,
- arch.hidden_size * arch.num_hidden_layers * arch.hidden_size_per_layer_input,
+ arch.hidden_size
+ * arch.num_hidden_layers
+ * arch.hidden_size_per_layer_input,
)
def test_layer_aggregate_skip_includes_per_layer_input_modules(self):
@@ -1554,7 +1576,9 @@ class TestSharedExpertVariants(unittest.TestCase):
def test_shared_expert_size_separate_from_routed_changes_weight_count(self):
from utils.hardware.vram_estimation import _compute_moe_mlp_elements
- arch_separate = extract_arch_config(self._hf(shared_expert_intermediate_size = 64))
+ arch_separate = extract_arch_config(
+ self._hf(shared_expert_intermediate_size = 64)
+ )
arch_implicit = extract_arch_config(self._hf(n_shared_experts = 1))
# Different shared sizes (64 vs default moe_intermediate_size=128) must
# give different MoE element counts.
@@ -1598,7 +1622,9 @@ class TestSharedExpertActivation(unittest.TestCase):
moe_intermediate_size = 64,
**fields,
)
- return extract_arch_config(SimpleNamespace(text_config = text_config, quantization_config = {}))
+ return extract_arch_config(
+ SimpleNamespace(text_config = text_config, quantization_config = {})
+ )
def test_shared_expert_increases_activation_bytes(self):
with_shared = self._make(shared_expert_intermediate_size = 64)
@@ -1650,7 +1676,9 @@ class TestPerLayerInputActivation(unittest.TestCase):
tie_word_embeddings = False,
**fields,
)
- return extract_arch_config(SimpleNamespace(text_config = text_config, quantization_config = {}))
+ return extract_arch_config(
+ SimpleNamespace(text_config = text_config, quantization_config = {})
+ )
def test_ple_increases_activation_bytes(self):
with_ple = self._make(
@@ -1714,7 +1742,9 @@ class TestKvSharedActivation(unittest.TestCase):
num_kv_shared_layers = kv_shared,
layer_types = ["full_attention"] * 4,
)
- return extract_arch_config(SimpleNamespace(text_config = text_config, quantization_config = {}))
+ return extract_arch_config(
+ SimpleNamespace(text_config = text_config, quantization_config = {})
+ )
def test_kv_shared_layers_keep_activation_bytes(self):
shared = self._make(kv_shared = 2)
@@ -1760,7 +1790,9 @@ class TestSparseMoeSkipAliases(unittest.TestCase):
def test_gemma4_layers_experts_alias_pulls_routed(self):
from utils.hardware.vram_estimation import _compute_skipped_quantizable_elements
- arch = extract_arch_config(self._hf(["model.layers.0.experts"], enable_moe_block = True))
+ arch = extract_arch_config(
+ self._hf(["model.layers.0.experts"], enable_moe_block = True)
+ )
self.assertGreater(_compute_skipped_quantizable_elements(arch), 0)
def test_qwen_shared_expert_skip_pulls_only_shared(self):
@@ -1811,7 +1843,9 @@ class TestAllLinearMoELoraExclusion(unittest.TestCase):
moe_intermediate_size = 64,
**fields,
)
- return extract_arch_config(SimpleNamespace(text_config = text_config, quantization_config = {}))
+ return extract_arch_config(
+ SimpleNamespace(text_config = text_config, quantization_config = {})
+ )
def test_all_linear_drops_routed_moe_expert_lora(self):
arch = self._arch()
@@ -1829,7 +1863,9 @@ class TestAllLinearMoELoraExclusion(unittest.TestCase):
def test_all_linear_includes_attention_lora(self):
arch = self._arch()
all_linear = compute_lora_params(arch, 8, "all-linear")
- attn_only = compute_lora_params(arch, 8, ["q_proj", "k_proj", "v_proj", "o_proj"])
+ attn_only = compute_lora_params(
+ arch, 8, ["q_proj", "k_proj", "v_proj", "o_proj"]
+ )
# all-linear still attaches to attention nn.Linear modules.
self.assertGreaterEqual(all_linear, attn_only)
@@ -1847,7 +1883,9 @@ class TestExplicitPerLayerInputLora(unittest.TestCase):
hidden_size_per_layer_input = 32,
vocab_size_per_layer_input = 128,
)
- return extract_arch_config(SimpleNamespace(text_config = text_config, quantization_config = {}))
+ return extract_arch_config(
+ SimpleNamespace(text_config = text_config, quantization_config = {})
+ )
def test_explicit_per_layer_input_gate_returns_nonzero(self):
arch = self._arch()
@@ -1886,7 +1924,9 @@ class TestTopKExpertActivation(unittest.TestCase):
moe_intermediate_size = 64,
**fields,
)
- return extract_arch_config(SimpleNamespace(text_config = text_config, quantization_config = {}))
+ return extract_arch_config(
+ SimpleNamespace(text_config = text_config, quantization_config = {})
+ )
def test_num_experts_per_tok_extracted(self):
arch = self._make(num_experts_per_tok = 4)
diff --git a/studio/backend/tests/test_web_fetch_binary_guard.py b/studio/backend/tests/test_web_fetch_binary_guard.py
index 10db953913..05d6c630e4 100644
--- a/studio/backend/tests/test_web_fetch_binary_guard.py
+++ b/studio/backend/tests/test_web_fetch_binary_guard.py
@@ -29,7 +29,11 @@ class _FakeResp:
def read(self, n: int | None = None) -> bytes:
# Advance a cursor like a real stream so the chunked reader reaches EOF.
- chunk = self._body[self._pos :] if n is None else self._body[self._pos : self._pos + n]
+ chunk = (
+ self._body[self._pos :]
+ if n is None
+ else self._body[self._pos : self._pos + n]
+ )
self._pos += len(chunk)
return chunk
@@ -49,7 +53,9 @@ class _FakeOpener:
def _fetch_with(monkeypatch, body: bytes, content_type: str | None) -> str:
# Pass SSRF validation and skip real DNS/network.
monkeypatch.setattr(
- tools, "_validate_and_resolve_host", lambda host, port: (True, "", "93.184.216.34")
+ tools,
+ "_validate_and_resolve_host",
+ lambda host, port: (True, "", "93.184.216.34"),
)
monkeypatch.setattr(
tools.urllib.request,
@@ -93,7 +99,10 @@ def _pdf_bytes(*page_texts: str) -> bytes:
("application/octet-stream", True),
("application/zip", False),
("application/vnd.ms-excel", True),
- ("application/vnd.openxmlformats-officedocument.wordprocessingml.document", True),
+ (
+ "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
+ True,
+ ),
("", True),
(None, True),
],
@@ -144,7 +153,9 @@ def test_encrypted_pdf_returns_safe_placeholder(monkeypatch):
def test_pdf_download_limit_enforced(monkeypatch):
monkeypatch.setattr(tools, "_MAX_PDF_FETCH_BYTES", 256)
- out = _fetch_with(monkeypatch, _pdf_bytes("Readable but oversized"), "application/pdf")
+ out = _fetch_with(
+ monkeypatch, _pdf_bytes("Readable but oversized"), "application/pdf"
+ )
assert out == "(PDF content exceeds the download limit; not readable as text)"
@@ -163,7 +174,9 @@ def test_pdf_extraction_caps_pages_and_intermediate_text(monkeypatch):
def fake_parse(data, *, max_pages = None):
seen["max_pages"] = max_pages
- pages = [Page(text = "x" * 1000, page_number = i, char_count = 1000) for i in range(1, 51)]
+ pages = [
+ Page(text = "x" * 1000, page_number = i, char_count = 1000) for i in range(1, 51)
+ ]
return pages, 60 # document actually has more pages than the cap
monkeypatch.setattr("core.rag.parsers.parse_pdf_bytes", fake_parse)
@@ -234,9 +247,13 @@ def test_binary_candidates_rejected_after_sniffing(monkeypatch, content_type):
assert "binary content" in out
-@pytest.mark.parametrize("content_type", ["application/sql", "application/x-www-form-urlencoded"])
+@pytest.mark.parametrize(
+ "content_type", ["application/sql", "application/x-www-form-urlencoded"]
+)
def test_unknown_application_text_kept_after_sniffing(monkeypatch, content_type):
- out = _fetch_with(monkeypatch, b"select readable_text from artifacts;\n" * 100, content_type)
+ out = _fetch_with(
+ monkeypatch, b"select readable_text from artifacts;\n" * 100, content_type
+ )
assert "readable_text" in out
assert "non-text content" not in out and "binary content" not in out
@@ -258,7 +275,9 @@ def test_excel_labeled_csv_kept_after_sniffing(monkeypatch):
],
)
@pytest.mark.parametrize("content_type", ["text/plain", "application/vnd.ms-excel"])
-def test_bom_unicode_text_without_charset_kept(monkeypatch, bom, encoding, content_type):
+def test_bom_unicode_text_without_charset_kept(
+ monkeypatch, bom, encoding, content_type
+):
body = bom + ("name,value\nreadable,42\n" * 100).encode(encoding)
out = _fetch_with(monkeypatch, body, content_type)
assert "readable" in out
@@ -284,7 +303,9 @@ def test_valid_utf8_binary_caught_by_control_chars(monkeypatch):
],
)
def test_text_labeled_binary_caught_by_magic(monkeypatch, magic):
- out = _fetch_with(monkeypatch, magic + b" printable text-heavy body" * 100, "text/plain")
+ out = _fetch_with(
+ monkeypatch, magic + b" printable text-heavy body" * 100, "text/plain"
+ )
assert "binary content" in out
@@ -317,14 +338,18 @@ def test_binary_magic_after_harmless_prefix(monkeypatch, prefix):
],
)
def test_office_labeled_binary_caught_by_magic(monkeypatch, content_type, magic):
- out = _fetch_with(monkeypatch, magic + b" printable text-heavy body" * 100, content_type)
+ out = _fetch_with(
+ monkeypatch, magic + b" printable text-heavy body" * 100, content_type
+ )
assert "binary content" in out
def test_latin1_text_without_charset_kept(monkeypatch):
# The cp1252 retry should rescue accent-heavy text with ASCII structure.
body = (
- "Muller lauft uber die Strasse: schoene, groesse. MARKERWORD ".replace("ue", "ü")
+ "Muller lauft uber die Strasse: schoene, groesse. MARKERWORD ".replace(
+ "ue", "ü"
+ )
+ "äöüß éèà "
) * 30
out = _fetch_with(monkeypatch, body.encode("cp1252"), "text/plain")
@@ -363,7 +388,9 @@ def test_html_page_unaffected(monkeypatch):
def test_content_type_sanitized_in_message(monkeypatch):
# Do not echo obs-folded header content into the model response.
- out = _fetch_with(monkeypatch, b"PK\x03\x04" * 500, "application/zip\r\n data: injected")
+ out = _fetch_with(
+ monkeypatch, b"PK\x03\x04" * 500, "application/zip\r\n data: injected"
+ )
assert "\n" not in out and "\r" not in out
assert "injected" not in out
assert "application/zip" in out
diff --git a/studio/backend/tests/test_web_fetch_extraction.py b/studio/backend/tests/test_web_fetch_extraction.py
index b794ee3e81..b7c81c1c6a 100644
--- a/studio/backend/tests/test_web_fetch_extraction.py
+++ b/studio/backend/tests/test_web_fetch_extraction.py
@@ -162,7 +162,8 @@ def test_inline_style_display_none_important_is_dropped():
def test_inline_style_display_none_among_other_declarations():
html = (
- "
keep
" '
gone
'
+ "
keep
"
+ '
gone
'
)
out = html_to_markdown(html)
assert "keep" in out
@@ -581,14 +582,14 @@ def test_looks_like_html_markdown_with_leading_fenced_example_stays_markdown():
# A Markdown README OPENING with a fenced HTML example must not be sniffed as
# HTML just because a doctype/tag appears in the first 256 chars; html_to_markdown
# would corrupt the fences and prose.
- fenced = (
- "```html\n\n
hi
\n```\n\n# Real README\n"
- )
+ fenced = "```html\n\n
hi
\n```\n\n# Real README\n"
assert not _looks_like_html(fenced)
# Prose that mentions a tag inline, and a centered-logo README that opens
# with
/
/
, also stay Markdown.
assert not _looks_like_html("Use the element to start a page.")
- assert not _looks_like_html('
\n\n# Project\n')
+ assert not _looks_like_html(
+ '
\n\n# Project\n'
+ )
assert not _looks_like_html('
\n\n# Project\n\n
\n')
assert not _looks_like_html('
Project
\n\nMarkdown body.\n')
# An autolink is not a tag opener.
@@ -797,7 +798,9 @@ def test_hidden_paragraph_with_inline_child_implicitly_closed_by_block():
# A browser closes an open
when a
arrives, even with an unclosed
# on top of it. The hidden region must end there, not swallow the
# following visible blocks.
- html = "
secret
visible div
visible paragraph"
+ html = (
+ "
secret
visible div
visible paragraph"
+ )
out = html_to_markdown(html)
assert "secret" not in out
assert "visible div" in out
@@ -868,7 +871,8 @@ def test_nested_hidden_table_does_not_leak_inner_cells():
def test_many_tiny_articles_do_not_displace_substantial_main():
cards = "".join(
- f"
Teaser {i}
Advertisement card blurb.
" for i in range(12)
+ f"
Teaser {i}
Advertisement card blurb.
"
+ for i in range(12)
)
main_body = "Authoritative main documentation content. " * 30
html = f"
{cards}
Real page
{main_body}
"
@@ -899,7 +903,9 @@ def test_truncated_open_article_scope_is_scored_and_preferred():
# _fetch_url_raw caps large pages, so the download can end before the closing
# . The scope is still the main content and must be preferred over the
# whole document (which re-leaks the page chrome).
- chrome = "
Repository file tree and page chrome.
"
+ chrome = (
+ "
Repository file tree and page chrome.
"
+ )
article_body = "Real README documentation body text. " * 20
# No closing / -- the fetch cap truncated the page.
html = f"{chrome}