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

+60 -11
View File
@@ -26,7 +26,7 @@ from resim_context import resimleri_sadelestir
import ide_catalog
import ozet_kopru
SURUM = "5"
SURUM = "6"
HERE = os.path.dirname(os.path.abspath(__file__))
# Codex sends request bodies zstd-compressed. Python gained zstd in 3.14; for older
@@ -191,7 +191,8 @@ def load_ide_catalog():
if slug in model_map:
continue
extra_models.append({"slug": slug, "display_name": adlar[g["id"]],
"description": f"AITURK IDE: {g['ad']}", "route": IDE_ROUTE, "target": g["id"],
"description": g.get("aciklama") or f"AITURK IDE: {g['ad']}",
"route": IDE_ROUTE, "target": g["id"],
"clone_from": clone, "priority": 300 + n, "reasoning_levels": levels,
"kind": g["kind"]})
model_map[slug] = (IDE_ROUTE, g["id"], None)
@@ -426,6 +427,9 @@ class SseRewriter:
inp = args
d["item"] = {"type": "custom_tool_call", "id": item.get("id"), "call_id": item.get("call_id"),
"name": item.get("name"), "input": inp, "status": item.get("status", "completed")}
# Normalize before Codex persists the response. Request-only repair
# cannot help when the user later disables the bridge.
fix_tool_ids({"input": [d["item"]]})
return d
if t.startswith("response.function_call_arguments.") and d.get("item_id") in self.custom_ids:
return None # JSON parcalari custom input'a cevrilemez; done olayinda tam metin gidiyor
@@ -437,6 +441,7 @@ class SseRewriter:
except Exception: inp = item.get("arguments", "")
item = {"type": "custom_tool_call", "id": item.get("id"), "call_id": item.get("call_id"),
"name": item.get("name"), "input": inp, "status": "completed"}
fix_tool_ids({"input": [item]})
out.append(item)
d["response"]["output"] = out
return d
@@ -678,6 +683,40 @@ class H(http.server.BaseHTTPRequestHandler):
log(f"[{n} ozet kopru] OpenAI ozeti duz metne cevrildi ({len(metin)} karakter, onbellege yazildi)")
return metin or None
def _ozet_openai(self, n, path, req, slug, ozet_cfg, girdi_bayt):
"""Kopru modeline (ide/, 9r/) gelen sikistirma isteginin ozetini abonelik modeline yazdirir.
Donus: True = yanit istemciye gitti; False = abonelik istegi baslamadan basarisiz oldu
(istemciye hicbir sey yazilmadi, cagiran hedef modelle devam eder). req cagiranin kopyasidir."""
model = ozet_cfg.get("ozet_modeli", "gpt-6.1-sol")
tag = f"[{n} CHATGPT {model} <- {slug}]"
try:
req["model"] = model
fix_tool_ids(req)
atilan = drop_foreign_items(req)
ozet_kopru.ozetleri_ac(req, yabanci_hedef=False)
ozet_kopru.ozet_istegi_hazirla(req, ozet_cfg.get("istem"))
body = json.dumps(req, ensure_ascii=False).encode("utf-8")
except Exception as e:
log(f"{tag} ozet kopru: abonelik ozeti hazirlanamadi ({type(e).__name__}: {e}), hedef model deneniyor")
return False
hdrs = {k: v for k, v in self.headers.items() if k.lower() not in REQ_HOP and k.lower() != "content-encoding"}
hdrs["Content-Type"] = "application/json"; hdrs["Accept"] = "text/event-stream"
r = urllib.request.Request(CHATGPT + path, data=body, headers=hdrs, method="POST")
try:
resp = urllib.request.urlopen(r, timeout=UPSTREAM_IDLE_TIMEOUT)
except urllib.error.HTTPError as e:
log(f"{tag} ozet kopru: abonelik ozeti reddedildi (HTTP {e.code} {e.read()[:200]!r}), hedef model deneniyor")
return False
except Exception as e:
log(f"{tag} ozet kopru: abonelik ozeti baslatilamadi ({type(e).__name__}), hedef model deneniyor")
return False
log(f"{tag} ozet kopru: sikistirma ozeti abonelik modeline yazdiriliyor (girdi={len(req.get('input', []))} oge, "
f"{girdi_bayt} bayt, {atilan} yabanci oge atildi)")
akis = ozet_kopru.OzetAkisi(None, tohum=str(n), en_az=ozet_kopru.en_az_karakter(girdi_bayt, ozet_cfg))
self._stream(resp, resp.status, tag, akis)
log(f"{tag} ozet kopru: {akis.sonuc_notu()}")
return True
def _models(self, resp, tag):
raw = resp.read()
try:
@@ -797,8 +836,9 @@ class H(http.server.BaseHTTPRequestHandler):
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))
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:
@@ -816,7 +856,7 @@ class H(http.server.BaseHTTPRequestHandler):
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 {'uretildi, ' + str(len(ozet_akisi.metin)) + ' karakter' if ozet_akisi.metin else 'URETILEMEDI'}")
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))
@@ -826,11 +866,25 @@ class H(http.server.BaseHTTPRequestHandler):
except Exception as e:
ozet_tetik = False; ozet_acik = False
log(f"[{n} {slug}] ozet kopru atlandi: {type(e).__name__}: {e}")
girdi_bayt = 0
if ozet_tetik:
OZET_FRENI.sinir = int(ozet_cfg.get("dongu_siniri", 3)); OZET_FRENI.pencere = int(ozet_cfg.get("dongu_pencere_sn", 600))
if not OZET_FRENI.izin(str(req.get("prompt_cache_key") or slug), time.time()):
log(f"[{n} {slug}] ozet kopru: DONGU FRENI, ozet istegi reddedildi (ayni sohbette {OZET_FRENI.sinir} ozetten sonra)")
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"))
if ozet_tetik and ozet_cfg.get("model"):
# Ozeti baglami alabilen baska bir modele yazdirmak icin (ornegin kucuk hedef kendi basina ozetleyemiyorsa).
alt = MODEL_MAP.get(ozet_cfg["model"])
if alt:
route_name, target, flat = alt
elif ozet_tetik and ozet_cfg.get("ozetleyen", "openai") == "openai":
# Ozeti secili kopru modeli degil, abonelikteki guclu model yazar (10 Ekim 2026: 1 MB'lik
# sohbeti kucuk bir model 2201 karaktere indirdi ve model degisince hafiza gitti).
# Abonelik istegi baslamadan reddedilirse asagidaki eski yol (hedef model) calisir.
if self._ozet_openai(n, path, copy.deepcopy(req), slug, ozet_cfg, girdi_bayt):
return
# 9Router cevirisinden gecen hedefler (9Router ve AITURK IDE gecidi): ayni istek duzeltmeleri uygulanir.
via_router = route_name in (catalog.get("route", "9router"), ide_route)
route = ROUTES[route_name]
@@ -844,11 +898,6 @@ class H(http.server.BaseHTTPRequestHandler):
if ob["acilan"] or ob["cozulen"] or ob["okunamayan"]:
log(f"{tag} ozet kopru: kopru ozeti={ob['acilan']}, OpenAI ozeti duz metne cevrildi={ob['cozulen']}, okunamayan={ob['okunamayan']}")
if ozet_tetik:
OZET_FRENI.sinir = int(ozet_cfg.get("dongu_siniri", 3)); OZET_FRENI.pencere = int(ozet_cfg.get("dongu_pencere_sn", 600))
if not OZET_FRENI.izin(str(req.get("prompt_cache_key") or slug), time.time()):
log(f"{tag} ozet kopru: DONGU FRENI, ozet istegi reddedildi (ayni sohbette {OZET_FRENI.sinir} ozetten sonra)")
return self._send_json(400, {"error": {"message": ozet_kopru.DONGU_MESAJI % OZET_FRENI.sinir,
"type": "invalid_request_error", "code": "astrabridge_ozet_dongusu"}})
ozet_kopru.ozet_istegi_hazirla(req, ozet_cfg.get("istem"))
log(f"{tag} ozet kopru: sikistirma istegi duz metin ozet istegine cevrildi (girdi={len(req.get('input', []))} oge)")
rewriter = None
@@ -919,9 +968,9 @@ class H(http.server.BaseHTTPRequestHandler):
time.sleep(min(2 * attempt, 8)); continue
# Headers go to the client from here on: a cut stream is never resent.
if ozet_tetik:
akis = ozet_kopru.OzetAkisi(rewriter, tohum=str(n))
akis = ozet_kopru.OzetAkisi(rewriter, tohum=str(n), en_az=ozet_kopru.en_az_karakter(girdi_bayt, ozet_cfg))
sonuc = self._stream(resp, resp.status, tag, akis)
log(f"{tag} ozet kopru: ozet {'uretildi, ' + str(len(akis.metin)) + ' karakter' if akis.metin else 'URETILEMEDI'}")
log(f"{tag} ozet kopru: {akis.sonuc_notu()}")
return sonuc
return self._stream(resp, resp.status, tag, rewriter)
self._send_json(503, {"error": {"message": f"bridge: hedef ulasilamadi {last}"}})
@@ -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
+32 -1
View File
@@ -45,6 +45,20 @@ OKUNAMADI_NOTU = ("(Kopru notu: Bu noktadan onceki konusma, onceki modelin sifre
"aktarilamadi. Onceki kararlari bilmiyorsan uydurma; kullaniciya sor.)")
KISA_MESAJI = ("AstraBridge: Sohbet ozeti cok kisa cikti (%d karakter, en az %d bekleniyordu) ve gecmisin yerine "
"konmadi; mesajlariniz sohbette duruyor. Tekrar deneyin ya da daha guclu bir model secin.")
def en_az_karakter(girdi_bayt, ayar=None):
"""Bu buyuklukteki bir konusmanin ozeti en az kac karakter olmali.
Olcu: 10 Ekim 2026'da 1.018.151 baytlik sohbet 2201 karaktere indi ve hafiza kayboldu.
Kucuk sohbetlerde esik kuculur ki kisa ama dogru ozetler reddedilmesin."""
ayar = ayar or {}
tavan = int(ayar.get("en_az_tavan", 4000))
oran = max(1, int(ayar.get("en_az_oran", 250))) # her 250 bayt girdi icin 1 karakter
return max(0, min(tavan, int(girdi_bayt) // oran))
def tetik_mi(req):
"""Istek bir sikistirma istegi mi (girdinin son ogesi compaction_trigger)?"""
ogeler = req.get("input")
@@ -129,9 +143,11 @@ class OzetAkisi:
"""Ozet isteginin SSE yanitini Codex'in bekledigi tek 'compaction' ogesine cevirir.
ic: once calisan cevirici (SseRewriter). Metin gelmezse compaction ogesi uretmez (Codex hatayi gosterir)."""
def __init__(self, ic=None, tohum=""):
def __init__(self, ic=None, tohum="", en_az=0):
self.ic = ic
self.tohum = tohum
self.en_az = en_az # bundan kisa ozet kabul edilmez (bkz. en_az_karakter)
self.kisa = False
self.buf = b""
self.parcalar = {} # item_id -> delta metni
self.bitenler = [] # tamamlanan mesaj metinleri
@@ -171,6 +187,13 @@ class OzetAkisi:
self.bitti = True
if not self.metin:
return _olay(d) # ozet yok: Codex "0 compaction ogesi" hatasini gosterir
if len(self.metin) < self.en_az:
# Anormal kisa ozet gecmisin yerine konmaz: Codex hatayi gosterir, sohbet oldugu gibi kalir.
self.kisa = True
yanit = dict(d.get("response") or {})
yanit["status"] = "failed"; yanit["output"] = []
yanit["error"] = {"code": "astrabridge_ozet_kisa", "message": KISA_MESAJI % (len(self.metin), self.en_az)}
return _olay({"type": "response.failed", "response": yanit})
oge = ozet_ogesi(self.metin, self.tohum)
yanit = dict(d.get("response") or {})
yanit["output"] = [oge]
@@ -180,6 +203,14 @@ class OzetAkisi:
return _olay(d)
return b""
def sonuc_notu(self):
"""Gunluk satiri icin sonuc (icerik yazilmaz)."""
if not self.metin:
return "ozet URETILEMEDI"
if self.kisa:
return f"ozet REDDEDILDI: {len(self.metin)} karakter, en az {self.en_az} bekleniyordu; gecmis korunuyor"
return f"ozet uretildi, {len(self.metin)} karakter"
def feed(self, chunk):
if self.ic:
chunk = self.ic.feed(chunk)
@@ -76,6 +76,35 @@ class MemberCatalog(unittest.TestCase):
self.assertEqual([m["priority"] for m in models[3:]], [44, 45, 46])
self.assertEqual([m["display_name"] for m in models[4:]], ["GPT-6-1-Sol (AITURK)", "GPT-6-Astra (AITURK)"])
def test_description_line_tells_what_the_entry_is_and_fits_a_phone_row(self):
yedek = {"kind": "combo", "model": "Yedek", "strategy": "priority", "members": [
{"kind": "model", "model": "nano/z-ai/glm-5.3-flash-uncensored"},
{"kind": "model", "model": "nano/abliteration-ai/abliterated-model-large-v2"}]}
takim = {"topology": {"kind": "combo", "strategy": "priority", "members": [
{"kind": "combo", "model": "Takim-Hizli", "strategy": "fusion", "members": [
{"kind": "model", "model": "ag/gemini-3.8-flash-high"}, {"kind": "model", "model": "cc/claude-sonnet-5-5"}]},
yedek]}}
self.assertEqual(c.aciklama("Takim-Hizli", "Takim-Hizli", "combo", takim), "Takım: gemini+sonnet")
sirali = {"topology": {"kind": "combo", "strategy": "priority", "members": [
{"kind": "model", "model": "cx/gpt-6-astra"}, {"kind": "model", "model": "cc/claude-opus-5-5"},
{"kind": "model", "model": "cc/claude-sonnet-5-5"}, {"kind": "model", "model": "ag/gemini-3.8-flash-high"}]}}
self.assertEqual(c.aciklama("Ajan-Arac", "Ajan-Arac", "combo", sirali), "Sıralı: astra>opus>sonnet +1")
# An unresolved first member stays visible instead of being hidden behind the fallback.
eksik = {"topology": {"kind": "combo", "strategy": "priority", "members": [{"kind": "combo", "model": None}, yedek]}}
self.assertEqual(c.aciklama("GPT-6-Sol", "GPT-6-Sol", "combo", eksik), "Sıralı: ?>glm>abliterated")
donen = {"topology": {"kind": "combo", "strategy": "round-robin", "members": [yedek, yedek, yedek]}}
self.assertEqual(c.aciklama("Takim-Sira", "Takim-Sira", "combo", donen), "Dönüşümlü: 3 üye")
self.assertEqual(c.aciklama("X", "X", "combo", None), "Takım")
self.assertEqual(c.aciklama("9router-cc-claude-opus-5-5", "9R · cc/claude-opus-5-5", "model"), "Tek model · Claude")
self.assertEqual(c.aciklama("9router-nano-qwen-qwen3.8-27b-uncensored-thinking",
"9R · nano/qwen3.8-27b-uncensored:thinking", "model"),
"NanoGPT · sansürsüz · düşünen")
self.assertEqual(c.aciklama("bekci-kurnaz", "kurnaz", "model"), "Tek model · Bekçi")
uzun = {"topology": {"kind": "combo", "strategy": "priority", "members": [
{"kind": "model", "model": f"x/model{chr(97 + i) * 9}"} for i in range(8)]}}
self.assertLessEqual(len(c.aciklama("U", "U", "combo", uzun)), c.ACIKLAMA_SINIRI)
for ad in ("Takim-Hizli", "9R · cc/claude-opus-5-5"):
self.assertNotIn(ad, c.aciklama(ad, ad, "combo" if "Takim" in ad else "model", takim))
if __name__ == "__main__":
unittest.main()
@@ -62,6 +62,27 @@ class ModelSwitch(unittest.TestCase):
self.assertEqual(rewritten["item"]["type"], "custom_tool_call")
self.assertEqual(rewritten["item"]["input"], "remember-4827")
self.assertEqual(rewritten["item"]["call_id"], "call_4827")
self.assertEqual(rewritten["item"]["id"], "ctc_chatcmpl-probe")
def test_custom_stream_ids_are_valid_before_history_is_saved(self):
# Real failure after disabling the bridge and returning to OpenAI:
# input[32].id fc_call_... was saved as a custom_tool_call by Codex.
item = {"type": "function_call", "id": "fc_call_3f427ecc539d4c779c1252077701216f",
"call_id": "call_original", "name": "probe",
"arguments": json.dumps({"input": "keep this exact tool input"})}
adapter = env["SseRewriter"]({"probe"})
converted = []
for kind in ("response.output_item.added", "response.output_item.done"):
converted.append(adapter._fix({"type": kind, "item": dict(item)})["item"])
final = adapter._fix({"type": "response.completed", "response": {"output": [dict(item)]}})
converted.append(final["response"]["output"][0])
for result in converted:
self.assertEqual(result["type"], "custom_tool_call")
self.assertEqual(result["id"], "ctc_call_3f427ecc539d4c779c1252077701216f")
self.assertEqual(result["call_id"], "call_original")
self.assertEqual(result["input"], "keep this exact tool input")
history = {"input": converted}
self.assertEqual(env["fix_tool_ids"](history), 0)
if __name__ == "__main__":
@@ -109,6 +109,31 @@ class Akis(unittest.TestCase):
a = o.OzetAkisi()
self.assertEqual(o.ozet_metni(coz(a.feed(ham))[1]["item"]), "ozet metni")
def test_anormal_kisa_ozet_gecmisin_yerine_konmaz(self):
# 10 Ekim 2026: 1.018.151 baytlik sohbet 2201 karaktere indi; bu ozet kabul edilmemeli.
self.assertEqual(o.en_az_karakter(1018151), 4000)
self.assertEqual(o.en_az_karakter(100000), 400)
self.assertEqual(o.en_az_karakter(0), 0)
self.assertEqual(o.en_az_karakter(1018151, {"en_az_tavan": 1500}), 1500)
ham = sse({"type": "response.created", "response": {"id": "r1"}},
{"type": "response.output_item.done", "item": self._mesaj("k" * 2201)},
{"type": "response.completed", "response": {"id": "r1", "status": "completed"}})
a = o.OzetAkisi(tohum="7", en_az=o.en_az_karakter(1018151))
olaylar = coz(a.feed(ham) + a.flush())
self.assertEqual([e["type"] for e in olaylar], ["response.created", "response.failed"])
self.assertNotIn("compaction", json.dumps(olaylar))
self.assertEqual(olaylar[1]["response"]["error"]["code"], "astrabridge_ozet_kisa")
self.assertTrue(a.kisa)
self.assertIn("REDDEDILDI", a.sonuc_notu())
# Yeterli uzunlukta ozet ayni esikle kabul edilir; kucuk sohbetin kisa ozeti de reddedilmez.
for metin, esik in (("u" * 4000, o.en_az_karakter(1018151)), ("kisa ama dogru ozet", o.en_az_karakter(3000))):
b = o.OzetAkisi(tohum="8", en_az=esik)
ham2 = sse({"type": "response.output_item.done", "item": self._mesaj(metin)},
{"type": "response.completed", "response": {"id": "r2"}})
cikti = coz(b.feed(ham2))
self.assertEqual(o.ozet_metni(cikti[0]["item"]), metin)
self.assertFalse(b.kisa)
def test_metin_yoksa_compaction_uretilmez_hata_gorunur(self):
ham = sse({"type": "response.created", "response": {}},
{"type": "response.output_item.done", "item": {"type": "function_call", "name": "x"}},
+1 -1
View File
@@ -27,7 +27,7 @@ export const CODEX_LINK_REFRESH_URL = `${CODEX_LINK_ORIGIN}/aiturk/codex-baglama
export const CODEX_LINK_MARKER = '# AITURK IDE Codex Baglama'
// Must equal SURUM in assets/codex-bridge/bridge.py; a running bridge that reports
// another version is restarted so an updated IDE never keeps an old bridge alive.
export const CODEX_BRIDGE_VERSION = '5'
export const CODEX_BRIDGE_VERSION = '6'
export const CODEX_LINK_LINE = `openai_base_url = "${CODEX_LINK_ORIGIN}/backend-api/codex" ${CODEX_LINK_MARKER}`
export const CODEX_LINK_RUN_KEY = 'HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Run'