parent
1570fbb096
commit
ceff3771ea
9 changed files with 69 additions and 162 deletions
10
README-JA.md
10
README-JA.md
|
|
@ -47,16 +47,6 @@
|
|||
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">
|
||||
|
|
|
|||
10
README-ZH.md
10
README-ZH.md
|
|
@ -46,16 +46,6 @@
|
|||
感谢 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">
|
||||
|
|
|
|||
10
README.md
10
README.md
|
|
@ -47,16 +47,6 @@
|
|||
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">
|
||||
|
|
|
|||
2
VERSION
2
VERSION
|
|
@ -1 +1 @@
|
|||
2.12.3
|
||||
2.12.2.2
|
||||
|
|
|
|||
|
|
@ -2,70 +2,70 @@
|
|||
$OutputEncoding = [System.Text.Encoding]::UTF8
|
||||
|
||||
Write-Host "========================================" -ForegroundColor Cyan
|
||||
Write-Host " AI Client 2 API Quick Setup Script" -ForegroundColor Cyan
|
||||
Write-Host " AI Client 2 API 快速安装启动脚本" -ForegroundColor Cyan
|
||||
Write-Host "========================================" -ForegroundColor Cyan
|
||||
Write-Host ""
|
||||
|
||||
# Handle parameters
|
||||
# 处理参数
|
||||
$forcePull = $args -contains "--pull"
|
||||
|
||||
# Check Git and Pull
|
||||
# 检查 Git 并拉取
|
||||
if ($forcePull) {
|
||||
Write-Host "[UPDATE] Pulling latest code from remote repository..."
|
||||
Write-Host "[更新] 正在从远程仓库拉取最新代码..."
|
||||
if (Get-Command git -ErrorAction SilentlyContinue) {
|
||||
git pull
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
Write-Warning "Git pull failed. Please check your network or handle conflicts manually."
|
||||
Write-Warning "Git pull 失败,请检查网络或手动处理冲突。"
|
||||
} else {
|
||||
Write-Host "[SUCCESS] Code updated." -ForegroundColor Green
|
||||
Write-Host "[成功] 代码已更新。" -ForegroundColor Green
|
||||
}
|
||||
} else {
|
||||
Write-Warning "Git not detected. Skipping code pull."
|
||||
Write-Warning "未检测到 Git,跳过代码拉取。"
|
||||
}
|
||||
}
|
||||
|
||||
# Check Node.js
|
||||
Write-Host "[CHECK] Checking if Node.js is installed..."
|
||||
# 检查 Node.js
|
||||
Write-Host "[检查] 正在检查Node.js是否已安装..."
|
||||
if (-not (Get-Command node -ErrorAction SilentlyContinue)) {
|
||||
Write-Host "[ERROR] Node.js not detected. Please install Node.js (https://nodejs.org/)" -ForegroundColor Red
|
||||
Write-Host "[错误] 未检测到Node.js,请先安装Node.js (https://nodejs.org/)" -ForegroundColor Red
|
||||
Pause
|
||||
exit 1
|
||||
}
|
||||
|
||||
$nodeVersion = node --version
|
||||
Write-Host "[SUCCESS] Node.js installed, version: $nodeVersion" -ForegroundColor Green
|
||||
Write-Host "[成功] Node.js已安装,版本: $nodeVersion" -ForegroundColor Green
|
||||
|
||||
# Check package.json
|
||||
# 检查 package.json
|
||||
if (-not (Test-Path "package.json")) {
|
||||
Write-Host "[ERROR] package.json not found. Please ensure you are running this script from the project root." -ForegroundColor Red
|
||||
Write-Host "[错误] 未找到package.json文件,请确保在项目根目录下运行此脚本" -ForegroundColor Red
|
||||
Pause
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Determine package manager
|
||||
# 确定包管理器
|
||||
$pkgManager = if (Get-Command pnpm -ErrorAction SilentlyContinue) { "pnpm" } else { "npm" }
|
||||
Write-Host "[INSTALL] Installing/updating dependencies using $pkgManager..." -ForegroundColor Cyan
|
||||
Write-Host "[安装] 正在使用 $pkgManager 安装/更新依赖..." -ForegroundColor Cyan
|
||||
|
||||
& $pkgManager install
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
Write-Host "[ERROR] Dependency installation failed. Please check your network connection." -ForegroundColor Red
|
||||
Write-Host "[错误] 依赖安装失败,请检查网络连接。" -ForegroundColor Red
|
||||
Pause
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Check master file
|
||||
# 检查主文件
|
||||
if (-not (Test-Path "src\core\master.js")) {
|
||||
Write-Host "[ERROR] src\core\master.js not found." -ForegroundColor Red
|
||||
Write-Host "[错误] 未找到 src\core\master.js 文件" -ForegroundColor Red
|
||||
Pause
|
||||
exit 1
|
||||
}
|
||||
|
||||
Write-Host ""
|
||||
Write-Host "========================================" -ForegroundColor Green
|
||||
Write-Host " Starting AIClient2API Server..." -ForegroundColor Green
|
||||
Write-Host " 启动 AIClient2API 服务器..." -ForegroundColor Green
|
||||
Write-Host "========================================" -ForegroundColor Green
|
||||
Write-Host "Server will start at http://localhost:3000"
|
||||
Write-Host "Press Ctrl+C to stop the server"
|
||||
Write-Host "服务器将在 http://localhost:3000 启动"
|
||||
Write-Host "按 Ctrl+C 停止服务器"
|
||||
Write-Host ""
|
||||
|
||||
node src\core\master.js
|
||||
|
|
|
|||
|
|
@ -40,7 +40,6 @@ 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);
|
||||
|
|
@ -200,16 +199,16 @@ function normalizeAntigravityThinking(modelName, payload, isClaudeModel) {
|
|||
|
||||
let normalizedBudget = normalizeThinkingBudget(modelName, budget);
|
||||
|
||||
// 确保 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
|
||||
// 对于 Claude 模型,确保 thinking budget < max_tokens
|
||||
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 && normalizedBudget !== -1) {
|
||||
if (normalizedBudget >= 0 && normalizedBudget < minBudget) {
|
||||
// Budget 低于最小值,移除 thinking 配置
|
||||
delete payload.request.generationConfig.thinkingConfig;
|
||||
return payload;
|
||||
|
|
@ -600,7 +599,7 @@ function toGeminiApiResponse(antigravityResponse) {
|
|||
}
|
||||
|
||||
/**
|
||||
* 确保请求体中的内容部分都有角色属性,并修复历史记录中的思考签名
|
||||
* 确保请求体中的内容部分都有角色属性
|
||||
* @param {Object} requestBody - 请求体
|
||||
* @returns {Object} 处理后的请求体
|
||||
*/
|
||||
|
|
@ -675,24 +674,6 @@ 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
|
||||
};
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -715,6 +696,25 @@ 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,32 +730,6 @@ 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) {
|
||||
|
|
|
|||
|
|
@ -287,6 +287,24 @@ 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 = [];
|
||||
|
|
@ -304,29 +322,6 @@ 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() {
|
||||
|
|
|
|||
|
|
@ -50,38 +50,6 @@ 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 = {
|
||||
|
|
@ -612,7 +580,7 @@ export class QwenApiService {
|
|||
this.currentAxiosInstance = axios.create(axiosConfig);
|
||||
|
||||
// Process message content before sending the request
|
||||
const processedBody = applyQwenDefaultSystemPrompt(body);
|
||||
const processedBody = body;//this.processMessageContent(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)) {
|
||||
|
|
|
|||
Binary file not shown.
|
Before Width: | Height: | Size: 196 KiB |
Loading…
Reference in a new issue