dotaevo/a2abase2api
0
1const express = require('express');2const fetch = require('node-fetch');3 4const app = express();5const PORT = process.env.PORT || 4777;6const A2BASE_API_URL = 'https://a2abase.ai/api';7 8app.use(express.json());9 10// 模型列表11const MODELS_DATA = {12 "models": [13 {14 "id": "gemini/gemini-ultra",15 "display_name": "Gemini Ultra",16 "short_name": "Gemini Ultra",17 "requires_subscription": false,18 "input_cost_per_million_tokens": 5.0,19 "output_cost_per_million_tokens": 20.0,20 "context_window": 2000000,21 "capabilities": ["chat", "function_calling", "vision", "structured_output", "thinking"],22 "recommended": false,23 "priority": 10624 },25 {26 "id": "gemini/gemini-3-deep-think-exp",27 "display_name": "Gemini 3 Deep Think",28 "short_name": "Gemini 3 Deep Think",29 "requires_subscription": false,30 "input_cost_per_million_tokens": 2.5,31 "output_cost_per_million_tokens": 10.0,32 "context_window": 2000000,33 "capabilities": ["chat", "function_calling", "vision", "thinking"],34 "recommended": false,35 "priority": 10536 },37 {38 "id": "gemini/gemini-3-flash-preview",39 "display_name": "Gemini 3 Flash",40 "short_name": "Gemini 3 Flash",41 "requires_subscription": false,42 "input_cost_per_million_tokens": 0.3,43 "output_cost_per_million_tokens": 1.5,44 "context_window": 2000000,45 "capabilities": ["chat", "function_calling", "vision", "structured_output"],46 "recommended": false,47 "priority": 10448 },49 {50 "id": "gemini/gemini-3-pro-preview",51 "display_name": "Gemini 3",52 "short_name": "Gemini 3",53 "requires_subscription": false,54 "input_cost_per_million_tokens": 0.36,55 "output_cost_per_million_tokens": 3.0,56 "context_window": 2000000,57 "capabilities": ["chat", "function_calling", "vision", "structured_output"],58 "recommended": false,59 "priority": 10360 },61 {62 "id": "anthropic/claude-sonnet-4-5-20250929",63 "display_name": "Claude Sonnet 4.5",64 "short_name": "Claude Sonnet 4.5",65 "requires_subscription": false,66 "input_cost_per_million_tokens": 3.6,67 "output_cost_per_million_tokens": 18.0,68 "context_window": 200000,69 "capabilities": ["chat", "function_calling", "vision", "thinking", "structured_output"],70 "recommended": false,71 "priority": 10272 },73 {74 "id": "gemini/gemini-2.5-flash",75 "display_name": "Gemini 2.5 Flash",76 "short_name": "Gemini 2.5 Flash",77 "requires_subscription": false,78 "input_cost_per_million_tokens": 0.36,79 "output_cost_per_million_tokens": 3.0,80 "context_window": 2000000,81 "capabilities": ["chat", "function_calling", "vision", "structured_output"],82 "recommended": false,83 "priority": 10284 },85 {86 "id": "anthropic/claude-haiku-4-5-20251001",87 "display_name": "Claude Haiku 4.5",88 "short_name": "Claude Haiku 4.5",89 "requires_subscription": false,90 "input_cost_per_million_tokens": 1.0,91 "output_cost_per_million_tokens": 5.0,92 "context_window": 200000,93 "capabilities": ["chat", "function_calling", "vision", "structured_output"],94 "recommended": false,95 "priority": 10196 },97 {98 "id": "gemini/gemini-2.5-pro",99 "display_name": "Gemini 2.5 Pro",100 "short_name": "Gemini 2.5 Pro",101 "requires_subscription": false,102 "input_cost_per_million_tokens": 1.25,103 "output_cost_per_million_tokens": 5.0,104 "context_window": 2000000,105 "capabilities": ["chat", "function_calling", "vision", "structured_output", "thinking"],106 "recommended": false,107 "priority": 101108 },109 {110 "id": "openrouter/z-ai/glm-4.7",111 "display_name": "GLM 4.7",112 "short_name": "GLM 4.7",113 "requires_subscription": false,114 "input_cost_per_million_tokens": 0.5,115 "output_cost_per_million_tokens": 2.2,116 "context_window": 205000,117 "capabilities": ["chat", "function_calling", "code_interpreter", "thinking"],118 "recommended": false,119 "priority": 100120 },121 {122 "id": "openrouter/x-ai/grok-code-fast-1",123 "display_name": "Grok Code Fast 1",124 "short_name": "Grok Code Fast 1",125 "requires_subscription": false,126 "input_cost_per_million_tokens": 0.24,127 "output_cost_per_million_tokens": 1.8,128 "context_window": 256000,129 "capabilities": ["chat", "function_calling", "code_interpreter"],130 "recommended": false,131 "priority": 100132 },133 {134 "id": "openrouter/z-ai/glm-4.6",135 "display_name": "GLM 4.6",136 "short_name": "GLM 4.6",137 "requires_subscription": false,138 "input_cost_per_million_tokens": 0.48,139 "output_cost_per_million_tokens": 2.1,140 "context_window": 202752,141 "capabilities": ["chat", "function_calling"],142 "recommended": false,143 "priority": 99144 },145 {146 "id": "openrouter/minimax/minimax-m2",147 "display_name": "Minimax M2",148 "short_name": "Minimax M2",149 "requires_subscription": false,150 "input_cost_per_million_tokens": 0.306,151 "output_cost_per_million_tokens": 1.224,152 "context_window": 204800,153 "capabilities": ["chat", "function_calling", "vision"],154 "recommended": false,155 "priority": 98156 },157 {158 "id": "openai/gpt-5.2",159 "display_name": "GPT-5.2",160 "short_name": "GPT-5.2",161 "requires_subscription": false,162 "input_cost_per_million_tokens": 5.0,163 "output_cost_per_million_tokens": 15.0,164 "context_window": 400000,165 "capabilities": ["chat", "function_calling", "vision", "structured_output", "thinking"],166 "recommended": true,167 "priority": 97168 },169 {170 "id": "openai/gpt-5-mini",171 "display_name": "GPT-5 Mini",172 "short_name": "GPT-5 Mini",173 "requires_subscription": false,174 "input_cost_per_million_tokens": 0.3,175 "output_cost_per_million_tokens": 2.4,176 "context_window": 400000,177 "capabilities": ["chat", "function_calling", "structured_output"],178 "recommended": false,179 "priority": 96180 },181 {182 "id": "openrouter/deepseek/deepseek-chat",183 "display_name": "DeepSeek Chat",184 "short_name": "DeepSeek Chat",185 "requires_subscription": false,186 "input_cost_per_million_tokens": 0.46,187 "output_cost_per_million_tokens": 1.07,188 "context_window": 128000,189 "capabilities": ["chat", "function_calling"],190 "recommended": false,191 "priority": 95192 },193 {194 "id": "openai/o3-mini",195 "display_name": "o3-mini",196 "short_name": "o3-mini",197 "requires_subscription": false,198 "input_cost_per_million_tokens": 3.5,199 "output_cost_per_million_tokens": 14.0,200 "context_window": 128000,201 "capabilities": ["chat", "thinking"],202 "recommended": false,203 "priority": 95204 },205 {206 "id": "openai/gpt-4.1-mini",207 "display_name": "GPT-4.1 Mini",208 "short_name": "GPT-4.1 Mini",209 "requires_subscription": false,210 "input_cost_per_million_tokens": 0.48,211 "output_cost_per_million_tokens": 1.92,212 "context_window": 1047576,213 "capabilities": ["chat", "function_calling", "structured_output"],214 "recommended": false,215 "priority": 94216 },217 {218 "id": "openrouter/x-ai/grok-4-fast",219 "display_name": "Grok 4 Fast",220 "short_name": "Grok 4 Fast",221 "requires_subscription": false,222 "input_cost_per_million_tokens": 0.24,223 "output_cost_per_million_tokens": 0.6,224 "context_window": 2000000,225 "capabilities": ["chat", "function_calling"],226 "recommended": false,227 "priority": 93228 },229 {230 "id": "openai/gpt-4o-2024-11-20",231 "display_name": "GPT-4o (Nov 2024)",232 "short_name": "GPT-4o (Nov 2024)",233 "requires_subscription": false,234 "input_cost_per_million_tokens": 2.5,235 "output_cost_per_million_tokens": 10.0,236 "context_window": 128000,237 "capabilities": ["chat", "function_calling", "vision", "structured_output"],238 "recommended": false,239 "priority": 91240 },241 {242 "id": "openai/gpt-4o",243 "display_name": "GPT-4o",244 "short_name": "GPT-4o",245 "requires_subscription": false,246 "input_cost_per_million_tokens": 2.5,247 "output_cost_per_million_tokens": 10.0,248 "context_window": 128000,249 "capabilities": ["chat", "function_calling", "vision", "structured_output"],250 "recommended": false,251 "priority": 90252 },253 {254 "id": "gemini/gemini-2.0-flash-exp",255 "display_name": "Gemini 2.0 Flash",256 "short_name": "Gemini 2.0 Flash",257 "requires_subscription": false,258 "input_cost_per_million_tokens": 0.075,259 "output_cost_per_million_tokens": 0.3,260 "context_window": 1000000,261 "capabilities": ["chat", "function_calling", "vision", "structured_output"],262 "recommended": false,263 "priority": 89264 },265 {266 "id": "openai/gpt-4o-mini",267 "display_name": "GPT-4o Mini",268 "short_name": "GPT-4o Mini",269 "requires_subscription": false,270 "input_cost_per_million_tokens": 0.18,271 "output_cost_per_million_tokens": 0.72,272 "context_window": 128000,273 "capabilities": ["chat", "function_calling", "vision", "structured_output"],274 "recommended": false,275 "priority": 88276 },277 {278 "id": "gemini/gemini-2.0-flash-lite",279 "display_name": "Gemini 2.0 Flash-Lite",280 "short_name": "Gemini 2.0 Flash-Lite",281 "requires_subscription": false,282 "input_cost_per_million_tokens": 0.0375,283 "output_cost_per_million_tokens": 0.15,284 "context_window": 1000000,285 "capabilities": ["chat", "function_calling", "vision"],286 "recommended": false,287 "priority": 86288 }289 ],290 "subscription_tier": "free",291 "total_models": 23,292 "allowed_models_count": 23293};294 295// GET /v1/models - 返回模型列表296app.get('/v1/models', (req, res) => {297 res.json({298 object: 'list',299 data: MODELS_DATA.models.map(model => ({300 id: model.id,301 object: 'model',302 created: Date.now(),303 owned_by: 'a2base',304 permission: [],305 root: model.id,306 parent: null307 }))308 });309});310 311// POST /v1/chat/completions - 处理聊天请求312app.post('/v1/chat/completions', async (req, res) => {313 try {314 // 从Authorization header中提取apiKey和agentId315 const authHeader = req.headers.authorization;316 if (!authHeader || !authHeader.startsWith('Bearer ')) {317 return res.status(401).json({ error: { message: 'Missing or invalid Authorization header', type: 'invalid_request_error' } });318 }319 320 const token = authHeader.substring(7); // 去除 'Bearer '321 const [apiKey, agentId] = token.split('|');322 323 if (!apiKey || !agentId) {324 return res.status(401).json({ error: { message: 'Invalid token format. Expected: apiKey|agentId', type: 'invalid_request_error' } });325 }326 327 const { messages, model, stream = false, temperature, max_tokens } = req.body;328 329 if (!messages || !Array.isArray(messages) || messages.length === 0) {330 return res.status(400).json({ error: { message: 'Messages array is required', type: 'invalid_request_error' } });331 }332 333 // 步骤1: 创建Thread334 const threadResponse = await fetch(`${A2BASE_API_URL}/threads`, {335 method: 'POST',336 headers: {337 'X-API-Key': apiKey338 }339 });340 341 if (!threadResponse.ok) {342 const errorText = await threadResponse.text();343 return res.status(threadResponse.status).json({ error: { message: `Failed to create thread: ${errorText}`, type: 'api_error' } });344 }345 346 const threadData = await threadResponse.json();347 const threadId = threadData.thread_id;348 349 // 步骤2: 添加消息到Thread350 await fetch(`${A2BASE_API_URL}/threads/${threadId}/messages`, {351 method: 'POST',352 headers: {353 'X-API-Key': apiKey,354 'Content-Type': 'application/json'355 },356 body: JSON.stringify({357 content: messages.map(msg => `${msg.role === 'user' ? 'User' : 'Assistant'}: ${msg.content}`).join('\n'),358 type: 'user',359 is_llm_message: true360 })361 });362 363 // 步骤3: 启动Agent执行364 const startResponse = await fetch(`${A2BASE_API_URL}/thread/${threadId}/agent/start`, {365 method: 'POST',366 headers: {367 'X-API-Key': apiKey,368 'Content-Type': 'application/json'369 },370 body: JSON.stringify({371 agent_id: agentId,372 model_name: model || 'gemini/gemini-3-pro-preview',373 stream: true374 })375 });376 377 if (!startResponse.ok) {378 const errorText = await startResponse.text();379 return res.status(startResponse.status).json({ error: { message: `Failed to start agent: ${errorText}`, type: 'api_error' } });380 }381 382 const startData = await startResponse.json();383 const agentRunId = startData.agent_run_id;384 385 // 步骤4: 获取流式响应386 const streamResponse = await fetch(`${A2BASE_API_URL}/agent-run/${agentRunId}/stream`, {387 method: 'GET',388 headers: {389 'X-API-Key': apiKey,390 'Accept': 'text/event-stream'391 }392 });393 394 if (!streamResponse.ok) {395 const errorText = await streamResponse.text();396 return res.status(streamResponse.status).json({ error: { message: `Failed to get stream: ${errorText}`, type: 'api_error' } });397 }398 399 const chatId = `chatcmpl-${Date.now()}`;400 const created = Math.floor(Date.now() / 1000);401 402 if (stream) {403 // 流式返回404 res.setHeader('Content-Type', 'text/event-stream');405 res.setHeader('Cache-Control', 'no-cache');406 res.setHeader('Connection', 'keep-alive');407 408 let buffer = '';409 410 streamResponse.body.on('data', (chunk) => {411 // console.log('Received chunk: ', chunk.toString());412 buffer += chunk.toString();413 const lines = buffer.split('\n');414 buffer = lines.pop() || '';415 416 for (const line of lines) {417 if (line.startsWith('data: ')) {418 const jsonStr = line.substring(6);419 try {420 const data = JSON.parse(jsonStr);421 422 // 处理assistant类型的消息423 if (data.type === 'assistant' && data.content) {424 const contentObj = JSON.parse(data.content);425 const metaObj = JSON.parse(data.metadata || '{}');426 if (contentObj.content && metaObj.stream_status === 'chunk') {427 const openaiChunk = {428 id: chatId,429 object: 'chat.completion.chunk',430 created: created,431 model: model || 'gemini/gemini-2.5-pro',432 choices: [{433 index: 0,434 delta: {435 content: contentObj.content436 },437 finish_reason: null438 }]439 };440 res.write(`data: ${JSON.stringify(openaiChunk)}\n\n`);441 }442 }443 444 // 处理完成状态445 if (data.status === 'completed' || data.type === 'assistant_response_end') {446 // const finalChunk = {447 // id: chatId,448 // object: 'chat.completion.chunk',449 // created: created,450 // model: model || 'gemini/gemini-2.5-pro',451 // choices: [{452 // index: 0,453 // delta: {},454 // finish_reason: 'stop'455 // }]456 // };457 // res.write(`data: ${JSON.stringify(finalChunk)}\n\n`);458 res.write('data: [DONE]\n\n');459 res.end();460 }461 } catch (e) {462 // 忽略解析错误463 }464 }465 }466 });467 468 streamResponse.body.on('end', () => {469 if (!res.writableEnded) {470 res.end();471 }472 });473 474 streamResponse.body.on('error', (err) => {475 console.error('Stream error:', err);476 if (!res.writableEnded) {477 res.end();478 }479 });480 481 } else {482 // 非流式返回 - 收集所有内容后一次性返回483 let fullContent = '';484 let buffer = '';485 486 await new Promise((resolve, reject) => {487 streamResponse.body.on('data', (chunk) => {488 buffer += chunk.toString();489 const lines = buffer.split('\n');490 buffer = lines.pop() || '';491 492 for (const line of lines) {493 if (line.startsWith('data: ')) {494 const jsonStr = line.substring(6);495 try {496 const data = JSON.parse(jsonStr);497 498 if (data.type === 'assistant' && data.content) {499 const contentObj = JSON.parse(data.content);500 const metaObj = JSON.parse(data.metadata || '{}');501 if (contentObj.content && metaObj.stream_status === 'chunk') {502 fullContent += contentObj.content;503 }504 }505 506 if (data.status === 'completed' || data.type === 'assistant_response_end') {507 resolve();508 }509 } catch (e) {510 // 忽略解析错误511 }512 }513 }514 });515 516 streamResponse.body.on('end', resolve);517 streamResponse.body.on('error', reject);518 });519 520 // 返回OpenAI格式的响应521 const response = {522 id: chatId,523 object: 'chat.completion',524 created: created,525 model: model || 'gemini/gemini-2.5-pro',526 choices: [{527 index: 0,528 message: {529 role: 'assistant',530 content: fullContent531 },532 finish_reason: 'stop'533 }],534 usage: {535 prompt_tokens: 0,536 completion_tokens: 0,537 total_tokens: 0538 }539 };540 541 res.json(response);542 }543 544 } catch (error) {545 console.error('Error:', error);546 res.status(500).json({ error: { message: error.message, type: 'internal_error' } });547 }548});549 550// 健康检查端点551app.get('/health', (req, res) => {552 res.json({ status: 'ok' });553});554 555app.listen(PORT, () => {556 console.log(`A2Base to OpenAI API server running on port ${PORT}`);557});558 