Files
agents/main.py
T
2026-09-26 12:50:19 +03:00

124 lines
3.6 KiB
Python

import logging
import os
from contextlib import asynccontextmanager
from fastapi import FastAPI, HTTPException, Depends
from pydantic import BaseModel
from agents.coders.BaseCoder import CoderAgent
from agents.issue_triage.IssueTriageAgent import IssueTriageAgent
from common.gitea_mcp_client import GiteaMCPClient
from common.hot_env import load_env, reload_if_changed
from common.singleton import AgentSingleton
from common.youtrack_mcp_client import IssueNotFound
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
_last_hash: str | None = None
if not load_env(force=False):
logger.warning("No .env file found at startup, will rely on os.environ")
def env_optional(key: str, default: str | None = None) -> str | None:
value = os.environ.get(key)
if value is None:
return default
return value
def env(key: str) -> str:
reload_if_changed()
value = os.environ.get(key)
if not value:
raise RuntimeError(f"Missing required env var: {key}")
return value
@asynccontextmanager
async def lifespan(app: FastAPI):
import asyncio
singleton = await AgentSingleton.get()
await singleton.ensure_initialized()
async def watch_and_reset():
while True:
try:
if reload_if_changed():
await singleton.reset()
await singleton.ensure_initialized()
except Exception:
logger.exception("watch_and_reset failed")
await asyncio.sleep(5)
watcher = asyncio.create_task(watch_and_reset())
yield
# ---- shutdown ----
watcher.cancel()
app = FastAPI(lifespan=lifespan)
async def get_agent() -> IssueTriageAgent:
singleton = await AgentSingleton.get()
await singleton.ensure_initialized()
return singleton.agent
@app.get("/")
async def root():
return {"message": "Hello World"}
@app.get("/hello/{name}")
async def say_hello(name: str):
return {"message": f"Hello {name}"}
class IncomingTask(BaseModel):
issue: str
@app.post("/consume_task")
async def decomposing_issue(body: IncomingTask, agent: IssueTriageAgent = Depends(get_agent)):
try:
logger.info(f"Received an issue: {body.issue}")
plan = await agent.plan_issue(body.issue)
logger.info("Task planning successful.")
logger.info(plan.render())
return {"plan": plan}
except IssueNotFound:
logger.error(f"Issue {body.issue} not found.")
raise HTTPException(status_code=404, detail=f"Issue {body.issue} not found")
except Exception as e:
logger.exception("An unexpected error occurred")
return {"error": str(e)}
@app.get("/code_issue")
async def code_issue(issue: str, issue_agent: IssueTriageAgent = Depends(get_agent)):
gitea_mcp_client = GiteaMCPClient()
coder_agent: CoderAgent = CoderAgent(
str(os.getenv("CODER_ID")),
issue_agent.ctx_builder,
issue_agent.llm,
issue_agent.agent_registry,
issue_agent.youtrack_mcp,
gitea_mcp_client
)
try:
logger.info(f"Resolve an issue: {issue}")
issue_ctx = await issue_agent.ctx_builder.build(issue)
if issue_ctx:
response = await coder_agent.fix_an_issue(issue_ctx)
logger.info("Task planning successful.")
return {"status": "ok", "response": response}
except IssueNotFound:
logger.error(f"Issue {issue} not found.")
raise HTTPException(status_code=404, detail=f"Issue {issue} not found")
except Exception as e:
logger.exception("An unexpected error occurred")
return {"error": str(e)}
return {"error": "No context was created"}