Spaces:
Build error
Build error
| const express = require('express'); | |
| const helmet = require('helmet'); | |
| const cors = require('cors'); | |
| const rateLimit = require('express-rate-limit'); | |
| const { GoogleGenerativeAI } = require('@google/generative-ai'); | |
| const Database = require('better-sqlite3'); | |
| const app = express(); | |
| const PORT = process.env.PORT || 7860; | |
| // Security | |
| app.use(helmet()); | |
| app.use(cors()); | |
| app.use(express.json()); | |
| // Rate limiting | |
| const limiter = rateLimit({ | |
| windowMs: 15 * 60 * 1000, // 15 minutes | |
| max: 100, // Limit each IP to 100 requests per windowMs | |
| message: { error: { type: 'rate_limit_error', message: 'Too many requests' }} | |
| }); | |
| app.use('/anthropic', limiter); | |
| // Database for usage tracking | |
| const db = new Database(':memory:'); | |
| db.exec(`CREATE TABLE IF NOT EXISTS usage ( id INTEGER PRIMARY KEY AUTOINCREMENT, api_key TEXT, input_tokens INTEGER, output_tokens INTEGER, timestamp DATETIME DEFAULT CURRENT_TIMESTAMP )`); | |
| // Gemini client | |
| const genAI = new GoogleGenerativeAI(process.env.GEMINI_API_KEY || ''); | |
| // Auth middleware | |
| const authenticate = (req, res, next) => { | |
| const apiKey = req.headers['x-api-key']; | |
| if (!apiKey || apiKey !== process.env.PROXY_API_KEY) { | |
| return res.status(401).json({ | |
| error: { type: 'authentication_error', message: 'Invalid API key' } | |
| }); | |
| } | |
| req.apiKey = apiKey; | |
| next(); | |
| }; | |
| // Health check | |
| app.get('/health', (req, res) => { | |
| res.json({ status: 'healthy', timestamp: new Date().toISOString() }); | |
| }); | |
| // Models endpoint | |
| app.get('/anthropic/v1/models', authenticate, (req, res) => { | |
| res.json({ | |
| data: [ | |
| { | |
| id: 'claude-3-5-sonnet-20241022', | |
| name: 'Claude 3.5 Sonnet', | |
| type: 'model' | |
| } | |
| ] | |
| }); | |
| }); | |
| // Messages endpoint | |
| app.post('/anthropic/v1/messages', authenticate, async (req, res) => { | |
| try { | |
| const { messages, max_tokens = 1024, stream = false } = req.body; | |
| if (!messages || !Array.isArray(messages)) { | |
| return res.status(400).json({ | |
| error: { type: 'invalid_request_error', message: 'messages is required' } | |
| }); | |
| } | |
| // Convert to Gemini format | |
| const prompt = messages.map(m => `${m.role}: ${m.content}`).join('\n'); | |
| const model = genAI.getGenerativeModel({ model: 'gemini-2.0-flash-exp' }); | |
| if (stream) { | |
| res.setHeader('Content-Type', 'text/event-stream'); | |
| res.setHeader('Cache-Control', 'no-cache'); | |
| res.setHeader('Connection', 'keep-alive'); | |
| const result = await model.generateContentStream(prompt); | |
| for await (const chunk of result.stream) { | |
| const text = chunk.text(); | |
| res.write(`event: content_block_delta\n`); | |
| res.write(`data: ${JSON.stringify({ | |
| type: 'content_block_delta', | |
| delta: { type: 'text_delta', text } | |
| })}\n\n`); | |
| } | |
| res.write(`event: message_stop\n`); | |
| res.write(`data: {}\n\n`); | |
| res.end(); | |
| } else { | |
| const result = await model.generateContent(prompt); | |
| const text = result.response.text(); | |
| // Track usage | |
| const inputTokens = Math.ceil(prompt.length / 4); | |
| const outputTokens = Math.ceil(text.length / 4); | |
| db.prepare('INSERT INTO usage (api_key, input_tokens, output_tokens) VALUES (?, ?, ?)') | |
| .run(req.apiKey, inputTokens, outputTokens); | |
| res.json({ | |
| id: `msg_${Date.now()}`, | |
| type: 'message', | |
| role: 'assistant', | |
| content: [{ type: 'text', text }], | |
| model: 'claude-3-5-sonnet-20241022', | |
| stop_reason: 'end_turn', | |
| usage: { | |
| input_tokens: inputTokens, | |
| output_tokens: outputTokens | |
| } | |
| }); | |
| } | |
| } catch (error) { | |
| console.error('Error:', error); | |
| res.status(500).json({ | |
| error: { type: 'api_error', message: error.message } | |
| }); | |
| } | |
| }); | |
| app.listen(PORT, () => { | |
| console.log(`🚀 Server running on port ${PORT}`); | |
| console.log(`📊 Health: http://localhost:${PORT}/health`); | |
| }); | |