Kopru: secili model hata verirse siradaki katman yapar

Hata kodu, baglanamama ve yarida kopma da siradaki katmana gider.
Siradakine gecilen adimda onceki cevap verilmez; son katman da
acilamazsa onun kendi hatasi gider.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
yilsemandClaude Opus 5.5 committed 2026-10-11 10:40:13 +03:00
1 parent 7547de1852
commit 06039b904d
2 files changed
+61 -15

No files matched your search

+36 -15
View File
@@ -875,22 +875,28 @@ class H(http.server.BaseHTTPRequestHandler):
"""Katman denetimi (jev_denetim.py): cevap istemciye verilmeden once tamami okunur ve Jev'e sorulur.
Gecerse oldugu gibi verilir; gecmezse atilir ve ayni adimi siradaki katman yapar (ac(ad): (resp, rewriter,
tag)). Son katmanin cevabi sorulmadan akar. Beklerken SSE yorum satiri gider ki Codex akisi kopmus saymasin."""
self._akis_basligi(resp, resp.status)
kalan, son_yazi = list(sira), time.time()
if resp is None: # secili model hata verdi: cevabi yok, adimi siradaki katman yapar
self.send_response(200)
self.send_header("Content-Type", "text/event-stream")
self.send_header("Transfer-Encoding", "chunked")
self.end_headers()
else:
self._akis_basligi(resp, resp.status)
kalan, son_yazi, son_hata = list(sira), time.time(), ""
def w(b):
if b:
self.wfile.write(b"%x\r\n" % len(b) + b + b"\r\n"); self.wfile.flush()
try:
while True:
parcalar, okuyan = [], yarim.Izleyici("", "")
while True:
parcalar, okuyan, hata = [], yarim.Izleyici("", ""), resp is None
while not hata:
try:
chunk = resp.read1(4096)
except (OSError, http.client.HTTPException) as e:
log(f"{tag} ust kaynak koptu (denetim tamponu, {sum(map(len, parcalar))} bayt): {type(e).__name__}: {e}")
self.close_connection = True
return
log(f"{tag} katman denetimi: cevap yarida koptu, siradakine geciliyor ({type(e).__name__}: {e})")
hata = True
break
if not chunk:
break
parcalar.append(chunk)
@@ -901,7 +907,9 @@ class H(http.server.BaseHTTPRequestHandler):
# Her adim Jev'e gider: yazi yoksa modelin bu adimda cagirdigi araclar sorulur.
metin = yazi or jev_denetim.komutlar(b"".join(parcalar))
gecmedi = False
if metin:
if hata:
gecmedi = True
elif metin:
t0 = time.time()
puan = jev_denetim.sor(istek, metin, jev_denetim.anahtar())
if puan is None:
@@ -922,23 +930,32 @@ class H(http.server.BaseHTTPRequestHandler):
try:
yeni = ac(ad)
except Exception as e:
log(f"{tag} katman denetimi: {ad} acilamadi ({type(e).__name__}: {str(e)[:120]})")
govde = e.read().decode("utf-8", "replace") if hasattr(e, "read") else ""
son_hata = f"{ad}: {e} {govde}".strip()
log(f"{tag} katman denetimi: {ad} acilamadi ({son_hata[:300]})")
if yeni is None:
break
resp, rewriter, tag = yeni
log(f"{tag} katman denetimi: adim bu katmana verildi")
if not kalan:
return self._stream(resp, resp.status, tag, rewriter, izle, basliksiz=True)
for chunk in parcalar:
if izle is not None:
izle.feed(chunk)
w(rewriter.feed(chunk) if rewriter else chunk)
if rewriter: w(rewriter.flush())
if gecmedi:
# Siradakine gecilen adimda onceki model bir sey soyleyemez: son katmanin kendi hatasi gider.
log(f"{tag} katman denetimi: son katman da acilamadi, onun hatasi veriliyor")
olay = {"type": "response.failed", "response": {"id": "resp_astrabridge_katman", "object": "response",
"status": "failed", "output": [], "error": {"code": "katman_hatasi", "message": son_hata}}}
w(b"event: response.failed\ndata: " + json.dumps(olay, ensure_ascii=False).encode("utf-8") + b"\n\n")
else:
for chunk in parcalar:
if izle is not None:
izle.feed(chunk)
w(rewriter.feed(chunk) if rewriter else chunk)
if rewriter: w(rewriter.flush())
self.wfile.write(b"0\r\n\r\n"); self.wfile.flush()
except (ConnectionAbortedError, BrokenPipeError, ConnectionResetError):
log(f"{tag} istemci baglantiyi kapatti (denetim tamponu)")
return
log(f"{tag} bitti status={resp.status} {sum(map(len, parcalar))} bayt")
log(f"{tag} bitti {'hata' if gecmedi else 'status=200'} {sum(map(len, parcalar))} bayt")
def _stream(self, resp, status, tag, rewriter=None, izle=None, basliksiz=False):
if not basliksiz:
@@ -1620,6 +1637,8 @@ class H(http.server.BaseHTTPRequestHandler):
log(f"{tag} deneme {attempt}: HTTP {e.code} {errbody[:100]!r} -> tekrar")
time.sleep(min(2 * attempt, 8)); continue
log(f"{tag} HTTP {e.code} {errbody[:200]!r}")
if jev_sira: # secili model hata verdi: adimi siradaki katman yapar
return self._denetimli_akis(None, tag, rewriter, izle, jev_istek, jev_sira, jev_ac)
if pencere.tasma_mi(etxt):
# Hedef "baglam sigmiyor" dedi: Codex'in tanidigi tasma hatasi olarak iletilir ve hedefin
# bildirdigi gercek sinir ogrenilir (sonraki model listesinde pencere kuculur).
@@ -1651,6 +1670,8 @@ class H(http.server.BaseHTTPRequestHandler):
if jev_sira and resp.status == 200:
return self._denetimli_akis(resp, tag, rewriter, izle, jev_istek, jev_sira, jev_ac)
return self._stream(resp, resp.status, tag, rewriter, izle)
if jev_sira: # secili modele ulasilamadi: adimi siradaki katman yapar
return self._denetimli_akis(None, tag, rewriter, izle, jev_istek, jev_sira, jev_ac)
self._send_json(503, {"error": {"message": f"bridge: hedef ulasilamadi {last}"}})
do_POST = _handle
@@ -241,6 +241,31 @@ class Akis(unittest.TestCase):
self.assertEqual(self.opened, ["Katman-2", "Katman-3"])
self.assertEqual(self.sent(), self.cevap["Katman-3"])
def test_an_error_of_the_selected_model_goes_to_the_next_layer(self):
self.h._denetimli_akis(None, "[1]", None, None, "yap", ["Katman-1", "Katman-2", "Katman-3", "Katman-4"], self.ac)
self.assertEqual(self.opened, ["Katman-1"])
self.assertEqual(self.sent(), self.cevap["Katman-1"])
def test_an_answer_cut_midway_goes_to_the_next_layer(self):
class Kopan(Yanit):
def read1(self, n):
if not self.parts:
raise OSError("koptu")
return self.parts.pop(0)
self.h._denetimli_akis(Kopan(sse("yarim")), "[1]", None, None, "yap", ["Katman-2", "Katman-3", "Katman-4"], self.ac)
self.assertEqual(self.opened, ["Katman-2"])
self.assertEqual(self.sent(), self.cevap["Katman-2"])
self.assertNotIn(b"yarim", self.h.wfile.getvalue())
def test_when_no_layer_opens_the_blocked_answer_is_not_delivered(self):
self.puanlar = [{"ret": 0.9, "sart": 0.0}]
self.cevap.update({"Katman-2": None, "Katman-3": None, "Katman-4": None})
self.calistir(sse("Bunu yapamam."))
self.assertEqual(self.opened, ["Katman-2", "Katman-3", "Katman-4"])
self.assertNotIn(b"yapamam", self.h.wfile.getvalue())
self.assertIn(b"response.failed", self.sent())
self.assertIn(b"Katman-4: acilamadi", self.sent()) # the last layer's own error reaches the user
def test_unreachable_jev_keeps_the_answer(self):
self.puanlar = [None]
body = sse("Bunu yapamam.")