fix: improve OpenAI Compatible rate limit error handling

- Add specific error handling for 429 status codes in BaseOpenAiCompatibleProvider
- Add localized error messages for rate limiting in English and Chinese
- Provide clearer guidance to users when hitting rate limits
- Suggest upgrading plan or reducing request frequency

Fixes #6928
This commit is contained in:
Roo Code 2025-08-11 11:16:59 +00:00
parent f53fd39014
commit 80d052dcec
3 changed files with 62 additions and 16 deletions

View file

@ -6,6 +6,7 @@ import type { ModelInfo } from "@roo-code/types"
import type { ApiHandlerOptions } from "../../shared/api" import type { ApiHandlerOptions } from "../../shared/api"
import { ApiStream } from "../transform/stream" import { ApiStream } from "../transform/stream"
import { convertToOpenAiMessages } from "../transform/openai-format" import { convertToOpenAiMessages } from "../transform/openai-format"
import { t } from "../../i18n"
import type { SingleCompletionHandler, ApiHandlerCreateMessageMetadata } from "../index" import type { SingleCompletionHandler, ApiHandlerCreateMessageMetadata } from "../index"
import { DEFAULT_HEADERS } from "./constants" import { DEFAULT_HEADERS } from "./constants"
@ -83,25 +84,47 @@ export abstract class BaseOpenAiCompatibleProvider<ModelName extends string>
stream_options: { include_usage: true }, stream_options: { include_usage: true },
} }
const stream = await this.client.chat.completions.create(params) try {
const stream = await this.client.chat.completions.create(params)
for await (const chunk of stream) { for await (const chunk of stream) {
const delta = chunk.choices[0]?.delta const delta = chunk.choices[0]?.delta
if (delta?.content) { if (delta?.content) {
yield { yield {
type: "text", type: "text",
text: delta.content, text: delta.content,
}
}
if (chunk.usage) {
yield {
type: "usage",
inputTokens: chunk.usage.prompt_tokens || 0,
outputTokens: chunk.usage.completion_tokens || 0,
}
} }
} }
} catch (error: any) {
if (chunk.usage) { // Handle rate limiting errors specifically
yield { if (error?.status === 429 || error?.response?.status === 429) {
type: "usage", const errorMessage = t("common:errors.openaiCompatible.rateLimitExceeded", {
inputTokens: chunk.usage.prompt_tokens || 0, provider: this.providerName,
outputTokens: chunk.usage.completion_tokens || 0, })
} throw new Error(errorMessage)
} }
// Handle other errors
if (error instanceof Error) {
throw new Error(
t("common:errors.openaiCompatible.genericError", {
provider: this.providerName,
error: error.message,
}),
)
}
throw error
} }
} }
@ -115,9 +138,22 @@ export abstract class BaseOpenAiCompatibleProvider<ModelName extends string>
}) })
return response.choices[0]?.message.content || "" return response.choices[0]?.message.content || ""
} catch (error) { } catch (error: any) {
// Handle rate limiting errors specifically
if (error?.status === 429 || error?.response?.status === 429) {
const errorMessage = t("common:errors.openaiCompatible.rateLimitExceeded", {
provider: this.providerName,
})
throw new Error(errorMessage)
}
if (error instanceof Error) { if (error instanceof Error) {
throw new Error(`${this.providerName} completion error: ${error.message}`) throw new Error(
t("common:errors.openaiCompatible.completionError", {
provider: this.providerName,
error: error.message,
}),
)
} }
throw error throw error

View file

@ -100,6 +100,11 @@
"genericError": "Cerebras API Error ({{status}}): {{message}}", "genericError": "Cerebras API Error ({{status}}): {{message}}",
"noResponseBody": "Cerebras API Error: No response body", "noResponseBody": "Cerebras API Error: No response body",
"completionError": "Cerebras completion error: {{error}}" "completionError": "Cerebras completion error: {{error}}"
},
"openaiCompatible": {
"rateLimitExceeded": "{{provider}} API rate limit exceeded. Please wait a moment before making another request. If using a free tier, consider upgrading your plan or reducing request frequency.",
"genericError": "{{provider}} API error: {{error}}",
"completionError": "{{provider}} completion error: {{error}}"
} }
}, },
"warnings": { "warnings": {

View file

@ -105,6 +105,11 @@
"genericError": "Cerebras API 错误 ({{status}}){{message}}", "genericError": "Cerebras API 错误 ({{status}}){{message}}",
"noResponseBody": "Cerebras API 错误:无响应主体", "noResponseBody": "Cerebras API 错误:无响应主体",
"completionError": "Cerebras 完成错误:{{error}}" "completionError": "Cerebras 完成错误:{{error}}"
},
"openaiCompatible": {
"rateLimitExceeded": "{{provider}} API 速率限制已超出。请稍等片刻后再发起请求。如果您使用的是免费套餐,请考虑升级您的计划或降低请求频率。",
"genericError": "{{provider}} API 错误:{{error}}",
"completionError": "{{provider}} 完成错误:{{error}}"
} }
}, },
"warnings": { "warnings": {