✨ Add Zemantic AI Integration block (#752)
Co-authored-by: Baptiste Arnaud <contact@baptiste-arnaud.fr>
This commit is contained in:
@@ -99,9 +99,10 @@ export const NumberInput = <HasVariable extends boolean>({
|
||||
isRequired={isRequired}
|
||||
justifyContent="space-between"
|
||||
width={label ? 'full' : 'auto'}
|
||||
spacing={0}
|
||||
>
|
||||
{label && (
|
||||
<FormLabel mb="0" flexShrink={0}>
|
||||
<FormLabel mb="2" flexShrink={0}>
|
||||
{label}{' '}
|
||||
{moreInfoTooltip && (
|
||||
<MoreInfoTooltip>{moreInfoTooltip}</MoreInfoTooltip>
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
import { Select } from '@/components/inputs/Select'
|
||||
import { useTypebot } from '@/features/editor/providers/TypebotProvider'
|
||||
import { useWorkspace } from '@/features/workspace/WorkspaceProvider'
|
||||
import { useToast } from '@/hooks/useToast'
|
||||
import { trpc } from '@/lib/trpc'
|
||||
|
||||
type Props = {
|
||||
credentialsId: string
|
||||
blockId: string
|
||||
defaultValue: string
|
||||
onChange: (projectId: string | undefined) => void
|
||||
}
|
||||
|
||||
export const ProjectsDropdown = ({
|
||||
defaultValue,
|
||||
onChange,
|
||||
credentialsId,
|
||||
}: Props) => {
|
||||
const { typebot } = useTypebot()
|
||||
const { workspace } = useWorkspace()
|
||||
const { showToast } = useToast()
|
||||
|
||||
const { data } = trpc.zemanticAi.listProjects.useQuery(
|
||||
{
|
||||
credentialsId,
|
||||
workspaceId: workspace?.id as string,
|
||||
},
|
||||
{
|
||||
enabled: !!typebot && !!workspace,
|
||||
onError: (error) => {
|
||||
showToast({
|
||||
description: error.message,
|
||||
status: 'error',
|
||||
})
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
return (
|
||||
<Select
|
||||
items={data?.projects as { label: string; value: string }[]}
|
||||
selectedItem={defaultValue}
|
||||
onSelect={onChange}
|
||||
placeholder="Select a project"
|
||||
/>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
import { DropdownList } from '@/components/DropdownList'
|
||||
import { VariableSearchInput } from '@/components/inputs/VariableSearchInput'
|
||||
import { TableListItemProps } from '@/components/TableList'
|
||||
import { Stack } from '@chakra-ui/react'
|
||||
import { Variable } from '@typebot.io/schemas'
|
||||
import {
|
||||
ZemanticAiOptions,
|
||||
searchResponseValues,
|
||||
} from '@typebot.io/schemas/features/blocks/integrations/zemanticAi'
|
||||
|
||||
type Props = TableListItemProps<ZemanticAiOptions['responseMapping'][number]>
|
||||
|
||||
export const SearchResponseItem = ({ item, onItemChange }: Props) => {
|
||||
const changeValueToExtract = (
|
||||
valueToExtract: (typeof searchResponseValues)[number]
|
||||
) => {
|
||||
onItemChange({ ...item, valueToExtract })
|
||||
}
|
||||
|
||||
const changeVariableId = (variable: Pick<Variable, 'id'> | undefined) => {
|
||||
onItemChange({ ...item, variableId: variable ? variable.id : undefined })
|
||||
}
|
||||
|
||||
return (
|
||||
<Stack p="4" rounded="md" flex="1" borderWidth="1px">
|
||||
<DropdownList
|
||||
currentItem={item.valueToExtract ?? 'Summary'}
|
||||
items={searchResponseValues}
|
||||
onItemSelect={changeValueToExtract}
|
||||
/>
|
||||
<VariableSearchInput
|
||||
onSelectVariable={changeVariableId}
|
||||
initialVariableId={item.variableId}
|
||||
/>
|
||||
</Stack>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,125 @@
|
||||
import { TextInput } from '@/components/inputs/TextInput'
|
||||
import { TextLink } from '@/components/TextLink'
|
||||
import { useWorkspace } from '@/features/workspace/WorkspaceProvider'
|
||||
import { useToast } from '@/hooks/useToast'
|
||||
import { trpc } from '@/lib/trpc'
|
||||
import {
|
||||
Modal,
|
||||
ModalOverlay,
|
||||
ModalContent,
|
||||
ModalHeader,
|
||||
ModalCloseButton,
|
||||
ModalBody,
|
||||
Stack,
|
||||
ModalFooter,
|
||||
Button,
|
||||
} from '@chakra-ui/react'
|
||||
import React, { useState } from 'react'
|
||||
|
||||
const zemanticAIDashboardPage = 'https://zemantic.ai/dashboard/settings'
|
||||
|
||||
type Props = {
|
||||
isOpen: boolean
|
||||
onClose: () => void
|
||||
onNewCredentials: (id: string) => void
|
||||
}
|
||||
|
||||
export const ZemanticAiCredentialsModal = ({
|
||||
isOpen,
|
||||
onClose,
|
||||
onNewCredentials,
|
||||
}: Props) => {
|
||||
const { workspace } = useWorkspace()
|
||||
const { showToast } = useToast()
|
||||
const [apiKey, setApiKey] = useState('')
|
||||
const [name, setName] = useState('')
|
||||
|
||||
const [isCreating, setIsCreating] = useState(false)
|
||||
|
||||
const {
|
||||
credentials: {
|
||||
listCredentials: { refetch: refetchCredentials },
|
||||
},
|
||||
} = trpc.useContext()
|
||||
const { mutate } = trpc.credentials.createCredentials.useMutation({
|
||||
onMutate: () => setIsCreating(true),
|
||||
onSettled: () => setIsCreating(false),
|
||||
onError: (err) => {
|
||||
showToast({
|
||||
description: err.message,
|
||||
status: 'error',
|
||||
})
|
||||
},
|
||||
onSuccess: (data) => {
|
||||
refetchCredentials()
|
||||
onNewCredentials(data.credentialsId)
|
||||
onClose()
|
||||
},
|
||||
})
|
||||
|
||||
const createZemanticAiCredentials = async (e: React.FormEvent) => {
|
||||
e.preventDefault()
|
||||
if (!workspace) return
|
||||
mutate({
|
||||
credentials: {
|
||||
type: 'zemanticAi',
|
||||
workspaceId: workspace.id,
|
||||
name,
|
||||
data: {
|
||||
apiKey,
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
return (
|
||||
<Modal isOpen={isOpen} onClose={onClose} size="lg">
|
||||
<ModalOverlay />
|
||||
<ModalContent>
|
||||
<ModalHeader>Add Zemantic AI account</ModalHeader>
|
||||
<ModalCloseButton />
|
||||
<form onSubmit={createZemanticAiCredentials}>
|
||||
<ModalBody as={Stack} spacing="6">
|
||||
<TextInput
|
||||
isRequired
|
||||
label="Name"
|
||||
onChange={setName}
|
||||
placeholder="My account"
|
||||
withVariableButton={false}
|
||||
debounceTimeout={0}
|
||||
/>
|
||||
<TextInput
|
||||
isRequired
|
||||
type="password"
|
||||
label="API key"
|
||||
helperText={
|
||||
<>
|
||||
You can generate an API key{' '}
|
||||
<TextLink href={zemanticAIDashboardPage} isExternal>
|
||||
here
|
||||
</TextLink>
|
||||
.
|
||||
</>
|
||||
}
|
||||
onChange={setApiKey}
|
||||
placeholder="ze..."
|
||||
withVariableButton={false}
|
||||
debounceTimeout={0}
|
||||
/>
|
||||
</ModalBody>
|
||||
|
||||
<ModalFooter>
|
||||
<Button
|
||||
type="submit"
|
||||
isLoading={isCreating}
|
||||
isDisabled={apiKey === '' || name === ''}
|
||||
colorScheme="blue"
|
||||
>
|
||||
Create
|
||||
</Button>
|
||||
</ModalFooter>
|
||||
</form>
|
||||
</ModalContent>
|
||||
</Modal>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
import { Icon, IconProps } from '@chakra-ui/react'
|
||||
|
||||
export const ZemanticAiLogo = (props: IconProps) => (
|
||||
<Icon viewBox="0 0 24 24" {...props}>
|
||||
<g transform="matrix(.049281 0 0 .064343 -.27105 -3.4424)">
|
||||
<path
|
||||
d="m99.5 205.5v221h-94v-373h94v152z"
|
||||
fill="#8771b1"
|
||||
opacity=".991"
|
||||
/>
|
||||
<path
|
||||
d="m284.5 426.5v-221-152h94v373h-94z"
|
||||
fill="#f05b4e"
|
||||
opacity=".99"
|
||||
/>
|
||||
<path d="m99.5 205.5h93v221h-93v-221z" fill="#ec9896" />
|
||||
<path d="m192.5 205.5h92v221h-92v-221z" fill="#efe894" />
|
||||
<path d="m398.5 298.5h94v128h-94v-128z" fill="#46bb91" opacity=".989" />
|
||||
</g>
|
||||
</Icon>
|
||||
)
|
||||
@@ -0,0 +1,37 @@
|
||||
import React from 'react'
|
||||
import { Stack, Text } from '@chakra-ui/react'
|
||||
import { ZemanticAiOptions } from '@typebot.io/schemas'
|
||||
import { useTypebot } from '@/features/editor/providers/TypebotProvider'
|
||||
import { SetVariableLabel } from '@/components/SetVariableLabel'
|
||||
|
||||
type Props = {
|
||||
options: ZemanticAiOptions
|
||||
}
|
||||
|
||||
export const ZemanticAiNodeBody = ({
|
||||
options: { query, projectId, responseMapping },
|
||||
}: Props) => {
|
||||
const { typebot } = useTypebot()
|
||||
return (
|
||||
<Stack>
|
||||
<Text
|
||||
color={query && projectId ? 'currentcolor' : 'gray.500'}
|
||||
noOfLines={1}
|
||||
>
|
||||
{query && projectId ? `Ask: ${query}` : 'Configure...'}
|
||||
</Text>
|
||||
{typebot &&
|
||||
responseMapping
|
||||
.map((mapping) => mapping.variableId)
|
||||
.map((variableId, idx) =>
|
||||
variableId ? (
|
||||
<SetVariableLabel
|
||||
key={variableId + idx}
|
||||
variables={typebot.variables}
|
||||
variableId={variableId}
|
||||
/>
|
||||
) : null
|
||||
)}
|
||||
</Stack>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,185 @@
|
||||
import { TextInput, Textarea, NumberInput } from '@/components/inputs'
|
||||
import { CredentialsDropdown } from '@/features/credentials/components/CredentialsDropdown'
|
||||
import { useWorkspace } from '@/features/workspace/WorkspaceProvider'
|
||||
import {
|
||||
Accordion,
|
||||
AccordionButton,
|
||||
AccordionIcon,
|
||||
AccordionItem,
|
||||
AccordionPanel,
|
||||
Stack,
|
||||
Text,
|
||||
useDisclosure,
|
||||
} from '@chakra-ui/react'
|
||||
import { isEmpty } from '@typebot.io/lib'
|
||||
import { ZemanticAiBlock } from '@typebot.io/schemas'
|
||||
import { ZemanticAiCredentialsModal } from './ZemanticAiCredentialsModal'
|
||||
import { ProjectsDropdown } from './ProjectsDropdown'
|
||||
import { SearchResponseItem } from './SearchResponseItem'
|
||||
import { TableList } from '@/components/TableList'
|
||||
import { createId } from '@paralleldrive/cuid2'
|
||||
|
||||
type Props = {
|
||||
block: ZemanticAiBlock
|
||||
onOptionsChange: (options: ZemanticAiBlock['options']) => void
|
||||
}
|
||||
|
||||
export const ZemanticAiSettings = ({
|
||||
block: { id: blockId, options },
|
||||
onOptionsChange,
|
||||
}: Props) => {
|
||||
const { workspace } = useWorkspace()
|
||||
const { isOpen, onOpen, onClose } = useDisclosure()
|
||||
|
||||
const updateCredentialsId = (credentialsId: string | undefined) => {
|
||||
onOptionsChange({
|
||||
...options,
|
||||
credentialsId,
|
||||
responseMapping: [
|
||||
{
|
||||
id: createId(),
|
||||
valueToExtract: 'Summary',
|
||||
},
|
||||
],
|
||||
})
|
||||
}
|
||||
|
||||
const updateProjectId = (projectId: string | undefined) => {
|
||||
onOptionsChange({
|
||||
...options,
|
||||
projectId: isEmpty(projectId) ? undefined : projectId,
|
||||
})
|
||||
}
|
||||
|
||||
const updateQuery = (query: string) => {
|
||||
onOptionsChange({
|
||||
...options,
|
||||
query: isEmpty(query) ? undefined : query,
|
||||
})
|
||||
}
|
||||
|
||||
const updateMaxResults = (
|
||||
maxResults: number | `{{${string}}}` | undefined
|
||||
) => {
|
||||
onOptionsChange({
|
||||
...options,
|
||||
maxResults: maxResults as number,
|
||||
})
|
||||
}
|
||||
|
||||
const updateSystemPrompt = (systemPrompt: string) => {
|
||||
onOptionsChange({
|
||||
...options,
|
||||
systemPrompt: isEmpty(systemPrompt) ? undefined : systemPrompt,
|
||||
})
|
||||
}
|
||||
|
||||
const updatePrompt = (prompt: string) => {
|
||||
onOptionsChange({
|
||||
...options,
|
||||
prompt: isEmpty(prompt) ? undefined : prompt,
|
||||
})
|
||||
}
|
||||
|
||||
const updateResponseMapping = (
|
||||
responseMapping: typeof options.responseMapping
|
||||
) => {
|
||||
onOptionsChange({
|
||||
...options,
|
||||
responseMapping,
|
||||
})
|
||||
}
|
||||
|
||||
return (
|
||||
<Stack spacing={4}>
|
||||
{workspace && (
|
||||
<>
|
||||
<CredentialsDropdown
|
||||
type="zemanticAi"
|
||||
workspaceId={workspace.id}
|
||||
currentCredentialsId={options?.credentialsId}
|
||||
onCredentialsSelect={updateCredentialsId}
|
||||
onCreateNewClick={onOpen}
|
||||
credentialsName="Zemantic AI account"
|
||||
/>
|
||||
<ZemanticAiCredentialsModal
|
||||
isOpen={isOpen}
|
||||
onClose={onClose}
|
||||
onNewCredentials={updateCredentialsId}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
{options?.credentialsId && (
|
||||
<>
|
||||
<ProjectsDropdown
|
||||
credentialsId={options?.credentialsId as string}
|
||||
defaultValue={(options?.projectId as string) ?? ''}
|
||||
onChange={updateProjectId}
|
||||
blockId={blockId as string}
|
||||
/>
|
||||
<TextInput
|
||||
label="Query:"
|
||||
moreInfoTooltip="The question you want to ask or search against the documents in the project."
|
||||
defaultValue={options?.query ?? ''}
|
||||
onChange={updateQuery}
|
||||
withVariableButton={true}
|
||||
placeholder="Content"
|
||||
/>
|
||||
<NumberInput
|
||||
label="Max Results:"
|
||||
moreInfoTooltip="The maximum number of document chunk results to return from your search."
|
||||
direction="column"
|
||||
defaultValue={options?.maxResults}
|
||||
onValueChange={updateMaxResults}
|
||||
placeholder="i.e. 3"
|
||||
w="full"
|
||||
/>
|
||||
<Accordion allowMultiple={true}>
|
||||
<AccordionItem>
|
||||
<AccordionButton>
|
||||
<Text w="full" textAlign="left">
|
||||
Advanced settings
|
||||
</Text>
|
||||
<AccordionIcon />
|
||||
</AccordionButton>
|
||||
<AccordionPanel pb={4} as={Stack} spacing={6}>
|
||||
<Textarea
|
||||
label="System Prompt:"
|
||||
moreInfoTooltip="System prompt to send to the summarization LLM. This is prepended to the prompt and helps guide system behavior."
|
||||
defaultValue={options?.systemPrompt ?? ''}
|
||||
onChange={updateSystemPrompt}
|
||||
placeholder="System Prompt"
|
||||
withVariableButton={true}
|
||||
/>
|
||||
<Textarea
|
||||
label="Prompt:"
|
||||
moreInfoTooltip="Prompt to send to the summarization LLM."
|
||||
defaultValue={options?.prompt ?? ''}
|
||||
onChange={updatePrompt}
|
||||
placeholder="Prompt"
|
||||
withVariableButton={true}
|
||||
/>
|
||||
</AccordionPanel>
|
||||
</AccordionItem>
|
||||
<AccordionItem>
|
||||
<AccordionButton>
|
||||
<Text w="full" textAlign="left">
|
||||
Save answer
|
||||
</Text>
|
||||
<AccordionIcon />
|
||||
</AccordionButton>
|
||||
<AccordionPanel pt="4">
|
||||
<TableList
|
||||
initialItems={options.responseMapping ?? []}
|
||||
Item={SearchResponseItem}
|
||||
onItemsChange={updateResponseMapping}
|
||||
newItemDefaultProps={{ valueToExtract: 'Summary' }}
|
||||
/>
|
||||
</AccordionPanel>
|
||||
</AccordionItem>
|
||||
</Accordion>
|
||||
</>
|
||||
)}
|
||||
</Stack>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
import prisma from '@/lib/prisma'
|
||||
import { authenticatedProcedure } from '@/helpers/server/trpc'
|
||||
import { TRPCError } from '@trpc/server'
|
||||
import { z } from 'zod'
|
||||
import { isReadWorkspaceFobidden } from '@/features/workspace/helpers/isReadWorkspaceFobidden'
|
||||
import { decrypt } from '@typebot.io/lib/api'
|
||||
import { ZemanticAiCredentials } from '@typebot.io/schemas/features/blocks/integrations/zemanticAi'
|
||||
import got from 'got'
|
||||
|
||||
export const listProjects = authenticatedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
method: 'GET',
|
||||
path: '/zemantic-ai/projects',
|
||||
protect: true,
|
||||
summary: 'List Zemantic AI projects',
|
||||
tags: ['ZemanticAi'],
|
||||
},
|
||||
})
|
||||
.input(
|
||||
z.object({
|
||||
credentialsId: z.string(),
|
||||
workspaceId: z.string(),
|
||||
})
|
||||
)
|
||||
.output(
|
||||
z.object({
|
||||
projects: z.array(
|
||||
z.object({
|
||||
label: z.string(),
|
||||
value: z.string(),
|
||||
})
|
||||
),
|
||||
})
|
||||
)
|
||||
.query(async ({ input: { credentialsId, workspaceId }, ctx: { user } }) => {
|
||||
const workspace = await prisma.workspace.findFirst({
|
||||
where: { id: workspaceId },
|
||||
select: {
|
||||
members: {
|
||||
select: {
|
||||
userId: true,
|
||||
},
|
||||
},
|
||||
credentials: {
|
||||
where: {
|
||||
id: credentialsId,
|
||||
},
|
||||
select: {
|
||||
id: true,
|
||||
data: true,
|
||||
iv: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
if (!workspace || isReadWorkspaceFobidden(workspace, user))
|
||||
throw new TRPCError({
|
||||
code: 'NOT_FOUND',
|
||||
message: 'No workspace found',
|
||||
})
|
||||
|
||||
const credentials = workspace.credentials.at(0)
|
||||
|
||||
if (!credentials)
|
||||
throw new TRPCError({
|
||||
code: 'NOT_FOUND',
|
||||
message: 'No credentials found',
|
||||
})
|
||||
|
||||
const data = (await decrypt(
|
||||
credentials.data,
|
||||
credentials.iv
|
||||
)) as ZemanticAiCredentials['data']
|
||||
|
||||
const url = 'https://api.zemantic.ai/v1/projects'
|
||||
|
||||
try {
|
||||
const response = await got
|
||||
.get(url, {
|
||||
headers: {
|
||||
Authorization: `Bearer ${data.apiKey}`,
|
||||
},
|
||||
})
|
||||
.json()
|
||||
|
||||
const projectsData = response as {
|
||||
id: string
|
||||
name: string
|
||||
}[]
|
||||
|
||||
return {
|
||||
projects: projectsData.map((project) => ({
|
||||
label: project.name,
|
||||
value: project.id,
|
||||
})),
|
||||
}
|
||||
} catch (e) {
|
||||
throw new TRPCError({
|
||||
code: 'INTERNAL_SERVER_ERROR',
|
||||
message: 'Could not list projects',
|
||||
cause: e,
|
||||
})
|
||||
}
|
||||
})
|
||||
@@ -0,0 +1,6 @@
|
||||
import { router } from '@/helpers/server/trpc'
|
||||
import { listProjects } from './listProjects'
|
||||
|
||||
export const zemanticAiRouter = router({
|
||||
listProjects,
|
||||
})
|
||||
@@ -8,7 +8,7 @@ import { smtpCredentialsSchema } from '@typebot.io/schemas/features/blocks/integ
|
||||
import { encrypt } from '@typebot.io/lib/api/encryption'
|
||||
import { z } from 'zod'
|
||||
import { whatsAppCredentialsSchema } from '@typebot.io/schemas/features/whatsapp'
|
||||
import { Credentials } from '@typebot.io/schemas'
|
||||
import { Credentials, zemanticAiCredentialsSchema } from '@typebot.io/schemas'
|
||||
import { isDefined } from '@typebot.io/lib/utils'
|
||||
import { isWriteWorkspaceForbidden } from '@/features/workspace/helpers/isWriteWorkspaceForbidden'
|
||||
|
||||
@@ -37,6 +37,7 @@ export const createCredentials = authenticatedProcedure
|
||||
googleSheetsCredentialsSchema.pick(inputShape),
|
||||
openAICredentialsSchema.pick(inputShape),
|
||||
whatsAppCredentialsSchema.pick(inputShape),
|
||||
zemanticAiCredentialsSchema.pick(inputShape),
|
||||
]),
|
||||
})
|
||||
)
|
||||
|
||||
@@ -8,6 +8,7 @@ import { smtpCredentialsSchema } from '@typebot.io/schemas/features/blocks/integ
|
||||
import { z } from 'zod'
|
||||
import { isReadWorkspaceFobidden } from '@/features/workspace/helpers/isReadWorkspaceFobidden'
|
||||
import { whatsAppCredentialsSchema } from '@typebot.io/schemas/features/whatsapp'
|
||||
import { zemanticAiCredentialsSchema } from '@typebot.io/schemas/features/blocks/integrations/zemanticAi'
|
||||
|
||||
export const listCredentials = authenticatedProcedure
|
||||
.meta({
|
||||
@@ -26,7 +27,8 @@ export const listCredentials = authenticatedProcedure
|
||||
.or(smtpCredentialsSchema.shape.type)
|
||||
.or(googleSheetsCredentialsSchema.shape.type)
|
||||
.or(openAICredentialsSchema.shape.type)
|
||||
.or(whatsAppCredentialsSchema.shape.type),
|
||||
.or(whatsAppCredentialsSchema.shape.type)
|
||||
.or(zemanticAiCredentialsSchema.shape.type),
|
||||
})
|
||||
)
|
||||
.output(
|
||||
|
||||
@@ -40,6 +40,7 @@ import { TypebotLinkIcon } from '@/features/blocks/logic/typebotLink/components/
|
||||
import { AbTestIcon } from '@/features/blocks/logic/abTest/components/AbTestIcon'
|
||||
import { PictureChoiceIcon } from '@/features/blocks/inputs/pictureChoice/components/PictureChoiceIcon'
|
||||
import { PixelLogo } from '@/features/blocks/integrations/pixel/components/PixelLogo'
|
||||
import { ZemanticAiLogo } from '@/features/blocks/integrations/zemanticAi/ZemanticAiLogo'
|
||||
|
||||
type BlockIconProps = { type: BlockType } & IconProps
|
||||
|
||||
@@ -118,6 +119,8 @@ export const BlockIcon = ({ type, ...props }: BlockIconProps): JSX.Element => {
|
||||
return <OpenAILogo fill={openAIColor} {...props} />
|
||||
case IntegrationBlockType.PIXEL:
|
||||
return <PixelLogo {...props} />
|
||||
case IntegrationBlockType.ZEMANTIC_AI:
|
||||
return <ZemanticAiLogo {...props} />
|
||||
case 'start':
|
||||
return <FlagIcon {...props} />
|
||||
}
|
||||
|
||||
@@ -84,5 +84,7 @@ export const BlockLabel = ({ type }: Props): JSX.Element => {
|
||||
return <Text fontSize="sm">{scopedT('openai.label')}</Text>
|
||||
case IntegrationBlockType.PIXEL:
|
||||
return <Text fontSize="sm">{scopedT('pixel.label')}</Text>
|
||||
case IntegrationBlockType.ZEMANTIC_AI:
|
||||
return <Text fontSize="sm">{scopedT('zemanticAi.label')}</Text>
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,6 +43,7 @@ import { AbTestNodeBody } from '@/features/blocks/logic/abTest/components/AbTest
|
||||
import { PictureChoiceNode } from '@/features/blocks/inputs/pictureChoice/components/PictureChoiceNode'
|
||||
import { PixelNodeBody } from '@/features/blocks/integrations/pixel/components/PixelNodeBody'
|
||||
import { useScopedI18n } from '@/locales'
|
||||
import { ZemanticAiNodeBody } from '@/features/blocks/integrations/zemanticAi/ZemanticAiNodeBody'
|
||||
|
||||
type Props = {
|
||||
block: Block | StartBlock
|
||||
@@ -200,6 +201,9 @@ export const BlockNodeContent = ({ block, indices }: Props): JSX.Element => {
|
||||
case IntegrationBlockType.PIXEL: {
|
||||
return <PixelNodeBody options={block.options} />
|
||||
}
|
||||
case IntegrationBlockType.ZEMANTIC_AI: {
|
||||
return <ZemanticAiNodeBody options={block.options} />
|
||||
}
|
||||
case 'start': {
|
||||
return <Text>{scopedT('text')}</Text>
|
||||
}
|
||||
|
||||
@@ -47,6 +47,7 @@ import { AbTestSettings } from '@/features/blocks/logic/abTest/components/AbTest
|
||||
import { PictureChoiceSettings } from '@/features/blocks/inputs/pictureChoice/components/PictureChoiceSettings'
|
||||
import { SettingsHoverBar } from './SettingsHoverBar'
|
||||
import { PixelSettings } from '@/features/blocks/integrations/pixel/components/PixelSettings'
|
||||
import { ZemanticAiSettings } from '@/features/blocks/integrations/zemanticAi/ZemanticAiSettings'
|
||||
|
||||
type Props = {
|
||||
block: BlockWithOptions
|
||||
@@ -314,5 +315,10 @@ export const BlockSettings = ({
|
||||
/>
|
||||
)
|
||||
}
|
||||
case IntegrationBlockType.ZEMANTIC_AI: {
|
||||
return (
|
||||
<ZemanticAiSettings block={block} onOptionsChange={updateOptions} />
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,5 +63,7 @@ export const getHelpDocUrl = (blockType: BlockWithOptions['type']): string => {
|
||||
return 'https://docs.typebot.io/editor/blocks/logic/jump'
|
||||
case IntegrationBlockType.PIXEL:
|
||||
return 'https://docs.typebot.io/editor/blocks/integrations/pixel'
|
||||
case IntegrationBlockType.ZEMANTIC_AI:
|
||||
return 'https://docs.typebot.io/editor/blocks/integrations/zemantic-ai'
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,6 +44,7 @@ import {
|
||||
defaultAbTestOptions,
|
||||
BlockWithItems,
|
||||
defaultTypebotLinkOptions,
|
||||
zemanticAiDefaultOptions,
|
||||
} from '@typebot.io/schemas'
|
||||
import { defaultPictureChoiceOptions } from '@typebot.io/schemas/features/blocks/inputs/pictureChoice'
|
||||
|
||||
@@ -143,6 +144,8 @@ const parseDefaultBlockOptions = (type: BlockWithOptionsType): BlockOptions => {
|
||||
return {}
|
||||
case IntegrationBlockType.PIXEL:
|
||||
return {}
|
||||
case IntegrationBlockType.ZEMANTIC_AI:
|
||||
return zemanticAiDefaultOptions
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@ import { customDomainsRouter } from '@/features/customDomains/api/router'
|
||||
import { whatsAppRouter } from '@/features/whatsapp/router'
|
||||
import { openAIRouter } from '@/features/blocks/integrations/openai/api/router'
|
||||
import { generateUploadUrl } from '@/features/upload/api/generateUploadUrl'
|
||||
import { zemanticAiRouter } from '@/features/blocks/integrations/zemanticAi/api/router'
|
||||
|
||||
export const trpcRouter = router({
|
||||
getAppVersionProcedure,
|
||||
@@ -35,6 +36,7 @@ export const trpcRouter = router({
|
||||
whatsApp: whatsAppRouter,
|
||||
openAI: openAIRouter,
|
||||
generateUploadUrl,
|
||||
zemanticAi: zemanticAiRouter,
|
||||
})
|
||||
|
||||
export type AppRouter = typeof trpcRouter
|
||||
|
||||
@@ -251,6 +251,7 @@ export default {
|
||||
'editor.sidebarBlock.chatwoot.label': 'Chatwoot',
|
||||
'editor.sidebarBlock.openai.label': 'OpenAI',
|
||||
'editor.sidebarBlock.pixel.label': 'Pixel',
|
||||
'editor.sidebarBlock.zemanticAi.label': 'Zemantic AI',
|
||||
'editor.blockCard.bubbleBlock.tooltip.label':
|
||||
'Ein PDF, ein iframe, eine Website einbetten...',
|
||||
'editor.blockCard.inputBlock.tooltip.files.label': 'Dateien hochladen',
|
||||
|
||||
@@ -245,6 +245,7 @@ export default {
|
||||
'editor.sidebarBlock.chatwoot.label': 'Chatwoot',
|
||||
'editor.sidebarBlock.openai.label': 'OpenAI',
|
||||
'editor.sidebarBlock.pixel.label': 'Pixel',
|
||||
'editor.sidebarBlock.zemanticAi.label': 'Zemantic AI',
|
||||
'editor.blockCard.bubbleBlock.tooltip.label':
|
||||
'Embed a pdf, an iframe, a website...',
|
||||
'editor.blockCard.inputBlock.tooltip.files.label': 'Upload Files',
|
||||
|
||||
@@ -251,6 +251,7 @@ export default {
|
||||
'editor.sidebarBlock.chatwoot.label': 'Chatwoot',
|
||||
'editor.sidebarBlock.openai.label': 'OpenAI',
|
||||
'editor.sidebarBlock.pixel.label': 'Pixel',
|
||||
'editor.sidebarBlock.zemanticAi.label': 'Zemantic AI',
|
||||
'editor.blockCard.bubbleBlock.tooltip.label':
|
||||
'Intégrer un pdf, un iframe, un site web...',
|
||||
'editor.blockCard.inputBlock.tooltip.files.label': 'Télécharger des fichiers',
|
||||
|
||||
@@ -252,6 +252,7 @@ export default {
|
||||
'editor.sidebarBlock.chatwoot.label': 'Chatwoot',
|
||||
'editor.sidebarBlock.openai.label': 'OpenAI',
|
||||
'editor.sidebarBlock.pixel.label': 'Pixel',
|
||||
'editor.sidebarBlock.zemanticAi.label': 'Zemantic AI',
|
||||
'editor.blockCard.bubbleBlock.tooltip.label':
|
||||
'Incorporar pdf, iframe, website...',
|
||||
'editor.blockCard.inputBlock.tooltip.files.label': 'Carregar Ficheiros',
|
||||
|
||||
@@ -253,6 +253,7 @@ export default {
|
||||
'editor.sidebarBlock.chatwoot.label': 'Chatwoot',
|
||||
'editor.sidebarBlock.openai.label': 'OpenAI',
|
||||
'editor.sidebarBlock.pixel.label': 'Pixel',
|
||||
'editor.sidebarBlock.zemanticAi.label': 'Zemantic AI',
|
||||
'editor.blockCard.bubbleBlock.tooltip.label':
|
||||
'Incorporar pdf, iframe, website...',
|
||||
'editor.blockCard.inputBlock.tooltip.files.label': 'Carregar Ficheiros',
|
||||
|
||||
44
apps/docs/docs/editor/blocks/integrations/zemantic-ai.mdx
Normal file
44
apps/docs/docs/editor/blocks/integrations/zemantic-ai.mdx
Normal file
@@ -0,0 +1,44 @@
|
||||
# Zemantic AI
|
||||
|
||||
With the Zemantic AI block, you can search and retrieve results or LLM summaries from your documents stored on Zemantic AI.
|
||||
|
||||
<img
|
||||
src="/img/blocks/integrations/zemanticAi/overview.png"
|
||||
width="600"
|
||||
alt="OpenAI block"
|
||||
/>
|
||||
|
||||
## Settings
|
||||
|
||||
This integration requires a Zemantic AI account. If you don't have one yet, you can create one [here](https://zemantic.ai/).
|
||||
|
||||
The block has the following settings:
|
||||
|
||||
<img
|
||||
src="/img/blocks/integrations/zemanticAi/settings.png"
|
||||
width="600"
|
||||
alt="OpenAI messages sequence"
|
||||
/>
|
||||
|
||||
- Zemantic AI account: create or select the Zemantic AI credentials you want to use.
|
||||
- Project ID: The project id of the project containing the documents you want to search
|
||||
- Question or Query: The question you want to ask your Zemantic AI documents
|
||||
- Max Results: The maximum number of results you want to retrieve
|
||||
|
||||
### Prompt Settings
|
||||
|
||||
- System Prompt: The prompt you want to use to guide the LLM behavior
|
||||
- Prompt: The prompt you want to use to summarize your documents
|
||||
|
||||
## Troobleshooting
|
||||
|
||||
### Error message: "Zemantic AI block returned error"
|
||||
|
||||
It means your Zemantic AI block is not configured properly. Please check the following:
|
||||
|
||||
- You have selected an Zemantic AI account
|
||||
- You have filled out the Question or Query field
|
||||
|
||||
### It returns an empty message
|
||||
|
||||
Either you misconfigured the block or your may have have gone over the context limits of the LLM. You can try lowering the number of results to retrieve or shortening your prompt.
|
||||
@@ -3906,6 +3906,88 @@
|
||||
"options"
|
||||
],
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"groupId": {
|
||||
"type": "string"
|
||||
},
|
||||
"outgoingEdgeId": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"Zemantic AI"
|
||||
]
|
||||
},
|
||||
"blockId": {
|
||||
"type": "string"
|
||||
},
|
||||
"options": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"credentialsId": {
|
||||
"type": "string"
|
||||
},
|
||||
"projectId": {
|
||||
"type": "string"
|
||||
},
|
||||
"systemPrompt": {
|
||||
"type": "string"
|
||||
},
|
||||
"prompt": {
|
||||
"type": "string"
|
||||
},
|
||||
"query": {
|
||||
"type": "string"
|
||||
},
|
||||
"maxResults": {
|
||||
"type": "integer"
|
||||
},
|
||||
"responseMapping": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"valueToExtract": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"Summary",
|
||||
"Results"
|
||||
]
|
||||
},
|
||||
"variableId": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"id"
|
||||
],
|
||||
"additionalProperties": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"responseMapping"
|
||||
],
|
||||
"additionalProperties": false
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"id",
|
||||
"groupId",
|
||||
"type",
|
||||
"options"
|
||||
],
|
||||
"additionalProperties": false
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -8211,6 +8293,88 @@
|
||||
"options"
|
||||
],
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"groupId": {
|
||||
"type": "string"
|
||||
},
|
||||
"outgoingEdgeId": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"Zemantic AI"
|
||||
]
|
||||
},
|
||||
"blockId": {
|
||||
"type": "string"
|
||||
},
|
||||
"options": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"credentialsId": {
|
||||
"type": "string"
|
||||
},
|
||||
"projectId": {
|
||||
"type": "string"
|
||||
},
|
||||
"systemPrompt": {
|
||||
"type": "string"
|
||||
},
|
||||
"prompt": {
|
||||
"type": "string"
|
||||
},
|
||||
"query": {
|
||||
"type": "string"
|
||||
},
|
||||
"maxResults": {
|
||||
"type": "integer"
|
||||
},
|
||||
"responseMapping": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"valueToExtract": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"Summary",
|
||||
"Results"
|
||||
]
|
||||
},
|
||||
"variableId": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"id"
|
||||
],
|
||||
"additionalProperties": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"responseMapping"
|
||||
],
|
||||
"additionalProperties": false
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"id",
|
||||
"groupId",
|
||||
"type",
|
||||
"options"
|
||||
],
|
||||
"additionalProperties": false
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -12151,6 +12315,88 @@
|
||||
"options"
|
||||
],
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"groupId": {
|
||||
"type": "string"
|
||||
},
|
||||
"outgoingEdgeId": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"Zemantic AI"
|
||||
]
|
||||
},
|
||||
"blockId": {
|
||||
"type": "string"
|
||||
},
|
||||
"options": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"credentialsId": {
|
||||
"type": "string"
|
||||
},
|
||||
"projectId": {
|
||||
"type": "string"
|
||||
},
|
||||
"systemPrompt": {
|
||||
"type": "string"
|
||||
},
|
||||
"prompt": {
|
||||
"type": "string"
|
||||
},
|
||||
"query": {
|
||||
"type": "string"
|
||||
},
|
||||
"maxResults": {
|
||||
"type": "integer"
|
||||
},
|
||||
"responseMapping": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"valueToExtract": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"Summary",
|
||||
"Results"
|
||||
]
|
||||
},
|
||||
"variableId": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"id"
|
||||
],
|
||||
"additionalProperties": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"responseMapping"
|
||||
],
|
||||
"additionalProperties": false
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"id",
|
||||
"groupId",
|
||||
"type",
|
||||
"options"
|
||||
],
|
||||
"additionalProperties": false
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -16231,6 +16477,88 @@
|
||||
"options"
|
||||
],
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"groupId": {
|
||||
"type": "string"
|
||||
},
|
||||
"outgoingEdgeId": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"Zemantic AI"
|
||||
]
|
||||
},
|
||||
"blockId": {
|
||||
"type": "string"
|
||||
},
|
||||
"options": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"credentialsId": {
|
||||
"type": "string"
|
||||
},
|
||||
"projectId": {
|
||||
"type": "string"
|
||||
},
|
||||
"systemPrompt": {
|
||||
"type": "string"
|
||||
},
|
||||
"prompt": {
|
||||
"type": "string"
|
||||
},
|
||||
"query": {
|
||||
"type": "string"
|
||||
},
|
||||
"maxResults": {
|
||||
"type": "integer"
|
||||
},
|
||||
"responseMapping": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"valueToExtract": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"Summary",
|
||||
"Results"
|
||||
]
|
||||
},
|
||||
"variableId": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"id"
|
||||
],
|
||||
"additionalProperties": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"responseMapping"
|
||||
],
|
||||
"additionalProperties": false
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"id",
|
||||
"groupId",
|
||||
"type",
|
||||
"options"
|
||||
],
|
||||
"additionalProperties": false
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -20191,6 +20519,88 @@
|
||||
"options"
|
||||
],
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"groupId": {
|
||||
"type": "string"
|
||||
},
|
||||
"outgoingEdgeId": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"Zemantic AI"
|
||||
]
|
||||
},
|
||||
"blockId": {
|
||||
"type": "string"
|
||||
},
|
||||
"options": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"credentialsId": {
|
||||
"type": "string"
|
||||
},
|
||||
"projectId": {
|
||||
"type": "string"
|
||||
},
|
||||
"systemPrompt": {
|
||||
"type": "string"
|
||||
},
|
||||
"prompt": {
|
||||
"type": "string"
|
||||
},
|
||||
"query": {
|
||||
"type": "string"
|
||||
},
|
||||
"maxResults": {
|
||||
"type": "integer"
|
||||
},
|
||||
"responseMapping": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"valueToExtract": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"Summary",
|
||||
"Results"
|
||||
]
|
||||
},
|
||||
"variableId": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"id"
|
||||
],
|
||||
"additionalProperties": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"responseMapping"
|
||||
],
|
||||
"additionalProperties": false
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"id",
|
||||
"groupId",
|
||||
"type",
|
||||
"options"
|
||||
],
|
||||
"additionalProperties": false
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -24206,6 +24616,88 @@
|
||||
"options"
|
||||
],
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"groupId": {
|
||||
"type": "string"
|
||||
},
|
||||
"outgoingEdgeId": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"Zemantic AI"
|
||||
]
|
||||
},
|
||||
"blockId": {
|
||||
"type": "string"
|
||||
},
|
||||
"options": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"credentialsId": {
|
||||
"type": "string"
|
||||
},
|
||||
"projectId": {
|
||||
"type": "string"
|
||||
},
|
||||
"systemPrompt": {
|
||||
"type": "string"
|
||||
},
|
||||
"prompt": {
|
||||
"type": "string"
|
||||
},
|
||||
"query": {
|
||||
"type": "string"
|
||||
},
|
||||
"maxResults": {
|
||||
"type": "integer"
|
||||
},
|
||||
"responseMapping": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"valueToExtract": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"Summary",
|
||||
"Results"
|
||||
]
|
||||
},
|
||||
"variableId": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"id"
|
||||
],
|
||||
"additionalProperties": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"responseMapping"
|
||||
],
|
||||
"additionalProperties": false
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"id",
|
||||
"groupId",
|
||||
"type",
|
||||
"options"
|
||||
],
|
||||
"additionalProperties": false
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -28284,6 +28776,88 @@
|
||||
"options"
|
||||
],
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"groupId": {
|
||||
"type": "string"
|
||||
},
|
||||
"outgoingEdgeId": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"Zemantic AI"
|
||||
]
|
||||
},
|
||||
"blockId": {
|
||||
"type": "string"
|
||||
},
|
||||
"options": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"credentialsId": {
|
||||
"type": "string"
|
||||
},
|
||||
"projectId": {
|
||||
"type": "string"
|
||||
},
|
||||
"systemPrompt": {
|
||||
"type": "string"
|
||||
},
|
||||
"prompt": {
|
||||
"type": "string"
|
||||
},
|
||||
"query": {
|
||||
"type": "string"
|
||||
},
|
||||
"maxResults": {
|
||||
"type": "integer"
|
||||
},
|
||||
"responseMapping": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"valueToExtract": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"Summary",
|
||||
"Results"
|
||||
]
|
||||
},
|
||||
"variableId": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"id"
|
||||
],
|
||||
"additionalProperties": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"responseMapping"
|
||||
],
|
||||
"additionalProperties": false
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"id",
|
||||
"groupId",
|
||||
"type",
|
||||
"options"
|
||||
],
|
||||
"additionalProperties": false
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -30536,6 +31110,42 @@
|
||||
"name"
|
||||
],
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"data": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"apiKey": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"apiKey"
|
||||
],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"type": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"zemanticAi"
|
||||
]
|
||||
},
|
||||
"workspaceId": {
|
||||
"type": "string"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"data",
|
||||
"type",
|
||||
"workspaceId",
|
||||
"name"
|
||||
],
|
||||
"additionalProperties": false
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -30607,15 +31217,25 @@
|
||||
{
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"stripe"
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"stripe"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"smtp"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"smtp"
|
||||
"google sheets"
|
||||
]
|
||||
}
|
||||
]
|
||||
@@ -30623,7 +31243,7 @@
|
||||
{
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"google sheets"
|
||||
"openai"
|
||||
]
|
||||
}
|
||||
]
|
||||
@@ -30631,7 +31251,7 @@
|
||||
{
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"openai"
|
||||
"whatsApp"
|
||||
]
|
||||
}
|
||||
]
|
||||
@@ -30639,7 +31259,7 @@
|
||||
{
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"whatsApp"
|
||||
"zemanticAi"
|
||||
]
|
||||
}
|
||||
]
|
||||
@@ -32356,6 +32976,78 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/zemantic-ai/projects": {
|
||||
"get": {
|
||||
"operationId": "zemanticAi-listProjects",
|
||||
"summary": "List Zemantic AI projects",
|
||||
"tags": [
|
||||
"ZemanticAi"
|
||||
],
|
||||
"security": [
|
||||
{
|
||||
"Authorization": []
|
||||
}
|
||||
],
|
||||
"parameters": [
|
||||
{
|
||||
"name": "credentialsId",
|
||||
"in": "query",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "workspaceId",
|
||||
"in": "query",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Successful response",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"projects": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"label": {
|
||||
"type": "string"
|
||||
},
|
||||
"value": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"label",
|
||||
"value"
|
||||
],
|
||||
"additionalProperties": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"projects"
|
||||
],
|
||||
"additionalProperties": false
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"default": {
|
||||
"$ref": "#/components/responses/error"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"components": {
|
||||
|
||||
@@ -3489,6 +3489,88 @@
|
||||
"options"
|
||||
],
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"groupId": {
|
||||
"type": "string"
|
||||
},
|
||||
"outgoingEdgeId": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"Zemantic AI"
|
||||
]
|
||||
},
|
||||
"blockId": {
|
||||
"type": "string"
|
||||
},
|
||||
"options": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"credentialsId": {
|
||||
"type": "string"
|
||||
},
|
||||
"projectId": {
|
||||
"type": "string"
|
||||
},
|
||||
"systemPrompt": {
|
||||
"type": "string"
|
||||
},
|
||||
"prompt": {
|
||||
"type": "string"
|
||||
},
|
||||
"query": {
|
||||
"type": "string"
|
||||
},
|
||||
"maxResults": {
|
||||
"type": "integer"
|
||||
},
|
||||
"responseMapping": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"valueToExtract": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"Summary",
|
||||
"Results"
|
||||
]
|
||||
},
|
||||
"variableId": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"id"
|
||||
],
|
||||
"additionalProperties": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"responseMapping"
|
||||
],
|
||||
"additionalProperties": false
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"id",
|
||||
"groupId",
|
||||
"type",
|
||||
"options"
|
||||
],
|
||||
"additionalProperties": false
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
BIN
apps/docs/static/img/blocks/integrations/zemanticAi/overview.png
vendored
Normal file
BIN
apps/docs/static/img/blocks/integrations/zemanticAi/overview.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 107 KiB |
BIN
apps/docs/static/img/blocks/integrations/zemanticAi/settings.png
vendored
Normal file
BIN
apps/docs/static/img/blocks/integrations/zemanticAi/settings.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 151 KiB |
@@ -0,0 +1,129 @@
|
||||
import { ExecuteIntegrationResponse } from '@/features/chat/types'
|
||||
import prisma from '@/lib/prisma'
|
||||
import { SessionState } from '@typebot.io/schemas'
|
||||
import {
|
||||
ZemanticAiBlock,
|
||||
ZemanticAiCredentials,
|
||||
ZemanticAiResponse,
|
||||
} from '@typebot.io/schemas/features/blocks/integrations/zemanticAi'
|
||||
import got from 'got'
|
||||
import { decrypt } from '@typebot.io/lib/api/encryption'
|
||||
import { byId, isDefined, isEmpty } from '@typebot.io/lib'
|
||||
import { updateVariables } from '@/features/variables/updateVariables'
|
||||
import { getDefinedVariables, parseAnswers } from '@typebot.io/lib/results'
|
||||
|
||||
const URL = 'https://api.zemantic.ai/v1/search-documents'
|
||||
|
||||
export const executeZemanticAiBlock = async (
|
||||
state: SessionState,
|
||||
block: ZemanticAiBlock
|
||||
): Promise<ExecuteIntegrationResponse> => {
|
||||
let newSessionState = state
|
||||
|
||||
const noCredentialsError = {
|
||||
status: 'error',
|
||||
description: 'Make sure to select a Zemantic AI account',
|
||||
}
|
||||
|
||||
const zemanticRequestError = {
|
||||
status: 'error',
|
||||
description: 'Could not execute Zemantic AI request',
|
||||
}
|
||||
|
||||
const credentials = await prisma.credentials.findUnique({
|
||||
where: {
|
||||
id: block.options.credentialsId,
|
||||
},
|
||||
})
|
||||
if (!credentials) {
|
||||
console.error('Could not find credentials in database')
|
||||
return {
|
||||
outgoingEdgeId: block.outgoingEdgeId,
|
||||
logs: [noCredentialsError],
|
||||
}
|
||||
}
|
||||
const { apiKey } = (await decrypt(
|
||||
credentials.data,
|
||||
credentials.iv
|
||||
)) as ZemanticAiCredentials['data']
|
||||
|
||||
const { typebot, answers } = newSessionState.typebotsQueue[0]
|
||||
|
||||
const templateVars = parseAnswers({
|
||||
variables: getDefinedVariables(typebot.variables),
|
||||
answers: answers,
|
||||
})
|
||||
|
||||
try {
|
||||
const res: ZemanticAiResponse = await got
|
||||
.post(URL, {
|
||||
headers: {
|
||||
Authorization: `Bearer ${apiKey}`,
|
||||
},
|
||||
json: {
|
||||
projectId: block.options.projectId,
|
||||
query: replaceTemplateVars(
|
||||
block.options.query as string,
|
||||
templateVars
|
||||
),
|
||||
maxResults: block.options.maxResults,
|
||||
summarize: true,
|
||||
summaryOptions: {
|
||||
system_prompt:
|
||||
replaceTemplateVars(
|
||||
block.options.systemPrompt as string,
|
||||
templateVars
|
||||
) ?? '',
|
||||
prompt:
|
||||
replaceTemplateVars(
|
||||
block.options.prompt as string,
|
||||
templateVars
|
||||
) ?? '',
|
||||
},
|
||||
},
|
||||
})
|
||||
.json()
|
||||
|
||||
for (const r of block.options.responseMapping || []) {
|
||||
const variable = typebot.variables.find(byId(r.variableId))
|
||||
switch (r.valueToExtract) {
|
||||
case 'Summary':
|
||||
if (isDefined(variable) && !isEmpty(res.summary)) {
|
||||
newSessionState = updateVariables(newSessionState)([
|
||||
{ ...variable, value: res.summary },
|
||||
])
|
||||
}
|
||||
break
|
||||
case 'Results':
|
||||
if (isDefined(variable) && res.results.length) {
|
||||
newSessionState = updateVariables(newSessionState)([
|
||||
{ ...variable, value: JSON.stringify(res.results) },
|
||||
])
|
||||
}
|
||||
break
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
return {
|
||||
outgoingEdgeId: block.outgoingEdgeId,
|
||||
logs: [zemanticRequestError],
|
||||
}
|
||||
}
|
||||
|
||||
return { outgoingEdgeId: block.outgoingEdgeId, newSessionState }
|
||||
}
|
||||
|
||||
const replaceTemplateVars = (
|
||||
template: string,
|
||||
vars: Record<string, string>
|
||||
) => {
|
||||
if (!template) return
|
||||
let result = template
|
||||
for (const [key, value] of Object.entries(vars)) {
|
||||
result = result.replaceAll(`{{${key}}}`, value)
|
||||
}
|
||||
return result
|
||||
}
|
||||
@@ -5,6 +5,7 @@ import { executeChatwootBlock } from '@/features/blocks/integrations/chatwoot/ex
|
||||
import { executeGoogleAnalyticsBlock } from '@/features/blocks/integrations/googleAnalytics/executeGoogleAnalyticsBlock'
|
||||
import { executeGoogleSheetBlock } from '@/features/blocks/integrations/googleSheets/executeGoogleSheetBlock'
|
||||
import { executePixelBlock } from '@/features/blocks/integrations/pixel/executePixelBlock'
|
||||
import { executeZemanticAiBlock } from '@/features/blocks/integrations/zemanticAi/executeZemanticAiBlock'
|
||||
import {
|
||||
IntegrationBlock,
|
||||
IntegrationBlockType,
|
||||
@@ -33,5 +34,7 @@ export const executeIntegration =
|
||||
return executeOpenAIBlock(state, block)
|
||||
case IntegrationBlockType.PIXEL:
|
||||
return executePixelBlock(state, block)
|
||||
case IntegrationBlockType.ZEMANTIC_AI:
|
||||
return executeZemanticAiBlock(state, block)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,4 +9,5 @@ export enum IntegrationBlockType {
|
||||
PABBLY_CONNECT = 'Pabbly',
|
||||
CHATWOOT = 'Chatwoot',
|
||||
PIXEL = 'Pixel',
|
||||
ZEMANTIC_AI = 'Zemantic AI',
|
||||
}
|
||||
|
||||
@@ -9,3 +9,4 @@ export * from './webhook/schemas'
|
||||
export * from './zapier'
|
||||
export * from './pixel/schemas'
|
||||
export * from './pixel/constants'
|
||||
export * from './zemanticAi'
|
||||
|
||||
62
packages/schemas/features/blocks/integrations/zemanticAi.ts
Normal file
62
packages/schemas/features/blocks/integrations/zemanticAi.ts
Normal file
@@ -0,0 +1,62 @@
|
||||
import { z } from 'zod'
|
||||
import { blockBaseSchema, credentialsBaseSchema } from '../baseSchemas'
|
||||
import { IntegrationBlockType } from './enums'
|
||||
|
||||
export const searchResponseValues = ['Summary', 'Results'] as const
|
||||
|
||||
export const zemanticAiOptionsSchema = z.object({
|
||||
credentialsId: z.string().optional(),
|
||||
projectId: z.string().optional(),
|
||||
systemPrompt: z.string().optional(),
|
||||
prompt: z.string().optional(),
|
||||
query: z.string().optional(),
|
||||
maxResults: z.number().int().optional(),
|
||||
responseMapping: z.array(
|
||||
z.object({
|
||||
id: z.string(),
|
||||
valueToExtract: z.preprocess(
|
||||
(val) => (!val ? 'Summary' : val),
|
||||
z.enum(searchResponseValues)
|
||||
),
|
||||
variableId: z.string().optional(),
|
||||
})
|
||||
),
|
||||
})
|
||||
|
||||
export const zemanticAiBlockSchema = blockBaseSchema.merge(
|
||||
z.object({
|
||||
type: z.enum([IntegrationBlockType.ZEMANTIC_AI]),
|
||||
blockId: z.string().optional(),
|
||||
options: zemanticAiOptionsSchema,
|
||||
})
|
||||
)
|
||||
|
||||
export const zemanticAiCredentialsSchema = z
|
||||
.object({
|
||||
type: z.literal('zemanticAi'),
|
||||
data: z.object({
|
||||
apiKey: z.string(),
|
||||
}),
|
||||
})
|
||||
.merge(credentialsBaseSchema)
|
||||
|
||||
export const zemanticSearchResponseSchema = z.object({
|
||||
results: z.array(
|
||||
z.object({
|
||||
documentId: z.string(),
|
||||
text: z.string(),
|
||||
score: z.number(),
|
||||
})
|
||||
),
|
||||
summary: z.string(),
|
||||
})
|
||||
|
||||
export const zemanticAiDefaultOptions: ZemanticAiOptions = {
|
||||
maxResults: 3,
|
||||
responseMapping: [],
|
||||
}
|
||||
|
||||
export type ZemanticAiResponse = z.infer<typeof zemanticSearchResponseSchema>
|
||||
export type ZemanticAiCredentials = z.infer<typeof zemanticAiCredentialsSchema>
|
||||
export type ZemanticAiOptions = z.infer<typeof zemanticAiOptionsSchema>
|
||||
export type ZemanticAiBlock = z.infer<typeof zemanticAiBlockSchema>
|
||||
@@ -48,6 +48,7 @@ import {
|
||||
import { jumpBlockSchema } from './logic/jump'
|
||||
import { pictureChoiceBlockSchema } from './inputs/pictureChoice'
|
||||
import { Item } from '../items'
|
||||
import { zemanticAiBlockSchema } from './integrations/zemanticAi'
|
||||
|
||||
export type DraggableBlock =
|
||||
| BubbleBlock
|
||||
@@ -127,6 +128,7 @@ export const blockSchema = z.discriminatedUnion('type', [
|
||||
webhookBlockSchema,
|
||||
zapierBlockSchema,
|
||||
pixelBlockSchema,
|
||||
zemanticAiBlockSchema,
|
||||
])
|
||||
|
||||
export type Block = z.infer<typeof blockSchema>
|
||||
|
||||
@@ -4,6 +4,7 @@ import { googleSheetsCredentialsSchema } from './blocks/integrations/googleSheet
|
||||
import { openAICredentialsSchema } from './blocks/integrations/openai'
|
||||
import { smtpCredentialsSchema } from './blocks/integrations/sendEmail'
|
||||
import { whatsAppCredentialsSchema } from './whatsapp'
|
||||
import { zemanticAiCredentialsSchema } from './blocks'
|
||||
|
||||
export const credentialsSchema = z.discriminatedUnion('type', [
|
||||
smtpCredentialsSchema,
|
||||
@@ -11,6 +12,7 @@ export const credentialsSchema = z.discriminatedUnion('type', [
|
||||
stripeCredentialsSchema,
|
||||
openAICredentialsSchema,
|
||||
whatsAppCredentialsSchema,
|
||||
zemanticAiCredentialsSchema,
|
||||
])
|
||||
|
||||
export type Credentials = z.infer<typeof credentialsSchema>
|
||||
|
||||
Reference in New Issue
Block a user