Replace vision hotswap with native multimodal profiles

This commit is contained in:
Mikei386 committed 2026-08-20 14:24:56 +02:00
1 parent 8a45a0d851
commit cb07779f5a
19 files changed
+169 -719

No files matched your search

+1 -1
View File
@@ -1 +1 @@
ExecStart=/usr/bin/mock-llama --ctx-size 73728
ExecStart=/usr/bin/mock-llama --ctx-size 76800
@@ -1 +1 @@
ExecStart=/usr/bin/mock-llama --ctx-size 73728
ExecStart=/usr/bin/mock-llama --ctx-size 76800
+1 -1
View File
@@ -25,7 +25,7 @@ def current_ctx() -> int:
return int(line.split("--ctx-size")[1].split()[0])
except (OSError, ValueError, IndexError):
pass
return 73728
return 76800
class Handler(BaseHTTPRequestHandler):
+4 -4
View File
@@ -124,7 +124,7 @@ import json,sys
d=json.load(sys.stdin)
ids={m["id"]:m for m in d["data"]}
assert set(ids)=={"qwen-fast","qwen-medium","qwen-long"}, ids
assert ids["qwen-fast"]["context_length"]==73728
assert ids["qwen-fast"]["context_length"]==76800
assert ids["qwen-medium"]["context_length"]==94208
assert ids["qwen-long"]["context_length"]==131072
' && ok "drei virtuelle Modelle mit korrekten Context Windows" || bad "/v1/models"
@@ -138,7 +138,7 @@ import json,sys
d=json.load(sys.stdin)
assert d["current_profile"]=="fast", d
assert d["upstream"]["reachable"] is True, d
assert d["upstream"]["ctx"]==73728, d
assert d["upstream"]["ctx"]==76800, d
' && ok "Status zeigt Profil fast + erreichbares Upstream" || bad "/status"
# --- 3. Normales Forwarding (non-streaming) -------------------------------------
@@ -199,7 +199,7 @@ echo "$RESP" | python3 -m json.tool
echo "$RESP" | python3 -c '
import json,sys
d=json.load(sys.stdin)
assert d["profile"]=="fast" and d["context_length"]==73728, d
assert d["profile"]=="fast" and d["context_length"]==76800, d
' && ok "Profil fast wieder aktiv" || bad "Profilwechsel fast"
# --- 8. Virtuelles Modell triggert Profilwechsel ----------------------------------------
@@ -270,7 +270,7 @@ echo "$RESP" | python3 -m json.tool
echo "$RESP" | python3 -c '
import json,sys
d=json.load(sys.stdin)
assert d["profile"]=="fast" and d["model"]=="mock-model-73728", d
assert d["profile"]=="fast" and d["model"]=="mock-model-76800", d
' && ok "Recovery: /fast wartet auf Upstream, dann Erfolg" || bad "Recovery"
# --- 11. Bildgenerierung (Mock-Worker) -------------------------------------------------
+31 -7
View File
@@ -23,7 +23,11 @@ from router_support import ( # noqa: E402
enforce_artifact_retention,
load_profile_registry,
)
from ai_profile_router import _normalize_vision_image # noqa: E402
from ai_profile_router import ( # noqa: E402
_normalize_chat_image,
_normalize_chat_images,
_request_has_image,
)
class AuthPolicyTests(unittest.TestCase):
@@ -47,9 +51,9 @@ class RuntimeStoreTests(unittest.TestCase):
def test_atomic_roundtrip_and_delete(self) -> None:
with tempfile.TemporaryDirectory() as temp:
store = RuntimeStore(str(Path(temp) / "state.json"))
store.save(worker="vision", worker_pid=123, last_profile="fast")
store.save(worker="image", worker_pid=123, last_profile="fast")
self.assertEqual(store.load()["worker_pid"], 123)
store.clear_worker("vision")
store.clear_worker("image")
state = store.load()
self.assertNotIn("worker", state)
self.assertEqual(state["last_profile"], "fast")
@@ -75,18 +79,38 @@ class ProfileRegistryTests(unittest.TestCase):
load_profile_registry("/definitely/missing/profiles.json")
class VisionInputTests(unittest.TestCase):
class ChatImageInputTests(unittest.TestCase):
def test_small_png_data_url_is_accepted(self) -> None:
value = "data:image/png;base64,iVBORw0KGgo="
self.assertEqual(_normalize_vision_image(value), value)
self.assertEqual(_normalize_chat_image(value), value)
def test_remote_url_is_denied_by_default(self) -> None:
with self.assertRaisesRegex(ValueError, "deaktiviert"):
_normalize_vision_image("https://example.com/private.png")
_normalize_chat_image("https://example.com/private.png")
def test_invalid_base64_is_rejected(self) -> None:
with self.assertRaisesRegex(ValueError, "Base64"):
_normalize_vision_image("data:image/png;base64,not!base64")
_normalize_chat_image("data:image/png;base64,not!base64")
def test_multimodal_part_is_retained_for_direct_forwarding(self) -> None:
value = "data:image/png;base64,iVBORw0KGgo="
request = {
"model": "qwen-fast",
"messages": [{
"role": "user",
"content": [
{"type": "text", "text": "Was ist zu sehen?"},
{"type": "image_url", "image_url": {"url": value}},
],
}],
}
self.assertTrue(_request_has_image(request))
normalized = _normalize_chat_images(request)
self.assertEqual(
normalized["messages"][0]["content"][1]["image_url"]["url"],
value,
)
self.assertEqual(request, normalized)
class RetentionTests(unittest.TestCase):