2
0

Introducing The Forge (#1072)

The Forge allows anyone to easily create their own Typebot Block.

Closes #380
This commit is contained in:
Baptiste Arnaud
2023-12-13 10:22:02 +01:00
committed by GitHub
parent c373108b55
commit 5e019bbb22
184 changed files with 42659 additions and 37411 deletions

View File

@ -0,0 +1,110 @@
import { createAction, option } from '@typebot.io/forge'
import { isDefined } from '@typebot.io/lib'
import { ZemanticAiResponse } from '../types'
import { got } from 'got'
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.',
input: 'textarea',
}),
prompt: option.string.layout({
accordion: 'Advanced settings',
label: 'Prompt',
moreInfoTooltip: 'Prompt to send to the summarization LLM.',
input: '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: ZemanticAiResponse = await got
.post(apiBaseUrl, {
headers: {
Authorization: `Bearer ${apiKey}`,
},
json: {
projectId,
query,
maxResults,
summarize: true,
summaryOptions: {
system_prompt: systemPrompt,
prompt: prompt,
},
},
})
.json()
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

@ -0,0 +1,15 @@
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...',
helperText:
'You can generate an API key [here](https://zemantic.ai/dashboard/settings).',
}),
}),
} satisfies AuthDefinition

View File

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

View File

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

View File

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

View File

@ -0,0 +1,21 @@
import React from '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

@ -0,0 +1,16 @@
{
"name": "@typebot.io/zemantic-ai-block",
"version": "1.0.0",
"description": "",
"main": "index.ts",
"keywords": [],
"license": "ISC",
"devDependencies": {
"@typebot.io/forge": "workspace:*",
"@typebot.io/tsconfig": "workspace:*",
"@types/react": "18.2.15",
"typescript": "5.3.2",
"@typebot.io/lib": "workspace:*",
"got": "12.6.0"
}
}

View File

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

View File

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