diff --git a/.env.example b/.env.example index de40df7..71069df 100644 --- a/.env.example +++ b/.env.example @@ -20,6 +20,9 @@ THINKING_BUDGET_MAP={"gemini-2.5-flash-preview-04-17": 4000} IMAGE_MODELS=["gemini-2.0-flash-exp"] SEARCH_MODELS=["gemini-2.0-flash-exp","gemini-2.0-pro-exp"] FILTERED_MODELS=["gemini-1.0-pro-vision-latest", "gemini-pro-vision", "chat-bison-001", "text-bison-001", "embedding-gecko-001"] +# 是否启用网址上下文,默认启用 +URL_CONTEXT_ENABLED=true +URL_CONTEXT_MODELS=["gemini-2.5-pro","gemini-2.5-flash","gemini-2.5-flash-lite","gemini-2.0-flash","gemini-2.0-flash-live-001"] TOOLS_CODE_EXECUTION_ENABLED=false SHOW_SEARCH_LINK=true SHOW_THINKING_PROCESS=true diff --git a/README.md b/README.md index 2c3335d..7fbf24c 100644 --- a/README.md +++ b/README.md @@ -192,6 +192,8 @@ If you want to run the source code directly locally for development or testing, | `THINKING_MODELS` | Optional, list of models that support thinking functions | `[]` | | `THINKING_BUDGET_MAP` | Optional, thinking function budget mapping (model_name:budget_value) | `{}` | | `URL_NORMALIZATION_ENABLED` | Optional, whether to enable intelligent URL routing mapping | `false` | +| `URL_CONTEXT_ENABLED` | Optional, whether to enable URL context understanding | `false` | +| `URL_CONTEXT_MODELS` | Optional, list of models that support URL context understanding | `[]` | | `BASE_URL` | Optional, Gemini API base URL, no modification needed by default | `https://generativelanguage.googleapis.com/v1beta` | | `MAX_FAILURES` | Optional, number of times a single key is allowed to fail | `3` | | `MAX_RETRIES` | Optional, maximum number of retries for failed API requests | `3` | diff --git a/README_ZH.md b/README_ZH.md index bebfa50..14b7264 100644 --- a/README_ZH.md +++ b/README_ZH.md @@ -185,6 +185,8 @@ app/ | `THINKING_MODELS` | 可选,支持思考功能的模型列表 | `[]` | | `THINKING_BUDGET_MAP` | 可选,思考功能预算映射 (模型名:预算值) | `{}` | | `URL_NORMALIZATION_ENABLED` | 可选,是否启用智能路由映射功能 | `false` | +| `URL_CONTEXT_ENABLED` | 可选,是否启用URL上下文理解功能 | `false` | +| `URL_CONTEXT_MODELS` | 可选,支持URL上下文理解功能的模型列表 | `[]` | | `BASE_URL` | 可选,Gemini API 基础 URL,默认无需修改 | `https://generativelanguage.googleapis.com/v1beta` | | `MAX_FAILURES` | 可选,允许单个key失败的次数 | `3` | | `MAX_RETRIES` | 可选,API 请求失败时的最大重试次数 | `3` | diff --git a/app/config/config.py b/app/config/config.py index fd5aec6..096fcd7 100644 --- a/app/config/config.py +++ b/app/config/config.py @@ -75,6 +75,9 @@ class Settings(BaseSettings): IMAGE_MODELS: List[str] = ["gemini-2.0-flash-exp"] FILTERED_MODELS: List[str] = DEFAULT_FILTER_MODELS TOOLS_CODE_EXECUTION_ENABLED: bool = False + # 是否启用网址上下文 + URL_CONTEXT_ENABLED: bool = True + URL_CONTEXT_MODELS: List[str] = ["gemini-2.5-pro","gemini-2.5-flash","gemini-2.5-flash-lite","gemini-2.0-flash","gemini-2.0-flash-live-001"] SHOW_SEARCH_LINK: bool = True SHOW_THINKING_PROCESS: bool = True THINKING_MODELS: List[str] = [] diff --git a/app/service/chat/gemini_chat_service.py b/app/service/chat/gemini_chat_service.py index 7348812..149dda5 100644 --- a/app/service/chat/gemini_chat_service.py +++ b/app/service/chat/gemini_chat_service.py @@ -119,15 +119,32 @@ def _build_tools(model: str, payload: Dict[str, Any]) -> List[Dict[str, Any]]: tool["codeExecution"] = {} if model.endswith("-search"): tool["googleSearch"] = {} - + + real_model = _get_real_model(model) + if real_model in settings.URL_CONTEXT_MODELS and settings.URL_CONTEXT_ENABLED: + tool["urlContext"] = {} + # 解决 "Tool use with function calling is unsupported" 问题 if tool.get("functionDeclarations"): tool.pop("googleSearch", None) tool.pop("codeExecution", None) + tool.pop("urlContext", None) return [tool] if tool else [] +def _get_real_model(model: str) -> str: + if model.endswith("-search"): + model = model[:-7] + if model.endswith("-image"): + model = model[:-6] + if model.endswith("-non-thinking"): + model = model[:-13] + if "-search" in model and "-non-thinking" in model: + model = model[:-20] + return model + + def _get_safety_settings(model: str) -> List[Dict[str, str]]: """获取安全设置""" if model == "gemini-2.0-flash-exp": diff --git a/app/service/chat/openai_chat_service.py b/app/service/chat/openai_chat_service.py index 526e3c2..47c6bd3 100644 --- a/app/service/chat/openai_chat_service.py +++ b/app/service/chat/openai_chat_service.py @@ -87,6 +87,10 @@ def _build_tools( if model.endswith("-search"): tool["googleSearch"] = {} + + real_model = _get_real_model(model) + if real_model in settings.URL_CONTEXT_MODELS and settings.URL_CONTEXT_ENABLED: + tool["urlContext"] = {} # 将 request 中的 tools 合并到 tools 中 if request.tools: @@ -126,10 +130,23 @@ def _build_tools( if tool.get("functionDeclarations"): tool.pop("googleSearch", None) tool.pop("codeExecution", None) + tool.pop("urlContext",None) return [tool] if tool else [] +def _get_real_model(model: str) -> str: + if model.endswith("-search"): + model = model[:-7] + if model.endswith("-image"): + model = model[:-6] + if model.endswith("-non-thinking"): + model = model[:-13] + if "-search" in model and "-non-thinking" in model: + model = model[:-20] + return model + + def _get_safety_settings(model: str) -> List[Dict[str, str]]: """获取安全设置""" # if ( diff --git a/app/service/chat/vertex_express_chat_service.py b/app/service/chat/vertex_express_chat_service.py index fea7929..50dea73 100644 --- a/app/service/chat/vertex_express_chat_service.py +++ b/app/service/chat/vertex_express_chat_service.py @@ -97,15 +97,32 @@ def _build_tools(model: str, payload: Dict[str, Any]) -> List[Dict[str, Any]]: tool["codeExecution"] = {} if model.endswith("-search"): tool["googleSearch"] = {} + + real_model = _get_real_model(model) + if real_model in settings.URL_CONTEXT_MODELS and settings.URL_CONTEXT_ENABLED: + tool["urlContext"] = {} # 解决 "Tool use with function calling is unsupported" 问题 if tool.get("functionDeclarations"): tool.pop("googleSearch", None) tool.pop("codeExecution", None) + tool.pop("urlContext", None) return [tool] if tool else [] +def _get_real_model(model: str) -> str: + if model.endswith("-search"): + model = model[:-7] + if model.endswith("-image"): + model = model[:-6] + if model.endswith("-non-thinking"): + model = model[:-13] + if "-search" in model and "-non-thinking" in model: + model = model[:-20] + return model + + def _get_safety_settings(model: str) -> List[Dict[str, str]]: """获取安全设置""" if model == "gemini-2.0-flash-exp": diff --git a/app/static/js/config_editor.js b/app/static/js/config_editor.js index 858078d..8cbeb60 100644 --- a/app/static/js/config_editor.js +++ b/app/static/js/config_editor.js @@ -13,7 +13,7 @@ const SHOW_CLASS = "show"; // For modals const API_KEY_REGEX = /AIzaSy\S{33}/g; const PROXY_REGEX = /(?:https?|socks5):\/\/(?:[^:@\/]+(?::[^@\/]+)?@)?(?:[^:\/\s]+)(?::\d+)?/g; -const VERTEX_API_KEY_REGEX = /AQ\.[a-zA-Z0-9_]{50}/g; // 新增 Vertex Express API Key 正则 +const VERTEX_API_KEY_REGEX = /AQ\.[a-zA-Z0-9_\-]{50}/g; // 新增 Vertex Express API Key 正则 const MASKED_VALUE = "••••••••"; // DOM Elements - Global Scope for frequently accessed elements @@ -713,7 +713,13 @@ async function initConfig() { config.SAFETY_SETTINGS = []; // 默认为空数组 } // --- 结束:处理 SAFETY_SETTINGS 默认值 --- - + if (typeof config.URL_CONTEXT_ENABLED === "undefined") { + config.URL_CONTEXT_ENABLED = true; + } + if (!config.URL_CONTEXT_MODELS || !Array.isArray(config.URL_CONTEXT_MODELS)) { + config.URL_CONTEXT_MODELS = []; + } + // --- 新增:处理自动删除错误日志配置的默认值 --- if (typeof config.AUTO_DELETE_ERROR_LOGS_ENABLED === "undefined") { config.AUTO_DELETE_ERROR_LOGS_ENABLED = false; diff --git a/app/templates/config_editor.html b/app/templates/config_editor.html index c142fb2..c2d2210 100644 --- a/app/templates/config_editor.html +++ b/app/templates/config_editor.html @@ -1421,6 +1421,59 @@ endblock %} {% block head_extra_styles %} + +