- New /settings/tools page: max_risk selector (low/medium/high) + per-tool override dropdowns (Default / Force include / Force exclude) for all 58 tools grouped by category with color-coded risk badges; JS updates Auto status live - get_tools_for_role() + get_openai_tools_for_role() now accept max_risk, whitelist, blacklist; _apply_risk_policy() handles the filtering logic - get_risk_policy() helper in auth_utils reads from tool_policy.json - Risk policy wired through orchestrator.py, openai_orchestrator.py, orchestrator_engine.py, nextcloud_talk.py, homeassistant.py - Tools nav link added to settings.html and notifications.html - CLAUDE.md and ARCH__SYSTEM.md updated: tool count 50→58, risk system docs, tool access control three-layer model documented Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
76 lines
2.2 KiB
Python
76 lines
2.2 KiB
Python
import logging
|
|
from contextlib import asynccontextmanager
|
|
from fastapi import FastAPI
|
|
from fastapi.staticfiles import StaticFiles
|
|
import uvicorn
|
|
|
|
logging.basicConfig(level=logging.INFO, format="%(levelname)s:%(name)s: %(message)s")
|
|
|
|
from config import settings
|
|
from auth_middleware import SessionAuthMiddleware
|
|
from routers import chat, google_chat, nextcloud_talk, homeassistant, files, distill, auth, orchestrator
|
|
from routers import ui, onboarding, settings, tools_settings, help, auth_google, local_llm, push, audit, usage
|
|
|
|
|
|
@asynccontextmanager
|
|
async def lifespan(app: FastAPI):
|
|
import scheduler
|
|
scheduler.start()
|
|
yield
|
|
scheduler.stop()
|
|
from llm_client import cleanup
|
|
await cleanup()
|
|
|
|
|
|
app = FastAPI(title="Cortex Dispatcher", lifespan=lifespan)
|
|
|
|
app.add_middleware(SessionAuthMiddleware)
|
|
|
|
# API routers
|
|
app.include_router(chat.router)
|
|
app.include_router(google_chat.router)
|
|
app.include_router(nextcloud_talk.router)
|
|
app.include_router(homeassistant.router)
|
|
app.include_router(files.router)
|
|
app.include_router(distill.router)
|
|
app.include_router(auth.router)
|
|
app.include_router(orchestrator.router)
|
|
app.include_router(push.router)
|
|
app.include_router(audit.router)
|
|
app.include_router(usage.router)
|
|
|
|
# Static files — must be mounted BEFORE ui.router so /static/* is matched first.
|
|
# ui.router has a wildcard /{username}/{persona} that would otherwise catch /static/style.css etc.
|
|
app.mount("/static", StaticFiles(directory="static"), name="static")
|
|
|
|
# Google OAuth — must be before ui.router (wildcard /{user}/{persona} would swallow it)
|
|
app.include_router(auth_google.router)
|
|
|
|
# Onboarding (invite tokens + persona creation — before ui.router)
|
|
app.include_router(onboarding.router)
|
|
|
|
# Account settings
|
|
app.include_router(settings.router)
|
|
app.include_router(tools_settings.router)
|
|
app.include_router(local_llm.router)
|
|
|
|
# Help page
|
|
app.include_router(help.router)
|
|
|
|
# UI router (login + /{user}/{persona} — must be last to avoid swallowing API paths)
|
|
app.include_router(ui.router)
|
|
|
|
|
|
@app.get("/health")
|
|
async def health() -> dict:
|
|
return {"status": "ok"}
|
|
|
|
|
|
if __name__ == "__main__":
|
|
uvicorn.run(
|
|
"main:app",
|
|
host=settings.host,
|
|
port=settings.port,
|
|
reload=True,
|
|
)
|