2
0

Add API endpoint to update the typebot in ongoing chat session

This commit is contained in:
Baptiste Arnaud
2023-08-04 14:53:49 +02:00
parent f7de11611f
commit 53e4bc2b75
5 changed files with 175 additions and 30 deletions

View File

@ -15,11 +15,6 @@
"operationId": "sendMessage", "operationId": "sendMessage",
"summary": "Send a message", "summary": "Send a message",
"description": "To initiate a chat, do not provide a `sessionId` nor a `message`.\n\nContinue the conversation by providing the `sessionId` and the `message` that should answer the previous question.\n\nSet the `isPreview` option to `true` to chat with the non-published version of the typebot.", "description": "To initiate a chat, do not provide a `sessionId` nor a `message`.\n\nContinue the conversation by providing the `sessionId` and the `message` that should answer the previous question.\n\nSet the `isPreview` option to `true` to chat with the non-published version of the typebot.",
"security": [
{
"Authorization": []
}
],
"requestBody": { "requestBody": {
"required": true, "required": true,
"content": { "content": {
@ -35,27 +30,6 @@
"type": "string", "type": "string",
"description": "Session ID that you get from the initial chat request to a bot. If not provided, it will create a new session." "description": "Session ID that you get from the initial chat request to a bot. If not provided, it will create a new session."
}, },
"clientLogs": {
"type": "array",
"items": {
"type": "object",
"properties": {
"status": {
"type": "string"
},
"description": {
"type": "string"
},
"details": {}
},
"required": [
"status",
"description"
],
"additionalProperties": false
},
"description": "Logs while executing client side actions"
},
"startParams": { "startParams": {
"type": "object", "type": "object",
"properties": { "properties": {
@ -3566,13 +3540,35 @@
"description": "[More info about prefilled variables.](https://docs.typebot.io/editor/variables#prefilled-variables)" "description": "[More info about prefilled variables.](https://docs.typebot.io/editor/variables#prefilled-variables)"
}, },
"isStreamEnabled": { "isStreamEnabled": {
"type": "boolean" "type": "boolean",
"description": "Set this to `true` if you intend to stream OpenAI completions on a client."
} }
}, },
"required": [ "required": [
"typebot" "typebot"
], ],
"additionalProperties": false "additionalProperties": false
},
"clientLogs": {
"type": "array",
"items": {
"type": "object",
"properties": {
"status": {
"type": "string"
},
"description": {
"type": "string"
},
"details": {}
},
"required": [
"status",
"description"
],
"additionalProperties": false
},
"description": "Logs while executing client side actions"
} }
}, },
"additionalProperties": false "additionalProperties": false
@ -5811,6 +5807,55 @@
} }
} }
} }
},
"/session/{sessionId}/updateTypebot": {
"post": {
"operationId": "updateTypebotInSession",
"summary": "Update typebot in session",
"description": "Update chat session with latest typebot modifications. This is useful when you want to update the typebot in an ongoing session after making changes to it.",
"security": [
{
"Authorization": []
}
],
"parameters": [
{
"name": "sessionId",
"in": "path",
"required": true,
"schema": {
"type": "string"
}
}
],
"responses": {
"200": {
"description": "Successful response",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"message": {
"type": "string",
"enum": [
"success"
]
}
},
"required": [
"message"
],
"additionalProperties": false
}
}
}
},
"default": {
"$ref": "#/components/responses/error"
}
}
}
} }
}, },
"components": { "components": {

View File

@ -40,12 +40,11 @@ export const sendMessage = publicProcedure
summary: 'Send a message', summary: 'Send a message',
description: description:
'To initiate a chat, do not provide a `sessionId` nor a `message`.\n\nContinue the conversation by providing the `sessionId` and the `message` that should answer the previous question.\n\nSet the `isPreview` option to `true` to chat with the non-published version of the typebot.', 'To initiate a chat, do not provide a `sessionId` nor a `message`.\n\nContinue the conversation by providing the `sessionId` and the `message` that should answer the previous question.\n\nSet the `isPreview` option to `true` to chat with the non-published version of the typebot.',
protect: true,
}, },
}) })
.input(sendMessageInputSchema) .input(sendMessageInputSchema)
.output(chatReplySchema) .output(chatReplySchema)
.query( .mutation(
async ({ async ({
input: { sessionId, message, startParams, clientLogs }, input: { sessionId, message, startParams, clientLogs },
ctx: { user }, ctx: { user },

View File

@ -0,0 +1,99 @@
import { publicProcedure } from '@/helpers/server/trpc'
import { TRPCError } from '@trpc/server'
import { z } from 'zod'
import { getSession } from '../queries/getSession'
import prisma from '@/lib/prisma'
import { PublicTypebot, SessionState, Typebot } from '@typebot.io/schemas'
export const updateTypebotInSession = publicProcedure
.meta({
openapi: {
method: 'POST',
path: '/sessions/{sessionId}/updateTypebot',
summary: 'Update typebot in session',
description:
'Update chat session with latest typebot modifications. This is useful when you want to update the typebot in an ongoing session after making changes to it.',
protect: true,
},
})
.input(
z.object({
sessionId: z.string(),
})
)
.output(z.object({ message: z.literal('success') }))
.mutation(async ({ input: { sessionId }, ctx: { user } }) => {
if (!user)
throw new TRPCError({ code: 'UNAUTHORIZED', message: 'Unauthorized' })
const session = await getSession(sessionId)
if (!session)
throw new TRPCError({ code: 'NOT_FOUND', message: 'Session not found' })
const publicTypebot = (await prisma.publicTypebot.findFirst({
where: {
typebot: {
id: session.state.typebot.id,
OR: [
{
workspace: {
members: {
some: { userId: user.id, role: { in: ['ADMIN', 'MEMBER'] } },
},
},
},
{
collaborators: {
some: { userId: user.id, type: { in: ['WRITE'] } },
},
},
],
},
},
select: {
edges: true,
groups: true,
variables: true,
},
})) as Pick<PublicTypebot, 'edges' | 'variables' | 'groups'> | null
if (!publicTypebot)
throw new TRPCError({ code: 'UNAUTHORIZED', message: 'Unauthorized' })
const newSessionState = updateSessionState(session.state, publicTypebot)
await prisma.chatSession.updateMany({
where: { id: session.id },
data: { state: newSessionState },
})
return { message: 'success' }
})
const updateSessionState = (
currentState: SessionState,
newTypebot: Pick<PublicTypebot, 'edges' | 'variables' | 'groups'>
): SessionState => ({
...currentState,
typebot: {
...currentState.typebot,
edges: newTypebot.edges,
variables: updateVariablesInSession(
currentState.typebot.variables,
newTypebot.variables
),
groups: newTypebot.groups,
},
})
const updateVariablesInSession = (
currentVariables: SessionState['typebot']['variables'],
newVariables: Typebot['variables']
): SessionState['typebot']['variables'] => [
...currentVariables,
...newVariables.filter(
(newVariable) =>
!currentVariables.find(
(currentVariable) => currentVariable.id === newVariable.id
)
),
]

View File

@ -1,10 +1,12 @@
import { getUploadUrl } from '@/features/blocks/inputs/fileUpload/api/getUploadUrl' import { getUploadUrl } from '@/features/blocks/inputs/fileUpload/api/getUploadUrl'
import { sendMessage } from '@/features/chat/api/sendMessage' import { sendMessage } from '@/features/chat/api/sendMessage'
import { router } from '../../trpc' import { router } from '../../trpc'
import { updateTypebotInSession } from '@/features/chat/api/updateTypebotInSession'
export const appRouter = router({ export const appRouter = router({
sendMessage, sendMessage,
getUploadUrl, getUploadUrl,
updateTypebotInSession,
}) })
export type AppRouter = typeof appRouter export type AppRouter = typeof appRouter

View File

@ -150,7 +150,7 @@ const startParamsSchema = z.object({
.boolean() .boolean()
.optional() .optional()
.describe( .describe(
"If set to `true`, it will start a Preview session with the unpublished bot and it won't be saved in the Results tab. You need to be authenticated for this to work." "If set to `true`, it will start a Preview session with the unpublished bot and it won't be saved in the Results tab. You need to be authenticated with a bearer token for this to work."
), ),
resultId: z resultId: z
.string() .string()