CoolFace
Apppublic

faaiza/CodeSage

sourceHugging Faceupdated 4mo agoView on Hugging Face
0likes
server.js165 linesDownload Raw Back to root
1require('dotenv').config();2const express = require('express');3const cors = require('cors');4const fetch = require('node-fetch');5const path = require('path');6 7const app = express();8const PORT = process.env.PORT || 3000;9 10app.use(cors());11app.use(express.json());12app.use(express.static(path.join(__dirname, 'public')));13 14// Health check route15app.get('/health', (req, res) => {16  res.json({ status: 'ok', message: 'CodeSage server is running' });17});18 19// Main AI route โ€” streams response from Groq back to browser20app.post('/api/analyze', async (req, res) => {21  const { prompt, code, action } = req.body;22 23  if (!action) {24    return res.status(400).json({ error: 'No action provided' });25  }26 27  // Validate input (custom questions may omit code)28  if (action !== 'custom' && (!code || code.trim() === '')) {29    return res.status(400).json({ error: 'No code provided' });30  }31 32  // Build the system prompt based on action33  const systemPrompts = {34    explain: 'You are a code explainer. Explain this code in simple english. Format: 1) Purpose (1 sentence) 2) How it works (max 4 bullet points, each one line) 3) Issues (max 2 bullets). Be concise.',35 36    refactor: 'You are a code refactorer. Fix ALL issues: SQL injection must use parameterized queries, add error handling, remove sensitive data exposure, use modern syntax. Show the complete improved code in one code block. Then list changes in max 4 bullet points.',37 38    bugs: 'You are a code debugger. Respond in EXACTLY this format, no exceptions:\n\n๐Ÿ”ด CRITICAL (security/crashes):\n- issue: fix\n\n๐ŸŸก WARNING (runtime errors):\n- issue: fix\n\n๐Ÿ”ต SUGGESTION (improvements):\n- issue: fix\n\nMax 3 items per category. Skip empty categories. Never call style issues bugs.',39 40    tests: 'You are a test writer. Write practical tests using pytest for Python, Jest for JavaScript, PHPUnit for PHP. Cover: happy path, one error case, one edge case. Keep each test short. Show complete runnable test file.',41 42    improve: 'You are a code reviewer. Give exactly 4 improvements. Format each as: **Title** - one line description, then show only the specific improved code snippet. No long paragraphs.',43 44    animate: 'You are a code execution visualizer. Return ONLY valid JSON. Zero text outside JSON. Zero markdown. Zero backticks. Structure: {"steps":[{"lineNumbers":[1],"explanation":"plain english, max 20 words","variables":[{"name":"x","value":"10","type":"number","isNew":true}],"callStack":[{"name":"main","isActive":true}],"status":"normal"}]}. CRITICAL: The user message states the exact line count N โ€” you MUST return exactly N steps in order. Step 1 covers line 1 only (lineNumbers:[1]), step 2 covers line 2 only (lineNumbers:[2]), etc. Never skip, merge, or group lines. Every line gets its own step from first to last. For complex values use short strings like "[n items]" or "{n keys}". Status: normal, highlight, error, or return. Always valid JSON.'45  };46 47  const tokenLimits = {48    explain: 500,49    refactor: 900,50    bugs: 600,51    tests: 900,52    improve: 700,53    animate: 120054  };55 56  const systemMessage = systemPrompts[action] || systemPrompts.explain;57 58  let userMessage;59  if (action === 'custom' && prompt) {60    userMessage = prompt;61  } else if (action === 'animate') {62    const codeLines = code.split('\n');63    const numbered = codeLines.map((line, i) => `${i + 1}: ${line}`).join('\n');64    userMessage = `This code has exactly ${codeLines.length} lines. Generate exactly ${codeLines.length} animation steps โ€” one step per line, sequential, no skipping.\n\n${numbered}`;65  } else {66    userMessage = `Here is the code to analyze:\n\n\`\`\`\n${code}\n\`\`\``;67  }68 69  const animateTokenLimit = action === 'animate'70    ? Math.min(4000, Math.max(1200, code.split('\n').length * 140))71    : (tokenLimits[action] || 800);72 73  // Set headers for Server-Sent Events streaming74  res.setHeader('Content-Type', 'text/event-stream');75  res.setHeader('Cache-Control', 'no-cache');76  res.setHeader('Connection', 'keep-alive');77  res.setHeader('Access-Control-Allow-Origin', '*');78  res.flushHeaders();79 80  try {81    const groqResponse = await fetch('https://api.groq.com/openai/v1/chat/completions', {82      method: 'POST',83      headers: {84        'Authorization': `Bearer ${process.env.GROQ_API_KEY}`,85        'Content-Type': 'application/json'86      },87      body: JSON.stringify({88        model: 'llama-3.3-70b-versatile',89        messages: [90          { role: 'system', content: systemMessage },91          { role: 'user', content: userMessage }92        ],93        max_tokens: animateTokenLimit,94        temperature: 0.2,95        stream: true96      })97    });98 99    if (!groqResponse.ok) {100      const errorBody = await groqResponse.text();101      console.error('Groq API error:', groqResponse.status, errorBody);102      res.write(`data: ${JSON.stringify({ error: 'Groq API error: ' + groqResponse.status })}\n\n`);103      res.end();104      return;105    }106 107    // Stream Groq response to the browser108    const reader = groqResponse.body;109    let buffer = '';110 111    reader.on('data', (chunk) => {112      buffer += chunk.toString();113      const lines = buffer.split('\n');114      buffer = lines.pop(); // keep incomplete line in buffer115 116      for (const line of lines) {117        const trimmed = line.trim();118        if (!trimmed || !trimmed.startsWith('data: ')) continue;119 120        const data = trimmed.slice(6);121        if (data === '[DONE]') {122          res.write('data: [DONE]\n\n');123          continue;124        }125 126        try {127          const parsed = JSON.parse(data);128          const content = parsed.choices?.[0]?.delta?.content;129          if (content) {130            res.write(`data: ${JSON.stringify({ text: content })}\n\n`);131          }132        } catch (e) {133          // Skip malformed chunks134        }135      }136    });137 138    reader.on('end', () => {139      res.write('data: [DONE]\n\n');140      res.end();141    });142 143    reader.on('error', (err) => {144      console.error('Stream error:', err);145      res.write(`data: ${JSON.stringify({ error: 'Stream error' })}\n\n`);146      res.end();147    });148 149  } catch (err) {150    console.error('Server error:', err);151    res.write(`data: ${JSON.stringify({ error: 'Server error: ' + err.message })}\n\n`);152    res.end();153  }154});155 156// Catch-all: serve index.html for any unknown route157app.get('*', (req, res) => {158  res.sendFile(path.join(__dirname, 'public', 'index.html'));159});160 161app.listen(PORT, () => {162  console.log(`โœ… CodeSage server running at http://localhost:${PORT}`);163  console.log(`   Groq API key loaded: ${process.env.GROQ_API_KEY ? 'YES โœ“' : 'NO โœ— โ€” check your .env file'}`);164});165