2
0

(openai) Add create speech OpenAI action

Closes #1025
This commit is contained in:
Baptiste Arnaud
2023-11-20 10:32:38 +01:00
parent f6ac3891f8
commit 1a44bf4935
23 changed files with 1772 additions and 74 deletions

View File

@@ -25,6 +25,7 @@ export const listModels = authenticatedProcedure
workspaceId: z.string(),
baseUrl: z.string(),
apiVersion: z.string().optional(),
type: z.enum(['gpt', 'tts']),
})
)
.output(
@@ -34,7 +35,7 @@ export const listModels = authenticatedProcedure
)
.query(
async ({
input: { credentialsId, workspaceId, baseUrl, apiVersion },
input: { credentialsId, workspaceId, baseUrl, apiVersion, type },
ctx: { user },
}) => {
const workspace = await prisma.workspace.findFirst({
@@ -97,6 +98,7 @@ export const listModels = authenticatedProcedure
return {
models:
models.data
.filter((model) => model.id.includes(type))
.sort((a, b) => b.created - a.created)
.map((model) => model.id) ?? [],
}

View File

@@ -9,6 +9,7 @@ type Props = {
apiVersion?: string
credentialsId: string
defaultValue?: string
type: 'gpt' | 'tts'
onChange: (model: string | undefined) => void
}
@@ -18,6 +19,7 @@ export const ModelsDropdown = ({
defaultValue,
onChange,
credentialsId,
type,
}: Props) => {
const { workspace } = useWorkspace()
const { showToast } = useToast()
@@ -28,6 +30,7 @@ export const ModelsDropdown = ({
baseUrl: baseUrl ?? defaultOpenAIOptions.baseUrl,
workspaceId: workspace?.id as string,
apiVersion,
type,
},
{
enabled: !!workspace,

View File

@@ -1,29 +1,24 @@
import { SetVariableLabel } from '@/components/SetVariableLabel'
import { useTypebot } from '@/features/editor/providers/TypebotProvider'
import { Stack, Text } from '@chakra-ui/react'
import {
ChatCompletionOpenAIOptions,
CreateImageOpenAIOptions,
OpenAIBlock,
} from '@typebot.io/schemas/features/blocks/integrations/openai'
import { OpenAIBlock } from '@typebot.io/schemas/features/blocks/integrations/openai'
type Props = {
task: NonNullable<OpenAIBlock['options']>['task']
responseMapping:
| ChatCompletionOpenAIOptions['responseMapping']
| CreateImageOpenAIOptions['responseMapping']
options: OpenAIBlock['options']
}
export const OpenAINodeBody = ({ task, responseMapping }: Props) => {
export const OpenAINodeBody = ({ options }: Props) => {
const { typebot } = useTypebot()
return (
<Stack>
<Text color={task ? 'currentcolor' : 'gray.500'} noOfLines={1}>
{task ?? 'Configure...'}
<Text color={options?.task ? 'currentcolor' : 'gray.500'} noOfLines={1}>
{options?.task ?? 'Configure...'}
</Text>
{typebot &&
responseMapping
options &&
'responseMapping' in options &&
options.responseMapping
?.map((mapping) => mapping.variableId)
.map((variableId, idx) =>
variableId ? (
@@ -34,6 +29,15 @@ export const OpenAINodeBody = ({ task, responseMapping }: Props) => {
/>
) : null
)}
{typebot &&
options &&
'saveUrlInVariableId' in options &&
options.saveUrlInVariableId && (
<SetVariableLabel
variables={typebot.variables}
variableId={options.saveUrlInVariableId}
/>
)}
</Stack>
)
}

View File

@@ -13,6 +13,7 @@ import { CredentialsDropdown } from '@/features/credentials/components/Credentia
import {
ChatCompletionOpenAIOptions,
CreateImageOpenAIOptions,
CreateSpeechOpenAIOptions,
OpenAIBlock,
} from '@typebot.io/schemas/features/blocks/integrations/openai'
import { OpenAICredentialsModal } from './OpenAICredentialsModal'
@@ -24,6 +25,7 @@ import {
defaultOpenAIOptions,
openAITasks,
} from '@typebot.io/schemas/features/blocks/integrations/openai/constants'
import { OpenAICreateSpeechSettings } from './audio/OpenAICreateSpeechSettings'
type OpenAITask = (typeof openAITasks)[number]
@@ -47,15 +49,10 @@ export const OpenAISettings = ({
}
const updateTask = (task: OpenAITask) => {
switch (task) {
case 'Create chat completion': {
onOptionsChange({
credentialsId: options?.credentialsId,
task,
})
break
}
}
onOptionsChange({
credentialsId: options?.credentialsId,
task,
} as OpenAIBlock['options'])
}
const updateBaseUrl = (baseUrl: string) => {
@@ -142,9 +139,12 @@ const OpenAITaskSettings = ({
options,
onOptionsChange,
}: {
options: ChatCompletionOpenAIOptions | CreateImageOpenAIOptions
options:
| ChatCompletionOpenAIOptions
| CreateImageOpenAIOptions
| CreateSpeechOpenAIOptions
onOptionsChange: (options: OpenAIBlock['options']) => void
}) => {
}): JSX.Element | null => {
switch (options.task) {
case 'Create chat completion': {
return (
@@ -154,6 +154,14 @@ const OpenAITaskSettings = ({
/>
)
}
case 'Create speech': {
return (
<OpenAICreateSpeechSettings
options={options}
onOptionsChange={onOptionsChange}
/>
)
}
case 'Create image': {
return null
}

View File

@@ -0,0 +1,98 @@
import { CreateSpeechOpenAIOptions } from '@typebot.io/schemas/features/blocks/integrations/openai'
import { FormControl, FormLabel, Stack, Text } from '@chakra-ui/react'
import { TextLink } from '@/components/TextLink'
import { ModelsDropdown } from '../ModelsDropdown'
import { Textarea } from '@/components/inputs'
import { DropdownList } from '@/components/DropdownList'
import { openAIVoices } from '@typebot.io/schemas/features/blocks/integrations/openai/constants'
import { VariableSearchInput } from '@/components/inputs/VariableSearchInput'
import { Variable } from '@typebot.io/schemas'
const apiReferenceUrl =
'https://platform.openai.com/docs/api-reference/audio/createSpeech'
type Props = {
options: CreateSpeechOpenAIOptions
onOptionsChange: (options: CreateSpeechOpenAIOptions) => void
}
export const OpenAICreateSpeechSettings = ({
options,
onOptionsChange,
}: Props) => {
const updateModel = (model: string | undefined) => {
onOptionsChange({
...options,
model,
})
}
const updateInput = (input: string | undefined) => {
onOptionsChange({
...options,
input,
})
}
const updateVoice = (voice: (typeof openAIVoices)[number]) => {
onOptionsChange({
...options,
voice,
})
}
const updateSaveUrlInVariableId = (
variable: Pick<Variable, 'id' | 'name'> | undefined
) => {
onOptionsChange({
...options,
saveUrlInVariableId: variable?.id,
})
}
return (
<Stack spacing={4} pt="2">
<Text fontSize="sm" color="gray.500">
Read the{' '}
<TextLink href={apiReferenceUrl} isExternal>
API reference
</TextLink>{' '}
to better understand the available options.
</Text>
{options.credentialsId && (
<>
<ModelsDropdown
credentialsId={options.credentialsId}
defaultValue={options.model}
baseUrl={options.baseUrl}
apiVersion={options.apiVersion}
type="tts"
onChange={updateModel}
/>
<Textarea
defaultValue={options.input}
onChange={updateInput}
label="Input:"
/>
<FormControl>
<FormLabel>Voice:</FormLabel>
<DropdownList
currentItem={options.voice}
onItemSelect={updateVoice}
items={openAIVoices}
placeholder="Select a voice"
w="full"
/>
</FormControl>
<FormControl>
<FormLabel>Save URL:</FormLabel>
<VariableSearchInput
initialVariableId={options.saveUrlInVariableId}
onSelectVariable={updateSaveUrlInVariableId}
/>
</FormControl>
</>
)}
</Stack>
)
}

View File

@@ -13,7 +13,7 @@ import {
import { TextLink } from '@/components/TextLink'
import { ChatCompletionResponseItem } from './ChatCompletionResponseItem'
import { NumberInput } from '@/components/inputs'
import { ModelsDropdown } from './ModelsDropdown'
import { ModelsDropdown } from '../ModelsDropdown'
const apiReferenceUrl =
'https://platform.openai.com/docs/api-reference/chat/create'
@@ -79,6 +79,7 @@ export const OpenAIChatCompletionSettings = ({
defaultValue={options.model}
baseUrl={options.baseUrl}
apiVersion={options.apiVersion}
type="gpt"
onChange={updateModel}
/>
<Accordion allowMultiple>

View File

@@ -145,16 +145,7 @@ export const BlockNodeContent = ({
return <ChatwootNodeBody block={block} />
}
case IntegrationBlockType.OPEN_AI: {
return (
<OpenAINodeBody
task={block.options?.task}
responseMapping={
block.options && 'responseMapping' in block.options
? block.options.responseMapping
: []
}
/>
)
return <OpenAINodeBody options={block.options} />
}
case IntegrationBlockType.PIXEL: {
return <PixelNodeBody options={block.options} />

View File

@@ -94,6 +94,14 @@ export const templates: TemplateProps[] = [
description:
'A bot that uses the ChatGPT model to generate responses based on the user input',
},
{
name: 'Audio ChatGPT',
emoji: '🤖',
fileName: 'audio-chat-gpt.json',
description:
'An audio AI bot that uses the OpenAI block to generate responses based on the user input',
isNew: true,
},
{
name: 'ChatGPT personas',
emoji: '🎭',
@@ -117,4 +125,12 @@ export const templates: TemplateProps[] = [
description:
'You are a dog insurance company. This bot allows you to collect information about the dog and provide a quote.',
},
{
name: 'OpenAI conditions',
emoji: '🧠',
fileName: 'openai-conditions.json',
isNew: true,
description:
'This is an example of how you can use the OpenAI block to take smart decisions based on the user input and redirect the conversation to the right path.',
},
]