⚡ Add API endpoint to update the typebot in ongoing chat session
This commit is contained in:
@ -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": {
|
||||||
|
@ -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 },
|
||||||
|
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 { 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
|
||||||
|
@ -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()
|
||||||
|
Reference in New Issue
Block a user