⚡ (openai) Add Messages sequence type
To make it easy to just plug a sequence of user / assistant messages to Chat completion task Closes #387
This commit is contained in:
@ -1,7 +1,14 @@
|
||||
import { ExecuteIntegrationResponse } from '@/features/chat/types'
|
||||
import { saveErrorLog } from '@/features/logs/api'
|
||||
import { transformStringVariablesToList } from '@/features/variables/transformVariablesToList'
|
||||
import { parseVariables, updateVariables } from '@/features/variables/utils'
|
||||
import prisma from '@/lib/prisma'
|
||||
import { SessionState, VariableWithUnknowValue } from 'models'
|
||||
import {
|
||||
SessionState,
|
||||
Variable,
|
||||
VariableWithUnknowValue,
|
||||
VariableWithValue,
|
||||
} from 'models'
|
||||
import {
|
||||
ChatCompletionOpenAIOptions,
|
||||
OpenAICredentials,
|
||||
@ -20,6 +27,7 @@ export const createChatCompletionOpenAI = async (
|
||||
const {
|
||||
typebot: { variables },
|
||||
} = state
|
||||
let newSessionState = state
|
||||
if (!options.credentialsId) return { outgoingEdgeId }
|
||||
const credentials = await prisma.credentials.findUnique({
|
||||
where: {
|
||||
@ -34,52 +42,147 @@ export const createChatCompletionOpenAI = async (
|
||||
const configuration = new Configuration({
|
||||
apiKey,
|
||||
})
|
||||
const { variablesTransformedToList, messages } = parseMessages(variables)(
|
||||
options.messages
|
||||
)
|
||||
if (variablesTransformedToList.length > 0)
|
||||
newSessionState = await updateVariables(state)(variablesTransformedToList)
|
||||
const openai = new OpenAIApi(configuration)
|
||||
const {
|
||||
data: { choices, usage },
|
||||
} = await openai.createChatCompletion({
|
||||
model: options.model,
|
||||
messages: options.messages
|
||||
.map((message) => ({
|
||||
role: message.role,
|
||||
content: parseVariables(variables)(message.content),
|
||||
}))
|
||||
.filter(
|
||||
(message) => isNotEmpty(message.role) && isNotEmpty(message.content)
|
||||
) as ChatCompletionRequestMessage[],
|
||||
})
|
||||
const messageContent = choices[0].message?.content
|
||||
const totalTokens = usage?.total_tokens
|
||||
if (!messageContent) {
|
||||
return { outgoingEdgeId }
|
||||
}
|
||||
const newVariables = options.responseMapping.reduce<
|
||||
VariableWithUnknowValue[]
|
||||
>((newVariables, mapping) => {
|
||||
const existingVariable = variables.find(byId(mapping.variableId))
|
||||
if (!existingVariable) return newVariables
|
||||
if (mapping.valueToExtract === 'Message content') {
|
||||
newVariables.push({
|
||||
...existingVariable,
|
||||
value: messageContent,
|
||||
})
|
||||
try {
|
||||
const {
|
||||
data: { choices, usage },
|
||||
} = await openai.createChatCompletion({
|
||||
model: options.model,
|
||||
messages,
|
||||
})
|
||||
const messageContent = choices[0].message?.content
|
||||
const totalTokens = usage?.total_tokens
|
||||
if (!messageContent) {
|
||||
return { outgoingEdgeId, newSessionState }
|
||||
}
|
||||
if (mapping.valueToExtract === 'Total tokens' && isDefined(totalTokens)) {
|
||||
newVariables.push({
|
||||
...existingVariable,
|
||||
value: totalTokens,
|
||||
})
|
||||
const newVariables = options.responseMapping.reduce<
|
||||
VariableWithUnknowValue[]
|
||||
>((newVariables, mapping) => {
|
||||
const existingVariable = variables.find(byId(mapping.variableId))
|
||||
if (!existingVariable) return newVariables
|
||||
if (mapping.valueToExtract === 'Message content') {
|
||||
newVariables.push({
|
||||
...existingVariable,
|
||||
value: Array.isArray(existingVariable.value)
|
||||
? existingVariable.value.concat(messageContent)
|
||||
: messageContent,
|
||||
})
|
||||
}
|
||||
if (mapping.valueToExtract === 'Total tokens' && isDefined(totalTokens)) {
|
||||
newVariables.push({
|
||||
...existingVariable,
|
||||
value: totalTokens,
|
||||
})
|
||||
}
|
||||
return newVariables
|
||||
}, [])
|
||||
if (newVariables.length > 0) {
|
||||
newSessionState = await updateVariables(newSessionState)(newVariables)
|
||||
return {
|
||||
outgoingEdgeId,
|
||||
newSessionState,
|
||||
}
|
||||
}
|
||||
return newVariables
|
||||
}, [])
|
||||
if (newVariables.length > 0) {
|
||||
const newSessionState = await updateVariables(state)(newVariables)
|
||||
return {
|
||||
outgoingEdgeId,
|
||||
newSessionState,
|
||||
}
|
||||
}
|
||||
return {
|
||||
outgoingEdgeId,
|
||||
} catch (err) {
|
||||
const log = {
|
||||
status: 'error',
|
||||
description: 'OpenAI block returned error',
|
||||
details: JSON.stringify(err, null, 2).substring(0, 1000),
|
||||
}
|
||||
state.result &&
|
||||
(await saveErrorLog({
|
||||
resultId: state.result.id,
|
||||
message: log.description,
|
||||
details: log.details,
|
||||
}))
|
||||
return {
|
||||
outgoingEdgeId,
|
||||
logs: [log],
|
||||
newSessionState,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const parseMessages =
|
||||
(variables: Variable[]) =>
|
||||
(
|
||||
messages: ChatCompletionOpenAIOptions['messages']
|
||||
): {
|
||||
variablesTransformedToList: VariableWithValue[]
|
||||
messages: ChatCompletionRequestMessage[]
|
||||
} => {
|
||||
const variablesTransformedToList: VariableWithValue[] = []
|
||||
const parsedMessages = messages
|
||||
.flatMap((message) => {
|
||||
if (!message.role) return
|
||||
if (message.role === 'Messages sequence ✨') {
|
||||
if (
|
||||
!message.content?.assistantMessagesVariableId ||
|
||||
!message.content?.userMessagesVariableId
|
||||
)
|
||||
return
|
||||
variablesTransformedToList.push(
|
||||
...transformStringVariablesToList(variables)([
|
||||
message.content.assistantMessagesVariableId,
|
||||
message.content.userMessagesVariableId,
|
||||
])
|
||||
)
|
||||
const updatedVariables = variables.map((variable) => {
|
||||
const variableTransformedToList = variablesTransformedToList.find(
|
||||
byId(variable.id)
|
||||
)
|
||||
if (variableTransformedToList) return variableTransformedToList
|
||||
return variable
|
||||
})
|
||||
|
||||
const userMessages = (updatedVariables.find(
|
||||
(variable) =>
|
||||
variable.id === message.content?.userMessagesVariableId
|
||||
)?.value ?? []) as string[]
|
||||
|
||||
const assistantMessages = (updatedVariables.find(
|
||||
(variable) =>
|
||||
variable.id === message.content?.assistantMessagesVariableId
|
||||
)?.value ?? []) as string[]
|
||||
|
||||
if (userMessages.length > assistantMessages.length)
|
||||
return userMessages.flatMap((userMessage, index) => [
|
||||
{
|
||||
role: 'user',
|
||||
content: userMessage,
|
||||
},
|
||||
{ role: 'assistant', content: assistantMessages[index] },
|
||||
]) satisfies ChatCompletionRequestMessage[]
|
||||
else {
|
||||
return assistantMessages.flatMap((assistantMessage, index) => [
|
||||
{ role: 'assistant', content: assistantMessage },
|
||||
{
|
||||
role: 'user',
|
||||
content: userMessages[index],
|
||||
},
|
||||
]) satisfies ChatCompletionRequestMessage[]
|
||||
}
|
||||
}
|
||||
return {
|
||||
role: message.role,
|
||||
content: parseVariables(variables)(message.content),
|
||||
} satisfies ChatCompletionRequestMessage
|
||||
})
|
||||
.filter(
|
||||
(message) => isNotEmpty(message?.role) && isNotEmpty(message?.content)
|
||||
) as ChatCompletionRequestMessage[]
|
||||
|
||||
return {
|
||||
variablesTransformedToList,
|
||||
messages: parsedMessages,
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user