diff --git a/packages/provider-pi/src/server/textGeneration.test.ts b/packages/provider-pi/src/server/textGeneration.test.ts index 0e6a8a3c07780..e1b6c682b7f85 100644 --- a/packages/provider-pi/src/server/textGeneration.test.ts +++ b/packages/provider-pi/src/server/textGeneration.test.ts @@ -155,6 +155,22 @@ it.effect("reports Pi's turn error when the reply is empty", () => assert.equal(error._tag, "TextGenerationError"); assert.equal(error.detail, "Pi could not generate text: the model provider returned HTTP 401."); + + // Pi's errorMessage for a 429 from its Gemini and Mistral providers. + for (const errorMessage of [ + '{"error":{"code":429,"message":"Quota exceeded for key AIzaSECRET123","status":"RESOURCE_EXHAUSTED"}}', + 'Mistral API error (429): {"object":"error","message":"Rate limit exceeded for SECRET123"}', + ]) { + const providerError = yield* generateCommitMessage([failedTurnWith(errorMessage)]).pipe( + Effect.flip, + ); + + assert.equal( + providerError.detail, + "Pi could not generate text: the model provider returned HTTP 429.", + ); + assert.notInclude(providerError.message, "SECRET123"); + } }), ); diff --git a/packages/provider-pi/src/server/textGeneration.ts b/packages/provider-pi/src/server/textGeneration.ts index 40697f2d4280a..e565c0f2e5c94 100644 --- a/packages/provider-pi/src/server/textGeneration.ts +++ b/packages/provider-pi/src/server/textGeneration.ts @@ -16,9 +16,14 @@ const PI_TIMEOUT_MS = 180_000; const isTextGenerationError = Schema.is(TextGenerationError); // Pi's `errorMessage` is the provider's raw response text and can echo -// credentials, so only a leading HTTP status reaches the error detail. +// credentials, so only its HTTP status reaches the error detail. Most SDKs lead +// with it; Mistral's follows "Mistral API error (", and Gemini's is the body's `error.code`. +const PI_ERROR_STATUS = + /^(?:Mistral API error \()?([45]\d\d)\b|^\{"error":\{.*?"code":([45]\d\d)[,}]/; + const piTurnErrorDetail = (errorMessage: string | undefined) => { - const status = errorMessage?.match(/^([45]\d\d)\b/)?.[1]; + const match = errorMessage?.match(PI_ERROR_STATUS); + const status = match?.[1] ?? match?.[2]; return status === undefined ? "Pi could not generate text: the model provider reported an error." : `Pi could not generate text: the model provider returned HTTP ${status}.`;