2
0

Add OpenAI block

Also migrate credentials to tRPC

Closes #253
This commit is contained in:
Baptiste Arnaud
2023-03-09 08:46:36 +01:00
parent 97cfdfe79f
commit ff04edf139
86 changed files with 2583 additions and 1055 deletions

View File

@ -1,35 +1,29 @@
import { z } from 'zod'
import { schemaForType } from './utils'
import { Answer as AnswerPrisma, Prisma } from 'db'
export const answerSchema = schemaForType<AnswerPrisma>()(
z.object({
createdAt: z.date(),
resultId: z.string(),
blockId: z.string(),
groupId: z.string(),
variableId: z.string().nullable(),
content: z.string(),
storageUsed: z.number().nullable(),
})
)
export const answerSchema = z.object({
createdAt: z.date(),
resultId: z.string(),
blockId: z.string(),
groupId: z.string(),
variableId: z.string().nullable(),
content: z.string(),
storageUsed: z.number().nullable(),
}) satisfies z.ZodType<AnswerPrisma>
export const answerInputSchema =
schemaForType<Prisma.AnswerUncheckedUpdateInput>()(
answerSchema
.omit({
createdAt: true,
resultId: true,
variableId: true,
storageUsed: true,
})
.and(
z.object({
variableId: z.string().nullish(),
storageUsed: z.number().nullish(),
})
)
)
export const answerInputSchema = answerSchema
.omit({
createdAt: true,
resultId: true,
variableId: true,
storageUsed: true,
})
.and(
z.object({
variableId: z.string().nullish(),
storageUsed: z.number().nullish(),
})
) satisfies z.ZodType<Prisma.AnswerUncheckedUpdateInput>
export type Stats = {
totalViews: number

View File

@ -1,4 +1,5 @@
import { z } from 'zod'
import { Credentials as CredentialsFromPrisma } from 'db'
export const blockBaseSchema = z.object({
id: z.string(),
@ -9,3 +10,11 @@ export const blockBaseSchema = z.object({
export const optionBaseSchema = z.object({
variableId: z.string().optional(),
})
export const credentialsBaseSchema = z.object({
id: z.string(),
createdAt: z.date(),
workspaceId: z.string(),
name: z.string(),
iv: z.string(),
}) satisfies z.ZodType<Omit<CredentialsFromPrisma, 'data' | 'type'>>

View File

@ -1,5 +1,9 @@
import { z } from 'zod'
import { optionBaseSchema, blockBaseSchema } from '../../baseSchemas'
import {
optionBaseSchema,
blockBaseSchema,
credentialsBaseSchema,
} from '../../baseSchemas'
import { InputBlockType } from '../enums'
import { PaymentProvider } from './enums'
@ -43,6 +47,22 @@ export const paymentInputSchema = blockBaseSchema.and(
})
)
export const stripeCredentialsSchema = z
.object({
type: z.literal('stripe'),
data: z.object({
live: z.object({
secretKey: z.string(),
publicKey: z.string(),
}),
test: z.object({
secretKey: z.string().optional(),
publicKey: z.string().optional(),
}),
}),
})
.merge(credentialsBaseSchema)
export const defaultPaymentInputOptions: PaymentInputOptions = {
provider: PaymentProvider.STRIPE,
labels: { button: 'Pay', success: 'Success' },
@ -54,3 +74,4 @@ export type PaymentInputOptions = z.infer<typeof paymentInputOptionsSchema>
export type PaymentInputRuntimeOptions = z.infer<
typeof paymentInputRuntimeOptionsSchema
>
export type StripeCredentials = z.infer<typeof stripeCredentialsSchema>

View File

@ -1,5 +1,6 @@
export enum IntegrationBlockType {
GOOGLE_SHEETS = 'Google Sheets',
OPEN_AI = 'OpenAI',
GOOGLE_ANALYTICS = 'Google Analytics',
WEBHOOK = 'Webhook',
EMAIL = 'Email',

View File

@ -2,7 +2,7 @@ import { z } from 'zod'
import { ComparisonOperators, LogicalOperator } from '../../logic/condition'
import { IntegrationBlockType } from '../enums'
import { GoogleSheetsAction } from './enums'
import { blockBaseSchema } from '../../baseSchemas'
import { blockBaseSchema, credentialsBaseSchema } from '../../baseSchemas'
const cellSchema = z.object({
column: z.string().optional(),
@ -69,6 +69,20 @@ export const googleSheetsBlockSchema = blockBaseSchema.and(
})
)
export const googleSheetsCredentialsSchema = z
.object({
type: z.literal('google sheets'),
data: z.object({
refresh_token: z.string().nullish(),
expiry_date: z.number().nullish(),
access_token: z.string().nullish(),
token_type: z.string().nullish(),
id_token: z.string().nullish(),
scope: z.string().optional(),
}),
})
.merge(credentialsBaseSchema)
export const defaultGoogleSheetsOptions: GoogleSheetsOptions = {}
export const defaultGoogleSheetsGetOptions = (
@ -129,3 +143,6 @@ export type GoogleSheetsUpdateRowOptions = z.infer<
export type Cell = z.infer<typeof cellSchema>
export type ExtractingCell = z.infer<typeof extractingCellSchema>
export type RowsFilterComparison = z.infer<typeof rowsFilterComparisonSchema>
export type GoogleSheetsCredentials = z.infer<
typeof googleSheetsCredentialsSchema
>

View File

@ -0,0 +1,114 @@
import { z } from 'zod'
import { blockBaseSchema, credentialsBaseSchema } from '../baseSchemas'
import { IntegrationBlockType } from './enums'
export const openAITasks = ['Create chat completion', 'Create image'] as const
export const chatCompletionModels = [
'gpt-3.5-turbo',
'gpt-3.5-turbo-0301',
] as const
export const chatCompletionMessageRoles = [
'system',
'user',
'assistant',
] as const
export const chatCompletionResponseValues = [
'Message content',
'Total tokens',
] as const
const openAIBaseOptionsSchema = z.object({
credentialsId: z.string().optional(),
})
const initialOptionsSchema = z
.object({
task: z.undefined(),
})
.merge(openAIBaseOptionsSchema)
const chatCompletionMessageSchema = z.object({
id: z.string(),
role: z.enum(chatCompletionMessageRoles).optional(),
content: z.string().optional(),
})
const chatCompletionOptionsSchema = z
.object({
task: z.literal(openAITasks[0]),
model: z.enum(chatCompletionModels),
messages: z.array(chatCompletionMessageSchema),
responseMapping: z.array(
z.object({
id: z.string(),
valueToExtract: z.enum(chatCompletionResponseValues),
variableId: z.string().optional(),
})
),
})
.merge(openAIBaseOptionsSchema)
const createImageOptionsSchema = z
.object({
task: z.literal(openAITasks[1]),
prompt: z.string().optional(),
advancedOptions: z.object({
size: z.enum(['256x256', '512x512', '1024x1024']).optional(),
}),
responseMapping: z.array(
z.object({
id: z.string(),
valueToExtract: z.enum(['Image URL']),
variableId: z.string().optional(),
})
),
})
.merge(openAIBaseOptionsSchema)
export const openAIBlockSchema = blockBaseSchema.merge(
z.object({
type: z.enum([IntegrationBlockType.OPEN_AI]),
options: z.discriminatedUnion('task', [
initialOptionsSchema,
chatCompletionOptionsSchema,
createImageOptionsSchema,
]),
})
)
export const openAICredentialsSchema = z
.object({
type: z.literal('openai'),
data: z.object({
apiKey: z.string(),
}),
})
.merge(credentialsBaseSchema)
export const defaultChatCompletionOptions = (
createId: () => string
): ChatCompletionOpenAIOptions => ({
task: 'Create chat completion',
messages: [
{
id: createId(),
},
],
responseMapping: [
{
id: createId(),
valueToExtract: 'Message content',
},
],
model: 'gpt-3.5-turbo',
})
export type OpenAICredentials = z.infer<typeof openAICredentialsSchema>
export type OpenAIBlock = z.infer<typeof openAIBlockSchema>
export type ChatCompletionOpenAIOptions = z.infer<
typeof chatCompletionOptionsSchema
>
export type CreateImageOpenAIOptions = z.infer<typeof createImageOptionsSchema>

View File

@ -1,25 +1,14 @@
import { z } from 'zod'
import { chatwootBlockSchema, chatwootOptionsSchema } from './chatwoot'
import {
googleAnalyticsOptionsSchema,
googleAnalyticsBlockSchema,
} from './googleAnalytics'
import {
googleSheetsOptionsSchema,
googleSheetsBlockSchema,
} from './googleSheets/schemas'
import { chatwootBlockSchema } from './chatwoot'
import { googleAnalyticsBlockSchema } from './googleAnalytics'
import { googleSheetsBlockSchema } from './googleSheets/schemas'
import { makeComBlockSchema } from './makeCom'
import { openAIBlockSchema } from './openai'
import { pabblyConnectBlockSchema } from './pabblyConnect'
import { sendEmailOptionsSchema, sendEmailBlockSchema } from './sendEmail'
import { webhookOptionsSchema, webhookBlockSchema } from './webhook'
import { sendEmailBlockSchema } from './sendEmail'
import { webhookBlockSchema } from './webhook'
import { zapierBlockSchema } from './zapier'
const integrationBlockOptionsSchema = googleSheetsOptionsSchema
.or(googleAnalyticsOptionsSchema)
.or(webhookOptionsSchema)
.or(sendEmailOptionsSchema)
.or(chatwootOptionsSchema)
export const integrationBlockSchema = googleSheetsBlockSchema
.or(googleAnalyticsBlockSchema)
.or(webhookBlockSchema)
@ -28,8 +17,7 @@ export const integrationBlockSchema = googleSheetsBlockSchema
.or(makeComBlockSchema)
.or(pabblyConnectBlockSchema)
.or(chatwootBlockSchema)
.or(openAIBlockSchema)
export type IntegrationBlock = z.infer<typeof integrationBlockSchema>
export type IntegrationBlockOptions = z.infer<
typeof integrationBlockOptionsSchema
>
export type IntegrationBlockOptions = IntegrationBlock['options']

View File

@ -1,5 +1,5 @@
import { z } from 'zod'
import { blockBaseSchema } from '../baseSchemas'
import { blockBaseSchema, credentialsBaseSchema } from '../baseSchemas'
import { IntegrationBlockType } from './enums'
export const sendEmailOptionsSchema = z.object({
@ -22,6 +22,23 @@ export const sendEmailBlockSchema = blockBaseSchema.and(
})
)
export const smtpCredentialsSchema = z
.object({
type: z.literal('smtp'),
data: z.object({
host: z.string().optional(),
username: z.string().optional(),
password: z.string().optional(),
isTlsEnabled: z.boolean().optional(),
port: z.number(),
from: z.object({
email: z.string().optional(),
name: z.string().optional(),
}),
}),
})
.merge(credentialsBaseSchema)
export const defaultSendEmailOptions: SendEmailOptions = {
credentialsId: 'default',
isCustomBody: false,
@ -30,3 +47,4 @@ export const defaultSendEmailOptions: SendEmailOptions = {
export type SendEmailBlock = z.infer<typeof sendEmailBlockSchema>
export type SendEmailOptions = z.infer<typeof sendEmailOptionsSchema>
export type SmtpCredentials = z.infer<typeof smtpCredentialsSchema>

View File

@ -1,58 +1,14 @@
import { Credentials as CredentialsFromPrisma } from 'db'
import { z } from 'zod'
import { stripeCredentialsSchema } from './blocks/inputs/payment/schemas'
import { googleSheetsCredentialsSchema } from './blocks/integrations/googleSheets/schemas'
import { openAICredentialsSchema } from './blocks/integrations/openai'
import { smtpCredentialsSchema } from './blocks/integrations/sendEmail'
export type Credentials =
| SmtpCredentials
| GoogleSheetsCredentials
| StripeCredentials
export const credentialsSchema = z.discriminatedUnion('type', [
smtpCredentialsSchema,
googleSheetsCredentialsSchema,
stripeCredentialsSchema,
openAICredentialsSchema,
])
export type CredentialsBase = Omit<CredentialsFromPrisma, 'data' | 'type'>
export enum CredentialsType {
GOOGLE_SHEETS = 'google sheets',
SMTP = 'smtp',
STRIPE = 'stripe',
}
export type SmtpCredentials = CredentialsBase & {
type: CredentialsType.SMTP
data: SmtpCredentialsData
}
export type GoogleSheetsCredentials = CredentialsBase & {
type: CredentialsType.GOOGLE_SHEETS
data: GoogleSheetsCredentialsData
}
export type StripeCredentials = CredentialsBase & {
type: CredentialsType.STRIPE
data: StripeCredentialsData
}
export type GoogleSheetsCredentialsData = {
refresh_token?: string | null
expiry_date?: number | null
access_token?: string | null
token_type?: string | null
id_token?: string | null
scope?: string
}
export type SmtpCredentialsData = {
host?: string
username?: string
password?: string
isTlsEnabled?: boolean
port: number
from: { email?: string; name?: string }
}
export type StripeCredentialsData = {
live: {
secretKey: string
publicKey: string
}
test?: {
secretKey?: string
publicKey?: string
}
}
export type Credentials = z.infer<typeof credentialsSchema>

View File

@ -1,3 +1,4 @@
import { PublicTypebot as PrismaPublicTypebot } from 'db'
import {
groupSchema,
edgeSchema,
@ -6,24 +7,20 @@ import {
settingsSchema,
typebotSchema,
} from './typebot'
import { PublicTypebot as PublicTypebotPrisma } from 'db'
import { z } from 'zod'
import { schemaForType } from './utils'
export const publicTypebotSchema = schemaForType<PublicTypebotPrisma>()(
z.object({
id: z.string(),
version: z.enum(['3']).nullable(),
createdAt: z.date(),
updatedAt: z.date(),
typebotId: z.string(),
groups: z.array(groupSchema),
edges: z.array(edgeSchema),
variables: z.array(variableSchema),
theme: themeSchema,
settings: settingsSchema,
})
)
export const publicTypebotSchema = z.object({
id: z.string(),
version: z.enum(['3']).nullable(),
createdAt: z.date(),
updatedAt: z.date(),
typebotId: z.string(),
groups: z.array(groupSchema),
edges: z.array(edgeSchema),
variables: z.array(variableSchema),
theme: themeSchema,
settings: settingsSchema,
}) satisfies z.ZodType<PrismaPublicTypebot>
const publicTypebotWithName = publicTypebotSchema.and(
typebotSchema.pick({ name: true, isArchived: true, isClosed: true })

View File

@ -3,19 +3,16 @@ import { answerInputSchema, answerSchema } from './answer'
import { InputBlockType } from './blocks'
import { variableWithValueSchema } from './typebot/variable'
import { Result as ResultPrisma, Log as LogPrisma } from 'db'
import { schemaForType } from './utils'
export const resultSchema = schemaForType<ResultPrisma>()(
z.object({
id: z.string(),
createdAt: z.date(),
typebotId: z.string(),
variables: z.array(variableWithValueSchema),
isCompleted: z.boolean(),
hasStarted: z.boolean().nullable(),
isArchived: z.boolean().nullable(),
})
)
export const resultSchema = z.object({
id: z.string(),
createdAt: z.date(),
typebotId: z.string(),
variables: z.array(variableWithValueSchema),
isCompleted: z.boolean(),
hasStarted: z.boolean().nullable(),
isArchived: z.boolean().nullable(),
}) satisfies z.ZodType<ResultPrisma>
export const resultWithAnswersSchema = resultSchema.and(
z.object({
@ -29,16 +26,14 @@ export const resultWithAnswersInputSchema = resultSchema.and(
})
)
export const logSchema = schemaForType<LogPrisma>()(
z.object({
id: z.string(),
createdAt: z.date(),
resultId: z.string(),
status: z.string(),
description: z.string(),
details: z.string().nullable(),
})
)
export const logSchema = z.object({
id: z.string(),
createdAt: z.date(),
resultId: z.string(),
status: z.string(),
description: z.string(),
details: z.string().nullable(),
}) satisfies z.ZodType<LogPrisma>
export type Result = z.infer<typeof resultSchema>
export type ResultWithAnswers = z.infer<typeof resultWithAnswersSchema>

View File

@ -4,7 +4,6 @@ import { blockSchema } from '../blocks'
import { themeSchema } from './theme'
import { variableSchema } from './variable'
import { Typebot as TypebotPrisma } from 'db'
import { schemaForType } from '../utils'
export const groupSchema = z.object({
id: z.string(),
@ -39,28 +38,26 @@ const resultsTablePreferencesSchema = z.object({
columnsWidth: z.record(z.string(), z.number()),
})
export const typebotSchema = schemaForType<TypebotPrisma>()(
z.object({
version: z.enum(['3']).nullable(),
id: z.string(),
name: z.string(),
groups: z.array(groupSchema),
edges: z.array(edgeSchema),
variables: z.array(variableSchema),
theme: themeSchema,
settings: settingsSchema,
createdAt: z.date(),
updatedAt: z.date(),
icon: z.string().nullable(),
folderId: z.string().nullable(),
publicId: z.string().nullable(),
customDomain: z.string().nullable(),
workspaceId: z.string(),
resultsTablePreferences: resultsTablePreferencesSchema.nullable(),
isArchived: z.boolean(),
isClosed: z.boolean(),
})
)
export const typebotSchema = z.object({
version: z.enum(['3']).nullable(),
id: z.string(),
name: z.string(),
groups: z.array(groupSchema),
edges: z.array(edgeSchema),
variables: z.array(variableSchema),
theme: themeSchema,
settings: settingsSchema,
createdAt: z.date(),
updatedAt: z.date(),
icon: z.string().nullable(),
folderId: z.string().nullable(),
publicId: z.string().nullable(),
customDomain: z.string().nullable(),
workspaceId: z.string(),
resultsTablePreferences: resultsTablePreferencesSchema.nullable(),
isArchived: z.boolean(),
isClosed: z.boolean(),
}) satisfies z.ZodType<TypebotPrisma>
export type Typebot = z.infer<typeof typebotSchema>
export type Target = z.infer<typeof targetSchema>

View File

@ -2,12 +2,6 @@ import { z } from 'zod'
export type IdMap<T> = { [id: string]: T }
export const schemaForType =
<T>() =>
<S extends z.ZodType<T, any, any>>(arg: S) => {
return arg
}
export const variableStringSchema = z.custom<`{{${string}}}`>((val) =>
/^{{.+}}$/g.test(val as string)
)

View File

@ -1,5 +1,4 @@
import { z } from 'zod'
import { schemaForType } from './utils'
import {
Workspace as WorkspacePrisma,
Plan,
@ -9,53 +8,47 @@ import {
WorkspaceInvitation as WorkspaceInvitationPrisma,
} from 'db'
export const workspaceMemberSchema = schemaForType<
export const workspaceMemberSchema = z.object({
workspaceId: z.string(),
user: z.object({
name: z.string().nullable(),
email: z.string().nullable(),
image: z.string().nullable(),
}),
role: z.nativeEnum(WorkspaceRole),
}) satisfies z.ZodType<
Omit<MemberInWorkspacePrisma, 'userId' | 'createdAt' | 'updatedAt'> & {
user: Pick<UserPrisma, 'name' | 'email' | 'image'>
}
>()(
z.object({
workspaceId: z.string(),
user: z.object({
name: z.string().nullable(),
email: z.string().nullable(),
image: z.string().nullable(),
}),
role: z.nativeEnum(WorkspaceRole),
})
)
>
export const workspaceInvitationSchema = schemaForType<
export const workspaceInvitationSchema = z.object({
createdAt: z.date(),
updatedAt: z.date(),
email: z.string(),
type: z.nativeEnum(WorkspaceRole),
}) satisfies z.ZodType<
Omit<WorkspaceInvitationPrisma, 'workspaceId' | 'userId' | 'id'>
>()(
z.object({
createdAt: z.date(),
updatedAt: z.date(),
email: z.string(),
type: z.nativeEnum(WorkspaceRole),
})
)
>
export const workspaceSchema = schemaForType<WorkspacePrisma>()(
z.object({
id: z.string(),
createdAt: z.date(),
updatedAt: z.date(),
name: z.string(),
icon: z.string().nullable(),
plan: z.nativeEnum(Plan),
stripeId: z.string().nullable(),
additionalChatsIndex: z.number(),
additionalStorageIndex: z.number(),
chatsLimitFirstEmailSentAt: z.date().nullable(),
chatsLimitSecondEmailSentAt: z.date().nullable(),
storageLimitFirstEmailSentAt: z.date().nullable(),
storageLimitSecondEmailSentAt: z.date().nullable(),
customChatsLimit: z.number().nullable(),
customStorageLimit: z.number().nullable(),
customSeatsLimit: z.number().nullable(),
})
)
export const workspaceSchema = z.object({
id: z.string(),
createdAt: z.date(),
updatedAt: z.date(),
name: z.string(),
icon: z.string().nullable(),
plan: z.nativeEnum(Plan),
stripeId: z.string().nullable(),
additionalChatsIndex: z.number(),
additionalStorageIndex: z.number(),
chatsLimitFirstEmailSentAt: z.date().nullable(),
chatsLimitSecondEmailSentAt: z.date().nullable(),
storageLimitFirstEmailSentAt: z.date().nullable(),
storageLimitSecondEmailSentAt: z.date().nullable(),
customChatsLimit: z.number().nullable(),
customStorageLimit: z.number().nullable(),
customSeatsLimit: z.number().nullable(),
}) satisfies z.ZodType<WorkspacePrisma>
export type Workspace = z.infer<typeof workspaceSchema>
export type WorkspaceMember = z.infer<typeof workspaceMemberSchema>