-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmain.py
More file actions
131 lines (112 loc) · 3.89 KB
/
Copy pathmain.py
File metadata and controls
131 lines (112 loc) · 3.89 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
"""FastAPI application factory."""
from __future__ import annotations
from contextlib import asynccontextmanager, suppress
from pathlib import Path
from typing import Any
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
from fastapi.staticfiles import StaticFiles
from uvicorn.middleware.proxy_headers import ProxyHeadersMiddleware
from minibot.api.routes import (
approvals,
auth,
automations,
channels_feishu,
channels_weixin,
media,
misc,
sessions,
settings,
status,
workspaces,
)
from minibot.api.ws import router as ws_router
from minibot.app_state import build_app_state
from minibot.webui_static import mount_webui, root_redirect_to_devui
_DEVUI_DIR = Path(__file__).resolve().parent / "static" / "devui"
@asynccontextmanager
async def lifespan(app: FastAPI):
from minibot.observability import langfuse as lf
state = build_app_state()
app.state.app_state = state
lf.init_from_settings(state.settings)
state.score_queue.start()
runtimes = state.preload_user_runtimes()
if state.bus_worker is not None:
state.bus_worker.start()
for runtime in runtimes:
if runtime.channels is None or not runtime.channels.channels:
continue
try:
await runtime.channels.start()
except Exception: # noqa: BLE001 — never block boot on IM
pass
try:
await state.mcp.start(state.config.mcp_presets or [])
except Exception: # noqa: BLE001 — never block boot on MCP
pass
if state.cron is not None:
try:
await state.cron.start()
from minibot.cron.system_jobs import ensure_system_cron_jobs
ensure_system_cron_jobs(state.cron, state.config)
except Exception: # noqa: BLE001 — never block boot on cron store
pass
try:
yield
finally:
if state.cron is not None:
with suppress(Exception):
await state.cron.stop()
with suppress(Exception):
await state.mcp.stop()
for runtime in list(state.user_runtimes.values()):
if runtime.channels is not None:
with suppress(Exception):
await runtime.channels.stop()
if state.bus_worker is not None:
await state.bus_worker.stop()
if state.sandbox_backend is not None:
aclose = getattr(state.sandbox_backend, "aclose", None)
if callable(aclose):
with suppress(Exception):
await aclose()
await state.score_queue.stop()
lf.shutdown()
def create_app() -> FastAPI:
app = FastAPI(title="minibot", version="0.1.0", lifespan=lifespan)
app.add_middleware(
ProxyHeadersMiddleware,
trusted_hosts="*",
)
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
@app.get("/health")
async def health() -> dict[str, Any]:
return {"status": "ok", "runtime": "minibot"}
app.include_router(auth.router)
app.include_router(media.router)
app.include_router(approvals.router)
app.include_router(channels_feishu.router)
app.include_router(channels_weixin.router)
app.include_router(sessions.router)
app.include_router(workspaces.router)
app.include_router(settings.router)
app.include_router(misc.router)
app.include_router(automations.router)
app.include_router(status.router)
app.include_router(ws_router)
if _DEVUI_DIR.is_dir():
app.mount("/ui", StaticFiles(directory=str(_DEVUI_DIR), html=True), name="devui")
# SPA last so /api /ws /ui win. If dist missing, / → DevUI.
if mount_webui(app) is None:
@app.get("/")
async def root_fallback():
return root_redirect_to_devui()
return app
app = create_app()