main.py 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117
  1. from __future__ import annotations
  2. import logging
  3. import os
  4. from pathlib import Path
  5. from fastapi import FastAPI, HTTPException
  6. from fastapi.middleware.cors import CORSMiddleware
  7. from fastapi.responses import RedirectResponse
  8. from fastapi.staticfiles import StaticFiles
  9. from backend.agents.registry import build_default_registry
  10. from backend.config import settings
  11. from backend.events import event_logger
  12. from backend.models import AgentRequest, AgentResponse, BatchRunRequest, TaskCreateRequest, TaskRecord
  13. from backend.tasks.batch import BatchRunner
  14. from backend.tasks.manager import task_manager
  15. from backend.tasks.runner import TaskRunner
  16. if os.getenv("APP_ACCESS_LOG", "false").strip().lower() not in {"1", "true", "yes", "on"}:
  17. logging.getLogger("uvicorn.access").disabled = True
  18. logging.getLogger("uvicorn.access").setLevel(logging.CRITICAL + 1)
  19. for noisy_logger in ("agent", "services", "services.planner", "services.tool_events"):
  20. logging.getLogger(noisy_logger).setLevel(logging.WARNING)
  21. app = FastAPI(title=settings.app_name, version="0.1.0")
  22. app.add_middleware(
  23. CORSMiddleware,
  24. allow_origins=["*"],
  25. allow_credentials=True,
  26. allow_methods=["*"],
  27. allow_headers=["*"],
  28. )
  29. registry = build_default_registry()
  30. task_runner = TaskRunner(registry, task_manager)
  31. batch_runner = BatchRunner(registry)
  32. FRONTEND_DIR = Path(__file__).resolve().parents[1] / "frontend"
  33. if FRONTEND_DIR.exists():
  34. app.mount("/app", StaticFiles(directory=FRONTEND_DIR, html=True), name="frontend")
  35. RSS_DIGEST_DIR = Path(settings.rss_digest_data_root).resolve() / "runs" / "digests"
  36. if RSS_DIGEST_DIR.exists():
  37. app.mount("/rss-digests", StaticFiles(directory=RSS_DIGEST_DIR, html=True), name="rss_digests")
  38. @app.get("/", include_in_schema=False)
  39. def index() -> RedirectResponse:
  40. return RedirectResponse(url="/app/")
  41. @app.get("/health")
  42. def health() -> dict:
  43. return {"status": "healthy", "service": settings.app_name}
  44. @app.get("/agents")
  45. def list_agents() -> dict:
  46. profiles = registry.list_profiles()
  47. return {"agents": profiles, "total": len(profiles)}
  48. @app.post("/agents/{agent_id}/run", response_model=AgentResponse)
  49. def run_agent(agent_id: str, request: AgentRequest) -> AgentResponse:
  50. try:
  51. return registry.get(agent_id).run(request)
  52. except KeyError:
  53. raise HTTPException(status_code=404, detail=f"Agent '{agent_id}' not found")
  54. @app.post("/tasks", response_model=TaskRecord)
  55. def create_task(request: TaskCreateRequest) -> TaskRecord:
  56. if request.agent_id not in set(registry.ids()):
  57. raise HTTPException(status_code=404, detail=f"Agent '{request.agent_id}' not found")
  58. return task_manager.create(request)
  59. @app.get("/tasks")
  60. def list_tasks() -> dict:
  61. tasks = task_manager.list()
  62. return {"tasks": tasks, "total": len(tasks)}
  63. @app.get("/tasks/{task_id}", response_model=TaskRecord)
  64. def get_task(task_id: str) -> TaskRecord:
  65. try:
  66. return task_manager.get(task_id)
  67. except KeyError:
  68. raise HTTPException(status_code=404, detail=f"Task '{task_id}' not found")
  69. @app.post("/tasks/{task_id}/run", response_model=TaskRecord)
  70. def run_task(task_id: str, background: bool = True) -> TaskRecord:
  71. try:
  72. task_manager.get(task_id)
  73. except KeyError:
  74. raise HTTPException(status_code=404, detail=f"Task '{task_id}' not found")
  75. if background:
  76. return task_runner.start_background(task_id)
  77. return task_runner.run(task_id)
  78. @app.post("/batch/run")
  79. def run_batch(request: BatchRunRequest) -> dict:
  80. try:
  81. return {"responses": batch_runner.run(request.requests)}
  82. except KeyError as exc:
  83. raise HTTPException(status_code=404, detail=f"Agent '{exc.args[0]}' not found")
  84. @app.get("/events")
  85. def list_events(task_id: str | None = None, limit: int = 100) -> dict:
  86. return {"events": event_logger.list_events(task_id=task_id, limit=limit)}