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:
1 parent
dd5f99c406
commit
12eeec7b0f
5 files changed
+139
-77
No files matched your search
@@ -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")])
|
||||
|
||||
Reference in new issue
Block a user