diff --git a/apps/desktop/assets/codex-bridge/bridge.py b/apps/desktop/assets/codex-bridge/bridge.py index 467f28f..e4ea1ac 100644 --- a/apps/desktop/assets/codex-bridge/bridge.py +++ b/apps/desktop/assets/codex-bridge/bridge.py @@ -444,7 +444,8 @@ class SseRewriter: Ayrica dis modelin asistan mesajlarina phase damgasi koyar: mesajin "bitti" olayi, arkasindan ne geldigi belli olana kadar bekletilir (arac cagrisi ya da baska oge -> commentary; yanit bitti -> final_answer). Metin parcalari (delta) bekletilmez, canli akar.""" - def __init__(self, custom, hedef=None, tasma=None, aracsiz=None): + def __init__(self, custom, hedef=None, tasma=None, aracsiz=None, dolu=False): + self.dolu = dolu # istek hedef pencerenin buyuk kismini dolduruyor self.custom = custom self.buf = b"" self.custom_ids = set() @@ -477,6 +478,13 @@ class SseRewriter: "id": "resp_astrabridge_hata", "object": "response", "status": "failed", "output": [], "error": {"code": "invalid_request_error", "message": "Bu model araç çağrısını desteklemiyor; Codex'te çalışamaz. Başka bir model seçin."}}}] + if self.dolu and pencere.kapali_hata_mi(metin): + # Pencereye yakin istekte gecidin nedensiz 400'u: buyuk olasilikla sigmadi. Codex'e sessiz kopma + # yerine anlasilir hata gider (ogrenme yapilmaz; neden kesin degil). + return [{"type": "response.failed", "response": { + "id": "resp_astrabridge_hata", "object": "response", "status": "failed", "output": [], + "error": {"code": "context_length_exceeded", "message": + "Sohbet seçili modelin bağlam penceresine sığmadı. Sohbeti sıkıştırın ya da daha büyük pencereli bir model seçin."}}}] if pencere.tasma_mi(metin): if self.tasma is not None: self.tasma(self.hedef, metin) @@ -1244,7 +1252,9 @@ class H(http.server.BaseHTTPRequestHandler): # Penceresi bilinmeyen (varsayilan degerli) modelde bekci calismaz: tahmine dayanarak gecmis kirpilmaz. pencere_biliniyor = "varsayilan" not in ((IDE_BILGI.get(slug) or {}).get("kaynak") or "varsayilan") if hedef_pencere and pencere_biliniyor and CFG.get("tasma_bekcisi", True) and not ozet_tetik: - kirpilan, sigdi, tahmin = pencere.sigdir(req, hedef_pencere) + kirpilan, sigdi, tahmin = pencere.sigdir(req, hedef_pencere, pencere.oran_sec(target)) + if rewriter is not None: + rewriter.dolu = tahmin >= hedef_pencere * 0.7 if kirpilan: log(f"{tag} tasma bekcisi: {kirpilan} buyuk eski parca kisaltildi (tahmini {tahmin} token, pencere {hedef_pencere})") if not sigdi: diff --git a/apps/desktop/assets/codex-bridge/pencere.py b/apps/desktop/assets/codex-bridge/pencere.py index 119365d..b879276 100644 --- a/apps/desktop/assets/codex-bridge/pencere.py +++ b/apps/desktop/assets/codex-bridge/pencere.py @@ -141,6 +141,11 @@ def ogren_aracsiz(home, kimlik): return True +def kapali_hata_mi(metin): + """Gecit asil nedeni gizleyip yalniz "upstream 400" derse (olcum 11 Ekim 2026).""" + return bool(re.fullmatch(r"\s*upstream 4(?:00|13)\s*", metin or "", re.I)) + + def tasma_mi(metin): return bool(_TASMA.search(metin or "")) @@ -243,8 +248,17 @@ KIRP_ESIK = 6000 # bundan kisa metin parcasi kirpilmaz KIRP_BAS = KIRP_SON = 1500 -def tahmini_token(req): - return int(len(json.dumps(req, ensure_ascii=False).encode("utf-8")) / BAYT_BOLU_TOKEN) +# Claude ayni metni daha cok tokena boler. Olcum (11 Ekim 2026): 607 KB'lik gercek Codex istegi 3,2 oraniyla +# 189776 token tahmin edildi (pencere 200000) ve Claude tarafindan reddedildi; yani gercek oran 3,03'un altinda. +CLAUDE_BAYT_BOLU_TOKEN = 2.6 + + +def oran_sec(hedef): + return CLAUDE_BAYT_BOLU_TOKEN if "claude" in str(hedef or "").lower() else BAYT_BOLU_TOKEN + + +def tahmini_token(req, oran=None): + return int(len(json.dumps(req, ensure_ascii=False).encode("utf-8")) / (oran or BAYT_BOLU_TOKEN)) def _metin_parcalari(req): @@ -271,10 +285,10 @@ def _metin_parcalari(req): return parcalar -def sigdir(req, pencere_token): +def sigdir(req, pencere_token, oran=None): """Istegi hedef pencereye sigdirir. Donus: (kirpilan parca sayisi, sigdi mi, tahmini token).""" sinir = int(pencere_token * DOLULUK) - tahmin = tahmini_token(req) + tahmin = tahmini_token(req, oran) if not pencere_token or tahmin <= sinir: return 0, True, tahmin kirpilan = 0 @@ -286,7 +300,7 @@ def sigdir(req, pencere_token): sahip[anahtar] = (metin[:KIRP_BAS] + f"\n[... {len(metin) - KIRP_BAS - KIRP_SON} karakter çıkarıldı (köprü): " f"seçili modelin bağlam penceresine sığmıyordu ...]\n" + metin[-KIRP_SON:]) kirpilan += 1 - tahmin = tahmini_token(req) + tahmin = tahmini_token(req, oran) if tahmin <= sinir: return kirpilan, True, tahmin return kirpilan, tahmin <= sinir, tahmin diff --git a/apps/desktop/assets/codex-bridge/test_pencere_phase.py b/apps/desktop/assets/codex-bridge/test_pencere_phase.py index da6ae9e..8f71c46 100644 --- a/apps/desktop/assets/codex-bridge/test_pencere_phase.py +++ b/apps/desktop/assets/codex-bridge/test_pencere_phase.py @@ -225,5 +225,20 @@ class Images(unittest.TestCase): self.assertEqual(resim_context.resimleri_sadelestir(big, tut_bayt=500, gorsel=True)[0], 2) # over budget + +class ClaudeOrani(unittest.TestCase): + def test_claude_targets_are_estimated_more_strictly_than_others(self): + import pencere as p + req = {"input": [{"type": "function_call_output", "call_id": "c", "output": "x" * 600_000}, + {"type": "message", "role": "user", "content": "son"}]} + self.assertEqual(p.oran_sec("9router-cc-claude-opus-5-5"), p.CLAUDE_BAYT_BOLU_TOKEN) + self.assertEqual(p.oran_sec("9router-ag-gemini-3.8-flash-low"), p.BAYT_BOLU_TOKEN) + self.assertEqual(p.sigdir({"input": [dict(i) for i in req["input"]]}, 200_000)[0], 0) # 187500: fits + trimmed, fits, estimate = p.sigdir(req, 200_000, p.oran_sec("cc-claude-opus")) # 230769: trimmed + self.assertEqual((trimmed, fits), (1, True)) + self.assertLess(estimate, 194_000) + self.assertTrue(p.kapali_hata_mi("upstream 400")) + self.assertFalse(p.kapali_hata_mi("upstream 400: invalid tool schema")) + if __name__ == "__main__": unittest.main()