fix(codex-bridge): audit findings: visible permanent errors, stricter model command, unbuffered stream
From ten independent read-only audits (11 Oct 2026), verified against the code: - a permanent gateway error (no tools, 401/402/403, bare upstream 400) ends the turn with a visible assistant message instead of a silent retry and an empty turn; - "modeli ... yapma", "X modeli için test yap" and general "hangi model ..." questions are no longer commands; a name whose version does not fit, or that several models share, is asked back; - the stream is forwarded as it arrives (read1) and an upstream drop is no longer logged as a client stop nor saved as an interrupted answer; - a lone "." joining a running turn is removed without the hook messages after it; - the interrupted-answer note finds the user message behind hook messages and detects completion by event type; a per-minute rate limit is not learned as a context window. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
1e2b8e2b9c
commit
e2793fa38f
7 files changed
+129
-32
No files matched your search
@@ -15,7 +15,7 @@ Data directory (bridge.json, log/): --home <dir> or AITURK_CODEX_BRIDGE_HOME,
|
||||
otherwise the script's own directory. Agent home: --hermes-home or HERMES_HOME.
|
||||
Local control: GET /aiturk/codex-baglama/status, POST /aiturk/codex-baglama/stop.
|
||||
"""
|
||||
import http.server, urllib.request, urllib.error, json, os, sys, time, threading, re, copy, hashlib
|
||||
import http.server, http.client, urllib.request, urllib.error, json, os, sys, time, threading, re, copy, hashlib
|
||||
from gemini_context import coalesce_context
|
||||
try:
|
||||
from kanca_context import pano_blogunu_ulastir # optional, site-specific hook context
|
||||
@@ -502,6 +502,25 @@ class SseRewriter:
|
||||
if d is not None and not d["item"].get("phase"):
|
||||
d["item"]["phase"] = phase
|
||||
return d
|
||||
def _gorunur_hata(self, mesaj):
|
||||
"""Kalici hata: Codex'in sessizce yeniden deneyip turu cevapsiz bitirmesi yerine (denetim 11 Ekim 2026)
|
||||
hata, gorunur bir asistan mesaji olarak yazilir ve yanit normal bicimde tamamlanir. Sesli kullanimda
|
||||
kullanici hatayi duyar."""
|
||||
damga = "%d" % int(time.time() * 1000)
|
||||
rid, mid = "resp_astrabridge_hata_" + damga, "msg_astrabridge_hata_" + damga
|
||||
oge = {"id": mid, "type": "message", "role": "assistant", "status": "completed", "phase": "final_answer",
|
||||
"content": [{"type": "output_text", "text": mesaj, "annotations": []}]}
|
||||
kullanim = {"input_tokens": 0, "input_tokens_details": {"cached_tokens": 0}, "output_tokens": 0,
|
||||
"output_tokens_details": {"reasoning_tokens": 0}, "total_tokens": 0}
|
||||
return [
|
||||
{"type": "response.output_item.added", "output_index": 0,
|
||||
"item": {"id": mid, "type": "message", "role": "assistant", "status": "in_progress", "content": []}},
|
||||
{"type": "response.output_text.delta", "item_id": mid, "output_index": 0, "content_index": 0, "delta": mesaj},
|
||||
{"type": "response.output_item.done", "output_index": 0, "item": oge},
|
||||
{"type": "response.completed", "response": {"id": rid, "object": "response", "status": "completed",
|
||||
"output": [oge], "usage": kullanim}},
|
||||
]
|
||||
|
||||
def _sirala(self, d):
|
||||
"""Bir olay -> gonderilecek olaylar listesi (bekleyen mesaj dogru phase ile one alinir)."""
|
||||
t = d.get("type", "")
|
||||
@@ -517,10 +536,7 @@ class SseRewriter:
|
||||
if pencere.aracsiz_mi(metin):
|
||||
if self.aracsiz is not None:
|
||||
self.aracsiz(self.hedef)
|
||||
return [{"type": "response.failed", "response": {
|
||||
"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."}}}]
|
||||
return self._gorunur_hata("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).
|
||||
@@ -535,11 +551,12 @@ class SseRewriter:
|
||||
"id": "resp_astrabridge_hata", "object": "response", "status": "failed", "output": [],
|
||||
"error": {"code": "context_length_exceeded", "message": metin[:300]}}}]
|
||||
if re.search(r"(?<![0-9])(401|402|403)(?![0-9])|credit|quota|billing|payment|unauthori[sz]ed|forbidden", metin, re.I):
|
||||
kod = "insufficient_quota" if re.search(r"402|credit|quota|billing|payment", metin, re.I) else "invalid_api_key"
|
||||
d = {"type": "response.failed", "response": {
|
||||
"id": "resp_astrabridge_hata", "object": "response", "status": "failed", "output": [],
|
||||
"error": {"code": kod, "message": "AITURK ağ geçidi isteği reddetti: " + metin[:300]}}}
|
||||
t = "response.failed"
|
||||
neden = "kredi ya da kota" if re.search(r"402|credit|quota|billing|payment", metin, re.I) else "yetki"
|
||||
return self._gorunur_hata(f"Seçili model şu an kullanılamıyor ({neden} sorunu). Başka bir model seçin. "
|
||||
f"Ayrıntı: {metin[:200]}")
|
||||
if pencere.kapali_hata_mi(metin):
|
||||
return self._gorunur_hata("Seçili model isteği reddetti ve nedenini bildirmedi. Başka bir model seçip "
|
||||
"yeniden deneyin.")
|
||||
item = d.get("item") if isinstance(d.get("item"), dict) else None
|
||||
if item and item.get("type") in ARAC_OGELERI:
|
||||
self.arac_goruldu = True
|
||||
@@ -768,7 +785,15 @@ class H(http.server.BaseHTTPRequestHandler):
|
||||
self.wfile.write(b"%x\r\n" % len(b) + b + b"\r\n"); self.wfile.flush()
|
||||
try:
|
||||
while True:
|
||||
chunk = resp.read(4096)
|
||||
try:
|
||||
# read1: gelen kadarini hemen verir; read(4096) kucuk parcalari 4096 bayt dolana kadar bekletiyordu.
|
||||
chunk = resp.read1(4096)
|
||||
except (OSError, http.client.HTTPException) as e:
|
||||
# Ust kaynak koptu (istemci degil): yarim cevap kaydi yapilmaz, baglanti kapatilir ki Codex
|
||||
# yarim akisi tam sanmasin ve yeniden denesin.
|
||||
log(f"{tag} ust kaynak koptu ({total} bayt): {type(e).__name__}: {e}")
|
||||
self.close_connection = True
|
||||
return
|
||||
if not chunk: break
|
||||
total += len(chunk)
|
||||
if len(bas) < 600:
|
||||
@@ -1142,7 +1167,7 @@ class H(http.server.BaseHTTPRequestHandler):
|
||||
if CFG.get("nokta_karsila", True) and nokta_mesaji_mi(req) and model_gecis.tur_ortasi(req, model_gecis.son_sira(req["input"])):
|
||||
# Calisan turun icine dusen nokta secimi tasimaz (olcum 10 Ekim 2026) ve cevaplanirsa isi yarida
|
||||
# keser. Mesaj cikarilir, model isine devam eder; kullanici model bosalinca yeniden yazar.
|
||||
del req["input"][model_gecis.son_sira(req["input"]):]
|
||||
del req["input"][model_gecis.son_sira(req["input"])] # yalniz nokta; ardindaki kanca mesajlari kalir
|
||||
govde_degisti = True
|
||||
log(f"[{n} {slug}] tur ortasinda gelen nokta cikarildi; calisan is kesilmedi")
|
||||
if slug != gelen or once != (len(req.get("input") or []), json.dumps((req.get("input") or [None])[-3:], ensure_ascii=False, sort_keys=True)):
|
||||
|
||||
Reference in new issue
Block a user