@@ -23,6 +23,37 @@ import {
2323} from "@/types" ;
2424import { 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 */
3263async 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