fix(llm): surface llm-connect's error body instead of a bare 502
llm-connect maps every provider API error onto HTTP 502 and puts the real cause in the body (llm_connect/server.py::_error_response: error, provider_status). LLMConnectClient.complete called raise_for_status() and threw that body away, so a revoked OpenRouter key was indistinguishable from a downed gateway — four days of production evidence read as "llm-connect is down". Live check confirms one fault, not two: the llm-connect pod is Running 1/1 with healthy endpoints, and today's FI/Binky/triage runs still 502 after yesterday's rollout, matching the sanitized upstream 401 railiance-platform reported. The client now raises with error, provider_status, provider, model and a bounded copy of llm-connect's already-sanitized message, under a field allowlist so no provider blob or key material reaches the run artefact. Refs ACTIVITY-WP-0031-T01, T03. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
0d9ddbaa08
commit
459a272974
4 changed files with 169 additions and 5 deletions
|
|
@ -55,7 +55,8 @@ class LLMConnectClient:
|
|||
json=payload,
|
||||
timeout=self.timeout_seconds,
|
||||
)
|
||||
resp.raise_for_status()
|
||||
if resp.status_code >= 400:
|
||||
raise RuntimeError(_llm_connect_error_text(resp))
|
||||
data = resp.json()
|
||||
self.last_response_metadata = _extract_response_metadata(data)
|
||||
content = data.get("content")
|
||||
|
|
@ -64,6 +65,43 @@ class LLMConnectClient:
|
|||
return content
|
||||
|
||||
|
||||
_SAFE_ERROR_KEYS = ("error", "provider_status", "provider", "model")
|
||||
_MAX_ERROR_MESSAGE_CHARS = 400
|
||||
|
||||
|
||||
def _llm_connect_error_text(resp: httpx.Response) -> str:
|
||||
"""Describe an llm-connect error using its own diagnostic body.
|
||||
|
||||
llm-connect maps every provider API error onto HTTP 502 and puts the real
|
||||
cause in the body (``error``, ``provider_status``) — see
|
||||
``llm_connect/server.py::_error_response``. Reporting only the transport
|
||||
status makes a rejected provider key look identical to a dead gateway,
|
||||
which is exactly the ambiguity ACTIVITY-WP-0031-T03 evidence must not have.
|
||||
|
||||
llm-connect sanitizes its own ``message`` before returning it; this keeps a
|
||||
bounded copy of that text plus an allowlist of non-secret fields.
|
||||
"""
|
||||
base = f"llm-connect returned HTTP {resp.status_code}"
|
||||
try:
|
||||
body = resp.json()
|
||||
except ValueError:
|
||||
return base
|
||||
if not isinstance(body, dict):
|
||||
return base
|
||||
|
||||
parts = [
|
||||
f"{key}={body[key]}"
|
||||
for key in _SAFE_ERROR_KEYS
|
||||
if body.get(key) not in (None, "")
|
||||
]
|
||||
message = body.get("message")
|
||||
if isinstance(message, str) and message.strip():
|
||||
parts.append(f"message={message.strip()[:_MAX_ERROR_MESSAGE_CHARS]}")
|
||||
if not parts:
|
||||
return base
|
||||
return f"{base}: " + "; ".join(parts)
|
||||
|
||||
|
||||
_SAFE_RESPONSE_METADATA_KEYS = {
|
||||
"finish_reason",
|
||||
"usage",
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue