fix(bridge): stop replaying explicit terminal provider failures
This commit is contained in:
1 parent
4037c65204
commit
4924781e2d
3 files changed
+313
No files matched your search
@@ -32,6 +32,7 @@ import time
|
||||
import urllib.error
|
||||
import urllib.request
|
||||
import gecis_context
|
||||
import terminal_stream
|
||||
|
||||
HERE = os.path.dirname(os.path.abspath(__file__))
|
||||
SURUM = "17" # AITURK IDE'nin bekledigi kopru surumu (codex-link-config.ts CODEX_BRIDGE_VERSION)
|
||||
@@ -348,6 +349,36 @@ class H(http.server.BaseHTTPRequestHandler):
|
||||
self.end_headers()
|
||||
self.wfile.write(hata)
|
||||
return True
|
||||
observer = None
|
||||
first_chunks = []
|
||||
failure_key = getattr(self, "_failure_key", None)
|
||||
if (hedef == KOPRU and failure_key is not None and durum == 200
|
||||
and "text/event-stream" in yanit.headers.get("Content-Type", "")
|
||||
and yanit.headers.get("Content-Encoding", "identity") == "identity"):
|
||||
observer = terminal_stream.Observer()
|
||||
if not started:
|
||||
# Inspect only until the first SSE frame/comment. Keep normal
|
||||
# streaming and heartbeats; an error-only response can still
|
||||
# return a truthful HTTP failure before headers are committed.
|
||||
try:
|
||||
while True:
|
||||
first = yanit.read1(4096)
|
||||
if not first:
|
||||
break
|
||||
first_chunks.append(first)
|
||||
observer.feed(first)
|
||||
if observer.error and not observer.payload_seen:
|
||||
yanit.close()
|
||||
self._json(400, terminal_stream.error_body(observer.error))
|
||||
return True
|
||||
if observer.comment_seen or observer.payload_seen or sum(map(len, first_chunks)) >= observer.LIMIT:
|
||||
break
|
||||
except (OSError, http.client.HTTPException):
|
||||
# A transport interruption without an explicit provider
|
||||
# failure keeps the client's existing recovery behavior.
|
||||
yanit.close()
|
||||
self.close_connection = True
|
||||
return True
|
||||
if not started:
|
||||
self.send_response(durum)
|
||||
for k, v in yanit.headers.items():
|
||||
@@ -359,6 +390,10 @@ class H(http.server.BaseHTTPRequestHandler):
|
||||
event_tail = b""
|
||||
completed = False
|
||||
try:
|
||||
for first in first_chunks:
|
||||
toplam += len(first)
|
||||
self.wfile.write(b"%x\r\n" % len(first) + first + b"\r\n")
|
||||
self.wfile.flush()
|
||||
while True:
|
||||
try:
|
||||
parca = yanit.read1(4096) # gelen kadarini hemen verir; bekletmez
|
||||
@@ -370,6 +405,8 @@ class H(http.server.BaseHTTPRequestHandler):
|
||||
if not parca:
|
||||
break
|
||||
toplam += len(parca)
|
||||
if observer is not None:
|
||||
observer.feed(parca)
|
||||
if getattr(self, "_native_return_scope", None):
|
||||
event_tail += parca
|
||||
while b"\n" in event_tail:
|
||||
@@ -382,6 +419,9 @@ class H(http.server.BaseHTTPRequestHandler):
|
||||
pass
|
||||
self.wfile.write(b"%x\r\n" % len(parca) + parca + b"\r\n")
|
||||
self.wfile.flush()
|
||||
if observer is not None and observer.error and not observer.completed:
|
||||
self.server.failure_replays.remember(failure_key, observer.error)
|
||||
log(f"{tag} terminal worker error; identical automatic replay will return HTTP 400")
|
||||
self.wfile.write(b"0\r\n\r\n")
|
||||
self.wfile.flush()
|
||||
except (ConnectionAbortedError, BrokenPipeError, ConnectionResetError):
|
||||
@@ -453,6 +493,7 @@ class H(http.server.BaseHTTPRequestHandler):
|
||||
|
||||
def _isle(self):
|
||||
self._native_return_scope = None
|
||||
self._failure_key = None
|
||||
with kilit:
|
||||
sira[0] += 1
|
||||
n = sira[0]
|
||||
@@ -479,6 +520,12 @@ class H(http.server.BaseHTTPRequestHandler):
|
||||
req = {}
|
||||
except Exception:
|
||||
req = {}
|
||||
if external:
|
||||
self._failure_key = terminal_stream.request_key(req or {"raw": govde.hex()}, self.headers)
|
||||
previous_failure = self.server.failure_replays.take(self._failure_key)
|
||||
if previous_failure:
|
||||
log(f"[{n} {model}] terminal worker error replay stopped before upstream request")
|
||||
return self._json(400, terminal_stream.error_body(previous_failure))
|
||||
thread = str(req.get("prompt_cache_key") or "")
|
||||
scope = hashlib.sha256((str(self.headers.get("chatgpt-account-id") or "") + "|" + thread).encode()).hexdigest()
|
||||
source = TRANSFERS.get(scope) if thread else None
|
||||
@@ -541,6 +588,10 @@ class H(http.server.BaseHTTPRequestHandler):
|
||||
|
||||
|
||||
class Sunucu(http.server.ThreadingHTTPServer):
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.failure_replays = terminal_stream.FailureReplays()
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
daemon_threads = True
|
||||
# Shipped Python 3.11 defaults to only five pending connections. Model
|
||||
# discovery, voice and simultaneous chats can arrive in the same burst.
|
||||
|
||||
Reference in new issue
Block a user