⚡ (bot) Use ky for queries in bot to improve reliability
This commit is contained in:
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@typebot.io/js",
|
"name": "@typebot.io/js",
|
||||||
"version": "0.2.28",
|
"version": "0.2.29",
|
||||||
"description": "Javascript library to display typebots on your website",
|
"description": "Javascript library to display typebots on your website",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "dist/index.js",
|
"main": "dist/index.js",
|
||||||
@ -15,6 +15,7 @@
|
|||||||
"@stripe/stripe-js": "1.54.1",
|
"@stripe/stripe-js": "1.54.1",
|
||||||
"@udecode/plate-common": "21.1.5",
|
"@udecode/plate-common": "21.1.5",
|
||||||
"dompurify": "3.0.6",
|
"dompurify": "3.0.6",
|
||||||
|
"ky": "^1.1.3",
|
||||||
"marked": "9.0.3",
|
"marked": "9.0.3",
|
||||||
"solid-element": "1.7.1",
|
"solid-element": "1.7.1",
|
||||||
"solid-js": "1.7.8"
|
"solid-js": "1.7.8"
|
||||||
|
@ -16,6 +16,7 @@ import { InputBlock } from '@typebot.io/schemas'
|
|||||||
import { StartFrom } from '@typebot.io/schemas'
|
import { StartFrom } from '@typebot.io/schemas'
|
||||||
import { defaultTheme } from '@typebot.io/schemas/features/typebot/theme/constants'
|
import { defaultTheme } from '@typebot.io/schemas/features/typebot/theme/constants'
|
||||||
import { clsx } from 'clsx'
|
import { clsx } from 'clsx'
|
||||||
|
import { HTTPError } from 'ky'
|
||||||
|
|
||||||
export type BotProps = {
|
export type BotProps = {
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
@ -52,7 +53,7 @@ export const Bot = (props: BotProps & { class?: string }) => {
|
|||||||
typeof props.typebot === 'string' ? props.typebot : undefined
|
typeof props.typebot === 'string' ? props.typebot : undefined
|
||||||
const isPreview =
|
const isPreview =
|
||||||
typeof props.typebot !== 'string' || (props.isPreview ?? false)
|
typeof props.typebot !== 'string' || (props.isPreview ?? false)
|
||||||
const { data, error, response } = await startChatQuery({
|
const { data, error } = await startChatQuery({
|
||||||
stripeRedirectStatus: urlParams.get('redirect_status') ?? undefined,
|
stripeRedirectStatus: urlParams.get('redirect_status') ?? undefined,
|
||||||
typebot: props.typebot,
|
typebot: props.typebot,
|
||||||
apiHost: props.apiHost,
|
apiHost: props.apiHost,
|
||||||
@ -66,24 +67,42 @@ export const Bot = (props: BotProps & { class?: string }) => {
|
|||||||
},
|
},
|
||||||
startFrom: props.startFrom,
|
startFrom: props.startFrom,
|
||||||
})
|
})
|
||||||
if (error && 'code' in error && typeof error.code === 'string') {
|
if (error instanceof HTTPError) {
|
||||||
if (isPreview) {
|
if (isPreview) {
|
||||||
return setError(
|
return setError(
|
||||||
new Error('An error occurred while loading the bot.', {
|
new Error(`An error occurred while loading the bot.`, {
|
||||||
cause: error.message,
|
cause: {
|
||||||
|
status: error.response.status,
|
||||||
|
body: await error.response.json(),
|
||||||
|
},
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
if (['BAD_REQUEST', 'FORBIDDEN'].includes(error.code))
|
if (error.response.status === 400 || error.response.status === 403)
|
||||||
return setError(new Error('This bot is now closed.'))
|
return setError(new Error('This bot is now closed.'))
|
||||||
if (error.code === 'NOT_FOUND')
|
if (error.response.status === 404)
|
||||||
return setError(new Error("The bot you're looking for doesn't exist."))
|
return setError(new Error("The bot you're looking for doesn't exist."))
|
||||||
|
return setError(
|
||||||
|
new Error(
|
||||||
|
`Error! Couldn't initiate the chat. (${error.response.statusText})`
|
||||||
|
)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!data) {
|
if (!data) {
|
||||||
if (error) console.error(error)
|
if (error) {
|
||||||
console.error({ data, error, response })
|
console.error(error)
|
||||||
return setError(new Error("Error! Couldn't initiate the chat."))
|
if (isPreview) {
|
||||||
|
return setError(
|
||||||
|
new Error(`Error! Could not reach server. Check your connection.`, {
|
||||||
|
cause: error,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return setError(
|
||||||
|
new Error('Error! Could not reach server. Check your connection.')
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (data.resultId && typebotIdFromProps)
|
if (data.resultId && typebotIdFromProps)
|
||||||
|
@ -31,6 +31,7 @@ import {
|
|||||||
} from '@/utils/formattedMessagesSignal'
|
} from '@/utils/formattedMessagesSignal'
|
||||||
import { InputBlockType } from '@typebot.io/schemas/features/blocks/inputs/constants'
|
import { InputBlockType } from '@typebot.io/schemas/features/blocks/inputs/constants'
|
||||||
import { saveClientLogsQuery } from '@/queries/saveClientLogsQuery'
|
import { saveClientLogsQuery } from '@/queries/saveClientLogsQuery'
|
||||||
|
import { HTTPError } from 'ky'
|
||||||
|
|
||||||
const parseDynamicTheme = (
|
const parseDynamicTheme = (
|
||||||
initialTheme: Theme,
|
initialTheme: Theme,
|
||||||
@ -173,7 +174,13 @@ export const ConversationContainer = (props: Props) => {
|
|||||||
const errorLogs = [
|
const errorLogs = [
|
||||||
{
|
{
|
||||||
description: 'Failed to send the reply',
|
description: 'Failed to send the reply',
|
||||||
details: error,
|
details:
|
||||||
|
error instanceof HTTPError
|
||||||
|
? {
|
||||||
|
status: error.response.status,
|
||||||
|
body: await error.response.json(),
|
||||||
|
}
|
||||||
|
: error,
|
||||||
status: 'error',
|
status: 'error',
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
@ -5,7 +5,7 @@ export const ErrorMessage = (props: Props) => {
|
|||||||
return (
|
return (
|
||||||
<div class="h-full flex justify-center items-center flex-col">
|
<div class="h-full flex justify-center items-center flex-col">
|
||||||
<p class="text-2xl text-center">{props.error.message}</p>
|
<p class="text-2xl text-center">{props.error.message}</p>
|
||||||
<p class="text-center">{props.error.cause as string}</p>
|
<pre>{JSON.stringify(props.error.cause, null, 2) as string}</pre>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
import { guessApiHost } from '@/utils/guessApiHost'
|
import { guessApiHost } from '@/utils/guessApiHost'
|
||||||
import { isNotEmpty, sendRequest } from '@typebot.io/lib'
|
import { isNotEmpty } from '@typebot.io/lib'
|
||||||
import { ContinueChatResponse } from '@typebot.io/schemas'
|
import { ContinueChatResponse } from '@typebot.io/schemas'
|
||||||
|
import ky from 'ky'
|
||||||
|
|
||||||
export const continueChatQuery = ({
|
export const continueChatQuery = async ({
|
||||||
apiHost,
|
apiHost,
|
||||||
message,
|
message,
|
||||||
sessionId,
|
sessionId,
|
||||||
@ -10,13 +11,24 @@ export const continueChatQuery = ({
|
|||||||
apiHost?: string
|
apiHost?: string
|
||||||
message: string | undefined
|
message: string | undefined
|
||||||
sessionId: string
|
sessionId: string
|
||||||
}) =>
|
}) => {
|
||||||
sendRequest<ContinueChatResponse>({
|
try {
|
||||||
method: 'POST',
|
const data = await ky
|
||||||
url: `${
|
.post(
|
||||||
isNotEmpty(apiHost) ? apiHost : guessApiHost()
|
`${
|
||||||
}/api/v1/sessions/${sessionId}/continueChat`,
|
isNotEmpty(apiHost) ? apiHost : guessApiHost()
|
||||||
body: {
|
}/api/v1/sessions/${sessionId}/continueChat`,
|
||||||
message,
|
{
|
||||||
},
|
json: {
|
||||||
})
|
message,
|
||||||
|
},
|
||||||
|
timeout: false,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.json<ContinueChatResponse>()
|
||||||
|
|
||||||
|
return { data }
|
||||||
|
} catch (error) {
|
||||||
|
return { error }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,33 +0,0 @@
|
|||||||
import { guessApiHost } from '@/utils/guessApiHost'
|
|
||||||
import { isNotEmpty } from '@typebot.io/lib'
|
|
||||||
|
|
||||||
export const getOpenAiStreamerQuery =
|
|
||||||
({ apiHost, sessionId }: { apiHost?: string; sessionId: string }) =>
|
|
||||||
async (
|
|
||||||
messages: {
|
|
||||||
content?: string | undefined
|
|
||||||
role?: 'system' | 'user' | 'assistant' | undefined
|
|
||||||
}[]
|
|
||||||
) => {
|
|
||||||
const response = await fetch(
|
|
||||||
`${
|
|
||||||
isNotEmpty(apiHost) ? apiHost : guessApiHost()
|
|
||||||
}/api/integrations/openai/streamer`,
|
|
||||||
{
|
|
||||||
method: 'POST',
|
|
||||||
headers: {
|
|
||||||
'Content-Type': 'application/json',
|
|
||||||
},
|
|
||||||
body: JSON.stringify({
|
|
||||||
sessionId,
|
|
||||||
messages,
|
|
||||||
}),
|
|
||||||
}
|
|
||||||
)
|
|
||||||
if (!response.ok) {
|
|
||||||
throw new Error(response.statusText)
|
|
||||||
}
|
|
||||||
|
|
||||||
const data = response.body
|
|
||||||
return data
|
|
||||||
}
|
|
@ -1,8 +1,9 @@
|
|||||||
import { guessApiHost } from '@/utils/guessApiHost'
|
import { guessApiHost } from '@/utils/guessApiHost'
|
||||||
import type { ChatLog } from '@typebot.io/schemas'
|
import type { ChatLog } from '@typebot.io/schemas'
|
||||||
import { isNotEmpty, sendRequest } from '@typebot.io/lib'
|
import { isNotEmpty } from '@typebot.io/lib'
|
||||||
|
import ky from 'ky'
|
||||||
|
|
||||||
export const saveClientLogsQuery = ({
|
export const saveClientLogsQuery = async ({
|
||||||
apiHost,
|
apiHost,
|
||||||
sessionId,
|
sessionId,
|
||||||
clientLogs,
|
clientLogs,
|
||||||
@ -10,13 +11,19 @@ export const saveClientLogsQuery = ({
|
|||||||
apiHost?: string
|
apiHost?: string
|
||||||
sessionId: string
|
sessionId: string
|
||||||
clientLogs: ChatLog[]
|
clientLogs: ChatLog[]
|
||||||
}) =>
|
}) => {
|
||||||
sendRequest({
|
try {
|
||||||
method: 'POST',
|
await ky.post(
|
||||||
url: `${
|
`${
|
||||||
isNotEmpty(apiHost) ? apiHost : guessApiHost()
|
isNotEmpty(apiHost) ? apiHost : guessApiHost()
|
||||||
}/api/v1/sessions/${sessionId}/clientLogs`,
|
}/api/v1/sessions/${sessionId}/clientLogs`,
|
||||||
body: {
|
{
|
||||||
clientLogs,
|
json: {
|
||||||
},
|
clientLogs,
|
||||||
})
|
},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
} catch (e) {
|
||||||
|
console.log(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { BotContext, InitialChatReply } from '@/types'
|
import { BotContext, InitialChatReply } from '@/types'
|
||||||
import { guessApiHost } from '@/utils/guessApiHost'
|
import { guessApiHost } from '@/utils/guessApiHost'
|
||||||
import { isNotDefined, isNotEmpty, sendRequest } from '@typebot.io/lib'
|
import { isNotDefined, isNotEmpty } from '@typebot.io/lib'
|
||||||
import {
|
import {
|
||||||
getPaymentInProgressInStorage,
|
getPaymentInProgressInStorage,
|
||||||
removePaymentInProgressFromStorage,
|
removePaymentInProgressFromStorage,
|
||||||
@ -10,6 +10,18 @@ import {
|
|||||||
StartFrom,
|
StartFrom,
|
||||||
StartPreviewChatInput,
|
StartPreviewChatInput,
|
||||||
} from '@typebot.io/schemas'
|
} from '@typebot.io/schemas'
|
||||||
|
import ky from 'ky'
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
typebot: string | any
|
||||||
|
stripeRedirectStatus?: string
|
||||||
|
apiHost?: string
|
||||||
|
startFrom?: StartFrom
|
||||||
|
isPreview: boolean
|
||||||
|
prefilledVariables?: Record<string, unknown>
|
||||||
|
resultId?: string
|
||||||
|
}
|
||||||
|
|
||||||
export async function startChatQuery({
|
export async function startChatQuery({
|
||||||
typebot,
|
typebot,
|
||||||
@ -19,16 +31,7 @@ export async function startChatQuery({
|
|||||||
resultId,
|
resultId,
|
||||||
stripeRedirectStatus,
|
stripeRedirectStatus,
|
||||||
startFrom,
|
startFrom,
|
||||||
}: {
|
}: Props) {
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
||||||
typebot: string | any
|
|
||||||
stripeRedirectStatus?: string
|
|
||||||
apiHost?: string
|
|
||||||
startFrom?: StartFrom
|
|
||||||
isPreview: boolean
|
|
||||||
prefilledVariables?: Record<string, unknown>
|
|
||||||
resultId?: string
|
|
||||||
}) {
|
|
||||||
if (isNotDefined(typebot))
|
if (isNotDefined(typebot))
|
||||||
throw new Error('Typebot ID is required to get initial messages')
|
throw new Error('Typebot ID is required to get initial messages')
|
||||||
|
|
||||||
@ -41,67 +44,75 @@ export async function startChatQuery({
|
|||||||
: undefined
|
: undefined
|
||||||
if (paymentInProgressState) {
|
if (paymentInProgressState) {
|
||||||
removePaymentInProgressFromStorage()
|
removePaymentInProgressFromStorage()
|
||||||
const { data, error, response } = await sendRequest<InitialChatReply>({
|
|
||||||
method: 'POST',
|
try {
|
||||||
url: `${isNotEmpty(apiHost) ? apiHost : guessApiHost()}/api/v1/sessions/${
|
const data = await ky
|
||||||
paymentInProgressState.sessionId
|
.post(
|
||||||
}/continueChat`,
|
`${isNotEmpty(apiHost) ? apiHost : guessApiHost()}/api/v1/sessions/${
|
||||||
body: {
|
paymentInProgressState.sessionId
|
||||||
message: paymentInProgressState
|
}/continueChat`,
|
||||||
? stripeRedirectStatus === 'failed'
|
{
|
||||||
? 'fail'
|
json: {
|
||||||
: 'Success'
|
message: paymentInProgressState
|
||||||
: undefined,
|
? stripeRedirectStatus === 'failed'
|
||||||
},
|
? 'fail'
|
||||||
})
|
: 'Success'
|
||||||
return {
|
: undefined,
|
||||||
data: data
|
},
|
||||||
? {
|
timeout: false,
|
||||||
...data,
|
|
||||||
...(paymentInProgressState
|
|
||||||
? { typebot: paymentInProgressState.typebot }
|
|
||||||
: {}),
|
|
||||||
}
|
}
|
||||||
: undefined,
|
)
|
||||||
error,
|
.json<InitialChatReply>()
|
||||||
response,
|
|
||||||
|
return { data }
|
||||||
|
} catch (error) {
|
||||||
|
return { error }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const typebotId = typeof typebot === 'string' ? typebot : typebot.id
|
const typebotId = typeof typebot === 'string' ? typebot : typebot.id
|
||||||
if (isPreview) {
|
if (isPreview) {
|
||||||
const { data, error, response } = await sendRequest<InitialChatReply>({
|
try {
|
||||||
method: 'POST',
|
const data = await ky
|
||||||
url: `${
|
.post(
|
||||||
isNotEmpty(apiHost) ? apiHost : guessApiHost()
|
`${
|
||||||
}/api/v1/typebots/${typebotId}/preview/startChat`,
|
isNotEmpty(apiHost) ? apiHost : guessApiHost()
|
||||||
body: {
|
}/api/v1/typebots/${typebotId}/preview/startChat`,
|
||||||
isStreamEnabled: true,
|
{
|
||||||
startFrom,
|
json: {
|
||||||
typebot,
|
isStreamEnabled: true,
|
||||||
} satisfies Omit<StartPreviewChatInput, 'typebotId'>,
|
startFrom,
|
||||||
})
|
typebot,
|
||||||
return {
|
} satisfies Omit<StartPreviewChatInput, 'typebotId'>,
|
||||||
data,
|
timeout: false,
|
||||||
error,
|
}
|
||||||
response,
|
)
|
||||||
|
.json<InitialChatReply>()
|
||||||
|
|
||||||
|
return { data }
|
||||||
|
} catch (error) {
|
||||||
|
return { error }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const { data, error, response } = await sendRequest<InitialChatReply>({
|
try {
|
||||||
method: 'POST',
|
const data = await ky
|
||||||
url: `${
|
.post(
|
||||||
isNotEmpty(apiHost) ? apiHost : guessApiHost()
|
`${
|
||||||
}/api/v1/typebots/${typebotId}/startChat`,
|
isNotEmpty(apiHost) ? apiHost : guessApiHost()
|
||||||
body: {
|
}/api/v1/typebots/${typebotId}/startChat`,
|
||||||
isStreamEnabled: true,
|
{
|
||||||
prefilledVariables,
|
json: {
|
||||||
resultId,
|
isStreamEnabled: true,
|
||||||
} satisfies Omit<StartChatInput, 'publicId'>,
|
prefilledVariables,
|
||||||
})
|
resultId,
|
||||||
|
} satisfies Omit<StartChatInput, 'publicId'>,
|
||||||
|
timeout: false,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.json<InitialChatReply>()
|
||||||
|
|
||||||
return {
|
return { data }
|
||||||
data,
|
} catch (error) {
|
||||||
error,
|
return { error }
|
||||||
response,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@typebot.io/nextjs",
|
"name": "@typebot.io/nextjs",
|
||||||
"version": "0.2.28",
|
"version": "0.2.29",
|
||||||
"description": "Convenient library to display typebots on your Next.js website",
|
"description": "Convenient library to display typebots on your Next.js website",
|
||||||
"main": "dist/index.js",
|
"main": "dist/index.js",
|
||||||
"types": "dist/index.d.ts",
|
"types": "dist/index.d.ts",
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@typebot.io/react",
|
"name": "@typebot.io/react",
|
||||||
"version": "0.2.28",
|
"version": "0.2.29",
|
||||||
"description": "Convenient library to display typebots on your React app",
|
"description": "Convenient library to display typebots on your React app",
|
||||||
"main": "dist/index.js",
|
"main": "dist/index.js",
|
||||||
"types": "dist/index.d.ts",
|
"types": "dist/index.d.ts",
|
||||||
|
8
pnpm-lock.yaml
generated
8
pnpm-lock.yaml
generated
@ -956,6 +956,9 @@ importers:
|
|||||||
dompurify:
|
dompurify:
|
||||||
specifier: 3.0.6
|
specifier: 3.0.6
|
||||||
version: 3.0.6
|
version: 3.0.6
|
||||||
|
ky:
|
||||||
|
specifier: ^1.1.3
|
||||||
|
version: 1.1.3
|
||||||
marked:
|
marked:
|
||||||
specifier: 9.0.3
|
specifier: 9.0.3
|
||||||
version: 9.0.3
|
version: 9.0.3
|
||||||
@ -17743,6 +17746,11 @@ packages:
|
|||||||
resolution: {integrity: sha512-RTSoaUAfLvpR357vWzAz/50Q/BmHfmE6ETSWfutT0AJiw10e6CmcdYRQJlLRd95B53D0Y2aD1jSxD3V3ySF+PA==}
|
resolution: {integrity: sha512-RTSoaUAfLvpR357vWzAz/50Q/BmHfmE6ETSWfutT0AJiw10e6CmcdYRQJlLRd95B53D0Y2aD1jSxD3V3ySF+PA==}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/ky@1.1.3:
|
||||||
|
resolution: {integrity: sha512-t7q8sJfazzHbfYxiCtuLIH4P+pWoCgunDll17O/GBZBqMt2vHjGSx5HzSxhOc2BDEg3YN/EmeA7VKrHnwuWDag==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
dev: false
|
||||||
|
|
||||||
/language-subtag-registry@0.3.22:
|
/language-subtag-registry@0.3.22:
|
||||||
resolution: {integrity: sha512-tN0MCzyWnoz/4nHS6uxdlFWoUZT7ABptwKPQ52Ea7URk6vll88bWBVhodtnlfEuCcKWNGoc+uGbw1cwa9IKh/w==}
|
resolution: {integrity: sha512-tN0MCzyWnoz/4nHS6uxdlFWoUZT7ABptwKPQ52Ea7URk6vll88bWBVhodtnlfEuCcKWNGoc+uGbw1cwa9IKh/w==}
|
||||||
dev: false
|
dev: false
|
||||||
|
Reference in New Issue
Block a user