diff --git a/src/api/providers/base-openai-compatible-provider.ts b/src/api/providers/base-openai-compatible-provider.ts index f196b5f309..ff5e41b88b 100644 --- a/src/api/providers/base-openai-compatible-provider.ts +++ b/src/api/providers/base-openai-compatible-provider.ts @@ -6,6 +6,7 @@ import type { ModelInfo } from "@roo-code/types" import type { ApiHandlerOptions } from "../../shared/api" import { ApiStream } from "../transform/stream" import { convertToOpenAiMessages } from "../transform/openai-format" +import { t } from "../../i18n" import type { SingleCompletionHandler, ApiHandlerCreateMessageMetadata } from "../index" import { DEFAULT_HEADERS } from "./constants" @@ -83,25 +84,47 @@ export abstract class BaseOpenAiCompatibleProvider 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) { - const delta = chunk.choices[0]?.delta + for await (const chunk of stream) { + const delta = chunk.choices[0]?.delta - if (delta?.content) { - yield { - type: "text", - text: delta.content, + if (delta?.content) { + yield { + type: "text", + text: delta.content, + } + } + + if (chunk.usage) { + yield { + type: "usage", + inputTokens: chunk.usage.prompt_tokens || 0, + outputTokens: chunk.usage.completion_tokens || 0, + } } } - - if (chunk.usage) { - yield { - type: "usage", - inputTokens: chunk.usage.prompt_tokens || 0, - outputTokens: chunk.usage.completion_tokens || 0, - } + } 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) } + + // 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 }) 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) { - 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 diff --git a/src/i18n/locales/en/common.json b/src/i18n/locales/en/common.json index 05d039a495..d073b163a9 100644 --- a/src/i18n/locales/en/common.json +++ b/src/i18n/locales/en/common.json @@ -100,6 +100,11 @@ "genericError": "Cerebras API Error ({{status}}): {{message}}", "noResponseBody": "Cerebras API Error: No response body", "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": { diff --git a/src/i18n/locales/zh-CN/common.json b/src/i18n/locales/zh-CN/common.json index 808d534572..0cf7f16b12 100644 --- a/src/i18n/locales/zh-CN/common.json +++ b/src/i18n/locales/zh-CN/common.json @@ -105,6 +105,11 @@ "genericError": "Cerebras API 错误 ({{status}}):{{message}}", "noResponseBody": "Cerebras API 错误:无响应主体", "completionError": "Cerebras 完成错误:{{error}}" + }, + "openaiCompatible": { + "rateLimitExceeded": "{{provider}} API 速率限制已超出。请稍等片刻后再发起请求。如果您使用的是免费套餐,请考虑升级您的计划或降低请求频率。", + "genericError": "{{provider}} API 错误:{{error}}", + "completionError": "{{provider}} 完成错误:{{error}}" } }, "warnings": {