@chat-js/cli 0.2.1 → 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 CHANGED
@@ -3914,7 +3914,7 @@ var {
3914
3914
  // package.json
3915
3915
  var package_default = {
3916
3916
  name: "@chat-js/cli",
3917
- version: "0.2.1",
3917
+ version: "0.3.0",
3918
3918
  description: "CLI for creating and extending ChatJS apps",
3919
3919
  license: "Apache-2.0",
3920
3920
  repository: {
@@ -17611,6 +17611,71 @@ var add = new Command().name("add").description("add a component or feature to a
17611
17611
  }
17612
17612
  });
17613
17613
 
17614
+ // src/commands/config.ts
17615
+ import { spawn } from "node:child_process";
17616
+ import path from "node:path";
17617
+
17618
+ // src/utils/get-package-manager.ts
17619
+ function inferPackageManager() {
17620
+ const ua = process.env.npm_config_user_agent ?? "";
17621
+ if (ua.startsWith("pnpm/"))
17622
+ return "pnpm";
17623
+ if (ua.startsWith("yarn/"))
17624
+ return "yarn";
17625
+ if (ua.startsWith("npm/"))
17626
+ return "npm";
17627
+ if (ua.startsWith("bun/"))
17628
+ return "bun";
17629
+ return "bun";
17630
+ }
17631
+
17632
+ // src/commands/config.ts
17633
+ var EVAL_SCRIPT = `
17634
+ import userConfig from "./chat.config.ts";
17635
+ import { applyDefaults } from "./lib/config-schema";
17636
+ console.log(JSON.stringify(applyDefaults(userConfig), null, 2));
17637
+ `;
17638
+ function getTsEvalCommand(pm) {
17639
+ switch (pm) {
17640
+ case "bun":
17641
+ return ["bun", ["--eval", EVAL_SCRIPT]];
17642
+ case "pnpm":
17643
+ return ["pnpm", ["dlx", "tsx", "--eval", EVAL_SCRIPT]];
17644
+ case "yarn":
17645
+ return ["yarn", ["dlx", "tsx", "--eval", EVAL_SCRIPT]];
17646
+ default:
17647
+ return ["npx", ["tsx", "--eval", EVAL_SCRIPT]];
17648
+ }
17649
+ }
17650
+ var config2 = new Command().name("config").description("print the resolved configuration for the current ChatJS project").option("-c, --cwd <cwd>", "the working directory (defaults to current directory)", process.cwd()).action(async (opts) => {
17651
+ try {
17652
+ const cwd = path.resolve(opts.cwd);
17653
+ const pm = inferPackageManager();
17654
+ const [cmd, args] = getTsEvalCommand(pm);
17655
+ await new Promise((resolve, reject) => {
17656
+ const child = spawn(cmd, args, {
17657
+ cwd,
17658
+ stdio: ["ignore", "inherit", "pipe"]
17659
+ });
17660
+ const stderr = [];
17661
+ child.stderr?.on("data", (data) => stderr.push(String(data)));
17662
+ child.on("error", (err) => {
17663
+ reject(new Error(`Could not spawn ${cmd}. Make sure ${pm} is installed.
17664
+ ${err.message}`));
17665
+ });
17666
+ child.on("close", (code) => {
17667
+ if (code === 0)
17668
+ resolve();
17669
+ else
17670
+ reject(new Error(`Failed to resolve config:
17671
+ ${stderr.join("").trim()}`));
17672
+ });
17673
+ });
17674
+ } catch (error48) {
17675
+ handleError(error48);
17676
+ }
17677
+ });
17678
+
17614
17679
  // src/commands/create.ts
17615
17680
  import { readFile, writeFile } from "node:fs/promises";
17616
17681
  import { join as join2, resolve as resolve2 } from "node:path";
@@ -18661,7 +18726,7 @@ ${l}
18661
18726
  } }).prompt();
18662
18727
 
18663
18728
  // ../../apps/chat/lib/ai/gateway-model-defaults.ts
18664
- var multiProviderDefaults = {
18729
+ var vercelDefaults = {
18665
18730
  providerOrder: ["openai", "google", "anthropic"],
18666
18731
  disabledModels: [],
18667
18732
  curatedDefaults: [
@@ -18684,36 +18749,66 @@ var multiProviderDefaults = {
18684
18749
  chatImageCompatible: "openai/gpt-4o-mini"
18685
18750
  },
18686
18751
  tools: {
18687
- webSearch: {
18688
- enabled: false
18689
- },
18690
- urlRetrieval: {
18691
- enabled: false
18692
- },
18693
- codeExecution: {
18694
- enabled: false
18695
- },
18696
- mcp: {
18697
- enabled: false
18698
- },
18752
+ webSearch: { enabled: false },
18753
+ urlRetrieval: { enabled: false },
18754
+ codeExecution: { enabled: false },
18755
+ mcp: { enabled: false },
18699
18756
  followupSuggestions: {
18700
18757
  enabled: false,
18701
18758
  default: "google/gemini-2.5-flash-lite"
18702
18759
  },
18703
- text: {
18704
- polish: "openai/gpt-5-mini"
18705
- },
18706
- sheet: {
18707
- format: "openai/gpt-5-mini",
18708
- analyze: "openai/gpt-5-mini"
18709
- },
18710
- code: {
18711
- edits: "openai/gpt-5-mini"
18712
- },
18713
- image: {
18760
+ text: { polish: "openai/gpt-5-mini" },
18761
+ sheet: { format: "openai/gpt-5-mini", analyze: "openai/gpt-5-mini" },
18762
+ code: { edits: "openai/gpt-5-mini" },
18763
+ image: { enabled: false, default: "google/gemini-3-pro-image" },
18764
+ video: { enabled: false, default: "xai/grok-imagine-video" },
18765
+ deepResearch: {
18714
18766
  enabled: false,
18715
- default: "google/gemini-3-pro-image"
18767
+ defaultModel: "google/gemini-2.5-flash-lite",
18768
+ finalReportModel: "google/gemini-3-flash",
18769
+ allowClarification: true,
18770
+ maxResearcherIterations: 1,
18771
+ maxConcurrentResearchUnits: 2,
18772
+ maxSearchQueries: 2
18773
+ }
18774
+ }
18775
+ };
18776
+ var openrouterDefaults = {
18777
+ providerOrder: ["openai", "google", "anthropic"],
18778
+ disabledModels: [],
18779
+ curatedDefaults: [
18780
+ "openai/gpt-5-nano",
18781
+ "openai/gpt-5-mini",
18782
+ "openai/gpt-5.2",
18783
+ "openai/gpt-5.2-chat",
18784
+ "google/gemini-2.5-flash-lite",
18785
+ "google/gemini-3-flash",
18786
+ "google/gemini-3-pro-preview",
18787
+ "anthropic/claude-sonnet-4.5",
18788
+ "anthropic/claude-opus-4.5",
18789
+ "xai/grok-4"
18790
+ ],
18791
+ anonymousModels: ["google/gemini-2.5-flash-lite", "openai/gpt-5-nano"],
18792
+ workflows: {
18793
+ chat: "openai/gpt-5-mini",
18794
+ title: "openai/gpt-5-nano",
18795
+ pdf: "openai/gpt-5-mini",
18796
+ chatImageCompatible: "openai/gpt-4o-mini"
18797
+ },
18798
+ tools: {
18799
+ webSearch: { enabled: false },
18800
+ urlRetrieval: { enabled: false },
18801
+ codeExecution: { enabled: false },
18802
+ mcp: { enabled: false },
18803
+ followupSuggestions: {
18804
+ enabled: false,
18805
+ default: "google/gemini-2.5-flash-lite"
18716
18806
  },
18807
+ text: { polish: "openai/gpt-5-mini" },
18808
+ sheet: { format: "openai/gpt-5-mini", analyze: "openai/gpt-5-mini" },
18809
+ code: { edits: "openai/gpt-5-mini" },
18810
+ image: { enabled: false },
18811
+ video: { enabled: false },
18717
18812
  deepResearch: {
18718
18813
  enabled: false,
18719
18814
  defaultModel: "google/gemini-2.5-flash-lite",
@@ -18725,7 +18820,7 @@ var multiProviderDefaults = {
18725
18820
  }
18726
18821
  }
18727
18822
  };
18728
- var openaiOnlyDefaults = {
18823
+ var openaiDefaults = {
18729
18824
  providerOrder: ["openai"],
18730
18825
  disabledModels: [],
18731
18826
  curatedDefaults: [
@@ -18742,36 +18837,54 @@ var openaiOnlyDefaults = {
18742
18837
  chatImageCompatible: "gpt-4o-mini"
18743
18838
  },
18744
18839
  tools: {
18745
- webSearch: {
18746
- enabled: false
18747
- },
18748
- urlRetrieval: {
18749
- enabled: false
18750
- },
18751
- codeExecution: {
18752
- enabled: false
18753
- },
18754
- mcp: {
18755
- enabled: false
18756
- },
18757
- followupSuggestions: {
18758
- enabled: false,
18759
- default: "gpt-5-nano"
18760
- },
18761
- text: {
18762
- polish: "gpt-5-mini"
18763
- },
18764
- sheet: {
18765
- format: "gpt-5-mini",
18766
- analyze: "gpt-5-mini"
18767
- },
18768
- code: {
18769
- edits: "gpt-5-mini"
18770
- },
18771
- image: {
18840
+ webSearch: { enabled: false },
18841
+ urlRetrieval: { enabled: false },
18842
+ codeExecution: { enabled: false },
18843
+ mcp: { enabled: false },
18844
+ followupSuggestions: { enabled: false, default: "gpt-5-nano" },
18845
+ text: { polish: "gpt-5-mini" },
18846
+ sheet: { format: "gpt-5-mini", analyze: "gpt-5-mini" },
18847
+ code: { edits: "gpt-5-mini" },
18848
+ image: { enabled: false },
18849
+ video: { enabled: false },
18850
+ deepResearch: {
18772
18851
  enabled: false,
18773
- default: "gpt-image-1"
18774
- },
18852
+ defaultModel: "gpt-5-nano",
18853
+ finalReportModel: "gpt-5-mini",
18854
+ allowClarification: true,
18855
+ maxResearcherIterations: 1,
18856
+ maxConcurrentResearchUnits: 2,
18857
+ maxSearchQueries: 2
18858
+ }
18859
+ }
18860
+ };
18861
+ var openaiCompatibleDefaults = {
18862
+ providerOrder: ["openai"],
18863
+ disabledModels: [],
18864
+ curatedDefaults: [
18865
+ "gpt-5-nano",
18866
+ "gpt-5-mini",
18867
+ "gpt-5.2",
18868
+ "gpt-5.2-chat-latest"
18869
+ ],
18870
+ anonymousModels: ["gpt-5-nano"],
18871
+ workflows: {
18872
+ chat: "gpt-5-mini",
18873
+ title: "gpt-5-nano",
18874
+ pdf: "gpt-5-mini",
18875
+ chatImageCompatible: "gpt-4o-mini"
18876
+ },
18877
+ tools: {
18878
+ webSearch: { enabled: false },
18879
+ urlRetrieval: { enabled: false },
18880
+ codeExecution: { enabled: false },
18881
+ mcp: { enabled: false },
18882
+ followupSuggestions: { enabled: false, default: "gpt-5-nano" },
18883
+ text: { polish: "gpt-5-mini" },
18884
+ sheet: { format: "gpt-5-mini", analyze: "gpt-5-mini" },
18885
+ code: { edits: "gpt-5-mini" },
18886
+ image: { enabled: false },
18887
+ video: { enabled: false },
18775
18888
  deepResearch: {
18776
18889
  enabled: false,
18777
18890
  defaultModel: "gpt-5-nano",
@@ -18784,10 +18897,10 @@ var openaiOnlyDefaults = {
18784
18897
  }
18785
18898
  };
18786
18899
  var GATEWAY_MODEL_DEFAULTS = {
18787
- vercel: multiProviderDefaults,
18788
- openrouter: multiProviderDefaults,
18789
- openai: openaiOnlyDefaults,
18790
- "openai-compatible": openaiOnlyDefaults
18900
+ vercel: vercelDefaults,
18901
+ openrouter: openrouterDefaults,
18902
+ openai: openaiDefaults,
18903
+ "openai-compatible": openaiCompatibleDefaults
18791
18904
  };
18792
18905
 
18793
18906
  // ../../apps/chat/lib/config-schema.ts
@@ -18850,14 +18963,26 @@ function createAiSchema(g) {
18850
18963
  code: exports_external.object({
18851
18964
  edits: gatewayModelId()
18852
18965
  }),
18853
- image: exports_external.object({
18854
- enabled: exports_external.boolean(),
18855
- default: gatewayImageModelId()
18856
- }),
18857
- video: exports_external.object({
18858
- enabled: exports_external.boolean(),
18859
- default: gatewayVideoModelId()
18860
- }),
18966
+ image: exports_external.discriminatedUnion("enabled", [
18967
+ exports_external.object({
18968
+ enabled: exports_external.literal(true),
18969
+ default: gatewayImageModelId()
18970
+ }),
18971
+ exports_external.object({
18972
+ enabled: exports_external.literal(false),
18973
+ default: gatewayImageModelId().optional()
18974
+ })
18975
+ ]),
18976
+ video: exports_external.discriminatedUnion("enabled", [
18977
+ exports_external.object({
18978
+ enabled: exports_external.literal(true),
18979
+ default: gatewayVideoModelId()
18980
+ }),
18981
+ exports_external.object({
18982
+ enabled: exports_external.literal(false),
18983
+ default: gatewayVideoModelId().optional()
18984
+ })
18985
+ ]),
18861
18986
  deepResearch: deepResearchToolConfigSchema.extend({
18862
18987
  enabled: exports_external.boolean(),
18863
18988
  defaultModel: gatewayModelId(),
@@ -18879,72 +19004,7 @@ var aiConfigSchema = exports_external.discriminatedUnion("gateway", [
18879
19004
  gatewaySchemaMap["openai-compatible"]
18880
19005
  ]).default({
18881
19006
  gateway: DEFAULT_GATEWAY,
18882
- providerOrder: ["openai", "google", "anthropic"],
18883
- disabledModels: [],
18884
- curatedDefaults: [
18885
- "openai/gpt-5-nano",
18886
- "openai/gpt-5-mini",
18887
- "openai/gpt-5.2",
18888
- "openai/gpt-5.2-chat",
18889
- "google/gemini-2.5-flash-lite",
18890
- "google/gemini-3-flash",
18891
- "google/gemini-3-pro-preview",
18892
- "anthropic/claude-sonnet-4.5",
18893
- "anthropic/claude-opus-4.5",
18894
- "xai/grok-4"
18895
- ],
18896
- anonymousModels: ["google/gemini-2.5-flash-lite", "openai/gpt-5-nano"],
18897
- workflows: {
18898
- chat: "openai/gpt-5-mini",
18899
- title: "openai/gpt-5-nano",
18900
- pdf: "openai/gpt-5-mini",
18901
- chatImageCompatible: "openai/gpt-4o-mini"
18902
- },
18903
- tools: {
18904
- webSearch: {
18905
- enabled: false
18906
- },
18907
- urlRetrieval: {
18908
- enabled: false
18909
- },
18910
- codeExecution: {
18911
- enabled: false
18912
- },
18913
- mcp: {
18914
- enabled: false
18915
- },
18916
- followupSuggestions: {
18917
- enabled: false,
18918
- default: "google/gemini-2.5-flash-lite"
18919
- },
18920
- text: {
18921
- polish: "openai/gpt-5-mini"
18922
- },
18923
- sheet: {
18924
- format: "openai/gpt-5-mini",
18925
- analyze: "openai/gpt-5-mini"
18926
- },
18927
- code: {
18928
- edits: "openai/gpt-5-mini"
18929
- },
18930
- image: {
18931
- enabled: false,
18932
- default: "google/gemini-3-pro-image"
18933
- },
18934
- video: {
18935
- enabled: false,
18936
- default: "xai/grok-imagine-video"
18937
- },
18938
- deepResearch: {
18939
- enabled: false,
18940
- defaultModel: "google/gemini-2.5-flash-lite",
18941
- finalReportModel: "google/gemini-3-flash",
18942
- allowClarification: true,
18943
- maxResearcherIterations: 1,
18944
- maxConcurrentResearchUnits: 2,
18945
- maxSearchQueries: 2
18946
- }
18947
- }
19007
+ ...GATEWAY_MODEL_DEFAULTS[DEFAULT_GATEWAY]
18948
19008
  });
18949
19009
  var pricingConfigSchema = exports_external.object({
18950
19010
  currency: exports_external.string().optional(),
@@ -19074,8 +19134,8 @@ function extractDescriptions(schema, prefix = "", result = new Map) {
19074
19134
  if (unwrapped instanceof exports_external.ZodObject) {
19075
19135
  const shape = unwrapped._zod.def.shape;
19076
19136
  for (const [key, propSchema] of Object.entries(shape)) {
19077
- const path = prefix ? `${prefix}.${key}` : key;
19078
- extractDescriptions(propSchema, path, result);
19137
+ const path2 = prefix ? `${prefix}.${key}` : key;
19138
+ extractDescriptions(propSchema, path2, result);
19079
19139
  }
19080
19140
  }
19081
19141
  return result;
@@ -19117,11 +19177,11 @@ ${spaces}}`;
19117
19177
  function generateConfig(obj, indent, pathPrefix, descs) {
19118
19178
  const spaces = " ".repeat(indent);
19119
19179
  return Object.entries(obj).map(([key, value]) => {
19120
- const path = pathPrefix ? `${pathPrefix}.${key}` : key;
19121
- const desc = descs.get(path);
19180
+ const path2 = pathPrefix ? `${pathPrefix}.${key}` : key;
19181
+ const desc = descs.get(path2);
19122
19182
  const comment = desc ? ` // ${desc}` : "";
19123
19183
  if (typeof value === "object" && value !== null && !Array.isArray(value)) {
19124
- const nested = generateConfig(value, indent + 1, path, descs);
19184
+ const nested = generateConfig(value, indent + 1, path2, descs);
19125
19185
  return `${spaces}${formatKey(key)}: {
19126
19186
  ${nested}
19127
19187
  ${spaces}},`;
@@ -19131,7 +19191,6 @@ ${spaces}},`;
19131
19191
  `);
19132
19192
  }
19133
19193
  function buildConfigTs(input) {
19134
- const modelDefaults = GATEWAY_MODEL_DEFAULTS[input.gateway];
19135
19194
  const fullConfig = {
19136
19195
  appPrefix: input.appPrefix,
19137
19196
  appName: input.appName,
@@ -19160,7 +19219,7 @@ function buildConfigTs(input) {
19160
19219
  terms: { title: "Terms of Service" }
19161
19220
  },
19162
19221
  authentication: input.auth,
19163
- models: { gateway: input.gateway, ...modelDefaults },
19222
+ ai: { gateway: input.gateway },
19164
19223
  anonymous: {
19165
19224
  credits: 10,
19166
19225
  availableTools: [],
@@ -19177,15 +19236,9 @@ function buildConfigTs(input) {
19177
19236
  "image/jpeg": [".jpg", ".jpeg"],
19178
19237
  "application/pdf": [".pdf"]
19179
19238
  }
19180
- },
19181
- deepResearch: {
19182
- allowClarification: true,
19183
- maxResearcherIterations: 1,
19184
- maxConcurrentResearchUnits: 2,
19185
- maxSearchQueries: 2
19186
19239
  }
19187
19240
  };
19188
- return `import type { ConfigInput } from "@/lib/config-schema";
19241
+ return `import { defineConfig } from "@/lib/config-schema";
19189
19242
 
19190
19243
  /**
19191
19244
  * ChatJS Configuration
@@ -19193,9 +19246,9 @@ function buildConfigTs(input) {
19193
19246
  * Edit this file to customize your app.
19194
19247
  * @see https://chatjs.dev/docs/reference/config
19195
19248
  */
19196
- const config: ConfigInput = {
19249
+ const config = defineConfig({
19197
19250
  ${generateConfig(fullConfig, 1, "", descriptions)}
19198
- };
19251
+ });
19199
19252
 
19200
19253
  export default config;
19201
19254
  `;
@@ -19514,10 +19567,10 @@ import { dirname, join, resolve } from "node:path";
19514
19567
  import { fileURLToPath } from "node:url";
19515
19568
 
19516
19569
  // src/utils/run-command.ts
19517
- import { spawn } from "node:child_process";
19570
+ import { spawn as spawn2 } from "node:child_process";
19518
19571
  async function runCommand(command, args, cwd) {
19519
19572
  await new Promise((resolvePromise, rejectPromise) => {
19520
- const child = spawn(command, args, { cwd, stdio: "pipe" });
19573
+ const child = spawn2(command, args, { cwd, stdio: "pipe" });
19521
19574
  const stderr = [];
19522
19575
  child.stderr?.on("data", (data) => {
19523
19576
  stderr.push(String(data));
@@ -19552,20 +19605,6 @@ async function scaffoldFromGit(url2, destination) {
19552
19605
  await rm(join(destination, ".git"), { recursive: true, force: true });
19553
19606
  }
19554
19607
 
19555
- // src/utils/get-package-manager.ts
19556
- function inferPackageManager() {
19557
- const ua = process.env.npm_config_user_agent ?? "";
19558
- if (ua.startsWith("pnpm/"))
19559
- return "pnpm";
19560
- if (ua.startsWith("yarn/"))
19561
- return "yarn";
19562
- if (ua.startsWith("npm/"))
19563
- return "npm";
19564
- if (ua.startsWith("bun/"))
19565
- return "bun";
19566
- return "bun";
19567
- }
19568
-
19569
19608
  // ../../node_modules/ora/index.js
19570
19609
  import process10 from "node:process";
19571
19610
 
@@ -21056,4 +21095,5 @@ process.on("SIGTERM", () => process.exit(0));
21056
21095
  var program2 = new Command().name("chat-js").description("ChatJS CLI").version(package_default.version, "-v, --version", "display the version number");
21057
21096
  program2.addCommand(create, { isDefault: true });
21058
21097
  program2.addCommand(add);
21098
+ program2.addCommand(config2);
21059
21099
  program2.parse();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@chat-js/cli",
3
- "version": "0.2.1",
3
+ "version": "0.3.0",
4
4
  "description": "CLI for creating and extending ChatJS apps",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
@@ -0,0 +1,19 @@
1
+ # @chatjs/chat
2
+
3
+ ## 0.2.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [#94](https://github.com/FranciscoMoretti/chat-js/pull/94) [`2a8a7cc`](https://github.com/FranciscoMoretti/chat-js/commit/2a8a7cc2b0649bd73e41999dbf0528a21e8065be) Thanks [@FranciscoMoretti](https://github.com/FranciscoMoretti)! - ## Config defaults & `defineConfig` helper
8
+
9
+ ### New features
10
+
11
+ - **`defineConfig()` helper** — new type-safe wrapper for `chat.config.ts`. The gateway type is inferred from `ai.gateway`, so autocomplete and type errors are scoped to the model IDs available in the chosen gateway. Replace `satisfies ConfigInput` with `defineConfig({...})`.
12
+ - **Gateway-specific defaults** — all AI config fields (models, tools, workflows) are now optional. Omitted fields are automatically filled from per-gateway defaults at runtime via `applyDefaults()`. Only `ai.gateway` is required.
13
+ - **`chatjs config` CLI command** — new command that prints the fully-resolved configuration for the current project, applying all defaults. Useful for debugging and verifying your setup.
14
+ - **Separate defaults per gateway** — `vercel`, `openrouter`, `openai`, and `openai-compatible` each have their own typed defaults (`ModelDefaultsFor<G>`), ensuring model IDs are validated against the correct gateway's model registry.
15
+ - **Stricter image/video tool schemas** — `tools.image` and `tools.video` now use a discriminated union: `enabled: true` requires a `default` model, while `enabled: false` makes it optional.
16
+
17
+ ### Breaking changes
18
+
19
+ None — existing configs using `satisfies ConfigInput` continue to work. Migrating to `defineConfig()` is recommended for better DX but not required.
@@ -6,13 +6,13 @@ import type { ChatMessage } from "@/lib/ai/types";
6
6
  import { config } from "@/lib/config";
7
7
 
8
8
  export async function generateTitleFromUserMessage({
9
- message,
9
+ message,
10
10
  }: {
11
- message: ChatMessage;
11
+ message: ChatMessage;
12
12
  }) {
13
- const { text: title } = await generateText({
14
- model: await getLanguageModel(config.ai.workflows.title),
15
- system: `Generate a concise title for a chat conversation based on the user's first message.
13
+ const { text: title } = await generateText({
14
+ model: await getLanguageModel(config.ai.workflows.title),
15
+ system: `Generate a concise title for a chat conversation based on the user's first message.
16
16
 
17
17
  Rules (strictly follow all):
18
18
  - Maximum 40 characters — hard limit, never exceed this
@@ -21,9 +21,9 @@ Rules (strictly follow all):
21
21
  - No filler words like "How to" or "Question about"
22
22
  - Use title case
23
23
  - Return ONLY the title, nothing else`,
24
- prompt: JSON.stringify(message),
25
- experimental_telemetry: { isEnabled: true },
26
- });
24
+ prompt: JSON.stringify(message),
25
+ experimental_telemetry: { isEnabled: true },
26
+ });
27
27
 
28
- return title;
28
+ return title;
29
29
  }
@@ -1,4 +1,4 @@
1
- import type { ConfigInput } from "@/lib/config-schema";
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: "vercel",
72
- providerOrder: ["openai", "google", "anthropic", "xai"],
73
- disabledModels: ["morph/morph-v3-large", "morph/morph-v3-fast"],
74
- curatedDefaults: [
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
- chat: "openai/gpt-5-mini",
98
- title: "google/gemini-2.5-flash-lite",
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: "google/gemini-2.5-flash-lite",
118
93
  },
119
94
  text: {
120
- polish: "openai/gpt-5-mini",
95
+ polish: "gpt-5-mini",
121
96
  },
122
97
  sheet: {
123
- format: "openai/gpt-5-mini",
124
- analyze: "openai/gpt-5-mini",
98
+ format: "gpt-5-mini",
99
+ analyze: "gpt-5-mini",
125
100
  },
126
101
  code: {
127
- edits: "openai/gpt-5-mini",
102
+ edits: "gpt-5-mini",
128
103
  },
129
104
  image: {
130
105
  enabled: true, // Requires BLOB_READ_WRITE_TOKEN
131
- default: "google/gemini-3-pro-image",
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: "google/gemini-2.5-flash-lite",
140
- finalReportModel: "google/gemini-3-flash",
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
- } satisfies ConfigInput;
136
+ });
166
137
 
167
138
  export default config;