Spaces:
Sleeping
Sleeping
| from fastapi import FastAPI | |
| from fastapi.responses import StreamingResponse, HTMLResponse | |
| from pydantic import BaseModel | |
| from transformers import AutoTokenizer, AutoModelForCausalLM, TextIteratorStreamer | |
| import torch | |
| import threading | |
| app = FastAPI() | |
| MODEL_NAME = "unsloth/gemma-3-1b-pt" | |
| tokenizer = AutoTokenizer.from_pretrained(MODEL_NAME) | |
| model = AutoModelForCausalLM.from_pretrained( | |
| MODEL_NAME, | |
| device_map="cpu", | |
| torch_dtype=torch.float32, | |
| low_cpu_mem_usage=True | |
| ) | |
| class Request(BaseModel): | |
| message: str | |
| # ========================= | |
| # FRONTEND HTML (TON CODE) | |
| # ========================= | |
| def home(): | |
| return """ | |
| <!DOCTYPE html> | |
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <title>Server Status</title> | |
| <style> | |
| @import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;600;700&display=swap'); | |
| body { | |
| margin: 0; | |
| height: 100vh; | |
| display: flex; | |
| justify-content: center; | |
| align-items: center; | |
| background: radial-gradient(circle at top, #0b1020, #030712); | |
| font-family: Inter, sans-serif; | |
| color: white; | |
| overflow: hidden; | |
| } | |
| /* background glow */ | |
| .glow { | |
| position: absolute; | |
| width: 700px; | |
| height: 700px; | |
| background: radial-gradient(circle, rgba(99,102,241,0.25), transparent 60%); | |
| filter: blur(70px); | |
| animation: float 6s ease-in-out infinite; | |
| } | |
| @keyframes float { | |
| 0%,100% { transform: translateY(0px); } | |
| 50% { transform: translateY(-50px); } | |
| } | |
| /* main card */ | |
| .card { | |
| position: relative; | |
| text-align: center; | |
| padding: 60px 70px; | |
| border-radius: 28px; | |
| background: rgba(255,255,255,0.05); | |
| border: 1px solid rgba(255,255,255,0.08); | |
| backdrop-filter: blur(22px); | |
| box-shadow: 0 30px 100px rgba(0,0,0,0.7); | |
| animation: fadeIn 1.2s ease; | |
| } | |
| @keyframes fadeIn { | |
| from { opacity: 0; transform: scale(0.9); } | |
| to { opacity: 1; transform: scale(1); } | |
| } | |
| /* STATUS TEXT */ | |
| .status { | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| gap: 14px; | |
| font-size: 30px; | |
| font-weight: 600; | |
| letter-spacing: 1px; | |
| } | |
| /* ULTRA DOT ANIMATION */ | |
| .dot { | |
| width: 14px; | |
| height: 14px; | |
| background: #10b981; | |
| border-radius: 50%; | |
| position: relative; | |
| box-shadow: 0 0 15px rgba(16,185,129,0.9); | |
| animation: flicker 2.2s ease-in-out infinite; | |
| } | |
| .dot::before { | |
| content: ""; | |
| position: absolute; | |
| top: 50%; | |
| left: 50%; | |
| width: 100%; | |
| height: 100%; | |
| background: #10b981; | |
| border-radius: 50%; | |
| transform: translate(-50%, -50%); | |
| animation: pulse1 1.8s ease-out infinite; | |
| opacity: 0.6; | |
| } | |
| .dot::after { | |
| content: ""; | |
| position: absolute; | |
| top: 50%; | |
| left: 50%; | |
| width: 100%; | |
| height: 100%; | |
| background: #34d399; | |
| border-radius: 50%; | |
| transform: translate(-50%, -50%); | |
| animation: pulse2 2.4s ease-out infinite; | |
| opacity: 0.4; | |
| } | |
| @keyframes flicker { | |
| 0%, 100% { transform: scale(1); opacity: 1; } | |
| 50% { transform: scale(0.92); opacity: 0.85; } | |
| } | |
| @keyframes pulse1 { | |
| 0% { | |
| transform: translate(-50%, -50%) scale(1); | |
| opacity: 0.7; | |
| } | |
| 100% { | |
| transform: translate(-50%, -50%) scale(3.8); | |
| opacity: 0; | |
| } | |
| } | |
| @keyframes pulse2 { | |
| 0% { | |
| transform: translate(-50%, -50%) scale(1); | |
| opacity: 0.5; | |
| } | |
| 100% { | |
| transform: translate(-50%, -50%) scale(5.5); | |
| opacity: 0; | |
| } | |
| } | |
| .subtitle { | |
| margin-top: 14px; | |
| font-size: 14px; | |
| color: #9ca3af; | |
| letter-spacing: 0.4px; | |
| } | |
| .badge { | |
| margin-top: 22px; | |
| display: inline-block; | |
| padding: 6px 14px; | |
| border-radius: 999px; | |
| font-size: 12px; | |
| background: rgba(99,102,241,0.15); | |
| border: 1px solid rgba(99,102,241,0.35); | |
| color: #a5b4fc; | |
| } | |
| /* particles */ | |
| .particle { | |
| position: absolute; | |
| width: 6px; | |
| height: 6px; | |
| background: rgba(255,255,255,0.08); | |
| border-radius: 50%; | |
| animation: move 10s linear infinite; | |
| } | |
| @keyframes move { | |
| from { transform: translateY(0px); opacity: 0; } | |
| 50% { opacity: 1; } | |
| to { transform: translateY(-600px); opacity: 0; } | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <div class="glow"></div> | |
| <div class="particle" style="left:10%; top:80%;"></div> | |
| <div class="particle" style="left:30%; top:90%;"></div> | |
| <div class="particle" style="left:70%; top:85%;"></div> | |
| <div class="particle" style="left:90%; top:95%;"></div> | |
| <div class="card"> | |
| <div class="status"> | |
| <div class="dot"></div> | |
| Server Running | |
| </div> | |
| </div> | |
| </body> | |
| </html> | |
| """ | |
| # ========================= | |
| # CHAT STREAM (FIXÉ) | |
| # ========================= | |
| def chat_stream(req: Request): | |
| # ✅ IMPORTANT : format chat propre | |
| messages = [ | |
| {"role": "user", "content": req.message} | |
| ] | |
| streamer = TextIteratorStreamer( | |
| tokenizer, | |
| skip_prompt=True, | |
| skip_special_tokens=True | |
| ) | |
| def generate(): | |
| model.generate( | |
| **inputs, | |
| max_new_tokens=200, | |
| temperature=0.4, | |
| top_p=0.9, | |
| repetition_penalty=1.2, | |
| do_sample=True, | |
| streamer=streamer | |
| ) | |
| thread = threading.Thread(target=generate) | |
| thread.start() | |
| def token_stream(): | |
| for text in streamer: | |
| yield text | |
| return StreamingResponse(token_stream(), media_type="text/plain") |