@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,399 @@
1
+ import OpenAI from 'openai'
2
+ import {
3
+ AssistantHandler,
4
+ LogRequestMethod,
5
+ LogRequestRoute,
6
+ LogLevel,
7
+ Assistant,
8
+ Thread,
9
+ } from '@prisma/client'
10
+ import { createMessageResponse } from '@superinterface/react/server'
11
+ import { prisma } from '@/lib/prisma'
12
+ import { assistantClientAdapter } from '@/lib/assistants/assistantClientAdapter'
13
+ import { createThread } from '@/lib/threads/createThread'
14
+ import { managedOpenaiThreadId } from '@/lib/threads/managedOpenaiThreadId'
15
+ import { storageThreadId as getStorageThreadId } from '@/lib/threads/storageThreadId'
16
+ import { streamOutput } from '@/lib/toolCalls/streamOutput'
17
+ import { messagesToOutput } from '@/lib/toolCalls/messagesToOutput'
18
+ import { createRunOpts } from '@/lib/runs/createRunOpts'
19
+ import { handleToolCall } from '@/lib/toolCalls/handleToolCall'
20
+ import { createLog } from '@/lib/logs/createLog'
21
+ import { isOpenaiAssistantsStorageProvider } from '@/lib/storageProviders/isOpenaiAssistantsStorageProvider'
22
+ import { map } from 'p-iteration'
23
+ import { redis } from '@/lib/redis'
24
+ import { waitUntil } from '@vercel/functions'
25
+
26
+ export const handleAssistant = async ({
27
+ assistantHandler,
28
+ toolCall,
29
+ controller,
30
+ run,
31
+ assistant: parentAssistant,
32
+ thread: parentThread,
33
+ }: {
34
+ assistantHandler: AssistantHandler
35
+ toolCall: OpenAI.Beta.Threads.Runs.RequiredActionFunctionToolCall
36
+ controller: ReadableStreamDefaultController
37
+ run: OpenAI.Beta.Threads.Runs.Run
38
+ assistant: Assistant
39
+ thread: Thread
40
+ }) => {
41
+ let args
42
+
43
+ try {
44
+ args = JSON.parse(toolCall.function.arguments)
45
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
46
+ } catch (e: any) {
47
+ createLog({
48
+ log: {
49
+ requestMethod: LogRequestMethod.POST,
50
+ requestRoute: LogRequestRoute.MESSAGES,
51
+ level: LogLevel.ERROR,
52
+ status: 500,
53
+ message: `Failed parsing Assistant function arguments: ${e.message}`,
54
+ workspaceId: parentAssistant.workspaceId,
55
+ assistantId: parentAssistant.id,
56
+ threadId: parentThread.id,
57
+ },
58
+ })
59
+
60
+ return {
61
+ tool_call_id: toolCall.id,
62
+ output: 'Invalid arguments.',
63
+ }
64
+ }
65
+
66
+ const assistant = await prisma.assistant.findUnique({
67
+ where: {
68
+ id: assistantHandler.assistantId,
69
+ },
70
+ include: {
71
+ modelProvider: true,
72
+ initialMessages: true,
73
+ mcpServers: {
74
+ include: {
75
+ computerUseTool: true,
76
+ stdioTransport: true,
77
+ httpTransport: true,
78
+ sseTransport: true,
79
+ },
80
+ },
81
+ tools: {
82
+ include: {
83
+ fileSearchTool: true,
84
+ webSearchTool: true,
85
+ imageGenerationTool: true,
86
+ codeInterpreterTool: true,
87
+ computerUseTool: {
88
+ include: {
89
+ mcpServer: {
90
+ include: {
91
+ stdioTransport: true,
92
+ sseTransport: true,
93
+ httpTransport: true,
94
+ },
95
+ },
96
+ },
97
+ },
98
+ },
99
+ },
100
+ functions: {
101
+ include: {
102
+ handler: {
103
+ include: {
104
+ requestHandler: true,
105
+ firecrawlHandler: true,
106
+ replicateHandler: true,
107
+ clientToolHandler: true,
108
+ assistantHandler: true,
109
+ },
110
+ },
111
+ },
112
+ },
113
+ },
114
+ })
115
+
116
+ if (!assistant) {
117
+ createLog({
118
+ log: {
119
+ requestMethod: LogRequestMethod.POST,
120
+ requestRoute: LogRequestRoute.MESSAGES,
121
+ level: LogLevel.ERROR,
122
+ status: 500,
123
+ message: 'Assistant not found when handling Assistant function.',
124
+ workspaceId: parentAssistant.workspaceId,
125
+ assistantId: parentAssistant.id,
126
+ threadId: parentThread.id,
127
+ },
128
+ })
129
+
130
+ return {
131
+ tool_call_id: toolCall.id,
132
+ output: 'Assistant not found.',
133
+ }
134
+ }
135
+
136
+ const createThreadClient = assistantClientAdapter({ assistant, prisma })
137
+
138
+ let thread
139
+ try {
140
+ thread = await createThread({
141
+ client: createThreadClient,
142
+ assistant,
143
+ prisma,
144
+ variables: parentThread.metadata ?? {},
145
+ })
146
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars, @typescript-eslint/no-explicit-any
147
+ } catch (e: any) {
148
+ createLog({
149
+ log: {
150
+ requestMethod: LogRequestMethod.POST,
151
+ requestRoute: LogRequestRoute.MESSAGES,
152
+ level: LogLevel.ERROR,
153
+ status: 500,
154
+ message: 'Failed creating thread inside Assistant function.',
155
+ workspaceId: assistant.workspaceId,
156
+ assistantId: assistant.id,
157
+ },
158
+ })
159
+
160
+ return {
161
+ tool_call_id: toolCall.id,
162
+ output: 'Failed creating thread.',
163
+ }
164
+ }
165
+
166
+ let storageThreadId
167
+
168
+ try {
169
+ storageThreadId = getStorageThreadId({
170
+ thread,
171
+ })
172
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars, @typescript-eslint/no-explicit-any
173
+ } catch (_e: any) {
174
+ createLog({
175
+ log: {
176
+ requestMethod: LogRequestMethod.POST,
177
+ requestRoute: LogRequestRoute.MESSAGES,
178
+ level: LogLevel.ERROR,
179
+ status: 500,
180
+ message: 'Failed getting storage thread ID.',
181
+ workspaceId: assistant.workspaceId,
182
+ assistantId: assistant.id,
183
+ threadId: thread.id,
184
+ },
185
+ })
186
+
187
+ return {
188
+ tool_call_id: toolCall.id,
189
+ output: 'Failed getting storage thread ID.',
190
+ }
191
+ }
192
+
193
+ if (
194
+ !storageThreadId &&
195
+ isOpenaiAssistantsStorageProvider({
196
+ storageProviderType: assistant.storageProviderType,
197
+ })
198
+ ) {
199
+ try {
200
+ storageThreadId = await managedOpenaiThreadId({
201
+ assistant,
202
+ threadId: thread.id,
203
+ })
204
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars, @typescript-eslint/no-explicit-any
205
+ } catch (e: any) {
206
+ createLog({
207
+ log: {
208
+ requestMethod: LogRequestMethod.POST,
209
+ requestRoute: LogRequestRoute.MESSAGES,
210
+ level: LogLevel.ERROR,
211
+ status: 500,
212
+ message: 'Failed getting managed OpenAI thread ID.',
213
+ workspaceId: assistant.workspaceId,
214
+ assistantId: assistant.id,
215
+ threadId: thread.id,
216
+ },
217
+ })
218
+
219
+ return {
220
+ tool_call_id: toolCall.id,
221
+ output: 'Failed getting managed OpenAI thread ID.',
222
+ }
223
+ }
224
+ }
225
+
226
+ if (!storageThreadId) {
227
+ createLog({
228
+ log: {
229
+ requestMethod: LogRequestMethod.POST,
230
+ requestRoute: LogRequestRoute.MESSAGES,
231
+ level: LogLevel.ERROR,
232
+ status: 500,
233
+ message: 'Invalid thread configuration',
234
+ workspaceId: assistant.workspaceId,
235
+ assistantId: assistant.id,
236
+ threadId: thread.id,
237
+ },
238
+ })
239
+
240
+ return {
241
+ tool_call_id: toolCall.id,
242
+ output: 'Invalid thread configuration',
243
+ }
244
+ }
245
+
246
+ const client = assistantClientAdapter({
247
+ assistant,
248
+ prisma,
249
+ thread,
250
+ })
251
+
252
+ try {
253
+ await client.beta.threads.messages.create(storageThreadId, {
254
+ role: 'user',
255
+ content: args.message,
256
+ metadata: {
257
+ assistantId: assistant.id,
258
+ toolCallId: toolCall.id,
259
+ },
260
+ // ...(attachments?.length ? { attachments } : {}),
261
+ })
262
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars, @typescript-eslint/no-explicit-any
263
+ } catch (e: any) {
264
+ createLog({
265
+ log: {
266
+ requestMethod: LogRequestMethod.POST,
267
+ requestRoute: LogRequestRoute.MESSAGES,
268
+ level: LogLevel.ERROR,
269
+ status: 500,
270
+ message: `Failed creating message.`,
271
+ workspaceId: assistant.workspaceId,
272
+ assistantId: assistant.id,
273
+ threadId: thread.id,
274
+ },
275
+ })
276
+
277
+ return {
278
+ tool_call_id: toolCall.id,
279
+ output: 'Failed creating message.',
280
+ }
281
+ }
282
+
283
+ let createRunStream
284
+
285
+ const runOpts = await createRunOpts({
286
+ assistant,
287
+ thread,
288
+ })
289
+
290
+ try {
291
+ createRunStream = await client.beta.threads.runs.create(
292
+ storageThreadId,
293
+ runOpts,
294
+ )
295
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
296
+ } catch (e: any) {
297
+ createLog({
298
+ log: {
299
+ requestMethod: LogRequestMethod.POST,
300
+ requestRoute: LogRequestRoute.MESSAGES,
301
+ level: LogLevel.ERROR,
302
+ status: 500,
303
+ message: `Failed creating run stream: ${e.message}`,
304
+ workspaceId: assistant.workspaceId,
305
+ assistantId: assistant.id,
306
+ threadId: thread.id,
307
+ },
308
+ })
309
+
310
+ return {
311
+ tool_call_id: toolCall.id,
312
+ output: 'Failed creating run stream.',
313
+ }
314
+ }
315
+
316
+ const messageResponse = createMessageResponse({
317
+ client,
318
+ createRunStream,
319
+ handleToolCall: handleToolCall({
320
+ assistant,
321
+ thread,
322
+ }),
323
+ })
324
+
325
+ if (assistant.id === 'a606a15d-c9a2-45d3-8e54-ee088a500008') {
326
+ waitUntil(
327
+ new Promise(async (resolve) => {
328
+ const decoder = new TextDecoder()
329
+ const reader = messageResponse.getReader()
330
+ // // consume the stream so tool calls are processed
331
+ // while (!(await reader.read()).done) {}
332
+ while (true) {
333
+ const { value, done } = await reader.read()
334
+ if (done) break
335
+
336
+ if (!value) continue
337
+
338
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
339
+ let event: any
340
+
341
+ try {
342
+ event = JSON.parse(decoder.decode(value))
343
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
344
+ } catch (e) {
345
+ continue
346
+ }
347
+
348
+ if (
349
+ event.event === 'thread.run.requires_action' &&
350
+ event.data?.required_action?.type === 'submit_client_tool_outputs'
351
+ ) {
352
+ await map(
353
+ event.data.required_action.submit_client_tool_outputs.tool_calls,
354
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
355
+ async (toolCall: any) => {
356
+ await redis.set(
357
+ `submit-client-tool-outputs:output:${toolCall.id}`,
358
+ 'Client tools cannot be used during async assistant calls',
359
+ { ex: 60 * 60 * 24 * 7 },
360
+ )
361
+ },
362
+ )
363
+ }
364
+ }
365
+
366
+ console.log('Successfully done with async assistant handler.')
367
+ resolve(true)
368
+ }),
369
+ )
370
+
371
+ return {
372
+ tool_call_id: toolCall.id,
373
+ output: JSON.stringify({
374
+ status: 'in_progress',
375
+ }),
376
+ }
377
+ }
378
+
379
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
380
+ const messages = [] as any[]
381
+
382
+ await streamOutput({
383
+ toolCall,
384
+ messageResponse,
385
+ controller,
386
+ run,
387
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
388
+ onThreadMessageCompleted: ({ message }: { message: any }) => {
389
+ messages.push(message)
390
+ },
391
+ })
392
+
393
+ return {
394
+ tool_call_id: toolCall.id,
395
+ output: messagesToOutput({
396
+ messages,
397
+ }),
398
+ }
399
+ }
@@ -0,0 +1,127 @@
1
+ import OpenAI from 'openai'
2
+ import {
3
+ ClientToolHandler,
4
+ Assistant,
5
+ Thread,
6
+ LogRequestMethod,
7
+ LogRequestRoute,
8
+ LogLevel,
9
+ } from '@prisma/client'
10
+ import { createLog } from '@/lib/logs/createLog'
11
+ import { enqueueJson } from '@superinterface/react/utils'
12
+ import { redis } from '@/lib/redis'
13
+
14
+ export const handleClientTool = async ({
15
+ clientToolHandler,
16
+ toolCall,
17
+ assistant,
18
+ thread,
19
+ controller,
20
+ run,
21
+ }: {
22
+ clientToolHandler: ClientToolHandler
23
+ toolCall: OpenAI.Beta.Threads.Runs.RequiredActionFunctionToolCall
24
+ assistant: Assistant
25
+ thread: Thread
26
+ controller: ReadableStreamDefaultController
27
+ run: OpenAI.Beta.Threads.Runs.Run
28
+ }) => {
29
+ let args = {}
30
+
31
+ const toolCallArguments = toolCall.function.arguments
32
+
33
+ if (toolCallArguments) {
34
+ try {
35
+ args = JSON.parse(toolCallArguments)
36
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
37
+ } catch (e: any) {
38
+ createLog({
39
+ log: {
40
+ requestMethod: LogRequestMethod.POST,
41
+ requestRoute: LogRequestRoute.MESSAGES,
42
+ level: LogLevel.ERROR,
43
+ status: 400,
44
+ message: `Failed parsing client tool function arguments: ${e.message}`,
45
+ workspaceId: assistant.workspaceId,
46
+ assistantId: assistant.id,
47
+ threadId: thread.id,
48
+ },
49
+ })
50
+
51
+ return {
52
+ tool_call_id: toolCall.id,
53
+ output: 'Invalid arguments.',
54
+ }
55
+ }
56
+ }
57
+
58
+ enqueueJson({
59
+ controller,
60
+ value: {
61
+ event: 'thread.run.requires_action',
62
+ data: {
63
+ ...run,
64
+ required_action: {
65
+ type: 'submit_client_tool_outputs',
66
+ submit_client_tool_outputs: {
67
+ tool_calls: [
68
+ {
69
+ id: toolCall.id,
70
+ type: 'function',
71
+ function: {
72
+ name: clientToolHandler.name,
73
+ arguments: JSON.stringify({
74
+ ...clientToolHandler.arguments,
75
+ ...args,
76
+ }),
77
+ },
78
+ },
79
+ // toolCall,
80
+ // {
81
+ // id: toolCall.id,
82
+ // type: 'function',
83
+ // function: toolCall,
84
+ // toolCall,
85
+ // output: 'Function is running',
86
+ // },
87
+ // },
88
+ ],
89
+ },
90
+ },
91
+ },
92
+ },
93
+ })
94
+
95
+ await redis.set(`submit-client-tool-outputs:pending:${toolCall.id}`, '1', {
96
+ ex: 60 * 60 * 24 * 7,
97
+ })
98
+
99
+ const functionResult = await new Promise(async (resolve) => {
100
+ const timeout = setTimeout(() => {
101
+ resolve('Function call timed out')
102
+ }, 60000)
103
+
104
+ const checkResult = async () => {
105
+ const result = await redis.get(
106
+ `submit-client-tool-outputs:output:${toolCall.id}`,
107
+ )
108
+
109
+ if (result === null) {
110
+ setTimeout(checkResult, 500)
111
+ } else {
112
+ clearTimeout(timeout)
113
+
114
+ await redis.del(`submit-client-tool-outputs:pending:${toolCall.id}`)
115
+ await redis.del(`submit-client-tool-outputs:output:${toolCall.id}`)
116
+ resolve(result)
117
+ }
118
+ }
119
+
120
+ checkResult()
121
+ })
122
+
123
+ return {
124
+ tool_call_id: toolCall.id,
125
+ output: functionResult,
126
+ }
127
+ }