@@ -646,6 +646,62 @@ const getLaunchOptions = (headless: boolean, downloadsPath: string | null = null
646646 } }
647647} ;
648648
649+ // LLM config resolution with graceful fallbacks. Default route is the
650+ // Unity Comms unillm proxy (when UNITY_COMMS_URL is set). When that
651+ // proxy isn't available (e.g. running agent-service standalone outside
652+ // a full Unity stack), fall back to Anthropic or OpenAI direct using
653+ // the corresponding API key from env.
654+ const getLlmConfig = ( ) : any => {
655+ if ( process . env . UNITY_COMMS_URL ) {
656+ return {
657+ provider : 'openai-generic' as const ,
658+ options : {
659+ model : 'claude-4.6-sonnet@anthropic' ,
660+ baseUrl : `${ process . env . UNITY_COMMS_URL } /unillm` ,
661+ headers : {
662+ 'Authorization' : `Bearer ${ process . env . UNIFY_KEY } ` ,
663+ } ,
664+ temperature : 0.2 ,
665+ }
666+ } ;
667+ }
668+ // Prefer Anthropic for computer-use: Claude is significantly more
669+ // accurate at vision-grounded action planning than GPT-4o.
670+ if ( process . env . ANTHROPIC_API_KEY ) {
671+ // Anthropic ships an OpenAI-compatible endpoint that speaks the
672+ // same Chat Completions wire format, so we keep provider as
673+ // 'openai-generic' and point at the compat path.
674+ return {
675+ provider : 'openai-generic' as const ,
676+ options : {
677+ model : 'claude-sonnet-4-5' ,
678+ baseUrl : 'https://api.anthropic.com/v1' ,
679+ headers : {
680+ 'Authorization' : `Bearer ${ process . env . ANTHROPIC_API_KEY } ` ,
681+ } ,
682+ temperature : 0.2 ,
683+ }
684+ } ;
685+ }
686+ if ( process . env . OPENAI_API_KEY ) {
687+ return {
688+ provider : 'openai-generic' as const ,
689+ options : {
690+ model : 'gpt-4o' ,
691+ baseUrl : 'https://api.openai.com/v1' ,
692+ headers : {
693+ 'Authorization' : `Bearer ${ process . env . OPENAI_API_KEY } ` ,
694+ } ,
695+ temperature : 0.2 ,
696+ }
697+ } ;
698+ }
699+ throw new Error (
700+ 'No LLM provider configured: set UNITY_COMMS_URL (preferred), or ' +
701+ 'ANTHROPIC_API_KEY or OPENAI_API_KEY for direct provider fallback.'
702+ ) ;
703+ } ;
704+
649705const startDesktop = async ( ) : Promise < BrowserAgent > => {
650706 try {
651707 const encodedPassword = encodeURIComponent ( process . env . UNIFY_KEY || '' ) ;
@@ -656,18 +712,9 @@ const startDesktop = async (): Promise<BrowserAgent> => {
656712 browser : getLaunchOptions ( true ) ,
657713 prompt : "You're controlling a noVNC virtual desktop page. Do not navigate to other page and use mouse and keyboard to control the browser and apps within the virtual desktop. There may be a terminal (xterm) app launched in the desktop for use." ,
658714 narrate : true ,
659- // Route LLM calls through Orchestra/UniLLM proxy for billing and caching
660- llm : {
661- provider : 'openai-generic' ,
662- options : {
663- model : 'claude-4.6-sonnet@anthropic' ,
664- baseUrl : `${ process . env . UNITY_COMMS_URL } /unillm` ,
665- headers : {
666- 'Authorization' : `Bearer ${ process . env . UNIFY_KEY } ` ,
667- } ,
668- temperature : 0.2 ,
669- }
670- }
715+ // Route LLM calls through Orchestra/UniLLM proxy for billing/caching,
716+ // with fallback to direct OpenAI/Anthropic when UNITY_COMMS_URL is unset.
717+ llm : getLlmConfig ( )
671718 } ) ;
672719 agent . context . setDefaultNavigationTimeout ( 90000 ) ;
673720 // Auto-grant clipboard permissions so the noVNC "Share clipboard?" popup is suppressed
@@ -690,18 +737,9 @@ const startBrowser = async (headless: boolean, urlMappings?: Record<string, stri
690737 browser : getLaunchOptions ( headless , defaultBrowserPaths . downloadsPath , defaultBrowserPaths . tracesDir ) ,
691738 narrate : true ,
692739 urlMappings,
693- // Route LLM calls through Orchestra/UniLLM proxy for billing and caching
694- llm : {
695- provider : 'openai-generic' ,
696- options : {
697- model : 'claude-4.6-sonnet@anthropic' ,
698- baseUrl : `${ process . env . UNITY_COMMS_URL } /unillm` ,
699- headers : {
700- 'Authorization' : `Bearer ${ process . env . UNIFY_KEY } ` ,
701- } ,
702- temperature : 0.2 ,
703- }
704- }
740+ // Route LLM calls through Orchestra/UniLLM proxy for billing/caching,
741+ // with fallback to direct OpenAI/Anthropic when UNITY_COMMS_URL is unset.
742+ llm : getLlmConfig ( )
705743 } ) ;
706744 agent . context . setDefaultNavigationTimeout ( 90000 ) ;
707745 console . log ( "β
BrowserAgent started successfully." ) ;
@@ -731,17 +769,7 @@ const startBrowserOnVm = async (urlMappings?: Record<string, string>): Promise<B
731769 } ,
732770 narrate : true ,
733771 urlMappings,
734- llm : {
735- provider : 'openai-generic' ,
736- options : {
737- model : 'claude-4.6-sonnet@anthropic' ,
738- baseUrl : `${ process . env . UNITY_COMMS_URL } /unillm` ,
739- headers : {
740- 'Authorization' : `Bearer ${ process . env . UNIFY_KEY } ` ,
741- } ,
742- temperature : 0.2 ,
743- }
744- }
772+ llm : getLlmConfig ( )
745773 } ) ;
746774 agent . context . setDefaultNavigationTimeout ( 90000 ) ;
747775 console . log ( "β
Web-VM BrowserAgent started successfully." ) ;
@@ -781,17 +809,7 @@ const startGoogleMeetBrowser = async (meetUrl: string): Promise<BrowserAgent> =>
781809 } ,
782810 } ,
783811 narrate : true ,
784- llm : {
785- provider : 'openai-generic' ,
786- options : {
787- model : 'claude-4.6-sonnet@anthropic' ,
788- baseUrl : `${ process . env . UNITY_COMMS_URL } /unillm` ,
789- headers : {
790- 'Authorization' : `Bearer ${ process . env . UNIFY_KEY } ` ,
791- } ,
792- temperature : 0.2 ,
793- }
794- }
812+ llm : getLlmConfig ( )
795813 } ) ;
796814 agent . context . setDefaultNavigationTimeout ( 90000 ) ;
797815 console . log ( "β
Google Meet BrowserAgent started successfully." ) ;
@@ -2264,17 +2282,7 @@ const startTeamsMeetBrowser = async (meetUrl: string): Promise<BrowserAgent> =>
22642282 } ,
22652283 } ,
22662284 narrate : true ,
2267- llm : {
2268- provider : 'openai-generic' ,
2269- options : {
2270- model : 'claude-4.6-sonnet@anthropic' ,
2271- baseUrl : `${ process . env . UNITY_COMMS_URL } /unillm` ,
2272- headers : {
2273- 'Authorization' : `Bearer ${ process . env . UNIFY_KEY } ` ,
2274- } ,
2275- temperature : 0.2 ,
2276- }
2277- }
2285+ llm : getLlmConfig ( )
22782286 } ) ;
22792287 agent . context . setDefaultNavigationTimeout ( 90000 ) ;
22802288 console . log ( "β
Teams Meet BrowserAgent started successfully." ) ;
0 commit comments