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% オフになります! +
+
+
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)!
+
+
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!
+
+
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