diff --git a/.changeset/wild-dragons-leave.md b/.changeset/wild-dragons-leave.md new file mode 100644 index 0000000000..05320a4aa2 --- /dev/null +++ b/.changeset/wild-dragons-leave.md @@ -0,0 +1,5 @@ +--- +"roo-cline": patch +--- + +Add o3-mini support to openai compatible diff --git a/src/api/providers/openai.ts b/src/api/providers/openai.ts index 9262f3b75a..caa99def09 100644 --- a/src/api/providers/openai.ts +++ b/src/api/providers/openai.ts @@ -66,6 +66,11 @@ export class OpenAiHandler extends BaseProvider implements SingleCompletionHandl const deepseekReasoner = modelId.includes("deepseek-reasoner") const ark = modelUrl.includes(".volces.com") + if (modelId.startsWith("o3-mini")) { + yield* this.handleO3FamilyMessage(modelId, systemPrompt, messages) + return + } + if (this.options.openAiStreamingEnabled ?? true) { const systemMessage: OpenAI.Chat.ChatCompletionSystemMessageParam = { role: "system", @@ -169,6 +174,69 @@ export class OpenAiHandler extends BaseProvider implements SingleCompletionHandl throw error } } + + private async *handleO3FamilyMessage( + modelId: string, + systemPrompt: string, + messages: Anthropic.Messages.MessageParam[], + ): ApiStream { + if (this.options.openAiStreamingEnabled ?? true) { + const stream = await this.client.chat.completions.create({ + model: "o3-mini", + messages: [ + { + role: "developer", + content: `Formatting re-enabled\n${systemPrompt}`, + }, + ...convertToOpenAiMessages(messages), + ], + stream: true, + stream_options: { include_usage: true }, + reasoning_effort: this.getModel().info.reasoningEffort, + }) + + yield* this.handleStreamResponse(stream) + } else { + const requestOptions: OpenAI.Chat.Completions.ChatCompletionCreateParamsNonStreaming = { + model: modelId, + messages: [ + { + role: "developer", + content: `Formatting re-enabled\n${systemPrompt}`, + }, + ...convertToOpenAiMessages(messages), + ], + } + + const response = await this.client.chat.completions.create(requestOptions) + + yield { + type: "text", + text: response.choices[0]?.message.content || "", + } + yield this.processUsageMetrics(response.usage) + } + } + + private async *handleStreamResponse(stream: AsyncIterable): ApiStream { + for await (const chunk of stream) { + const delta = chunk.choices[0]?.delta + 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, + } + } + } + } } export async function getOpenAiModels(baseUrl?: string, apiKey?: string) {