v3.9.7 — Forward real codebuff error messages, fix BrokenPipeError crash, fix SyntaxWarnings
This commit is contained in:
@@ -70,9 +70,9 @@ FIX 6: Double-wrapped arguments (nested {"cmd": "{\"cmd\": \"curl...\"}"}")
|
||||
|
||||
FIX 7: _extract_field can't read values starting with \"
|
||||
Symptom: sandbox_permissions="allow_all" passes through unnormalized because
|
||||
_extract_field sees val_start=\ (backslash) which != " or { → returns None
|
||||
_extract_field sees val_start=\\ (backslash) which != \" or { → returns None
|
||||
Fix: Skip leading backslash before checking for " or { value type.
|
||||
Location: _extract_field() leading-\ skip
|
||||
Location: _extract_field() leading-backslash skip
|
||||
|
||||
FIX 8: Adaptive probing caused format mismatch (REVERTED)
|
||||
Symptom: Probe system discovered OpenAI tool_calls+role=tool format but CC API couldn't
|
||||
@@ -335,10 +335,31 @@ def _codebuff_get_session(token, model):
|
||||
req = urllib.request.Request(url, data=body, headers={
|
||||
"Content-Type": "application/json",
|
||||
"Authorization": f"Bearer {token}",
|
||||
"User-Agent": "codex-launcher/3.9.0",
|
||||
"User-Agent": "codex-launcher/3.9.7",
|
||||
"x-codebuff-model": model,
|
||||
})
|
||||
resp = urllib.request.urlopen(req, timeout=15)
|
||||
try:
|
||||
resp = urllib.request.urlopen(req, timeout=15)
|
||||
except urllib.error.HTTPError as e:
|
||||
err_body = e.read().decode()[:1000]
|
||||
if e.code == 429:
|
||||
retry_s = 120
|
||||
user_msg = ""
|
||||
try:
|
||||
err_data = json.loads(err_body)
|
||||
retry_ms = err_data.get("retryAfterMs", 0)
|
||||
if retry_ms:
|
||||
retry_s = retry_ms / 1000
|
||||
user_msg = err_data.get("message", err_data.get("error", ""))
|
||||
if isinstance(user_msg, dict):
|
||||
user_msg = user_msg.get("message", "")
|
||||
except Exception:
|
||||
pass
|
||||
if not user_msg:
|
||||
user_msg = _sanitize_err_body(err_body)
|
||||
raise RateLimitError(retry_s, user_msg)
|
||||
print(f"[codebuff] session HTTP {e.code}: {err_body[:200]}", file=sys.stderr)
|
||||
return None
|
||||
data = json.loads(resp.read())
|
||||
instance_id = data.get("instanceId", data.get("data", {}).get("instance_id", ""))
|
||||
expires_at = data.get("remainingMs", 0)
|
||||
@@ -350,6 +371,8 @@ def _codebuff_get_session(token, model):
|
||||
print(f"[codebuff] session active, instance={instance_id[:8]}...", file=sys.stderr)
|
||||
return instance_id
|
||||
return None
|
||||
except RateLimitError:
|
||||
raise
|
||||
except Exception as e:
|
||||
print(f"[codebuff] session failed: {e}", file=sys.stderr)
|
||||
return None
|
||||
@@ -360,21 +383,31 @@ def _codebuff_start_run(token, agent_id):
|
||||
req = urllib.request.Request(url, data=body, headers={
|
||||
"Content-Type": "application/json",
|
||||
"Authorization": f"Bearer {token}",
|
||||
"User-Agent": "codex-launcher/3.9.0",
|
||||
"User-Agent": "codex-launcher/3.9.7",
|
||||
})
|
||||
try:
|
||||
resp = urllib.request.urlopen(req, timeout=15)
|
||||
data = json.loads(resp.read())
|
||||
run_id = data.get("runId")
|
||||
print(f"[codebuff] started run {run_id} for agent {agent_id}", file=sys.stderr)
|
||||
return run_id
|
||||
return run_id, None
|
||||
except urllib.error.HTTPError as e:
|
||||
err = e.read().decode()[:300]
|
||||
err = e.read().decode()[:500]
|
||||
print(f"[codebuff] start run failed: HTTP {e.code}: {err}", file=sys.stderr)
|
||||
return None
|
||||
if e.code == 429:
|
||||
retry_s = 120
|
||||
try:
|
||||
err_data = json.loads(err)
|
||||
retry_ms = err_data.get("retryAfterMs", 0)
|
||||
if retry_ms:
|
||||
retry_s = retry_ms / 1000
|
||||
except Exception:
|
||||
pass
|
||||
return None, ("rate_limit_error", 429, retry_s, _sanitize_err_body(err))
|
||||
return None, ("upstream_error", e.code, 0, _sanitize_err_body(err))
|
||||
except Exception as e:
|
||||
print(f"[codebuff] start run error: {e}", file=sys.stderr)
|
||||
return None
|
||||
return None, ("proxy_error", 502, 0, str(e))
|
||||
|
||||
def _codebuff_finish_run(token, run_id, status="completed"):
|
||||
url = f"{_FREEBUFF_API_URL}/api/v1/agent-runs"
|
||||
@@ -383,7 +416,7 @@ def _codebuff_finish_run(token, run_id, status="completed"):
|
||||
req = urllib.request.Request(url, data=body, headers={
|
||||
"Content-Type": "application/json",
|
||||
"Authorization": f"Bearer {token}",
|
||||
"User-Agent": "codex-launcher/3.9.0",
|
||||
"User-Agent": "codex-launcher/3.9.7",
|
||||
})
|
||||
try:
|
||||
urllib.request.urlopen(req, timeout=10)
|
||||
@@ -392,6 +425,12 @@ def _codebuff_finish_run(token, run_id, status="completed"):
|
||||
|
||||
# ═══════════════════════════════════════════════════════════════════
|
||||
# Multi-account rotation system
|
||||
class RateLimitError(Exception):
|
||||
def __init__(self, retry_seconds, message=""):
|
||||
self.retry_seconds = retry_seconds
|
||||
self.message = message
|
||||
super().__init__(f"rate-limited for {retry_seconds:.0f}s: {message}")
|
||||
|
||||
# ═══════════════════════════════════════════════════════════════════
|
||||
|
||||
class AccountPool:
|
||||
@@ -2804,7 +2843,7 @@ def _parse_commandcode_text_tool_calls(text):
|
||||
|
||||
Delegates to _extract_args() for the arguments field (handles unescaped + escaped JSON).
|
||||
Delegates to _extract_field() for name/id/sandbox_permissions/justification
|
||||
(with FIX 7 for leading-\ handling).
|
||||
(with FIX 7 for leading-backslash handling).
|
||||
|
||||
Normalizes sandbox_permissions to valid values (use_default|require_escalated|with_user_approval)
|
||||
[FIX 6] Prevents double-wrapped args: {"cmd": "{\"cmd\": \"curl...\"}"}
|
||||
@@ -5209,13 +5248,30 @@ class Handler(http.server.BaseHTTPRequestHandler):
|
||||
if attempt > 0:
|
||||
print(f"[codebuff] rotation attempt {attempt+1}/{n_accounts}, trying account {acct_id}", file=sys.stderr)
|
||||
|
||||
run_id = _codebuff_start_run(token, agent_id)
|
||||
run_id, run_err = _codebuff_start_run(token, agent_id)
|
||||
if not run_id:
|
||||
_fb_pool.mark_rate_limited(acct, 60)
|
||||
last_err = ("upstream_error", 502, "Failed to start codebuff agent run. Check credentials and network.")
|
||||
if run_err and run_err[0] == "rate_limit_error":
|
||||
retry_s = run_err[2]
|
||||
_fb_pool.mark_rate_limited(acct, retry_s)
|
||||
last_err = ("rate_limit_error", run_err[1], f"Account {acct_id} rate-limited by Codebuff: {run_err[3]}")
|
||||
else:
|
||||
_fb_pool.mark_rate_limited(acct, 60)
|
||||
last_err = ("upstream_error", run_err[1] if run_err else 502,
|
||||
f"Failed to start agent run for {acct_id}: {run_err[3] if run_err else 'unknown error'}")
|
||||
continue
|
||||
|
||||
instance_id = _codebuff_get_session(token, model)
|
||||
try:
|
||||
instance_id = _codebuff_get_session(token, model)
|
||||
except RateLimitError as rle:
|
||||
retry_s = rle.retry_seconds
|
||||
fb_msg = rle.message
|
||||
mins = int(retry_s // 60)
|
||||
user_msg = fb_msg if fb_msg else f"Daily session limit reached. Resets in {mins}m."
|
||||
print(f"[codebuff] session 429 for {acct_id}, retry after {retry_s:.0f}s", file=sys.stderr)
|
||||
_fb_pool.mark_rate_limited(acct, retry_s)
|
||||
_codebuff_finish_run(token, run_id, "completed")
|
||||
last_err = ("rate_limit_error", 429, user_msg)
|
||||
continue
|
||||
|
||||
input_data = body.get("input", "")
|
||||
instructions = body.get("instructions", "").strip()
|
||||
@@ -5249,7 +5305,7 @@ class Handler(http.server.BaseHTTPRequestHandler):
|
||||
headers = {
|
||||
"Content-Type": "application/json",
|
||||
"Authorization": f"Bearer {token}",
|
||||
"User-Agent": "codex-launcher/3.9.0",
|
||||
"User-Agent": "codex-launcher/3.9.7",
|
||||
"x-codebuff-model": model,
|
||||
}
|
||||
if instance_id:
|
||||
@@ -5266,14 +5322,22 @@ class Handler(http.server.BaseHTTPRequestHandler):
|
||||
_codebuff_finish_run(token, run_id, "failed")
|
||||
if e.code in (429, 426):
|
||||
reset_ms = 0
|
||||
fb_msg = ""
|
||||
try:
|
||||
err_json = json.loads(err_body)
|
||||
reset_ms = err_json.get("retryAfterMs", 0)
|
||||
fb_msg = err_json.get("message", err_json.get("error", ""))
|
||||
if isinstance(fb_msg, dict):
|
||||
fb_msg = fb_msg.get("message", "")
|
||||
except Exception:
|
||||
pass
|
||||
duration = max(reset_ms / 1000, 120) if reset_ms else 120
|
||||
mins = int(duration // 60)
|
||||
if not fb_msg:
|
||||
fb_msg = _sanitize_err_body(err_body)
|
||||
user_msg = f"{fb_msg} (resets in {mins}m)" if fb_msg else f"Rate limited. Resets in {mins}m."
|
||||
_fb_pool.mark_rate_limited(acct, duration)
|
||||
last_err = ("upstream_error", e.code, _sanitize_err_body(err_body))
|
||||
last_err = ("rate_limit_error", e.code, user_msg)
|
||||
print(f"[codebuff] account {acct_id} got HTTP {e.code}, rotating", file=sys.stderr)
|
||||
continue
|
||||
if _is_reasoning_content_error(err_body):
|
||||
@@ -5357,13 +5421,38 @@ class Handler(http.server.BaseHTTPRequestHandler):
|
||||
return
|
||||
|
||||
if last_err:
|
||||
return self.send_json(last_err[1], {"error": {"type": last_err[0], "message": f"All {n_accounts} accounts exhausted. {last_err[2]}"}})
|
||||
msg = last_err[2]
|
||||
resp_id = f"resp_{uuid.uuid4().hex[:24]}"
|
||||
result = {
|
||||
"id": resp_id,
|
||||
"object": "response",
|
||||
"created_at": int(time.time()),
|
||||
"model": model,
|
||||
"status": "completed",
|
||||
"output": [{
|
||||
"id": f"msg_{uuid.uuid4().hex[:24]}",
|
||||
"type": "message",
|
||||
"role": "assistant",
|
||||
"content": [{
|
||||
"type": "output_text",
|
||||
"text": msg,
|
||||
"annotations": [],
|
||||
}],
|
||||
"status": "completed",
|
||||
}],
|
||||
"usage": {"input_tokens": 0, "output_tokens": 0, "total_tokens": 0},
|
||||
}
|
||||
try:
|
||||
return self.send_json(200, result)
|
||||
except (BrokenPipeError, ConnectionResetError, ConnectionAbortedError):
|
||||
return
|
||||
|
||||
def _fb_retry_thinking_disabled(self, body, model, token, agent_id, stream, tracker, input_data, instructions, original_error, acct=None):
|
||||
run_id = _codebuff_start_run(token, agent_id)
|
||||
run_id, run_err = _codebuff_start_run(token, agent_id)
|
||||
if not run_id:
|
||||
return self.send_json(502, {"error": {"type": "upstream_error",
|
||||
"message": "Failed to start codebuff agent run for retry."}})
|
||||
msg = run_err[3] if run_err else "unknown error"
|
||||
return self.send_json(run_err[1] if run_err else 502, {"error": {"type": run_err[0] if run_err else "upstream_error",
|
||||
"message": f"Failed to start agent run for retry: {msg}"}})
|
||||
instance_id = _codebuff_get_session(token, model)
|
||||
messages = _fb_input_to_messages(input_data, instructions)
|
||||
_codebuff_hard_disable_reasoning(messages)
|
||||
@@ -5385,7 +5474,7 @@ class Handler(http.server.BaseHTTPRequestHandler):
|
||||
if body.get("tool_choice"):
|
||||
chat_body["tool_choice"] = body["tool_choice"]
|
||||
target = f"{_FREEBUFF_API_URL}/api/v1/chat/completions"
|
||||
headers = {"Content-Type": "application/json", "Authorization": f"Bearer {token}", "User-Agent": "codex-launcher/3.9.0", "x-codebuff-model": model}
|
||||
headers = {"Content-Type": "application/json", "Authorization": f"Bearer {token}", "User-Agent": "codex-launcher/3.9.7", "x-codebuff-model": model}
|
||||
if instance_id:
|
||||
headers["x-codebuff-instance-id"] = instance_id
|
||||
print(f"[codebuff] retry POST {target} model={model} stream={stream} run={run_id} (thinking disabled via DeepSeek native)", file=sys.stderr)
|
||||
|
||||
Reference in New Issue
Block a user