(error: unknown)
| 90 | * Convert an OpenAI SDK error (or raw Error) to a kosong `ChatProviderError`. |
| 91 | */ |
| 92 | export function convertOpenAIError(error: unknown): ChatProviderError { |
| 93 | if (error instanceof ChatProviderError) { |
| 94 | return error; |
| 95 | } |
| 96 | // v6: APIConnectionTimeoutError extends APIConnectionError, check timeout first |
| 97 | if (error instanceof OpenAITimeoutError) { |
| 98 | return new APITimeoutError(error.message); |
| 99 | } |
| 100 | if (error instanceof OpenAIConnectionError) { |
| 101 | return new APIConnectionError(error.message); |
| 102 | } |
| 103 | // APIError with a status code => status error |
| 104 | if (error instanceof OpenAIAPIError && typeof error.status === 'number') { |
| 105 | const reqId = error.requestID ?? null; |
| 106 | return normalizeAPIStatusError(error.status, error.message, reqId); |
| 107 | } |
| 108 | // Base APIError with no status and no body => transport-layer failure. |
| 109 | // When the error has a body (e.g. SSE error events from the server), |
| 110 | // skip the heuristic to avoid misclassifying server-side errors. |
| 111 | if ( |
| 112 | error instanceof OpenAIAPIError && |
| 113 | error.constructor === OpenAIAPIError && |
| 114 | error.error === undefined |
| 115 | ) { |
| 116 | return classifyBaseApiError(error.message); |
| 117 | } |
| 118 | if (error instanceof OpenAIError) { |
| 119 | return new ChatProviderError(`Error: ${error.message}`); |
| 120 | } |
| 121 | // Raw, non-SDK errors (e.g. undici's `TypeError: terminated` raised when a |
| 122 | // streaming response body is dropped mid-flight) never get wrapped by the |
| 123 | // OpenAI SDK during stream iteration. Route them through the same |
| 124 | // transport-layer heuristic so genuine connection failures become |
| 125 | // retryable instead of fatal generic errors. |
| 126 | if (error instanceof Error) { |
| 127 | return classifyBaseApiError(error.message); |
| 128 | } |
| 129 | return new ChatProviderError(`Error: ${String(error)}`); |
| 130 | } |
| 131 | /** Shape of a function-type tool call (subset used by the guard). */ |
| 132 | export interface FunctionToolCallShape { |
| 133 | type: 'function'; |
no test coverage detected