@ -13,7 +13,6 @@ type Props = {
|
||||
sessionId: string
|
||||
}
|
||||
export const continueChat = async ({ origin, sessionId, message }: Props) => {
|
||||
console.log('test')
|
||||
const session = await getSession(sessionId)
|
||||
|
||||
if (!session) {
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { isNotEmpty } from '@typebot.io/lib/utils'
|
||||
import { ContinueChatResponse } from '@typebot.io/schemas'
|
||||
import { OpenAIBlock } from '@typebot.io/schemas/features/blocks/integrations/openai'
|
||||
import { HTTPError } from 'got'
|
||||
import { HTTPError } from 'ky'
|
||||
import { ClientOptions, OpenAI } from 'openai'
|
||||
|
||||
type Props = Pick<
|
||||
@ -55,9 +55,9 @@ export const executeChatCompletionOpenAIRequest = async ({
|
||||
} catch (error) {
|
||||
if (error instanceof HTTPError) {
|
||||
if (
|
||||
(error.response.statusCode === 503 ||
|
||||
error.response.statusCode === 500 ||
|
||||
error.response.statusCode === 403) &&
|
||||
(error.response.status === 503 ||
|
||||
error.response.status === 500 ||
|
||||
error.response.status === 403) &&
|
||||
!isRetrying
|
||||
) {
|
||||
console.log('OpenAI API error - 503, retrying in 3 seconds')
|
||||
@ -73,7 +73,7 @@ export const executeChatCompletionOpenAIRequest = async ({
|
||||
isRetrying: true,
|
||||
})
|
||||
}
|
||||
if (error.response.statusCode === 400) {
|
||||
if (error.response.status === 400) {
|
||||
const log = {
|
||||
status: 'info',
|
||||
description:
|
||||
@ -93,8 +93,8 @@ export const executeChatCompletionOpenAIRequest = async ({
|
||||
}
|
||||
logs.push({
|
||||
status: 'error',
|
||||
description: `OpenAI API error - ${error.response.statusCode}`,
|
||||
details: error.response.body,
|
||||
description: `OpenAI API error - ${error.response.status}`,
|
||||
details: await error.response.text(),
|
||||
})
|
||||
return { logs }
|
||||
}
|
||||
|
@ -11,10 +11,11 @@ import {
|
||||
ChatLog,
|
||||
ExecutableHttpRequest,
|
||||
AnswerInSessionState,
|
||||
TypebotInSession,
|
||||
} from '@typebot.io/schemas'
|
||||
import { stringify } from 'qs'
|
||||
import { isDefined, isEmpty, isNotDefined, omit } from '@typebot.io/lib'
|
||||
import got, { Method, HTTPError, OptionsInit } from 'got'
|
||||
import ky, { HTTPError, Options } from 'ky'
|
||||
import { resumeWebhookExecution } from './resumeWebhookExecution'
|
||||
import { ExecuteIntegrationResponse } from '../../../types'
|
||||
import { parseVariables } from '@typebot.io/variables/parseVariables'
|
||||
@ -60,9 +61,11 @@ export const executeWebhookBlock = async (
|
||||
})) as HttpRequest | null)
|
||||
: null)
|
||||
if (!webhook) return { outgoingEdgeId: block.outgoingEdgeId }
|
||||
const parsedWebhook = await parseWebhookAttributes(state)({
|
||||
const parsedWebhook = await parseWebhookAttributes({
|
||||
webhook,
|
||||
isCustomBody: block.options?.isCustomBody,
|
||||
typebot: state.typebotsQueue[0].typebot,
|
||||
answers: state.typebotsQueue[0].answers,
|
||||
})
|
||||
if (!parsedWebhook) {
|
||||
logs.push({
|
||||
@ -104,69 +107,69 @@ export const executeWebhookBlock = async (
|
||||
|
||||
const checkIfBodyIsAVariable = (body: string) => /^{{.+}}$/.test(body)
|
||||
|
||||
const parseWebhookAttributes =
|
||||
(state: SessionState) =>
|
||||
async ({
|
||||
webhook,
|
||||
isCustomBody,
|
||||
}: {
|
||||
webhook: HttpRequest
|
||||
isCustomBody?: boolean
|
||||
}): Promise<ParsedWebhook | undefined> => {
|
||||
if (!webhook.url) return
|
||||
const { typebot } = state.typebotsQueue[0]
|
||||
const basicAuth: { username?: string; password?: string } = {}
|
||||
const basicAuthHeaderIdx = webhook.headers?.findIndex(
|
||||
(h) =>
|
||||
h.key?.toLowerCase() === 'authorization' &&
|
||||
h.value?.toLowerCase()?.includes('basic')
|
||||
)
|
||||
const isUsernamePasswordBasicAuth =
|
||||
basicAuthHeaderIdx !== -1 &&
|
||||
isDefined(basicAuthHeaderIdx) &&
|
||||
webhook.headers?.at(basicAuthHeaderIdx)?.value?.includes(':')
|
||||
if (isUsernamePasswordBasicAuth) {
|
||||
const [username, password] =
|
||||
webhook.headers?.at(basicAuthHeaderIdx)?.value?.slice(6).split(':') ??
|
||||
[]
|
||||
basicAuth.username = username
|
||||
basicAuth.password = password
|
||||
webhook.headers?.splice(basicAuthHeaderIdx, 1)
|
||||
}
|
||||
const headers = convertKeyValueTableToObject(
|
||||
webhook.headers,
|
||||
typebot.variables
|
||||
) as ExecutableHttpRequest['headers'] | undefined
|
||||
const queryParams = stringify(
|
||||
convertKeyValueTableToObject(webhook.queryParams, typebot.variables)
|
||||
)
|
||||
const bodyContent = await getBodyContent({
|
||||
body: webhook.body,
|
||||
answers: state.typebotsQueue[0].answers,
|
||||
variables: typebot.variables,
|
||||
isCustomBody,
|
||||
})
|
||||
const method = webhook.method ?? defaultWebhookAttributes.method
|
||||
const { data: body, isJson } =
|
||||
bodyContent && method !== HttpMethod.GET
|
||||
? safeJsonParse(
|
||||
parseVariables(typebot.variables, {
|
||||
isInsideJson: !checkIfBodyIsAVariable(bodyContent),
|
||||
})(bodyContent)
|
||||
)
|
||||
: { data: undefined, isJson: false }
|
||||
|
||||
return {
|
||||
url: parseVariables(typebot.variables)(
|
||||
webhook.url + (queryParams !== '' ? `?${queryParams}` : '')
|
||||
),
|
||||
basicAuth,
|
||||
method,
|
||||
headers,
|
||||
body,
|
||||
isJson,
|
||||
}
|
||||
export const parseWebhookAttributes = async ({
|
||||
webhook,
|
||||
isCustomBody,
|
||||
typebot,
|
||||
answers,
|
||||
}: {
|
||||
webhook: HttpRequest
|
||||
isCustomBody?: boolean
|
||||
typebot: TypebotInSession
|
||||
answers: AnswerInSessionState[]
|
||||
}): Promise<ParsedWebhook | undefined> => {
|
||||
if (!webhook.url) return
|
||||
const basicAuth: { username?: string; password?: string } = {}
|
||||
const basicAuthHeaderIdx = webhook.headers?.findIndex(
|
||||
(h) =>
|
||||
h.key?.toLowerCase() === 'authorization' &&
|
||||
h.value?.toLowerCase()?.includes('basic')
|
||||
)
|
||||
const isUsernamePasswordBasicAuth =
|
||||
basicAuthHeaderIdx !== -1 &&
|
||||
isDefined(basicAuthHeaderIdx) &&
|
||||
webhook.headers?.at(basicAuthHeaderIdx)?.value?.includes(':')
|
||||
if (isUsernamePasswordBasicAuth) {
|
||||
const [username, password] =
|
||||
webhook.headers?.at(basicAuthHeaderIdx)?.value?.slice(6).split(':') ?? []
|
||||
basicAuth.username = username
|
||||
basicAuth.password = password
|
||||
webhook.headers?.splice(basicAuthHeaderIdx, 1)
|
||||
}
|
||||
const headers = convertKeyValueTableToObject(
|
||||
webhook.headers,
|
||||
typebot.variables
|
||||
) as ExecutableHttpRequest['headers'] | undefined
|
||||
const queryParams = stringify(
|
||||
convertKeyValueTableToObject(webhook.queryParams, typebot.variables)
|
||||
)
|
||||
const bodyContent = await getBodyContent({
|
||||
body: webhook.body,
|
||||
answers,
|
||||
variables: typebot.variables,
|
||||
isCustomBody,
|
||||
})
|
||||
const method = webhook.method ?? defaultWebhookAttributes.method
|
||||
const { data: body, isJson } =
|
||||
bodyContent && method !== HttpMethod.GET
|
||||
? safeJsonParse(
|
||||
parseVariables(typebot.variables, {
|
||||
isInsideJson: !checkIfBodyIsAVariable(bodyContent),
|
||||
})(bodyContent)
|
||||
)
|
||||
: { data: undefined, isJson: false }
|
||||
|
||||
return {
|
||||
url: parseVariables(typebot.variables)(
|
||||
webhook.url + (queryParams !== '' ? `?${queryParams}` : '')
|
||||
),
|
||||
basicAuth,
|
||||
method,
|
||||
headers,
|
||||
body,
|
||||
isJson,
|
||||
}
|
||||
}
|
||||
|
||||
export const executeWebhook = async (
|
||||
webhook: ParsedWebhook,
|
||||
@ -177,7 +180,8 @@ export const executeWebhook = async (
|
||||
startTimeShouldBeUpdated?: boolean
|
||||
}> => {
|
||||
const logs: ChatLog[] = []
|
||||
const { headers, url, method, basicAuth, body, isJson } = webhook
|
||||
|
||||
const { headers, url, method, basicAuth, isJson } = webhook
|
||||
const contentType = headers ? headers['Content-Type'] : undefined
|
||||
|
||||
const isLongRequest = params.disableRequestTimeout
|
||||
@ -186,59 +190,60 @@ export const executeWebhook = async (
|
||||
url?.includes(whiteListedUrl)
|
||||
)
|
||||
|
||||
const isFormData = contentType?.includes('x-www-form-urlencoded')
|
||||
|
||||
let body = webhook.body
|
||||
|
||||
if (isFormData && isJson) body = parseFormDataBody(body as object)
|
||||
|
||||
const request = {
|
||||
url,
|
||||
method: method as Method,
|
||||
method,
|
||||
headers: headers ?? {},
|
||||
...(basicAuth ?? {}),
|
||||
json:
|
||||
!contentType?.includes('x-www-form-urlencoded') && body && isJson
|
||||
? body
|
||||
: undefined,
|
||||
form:
|
||||
contentType?.includes('x-www-form-urlencoded') && body ? body : undefined,
|
||||
body: body && !isJson ? (body as string) : undefined,
|
||||
timeout: {
|
||||
response: isNotDefined(env.CHAT_API_TIMEOUT)
|
||||
? undefined
|
||||
: params.timeout && params.timeout !== defaultTimeout
|
||||
? Math.min(params.timeout, maxTimeout) * 1000
|
||||
: isLongRequest
|
||||
? maxTimeout * 1000
|
||||
: defaultTimeout * 1000,
|
||||
},
|
||||
} satisfies OptionsInit
|
||||
json: !isFormData && body && isJson ? body : undefined,
|
||||
body: (isFormData && body ? body : undefined) as any,
|
||||
timeout: isNotDefined(env.CHAT_API_TIMEOUT)
|
||||
? undefined
|
||||
: params.timeout && params.timeout !== defaultTimeout
|
||||
? Math.min(params.timeout, maxTimeout) * 1000
|
||||
: isLongRequest
|
||||
? maxTimeout * 1000
|
||||
: defaultTimeout * 1000,
|
||||
} satisfies Options & { url: string; body: any }
|
||||
|
||||
try {
|
||||
const response = await got(request.url, omit(request, 'url'))
|
||||
const response = await ky(request.url, omit(request, 'url'))
|
||||
const body = await response.text()
|
||||
logs.push({
|
||||
status: 'success',
|
||||
description: webhookSuccessDescription,
|
||||
details: {
|
||||
statusCode: response.statusCode,
|
||||
response: safeJsonParse(response.body).data,
|
||||
statusCode: response.status,
|
||||
response: safeJsonParse(body).data,
|
||||
request,
|
||||
},
|
||||
})
|
||||
return {
|
||||
response: {
|
||||
statusCode: response.statusCode,
|
||||
data: safeJsonParse(response.body).data,
|
||||
statusCode: response.status,
|
||||
data: safeJsonParse(body).data,
|
||||
},
|
||||
logs,
|
||||
startTimeShouldBeUpdated: true,
|
||||
}
|
||||
} catch (error) {
|
||||
if (error instanceof HTTPError) {
|
||||
const responseBody = await error.response.text()
|
||||
const response = {
|
||||
statusCode: error.response.statusCode,
|
||||
data: safeJsonParse(error.response.body as string).data,
|
||||
statusCode: error.response.status,
|
||||
data: safeJsonParse(responseBody).data,
|
||||
}
|
||||
logs.push({
|
||||
status: 'error',
|
||||
description: webhookErrorDescription,
|
||||
details: {
|
||||
statusCode: error.response.statusCode,
|
||||
statusCode: error.response.status,
|
||||
request,
|
||||
response,
|
||||
},
|
||||
@ -257,7 +262,7 @@ export const executeWebhook = async (
|
||||
}
|
||||
logs.push({
|
||||
status: 'error',
|
||||
description: `Webhook request timed out. (${request.timeout.response}ms)`,
|
||||
description: `Webhook request timed out. (${request.timeout}ms)`,
|
||||
details: {
|
||||
response,
|
||||
request,
|
||||
@ -320,10 +325,18 @@ export const convertKeyValueTableToObject = (
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const safeJsonParse = (json: string): { data: any; isJson: boolean } => {
|
||||
const safeJsonParse = (json: unknown): { data: any; isJson: boolean } => {
|
||||
try {
|
||||
return { data: JSON.parse(json), isJson: true }
|
||||
return { data: JSON.parse(json as string), isJson: true }
|
||||
} catch (err) {
|
||||
return { data: json, isJson: false }
|
||||
}
|
||||
}
|
||||
|
||||
const parseFormDataBody = (body: object) => {
|
||||
const searchParams = new URLSearchParams()
|
||||
Object.entries(body as object).forEach(([key, value]) => {
|
||||
searchParams.set(key, value)
|
||||
})
|
||||
return searchParams
|
||||
}
|
||||
|
@ -4,7 +4,7 @@ import {
|
||||
ZemanticAiCredentials,
|
||||
ZemanticAiResponse,
|
||||
} from '@typebot.io/schemas/features/blocks/integrations/zemanticAi'
|
||||
import got from 'got'
|
||||
import ky from 'ky'
|
||||
import { decrypt } from '@typebot.io/lib/api/encryption/decrypt'
|
||||
import { byId, isDefined, isEmpty } from '@typebot.io/lib'
|
||||
import { ExecuteIntegrationResponse } from '../../../types'
|
||||
@ -51,7 +51,7 @@ export const executeZemanticAiBlock = async (
|
||||
})
|
||||
|
||||
try {
|
||||
const res: ZemanticAiResponse = await got
|
||||
const res: ZemanticAiResponse = await ky
|
||||
.post(URL, {
|
||||
headers: {
|
||||
Authorization: `Bearer ${apiKey}`,
|
||||
|
@ -11,12 +11,12 @@ import { LogicBlockType } from '@typebot.io/schemas/features/blocks/logic/consta
|
||||
|
||||
type Props = {
|
||||
typebots: Pick<PublicTypebot, 'groups'>[]
|
||||
user?: User
|
||||
userId: string | undefined
|
||||
isPreview?: boolean
|
||||
}
|
||||
|
||||
export const getPreviouslyLinkedTypebots =
|
||||
({ typebots, user, isPreview }: Props) =>
|
||||
export const fetchLinkedChildTypebots =
|
||||
({ typebots, userId, isPreview }: Props) =>
|
||||
async (
|
||||
capturedLinkedBots: (Typebot | PublicTypebot)[]
|
||||
): Promise<(Typebot | PublicTypebot)[]> => {
|
||||
@ -40,13 +40,13 @@ export const getPreviouslyLinkedTypebots =
|
||||
.filter(isDefined)
|
||||
if (linkedTypebotIds.length === 0) return capturedLinkedBots
|
||||
const linkedTypebots = (await fetchLinkedTypebots({
|
||||
user,
|
||||
userId,
|
||||
typebotIds: linkedTypebotIds,
|
||||
isPreview,
|
||||
})) as (Typebot | PublicTypebot)[]
|
||||
return getPreviouslyLinkedTypebots({
|
||||
return fetchLinkedChildTypebots({
|
||||
typebots: linkedTypebots,
|
||||
user,
|
||||
userId,
|
||||
isPreview,
|
||||
})([...capturedLinkedBots, ...linkedTypebots])
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
import { fetchLinkedTypebots } from './fetchLinkedTypebots'
|
||||
|
||||
type Props = {
|
||||
parentTypebotIds: string[]
|
||||
userId: string | undefined
|
||||
isPreview?: boolean
|
||||
}
|
||||
|
||||
export const fetchLinkedParentTypebots = ({
|
||||
parentTypebotIds,
|
||||
isPreview,
|
||||
userId,
|
||||
}: Props) =>
|
||||
parentTypebotIds.length > 0
|
||||
? fetchLinkedTypebots({
|
||||
typebotIds: parentTypebotIds,
|
||||
isPreview,
|
||||
userId,
|
||||
})
|
||||
: []
|
@ -1,20 +1,19 @@
|
||||
import prisma from '@typebot.io/lib/prisma'
|
||||
import { User } from '@typebot.io/prisma'
|
||||
|
||||
type Props = {
|
||||
isPreview?: boolean
|
||||
typebotIds: string[]
|
||||
user?: User
|
||||
userId: string | undefined
|
||||
}
|
||||
|
||||
export const fetchLinkedTypebots = async ({
|
||||
user,
|
||||
userId,
|
||||
isPreview,
|
||||
typebotIds,
|
||||
}: Props) => {
|
||||
if (!user || !isPreview)
|
||||
if (!userId || !isPreview)
|
||||
return prisma.publicTypebot.findMany({
|
||||
where: { id: { in: typebotIds } },
|
||||
where: { typebotId: { in: typebotIds } },
|
||||
})
|
||||
const linkedTypebots = await prisma.typebot.findMany({
|
||||
where: { id: { in: typebotIds } },
|
||||
@ -39,7 +38,7 @@ export const fetchLinkedTypebots = async ({
|
||||
return linkedTypebots.filter(
|
||||
(typebot) =>
|
||||
typebot.collaborators.some(
|
||||
(collaborator) => collaborator.userId === user.id
|
||||
) || typebot.workspace.members.some((member) => member.userId === user.id)
|
||||
(collaborator) => collaborator.userId === userId
|
||||
) || typebot.workspace.members.some((member) => member.userId === userId)
|
||||
)
|
||||
}
|
||||
|
@ -111,7 +111,6 @@ export const executeGroup = async (
|
||||
logs,
|
||||
visitedEdges,
|
||||
}
|
||||
console.log('yes')
|
||||
const executionResponse = (
|
||||
isLogicBlock(block)
|
||||
? await executeLogic(newSessionState)(block)
|
||||
|
@ -16,7 +16,6 @@ import { env } from '@typebot.io/env'
|
||||
export const executeIntegration =
|
||||
(state: SessionState) =>
|
||||
async (block: IntegrationBlock): Promise<ExecuteIntegrationResponse> => {
|
||||
console.log('HI')
|
||||
switch (block.type) {
|
||||
case IntegrationBlockType.GOOGLE_SHEETS:
|
||||
return {
|
||||
|
@ -27,7 +27,6 @@ export const executeForgedBlock = async (
|
||||
const blockDef = forgedBlocks[block.type]
|
||||
if (!blockDef) return { outgoingEdgeId: block.outgoingEdgeId }
|
||||
const action = blockDef.actions.find((a) => a.name === block.options.action)
|
||||
console.log('test', action)
|
||||
const noCredentialsError = {
|
||||
status: 'error',
|
||||
description: 'Credentials not provided for integration',
|
||||
|
@ -14,6 +14,7 @@
|
||||
"@typebot.io/env": "workspace:*",
|
||||
"@typebot.io/lib": "workspace:*",
|
||||
"@typebot.io/prisma": "workspace:*",
|
||||
"@typebot.io/results": "workspace:*",
|
||||
"@typebot.io/schemas": "workspace:*",
|
||||
"@typebot.io/tsconfig": "workspace:*",
|
||||
"@typebot.io/variables": "workspace:*",
|
||||
@ -24,19 +25,18 @@
|
||||
"date-fns-tz": "2.0.0",
|
||||
"google-auth-library": "8.9.0",
|
||||
"google-spreadsheet": "4.1.1",
|
||||
"got": "12.6.0",
|
||||
"ky": "^1.1.3",
|
||||
"ky": "1.2.3",
|
||||
"libphonenumber-js": "1.10.37",
|
||||
"node-html-parser": "6.1.5",
|
||||
"nodemailer": "6.9.8",
|
||||
"openai": "4.28.4",
|
||||
"qs": "6.11.2",
|
||||
"stripe": "12.13.0",
|
||||
"@typebot.io/results": "workspace:*"
|
||||
"stripe": "12.13.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@typebot.io/forge": "workspace:*",
|
||||
"@typebot.io/forge-repository": "workspace:*",
|
||||
"@types/node": "^20.12.3",
|
||||
"@types/nodemailer": "6.4.14",
|
||||
"@types/qs": "6.9.7"
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { env } from '@typebot.io/env'
|
||||
import got from 'got'
|
||||
import ky from 'ky'
|
||||
|
||||
type Props = {
|
||||
mediaId: string
|
||||
@ -10,21 +10,24 @@ export const downloadMedia = async ({
|
||||
mediaId,
|
||||
systemUserAccessToken,
|
||||
}: Props): Promise<{ file: Buffer; mimeType: string }> => {
|
||||
const { body } = await got.get({
|
||||
url: `${env.WHATSAPP_CLOUD_API_URL}/v17.0/${mediaId}`,
|
||||
headers: {
|
||||
Authorization: `Bearer ${systemUserAccessToken}`,
|
||||
},
|
||||
})
|
||||
|
||||
const parsedBody = JSON.parse(body) as { url: string; mime_type: string }
|
||||
|
||||
return {
|
||||
file: await got(parsedBody.url, {
|
||||
const { url, mime_type } = await ky
|
||||
.get(`${env.WHATSAPP_CLOUD_API_URL}/v17.0/${mediaId}`, {
|
||||
headers: {
|
||||
Authorization: `Bearer ${systemUserAccessToken}`,
|
||||
},
|
||||
}).buffer(),
|
||||
mimeType: parsedBody.mime_type,
|
||||
})
|
||||
.json<{ url: string; mime_type: string }>()
|
||||
|
||||
return {
|
||||
file: Buffer.from(
|
||||
await ky
|
||||
.get(url, {
|
||||
headers: {
|
||||
Authorization: `Bearer ${systemUserAccessToken}`,
|
||||
},
|
||||
})
|
||||
.arrayBuffer()
|
||||
),
|
||||
mimeType: mime_type,
|
||||
}
|
||||
}
|
||||
|
@ -10,7 +10,7 @@ import {
|
||||
import { convertMessageToWhatsAppMessage } from './convertMessageToWhatsAppMessage'
|
||||
import { sendWhatsAppMessage } from './sendWhatsAppMessage'
|
||||
import * as Sentry from '@sentry/nextjs'
|
||||
import { HTTPError } from 'got'
|
||||
import { HTTPError } from 'ky'
|
||||
import { convertInputToWhatsAppMessages } from './convertInputToWhatsAppMessage'
|
||||
import { isNotDefined } from '@typebot.io/lib/utils'
|
||||
import { computeTypingDuration } from '../computeTypingDuration'
|
||||
@ -141,7 +141,7 @@ export const sendChatReplyToWhatsApp = async ({
|
||||
Sentry.captureException(err, { extra: { message } })
|
||||
console.log('Failed to send message:', JSON.stringify(message, null, 2))
|
||||
if (err instanceof HTTPError)
|
||||
console.log('HTTPError', err.response.statusCode, err.response.body)
|
||||
console.log('HTTPError', err.response.status, await err.response.text())
|
||||
}
|
||||
}
|
||||
|
||||
@ -172,7 +172,11 @@ export const sendChatReplyToWhatsApp = async ({
|
||||
Sentry.captureException(err, { extra: { message } })
|
||||
console.log('Failed to send message:', JSON.stringify(message, null, 2))
|
||||
if (err instanceof HTTPError)
|
||||
console.log('HTTPError', err.response.statusCode, err.response.body)
|
||||
console.log(
|
||||
'HTTPError',
|
||||
err.response.status,
|
||||
await err.response.text()
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -253,7 +257,11 @@ const executeClientSideAction =
|
||||
Sentry.captureException(err, { extra: { message } })
|
||||
console.log('Failed to send message:', JSON.stringify(message, null, 2))
|
||||
if (err instanceof HTTPError)
|
||||
console.log('HTTPError', err.response.statusCode, err.response.body)
|
||||
console.log(
|
||||
'HTTPError',
|
||||
err.response.status,
|
||||
await err.response.text()
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3,7 +3,7 @@ import {
|
||||
WhatsAppSendingMessage,
|
||||
} from '@typebot.io/schemas/features/whatsapp'
|
||||
import { env } from '@typebot.io/env'
|
||||
import got from 'got'
|
||||
import ky from 'ky'
|
||||
|
||||
type Props = {
|
||||
to: string
|
||||
@ -16,7 +16,7 @@ export const sendWhatsAppMessage = async ({
|
||||
message,
|
||||
credentials,
|
||||
}: Props) =>
|
||||
got.post(
|
||||
ky.post(
|
||||
`${env.WHATSAPP_CLOUD_API_URL}/v17.0/${credentials.phoneNumberId}/messages`,
|
||||
{
|
||||
headers: {
|
||||
|
@ -1,5 +1,6 @@
|
||||
import { createAction, option } from '@typebot.io/forge'
|
||||
import { auth } from '../auth'
|
||||
import { Anthropic } from '@anthropic-ai/sdk'
|
||||
import { AnthropicStream } from 'ai'
|
||||
import { anthropicModels, defaultAnthropicOptions } from '../constants'
|
||||
import { parseChatMessages } from '../helpers/parseChatMessages'
|
||||
@ -103,8 +104,6 @@ export const createChatMessage = createAction({
|
||||
responseMapping?.map((res) => res.variableId).filter(isDefined) ?? [],
|
||||
run: {
|
||||
server: async ({ credentials: { apiKey }, options, variables, logs }) => {
|
||||
const { Anthropic } = await import('@anthropic-ai/sdk')
|
||||
|
||||
const client = new Anthropic({
|
||||
apiKey: apiKey,
|
||||
})
|
||||
@ -150,8 +149,6 @@ export const createChatMessage = createAction({
|
||||
(res) => res.item === 'Message Content' || !res.item
|
||||
)?.variableId,
|
||||
run: async ({ credentials: { apiKey }, options, variables }) => {
|
||||
const { Anthropic } = await import('@anthropic-ai/sdk')
|
||||
|
||||
const client = new Anthropic({
|
||||
apiKey: apiKey,
|
||||
})
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { createAction, option } from '@typebot.io/forge'
|
||||
import { isDefined, isEmpty } from '@typebot.io/lib'
|
||||
import { HTTPError, got } from 'got'
|
||||
import ky, { HTTPError } from 'ky'
|
||||
import { apiBaseUrl } from '../constants'
|
||||
import { auth } from '../auth'
|
||||
import { ChatNodeResponse } from '../types'
|
||||
@ -40,7 +40,7 @@ export const sendMessage = createAction({
|
||||
logs,
|
||||
}) => {
|
||||
try {
|
||||
const res: ChatNodeResponse = await got
|
||||
const res: ChatNodeResponse = await ky
|
||||
.post(apiBaseUrl + botId, {
|
||||
headers: {
|
||||
Authorization: `Bearer ${apiKey}`,
|
||||
@ -66,7 +66,7 @@ export const sendMessage = createAction({
|
||||
return logs.add({
|
||||
status: 'error',
|
||||
description: error.message,
|
||||
details: error.response.body,
|
||||
details: await error.response.text(),
|
||||
})
|
||||
console.error(error)
|
||||
}
|
||||
|
@ -11,6 +11,6 @@
|
||||
"@typebot.io/tsconfig": "workspace:*",
|
||||
"@types/react": "18.2.15",
|
||||
"typescript": "5.3.2",
|
||||
"got": "12.6.0"
|
||||
"ky": "1.2.3"
|
||||
}
|
||||
}
|
||||
|
@ -1,9 +1,9 @@
|
||||
import { createAction, option } from '@typebot.io/forge'
|
||||
import { isDefined, isEmpty, isNotEmpty } from '@typebot.io/lib'
|
||||
import { HTTPError, got } from 'got'
|
||||
import { auth } from '../auth'
|
||||
import { defaultBaseUrl } from '../constants'
|
||||
import { Chunk } from '../types'
|
||||
import ky from 'ky'
|
||||
|
||||
export const createChatMessage = createAction({
|
||||
auth,
|
||||
@ -44,13 +44,15 @@ export const createChatMessage = createAction({
|
||||
logs,
|
||||
}) => {
|
||||
try {
|
||||
const stream = got.post(
|
||||
const response = await ky(
|
||||
(apiEndpoint ?? defaultBaseUrl) + '/v1/chat-messages',
|
||||
{
|
||||
method: 'POST',
|
||||
headers: {
|
||||
Authorization: `Bearer ${apiKey}`,
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
json: {
|
||||
body: JSON.stringify({
|
||||
inputs:
|
||||
inputs?.reduce((acc, { key, value }) => {
|
||||
if (isEmpty(key) || isEmpty(value)) return acc
|
||||
@ -64,56 +66,70 @@ export const createChatMessage = createAction({
|
||||
conversation_id,
|
||||
user,
|
||||
files: [],
|
||||
},
|
||||
isStream: true,
|
||||
}),
|
||||
}
|
||||
)
|
||||
|
||||
const reader = response.body?.getReader()
|
||||
|
||||
if (!reader)
|
||||
return logs.add({
|
||||
status: 'error',
|
||||
description: 'Failed to read response stream',
|
||||
})
|
||||
|
||||
const { answer, conversationId, totalTokens } = await new Promise<{
|
||||
answer: string
|
||||
conversationId: string | undefined
|
||||
totalTokens: number | undefined
|
||||
}>((resolve, reject) => {
|
||||
}>(async (resolve, reject) => {
|
||||
let jsonChunk = ''
|
||||
let answer = ''
|
||||
let conversationId: string | undefined
|
||||
let totalTokens: number | undefined
|
||||
|
||||
stream.on('data', (chunk) => {
|
||||
const lines = chunk.toString().split('\n') as string[]
|
||||
lines
|
||||
.filter((line) => line.length > 0 && line !== '\n')
|
||||
.forEach((line) => {
|
||||
jsonChunk += line
|
||||
if (jsonChunk.startsWith('event: ')) {
|
||||
try {
|
||||
while (true) {
|
||||
const { value, done } = await reader.read()
|
||||
if (done) {
|
||||
resolve({ answer, conversationId, totalTokens })
|
||||
return
|
||||
}
|
||||
|
||||
const chunk = new TextDecoder().decode(value)
|
||||
|
||||
const lines = chunk.toString().split('\n') as string[]
|
||||
lines
|
||||
.filter((line) => line.length > 0 && line !== '\n')
|
||||
.forEach((line) => {
|
||||
jsonChunk += line
|
||||
if (jsonChunk.startsWith('event: ')) {
|
||||
jsonChunk = ''
|
||||
return
|
||||
}
|
||||
if (
|
||||
!jsonChunk.startsWith('data: ') ||
|
||||
!jsonChunk.endsWith('}')
|
||||
)
|
||||
return
|
||||
|
||||
const data = JSON.parse(jsonChunk.slice(6)) as Chunk
|
||||
jsonChunk = ''
|
||||
return
|
||||
}
|
||||
if (!jsonChunk.startsWith('data: ') || !jsonChunk.endsWith('}'))
|
||||
return
|
||||
|
||||
const data = JSON.parse(jsonChunk.slice(6)) as Chunk
|
||||
jsonChunk = ''
|
||||
if (
|
||||
data.event === 'message' ||
|
||||
data.event === 'agent_message'
|
||||
) {
|
||||
answer += data.answer
|
||||
}
|
||||
if (data.event === 'message_end') {
|
||||
totalTokens = data.metadata.usage.total_tokens
|
||||
conversationId = data.conversation_id
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
stream.on('end', () => {
|
||||
resolve({ answer, conversationId, totalTokens })
|
||||
})
|
||||
|
||||
stream.on('error', (error) => {
|
||||
reject(error)
|
||||
})
|
||||
if (
|
||||
data.event === 'message' ||
|
||||
data.event === 'agent_message'
|
||||
) {
|
||||
answer += data.answer
|
||||
}
|
||||
if (data.event === 'message_end') {
|
||||
totalTokens = data.metadata.usage.total_tokens
|
||||
conversationId = data.conversation_id
|
||||
}
|
||||
})
|
||||
}
|
||||
} catch (e) {
|
||||
reject(e)
|
||||
}
|
||||
})
|
||||
|
||||
responseMapping?.forEach((mapping) => {
|
||||
@ -130,12 +146,10 @@ export const createChatMessage = createAction({
|
||||
variables.set(mapping.variableId, totalTokens)
|
||||
})
|
||||
} catch (error) {
|
||||
if (error instanceof HTTPError)
|
||||
return logs.add({
|
||||
status: 'error',
|
||||
description: error.message,
|
||||
details: error.response.body,
|
||||
})
|
||||
logs.add({
|
||||
status: 'error',
|
||||
description: 'Failed to create chat message',
|
||||
})
|
||||
console.error(error)
|
||||
}
|
||||
},
|
||||
|
@ -10,7 +10,7 @@
|
||||
"@typebot.io/lib": "workspace:*",
|
||||
"@typebot.io/tsconfig": "workspace:*",
|
||||
"@types/react": "18.2.15",
|
||||
"got": "12.6.0",
|
||||
"ky": "1.2.3",
|
||||
"typescript": "5.3.2"
|
||||
}
|
||||
}
|
||||
|
@ -2,7 +2,7 @@ import { createAction, option } from '@typebot.io/forge'
|
||||
import { auth } from '../auth'
|
||||
import { baseUrl } from '../constants'
|
||||
import { ModelsResponse, VoicesResponse } from '../type'
|
||||
import got, { HTTPError } from 'got'
|
||||
import got, { HTTPError } from 'ky'
|
||||
import { uploadFileToBucket } from '@typebot.io/lib/s3/uploadFileToBucket'
|
||||
import { createId } from '@typebot.io/lib/createId'
|
||||
|
||||
@ -93,10 +93,10 @@ export const convertTextToSpeech = createAction({
|
||||
text: options.text,
|
||||
},
|
||||
})
|
||||
.buffer()
|
||||
.arrayBuffer()
|
||||
|
||||
const url = await uploadFileToBucket({
|
||||
file: response,
|
||||
file: Buffer.from(response),
|
||||
key: `tmp/elevenlabs/audio/${createId() + createId()}.mp3`,
|
||||
mimeType: 'audio/mpeg',
|
||||
})
|
||||
@ -107,7 +107,7 @@ export const convertTextToSpeech = createAction({
|
||||
return logs.add({
|
||||
status: 'error',
|
||||
description: err.message,
|
||||
details: err.response.body,
|
||||
details: await err.response.text(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -13,7 +13,7 @@
|
||||
"typescript": "5.3.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"got": "12.6.0",
|
||||
"ky": "1.2.3",
|
||||
"@typebot.io/lib": "workspace:*"
|
||||
}
|
||||
}
|
||||
|
@ -3,6 +3,8 @@ import { isDefined } from '@typebot.io/lib'
|
||||
import { auth } from '../auth'
|
||||
import { parseMessages } from '../helpers/parseMessages'
|
||||
import { OpenAIStream } from 'ai'
|
||||
// @ts-ignore
|
||||
import MistralClient from '../helpers/client'
|
||||
|
||||
const nativeMessageContentSchema = {
|
||||
content: option.string.layout({
|
||||
@ -95,15 +97,17 @@ export const createChatCompletion = createAction({
|
||||
id: 'fetchModels',
|
||||
dependencies: [],
|
||||
fetch: async ({ credentials }) => {
|
||||
const MistralClient = (await import('@mistralai/mistralai')).default
|
||||
const client = new MistralClient(credentials.apiKey)
|
||||
|
||||
const listModelsResponse = await client.listModels()
|
||||
const listModelsResponse: any = await client.listModels()
|
||||
|
||||
return (
|
||||
listModelsResponse.data
|
||||
.sort((a, b) => b.created - a.created)
|
||||
.map((model) => model.id) ?? []
|
||||
.sort(
|
||||
(a: { created: number }, b: { created: number }) =>
|
||||
b.created - a.created
|
||||
)
|
||||
.map((model: { id: any }) => model.id) ?? []
|
||||
)
|
||||
},
|
||||
},
|
||||
@ -111,10 +115,9 @@ export const createChatCompletion = createAction({
|
||||
run: {
|
||||
server: async ({ credentials: { apiKey }, options, variables, logs }) => {
|
||||
if (!options.model) return logs.add('No model selected')
|
||||
const MistralClient = (await import('@mistralai/mistralai')).default
|
||||
const client = new MistralClient(apiKey)
|
||||
|
||||
const response = await client.chat({
|
||||
const response: any = await client.chat({
|
||||
model: options.model,
|
||||
messages: parseMessages({ options, variables }),
|
||||
})
|
||||
@ -132,15 +135,13 @@ export const createChatCompletion = createAction({
|
||||
)?.variableId,
|
||||
run: async ({ credentials: { apiKey }, options, variables }) => {
|
||||
if (!options.model) return
|
||||
const MistralClient = (await import('@mistralai/mistralai')).default
|
||||
const client = new MistralClient(apiKey)
|
||||
|
||||
const response = client.chatStream({
|
||||
const response: any = client.chatStream({
|
||||
model: options.model,
|
||||
messages: parseMessages({ options, variables }),
|
||||
})
|
||||
|
||||
// @ts-ignore https://github.com/vercel/ai/issues/936
|
||||
return OpenAIStream(response)
|
||||
},
|
||||
},
|
||||
|
341
packages/forge/blocks/mistral/helpers/client.js
Normal file
341
packages/forge/blocks/mistral/helpers/client.js
Normal file
@ -0,0 +1,341 @@
|
||||
// Taken from https://github.com/mistralai/client-js/blob/main/src/client.js
|
||||
// Lib seems not actively maintained, and we need this patch: https://github.com/mistralai/client-js/pull/42
|
||||
|
||||
let isNode = false
|
||||
|
||||
let fetch
|
||||
|
||||
const VERSION = '0.0.3'
|
||||
const RETRY_STATUS_CODES = [429, 500, 502, 503, 504]
|
||||
const ENDPOINT = 'https://api.mistral.ai'
|
||||
|
||||
/**
|
||||
* Initialize fetch
|
||||
* @return {Promise<void>}
|
||||
*/
|
||||
async function initializeFetch() {
|
||||
if (typeof globalThis.fetch === 'undefined')
|
||||
throw new Error('No fetch implementation found')
|
||||
if (typeof window === 'undefined') {
|
||||
isNode = true
|
||||
}
|
||||
fetch = globalThis.fetch
|
||||
}
|
||||
|
||||
initializeFetch()
|
||||
|
||||
/**
|
||||
* MistralAPIError
|
||||
* @return {MistralAPIError}
|
||||
* @extends {Error}
|
||||
*/
|
||||
class MistralAPIError extends Error {
|
||||
/**
|
||||
* A simple error class for Mistral API errors
|
||||
* @param {*} message
|
||||
*/
|
||||
constructor(message) {
|
||||
super(message)
|
||||
this.name = 'MistralAPIError'
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* MistralClient
|
||||
* @return {MistralClient}
|
||||
*/
|
||||
class MistralClient {
|
||||
/**
|
||||
* A simple and lightweight client for the Mistral API
|
||||
* @param {*} apiKey can be set as an environment variable MISTRAL_API_KEY,
|
||||
* or provided in this parameter
|
||||
* @param {*} endpoint defaults to https://api.mistral.ai
|
||||
* @param {*} maxRetries defaults to 5
|
||||
* @param {*} timeout defaults to 120 seconds
|
||||
*/
|
||||
constructor(
|
||||
apiKey = process.env.MISTRAL_API_KEY,
|
||||
endpoint = ENDPOINT,
|
||||
maxRetries = 5,
|
||||
timeout = 120
|
||||
) {
|
||||
this.endpoint = endpoint
|
||||
this.apiKey = apiKey
|
||||
|
||||
this.maxRetries = maxRetries
|
||||
this.timeout = timeout
|
||||
|
||||
if (this.endpoint.indexOf('inference.azure.com')) {
|
||||
this.modelDefault = 'mistral'
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {*} method
|
||||
* @param {*} path
|
||||
* @param {*} request
|
||||
* @return {Promise<*>}
|
||||
*/
|
||||
_request = async function (method, path, request) {
|
||||
const url = `${this.endpoint}/${path}`
|
||||
const options = {
|
||||
method: method,
|
||||
headers: {
|
||||
'User-Agent': `mistral-client-js/${VERSION}`,
|
||||
Accept: request?.stream ? 'text/event-stream' : 'application/json',
|
||||
'Content-Type': 'application/json',
|
||||
Authorization: `Bearer ${this.apiKey}`,
|
||||
},
|
||||
body: method !== 'get' ? JSON.stringify(request) : null,
|
||||
timeout: this.timeout * 1000,
|
||||
}
|
||||
|
||||
for (let attempts = 0; attempts < this.maxRetries; attempts++) {
|
||||
try {
|
||||
const response = await fetch(url, options)
|
||||
|
||||
if (response.ok) {
|
||||
if (request?.stream) {
|
||||
if (isNode) {
|
||||
return response.body
|
||||
} else {
|
||||
const reader = response.body.getReader()
|
||||
// Chrome does not support async iterators yet, so polyfill it
|
||||
const asyncIterator = async function* () {
|
||||
try {
|
||||
while (true) {
|
||||
// Read from the stream
|
||||
const { done, value } = await reader.read()
|
||||
// Exit if we're done
|
||||
if (done) return
|
||||
// Else yield the chunk
|
||||
yield value
|
||||
}
|
||||
} finally {
|
||||
reader.releaseLock()
|
||||
}
|
||||
}
|
||||
|
||||
return asyncIterator()
|
||||
}
|
||||
}
|
||||
return await response.json()
|
||||
} else if (RETRY_STATUS_CODES.includes(response.status)) {
|
||||
console.debug(
|
||||
`Retrying request on response status: ${response.status}`,
|
||||
`Response: ${await response.text()}`,
|
||||
`Attempt: ${attempts + 1}`
|
||||
)
|
||||
// eslint-disable-next-line max-len
|
||||
await new Promise((resolve) =>
|
||||
setTimeout(resolve, Math.pow(2, attempts + 1) * 500)
|
||||
)
|
||||
} else {
|
||||
throw new MistralAPIError(
|
||||
`HTTP error! status: ${response.status} ` +
|
||||
`Response: \n${await response.text()}`
|
||||
)
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(`Request failed: ${error.message}`)
|
||||
if (error.name === 'MistralAPIError') {
|
||||
throw error
|
||||
}
|
||||
if (attempts === this.maxRetries - 1) throw error
|
||||
// eslint-disable-next-line max-len
|
||||
await new Promise((resolve) =>
|
||||
setTimeout(resolve, Math.pow(2, attempts + 1) * 500)
|
||||
)
|
||||
}
|
||||
}
|
||||
throw new Error('Max retries reached')
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a chat completion request
|
||||
* @param {*} model
|
||||
* @param {*} messages
|
||||
* @param {*} tools
|
||||
* @param {*} temperature
|
||||
* @param {*} maxTokens
|
||||
* @param {*} topP
|
||||
* @param {*} randomSeed
|
||||
* @param {*} stream
|
||||
* @param {*} safeMode deprecated use safePrompt instead
|
||||
* @param {*} safePrompt
|
||||
* @param {*} toolChoice
|
||||
* @param {*} responseFormat
|
||||
* @return {Promise<Object>}
|
||||
*/
|
||||
_makeChatCompletionRequest = function (
|
||||
model,
|
||||
messages,
|
||||
tools,
|
||||
temperature,
|
||||
maxTokens,
|
||||
topP,
|
||||
randomSeed,
|
||||
stream,
|
||||
safeMode,
|
||||
safePrompt,
|
||||
toolChoice,
|
||||
responseFormat
|
||||
) {
|
||||
// if modelDefault and model are undefined, throw an error
|
||||
if (!model && !this.modelDefault) {
|
||||
throw new MistralAPIError('You must provide a model name')
|
||||
}
|
||||
return {
|
||||
model: model ?? this.modelDefault,
|
||||
messages: messages,
|
||||
tools: tools ?? undefined,
|
||||
temperature: temperature ?? undefined,
|
||||
max_tokens: maxTokens ?? undefined,
|
||||
top_p: topP ?? undefined,
|
||||
random_seed: randomSeed ?? undefined,
|
||||
stream: stream ?? undefined,
|
||||
safe_prompt: (safeMode || safePrompt) ?? undefined,
|
||||
tool_choice: toolChoice ?? undefined,
|
||||
response_format: responseFormat ?? undefined,
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of the available models
|
||||
* @return {Promise<Object>}
|
||||
*/
|
||||
listModels = async function () {
|
||||
const response = await this._request('get', 'v1/models')
|
||||
return response
|
||||
}
|
||||
|
||||
/**
|
||||
* A chat endpoint without streaming
|
||||
* @param {*} model the name of the model to chat with, e.g. mistral-tiny
|
||||
* @param {*} messages an array of messages to chat with, e.g.
|
||||
* [{role: 'user', content: 'What is the best French cheese?'}]
|
||||
* @param {*} tools a list of tools to use.
|
||||
* @param {*} temperature the temperature to use for sampling, e.g. 0.5
|
||||
* @param {*} maxTokens the maximum number of tokens to generate, e.g. 100
|
||||
* @param {*} topP the cumulative probability of tokens to generate, e.g. 0.9
|
||||
* @param {*} randomSeed the random seed to use for sampling, e.g. 42
|
||||
* @param {*} safeMode deprecated use safePrompt instead
|
||||
* @param {*} safePrompt whether to use safe mode, e.g. true
|
||||
* @param {*} toolChoice the tool to use, e.g. 'auto'
|
||||
* @param {*} responseFormat the format of the response, e.g. 'json_format'
|
||||
* @return {Promise<Object>}
|
||||
*/
|
||||
chat = async function ({
|
||||
model,
|
||||
messages,
|
||||
tools,
|
||||
temperature,
|
||||
maxTokens,
|
||||
topP,
|
||||
randomSeed,
|
||||
safeMode,
|
||||
safePrompt,
|
||||
toolChoice,
|
||||
responseFormat,
|
||||
}) {
|
||||
const request = this._makeChatCompletionRequest(
|
||||
model,
|
||||
messages,
|
||||
tools,
|
||||
temperature,
|
||||
maxTokens,
|
||||
topP,
|
||||
randomSeed,
|
||||
false,
|
||||
safeMode,
|
||||
safePrompt,
|
||||
toolChoice,
|
||||
responseFormat
|
||||
)
|
||||
const response = await this._request('post', 'v1/chat/completions', request)
|
||||
return response
|
||||
}
|
||||
|
||||
/**
|
||||
* A chat endpoint that streams responses.
|
||||
* @param {*} model the name of the model to chat with, e.g. mistral-tiny
|
||||
* @param {*} messages an array of messages to chat with, e.g.
|
||||
* [{role: 'user', content: 'What is the best French cheese?'}]
|
||||
* @param {*} tools a list of tools to use.
|
||||
* @param {*} temperature the temperature to use for sampling, e.g. 0.5
|
||||
* @param {*} maxTokens the maximum number of tokens to generate, e.g. 100
|
||||
* @param {*} topP the cumulative probability of tokens to generate, e.g. 0.9
|
||||
* @param {*} randomSeed the random seed to use for sampling, e.g. 42
|
||||
* @param {*} safeMode deprecated use safePrompt instead
|
||||
* @param {*} safePrompt whether to use safe mode, e.g. true
|
||||
* @param {*} toolChoice the tool to use, e.g. 'auto'
|
||||
* @param {*} responseFormat the format of the response, e.g. 'json_format'
|
||||
* @return {Promise<Object>}
|
||||
*/
|
||||
chatStream = async function* ({
|
||||
model,
|
||||
messages,
|
||||
tools,
|
||||
temperature,
|
||||
maxTokens,
|
||||
topP,
|
||||
randomSeed,
|
||||
safeMode,
|
||||
safePrompt,
|
||||
toolChoice,
|
||||
responseFormat,
|
||||
}) {
|
||||
const request = this._makeChatCompletionRequest(
|
||||
model,
|
||||
messages,
|
||||
tools,
|
||||
temperature,
|
||||
maxTokens,
|
||||
topP,
|
||||
randomSeed,
|
||||
true,
|
||||
safeMode,
|
||||
safePrompt,
|
||||
toolChoice,
|
||||
responseFormat
|
||||
)
|
||||
const response = await this._request('post', 'v1/chat/completions', request)
|
||||
|
||||
let buffer = ''
|
||||
const decoder = new TextDecoder()
|
||||
for await (const chunk of response) {
|
||||
buffer += decoder.decode(chunk, { stream: true })
|
||||
let firstNewline
|
||||
while ((firstNewline = buffer.indexOf('\n')) !== -1) {
|
||||
const chunkLine = buffer.substring(0, firstNewline)
|
||||
buffer = buffer.substring(firstNewline + 1)
|
||||
if (chunkLine.startsWith('data:')) {
|
||||
const json = chunkLine.substring(6).trim()
|
||||
if (json !== '[DONE]') {
|
||||
yield JSON.parse(json)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An embeddings endpoint that returns embeddings for a single,
|
||||
* or batch of inputs
|
||||
* @param {*} model The embedding model to use, e.g. mistral-embed
|
||||
* @param {*} input The input to embed,
|
||||
* e.g. ['What is the best French cheese?']
|
||||
* @return {Promise<Object>}
|
||||
*/
|
||||
embeddings = async function ({ model, input }) {
|
||||
const request = {
|
||||
model: model,
|
||||
input: input,
|
||||
}
|
||||
const response = await this._request('post', 'v1/embeddings', request)
|
||||
return response
|
||||
}
|
||||
}
|
||||
|
||||
export default MistralClient
|
@ -9,11 +9,11 @@
|
||||
"@typebot.io/forge": "workspace:*",
|
||||
"@typebot.io/lib": "workspace:*",
|
||||
"@typebot.io/tsconfig": "workspace:*",
|
||||
"@types/node": "^20.12.4",
|
||||
"@types/react": "18.2.15",
|
||||
"typescript": "5.3.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"@mistralai/mistralai": "0.1.3",
|
||||
"ai": "3.0.12"
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"extends": "@typebot.io/tsconfig/base.json",
|
||||
"include": ["**/*.ts", "**/*.tsx"],
|
||||
"include": ["**/*.ts", "**/*.tsx", "helpers/client.ts"],
|
||||
"exclude": ["node_modules"],
|
||||
"compilerOptions": {
|
||||
"lib": ["ESNext", "DOM"],
|
||||
|
@ -6,7 +6,7 @@ import { getChatCompletionStreamVarId } from '@typebot.io/openai-block/shared/ge
|
||||
import { runChatCompletion } from '@typebot.io/openai-block/shared/runChatCompletion'
|
||||
import { runChatCompletionStream } from '@typebot.io/openai-block/shared/runChatCompletionStream'
|
||||
import { defaultOpenRouterOptions } from '../constants'
|
||||
import { got } from 'got'
|
||||
import ky from 'ky'
|
||||
import { ModelsResponse } from '../types'
|
||||
|
||||
export const createChatCompletion = createAction({
|
||||
@ -42,7 +42,7 @@ export const createChatCompletion = createAction({
|
||||
id: 'fetchModels',
|
||||
dependencies: [],
|
||||
fetch: async () => {
|
||||
const response = await got
|
||||
const response = await ky
|
||||
.get(defaultOpenRouterOptions.baseUrl + '/models')
|
||||
.json<ModelsResponse>()
|
||||
|
||||
|
@ -12,6 +12,6 @@
|
||||
"typescript": "5.3.2",
|
||||
"@typebot.io/lib": "workspace:*",
|
||||
"@typebot.io/openai-block": "workspace:*",
|
||||
"got": "12.6.0"
|
||||
"ky": "1.2.3"
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
import { createAction, option } from '@typebot.io/forge'
|
||||
import { toBuffer as generateQrCodeBuffer } from 'qrcode'
|
||||
import { uploadFileToBucket } from '@typebot.io/lib/s3/uploadFileToBucket'
|
||||
import { createId } from '@typebot.io/lib/createId'
|
||||
|
||||
@ -28,8 +29,6 @@ export const generateQrCode = createAction({
|
||||
'QR code image URL is not specified. Please select a variable to save the generated QR code image.'
|
||||
)
|
||||
|
||||
const generateQrCodeBuffer = (await import('qrcode')).toBuffer
|
||||
|
||||
const url = await uploadFileToBucket({
|
||||
file: await generateQrCodeBuffer(options.data),
|
||||
key: `tmp/qrcodes/${createId() + createId()}.png`,
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { createAction, option } from '@typebot.io/forge'
|
||||
import { isDefined } from '@typebot.io/lib'
|
||||
import { ZemanticAiResponse } from '../types'
|
||||
import { got } from 'got'
|
||||
import ky from 'ky'
|
||||
import { apiBaseUrl } from '../constants'
|
||||
import { auth } from '../auth'
|
||||
import { baseOptions } from '../baseOptions'
|
||||
@ -63,7 +63,7 @@ export const searchDocuments = createAction({
|
||||
},
|
||||
variables,
|
||||
}) => {
|
||||
const res: ZemanticAiResponse = await got
|
||||
const res = await ky
|
||||
.post(apiBaseUrl, {
|
||||
headers: {
|
||||
Authorization: `Bearer ${apiKey}`,
|
||||
@ -79,7 +79,7 @@ export const searchDocuments = createAction({
|
||||
},
|
||||
},
|
||||
})
|
||||
.json()
|
||||
.json<ZemanticAiResponse>()
|
||||
|
||||
responseMapping?.forEach((mapping) => {
|
||||
if (!mapping.variableId || !mapping.item) return
|
||||
|
@ -8,6 +8,7 @@ export const auth = {
|
||||
label: 'API key',
|
||||
isRequired: true,
|
||||
placeholder: 'ze...',
|
||||
inputType: 'password',
|
||||
helperText:
|
||||
'You can generate an API key [here](https://zemantic.ai/dashboard/settings).',
|
||||
isDebounceDisabled: true,
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { createBlock } from '@typebot.io/forge'
|
||||
import { ZemanticAiLogo } from './logo'
|
||||
import { got } from 'got'
|
||||
import ky from 'ky'
|
||||
import { searchDocuments } from './actions/searchDocuments'
|
||||
import { auth } from './auth'
|
||||
import { baseOptions } from './baseOptions'
|
||||
@ -19,7 +19,7 @@ export const zemanticAiBlock = createBlock({
|
||||
fetch: async ({ credentials: { apiKey } }) => {
|
||||
const url = 'https://api.zemantic.ai/v1/projects'
|
||||
|
||||
const response = await got
|
||||
const response = await ky
|
||||
.get(url, {
|
||||
headers: {
|
||||
Authorization: `Bearer ${apiKey}`,
|
||||
|
@ -11,6 +11,6 @@
|
||||
"@types/react": "18.2.15",
|
||||
"typescript": "5.3.2",
|
||||
"@typebot.io/lib": "workspace:*",
|
||||
"got": "12.6.0"
|
||||
"ky": "1.2.3"
|
||||
}
|
||||
}
|
||||
|
@ -38,7 +38,7 @@
|
||||
"@udecode/plate-paragraph": "30.5.3",
|
||||
"escape-html": "1.0.3",
|
||||
"google-auth-library": "8.9.0",
|
||||
"got": "12.6.0",
|
||||
"ky": "1.2.3",
|
||||
"minio": "7.1.3",
|
||||
"posthog-node": "3.1.1",
|
||||
"remark-parse": "11.0.0",
|
||||
|
@ -1,8 +1,7 @@
|
||||
import { PrismaClient } from '@typebot.io/prisma'
|
||||
import { promptAndSetEnvironment } from './utils'
|
||||
import got, { HTTPError } from 'got'
|
||||
import ky, { HTTPError } from 'ky'
|
||||
import { confirm, text, isCancel } from '@clack/prompts'
|
||||
import { writeFileSync } from 'fs'
|
||||
|
||||
const insertUsersInBrevoList = async () => {
|
||||
await promptAndSetEnvironment('production')
|
||||
@ -44,7 +43,7 @@ const insertUsersInBrevoList = async () => {
|
||||
}
|
||||
|
||||
try {
|
||||
await got.post('https://api.brevo.com/v3/contacts/import', {
|
||||
await ky.post('https://api.brevo.com/v3/contacts/import', {
|
||||
headers: {
|
||||
'api-key': process.env.BREVO_API_KEY,
|
||||
},
|
||||
@ -58,7 +57,7 @@ const insertUsersInBrevoList = async () => {
|
||||
})
|
||||
} catch (err) {
|
||||
if (err instanceof HTTPError) {
|
||||
console.log(err.response.body)
|
||||
console.log(await err.response.text())
|
||||
return
|
||||
}
|
||||
console.log(err)
|
||||
|
@ -42,7 +42,7 @@
|
||||
"@types/papaparse": "5.3.7",
|
||||
"@types/prompts": "2.4.4",
|
||||
"deep-object-diff": "1.1.9",
|
||||
"got": "12.6.0",
|
||||
"ky": "1.2.3",
|
||||
"prompts": "2.4.2",
|
||||
"stripe": "12.13.0",
|
||||
"tsx": "3.12.7",
|
||||
|
@ -8,7 +8,7 @@
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@typebot.io/schemas": "workspace:*",
|
||||
"got": "12.6.0",
|
||||
"ky": "1.2.3",
|
||||
"posthog-node": "3.1.1",
|
||||
"@typebot.io/env": "workspace:*"
|
||||
},
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { env } from '@typebot.io/env'
|
||||
import { TelemetryEvent } from '@typebot.io/schemas/features/telemetry'
|
||||
import { PostHog } from 'posthog-node'
|
||||
import got from 'got'
|
||||
import ky from 'ky'
|
||||
|
||||
export const trackEvents = async (events: TelemetryEvent[]) => {
|
||||
if (!env.NEXT_PUBLIC_POSTHOG_KEY) return
|
||||
@ -17,7 +17,7 @@ export const trackEvents = async (events: TelemetryEvent[]) => {
|
||||
})
|
||||
if (env.USER_CREATED_WEBHOOK_URL) {
|
||||
try {
|
||||
await got.post(env.USER_CREATED_WEBHOOK_URL, {
|
||||
await ky.post(env.USER_CREATED_WEBHOOK_URL, {
|
||||
json: {
|
||||
email: event.data.email,
|
||||
name: event.data.name ? event.data.name.split(' ')[0] : undefined,
|
||||
|
Reference in New Issue
Block a user