Deployment
This commit is contained in:
@@ -2,23 +2,35 @@ import logging
|
||||
import os
|
||||
from contextlib import asynccontextmanager
|
||||
|
||||
import httpx
|
||||
from dotenv import load_dotenv
|
||||
from fastapi import FastAPI, HTTPException
|
||||
from fastapi import FastAPI, HTTPException, Depends
|
||||
|
||||
from agents.coders.BaseCoder import CoderAgent
|
||||
from agents.issue_triage.IssueTriageAgent import IssueTriageAgent
|
||||
from agents.issue_triage.context_builder import YouTrackContextBuilder
|
||||
from agents.registry import AgentRegistry
|
||||
from common.gitea_mcp_client import GiteaMCPClient
|
||||
from common.hot_env import load_env, reload_if_changed
|
||||
from common.llm_client import LLMClient
|
||||
from common.singleton import AgentSingleton
|
||||
from common.youtrack_mcp_client import YouTrackMCPClient, IssueNotFound
|
||||
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
load_dotenv()
|
||||
_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:
|
||||
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}")
|
||||
@@ -26,42 +38,43 @@ def env(key: str) -> str:
|
||||
|
||||
@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())
|
||||
|
||||
app.state.youtrack_mcp = await YouTrackMCPClient(
|
||||
str(os.getenv('YOUTRACK_MCP_SERVER')),
|
||||
str(os.getenv('YOUTRACK_MCP_TOKEN')),
|
||||
).connect()
|
||||
#
|
||||
#
|
||||
# res = await mcp.call_tool(
|
||||
# "add_issue_comment",
|
||||
# {"issueId": "ARCH-229", "text": "test"}
|
||||
# )
|
||||
# logger.info(res)
|
||||
|
||||
http_for_attachments = httpx.AsyncClient(
|
||||
headers={"Authorization": f"Bearer {env('YOUTRACK_MCP_TOKEN')}"},
|
||||
proxy=env("HTTPS_PROXY"),
|
||||
timeout=httpx.Timeout(30.0, connect=10.0, read=60.0),
|
||||
follow_redirects=True,
|
||||
)
|
||||
|
||||
app.state.issue_reader_agent = IssueTriageAgent(
|
||||
YouTrackContextBuilder(app.state.youtrack_mcp, http_for_attachments),
|
||||
LLMClient(
|
||||
base_url=env("LLM_ADDRESS"),
|
||||
api_key=env("LLM_API_KEY"),
|
||||
model=env("LLM_MODEL"),
|
||||
),
|
||||
AgentRegistry(),
|
||||
app.state.youtrack_mcp
|
||||
)
|
||||
|
||||
yield
|
||||
|
||||
# ---- shutdown ----
|
||||
watcher.cancel()
|
||||
await app.state.youtrack_mcp.close()
|
||||
|
||||
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"}
|
||||
@@ -73,8 +86,7 @@ async def say_hello(name: str):
|
||||
|
||||
|
||||
@app.get("/consume_task")
|
||||
async def decomposing_issue(issue: str):
|
||||
agent: IssueTriageAgent = app.state.issue_reader_agent
|
||||
async def decomposing_issue(issue: str, agent: IssueTriageAgent = Depends(get_agent)):
|
||||
try:
|
||||
logger.info(f"Received an issue: {issue}")
|
||||
plan = await agent.plan_issue(issue)
|
||||
@@ -89,8 +101,7 @@ async def decomposing_issue(issue: str):
|
||||
return {"error": str(e)}
|
||||
|
||||
@app.get("/code_issue")
|
||||
async def code_issue(issue: str):
|
||||
issue_agent: IssueTriageAgent = app.state.issue_reader_agent
|
||||
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")),
|
||||
|
||||
Reference in New Issue
Block a user