fix(ai): prefix HTTP status codes onto Azure OpenAI and OpenAI Responses error messages so auto-retry fires on 5xx/429
closes #4232
This commit is contained in:
@@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
|
- Fixed Azure OpenAI Responses and OpenAI Responses error formatting to prefix HTTP status codes onto `errorMessage`, so transient 5xx and 429 errors are correctly matched by the agent-level auto-retry classifier ([#4232](https://github.com/earendil-works/pi/issues/4232)).
|
||||||
- Fixed Xiaomi MiMo model metadata to use the OpenAI-compatible endpoints and `openai-completions` API, restoring multi-turn thinking/tool-call sessions ([#4505](https://github.com/earendil-works/pi/issues/4505)).
|
- Fixed Xiaomi MiMo model metadata to use the OpenAI-compatible endpoints and `openai-completions` API, restoring multi-turn thinking/tool-call sessions ([#4505](https://github.com/earendil-works/pi/issues/4505)).
|
||||||
- Fixed OpenCode Go Kimi reasoning replay by normalizing streamed `reasoning` fields back to `reasoning_content` for OpenCode Go only ([#4251](https://github.com/earendil-works/pi/issues/4251)).
|
- Fixed OpenCode Go Kimi reasoning replay by normalizing streamed `reasoning` fields back to `reasoning_content` for OpenCode Go only ([#4251](https://github.com/earendil-works/pi/issues/4251)).
|
||||||
|
|
||||||
|
|||||||
@@ -40,6 +40,22 @@ function resolveDeploymentName(model: Model<"azure-openai-responses">, options?:
|
|||||||
return mappedDeployment || model.id;
|
return mappedDeployment || model.id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function formatAzureOpenAIError(error: unknown): string {
|
||||||
|
if (error instanceof Error) {
|
||||||
|
const status = (error as Error & { status?: unknown }).status;
|
||||||
|
const statusCode = typeof status === "number" ? status : undefined;
|
||||||
|
if (statusCode !== undefined) {
|
||||||
|
return `Azure OpenAI API error (${statusCode}): ${error.message}`;
|
||||||
|
}
|
||||||
|
return error.message;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return JSON.stringify(error);
|
||||||
|
} catch {
|
||||||
|
return String(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Azure OpenAI Responses-specific options
|
// Azure OpenAI Responses-specific options
|
||||||
export interface AzureOpenAIResponsesOptions extends StreamOptions {
|
export interface AzureOpenAIResponsesOptions extends StreamOptions {
|
||||||
reasoningEffort?: "minimal" | "low" | "medium" | "high" | "xhigh";
|
reasoningEffort?: "minimal" | "low" | "medium" | "high" | "xhigh";
|
||||||
@@ -119,7 +135,7 @@ export const streamAzureOpenAIResponses: StreamFunction<"azure-openai-responses"
|
|||||||
delete (block as { partialJson?: string }).partialJson;
|
delete (block as { partialJson?: string }).partialJson;
|
||||||
}
|
}
|
||||||
output.stopReason = options?.signal?.aborted ? "aborted" : "error";
|
output.stopReason = options?.signal?.aborted ? "aborted" : "error";
|
||||||
output.errorMessage = error instanceof Error ? error.message : JSON.stringify(error);
|
output.errorMessage = formatAzureOpenAIError(error);
|
||||||
stream.push({ type: "error", reason: output.stopReason, error: output });
|
stream.push({ type: "error", reason: output.stopReason, error: output });
|
||||||
stream.end();
|
stream.end();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -51,6 +51,22 @@ function getPromptCacheRetention(
|
|||||||
return cacheRetention === "long" && compat.supportsLongCacheRetention ? "24h" : undefined;
|
return cacheRetention === "long" && compat.supportsLongCacheRetention ? "24h" : undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function formatOpenAIResponsesError(error: unknown): string {
|
||||||
|
if (error instanceof Error) {
|
||||||
|
const status = (error as Error & { status?: unknown }).status;
|
||||||
|
const statusCode = typeof status === "number" ? status : undefined;
|
||||||
|
if (statusCode !== undefined) {
|
||||||
|
return `OpenAI API error (${statusCode}): ${error.message}`;
|
||||||
|
}
|
||||||
|
return error.message;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return JSON.stringify(error);
|
||||||
|
} catch {
|
||||||
|
return String(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// OpenAI Responses-specific options
|
// OpenAI Responses-specific options
|
||||||
export interface OpenAIResponsesOptions extends StreamOptions {
|
export interface OpenAIResponsesOptions extends StreamOptions {
|
||||||
reasoningEffort?: "minimal" | "low" | "medium" | "high" | "xhigh";
|
reasoningEffort?: "minimal" | "low" | "medium" | "high" | "xhigh";
|
||||||
@@ -130,7 +146,7 @@ export const streamOpenAIResponses: StreamFunction<"openai-responses", OpenAIRes
|
|||||||
delete (block as { partialJson?: string }).partialJson;
|
delete (block as { partialJson?: string }).partialJson;
|
||||||
}
|
}
|
||||||
output.stopReason = options?.signal?.aborted ? "aborted" : "error";
|
output.stopReason = options?.signal?.aborted ? "aborted" : "error";
|
||||||
output.errorMessage = error instanceof Error ? error.message : JSON.stringify(error);
|
output.errorMessage = formatOpenAIResponsesError(error);
|
||||||
stream.push({ type: "error", reason: output.stopReason, error: output });
|
stream.push({ type: "error", reason: output.stopReason, error: output });
|
||||||
stream.end();
|
stream.end();
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user