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
|
|
|
"""Logging configuration routes: get/set log levels, filters, components."""
|
|
|
|
|
|
|
|
|
|
from fastapi import APIRouter
|
2026-04-15 15:43:18 +03:00
|
|
|
from fastapi.responses import JSONResponse
|
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
|
|
|
from routes.models import LogConfigUpdateRequest, LogFilterUpdateRequest
|
|
|
|
|
from utils.logger import get_logger, list_components, get_component_stats
|
|
|
|
|
from utils.log_config import (
|
|
|
|
|
load_config as load_log_config,
|
|
|
|
|
update_component, reload_all_loggers, update_api_filters,
|
|
|
|
|
reset_to_defaults, update_timestamp_format,
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
logger = get_logger('api')
|
|
|
|
|
|
|
|
|
|
router = APIRouter()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@router.get("/api/log/config")
|
|
|
|
|
async def get_log_config():
|
|
|
|
|
"""Get current logging configuration."""
|
|
|
|
|
try:
|
|
|
|
|
config = load_log_config()
|
|
|
|
|
logger.debug("Log config requested")
|
|
|
|
|
return {"success": True, "config": config}
|
|
|
|
|
except Exception as e:
|
|
|
|
|
logger.error(f"Failed to get log config: {e}")
|
2026-04-15 15:43:18 +03:00
|
|
|
return JSONResponse(status_code=500, content={"success": False, "error": str(e)})
|
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
|
|
|
|
|
|
|
|
|
|
|
|
|
@router.post("/api/log/config")
|
|
|
|
|
async def update_log_config(request: LogConfigUpdateRequest):
|
|
|
|
|
"""Update logging configuration."""
|
|
|
|
|
try:
|
|
|
|
|
if request.component:
|
|
|
|
|
success = update_component(
|
|
|
|
|
request.component,
|
|
|
|
|
enabled=request.enabled,
|
|
|
|
|
enabled_levels=request.enabled_levels
|
|
|
|
|
)
|
|
|
|
|
if not success:
|
2026-04-15 15:43:18 +03:00
|
|
|
return JSONResponse(status_code=500, content={"success": False, "error": f"Failed to update component {request.component}"})
|
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
|
|
|
|
|
|
|
|
logger.info(f"Log config updated: component={request.component}, enabled_levels={request.enabled_levels}, enabled={request.enabled}")
|
|
|
|
|
return {"success": True, "message": "Configuration updated"}
|
|
|
|
|
except Exception as e:
|
|
|
|
|
logger.error(f"Failed to update log config: {e}")
|
2026-04-15 15:43:18 +03:00
|
|
|
return JSONResponse(status_code=500, content={"success": False, "error": str(e)})
|
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
|
|
|
|
|
|
|
|
|
|
|
|
|
@router.get("/api/log/components")
|
|
|
|
|
async def get_log_components():
|
|
|
|
|
"""Get list of all logging components with their descriptions."""
|
|
|
|
|
try:
|
|
|
|
|
components = list_components()
|
|
|
|
|
stats = get_component_stats()
|
|
|
|
|
logger.debug("Log components list requested")
|
|
|
|
|
return {
|
|
|
|
|
"success": True,
|
|
|
|
|
"components": components,
|
|
|
|
|
"stats": stats
|
|
|
|
|
}
|
|
|
|
|
except Exception as e:
|
|
|
|
|
logger.error(f"Failed to get log components: {e}")
|
2026-04-15 15:43:18 +03:00
|
|
|
return JSONResponse(status_code=500, content={"success": False, "error": str(e)})
|
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
|
|
|
|
|
|
|
|
|
|
|
|
|
@router.post("/api/log/reload")
|
|
|
|
|
async def reload_log_config():
|
|
|
|
|
"""Reload logging configuration from file."""
|
|
|
|
|
try:
|
|
|
|
|
success = reload_all_loggers()
|
|
|
|
|
if success:
|
|
|
|
|
logger.info("Log configuration reloaded")
|
|
|
|
|
return {"success": True, "message": "Configuration reloaded"}
|
|
|
|
|
else:
|
2026-04-15 15:43:18 +03:00
|
|
|
return JSONResponse(status_code=500, content={"success": False, "error": "Failed to reload configuration"})
|
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
|
|
|
except Exception as e:
|
|
|
|
|
logger.error(f"Failed to reload log config: {e}")
|
2026-04-15 15:43:18 +03:00
|
|
|
return JSONResponse(status_code=500, content={"success": False, "error": str(e)})
|
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
|
|
|
|
|
|
|
|
|
|
|
|
|
@router.post("/api/log/filters")
|
|
|
|
|
async def update_log_filters(request: LogFilterUpdateRequest):
|
|
|
|
|
"""Update API request filtering configuration."""
|
|
|
|
|
try:
|
|
|
|
|
success = update_api_filters(
|
|
|
|
|
exclude_paths=request.exclude_paths,
|
|
|
|
|
exclude_status=request.exclude_status,
|
|
|
|
|
include_slow_requests=request.include_slow_requests,
|
|
|
|
|
slow_threshold_ms=request.slow_threshold_ms
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
if success:
|
|
|
|
|
logger.info(f"API filters updated: {request.dict(exclude_none=True)}")
|
|
|
|
|
return {"success": True, "message": "Filters updated"}
|
|
|
|
|
else:
|
2026-04-15 15:43:18 +03:00
|
|
|
return JSONResponse(status_code=500, content={"success": False, "error": "Failed to update filters"})
|
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
|
|
|
except Exception as e:
|
|
|
|
|
logger.error(f"Failed to update filters: {e}")
|
2026-04-15 15:43:18 +03:00
|
|
|
return JSONResponse(status_code=500, content={"success": False, "error": str(e)})
|
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
|
|
|
|
|
|
|
|
|
|
|
|
|
@router.post("/api/log/reset")
|
|
|
|
|
async def reset_log_config():
|
|
|
|
|
"""Reset logging configuration to defaults."""
|
|
|
|
|
try:
|
|
|
|
|
success = reset_to_defaults()
|
|
|
|
|
if success:
|
|
|
|
|
logger.info("Log configuration reset to defaults")
|
|
|
|
|
return {"success": True, "message": "Configuration reset to defaults"}
|
|
|
|
|
else:
|
2026-04-15 15:43:18 +03:00
|
|
|
return JSONResponse(status_code=500, content={"success": False, "error": "Failed to reset configuration"})
|
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
|
|
|
except Exception as e:
|
|
|
|
|
logger.error(f"Failed to reset log config: {e}")
|
2026-04-15 15:43:18 +03:00
|
|
|
return JSONResponse(status_code=500, content={"success": False, "error": str(e)})
|
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
|
|
|
|
|
|
|
|
|
|
|
|
|
@router.post("/api/log/global-level")
|
|
|
|
|
async def update_global_level_endpoint(level: str, enabled: bool):
|
|
|
|
|
"""Enable or disable a specific log level across all components."""
|
|
|
|
|
try:
|
|
|
|
|
from utils.log_config import update_global_level
|
|
|
|
|
success = update_global_level(level, enabled)
|
|
|
|
|
if success:
|
|
|
|
|
action = "enabled" if enabled else "disabled"
|
|
|
|
|
logger.info(f"Global level {level} {action} across all components")
|
|
|
|
|
return {"success": True, "message": f"Level {level} {action} globally"}
|
|
|
|
|
else:
|
2026-04-15 15:43:18 +03:00
|
|
|
return JSONResponse(status_code=500, content={"success": False, "error": f"Failed to update global level {level}"})
|
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
|
|
|
except Exception as e:
|
|
|
|
|
logger.error(f"Failed to update global level: {e}")
|
2026-04-15 15:43:18 +03:00
|
|
|
return JSONResponse(status_code=500, content={"success": False, "error": str(e)})
|
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
|
|
|
|
|
|
|
|
|
|
|
|
|
@router.post("/api/log/timestamp-format")
|
|
|
|
|
async def update_timestamp_format_endpoint(format_type: str):
|
|
|
|
|
"""Update timestamp format for all log outputs."""
|
|
|
|
|
try:
|
|
|
|
|
success = update_timestamp_format(format_type)
|
|
|
|
|
if success:
|
|
|
|
|
logger.info(f"Timestamp format updated to: {format_type}")
|
|
|
|
|
return {"success": True, "message": f"Timestamp format set to: {format_type}"}
|
|
|
|
|
else:
|
2026-04-15 15:43:18 +03:00
|
|
|
return JSONResponse(status_code=400, content={"success": False, "error": f"Invalid timestamp format: {format_type}"})
|
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
|
|
|
except Exception as e:
|
|
|
|
|
logger.error(f"Failed to update timestamp format: {e}")
|
2026-04-15 15:43:18 +03:00
|
|
|
return JSONResponse(status_code=500, content={"success": False, "error": str(e)})
|
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
|
|
|
|
|
|
|
|
|
|
|
|
|
@router.get("/api/log/files/{component}")
|
|
|
|
|
async def get_log_file(component: str, lines: int = 100):
|
|
|
|
|
"""Get last N lines from a component's log file."""
|
|
|
|
|
try:
|
|
|
|
|
from pathlib import Path
|
|
|
|
|
log_dir = Path('/app/memory/logs')
|
|
|
|
|
log_file = log_dir / f'{component.replace(".", "_")}.log'
|
|
|
|
|
|
|
|
|
|
if not log_file.exists():
|
2026-04-15 15:43:18 +03:00
|
|
|
return JSONResponse(status_code=404, content={"success": False, "error": "Log file not found"})
|
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
|
|
|
|
|
|
|
|
with open(log_file, 'r', encoding='utf-8') as f:
|
|
|
|
|
all_lines = f.readlines()
|
|
|
|
|
last_lines = all_lines[-lines:] if len(all_lines) >= lines else all_lines
|
|
|
|
|
|
|
|
|
|
logger.debug(f"Log file requested: {component} ({lines} lines)")
|
|
|
|
|
return {
|
|
|
|
|
"success": True,
|
|
|
|
|
"component": component,
|
|
|
|
|
"lines": last_lines,
|
|
|
|
|
"total_lines": len(all_lines)
|
|
|
|
|
}
|
|
|
|
|
except Exception as e:
|
|
|
|
|
logger.error(f"Failed to read log file for {component}: {e}")
|
2026-04-15 15:43:18 +03:00
|
|
|
return JSONResponse(status_code=500, content={"success": False, "error": str(e)})
|