@@ -644,7 +644,7 @@ def build_llm_node(config_metadata: dict, node_name, skill_name, owner, bp_manag
644644 hard_timeout_config = str (hard_timeout_val ).lower () in ('true' , '1' , 'yes' , 'on' ) if hard_timeout_val else False
645645 except Exception :
646646 pass
647- # Prefer explicit provider; infer from apiHost if absent
647+ # Get explicit provider from frontend (guaranteed by form-meta.tsx)
648648 raw_provider = None
649649 try :
650650 raw_provider = ((inputs .get ("modelProvider" ) or {}).get ("content" )
@@ -705,28 +705,13 @@ def build_llm_node(config_metadata: dict, node_name, skill_name, owner, bp_manag
705705 user_prompt_template = get_prompt_content (user_prompt_id , resolved_user_prompt )
706706 else :
707707 user_prompt_template = resolved_user_prompt
708- # Infer provider when not explicitly set
709- def _infer_provider (host : str , model : str ) -> str :
710- try :
711- h = (host or "" ).lower ()
712- m = (model or "" ).lower ()
713- if "anthropic" in h or m .startswith ("claude" ):
714- return "anthropic"
715- if "google" in h or "generativeai" in h or m .startswith ("gemini" ):
716- return "google"
717- return "openai"
718- except Exception :
719- return "openai"
720-
721- model_provider = raw_provider or _infer_provider (api_host , model_name )
722- llm_provider = (model_provider or "openai" ).lower ()
723-
724708 # Normalize provider names dynamically from llm_manager
725709 # This automatically syncs with gui/config/llm_providers.json
726710 def _get_provider_mapping () -> dict :
727711 """
728712 Dynamically build provider mapping from llm_manager.
729- This ensures consistency with llm_providers.json and reduces maintenance.
713+ Maps all known name variants (name, display_name, class_name, provider_id)
714+ to the canonical provider_id. No hardcoded provider list needed.
730715
731716 Returns:
732717 Dictionary mapping various provider name formats to canonical provider identifiers
@@ -774,18 +759,17 @@ def _get_provider_mapping() -> dict:
774759 return {}
775760
776761 # Get dynamic provider mapping from llm_manager
777- # This automatically includes all providers defined in llm_providers.json:
778- # - provider_id (e.g., "deepseek")
779- # - name (e.g., "DeepSeek")
780- # - display_name
781- # - class_name (e.g., "ChatDeepSeek")
762+ # Resolves any name variant (name/display_name/class_name/provider_id) → canonical provider_id
782763 provider_mapping = _get_provider_mapping ()
764+
765+ # Resolve raw_provider (e.g. "DeepSeek", "Qwen (DashScope)") to canonical provider_id (e.g. "deepseek", "dashscope")
766+ # Frontend is responsible for always passing correct modelProvider via form-meta.tsx
767+ model_provider = provider_mapping .get ((raw_provider or "" ).lower (), raw_provider or "openai" )
768+ llm_provider = (model_provider or "openai" ).lower ()
783769
784770 logger .info (f"llm config: system_prompt_template='{ system_prompt_template } ' user_prompt_template='{ user_prompt_template } ' " )
785771 logger .info (f"llm config: model_name={ model_name } api_host={ api_host } api_key={ api_key } model_provider={ model_provider } llm_provider={ llm_provider } " )
786772
787- llm_provider = provider_mapping .get (llm_provider , llm_provider )
788-
789773 # This is the actual function that will be executed as the node in the graph
790774 def llm_node_callable (state : dict , runtime = None , store = None , ** kwargs ) -> dict :
791775 """
@@ -3262,6 +3246,11 @@ async def _run_browser_use(task: str, mainwin, state: dict | None = None, callin
32623246 from browser_use .browser .profile import BrowserProfile
32633247 from agent .ec_skills .browser_use_extension .extension_tools_service import custom_controller
32643248 # from browser_use.browser.context import BUBrowserContext as BUBrowserContext
3249+
3250+ # Patch navigation timeout to reduce "Page readiness timeout" warnings
3251+ # browser_use hardcodes 4s cross-domain / 2s same-domain, which is too short for many sites
3252+ from agent .ec_skills .browser_use_extension .session_patch import patch_navigation_timeout
3253+ patch_navigation_timeout (cross_domain_timeout = 10.0 , same_domain_timeout = 5.0 )
32653254 log_msg = f"🤖 Executing node Browser Automation node: { node_name } "
32663255 logger .debug (log_msg )
32673256 send_skill_editor_log ("log" , log_msg )
@@ -3491,9 +3480,22 @@ async def _run_browser_use(task: str, mainwin, state: dict | None = None, callin
34913480 from config .app_settings import app_settings
34923481 disable_extensions = app_settings .is_dev_mode
34933482
3494- # Create browser profile with extensions control
3483+ # Create browser profile with extensions control and persistent user_data_dir
34953484 # Note: BrowserProfile uses enable_default_extensions (not disable_extensions)
3496- browser_profile = BrowserProfile (enable_default_extensions = not disable_extensions )
3485+ # Auto-assign persistent user_data_dir so login state survives restarts
3486+ profile_settings = _get_browser_profile_settings (node_profile )
3487+ _bp_user_data_dir = profile_settings .get ('user_data_dir' , '' ) if profile_settings else ''
3488+ if not _bp_user_data_dir :
3489+ from utils .user_path_helper import ensure_user_data_dir
3490+ import re as _re
3491+ _bp_id = profile_settings .get ('id' ) or profile_settings .get ('name' ) or node_profile or 'default'
3492+ _bp_safe_id = _re .sub (r'[^\w\-]' , '_' , str (_bp_id ))
3493+ _bp_user_data_dir = ensure_user_data_dir (subdir = os .path .join ('browser_profiles' , _bp_safe_id ))
3494+ logger .info (f"[BrowserAutomation] Auto-assigned user_data_dir: { _bp_user_data_dir } " )
3495+ browser_profile = BrowserProfile (
3496+ enable_default_extensions = not disable_extensions ,
3497+ user_data_dir = _bp_user_data_dir ,
3498+ )
34973499 logger .info (f"[BrowserAutomation] Extensions { 'disabled (dev mode)' if disable_extensions else 'enabled (production mode)' } " )
34983500
34993501 if browser_profile :
0 commit comments