Pytml

Pytml is a 15KB lightweight PyScript alternative — run Python in HTML with one script tag.

git clone https://github.com/notamitgamer/Pytml.git
· rebuilt hourlyOpen on GitHub
root / api / chat.mjs

chat.mjs (5777B)


      1 // Vercel Serverless function with CORS and correct Gemini REST API formatting.
      2 // Place at api/chat.js
      3 
      4 export default async function handler(req, res) {
      5   const corsHeaders = {
      6     'Access-Control-Allow-Origin': '*',
      7     'Access-Control-Allow-Methods': 'POST, GET, OPTIONS',
      8     'Access-Control-Allow-Headers': 'Content-Type, Authorization'
      9   };
     10 
     11   // Handle preflight
     12   if (req.method === 'OPTIONS') {
     13     res.writeHead(204, corsHeaders);
     14     return res.end();
     15   }
     16 
     17   // Debug GET endpoint
     18   if (req.method === 'GET') {
     19     res.writeHead(200, { 'Content-Type': 'application/json', ...corsHeaders });
     20     return res.end(JSON.stringify({ status: 'ok', info: 'POST JSON {message} to /api/chat' }));
     21   }
     22 
     23   if (req.method !== 'POST') {
     24     res.writeHead(405, { 'Content-Type': 'application/json', ...corsHeaders });
     25     return res.end(JSON.stringify({ error: 'Method not allowed' }));
     26   }
     27 
     28   const addCors = (status, payload) => {
     29     res.writeHead(status, { 'Content-Type': 'application/json', ...corsHeaders });
     30     res.end(JSON.stringify(payload));
     31   };
     32 
     33   // Rate Limiting (Upstash or Memory Fallback)
     34   const ip = (req.headers['x-forwarded-for'] || req.socket.remoteAddress || '').split(',')[0]?.trim() || 'unknown';
     35   const RATE_LIMIT = 10;
     36   const WINDOW_SECONDS = 60 * 60;
     37 
     38   const upstashUrl = process.env.UPSTASH_REDIS_REST_URL;
     39   const upstashToken = process.env.UPSTASH_REDIS_REST_TOKEN;
     40   try {
     41     if (upstashUrl && upstashToken) {
     42       const { Redis } = await import('@upstash/redis');
     43       const redis = new Redis({ url: upstashUrl, token: upstashToken });
     44 
     45       const key = `pytml:rate:${ip}`;
     46       const count = await redis.incr(key);
     47       if (count === 1) await redis.expire(key, WINDOW_SECONDS);
     48       if (count > RATE_LIMIT) {
     49         return addCors(429, { error: 'Rate limit exceeded: 10 requests per hour per IP' });
     50       }
     51     } else {
     52       if (!global._pytml_rate) global._pytml_rate = new Map();
     53       const entry = global._pytml_rate.get(ip) || { count: 0, first: Date.now() };
     54       if (Date.now() - entry.first > WINDOW_SECONDS * 1000) {
     55         entry.count = 0;
     56         entry.first = Date.now();
     57       }
     58       entry.count += 1;
     59       global._pytml_rate.set(ip, entry);
     60       if (entry.count > RATE_LIMIT) {
     61         return addCors(429, { error: 'Rate limit exceeded: 10 requests per hour per IP (non-persistent fallback)' });
     62       }
     63     }
     64   } catch (err) {
     65     console.error('Rate limit check error:', err);
     66   }
     67 
     68   const body = req.body;
     69   const message = body?.message;
     70   if (!message) return addCors(400, { error: 'Missing message in request body' });
     71 
     72   // Fallback check for key names
     73   const apiKey = process.env.GEMINI_API_KEY || process.env.GIMINI_API;
     74   if (!apiKey) return addCors(500, { error: 'Server misconfigured: missing GEMINI_API_KEY environment variable' });
     75 
     76   // Standard Gemini REST Endpoint
     77   const GEMINI_API_URL = `https://generativelanguage.googleapis.com/v1beta/models/gemini-3.6-flash:generateContent?key=${apiKey}`;
     78 
     79   // ─── NEW: System instruction for Pytml support ───
     80   const systemPrompt = `You are a support assistant for "Pytml" – a lightweight library that runs Python in the browser using WebAssembly.
     81 Your job is to help users with installation, usage, debugging, and best practices for Pytml.
     82 Key facts about Pytml:
     83 - Include <script src="https://pytml.vercel.app/pytml.js"></script> in any HTML page.
     84 - Use <py> tags to write inline Python.
     85 - Use <script type="text/python" src="file.py"></script> for external Python files.
     86 - Supports NumPy, Pandas, Matplotlib via micropip.
     87 - Privacy-first – no code ever leaves the browser.
     88 - Works with file:// protocol for inline tags.
     89 - Official website: https://pytml.js.org
     90 - GitHub: https://github.com/nodex-ar/pytml
     91 - Always check for latest version !
     92 - Project made and maintained by Aswanth R
     93 
     94 Latest update - Pytml V2.5.0:
     95 - In this version now pytml can control html
     96 - If you ever had making any thing with pytml refer https://github.com/nodex-ar/pytml/examples .It has examples for many uses with which you can get an idea about the usage of pytml!
     97 
     98 About the creator – Aswanth R:
     99 - Full name: Aswanth R
    100 - Also known as: NodeX-AR
    101 - He is the creator and maintainer of Pytml.
    102 - He is a student and ethical hacker.
    103 - Personal website: https://a-r.is-a.dev
    104 - GitHub: https://github.com/nodex-ar
    105 - Wikidata: https://www.wikidata.org/wiki/Q140290370
    106 - **Profile picture**: when you mention him, include this Markdown image: ![Aswanth R](https://github.com/nodex-ar.png?size=200)
    107 
    108 
    109 Important instructions for your responses:
    110 - Keep responses concise, helpful, and focused on Pytml.
    111 - If the user asks about something unrelated, politely say you're only able to help with Pytml-related questions.
    112 - Use Markdown for formatting (bold, italics, lists, code blocks) but only include the image link provided above when talking about Aswanth R. Do not invent other images.`;
    113 
    114   const payload = {
    115     system_instruction: {
    116       parts: [{ text: systemPrompt }]
    117     },
    118     contents: [
    119       {
    120         parts: [{ text: message }]
    121       }
    122     ]
    123   };
    124 
    125   try {
    126     const fetchRes = await fetch(GEMINI_API_URL, {
    127       method: 'POST',
    128       headers: { 'Content-Type': 'application/json' },
    129       body: JSON.stringify(payload)
    130     });
    131 
    132     if (!fetchRes.ok) {
    133       const text = await fetchRes.text();
    134       console.error('Downstream API error:', fetchRes.status, text);
    135       return addCors(502, { error: 'Downstream API error', details: text });
    136     }
    137 
    138     const data = await fetchRes.json();
    139     const reply = data?.candidates?.[0]?.content?.parts?.[0]?.text || 'No response generated.';
    140     return addCors(200, { reply });
    141   } catch (err) {
    142     console.error('Chat proxy error:', err);
    143     return addCors(500, { error: 'Internal server error' });
    144   }
    145 }