2
0
Files
bot/packages/forge/blocks/openai/shared/runChatCompletionStream.ts

108 lines
3.2 KiB
TypeScript
Raw Normal View History

import { VariableStore } from '@typebot.io/forge/types'
import { ChatCompletionOptions } from './parseChatCompletionOptions'
import { executeFunction } from '@typebot.io/variables/executeFunction'
import { OpenAIStream, ToolCallPayload } from 'ai'
import OpenAI, { ClientOptions } from 'openai'
import { ChatCompletionTool } from 'openai/resources'
import { parseChatCompletionMessages } from '../helpers/parseChatCompletionMessages'
import { parseToolParameters } from '../helpers/parseToolParameters'
type Props = {
credentials: { apiKey?: string }
options: ChatCompletionOptions
variables: VariableStore
config: { baseUrl: string; defaultModel?: string }
}
export const runChatCompletionStream = async ({
credentials: { apiKey },
options,
variables,
config: openAIConfig,
}: Props) => {
const model = options.model?.trim() ?? openAIConfig.defaultModel
if (!model) return
const config = {
apiKey,
baseURL: openAIConfig.baseUrl ?? options.baseUrl,
defaultHeaders: {
'api-key': apiKey,
},
defaultQuery: options.apiVersion
? {
'api-version': options.apiVersion,
}
: undefined,
} satisfies ClientOptions
const openai = new OpenAI(config)
const tools = options.tools
?.filter((t) => t.name && t.parameters)
.map((t) => ({
type: 'function',
function: {
name: t.name as string,
description: t.description,
parameters: parseToolParameters(t.parameters!),
},
})) satisfies ChatCompletionTool[] | undefined
const messages = await parseChatCompletionMessages({ options, variables })
const response = await openai.chat.completions.create({
model,
temperature: options.temperature ? Number(options.temperature) : undefined,
stream: true,
messages,
tools: (tools?.length ?? 0) > 0 ? tools : undefined,
})
return OpenAIStream(response, {
experimental_onToolCall: async (
call: ToolCallPayload,
appendToolCallMessage
) => {
for (const toolCall of call.tools) {
const name = toolCall.func?.name
if (!name) continue
const toolDefinition = options.tools?.find((t) => t.name === name)
if (!toolDefinition?.code || !toolDefinition.parameters) {
messages.push({
tool_call_id: toolCall.id,
role: 'tool',
content: 'Function not found',
})
continue
}
const { output, newVariables } = await executeFunction({
variables: variables.list(),
args:
typeof toolCall.func.arguments === 'string'
? JSON.parse(toolCall.func.arguments)
: toolCall.func.arguments,
body: toolDefinition.code,
})
newVariables?.forEach((v) => variables.set(v.id, v.value))
const newMessages = appendToolCallMessage({
tool_call_id: toolCall.id,
function_name: toolCall.func.name,
tool_call_result: output,
})
return openai.chat.completions.create({
messages: [
...messages,
...newMessages,
] as OpenAI.Chat.Completions.ChatCompletionMessageParam[],
model,
stream: true,
tools,
})
}
},
})
}