Measured with the unmodified Codex binary on 10 Oct 2026: - real context windows per model (NanoGPT list, local table, combo = its smallest member, capped at the summarizer's window) so Codex compacts by itself when switching to a smaller model; an overflow guard trims oversized old parts and reports what still does not fit - classic tools instead of code mode for external models: GLM 0/3 -> 3/3, Qwen 0/3 -> 2/3 on simple tool tasks, Claude/cx/Gemini unchanged - phase (commentary / final_answer) on external assistant messages - an answer cut by the client is carried to the next turn once - images stay for vision-capable targets - summary brake stops tight loops, not frequent compaction - permanent gateway errors and tool-less targets fail at once with a readable message instead of five reconnects - a lone "." (used to apply a model change in voice) is answered locally - the bridge listens within seconds; the IDE waits 45 s for it Release: AITURK IDE 1.0.0-beta.26. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
230 lines
13 KiB
Python
230 lines
13 KiB
Python
"""Bridge version 7: real context windows, phase stamping, overflow errors, images for vision models."""
|
||
import ast
|
||
import json
|
||
import os
|
||
import tempfile
|
||
import unittest
|
||
from pathlib import Path
|
||
|
||
import ide_catalog as c
|
||
import pencere
|
||
import resim_context
|
||
|
||
tree = ast.parse(Path(__file__).with_name("bridge.py").read_text(encoding="utf-8"))
|
||
names = {"fix_tool_ids", "SseRewriter", "phase_damgala", "asistan_mesaji_mi"}
|
||
nodes = [n for n in tree.body if isinstance(n, (ast.FunctionDef, ast.ClassDef)) and n.name in names]
|
||
for node in tree.body:
|
||
if isinstance(node, ast.Assign) and any(isinstance(t, ast.Name) and t.id in
|
||
{"ID_PREFIX", "FOREIGN_ID_MARK", "REFERENCE_TYPES", "INLINE_TYPES", "ARAC_OGELERI"} for t in node.targets):
|
||
nodes.insert(0, node)
|
||
env = {"json": json, "re": __import__("re"), "log": lambda message: None, "pencere": pencere}
|
||
exec(compile(ast.Module(body=nodes, type_ignores=[]), "bridge.py", "exec"), env)
|
||
|
||
NANO = pencere.nano_sadelestir({"data": [
|
||
{"id": "z-ai/glm-5.3-flash-uncensored", "context_length": 1048576,
|
||
"capabilities": {"vision": True, "tool_calling": True}},
|
||
{"id": "mlabonne/NeuralDaredevil-8B-abliterated", "context_length": 8192,
|
||
"capabilities": {"vision": False, "tool_calling": False}},
|
||
{"id": "nousresearch/hermes-3-llama-3.1-70b", "context_length": 65536,
|
||
"capabilities": {"vision": False, "tool_calling": True}},
|
||
{"id": "broken", "context_length": 0}]})
|
||
|
||
|
||
def sse(*events):
|
||
return b"".join(b"event: " + e["type"].encode() + b"\ndata: " + json.dumps(e).encode() + b"\n\n" for e in events)
|
||
|
||
|
||
def parse(raw):
|
||
return [json.loads(line[6:]) for line in raw.decode().splitlines() if line.startswith("data: ")]
|
||
|
||
|
||
def message(text, ident="msg_1"):
|
||
return {"type": "message", "id": ident, "role": "assistant", "content": [{"type": "output_text", "text": text}]}
|
||
|
||
|
||
class Windows(unittest.TestCase):
|
||
def test_provider_data_local_table_and_cap(self):
|
||
coz = pencere.coz
|
||
# The provider that carries the request decides; nothing is declared above the summarizer's window.
|
||
self.assertEqual(coz("9router-nano-z-ai-glm-5.3-flash-uncensored", "model", "nano/z-ai/glm-5.3-flash-uncensored",
|
||
nano=NANO)["pencere"], pencere.TAVAN)
|
||
small = coz("9router-nano-mlabonne-NeuralDaredevil-8B-abliterated", "model",
|
||
"nano/mlabonne/NeuralDaredevil-8B-abliterated", nano=NANO)
|
||
self.assertEqual((small["pencere"], small["arac"], small["gorsel"]), (8192, False, False))
|
||
self.assertEqual(coz("9router-cc-claude-opus-5-5", "model", "cc/claude-opus-5-5")["pencere"], 200000)
|
||
self.assertEqual(coz("9router-cx-gpt-6-astra", "model", "cx/gpt-6-astra")["pencere"], 272000)
|
||
self.assertEqual(coz("no-think-9router-cc-claude-opus-5-5", "model", "no-think/claude-opus-5-5")["pencere"], 200000)
|
||
unknown = coz("agnes-agnes-2.5-flash", "model", "Agnes 2.5 Flash", nano=NANO)
|
||
self.assertEqual((unknown["pencere"], unknown["kaynak"]), (pencere.VARSAYILAN, "varsayilan"))
|
||
self.assertNotIn("broken", NANO)
|
||
|
||
def test_combo_declares_its_smallest_member(self):
|
||
combo = {"topology": {"kind": "combo", "strategy": "priority", "members": [
|
||
{"kind": "model", "model": "cx/gpt-6-astra"},
|
||
{"kind": "combo", "strategy": "priority", "members": [
|
||
{"kind": "model", "model": "nano/z-ai/glm-5.3-flash-uncensored"},
|
||
{"kind": "model", "model": "nano/nousresearch/hermes-3-llama-3.1-70b"}]}]}}
|
||
self.assertEqual(pencere.coz("X", "combo", "X", combo, NANO)["pencere"], 65536)
|
||
self.assertEqual(pencere.coz("X", "combo", "X", None, NANO)["pencere"], pencere.VARSAYILAN)
|
||
|
||
def test_limit_learned_from_the_target_error_only_shrinks(self):
|
||
home = tempfile.mkdtemp()
|
||
text = "This model's maximum context length is 32768 tokens. However, you requested 51234 tokens"
|
||
self.assertTrue(pencere.tasma_mi(text))
|
||
self.assertEqual(pencere.sinir_oku(text), 32768)
|
||
self.assertEqual(pencere.sinir_oku("prompt is too long: 3040251 tokens > 1000000 maximum"), 1000000)
|
||
gateway = ("Your prompt exceeds the model's context length. Estimated input tokens: 171016. "
|
||
"Max context tokens: 65536.")
|
||
self.assertTrue(pencere.tasma_mi(gateway))
|
||
self.assertEqual(pencere.sinir_oku(gateway), 65536)
|
||
self.assertIsNone(pencere.sinir_oku("rate limit exceeded"))
|
||
self.assertFalse(pencere.tasma_mi("rate limit exceeded"))
|
||
self.assertTrue(pencere.ogren(home, "m", 32768))
|
||
self.assertFalse(pencere.ogren(home, "m", 65536)) # a larger number never overrides
|
||
self.assertEqual(pencere.ogrenilen(home), {"m": 32768})
|
||
self.assertEqual(pencere.coz("m", "model", "cc/claude-opus-5-5", ogr=pencere.ogrenilen(home))["pencere"], 32768)
|
||
|
||
def test_nano_cache_survives_a_network_failure(self):
|
||
home = tempfile.mkdtemp()
|
||
fresh = {"data": [{"id": "A/b", "context_length": 16384, "capabilities": {}}]}
|
||
self.assertEqual(pencere.nano_yukle(home, simdi=1000, getir=lambda: fresh)["a/b"]["pencere"], 16384)
|
||
|
||
def fail():
|
||
raise OSError("offline")
|
||
later = 1000 + pencere.NANO_OMUR_SN + 5
|
||
self.assertEqual(pencere.nano_yukle(home, simdi=later, getir=fail)["a/b"]["pencere"], 16384)
|
||
self.assertEqual(pencere.nano_yukle(tempfile.mkdtemp(), simdi=later, getir=fail), {})
|
||
self.assertEqual((pencere.kisa(8192), pencere.kisa(131072), pencere.kisa(200000)), ("8K", "128K", "200K"))
|
||
|
||
def test_description_line_warns_about_small_window_and_no_tools(self):
|
||
bilgi = {"pencere": 8192, "arac": False, "gorsel": False}
|
||
line = c.aciklama("9router-nano-mlabonne-NeuralDaredevil-8B-abliterated", "9R · nano/NeuralDaredevil-8B-abliterated",
|
||
"model", None, bilgi)
|
||
self.assertEqual(line, "NanoGPT · 8K · araçsız") # warnings first; the rest no longer fits in 30
|
||
self.assertLessEqual(len(line), c.ACIKLAMA_SINIRI)
|
||
self.assertEqual(c.aciklama("9router-cc-claude-opus-5-5", "9R · cc/claude-opus-5-5", "model", None,
|
||
{"pencere": 200000, "arac": True, "gorsel": True}), "Tek model · Claude")
|
||
|
||
|
||
class Guard(unittest.TestCase):
|
||
def req(self, old, last="son soru"):
|
||
return {"instructions": "talimat", "input": [
|
||
{"type": "message", "role": "user", "content": [{"type": "input_text", "text": old}]},
|
||
{"type": "function_call_output", "call_id": "c", "output": "cikti " * 4000},
|
||
{"type": "message", "role": "user", "content": [{"type": "input_text", "text": last}]}]}
|
||
|
||
def test_request_that_fits_is_untouched(self):
|
||
req = self.req("kisa")
|
||
before = json.dumps(req)
|
||
self.assertEqual(pencere.sigdir(req, 272000)[:2], (0, True))
|
||
self.assertEqual(json.dumps(req), before)
|
||
|
||
def test_oversized_old_parts_lose_their_middle_and_the_last_user_message_never_does(self):
|
||
req = self.req("BAS " + "dolgu " * 60000 + " SON", last="yeni " * 3000)
|
||
kirpilan, sigdi, tahmin = pencere.sigdir(req, 65536)
|
||
self.assertTrue(sigdi)
|
||
self.assertGreaterEqual(kirpilan, 1)
|
||
old = req["input"][0]["content"][0]["text"]
|
||
self.assertTrue(old.startswith("BAS ") and old.endswith(" SON"))
|
||
self.assertIn("çıkarıldı (köprü)", old)
|
||
self.assertEqual(req["input"][2]["content"][0]["text"], "yeni " * 3000) # the new message is intact
|
||
self.assertLessEqual(tahmin, int(65536 * pencere.DOLULUK))
|
||
|
||
def test_a_last_message_larger_than_the_window_is_reported_not_sent(self):
|
||
req = self.req("kisa", last="x" * 400000)
|
||
self.assertFalse(pencere.sigdir(req, 32768)[1])
|
||
|
||
|
||
class Phase(unittest.TestCase):
|
||
def run_stream(self, *events, chunk=37):
|
||
adapter = env["SseRewriter"](set())
|
||
raw = sse(*events)
|
||
out = b"".join(adapter.feed(raw[i:i + chunk]) for i in range(0, len(raw), chunk)) + adapter.flush()
|
||
return parse(out)
|
||
|
||
def test_message_before_a_tool_call_is_commentary(self):
|
||
call = {"type": "function_call", "id": "fc_1", "call_id": "call_1", "name": "shell", "arguments": "{}"}
|
||
out = self.run_stream(
|
||
{"type": "response.output_item.added", "item": message("")},
|
||
{"type": "response.output_text.delta", "item_id": "msg_1", "delta": "bakiyorum"},
|
||
{"type": "response.output_item.done", "item": message("bakiyorum")},
|
||
{"type": "response.output_item.added", "item": call},
|
||
{"type": "response.output_item.done", "item": call},
|
||
{"type": "response.completed", "response": {"output": [message("bakiyorum"), call]}})
|
||
self.assertEqual([e["type"] for e in out], [
|
||
"response.output_item.added", "response.output_text.delta", "response.output_item.done",
|
||
"response.output_item.added", "response.output_item.done", "response.completed"]) # order kept
|
||
self.assertEqual(out[2]["item"]["phase"], "commentary")
|
||
self.assertEqual(out[5]["response"]["output"][0]["phase"], "commentary")
|
||
self.assertNotIn("phase", out[5]["response"]["output"][1])
|
||
|
||
def test_last_message_without_tool_calls_is_the_final_answer(self):
|
||
out = self.run_stream(
|
||
{"type": "response.output_item.done", "item": message("ara", "msg_a")},
|
||
{"type": "response.output_item.added", "item": message("", "msg_b")},
|
||
{"type": "response.output_item.done", "item": message("son", "msg_b")},
|
||
{"type": "response.completed", "response": {"output": [message("ara", "msg_a"), message("son", "msg_b")]}})
|
||
done = [e for e in out if e["type"] == "response.output_item.done"]
|
||
self.assertEqual([e["item"]["phase"] for e in done], ["commentary", "final_answer"])
|
||
self.assertEqual([o["phase"] for o in out[-1]["response"]["output"]], ["commentary", "final_answer"])
|
||
self.assertEqual(out[-1]["type"], "response.completed") # done comes first
|
||
|
||
def test_existing_phase_and_cut_streams_are_left_alone(self):
|
||
native = dict(message("x"), phase="final_answer")
|
||
out = self.run_stream({"type": "response.output_item.done", "item": native},
|
||
{"type": "response.completed", "response": {"output": [dict(native)]}})
|
||
self.assertEqual(out[0]["item"]["phase"], "final_answer")
|
||
# No terminal event: the held message is still delivered, unmarked rather than guessed.
|
||
cut = self.run_stream({"type": "response.output_item.done", "item": message("yarim")})
|
||
self.assertEqual(len(cut), 1)
|
||
self.assertNotIn("phase", cut[0]["item"])
|
||
|
||
def test_overflow_failure_gets_the_code_codex_understands(self):
|
||
seen = []
|
||
adapter = env["SseRewriter"](set(), hedef="model-x", tasma=lambda hedef, metin: seen.append((hedef, metin)))
|
||
failed = {"type": "response.failed", "response": {"error": {
|
||
"code": "server_error", "message": "maximum context length is 32768 tokens"}}}
|
||
out = parse(adapter.feed(sse(failed)) + adapter.flush())
|
||
self.assertEqual(out[0]["response"]["error"]["code"], "context_length_exceeded")
|
||
self.assertEqual(seen[0][0], "model-x")
|
||
other = {"type": "response.failed", "response": {"error": {"code": "rate_limit", "message": "slow down"}}}
|
||
self.assertEqual(parse(adapter.feed(sse(other)))[0]["response"]["error"]["code"], "rate_limit")
|
||
|
||
|
||
class GatewayErrors(unittest.TestCase):
|
||
def test_permanent_gateway_error_becomes_a_failure_codex_shows_at_once(self):
|
||
adapter = env["SseRewriter"](set())
|
||
raw = sse({"type": "error", "error": {"type": "upstream_error", "message": "upstream 402"}})
|
||
out = parse(adapter.feed(raw) + adapter.flush())
|
||
self.assertEqual(out[0]["type"], "response.failed")
|
||
self.assertEqual(out[0]["response"]["error"]["code"], "insufficient_quota")
|
||
self.assertIn("upstream 402", out[0]["response"]["error"]["message"])
|
||
# A transient error is left alone so that Codex retries it.
|
||
raw = sse({"type": "error", "error": {"type": "upstream_error", "message": "upstream 503"}})
|
||
self.assertEqual(parse(env["SseRewriter"](set()).feed(raw))[0]["type"], "error")
|
||
|
||
|
||
class Images(unittest.TestCase):
|
||
def req(self):
|
||
img = {"type": "input_image", "image_url": "data:image/png;base64," + "A" * 2000}
|
||
return {"input": [
|
||
{"type": "message", "role": "user", "content": [{"type": "input_text", "text": "bak"}, dict(img)]},
|
||
{"type": "message", "role": "assistant", "content": [{"type": "output_text", "text": "OKUDUM"}]},
|
||
{"type": "function_call_output", "call_id": "c", "output": [dict(img)]},
|
||
{"type": "message", "role": "user", "content": [{"type": "input_text", "text": "ne yaziyordu"}]}]}
|
||
|
||
def test_text_model_gets_notes_vision_model_keeps_user_images(self):
|
||
text_only = self.req()
|
||
self.assertEqual(resim_context.resimleri_sadelestir(text_only)[0], 2)
|
||
self.assertNotIn("input_image", json.dumps(text_only))
|
||
vision = self.req()
|
||
self.assertEqual(resim_context.resimleri_sadelestir(vision, gorsel=True)[0], 1) # only the tool output
|
||
self.assertEqual(vision["input"][0]["content"][1]["type"], "input_image")
|
||
self.assertNotIn("input_image", json.dumps(vision["input"][2]))
|
||
big = self.req()
|
||
self.assertEqual(resim_context.resimleri_sadelestir(big, tut_bayt=500, gorsel=True)[0], 2) # over budget
|
||
|
||
|
||
if __name__ == "__main__":
|
||
unittest.main()
|