Separate encrypted reasoning from actual compaction, stop migration after native return, and retain full history and recent exchanges. Add typed settings, Gemini fallback, verified provider windows, bounded summaries and durable concurrent checkpoints. Validation: 245 bridge tests plus focused concurrency tests; 71 settings/connection tests; 66 supplementary tests; TypeScript checks; renderer and Electron bundles; real primary/native/fallback summary probes and 700K input acceptance.
259 lines
15 KiB
Python
259 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_current_provider_aliases_keep_their_own_capacity(self):
|
||
self.assertEqual(pencere.coz('agy-gemini-3.8-flash-high', 'model', 'Gemini 3.8 Flash (High)')['pencere'], 1000000)
|
||
self.assertEqual(pencere.yol_bilgisi('codex/gpt-6-astra', {})['pencere'], 272000)
|
||
|
||
def test_provider_data_and_local_table_preserve_large_windows(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"], 1048576)
|
||
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)
|
||
self.assertEqual(pencere.sinir_oku("Input exceeds maximum input tokens: estimated 1125036 input tokens, max input 1048576."), 1048576)
|
||
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()
|