from fastapi import FastAPI, HTTPException, WebSocket, WebSocketDisconnect, Query, Request from fastapi.responses import StreamingResponse, HTMLResponse, Response, RedirectResponse, JSONResponse, FileResponse from fastapi.middleware.cors import CORSMiddleware from pydantic import BaseModel import uvicorn, asyncio, json, os, requests as req, time, concurrent.futures from brain import Brain from crawler import crawl as do_crawl, CreditLedger from auth import ( get_current_user, get_user_db, create_session, google_login_url, exchange_code, get_google_userinfo, COOKIE_NAME, GOOGLE_CLIENT_ID, DEFAULT_CREDITS, is_admin_email, ) from errors import get_logger log = get_logger("app") app = FastAPI() app.add_middleware(CORSMiddleware, allow_origins=["*"], allow_methods=["*"], allow_headers=["*"]) brain: Brain | None = None BASE = "https://amogaddy-generai.hf.space" @app.get("/logo.png") def logo(): return FileResponse("logo.png", media_type="image/png") # ── Layout condiviso ─────────────────────────────────────────────────────────── def page(title, content, active=""): nav = [("/","Home","Home"),("/ragno","Ragno","Rete Ragno"), ("/ws/ui","Live","Live"),("/worldmonitor/","🌍","World Monitor"), ("/status/ui","Stats","Stato"),("/commands","CMD","Comandi"),("/docs","API","API"), ("/about","Info","Come funziona")] nav_html = "".join(f'{i} {l}' for h,i,l in nav) return f"""
Il motore di ricerca puro.
Nessun intermediario — solo il web.
Accedendo ricevi 10.000 crediti gratuiti.
1 credito = 1 pagina esplorata dal ragno.
Il login non funziona? Prova qui
Il tuo assistente AI specializzato in italiano.
Cerco sul web e apprendo autonomamente per darti la risposta migliore.
| Modulo | Stato | Dettagli |
|---|---|---|
| AI Cervello | {"Operativo" if ok else "Avvio in corso..."} | Ricerca e risposta |
| KB Memoria | Autonoma | {kb} concetti appresi |
| Web Ricerca Web | Chromium | Browser headless (Playwright) |
| Cloud MEGA | Sincronizzato | Backup DB nel cloud |
{user.get("email","")}
1 credito = 1 pagina esplorata dalla Rete Ragno
| Campo | Valore |
|---|---|
| Nome | {user.get("name","")} |
| {user.get("email","")} | |
| Iscritto dal | {created_str} |
| ID account | {user.get("id","")} |
Motore di ricerca puro — il ragno esplora il web autonomamente, saltando di link in link. Nessun intermediario. Ogni pagina visitata costa 1 credito.
Analisi in tempo reale delle attività automatiche e della stabilità dei servizi.
Sincronizzazione dati in corso...
Architettura del sistema e licenze dei componenti usati.
GenerAI è un assistente AI in italiano con memoria locale e ricerca web autonoma. Il backend è scritto in Python (FastAPI). Non usa un motore di ricerca esterno come Google o Bing tramite API a pagamento: la ricerca web avviene tramite un browser headless (Chromium via Playwright) che naviga direttamente Bing, con Wikipedia come fallback se non trova risultati utili.
| Componente | Cosa fa |
|---|---|
| Cervello (brain.py) | Decide se rispondere dalla memoria locale, cercare sul web, o usare un modello LLM esterno |
| Memoria (knowledge_base.py) | Database vettoriale locale (ChromaDB) che impara dalle domande frequenti |
| Ricerca web (scraper.py) | Chromium headless (Playwright) su Bing, con fallback Wikipedia |
| Rete Ragno (crawler.py) | Esplorazione autonoma del web salto per salto, a partire da Wikipedia, con crediti per utente |
| Login (auth.py) | Autenticazione Google OAuth, crediti per account |
| Backup (mega_sync.py) | Sincronizzazione del database su MEGA |
| World Monitor | Dashboard di intelligence globale (notizie, mappe, dati geopolitici) integrata come componente separato — vedi licenza sotto |
Il codice originale di GenerAI (questo assistente: brain, scraper, crawler, knowledge base, interfaccia) è rilasciato con licenza MIT — puoi vederlo, copiarlo e modificarlo liberamente.
Il componente World Monitor, se presente in questo Space, è un progetto di terze parti di koala73/worldmonitor, distribuito con licenza AGPL-3.0-only. L'AGPL richiede che, essendo un servizio consultabile in rete, il codice sorgente (comprese eventuali modifiche fatte per farlo girare qui) resti disponibile a chi lo usa — il codice sorgente originale è pubblico al link sopra. GenerAI non rivendica alcuna proprietà su World Monitor: è integrato così com'è, nel rispetto della sua licenza.
Copia i comandi per PowerShell o CMD. Scarica gli script completi con menu interattivo.
{sections} """ return HTMLResponse(page("Comandi", content, "/commands")) # ── Script downloads ─────────────────────────────────────────────────────────── @app.get("/scripts/powershell") def script_ps(): s = f"""# GenerAI — Script PowerShell # Come usarlo: apri PowerShell, vai nella cartella dove hai salvato il file, scrivi: .\\generai.ps1 $BASE = "{BASE}" function Ask($d) {{ $body = @{{prompt = $d}} | ConvertTo-Json $r = Invoke-RestMethod -Uri "$BASE/ask" -Method POST -ContentType "application/json" -Body $body Write-Host "" Write-Host "Risposta:" -ForegroundColor Cyan Write-Host $r.result Write-Host "(Fonte: $($r.status))" -ForegroundColor DarkGray }} function AskStream($d) {{ $body = @{{prompt = $d}} | ConvertTo-Json -Compress Write-Host "Cosa sta facendo la AI:" -ForegroundColor Yellow curl -s -N -X POST "$BASE/ask/stream" -H "Content-Type: application/json" -d $body }} function Stato {{ $r = Invoke-RestMethod "$BASE/" Write-Host "AI accesa: $($r.status)" -ForegroundColor Green Write-Host "Cose in memoria: $($r.kb_size)" }} function Feedback($positivo) {{ Invoke-RestMethod -Uri "$BASE/feedback?positive=$positivo" -Method POST | Out-Null if ($positivo -eq "true") {{ Write-Host "Grazie! La AI imparera' da questa risposta." -ForegroundColor Green }} else {{ Write-Host "Capito, la AI cerchera' di migliorare." -ForegroundColor Yellow }} }} while ($true) {{ Write-Host "" Write-Host "========== GENERAI ==========" -ForegroundColor Magenta Write-Host "1. Fai una domanda" Write-Host "2. Fai una domanda (vedi i passi in diretta)" Write-Host "3. Controlla se la AI e' accesa" Write-Host "4. Questa risposta era utile" Write-Host "5. Questa risposta non era utile" Write-Host "0. Esci" $s = Read-Host "Cosa vuoi fare?" switch ($s) {{ "1" {{ Ask (Read-Host "Scrivi la tua domanda") }} "2" {{ AskStream (Read-Host "Scrivi la tua domanda") }} "3" {{ Stato }} "4" {{ Feedback "true" }} "5" {{ Feedback "false" }} "0" {{ Write-Host "A presto!"; exit }} default {{ Write-Host "Non ho capito, riprova." -ForegroundColor Red }} }} }} """ return Response(content=s, media_type="text/plain", headers={"Content-Disposition": "attachment; filename=generai.ps1"}) @app.get("/scripts/batch") def script_bat(): s = f"""@echo off chcp 65001 >nul set BASE={BASE} :menu echo. echo ========== GENERAI ========== echo 1. Fai una domanda echo 2. Fai una domanda (vedi i passi in diretta) echo 3. Controlla se la AI e' accesa echo 0. Esci set /p s=Cosa vuoi fare? if "%s%"=="1" goto ask if "%s%"=="2" goto stream if "%s%"=="3" goto stato if "%s%"=="0" goto fine echo Non ho capito, riprova. goto menu :ask set /p d=Scrivi la tua domanda: curl -s -X POST "%BASE%/ask" -H "Content-Type: application/json" -d "{{\\"prompt\\":\\"%d%\\"}}" echo. goto menu :stream set /p d=Scrivi la tua domanda: curl -N -X POST "%BASE%/ask/stream" -H "Content-Type: application/json" -d "{{\\"prompt\\":\\"%d%\\"}}" echo. goto menu :stato curl -s "%BASE%/" echo. goto menu :fine echo A presto! exit /b """ return Response(content=s, media_type="text/plain", headers={"Content-Disposition": "attachment; filename=generai.bat"}) # ── Ask / Feedback ───────────────────────────────────────────────────────────── class AskRequest(BaseModel): prompt: str def _schedule_mega_upload(): email = os.environ.get("MEGA_EMAIL", "") password = os.environ.get("MEGA_PASSWORD", "") if email and password: from mega_sync import upload_db asyncio.create_task(asyncio.to_thread(upload_db, email, password)) @app.on_event("startup") async def startup(): global brain email = os.environ.get("MEGA_EMAIL", "") password = os.environ.get("MEGA_PASSWORD", "") if email and password: from mega_sync import download_db log.info("Scarico DB da MEGA...") await asyncio.to_thread(download_db, email, password) else: log.warning("MEGA_EMAIL/MEGA_PASSWORD non impostati — DB solo locale.") brain = Brain() @app.post("/ask") async def ask(request: AskRequest): if not brain: raise HTTPException(status_code=503, detail="AI non ancora pronta") answer, status = await brain.ask(request.prompt) if brain._last_doc_id: _schedule_mega_upload() return {"result": answer, "status": status} @app.post("/ask/stream") async def ask_stream(request: AskRequest): if not brain: raise HTTPException(status_code=503, detail="AI non ancora pronta") async def generate(): queue: asyncio.Queue = asyncio.Queue() async def on_status(msg): await queue.put(("status", msg)) async def run(): a, s = await brain.ask(request.prompt, on_status=on_status) await queue.put(("result", a, s)) if brain._last_doc_id: _schedule_mega_upload() task = asyncio.create_task(run()) while True: item = await queue.get() if item[0] == "status": yield f"data: {json.dumps({'type':'status','message':item[1]})}\n\n" elif item[0] == "result": yield f"data: {json.dumps({'type':'result','message':item[1],'status':item[2]})}\n\n" break await task return StreamingResponse(generate(), media_type="text/event-stream", headers={"Cache-Control":"no-cache","X-Accel-Buffering":"no","Connection":"keep-alive"}) @app.post("/feedback") def feedback(positive: bool): if brain: brain.give_feedback(positive) return {"ok": True} @app.post("/search") async def search_raw(request: AskRequest): from scraper import search_and_extract try: results = await asyncio.to_thread(search_and_extract, request.prompt, max_results=8) except Exception as e: log.warning("Search raw fallita: %s", fmt_exc(e)) results = [] return {"query": request.prompt, "results": results} # ── WebSocket — sessione continua ────────────────────────────────────────────── @app.websocket("/ws/chat") async def ws_chat(ws: WebSocket): """ WebSocket per input/output continui con storia della conversazione. Il client manda: {"prompt": "tua domanda"} Il server risponde con messaggi di stato e poi la risposta finale. """ await ws.accept() history = [] # storia della conversazione nella sessione await ws.send_text(json.dumps({ "type": "connected", "message": "Connesso a GenerAI. Manda {\"prompt\": \"la tua domanda\"}." })) try: while True: raw = await ws.receive_text() try: data = json.loads(raw) except Exception: await ws.send_text(json.dumps({"type":"error","message":"JSON non valido"})) continue prompt = data.get("prompt", "").strip() if not prompt: await ws.send_text(json.dumps({"type":"error","message":"Campo 'prompt' mancante"})) continue if not brain: await ws.send_text(json.dumps({"type":"error","message":"AI non ancora pronta"})) continue # Manda aggiornamenti di stato in tempo reale async def on_status(msg: str): await ws.send_text(json.dumps({"type":"status","message":msg})) answer, status = await brain.ask(prompt, on_status=on_status) # Salva nella storia history.append({"role":"user","content":prompt}) history.append({"role":"assistant","content":answer}) await ws.send_text(json.dumps({ "type": "result", "message": answer, "status": status, "history_len": len(history) // 2, })) except WebSocketDisconnect: pass @app.get("/ws/ui", response_class=HTMLResponse) def ws_ui(): content = f"""Connessione persistente — manda quante domande vuoi senza ricaricare la pagina. La sessione ricorda tutto.