fix(codex-bridge): exempt the voice combo from Jev checks

This commit is contained in:
yilsem committed 2026-10-12 16:42:22 +03:00
1 parent 8bd662ea92
commit 017c6629d2
4 files changed
+73 -7

No files matched your search

@@ -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 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." 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 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. yalniz ondan sonrakiler denenir. Katmanlar OmniRoute combolaridir (KATMAN); kopru onlari yalniz adiyla cagirir.
@@ -29,6 +31,13 @@ SORU = {
} }
_ANAHTAR = {"okundu": False, "deger": ""} _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): def sonrakiler(hedef):
"""Jev gecirmezse sirayla denenecek katmanlar. Secili model bir katmansa ondan sonrakiler; katman degilse """Jev gecirmezse sirayla denenecek katmanlar. Secili model bir katmansa ondan sonrakiler; katman degilse
+8 -5
View File
@@ -871,7 +871,7 @@ class H(http.server.BaseHTTPRequestHandler):
self.send_header("Transfer-Encoding", "chunked") self.send_header("Transfer-Encoding", "chunked")
self.end_headers() 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. """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, 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.""" 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 gecmedi = False
if hata: if hata:
gecmedi = True gecmedi = True
elif metin and jev_denetim.muaf_mi(hedef):
log(f"{tag} Jev atlandi: Sesli-Hizli-Zeki")
elif metin: elif metin:
t0 = time.time() t0 = time.time()
puan = jev_denetim.sor(istek, metin, jev_denetim.anahtar()) puan = jev_denetim.sor(istek, metin, jev_denetim.anahtar())
@@ -944,6 +946,7 @@ class H(http.server.BaseHTTPRequestHandler):
if yeni is None: if yeni is None:
break break
resp, rewriter, tag = yeni 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") log(f"{tag} katman denetimi: adim bu katmana verildi")
if not kalan: if not kalan:
return self._stream(resp, resp.status, tag, rewriter, izle, basliksiz=True) 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) 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} hdrs = {"Authorization": f"Bearer {route['key']}", "Content-Type": "application/json", "Accept": "text/event-stream", "User-Agent": UA}
last = None 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 # 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, # 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 # 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 time.sleep(min(2 * attempt, 8)); continue
log(f"{tag} HTTP {e.code} {errbody[:200]!r}") log(f"{tag} HTTP {e.code} {errbody[:200]!r}")
if jev_sira: # secili model hata verdi: adimi siradaki katman yapar 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): if pencere.tasma_mi(etxt):
# Hedef "baglam sigmiyor" dedi: Codex'in tanidigi tasma hatasi olarak iletilir ve hedefin # Hedef "baglam sigmiyor" dedi: Codex'in tanidigi tasma hatasi olarak iletilir ve hedefin
# bildirdigi gercek sinir ogrenilir (sonraki model listesinde pencere kuculur). # 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()}") log(f"{tag} ozet kopru: {akis.sonuc_notu()}")
return sonuc return sonuc
if jev_sira and resp.status == 200: 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) return self._stream(resp, resp.status, tag, rewriter, izle)
if jev_sira: # secili modele ulasilamadi: adimi siradaki katman yapar 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}"}}) self._send_json(503, {"error": {"message": f"bridge: hedef ulasilamadi {last}"}})
do_POST = _handle do_POST = _handle
@@ -12,7 +12,7 @@ import unittest
import urllib.error import urllib.error
import urllib.request import urllib.request
from pathlib import Path from pathlib import Path
from unittest.mock import Mock from unittest.mock import Mock, patch
import ide_catalog import ide_catalog
import is_hatti 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["MODEL_MAP"].update({"x/combo": ("r", "Takim", None), "x/tek": ("r", "cc/claude", None)})
self.env["urllib"].request.urlopen.side_effect = error 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): def test_combo_dropped_after_sending_is_not_resent_but_a_single_model_is(self):
import http.client import http.client
self.external(http.client.RemoteDisconnected("closed")) self.external(http.client.RemoteDisconnected("closed"))
@@ -128,6 +128,7 @@ class Akis(unittest.TestCase):
sahte.karar = jev_denetim.karar sahte.karar = jev_denetim.karar
sahte.komutlar = jev_denetim.komutlar sahte.komutlar = jev_denetim.komutlar
sahte.hazirlik_mi = jev_denetim.hazirlik_mi 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"), env = {"jev_denetim": sahte, "yarim": yarim, "time": time, "http": __import__("http.client"),
"log": self.logs.append, "RESP_HOP": set(), "json": json} "log": self.logs.append, "RESP_HOP": set(), "json": json}
adlar = ("_akis_basligi", "_denetimli_akis", "_stream") 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.asked, ["Fenerbahçe'nin 13 puanı var."])
self.assertEqual(self.opened, []) 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): 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.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")) self.calistir(sse("Bunu yapamam."), sira=("Katman-1", "Katman-2", "Katman-3", "Katman-4"))