@chat-js/cli 0.2.0 → 0.3.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 +209 -169
- package/package.json +48 -48
- package/templates/chat-app/CHANGELOG.md +19 -0
- package/templates/chat-app/app/(chat)/actions.ts +9 -5
- package/templates/chat-app/app/globals.css +1 -1
- package/templates/chat-app/chat.config.ts +15 -44
- package/templates/chat-app/lib/ai/followup-suggestions.ts +4 -1
- package/templates/chat-app/lib/ai/gateway-model-defaults.ts +154 -100
- package/templates/chat-app/lib/ai/gateways/openrouter-gateway.ts +2 -2
- package/templates/chat-app/lib/ai/tools/generate-image.ts +9 -2
- package/templates/chat-app/lib/ai/tools/generate-video.ts +3 -0
- package/templates/chat-app/lib/ai/types.ts +2 -2
- package/templates/chat-app/lib/config-schema.ts +126 -132
- package/templates/chat-app/lib/config.ts +2 -2
- package/templates/chat-app/package.json +2 -2
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { defineConfig } from "@/lib/config-schema";
|
|
2
2
|
|
|
3
3
|
const isProd = process.env.NODE_ENV === "production";
|
|
4
4
|
|
|
@@ -8,7 +8,7 @@ const isProd = process.env.NODE_ENV === "production";
|
|
|
8
8
|
* Edit this file to customize your app.
|
|
9
9
|
* @see https://chatjs.dev/docs/reference/config
|
|
10
10
|
*/
|
|
11
|
-
const config = {
|
|
11
|
+
const config = defineConfig({
|
|
12
12
|
appPrefix: "chatjs",
|
|
13
13
|
appName: "ChatJS",
|
|
14
14
|
appTitle: "ChatJS - The prod ready AI chat app",
|
|
@@ -68,36 +68,12 @@ const config = {
|
|
|
68
68
|
vercel: true, // Requires VERCEL_APP_CLIENT_ID + VERCEL_APP_CLIENT_SECRET
|
|
69
69
|
},
|
|
70
70
|
ai: {
|
|
71
|
-
gateway: "
|
|
72
|
-
providerOrder: ["openai"
|
|
73
|
-
disabledModels: [
|
|
74
|
-
|
|
75
|
-
// OpenAI
|
|
76
|
-
"openai/gpt-5-nano",
|
|
77
|
-
"openai/gpt-5-mini",
|
|
78
|
-
"openai/gpt-5.2",
|
|
79
|
-
"openai/gpt-5.2-chat",
|
|
80
|
-
// Google
|
|
81
|
-
"google/gemini-2.5-flash-lite",
|
|
82
|
-
"google/gemini-3-flash",
|
|
83
|
-
"google/gemini-3-pro-preview",
|
|
84
|
-
// Anthropic
|
|
85
|
-
"anthropic/claude-sonnet-4.5",
|
|
86
|
-
"anthropic/claude-opus-4.5",
|
|
87
|
-
// xAI
|
|
88
|
-
"xai/grok-4",
|
|
89
|
-
],
|
|
90
|
-
anonymousModels: [
|
|
91
|
-
"google/gemini-2.5-flash-lite",
|
|
92
|
-
"openai/gpt-5-mini",
|
|
93
|
-
"openai/gpt-5-nano",
|
|
94
|
-
"anthropic/claude-haiku-4.5",
|
|
95
|
-
],
|
|
71
|
+
gateway: "openai",
|
|
72
|
+
providerOrder: ["openai"],
|
|
73
|
+
disabledModels: [],
|
|
74
|
+
anonymousModels: ["gpt-5-nano"],
|
|
96
75
|
workflows: {
|
|
97
|
-
|
|
98
|
-
title: "openai/gpt-5-nano",
|
|
99
|
-
pdf: "openai/gpt-5-mini",
|
|
100
|
-
chatImageCompatible: "openai/gpt-4o-mini",
|
|
76
|
+
chatImageCompatible: "gpt-4o-mini",
|
|
101
77
|
},
|
|
102
78
|
tools: {
|
|
103
79
|
webSearch: {
|
|
@@ -114,30 +90,25 @@ const config = {
|
|
|
114
90
|
},
|
|
115
91
|
followupSuggestions: {
|
|
116
92
|
enabled: true,
|
|
117
|
-
default: "openai/gpt-5-nano",
|
|
118
93
|
},
|
|
119
94
|
text: {
|
|
120
|
-
polish: "
|
|
95
|
+
polish: "gpt-5-mini",
|
|
121
96
|
},
|
|
122
97
|
sheet: {
|
|
123
|
-
format: "
|
|
124
|
-
analyze: "
|
|
98
|
+
format: "gpt-5-mini",
|
|
99
|
+
analyze: "gpt-5-mini",
|
|
125
100
|
},
|
|
126
101
|
code: {
|
|
127
|
-
edits: "
|
|
102
|
+
edits: "gpt-5-mini",
|
|
128
103
|
},
|
|
129
104
|
image: {
|
|
130
105
|
enabled: true, // Requires BLOB_READ_WRITE_TOKEN
|
|
131
|
-
default: "
|
|
132
|
-
},
|
|
133
|
-
video: {
|
|
134
|
-
enabled: true, // Requires BLOB_READ_WRITE_TOKEN
|
|
135
|
-
default: "xai/grok-imagine-video",
|
|
106
|
+
default: "gpt-image-1",
|
|
136
107
|
},
|
|
137
108
|
deepResearch: {
|
|
138
109
|
enabled: true, // Requires webSearch
|
|
139
|
-
defaultModel: "
|
|
140
|
-
finalReportModel: "
|
|
110
|
+
defaultModel: "gpt-5-nano",
|
|
111
|
+
finalReportModel: "gpt-5-mini",
|
|
141
112
|
allowClarification: true,
|
|
142
113
|
maxResearcherIterations: 1,
|
|
143
114
|
maxConcurrentResearchUnits: 2,
|
|
@@ -162,6 +133,6 @@ const config = {
|
|
|
162
133
|
"application/pdf": [".pdf"],
|
|
163
134
|
},
|
|
164
135
|
},
|
|
165
|
-
}
|
|
136
|
+
});
|
|
166
137
|
|
|
167
138
|
export default config;
|
|
@@ -5,16 +5,19 @@ import type { StreamWriter } from "@/lib/ai/types";
|
|
|
5
5
|
import { config } from "@/lib/config";
|
|
6
6
|
import { generateUUID } from "@/lib/utils";
|
|
7
7
|
|
|
8
|
+
const FOLLOWUP_CONTEXT_MESSAGES = 2;
|
|
9
|
+
|
|
8
10
|
export async function generateFollowupSuggestions(
|
|
9
11
|
modelMessages: ModelMessage[]
|
|
10
12
|
) {
|
|
11
13
|
const maxQuestionCount = 5;
|
|
12
14
|
const minQuestionCount = 3;
|
|
13
15
|
const maxCharactersPerQuestion = 80;
|
|
16
|
+
const recentMessages = modelMessages.slice(-FOLLOWUP_CONTEXT_MESSAGES);
|
|
14
17
|
return streamText({
|
|
15
18
|
model: await getLanguageModel(config.ai.tools.followupSuggestions.default),
|
|
16
19
|
messages: [
|
|
17
|
-
...
|
|
20
|
+
...recentMessages,
|
|
18
21
|
{
|
|
19
22
|
role: "user",
|
|
20
23
|
content: `What question should I ask next? Return an array of suggested questions (minimum ${minQuestionCount}, maximum ${maxQuestionCount}). Each question should be no more than ${maxCharactersPerQuestion} characters.`,
|
|
@@ -1,45 +1,46 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type {
|
|
2
|
+
GatewayImageModelIdMap,
|
|
3
|
+
GatewayModelIdMap,
|
|
4
|
+
GatewayType,
|
|
5
|
+
GatewayVideoModelIdMap,
|
|
6
|
+
} from "./gateways/registry";
|
|
2
7
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
8
|
+
type VideoDefault<G extends GatewayType> = [GatewayVideoModelIdMap[G]] extends [
|
|
9
|
+
never,
|
|
10
|
+
]
|
|
11
|
+
? { enabled: false }
|
|
12
|
+
:
|
|
13
|
+
| { enabled: true; default: GatewayVideoModelIdMap[G] }
|
|
14
|
+
| { enabled: false; default?: GatewayVideoModelIdMap[G] };
|
|
15
|
+
|
|
16
|
+
type ImageDefault<G extends GatewayType> = [GatewayImageModelIdMap[G]] extends [
|
|
17
|
+
never,
|
|
18
|
+
]
|
|
19
|
+
? { enabled: false }
|
|
20
|
+
:
|
|
21
|
+
| { enabled: true; default: GatewayImageModelIdMap[G] }
|
|
22
|
+
| { enabled: false; default?: GatewayImageModelIdMap[G] };
|
|
23
|
+
|
|
24
|
+
export interface ModelDefaultsFor<G extends GatewayType> {
|
|
25
|
+
anonymousModels: GatewayModelIdMap[G][];
|
|
26
|
+
curatedDefaults: GatewayModelIdMap[G][];
|
|
27
|
+
disabledModels: GatewayModelIdMap[G][];
|
|
7
28
|
providerOrder: string[];
|
|
8
29
|
tools: {
|
|
9
|
-
webSearch: {
|
|
10
|
-
|
|
11
|
-
};
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
};
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
enabled: boolean;
|
|
20
|
-
};
|
|
21
|
-
followupSuggestions: {
|
|
22
|
-
enabled: boolean;
|
|
23
|
-
default: string;
|
|
24
|
-
};
|
|
25
|
-
text: {
|
|
26
|
-
polish: string;
|
|
27
|
-
};
|
|
28
|
-
sheet: {
|
|
29
|
-
format: string;
|
|
30
|
-
analyze: string;
|
|
31
|
-
};
|
|
32
|
-
code: {
|
|
33
|
-
edits: string;
|
|
34
|
-
};
|
|
35
|
-
image: {
|
|
36
|
-
enabled: boolean;
|
|
37
|
-
default: string;
|
|
38
|
-
};
|
|
30
|
+
webSearch: { enabled: boolean };
|
|
31
|
+
urlRetrieval: { enabled: boolean };
|
|
32
|
+
codeExecution: { enabled: boolean };
|
|
33
|
+
mcp: { enabled: boolean };
|
|
34
|
+
followupSuggestions: { enabled: boolean; default: GatewayModelIdMap[G] };
|
|
35
|
+
text: { polish: GatewayModelIdMap[G] };
|
|
36
|
+
sheet: { format: GatewayModelIdMap[G]; analyze: GatewayModelIdMap[G] };
|
|
37
|
+
code: { edits: GatewayModelIdMap[G] };
|
|
38
|
+
image: ImageDefault<G>;
|
|
39
|
+
video: VideoDefault<G>;
|
|
39
40
|
deepResearch: {
|
|
40
41
|
enabled: boolean;
|
|
41
|
-
defaultModel:
|
|
42
|
-
finalReportModel:
|
|
42
|
+
defaultModel: GatewayModelIdMap[G];
|
|
43
|
+
finalReportModel: GatewayModelIdMap[G];
|
|
43
44
|
allowClarification: boolean;
|
|
44
45
|
maxResearcherIterations: number;
|
|
45
46
|
maxConcurrentResearchUnits: number;
|
|
@@ -47,14 +48,14 @@ interface ModelDefaults {
|
|
|
47
48
|
};
|
|
48
49
|
};
|
|
49
50
|
workflows: {
|
|
50
|
-
chat:
|
|
51
|
-
title:
|
|
52
|
-
pdf:
|
|
53
|
-
chatImageCompatible:
|
|
51
|
+
chat: GatewayModelIdMap[G];
|
|
52
|
+
title: GatewayModelIdMap[G];
|
|
53
|
+
pdf: GatewayModelIdMap[G];
|
|
54
|
+
chatImageCompatible: GatewayModelIdMap[G];
|
|
54
55
|
};
|
|
55
56
|
}
|
|
56
57
|
|
|
57
|
-
const
|
|
58
|
+
const vercelDefaults = {
|
|
58
59
|
providerOrder: ["openai", "google", "anthropic"],
|
|
59
60
|
disabledModels: [],
|
|
60
61
|
curatedDefaults: [
|
|
@@ -77,36 +78,67 @@ const multiProviderDefaults = {
|
|
|
77
78
|
chatImageCompatible: "openai/gpt-4o-mini",
|
|
78
79
|
},
|
|
79
80
|
tools: {
|
|
80
|
-
webSearch: {
|
|
81
|
-
|
|
82
|
-
},
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
},
|
|
86
|
-
codeExecution: {
|
|
81
|
+
webSearch: { enabled: false },
|
|
82
|
+
urlRetrieval: { enabled: false },
|
|
83
|
+
codeExecution: { enabled: false },
|
|
84
|
+
mcp: { enabled: false },
|
|
85
|
+
followupSuggestions: {
|
|
87
86
|
enabled: false,
|
|
87
|
+
default: "google/gemini-2.5-flash-lite",
|
|
88
88
|
},
|
|
89
|
-
|
|
89
|
+
text: { polish: "openai/gpt-5-mini" },
|
|
90
|
+
sheet: { format: "openai/gpt-5-mini", analyze: "openai/gpt-5-mini" },
|
|
91
|
+
code: { edits: "openai/gpt-5-mini" },
|
|
92
|
+
image: { enabled: false, default: "google/gemini-3-pro-image" },
|
|
93
|
+
video: { enabled: false, default: "xai/grok-imagine-video" },
|
|
94
|
+
deepResearch: {
|
|
90
95
|
enabled: false,
|
|
96
|
+
defaultModel: "google/gemini-2.5-flash-lite",
|
|
97
|
+
finalReportModel: "google/gemini-3-flash",
|
|
98
|
+
allowClarification: true,
|
|
99
|
+
maxResearcherIterations: 1,
|
|
100
|
+
maxConcurrentResearchUnits: 2,
|
|
101
|
+
maxSearchQueries: 2,
|
|
91
102
|
},
|
|
103
|
+
},
|
|
104
|
+
} satisfies ModelDefaultsFor<"vercel">;
|
|
105
|
+
|
|
106
|
+
const openrouterDefaults = {
|
|
107
|
+
providerOrder: ["openai", "google", "anthropic"],
|
|
108
|
+
disabledModels: [],
|
|
109
|
+
curatedDefaults: [
|
|
110
|
+
"openai/gpt-5-nano",
|
|
111
|
+
"openai/gpt-5-mini",
|
|
112
|
+
"openai/gpt-5.2",
|
|
113
|
+
"openai/gpt-5.2-chat",
|
|
114
|
+
"google/gemini-2.5-flash-lite",
|
|
115
|
+
"google/gemini-3-flash",
|
|
116
|
+
"google/gemini-3-pro-preview",
|
|
117
|
+
"anthropic/claude-sonnet-4.5",
|
|
118
|
+
"anthropic/claude-opus-4.5",
|
|
119
|
+
"xai/grok-4",
|
|
120
|
+
],
|
|
121
|
+
anonymousModels: ["google/gemini-2.5-flash-lite", "openai/gpt-5-nano"],
|
|
122
|
+
workflows: {
|
|
123
|
+
chat: "openai/gpt-5-mini",
|
|
124
|
+
title: "openai/gpt-5-nano",
|
|
125
|
+
pdf: "openai/gpt-5-mini",
|
|
126
|
+
chatImageCompatible: "openai/gpt-4o-mini",
|
|
127
|
+
},
|
|
128
|
+
tools: {
|
|
129
|
+
webSearch: { enabled: false },
|
|
130
|
+
urlRetrieval: { enabled: false },
|
|
131
|
+
codeExecution: { enabled: false },
|
|
132
|
+
mcp: { enabled: false },
|
|
92
133
|
followupSuggestions: {
|
|
93
134
|
enabled: false,
|
|
94
135
|
default: "google/gemini-2.5-flash-lite",
|
|
95
136
|
},
|
|
96
|
-
text: {
|
|
97
|
-
|
|
98
|
-
},
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
analyze: "openai/gpt-5-mini",
|
|
102
|
-
},
|
|
103
|
-
code: {
|
|
104
|
-
edits: "openai/gpt-5-mini",
|
|
105
|
-
},
|
|
106
|
-
image: {
|
|
107
|
-
enabled: false,
|
|
108
|
-
default: "google/gemini-3-pro-image",
|
|
109
|
-
},
|
|
137
|
+
text: { polish: "openai/gpt-5-mini" },
|
|
138
|
+
sheet: { format: "openai/gpt-5-mini", analyze: "openai/gpt-5-mini" },
|
|
139
|
+
code: { edits: "openai/gpt-5-mini" },
|
|
140
|
+
image: { enabled: false },
|
|
141
|
+
video: { enabled: false },
|
|
110
142
|
deepResearch: {
|
|
111
143
|
enabled: false,
|
|
112
144
|
defaultModel: "google/gemini-2.5-flash-lite",
|
|
@@ -117,9 +149,9 @@ const multiProviderDefaults = {
|
|
|
117
149
|
maxSearchQueries: 2,
|
|
118
150
|
},
|
|
119
151
|
},
|
|
120
|
-
} satisfies
|
|
152
|
+
} satisfies ModelDefaultsFor<"openrouter">;
|
|
121
153
|
|
|
122
|
-
const
|
|
154
|
+
const openaiDefaults = {
|
|
123
155
|
providerOrder: ["openai"],
|
|
124
156
|
disabledModels: [],
|
|
125
157
|
curatedDefaults: [
|
|
@@ -136,36 +168,55 @@ const openaiOnlyDefaults = {
|
|
|
136
168
|
chatImageCompatible: "gpt-4o-mini",
|
|
137
169
|
},
|
|
138
170
|
tools: {
|
|
139
|
-
webSearch: {
|
|
140
|
-
|
|
141
|
-
},
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
},
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
},
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
},
|
|
151
|
-
followupSuggestions: {
|
|
152
|
-
enabled: false,
|
|
153
|
-
default: "gpt-5-nano",
|
|
154
|
-
},
|
|
155
|
-
text: {
|
|
156
|
-
polish: "gpt-5-mini",
|
|
157
|
-
},
|
|
158
|
-
sheet: {
|
|
159
|
-
format: "gpt-5-mini",
|
|
160
|
-
analyze: "gpt-5-mini",
|
|
161
|
-
},
|
|
162
|
-
code: {
|
|
163
|
-
edits: "gpt-5-mini",
|
|
164
|
-
},
|
|
165
|
-
image: {
|
|
171
|
+
webSearch: { enabled: false },
|
|
172
|
+
urlRetrieval: { enabled: false },
|
|
173
|
+
codeExecution: { enabled: false },
|
|
174
|
+
mcp: { enabled: false },
|
|
175
|
+
followupSuggestions: { enabled: false, default: "gpt-5-nano" },
|
|
176
|
+
text: { polish: "gpt-5-mini" },
|
|
177
|
+
sheet: { format: "gpt-5-mini", analyze: "gpt-5-mini" },
|
|
178
|
+
code: { edits: "gpt-5-mini" },
|
|
179
|
+
image: { enabled: false },
|
|
180
|
+
video: { enabled: false },
|
|
181
|
+
deepResearch: {
|
|
166
182
|
enabled: false,
|
|
167
|
-
|
|
183
|
+
defaultModel: "gpt-5-nano",
|
|
184
|
+
finalReportModel: "gpt-5-mini",
|
|
185
|
+
allowClarification: true,
|
|
186
|
+
maxResearcherIterations: 1,
|
|
187
|
+
maxConcurrentResearchUnits: 2,
|
|
188
|
+
maxSearchQueries: 2,
|
|
168
189
|
},
|
|
190
|
+
},
|
|
191
|
+
} satisfies ModelDefaultsFor<"openai">;
|
|
192
|
+
|
|
193
|
+
const openaiCompatibleDefaults = {
|
|
194
|
+
providerOrder: ["openai"],
|
|
195
|
+
disabledModels: [],
|
|
196
|
+
curatedDefaults: [
|
|
197
|
+
"gpt-5-nano",
|
|
198
|
+
"gpt-5-mini",
|
|
199
|
+
"gpt-5.2",
|
|
200
|
+
"gpt-5.2-chat-latest",
|
|
201
|
+
],
|
|
202
|
+
anonymousModels: ["gpt-5-nano"],
|
|
203
|
+
workflows: {
|
|
204
|
+
chat: "gpt-5-mini",
|
|
205
|
+
title: "gpt-5-nano",
|
|
206
|
+
pdf: "gpt-5-mini",
|
|
207
|
+
chatImageCompatible: "gpt-4o-mini",
|
|
208
|
+
},
|
|
209
|
+
tools: {
|
|
210
|
+
webSearch: { enabled: false },
|
|
211
|
+
urlRetrieval: { enabled: false },
|
|
212
|
+
codeExecution: { enabled: false },
|
|
213
|
+
mcp: { enabled: false },
|
|
214
|
+
followupSuggestions: { enabled: false, default: "gpt-5-nano" },
|
|
215
|
+
text: { polish: "gpt-5-mini" },
|
|
216
|
+
sheet: { format: "gpt-5-mini", analyze: "gpt-5-mini" },
|
|
217
|
+
code: { edits: "gpt-5-mini" },
|
|
218
|
+
image: { enabled: false },
|
|
219
|
+
video: { enabled: false },
|
|
169
220
|
deepResearch: {
|
|
170
221
|
enabled: false,
|
|
171
222
|
defaultModel: "gpt-5-nano",
|
|
@@ -176,11 +227,14 @@ const openaiOnlyDefaults = {
|
|
|
176
227
|
maxSearchQueries: 2,
|
|
177
228
|
},
|
|
178
229
|
},
|
|
179
|
-
} satisfies
|
|
230
|
+
} satisfies ModelDefaultsFor<"openai-compatible">;
|
|
180
231
|
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
232
|
+
// Record ensures a compile error if a new gateway is added but not here.
|
|
233
|
+
export const GATEWAY_MODEL_DEFAULTS: {
|
|
234
|
+
[G in GatewayType]: ModelDefaultsFor<G>;
|
|
235
|
+
} = {
|
|
236
|
+
vercel: vercelDefaults,
|
|
237
|
+
openrouter: openrouterDefaults,
|
|
238
|
+
openai: openaiDefaults,
|
|
239
|
+
"openai-compatible": openaiCompatibleDefaults,
|
|
186
240
|
};
|
|
@@ -96,7 +96,7 @@ function toAiGatewayModel(model: OpenRouterModelResponse): AiGatewayModel {
|
|
|
96
96
|
}
|
|
97
97
|
|
|
98
98
|
export class OpenRouterGateway
|
|
99
|
-
implements GatewayProvider<"openrouter", string,
|
|
99
|
+
implements GatewayProvider<"openrouter", string, never, never>
|
|
100
100
|
{
|
|
101
101
|
readonly type = "openrouter" as const;
|
|
102
102
|
|
|
@@ -113,7 +113,7 @@ export class OpenRouterGateway
|
|
|
113
113
|
return provider.chat(modelId);
|
|
114
114
|
}
|
|
115
115
|
|
|
116
|
-
createImageModel(_modelId:
|
|
116
|
+
createImageModel(_modelId: never): ImageModel | null {
|
|
117
117
|
// OpenRouter routes image generation through multimodal language models.
|
|
118
118
|
// Return null to signal callers should use createLanguageModel instead.
|
|
119
119
|
return null;
|
|
@@ -41,6 +41,9 @@ async function resolveImageModel(selectedModel?: string): Promise<{
|
|
|
41
41
|
}
|
|
42
42
|
|
|
43
43
|
// Fall back to the configured default image model
|
|
44
|
+
if (!config.ai.tools.image.enabled) {
|
|
45
|
+
throw new Error("Image generation is not enabled");
|
|
46
|
+
}
|
|
44
47
|
const defaultId = config.ai.tools.image.default;
|
|
45
48
|
try {
|
|
46
49
|
const model = await getAppModelDefinition(defaultId as AppModelId);
|
|
@@ -134,6 +137,10 @@ async function runGenerateImageTraditional({
|
|
|
134
137
|
startMs: number;
|
|
135
138
|
costAccumulator?: CostAccumulator;
|
|
136
139
|
}): Promise<{ imageUrl: string; prompt: string }> {
|
|
140
|
+
if (!config.ai.tools.image.enabled) {
|
|
141
|
+
throw new Error("Image generation is not enabled");
|
|
142
|
+
}
|
|
143
|
+
const imageDefault = config.ai.tools.image.default;
|
|
137
144
|
let promptInput:
|
|
138
145
|
| string
|
|
139
146
|
| {
|
|
@@ -161,7 +168,7 @@ async function runGenerateImageTraditional({
|
|
|
161
168
|
}
|
|
162
169
|
|
|
163
170
|
const res = await generateImage({
|
|
164
|
-
model: getImageModel(
|
|
171
|
+
model: getImageModel(imageDefault),
|
|
165
172
|
prompt: promptInput,
|
|
166
173
|
n: 1,
|
|
167
174
|
providerOptions: {
|
|
@@ -184,7 +191,7 @@ async function runGenerateImageTraditional({
|
|
|
184
191
|
|
|
185
192
|
if (res.usage) {
|
|
186
193
|
costAccumulator?.addLLMCost(
|
|
187
|
-
|
|
194
|
+
imageDefault as AppModelId,
|
|
188
195
|
{
|
|
189
196
|
inputTokens: res.usage.inputTokens,
|
|
190
197
|
outputTokens: res.usage.outputTokens,
|
|
@@ -48,6 +48,9 @@ async function resolveVideoModel(selectedModel?: string): Promise<string> {
|
|
|
48
48
|
// Not in app models registry, fall through
|
|
49
49
|
}
|
|
50
50
|
}
|
|
51
|
+
if (!config.ai.tools.video.enabled) {
|
|
52
|
+
throw new Error("Video generation is not enabled");
|
|
53
|
+
}
|
|
51
54
|
return config.ai.tools.video.default;
|
|
52
55
|
}
|
|
53
56
|
|
|
@@ -101,7 +101,7 @@ type webSearchTool = InferUITool<ReturnType<typeof tavilyWebSearch>>;
|
|
|
101
101
|
type codeExecutionTool = InferUITool<ReturnType<typeof codeExecution>>;
|
|
102
102
|
type retrieveUrlTool = InferUITool<typeof retrieveUrl>;
|
|
103
103
|
|
|
104
|
-
// biome-ignore lint/style/useConsistentTypeDefinitions:
|
|
104
|
+
// biome-ignore lint/style/useConsistentTypeDefinitions: using type for mapped type compatibility
|
|
105
105
|
export type ChatTools = {
|
|
106
106
|
codeExecution: codeExecutionTool;
|
|
107
107
|
createCodeDocument: createCodeDocumentToolType;
|
|
@@ -123,7 +123,7 @@ interface FollowupSuggestions {
|
|
|
123
123
|
suggestions: string[];
|
|
124
124
|
}
|
|
125
125
|
|
|
126
|
-
// biome-ignore lint/style/useConsistentTypeDefinitions:
|
|
126
|
+
// biome-ignore lint/style/useConsistentTypeDefinitions: using type for mapped type compatibility
|
|
127
127
|
export type CustomUIDataTypes = {
|
|
128
128
|
appendMessage: string;
|
|
129
129
|
chatConfirmed: {
|