@chat-js/cli 0.3.0 → 0.4.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.
- package/dist/index.js +11 -6
- package/package.json +1 -1
- package/templates/chat-app/app/(chat)/api/chat/prepare/route.ts +94 -0
- package/templates/chat-app/app/(chat)/api/chat/route.ts +97 -14
- package/templates/chat-app/chat.config.ts +141 -124
- package/templates/chat-app/components/chat-sync.tsx +6 -3
- package/templates/chat-app/components/feedback-actions.tsx +7 -3
- package/templates/chat-app/components/message-editor.tsx +8 -3
- package/templates/chat-app/components/message-siblings.tsx +14 -1
- package/templates/chat-app/components/model-selector.tsx +669 -407
- package/templates/chat-app/components/multimodal-input.tsx +252 -18
- package/templates/chat-app/components/parallel-response-cards.tsx +157 -0
- package/templates/chat-app/components/part/text-message-part.tsx +9 -5
- package/templates/chat-app/components/retry-button.tsx +25 -8
- package/templates/chat-app/components/user-message.tsx +136 -125
- package/templates/chat-app/hooks/chat-sync-hooks.ts +11 -0
- package/templates/chat-app/hooks/use-navigate-to-message.ts +39 -0
- package/templates/chat-app/lib/ai/types.ts +74 -3
- package/templates/chat-app/lib/config-schema.ts +5 -0
- package/templates/chat-app/lib/db/migrations/0044_gray_red_shift.sql +5 -0
- package/templates/chat-app/lib/db/migrations/meta/0044_snapshot.json +1567 -0
- package/templates/chat-app/lib/db/migrations/meta/_journal.json +8 -1
- package/templates/chat-app/lib/db/queries.ts +84 -4
- package/templates/chat-app/lib/db/schema.ts +4 -1
- package/templates/chat-app/lib/message-conversion.ts +14 -2
- package/templates/chat-app/lib/stores/hooks-threads.ts +37 -1
- package/templates/chat-app/lib/stores/with-threads.test.ts +137 -0
- package/templates/chat-app/lib/stores/with-threads.ts +157 -4
- package/templates/chat-app/lib/thread-utils.ts +23 -2
- package/templates/chat-app/providers/chat-input-provider.tsx +40 -2
- package/templates/chat-app/scripts/db-branch-delete.sh +7 -1
- package/templates/chat-app/scripts/db-branch-use.sh +7 -1
- package/templates/chat-app/scripts/with-db.sh +7 -1
- package/templates/chat-app/vitest.config.ts +2 -0
package/dist/index.js
CHANGED
|
@@ -3914,7 +3914,7 @@ var {
|
|
|
3914
3914
|
// package.json
|
|
3915
3915
|
var package_default = {
|
|
3916
3916
|
name: "@chat-js/cli",
|
|
3917
|
-
version: "0.
|
|
3917
|
+
version: "0.4.0",
|
|
3918
3918
|
description: "CLI for creating and extending ChatJS apps",
|
|
3919
3919
|
license: "Apache-2.0",
|
|
3920
3920
|
repository: {
|
|
@@ -19051,9 +19051,11 @@ var attachmentsConfigSchema = exports_external.object({
|
|
|
19051
19051
|
}
|
|
19052
19052
|
});
|
|
19053
19053
|
var featuresConfigSchema = exports_external.object({
|
|
19054
|
-
attachments: exports_external.boolean().describe("File attachments (requires BLOB_READ_WRITE_TOKEN)")
|
|
19054
|
+
attachments: exports_external.boolean().describe("File attachments (requires BLOB_READ_WRITE_TOKEN)"),
|
|
19055
|
+
parallelResponses: exports_external.boolean().default(true).describe("Send one message to multiple models simultaneously")
|
|
19055
19056
|
}).default({
|
|
19056
|
-
attachments: false
|
|
19057
|
+
attachments: false,
|
|
19058
|
+
parallelResponses: true
|
|
19057
19059
|
});
|
|
19058
19060
|
var authenticationConfigSchema = exports_external.object({
|
|
19059
19061
|
google: exports_external.boolean().describe("Google OAuth (requires AUTH_GOOGLE_ID + AUTH_GOOGLE_SECRET)"),
|
|
@@ -19352,7 +19354,8 @@ var FEATURE_KEYS = [
|
|
|
19352
19354
|
"mcp",
|
|
19353
19355
|
"imageGeneration",
|
|
19354
19356
|
"attachments",
|
|
19355
|
-
"followupSuggestions"
|
|
19357
|
+
"followupSuggestions",
|
|
19358
|
+
"parallelResponses"
|
|
19356
19359
|
];
|
|
19357
19360
|
|
|
19358
19361
|
// src/helpers/env-checklist.ts
|
|
@@ -19427,7 +19430,8 @@ var FEATURE_DEFAULTS = {
|
|
|
19427
19430
|
mcp: false,
|
|
19428
19431
|
imageGeneration: false,
|
|
19429
19432
|
attachments: false,
|
|
19430
|
-
followupSuggestions: true
|
|
19433
|
+
followupSuggestions: true,
|
|
19434
|
+
parallelResponses: true
|
|
19431
19435
|
};
|
|
19432
19436
|
var AUTH_DEFAULTS = {
|
|
19433
19437
|
google: false,
|
|
@@ -19442,7 +19446,8 @@ var FEATURE_LABELS = {
|
|
|
19442
19446
|
mcp: "MCP Tool Servers",
|
|
19443
19447
|
imageGeneration: "Image Generation",
|
|
19444
19448
|
attachments: "File Attachments",
|
|
19445
|
-
followupSuggestions: "Follow-up Suggestions"
|
|
19449
|
+
followupSuggestions: "Follow-up Suggestions",
|
|
19450
|
+
parallelResponses: "Parallel Responses"
|
|
19446
19451
|
};
|
|
19447
19452
|
var AUTH_LABELS = {
|
|
19448
19453
|
google: "Google OAuth",
|
package/package.json
CHANGED
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { headers } from "next/headers";
|
|
2
|
+
import type { NextRequest } from "next/server";
|
|
3
|
+
import type { ChatMessage } from "@/lib/ai/types";
|
|
4
|
+
import { auth } from "@/lib/auth";
|
|
5
|
+
import {
|
|
6
|
+
getChatById,
|
|
7
|
+
getMessageById,
|
|
8
|
+
getProjectById,
|
|
9
|
+
getUserById,
|
|
10
|
+
saveChatIfNotExists,
|
|
11
|
+
saveMessageIfNotExists,
|
|
12
|
+
} from "@/lib/db/queries";
|
|
13
|
+
import { createModuleLogger } from "@/lib/logger";
|
|
14
|
+
import { generateTitleFromUserMessage } from "../../../actions";
|
|
15
|
+
|
|
16
|
+
const log = createModuleLogger("api:chat:prepare");
|
|
17
|
+
|
|
18
|
+
export async function POST(request: NextRequest) {
|
|
19
|
+
try {
|
|
20
|
+
const {
|
|
21
|
+
id: chatId,
|
|
22
|
+
message: userMessage,
|
|
23
|
+
projectId,
|
|
24
|
+
}: {
|
|
25
|
+
id: string;
|
|
26
|
+
message: ChatMessage;
|
|
27
|
+
projectId?: string;
|
|
28
|
+
} = await request.json();
|
|
29
|
+
|
|
30
|
+
if (!userMessage) {
|
|
31
|
+
return new Response("Missing user message", { status: 400 });
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const session = await auth.api.getSession({ headers: await headers() });
|
|
35
|
+
const userId = session?.user?.id;
|
|
36
|
+
|
|
37
|
+
if (!userId) {
|
|
38
|
+
return new Response("Multiple models require authentication", {
|
|
39
|
+
status: 401,
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const user = await getUserById({ userId });
|
|
44
|
+
if (!user) {
|
|
45
|
+
return new Response("User not found", { status: 404 });
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const chat = await getChatById({ id: chatId });
|
|
49
|
+
let isNewChat = false;
|
|
50
|
+
|
|
51
|
+
if (chat) {
|
|
52
|
+
if (chat.userId !== userId) {
|
|
53
|
+
return new Response("Unauthorized", { status: 401 });
|
|
54
|
+
}
|
|
55
|
+
} else {
|
|
56
|
+
isNewChat = true;
|
|
57
|
+
|
|
58
|
+
if (projectId) {
|
|
59
|
+
const project = await getProjectById({ id: projectId });
|
|
60
|
+
if (!project || project.userId !== userId) {
|
|
61
|
+
return new Response("Unauthorized", { status: 401 });
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const title = await generateTitleFromUserMessage({
|
|
66
|
+
message: userMessage,
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
await saveChatIfNotExists({
|
|
70
|
+
id: chatId,
|
|
71
|
+
userId,
|
|
72
|
+
title,
|
|
73
|
+
projectId,
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const [existingMessage] = await getMessageById({ id: userMessage.id });
|
|
78
|
+
|
|
79
|
+
if (existingMessage && existingMessage.chatId !== chatId) {
|
|
80
|
+
return new Response("Unauthorized", { status: 401 });
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
await saveMessageIfNotExists({
|
|
84
|
+
id: userMessage.id,
|
|
85
|
+
chatId,
|
|
86
|
+
message: userMessage,
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
return Response.json({ isNewChat });
|
|
90
|
+
} catch (error) {
|
|
91
|
+
log.error({ error }, "POST /api/chat/prepare failed");
|
|
92
|
+
return new Response("Internal Server Error", { status: 500 });
|
|
93
|
+
}
|
|
94
|
+
}
|
|
@@ -27,7 +27,11 @@ import {
|
|
|
27
27
|
import { systemPrompt } from "@/lib/ai/prompts";
|
|
28
28
|
import { calculateMessagesTokens } from "@/lib/ai/token-utils";
|
|
29
29
|
import { allTools } from "@/lib/ai/tools/tools-definitions";
|
|
30
|
-
import
|
|
30
|
+
import {
|
|
31
|
+
getPrimarySelectedModelId,
|
|
32
|
+
type ChatMessage,
|
|
33
|
+
type ToolName,
|
|
34
|
+
} from "@/lib/ai/types";
|
|
31
35
|
import {
|
|
32
36
|
getAnonymousSession,
|
|
33
37
|
setAnonymousSession,
|
|
@@ -44,8 +48,9 @@ import {
|
|
|
44
48
|
getMessageCanceledAt,
|
|
45
49
|
getProjectById,
|
|
46
50
|
getUserById,
|
|
47
|
-
|
|
51
|
+
saveChatIfNotExists,
|
|
48
52
|
saveMessage,
|
|
53
|
+
saveMessageIfNotExists,
|
|
49
54
|
updateMessage,
|
|
50
55
|
updateMessageActiveStreamId,
|
|
51
56
|
} from "@/lib/db/queries";
|
|
@@ -191,7 +196,7 @@ async function handleChatValidation({
|
|
|
191
196
|
message: userMessage,
|
|
192
197
|
});
|
|
193
198
|
|
|
194
|
-
await
|
|
199
|
+
await saveChatIfNotExists({ id: chatId, userId, title, projectId });
|
|
195
200
|
}
|
|
196
201
|
|
|
197
202
|
const [existentMessage] = await getMessageById({ id: userMessage.id });
|
|
@@ -201,18 +206,35 @@ async function handleChatValidation({
|
|
|
201
206
|
return { error: new Response("Unauthorized", { status: 401 }), isNewChat };
|
|
202
207
|
}
|
|
203
208
|
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
message: userMessage,
|
|
210
|
-
});
|
|
211
|
-
}
|
|
209
|
+
await saveMessageIfNotExists({
|
|
210
|
+
id: userMessage.id,
|
|
211
|
+
chatId,
|
|
212
|
+
message: userMessage,
|
|
213
|
+
});
|
|
212
214
|
|
|
213
215
|
return { error: null, isNewChat };
|
|
214
216
|
}
|
|
215
217
|
|
|
218
|
+
function resolveSelectedModelId({
|
|
219
|
+
requestSelectedModelId,
|
|
220
|
+
selectedModel,
|
|
221
|
+
}: {
|
|
222
|
+
requestSelectedModelId?: AppModelId;
|
|
223
|
+
selectedModel: ChatMessage["metadata"]["selectedModel"];
|
|
224
|
+
}): AppModelId | null {
|
|
225
|
+
if (typeof selectedModel === "string") {
|
|
226
|
+
return requestSelectedModelId ?? selectedModel;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
if (requestSelectedModelId) {
|
|
230
|
+
if (!selectedModel || typeof selectedModel !== "object") return null;
|
|
231
|
+
const requestedCount = selectedModel[requestSelectedModelId];
|
|
232
|
+
return requestedCount && requestedCount > 0 ? requestSelectedModelId : null;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
return getPrimarySelectedModelId(selectedModel);
|
|
236
|
+
}
|
|
237
|
+
|
|
216
238
|
async function checkUserCanSpend(userId: string): Promise<Response | null> {
|
|
217
239
|
const userCanSpend = await canSpend(userId);
|
|
218
240
|
if (!userCanSpend) {
|
|
@@ -309,6 +331,9 @@ async function createChatStream({
|
|
|
309
331
|
userMessage,
|
|
310
332
|
previousMessages,
|
|
311
333
|
selectedModelId,
|
|
334
|
+
parallelGroupId,
|
|
335
|
+
parallelIndex,
|
|
336
|
+
isPrimaryParallel,
|
|
312
337
|
explicitlyRequestedTools,
|
|
313
338
|
userId,
|
|
314
339
|
allowedTools,
|
|
@@ -325,6 +350,9 @@ async function createChatStream({
|
|
|
325
350
|
userMessage: ChatMessage;
|
|
326
351
|
previousMessages: ChatMessage[];
|
|
327
352
|
selectedModelId: AppModelId;
|
|
353
|
+
parallelGroupId: string | null;
|
|
354
|
+
parallelIndex: number | null;
|
|
355
|
+
isPrimaryParallel: boolean | null;
|
|
328
356
|
explicitlyRequestedTools: ToolName[] | null;
|
|
329
357
|
userId: string | null;
|
|
330
358
|
allowedTools: ToolName[];
|
|
@@ -377,6 +405,9 @@ async function createChatStream({
|
|
|
377
405
|
const initialMetadata: ChatMessage["metadata"] = {
|
|
378
406
|
createdAt: new Date(),
|
|
379
407
|
parentMessageId: userMessage.id,
|
|
408
|
+
parallelGroupId,
|
|
409
|
+
parallelIndex,
|
|
410
|
+
isPrimaryParallel,
|
|
380
411
|
selectedModel: selectedModelId,
|
|
381
412
|
activeStreamId: isAnonymous ? null : streamId,
|
|
382
413
|
};
|
|
@@ -435,6 +466,10 @@ async function createChatStream({
|
|
|
435
466
|
isAnonymous,
|
|
436
467
|
chatId,
|
|
437
468
|
costAccumulator,
|
|
469
|
+
selectedModelId,
|
|
470
|
+
parallelGroupId,
|
|
471
|
+
parallelIndex,
|
|
472
|
+
isPrimaryParallel,
|
|
438
473
|
});
|
|
439
474
|
},
|
|
440
475
|
onError: (error) => {
|
|
@@ -468,6 +503,10 @@ async function executeChatRequest({
|
|
|
468
503
|
userMessage,
|
|
469
504
|
previousMessages,
|
|
470
505
|
selectedModelId,
|
|
506
|
+
assistantMessageId,
|
|
507
|
+
parallelGroupId,
|
|
508
|
+
parallelIndex,
|
|
509
|
+
isPrimaryParallel,
|
|
471
510
|
explicitlyRequestedTools,
|
|
472
511
|
userId,
|
|
473
512
|
isAnonymous,
|
|
@@ -481,6 +520,10 @@ async function executeChatRequest({
|
|
|
481
520
|
userMessage: ChatMessage;
|
|
482
521
|
previousMessages: ChatMessage[];
|
|
483
522
|
selectedModelId: AppModelId;
|
|
523
|
+
assistantMessageId?: string;
|
|
524
|
+
parallelGroupId: string | null;
|
|
525
|
+
parallelIndex: number | null;
|
|
526
|
+
isPrimaryParallel: boolean | null;
|
|
484
527
|
explicitlyRequestedTools: ToolName[] | null;
|
|
485
528
|
userId: string | null;
|
|
486
529
|
isAnonymous: boolean;
|
|
@@ -491,7 +534,7 @@ async function executeChatRequest({
|
|
|
491
534
|
mcpConnectors: McpConnector[];
|
|
492
535
|
}): Promise<Response> {
|
|
493
536
|
const log = createModuleLogger("api:chat:execute");
|
|
494
|
-
const messageId = generateUUID();
|
|
537
|
+
const messageId = assistantMessageId ?? generateUUID();
|
|
495
538
|
const streamId = generateUUID();
|
|
496
539
|
|
|
497
540
|
if (!isAnonymous) {
|
|
@@ -506,6 +549,9 @@ async function executeChatRequest({
|
|
|
506
549
|
metadata: {
|
|
507
550
|
createdAt: new Date(),
|
|
508
551
|
parentMessageId: userMessage.id,
|
|
552
|
+
parallelGroupId,
|
|
553
|
+
parallelIndex,
|
|
554
|
+
isPrimaryParallel,
|
|
509
555
|
selectedModel: selectedModelId,
|
|
510
556
|
selectedTool: undefined,
|
|
511
557
|
activeStreamId: streamId,
|
|
@@ -532,6 +578,9 @@ async function executeChatRequest({
|
|
|
532
578
|
userMessage,
|
|
533
579
|
previousMessages,
|
|
534
580
|
selectedModelId,
|
|
581
|
+
parallelGroupId,
|
|
582
|
+
parallelIndex,
|
|
583
|
+
isPrimaryParallel,
|
|
535
584
|
explicitlyRequestedTools,
|
|
536
585
|
userId,
|
|
537
586
|
allowedTools,
|
|
@@ -712,12 +761,20 @@ async function finalizeMessageAndCredits({
|
|
|
712
761
|
isAnonymous,
|
|
713
762
|
chatId,
|
|
714
763
|
costAccumulator,
|
|
764
|
+
selectedModelId,
|
|
765
|
+
parallelGroupId,
|
|
766
|
+
parallelIndex,
|
|
767
|
+
isPrimaryParallel,
|
|
715
768
|
}: {
|
|
716
769
|
messages: ChatMessage[];
|
|
717
770
|
userId: string | null;
|
|
718
771
|
isAnonymous: boolean;
|
|
719
772
|
chatId: string;
|
|
720
773
|
costAccumulator: CostAccumulator;
|
|
774
|
+
selectedModelId: AppModelId;
|
|
775
|
+
parallelGroupId: string | null;
|
|
776
|
+
parallelIndex: number | null;
|
|
777
|
+
isPrimaryParallel: boolean | null;
|
|
721
778
|
}): Promise<void> {
|
|
722
779
|
const log = createModuleLogger("api:chat:finalize");
|
|
723
780
|
|
|
@@ -736,6 +793,15 @@ async function finalizeMessageAndCredits({
|
|
|
736
793
|
...assistantMessage,
|
|
737
794
|
metadata: {
|
|
738
795
|
...assistantMessage.metadata,
|
|
796
|
+
parallelGroupId:
|
|
797
|
+
parallelGroupId ?? assistantMessage.metadata.parallelGroupId ?? null,
|
|
798
|
+
parallelIndex:
|
|
799
|
+
parallelIndex ?? assistantMessage.metadata.parallelIndex ?? null,
|
|
800
|
+
isPrimaryParallel:
|
|
801
|
+
isPrimaryParallel ??
|
|
802
|
+
assistantMessage.metadata.isPrimaryParallel ??
|
|
803
|
+
null,
|
|
804
|
+
selectedModel: selectedModelId,
|
|
739
805
|
activeStreamId: null,
|
|
740
806
|
},
|
|
741
807
|
},
|
|
@@ -768,11 +834,21 @@ export async function POST(request: NextRequest) {
|
|
|
768
834
|
message: userMessage,
|
|
769
835
|
prevMessages: anonymousPreviousMessages,
|
|
770
836
|
projectId,
|
|
837
|
+
assistantMessageId,
|
|
838
|
+
selectedModelId: requestSelectedModelId,
|
|
839
|
+
parallelGroupId,
|
|
840
|
+
parallelIndex,
|
|
841
|
+
isPrimaryParallel,
|
|
771
842
|
}: {
|
|
772
843
|
id: string;
|
|
773
844
|
message: ChatMessage;
|
|
774
845
|
prevMessages: ChatMessage[];
|
|
775
846
|
projectId?: string;
|
|
847
|
+
assistantMessageId?: string;
|
|
848
|
+
selectedModelId?: AppModelId;
|
|
849
|
+
parallelGroupId?: string | null;
|
|
850
|
+
parallelIndex?: number | null;
|
|
851
|
+
isPrimaryParallel?: boolean | null;
|
|
776
852
|
} = await request.json();
|
|
777
853
|
|
|
778
854
|
if (!userMessage) {
|
|
@@ -780,8 +856,10 @@ export async function POST(request: NextRequest) {
|
|
|
780
856
|
return new ChatSDKError("bad_request:api").toResponse();
|
|
781
857
|
}
|
|
782
858
|
|
|
783
|
-
|
|
784
|
-
|
|
859
|
+
const selectedModelId = resolveSelectedModelId({
|
|
860
|
+
requestSelectedModelId,
|
|
861
|
+
selectedModel: userMessage.metadata.selectedModel,
|
|
862
|
+
});
|
|
785
863
|
|
|
786
864
|
if (!selectedModelId) {
|
|
787
865
|
log.warn("No selectedModel in user message metadata");
|
|
@@ -857,6 +935,11 @@ export async function POST(request: NextRequest) {
|
|
|
857
935
|
userMessage,
|
|
858
936
|
previousMessages,
|
|
859
937
|
selectedModelId,
|
|
938
|
+
assistantMessageId,
|
|
939
|
+
parallelGroupId:
|
|
940
|
+
parallelGroupId ?? userMessage.metadata.parallelGroupId ?? null,
|
|
941
|
+
parallelIndex: parallelIndex ?? null,
|
|
942
|
+
isPrimaryParallel: isPrimaryParallel ?? null,
|
|
860
943
|
explicitlyRequestedTools,
|
|
861
944
|
userId,
|
|
862
945
|
isAnonymous,
|
|
@@ -9,130 +9,147 @@ const isProd = process.env.NODE_ENV === "production";
|
|
|
9
9
|
* @see https://chatjs.dev/docs/reference/config
|
|
10
10
|
*/
|
|
11
11
|
const config = defineConfig({
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
12
|
+
appPrefix: "chatjs",
|
|
13
|
+
appName: "ChatJS",
|
|
14
|
+
appTitle: "ChatJS - The prod ready AI chat app",
|
|
15
|
+
appDescription:
|
|
16
|
+
"Build and deploy AI chat applications in minutes. ChatJS provides authentication, streaming, tool calling, and all the features you need for production-ready AI conversations.",
|
|
17
|
+
appUrl: "https://chatjs.dev",
|
|
18
|
+
organization: {
|
|
19
|
+
name: "ChatJS",
|
|
20
|
+
contact: {
|
|
21
|
+
privacyEmail: "privacy@chatjs.dev",
|
|
22
|
+
legalEmail: "legal@chatjs.dev",
|
|
23
|
+
},
|
|
24
|
+
},
|
|
25
|
+
services: {
|
|
26
|
+
hosting: "Vercel",
|
|
27
|
+
aiProviders: [
|
|
28
|
+
"OpenAI",
|
|
29
|
+
"Anthropic",
|
|
30
|
+
"xAI",
|
|
31
|
+
"Google",
|
|
32
|
+
"Meta",
|
|
33
|
+
"Mistral",
|
|
34
|
+
"Alibaba",
|
|
35
|
+
"Amazon",
|
|
36
|
+
"Cohere",
|
|
37
|
+
"DeepSeek",
|
|
38
|
+
"Perplexity",
|
|
39
|
+
"Vercel",
|
|
40
|
+
"Inception",
|
|
41
|
+
"Moonshot",
|
|
42
|
+
"Morph",
|
|
43
|
+
"ZAI",
|
|
44
|
+
],
|
|
45
|
+
paymentProcessors: [],
|
|
46
|
+
},
|
|
47
|
+
features: {
|
|
48
|
+
attachments: true, // Requires BLOB_READ_WRITE_TOKEN
|
|
49
|
+
parallelResponses: true,
|
|
50
|
+
},
|
|
51
|
+
legal: {
|
|
52
|
+
minimumAge: 13,
|
|
53
|
+
governingLaw: "United States",
|
|
54
|
+
refundPolicy: "no-refunds",
|
|
55
|
+
},
|
|
56
|
+
policies: {
|
|
57
|
+
privacy: {
|
|
58
|
+
title: "Privacy Policy",
|
|
59
|
+
lastUpdated: "July 24, 2025",
|
|
60
|
+
},
|
|
61
|
+
terms: {
|
|
62
|
+
title: "Terms of Service",
|
|
63
|
+
lastUpdated: "July 24, 2025",
|
|
64
|
+
},
|
|
65
|
+
},
|
|
66
|
+
authentication: {
|
|
67
|
+
google: true, // Requires AUTH_GOOGLE_ID + AUTH_GOOGLE_SECRET
|
|
68
|
+
github: true, // Requires AUTH_GITHUB_ID + AUTH_GITHUB_SECRET
|
|
69
|
+
vercel: true, // Requires VERCEL_APP_CLIENT_ID + VERCEL_APP_CLIENT_SECRET
|
|
70
|
+
},
|
|
71
|
+
ai: {
|
|
72
|
+
gateway: "vercel",
|
|
73
|
+
providerOrder: [
|
|
74
|
+
"openai",
|
|
75
|
+
"anthropic",
|
|
76
|
+
"google",
|
|
77
|
+
"xai",
|
|
78
|
+
"meta",
|
|
79
|
+
"mistral",
|
|
80
|
+
"deepseek",
|
|
81
|
+
"perplexity",
|
|
82
|
+
"cohere",
|
|
83
|
+
"alibaba",
|
|
84
|
+
"amazon",
|
|
85
|
+
"inception",
|
|
86
|
+
"moonshot",
|
|
87
|
+
"morph",
|
|
88
|
+
"zai",
|
|
89
|
+
],
|
|
90
|
+
disabledModels: [],
|
|
91
|
+
anonymousModels: ["openai/gpt-5-nano"],
|
|
92
|
+
workflows: {
|
|
93
|
+
chatImageCompatible: "openai/gpt-4o-mini",
|
|
94
|
+
},
|
|
95
|
+
tools: {
|
|
96
|
+
webSearch: {
|
|
97
|
+
enabled: true, // Requires TAVILY_API_KEY or FIRECRAWL_API_KEY
|
|
98
|
+
},
|
|
99
|
+
urlRetrieval: {
|
|
100
|
+
enabled: true, // Requires FIRECRAWL_API_KEY
|
|
101
|
+
},
|
|
102
|
+
codeExecution: {
|
|
103
|
+
enabled: true, // Vercel-native, no key needed
|
|
104
|
+
},
|
|
105
|
+
mcp: {
|
|
106
|
+
enabled: true, // Requires MCP_ENCRYPTION_KEY
|
|
107
|
+
},
|
|
108
|
+
followupSuggestions: {
|
|
109
|
+
enabled: true,
|
|
110
|
+
},
|
|
111
|
+
text: {
|
|
112
|
+
polish: "openai/gpt-5-mini",
|
|
113
|
+
},
|
|
114
|
+
sheet: {
|
|
115
|
+
format: "openai/gpt-5-mini",
|
|
116
|
+
analyze: "openai/gpt-5-mini",
|
|
117
|
+
},
|
|
118
|
+
code: {
|
|
119
|
+
edits: "openai/gpt-5-mini",
|
|
120
|
+
},
|
|
121
|
+
image: {
|
|
122
|
+
enabled: true, // Requires BLOB_READ_WRITE_TOKEN
|
|
123
|
+
default: "google/gemini-3-pro-image",
|
|
124
|
+
},
|
|
125
|
+
deepResearch: {
|
|
126
|
+
enabled: true, // Requires webSearch
|
|
127
|
+
defaultModel: "openai/gpt-5-nano",
|
|
128
|
+
finalReportModel: "openai/gpt-5-mini",
|
|
129
|
+
allowClarification: true,
|
|
130
|
+
maxResearcherIterations: 1,
|
|
131
|
+
maxConcurrentResearchUnits: 2,
|
|
132
|
+
maxSearchQueries: 2,
|
|
133
|
+
},
|
|
134
|
+
},
|
|
135
|
+
},
|
|
136
|
+
anonymous: {
|
|
137
|
+
credits: isProd ? 10 : 1000,
|
|
138
|
+
availableTools: [],
|
|
139
|
+
rateLimit: {
|
|
140
|
+
requestsPerMinute: isProd ? 5 : 60,
|
|
141
|
+
requestsPerMonth: isProd ? 10 : 1000,
|
|
142
|
+
},
|
|
143
|
+
},
|
|
144
|
+
attachments: {
|
|
145
|
+
maxBytes: 1024 * 1024, // 1MB
|
|
146
|
+
maxDimension: 2048,
|
|
147
|
+
acceptedTypes: {
|
|
148
|
+
"image/png": [".png"],
|
|
149
|
+
"image/jpeg": [".jpg", ".jpeg"],
|
|
150
|
+
"application/pdf": [".pdf"],
|
|
151
|
+
},
|
|
152
|
+
},
|
|
136
153
|
});
|
|
137
154
|
|
|
138
155
|
export default config;
|