diff --git a/README-JA.md b/README-JA.md index 227c362..529829a 100644 --- a/README-JA.md +++ b/README-JA.md @@ -47,6 +47,16 @@ AICodeMirror の本プロジェクトへのスポンサーシップに感謝します!AICodeMirror は、Claude Code / Codex / Gemini CLI 向けに公式の高安定性リレーサービスを提供しており、企業レベルの同時実行性、迅速な請求書発行、24時間365日の専用技術サポートを備えています。Claude Code / Codex / Gemini の公式チャンネルを、元の価格の 38% / 2% / 9% で利用でき、チャージ時にはさらなる割引もあります!AICodeMirror は AIClient-2-API ユーザーに特別な特典を提供しています:このリンクから登録すると、初回チャージが 20% オフになり、法人のお客様は最大 25% オフになります! + + + + LingtrueAPI Sponsor + + + + LingtrueAPIによる本プロジェクトへのスポンサーに感謝します!LingtrueAPIは世界的な大規模言語モデルAPI中継プラットフォームであり、Claude opus 4.6、GPT 5.4、Gemini 3.1 proなど各種モデルのAPI呼び出しサービスを提供しています。低コスト、高安定性で世界中のAI機能に接続し、生産性を最大化することを目指しています。LingtrueAPIは本ソフトウェアユーザー向けに特別優遇を提供しています。このリンクから登録し、初回チャージ時に「LingtrueAPI」のクーポンコードを入力すると、10%オフで利用できます。 + + Sponsor Contact diff --git a/README-ZH.md b/README-ZH.md index e7e2ddf..048bf2f 100644 --- a/README-ZH.md +++ b/README-ZH.md @@ -46,6 +46,16 @@ 感谢 AICodeMirror 赞助本项目!AICodeMirror 为 Claude Code / Codex / Gemini CLI 提供官方高稳定性中转服务,具备企业级并发能力、快速开票和 7/24 专属技术支持。Claude Code / Codex / Gemini 官方渠道价格仅为原价的 38% / 2% / 9%,充值还有额外优惠!AICodeMirror 为 AIClient-2-API 用户提供专属福利:通过此链接注册即可享受首充 8折(20% off) 优惠,企业客户最高可享 75折(25% off)! + + + + LingtrueAPI Sponsor + + + + 感谢 LingtrueAPI 对本项目的赞助!LingtrueAPI 是一家全球大模型API中转服务平台,提供Claude opus 4.6、GPT 5.4、Gemini 3.1 pro等多种模型API调用服务,致力于让用户以低成本、高稳定性链接全球AI能力,最大化生产效率。LingtrueAPI为本软件用户提供了特别优惠:通过此链接注册并在首次充值时输入 LingtrueAPI 优惠码即可享受 9折优惠。 + + Sponsor Contact diff --git a/README.md b/README.md index 8c0895e..3aef936 100644 --- a/README.md +++ b/README.md @@ -47,6 +47,16 @@ Thanks to AICodeMirror for sponsoring this project! AICodeMirror provides official high-stability relay services for Claude Code / Codex / Gemini CLI, with enterprise-grade concurrency, fast invoicing, and 24/7 dedicated technical support. Claude Code / Codex / Gemini official channels at 38% / 2% / 9% of original price, with extra discounts on top-ups! AICodeMirror offers special benefits for AIClient-2-API users: register via this link to enjoy 20% off your first top-up, and enterprise customers can get up to 25% off! + + + + LingtrueAPI Sponsor + + + + Thanks to LingtrueAPI for its sponsorship of this project! LingtrueAPI is a global large-model API intermediary service platform that offers API calling services for various models such as Claude opus 4.6, GPT 5.4, and Gemini 3.1 pro. It is committed to enabling users to connect to global AI capabilities at low cost and with high stability, maximizing production efficiency. LingtrueAPI provides special discounts for users of this software: register using this link and enter the LingtrueAPI promo code when making the first recharge to enjoy a 10% discount. + + Sponsor Contact diff --git a/VERSION b/VERSION index d463c35..ccc99d0 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -2.12.2.2 +2.12.3 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)) { diff --git a/static/lingtrueapi.png b/static/lingtrueapi.png new file mode 100644 index 0000000..0829a7c Binary files /dev/null and b/static/lingtrueapi.png differ