import os os.environ.setdefault("ASTRANEXUS_FINETUNED", "1") # run the SP3 fine-tuned encoder import astranexus.config # noqa: F401 — loads any HF Space secrets from env import os.path import gradio as gr from fastapi import FastAPI from fastapi.responses import HTMLResponse, FileResponse, JSONResponse from astranexus.ui.app import build_app, DARK app = FastAPI(title="AstraNexus") # 'Sign in with Google' web flow (live mode uses the Gmail REST API over 443, # because the Space firewalls outbound IMAP). No-op if the OAuth client secrets # aren't set, so the fixture demo still runs. from astranexus.web import google_oauth google_oauth.attach(app) LANDING = """ AstraNexus

✦ AstraNexus

Your inbox, as a navigable galaxy.

Unsupervised email organizer. A fine-tuned multimodal encoder (MiniLM text + SigLIP vision, LoRA) embeds every email; HDBSCAN groups them into constellations that NVIDIA Nemotron-3-Nano-4B names and reasons over; threads spanning accounts surface as wormholes. No rules, no folders — the structure is discovered.

Launch the demo →
Color — the constellation (topic the encoder grouped it into). Each has a Nemotron-named hub star.
Size — unread (big) vs read. Brightness ∝ urgency.
Gold halo — the email carries an image (where the multimodal model earns its keep).
Pink dashed — a wormhole: one thread across two accounts.

Under the hood: the whole AI pipeline runs on a Modal GPU as a LangGraph agentic graph — fetch→embed→cluster→label→judge→serialize. An LLM-as-judge (Nemotron) scores the labels and loops back to relabel until they pass, then it writes an open agent trace — view the latest trace.json →. An in-app A/B toggle pits the fine-tuned encoder against off-the-shelf SigLIP; layered fallbacks (Modal → NIM-hosted Nemotron → keyword heuristic) keep it alive.

About the data: the default fixture is a synthetic dataset built only for demo/testing — no real email. Google restricts reading people's mail, so live Gmail is invite-only (approved test accounts / my own mailboxes); session-only, never stored.

🧠 Nemotron-3-Nano-4B⚡ Modal GPU 🕸 LangGraph + judge loop🎯 Fine-tuned encoder + A/B 📡 Open agent trace

Fine-tuned encoder: DriptoBhattacharyya/astranexus-mm-encoder · built for the Build Small hackathon.

""" @app.get("/", response_class=HTMLResponse) def home(): return LANDING @app.get("/trace.json") def trace(): # the LangGraph agent trace from the most recent map (Open Trace) if os.path.exists("trace.json"): return FileResponse("trace.json", media_type="application/json") return JSONResponse({"detail": "Run a map first to generate the agent trace."}, status_code=404) demo = build_app(theme=DARK) # custom dark Gradio mounted under /app app = gr.mount_gradio_app(app, demo, path="/app")