@superinterface/server 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (217) hide show
  1. package/README.md +36 -0
  2. package/eslint.config.mjs +31 -0
  3. package/next.config.ts +7 -0
  4. package/package.json +176 -0
  5. package/prisma/Account.prisma +18 -0
  6. package/prisma/ApiKey.prisma +13 -0
  7. package/prisma/Assistant.prisma +32 -0
  8. package/prisma/AssistantHandler.prisma +12 -0
  9. package/prisma/Avatar.prisma +10 -0
  10. package/prisma/ClientToolHandler.prisma +13 -0
  11. package/prisma/CodeInterpreterTool.prisma +7 -0
  12. package/prisma/ComputerUseTool.prisma +12 -0
  13. package/prisma/CreateTaskHandler.prisma +10 -0
  14. package/prisma/DeleteTaskHandler.prisma +10 -0
  15. package/prisma/FileSearchTool.prisma +9 -0
  16. package/prisma/FirecrawlHandler.prisma +13 -0
  17. package/prisma/Function.prisma +13 -0
  18. package/prisma/Handler.prisma +19 -0
  19. package/prisma/HttpTransport.prisma +12 -0
  20. package/prisma/IconAvatar.prisma +8 -0
  21. package/prisma/ImageAvatar.prisma +8 -0
  22. package/prisma/ImageGenerationTool.prisma +11 -0
  23. package/prisma/InitialMessage.prisma +17 -0
  24. package/prisma/Invitation.prisma +15 -0
  25. package/prisma/ListTasksHandler.prisma +10 -0
  26. package/prisma/Log.prisma +21 -0
  27. package/prisma/McpServer.prisma +14 -0
  28. package/prisma/Message.prisma +30 -0
  29. package/prisma/ModelProvider.prisma +15 -0
  30. package/prisma/Organization.prisma +10 -0
  31. package/prisma/OrganizationApiKey.prisma +12 -0
  32. package/prisma/OrganizationInvitation.prisma +15 -0
  33. package/prisma/OrganizationUserRole.prisma +14 -0
  34. package/prisma/ReplicateHandler.prisma +14 -0
  35. package/prisma/RequestHandler.prisma +15 -0
  36. package/prisma/Run.prisma +36 -0
  37. package/prisma/RunStep.prisma +30 -0
  38. package/prisma/Session.prisma +7 -0
  39. package/prisma/SseTransport.prisma +12 -0
  40. package/prisma/StdioTransport.prisma +11 -0
  41. package/prisma/Task.prisma +15 -0
  42. package/prisma/Thread.prisma +20 -0
  43. package/prisma/Tool.prisma +13 -0
  44. package/prisma/UpdateTaskHandler.prisma +10 -0
  45. package/prisma/User.prisma +16 -0
  46. package/prisma/UserRole.prisma +14 -0
  47. package/prisma/VerificationToken.prisma +7 -0
  48. package/prisma/WebSearchTool.prisma +7 -0
  49. package/prisma/Workspace.prisma +14 -0
  50. package/prisma/enums/ApiKeyType.prisma +4 -0
  51. package/prisma/enums/AvatarType.prisma +5 -0
  52. package/prisma/enums/ClientToolHandlerType.prisma +3 -0
  53. package/prisma/enums/ComputerUseToolEnvironment.prisma +6 -0
  54. package/prisma/enums/FirecrawlHandlerType.prisma +6 -0
  55. package/prisma/enums/HandlerType.prisma +11 -0
  56. package/prisma/enums/IconAvatarName.prisma +14 -0
  57. package/prisma/enums/ImageGenerationToolOutputFormat.prisma +5 -0
  58. package/prisma/enums/ImageGenerationToolQuality.prisma +6 -0
  59. package/prisma/enums/ImageGenerationToolSize.prisma +6 -0
  60. package/prisma/enums/LogLevel.prisma +5 -0
  61. package/prisma/enums/LogRequestMethod.prisma +7 -0
  62. package/prisma/enums/LogRequestRoute.prisma +6 -0
  63. package/prisma/enums/MessageRole.prisma +4 -0
  64. package/prisma/enums/MessageStatus.prisma +5 -0
  65. package/prisma/enums/MethodType.prisma +7 -0
  66. package/prisma/enums/ModelProviderType.prisma +13 -0
  67. package/prisma/enums/OrganizationUserRoleType.prisma +3 -0
  68. package/prisma/enums/ReplicateHandlerType.prisma +3 -0
  69. package/prisma/enums/RunStatus.prisma +10 -0
  70. package/prisma/enums/RunStepStatus.prisma +7 -0
  71. package/prisma/enums/RunStepType.prisma +4 -0
  72. package/prisma/enums/StorageProviderType.prisma +7 -0
  73. package/prisma/enums/ToolType.prisma +7 -0
  74. package/prisma/enums/TransportType.prisma +5 -0
  75. package/prisma/enums/TruncationType.prisma +5 -0
  76. package/prisma/enums/UserRoleType.prisma +3 -0
  77. package/prisma/migrations/20251006235143_initial_setup/migration.sql +986 -0
  78. package/prisma/migrations/20251007163926_add_truncation_type/migration.sql +6 -0
  79. package/prisma/migrations/20251007190703_add_organizations/migration.sql +97 -0
  80. package/prisma/migrations/migration_lock.toml +3 -0
  81. package/prisma/schema.prisma +13 -0
  82. package/prisma.config.ts +6 -0
  83. package/scripts/cli.ts +84 -0
  84. package/scripts/commands/organizations/api-keys/create.ts +159 -0
  85. package/scripts/commands/organizations/create.ts +82 -0
  86. package/scripts/utils/env.ts +31 -0
  87. package/scripts/utils/errors.ts +6 -0
  88. package/src/app/api/api-keys/[apiKeyId]/route.ts +178 -0
  89. package/src/app/api/api-keys/route.ts +147 -0
  90. package/src/app/api/assistants/[assistantId]/functions/[functionId]/route.ts +245 -0
  91. package/src/app/api/assistants/[assistantId]/functions/route.ts +157 -0
  92. package/src/app/api/assistants/[assistantId]/initial-messages/route.ts +127 -0
  93. package/src/app/api/assistants/[assistantId]/mcp-servers/[mcpServerId]/route.ts +243 -0
  94. package/src/app/api/assistants/[assistantId]/mcp-servers/route.ts +163 -0
  95. package/src/app/api/assistants/[assistantId]/route.ts +336 -0
  96. package/src/app/api/assistants/route.ts +196 -0
  97. package/src/app/api/files/[fileId]/contents/route.ts +145 -0
  98. package/src/app/api/files/route.ts +117 -0
  99. package/src/app/api/messages/lib/getWorkspaceId.ts +12 -0
  100. package/src/app/api/messages/lib/initialMessagesResponse.ts +90 -0
  101. package/src/app/api/messages/lib/serializeThread.ts +22 -0
  102. package/src/app/api/messages/route.ts +710 -0
  103. package/src/app/api/providers/[modelProviderId]/assistants/route.ts +68 -0
  104. package/src/app/api/providers/[modelProviderId]/models/route.ts +68 -0
  105. package/src/app/api/providers/[modelProviderId]/route.ts +202 -0
  106. package/src/app/api/providers/route.ts +105 -0
  107. package/src/app/api/tasks/[taskId]/route.ts +213 -0
  108. package/src/app/api/tasks/callback/route.ts +280 -0
  109. package/src/app/api/tasks/route.ts +121 -0
  110. package/src/app/api/threads/runs/submit-client-tool-outputs/route.ts +54 -0
  111. package/src/app/api/workspaces/[workspaceId]/route.ts +137 -0
  112. package/src/app/api/workspaces/route.ts +139 -0
  113. package/src/app/layout.tsx +9 -0
  114. package/src/app/page.tsx +3 -0
  115. package/src/lib/apiKeys/formatApiKeyName.ts +13 -0
  116. package/src/lib/apiKeys/getApiKey.ts +25 -0
  117. package/src/lib/apiKeys/serializeApiKey.ts +11 -0
  118. package/src/lib/apiKeys/workspaceAccessWhere.ts +21 -0
  119. package/src/lib/assistants/assistantClientAdapter/buildGetOpenaiAssistant.ts +96 -0
  120. package/src/lib/assistants/assistantClientAdapter/index.ts +165 -0
  121. package/src/lib/assistants/formatName.ts +9 -0
  122. package/src/lib/assistants/serializeApiAssistant.ts +40 -0
  123. package/src/lib/assistants/serializeAssistant.ts +31 -0
  124. package/src/lib/assistants/storageAssistantId.ts +29 -0
  125. package/src/lib/avatars/defaultAvatar.ts +15 -0
  126. package/src/lib/avatars/serializeAvatar.ts +26 -0
  127. package/src/lib/cache/cacheHeaders.ts +5 -0
  128. package/src/lib/computerCalls/handleComputerCall/index.ts +173 -0
  129. package/src/lib/errors/index.ts +10 -0
  130. package/src/lib/errors/serializeError.ts +11 -0
  131. package/src/lib/functions/createFunction.ts +32 -0
  132. package/src/lib/functions/functionSchema.ts +201 -0
  133. package/src/lib/functions/handleFunction/getFunction.ts +43 -0
  134. package/src/lib/functions/handleFunction/handleAssistant.ts +399 -0
  135. package/src/lib/functions/handleFunction/handleClientTool.ts +127 -0
  136. package/src/lib/functions/handleFunction/handleFirecrawl.ts +212 -0
  137. package/src/lib/functions/handleFunction/handleReplicate.ts +109 -0
  138. package/src/lib/functions/handleFunction/handleRequest.ts +272 -0
  139. package/src/lib/functions/handleFunction/index.ts +474 -0
  140. package/src/lib/functions/handleFunction/tasks/handleCreateTask.ts +58 -0
  141. package/src/lib/functions/handleFunction/tasks/handleDeleteTask.ts +62 -0
  142. package/src/lib/functions/handleFunction/tasks/handleListTasks.ts +53 -0
  143. package/src/lib/functions/handleFunction/tasks/handleUpdateTask.ts +70 -0
  144. package/src/lib/functions/interpolateFunctionValue.ts +26 -0
  145. package/src/lib/functions/serializeApiFunction.ts +32 -0
  146. package/src/lib/functions/updateFunction.ts +42 -0
  147. package/src/lib/handlers/handlerPrismaInput.tsx +141 -0
  148. package/src/lib/handlers/serializeApiHandler.ts +94 -0
  149. package/src/lib/iconAvatars/serializeIconAvatar.ts +9 -0
  150. package/src/lib/imageAvatars/serializeImageAvatar.ts +9 -0
  151. package/src/lib/initialMessages/schema.ts +11 -0
  152. package/src/lib/initialMessages/serializeApiInitialMessage.ts +15 -0
  153. package/src/lib/initialMessages/updateInitialMessages.ts +33 -0
  154. package/src/lib/logs/createLog.ts +17 -0
  155. package/src/lib/mcpServers/closeMcpConnection.ts +16 -0
  156. package/src/lib/mcpServers/connectMcpServer.ts +117 -0
  157. package/src/lib/mcpServers/getToolCallMcpServer.ts +62 -0
  158. package/src/lib/mcpServers/headers.ts +113 -0
  159. package/src/lib/mcpServers/mcpServerSchema.ts +77 -0
  160. package/src/lib/mcpServers/serializeApiMcpServer.ts +51 -0
  161. package/src/lib/mcpServers/url.ts +98 -0
  162. package/src/lib/messages/content.ts +60 -0
  163. package/src/lib/messages/textContent.ts +13 -0
  164. package/src/lib/metadata/serializeMetadata.ts +34 -0
  165. package/src/lib/misc/isJSON.ts +9 -0
  166. package/src/lib/misc/merge/customizer.ts +8 -0
  167. package/src/lib/misc/merge/index.ts +6 -0
  168. package/src/lib/modelProviders/buildAzureOpenaiClientAdapter.ts +33 -0
  169. package/src/lib/modelProviders/buildOpenaiClient.ts +14 -0
  170. package/src/lib/modelProviders/buildOpenaiClientAdapter.ts +30 -0
  171. package/src/lib/modelProviders/clientAdapter.ts +121 -0
  172. package/src/lib/modelProviders/isModelProviderValid.ts +23 -0
  173. package/src/lib/modelProviders/modelProviderConfigs.ts +221 -0
  174. package/src/lib/modelProviders/serializeModelProvider.ts +19 -0
  175. package/src/lib/models/getModels.ts +27 -0
  176. package/src/lib/models/serializeApiModel.ts +5 -0
  177. package/src/lib/openai/getOpenaiAssistants.ts +30 -0
  178. package/src/lib/organizationApiKeys/getOrganizationApiKey.ts +23 -0
  179. package/src/lib/prisma/index.ts +29 -0
  180. package/src/lib/redis/index.ts +3 -0
  181. package/src/lib/runs/createRunOpts.ts +80 -0
  182. package/src/lib/storageProviders/getStorageProviderAssistants.ts +19 -0
  183. package/src/lib/storageProviders/getStorageProviderType.ts +21 -0
  184. package/src/lib/storageProviders/isOpenaiAssistantsStorageProvider.ts +8 -0
  185. package/src/lib/storageProviders/isResponsesStorageProvider.ts +8 -0
  186. package/src/lib/storageProviders/openaiAssistantsStorageProviderTypes.ts +6 -0
  187. package/src/lib/storageProviders/responsesStorageProviderTypes.ts +6 -0
  188. package/src/lib/storageProviders/serializeApiStorageProviderAssistant.ts +14 -0
  189. package/src/lib/tasks/cancelScheduledTask.ts +13 -0
  190. package/src/lib/tasks/getNextOccurrence.ts +77 -0
  191. package/src/lib/tasks/getTaskToolKey.ts +49 -0
  192. package/src/lib/tasks/parseTaskToolArgs.ts +101 -0
  193. package/src/lib/tasks/scheduleSchema.ts +34 -0
  194. package/src/lib/tasks/scheduleTask.ts +37 -0
  195. package/src/lib/tasks/schemas.ts +25 -0
  196. package/src/lib/tasks/serializeTask.ts +14 -0
  197. package/src/lib/tasks/validateSchedule.ts +23 -0
  198. package/src/lib/themes/defaultTheme.ts +7 -0
  199. package/src/lib/themes/serializeAccentColor.ts +9 -0
  200. package/src/lib/themes/serializeTheme/index.ts +25 -0
  201. package/src/lib/themes/serializeTheme/serializeScaling.ts +14 -0
  202. package/src/lib/threads/createThread/index.ts +185 -0
  203. package/src/lib/threads/createThread/initialMessages.ts +36 -0
  204. package/src/lib/threads/managedOpenaiThreadId.ts +72 -0
  205. package/src/lib/threads/storageThreadId.ts +49 -0
  206. package/src/lib/threads/validThreadId.ts +10 -0
  207. package/src/lib/toolCalls/handleToolCall.ts +121 -0
  208. package/src/lib/toolCalls/messagesToOutput.ts +8 -0
  209. package/src/lib/toolCalls/streamOutput.ts +106 -0
  210. package/src/lib/tools/tools/index.ts +316 -0
  211. package/src/lib/upstash/qstash.ts +5 -0
  212. package/src/lib/upstash/upstashWorkflowClient.ts +5 -0
  213. package/src/lib/workspaces/serializeApiWorkspace.ts +12 -0
  214. package/src/types/index.ts +133 -0
  215. package/tsconfig.build.json +13 -0
  216. package/tsconfig.json +27 -0
  217. package/types/prisma.d.ts +4 -0
@@ -0,0 +1,173 @@
1
+ import OpenAI from 'openai'
2
+ import {
3
+ Prisma,
4
+ Thread,
5
+ LogRequestMethod,
6
+ LogRequestRoute,
7
+ LogLevel,
8
+ ToolType,
9
+ } from '@prisma/client'
10
+ import { createLog } from '@/lib/logs/createLog'
11
+ import { closeMcpConnection } from '@/lib/mcpServers/closeMcpConnection'
12
+ import {
13
+ CallToolResultSchema,
14
+ CallToolResult,
15
+ } from '@modelcontextprotocol/sdk/types.js'
16
+ import { connectMcpServer } from '@/lib/mcpServers/connectMcpServer'
17
+
18
+ const getContent = ({
19
+ mcpServerToolOutput,
20
+ }: {
21
+ mcpServerToolOutput: CallToolResult
22
+ }) => mcpServerToolOutput.content.find((c) => c.type === 'image')
23
+
24
+ const getImageUrl = ({
25
+ mcpServerToolOutput,
26
+ }: {
27
+ mcpServerToolOutput: CallToolResult
28
+ }) => {
29
+ const content = getContent({ mcpServerToolOutput })
30
+ if (!content) return null
31
+
32
+ return `data:${content.mimeType};base64,${content.data}`
33
+ }
34
+
35
+ export const handleComputerCall = async ({
36
+ assistant,
37
+ toolCall,
38
+ thread,
39
+ }: {
40
+ assistant: Prisma.AssistantGetPayload<{
41
+ include: {
42
+ tools: {
43
+ include: {
44
+ computerUseTool: {
45
+ include: {
46
+ mcpServer: {
47
+ include: {
48
+ stdioTransport: true
49
+ sseTransport: true
50
+ httpTransport: true
51
+ }
52
+ }
53
+ }
54
+ }
55
+ }
56
+ }
57
+ }
58
+ }>
59
+ toolCall: OpenAI.Beta.Threads.Runs.RequiredActionFunctionToolCall
60
+ thread: Thread
61
+ }) => {
62
+ const tool = assistant.tools.find(
63
+ (tool) => tool.type === ToolType.COMPUTER_USE,
64
+ )
65
+
66
+ if (!tool || !tool.computerUseTool || !tool.computerUseTool.mcpServer) {
67
+ createLog({
68
+ log: {
69
+ requestMethod: LogRequestMethod.POST,
70
+ requestRoute: LogRequestRoute.MESSAGES,
71
+ level: LogLevel.ERROR,
72
+ status: 500,
73
+ message: 'No computer use tool configured.',
74
+ workspaceId: assistant.workspaceId,
75
+ assistantId: assistant.id,
76
+ threadId: thread.id,
77
+ },
78
+ })
79
+
80
+ return {
81
+ tool_call_id: toolCall.id,
82
+ output: 'No computer use tool configured.',
83
+ }
84
+ }
85
+
86
+ const { mcpConnection } = await connectMcpServer({
87
+ thread,
88
+ assistant,
89
+ mcpServer: tool.computerUseTool.mcpServer,
90
+ })
91
+
92
+ try {
93
+ const mcpServerToolOutput = (await mcpConnection.client.callTool(
94
+ {
95
+ name: 'computer_call',
96
+ arguments: {
97
+ // @ts-expect-error computer_call is compatability type
98
+ action: toolCall.computer_call.action,
99
+ },
100
+ },
101
+ CallToolResultSchema,
102
+ {
103
+ timeout: 300000,
104
+ },
105
+ )) as CallToolResult
106
+
107
+ await closeMcpConnection({
108
+ mcpConnection,
109
+ })
110
+
111
+ const imageUrl = getImageUrl({
112
+ mcpServerToolOutput,
113
+ })
114
+
115
+ if (!imageUrl) {
116
+ createLog({
117
+ log: {
118
+ requestMethod: LogRequestMethod.POST,
119
+ requestRoute: LogRequestRoute.MESSAGES,
120
+ level: LogLevel.ERROR,
121
+ status: 500,
122
+ // @ts-expect-error compat
123
+ message: `Error calling computer_call with action ${JSON.stringify(toolCall.computer_call.action)}: No image in content`,
124
+ workspaceId: assistant.workspaceId,
125
+ assistantId: assistant.id,
126
+ threadId: thread.id,
127
+ },
128
+ })
129
+
130
+ return {
131
+ tool_call_id: toolCall.id,
132
+ // @ts-expect-error compat
133
+ output: `Error calling computer_call with action ${JSON.stringify(toolCall.computer_call.action)}: No image in content`,
134
+ }
135
+ }
136
+
137
+ const acknowledgedSafetyChecks =
138
+ // @ts-expect-error compat
139
+ toolCall.computer_call.pending_safety_checks.map((psc) => ({
140
+ id: psc.id,
141
+ }))
142
+
143
+ return {
144
+ tool_call_id: toolCall.id,
145
+ output: JSON.stringify({
146
+ type: 'computer_screenshot',
147
+ image_url: imageUrl,
148
+ }),
149
+ acknowledged_safety_checks: acknowledgedSafetyChecks,
150
+ }
151
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
152
+ } catch (e: any) {
153
+ createLog({
154
+ log: {
155
+ requestMethod: LogRequestMethod.POST,
156
+ requestRoute: LogRequestRoute.MESSAGES,
157
+ level: LogLevel.ERROR,
158
+ status: 500,
159
+ // @ts-expect-error compat
160
+ message: `Error calling computer_call with action ${JSON.stringify(toolCall.computer_call.action)}: ${e.message}`,
161
+ workspaceId: assistant.workspaceId,
162
+ assistantId: assistant.id,
163
+ threadId: thread.id,
164
+ },
165
+ })
166
+
167
+ return {
168
+ tool_call_id: toolCall.id,
169
+ // @ts-expect-error compat
170
+ output: `Error calling computer_call with action ${JSON.stringify(toolCall.computer_call.action)}: ${e.message}`,
171
+ }
172
+ }
173
+ }
@@ -0,0 +1,10 @@
1
+ export class ValidationError extends Error {
2
+ static defaultMessage = 'Validation failed.'
3
+
4
+ constructor(message: string = ValidationError.defaultMessage) {
5
+ super(message)
6
+ this.name = this.constructor.name
7
+ }
8
+ }
9
+
10
+ export const publicErrors = [ValidationError]
@@ -0,0 +1,11 @@
1
+ const causeMessage = ({ error }: { error: Error }) => {
2
+ if (error.cause instanceof Error) return ` ${error.cause.message}`
3
+ if (error.cause) return ` ${error.cause}`
4
+ return ''
5
+ }
6
+
7
+ export const serializeError = ({ error }: { error: unknown }): string => {
8
+ if (!(error instanceof Error)) return String(error)
9
+
10
+ return `${error.message}${causeMessage({ error })}`
11
+ }
@@ -0,0 +1,32 @@
1
+ import type { Assistant, Prisma } from '@prisma/client'
2
+ import type { HandlerInput } from '@/types'
3
+ import { prisma } from '@/lib/prisma'
4
+ import { handlerPrismaInput } from '@/lib/handlers/handlerPrismaInput'
5
+
6
+ export async function createFunction<
7
+ TInclude extends Prisma.FunctionInclude,
8
+ >(params: {
9
+ assistant: Assistant
10
+ parsedInput: { openapiSpec: string; handler: HandlerInput }
11
+ include: TInclude // ← required
12
+ }): Promise<Prisma.FunctionGetPayload<{ include: TInclude }>> {
13
+ const { assistant, parsedInput, include } = params
14
+
15
+ return prisma.function.create({
16
+ data: {
17
+ openapiSpec: JSON.parse(parsedInput.openapiSpec),
18
+ assistantId: assistant.id,
19
+ handler: {
20
+ create: {
21
+ type: parsedInput.handler.type,
22
+ ...(await handlerPrismaInput({
23
+ parsedInput,
24
+ action: 'create',
25
+ assistant,
26
+ })),
27
+ },
28
+ },
29
+ },
30
+ include,
31
+ })
32
+ }
@@ -0,0 +1,201 @@
1
+ import {
2
+ HandlerType,
3
+ MethodType,
4
+ FirecrawlHandlerType,
5
+ ReplicateHandlerType,
6
+ ClientToolHandlerType,
7
+ } from '@prisma/client'
8
+ import { z } from 'zod'
9
+ import { isJSON } from '@/lib/misc/isJSON'
10
+
11
+ const requestHandlerSchema = z.object({
12
+ method: z.nativeEnum(MethodType),
13
+ url: z.string().min(1).url(),
14
+ headers: z.string().min(1).refine(isJSON, {
15
+ message: 'Must be a valid JSON string.',
16
+ }),
17
+ body: z.string().min(1).refine(isJSON, {
18
+ message: 'Must be a valid JSON string.',
19
+ }),
20
+ })
21
+
22
+ const firecrawlHandlerSchema = z.object({
23
+ type: z.nativeEnum(FirecrawlHandlerType),
24
+ apiKey: z.string().min(1),
25
+ body: z.string().min(1).refine(isJSON, {
26
+ message: 'Must be a valid JSON string.',
27
+ }),
28
+ })
29
+
30
+ const assistantHandlerSchema = z.object({
31
+ assistantId: z.string().min(1),
32
+ })
33
+
34
+ const replicateHandlerSchema = z.object({
35
+ type: z.nativeEnum(ReplicateHandlerType),
36
+ identifier: z.string().min(1),
37
+ apiKey: z.string().min(1),
38
+ body: z.string().min(1).refine(isJSON, {
39
+ message: 'Must be a valid JSON string.',
40
+ }),
41
+ })
42
+
43
+ const clientToolHandlerSchema = z.object({
44
+ type: z.nativeEnum(ClientToolHandlerType),
45
+ name: z.string().min(1),
46
+ arguments: z.string().min(1).refine(isJSON, {
47
+ message: 'Must be a valid JSON string.',
48
+ }),
49
+ })
50
+
51
+ const taskHandlerSchema = z.object({
52
+ keyTemplate: z.string(),
53
+ })
54
+
55
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
56
+ export const superRefine = (values: any, ctx: any) => {
57
+ if (values.handler.type === HandlerType.REQUEST) {
58
+ const result = requestHandlerSchema.safeParse(values.handler.requestHandler)
59
+
60
+ if (result.success) return
61
+
62
+ result.error.issues.forEach((issue) =>
63
+ ctx.addIssue({
64
+ ...issue,
65
+ path: ['handler', 'requestHandler', ...issue.path],
66
+ }),
67
+ )
68
+ }
69
+
70
+ if (values.handler.type === HandlerType.FIRECRAWL) {
71
+ const result = firecrawlHandlerSchema.safeParse(
72
+ values.handler.firecrawlHandler,
73
+ )
74
+
75
+ if (result.success) return
76
+
77
+ result.error.issues.forEach((issue) =>
78
+ ctx.addIssue({
79
+ ...issue,
80
+ path: ['handler', 'firecrawlHandler', ...issue.path],
81
+ }),
82
+ )
83
+ }
84
+
85
+ if (values.handler.type === HandlerType.ASSISTANT) {
86
+ const result = assistantHandlerSchema.safeParse(
87
+ values.handler.assistantHandler,
88
+ )
89
+
90
+ if (result.success) return
91
+
92
+ result.error.issues.forEach((issue) =>
93
+ ctx.addIssue({
94
+ ...issue,
95
+ path: ['handler', 'assistantHandler', ...issue.path],
96
+ }),
97
+ )
98
+ }
99
+
100
+ if (values.handler.type === HandlerType.REPLICATE) {
101
+ const result = replicateHandlerSchema.safeParse(
102
+ values.handler.replicateHandler,
103
+ )
104
+
105
+ if (result.success) return
106
+
107
+ result.error.issues.forEach((issue) =>
108
+ ctx.addIssue({
109
+ ...issue,
110
+ path: ['handler', 'replicateHandler', ...issue.path],
111
+ }),
112
+ )
113
+ }
114
+
115
+ if (values.handler.type === HandlerType.CLIENT_TOOL) {
116
+ const result = clientToolHandlerSchema.safeParse(
117
+ values.handler.clientToolHandler,
118
+ )
119
+
120
+ if (result.success) return
121
+
122
+ result.error.issues.forEach((issue) =>
123
+ ctx.addIssue({
124
+ ...issue,
125
+ path: ['handler', 'clientToolHandler', ...issue.path],
126
+ }),
127
+ )
128
+ }
129
+
130
+ if (values.handler.type === HandlerType.CREATE_TASK) {
131
+ const result = taskHandlerSchema.safeParse(values.handler.createTaskHandler)
132
+
133
+ if (result.success) return
134
+
135
+ result.error.issues.forEach((issue) =>
136
+ ctx.addIssue({
137
+ ...issue,
138
+ path: ['handler', 'createTaskHandler', ...issue.path],
139
+ }),
140
+ )
141
+ }
142
+
143
+ if (values.handler.type === HandlerType.LIST_TASKS) {
144
+ const result = taskHandlerSchema.safeParse(values.handler.listTasksHandler)
145
+
146
+ if (result.success) return
147
+
148
+ result.error.issues.forEach((issue) =>
149
+ ctx.addIssue({
150
+ ...issue,
151
+ path: ['handler', 'listTasksHandler', ...issue.path],
152
+ }),
153
+ )
154
+ }
155
+
156
+ if (values.handler.type === HandlerType.UPDATE_TASK) {
157
+ const result = taskHandlerSchema.safeParse(values.handler.updateTaskHandler)
158
+
159
+ if (result.success) return
160
+
161
+ result.error.issues.forEach((issue) =>
162
+ ctx.addIssue({
163
+ ...issue,
164
+ path: ['handler', 'updateTaskHandler', ...issue.path],
165
+ }),
166
+ )
167
+ }
168
+
169
+ if (values.handler.type === HandlerType.DELETE_TASK) {
170
+ const result = taskHandlerSchema.safeParse(values.handler.deleteTaskHandler)
171
+
172
+ if (result.success) return
173
+
174
+ result.error.issues.forEach((issue) =>
175
+ ctx.addIssue({
176
+ ...issue,
177
+ path: ['handler', 'deleteTaskHandler', ...issue.path],
178
+ }),
179
+ )
180
+ }
181
+ }
182
+
183
+ export const baseSchema = z.object({
184
+ openapiSpec: z.string().min(1).refine(isJSON, {
185
+ message: 'Must be a valid JSON string.',
186
+ }),
187
+ handler: z.object({
188
+ type: z.nativeEnum(HandlerType),
189
+ requestHandler: requestHandlerSchema.nullable().optional(),
190
+ firecrawlHandler: firecrawlHandlerSchema.nullable().optional(),
191
+ assistantHandler: assistantHandlerSchema.nullable().optional(),
192
+ replicateHandler: replicateHandlerSchema.nullable().optional(),
193
+ clientToolHandler: clientToolHandlerSchema.nullable().optional(),
194
+ createTaskHandler: taskHandlerSchema.nullable().optional(),
195
+ listTasksHandler: taskHandlerSchema.nullable().optional(),
196
+ updateTaskHandler: taskHandlerSchema.nullable().optional(),
197
+ deleteTaskHandler: taskHandlerSchema.nullable().optional(),
198
+ }),
199
+ })
200
+
201
+ export const functionSchema = baseSchema.superRefine(superRefine)
@@ -0,0 +1,43 @@
1
+ import OpenAI from 'openai'
2
+ import type { Prisma } from '@prisma/client'
3
+ import { isObject } from 'radash'
4
+
5
+ export const getFunction = ({
6
+ assistant,
7
+ toolCall,
8
+ }: {
9
+ assistant: Prisma.AssistantGetPayload<{
10
+ include: {
11
+ functions: {
12
+ include: {
13
+ handler: {
14
+ include: {
15
+ requestHandler: true
16
+ firecrawlHandler: true
17
+ replicateHandler: true
18
+ clientToolHandler: true
19
+ assistantHandler: true
20
+ createTaskHandler: true
21
+ listTasksHandler: true
22
+ updateTaskHandler: true
23
+ deleteTaskHandler: true
24
+ }
25
+ }
26
+ }
27
+ }
28
+ }
29
+ }>
30
+ toolCall: OpenAI.Beta.Threads.Runs.RequiredActionFunctionToolCall
31
+ }) => {
32
+ const validFunctions = assistant.functions.filter((fn) => {
33
+ if (!fn.openapiSpec) return false
34
+ if (!isObject(fn.openapiSpec)) return false
35
+ if (!fn.openapiSpec.name) return false
36
+
37
+ return true
38
+ })
39
+
40
+ return validFunctions.find(
41
+ (fn) => fn.openapiSpec.name === toolCall.function.name,
42
+ )
43
+ }