@chat-js/cli 0.1.4 → 0.2.1
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 +394 -247
- package/package.json +48 -48
- package/templates/chat-app/.claude/skiller.toml +18 -0
- package/templates/chat-app/.claude/skills/chat-context/SKILL.md +6 -0
- package/templates/chat-app/.claude/skills/chat-context/chat-context.mdc +36 -0
- package/templates/chat-app/.claude/skills/lazy-prefetch-pattern/lazy-prefetch-pattern.mdc +27 -0
- package/templates/chat-app/.claude/skills/react/react.mdc +29 -0
- package/templates/chat-app/.claude/skills/trpc-patterns/trpc-patterns.mdc +77 -0
- package/templates/chat-app/.claude/skills/typescript/typescript.mdc +53 -0
- package/templates/chat-app/.claude/skills/ultracite/ultracite.mdc +129 -0
- package/templates/chat-app/.cursor/skills/chat-context/SKILL.md +37 -0
- package/templates/chat-app/.cursor/skills/lazy-prefetch-pattern/SKILL.md +26 -0
- package/templates/chat-app/.cursor/skills/react/SKILL.md +28 -0
- package/templates/chat-app/.cursor/skills/trpc-patterns/SKILL.md +76 -0
- package/templates/chat-app/.cursor/skills/typescript/SKILL.md +52 -0
- package/templates/chat-app/.cursor/skills/ultracite/SKILL.md +128 -0
- package/templates/chat-app/app/(chat)/actions.ts +17 -13
- package/templates/chat-app/app/(chat)/api/chat/[id]/stream/route.ts +6 -5
- package/templates/chat-app/app/(chat)/api/chat/route.ts +14 -15
- package/templates/chat-app/app/(chat)/chat-providers.tsx +2 -2
- package/templates/chat-app/app/(chat)/layout.tsx +7 -6
- package/templates/chat-app/app/api/cron/cleanup/route.ts +4 -3
- package/templates/chat-app/app/globals.css +23 -23
- package/templates/chat-app/app/layout.tsx +1 -1
- package/templates/chat-app/biome.jsonc +3 -3
- package/templates/chat-app/chat.config.ts +48 -21
- package/templates/chat-app/components/anonymous-session-init.tsx +4 -12
- package/templates/chat-app/components/artifact-actions.tsx +5 -5
- package/templates/chat-app/components/artifact-panel.tsx +6 -6
- package/templates/chat-app/components/assistant-message.tsx +1 -1
- package/templates/chat-app/components/chat/chat-layout.tsx +2 -2
- package/templates/chat-app/components/chat/chat-welcome.tsx +1 -0
- package/templates/chat-app/components/chat-features-definitions.ts +11 -8
- package/templates/chat-app/components/chat-menu-items.tsx +4 -4
- package/templates/chat-app/components/chat-sync.tsx +1 -1
- package/templates/chat-app/components/clone-chat-button.tsx +2 -2
- package/templates/chat-app/components/code-editor.tsx +5 -5
- package/templates/chat-app/components/connectors-dropdown.tsx +2 -2
- package/templates/chat-app/components/console.tsx +5 -5
- package/templates/chat-app/components/create-artifact.tsx +28 -28
- package/templates/chat-app/components/data-stream-provider.tsx +2 -2
- package/templates/chat-app/components/deep-research-progress.tsx +2 -2
- package/templates/chat-app/components/delete-chat-dialog.tsx +3 -3
- package/templates/chat-app/components/delete-project-dialog.tsx +3 -3
- package/templates/chat-app/components/diffview.tsx +3 -3
- package/templates/chat-app/components/favicon-group.tsx +7 -7
- package/templates/chat-app/components/header-breadcrumb.tsx +11 -11
- package/templates/chat-app/components/image-editor.tsx +5 -5
- package/templates/chat-app/components/image-modal.tsx +4 -4
- package/templates/chat-app/components/interactive-chart-impl.tsx +269 -0
- package/templates/chat-app/components/interactive-charts.tsx +18 -246
- package/templates/chat-app/components/lexical-chat-input.tsx +10 -10
- package/templates/chat-app/components/message-editor.tsx +3 -3
- package/templates/chat-app/components/message-parts.tsx +8 -3
- package/templates/chat-app/components/messages-pane.tsx +4 -4
- package/templates/chat-app/components/messages.tsx +5 -5
- package/templates/chat-app/components/model-selector.tsx +4 -1
- package/templates/chat-app/components/multimodal-input.tsx +14 -5
- package/templates/chat-app/components/part/code-execution.tsx +4 -1
- package/templates/chat-app/components/part/document-common.tsx +8 -8
- package/templates/chat-app/components/part/document-preview.tsx +34 -16
- package/templates/chat-app/components/part/document-tool.tsx +3 -3
- package/templates/chat-app/components/part/dynamic-tool.tsx +3 -3
- package/templates/chat-app/components/part/generate-video.tsx +54 -0
- package/templates/chat-app/components/part/message-reasoning.tsx +3 -3
- package/templates/chat-app/components/project-details-dialog.tsx +4 -4
- package/templates/chat-app/components/project-home.tsx +1 -0
- package/templates/chat-app/components/project-icon-picker.tsx +5 -5
- package/templates/chat-app/components/project-icon.tsx +4 -4
- package/templates/chat-app/components/project-menu-items.tsx +3 -3
- package/templates/chat-app/components/research-tasks.tsx +3 -3
- package/templates/chat-app/components/sandbox.tsx +4 -4
- package/templates/chat-app/components/search-chats-dialog.tsx +11 -11
- package/templates/chat-app/components/settings/connectors-settings.tsx +1 -1
- package/templates/chat-app/components/settings/settings-nav.tsx +1 -1
- package/templates/chat-app/components/sheet-editor.tsx +5 -5
- package/templates/chat-app/components/sidebar-chats-list.tsx +5 -5
- package/templates/chat-app/components/suggested-actions.tsx +3 -3
- package/templates/chat-app/components/text-editor.tsx +5 -5
- package/templates/chat-app/components/toolbar.tsx +6 -6
- package/templates/chat-app/components/upgrade-cta/login-cta-banner.tsx +5 -5
- package/templates/chat-app/components/upgrade-cta/login-prompt.tsx +4 -4
- package/templates/chat-app/components/upgrade-cta/share-menu-item.tsx +3 -3
- package/templates/chat-app/components/user-message.tsx +3 -3
- package/templates/chat-app/components/version-footer.tsx +4 -4
- package/templates/chat-app/hooks/chat-sync-hooks.ts +0 -55
- package/templates/chat-app/hooks/use-artifact.tsx +3 -3
- package/templates/chat-app/hooks/use-auto-focus.ts +37 -7
- package/templates/chat-app/hooks/use-media-query.tsx +2 -4
- package/templates/chat-app/lib/ai/active-gateway.ts +1 -1
- package/templates/chat-app/lib/ai/ai-gateway-models-schemas.ts +30 -6
- package/templates/chat-app/lib/ai/app-model-id.ts +1 -1
- package/templates/chat-app/lib/ai/app-models.ts +4 -4
- package/templates/chat-app/lib/ai/eval-agent.ts +5 -5
- package/templates/chat-app/lib/ai/followup-suggestions.ts +5 -2
- package/templates/chat-app/lib/ai/gateway-model-defaults.ts +131 -41
- package/templates/chat-app/lib/ai/gateways/gateway-provider.ts +10 -6
- package/templates/chat-app/lib/ai/gateways/openai-compatible-gateway.ts +9 -4
- package/templates/chat-app/lib/ai/gateways/openai-gateway.ts +9 -4
- package/templates/chat-app/lib/ai/gateways/openrouter-gateway.ts +17 -12
- package/templates/chat-app/lib/ai/gateways/registry.ts +9 -0
- package/templates/chat-app/lib/ai/gateways/vercel-gateway.ts +36 -4
- package/templates/chat-app/lib/ai/mcp/cache.ts +13 -13
- package/templates/chat-app/lib/ai/model-data.ts +21 -20
- package/templates/chat-app/lib/ai/models.generated.ts +4397 -3592
- package/templates/chat-app/lib/ai/models.ts +1 -1
- package/templates/chat-app/lib/ai/providers.ts +10 -0
- package/templates/chat-app/lib/ai/text-splitter.ts +3 -4
- package/templates/chat-app/lib/ai/to-model-data.ts +1 -0
- package/templates/chat-app/lib/ai/tools/code-execution.ts +122 -53
- package/templates/chat-app/lib/ai/tools/deep-research/configuration.ts +35 -32
- package/templates/chat-app/lib/ai/tools/deep-research/pipeline.ts +2 -2
- package/templates/chat-app/lib/ai/tools/deep-research/types.ts +9 -9
- package/templates/chat-app/lib/ai/tools/documents/types.ts +4 -4
- package/templates/chat-app/lib/ai/tools/generate-image.ts +42 -20
- package/templates/chat-app/lib/ai/tools/generate-video.ts +166 -0
- package/templates/chat-app/lib/ai/tools/get-weather.ts +20 -20
- package/templates/chat-app/lib/ai/tools/read-document.ts +3 -3
- package/templates/chat-app/lib/ai/tools/steps/multi-query-web-search.ts +11 -11
- package/templates/chat-app/lib/ai/tools/steps/web-search.ts +6 -6
- package/templates/chat-app/lib/ai/tools/tools-definitions.ts +10 -5
- package/templates/chat-app/lib/ai/tools/tools.ts +15 -6
- package/templates/chat-app/lib/ai/tools/types.ts +2 -2
- package/templates/chat-app/lib/ai/types.ts +22 -13
- package/templates/chat-app/lib/artifacts/code/client.tsx +5 -5
- package/templates/chat-app/lib/artifacts/sheet/client.tsx +2 -2
- package/templates/chat-app/lib/artifacts/text/client.tsx +18 -3
- package/templates/chat-app/lib/clone-messages.test.ts +6 -1
- package/templates/chat-app/lib/config-requirements.ts +19 -10
- package/templates/chat-app/lib/config-schema.ts +189 -103
- package/templates/chat-app/lib/config.ts +4 -4
- package/templates/chat-app/lib/credits/cost-accumulator.ts +11 -8
- package/templates/chat-app/lib/env-schema.ts +1 -1
- package/templates/chat-app/lib/features-config.ts +6 -6
- package/templates/chat-app/lib/stores/with-threads.ts +3 -3
- package/templates/chat-app/lib/thread-utils.ts +2 -2
- package/templates/chat-app/lib/types/anonymous.ts +4 -4
- package/templates/chat-app/lib/types/ui-chat.ts +7 -7
- package/templates/chat-app/lib/utils/download-assets.ts +3 -3
- package/templates/chat-app/lib/utils/rate-limit.ts +8 -8
- package/templates/chat-app/next.config.ts +0 -25
- package/templates/chat-app/package.json +16 -16
- package/templates/chat-app/playwright.config.ts +5 -5
- package/templates/chat-app/providers/chat-id-provider.tsx +5 -5
- package/templates/chat-app/providers/chat-input-provider.tsx +15 -15
- package/templates/chat-app/providers/chat-models-provider.tsx +3 -3
- package/templates/chat-app/providers/default-model-provider.tsx +5 -5
- package/templates/chat-app/providers/parse-chat-id-from-pathname.test.ts +16 -0
- package/templates/chat-app/providers/session-provider.tsx +2 -2
- package/templates/chat-app/scripts/check-env.ts +36 -4
- package/templates/chat-app/tests/artifacts.e2e.ts +7 -0
- package/templates/chat-app/tests/auth.setup.e2e.ts +10 -0
- package/templates/chat-app/tests/chat.e2e.ts +7 -0
- package/templates/chat-app/tests/reasoning.e2e.ts +7 -0
- package/templates/chat-app/tests/reasoning.setup.e2e.ts +10 -0
- package/templates/chat-app/trpc/routers/chat.router.ts +1 -1
- package/templates/chat-app/trpc/routers/mcp.router.ts +3 -3
- package/templates/chat-app/vitest.config.ts +7 -0
- package/templates/chat-app/next-env.d.ts +0 -6
- package/templates/chat-app/tsconfig.tsbuildinfo +0 -1
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
import { experimental_generateVideo as generateVideo, tool } from "ai";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
import { type AppModelId, getAppModelDefinition } from "@/lib/ai/app-models";
|
|
4
|
+
import { getVideoModel } from "@/lib/ai/providers";
|
|
5
|
+
import { uploadFile } from "@/lib/blob";
|
|
6
|
+
import { config } from "@/lib/config";
|
|
7
|
+
import type { CostAccumulator } from "@/lib/credits/cost-accumulator";
|
|
8
|
+
import { createModuleLogger } from "@/lib/logger";
|
|
9
|
+
import { toolsDefinitions } from "./tools-definitions";
|
|
10
|
+
|
|
11
|
+
interface GenerateVideoProps {
|
|
12
|
+
costAccumulator?: CostAccumulator;
|
|
13
|
+
selectedModel?: string;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const log = createModuleLogger("ai.tools.generate-video");
|
|
17
|
+
const DEFAULT_ASPECT_RATIO = "16:9";
|
|
18
|
+
const DEFAULT_DURATION_SECONDS = 5;
|
|
19
|
+
const ALLOWED_EXTENSIONS = new Set(["mp4", "webm", "mov"]);
|
|
20
|
+
|
|
21
|
+
function resolveVideoExtension(mediaType?: string): string {
|
|
22
|
+
if (!mediaType) {
|
|
23
|
+
return "mp4";
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const [, subtypeWithParams] = mediaType.split("/");
|
|
27
|
+
if (!subtypeWithParams) {
|
|
28
|
+
return "mp4";
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const subtype = subtypeWithParams.split(";")[0]?.trim().toLowerCase();
|
|
32
|
+
if (!subtype) {
|
|
33
|
+
return "mp4";
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const mappedSubtype = subtype === "quicktime" ? "mov" : subtype;
|
|
37
|
+
return ALLOWED_EXTENSIONS.has(mappedSubtype) ? mappedSubtype : "mp4";
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
async function resolveVideoModel(selectedModel?: string): Promise<string> {
|
|
41
|
+
if (selectedModel) {
|
|
42
|
+
try {
|
|
43
|
+
const model = await getAppModelDefinition(selectedModel as AppModelId);
|
|
44
|
+
if (model.output.video) {
|
|
45
|
+
return selectedModel;
|
|
46
|
+
}
|
|
47
|
+
} catch {
|
|
48
|
+
// Not in app models registry, fall through
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return config.ai.tools.video.default;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export const generateVideoTool = ({
|
|
55
|
+
selectedModel,
|
|
56
|
+
costAccumulator,
|
|
57
|
+
}: GenerateVideoProps = {}) =>
|
|
58
|
+
tool({
|
|
59
|
+
description:
|
|
60
|
+
"Generate a short video clip from a text prompt. Use this when the user asks to create, make, or generate a video.",
|
|
61
|
+
inputSchema: z.object({
|
|
62
|
+
prompt: z
|
|
63
|
+
.string()
|
|
64
|
+
.describe("A descriptive prompt for the video to generate."),
|
|
65
|
+
aspectRatio: z
|
|
66
|
+
.enum(["16:9", "9:16", "1:1"])
|
|
67
|
+
.optional()
|
|
68
|
+
.describe("Optional output aspect ratio. Defaults to 16:9."),
|
|
69
|
+
durationSeconds: z
|
|
70
|
+
.number()
|
|
71
|
+
.int()
|
|
72
|
+
.min(1)
|
|
73
|
+
.max(10)
|
|
74
|
+
.optional()
|
|
75
|
+
.describe("Optional video duration in seconds. Defaults to 5."),
|
|
76
|
+
}),
|
|
77
|
+
execute: async ({ prompt, aspectRatio, durationSeconds }) => {
|
|
78
|
+
const startMs = Date.now();
|
|
79
|
+
const finalAspectRatio = aspectRatio ?? DEFAULT_ASPECT_RATIO;
|
|
80
|
+
const finalDurationSeconds = durationSeconds ?? DEFAULT_DURATION_SECONDS;
|
|
81
|
+
|
|
82
|
+
log.info(
|
|
83
|
+
{
|
|
84
|
+
promptLength: prompt.length,
|
|
85
|
+
selectedModel,
|
|
86
|
+
aspectRatio: finalAspectRatio,
|
|
87
|
+
durationSeconds: finalDurationSeconds,
|
|
88
|
+
},
|
|
89
|
+
"generateVideo: start"
|
|
90
|
+
);
|
|
91
|
+
|
|
92
|
+
try {
|
|
93
|
+
const modelId = await resolveVideoModel(selectedModel);
|
|
94
|
+
const isGoogleModel =
|
|
95
|
+
modelId.startsWith("google/") || modelId.includes("gemini");
|
|
96
|
+
|
|
97
|
+
log.debug({ modelId }, "generateVideo: resolved model");
|
|
98
|
+
|
|
99
|
+
const result = await generateVideo({
|
|
100
|
+
model: getVideoModel(modelId),
|
|
101
|
+
prompt,
|
|
102
|
+
aspectRatio: finalAspectRatio,
|
|
103
|
+
duration: finalDurationSeconds,
|
|
104
|
+
providerOptions: {
|
|
105
|
+
...(isGoogleModel && {
|
|
106
|
+
google: {
|
|
107
|
+
aspectRatio: finalAspectRatio,
|
|
108
|
+
},
|
|
109
|
+
}),
|
|
110
|
+
},
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
const video = result.video;
|
|
114
|
+
if (!video) {
|
|
115
|
+
throw new Error("No video generated");
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const buffer = Buffer.from(video.uint8Array);
|
|
119
|
+
const timestamp = Date.now();
|
|
120
|
+
const ext = resolveVideoExtension(video.mediaType);
|
|
121
|
+
const filename = `generated-video-${timestamp}.${ext}`;
|
|
122
|
+
const uploaded = await uploadFile(filename, buffer);
|
|
123
|
+
|
|
124
|
+
costAccumulator?.addAPICost(
|
|
125
|
+
"generateVideo",
|
|
126
|
+
toolsDefinitions.generateVideo.cost
|
|
127
|
+
);
|
|
128
|
+
|
|
129
|
+
log.info(
|
|
130
|
+
{
|
|
131
|
+
ms: Date.now() - startMs,
|
|
132
|
+
modelId,
|
|
133
|
+
videoUrl: uploaded.url,
|
|
134
|
+
},
|
|
135
|
+
"generateVideo: success"
|
|
136
|
+
);
|
|
137
|
+
|
|
138
|
+
return { videoUrl: uploaded.url, prompt };
|
|
139
|
+
} catch (error) {
|
|
140
|
+
const errorMessage = error instanceof Error ? error.message : "";
|
|
141
|
+
const isUnsupportedVideoGateway = errorMessage.includes(
|
|
142
|
+
"does not support video models"
|
|
143
|
+
);
|
|
144
|
+
|
|
145
|
+
log.error(
|
|
146
|
+
{
|
|
147
|
+
ms: Date.now() - startMs,
|
|
148
|
+
selectedModel,
|
|
149
|
+
error:
|
|
150
|
+
error instanceof Error
|
|
151
|
+
? { message: error.message, name: error.name }
|
|
152
|
+
: error,
|
|
153
|
+
},
|
|
154
|
+
"generateVideo: failure"
|
|
155
|
+
);
|
|
156
|
+
|
|
157
|
+
if (isUnsupportedVideoGateway) {
|
|
158
|
+
throw new Error(
|
|
159
|
+
"Video generation is not available for the active gateway."
|
|
160
|
+
);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
throw error;
|
|
164
|
+
}
|
|
165
|
+
},
|
|
166
|
+
});
|
|
@@ -23,40 +23,40 @@ export const getWeather = tool({
|
|
|
23
23
|
},
|
|
24
24
|
});
|
|
25
25
|
|
|
26
|
-
export
|
|
27
|
-
latitude: number;
|
|
28
|
-
longitude: number;
|
|
29
|
-
generationtime_ms: number;
|
|
30
|
-
utc_offset_seconds: number;
|
|
31
|
-
timezone: string;
|
|
32
|
-
timezone_abbreviation: string;
|
|
33
|
-
elevation: number;
|
|
34
|
-
current_units: {
|
|
35
|
-
time: string;
|
|
36
|
-
interval: string;
|
|
37
|
-
temperature_2m: string;
|
|
38
|
-
};
|
|
26
|
+
export interface WeatherAtLocation {
|
|
39
27
|
current: {
|
|
40
28
|
time: string;
|
|
41
29
|
interval: number;
|
|
42
30
|
temperature_2m: number;
|
|
43
31
|
};
|
|
44
|
-
|
|
32
|
+
current_units: {
|
|
45
33
|
time: string;
|
|
34
|
+
interval: string;
|
|
46
35
|
temperature_2m: string;
|
|
47
36
|
};
|
|
48
|
-
|
|
37
|
+
daily: {
|
|
49
38
|
time: string[];
|
|
50
|
-
|
|
39
|
+
sunrise: string[];
|
|
40
|
+
sunset: string[];
|
|
51
41
|
};
|
|
52
42
|
daily_units: {
|
|
53
43
|
time: string;
|
|
54
44
|
sunrise: string;
|
|
55
45
|
sunset: string;
|
|
56
46
|
};
|
|
57
|
-
|
|
47
|
+
elevation: number;
|
|
48
|
+
generationtime_ms: number;
|
|
49
|
+
hourly: {
|
|
58
50
|
time: string[];
|
|
59
|
-
|
|
60
|
-
sunset: string[];
|
|
51
|
+
temperature_2m: number[];
|
|
61
52
|
};
|
|
62
|
-
|
|
53
|
+
hourly_units: {
|
|
54
|
+
time: string;
|
|
55
|
+
temperature_2m: string;
|
|
56
|
+
};
|
|
57
|
+
latitude: number;
|
|
58
|
+
longitude: number;
|
|
59
|
+
timezone: string;
|
|
60
|
+
timezone_abbreviation: string;
|
|
61
|
+
utc_offset_seconds: number;
|
|
62
|
+
}
|
|
@@ -4,10 +4,10 @@ import type { ToolSession } from "@/lib/ai/tools/types";
|
|
|
4
4
|
import { getDocumentById } from "@/lib/db/queries";
|
|
5
5
|
import type { StreamWriter } from "../types";
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
session: ToolSession;
|
|
7
|
+
interface ReadDocumentProps {
|
|
9
8
|
dataStream: StreamWriter;
|
|
10
|
-
|
|
9
|
+
session: ToolSession;
|
|
10
|
+
}
|
|
11
11
|
|
|
12
12
|
export const readDocument = ({
|
|
13
13
|
session,
|
|
@@ -4,30 +4,30 @@ import { deduplicateByDomainAndUrl } from "./search-utils";
|
|
|
4
4
|
import type { SearchProviderOptions } from "./web-search";
|
|
5
5
|
import { webSearchStep } from "./web-search";
|
|
6
6
|
|
|
7
|
-
export
|
|
8
|
-
query: string;
|
|
7
|
+
export interface SearchQuery {
|
|
9
8
|
maxResults: number;
|
|
10
|
-
|
|
9
|
+
query: string;
|
|
10
|
+
}
|
|
11
11
|
|
|
12
|
-
export
|
|
12
|
+
export interface MultiQuerySearchOptions {
|
|
13
13
|
baseProviderOptions: SearchProviderOptions;
|
|
14
|
-
topics?: string[];
|
|
15
14
|
excludeDomains?: string[];
|
|
16
|
-
|
|
15
|
+
topics?: string[];
|
|
16
|
+
}
|
|
17
17
|
|
|
18
|
-
|
|
18
|
+
interface MultiQuerySearchResult {
|
|
19
19
|
query: SearchQuery;
|
|
20
20
|
results: Array<{
|
|
21
21
|
url: string;
|
|
22
22
|
title: string;
|
|
23
23
|
content: string;
|
|
24
24
|
}>;
|
|
25
|
-
}
|
|
25
|
+
}
|
|
26
26
|
|
|
27
|
-
export
|
|
28
|
-
searches: MultiQuerySearchResult[];
|
|
27
|
+
export interface MultiQuerySearchResponse {
|
|
29
28
|
error?: string;
|
|
30
|
-
|
|
29
|
+
searches: MultiQuerySearchResult[];
|
|
30
|
+
}
|
|
31
31
|
|
|
32
32
|
export async function multiQueryWebSearchStep({
|
|
33
33
|
queries,
|
|
@@ -11,17 +11,17 @@ export type SearchProviderOptions =
|
|
|
11
11
|
provider: "firecrawl";
|
|
12
12
|
} & SearchParams);
|
|
13
13
|
|
|
14
|
-
export
|
|
14
|
+
export interface WebSearchResult {
|
|
15
|
+
content: string;
|
|
15
16
|
source: "web";
|
|
16
17
|
title: string;
|
|
17
18
|
url: string;
|
|
18
|
-
|
|
19
|
-
};
|
|
19
|
+
}
|
|
20
20
|
|
|
21
|
-
export
|
|
22
|
-
results: WebSearchResult[];
|
|
21
|
+
export interface WebSearchResponse {
|
|
23
22
|
error?: string;
|
|
24
|
-
|
|
23
|
+
results: WebSearchResult[];
|
|
24
|
+
}
|
|
25
25
|
|
|
26
26
|
// Initialize search providers lazily to avoid runtime errors when keys are missing
|
|
27
27
|
const tvly = env.TAVILY_API_KEY ? tavily({ apiKey: env.TAVILY_API_KEY }) : null;
|
|
@@ -61,7 +61,12 @@ export const toolsDefinitions: Record<ToolName, ToolDefinition> = {
|
|
|
61
61
|
generateImage: {
|
|
62
62
|
name: "generateImage",
|
|
63
63
|
description: "Generate images from text descriptions",
|
|
64
|
-
cost:
|
|
64
|
+
cost: 0, // LLM cost tracked via token usage
|
|
65
|
+
},
|
|
66
|
+
generateVideo: {
|
|
67
|
+
name: "generateVideo",
|
|
68
|
+
description: "Generate video clips from text descriptions",
|
|
69
|
+
cost: 0, // LLM cost tracked via token usage
|
|
65
70
|
},
|
|
66
71
|
deepResearch: {
|
|
67
72
|
name: "deepResearch",
|
|
@@ -71,8 +76,8 @@ export const toolsDefinitions: Record<ToolName, ToolDefinition> = {
|
|
|
71
76
|
};
|
|
72
77
|
|
|
73
78
|
export const allTools = toolNameSchema.options;
|
|
74
|
-
|
|
75
|
-
name: string;
|
|
76
|
-
description: string;
|
|
79
|
+
interface ToolDefinition {
|
|
77
80
|
cost: number;
|
|
78
|
-
|
|
81
|
+
description: string;
|
|
82
|
+
name: string;
|
|
83
|
+
}
|
|
@@ -10,6 +10,7 @@ import { editCodeDocumentTool } from "@/lib/ai/tools/documents/edit-code-documen
|
|
|
10
10
|
import { editSheetDocumentTool } from "@/lib/ai/tools/documents/edit-sheet-document";
|
|
11
11
|
import { editTextDocumentTool } from "@/lib/ai/tools/documents/edit-text-document";
|
|
12
12
|
import { generateImageTool } from "@/lib/ai/tools/generate-image";
|
|
13
|
+
import { generateVideoTool } from "@/lib/ai/tools/generate-video";
|
|
13
14
|
import { getWeather } from "@/lib/ai/tools/get-weather";
|
|
14
15
|
import { readDocument } from "@/lib/ai/tools/read-document";
|
|
15
16
|
import { retrieveUrl } from "@/lib/ai/tools/retrieve-url";
|
|
@@ -62,8 +63,8 @@ export function getTools({
|
|
|
62
63
|
session,
|
|
63
64
|
dataStream,
|
|
64
65
|
}),
|
|
65
|
-
...(config.
|
|
66
|
-
...(config.
|
|
66
|
+
...(config.ai.tools.urlRetrieval.enabled ? { retrieveUrl } : {}),
|
|
67
|
+
...(config.ai.tools.webSearch.enabled
|
|
67
68
|
? {
|
|
68
69
|
webSearch: tavilyWebSearch({
|
|
69
70
|
dataStream,
|
|
@@ -73,10 +74,10 @@ export function getTools({
|
|
|
73
74
|
}
|
|
74
75
|
: {}),
|
|
75
76
|
|
|
76
|
-
...(config.
|
|
77
|
+
...(config.ai.tools.codeExecution.enabled
|
|
77
78
|
? { codeExecution: codeExecution({ costAccumulator }) }
|
|
78
79
|
: {}),
|
|
79
|
-
...(config.
|
|
80
|
+
...(config.ai.tools.image.enabled
|
|
80
81
|
? {
|
|
81
82
|
generateImage: generateImageTool({
|
|
82
83
|
attachments,
|
|
@@ -86,7 +87,7 @@ export function getTools({
|
|
|
86
87
|
}),
|
|
87
88
|
}
|
|
88
89
|
: {}),
|
|
89
|
-
...(config.
|
|
90
|
+
...(config.ai.tools.deepResearch.enabled
|
|
90
91
|
? {
|
|
91
92
|
deepResearch: deepResearch({
|
|
92
93
|
session,
|
|
@@ -97,6 +98,14 @@ export function getTools({
|
|
|
97
98
|
}),
|
|
98
99
|
}
|
|
99
100
|
: {}),
|
|
101
|
+
...(config.ai.tools.video.enabled
|
|
102
|
+
? {
|
|
103
|
+
generateVideo: generateVideoTool({
|
|
104
|
+
selectedModel,
|
|
105
|
+
costAccumulator,
|
|
106
|
+
}),
|
|
107
|
+
}
|
|
108
|
+
: {}),
|
|
100
109
|
};
|
|
101
110
|
}
|
|
102
111
|
|
|
@@ -113,7 +122,7 @@ export async function getMcpTools({
|
|
|
113
122
|
tools: Record<string, Tool>;
|
|
114
123
|
cleanup: () => Promise<void>;
|
|
115
124
|
}> {
|
|
116
|
-
if (!config.
|
|
125
|
+
if (!config.ai.tools.mcp.enabled) {
|
|
117
126
|
return {
|
|
118
127
|
tools: {},
|
|
119
128
|
cleanup: async () => Promise.resolve(),
|
|
@@ -3,6 +3,6 @@ import type { Session } from "@/lib/auth";
|
|
|
3
3
|
type SessionUser = NonNullable<Session["user"]>;
|
|
4
4
|
|
|
5
5
|
// Minimal session slice for tools - derived from Better Auth Session to avoid drift
|
|
6
|
-
export
|
|
6
|
+
export interface ToolSession {
|
|
7
7
|
user?: Pick<SessionUser, "id">;
|
|
8
|
-
}
|
|
8
|
+
}
|
|
@@ -8,6 +8,7 @@ import { z } from "zod";
|
|
|
8
8
|
import type { codeExecution } from "@/lib/ai/tools/code-execution";
|
|
9
9
|
import type { deepResearch } from "@/lib/ai/tools/deep-research/deep-research";
|
|
10
10
|
import type { generateImageTool as generateImageToolFactory } from "@/lib/ai/tools/generate-image";
|
|
11
|
+
import type { generateVideoTool as generateVideoToolFactory } from "@/lib/ai/tools/generate-video";
|
|
11
12
|
import type { getWeather } from "@/lib/ai/tools/get-weather";
|
|
12
13
|
import type { readDocument } from "@/lib/ai/tools/read-document";
|
|
13
14
|
import type { retrieveUrl } from "@/lib/ai/tools/retrieve-url";
|
|
@@ -34,6 +35,7 @@ export const toolNameSchema = z.enum([
|
|
|
34
35
|
"webSearch",
|
|
35
36
|
"codeExecution",
|
|
36
37
|
"generateImage",
|
|
38
|
+
"generateVideo",
|
|
37
39
|
"deepResearch",
|
|
38
40
|
]);
|
|
39
41
|
|
|
@@ -45,6 +47,7 @@ const frontendToolsSchema = z.enum([
|
|
|
45
47
|
"webSearch",
|
|
46
48
|
"deepResearch",
|
|
47
49
|
"generateImage",
|
|
50
|
+
"generateVideo",
|
|
48
51
|
"createTextDocument",
|
|
49
52
|
"createCodeDocument",
|
|
50
53
|
"createSheetDocument",
|
|
@@ -91,37 +94,43 @@ type readDocumentTool = InferUITool<ReturnType<typeof readDocument>>;
|
|
|
91
94
|
type generateImageTool = InferUITool<
|
|
92
95
|
ReturnType<typeof generateImageToolFactory>
|
|
93
96
|
>;
|
|
97
|
+
type generateVideoTool = InferUITool<
|
|
98
|
+
ReturnType<typeof generateVideoToolFactory>
|
|
99
|
+
>;
|
|
94
100
|
type webSearchTool = InferUITool<ReturnType<typeof tavilyWebSearch>>;
|
|
95
101
|
type codeExecutionTool = InferUITool<ReturnType<typeof codeExecution>>;
|
|
96
102
|
type retrieveUrlTool = InferUITool<typeof retrieveUrl>;
|
|
97
103
|
|
|
104
|
+
// biome-ignore lint/style/useConsistentTypeDefinitions: <explanation>
|
|
98
105
|
export type ChatTools = {
|
|
99
|
-
|
|
100
|
-
createTextDocument: createTextDocumentToolType;
|
|
106
|
+
codeExecution: codeExecutionTool;
|
|
101
107
|
createCodeDocument: createCodeDocumentToolType;
|
|
102
108
|
createSheetDocument: createSheetDocumentToolType;
|
|
103
|
-
|
|
109
|
+
createTextDocument: createTextDocumentToolType;
|
|
110
|
+
deepResearch: deepResearchTool;
|
|
104
111
|
editCodeDocument: editCodeDocumentToolType;
|
|
105
112
|
editSheetDocument: editSheetDocumentToolType;
|
|
106
|
-
|
|
107
|
-
readDocument: readDocumentTool;
|
|
113
|
+
editTextDocument: editTextDocumentToolType;
|
|
108
114
|
generateImage: generateImageTool;
|
|
109
|
-
|
|
110
|
-
|
|
115
|
+
generateVideo: generateVideoTool;
|
|
116
|
+
getWeather: weatherTool;
|
|
117
|
+
readDocument: readDocumentTool;
|
|
111
118
|
retrieveUrl: retrieveUrlTool;
|
|
119
|
+
webSearch: webSearchTool;
|
|
112
120
|
};
|
|
113
121
|
|
|
114
|
-
|
|
122
|
+
interface FollowupSuggestions {
|
|
115
123
|
suggestions: string[];
|
|
116
|
-
}
|
|
124
|
+
}
|
|
117
125
|
|
|
126
|
+
// biome-ignore lint/style/useConsistentTypeDefinitions: <explanation>
|
|
118
127
|
export type CustomUIDataTypes = {
|
|
119
128
|
appendMessage: string;
|
|
120
129
|
chatConfirmed: {
|
|
121
130
|
chatId: string;
|
|
122
131
|
};
|
|
123
|
-
researchUpdate: ResearchUpdate;
|
|
124
132
|
followupSuggestions: FollowupSuggestions;
|
|
133
|
+
researchUpdate: ResearchUpdate;
|
|
125
134
|
};
|
|
126
135
|
|
|
127
136
|
export type ChatMessage = Omit<
|
|
@@ -137,8 +146,8 @@ export type ToolOutput<T extends ToolName> = ChatTools[T]["output"];
|
|
|
137
146
|
|
|
138
147
|
export type StreamWriter = UIMessageStreamWriter<ChatMessage>;
|
|
139
148
|
|
|
140
|
-
export
|
|
149
|
+
export interface Attachment {
|
|
150
|
+
contentType: string;
|
|
141
151
|
name: string;
|
|
142
152
|
url: string;
|
|
143
|
-
|
|
144
|
-
};
|
|
153
|
+
}
|
|
@@ -56,10 +56,10 @@ function detectRequiredHandlers(code: string): string[] {
|
|
|
56
56
|
return handlers;
|
|
57
57
|
}
|
|
58
58
|
|
|
59
|
-
|
|
60
|
-
outputs: ConsoleOutput[];
|
|
59
|
+
interface Metadata {
|
|
61
60
|
language: string;
|
|
62
|
-
|
|
61
|
+
outputs: ConsoleOutput[];
|
|
62
|
+
}
|
|
63
63
|
|
|
64
64
|
export const codeArtifact = new Artifact<"code", Metadata>({
|
|
65
65
|
kind: "code",
|
|
@@ -257,7 +257,7 @@ export const codeArtifact = new Artifact<"code", Metadata>({
|
|
|
257
257
|
},
|
|
258
258
|
],
|
|
259
259
|
metadata: {
|
|
260
|
-
selectedModel: config.
|
|
260
|
+
selectedModel: config.ai.tools.code.edits,
|
|
261
261
|
createdAt: new Date(),
|
|
262
262
|
parentMessageId: storeApi.getState().getLastMessageId(),
|
|
263
263
|
activeStreamId: null,
|
|
@@ -278,7 +278,7 @@ export const codeArtifact = new Artifact<"code", Metadata>({
|
|
|
278
278
|
},
|
|
279
279
|
],
|
|
280
280
|
metadata: {
|
|
281
|
-
selectedModel: config.
|
|
281
|
+
selectedModel: config.ai.tools.code.edits,
|
|
282
282
|
createdAt: new Date(),
|
|
283
283
|
parentMessageId: storeApi.getState().getLastMessageId(),
|
|
284
284
|
activeStreamId: null,
|
|
@@ -87,7 +87,7 @@ export const sheetArtifact = new Artifact<"sheet", Metadata>({
|
|
|
87
87
|
{ type: "text", text: "Can you please format and clean the data?" },
|
|
88
88
|
],
|
|
89
89
|
metadata: {
|
|
90
|
-
selectedModel: config.
|
|
90
|
+
selectedModel: config.ai.tools.sheet.format,
|
|
91
91
|
createdAt: new Date(),
|
|
92
92
|
parentMessageId: storeApi.getState().getLastMessageId(),
|
|
93
93
|
activeStreamId: null,
|
|
@@ -108,7 +108,7 @@ export const sheetArtifact = new Artifact<"sheet", Metadata>({
|
|
|
108
108
|
},
|
|
109
109
|
],
|
|
110
110
|
metadata: {
|
|
111
|
-
selectedModel: config.
|
|
111
|
+
selectedModel: config.ai.tools.sheet.analyze,
|
|
112
112
|
createdAt: new Date(),
|
|
113
113
|
parentMessageId: storeApi.getState().getLastMessageId(),
|
|
114
114
|
activeStreamId: null,
|
|
@@ -1,10 +1,25 @@
|
|
|
1
1
|
import { Copy, History, Pen, Redo2, Undo2 } from "lucide-react";
|
|
2
|
+
import dynamic from "next/dynamic";
|
|
2
3
|
import { toast } from "sonner";
|
|
3
4
|
import { Artifact } from "@/components/create-artifact";
|
|
4
|
-
import { DiffView } from "@/components/diffview";
|
|
5
5
|
import { DocumentSkeleton } from "@/components/document-skeleton";
|
|
6
|
-
import { Editor } from "@/components/text-editor";
|
|
7
6
|
import { config } from "@/lib/config";
|
|
7
|
+
|
|
8
|
+
const DiffView = dynamic(
|
|
9
|
+
() => import("@/components/diffview").then((m) => ({ default: m.DiffView })),
|
|
10
|
+
{
|
|
11
|
+
loading: () => <DocumentSkeleton artifactKind="text" />,
|
|
12
|
+
ssr: false,
|
|
13
|
+
}
|
|
14
|
+
);
|
|
15
|
+
|
|
16
|
+
const Editor = dynamic(
|
|
17
|
+
() => import("@/components/text-editor").then((m) => ({ default: m.Editor })),
|
|
18
|
+
{
|
|
19
|
+
loading: () => <DocumentSkeleton artifactKind="text" />,
|
|
20
|
+
ssr: false,
|
|
21
|
+
}
|
|
22
|
+
);
|
|
8
23
|
export const textArtifact = new Artifact<"text">({
|
|
9
24
|
kind: "text",
|
|
10
25
|
description: "Useful for text content, like drafting essays and emails.",
|
|
@@ -113,7 +128,7 @@ export const textArtifact = new Artifact<"text">({
|
|
|
113
128
|
},
|
|
114
129
|
],
|
|
115
130
|
metadata: {
|
|
116
|
-
selectedModel: config.
|
|
131
|
+
selectedModel: config.ai.tools.text.polish,
|
|
117
132
|
createdAt: new Date(),
|
|
118
133
|
parentMessageId: storeApi.getState().getLastMessageId(),
|
|
119
134
|
activeStreamId: null,
|
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
import assert from "node:assert/strict";
|
|
2
|
-
import { describe, it } from "vitest";
|
|
2
|
+
import { describe, it, vi } from "vitest";
|
|
3
3
|
import type { ChatMessage } from "./ai/types";
|
|
4
|
+
|
|
5
|
+
vi.mock("@/lib/config", () => ({
|
|
6
|
+
config: { appPrefix: "test" },
|
|
7
|
+
}));
|
|
8
|
+
|
|
4
9
|
import { cloneMessagesWithDocuments } from "./clone-messages";
|
|
5
10
|
|
|
6
11
|
function createMessage({
|
|
@@ -1,12 +1,16 @@
|
|
|
1
1
|
import type { GatewayType } from "./ai/gateways/registry";
|
|
2
|
-
import type {
|
|
2
|
+
import type {
|
|
3
|
+
AiConfig,
|
|
4
|
+
AuthenticationConfig,
|
|
5
|
+
FeaturesConfig,
|
|
6
|
+
} from "./config-schema";
|
|
3
7
|
|
|
4
8
|
type EnvVarName = keyof NodeJS.ProcessEnv;
|
|
5
9
|
|
|
6
|
-
export
|
|
7
|
-
options: EnvVarName[][];
|
|
10
|
+
export interface EnvRequirement {
|
|
8
11
|
description: string;
|
|
9
|
-
|
|
12
|
+
options: EnvVarName[][];
|
|
13
|
+
}
|
|
10
14
|
|
|
11
15
|
export const gatewayEnvRequirements: Record<GatewayType, EnvRequirement> = {
|
|
12
16
|
openrouter: {
|
|
@@ -29,6 +33,15 @@ export const gatewayEnvRequirements: Record<GatewayType, EnvRequirement> = {
|
|
|
29
33
|
|
|
30
34
|
export const featureEnvRequirements: Partial<
|
|
31
35
|
Record<keyof FeaturesConfig, EnvRequirement>
|
|
36
|
+
> = {
|
|
37
|
+
attachments: {
|
|
38
|
+
options: [["BLOB_READ_WRITE_TOKEN"]],
|
|
39
|
+
description: "BLOB_READ_WRITE_TOKEN",
|
|
40
|
+
},
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
export const aiToolEnvRequirements: Partial<
|
|
44
|
+
Record<keyof AiConfig["tools"], EnvRequirement>
|
|
32
45
|
> = {
|
|
33
46
|
webSearch: {
|
|
34
47
|
options: [["TAVILY_API_KEY"], ["FIRECRAWL_API_KEY"]],
|
|
@@ -46,7 +59,7 @@ export const featureEnvRequirements: Partial<
|
|
|
46
59
|
options: [["MCP_ENCRYPTION_KEY"]],
|
|
47
60
|
description: "MCP_ENCRYPTION_KEY",
|
|
48
61
|
},
|
|
49
|
-
|
|
62
|
+
codeExecution: {
|
|
50
63
|
options: [
|
|
51
64
|
["VERCEL_OIDC_TOKEN"],
|
|
52
65
|
["VERCEL_TEAM_ID", "VERCEL_PROJECT_ID", "VERCEL_TOKEN"],
|
|
@@ -54,11 +67,7 @@ export const featureEnvRequirements: Partial<
|
|
|
54
67
|
description:
|
|
55
68
|
"VERCEL_OIDC_TOKEN (auto on Vercel) or VERCEL_TEAM_ID + VERCEL_PROJECT_ID + VERCEL_TOKEN",
|
|
56
69
|
},
|
|
57
|
-
|
|
58
|
-
options: [["BLOB_READ_WRITE_TOKEN"]],
|
|
59
|
-
description: "BLOB_READ_WRITE_TOKEN",
|
|
60
|
-
},
|
|
61
|
-
attachments: {
|
|
70
|
+
image: {
|
|
62
71
|
options: [["BLOB_READ_WRITE_TOKEN"]],
|
|
63
72
|
description: "BLOB_READ_WRITE_TOKEN",
|
|
64
73
|
},
|