@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,336 @@
1
+ import { type NextRequest, NextResponse } from 'next/server'
2
+ import {
3
+ StorageProviderType,
4
+ Prisma,
5
+ ApiKeyType,
6
+ ToolType,
7
+ } from '@prisma/client'
8
+ import { cacheHeaders } from '@/lib/cache/cacheHeaders'
9
+ import { workspaceAccessWhere as getWorkspaceAccessWhere } from '@/lib/apiKeys/workspaceAccessWhere'
10
+ import { prisma } from '@/lib/prisma'
11
+ import { serializeAssistant } from '@/lib/assistants/serializeAssistant'
12
+ import { serializeApiAssistant } from '@/lib/assistants/serializeApiAssistant'
13
+ import { headers } from 'next/headers'
14
+ import { z } from 'zod'
15
+ import { getApiKey } from '@/lib/apiKeys/getApiKey'
16
+
17
+ export const GET = async (
18
+ request: NextRequest,
19
+ props: {
20
+ params: Promise<{
21
+ assistantId: string
22
+ }>
23
+ },
24
+ ) => {
25
+ const params = await props.params
26
+
27
+ const { assistantId } = params
28
+
29
+ const headersList = await headers()
30
+ const authorization = headersList.get('authorization')
31
+
32
+ if (authorization) {
33
+ const privateApiKey = await getApiKey({
34
+ authorization,
35
+ type: ApiKeyType.PRIVATE,
36
+ })
37
+
38
+ if (!privateApiKey) {
39
+ return NextResponse.json({ error: 'Invalid api key' }, { status: 400 })
40
+ }
41
+
42
+ const assistant = await prisma.assistant.findUnique({
43
+ where: {
44
+ id: assistantId,
45
+ workspaceId: privateApiKey.workspaceId,
46
+ },
47
+ include: {
48
+ tools: true,
49
+ avatar: {
50
+ include: {
51
+ imageAvatar: true,
52
+ iconAvatar: true,
53
+ },
54
+ },
55
+ },
56
+ })
57
+
58
+ if (!assistant) {
59
+ return NextResponse.json({ error: 'No assistant found' }, { status: 400 })
60
+ }
61
+
62
+ return NextResponse.json(
63
+ {
64
+ assistant: serializeApiAssistant({ assistant }),
65
+ },
66
+ { headers: cacheHeaders },
67
+ )
68
+ }
69
+
70
+ const workspaceAccessWhere = await getWorkspaceAccessWhere({
71
+ publicApiKey: request.nextUrl.searchParams.get('publicApiKey'),
72
+ })
73
+
74
+ if (!workspaceAccessWhere) {
75
+ return NextResponse.json({ error: 'Invalid api key' }, { status: 400 })
76
+ }
77
+
78
+ if (!assistantId) {
79
+ return NextResponse.json(
80
+ { error: 'No assistant id found' },
81
+ { status: 400 },
82
+ )
83
+ }
84
+
85
+ const assistant = await prisma.assistant.findFirst({
86
+ where: {
87
+ id: assistantId,
88
+ workspace: workspaceAccessWhere,
89
+ },
90
+ include: {
91
+ avatar: {
92
+ include: {
93
+ imageAvatar: true,
94
+ iconAvatar: true,
95
+ },
96
+ },
97
+ },
98
+ })
99
+
100
+ if (!assistant) {
101
+ return NextResponse.json({ error: 'No assistant found' }, { status: 400 })
102
+ }
103
+
104
+ return NextResponse.json(
105
+ {
106
+ assistant: serializeAssistant({ assistant }),
107
+ },
108
+ { headers: cacheHeaders },
109
+ )
110
+ }
111
+
112
+ export const PATCH = async (
113
+ request: NextRequest,
114
+ props: {
115
+ params: Promise<{
116
+ assistantId: string
117
+ }>
118
+ },
119
+ ) => {
120
+ const params = await props.params
121
+
122
+ const { assistantId } = params
123
+
124
+ const headersList = await headers()
125
+ const authorization = headersList.get('authorization')
126
+ if (!authorization) {
127
+ return NextResponse.json(
128
+ { error: 'No authorization header found' },
129
+ { status: 400 },
130
+ )
131
+ }
132
+
133
+ const privateApiKey = await getApiKey({
134
+ authorization,
135
+ type: ApiKeyType.PRIVATE,
136
+ })
137
+
138
+ if (!privateApiKey) {
139
+ return NextResponse.json({ error: 'Invalid api key' }, { status: 400 })
140
+ }
141
+
142
+ const updateSchema = z
143
+ .object({
144
+ storageProviderType: z.nativeEnum(StorageProviderType).optional(),
145
+ storageProviderAssistantId: z.string().nullable().optional(),
146
+ modelProviderId: z.string().optional(),
147
+ model: z.string().optional(),
148
+ name: z.string().optional(),
149
+ description: z.string().optional(),
150
+ instructions: z.string().optional(),
151
+ codeInterpreterEnabled: z.boolean().optional(),
152
+ fileSearchEnabled: z.boolean().optional(),
153
+ })
154
+ .superRefine((data, ctx) => {
155
+ if (!data.storageProviderType) return
156
+
157
+ if (
158
+ data.storageProviderType === StorageProviderType.SUPERINTERFACE_CLOUD &&
159
+ data.storageProviderAssistantId
160
+ ) {
161
+ ctx.addIssue({
162
+ code: z.ZodIssueCode.custom,
163
+ message:
164
+ 'storageProviderAssistantId should not be provided for SUPERINTERFACE_CLOUD',
165
+ path: ['storageProviderAssistantId'],
166
+ })
167
+ }
168
+
169
+ if (
170
+ data.storageProviderType !== StorageProviderType.SUPERINTERFACE_CLOUD &&
171
+ !data.storageProviderAssistantId
172
+ ) {
173
+ ctx.addIssue({
174
+ code: z.ZodIssueCode.custom,
175
+ message:
176
+ 'storageProviderAssistantId is required for this storage provider',
177
+ path: ['storageProviderAssistantId'],
178
+ })
179
+ }
180
+ })
181
+
182
+ const parseResult = updateSchema.safeParse(await request.json())
183
+
184
+ if (!parseResult.success) {
185
+ return NextResponse.json(
186
+ {
187
+ error: 'Invalid body: ' + parseResult.error.message,
188
+ },
189
+ { status: 400 },
190
+ )
191
+ }
192
+
193
+ const {
194
+ storageProviderType,
195
+ storageProviderAssistantId,
196
+ modelProviderId,
197
+ model,
198
+ name,
199
+ description,
200
+ instructions,
201
+ codeInterpreterEnabled,
202
+ fileSearchEnabled,
203
+ } = parseResult.data
204
+
205
+ const data: Prisma.AssistantUpdateInput = {
206
+ ...(storageProviderType !== undefined && { storageProviderType }),
207
+ ...(storageProviderAssistantId !== undefined && {
208
+ openaiAssistantId: storageProviderAssistantId,
209
+ }),
210
+ ...(modelProviderId !== undefined && {
211
+ modelProvider: { connect: { id: modelProviderId } },
212
+ }),
213
+ ...(model !== undefined && { modelSlug: model }),
214
+ ...(name !== undefined && { name }),
215
+ ...(description !== undefined && { description }),
216
+ ...(instructions !== undefined && { instructions }),
217
+ tools: {
218
+ create: [
219
+ ...(codeInterpreterEnabled === true
220
+ ? [
221
+ {
222
+ type: ToolType.CODE_INTERPRETER,
223
+ codeInterpreterTool: { create: {} },
224
+ },
225
+ ]
226
+ : []),
227
+ ...(fileSearchEnabled === true
228
+ ? [{ type: ToolType.FILE_SEARCH, fileSearchTool: { create: {} } }]
229
+ : []),
230
+ ],
231
+ deleteMany: [
232
+ ...(codeInterpreterEnabled === false
233
+ ? [{ assistantId, type: ToolType.CODE_INTERPRETER }]
234
+ : []),
235
+ ...(fileSearchEnabled === false
236
+ ? [{ assistantId, type: ToolType.FILE_SEARCH }]
237
+ : []),
238
+ ],
239
+ },
240
+ }
241
+
242
+ const assistant = await prisma.assistant.update({
243
+ where: {
244
+ id: assistantId,
245
+ workspaceId: privateApiKey.workspaceId,
246
+ },
247
+ data,
248
+ include: {
249
+ tools: true,
250
+ avatar: {
251
+ include: {
252
+ imageAvatar: true,
253
+ iconAvatar: true,
254
+ },
255
+ },
256
+ },
257
+ })
258
+
259
+ return NextResponse.json(
260
+ {
261
+ assistant: serializeApiAssistant({ assistant }),
262
+ },
263
+ { headers: cacheHeaders },
264
+ )
265
+ }
266
+
267
+ export const DELETE = async (
268
+ request: NextRequest,
269
+ props: {
270
+ params: Promise<{
271
+ assistantId: string
272
+ }>
273
+ },
274
+ ) => {
275
+ const params = await props.params
276
+
277
+ const { assistantId } = params
278
+
279
+ const headersList = await headers()
280
+ const authorization = headersList.get('authorization')
281
+ if (!authorization) {
282
+ return NextResponse.json(
283
+ { error: 'No authorization header found' },
284
+ { status: 400 },
285
+ )
286
+ }
287
+
288
+ const privateApiKey = await getApiKey({
289
+ authorization,
290
+ type: ApiKeyType.PRIVATE,
291
+ })
292
+
293
+ if (!privateApiKey) {
294
+ return NextResponse.json({ error: 'Invalid api key' }, { status: 400 })
295
+ }
296
+ const assistant = await prisma.assistant.findFirst({
297
+ where: {
298
+ id: assistantId,
299
+ workspaceId: privateApiKey.workspaceId,
300
+ },
301
+ include: {
302
+ tools: true,
303
+ avatar: {
304
+ include: {
305
+ imageAvatar: true,
306
+ iconAvatar: true,
307
+ },
308
+ },
309
+ },
310
+ })
311
+
312
+ if (!assistant) {
313
+ return NextResponse.json({ error: 'No assistant found' }, { status: 400 })
314
+ }
315
+
316
+ await prisma.assistant.delete({
317
+ where: {
318
+ id: assistant.id,
319
+ },
320
+ })
321
+
322
+ return NextResponse.json(
323
+ {
324
+ assistant: serializeApiAssistant({ assistant }),
325
+ },
326
+ { headers: cacheHeaders },
327
+ )
328
+ }
329
+
330
+ export const OPTIONS = () =>
331
+ NextResponse.json(
332
+ {},
333
+ {
334
+ headers: cacheHeaders,
335
+ },
336
+ )
@@ -0,0 +1,196 @@
1
+ import { headers } from 'next/headers'
2
+ import { ApiKeyType, StorageProviderType, ToolType } from '@prisma/client'
3
+ import { NextResponse } from 'next/server'
4
+ import { z } from 'zod'
5
+ import { cacheHeaders } from '@/lib/cache/cacheHeaders'
6
+ import { prisma } from '@/lib/prisma'
7
+ import { serializeApiAssistant } from '@/lib/assistants/serializeApiAssistant'
8
+ import { getApiKey } from '@/lib/apiKeys/getApiKey'
9
+
10
+ export const GET = async () => {
11
+ const headersList = await headers()
12
+ const authorization = headersList.get('authorization')
13
+ if (!authorization) {
14
+ return NextResponse.json(
15
+ { error: 'No authorization header found' },
16
+ { status: 400 },
17
+ )
18
+ }
19
+
20
+ const privateApiKey = await getApiKey({
21
+ authorization,
22
+ type: ApiKeyType.PRIVATE,
23
+ })
24
+
25
+ if (!privateApiKey) {
26
+ return NextResponse.json({ error: 'Invalid api key' }, { status: 400 })
27
+ }
28
+
29
+ const assistants = await prisma.assistant.findMany({
30
+ where: {
31
+ workspaceId: privateApiKey.workspaceId,
32
+ },
33
+ include: {
34
+ tools: true,
35
+ avatar: {
36
+ include: {
37
+ imageAvatar: true,
38
+ iconAvatar: true,
39
+ },
40
+ },
41
+ },
42
+ })
43
+
44
+ return NextResponse.json(
45
+ {
46
+ assistants: assistants.map((a) =>
47
+ serializeApiAssistant({ assistant: a }),
48
+ ),
49
+ },
50
+ { headers: cacheHeaders },
51
+ )
52
+ }
53
+
54
+ const createAssistantSchema = z
55
+ .object({
56
+ storageProviderType: z.nativeEnum(StorageProviderType),
57
+ storageProviderAssistantId: z.string().nullable().optional(),
58
+ modelProviderId: z.string(),
59
+ model: z.string(),
60
+ name: z.string().optional(),
61
+ description: z.string().optional().default(''),
62
+ instructions: z.string().optional().default(''),
63
+ codeInterpreterEnabled: z.boolean().optional().default(false),
64
+ fileSearchEnabled: z.boolean().optional().default(false),
65
+ })
66
+ .superRefine((data, ctx) => {
67
+ if (
68
+ data.storageProviderType === StorageProviderType.SUPERINTERFACE_CLOUD &&
69
+ data.storageProviderAssistantId
70
+ ) {
71
+ ctx.addIssue({
72
+ code: z.ZodIssueCode.custom,
73
+ message:
74
+ 'storageProviderAssistantId should not be provided for SUPERINTERFACE_CLOUD',
75
+ path: ['storageProviderAssistantId'],
76
+ })
77
+ }
78
+
79
+ if (
80
+ data.storageProviderType !== StorageProviderType.SUPERINTERFACE_CLOUD &&
81
+ !data.storageProviderAssistantId
82
+ ) {
83
+ ctx.addIssue({
84
+ code: z.ZodIssueCode.custom,
85
+ message:
86
+ 'storageProviderAssistantId is required for this storage provider',
87
+ path: ['storageProviderAssistantId'],
88
+ })
89
+ }
90
+ })
91
+
92
+ export const POST = async (request: Request) => {
93
+ const headersList = await headers()
94
+ const authorization = headersList.get('authorization')
95
+ if (!authorization) {
96
+ return NextResponse.json(
97
+ { error: 'No authorization header found' },
98
+ { status: 400 },
99
+ )
100
+ }
101
+
102
+ const privateApiKey = await getApiKey({
103
+ authorization,
104
+ type: ApiKeyType.PRIVATE,
105
+ })
106
+
107
+ if (!privateApiKey) {
108
+ return NextResponse.json({ error: 'Invalid api key' }, { status: 400 })
109
+ }
110
+
111
+ const parseResult = createAssistantSchema.safeParse(await request.json())
112
+
113
+ if (!parseResult.success) {
114
+ return NextResponse.json(
115
+ {
116
+ error: `Missing required fields. ${JSON.stringify(parseResult.error.format())}`,
117
+ },
118
+ { status: 400 },
119
+ )
120
+ }
121
+
122
+ const {
123
+ storageProviderType,
124
+ storageProviderAssistantId,
125
+ modelProviderId,
126
+ model,
127
+ name,
128
+ description,
129
+ instructions,
130
+ codeInterpreterEnabled,
131
+ fileSearchEnabled,
132
+ } = parseResult.data
133
+
134
+ const workspaceId = privateApiKey.workspaceId
135
+
136
+ const assistant = await prisma.assistant.create({
137
+ data: {
138
+ workspace: { connect: { id: workspaceId } },
139
+ modelProvider: { connect: { id: modelProviderId } },
140
+ modelSlug: model,
141
+ name,
142
+ description,
143
+ instructions,
144
+ storageProviderType,
145
+ openaiAssistantId: storageProviderAssistantId ?? null,
146
+ tools: {
147
+ create: [
148
+ ...(fileSearchEnabled
149
+ ? [
150
+ {
151
+ type: ToolType.FILE_SEARCH,
152
+ fileSearchTool: {
153
+ create: {},
154
+ },
155
+ },
156
+ ]
157
+ : []),
158
+ ...(codeInterpreterEnabled
159
+ ? [
160
+ {
161
+ type: ToolType.CODE_INTERPRETER,
162
+ codeInterpreterTool: {
163
+ create: {},
164
+ },
165
+ },
166
+ ]
167
+ : []),
168
+ ],
169
+ },
170
+ },
171
+ include: {
172
+ tools: true,
173
+ avatar: {
174
+ include: {
175
+ imageAvatar: true,
176
+ iconAvatar: true,
177
+ },
178
+ },
179
+ },
180
+ })
181
+
182
+ return NextResponse.json(
183
+ {
184
+ assistant: serializeApiAssistant({ assistant }),
185
+ },
186
+ { headers: cacheHeaders },
187
+ )
188
+ }
189
+
190
+ export const OPTIONS = () =>
191
+ NextResponse.json(
192
+ {},
193
+ {
194
+ headers: cacheHeaders,
195
+ },
196
+ )
@@ -0,0 +1,145 @@
1
+ import type { OpenAI } from 'openai'
2
+ import { isString } from 'radash'
3
+ import type { Prisma } from '@prisma/client'
4
+ import { type NextRequest, NextResponse } from 'next/server'
5
+ import { cacheHeaders } from '@/lib/cache/cacheHeaders'
6
+ import { assistantClientAdapter } from '@/lib/assistants/assistantClientAdapter'
7
+ import { workspaceAccessWhere as getWorkspaceAccessWhere } from '@/lib/apiKeys/workspaceAccessWhere'
8
+ import { prisma } from '@/lib/prisma'
9
+ import { isOpenaiAssistantsStorageProvider } from '@/lib/storageProviders/isOpenaiAssistantsStorageProvider'
10
+
11
+ export const buildGET =
12
+ ({
13
+ purposeAssistantsResponse = () =>
14
+ NextResponse.json({ error: 'No file source found' }, { status: 404 }),
15
+ }: {
16
+ purposeAssistantsResponse?: ({
17
+ file,
18
+ }: {
19
+ file: OpenAI.Files.FileObject
20
+ workspaceAccessWhere: Prisma.WorkspaceWhereInput
21
+ }) => Promise<NextResponse> | NextResponse
22
+ }) =>
23
+ async (
24
+ request: NextRequest,
25
+ props: {
26
+ params: Promise<{
27
+ fileId: string
28
+ }>
29
+ },
30
+ ) => {
31
+ const params = await props.params
32
+
33
+ const { fileId } = params
34
+
35
+ const assistantId = request.nextUrl.searchParams.get('assistantId')
36
+
37
+ if (!isString(assistantId)) {
38
+ return NextResponse.json(
39
+ { error: 'Invalid assistantId' },
40
+ { status: 400 },
41
+ )
42
+ }
43
+
44
+ const publicApiKey = request.nextUrl.searchParams.get('publicApiKey')
45
+
46
+ if (!isString(publicApiKey)) {
47
+ return NextResponse.json(
48
+ { error: 'Invalid publicApiKey' },
49
+ { status: 400 },
50
+ )
51
+ }
52
+
53
+ const workspaceAccessWhere = await getWorkspaceAccessWhere({ publicApiKey })
54
+
55
+ if (!workspaceAccessWhere) {
56
+ return NextResponse.json({ error: 'Invalid api key' }, { status: 400 })
57
+ }
58
+
59
+ if (!assistantId) {
60
+ return NextResponse.json(
61
+ { error: 'No assistantId found' },
62
+ { status: 400 },
63
+ )
64
+ }
65
+
66
+ const assistant = await prisma.assistant.findFirst({
67
+ where: {
68
+ id: assistantId,
69
+ workspace: workspaceAccessWhere,
70
+ },
71
+ include: {
72
+ modelProvider: true,
73
+ tools: {
74
+ include: {
75
+ fileSearchTool: true,
76
+ webSearchTool: true,
77
+ imageGenerationTool: true,
78
+ codeInterpreterTool: true,
79
+ computerUseTool: true,
80
+ },
81
+ },
82
+ mcpServers: {
83
+ include: {
84
+ computerUseTool: true,
85
+ stdioTransport: true,
86
+ sseTransport: true,
87
+ httpTransport: true,
88
+ },
89
+ },
90
+ functions: true,
91
+ },
92
+ })
93
+
94
+ if (!assistant) {
95
+ return NextResponse.json({ error: 'No assistant found' }, { status: 400 })
96
+ }
97
+
98
+ if (
99
+ isOpenaiAssistantsStorageProvider({
100
+ storageProviderType: assistant.storageProviderType,
101
+ }) &&
102
+ !assistant.openaiAssistantId
103
+ ) {
104
+ return NextResponse.json(
105
+ { error: 'Assistant setup is not done.' },
106
+ { status: 400 },
107
+ )
108
+ }
109
+
110
+ const client = assistantClientAdapter({ assistant, prisma })
111
+
112
+ const file = await client.files.retrieve(fileId)
113
+
114
+ if (!file) {
115
+ return NextResponse.json({ error: 'No file found' }, { status: 404 })
116
+ }
117
+
118
+ if (file.purpose === 'assistants') {
119
+ return purposeAssistantsResponse({ file, workspaceAccessWhere })
120
+ }
121
+
122
+ const fileContentResponse = await client.files.content(fileId)
123
+ const fileData = await fileContentResponse.arrayBuffer()
124
+
125
+ return new NextResponse(fileData, {
126
+ headers: {
127
+ ...cacheHeaders,
128
+ 'Content-Type':
129
+ fileContentResponse.headers.get('Content-Type') ??
130
+ 'application/octet-stream',
131
+ 'Content-Disposition':
132
+ fileContentResponse.headers.get('Content-Disposition') ?? 'inline',
133
+ },
134
+ })
135
+ }
136
+
137
+ export const GET = buildGET({})
138
+
139
+ export const OPTIONS = () =>
140
+ NextResponse.json(
141
+ {},
142
+ {
143
+ headers: cacheHeaders,
144
+ },
145
+ )