feat: 更新版本至2.12.3并修复多个问题

更新项目版本至2.12.3,新增赞助商LingtrueAPI信息至README文档。修复Qwen提供商缺少系统提示词时自动添加默认提示词的问题。优化Gemini和Antigravity提供商的OAuth2Client代理配置逻辑,根据baseURL自动选择HTTP/HTTPS agent。修复Antigravity提供商中thinking budget逻辑及历史记录中思考签名缺失的问题。将Windows安装脚本翻译为英文。
This commit is contained in:
hex2077 2026-04-03 23:21:32 +08:00
parent 0c9d52f537
commit 1570fbb096
9 changed files with 162 additions and 69 deletions

View file

@ -47,6 +47,16 @@
AICodeMirror の本プロジェクトへのスポンサーシップに感謝しますAICodeMirror は、Claude Code / Codex / Gemini CLI 向けに公式の高安定性リレーサービスを提供しており、企業レベルの同時実行性、迅速な請求書発行、24時間365日の専用技術サポートを備えています。Claude Code / Codex / Gemini の公式チャンネルを、元の価格の 38% / 2% / 9% で利用でき、チャージ時にはさらなる割引もありますAICodeMirror は AIClient-2-API ユーザーに特別な特典を提供しています:<a href="https://www.aicodemirror.com/register?invitecode=5BUE62">このリンクから登録</a>すると、初回チャージが <strong>20% オフ</strong>になり、法人のお客様は最大 25% オフになります!
</td>
</tr>
<tr>
<td width="25%" align="center" valign="middle">
<a href="https://www.lingtrue.com/register?aff=MP34">
<img src="static/lingtrueapi.png" alt="LingtrueAPI Sponsor" width="180">
</a>
</td>
<td width="75%" align="left" valign="middle">
LingtrueAPIによる本プロジェクトへのスポンサーに感謝しますLingtrueAPIは世界的な大規模言語モデルAPI中継プラットフォームであり、Claude opus 4.6、GPT 5.4、Gemini 3.1 proなど各種モデルのAPI呼び出しサービスを提供しています。低コスト、高安定性で世界中のAI機能に接続し、生産性を最大化することを目指しています。LingtrueAPIは本ソフトウェアユーザー向けに特別優遇を提供しています。<a href="https://www.lingtrue.com/register?aff=MP34">このリンクから登録</a>し、初回チャージ時に「<strong>LingtrueAPI</strong>」のクーポンコードを入力すると、<strong>10%オフ</strong>で利用できます。
</td>
</tr>
<tr>
<td width="25%" align="center" valign="middle">
<img src="static/wechat.png" alt="Sponsor Contact" width="150">

View file

@ -46,6 +46,16 @@
感谢 AICodeMirror 赞助本项目AICodeMirror 为 Claude Code / Codex / Gemini CLI 提供官方高稳定性中转服务,具备企业级并发能力、快速开票和 7/24 专属技术支持。Claude Code / Codex / Gemini 官方渠道价格仅为原价的 38% / 2% / 9%充值还有额外优惠AICodeMirror 为 AIClient-2-API 用户提供专属福利:<a href="https://www.aicodemirror.com/register?invitecode=5BUE62">通过此链接注册</a>即可享受首充 <strong>8折20% off</strong> 优惠,企业客户最高可享 75折25% off
</td>
</tr>
<tr>
<td width="25%" align="center" valign="middle">
<a href="https://www.lingtrue.com/register?aff=MP34">
<img src="static/lingtrueapi.png" alt="LingtrueAPI Sponsor" width="180">
</a>
</td>
<td width="75%" align="left" valign="middle">
感谢 LingtrueAPI 对本项目的赞助LingtrueAPI 是一家全球大模型API中转服务平台提供Claude opus 4.6、GPT 5.4、Gemini 3.1 pro等多种模型API调用服务致力于让用户以低成本、高稳定性链接全球AI能力,最大化生产效率。LingtrueAPI为本软件用户提供了特别优惠<a href="https://www.lingtrue.com/register?aff=MP34">通过此链接注册</a>并在首次充值时输入 <strong>LingtrueAPI</strong> 优惠码即可享受 <strong>9折优惠</strong>
</td>
</tr>
<tr>
<td width="25%" align="center" valign="middle">
<img src="static/wechat.png" alt="Sponsor Contact" width="150">

View file

@ -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: <a href="https://www.aicodemirror.com/register?invitecode=5BUE62">register via this link</a> to enjoy <strong>20% off</strong> your first top-up, and enterprise customers can get up to 25% off!
</td>
</tr>
<tr>
<td width="25%" align="center" valign="middle">
<a href="https://www.lingtrue.com/register?aff=MP34">
<img src="static/lingtrueapi.png" alt="LingtrueAPI Sponsor" width="180">
</a>
</td>
<td width="75%" align="left" valign="middle">
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: <a href="https://www.lingtrue.com/register?aff=MP34">register using this link</a> and enter the <strong>LingtrueAPI</strong> promo code when making the first recharge to enjoy a <strong>10% discount</strong>.
</td>
</tr>
<tr>
<td width="25%" align="center" valign="middle">
<img src="static/wechat.png" alt="Sponsor Contact" width="150">

View file

@ -1 +1 @@
2.12.2.2
2.12.3

View file

@ -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

View file

@ -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) {

View file

@ -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() {

View file

@ -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)) {

BIN
static/lingtrueapi.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 196 KiB