2
0

Remove ZemanticAI block

Closes #1656, closes #1652
This commit is contained in:
Baptiste Arnaud
2024-07-22 17:24:02 +02:00
parent 71d09cdf7c
commit ec2a53fac1
50 changed files with 5 additions and 1821 deletions

View File

@@ -1,147 +0,0 @@
import { SessionState } from '@typebot.io/schemas'
import {
ZemanticAiBlock,
ZemanticAiCredentials,
ZemanticAiResponse,
} from '@typebot.io/schemas/features/blocks/integrations/zemanticAi'
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'
import { updateVariablesInSession } from '@typebot.io/variables/updateVariablesInSession'
import { getCredentials } from '../../../queries/getCredentials'
import { parseAnswers } from '@typebot.io/results/parseAnswers'
const URL = 'https://api.zemantic.ai/v1/search-documents'
export const executeZemanticAiBlock = async (
state: SessionState,
block: ZemanticAiBlock
): Promise<ExecuteIntegrationResponse> => {
let newSessionState = state
let setVariableHistory = []
if (!block.options?.credentialsId)
return {
outgoingEdgeId: block.outgoingEdgeId,
}
const credentials = await getCredentials(block.options.credentialsId)
if (!credentials) {
return {
outgoingEdgeId: block.outgoingEdgeId,
logs: [
{
status: 'error',
description: 'Make sure to select a Zemantic AI account',
},
],
}
}
const { apiKey } = (await decrypt(
credentials.data,
credentials.iv
)) as ZemanticAiCredentials['data']
const { typebot, answers } = newSessionState.typebotsQueue[0]
const templateVars = parseAnswers({
variables: typebot.variables,
answers: answers,
})
try {
const res: ZemanticAiResponse = await ky
.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))
let newVariables = []
switch (r.valueToExtract) {
case 'Summary':
if (isDefined(variable) && !isEmpty(res.summary)) {
newVariables.push({ ...variable, value: res.summary })
}
break
case 'Results':
if (isDefined(variable) && res.results.length) {
newVariables.push({
...variable,
value: JSON.stringify(res.results),
})
}
break
default:
break
}
if (newVariables.length > 0) {
const { newSetVariableHistory, updatedState } =
updateVariablesInSession({
newVariables,
state: newSessionState,
currentBlockId: block.id,
})
newSessionState = updatedState
setVariableHistory.push(...newSetVariableHistory)
}
}
} catch (e) {
console.error(e)
return {
startTimeShouldBeUpdated: true,
outgoingEdgeId: block.outgoingEdgeId,
logs: [
{
status: 'error',
description: 'Could not execute Zemantic AI request',
},
],
newSetVariableHistory: setVariableHistory,
}
}
return {
outgoingEdgeId: block.outgoingEdgeId,
newSessionState,
startTimeShouldBeUpdated: true,
}
}
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
}

View File

@@ -4,7 +4,6 @@ import { executeChatwootBlock } from './blocks/integrations/chatwoot/executeChat
import { executeGoogleAnalyticsBlock } from './blocks/integrations/legacy/googleAnalytics/executeGoogleAnalyticsBlock'
import { executeGoogleSheetBlock } from './blocks/integrations/googleSheets/executeGoogleSheetBlock'
import { executePixelBlock } from './blocks/integrations/pixel/executePixelBlock'
import { executeZemanticAiBlock } from './blocks/integrations/zemanticAi/executeZemanticAiBlock'
import { IntegrationBlock, SessionState } from '@typebot.io/schemas'
import { ExecuteIntegrationResponse } from './types'
import { IntegrationBlockType } from '@typebot.io/schemas/features/blocks/integrations/constants'
@@ -50,11 +49,6 @@ export const executeIntegration =
}
case IntegrationBlockType.PIXEL:
return executePixelBlock(state, block)
case IntegrationBlockType.ZEMANTIC_AI:
return {
...(await executeZemanticAiBlock(state, block)),
startTimeShouldBeUpdated: true,
}
default:
return {
...(await executeForgedBlock(state, block)),

View File

@@ -1,110 +0,0 @@
import { createAction, option } from '@typebot.io/forge'
import { isDefined } from '@typebot.io/lib'
import { ZemanticAiResponse } from '../types'
import ky from 'ky'
import { apiBaseUrl } from '../constants'
import { auth } from '../auth'
import { baseOptions } from '../baseOptions'
export const searchDocuments = createAction({
baseOptions,
auth,
name: 'Search documents',
options: option.object({
query: option.string.layout({
label: 'Query',
placeholder: 'Content',
moreInfoTooltip:
'The question you want to ask or search against the documents in the project.',
}),
maxResults: option.number.layout({
label: 'Max results',
placeholder: 'i.e. 3',
defaultValue: 3,
moreInfoTooltip:
'The maximum number of document chunk results to return from your search.',
}),
systemPrompt: option.string.layout({
accordion: 'Advanced settings',
label: 'System prompt',
moreInfoTooltip:
'System prompt to send to the summarization LLM. This is prepended to the prompt and helps guide system behavior.',
inputType: 'textarea',
}),
prompt: option.string.layout({
accordion: 'Advanced settings',
label: 'Prompt',
moreInfoTooltip: 'Prompt to send to the summarization LLM.',
inputType: 'textarea',
}),
responseMapping: option
.saveResponseArray([
'Summary',
'Document IDs',
'Texts',
'Scores',
] as const)
.layout({
accordion: 'Save response',
}),
}),
getSetVariableIds: ({ responseMapping }) =>
responseMapping?.map((r) => r.variableId).filter(isDefined) ?? [],
run: {
server: async ({
credentials: { apiKey },
options: {
maxResults,
projectId,
prompt,
query,
responseMapping,
systemPrompt,
},
variables,
}) => {
const res = await ky
.post(apiBaseUrl, {
headers: {
Authorization: `Bearer ${apiKey}`,
},
json: {
projectId,
query,
maxResults,
summarize: true,
summaryOptions: {
system_prompt: systemPrompt,
prompt: prompt,
},
},
})
.json<ZemanticAiResponse>()
responseMapping?.forEach((mapping) => {
if (!mapping.variableId || !mapping.item) return
if (mapping.item === 'Document IDs')
variables.set(
mapping.variableId,
res.results.map((r) => r.documentId)
)
if (mapping.item === 'Texts')
variables.set(
mapping.variableId,
res.results.map((r) => r.text)
)
if (mapping.item === 'Scores')
variables.set(
mapping.variableId,
res.results.map((r) => r.score)
)
if (mapping.item === 'Summary')
variables.set(mapping.variableId, res.summary)
})
},
},
})

View File

@@ -1,17 +0,0 @@
import { AuthDefinition, option } from '@typebot.io/forge'
export const auth = {
type: 'encryptedCredentials',
name: 'Zemantic AI account',
schema: option.object({
apiKey: option.string.layout({
label: 'API key',
isRequired: true,
placeholder: 'ze...',
inputType: 'password',
helperText:
'You can generate an API key [here](https://zemantic.ai/dashboard/settings).',
isDebounceDisabled: true,
}),
}),
} satisfies AuthDefinition

View File

@@ -1,8 +0,0 @@
import { option } from '@typebot.io/forge'
export const baseOptions = option.object({
projectId: option.string.layout({
placeholder: 'Select a project',
fetcher: 'fetchProjects',
}),
})

View File

@@ -1 +0,0 @@
export const apiBaseUrl = 'https://api.zemantic.ai/v1/search-documents'

View File

@@ -1,45 +0,0 @@
import { createBlock } from '@typebot.io/forge'
import { ZemanticAiLogo } from './logo'
import ky from 'ky'
import { searchDocuments } from './actions/searchDocuments'
import { auth } from './auth'
import { baseOptions } from './baseOptions'
export const zemanticAiBlock = createBlock({
id: 'zemantic-ai',
name: 'Zemantic AI',
tags: [],
LightLogo: ZemanticAiLogo,
auth,
options: baseOptions,
fetchers: [
{
id: 'fetchProjects',
dependencies: [],
fetch: async ({ credentials }) => {
if (!credentials?.apiKey) return []
const url = 'https://api.zemantic.ai/v1/projects'
const response = await ky
.get(url, {
headers: {
Authorization: `Bearer ${credentials.apiKey}`,
},
})
.json()
const projectsData = response as {
id: string
name: string
}[]
return projectsData.map((project) => ({
label: project.name,
value: project.id,
}))
},
},
],
actions: [searchDocuments],
})

View File

@@ -1,21 +0,0 @@
/** @jsxImportSource react */
export const ZemanticAiLogo = (props: React.SVGProps<SVGSVGElement>) => (
<svg 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>
</svg>
)

View File

@@ -1,16 +0,0 @@
{
"name": "@typebot.io/zemantic-ai-block",
"version": "1.0.0",
"description": "",
"main": "index.ts",
"keywords": [],
"license": "AGPL-3.0-or-later",
"devDependencies": {
"@typebot.io/forge": "workspace:*",
"@typebot.io/tsconfig": "workspace:*",
"@types/react": "18.2.15",
"typescript": "5.4.5",
"@typebot.io/lib": "workspace:*",
"ky": "1.2.4"
}
}

View File

@@ -1,10 +0,0 @@
// Do not edit this file manually
import { parseBlockCredentials, parseBlockSchema } from '@typebot.io/forge'
import { zemanticAiBlock } from '.'
import { auth } from './auth'
export const zemanticAiBlockSchema = parseBlockSchema(zemanticAiBlock)
export const zemanticAiCredentialsSchema = parseBlockCredentials(
zemanticAiBlock.id,
auth.schema
)

View File

@@ -1,11 +0,0 @@
{
"extends": "@typebot.io/tsconfig/base.json",
"include": ["**/*.ts", "**/*.tsx"],
"exclude": ["node_modules"],
"compilerOptions": {
"lib": ["ESNext", "DOM"],
"noEmit": true,
"jsx": "preserve",
"jsxImportSource": "react"
}
}

View File

@@ -1,4 +0,0 @@
export type ZemanticAiResponse = {
results: { documentId: string; text: string; score: number }[]
summary: string
}

View File

@@ -3,7 +3,6 @@ import { ForgedBlock } from './types'
export const forgedBlockIds = [
'openai',
'zemantic-ai',
'cal-com',
'chat-node',
'qr-code',

View File

@@ -14,14 +14,11 @@ import { openAIBlock } from '@typebot.io/openai-block'
import { openAICredentialsSchema } from '@typebot.io/openai-block/schemas'
import { togetherAiBlock } from '@typebot.io/together-ai-block'
import { togetherAiCredentialsSchema } from '@typebot.io/together-ai-block/schemas'
import { zemanticAiBlock } from '@typebot.io/zemantic-ai-block'
import { zemanticAiCredentialsSchema } from '@typebot.io/zemantic-ai-block/schemas'
import { nocodbBlock } from '@typebot.io/nocodb-block'
import { nocodbCredentialsSchema } from '@typebot.io/nocodb-block/schemas'
export const forgedCredentialsSchemas = {
[openAIBlock.id]: openAICredentialsSchema,
[zemanticAiBlock.id]: zemanticAiCredentialsSchema,
[chatNodeBlock.id]: chatNodeCredentialsSchema,
[difyAiBlock.id]: difyAiCredentialsSchema,
[mistralBlock.id]: mistralCredentialsSchema,

View File

@@ -8,13 +8,11 @@ import { mistralBlock } from '@typebot.io/mistral-block'
import { qrCodeBlock } from '@typebot.io/qrcode-block'
import { chatNodeBlock } from '@typebot.io/chat-node-block'
import { calComBlock } from '@typebot.io/cal-com-block'
import { zemanticAiBlock } from '@typebot.io/zemantic-ai-block'
import { openAIBlock } from '@typebot.io/openai-block'
import { nocodbBlock } from '@typebot.io/nocodb-block'
export const forgedBlocks = {
[openAIBlock.id]: openAIBlock,
[zemanticAiBlock.id]: zemanticAiBlock,
[calComBlock.id]: calComBlock,
[chatNodeBlock.id]: chatNodeBlock,
[qrCodeBlock.id]: qrCodeBlock,

View File

@@ -8,7 +8,6 @@
"devDependencies": {
"@typebot.io/forge": "workspace:*",
"@typebot.io/openai-block": "workspace:*",
"@typebot.io/zemantic-ai-block": "workspace:*",
"@typebot.io/cal-com-block": "workspace:*",
"@typebot.io/chat-node-block": "workspace:*",
"@typebot.io/qrcode-block": "workspace:*",

View File

@@ -19,14 +19,11 @@ import { qrCodeBlock } from '@typebot.io/qrcode-block'
import { qrCodeBlockSchema } from '@typebot.io/qrcode-block/schemas'
import { togetherAiBlock } from '@typebot.io/together-ai-block'
import { togetherAiBlockSchema } from '@typebot.io/together-ai-block/schemas'
import { zemanticAiBlock } from '@typebot.io/zemantic-ai-block'
import { zemanticAiBlockSchema } from '@typebot.io/zemantic-ai-block/schemas'
import { nocodbBlock } from '@typebot.io/nocodb-block'
import { nocodbBlockSchema } from '@typebot.io/nocodb-block/schemas'
export const forgedBlockSchemas = {
[openAIBlock.id]: openAIBlockSchema,
[zemanticAiBlock.id]: zemanticAiBlockSchema,
[calComBlock.id]: calComBlockSchema,
[chatNodeBlock.id]: chatNodeBlockSchema,
[qrCodeBlock.id]: qrCodeBlockSchema,

View File

@@ -9,5 +9,4 @@ export enum IntegrationBlockType {
PABBLY_CONNECT = 'Pabbly',
CHATWOOT = 'Chatwoot',
PIXEL = 'Pixel',
ZEMANTIC_AI = 'Zemantic AI',
}

View File

@@ -7,5 +7,4 @@ export * from './sendEmail'
export * from './webhook'
export * from './zapier'
export * from './pixel'
export * from './zemanticAi'
export * from './schema'

View File

@@ -5,7 +5,6 @@ import { googleSheetsBlockSchemas } from './googleSheets'
import { openAIBlockSchema } from './openai'
import { pixelBlockSchema } from './pixel/schema'
import { sendEmailBlockSchema } from './sendEmail'
import { zemanticAiBlockSchema } from './zemanticAi'
import { zapierBlockSchemas } from './zapier'
import { httpBlockSchemas } from './webhook'
import { makeComBlockSchemas } from './makeCom'
@@ -23,7 +22,6 @@ export const integrationBlockSchemas = {
httpBlockSchemas.v5,
zapierBlockSchemas.v5,
pixelBlockSchema,
zemanticAiBlockSchema,
],
v6: [
chatwootBlockSchema,
@@ -36,7 +34,6 @@ export const integrationBlockSchemas = {
httpBlockSchemas.v6,
zapierBlockSchemas.v6,
pixelBlockSchema,
zemanticAiBlockSchema,
],
} as const

View File

@@ -1,11 +0,0 @@
import { ZemanticAiBlock } from './schema'
export const searchResponseValues = ['Summary', 'Results'] as const
export const defaultZemanticAiOptions = {
maxResults: 3,
} as const satisfies ZemanticAiBlock['options']
export const defaultZemanticAiResponseMappingItem = {
valueToExtract: 'Summary',
} as const

View File

@@ -1 +0,0 @@
export * from './schema'

View File

@@ -1,57 +0,0 @@
import { z } from '../../../../zod'
import { blockBaseSchema, credentialsBaseSchema } from '../../shared'
import { IntegrationBlockType } from '../constants'
import { searchResponseValues } from './constants'
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(),
})
)
.optional(),
})
export const zemanticAiBlockSchema = blockBaseSchema.merge(
z.object({
type: z.enum([IntegrationBlockType.ZEMANTIC_AI]),
blockId: z.string().optional(),
options: zemanticAiOptionsSchema.optional(),
})
)
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 type ZemanticAiResponse = z.infer<typeof zemanticSearchResponseSchema>
export type ZemanticAiCredentials = z.infer<typeof zemanticAiCredentialsSchema>
export type ZemanticAiBlock = z.infer<typeof zemanticAiBlockSchema>

View File

@@ -1,5 +1,4 @@
import { z } from '../zod'
import { zemanticAiCredentialsSchema } from './blocks'
import { stripeCredentialsSchema } from './blocks/inputs/payment/schema'
import { googleSheetsCredentialsSchema } from './blocks/integrations/googleSheets/schema'
import { smtpCredentialsSchema } from './blocks/integrations/sendEmail'
@@ -11,30 +10,19 @@ const credentialsSchema = z.discriminatedUnion('type', [
googleSheetsCredentialsSchema,
stripeCredentialsSchema,
whatsAppCredentialsSchema,
zemanticAiCredentialsSchema,
...Object.values(forgedCredentialsSchemas),
])
export type Credentials = z.infer<typeof credentialsSchema>
export type CredentialsWithoutLegacy = Exclude<
Credentials,
{
type: 'zemanticAi'
}
>
export const credentialsTypes = [
'smtp',
'google sheets',
'stripe',
'whatsApp',
'zemanticAi',
...(Object.keys(forgedCredentialsSchemas) as Array<
keyof typeof forgedCredentialsSchemas
>),
] as const
export const credentialsTypeSchema = z.enum(credentialsTypes)
export const legacyCredentialsTypes = ['zemanticAi']