Files
miku-discord/bot/api.py
koko210Serve 979217e7cc refactor: split api.py monolith into 19 route modules (Phase B)
Split 3,598-line api.py into thin orchestrator (128 lines) + 19 route
modules in bot/routes/:

  core.py (7 routes), mood.py (10), language.py (3), evil_mode.py (6),
  bipolar_mode.py (9), gpu.py (2), bot_actions.py (4), autonomous.py (13),
  profile_picture.py (26), manual_send.py (3), servers.py (6),
  figurines.py (5), dms.py (18), image_generation.py (4), chat.py (1),
  config.py (7), logging_config.py (9), voice.py (3), memory.py (10)

All 146 routes verified present via test_route_split.py (149 tests).
21/21 regression tests (test_config_state.py) pass.
Monolith backup: bot/api_monolith_backup.py (revert: cp it to api.py).
2026-04-15 11:38:14 +03:00

129 lines
4.5 KiB
Python

# api.py — Thin orchestrator (routes live in routes/*.py)
#
# Monolith backup: api_monolith_backup.py
# To revert: cp api_monolith_backup.py api.py
from fastapi import FastAPI, Request
from fastapi.staticfiles import StaticFiles
from utils.logger import get_logger
from utils.log_config import load_config as load_log_config
import time
from fnmatch import fnmatch
import nest_asyncio
nest_asyncio.apply()
# Initialize API logger
logger = get_logger('api')
api_requests_logger = get_logger('api.requests')
app = FastAPI()
# ========== Global Exception Handler ==========
@app.exception_handler(Exception)
async def global_exception_handler(request: Request, exc: Exception):
"""Catch all unhandled exceptions and log them properly."""
logger.error(f"Unhandled exception on {request.method} {request.url.path}: {exc}", exc_info=True)
return {"success": False, "error": "Internal server error"}
# ========== Logging Middleware ==========
@app.middleware("http")
async def log_requests(request: Request, call_next):
"""Middleware to log HTTP requests based on configuration."""
start_time = time.time()
# Get logging config
log_config = load_log_config()
api_config = log_config.get('components', {}).get('api.requests', {})
# Check if API request logging is enabled
if not api_config.get('enabled', False):
return await call_next(request)
# Get filters
filters = api_config.get('filters', {})
exclude_paths = filters.get('exclude_paths', [])
exclude_status = filters.get('exclude_status', [])
include_slow_requests = filters.get('include_slow_requests', True)
slow_threshold_ms = filters.get('slow_threshold_ms', 1000)
# Process request
response = await call_next(request)
# Calculate duration
duration_ms = (time.time() - start_time) * 1000
# Check if path should be excluded
path = request.url.path
for pattern in exclude_paths:
if fnmatch(path, pattern):
return response
# Check if status should be excluded (unless it's a slow request)
is_slow = duration_ms >= slow_threshold_ms
if response.status_code in exclude_status and not (include_slow_requests and is_slow):
return response
# Log the request
log_msg = f"{request.method} {path} - {response.status_code} ({duration_ms:.2f}ms)"
if is_slow:
api_requests_logger.warning(f"SLOW REQUEST: {log_msg}")
elif response.status_code >= 500:
api_requests_logger.error(log_msg)
elif response.status_code >= 400:
api_requests_logger.warning(log_msg)
else:
api_requests_logger.api(log_msg)
return response
# Serve static folder
app.mount("/static", StaticFiles(directory="static"), name="static")
# ========== Include Route Modules ==========
from routes.core import router as core_router
from routes.mood import router as mood_router
from routes.language import router as language_router
from routes.evil_mode import router as evil_mode_router
from routes.bipolar_mode import router as bipolar_mode_router
from routes.gpu import router as gpu_router
from routes.bot_actions import router as bot_actions_router
from routes.autonomous import router as autonomous_router
from routes.profile_picture import router as profile_picture_router
from routes.manual_send import router as manual_send_router
from routes.servers import router as servers_router
from routes.figurines import router as figurines_router
from routes.dms import router as dms_router
from routes.image_generation import router as image_generation_router
from routes.chat import router as chat_router
from routes.config import router as config_router
from routes.logging_config import router as logging_config_router
from routes.voice import router as voice_router
from routes.memory import router as memory_router
app.include_router(core_router)
app.include_router(mood_router)
app.include_router(language_router)
app.include_router(evil_mode_router)
app.include_router(bipolar_mode_router)
app.include_router(gpu_router)
app.include_router(bot_actions_router)
app.include_router(autonomous_router)
app.include_router(profile_picture_router)
app.include_router(manual_send_router)
app.include_router(servers_router)
app.include_router(figurines_router)
app.include_router(dms_router)
app.include_router(image_generation_router)
app.include_router(chat_router)
app.include_router(config_router)
app.include_router(logging_config_router)
app.include_router(voice_router)
app.include_router(memory_router)
def start_api():
import uvicorn
uvicorn.run(app, host="0.0.0.0", port=3939)