Version 1.2.2. Added usage overview. Shows token used and cost in $.
This commit is contained in:
@@ -2,9 +2,38 @@
|
||||
main.py — FastAPI application entry point.
|
||||
|
||||
Provides:
|
||||
- HTML pages: /, /agents, /audit, /settings, /login, /setup, /admin/users
|
||||
- WebSocket: /ws/{session_id} (streaming agent responses)
|
||||
- REST API: /api/*
|
||||
- HTML pages: /, /chats, /agents, /models, /audit, /monitors, /files,
|
||||
/settings, /login, /setup, /admin/users, /help
|
||||
- WebSocket: /ws/{session_id} (streaming agent responses)
|
||||
- REST API: /api/* (see server/web/routes.py)
|
||||
- Brain MCP: /brain-mcp/sse (MCP protocol for 2nd Brain access)
|
||||
|
||||
Startup order (lifespan):
|
||||
1. init_db() — run PostgreSQL migrations, create pool
|
||||
2. _refresh_brand_globals() — load brand name/logo from DB into Jinja2 globals
|
||||
3. _ensure_session_secret() — auto-generate HMAC signing secret if not set
|
||||
4. check user_count() — set _needs_setup flag (redirects to /setup if 0 users)
|
||||
5. cleanup_stale_runs() — mark any interrupted "running" agent_runs as "error"
|
||||
6. init_brain_db() — connect to brain PostgreSQL (pgvector)
|
||||
7. build_registry() — create ToolRegistry with all production tools
|
||||
8. discover_and_register_mcp_tools() — connect to configured MCP servers and add their tools
|
||||
9. Agent(registry=...) — create the singleton agent loop
|
||||
10. agent_runner.init/start() — load agent cron schedules into APScheduler, start scheduler
|
||||
11. page_monitor / rss_monitor — wire into shared APScheduler, load schedules
|
||||
12. _migrate_email_accounts() — one-time migration of old inbox:* credentials
|
||||
13. inbox_listener.start_all() — start IMAP listeners for all email accounts
|
||||
14. telegram_listener.start() — start Telegram long-polling listener
|
||||
15. _session_manager.run() — start Brain MCP session manager
|
||||
|
||||
Key singletons (module-level, shared across all requests):
|
||||
_agent: Agent instance — owns in-memory session history
|
||||
_registry: ToolRegistry — the set of available tools
|
||||
Both are initialised in lifespan() and referenced by the WebSocket handler.
|
||||
|
||||
Auth middleware (_AuthMiddleware):
|
||||
Runs on every request before route handlers. Validates the session cookie or
|
||||
API key, stores the resolved CurrentUser in the current_user ContextVar.
|
||||
Routes use _require_auth() / _require_admin() helpers to enforce access control.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
@@ -716,6 +745,22 @@ async def setup_post(request: Request):
|
||||
|
||||
# ── HTML pages ────────────────────────────────────────────────────────────────
|
||||
|
||||
async def _user_can_view_usage(user) -> bool:
|
||||
"""Admins always; non-admins only if they have their own API key and aren't on admin keys."""
|
||||
if not user:
|
||||
return False
|
||||
if user.is_admin:
|
||||
return True
|
||||
from .database import user_settings_store
|
||||
if await user_settings_store.get(user.id, "use_admin_keys"):
|
||||
return False
|
||||
return bool(
|
||||
await user_settings_store.get(user.id, "anthropic_api_key") or
|
||||
await user_settings_store.get(user.id, "openrouter_api_key") or
|
||||
await user_settings_store.get(user.id, "openai_api_key")
|
||||
)
|
||||
|
||||
|
||||
async def _ctx(request: Request, **extra):
|
||||
"""Build template context with current_user and active theme CSS injected."""
|
||||
from .web.themes import get_theme_css, DEFAULT_THEME
|
||||
@@ -734,6 +779,7 @@ async def _ctx(request: Request, **extra):
|
||||
"current_user": user,
|
||||
"theme_css": theme_css,
|
||||
"needs_personality_setup": needs_personality_setup,
|
||||
"can_view_usage": await _user_can_view_usage(user),
|
||||
**extra,
|
||||
}
|
||||
|
||||
@@ -765,6 +811,15 @@ async def models_page(request: Request):
|
||||
return templates.TemplateResponse("models.html", await _ctx(request))
|
||||
|
||||
|
||||
@app.get("/usage", response_class=HTMLResponse)
|
||||
async def usage_page(request: Request):
|
||||
user = _get_current_user(request)
|
||||
if not await _user_can_view_usage(user):
|
||||
from fastapi.responses import RedirectResponse
|
||||
return RedirectResponse("/", status_code=303)
|
||||
return templates.TemplateResponse("usage.html", await _ctx(request))
|
||||
|
||||
|
||||
@app.get("/audit", response_class=HTMLResponse)
|
||||
async def audit_page(request: Request):
|
||||
return templates.TemplateResponse("audit.html", await _ctx(request))
|
||||
|
||||
Reference in New Issue
Block a user