bridge.py is now a small splitter with no features: a request for one of Codex's own models goes to chatgpt.com byte for byte, a request for an added model goes to kopru.py, which runs as its own process. Codex's own models no longer pass through the feature code at all, and restarting or losing the bridge process does not touch them. Measured with real Codex on spare ports: own model answers; forced compaction on the own model completes (twice) while the bridge process is restarted; added model answers; no own-model request reaches the bridge process. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
254 lines
15 KiB
Python
254 lines
15 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("kopru.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"), "time": __import__("time"), "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[-1]["type"], "response.completed") # visible answer, turn ends normally
|
||
self.assertIn("kredi ya da kota", out[1]["delta"])
|
||
self.assertIn("upstream 402", out[1]["delta"])
|
||
raw = sse({"type": "error", "error": {"type": "upstream_error", "message": "upstream 400"}})
|
||
out = parse(env["SseRewriter"](set()).feed(raw))
|
||
self.assertIn("nedenini bildirmedi", out[1]["delta"])
|
||
self.assertEqual(out[-1]["type"], "response.completed")
|
||
# 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
|
||
|
||
|
||
|
||
class ClaudeOrani(unittest.TestCase):
|
||
def test_claude_targets_are_estimated_more_strictly_than_others(self):
|
||
import pencere as p
|
||
req = {"input": [{"type": "function_call_output", "call_id": "c", "output": "x" * 600_000},
|
||
{"type": "message", "role": "user", "content": "son"}]}
|
||
self.assertEqual(p.oran_sec("9router-cc-claude-opus-5-5"), p.CLAUDE_BAYT_BOLU_TOKEN)
|
||
self.assertEqual(p.oran_sec("9router-ag-gemini-3.8-flash-low"), p.BAYT_BOLU_TOKEN)
|
||
self.assertEqual(p.sigdir({"input": [dict(i) for i in req["input"]]}, 200_000)[0], 0) # 187500: fits
|
||
trimmed, fits, estimate = p.sigdir(req, 200_000, p.oran_sec("cc-claude-opus")) # 230769: trimmed
|
||
self.assertEqual((trimmed, fits), (1, True))
|
||
self.assertLess(estimate, 194_000)
|
||
self.assertTrue(p.kapali_hata_mi("upstream 400"))
|
||
# A per-minute rate limit is neither an overflow nor a window size to learn.
|
||
rate = "Rate limit reached: limit of 32768 tokens per minute, context window ok"
|
||
self.assertFalse(p.tasma_mi(rate))
|
||
self.assertIsNone(p.sinir_oku(rate))
|
||
self.assertTrue(p.tasma_mi("maximum context length is 65536 tokens"))
|
||
self.assertFalse(p.kapali_hata_mi("upstream 400: invalid tool schema"))
|
||
|
||
if __name__ == "__main__":
|
||
unittest.main()
|