diff --git a/install-and-run.ps1 b/install-and-run.ps1 index 11821a0..d72a7f7 100644 --- a/install-and-run.ps1 +++ b/install-and-run.ps1 @@ -2,70 +2,70 @@ $OutputEncoding = [System.Text.Encoding]::UTF8 Write-Host "========================================" -ForegroundColor Cyan -Write-Host " AI Client 2 API 快速安装启动脚本" -ForegroundColor Cyan +Write-Host " AI Client 2 API Quick Setup Script" -ForegroundColor Cyan Write-Host "========================================" -ForegroundColor Cyan Write-Host "" -# 处理参数 +# Handle parameters $forcePull = $args -contains "--pull" -# 检查 Git 并拉取 +# Check Git and Pull if ($forcePull) { - Write-Host "[更新] 正在从远程仓库拉取最新代码..." + Write-Host "[UPDATE] Pulling latest code from remote repository..." if (Get-Command git -ErrorAction SilentlyContinue) { git pull if ($LASTEXITCODE -ne 0) { - Write-Warning "Git pull 失败,请检查网络或手动处理冲突。" + Write-Warning "Git pull failed. Please check your network or handle conflicts manually." } else { - Write-Host "[成功] 代码已更新。" -ForegroundColor Green + Write-Host "[SUCCESS] Code updated." -ForegroundColor Green } } else { - Write-Warning "未检测到 Git,跳过代码拉取。" + Write-Warning "Git not detected. Skipping code pull." } } -# 检查 Node.js -Write-Host "[检查] 正在检查Node.js是否已安装..." +# Check Node.js +Write-Host "[CHECK] Checking if Node.js is installed..." if (-not (Get-Command node -ErrorAction SilentlyContinue)) { - Write-Host "[错误] 未检测到Node.js,请先安装Node.js (https://nodejs.org/)" -ForegroundColor Red + Write-Host "[ERROR] Node.js not detected. Please install Node.js (https://nodejs.org/)" -ForegroundColor Red Pause exit 1 } $nodeVersion = node --version -Write-Host "[成功] Node.js已安装,版本: $nodeVersion" -ForegroundColor Green +Write-Host "[SUCCESS] Node.js installed, version: $nodeVersion" -ForegroundColor Green -# 检查 package.json +# Check package.json if (-not (Test-Path "package.json")) { - Write-Host "[错误] 未找到package.json文件,请确保在项目根目录下运行此脚本" -ForegroundColor Red + Write-Host "[ERROR] package.json not found. Please ensure you are running this script from the project root." -ForegroundColor Red Pause exit 1 } -# 确定包管理器 +# Determine package manager $pkgManager = if (Get-Command pnpm -ErrorAction SilentlyContinue) { "pnpm" } else { "npm" } -Write-Host "[安装] 正在使用 $pkgManager 安装/更新依赖..." -ForegroundColor Cyan +Write-Host "[INSTALL] Installing/updating dependencies using $pkgManager..." -ForegroundColor Cyan & $pkgManager install if ($LASTEXITCODE -ne 0) { - Write-Host "[错误] 依赖安装失败,请检查网络连接。" -ForegroundColor Red + Write-Host "[ERROR] Dependency installation failed. Please check your network connection." -ForegroundColor Red Pause exit 1 } -# 检查主文件 +# Check master file if (-not (Test-Path "src\core\master.js")) { - Write-Host "[错误] 未找到 src\core\master.js 文件" -ForegroundColor Red + Write-Host "[ERROR] src\core\master.js not found." -ForegroundColor Red Pause exit 1 } Write-Host "" Write-Host "========================================" -ForegroundColor Green -Write-Host " 启动 AIClient2API 服务器..." -ForegroundColor Green +Write-Host " Starting AIClient2API Server..." -ForegroundColor Green Write-Host "========================================" -ForegroundColor Green -Write-Host "服务器将在 http://localhost:3000 启动" -Write-Host "按 Ctrl+C 停止服务器" +Write-Host "Server will start at http://localhost:3000" +Write-Host "Press Ctrl+C to stop the server" Write-Host "" node src\core\master.js diff --git a/src/providers/gemini/antigravity-core.js b/src/providers/gemini/antigravity-core.js index c66fd2e..e1da3dc 100644 --- a/src/providers/gemini/antigravity-core.js +++ b/src/providers/gemini/antigravity-core.js @@ -40,6 +40,7 @@ const ANTIGRAVITY_SYSTEM_PROMPT = `You are Antigravity, a powerful agentic AI co // Thinking 配置相关常量 const DEFAULT_THINKING_MIN = 1024; const DEFAULT_THINKING_MAX = 100000; +const FALLBACK_THINKING_SIGNATURE = "skip_thought_signature_validator_fallback"; // 获取 Antigravity 模型列表 const ANTIGRAVITY_MODELS = getProviderModels(MODEL_PROVIDER.ANTIGRAVITY); @@ -199,16 +200,16 @@ function normalizeAntigravityThinking(modelName, payload, isClaudeModel) { let normalizedBudget = normalizeThinkingBudget(modelName, budget); - // 对于 Claude 模型,确保 thinking budget < max_tokens + // 确保 thinking budget < max_tokens (对所有模型生效,不仅是 Claude) + const maxTokens = payload?.request?.generationConfig?.maxOutputTokens || payload?.request?.generationConfig?.max_output_tokens; + if (maxTokens && maxTokens > 0 && normalizedBudget >= maxTokens) { + normalizedBudget = Math.max(0, maxTokens - 1); + } + + // 如果是 Claude 模型,检查最小 budget if (isClaudeModel) { - const maxTokens = payload?.request?.generationConfig?.maxOutputTokens; - if (maxTokens && maxTokens > 0 && normalizedBudget >= maxTokens) { - normalizedBudget = maxTokens - 1; - } - - // 检查最小 budget const minBudget = DEFAULT_THINKING_MIN; - if (normalizedBudget >= 0 && normalizedBudget < minBudget) { + if (normalizedBudget >= 0 && normalizedBudget < minBudget && normalizedBudget !== -1) { // Budget 低于最小值,移除 thinking 配置 delete payload.request.generationConfig.thinkingConfig; return payload; @@ -599,7 +600,7 @@ function toGeminiApiResponse(antigravityResponse) { } /** - * 确保请求体中的内容部分都有角色属性 + * 确保请求体中的内容部分都有角色属性,并修复历史记录中的思考签名 * @param {Object} requestBody - 请求体 * @returns {Object} 处理后的请求体 */ @@ -674,6 +675,24 @@ function ensureRolesInContents(requestBody, modelName) { if (!content.role) { content.role = 'user'; } + + // [FIX] 修复历史记录中的思考块,确保有签名 (messages.1.content.0.thinking.signature 报错修复) + if (content.parts && Array.isArray(content.parts)) { + content.parts.forEach(part => { + if (part && part.thought === true) { + if (!part.thoughtSignature && !part.thought_signature) { + part.thoughtSignature = FALLBACK_THINKING_SIGNATURE; + } + + // [FIX] 额外增加一个 'thinking' 对象以适配某些 Antigravity 内部验证逻辑 + if (!part.thinking) { + part.thinking = { + signature: part.thoughtSignature || part.thought_signature || FALLBACK_THINKING_SIGNATURE + }; + } + } + }); + } }); } @@ -696,25 +715,6 @@ export class AntigravityApiService { timeout: 120000, }); - // 检查是否需要使用代理 - const proxyConfig = getGoogleAuthProxyConfig(config, config.MODEL_PROVIDER || MODEL_PROVIDER.ANTIGRAVITY); - - // 配置 OAuth2Client 使用自定义的 HTTP agent - const oauth2Options = { - clientId: OAUTH_CLIENT_ID, - clientSecret: OAUTH_CLIENT_SECRET, - }; - - if (proxyConfig) { - oauth2Options.transporterOptions = proxyConfig; - logger.info('[Antigravity] Using proxy for OAuth2Client'); - } else { - oauth2Options.transporterOptions = { - agent: this.httpsAgent, - }; - } - - this.authClient = new OAuth2Client(oauth2Options); this.availableModels = []; this.isInitialized = false; @@ -730,6 +730,32 @@ export class AntigravityApiService { // 保存代理配置供后续使用 this.proxyConfig = getProxyConfigForProvider(config, config.MODEL_PROVIDER || MODEL_PROVIDER.ANTIGRAVITY); + + // 检查是否需要使用代理 + const proxyConfig = getGoogleAuthProxyConfig(config, config.MODEL_PROVIDER || MODEL_PROVIDER.ANTIGRAVITY); + + // 配置 OAuth2Client 使用自定义的 HTTP agent + const oauth2Options = { + clientId: OAUTH_CLIENT_ID, + clientSecret: OAUTH_CLIENT_SECRET, + }; + + if (proxyConfig) { + oauth2Options.transporterOptions = proxyConfig; + logger.info('[Antigravity] Using proxy for OAuth2Client'); + } else { + // 根据 base URL 判断使用 http 还是 https agent + const firstBaseURL = this.baseURLs && this.baseURLs.length > 0 ? this.baseURLs[0] : ''; + const useHttp = firstBaseURL.startsWith('http://'); + oauth2Options.transporterOptions = { + agent: useHttp ? this.httpAgent : this.httpsAgent, + }; + if (useHttp) { + logger.info('[Antigravity] Using HTTP agent for OAuth2Client'); + } + } + + this.authClient = new OAuth2Client(oauth2Options); } _applySidecar(requestOptions) { diff --git a/src/providers/gemini/gemini-core.js b/src/providers/gemini/gemini-core.js index b4f386c..57abb77 100644 --- a/src/providers/gemini/gemini-core.js +++ b/src/providers/gemini/gemini-core.js @@ -287,24 +287,6 @@ export class GeminiApiService { maxFreeSockets: 5, timeout: 120000, }); - - // 检查是否需要使用代理 - const proxyConfig = getGoogleAuthProxyConfig(config, config.MODEL_PROVIDER || MODEL_PROVIDER.GEMINI_CLI); - - // 配置 OAuth2Client 使用自定义的 HTTP agent - const oauth2Options = { - clientId: OAUTH_CLIENT_ID, - clientSecret: OAUTH_CLIENT_SECRET, - }; - - if (proxyConfig) { - oauth2Options.transporterOptions = proxyConfig; - logger.info('[Gemini] Using proxy for OAuth2Client'); - } else { - oauth2Options.transporterOptions = { - agent: this.httpsAgent, - }; - } this.authClient = new OAuth2Client(oauth2Options); this.availableModels = []; @@ -322,6 +304,29 @@ export class GeminiApiService { // 保存代理配置供后续使用 this.proxyConfig = getProxyConfigForProvider(config, config.MODEL_PROVIDER || MODEL_PROVIDER.GEMINI_CLI); + + // 检查是否需要使用代理 + const proxyConfig = getGoogleAuthProxyConfig(config, config.MODEL_PROVIDER || MODEL_PROVIDER.GEMINI_CLI); + + // 配置 OAuth2Client 使用自定义的 HTTP agent + const oauth2Options = { + clientId: OAUTH_CLIENT_ID, + clientSecret: OAUTH_CLIENT_SECRET, + }; + + if (proxyConfig) { + oauth2Options.transporterOptions = proxyConfig; + logger.info('[Gemini] Using proxy for OAuth2Client'); + } else { + // 根据 base URL 判断使用 http 还是 https agent + const useHttp = this.codeAssistEndpoint && this.codeAssistEndpoint.startsWith('http://'); + oauth2Options.transporterOptions = { + agent: useHttp ? this.httpAgent : this.httpsAgent, + }; + if (useHttp) { + logger.info('[Gemini] Using HTTP agent for OAuth2Client'); + } + } } async initialize() { diff --git a/src/providers/openai/qwen-core.js b/src/providers/openai/qwen-core.js index e2621ca..f84a679 100644 --- a/src/providers/openai/qwen-core.js +++ b/src/providers/openai/qwen-core.js @@ -50,6 +50,38 @@ export const qwenOAuth2Events = new EventEmitter(); // --- Helper Functions --- +/** + * Qwen 默认系统提示词 + */ +const QWEN_DEFAULT_SYSTEM_PROMPT = "You are a helpful assistant. You are Qwen, a large language model trained by Alibaba."; + +/** + * 应用 Qwen 默认系统提示词逻辑 + * @param {Object} requestBody - OpenAI 格式的请求体 + * @returns {Object} 处理后的请求体 + */ +function applyQwenDefaultSystemPrompt(requestBody) { + if (!requestBody || !requestBody.messages || !Array.isArray(requestBody.messages)) { + return requestBody; + } + + // 检查是否已有系统提示词 (role 为 system 或 developer) + const hasSystemPrompt = requestBody.messages.some(msg => + msg.role === 'system' || msg.role === 'developer' + ); + + // 如果没有系统提示词,则在消息列表最前面插入默认提示词 + if (!hasSystemPrompt) { + requestBody.messages.unshift({ + role: 'system', + content: QWEN_DEFAULT_SYSTEM_PROMPT + }); + logger.info('[Qwen Auth] 已应用默认系统提示词'); + } + + return requestBody; +} + // 封装公共的 await fetch 方法 async function commonFetch(url, options = {}, useSystemProxy = false) { const defaultOptions = { @@ -580,7 +612,7 @@ export class QwenApiService { this.currentAxiosInstance = axios.create(axiosConfig); // Process message content before sending the request - const processedBody = body;//this.processMessageContent(body); + const processedBody = applyQwenDefaultSystemPrompt(body); // Check if model in body is in QWEN_MODEL_LIST, if not, use the first model's id if (processedBody.model && !QWEN_MODEL_LIST.some(model => model.id === processedBody.model)) {