CoolFace
Apppublic

dotaevo/a2abase2api

sourceHugging Faceupdated 8mo agoView on Hugging Face
0likes
server.js558 linesDownload Raw Back to root
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