diff --git a/unified-server.js b/unified-server.js index 5b691f3..80a8dcd 100644 --- a/unified-server.js +++ b/unified-server.js @@ -1344,6 +1344,54 @@ class RequestHandler { } } + // 强制联网搜索和URL上下文 (Native Google Format) + if ( + (this.serverSystem.forceWebSearch || this.serverSystem.forceUrlContext) && + req.method === "POST" && + bodyObj && + bodyObj.contents + ) { + if (!bodyObj.tools) { + bodyObj.tools = []; + } + + const toolsToAdd = []; + + // 处理 Google Search + if (this.serverSystem.forceWebSearch) { + const hasSearch = bodyObj.tools.some((t) => t.googleSearch); + if (!hasSearch) { + bodyObj.tools.push({googleSearch: {}}); + toolsToAdd.push("googleSearch"); + } else { + this.logger.info( + `[Proxy] ✅ (Google原生格式) 检测到客户端自带联网搜索,跳过强制注入。` + ); + } + } + + // 处理 URL Context + if (this.serverSystem.forceUrlContext) { + const hasUrlContext = bodyObj.tools.some((t) => t.urlContext); + if (!hasUrlContext) { + bodyObj.tools.push({urlContext: {}}); + toolsToAdd.push("urlContext"); + } else { + this.logger.info( + `[Proxy] ✅ (Google原生格式) 检测到客户端自带网址上下文,跳过强制注入。` + ); + } + } + + if (toolsToAdd.length > 0) { + this.logger.info( + `[Proxy] ⚠️ (Google原生格式) 强制功能已启用,正在注入工具: [${toolsToAdd.join( + ", " + )}]` + ); + } + } + let requestBody = ""; if (bodyObj) { requestBody = JSON.stringify(bodyObj); @@ -1912,6 +1960,43 @@ class RequestHandler { { category: "HARM_CATEGORY_DANGEROUS_CONTENT", threshold: "BLOCK_NONE" }, ]; + // 强制联网搜索和URL上下文 + if ( + (this.serverSystem.forceWebSearch || this.serverSystem.forceUrlContext) + ) { + if (!googleRequest.tools) { + googleRequest.tools = []; + } + + const toolsToAdd = []; + + // 处理 Google Search + if (this.serverSystem.forceWebSearch) { + const hasSearch = googleRequest.tools.some((t) => t.googleSearch); + if (!hasSearch) { + googleRequest.tools.push({googleSearch: {}}); + toolsToAdd.push("googleSearch"); + } + } + + // 处理 URL Context + if (this.serverSystem.forceUrlContext) { + const hasUrlContext = googleRequest.tools.some((t) => t.urlContext); + if (!hasUrlContext) { + googleRequest.tools.push({urlContext: {}}); + toolsToAdd.push("urlContext"); + } + } + + if (toolsToAdd.length > 0) { + this.logger.info( + `[Adapter] ⚠️ 强制功能已启用,正在注入工具: [${toolsToAdd.join( + ", " + )}]` + ); + } + } + this.logger.info("[Adapter] 翻译完成。"); return googleRequest; } @@ -2021,7 +2106,9 @@ class ProxyServerSystem extends EventEmitter { this._loadConfiguration(); // 这个函数会执行下面的_loadConfiguration this.streamingMode = this.config.streamingMode; - this.forceThinking = false; + this.forceThinking = process.env.FORCE_THINKING === "true"; + this.forceWebSearch = process.env.FORCE_WEB_SEARCH === "true"; + this.forceUrlContext = process.env.FORCE_URL_CONTEXT === "true"; this.authSource = new AuthSource(this.logger); this.browserManager = new BrowserManager( @@ -2485,6 +2572,12 @@ class ProxyServerSystem extends EventEmitter { 强制推理: ${ this.forceThinking ? "✅ 已启用" : "❌ 已关闭" } +强制联网: ${ + this.forceWebSearch ? "✅ 已启用" : "❌ 已关闭" + } +强制网址上下文: ${ + this.forceUrlContext ? "✅ 已启用" : "❌ 已关闭" + } 立即切换 (状态码): ${ config.immediateSwitchStatusCodes.length > 0 ? `[${config.immediateSwitchStatusCodes.join(", ")}]` @@ -2515,6 +2608,8 @@ class ProxyServerSystem extends EventEmitter { + +
@@ -2535,6 +2630,8 @@ class ProxyServerSystem extends EventEmitter { '--- 服务配置 ---\\n' + '流模式: ' + data.status.streamingMode + '\\n' + '强制推理: ' + data.status.forceThinking + '\\n' + + '强制联网: ' + data.status.forceWebSearch + '\\n' + + '强制网址上下文: ' + data.status.forceUrlContext + '\\n' + '立即切换 (状态码): ' + data.status.immediateSwitchStatusCodes + '\\n' + 'API 密钥: ' + data.status.apiKeySource + '\\n' + '--- 账号状态 ---\\n' + @@ -2602,6 +2699,24 @@ class ProxyServerSystem extends EventEmitter { .catch(err => alert('设置失败: ' + err)); } + function toggleForceWebSearch() { + fetch('/api/toggle-force-web-search', { + method: 'POST', + headers: { 'Content-Type': 'application/json' } + }) + .then(res => res.text()).then(data => { alert(data); updateContent(); }) + .catch(err => alert('设置失败: ' + err)); + } + + function toggleForceUrlContext() { + fetch('/api/toggle-force-url-context', { + method: 'POST', + headers: { 'Content-Type': 'application/json' } + }) + .then(res => res.text()).then(data => { alert(data); updateContent(); }) + .catch(err => alert('设置失败: ' + err)); + } + document.addEventListener('DOMContentLoaded', () => { updateContent(); setInterval(updateContent, 5000); @@ -2633,6 +2748,8 @@ class ProxyServerSystem extends EventEmitter { status: { streamingMode: `${this.streamingMode} (仅启用流式传输时生效)`, forceThinking: this.forceThinking ? "✅ 已启用" : "❌ 已关闭", + forceWebSearch: this.forceWebSearch ? "✅ 已启用" : "❌ 已关闭", + forceUrlContext: this.forceUrlContext ? "✅ 已启用" : "❌ 已关闭", browserConnected: !!browserManager.browser, immediateSwitchStatusCodes: config.immediateSwitchStatusCodes.length > 0 @@ -2720,6 +2837,20 @@ class ProxyServerSystem extends EventEmitter { res.status(200).send(`强制推理模式: ${statusText}`); }); + app.post("/api/toggle-force-web-search", isAuthenticated, (req, res) => { + this.forceWebSearch = !this.forceWebSearch; + const statusText = this.forceWebSearch ? "已启用" : "已关闭"; + this.logger.info(`[WebUI] 强制联网搜索开关已切换为: ${statusText}`); + res.status(200).send(`强制联网搜索: ${statusText}`); + }); + + app.post("/api/toggle-force-url-context", isAuthenticated, (req, res) => { + this.forceUrlContext = !this.forceUrlContext; + const statusText = this.forceUrlContext ? "已启用" : "已关闭"; + this.logger.info(`[WebUI] 强制网址上下文开关已切换为: ${statusText}`); + res.status(200).send(`强制网址上下文: ${statusText}`); + }); + app.use(this._createAuthMiddleware()); app.get("/v1/models", (req, res) => { @@ -2781,4 +2912,3 @@ if (require.main === module) { } module.exports = { ProxyServerSystem, BrowserManager, initializeServer }; -