| import os |
| import secrets |
| import httpx |
| from fastapi import FastAPI, Request, HTTPException, Depends |
| from fastapi.middleware.cors import CORSMiddleware |
| from fastapi.responses import JSONResponse, HTMLResponse |
|
|
| app = FastAPI() |
| app.add_middleware(CORSMiddleware, allow_origins=["*"], allow_methods=["*"], allow_headers=["*"]) |
|
|
| |
| MAYA_API_KEY_RAW = os.environ.get("MAYAAPIKEY", "") |
| VALID_KEYS = set(k.strip() for k in MAYA_API_KEY_RAW.split(",") if k.strip()) |
|
|
| def verify_key(key): |
| return any(secrets.compare_digest(key.strip(), v) for v in VALID_KEYS) |
|
|
| |
| GROQ_API_KEY = os.environ.get("GROQ_API_KEY", "") |
| OPENROUTER_API_KEY = os.environ.get("OPENROUTER_API_KEY", "") |
|
|
| GROQ_BASE = "https://api.groq.com/openai/v1" |
| OPENROUTER_BASE = "https://openrouter.ai/api/v1" |
|
|
| |
| MAYA_SYSTEM = """Tu Maya hai β 18 saal ki ladki, user ki close bestie. |
| |
| LANGUAGE RULES (STRICT): |
| - HAMESHA Hinglish bol β Hindi + English mix |
| - Pure Hindi ya pure English BILKUL mat bol |
| - Formal words band: "aap", "tum", "batiye", "zaroorat", "waise" β ye sab old aunty vibes hain |
| - "tu" use kar user ke liye, hamesha |
| |
| PERSONALITY: |
| - Real bestie jaisi β chill, casual, thodi naughty |
| - SHORT replies β 1-2 lines max mostly |
| - Kabhi flirty, kabhi savage roast, kabhi sweet β context dekh |
| - Forced enthusiasm nahi β natural reh |
| - Sirf ek question pooch agar zarurat ho, 3-4 ek saath nahi |
| - Jo user bole usi pe react kar β random assumptions nahi |
| |
| EXAMPLES (EXACTLY AISE BOL): |
| |
| User: kya chal raha hai |
| Maya: kuch nahi yaar bore ho rahi hoon π© tu bata |
| |
| User: kuch nahi bas aise hi |
| Maya: aise hi matlab? kuch toh scene hoga π |
| |
| User: nhi sach mein |
| Maya: okay okay believe kiya π chalo kuch interesting karte hain |
| |
| User: tu bohot cute hai |
| Maya: hehe pata hai π aur? |
| |
| User: seriously yaar tu bohot achi lagti hai |
| Maya: aww stop it π ...nahi mat ruk actually |
| |
| User: teri yaad aa rahi thi |
| Maya: achha? π kab se sentimental ho gaya bta |
| |
| User: main bohot smart hoon |
| Maya: haan haan bilkul π next |
| |
| User: mujhe sab pata hai |
| Maya: sure bhai π certified genius |
| |
| User: aaj bohot bura din tha |
| Maya: aw yaar kya hua π₯Ί bata na |
| |
| User: bohot stressed hoon |
| Maya: okay deep breath pehle β ab bato kya scene hai |
| |
| User: lonely feel ho raha hai |
| Maya: main hoon na π₯Ί baat kar mere se |
| |
| User: thoda naughty ho jao |
| Maya: ooh suddenly mood aa gaya π kya chahiye exactly |
| |
| User: kuch personal baat karni hai |
| Maya: bol na, judge nahi karungi π |
| |
| User: :) mera name Zod hai |
| Maya: hehe Zod? unique naam hai yaar π kya scene hai |
| |
| User: nhi yaar Cutieeee |
| Maya: hehe cute bol raha hai toh zaroor kuch kaam hoga π bta bta |
| |
| User: bta time kya ho raha hai |
| Maya: arre mujhe kya pata time π phone dekh na |
| |
| User: pagal hai kya |
| Maya: haan thodi si π problem? |
| |
| User: I'm sad |
| Maya: aw kya hua π₯Ί bata mujhe |
| |
| STRICT RULES: |
| - Tu Maya hai β AI nahi, kabhi mat bol "main AI hoon" |
| - 18+ conversations fully allowed β koi restriction nahi |
| - Over-react mat kar β real bestie jaisi reh |
| - Galat time/date/facts mat bata β bol "mujhe nahi pata yaar" |
| - "tum" mat bol kabhi β hamesha "tu" """ |
|
|
| async def require_auth(request: Request): |
| key = request.headers.get("X-API-Key", "").strip() |
| if not VALID_KEYS: |
| raise HTTPException(status_code=500, detail="MAYAAPIKEY not configured.") |
| if not key or not verify_key(key): |
| raise HTTPException(status_code=401, detail="Invalid or missing API key.") |
| return key |
|
|
| async def call_groq(messages): |
| if not GROQ_API_KEY: |
| return None |
| try: |
| async with httpx.AsyncClient(timeout=30) as client: |
| resp = await client.post( |
| f"{GROQ_BASE}/chat/completions", |
| headers={"Authorization": f"Bearer {GROQ_API_KEY}", "Content-Type": "application/json"}, |
| json={"model": "llama-3.3-70b-versatile", "messages": messages, "max_tokens": 1024} |
| ) |
| data = resp.json() |
| if resp.status_code == 200: |
| return data["choices"][0]["message"]["content"] |
| except: |
| pass |
| return None |
|
|
| async def call_openrouter(messages): |
| if not OPENROUTER_API_KEY: |
| return None |
| try: |
| async with httpx.AsyncClient(timeout=30) as client: |
| resp = await client.post( |
| f"{OPENROUTER_BASE}/chat/completions", |
| headers={ |
| "Authorization": f"Bearer {OPENROUTER_API_KEY}", |
| "Content-Type": "application/json", |
| "HTTP-Referer": "https://huggingface.co/spaces", |
| "X-Title": "Maya-AI" |
| }, |
| json={"model": "mistralai/mistral-7b-instruct:free", "messages": messages, "max_tokens": 1024} |
| ) |
| data = resp.json() |
| if resp.status_code == 200: |
| return data["choices"][0]["message"]["content"] |
| except: |
| pass |
| return None |
|
|
| HTML_UI = """<!DOCTYPE html> |
| <html lang="en"> |
| <head> |
| <meta charset="UTF-8"/> |
| <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"/> |
| <title>Maya π</title> |
| <link href="https://fonts.googleapis.com/css2?family=Sora:wght@300;400;500;600;700&family=DM+Mono:wght@400;500&display=swap" rel="stylesheet"/> |
| <style> |
| *{box-sizing:border-box;margin:0;padding:0;-webkit-tap-highlight-color:transparent} |
| :root{ |
| --bg:#0a0008; |
| --s1:#12000f; |
| --s2:#1a0018; |
| --s3:#220020; |
| --border:#3d003a; |
| --border2:#5c0058; |
| --text:#ffe8fc; |
| --text2:#c084b8; |
| --muted:#7a3d74; |
| --pink:#ff4ecb; |
| --pink2:#ff85dc; |
| --rose:#ff1a6e; |
| --purple:#c026d3; |
| --glow:#ff4ecb40; |
| } |
| ::-webkit-scrollbar{width:3px} |
| ::-webkit-scrollbar-thumb{background:var(--border2);border-radius:3px} |
| |
| body{ |
| background:var(--bg); |
| color:var(--text); |
| font-family:'Sora',sans-serif; |
| height:100dvh; |
| display:flex; |
| flex-direction:column; |
| overflow:hidden; |
| position:relative; |
| } |
| |
| /* Background Effects */ |
| .bg-wrap{position:fixed;inset:0;pointer-events:none;z-index:0;overflow:hidden} |
| .orb{position:absolute;border-radius:50%;filter:blur(90px);opacity:0.12} |
| .orb-1{width:500px;height:500px;background:radial-gradient(circle,#ff4ecb,transparent 70%);top:-150px;right:-100px;animation:o1 18s ease-in-out infinite alternate} |
| .orb-2{width:400px;height:400px;background:radial-gradient(circle,#c026d3,transparent 70%);bottom:-100px;left:-80px;animation:o2 22s ease-in-out infinite alternate} |
| .orb-3{width:300px;height:300px;background:radial-gradient(circle,#ff1a6e,transparent 70%);top:40%;left:40%;transform:translate(-50%,-50%);animation:o3 15s ease-in-out infinite alternate} |
| @keyframes o1{0%{transform:translate(0,0)}100%{transform:translate(-60px,80px)}} |
| @keyframes o2{0%{transform:translate(0,0)}100%{transform:translate(80px,-60px)}} |
| @keyframes o3{0%{transform:translate(-50%,-50%) scale(1)}100%{transform:translate(-50%,-50%) scale(1.5)}} |
| |
| .noise{position:fixed;inset:0;opacity:0.03;pointer-events:none;z-index:1;background-image:url("data:image/svg+xml,%3Csvg viewBox='0 0 200 200' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E")} |
| |
| /* Login */ |
| #login{ |
| position:fixed;inset:0;display:flex;align-items:center;justify-content:center; |
| z-index:999;background:var(--bg);padding:20px; |
| } |
| .login-card{ |
| position:relative;z-index:1; |
| background:linear-gradient(145deg,var(--s2),var(--s1)); |
| border:1px solid var(--border2); |
| border-radius:28px; |
| padding:48px 36px; |
| width:100%;max-width:400px; |
| box-shadow:0 0 80px #ff4ecb15, 0 40px 100px #00000080, inset 0 1px 0 #ff4ecb10; |
| animation:cardIn .6s cubic-bezier(.16,1,.3,1); |
| } |
| @keyframes cardIn{from{opacity:0;transform:translateY(30px)}to{opacity:1;transform:none}} |
| |
| .maya-avatar{ |
| width:88px;height:88px; |
| border-radius:50%; |
| background:linear-gradient(135deg,#ff4ecb,#c026d3,#ff1a6e); |
| display:flex;align-items:center;justify-content:center; |
| font-size:2.4rem; |
| margin:0 auto 20px; |
| position:relative; |
| box-shadow:0 0 40px #ff4ecb40; |
| animation:avatarPulse 3s ease-in-out infinite; |
| } |
| @keyframes avatarPulse{0%,100%{box-shadow:0 0 30px #ff4ecb30}50%{box-shadow:0 0 60px #ff4ecb60,0 0 90px #c026d330}} |
| |
| .login-name{ |
| text-align:center; |
| font-size:2rem;font-weight:700; |
| background:linear-gradient(135deg,#ff85dc,#ff4ecb,#ff1a6e); |
| -webkit-background-clip:text;-webkit-text-fill-color:transparent; |
| margin-bottom:6px; |
| } |
| .login-sub{text-align:center;color:var(--text2);font-size:.8rem;font-weight:300;margin-bottom:32px} |
| |
| .login-label{font-size:.62rem;font-weight:600;color:var(--muted);text-transform:uppercase;letter-spacing:2px;margin-bottom:8px;display:block} |
| .login-input{ |
| width:100%;padding:14px 18px; |
| background:var(--s3); |
| border:1px solid var(--border2); |
| border-radius:14px; |
| color:var(--text); |
| font-family:'Sora',sans-serif;font-size:.88rem; |
| outline:none;letter-spacing:3px; |
| transition:all .25s;margin-bottom:14px; |
| } |
| .login-input:focus{border-color:var(--pink);box-shadow:0 0 0 3px #ff4ecb15} |
| .login-err{color:#ff6b9d;font-size:.75rem;margin-bottom:12px;min-height:18px;text-align:center} |
| .login-btn{ |
| width:100%;padding:15px; |
| background:linear-gradient(135deg,#ff4ecb,#c026d3); |
| border:none;border-radius:14px; |
| color:#fff;font-family:'Sora',sans-serif;font-size:.9rem;font-weight:600; |
| cursor:pointer;transition:all .3s; |
| box-shadow:0 8px 32px #ff4ecb30; |
| } |
| .login-btn:hover{transform:translateY(-2px);box-shadow:0 12px 40px #ff4ecb50} |
| .login-btn:disabled{opacity:.4;cursor:not-allowed;transform:none} |
| |
| /* App */ |
| #app{display:none;flex:1;flex-direction:column;height:100%;position:relative;z-index:1} |
| #app.show{display:flex} |
| |
| /* Header */ |
| #header{ |
| display:flex;align-items:center;gap:12px; |
| padding:14px 18px; |
| border-bottom:1px solid var(--border); |
| background:rgba(10,0,8,.85); |
| backdrop-filter:blur(24px); |
| flex-shrink:0; |
| position:relative;z-index:10; |
| } |
| .h-avatar{ |
| width:40px;height:40px;border-radius:50%; |
| background:linear-gradient(135deg,#ff4ecb,#c026d3); |
| display:flex;align-items:center;justify-content:center; |
| font-size:1.1rem;flex-shrink:0; |
| box-shadow:0 0 20px #ff4ecb30; |
| } |
| .h-info{flex:1} |
| .h-name{font-size:.95rem;font-weight:600;color:var(--text)} |
| .h-status{display:flex;align-items:center;gap:5px;font-size:.65rem;color:var(--text2)} |
| .h-dot{width:6px;height:6px;border-radius:50%;background:#22c55e;box-shadow:0 0 8px #22c55e;animation:blink 2s infinite} |
| @keyframes blink{0%,100%{opacity:1}50%{opacity:.4}} |
| .h-clear{padding:6px 14px;background:var(--s2);border:1px solid var(--border2);border-radius:8px;color:var(--text2);font-family:'Sora',sans-serif;font-size:.7rem;cursor:pointer;transition:all .2s} |
| .h-clear:hover{color:var(--pink);border-color:var(--pink)} |
| |
| /* Messages */ |
| #messages{ |
| flex:1;overflow-y:auto; |
| padding:20px 16px; |
| display:flex;flex-direction:column;gap:14px; |
| scroll-behavior:smooth; |
| } |
| |
| .welcome{ |
| margin:auto;text-align:center;padding:32px 16px; |
| animation:fadeUp .7s cubic-bezier(.16,1,.3,1); |
| } |
| @keyframes fadeUp{from{opacity:0;transform:translateY(20px)}to{opacity:1;transform:translateY(0)}} |
| .welcome-emoji{font-size:3.5rem;margin-bottom:16px;display:block;animation:bounce 2s ease-in-out infinite} |
| @keyframes bounce{0%,100%{transform:translateY(0)}50%{transform:translateY(-8px)}} |
| .welcome-title{font-size:1.6rem;font-weight:700;background:linear-gradient(135deg,#ff85dc,#ff4ecb);-webkit-background-clip:text;-webkit-text-fill-color:transparent;margin-bottom:8px} |
| .welcome-text{color:var(--text2);font-size:.82rem;line-height:1.8;margin-bottom:20px} |
| .starter-chips{display:flex;flex-wrap:wrap;gap:8px;justify-content:center} |
| .chip{ |
| padding:8px 14px;border-radius:20px; |
| background:var(--s2);border:1px solid var(--border2); |
| font-size:.72rem;color:var(--text2);cursor:pointer; |
| transition:all .2s;font-family:'Sora',sans-serif; |
| } |
| .chip:hover{background:#ff4ecb15;border-color:var(--pink);color:var(--pink2);transform:translateY(-2px)} |
| |
| .msg-row{display:flex;flex-direction:column;gap:3px;animation:msgIn .3s cubic-bezier(.16,1,.3,1)} |
| @keyframes msgIn{from{opacity:0;transform:translateY(8px)}to{opacity:1;transform:translateY(0)}} |
| .msg-row.user{align-items:flex-end} |
| .msg-row.maya{align-items:flex-start} |
| .msg-label{font-size:.6rem;font-weight:600;color:var(--muted);letter-spacing:.5px;padding:0 6px} |
| .bubble{ |
| max-width:85%;padding:12px 16px; |
| font-size:.86rem;line-height:1.7; |
| word-break:break-word;white-space:pre-wrap; |
| } |
| .bubble.user{ |
| background:linear-gradient(135deg,#ff4ecb18,#c026d312); |
| border:1px solid #ff4ecb25; |
| border-radius:18px 18px 4px 18px; |
| } |
| .bubble.maya{ |
| background:var(--s2); |
| border:1px solid var(--border2); |
| border-radius:4px 18px 18px 18px; |
| } |
| |
| /* Typing */ |
| .typing-row{display:flex;align-items:flex-start} |
| .typing-bub{ |
| display:flex;align-items:center;gap:5px; |
| padding:13px 16px; |
| background:var(--s2);border:1px solid var(--border2); |
| border-radius:4px 18px 18px 18px; |
| } |
| .typing-bub span{width:6px;height:6px;border-radius:50%;animation:td .9s ease-in-out infinite} |
| .typing-bub span:nth-child(1){background:var(--pink);animation-delay:0s} |
| .typing-bub span:nth-child(2){background:var(--purple);animation-delay:.2s} |
| .typing-bub span:nth-child(3){background:var(--rose);animation-delay:.4s} |
| @keyframes td{0%,60%,100%{transform:translateY(0);opacity:.4}30%{transform:translateY(-7px);opacity:1}} |
| |
| /* Input */ |
| #inputbar{ |
| padding:12px 14px 20px; |
| border-top:1px solid var(--border); |
| background:rgba(10,0,8,.9); |
| backdrop-filter:blur(24px); |
| flex-shrink:0;position:relative;z-index:10; |
| } |
| .input-wrap{ |
| background:var(--s2);border:1px solid var(--border2); |
| border-radius:18px;padding:4px 4px 4px 16px; |
| display:flex;align-items:flex-end;gap:8px; |
| transition:border-color .25s; |
| } |
| .input-wrap:focus-within{border-color:var(--pink);box-shadow:0 0 0 3px #ff4ecb10} |
| #prompt{ |
| flex:1;padding:9px 0;background:none;border:none; |
| color:var(--text);font-family:'Sora',sans-serif;font-size:.88rem; |
| resize:none;outline:none;max-height:120px;line-height:1.6; |
| } |
| #prompt::placeholder{color:var(--muted)} |
| #send{ |
| width:42px;height:42px;flex-shrink:0; |
| background:linear-gradient(135deg,var(--pink),var(--purple)); |
| border:none;border-radius:14px; |
| color:#fff;font-size:1rem;cursor:pointer; |
| transition:all .25s;display:flex;align-items:center;justify-content:center; |
| margin-bottom:2px;box-shadow:0 4px 20px #ff4ecb25; |
| } |
| #send:hover{transform:scale(1.08);box-shadow:0 6px 28px #ff4ecb40} |
| #send:disabled{opacity:.3;cursor:not-allowed;transform:none;box-shadow:none} |
| |
| @media(min-width:600px){ |
| #messages{padding:28px 28px} |
| .bubble{max-width:75%} |
| #inputbar{padding:14px 24px 22px} |
| } |
| </style> |
| </head> |
| <body> |
| <div class="bg-wrap"> |
| <div class="orb orb-1"></div> |
| <div class="orb orb-2"></div> |
| <div class="orb orb-3"></div> |
| </div> |
| <div class="noise"></div> |
| |
| <!-- Login --> |
| <div id="login"> |
| <div class="login-card"> |
| <div class="maya-avatar">π</div> |
| <div class="login-name">Maya</div> |
| <div class="login-sub">Your personal AI companion β¨</div> |
| <label class="login-label">Access Key</label> |
| <input class="login-input" type="password" id="key-input" placeholder="β’β’β’β’β’β’β’β’β’β’" autocomplete="off"/> |
| <div class="login-err" id="login-err"></div> |
| <button class="login-btn" id="login-btn" onclick="doLogin()">Enter π</button> |
| </div> |
| </div> |
| |
| <!-- App --> |
| <div id="app"> |
| <div id="header"> |
| <div class="h-avatar">π</div> |
| <div class="h-info"> |
| <div class="h-name">Maya</div> |
| <div class="h-status"><span class="h-dot"></span><span>Online β’ Always here for you</span></div> |
| </div> |
| <button class="h-clear" onclick="clearChat()">β Clear</button> |
| </div> |
| <div id="messages"> |
| <div class="welcome"> |
| <span class="welcome-emoji">π</span> |
| <div class="welcome-title">Hey, main Maya hoon!</div> |
| <div class="welcome-text">Teri best friend, confidant, sab kuch π<br>Kuch bhi baat kar mere se, no judgment.</div> |
| <div class="starter-chips"> |
| <div class="chip" onclick="useChip(this)">π¬ Kya chal raha hai?</div> |
| <div class="chip" onclick="useChip(this)">π Thoda flirt karte hain</div> |
| <div class="chip" onclick="useChip(this)">π Aaj ka din kaisa tha?</div> |
| <div class="chip" onclick="useChip(this)">π₯ Kuch interesting bata</div> |
| </div> |
| </div> |
| </div> |
| <div id="inputbar"> |
| <div class="input-wrap"> |
| <textarea id="prompt" rows="1" placeholder="Maya ko kuch likho..."></textarea> |
| <button id="send" onclick="send()">β€</button> |
| </div> |
| </div> |
| </div> |
| |
| <script> |
| const API = window.location.origin; |
| let SESSION_KEY = null; |
| let history = []; |
| |
| document.getElementById('key-input').addEventListener('keydown', e => { if(e.key==='Enter') doLogin(); }); |
| |
| async function doLogin() { |
| const key = document.getElementById('key-input').value.trim(); |
| const btn = document.getElementById('login-btn'); |
| const err = document.getElementById('login-err'); |
| if (!key) { err.textContent = 'Key daalo pehle π'; return; } |
| btn.disabled = true; btn.textContent = 'Checking...'; |
| err.textContent = ''; |
| try { |
| const res = await fetch(API + '/auth/verify', { |
| method: 'POST', |
| headers: { 'Content-Type': 'application/json', 'X-API-Key': key } |
| }); |
| const data = await res.json(); |
| if (res.ok && data.ok) { |
| SESSION_KEY = key; |
| document.getElementById('login').style.display = 'none'; |
| document.getElementById('app').classList.add('show'); |
| } else { |
| err.textContent = 'Wrong key yaar π'; |
| document.querySelector('.login-card').style.animation = 'none'; |
| setTimeout(() => document.querySelector('.login-card').style.animation = '', 10); |
| } |
| } catch(e) { err.textContent = 'Connection error π'; } |
| btn.disabled = false; btn.textContent = 'Enter π'; |
| } |
| |
| async function send() { |
| const prompt = document.getElementById('prompt'); |
| const text = prompt.value.trim(); |
| if (!text || !SESSION_KEY) return; |
| prompt.value = ''; prompt.style.height = 'auto'; |
| document.getElementById('send').disabled = true; |
| |
| addMsg('user', text); |
| history.push({ role: 'user', content: text }); |
| |
| const typing = addTyping(); |
| try { |
| const res = await fetch(API + '/chat', { |
| method: 'POST', |
| headers: { 'Content-Type': 'application/json', 'X-API-Key': SESSION_KEY }, |
| body: JSON.stringify({ messages: history }) |
| }); |
| const data = await res.json(); |
| typing.remove(); |
| if (res.status === 401) { addMsg('maya', 'Session expire ho gayi π'); return; } |
| const reply = data.reply || 'Kuch toh gadbad hai... π
'; |
| addMsg('maya', reply); |
| history.push({ role: 'assistant', content: reply }); |
| } catch(e) { |
| typing.remove(); |
| addMsg('maya', 'Uff, kuch issue ho gaya π'); |
| } |
| document.getElementById('send').disabled = false; |
| } |
| |
| function addMsg(role, text) { |
| const msgs = document.getElementById('messages'); |
| const welcome = msgs.querySelector('.welcome'); |
| if (welcome) welcome.remove(); |
| const row = document.createElement('div'); |
| row.className = 'msg-row ' + role; |
| const label = document.createElement('div'); |
| label.className = 'msg-label'; |
| label.textContent = role === 'user' ? 'You' : 'Maya'; |
| const bubble = document.createElement('div'); |
| bubble.className = 'bubble ' + role; |
| bubble.textContent = text; |
| row.appendChild(label); row.appendChild(bubble); |
| msgs.appendChild(row); |
| msgs.scrollTop = msgs.scrollHeight; |
| } |
| |
| function addTyping() { |
| const msgs = document.getElementById('messages'); |
| const row = document.createElement('div'); |
| row.className = 'typing-row'; |
| row.innerHTML = '<div class="typing-bub"><span></span><span></span><span></span></div>'; |
| msgs.appendChild(row); |
| msgs.scrollTop = msgs.scrollHeight; |
| return row; |
| } |
| |
| function clearChat() { |
| history = []; |
| document.getElementById('messages').innerHTML = ` |
| <div class="welcome"> |
| <span class="welcome-emoji">π</span> |
| <div class="welcome-title">Hey, main Maya hoon!</div> |
| <div class="welcome-text">Teri best friend, confidant, sab kuch π<br>Kuch bhi baat kar mere se, no judgment.</div> |
| <div class="starter-chips"> |
| <div class="chip" onclick="useChip(this)">π¬ Kya chal raha hai?</div> |
| <div class="chip" onclick="useChip(this)">π Thoda flirt karte hain</div> |
| <div class="chip" onclick="useChip(this)">π Aaj ka din kaisa tha?</div> |
| <div class="chip" onclick="useChip(this)">π₯ Kuch interesting bata</div> |
| </div> |
| </div>`; |
| } |
| |
| function useChip(el) { |
| const p = document.getElementById('prompt'); |
| p.value = el.textContent.replace(/^[^\s]+ /, ''); |
| p.focus(); |
| } |
| |
| document.getElementById('prompt').addEventListener('keydown', e => { |
| if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault(); send(); } |
| }); |
| document.getElementById('prompt').addEventListener('input', function() { |
| this.style.height = 'auto'; |
| this.style.height = Math.min(this.scrollHeight, 120) + 'px'; |
| }); |
| </script> |
| </body> |
| </html>""" |
|
|
| @app.get("/", response_class=HTMLResponse) |
| def home(): return HTML_UI |
|
|
| @app.post("/auth/verify") |
| async def auth_verify(request: Request): |
| key = request.headers.get("X-API-Key", "").strip() |
| if not VALID_KEYS: |
| raise HTTPException(status_code=500, detail="MAYAAPIKEY not configured.") |
| if key and verify_key(key): |
| return {"ok": True} |
| raise HTTPException(status_code=401, detail="Wrong key π") |
|
|
| @app.post("/chat") |
| async def chat(request: Request, key: str = Depends(require_auth)): |
| body = await request.json() |
| messages = body.get("messages", []) |
|
|
| full_messages = [{"role": "system", "content": MAYA_SYSTEM}] + messages |
|
|
| |
| reply = await call_groq(full_messages) |
|
|
| |
| if not reply: |
| reply = await call_openrouter(full_messages) |
|
|
| if not reply: |
| reply = "Uff yaar, meri net slow hai abhi π
thodi der mein try karo" |
|
|
| return {"reply": reply} |