mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-08 04:32:42 +00:00
fix(ai): codex-client mirrors openai-client structured-error pattern
P14. CodexClient previously threw a bare `Error("Codex request failed
(500)")` on upstream failure, which made it impossible to triage from
logs. Mirror the openai-client.ts:42-58 pattern: pull the body
(truncated 500 chars), mask any leaked `sk-` / Bearer token, and tag
the Error with `httpStatus`, `upstream`, `model`, and `bodyExcerpt`
fields. Glitchtip and Wide Events now have the same shape for both
providers.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
4a159d2b6c
commit
dcc697c33d
1 file changed
+31
-2
@@ -25,18 +25,47 @@ export class CodexClient implements AIProvider {
|
||||
this.accessToken = await this.onTokenRefresh();
|
||||
const retryAttempt = await this.doRequest(params);
|
||||
if (!retryAttempt.ok) {
|
||||
throw new Error(`Codex request failed after token refresh (${retryAttempt.status})`);
|
||||
throw await this.buildUpstreamError(
|
||||
retryAttempt,
|
||||
"Codex request failed after token refresh",
|
||||
);
|
||||
}
|
||||
return this.parseResponse(retryAttempt);
|
||||
}
|
||||
|
||||
if (!firstAttempt.ok) {
|
||||
throw new Error(`Codex request failed (${firstAttempt.status})`);
|
||||
throw await this.buildUpstreamError(firstAttempt, "Codex request failed");
|
||||
}
|
||||
|
||||
return this.parseResponse(firstAttempt);
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a structured error from a non-OK upstream response. Mirrors the
|
||||
* pattern used in `OpenAIClient.generateCompletion` so logs and Glitchtip
|
||||
* issues for both providers carry the same fields (httpStatus, upstream,
|
||||
* model, bodyExcerpt). The body excerpt is truncated to 500 chars and any
|
||||
* `sk-…` / `Bearer …` token is masked before it reaches log shipping.
|
||||
*/
|
||||
private async buildUpstreamError(
|
||||
res: Response,
|
||||
message: string,
|
||||
): Promise<Error> {
|
||||
const rawBody = await res.text().catch(() => "");
|
||||
const bodyExcerpt = rawBody
|
||||
.slice(0, 500)
|
||||
.replace(/sk-(?:ant-)?[A-Za-z0-9_-]{8,}/g, "sk-***redacted***")
|
||||
.replace(/Bearer\s+[A-Za-z0-9_.-]+/gi, "Bearer ***redacted***");
|
||||
const err = new Error(`${message} (${res.status})`);
|
||||
Object.assign(err, {
|
||||
httpStatus: res.status,
|
||||
upstream: "codex",
|
||||
model: CODEX_MODEL,
|
||||
bodyExcerpt,
|
||||
});
|
||||
return err;
|
||||
}
|
||||
|
||||
private async doRequest(params: CompletionParams): Promise<Response> {
|
||||
return fetch(CODEX_ENDPOINT, {
|
||||
method: "POST",
|
||||
|
||||
Reference in new issue
Block a user