diff --git a/frontend/src/lib/components/copilot/chat/AIChatManager.svelte.ts b/frontend/src/lib/components/copilot/chat/AIChatManager.svelte.ts index 149f6930d8..5645792971 100644 --- a/frontend/src/lib/components/copilot/chat/AIChatManager.svelte.ts +++ b/frontend/src/lib/components/copilot/chat/AIChatManager.svelte.ts @@ -408,24 +408,42 @@ class AIChatManager { } const model = getCurrentModel() - const completionFn = - model.provider === 'anthropic' - ? getAnthropicCompletion - : model.provider === 'openai' || model.provider === 'azure_openai' - ? getOpenAIResponsesCompletion - : getCompletion - const parseFn = - model.provider === 'anthropic' - ? parseAnthropicCompletion - : model.provider === 'openai' || model.provider === 'azure_openai' - ? parseOpenAIResponsesCompletion - : parseOpenAICompletion + const isOpenAI = model.provider === 'openai' || model.provider === 'azure_openai' + const isAnthropic = model.provider === 'anthropic' - const completion = await completionFn( - [systemMessage, ...messages, ...(pendingUserMessage ? [pendingUserMessage] : [])], - abortController, - tools.map((t) => t.def) - ) + let completion: any + let parseFn: any + + const messageParams = [ + systemMessage, + ...messages, + ...(pendingUserMessage ? [pendingUserMessage] : []) + ] + const toolDefs = tools.map((t) => t.def) + + // For OpenAI/Azure, try Responses API first, fallback to Completions API + if (isOpenAI) { + try { + completion = await getOpenAIResponsesCompletion( + messageParams, + abortController, + toolDefs + ) + parseFn = parseOpenAIResponsesCompletion + } catch (err) { + console.warn('OpenAI Responses API failed, falling back to Completions API:', err) + completion = await getCompletion(messageParams, abortController, toolDefs, { + forceCompletions: true + }) + parseFn = parseOpenAICompletion + } + } else if (isAnthropic) { + completion = await getAnthropicCompletion(messageParams, abortController, toolDefs) + parseFn = parseAnthropicCompletion + } else { + completion = await getCompletion(messageParams, abortController, toolDefs) + parseFn = parseOpenAICompletion + } if (completion) { const continueCompletion = await parseFn( diff --git a/frontend/src/lib/components/copilot/lib.ts b/frontend/src/lib/components/copilot/lib.ts index 3f2686765b..1fa2a0d07c 100644 --- a/frontend/src/lib/components/copilot/lib.ts +++ b/frontend/src/lib/components/copilot/lib.ts @@ -675,7 +675,16 @@ export async function getNonStreamingCompletion( // Use Responses API for OpenAI and Azure OpenAI if (provider === 'openai' || provider === 'azure_openai') { - return getNonStreamingOpenAIResponsesCompletion(messages, abortController, testOptions) + try { + const response = await getNonStreamingOpenAIResponsesCompletion( + messages, + abortController, + testOptions + ) + return response + } catch (error) { + console.error('Error using Responses API:', error) + } } const fetchOptions: { @@ -793,13 +802,21 @@ export async function getFimCompletion( export async function getCompletion( messages: ChatCompletionMessageParam[], abortController: AbortController, - tools?: OpenAI.Chat.Completions.ChatCompletionTool[] + tools?: OpenAI.Chat.Completions.ChatCompletionTool[], + options?: { + forceCompletions?: boolean + } ): Promise> { const { provider, config } = getProviderAndCompletionConfig({ messages, stream: true, tools }) // Use Responses API for OpenAI and Azure OpenAI - if (provider === 'openai' || provider === 'azure_openai') { - return getOpenAIResponsesCompletionStream(messages, abortController, tools) as any + if ((provider === 'openai' || provider === 'azure_openai') && !options?.forceCompletions) { + try { + const stream = getOpenAIResponsesCompletionStream(messages, abortController, tools) as any + return stream + } catch (error) { + console.error('Error using Responses API:', error) + } } // Use Completions API for other providers