⚡ Add API endpoint to update the typebot in ongoing chat session
This commit is contained in:
@ -15,11 +15,6 @@
|
||||
"operationId": "sendMessage",
|
||||
"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.",
|
||||
"security": [
|
||||
{
|
||||
"Authorization": []
|
||||
}
|
||||
],
|
||||
"requestBody": {
|
||||
"required": true,
|
||||
"content": {
|
||||
@ -35,27 +30,6 @@
|
||||
"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."
|
||||
},
|
||||
"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": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@ -3566,13 +3540,35 @@
|
||||
"description": "[More info about prefilled variables.](https://docs.typebot.io/editor/variables#prefilled-variables)"
|
||||
},
|
||||
"isStreamEnabled": {
|
||||
"type": "boolean"
|
||||
"type": "boolean",
|
||||
"description": "Set this to `true` if you intend to stream OpenAI completions on a client."
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"typebot"
|
||||
],
|
||||
"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
|
||||
@ -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": {
|
||||
|
@ -40,12 +40,11 @@ export const sendMessage = publicProcedure
|
||||
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.',
|
||||
protect: true,
|
||||
},
|
||||
})
|
||||
.input(sendMessageInputSchema)
|
||||
.output(chatReplySchema)
|
||||
.query(
|
||||
.mutation(
|
||||
async ({
|
||||
input: { sessionId, message, startParams, clientLogs },
|
||||
ctx: { user },
|
||||
|
99
apps/viewer/src/features/chat/api/updateTypebotInSession.ts
Normal file
99
apps/viewer/src/features/chat/api/updateTypebotInSession.ts
Normal 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
|
||||
)
|
||||
),
|
||||
]
|
@ -1,10 +1,12 @@
|
||||
import { getUploadUrl } from '@/features/blocks/inputs/fileUpload/api/getUploadUrl'
|
||||
import { sendMessage } from '@/features/chat/api/sendMessage'
|
||||
import { router } from '../../trpc'
|
||||
import { updateTypebotInSession } from '@/features/chat/api/updateTypebotInSession'
|
||||
|
||||
export const appRouter = router({
|
||||
sendMessage,
|
||||
getUploadUrl,
|
||||
updateTypebotInSession,
|
||||
})
|
||||
|
||||
export type AppRouter = typeof appRouter
|
||||
|
@ -150,7 +150,7 @@ const startParamsSchema = z.object({
|
||||
.boolean()
|
||||
.optional()
|
||||
.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
|
||||
.string()
|
||||
|
Reference in New Issue
Block a user