import json import os import gradio as gr from gradio.components import Component from typing import Any, Dict, Optional from src.webui.webui_manager import WebuiManager from src.utils import config import logging from distutils.util import strtobool from functools import partial logger = logging.getLogger(__name__) def update_model_dropdown(llm_provider, model_type="main"): model_dict = config.main_models if model_type == "main" else config.planning_models if llm_provider in model_dict: return gr.Dropdown(choices=model_dict[llm_provider], value=model_dict[llm_provider][0], interactive=True) else: return gr.Dropdown(choices=[], value="", interactive=True, allow_custom_value=True) async def update_mcp_server(mcp_file: str, webui_manager: WebuiManager): if hasattr(webui_manager, "bu_controller") and webui_manager.bu_controller: await webui_manager.bu_controller.close_mcp_client() webui_manager.bu_controller = None if not mcp_file or not os.path.exists(mcp_file) or not mcp_file.endswith('.json'): return None, gr.update(visible=False) with open(mcp_file, 'r') as f: mcp_server = json.load(f) return json.dumps(mcp_server, indent=2), gr.update(visible=True) async def close_browser_resources(webui_manager: WebuiManager): if webui_manager.bu_current_task and not webui_manager.bu_current_task.done(): webui_manager.bu_current_task.cancel() webui_manager.bu_current_task = None if webui_manager.bu_browser_context: await webui_manager.bu_browser_context.close() webui_manager.bu_browser_context = None if webui_manager.bu_browser: await webui_manager.bu_browser.close() webui_manager.bu_browser = None def create_settings_tab(webui_manager: WebuiManager): agent_tab_components = {} browser_tab_components = {} default_provider = os.getenv("DEFAULT_LLM", "openai") if default_provider not in config.main_models: default_provider = "openai" with gr.Accordion("🤖 LLM & Agent Configuration", open=True): with gr.Row(): llm_provider = gr.Dropdown(choices=list(config.main_models.keys()), label="LLM Provider", value=default_provider, interactive=True) llm_model_name = gr.Dropdown(label="LLM Model Name", choices=config.main_models[default_provider], value=config.main_models[default_provider][0], interactive=True, allow_custom_value=True) with gr.Row(): llm_temperature = gr.Slider(minimum=0.0, maximum=2.0, value=0.6, step=0.1, label="Temperature", interactive=True) use_vision = gr.Checkbox(label="Use Vision", value=True, interactive=True) ollama_num_ctx = gr.Slider(minimum=256, maximum=65536, value=16000, step=1, label="Ollama Context Length", visible=False, interactive=True) with gr.Row(): llm_base_url = gr.Textbox(label="Base URL", value="", placeholder="Optional API endpoint") llm_api_key = gr.Textbox(label="API Key", type="password", value="", placeholder="Leave blank to use .env") with gr.Accordion("📋 System Prompt & Memory", open=False): override_system_prompt = gr.Textbox(label="Override system prompt", lines=3, interactive=True) extend_system_prompt = gr.Textbox(label="Extend system prompt", lines=3, interactive=True) with gr.Accordion("🧠 Planner LLM Configuration", open=False): with gr.Row(): planner_llm_provider = gr.Dropdown(choices=list(config.planning_models.keys()), label="Planner Provider", value=None, interactive=True) planner_llm_model_name = gr.Dropdown(label="Planner Model Name", interactive=True, allow_custom_value=True) with gr.Row(): planner_llm_temperature = gr.Slider(minimum=0.0, maximum=2.0, value=0.6, step=0.1, label="Planner Temp", interactive=True) planner_use_vision = gr.Checkbox(label="Planner Vision", value=False, interactive=True) with gr.Row(): planner_llm_base_url = gr.Textbox(label="Planner Base URL", value="") planner_llm_api_key = gr.Textbox(label="Planner API Key", type="password", value="") with gr.Accordion("🌐 Browser Configuration", open=False): with gr.Row(): use_own_browser = gr.Checkbox(label="Use Own Browser", value=bool(strtobool(os.getenv("USE_OWN_BROWSER", "false"))), interactive=True) keep_browser_open = gr.Checkbox(label="Keep Browser Open", value=bool(strtobool(os.getenv("KEEP_BROWSER_OPEN", "true"))), interactive=True) headless = gr.Checkbox(label="Headless Mode", value=False, interactive=True) disable_security = gr.Checkbox(label="Disable Security", value=False, interactive=True) with gr.Row(): window_w = gr.Number(label="Width", value=1280, interactive=True) window_h = gr.Number(label="Height", value=1100, interactive=True) with gr.Row(): browser_binary_path = gr.Textbox(label="Browser Binary Path", placeholder="Path to Chrome/Edge binary") browser_user_data_dir = gr.Textbox(label="User Data Dir", placeholder="Custom profile path") with gr.Accordion("🛠️ Advanced Settings", open=False): with gr.Row(): max_steps = gr.Slider(minimum=1, maximum=1000, value=100, step=1, label="Max Steps") max_actions = gr.Slider(minimum=1, maximum=100, value=10, step=1, label="Max Actions/Step") with gr.Row(): max_input_tokens = gr.Number(label="Max Input Tokens", value=128000, precision=0) tool_calling_method = gr.Dropdown(label="Tool Calling", value="auto", choices=['function_calling', 'json_mode', 'raw', 'auto', 'tools', "None"]) with gr.Row(): mcp_json_file = gr.File(label="MCP Config (.json)", file_types=[".json"]) mcp_server_config = gr.Textbox(label="MCP JSON", lines=4, visible=False) # --- Navigation Logic --- def update_model_dropdown(provider, type="main"): models = config.main_models if type == "main" else config.planning_models return gr.update(choices=models.get(provider, []), value=models.get(provider, [""])[0]) # Register components to manager using legacy prefixes to keep compatibility with agents # Split them into agent_settings and browser_settings categories legacy_agent_settings = { "llm_provider": llm_provider, "llm_model_name": llm_model_name, "llm_temperature": llm_temperature, "use_vision": use_vision, "ollama_num_ctx": ollama_num_ctx, "llm_base_url": llm_base_url, "llm_api_key": llm_api_key, "override_system_prompt": override_system_prompt, "extend_system_prompt": extend_system_prompt, "planner_llm_provider": planner_llm_provider, "planner_llm_model_name": planner_llm_model_name, "planner_llm_temperature": planner_llm_temperature, "planner_use_vision": planner_use_vision, "planner_llm_base_url": planner_llm_base_url, "planner_llm_api_key": planner_llm_api_key, "max_steps": max_steps, "max_actions": max_actions, "max_input_tokens": max_input_tokens, "tool_calling_method": tool_calling_method, "mcp_json_file": mcp_json_file, "mcp_server_config": mcp_server_config } legacy_browser_settings = { "use_own_browser": use_own_browser, "keep_browser_open": keep_browser_open, "headless": headless, "disable_security": disable_security, "window_w": window_w, "window_h": window_h, "browser_binary_path": browser_binary_path, "browser_user_data_dir": browser_user_data_dir } webui_manager.add_components("agent_settings", legacy_agent_settings) webui_manager.add_components("browser_settings", legacy_browser_settings) # Handlers llm_provider.change(lambda x: gr.update(visible=x == "ollama"), inputs=llm_provider, outputs=ollama_num_ctx) llm_provider.change(lambda p: update_model_dropdown(p, "main"), inputs=[llm_provider], outputs=[llm_model_name]) planner_llm_provider.change(lambda p: update_model_dropdown(p, "planning"), inputs=[planner_llm_provider], outputs=[planner_llm_model_name]) async def mcp_wrapper(f): return await update_mcp_server(f, webui_manager) mcp_json_file.change(fn=mcp_wrapper, inputs=[mcp_json_file], outputs=[mcp_server_config, mcp_server_config]) async def resource_close_wrapper(): await close_browser_resources(webui_manager) for comp in [headless, keep_browser_open, disable_security, use_own_browser]: comp.change(resource_close_wrapper) return legacy_agent_settings, legacy_browser_settings