🐛 (openai) Fix assistant sequence was not correctly saved
Also add logs to debug open ai errors. Related to #393.
This commit is contained in:
@ -12,7 +12,7 @@ import {
|
|||||||
OpenAICredentials,
|
OpenAICredentials,
|
||||||
} from '@typebot.io/schemas/features/blocks/integrations/openai'
|
} from '@typebot.io/schemas/features/blocks/integrations/openai'
|
||||||
import { OpenAIApi, Configuration, ChatCompletionRequestMessage } from 'openai'
|
import { OpenAIApi, Configuration, ChatCompletionRequestMessage } from 'openai'
|
||||||
import { isDefined, byId, isNotEmpty } from '@typebot.io/lib'
|
import { isDefined, byId, isNotEmpty, isEmpty } from '@typebot.io/lib'
|
||||||
import { decrypt } from '@typebot.io/lib/api/encryption'
|
import { decrypt } from '@typebot.io/lib/api/encryption'
|
||||||
import { saveErrorLog } from '@/features/logs/saveErrorLog'
|
import { saveErrorLog } from '@/features/logs/saveErrorLog'
|
||||||
import { updateVariables } from '@/features/variables/updateVariables'
|
import { updateVariables } from '@/features/variables/updateVariables'
|
||||||
@ -25,17 +25,20 @@ export const createChatCompletionOpenAI = async (
|
|||||||
options,
|
options,
|
||||||
}: { outgoingEdgeId?: string; options: ChatCompletionOpenAIOptions }
|
}: { outgoingEdgeId?: string; options: ChatCompletionOpenAIOptions }
|
||||||
): Promise<ExecuteIntegrationResponse> => {
|
): Promise<ExecuteIntegrationResponse> => {
|
||||||
const {
|
|
||||||
typebot: { variables },
|
|
||||||
} = state
|
|
||||||
let newSessionState = state
|
let newSessionState = state
|
||||||
if (!options.credentialsId) return { outgoingEdgeId }
|
if (!options.credentialsId) {
|
||||||
|
console.error('OpenAI block has no credentials')
|
||||||
|
return { outgoingEdgeId }
|
||||||
|
}
|
||||||
const credentials = await prisma.credentials.findUnique({
|
const credentials = await prisma.credentials.findUnique({
|
||||||
where: {
|
where: {
|
||||||
id: options.credentialsId,
|
id: options.credentialsId,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
if (!credentials) return { outgoingEdgeId }
|
if (!credentials) {
|
||||||
|
console.error('Could not find credentials in database')
|
||||||
|
return { outgoingEdgeId }
|
||||||
|
}
|
||||||
const { apiKey } = decrypt(
|
const { apiKey } = decrypt(
|
||||||
credentials.data,
|
credentials.data,
|
||||||
credentials.iv
|
credentials.iv
|
||||||
@ -43,28 +46,30 @@ export const createChatCompletionOpenAI = async (
|
|||||||
const configuration = new Configuration({
|
const configuration = new Configuration({
|
||||||
apiKey,
|
apiKey,
|
||||||
})
|
})
|
||||||
const { variablesTransformedToList, messages } = parseMessages(variables)(
|
const { variablesTransformedToList, messages } = parseMessages(
|
||||||
options.messages
|
newSessionState.typebot.variables
|
||||||
)
|
)(options.messages)
|
||||||
if (variablesTransformedToList.length > 0)
|
if (variablesTransformedToList.length > 0)
|
||||||
newSessionState = await updateVariables(state)(variablesTransformedToList)
|
newSessionState = await updateVariables(state)(variablesTransformedToList)
|
||||||
const openai = new OpenAIApi(configuration)
|
|
||||||
try {
|
try {
|
||||||
const {
|
const openai = new OpenAIApi(configuration)
|
||||||
data: { choices, usage },
|
const response = await openai.createChatCompletion({
|
||||||
} = await openai.createChatCompletion({
|
|
||||||
model: options.model,
|
model: options.model,
|
||||||
messages,
|
messages,
|
||||||
})
|
})
|
||||||
const messageContent = choices[0].message?.content
|
const messageContent = response.data.choices.at(0)?.message?.content
|
||||||
const totalTokens = usage?.total_tokens
|
const totalTokens = response.data.usage?.total_tokens
|
||||||
if (!messageContent) {
|
if (isEmpty(messageContent)) {
|
||||||
|
console.error('OpenAI block returned empty message', response)
|
||||||
return { outgoingEdgeId, newSessionState }
|
return { outgoingEdgeId, newSessionState }
|
||||||
}
|
}
|
||||||
const newVariables = options.responseMapping.reduce<
|
const newVariables = options.responseMapping.reduce<
|
||||||
VariableWithUnknowValue[]
|
VariableWithUnknowValue[]
|
||||||
>((newVariables, mapping) => {
|
>((newVariables, mapping) => {
|
||||||
const existingVariable = variables.find(byId(mapping.variableId))
|
const existingVariable = newSessionState.typebot.variables.find(
|
||||||
|
byId(mapping.variableId)
|
||||||
|
)
|
||||||
if (!existingVariable) return newVariables
|
if (!existingVariable) return newVariables
|
||||||
if (mapping.valueToExtract === 'Message content') {
|
if (mapping.valueToExtract === 'Message content') {
|
||||||
newVariables.push({
|
newVariables.push({
|
||||||
@ -82,18 +87,14 @@ export const createChatCompletionOpenAI = async (
|
|||||||
}
|
}
|
||||||
return newVariables
|
return newVariables
|
||||||
}, [])
|
}, [])
|
||||||
if (newVariables.length > 0) {
|
if (newVariables.length > 0)
|
||||||
newSessionState = await updateVariables(newSessionState)(newVariables)
|
newSessionState = await updateVariables(newSessionState)(newVariables)
|
||||||
return {
|
|
||||||
outgoingEdgeId,
|
|
||||||
newSessionState,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return {
|
return {
|
||||||
outgoingEdgeId,
|
outgoingEdgeId,
|
||||||
newSessionState,
|
newSessionState,
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
console.error(err)
|
||||||
const log = {
|
const log = {
|
||||||
status: 'error',
|
status: 'error',
|
||||||
description: 'OpenAI block returned error',
|
description: 'OpenAI block returned error',
|
||||||
|
@ -136,6 +136,8 @@ const parseBubbleBlock =
|
|||||||
(variables: SessionState['typebot']['variables']) =>
|
(variables: SessionState['typebot']['variables']) =>
|
||||||
(block: BubbleBlock): ChatReply['messages'][0] => {
|
(block: BubbleBlock): ChatReply['messages'][0] => {
|
||||||
switch (block.type) {
|
switch (block.type) {
|
||||||
|
case BubbleBlockType.TEXT:
|
||||||
|
return deepParseVariables(variables, { takeLatestIfList: true })(block)
|
||||||
case BubbleBlockType.EMBED: {
|
case BubbleBlockType.EMBED: {
|
||||||
const message = deepParseVariables(variables)(block)
|
const message = deepParseVariables(variables)(block)
|
||||||
return {
|
return {
|
||||||
|
@ -1,27 +1,38 @@
|
|||||||
import { Variable } from '@typebot.io/schemas'
|
import { Variable } from '@typebot.io/schemas'
|
||||||
import { parseVariables } from './parseVariables'
|
import {
|
||||||
|
defaultParseVariablesOptions,
|
||||||
|
parseVariables,
|
||||||
|
ParseVariablesOptions,
|
||||||
|
} from './parseVariables'
|
||||||
|
|
||||||
export const deepParseVariables =
|
export const deepParseVariables =
|
||||||
(variables: Variable[]) =>
|
(
|
||||||
|
variables: Variable[],
|
||||||
|
options: ParseVariablesOptions = defaultParseVariablesOptions
|
||||||
|
) =>
|
||||||
<T extends Record<string, unknown>>(object: T): T =>
|
<T extends Record<string, unknown>>(object: T): T =>
|
||||||
Object.keys(object).reduce<T>((newObj, key) => {
|
Object.keys(object).reduce<T>((newObj, key) => {
|
||||||
const currentValue = object[key]
|
const currentValue = object[key]
|
||||||
|
|
||||||
if (typeof currentValue === 'string')
|
if (typeof currentValue === 'string')
|
||||||
return { ...newObj, [key]: parseVariables(variables)(currentValue) }
|
return {
|
||||||
|
...newObj,
|
||||||
|
[key]: parseVariables(variables, options)(currentValue),
|
||||||
|
}
|
||||||
|
|
||||||
if (currentValue instanceof Object && currentValue.constructor === Object)
|
if (currentValue instanceof Object && currentValue.constructor === Object)
|
||||||
return {
|
return {
|
||||||
...newObj,
|
...newObj,
|
||||||
[key]: deepParseVariables(variables)(
|
[key]: deepParseVariables(
|
||||||
currentValue as Record<string, unknown>
|
variables,
|
||||||
),
|
options
|
||||||
|
)(currentValue as Record<string, unknown>),
|
||||||
}
|
}
|
||||||
|
|
||||||
if (currentValue instanceof Array)
|
if (currentValue instanceof Array)
|
||||||
return {
|
return {
|
||||||
...newObj,
|
...newObj,
|
||||||
[key]: currentValue.map(deepParseVariables(variables)),
|
[key]: currentValue.map(deepParseVariables(variables, options)),
|
||||||
}
|
}
|
||||||
|
|
||||||
return { ...newObj, [key]: currentValue }
|
return { ...newObj, [key]: currentValue }
|
||||||
|
@ -2,13 +2,22 @@ import { isDefined } from '@typebot.io/lib'
|
|||||||
import { Variable, VariableWithValue } from '@typebot.io/schemas'
|
import { Variable, VariableWithValue } from '@typebot.io/schemas'
|
||||||
import { safeStringify } from './safeStringify'
|
import { safeStringify } from './safeStringify'
|
||||||
|
|
||||||
|
export type ParseVariablesOptions = {
|
||||||
|
fieldToParse?: 'value' | 'id'
|
||||||
|
escapeForJson?: boolean
|
||||||
|
takeLatestIfList?: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
export const defaultParseVariablesOptions: ParseVariablesOptions = {
|
||||||
|
fieldToParse: 'value',
|
||||||
|
escapeForJson: false,
|
||||||
|
takeLatestIfList: false,
|
||||||
|
}
|
||||||
|
|
||||||
export const parseVariables =
|
export const parseVariables =
|
||||||
(
|
(
|
||||||
variables: Variable[],
|
variables: Variable[],
|
||||||
options: { fieldToParse?: 'value' | 'id'; escapeForJson?: boolean } = {
|
options: ParseVariablesOptions = defaultParseVariablesOptions
|
||||||
fieldToParse: 'value',
|
|
||||||
escapeForJson: false,
|
|
||||||
}
|
|
||||||
) =>
|
) =>
|
||||||
(text: string | undefined): string => {
|
(text: string | undefined): string => {
|
||||||
if (!text || text === '') return ''
|
if (!text || text === '') return ''
|
||||||
@ -27,7 +36,11 @@ export const parseVariables =
|
|||||||
return jsonParse(
|
return jsonParse(
|
||||||
typeof value !== 'string' ? JSON.stringify(value) : value
|
typeof value !== 'string' ? JSON.stringify(value) : value
|
||||||
)
|
)
|
||||||
const parsedValue = safeStringify(value)
|
const parsedValue = safeStringify(
|
||||||
|
options.takeLatestIfList && Array.isArray(value)
|
||||||
|
? value[value.length - 1]
|
||||||
|
: value
|
||||||
|
)
|
||||||
if (!parsedValue) return ''
|
if (!parsedValue) return ''
|
||||||
return parsedValue
|
return parsedValue
|
||||||
})
|
})
|
||||||
|
Reference in New Issue
Block a user