v3.8.3: Fix codebuff streaming — SSE events now reach Codex client
- Root cause: _handle_codebuff streaming loop collected events but never wrote them to self.wfile (stream_buffered_events was not called) - Fix: Replaced manual loop with stream_buffered_events() + on_event callback - Confirmed working: raw API streaming, non-stream, and stream through proxy - Updated CHANGELOG.md, README.md, version labels to 3.8.3
This commit is contained in:
@@ -6,7 +6,7 @@ gi.require_version("Gtk", "3.0")
|
||||
from gi.repository import Gtk, GLib
|
||||
import subprocess, os, signal, sys, threading, time, json, urllib.request, urllib.parse, urllib.error, tempfile, shutil
|
||||
import hashlib, socket, ssl, contextlib, re, collections
|
||||
import base64, secrets
|
||||
import base64, secrets, uuid, webbrowser
|
||||
from pathlib import Path
|
||||
|
||||
HOME = Path.home()
|
||||
@@ -26,6 +26,22 @@ model_catalog_json = ""
|
||||
"""
|
||||
|
||||
CHANGELOG = [
|
||||
("3.8.3", "2026-05-24", [
|
||||
"FIXED: Codebuff streaming — SSE events now reach Codex client",
|
||||
"Root cause: stream_buffered_events was never called for codebuff",
|
||||
"Codebuff stream uses buffered flushing (30ms / 4KB / urgent)",
|
||||
"Codebuff OAuth — built-in login flow (no external CLI needed)",
|
||||
"Codebuff API: reverse-engineered www.codebuff.com endpoints",
|
||||
"Codebuff session management with instance ID (waiting room)",
|
||||
"Codebuff agent run lifecycle (start/finish) with model routing",
|
||||
"Free DeepSeek V4 Pro, V4 Flash, Kimi K2.6, MiniMax M2.7",
|
||||
"Reasoning mode works with codebuff (thinking tokens supported)",
|
||||
"GUI: Sandbox mode selector (Read-only / Workspace / Full Access)",
|
||||
"GUI: Approval mode selector (Untrusted / On Request / Full Auto)",
|
||||
"GUI: Codebuff Login button in endpoint editor",
|
||||
"Fixed _STATS undefined error in /health endpoint",
|
||||
"Fixed codebuff credential path (reads default account)",
|
||||
]),
|
||||
("3.8.1", "2026-05-24", [
|
||||
"Codebuff integration — free DeepSeek V4 Pro, V4 Flash, Kimi K2.6, MiniMax M2.7",
|
||||
"Codebuff backend: auto agent-run lifecycle, credential detection, model routing",
|
||||
@@ -321,6 +337,7 @@ PROVIDER_PRESETS = {
|
||||
"Codebuff (Free DeepSeek/Kimi)": {
|
||||
"backend_type": "codebuff",
|
||||
"base_url": "https://codebuff.com",
|
||||
"oauth_provider": "codebuff",
|
||||
"models": [
|
||||
"deepseek/deepseek-v4-pro", "deepseek/deepseek-v4-flash",
|
||||
"moonshotai/kimi-k2.6", "minimax/minimax-m2.7",
|
||||
@@ -995,6 +1012,11 @@ def safe_cleanup_owned(logfn=None):
|
||||
|
||||
def _start_proxy_for(endpoint, logfn):
|
||||
global _proxy_proc, _proxy_port
|
||||
# Clear stale Python bytecode cache so proxy picks up latest source changes
|
||||
import shutil
|
||||
pycache = os.path.join(os.path.dirname(os.path.abspath(__file__)), '__pycache__')
|
||||
if os.path.isdir(pycache):
|
||||
shutil.rmtree(pycache, ignore_errors=True)
|
||||
_stop_proxy()
|
||||
port = _pick_free_port()
|
||||
_proxy_port = port
|
||||
@@ -1684,7 +1706,7 @@ class LauncherWin(Gtk.Window):
|
||||
# header row
|
||||
hdr = Gtk.Box(spacing=8)
|
||||
vbox.pack_start(hdr, False, False, 0)
|
||||
lbl = Gtk.Label(label="<b>Codex Launcher v3.8.1</b>")
|
||||
lbl = Gtk.Label(label="<b>Codex Launcher v3.8.3</b>")
|
||||
lbl.set_use_markup(True)
|
||||
hdr.pack_start(lbl, False, False, 0)
|
||||
changelog_btn = Gtk.Button(label="Changelog")
|
||||
@@ -1790,6 +1812,26 @@ class LauncherWin(Gtk.Window):
|
||||
self._model_combo = Gtk.ComboBoxText()
|
||||
sel_box.pack_start(self._model_combo, True, True, 0)
|
||||
|
||||
# sandbox mode selector
|
||||
sel_box.pack_start(Gtk.Label(label="Sandbox:"), False, False, 0)
|
||||
self._sandbox_combo = Gtk.ComboBoxText()
|
||||
for v, l in [("read-only", "Read-only"),
|
||||
("workspace-write", "Workspace"),
|
||||
("danger-full-access", "Full Access")]:
|
||||
self._sandbox_combo.append(v, l)
|
||||
self._sandbox_combo.set_active_id("workspace-write")
|
||||
sel_box.pack_start(self._sandbox_combo, True, True, 0)
|
||||
|
||||
# approval mode selector
|
||||
sel_box.pack_start(Gtk.Label(label="Approval:"), False, False, 0)
|
||||
self._approval_combo = Gtk.ComboBoxText()
|
||||
for v, l in [("untrusted", "Untrusted"),
|
||||
("on-request", "On Request"),
|
||||
("never", "Never (Full Auto)")]:
|
||||
self._approval_combo.append(v, l)
|
||||
self._approval_combo.set_active_id("on-request")
|
||||
sel_box.pack_start(self._approval_combo, True, True, 0)
|
||||
|
||||
# launch buttons
|
||||
btn_box = Gtk.Box(spacing=8, homogeneous=True)
|
||||
vbox.pack_start(btn_box, False, False, 8)
|
||||
@@ -2523,7 +2565,6 @@ class LauncherWin(Gtk.Window):
|
||||
"""Launch codex CLI in a terminal with the selected endpoint."""
|
||||
self.log(f"Launching Codex CLI with {ep['name']}…")
|
||||
|
||||
# Find a terminal emulator
|
||||
terms = [
|
||||
("x-terminal-emulator", ["-e"]),
|
||||
("kgx", ["--"]),
|
||||
@@ -2543,16 +2584,17 @@ class LauncherWin(Gtk.Window):
|
||||
self.log("ERROR: no terminal emulator found (tried x-terminal-emulator, kgx, gnome-terminal, konsole, xterm)")
|
||||
return
|
||||
|
||||
# For proxied endpoints, the proxy is already running (from _run)
|
||||
# For native, no proxy needed
|
||||
sandbox = self._sandbox_combo.get_active_id() or "workspace-write"
|
||||
approval = self._approval_combo.get_active_id() or "on-request"
|
||||
|
||||
cmd_parts = [term] + term_args
|
||||
|
||||
if ep["backend_type"] == "native":
|
||||
# Just run codex directly — config.toml is already set up
|
||||
cmd_parts.extend(["codex", "-c", f"model={model}"])
|
||||
cmd_parts.extend(["codex", "-c", f"model={model}",
|
||||
"-s", sandbox, "-a", approval])
|
||||
else:
|
||||
# Proxy is running, run codex with the profile
|
||||
cmd_parts.extend(["codex", "--profile", ep["name"], "-c", f"model={model}"])
|
||||
cmd_parts.extend(["codex", "--profile", ep["name"], "-c", f"model={model}",
|
||||
"-s", sandbox, "-a", approval])
|
||||
|
||||
self.log(f"Running: {' '.join(cmd_parts)}")
|
||||
self._proc = subprocess.Popen(cmd_parts, preexec_fn=os.setsid)
|
||||
@@ -2618,7 +2660,9 @@ class LauncherWin(Gtk.Window):
|
||||
self.log("ERROR: no terminal emulator found")
|
||||
return
|
||||
|
||||
cmd_parts = [term] + term_args + ["codex"]
|
||||
sandbox = self._sandbox_combo.get_active_id() or "workspace-write"
|
||||
approval = self._approval_combo.get_active_id() or "on-request"
|
||||
cmd_parts = [term] + term_args + ["codex", "-s", sandbox, "-a", approval]
|
||||
self.log(f"Running: {' '.join(cmd_parts)}")
|
||||
self._proc = subprocess.Popen(cmd_parts, preexec_fn=os.setsid)
|
||||
pid = self._proc.pid
|
||||
@@ -3097,9 +3141,14 @@ class EditEndpointDialog(Gtk.Dialog):
|
||||
def _apply_selected_preset(self, initial=False):
|
||||
preset_name = self._combo_preset.get_active_text() or "Custom"
|
||||
preset = PROVIDER_PRESETS.get(preset_name, PROVIDER_PRESETS["Custom"])
|
||||
is_oauth = bool(preset.get("oauth_provider"))
|
||||
oauth_provider = preset.get("oauth_provider", "")
|
||||
is_oauth = bool(oauth_provider)
|
||||
self._oauth_btn.set_visible(is_oauth)
|
||||
if is_oauth:
|
||||
if oauth_provider == "codebuff":
|
||||
self._oauth_btn.set_label("Codebuff Login")
|
||||
self._entry_key.set_placeholder_text("Auto-filled by codebuff login")
|
||||
elif is_oauth:
|
||||
self._oauth_btn.set_label("OAuth Login")
|
||||
self._entry_key.set_placeholder_text("Auto-filled by OAuth")
|
||||
else:
|
||||
self._entry_key.set_placeholder_text("")
|
||||
@@ -3130,7 +3179,9 @@ class EditEndpointDialog(Gtk.Dialog):
|
||||
preset_name = self._combo_preset.get_active_text() or "Custom"
|
||||
preset = PROVIDER_PRESETS.get(preset_name, {})
|
||||
provider = preset.get("oauth_provider", "")
|
||||
if (provider or "").startswith("google"):
|
||||
if provider == "codebuff":
|
||||
self._codebuff_oauth_flow()
|
||||
elif (provider or "").startswith("google"):
|
||||
self._google_oauth_flow(provider)
|
||||
|
||||
def _google_oauth_flow(self, oauth_provider="google-cli"):
|
||||
@@ -3406,6 +3457,117 @@ class EditEndpointDialog(Gtk.Dialog):
|
||||
dlg.connect("response", lambda d, r: d.destroy())
|
||||
dlg.run()
|
||||
|
||||
def _codebuff_oauth_flow(self):
|
||||
dlg = Gtk.Dialog(title="Codebuff Login", parent=self, modal=True)
|
||||
dlg.add_button("Cancel", Gtk.ResponseType.CANCEL)
|
||||
dlg.set_default_size(500, 240)
|
||||
area = dlg.get_content_area()
|
||||
area.set_margin_start(16)
|
||||
area.set_margin_end(16)
|
||||
area.set_margin_top(12)
|
||||
area.set_margin_bottom(12)
|
||||
area.set_spacing(8)
|
||||
|
||||
area.pack_start(Gtk.Label(label="<b>Sign in with GitHub via Codebuff</b>", use_markup=True, xalign=0), False, False, 0)
|
||||
|
||||
self._oauth_status = Gtk.Label(label="Requesting login URL…", xalign=0)
|
||||
self._oauth_status.set_line_wrap(True)
|
||||
self._oauth_status.set_max_width_chars(60)
|
||||
area.pack_start(self._oauth_status, False, False, 4)
|
||||
|
||||
link_lbl = Gtk.Label(xalign=0)
|
||||
link_lbl.set_line_wrap(True)
|
||||
link_lbl.set_max_width_chars(60)
|
||||
area.pack_start(link_lbl, False, False, 4)
|
||||
|
||||
spinner = Gtk.Spinner()
|
||||
spinner.start()
|
||||
area.pack_start(spinner, False, False, 8)
|
||||
|
||||
area.show_all()
|
||||
link_lbl.set_visible(False)
|
||||
|
||||
self._fb_oauth_result = {"success": False, "user": None, "error": None}
|
||||
|
||||
def _codebuff_auth_thread():
|
||||
try:
|
||||
fingerprint_id = str(uuid.uuid4())
|
||||
auth_url = "https://codebuff.com/api/auth/cli/code"
|
||||
body = json.dumps({"fingerprintId": fingerprint_id}).encode()
|
||||
req = urllib.request.Request(auth_url, data=body,
|
||||
headers={"Content-Type": "application/json", "User-Agent": "codex-launcher/3.8.3"})
|
||||
resp = urllib.request.urlopen(req, timeout=30)
|
||||
data = json.loads(resp.read())
|
||||
login_url = data.get("loginUrl", "") or data.get("login_url", "")
|
||||
fingerprint_hash = data.get("fingerprintHash", "") or data.get("fingerprint_hash", "")
|
||||
expires_at = data.get("expiresAt", 0) or data.get("expires_at", 0)
|
||||
if not login_url:
|
||||
self._fb_oauth_result["error"] = "Server returned no login URL"
|
||||
GLib.idle_add(self._codebuff_oauth_done, dlg, spinner)
|
||||
return
|
||||
|
||||
def _set_link():
|
||||
self._oauth_status.set_text("Open this URL in your browser to log in:")
|
||||
link_lbl.set_markup(f'<a href="{login_url}">{login_url}</a>')
|
||||
link_lbl.set_visible(True)
|
||||
GLib.idle_add(_set_link)
|
||||
|
||||
webbrowser.open(login_url)
|
||||
|
||||
poll_url = f"https://codebuff.com/api/auth/cli/status?fingerprintId={urllib.parse.quote(fingerprint_id)}&fingerprintHash={urllib.parse.quote(fingerprint_hash)}&expiresAt={expires_at}"
|
||||
deadline = time.time() + 300
|
||||
while time.time() < deadline:
|
||||
time.sleep(2)
|
||||
try:
|
||||
poll_req = urllib.request.Request(poll_url,
|
||||
headers={"User-Agent": "codex-launcher/3.8.3"})
|
||||
poll_resp = urllib.request.urlopen(poll_req, timeout=10)
|
||||
poll_data = json.loads(poll_resp.read())
|
||||
user = poll_data.get("user")
|
||||
if user and user.get("authToken"):
|
||||
self._fb_oauth_result["success"] = True
|
||||
self._fb_oauth_result["user"] = user
|
||||
GLib.idle_add(self._codebuff_oauth_done, dlg, spinner)
|
||||
return
|
||||
except urllib.error.HTTPError:
|
||||
pass
|
||||
except Exception:
|
||||
pass
|
||||
self._fb_oauth_result["error"] = "Login timed out after 5 minutes."
|
||||
GLib.idle_add(self._codebuff_oauth_done, dlg, spinner)
|
||||
except Exception as e:
|
||||
self._fb_oauth_result["error"] = str(e)[:200]
|
||||
GLib.idle_add(self._codebuff_oauth_done, dlg, spinner)
|
||||
|
||||
threading.Thread(target=_codebuff_auth_thread, daemon=True).start()
|
||||
dlg.connect("response", lambda d, r: d.destroy())
|
||||
dlg.run()
|
||||
|
||||
def _codebuff_oauth_done(self, dlg, spinner):
|
||||
spinner.stop()
|
||||
if self._fb_oauth_result["success"] and self._fb_oauth_result["user"]:
|
||||
user = self._fb_oauth_result["user"]
|
||||
creds_path = os.path.expanduser("~/.config/manicode/credentials.json")
|
||||
os.makedirs(os.path.dirname(creds_path), exist_ok=True)
|
||||
creds = {"default": {
|
||||
"id": user.get("id", ""),
|
||||
"name": user.get("name", ""),
|
||||
"email": user.get("email", ""),
|
||||
"authToken": user.get("authToken", ""),
|
||||
"fingerprintId": user.get("fingerprintId", ""),
|
||||
"fingerprintHash": user.get("fingerprintHash", ""),
|
||||
}}
|
||||
with open(creds_path, "w") as f:
|
||||
json.dump(creds, f, indent=2)
|
||||
os.chmod(creds_path, 0o600)
|
||||
self._entry_key.set_text(user.get("authToken", ""))
|
||||
self._oauth_status.set_markup('<span foreground="#27ae60" weight="bold">Authorization successful! Credentials saved.</span>')
|
||||
dlg.set_title("Codebuff Login – Success")
|
||||
GLib.timeout_add(1500, lambda: dlg.response(Gtk.ResponseType.OK))
|
||||
else:
|
||||
self._oauth_status.set_markup(f'<span foreground="#e74c3c">{self._fb_oauth_result["error"] or "Login failed."}</span>')
|
||||
GLib.timeout_add(3000, lambda: dlg.response(Gtk.ResponseType.CANCEL))
|
||||
|
||||
def _oauth_success(self, dlg, access_token, spinner):
|
||||
spinner.stop()
|
||||
self._entry_key.set_text(access_token)
|
||||
|
||||
@@ -254,6 +254,7 @@ _stats_lock = threading.Lock()
|
||||
_stats_pending = []
|
||||
_stats_flush_timer = None
|
||||
_STATS_FLUSH_INTERVAL = 5.0
|
||||
_STATS = {}
|
||||
|
||||
try:
|
||||
_LOG_FILE = open(os.path.join(_LOG_DIR, "proxy.log"), "a")
|
||||
@@ -286,7 +287,8 @@ _conn_pool = {}
|
||||
|
||||
_STREAM_IDLE_TIMEOUT = 300
|
||||
|
||||
_FREEBUFF_BASE_URL = "https://codebuff.com"
|
||||
_FREEBUFF_AUTH_URL = "https://codebuff.com"
|
||||
_FREEBUFF_API_URL = "https://www.codebuff.com"
|
||||
_FREEBUFF_AGENT_MAP = {
|
||||
"deepseek/deepseek-v4-pro": "base2-free-deepseek",
|
||||
"deepseek/deepseek-v4-flash": "base2-free-deepseek-flash",
|
||||
@@ -295,6 +297,7 @@ _FREEBUFF_AGENT_MAP = {
|
||||
}
|
||||
_FREEBUFF_CREDS_PATH = os.path.join(os.path.expanduser("~"), ".config", "manicode", "credentials.json")
|
||||
_codebuff_token_cache = {"token": None, "checked": 0}
|
||||
_codebuff_session_cache = {"instance_id": None, "expires": 0, "model": None}
|
||||
_codebuff_token_lock = threading.Lock()
|
||||
|
||||
def _get_codebuff_token():
|
||||
@@ -304,7 +307,8 @@ def _get_codebuff_token():
|
||||
try:
|
||||
with open(_FREEBUFF_CREDS_PATH) as f:
|
||||
creds = json.load(f)
|
||||
token = creds.get("authToken") or creds.get("apiKey") or ""
|
||||
default_account = creds.get("default", {})
|
||||
token = default_account.get("authToken") or creds.get("apiKey") or ""
|
||||
with _codebuff_token_lock:
|
||||
_codebuff_token_cache["token"] = token
|
||||
_codebuff_token_cache["checked"] = time.time()
|
||||
@@ -313,13 +317,42 @@ def _get_codebuff_token():
|
||||
print(f"[codebuff] no credentials at {_FREEBUFF_CREDS_PATH}: {e}", file=sys.stderr)
|
||||
return ""
|
||||
|
||||
def _codebuff_get_session(token, model):
|
||||
with _codebuff_token_lock:
|
||||
sc = _codebuff_session_cache
|
||||
if sc["instance_id"] and sc["expires"] > time.time() + 60 and sc["model"] == model:
|
||||
return sc["instance_id"]
|
||||
try:
|
||||
url = f"{_FREEBUFF_API_URL}/api/v1/freebuff/session"
|
||||
body = json.dumps({"model": model}).encode()
|
||||
req = urllib.request.Request(url, data=body, headers={
|
||||
"Content-Type": "application/json",
|
||||
"Authorization": f"Bearer {token}",
|
||||
"User-Agent": "codex-launcher/3.8.3",
|
||||
})
|
||||
resp = urllib.request.urlopen(req, timeout=15)
|
||||
data = json.loads(resp.read())
|
||||
instance_id = data.get("instanceId", "")
|
||||
expires_at = data.get("remainingMs", 0)
|
||||
if instance_id:
|
||||
with _codebuff_token_lock:
|
||||
_codebuff_session_cache["instance_id"] = instance_id
|
||||
_codebuff_session_cache["expires"] = time.time() + min(expires_at / 1000, 3600)
|
||||
_codebuff_session_cache["model"] = model
|
||||
print(f"[codebuff] session active, instance={instance_id[:8]}...", file=sys.stderr)
|
||||
return instance_id
|
||||
return None
|
||||
except Exception as e:
|
||||
print(f"[codebuff] session failed: {e}", file=sys.stderr)
|
||||
return None
|
||||
|
||||
def _codebuff_start_run(token, agent_id):
|
||||
url = f"{_FREEBUFF_BASE_URL}/api/v1/agent-runs"
|
||||
url = f"{_FREEBUFF_API_URL}/api/v1/agent-runs"
|
||||
body = json.dumps({"action": "START", "agentId": agent_id, "ancestorRunIds": []}).encode()
|
||||
req = urllib.request.Request(url, data=body, headers={
|
||||
"Content-Type": "application/json",
|
||||
"Authorization": f"Bearer {token}",
|
||||
"User-Agent": "codex-launcher/3.8.1",
|
||||
"User-Agent": "codex-launcher/3.8.3",
|
||||
})
|
||||
try:
|
||||
resp = urllib.request.urlopen(req, timeout=15)
|
||||
@@ -336,13 +369,13 @@ def _codebuff_start_run(token, agent_id):
|
||||
return None
|
||||
|
||||
def _codebuff_finish_run(token, run_id, status="completed"):
|
||||
url = f"{_FREEBUFF_BASE_URL}/api/v1/agent-runs"
|
||||
url = f"{_FREEBUFF_API_URL}/api/v1/agent-runs"
|
||||
body = json.dumps({"action": "FINISH", "runId": run_id, "status": status,
|
||||
"totalSteps": 1, "directCredits": 0, "totalCredits": 0}).encode()
|
||||
req = urllib.request.Request(url, data=body, headers={
|
||||
"Content-Type": "application/json",
|
||||
"Authorization": f"Bearer {token}",
|
||||
"User-Agent": "codex-launcher/3.8.1",
|
||||
"User-Agent": "codex-launcher/3.8.3",
|
||||
})
|
||||
try:
|
||||
urllib.request.urlopen(req, timeout=10)
|
||||
@@ -4537,12 +4570,21 @@ class Handler(http.server.BaseHTTPRequestHandler):
|
||||
return self.send_json(502, {"error": {"type": "upstream_error",
|
||||
"message": "Failed to start codebuff agent run. Check credentials and network."}})
|
||||
|
||||
instance_id = _codebuff_get_session(token, model)
|
||||
|
||||
input_data = body.get("input", "")
|
||||
messages = oa_input_to_messages(input_data)
|
||||
instructions = body.get("instructions", "").strip()
|
||||
if instructions:
|
||||
messages.insert(0, {"role": "system", "content": instructions})
|
||||
|
||||
metadata = {
|
||||
"run_id": run_id,
|
||||
"cost_mode": "free",
|
||||
}
|
||||
if instance_id:
|
||||
metadata["codebuff_instance_id"] = instance_id
|
||||
|
||||
chat_body = {
|
||||
"model": model,
|
||||
"messages": messages,
|
||||
@@ -4550,10 +4592,7 @@ class Handler(http.server.BaseHTTPRequestHandler):
|
||||
"max_tokens": max(body.get("max_output_tokens", 0), 64000),
|
||||
"enable_thinking": REASONING_ENABLED and REASONING_EFFORT != "none",
|
||||
"reasoning_effort": REASONING_EFFORT if REASONING_ENABLED else "none",
|
||||
"codebuff_metadata": {
|
||||
"run_id": run_id,
|
||||
"cost_mode": "free",
|
||||
},
|
||||
"codebuff_metadata": metadata,
|
||||
}
|
||||
for k in ("temperature", "top_p"):
|
||||
if k in body:
|
||||
@@ -4564,11 +4603,11 @@ class Handler(http.server.BaseHTTPRequestHandler):
|
||||
if body.get("tool_choice"):
|
||||
chat_body["tool_choice"] = body["tool_choice"]
|
||||
|
||||
target = f"{_FREEBUFF_BASE_URL}/api/v1/chat/completions"
|
||||
target = f"{_FREEBUFF_API_URL}/api/v1/chat/completions"
|
||||
headers = {
|
||||
"Content-Type": "application/json",
|
||||
"Authorization": f"Bearer {token}",
|
||||
"User-Agent": "codex-launcher/3.8.1",
|
||||
"User-Agent": "codex-launcher/3.8.3",
|
||||
}
|
||||
|
||||
print(f"[{self._session_id}] [codebuff] POST {target} model={model} stream={stream} run={run_id}", file=sys.stderr)
|
||||
@@ -4600,40 +4639,44 @@ class Handler(http.server.BaseHTTPRequestHandler):
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
last_resp_id = None
|
||||
last_output = None
|
||||
last_status = None
|
||||
finish_reason = None
|
||||
collected_events = []
|
||||
last_resp_id = [None]
|
||||
last_output = [None]
|
||||
last_status = [None]
|
||||
finish_reason = [None]
|
||||
|
||||
def _on_fb_event(event):
|
||||
if tracker and tracker.cancelled.is_set():
|
||||
return False
|
||||
for line in event.strip().split("\n"):
|
||||
if line.startswith("data: "):
|
||||
try:
|
||||
d = json.loads(line[6:])
|
||||
if d.get("type") == "response.completed":
|
||||
last_resp_id[0] = d.get("response", {}).get("id")
|
||||
last_output[0] = d.get("response", {}).get("output", [])
|
||||
last_status[0] = d.get("response", {}).get("status")
|
||||
finish_reason[0] = "length" if last_status[0] == "incomplete" else "stop"
|
||||
except Exception:
|
||||
pass
|
||||
return None
|
||||
|
||||
try:
|
||||
for event in oa_stream_to_sse(upstream, model, body.get("request_id") or body.get("id")):
|
||||
if tracker and tracker.cancelled.is_set():
|
||||
break
|
||||
collected_events.append(event)
|
||||
for line in event.strip().split("\n"):
|
||||
if line.startswith("data: "):
|
||||
try:
|
||||
d = json.loads(line[6:])
|
||||
if d.get("type") == "response.completed":
|
||||
last_resp_id = d.get("response", {}).get("id")
|
||||
last_output = d.get("response", {}).get("output", [])
|
||||
last_status = d.get("response", {}).get("status")
|
||||
finish_reason = "length" if last_status == "incomplete" else "stop"
|
||||
except Exception:
|
||||
pass
|
||||
self.stream_buffered_events(
|
||||
oa_stream_to_sse(upstream, model, body.get("request_id") or body.get("id")),
|
||||
on_event=_on_fb_event)
|
||||
except (ConnectionResetError, BrokenPipeError, ConnectionAbortedError):
|
||||
print(f"[{self._session_id}] [codebuff] client disconnected", file=sys.stderr)
|
||||
return
|
||||
|
||||
success = finish_reason != "length"
|
||||
success = finish_reason[0] != "length"
|
||||
_record_usage("codebuff", model, success, time.time() - t0)
|
||||
if last_resp_id and input_data is not None:
|
||||
store_response(last_resp_id, input_data, last_output)
|
||||
print(f"[{self._session_id}] [codebuff] stream done status={last_status} in {time.time()-t0:.1f}s", file=sys.stderr)
|
||||
if last_resp_id[0] and input_data is not None:
|
||||
store_response(last_resp_id[0], input_data, last_output[0])
|
||||
print(f"[{self._session_id}] [codebuff] stream done status={last_status[0]} in {time.time()-t0:.1f}s", file=sys.stderr)
|
||||
else:
|
||||
raw = upstream.read().decode()
|
||||
result = oa_chat_to_responses(raw, model)
|
||||
chat_resp = json.loads(raw)
|
||||
result = oa_resp_to_responses(chat_resp, model)
|
||||
self.send_json(200, result)
|
||||
rid = result.get("id")
|
||||
if rid:
|
||||
|
||||
Reference in New Issue
Block a user