108 lines
3.2 KiB
TypeScript
108 lines
3.2 KiB
TypeScript
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,
|
|
})
|
|
}
|
|
},
|
|
})
|
|
}
|