@chat-js/cli 0.1.4 → 0.2.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 (160) hide show
  1. package/dist/index.js +391 -244
  2. package/package.json +1 -1
  3. package/templates/chat-app/.claude/skiller.toml +18 -0
  4. package/templates/chat-app/.claude/skills/chat-context/SKILL.md +6 -0
  5. package/templates/chat-app/.claude/skills/chat-context/chat-context.mdc +36 -0
  6. package/templates/chat-app/.claude/skills/lazy-prefetch-pattern/lazy-prefetch-pattern.mdc +27 -0
  7. package/templates/chat-app/.claude/skills/react/react.mdc +29 -0
  8. package/templates/chat-app/.claude/skills/trpc-patterns/trpc-patterns.mdc +77 -0
  9. package/templates/chat-app/.claude/skills/typescript/typescript.mdc +53 -0
  10. package/templates/chat-app/.claude/skills/ultracite/ultracite.mdc +129 -0
  11. package/templates/chat-app/.cursor/skills/chat-context/SKILL.md +37 -0
  12. package/templates/chat-app/.cursor/skills/lazy-prefetch-pattern/SKILL.md +26 -0
  13. package/templates/chat-app/.cursor/skills/react/SKILL.md +28 -0
  14. package/templates/chat-app/.cursor/skills/trpc-patterns/SKILL.md +76 -0
  15. package/templates/chat-app/.cursor/skills/typescript/SKILL.md +52 -0
  16. package/templates/chat-app/.cursor/skills/ultracite/SKILL.md +128 -0
  17. package/templates/chat-app/app/(chat)/actions.ts +1 -1
  18. package/templates/chat-app/app/(chat)/api/chat/[id]/stream/route.ts +6 -5
  19. package/templates/chat-app/app/(chat)/api/chat/route.ts +14 -15
  20. package/templates/chat-app/app/(chat)/chat-providers.tsx +2 -2
  21. package/templates/chat-app/app/(chat)/layout.tsx +7 -6
  22. package/templates/chat-app/app/api/cron/cleanup/route.ts +4 -3
  23. package/templates/chat-app/app/globals.css +22 -22
  24. package/templates/chat-app/app/layout.tsx +1 -1
  25. package/templates/chat-app/biome.jsonc +3 -3
  26. package/templates/chat-app/chat.config.ts +47 -20
  27. package/templates/chat-app/components/anonymous-session-init.tsx +4 -12
  28. package/templates/chat-app/components/artifact-actions.tsx +5 -5
  29. package/templates/chat-app/components/artifact-panel.tsx +6 -6
  30. package/templates/chat-app/components/assistant-message.tsx +1 -1
  31. package/templates/chat-app/components/chat/chat-layout.tsx +2 -2
  32. package/templates/chat-app/components/chat/chat-welcome.tsx +1 -0
  33. package/templates/chat-app/components/chat-features-definitions.ts +11 -8
  34. package/templates/chat-app/components/chat-menu-items.tsx +4 -4
  35. package/templates/chat-app/components/chat-sync.tsx +1 -1
  36. package/templates/chat-app/components/clone-chat-button.tsx +2 -2
  37. package/templates/chat-app/components/code-editor.tsx +5 -5
  38. package/templates/chat-app/components/connectors-dropdown.tsx +2 -2
  39. package/templates/chat-app/components/console.tsx +5 -5
  40. package/templates/chat-app/components/create-artifact.tsx +28 -28
  41. package/templates/chat-app/components/data-stream-provider.tsx +2 -2
  42. package/templates/chat-app/components/deep-research-progress.tsx +2 -2
  43. package/templates/chat-app/components/delete-chat-dialog.tsx +3 -3
  44. package/templates/chat-app/components/delete-project-dialog.tsx +3 -3
  45. package/templates/chat-app/components/diffview.tsx +3 -3
  46. package/templates/chat-app/components/favicon-group.tsx +7 -7
  47. package/templates/chat-app/components/header-breadcrumb.tsx +11 -11
  48. package/templates/chat-app/components/image-editor.tsx +5 -5
  49. package/templates/chat-app/components/image-modal.tsx +4 -4
  50. package/templates/chat-app/components/interactive-chart-impl.tsx +269 -0
  51. package/templates/chat-app/components/interactive-charts.tsx +18 -246
  52. package/templates/chat-app/components/lexical-chat-input.tsx +10 -10
  53. package/templates/chat-app/components/message-editor.tsx +3 -3
  54. package/templates/chat-app/components/message-parts.tsx +8 -3
  55. package/templates/chat-app/components/messages-pane.tsx +4 -4
  56. package/templates/chat-app/components/messages.tsx +5 -5
  57. package/templates/chat-app/components/model-selector.tsx +4 -1
  58. package/templates/chat-app/components/multimodal-input.tsx +14 -5
  59. package/templates/chat-app/components/part/code-execution.tsx +4 -1
  60. package/templates/chat-app/components/part/document-common.tsx +8 -8
  61. package/templates/chat-app/components/part/document-preview.tsx +34 -16
  62. package/templates/chat-app/components/part/document-tool.tsx +3 -3
  63. package/templates/chat-app/components/part/dynamic-tool.tsx +3 -3
  64. package/templates/chat-app/components/part/generate-video.tsx +54 -0
  65. package/templates/chat-app/components/part/message-reasoning.tsx +3 -3
  66. package/templates/chat-app/components/project-details-dialog.tsx +4 -4
  67. package/templates/chat-app/components/project-home.tsx +1 -0
  68. package/templates/chat-app/components/project-icon-picker.tsx +5 -5
  69. package/templates/chat-app/components/project-icon.tsx +4 -4
  70. package/templates/chat-app/components/project-menu-items.tsx +3 -3
  71. package/templates/chat-app/components/research-tasks.tsx +3 -3
  72. package/templates/chat-app/components/sandbox.tsx +4 -4
  73. package/templates/chat-app/components/search-chats-dialog.tsx +11 -11
  74. package/templates/chat-app/components/settings/connectors-settings.tsx +1 -1
  75. package/templates/chat-app/components/settings/settings-nav.tsx +1 -1
  76. package/templates/chat-app/components/sheet-editor.tsx +5 -5
  77. package/templates/chat-app/components/sidebar-chats-list.tsx +5 -5
  78. package/templates/chat-app/components/suggested-actions.tsx +3 -3
  79. package/templates/chat-app/components/text-editor.tsx +5 -5
  80. package/templates/chat-app/components/toolbar.tsx +6 -6
  81. package/templates/chat-app/components/upgrade-cta/login-cta-banner.tsx +5 -5
  82. package/templates/chat-app/components/upgrade-cta/login-prompt.tsx +4 -4
  83. package/templates/chat-app/components/upgrade-cta/share-menu-item.tsx +3 -3
  84. package/templates/chat-app/components/user-message.tsx +3 -3
  85. package/templates/chat-app/components/version-footer.tsx +4 -4
  86. package/templates/chat-app/hooks/chat-sync-hooks.ts +0 -55
  87. package/templates/chat-app/hooks/use-artifact.tsx +3 -3
  88. package/templates/chat-app/hooks/use-auto-focus.ts +37 -7
  89. package/templates/chat-app/hooks/use-media-query.tsx +2 -4
  90. package/templates/chat-app/lib/ai/active-gateway.ts +1 -1
  91. package/templates/chat-app/lib/ai/ai-gateway-models-schemas.ts +30 -6
  92. package/templates/chat-app/lib/ai/app-model-id.ts +1 -1
  93. package/templates/chat-app/lib/ai/app-models.ts +4 -4
  94. package/templates/chat-app/lib/ai/eval-agent.ts +5 -5
  95. package/templates/chat-app/lib/ai/followup-suggestions.ts +1 -1
  96. package/templates/chat-app/lib/ai/gateway-model-defaults.ts +131 -41
  97. package/templates/chat-app/lib/ai/gateways/gateway-provider.ts +10 -6
  98. package/templates/chat-app/lib/ai/gateways/openai-compatible-gateway.ts +9 -4
  99. package/templates/chat-app/lib/ai/gateways/openai-gateway.ts +9 -4
  100. package/templates/chat-app/lib/ai/gateways/openrouter-gateway.ts +17 -12
  101. package/templates/chat-app/lib/ai/gateways/registry.ts +9 -0
  102. package/templates/chat-app/lib/ai/gateways/vercel-gateway.ts +36 -4
  103. package/templates/chat-app/lib/ai/mcp/cache.ts +13 -13
  104. package/templates/chat-app/lib/ai/model-data.ts +21 -20
  105. package/templates/chat-app/lib/ai/models.generated.ts +4397 -3592
  106. package/templates/chat-app/lib/ai/models.ts +1 -1
  107. package/templates/chat-app/lib/ai/providers.ts +10 -0
  108. package/templates/chat-app/lib/ai/text-splitter.ts +3 -4
  109. package/templates/chat-app/lib/ai/to-model-data.ts +1 -0
  110. package/templates/chat-app/lib/ai/tools/code-execution.ts +122 -53
  111. package/templates/chat-app/lib/ai/tools/deep-research/configuration.ts +35 -32
  112. package/templates/chat-app/lib/ai/tools/deep-research/pipeline.ts +2 -2
  113. package/templates/chat-app/lib/ai/tools/deep-research/types.ts +9 -9
  114. package/templates/chat-app/lib/ai/tools/documents/types.ts +4 -4
  115. package/templates/chat-app/lib/ai/tools/generate-image.ts +42 -20
  116. package/templates/chat-app/lib/ai/tools/generate-video.ts +166 -0
  117. package/templates/chat-app/lib/ai/tools/get-weather.ts +20 -20
  118. package/templates/chat-app/lib/ai/tools/read-document.ts +3 -3
  119. package/templates/chat-app/lib/ai/tools/steps/multi-query-web-search.ts +11 -11
  120. package/templates/chat-app/lib/ai/tools/steps/web-search.ts +6 -6
  121. package/templates/chat-app/lib/ai/tools/tools-definitions.ts +10 -5
  122. package/templates/chat-app/lib/ai/tools/tools.ts +15 -6
  123. package/templates/chat-app/lib/ai/tools/types.ts +2 -2
  124. package/templates/chat-app/lib/ai/types.ts +22 -13
  125. package/templates/chat-app/lib/artifacts/code/client.tsx +5 -5
  126. package/templates/chat-app/lib/artifacts/sheet/client.tsx +2 -2
  127. package/templates/chat-app/lib/artifacts/text/client.tsx +18 -3
  128. package/templates/chat-app/lib/clone-messages.test.ts +6 -1
  129. package/templates/chat-app/lib/config-requirements.ts +19 -10
  130. package/templates/chat-app/lib/config-schema.ts +189 -103
  131. package/templates/chat-app/lib/config.ts +4 -4
  132. package/templates/chat-app/lib/credits/cost-accumulator.ts +11 -8
  133. package/templates/chat-app/lib/env-schema.ts +1 -1
  134. package/templates/chat-app/lib/features-config.ts +6 -6
  135. package/templates/chat-app/lib/stores/with-threads.ts +3 -3
  136. package/templates/chat-app/lib/thread-utils.ts +2 -2
  137. package/templates/chat-app/lib/types/anonymous.ts +4 -4
  138. package/templates/chat-app/lib/types/ui-chat.ts +7 -7
  139. package/templates/chat-app/lib/utils/download-assets.ts +3 -3
  140. package/templates/chat-app/lib/utils/rate-limit.ts +8 -8
  141. package/templates/chat-app/next.config.ts +0 -25
  142. package/templates/chat-app/package.json +15 -15
  143. package/templates/chat-app/playwright.config.ts +5 -5
  144. package/templates/chat-app/providers/chat-id-provider.tsx +5 -5
  145. package/templates/chat-app/providers/chat-input-provider.tsx +15 -15
  146. package/templates/chat-app/providers/chat-models-provider.tsx +3 -3
  147. package/templates/chat-app/providers/default-model-provider.tsx +5 -5
  148. package/templates/chat-app/providers/parse-chat-id-from-pathname.test.ts +16 -0
  149. package/templates/chat-app/providers/session-provider.tsx +2 -2
  150. package/templates/chat-app/scripts/check-env.ts +36 -4
  151. package/templates/chat-app/tests/artifacts.e2e.ts +7 -0
  152. package/templates/chat-app/tests/auth.setup.e2e.ts +10 -0
  153. package/templates/chat-app/tests/chat.e2e.ts +7 -0
  154. package/templates/chat-app/tests/reasoning.e2e.ts +7 -0
  155. package/templates/chat-app/tests/reasoning.setup.e2e.ts +10 -0
  156. package/templates/chat-app/trpc/routers/chat.router.ts +1 -1
  157. package/templates/chat-app/trpc/routers/mcp.router.ts +3 -3
  158. package/templates/chat-app/vitest.config.ts +7 -0
  159. package/templates/chat-app/next-env.d.ts +0 -6
  160. package/templates/chat-app/tsconfig.tsbuildinfo +0 -1
@@ -8,7 +8,7 @@ import type {
8
8
 
9
9
  /** The gateway type actively selected in chat.config.ts */
10
10
  export type ActiveGatewayType = typeof chatConfig extends {
11
- models: { gateway: infer G extends GatewayType };
11
+ ai: { gateway: infer G extends GatewayType };
12
12
  }
13
13
  ? G
14
14
  : DefaultGateway;
@@ -15,8 +15,8 @@ export type AppModelDefinition = Omit<ModelData, "id"> & {
15
15
  apiModelId: ModelId;
16
16
  };
17
17
 
18
- const DISABLED_MODELS = new Set(config.models.disabledModels);
19
- const PROVIDER_ORDER = config.models.providerOrder;
18
+ const DISABLED_MODELS = new Set(config.ai.disabledModels);
19
+ const PROVIDER_ORDER = config.ai.providerOrder;
20
20
 
21
21
  function buildAppModels(models: ModelData[]): AppModelDefinition[] {
22
22
  return models
@@ -116,7 +116,7 @@ export async function getAppModelDefinition(
116
116
  * so we fall back to an empty set (which auto-enables all models).
117
117
  */
118
118
  const KNOWN_MODEL_IDS = new Set<string>(
119
- generatedForGateway === config.models.gateway
119
+ generatedForGateway === config.ai.gateway
120
120
  ? generatedModels.map((m) => m.id)
121
121
  : []
122
122
  );
@@ -128,7 +128,7 @@ const KNOWN_MODEL_IDS = new Set<string>(
128
128
  export function getDefaultEnabledModels(
129
129
  appModels: AppModelDefinition[]
130
130
  ): Set<AppModelId> {
131
- const enabled = new Set<AppModelId>(config.models.curatedDefaults);
131
+ const enabled = new Set<AppModelId>(config.ai.curatedDefaults);
132
132
 
133
133
  // If a curated default has a -reasoning variant, enable it too
134
134
  for (const model of appModels) {
@@ -20,17 +20,17 @@ function createNoOpStreamWriter(): StreamWriter {
20
20
  } as unknown as StreamWriter;
21
21
  }
22
22
 
23
- export type EvalAgentResult = {
24
- finalText: string;
23
+ export interface EvalAgentResult {
25
24
  assistantMessage: ChatMessage;
26
- usage: LanguageModelUsage | undefined;
25
+ finalText: string;
26
+ followupSuggestions: string[];
27
27
  toolResults: Array<{
28
28
  toolName: string;
29
29
  type: string;
30
30
  state?: string;
31
31
  }>;
32
- followupSuggestions: string[];
33
- };
32
+ usage: LanguageModelUsage | undefined;
33
+ }
34
34
 
35
35
  async function executeAgentAndGetOutput({
36
36
  userMessage,
@@ -12,7 +12,7 @@ export async function generateFollowupSuggestions(
12
12
  const minQuestionCount = 3;
13
13
  const maxCharactersPerQuestion = 80;
14
14
  return streamText({
15
- model: await getLanguageModel(config.models.defaults.followupSuggestions),
15
+ model: await getLanguageModel(config.ai.tools.followupSuggestions.default),
16
16
  messages: [
17
17
  ...modelMessages,
18
18
  {
@@ -1,28 +1,58 @@
1
1
  import type { GatewayType } from "./gateways/registry";
2
2
 
3
- type ModelDefaults = {
4
- providerOrder: string[];
5
- disabledModels: string[];
6
- curatedDefaults: string[];
3
+ interface ModelDefaults {
7
4
  anonymousModels: string[];
8
- defaults: {
5
+ curatedDefaults: string[];
6
+ disabledModels: string[];
7
+ providerOrder: string[];
8
+ tools: {
9
+ webSearch: {
10
+ enabled: boolean;
11
+ };
12
+ urlRetrieval: {
13
+ enabled: boolean;
14
+ };
15
+ codeExecution: {
16
+ enabled: boolean;
17
+ };
18
+ mcp: {
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
+ };
39
+ deepResearch: {
40
+ enabled: boolean;
41
+ defaultModel: string;
42
+ finalReportModel: string;
43
+ allowClarification: boolean;
44
+ maxResearcherIterations: number;
45
+ maxConcurrentResearchUnits: number;
46
+ maxSearchQueries: number;
47
+ };
48
+ };
49
+ workflows: {
9
50
  chat: string;
10
51
  title: string;
11
52
  pdf: string;
12
- artifact: string;
13
- artifactSuggestion: string;
14
- followupSuggestions: string;
15
- suggestions: string;
16
- polishText: string;
17
- formatSheet: string;
18
- analyzeSheet: string;
19
- codeEdits: string;
20
53
  chatImageCompatible: string;
21
- image: string;
22
- deepResearch: string;
23
- deepResearchFinalReport: string;
24
54
  };
25
- };
55
+ }
26
56
 
27
57
  const multiProviderDefaults = {
28
58
  providerOrder: ["openai", "google", "anthropic"],
@@ -40,22 +70,52 @@ const multiProviderDefaults = {
40
70
  "xai/grok-4",
41
71
  ],
42
72
  anonymousModels: ["google/gemini-2.5-flash-lite", "openai/gpt-5-nano"],
43
- defaults: {
73
+ workflows: {
44
74
  chat: "openai/gpt-5-mini",
45
75
  title: "openai/gpt-5-nano",
46
76
  pdf: "openai/gpt-5-mini",
47
- artifact: "openai/gpt-5-nano",
48
- artifactSuggestion: "openai/gpt-5-mini",
49
- followupSuggestions: "google/gemini-2.5-flash-lite",
50
- suggestions: "openai/gpt-5-mini",
51
- polishText: "openai/gpt-5-mini",
52
- formatSheet: "openai/gpt-5-mini",
53
- analyzeSheet: "openai/gpt-5-mini",
54
- codeEdits: "openai/gpt-5-mini",
55
77
  chatImageCompatible: "openai/gpt-4o-mini",
56
- image: "google/gemini-3-pro-image",
57
- deepResearch: "google/gemini-2.5-flash-lite",
58
- deepResearchFinalReport: "google/gemini-3-flash",
78
+ },
79
+ tools: {
80
+ webSearch: {
81
+ enabled: false,
82
+ },
83
+ urlRetrieval: {
84
+ enabled: false,
85
+ },
86
+ codeExecution: {
87
+ enabled: false,
88
+ },
89
+ mcp: {
90
+ enabled: false,
91
+ },
92
+ followupSuggestions: {
93
+ enabled: false,
94
+ default: "google/gemini-2.5-flash-lite",
95
+ },
96
+ text: {
97
+ polish: "openai/gpt-5-mini",
98
+ },
99
+ sheet: {
100
+ format: "openai/gpt-5-mini",
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
+ },
110
+ deepResearch: {
111
+ enabled: false,
112
+ defaultModel: "google/gemini-2.5-flash-lite",
113
+ finalReportModel: "google/gemini-3-flash",
114
+ allowClarification: true,
115
+ maxResearcherIterations: 1,
116
+ maxConcurrentResearchUnits: 2,
117
+ maxSearchQueries: 2,
118
+ },
59
119
  },
60
120
  } satisfies ModelDefaults;
61
121
 
@@ -69,22 +129,52 @@ const openaiOnlyDefaults = {
69
129
  "gpt-5.2-chat-latest",
70
130
  ],
71
131
  anonymousModels: ["gpt-5-nano"],
72
- defaults: {
132
+ workflows: {
73
133
  chat: "gpt-5-mini",
74
134
  title: "gpt-5-nano",
75
135
  pdf: "gpt-5-mini",
76
- artifact: "gpt-5-nano",
77
- artifactSuggestion: "gpt-5-mini",
78
- followupSuggestions: "gpt-5-nano",
79
- suggestions: "gpt-5-mini",
80
- polishText: "gpt-5-mini",
81
- formatSheet: "gpt-5-mini",
82
- analyzeSheet: "gpt-5-mini",
83
- codeEdits: "gpt-5-mini",
84
136
  chatImageCompatible: "gpt-4o-mini",
85
- image: "gpt-image-1",
86
- deepResearch: "gpt-5-nano",
87
- deepResearchFinalReport: "gpt-5-mini",
137
+ },
138
+ tools: {
139
+ webSearch: {
140
+ enabled: false,
141
+ },
142
+ urlRetrieval: {
143
+ enabled: false,
144
+ },
145
+ codeExecution: {
146
+ enabled: false,
147
+ },
148
+ mcp: {
149
+ enabled: false,
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: {
166
+ enabled: false,
167
+ default: "gpt-image-1",
168
+ },
169
+ deepResearch: {
170
+ enabled: false,
171
+ defaultModel: "gpt-5-nano",
172
+ finalReportModel: "gpt-5-mini",
173
+ allowClarification: true,
174
+ maxResearcherIterations: 1,
175
+ maxConcurrentResearchUnits: 2,
176
+ maxSearchQueries: 2,
177
+ },
88
178
  },
89
179
  } satisfies ModelDefaults;
90
180
 
@@ -1,19 +1,23 @@
1
+ import type { Experimental_VideoModelV3 } from "@ai-sdk/provider";
1
2
  import type { ImageModel, LanguageModel } from "ai";
2
3
  import type { AiGatewayModel } from "../ai-gateway-models-schemas";
3
4
 
4
- export type GatewayProvider<
5
+ export interface GatewayProvider<
5
6
  TGateway extends string = string,
6
7
  TModelId extends string = string,
7
8
  TImageModelId extends string = string,
8
- > = {
9
- readonly type: TGateway;
9
+ TVideoModelId extends string = string,
10
+ > {
11
+ /** Create a dedicated image model instance, or null if unsupported */
12
+ createImageModel(modelId: TImageModelId): ImageModel | null;
10
13
 
11
14
  /** Create a language model instance from a model ID like "openai/gpt-5-nano" */
12
15
  createLanguageModel(modelId: TModelId): LanguageModel;
13
16
 
14
- /** Create a dedicated image model instance, or null if unsupported */
15
- createImageModel(modelId: TImageModelId): ImageModel | null;
17
+ /** Create a video model instance, or null if unsupported */
18
+ createVideoModel(modelId: TVideoModelId): Experimental_VideoModelV3 | null;
16
19
 
17
20
  /** Fetch the list of available models from the gateway's API */
18
21
  fetchModels(): Promise<AiGatewayModel[]>;
19
- };
22
+ readonly type: TGateway;
23
+ }
@@ -1,4 +1,5 @@
1
1
  import { createOpenAICompatible } from "@ai-sdk/openai-compatible";
2
+ import type { Experimental_VideoModelV3 } from "@ai-sdk/provider";
2
3
  import type { ImageModel, LanguageModel } from "ai";
3
4
  import { createModuleLogger } from "@/lib/logger";
4
5
  import type { AiGatewayModel } from "../ai-gateway-models-schemas";
@@ -7,12 +8,12 @@ import type { GatewayProvider } from "./gateway-provider";
7
8
 
8
9
  const log = createModuleLogger("ai/gateways/openai-compatible");
9
10
 
10
- type OpenAICompatibleModelResponse = {
11
+ interface OpenAICompatibleModelResponse {
12
+ created: number;
11
13
  id: string;
12
14
  object: string;
13
- created: number;
14
15
  owned_by: string;
15
- };
16
+ }
16
17
 
17
18
  function toAiGatewayModel(
18
19
  model: OpenAICompatibleModelResponse
@@ -32,7 +33,7 @@ function toAiGatewayModel(
32
33
  }
33
34
 
34
35
  export class OpenAICompatibleGateway
35
- implements GatewayProvider<"openai-compatible", string, string>
36
+ implements GatewayProvider<"openai-compatible", string, string, never>
36
37
  {
37
38
  readonly type = "openai-compatible" as const;
38
39
 
@@ -59,6 +60,10 @@ export class OpenAICompatibleGateway
59
60
  return provider.imageModel(modelId);
60
61
  }
61
62
 
63
+ createVideoModel(_modelId: never): Experimental_VideoModelV3 | null {
64
+ return null;
65
+ }
66
+
62
67
  private getApiKey(): string | undefined {
63
68
  return process.env.OPENAI_COMPATIBLE_API_KEY;
64
69
  }
@@ -1,4 +1,5 @@
1
1
  import { createOpenAI } from "@ai-sdk/openai";
2
+ import type { Experimental_VideoModelV3 } from "@ai-sdk/provider";
2
3
  import type { ImageModel, LanguageModel } from "ai";
3
4
  import { env } from "@/lib/env";
4
5
  import { createModuleLogger } from "@/lib/logger";
@@ -20,12 +21,12 @@ type OpenaiImageModelId = StrictLiterals<
20
21
  ExtractImageModelIdFromProvider<typeof createOpenAI>
21
22
  >;
22
23
 
23
- type OpenAIModelResponse = {
24
+ interface OpenAIModelResponse {
25
+ created: number;
24
26
  id: string;
25
27
  object: string;
26
- created: number;
27
28
  owned_by: string;
28
- };
29
+ }
29
30
 
30
31
  function toAiGatewayModel(model: OpenAIModelResponse): AiGatewayModel {
31
32
  return {
@@ -45,7 +46,7 @@ function toAiGatewayModel(model: OpenAIModelResponse): AiGatewayModel {
45
46
 
46
47
  export class OpenAIGateway
47
48
  implements
48
- GatewayProvider<"openai", OpenaiLanguageModelId, OpenaiImageModelId>
49
+ GatewayProvider<"openai", OpenaiLanguageModelId, OpenaiImageModelId, never>
49
50
  {
50
51
  readonly type = "openai" as const;
51
52
 
@@ -67,6 +68,10 @@ export class OpenAIGateway
67
68
  return provider.image(modelId);
68
69
  }
69
70
 
71
+ createVideoModel(_modelId: never): Experimental_VideoModelV3 | null {
72
+ return null;
73
+ }
74
+
70
75
  private getApiKey(): string | undefined {
71
76
  return env.OPENAI_API_KEY;
72
77
  }
@@ -1,3 +1,4 @@
1
+ import type { Experimental_VideoModelV3 } from "@ai-sdk/provider";
1
2
  import { createOpenRouter } from "@openrouter/ai-sdk-provider";
2
3
  import type { ImageModel, LanguageModel } from "ai";
3
4
  import { createModuleLogger } from "@/lib/logger";
@@ -7,21 +8,17 @@ import type { GatewayProvider } from "./gateway-provider";
7
8
 
8
9
  const log = createModuleLogger("ai/gateways/openrouter");
9
10
 
10
- type OpenRouterModelResponse = {
11
- id: string;
12
- name: string;
13
- created: number;
14
- description: string;
15
- context_length: number | null;
11
+ interface OpenRouterModelResponse {
16
12
  architecture: {
17
13
  modality?: string;
18
14
  input_modalities?: string[];
19
15
  output_modalities?: string[];
20
16
  } | null;
21
- top_provider: {
22
- context_length?: number | null;
23
- max_completion_tokens: number | null;
24
- } | null;
17
+ context_length: number | null;
18
+ created: number;
19
+ description: string;
20
+ id: string;
21
+ name: string;
25
22
  pricing: {
26
23
  prompt?: string;
27
24
  completion?: string;
@@ -32,7 +29,11 @@ type OpenRouterModelResponse = {
32
29
  input_cache_write?: string;
33
30
  } | null;
34
31
  supported_parameters?: string[] | null;
35
- };
32
+ top_provider: {
33
+ context_length?: number | null;
34
+ max_completion_tokens: number | null;
35
+ } | null;
36
+ }
36
37
 
37
38
  function deriveTags(model: OpenRouterModelResponse): string[] {
38
39
  const inputMods = model.architecture?.input_modalities ?? ["text"];
@@ -95,7 +96,7 @@ function toAiGatewayModel(model: OpenRouterModelResponse): AiGatewayModel {
95
96
  }
96
97
 
97
98
  export class OpenRouterGateway
98
- implements GatewayProvider<"openrouter", string, string>
99
+ implements GatewayProvider<"openrouter", string, string, never>
99
100
  {
100
101
  readonly type = "openrouter" as const;
101
102
 
@@ -118,6 +119,10 @@ export class OpenRouterGateway
118
119
  return null;
119
120
  }
120
121
 
122
+ createVideoModel(_modelId: never): Experimental_VideoModelV3 | null {
123
+ return null;
124
+ }
125
+
121
126
  private getApiKey(): string | undefined {
122
127
  return process.env.OPENROUTER_API_KEY;
123
128
  }
@@ -60,3 +60,12 @@ export type GatewayImageModelIdMap = {
60
60
  | InferImageModelId<K>
61
61
  | (K extends typeof generatedForGateway ? MultimodalImageModel : never);
62
62
  };
63
+
64
+ /** Infer video model ID type from a gateway's `createVideoModel` parameter */
65
+ type InferVideoModelId<T extends GatewayType> = Parameters<
66
+ ReturnType<(typeof gatewayRegistry)[T]>["createVideoModel"]
67
+ >[0];
68
+
69
+ export type GatewayVideoModelIdMap = {
70
+ [K in GatewayType]: InferVideoModelId<K>;
71
+ };
@@ -1,9 +1,11 @@
1
1
  import { gateway } from "@ai-sdk/gateway";
2
+ import type { Experimental_VideoModelV3 } from "@ai-sdk/provider";
2
3
  import type { ImageModel, LanguageModel } from "ai";
3
4
  import { createModuleLogger } from "@/lib/logger";
4
5
  import {
5
6
  type AiGatewayModel,
6
7
  aiGatewayModelsResponseSchema,
8
+ isAiGatewayModelType,
7
9
  } from "../ai-gateway-models-schemas";
8
10
  import { getFallbackModels } from "./fallback-models";
9
11
  import type { GatewayProvider } from "./gateway-provider";
@@ -12,13 +14,19 @@ import type { StrictLiterals } from "./provider-types";
12
14
  const log = createModuleLogger("ai/gateways/vercel");
13
15
 
14
16
  type VercelImageModelId = Parameters<(typeof gateway)["imageModel"]>[0];
17
+ type VercelVideoModelId = Parameters<(typeof gateway)["videoModel"]>[0];
15
18
  type VercelLanguageModelId = StrictLiterals<
16
19
  Parameters<(typeof gateway)["languageModel"]>[0]
17
20
  >;
18
21
 
19
22
  export class VercelGateway
20
23
  implements
21
- GatewayProvider<"vercel", VercelLanguageModelId, VercelImageModelId>
24
+ GatewayProvider<
25
+ "vercel",
26
+ VercelLanguageModelId,
27
+ VercelImageModelId,
28
+ VercelVideoModelId
29
+ >
22
30
  {
23
31
  readonly type = "vercel" as const;
24
32
 
@@ -30,6 +38,10 @@ export class VercelGateway
30
38
  return gateway.imageModel(modelId);
31
39
  }
32
40
 
41
+ createVideoModel(modelId: VercelVideoModelId): Experimental_VideoModelV3 {
42
+ return gateway.videoModel(modelId);
43
+ }
44
+
33
45
  private getApiKey(): string | undefined {
34
46
  return process.env.AI_GATEWAY_API_KEY || process.env.VERCEL_OIDC_TOKEN;
35
47
  }
@@ -68,13 +80,33 @@ export class VercelGateway
68
80
 
69
81
  const bodyRaw = await response.json();
70
82
  const body = aiGatewayModelsResponseSchema.parse(bodyRaw);
71
- const modelCount = body.data?.length ?? 0;
83
+ const unsupportedTypes = new Set<string>();
84
+ const models: AiGatewayModel[] = [];
85
+
86
+ for (const model of body.data) {
87
+ if (!isAiGatewayModelType(model.type)) {
88
+ unsupportedTypes.add(model.type);
89
+ continue;
90
+ }
91
+ models.push({ ...model, type: model.type });
92
+ }
93
+
94
+ if (unsupportedTypes.size > 0) {
95
+ log.warn(
96
+ {
97
+ unsupportedTypes: [...unsupportedTypes],
98
+ skippedModelCount: body.data.length - models.length,
99
+ modelCount: body.data.length,
100
+ },
101
+ "Skipping models with unsupported types from Vercel AI Gateway"
102
+ );
103
+ }
72
104
 
73
105
  log.info(
74
- { modelCount },
106
+ { modelCount: models.length },
75
107
  "Successfully fetched models from Vercel AI Gateway"
76
108
  );
77
- return body.data || [];
109
+ return models;
78
110
  } catch (error) {
79
111
  log.error(
80
112
  { err: error, url },
@@ -11,25 +11,18 @@ const mcpCacheTags = {
11
11
  } as const;
12
12
 
13
13
  // Types for cached results
14
- export type ConnectionStatusResult = {
14
+ export interface ConnectionStatusResult {
15
+ error?: string;
16
+ needsAuth: boolean;
15
17
  status:
16
18
  | "disconnected"
17
19
  | "connecting"
18
20
  | "connected"
19
21
  | "authorizing"
20
22
  | "incompatible";
21
- needsAuth: boolean;
22
- error?: string;
23
- };
23
+ }
24
24
 
25
- export type DiscoveryResult = {
26
- tools: Array<{ name: string; description: string | null }>;
27
- resources: Array<{
28
- name: string;
29
- uri: string;
30
- description: string | null;
31
- mimeType: string | null;
32
- }>;
25
+ export interface DiscoveryResult {
33
26
  prompts: Array<{
34
27
  name: string;
35
28
  description: string | null;
@@ -39,7 +32,14 @@ export type DiscoveryResult = {
39
32
  required: boolean;
40
33
  }>;
41
34
  }>;
42
- };
35
+ resources: Array<{
36
+ name: string;
37
+ uri: string;
38
+ description: string | null;
39
+ mimeType: string | null;
40
+ }>;
41
+ tools: Array<{ name: string; description: string | null }>;
42
+ }
43
43
 
44
44
  /**
45
45
  * Create a cached connection status fetcher for a specific connector.
@@ -1,23 +1,7 @@
1
- export type ModelData = {
2
- id: string;
3
- object: string;
4
- owned_by: string;
5
- name: string;
6
- description: string;
7
- type: "language" | "embedding" | "image";
8
- tags?: string[];
1
+ export interface ModelData {
9
2
  context_window: number;
10
- max_tokens: number;
11
- pricing: {
12
- input?: string;
13
- output?: string;
14
- input_cache_read?: string;
15
- input_cache_write?: string;
16
- web_search?: string;
17
- image?: string;
18
- };
19
- reasoning: boolean;
20
- toolCall: boolean;
3
+ description: string;
4
+ id: string;
21
5
  input: {
22
6
  image: boolean;
23
7
  text: boolean;
@@ -25,9 +9,26 @@ export type ModelData = {
25
9
  video: boolean;
26
10
  audio: boolean;
27
11
  };
12
+ max_tokens: number;
13
+ name: string;
14
+ object: string;
28
15
  output: {
29
16
  image: boolean;
30
17
  text: boolean;
31
18
  audio: boolean;
19
+ video: boolean;
32
20
  };
33
- };
21
+ owned_by: string;
22
+ pricing: {
23
+ input?: string;
24
+ output?: string;
25
+ input_cache_read?: string;
26
+ input_cache_write?: string;
27
+ web_search?: string;
28
+ image?: string;
29
+ };
30
+ reasoning: boolean;
31
+ tags?: string[];
32
+ toolCall: boolean;
33
+ type: "language" | "embedding" | "image" | "video";
34
+ }