fix(codex-link): summarize with a subscription model, reject tiny summaries, describe added models

Model switch lost the conversation: on 10 Oct a 1 MB thread was compacted by the
newly selected small model into 2201 characters. The compaction summary for a bridge
model is now written by a subscription model (ozet_kopru.ozet_modeli, default
gpt-6.1-sol); if that request is refused before it starts, the target model is used
as before. A summary far shorter than the conversation warrants is not accepted:
Codex gets response.failed and keeps the history.

The line under each added model no longer repeats its name. Teams show their
members, fallback chains their order, single models their provider and the traits
readable from the name; the text fits one phone row (30 characters).

Also normalizes tool call ids in the streamed response so Codex persists ids the
subscription backend accepts (fc_ -> ctc_), and bumps the bridge protocol to 6.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
yilsemandClaude Opus 5.5 committed 2026-10-10 15:31:53 +03:00
1 parent ce49d6d1cc
commit adf73eeedd
7 files changed
+248 -13

No files matched your search

@@ -201,6 +201,83 @@ def sirala_codex(modeller, ekler):
modeller[:] = ozgunler + ek_modeller
# ---- Secicide model adinin altindaki satir ----
# Telefonda bu satir tek satirdir ve yaklasik 30 karakterde kesilir (kullanici ekran goruntusu,
# 10 Ekim 2026); en ayirt edici bilgi basa yazilir, ad tekrar edilmez.
ACIKLAMA_SINIRI = 30
_AILE = (("opus", "opus"), ("sonnet", "sonnet"), ("haiku", "haiku"), ("astra", "astra"), ("terra", "terra"),
("luna", "luna"), ("sol", "sol"), ("gemini", "gemini"), ("qwen", "qwen"), ("glm", "glm"),
("abliterated", "abliterated"), ("deepseek", "deepseek"), ("llama", "llama"), ("gemma", "gemma"))
_SAGLAYICI = {"cc": "Claude", "cx": "ChatGPT", "ag": "Antigravity", "nano": "NanoGPT", "hf": "HuggingFace",
"huggingface": "HuggingFace", "agnes": "Agnes", "bekci": "Bekçi"}
_SANSURSUZ = re.compile(r"uncensored|abliterat|obliterat|derestrict|heretic|unshackled", re.I)
def kisa_model(ad):
""""cc/claude-opus-5-5" -> "opus", "nano/z-ai/glm-5.3-flash-uncensored" -> "glm"; bilinmeyen ad "?"."""
if not ad:
return "?"
son = ad.split("/")[-1].split(":")[0].lower()
parcalar = [p for p in re.split(r"[^a-z]+", son) if p]
for anahtar, kisa in _AILE:
if anahtar in parcalar:
return kisa
return (parcalar[0] if parcalar else son) or "?"
def _yapraklar(dugum, derinlik=0):
"""Sirali (priority) zinciri duz model listesine acar; ic takim kendi uyeleriyle yer alir."""
if not isinstance(dugum, dict) or derinlik > 6:
return ["?"]
if dugum.get("kind") != "combo":
return [kisa_model(dugum.get("model"))]
cikti = []
for uye in dugum.get("members") or []:
cikti.extend(_yapraklar(uye, derinlik + 1))
return cikti or ["?"]
def _sigdir(onek, adlar, ayrac):
"""Sinira sigdigi kadar ad yazar, kalani "+N" ile sayar."""
tekil = [a for i, a in enumerate(adlar) if i == 0 or a != adlar[i - 1]]
for adet in range(len(tekil), 0, -1):
kalan = len(tekil) - adet
metin = onek + ayrac.join(tekil[:adet]) + (f" +{kalan}" if kalan else "")
if len(metin) <= ACIKLAMA_SINIRI or adet == 1:
return metin
return onek.rstrip(": ")
def aciklama(kimlik, ad, tur, kayit=None):
"""Model adinin altinda gorunecek kisa satir. kayit: gecidin /router/catalog girdisi (yoksa None).
Takim -> uyeleri; sirali yedek -> sirasi; tek model -> saglayicisi ve adindan okunan ozellikleri."""
if tur == "combo":
topoloji = (kayit or {}).get("topology") or {}
uyeler = topoloji.get("members") or []
if topoloji.get("strategy") == "round-robin":
return f"Dönüşümlü: {len(uyeler)} üye"
takim = topoloji if topoloji.get("strategy") == "fusion" else None
if takim is None and uyeler and isinstance(uyeler[0], dict) and uyeler[0].get("strategy") == "fusion":
takim = uyeler[0]
if takim is not None:
return _sigdir("Takım: ", [kisa_model(u.get("model")) for u in takim.get("members") or []], "+")
if uyeler:
return _sigdir("Sıralı: ", _yapraklar(topoloji), ">")
return "Takım"
kisa = kisa_ad(ad)
if kisa.startswith(DOKUZ_ISARET):
kisa = kisa[len(DOKUZ_ISARET):]
kod = kisa.split("/")[0].lower() if "/" in kisa else (kimlik or "").split("-")[0].lower()
parcalar = [_SAGLAYICI.get(kod, kod) or "Tek model"]
if _SANSURSUZ.search(kisa) or _SANSURSUZ.search(kimlik or ""):
parcalar.append("sansürsüz")
if "thinking" in (kisa + " " + (kimlik or "")).lower():
parcalar.append("düşünen")
if len(parcalar) == 1:
parcalar.insert(0, "Tek model")
return " · ".join(parcalar)[:ACIKLAMA_SINIRI]
def yukle(ua="AstraBridge/1.0 (codex)", timeout=30):
"""Donus: (durum, base_url, anahtar, girdiler). durum: 'kapali' | 'anahtar-yok' | 'tamam'.
Ag hatasinda istisna atar; cagiran son listeyi korur."""
@@ -227,4 +304,7 @@ def yukle(ua="AstraBridge/1.0 (codex)", timeout=30):
girdiler = sec(data, adlar + [varsayilan], ayar["kapsam"], turler)
if not girdiler:
raise ValueError("Gecitte kullanilabilir OmniRoute sohbet modeli yok")
kayitlar = {m["id"]: m for m in metadata if m.get("id")}
for g in girdiler:
g["aciklama"] = aciklama(g["id"], g["ad"], g["kind"], kayitlar.get(g["id"]))
return "tamam", base, anahtar, girdiler