2
0
Files
bot/packages/schemas/features/chat/schema.ts

356 lines
9.4 KiB
TypeScript
Raw Normal View History

import { z } from '../../zod'
2022-11-29 10:02:40 +01:00
import {
buttonsInputSchemas,
dateInputSchema,
emailInputSchema,
fileInputBlockSchemas,
numberInputSchema,
paymentInputRuntimeOptionsSchema,
paymentInputSchema,
phoneNumberInputBlockSchema,
pictureChoiceBlockSchemas,
ratingInputBlockSchema,
textInputSchema,
urlInputSchema,
} from '../blocks'
import { logSchema } from '../result'
import { settingsSchema, themeSchema } from '../typebot'
import {
textBubbleContentSchema,
imageBubbleContentSchema,
videoBubbleContentSchema,
audioBubbleContentSchema,
embedBubbleContentSchema,
} from '../blocks/bubbles'
import { sessionStateSchema } from './sessionState'
import { dynamicThemeSchema } from './shared'
import { preprocessTypebot } from '../typebot/helpers/preprocessTypebot'
import { typebotV5Schema, typebotV6Schema } from '../typebot/typebot'
import { BubbleBlockType } from '../blocks/bubbles/constants'
import { clientSideActionSchema } from './clientSideAction'
2022-11-29 10:02:40 +01:00
const chatSessionSchema = z.object({
id: z.string(),
createdAt: z.date(),
updatedAt: z.date(),
state: sessionStateSchema,
})
export type ChatSession = z.infer<typeof chatSessionSchema>
2022-11-29 10:02:40 +01:00
const textMessageSchema = z
.object({
type: z.literal(BubbleBlockType.TEXT),
content: textBubbleContentSchema,
})
.openapi({
title: 'Text',
ref: 'textMessage',
})
const imageMessageSchema = z
.object({
type: z.enum([BubbleBlockType.IMAGE]),
content: imageBubbleContentSchema,
})
.openapi({
title: 'Image',
ref: 'imageMessage',
})
2022-11-29 10:02:40 +01:00
const videoMessageSchema = z
.object({
type: z.enum([BubbleBlockType.VIDEO]),
content: videoBubbleContentSchema,
})
.openapi({
title: 'Video',
ref: 'videoMessage',
})
const audioMessageSchema = z
.object({
type: z.enum([BubbleBlockType.AUDIO]),
content: audioBubbleContentSchema,
})
.openapi({
title: 'Audio',
ref: 'audioMessage',
})
const embedMessageSchema = z
.object({
type: z.enum([BubbleBlockType.EMBED]),
content: embedBubbleContentSchema
.omit({
height: true,
})
.merge(z.object({ height: z.number().optional() })),
})
.openapi({
title: 'Embed',
ref: 'embedMessage',
})
const displayEmbedBubbleSchema = z.object({
maxBubbleWidth: z.number().optional(),
waitForEventFunction: z
.object({
args: z.record(z.string(), z.unknown()),
content: z.string(),
})
.optional(),
initFunction: z.object({
args: z.record(z.string(), z.unknown()),
content: z.string(),
}),
})
const customEmbedSchema = z
.object({
type: z.literal('custom-embed'),
content: displayEmbedBubbleSchema,
})
.openapi({
title: 'Custom embed',
ref: 'customEmbedMessage',
})
export type CustomEmbedBubble = z.infer<typeof customEmbedSchema>
export const chatMessageSchema = z
.object({ id: z.string() })
.and(
z.discriminatedUnion('type', [
textMessageSchema,
imageMessageSchema,
videoMessageSchema,
audioMessageSchema,
embedMessageSchema,
customEmbedSchema,
])
)
export type ChatMessage = z.infer<typeof chatMessageSchema>
2022-11-29 10:02:40 +01:00
const startTypebotPick = {
version: true,
id: true,
groups: true,
events: true,
edges: true,
variables: true,
settings: true,
theme: true,
} as const
export const startTypebotSchema = z.preprocess(
preprocessTypebot,
z.discriminatedUnion('version', [
typebotV5Schema._def.schema.pick(startTypebotPick).openapi({
title: 'Typebot V5',
ref: 'typebotV5',
}),
typebotV6Schema.pick(startTypebotPick).openapi({
title: 'Typebot V6',
ref: 'typebotV6',
}),
])
)
export type StartTypebot = z.infer<typeof startTypebotSchema>
export const chatLogSchema = logSchema
.pick({
status: true,
description: true,
})
.merge(z.object({ details: z.unknown().optional() }))
export type ChatLog = z.infer<typeof chatLogSchema>
export const startChatInputSchema = z.object({
publicId: z
.string()
.describe(
"[Where to find my bot's public ID?](../how-to#how-to-find-my-publicid)"
),
message: z
.string()
.optional()
.describe(
"Only provide it if your flow starts with an input block and you'd like to directly provide an answer to it."
),
isStreamEnabled: z
.boolean()
.optional()
.default(false)
.describe(
'If enabled, you will be required to stream OpenAI completions on a client and send the generated response back to the API.'
),
resultId: z
.string()
.optional()
.describe("Provide it if you'd like to overwrite an existing result."),
isOnlyRegistering: z
.boolean()
.optional()
.default(false)
.describe(
'If set to `true`, it will only register the session and not start the bot. This is used for 3rd party chat platforms as it can require a session to be registered before sending the first message.'
),
prefilledVariables: z
.record(z.unknown())
.optional()
.describe(
'[More info about prefilled variables.](../../editor/variables#prefilled-variables)'
)
.openapi({
example: {
'First name': 'John',
Email: 'john@gmail.com',
},
}),
})
export type StartChatInput = z.infer<typeof startChatInputSchema>
export const startFromSchema = z.discriminatedUnion('type', [
z.object({
type: z.literal('group'),
groupId: z.string(),
}),
z.object({
type: z.literal('event'),
eventId: z.string(),
}),
])
export type StartFrom = z.infer<typeof startFromSchema>
export const startPreviewChatInputSchema = z.object({
typebotId: z
.string()
.describe(
"[Where to find my bot's ID?](../how-to#how-to-find-my-typebotid)"
),
:zap: Introduce a new high-performing standalone chat API (#1200) Closes #1154 <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **New Features** - Added authentication functionality for user sessions in chat API. - Introduced chat-related API endpoints for starting, previewing, and continuing chat sessions, and streaming messages. - Implemented WhatsApp API webhook handling for receiving and processing messages. - Added environment variable `NEXT_PUBLIC_CHAT_API_URL` for chat API URL configuration. - **Bug Fixes** - Adjusted file upload logic to correctly determine the API host. - Fixed message streaming URL in chat integration with OpenAI. - **Documentation** - Updated guides for creating blocks, local installation, self-hosting, and deployment to use `bun` instead of `pnpm`. - **Refactor** - Refactored chat API functionalities to use modular architecture. - Simplified client log saving and session update functionalities by using external functions. - Transitioned package management and workflow commands to use `bun`. - **Chores** - Switched to `bun` for package management in Dockerfiles and GitHub workflows. - Added new Dockerfile for chat API service setup with Bun framework. - Updated `.prettierignore` and documentation with new commands. - **Style** - No visible changes to end-users. - **Tests** - No visible changes to end-users. - **Revert** - No reverts in this release. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
2024-03-21 10:23:23 +01:00
isStreamEnabled: z.boolean().optional().default(false),
message: z.string().optional(),
isOnlyRegistering: z
.boolean()
.optional()
.describe(
'If set to `true`, it will only register the session and not start the bot. This is used for 3rd party chat platforms as it can require a session to be registered before sending the first message.'
:zap: Introduce a new high-performing standalone chat API (#1200) Closes #1154 <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **New Features** - Added authentication functionality for user sessions in chat API. - Introduced chat-related API endpoints for starting, previewing, and continuing chat sessions, and streaming messages. - Implemented WhatsApp API webhook handling for receiving and processing messages. - Added environment variable `NEXT_PUBLIC_CHAT_API_URL` for chat API URL configuration. - **Bug Fixes** - Adjusted file upload logic to correctly determine the API host. - Fixed message streaming URL in chat integration with OpenAI. - **Documentation** - Updated guides for creating blocks, local installation, self-hosting, and deployment to use `bun` instead of `pnpm`. - **Refactor** - Refactored chat API functionalities to use modular architecture. - Simplified client log saving and session update functionalities by using external functions. - Transitioned package management and workflow commands to use `bun`. - **Chores** - Switched to `bun` for package management in Dockerfiles and GitHub workflows. - Added new Dockerfile for chat API service setup with Bun framework. - Updated `.prettierignore` and documentation with new commands. - **Style** - No visible changes to end-users. - **Tests** - No visible changes to end-users. - **Revert** - No reverts in this release. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
2024-03-21 10:23:23 +01:00
)
.default(false),
typebot: startTypebotSchema
.optional()
.describe(
'If set, it will override the typebot that is used to start the chat.'
),
startFrom: startFromSchema.optional(),
prefilledVariables: z
.record(z.unknown())
.optional()
.describe(
'[More info about prefilled variables.](../../editor/variables#prefilled-variables)'
)
.openapi({
example: {
'First name': 'John',
Email: 'john@gmail.com',
},
}),
})
export type StartPreviewChatInput = z.infer<typeof startPreviewChatInputSchema>
export const runtimeOptionsSchema = paymentInputRuntimeOptionsSchema.optional()
export type RuntimeOptions = z.infer<typeof runtimeOptionsSchema>
const typebotInChatReplyPick = {
version: true,
id: true,
groups: true,
edges: true,
variables: true,
settings: true,
theme: true,
} as const
export const typebotInChatReply = z.preprocess(
preprocessTypebot,
z.discriminatedUnion('version', [
typebotV5Schema._def.schema.pick(typebotInChatReplyPick),
typebotV6Schema.pick(typebotInChatReplyPick),
])
)
const chatResponseBaseSchema = z.object({
lastMessageNewFormat: z
.string()
.optional()
.describe(
'The sent message is validated and formatted on the backend. For example, if for a date input you replied something like `tomorrow`, the backend will convert it to a date string. This field returns the formatted message.'
),
messages: z.array(chatMessageSchema),
input: z
.union([
z.discriminatedUnion('type', [
textInputSchema,
buttonsInputSchemas.v6,
emailInputSchema,
numberInputSchema,
urlInputSchema,
phoneNumberInputBlockSchema,
dateInputSchema,
paymentInputSchema,
ratingInputBlockSchema,
fileInputBlockSchemas.v6,
pictureChoiceBlockSchemas.v6,
]),
z.discriminatedUnion('type', [
buttonsInputSchemas.v5,
fileInputBlockSchemas.v5,
pictureChoiceBlockSchemas.v5,
]),
])
.and(
z.object({
prefilledValue: z.string().optional(),
runtimeOptions: runtimeOptionsSchema.optional(),
})
)
.optional(),
clientSideActions: z
.array(clientSideActionSchema)
.optional()
.describe('Actions to execute on the client side'),
logs: z
.array(chatLogSchema)
.optional()
.describe('Logs that were saved during the last execution'),
dynamicTheme: dynamicThemeSchema
.optional()
.describe(
'If the typebot contains dynamic avatars, dynamicTheme returns the new avatar URLs whenever their variables are updated.'
),
progress: z
.number()
.optional()
.describe(
'If progress bar is enabled, this field will return a number between 0 and 100 indicating the current progress based on the longest remaining path of the flow.'
),
})
export const startChatResponseSchema = z
.object({
sessionId: z
.string()
.describe('To save and use for /continueChat requests.'),
resultId: z.string().optional(),
typebot: z.object({
id: z.string(),
theme: themeSchema,
settings: settingsSchema,
}),
})
.merge(chatResponseBaseSchema)
export type StartChatResponse = z.infer<typeof startChatResponseSchema>
2022-11-29 10:02:40 +01:00
export const startPreviewChatResponseSchema = startChatResponseSchema.omit({
resultId: true,
})
export const continueChatResponseSchema = chatResponseBaseSchema
export type ContinueChatResponse = z.infer<typeof continueChatResponseSchema>