Skip to content

Commit 2c625ca

Browse files
authored
Add model dep in cli (#40)
1 parent f2a6308 commit 2c625ca

2 files changed

Lines changed: 45 additions & 6 deletions

File tree

package.json

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -64,11 +64,13 @@
6464
"peerDependencies": {
6565
"@langchain/core": ">=1.1.31"
6666
},
67-
"devDependencies": {
67+
"optionalDependencies": {
6868
"@langchain/anthropic": "^1.0.0",
69-
"@langchain/core": "^1.1.31",
7069
"@langchain/google-genai": "^2.1.24",
71-
"@langchain/openai": "^1.2.12",
70+
"@langchain/openai": "^1.2.12"
71+
},
72+
"devDependencies": {
73+
"@langchain/core": "^1.1.31",
7274
"@types/jest": "^29.5.14",
7375
"@types/lodash": "^4.17.16",
7476
"@types/merge-images": "^1.2.4",

src/cli/index.ts

Lines changed: 40 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,37 @@ import {
2323
} from "@/types";
2424
import { BrowserAgentError } from "@/agent/error";
2525

26+
/**
27+
* Dynamically load a provider SDK and surface a clear, actionable error if
28+
* it's missing. The provider packages are declared as
29+
* `optionalDependencies`, so they normally ship with the CLI — but a user
30+
* who ran `npm install --omit=optional` (or whose install was interrupted)
31+
* may not have them. In that case, tell them exactly which package to
32+
* install instead of printing a raw `MODULE_NOT_FOUND` stack.
33+
*/
34+
async function loadProvider<T>(
35+
packageName: string,
36+
providerLabel: string
37+
): Promise<T> {
38+
try {
39+
return (await import(packageName)) as T;
40+
} catch (err) {
41+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
42+
const code = (err as any)?.code;
43+
if (code === "ERR_MODULE_NOT_FOUND" || code === "MODULE_NOT_FOUND") {
44+
console.error(
45+
chalk.red(
46+
`${providerLabel} provider is not installed.\n` +
47+
`Install it and retry:\n\n` +
48+
` npm install -g ${packageName}\n`
49+
)
50+
);
51+
process.exit(1);
52+
}
53+
throw err;
54+
}
55+
}
56+
2657
/**
2758
* Select an LLM based on environment variables. Providers are checked in
2859
* priority order: OpenAI, Google, Anthropic. Per-provider model is
@@ -31,23 +62,29 @@ import { BrowserAgentError } from "@/agent/error";
3162
*/
3263
async function createDefaultLlm(): Promise<BaseChatModel | undefined> {
3364
if (process.env.OPENAI_API_KEY) {
34-
const { ChatOpenAI } = await import("@langchain/openai");
65+
const { ChatOpenAI } = await loadProvider<
66+
typeof import("@langchain/openai")
67+
>("@langchain/openai", "OpenAI");
3568
return new ChatOpenAI({
3669
apiKey: process.env.OPENAI_API_KEY,
3770
model: process.env.OPENAI_MODEL ?? "gpt-4.1-mini",
3871
temperature: 0,
3972
}) as unknown as BaseChatModel;
4073
}
4174
if (process.env.GOOGLE_API_KEY || process.env.GEMINI_API_KEY) {
42-
const { ChatGoogleGenerativeAI } = await import("@langchain/google-genai");
75+
const { ChatGoogleGenerativeAI } = await loadProvider<
76+
typeof import("@langchain/google-genai")
77+
>("@langchain/google-genai", "Google Gemini");
4378
return new ChatGoogleGenerativeAI({
4479
apiKey: process.env.GOOGLE_API_KEY ?? process.env.GEMINI_API_KEY,
4580
model: process.env.GEMINI_MODEL ?? "gemini-2.5-flash",
4681
temperature: 0,
4782
}) as unknown as BaseChatModel;
4883
}
4984
if (process.env.ANTHROPIC_API_KEY) {
50-
const { ChatAnthropic } = await import("@langchain/anthropic");
85+
const { ChatAnthropic } = await loadProvider<
86+
typeof import("@langchain/anthropic")
87+
>("@langchain/anthropic", "Anthropic");
5188
return new ChatAnthropic({
5289
apiKey: process.env.ANTHROPIC_API_KEY,
5390
model:

0 commit comments

Comments
 (0)