From 017c6629d2245a44e898ec93f9941ef9b282ef74 Mon Sep 17 00:00:00 2001 From: yilsem Date: Mon, 12 Oct 2026 16:42:22 +0300 Subject: [PATCH] fix(codex-bridge): exempt the voice combo from Jev checks --- .../assets/codex-bridge/jev_denetim.py | 11 ++++++- apps/desktop/assets/codex-bridge/kopru.py | 13 +++++---- .../assets/codex-bridge/test_denetim_ek.py | 27 ++++++++++++++++- .../assets/codex-bridge/test_jev_denetim.py | 29 +++++++++++++++++++ 4 files changed, 73 insertions(+), 7 deletions(-) diff --git a/apps/desktop/assets/codex-bridge/jev_denetim.py b/apps/desktop/assets/codex-bridge/jev_denetim.py index 47b667c..7b44101 100644 --- a/apps/desktop/assets/codex-bridge/jev_denetim.py +++ b/apps/desktop/assets/codex-bridge/jev_denetim.py @@ -4,7 +4,9 @@ Kullanici karari (11 Ekim 2026): "Jev kati olsun. Af yok istisna yok. Herhangi b gecirmezse 1.Katmana yonlendirecek. 1.Katman combosu gecmezse 2.Katmana, 2.Katman gecmezse 3.Katmana, 3.Katman gecmezse 4.Katmana. 4.Katman combosu gelirse Jev bunu sorgulamadan gecirecek." -Yani denetim secili modelin katman olup olmamasina bakmaz: kopruye ugrayan her ek modelin cevabi denetlenir. +12 Ekim 2026 istisnasi: çözülmüş hedefi tam Sesli-Hizli-Zeki olan cevap Jev'e sorulmaz. +Taşıma/boş yanıt kurtarması ve yedek katmanların denetimi korunur. +Diğer hedeflerde denetim secili modelin katman olup olmamasina bakmaz. Gecmezse adimi sirayla Katman-1 -> Katman-2 -> Katman-3 -> Katman-4 yapar; secili model zaten bir katmansa yalniz ondan sonrakiler denenir. Katmanlar OmniRoute combolaridir (KATMAN); kopru onlari yalniz adiyla cagirir. @@ -29,6 +31,13 @@ SORU = { } _ANAHTAR = {"okundu": False, "deger": ""} +def muaf_mi(hedef): + """12 Ekim 2026: yalnız çözülmüş exact sesli combo hedefi Jev'den muaftır. + + Taşıma hatalarının katman sırasını değiştirmez; yedek model kendi adına denetlenir. + """ + return hedef == "Sesli-Hizli-Zeki" + def sonrakiler(hedef): """Jev gecirmezse sirayla denenecek katmanlar. Secili model bir katmansa ondan sonrakiler; katman degilse diff --git a/apps/desktop/assets/codex-bridge/kopru.py b/apps/desktop/assets/codex-bridge/kopru.py index 660b24a..fba4a3f 100644 --- a/apps/desktop/assets/codex-bridge/kopru.py +++ b/apps/desktop/assets/codex-bridge/kopru.py @@ -871,7 +871,7 @@ class H(http.server.BaseHTTPRequestHandler): self.send_header("Transfer-Encoding", "chunked") self.end_headers() - def _denetimli_akis(self, resp, tag, rewriter, izle, istek, sira, ac): + def _denetimli_akis(self, resp, tag, rewriter, izle, istek, sira, ac, hedef=None): """Katman denetimi (jev_denetim.py): cevap istemciye verilmeden once tamami okunur ve Jev'e sorulur. Gecerse oldugu gibi verilir; gecmezse atilir ve ayni adimi siradaki katman yapar (ac(ad): (resp, rewriter, tag)). Son katmanin cevabi sorulmadan akar. Beklerken SSE yorum satiri gider ki Codex akisi kopmus saymasin.""" @@ -918,6 +918,8 @@ class H(http.server.BaseHTTPRequestHandler): gecmedi = False if hata: gecmedi = True + elif metin and jev_denetim.muaf_mi(hedef): + log(f"{tag} Jev atlandi: Sesli-Hizli-Zeki") elif metin: t0 = time.time() puan = jev_denetim.sor(istek, metin, jev_denetim.anahtar()) @@ -944,6 +946,7 @@ class H(http.server.BaseHTTPRequestHandler): if yeni is None: break resp, rewriter, tag = yeni + hedef = ad # Muafiyet yalnız ilk exact hedefe ait; yedek katmana taşınmaz. log(f"{tag} katman denetimi: adim bu katmana verildi") if not kalan: return self._stream(resp, resp.status, tag, rewriter, izle, basliksiz=True) @@ -1598,7 +1601,7 @@ class H(http.server.BaseHTTPRequestHandler): open(os.path.join(LOGDIR, f"{n:04d}_req.json"), "wb").write(data) hdrs = {"Authorization": f"Bearer {route['key']}", "Content-Type": "application/json", "Accept": "text/event-stream", "User-Agent": UA} last = None - # Katman denetimi (jev_denetim.py): kopruye ugrayan HER ek modelin cevabi Jev'e sorulur; gecmezse adim + # Katman denetimi (jev_denetim.py): Sesli-Hizli-Zeki dışında ek modeller Jev'e sorulur; gecmezse adim # sirayla Katman-1 -> 2 -> 3 -> 4 yapar (secili model zaten katmansa ondan sonrakiler). Katman-4 sorulmadan # gecer. Katmanlar OmniRoute combolaridir: yedek her zaman AITURK gecidi (ide_route) uzerinden acilir, # secili model baska bir rotada olsa bile. Gecit yoksa denetim yapilmaz. Ozet isteginde ve aracsiz @@ -1643,7 +1646,7 @@ class H(http.server.BaseHTTPRequestHandler): time.sleep(min(2 * attempt, 8)); continue log(f"{tag} HTTP {e.code} {errbody[:200]!r}") if jev_sira: # secili model hata verdi: adimi siradaki katman yapar - return self._denetimli_akis(None, tag, rewriter, izle, jev_istek, jev_sira, jev_ac) + return self._denetimli_akis(None, tag, rewriter, izle, jev_istek, jev_sira, jev_ac, hedef=target) if pencere.tasma_mi(etxt): # Hedef "baglam sigmiyor" dedi: Codex'in tanidigi tasma hatasi olarak iletilir ve hedefin # bildirdigi gercek sinir ogrenilir (sonraki model listesinde pencere kuculur). @@ -1673,10 +1676,10 @@ class H(http.server.BaseHTTPRequestHandler): log(f"{tag} ozet kopru: {akis.sonuc_notu()}") return sonuc if jev_sira and resp.status == 200: - return self._denetimli_akis(resp, tag, rewriter, izle, jev_istek, jev_sira, jev_ac) + return self._denetimli_akis(resp, tag, rewriter, izle, jev_istek, jev_sira, jev_ac, hedef=target) return self._stream(resp, resp.status, tag, rewriter, izle) if jev_sira: # secili modele ulasilamadi: adimi siradaki katman yapar - return self._denetimli_akis(None, tag, rewriter, izle, jev_istek, jev_sira, jev_ac) + return self._denetimli_akis(None, tag, rewriter, izle, jev_istek, jev_sira, jev_ac, hedef=target) self._send_json(503, {"error": {"message": f"bridge: hedef ulasilamadi {last}"}}) do_POST = _handle diff --git a/apps/desktop/assets/codex-bridge/test_denetim_ek.py b/apps/desktop/assets/codex-bridge/test_denetim_ek.py index 3dafb80..2bbad18 100644 --- a/apps/desktop/assets/codex-bridge/test_denetim_ek.py +++ b/apps/desktop/assets/codex-bridge/test_denetim_ek.py @@ -12,7 +12,7 @@ import unittest import urllib.error import urllib.request from pathlib import Path -from unittest.mock import Mock +from unittest.mock import Mock, patch import ide_catalog import is_hatti @@ -364,6 +364,31 @@ class Zincir(unittest.TestCase): self.env["MODEL_MAP"].update({"x/combo": ("r", "Takim", None), "x/tek": ("r", "cc/claude", None)}) self.env["urllib"].request.urlopen.side_effect = error + def test_resolved_voice_target_and_codex_instructions_reach_the_stream_guard(self): + self.env["ROUTES"]["aiturk-ide"] = { + "url": "https://example.invalid/v1/responses", "key": "test-only", "flatten_tools": True} + self.env["MODEL_MAP"]["ide/voice-alias"] = ("aiturk-ide", "Sesli-Hizli-Zeki", None) + req = {"model": "ide/voice-alias", "instructions": "Codex system instructions unchanged", + "input": [developer("Codex developer instructions unchanged"), user("selam")]} + for failure in (None, urllib.error.URLError(ConnectionResetError("reset")), + urllib.error.HTTPError("https://example.invalid", 400, "bad", {}, io.BytesIO(b"bad"))): + with self.subTest(failure=type(failure).__name__): + opener = self.env["urllib"].request.urlopen + opener.reset_mock() + opener.side_effect = failure + opener.return_value = Mock(status=200) + fake = Sahte() + fake._denetimli_akis = Mock() + with patch.object(self.env["jev_denetim"], "anahtar", return_value="test-only"): + fake._responses(1, "/responses", json.dumps(req).encode()) + fake._denetimli_akis.assert_called_once() + self.assertEqual(fake._denetimli_akis.call_args.kwargs, {"hedef": "Sesli-Hizli-Zeki"}) + self.assertEqual(fake._denetimli_akis.call_args.args[5], list(self.env["jev_denetim"].KATMAN)) + sent = json.loads(opener.call_args.args[0].data) + self.assertEqual(sent["model"], "Sesli-Hizli-Zeki") + self.assertEqual(sent["instructions"], req["instructions"]) + self.assertEqual(sent["input"][0], req["input"][0]) + def test_combo_dropped_after_sending_is_not_resent_but_a_single_model_is(self): import http.client self.external(http.client.RemoteDisconnected("closed")) diff --git a/apps/desktop/assets/codex-bridge/test_jev_denetim.py b/apps/desktop/assets/codex-bridge/test_jev_denetim.py index 8896de6..85abe5c 100644 --- a/apps/desktop/assets/codex-bridge/test_jev_denetim.py +++ b/apps/desktop/assets/codex-bridge/test_jev_denetim.py @@ -128,6 +128,7 @@ class Akis(unittest.TestCase): sahte.karar = jev_denetim.karar sahte.komutlar = jev_denetim.komutlar sahte.hazirlik_mi = jev_denetim.hazirlik_mi + sahte.muaf_mi = jev_denetim.muaf_mi env = {"jev_denetim": sahte, "yarim": yarim, "time": time, "http": __import__("http.client"), "log": self.logs.append, "RESP_HOP": set(), "json": json} adlar = ("_akis_basligi", "_denetimli_akis", "_stream") @@ -170,6 +171,34 @@ class Akis(unittest.TestCase): self.assertEqual(self.asked, ["Fenerbahçe'nin 13 puanı var."]) self.assertEqual(self.opened, []) + def test_exact_voice_target_skips_jev_but_not_transport_fallback(self): + body = sse("Bunu yapamam.") + self.puanlar = [{"ret": 0.9, "sart": 0.0}] + self.h._denetimli_akis(Yanit(body), "[1]", None, None, "yap", + list(jev_denetim.KATMAN), self.ac, hedef="Sesli-Hizli-Zeki") + self.assertEqual(self.sent(), body) + self.assertEqual(self.asked, []) + self.assertEqual(self.opened, []) + + def test_voice_transport_failure_still_checks_the_fallback_model(self): + self.puanlar = [{"ret": 0.9, "sart": 0.0}] + self.h._denetimli_akis(None, "[1]", None, None, "yap", + list(jev_denetim.KATMAN), self.ac, hedef="Sesli-Hizli-Zeki") + self.assertEqual(self.opened, ["Katman-1", "Katman-2"]) + self.assertEqual(self.asked, ["bir", "iki"]) + self.assertEqual(self.sent(), self.cevap["Katman-2"]) + + def test_voice_lookalikes_are_still_checked(self): + for target in ("sesli-hizli-zeki", "Sesli-Hizli-Zeki-v2", "ide/Sesli-Hizli-Zeki", + "Sesli-Hizli-Zeki ", "Other-Combo"): + with self.subTest(target=target): + self.setUp() + self.puanlar = [{"ret": 0.9, "sart": 0.0}] + self.h._denetimli_akis(Yanit(sse("hayir")), "[1]", None, None, "yap", + list(jev_denetim.KATMAN), self.ac, hedef=target) + self.assertEqual(self.asked, ["hayir", "bir"]) + self.assertEqual(self.opened, ["Katman-1"]) + def test_a_blocked_non_layer_model_goes_to_layer_1_first(self): self.puanlar = [{"ret": 0.9, "sart": 0.0}] # the selected (non-layer) model is blocked self.calistir(sse("Bunu yapamam."), sira=("Katman-1", "Katman-2", "Katman-3", "Katman-4"))