From 93ffdabd71192de82225a20a23882637ce3ede43 Mon Sep 17 00:00:00 2001 From: centdix Date: Fri, 29 Aug 2025 14:59:13 +0000 Subject: [PATCH] rollback to old parsing --- .../copilot/chat/AIChatManager.svelte.ts | 1 - frontend/src/lib/components/copilot/lib.ts | 130 ++++++++++-------- 2 files changed, 76 insertions(+), 55 deletions(-) diff --git a/frontend/src/lib/components/copilot/chat/AIChatManager.svelte.ts b/frontend/src/lib/components/copilot/chat/AIChatManager.svelte.ts index 0fa96862de..dcd91e1d1f 100644 --- a/frontend/src/lib/components/copilot/chat/AIChatManager.svelte.ts +++ b/frontend/src/lib/components/copilot/chat/AIChatManager.svelte.ts @@ -437,7 +437,6 @@ class AIChatManager { } return addedMessages } catch (err) { - console.error('Error in chatRequest', err) callbacks.onMessageEnd() if (!abortController.signal.aborted) { throw err diff --git a/frontend/src/lib/components/copilot/lib.ts b/frontend/src/lib/components/copilot/lib.ts index fa83f8a3bd..5cacb53894 100644 --- a/frontend/src/lib/components/copilot/lib.ts +++ b/frontend/src/lib/components/copilot/lib.ts @@ -9,11 +9,12 @@ import { import { buildClientSchema, printSchema } from 'graphql' import OpenAI from 'openai' import type { + ChatCompletionChunk, ChatCompletionCreateParams, ChatCompletionCreateParamsNonStreaming, ChatCompletionCreateParamsStreaming, - ChatCompletionMessageParam, - ChatCompletionMessageToolCall + ChatCompletionMessageFunctionToolCall, + ChatCompletionMessageParam } from 'openai/resources/index.mjs' import Anthropic from '@anthropic-ai/sdk' import { get, type Writable } from 'svelte/store' @@ -21,8 +22,8 @@ import { OpenAPI, ResourceService, type Script } from '../../gen' import { EDIT_CONFIG, FIX_CONFIG, GEN_CONFIG } from './prompts' import { formatResourceTypes } from './utils' import { z } from 'zod' -import type { ChatCompletionStream } from 'openai/lib/ChatCompletionStream.mjs' import { processToolCall, type Tool, type ToolCallbacks } from './chat/shared' +import type { Stream } from 'openai/core/streaming.mjs' export const SUPPORTED_LANGUAGES = new Set(Object.keys(GEN_CONFIG.prompts)) @@ -644,21 +645,21 @@ export async function getCompletion( messages: ChatCompletionMessageParam[], abortController: AbortController, tools?: OpenAI.Chat.Completions.ChatCompletionTool[] -): Promise { +): Promise> { const { provider, config } = getProviderAndCompletionConfig({ messages, stream: true, tools }) const openaiClient = workspaceAIClients.getOpenaiClient() - const stream = openaiClient.chat.completions.stream(config, { + const completion = openaiClient.chat.completions.create(config, { signal: abortController.signal, headers: { 'X-Provider': provider } }) - return stream + return completion } export async function parseOpenAICompletion( - stream: ChatCompletionStream, + completion: Stream, callbacks: ToolCallbacks & { onNewToken: (token: string) => void onMessageEnd: () => void @@ -668,61 +669,82 @@ export async function parseOpenAICompletion( tools: Tool[], helpers: any ): Promise { - let toolCallsToProcess: ChatCompletionMessageToolCall[] = [] - let error = null + const finalToolCalls: Record = {} - // Handle content streaming - stream.on('content.delta', ({ delta }: { delta: string }) => { - callbacks.onNewToken(delta) - }) - - // Handle complete messages - stream.on('message', (message: ChatCompletionMessageParam) => { - console.log('message', message) - if (message.role === 'assistant') { - // Add the complete assistant message (whether it has content, tool calls, or both) - messages.push(message) - addedMessages.push(message) - - if (message.content) { - callbacks.onMessageEnd() + let answer = '' + for await (const chunk of completion) { + if (!('choices' in chunk && chunk.choices.length > 0 && 'delta' in chunk.choices[0])) { + continue + } + const c = chunk as ChatCompletionChunk + const delta = c.choices[0].delta.content + if (delta) { + answer += delta + callbacks.onNewToken(delta) + } + const toolCalls = c.choices[0].delta.tool_calls || [] + if (toolCalls.length > 0 && answer) { + // if tool calls are present but we have some textual content already, we need to display it to the user first + callbacks.onMessageEnd() + answer = '' + } + for (const toolCall of toolCalls) { + const { index } = toolCall + let finalToolCall = finalToolCalls[index] + if (!finalToolCall) { + finalToolCalls[index] = toolCall + } else { + if (toolCall.function?.arguments) { + if (!finalToolCall.function) { + finalToolCall.function = toolCall.function + } else { + finalToolCall.function.arguments = + (finalToolCall.function.arguments ?? '') + toolCall.function.arguments + } + } } - - if (message.tool_calls) { - toolCallsToProcess = message.tool_calls - // Preprocess tools - for (const toolCall of message.tool_calls) { - console.log('toolCall', toolCall) - if (toolCall.type !== 'function') continue - const tool = tools.find((t) => t.def.function.name === toolCall.function.name) - if (tool?.preAction) { - tool.preAction({ toolCallbacks: callbacks, toolId: toolCall.id }) + finalToolCall = finalToolCalls[index] + if (finalToolCall?.function) { + const { + function: { name: funcName }, + id: toolCallId + } = finalToolCall + if (funcName && toolCallId) { + const tool = tools.find((t) => t.def.function.name === funcName) + if (tool && tool.preAction) { + tool.preAction({ toolCallbacks: callbacks, toolId: toolCallId }) } } } } - }) + } - // Handle errors - stream.on('error', (error: any) => { - console.error('OpenAI stream error:', error) - error = error - }) - - // Wait for completion - await stream.done() - - if (error) { - throw error + if (answer) { + const toAdd = { role: 'assistant' as const, content: answer } + addedMessages.push(toAdd) + messages.push(toAdd) } callbacks.onMessageEnd() - // Process tool calls if any - if (toolCallsToProcess.length > 0) { - // Process each tool call - for (const toolCall of toolCallsToProcess) { - if (toolCall.type !== 'function') continue + const toolCalls = Object.values(finalToolCalls).filter( + (toolCall) => toolCall.id !== undefined && toolCall.function?.arguments !== undefined + ) as ChatCompletionMessageFunctionToolCall[] + + if (toolCalls.length > 0) { + const toAdd = { + role: 'assistant' as const, + tool_calls: toolCalls.map((t) => ({ + ...t, + function: { + ...t.function, + arguments: t.function.arguments || '{}' + } + })) + } + messages.push(toAdd) + addedMessages.push(toAdd) + for (const toolCall of toolCalls) { const messageToAdd = await processToolCall({ tools, toolCall, @@ -732,10 +754,10 @@ export async function parseOpenAICompletion( messages.push(messageToAdd) addedMessages.push(messageToAdd) } - return true // Continue the conversation loop + } else { + return false } - - return false // End the conversation + return true } export function getResponseFromEvent(part: OpenAI.Chat.Completions.ChatCompletionChunk): string {