fix(codex-bridge): leave Codex's own models alone

User decision, 11 Oct 2026: Codex's own (subscription) models are one side,
the models we add are the other. A request for one of Codex's own models now
goes to chatgpt.com as it is: no model command, lone-dot answer, info note,
work-line instruction, half-answer note or compaction handling. Codex compacts
those chats itself; the bridge making the summary on gpt-6-astra failed four
times this morning and left Codex with 'Error running remote compact task'.

Only the bridge's own traces are still cleaned (foreign ids and bridge-made
summaries left by an added model earlier in the same chat); without any, the
body is byte-identical.

Voice switching stays among the added models. Asking for one of Codex's own
models by voice is answered with how to do it: pick it in the model picker and
send a lone dot.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
yilsemandClaude Opus 5.5 committed 2026-10-11 05:16:26 +03:00
1 parent dd5f99c406
commit 12eeec7b0f
5 files changed
+139 -77

No files matched your search

+73 -49
View File
@@ -790,6 +790,23 @@ def bilinen_modeller():
if m.get("slug") and m["slug"] not in gorulen]
return liste
def ek_model_mu(slug):
"""Bizim sonradan ekledigimiz model mi (ide/..., 9r/..., bridge.json'da tanimli)? Degilse Codex'in kendi
(abonelik) modelidir ve kopru ona DOKUNMAZ. Kullanici karari (11 Ekim 2026): "Codex'in modelleri bir tarafta.
Bizim sonradan eklediklerimiz diger tarafta. Bizim sonradan eklediklerimiz kopru kullaniyor. Codex'in
modelleri kopru kullanmiyor." """
slug = str(slug or "")
onek = (CFG.get("nine_router_catalog") or {}).get("prefix", "9r/")
return bool(slug) and (MODEL_MAP.get(slug) is not None or slug.startswith("ide/") or bool(onek and slug.startswith(onek)))
def yonlendirilen(sohbet, gelen):
"""Sohbetin sesle secilmis ek modeli; yoksa None. Codex'in kendi modeline giden eski kayit yok sayilir:
kendi modellerine sesle gecilmez, ekrandaki seciciden secilir."""
hedef = YONLENDIRME.al(sohbet, gelen)
return hedef if hedef and ek_model_mu(hedef) else None
_GECIT = {"zaman": 0.0, "liste": []}
def gecit_modelleri():
@@ -1185,7 +1202,7 @@ class H(http.server.BaseHTTPRequestHandler):
komut = model_gecis.komut_coz(metin) if metin else None
if not komut:
return None
etkin = YONLENDIRME.al(sohbet, gelen) or gelen
etkin = yonlendirilen(sohbet, gelen) or gelen
ortada = model_gecis.tur_ortasi(req, sira)
if komut["tur"] == "sor":
if ortada:
@@ -1197,9 +1214,27 @@ class H(http.server.BaseHTTPRequestHandler):
return None
log(f"[{n} {gelen}] adsiz model komutu: nasil soylenecegi anlatildi")
return f"Sohbetin modeli şu an {model_adi(etkin)}. " + model_gecis.NASIL
sonuc = model_gecis.eslestir(komut["ad"], bilinen_modeller())
# Sesle gecis yalniz bizim ekledigimiz modeller arasindadir; Codex'in kendi modelleri aday degildir
# (kullanici karari, 11 Ekim 2026). Iki taraf arasinda gecis: ekrandaki seciciden model secilir, "." yazilir.
bilinen = bilinen_modeller()
sonuc = model_gecis.eslestir(komut["ad"], [(s, a) for s, a in bilinen if ek_model_mu(s)])
if not sonuc:
sonuc = model_gecis.eslestir(komut["ad"], gecit_modelleri()) # Codex listesinde olmayan gecit modeli
if not sonuc:
ozgun = model_gecis.eslestir(komut["ad"], [(s, a) for s, a in bilinen if not ek_model_mu(s)])
if ozgun and (komut.get("kesin", True) or "slug" in ozgun):
kim = f"{ozgun['ad']} Codex'in kendi modeli" if "ad" in ozgun else "Bu ad Codex'in kendi modellerine uyuyor"
ileti = (f"{kim}; Codex'in kendi modellerine sesle geçilmiyor, model değişmedi. Geçmek için ekrandaki "
f"model seçiciden o modeli seç ve tek başına \".\" yaz. Sohbetin modeli hâlâ {model_adi(etkin)}.")
log(f"[{n} {gelen}] model komutu: Codex'in kendi modeli istendi {komut['ad']}, gecilmedi")
if not ortada:
return ileti
if not komut.get("kesin", True):
return None
model_gecis.metni_degistir(req, sira, "[Köprü notu: kullanıcı Codex'in kendi modeline geçmek istedi; bu sesle "
"yapılamıyor, model değişmedi. İşine devam et; cevabının sonunda şunu ilet: " + ileti + "]")
self.komut_govdeyi_degistirdi = True
return None
if not komut.get("kesin", True) and not (sonuc and "slug" in sonuc):
return None # "modeli" denmemis ve ad tek modele oturmuyor: komut degil, modele gider
if not sonuc:
@@ -1236,6 +1271,32 @@ class H(http.server.BaseHTTPRequestHandler):
return (f"Sohbetin modeli artık {sonuc['ad']}. Önceki model {model_adi(etkin)} idi. "
f"Bundan sonraki işleri {sonuc['ad']} yapacak; hangi model diye sorulursa bunu söyle.{donus}")
def _ozgun(self, n, path, body, req, slug):
"""Codex'in kendi (abonelik) modeli. Kullanici karari (11 Ekim 2026): "Codex kendi modelini kullanirken
kopruyle bir bagi olmamali. Codex kendi modelinden kendi modeline gectigi zaman kopruyle bir bagi olmamali.
Codex'in kendi modeline dokunulmamali." Istek chatgpt.com'a OLDUGU GIBI gider: model komutu, nokta
karsilama, bilgi notu, is hatti talimati, yarim cevap notu ve sikistirmaya karisma YOK. Sikistirmayi Codex
kendisi OpenAI'de yapar (olcum 11 Ekim 2026: kopru ozgun modelde ozeti kendi uretmeye calisip dort kez
"ozet uretilemedi" verdi, Codex "Error running remote compact task" ile takildi).
Tek dokunus koprunun KENDI izleridir ve yalniz sohbette daha once ek model kullanildiysa olur: ek modelin
urettigi yabanci kimlikler ve koprunun urettigi duz metin ozet ogeleri. Bunlar kalirsa chatgpt.com istegi
reddeder ("Item ... not found"). Iz yoksa govde bayt bayt ayni kalir."""
fixed = atilan = acilan = 0
try:
fixed = fix_tool_ids(req)
atilan = drop_foreign_items(req)
if (CFG.get("ozet_kopru") or {}).get("enabled", False):
acilan = ozet_kopru.ozetleri_ac(req, yabanci_hedef=False)["acilan"]
if fixed or atilan or acilan:
body = json.dumps(req, ensure_ascii=False).encode("utf-8")
log(f"[{n} CHATGPT {slug}] kopru izleri temizlendi: {fixed} arac kimligi, {atilan} yabanci oge, "
f"{acilan} kopru ozeti")
except Exception as e:
fixed = atilan = acilan = 0 # govde degismeden gider
log(f"[{n} CHATGPT {slug}] kopru izi temizligi atlandi, istek oldugu gibi gidiyor: {type(e).__name__}: {e}")
return self._passthrough(n, path, body, tagname=f"CHATGPT {slug}", rewritten=bool(fixed or atilan or acilan))
def _responses(self, n, path, body):
try:
req = json.loads(decompress(body, self.headers.get("Content-Encoding")))
@@ -1244,6 +1305,8 @@ class H(http.server.BaseHTTPRequestHandler):
return self._send_json(400, {"error": {"message": f"bridge: govde cozulemedi: {e}"}})
slug = req.get("model", "")
gelen = slug # Codex'in bu sohbet icin gonderdigi ad (listeden secilen model)
if not ek_model_mu(gelen):
return self._ozgun(n, path, body, req, slug)
govde_degisti = False # istek govdesi kopruce degistirildi: abonelik yolunda yeniden yazilir
once = len(req.get("input") or []), json.dumps((req.get("input") or [None])[-3:], ensure_ascii=False, sort_keys=True)
sohbet = str(req.get("prompt_cache_key") or "")
@@ -1259,11 +1322,11 @@ class H(http.server.BaseHTTPRequestHandler):
if self.komut_govdeyi_degistirdi:
govde_degisti = True # komut mesaji nota cevrildi: abonelik yolunda da degismis govde gider
try:
if model_gecis.gecmisi_temizle(req, bilinen_modeller(), YONLENDIRME.al(sohbet, gelen) or gelen):
if model_gecis.gecmisi_temizle(req, bilinen_modeller(), yonlendirilen(sohbet, gelen) or gelen):
govde_degisti = True
except Exception as e:
log(f"[{n} {gelen}] gecmis komut temizligi atlandi: {type(e).__name__}: {e}")
hedef = YONLENDIRME.al(sohbet, gelen)
hedef = yonlendirilen(sohbet, gelen)
if hedef and hedef != gelen:
req["model"] = slug = hedef
log(f"[{n} {gelen}] sohbet yonlendirmesi: istek {hedef} modeline gidiyor")
@@ -1333,53 +1396,14 @@ class H(http.server.BaseHTTPRequestHandler):
# deyip duruyordu (olcum 11 Ekim 2026, sunucu gecisi sirasinda). Kullanici durumu duysun.
if CFG.get("kesinti_mesaji", True):
return self._yerel_yanit(gelen, "AITURK modellerine şu an ulaşılamıyor (AITURK sunucusu cevap vermiyor ya da "
"bağlantı kapalı); bu model şimdi cevap veremez. Beklemek istemezsen Codex'in kendi "
"modeline geç: \"modeli gpt 6 astra yap\" de. AITURK geri gelince tekrar "
"\"modeli … yap\" diyerek dönebilirsin.")
"bağlantı kapalı); bu model şimdi cevap veremez. Beklemek istemezsen ekrandaki model "
"seçiciden Codex'in kendi modellerinden birini seç ve tek başına \".\" yaz.")
return self._send_json(503, {"error": {"message": "AITURK IDE Codex Baglama kapali ya da IDE oturumu bulunamadi; bu model su an kullanilamiyor."}})
if mapping is None:
# abonelik modeli: chatgpt.com'a oldugu gibi
fixed = fix_tool_ids(req)
atilan = drop_foreign_items(req)
degisti = bool(yarim_eklenen) or govde_degisti
ozet_akisi = None
if ozet_acik:
# Ozet bagdastirici (ozet_kopru.py; 9 Ekim 2026'da yalniz bu parca geri acildi): koprunun urettigi
# duz metin ozetleri OpenAI modeline de mesaj olarak gider.
try:
ob = ozet_kopru.ozetleri_ac(req, yabanci_hedef=False)
# kapsam "hepsi": OpenAI modelinde de ozeti kopru DUZ METIN urettirir; boylece ozet sonradan
# secilen her saglayicida okunur. kapsam "9router": OpenAI ozeti yerlesik (sifreli) kalir.
if ozet_cfg.get("kapsam", "hepsi") == "hepsi" and ozet_kopru.tetik_mi(req):
OZET_FRENI.sinir = int(ozet_cfg.get("dongu_siniri", 8)); OZET_FRENI.pencere = int(ozet_cfg.get("dongu_pencere_sn", 600)); OZET_FRENI.art_arda = int(ozet_cfg.get("dongu_art_arda", 3))
if not OZET_FRENI.izin(str(req.get("prompt_cache_key") or slug), time.time()):
log(f"[{n} CHATGPT {slug}] ozet kopru: DONGU FRENI, ozet istegi reddedildi")
return self._send_json(400, {"error": {"message": ozet_kopru.DONGU_MESAJI % OZET_FRENI.sinir,
"type": "invalid_request_error", "code": "astrabridge_ozet_dongusu"}})
girdi_bayt = len(json.dumps(req.get("input") or [], ensure_ascii=False).encode("utf-8"))
ozet_kopru.ozet_istegi_hazirla(req, ozet_cfg.get("istem"))
ozet_akisi = ozet_kopru.OzetAkisi(None, tohum=str(n), en_az=ozet_kopru.en_az_karakter(girdi_bayt, ozet_cfg))
ob["acilan"] += 1
log(f"[{n} CHATGPT {slug}] ozet kopru: sikistirma istegi duz metin ozet istegine cevrildi (girdi={len(req.get('input', []))} oge)")
except Exception as e:
ob = {"acilan": 0}; ozet_akisi = None
log(f"[{n} CHATGPT {slug}] ozet kopru atlandi: {type(e).__name__}: {e}")
if ob["acilan"]:
degisti = True
if ozet_akisi is None:
log(f"[{n} CHATGPT {slug}] ozet kopru: {ob['acilan']} kopru ozeti duz metne acildi")
if fixed or atilan or degisti:
body = json.dumps(req, ensure_ascii=False).encode("utf-8")
parcalar = []
if fixed: parcalar.append(f"{fixed} arac kimligi duzeltildi")
if atilan: parcalar.append(f"{atilan} yabanci oge atildi")
if parcalar: log(f"[{n} CHATGPT {slug}] " + ", ".join(parcalar))
if ozet_akisi is not None:
sonuc = self._passthrough(n, path, body, tagname=f"CHATGPT {slug}", rewritten=True, rewriter=ozet_akisi)
log(f"[{n} CHATGPT {slug}] ozet kopru: {ozet_akisi.sonuc_notu()}")
return sonuc
return self._passthrough(n, path, body, tagname=f"CHATGPT {slug}",
rewritten=bool(fixed or atilan or degisti), izle=izle)
# Codex'in kendi modelleri en basta _ozgun ile ayrildi; buraya yalniz rotasi cozulemeyen bir ek ad duser.
# Ek model chatgpt.com'a baska bir adla gonderilmez.
log(f"[{n} {slug}] ek modelin rotasi cozulemedi; istek gonderilmedi")
return self._send_json(503, {"error": {"message": "Bu model artik bu koprude tanimli degil; listeden baska bir model secin."}})
route_name, target, flat = mapping
try:
ozet_tetik = ozet_acik and ozet_kopru.tetik_mi(req)
@@ -294,9 +294,12 @@ BILGI = (BILGI_ISARETI + " Bu sohbetin modeli söylenerek değiştirilebilir: ku
"(örnek: \"modeli gemini flash low yap\", \"modeli opus beş beş yap\") değişikliği aradaki köprü yapar; senin bir "
"araç çağırman ya da ayar değiştirmen gerekmez. Kullanıcı modeli değiştirip değiştiremeyeceğini sorarsa "
"\"değiştiremem\" deme; bu cümleyi söylemesinin yeterli olduğunu kısaca anlat. Hangi modelde olduğunu öğrenmek "
"için \"seçili model hangisi\" demesi yeter.")
"için \"seçili model hangisi\" demesi yeter. Codex'in kendi modellerine (listenin başındaki abonelik modelleri) "
"sesle geçilmez: kullanıcı onlardan birine geçmek isterse ekrandaki model seçiciden seçip tek başına \".\" "
"yazması gerektiğini söyle.")
NASIL = ("Hangi modele geçeyim? Adını söylemen yeterli, örneğin: \"modeli gemini flash low yap\" ya da "
"\"modeli opus beş beş yap\". Şu anki modeli öğrenmek için \"seçili model hangisi\" de.")
"\"modeli opus beş beş yap\". Şu anki modeli öğrenmek için \"seçili model hangisi\" de. Codex'in kendi "
"modellerine sesle geçilmez; onlar için ekrandaki model seçiciden seçip tek başına \".\" yaz.")
def bilgi_ekle(req):
@@ -15,6 +15,8 @@ flatten = next(n for n in source.body if isinstance(n, ast.FunctionDef) and n.na
claude_sonu = next(n for n in source.body if isinstance(n, ast.FunctionDef) and n.name == "claude_sonu_duzelt")
handler = next(n for n in source.body if isinstance(n, ast.ClassDef) and n.name == "H")
responses = next(n for n in handler.body if isinstance(n, ast.FunctionDef) and n.name == "_responses")
ozgun = next(n for n in handler.body if isinstance(n, ast.FunctionDef) and n.name == "_ozgun")
taraf = [n for n in source.body if isinstance(n, ast.FunctionDef) and n.name in ("ek_model_mu", "yonlendirilen")]
class AccessPrograms(unittest.TestCase):
@@ -34,7 +36,7 @@ class AccessPrograms(unittest.TestCase):
"log": lambda message: None, "pencere": __import__("pencere"), "IDE_BILGI": {}, "yarim": __import__("yarim"), "is_hatti": __import__("is_hatti"), "GORULEN_ISTEMCI": set(), "arac_semalarini_duzelt": lambda req: 0, "nokta_mesaji_mi": lambda req: False, "EXTRA_MODELS": [],
"DEBUG": False, "UA": "test", "RETRIES": 1, "UPSTREAM_IDLE_TIMEOUT": 3900,
}
exec(compile(ast.Module(body=[flatten, claude_sonu, responses], type_ignores=[]), "bridge.py", "exec"), self.env)
exec(compile(ast.Module(body=taraf + [flatten, claude_sonu, responses, ozgun], type_ignores=[]), "bridge.py", "exec"), self.env)
def request(self, model):
return {"model": model, "access_programs": {"cyber": "standard"},
@@ -74,8 +76,10 @@ class AccessPrograms(unittest.TestCase):
def test_chatgpt_passthrough_preserves_original_body(self):
body = json.dumps(self.request("gpt-6-astra")).encode()
fake = Mock(headers={})
fake._ozgun = lambda *a: self.env["_ozgun"](fake, *a)
self.env["_responses"](fake, 1, "/responses", body)
self.assertEqual(fake._passthrough.call_args.args[2], body)
self.assertFalse(fake._passthrough.call_args.kwargs["rewritten"])
self.upstream.assert_not_called()
@@ -42,7 +42,8 @@ class ComboNotResent(unittest.TestCase):
"DEBUG": False, "UA": "test", "RETRIES": 6, "UPSTREAM_IDLE_TIMEOUT": 3600,
"NO_RETRY_PATTERNS": [], "pencere": __import__("pencere"), "IDE_BILGI": {}, "yarim": __import__("yarim"), "is_hatti": __import__("is_hatti"), "GORULEN_ISTEMCI": set(), "arac_semalarini_duzelt": lambda req: 0, "nokta_mesaji_mi": lambda req: False, "EXTRA_MODELS": [],
}
exec(compile(ast.Module(body=[pick("flatten_request"), pick("claude_sonu_duzelt"), pick("is_timeout"), pick("kopma_mi"), responses],
exec(compile(ast.Module(body=[pick("ek_model_mu"), pick("yonlendirilen"), pick("flatten_request"), pick("claude_sonu_duzelt"),
pick("is_timeout"), pick("kopma_mi"), responses],
type_ignores=[]), "bridge.py", "exec"), self.env)
def send(self, model):
@@ -253,7 +253,9 @@ class Zincir(unittest.TestCase):
def setUp(self):
self.env = build(top("nokta_mesaji_mi", "NOKTA_ISARETLERI", "is_timeout", "kopma_mi", "flatten_request",
"claude_sonu_duzelt") + [method("_responses"), method("_model_komutu")], {
"claude_sonu_duzelt", "ek_model_mu", "yonlendirilen")
+ [method("_responses"), method("_model_komutu"), method("_ozgun")], {
"gecit_modelleri": lambda: [],
"json": json, "re": __import__("re"), "os": __import__("os"), "copy": copy, "http": __import__("http.client"),
"time": types.SimpleNamespace(sleep=lambda s: None, time=time.time),
"urllib": types.SimpleNamespace(request=types.SimpleNamespace(Request=urllib.request.Request, urlopen=Mock()),
@@ -269,6 +271,7 @@ class Zincir(unittest.TestCase):
})
Sahte._responses = self.env["_responses"]
Sahte._model_komutu = self.env["_model_komutu"]
Sahte._ozgun = self.env["_ozgun"]
def send(self, model, items, key="sohbet-1", path="/responses"):
fake = Sahte()
@@ -276,40 +279,67 @@ class Zincir(unittest.TestCase):
fake._responses(1, path, body)
return fake, body
def test_mid_turn_note_reaches_the_subscription_model_behind_hook_messages(self):
items = [user("işe başla"), CALL, OUT, user("modeli sol yap"), developer("k1"), developer("k2"), developer("k3")]
fake, body = self.send("gpt-6-astra", items)
fake._yerel_yanit.assert_not_called()
def test_a_request_for_codex_own_model_is_passed_through_untouched(self):
"""User decision, 11 Oct 2026: Codex's own models are one side, the added models the other; the bridge
does not touch a request for Codex's own model (no command, dot, note, work line or compaction handling)."""
self.env["CFG"].update({"nokta_karsila": True, "model_bilgi": True, "yarim_cevap": True,
"ozet_kopru": {"enabled": True, "kapsam": "hepsi"},
"aiturk_hat": {"enabled": True, "komut": "aiturk is"}})
mid_turn = [user("işe başla"), CALL, OUT, user("modeli opus 5 5 yap"), developer("k1")]
for items in ([user("modeli opus 5 5 yap")], [user(".")], [user("seçili model hangisi")], mid_turn,
[user("uzun iş"), {"type": "compaction_trigger"}]):
with self.subTest(items=items):
fake = Sahte(headers={"originator": "Codex Desktop"})
body = json.dumps({"model": "gpt-6-astra", "prompt_cache_key": "sohbet-1", "input": items}).encode()
fake._responses(1, "/responses", body)
fake._yerel_yanit.assert_not_called()
args, kwargs = fake._passthrough.call_args
self.assertEqual(args[2], body)
self.assertFalse(kwargs["rewritten"])
self.assertEqual(self.env["YONLENDIRME"].veri, {})
def test_only_the_bridges_own_traces_are_cleaned_for_codex_own_model(self):
self.env["fix_tool_ids"] = lambda req: 2 # ids left behind by an added model earlier in the chat
fake, body = self.send("gpt-6-astra", [user("devam")])
args, kwargs = fake._passthrough.call_args
self.assertTrue(kwargs["rewritten"])
self.assertNotEqual(args[2], body)
sent = json.loads(args[2])
self.assertIn("ad belirsizdi", sent["input"][3]["content"][0]["text"])
self.assertEqual(sent["model"], "gpt-6-astra")
self.assertEqual(json.loads(args[2]), json.loads(body))
fake._yerel_yanit.assert_not_called()
def test_idle_switch_says_how_to_return_and_routes_the_next_request(self):
fake, _ = self.send("gpt-6-sol", [user("modeli gpt 6 astra yap")])
answer = fake._yerel_yanit.call_args.args[1]
self.assertIn("Sohbetin modeli artık GPT-6-Astra", answer)
self.assertIn('"modeli GPT-6-Sol yap"', answer)
final = {"type": "message", "role": "assistant", "phase": "final_answer", "content": "ok"}
fake, _ = self.send("gpt-6-sol", [user("modeli gpt 6 astra yap"), final, user("devam")])
sent = json.loads(fake._passthrough.call_args.args[2])
self.assertEqual(sent["model"], "gpt-6-astra")
self.assertIn("GPT-6-Astra olarak değiştirdi", sent["input"][0]["content"][0]["text"])
back, _ = self.send("gpt-6-sol", [user("modeli gpt 6 astra yap"), final, user("modeli gpt 6 sol yap")])
self.assertNotIn("dönmek için", back._yerel_yanit.call_args.args[1]) # already back on the picked model
def test_voice_switch_stays_among_the_added_models(self):
opus, flash = "ide/9router-cc-claude-opus-5-5", "ide/9router-ag-gemini-3.8-flash-low"
self.env["ROUTES"]["aiturk-ide"] = {"url": "https://example.invalid/v1/responses", "key": "k"}
fake = Sahte()
answer = fake._model_komutu(1, {"input": [user("modeli gemini flash low yap")]}, "sohbet-1", opus)
self.assertIn("Sohbetin modeli artık agy/gemini-3.8-flash-low", answer)
self.assertEqual(self.env["yonlendirilen"]("sohbet-1", opus), flash)
# Codex's own model is not a voice target, from an added model either.
answer = fake._model_komutu(1, {"input": [user("modeli gpt 6 astra yap")]}, "sohbet-1", opus)
self.assertIn("GPT-6-Astra Codex'in kendi modeli", answer)
self.assertIn("sesle geçilmiyor", answer)
self.assertIn('"."', answer)
self.assertEqual(self.env["yonlendirilen"]("sohbet-1", opus), flash)
# In the middle of a turn the running model is told to pass that on; the work is not cut.
req = {"input": [user("işe başla"), CALL, OUT, user("modeli gpt 6 astra yap")]}
self.assertIsNone(fake._model_komutu(1, req, "sohbet-1", opus))
self.assertIn("sesle yapılamıyor", req["input"][3]["content"][0]["text"])
# A record left from before the rule that points at Codex's own model is ignored.
self.env["YONLENDIRME"].koy("sohbet-2", opus, "gpt-6-astra")
self.assertIsNone(self.env["yonlendirilen"]("sohbet-2", opus))
def test_a_refused_switch_is_not_told_as_done_after_compaction(self):
items = [user("modeli opus 5 5 yap"), {"type": "compaction_trigger"}] # the ide/ route is closed here
fake, body = self.send("gpt-6-sol", items)
self.assertEqual(fake._passthrough.call_args.args[2], body)
def test_lone_dot_answer_says_selected_not_ready(self):
def test_lone_dot_is_answered_for_an_added_model_and_left_alone_for_codex_own(self):
self.env["CFG"]["nokta_karsila"] = True
fake, _ = self.send("gpt-6-sol", [user(".")])
self.assertIn("GPT-6-Sol seçildi", fake._yerel_yanit.call_args.args[1])
fake, _ = self.send("ide/9router-cc-claude-opus-5-5", [user(".")])
self.assertIn("cc/opus-5-5 seçildi", fake._yerel_yanit.call_args.args[1])
self.assertNotIn("hazır", fake._yerel_yanit.call_args.args[1])
fake, body = self.send("gpt-6-sol", [user(".")])
fake._yerel_yanit.assert_not_called()
self.assertEqual(fake._passthrough.call_args.args[2], body)
def test_old_router_name_without_a_route_is_refused_locally(self):
fake, _ = self.send("9r/cc/claude-opus-5-5", [user("selam")])