FinAgent / backend /api.py
Dev Goyal
Initial deployment of FinAgent
c6d67ac
import os
from contextlib import asynccontextmanager
from dotenv import load_dotenv
from fastapi import FastAPI, HTTPException, Request
from fastapi.concurrency import run_in_threadpool
from fastapi.responses import StreamingResponse
from pydantic import BaseModel, Field
import langchain
from core.config import Settings
from core.graph_builder import build_financial_graph
from core.runner import create_llm, run_financial_query, astream_financial_query
@asynccontextmanager
async def lifespan(app: FastAPI):
load_dotenv()
langchain.debug = os.getenv("LANGCHAIN_DEBUG", "").lower() in ("1", "true", "yes")
settings = Settings()
llm = create_llm(settings)
app.state.settings = settings
app.state.graph = build_financial_graph(llm)
yield
app = FastAPI(title="FinAgent", lifespan=lifespan)
class ChatRequest(BaseModel):
query: str = Field(..., min_length=1, max_length=16000)
class StepOut(BaseModel):
node: str
content: str
step_latency: float | None = None
total_latency: float | None = None
class ChatResponse(BaseModel):
memo: str | None = None
steps: list[StepOut] = Field(default_factory=list)
total_latency: float | None = None
@app.get("/health")
def health():
return {"status": "ok"}
@app.post("/chat", response_model=ChatResponse)
async def chat(request: Request, body: ChatRequest):
graph = request.app.state.graph
q = body.query.strip()
if not q:
raise HTTPException(status_code=400, detail="query must not be empty")
try:
result = await run_in_threadpool(run_financial_query, graph, q)
except Exception as e:
raise HTTPException(status_code=503, detail=str(e)) from e
return ChatResponse(**result)
@app.post("/chat/stream")
async def chat_stream(request: Request, body: ChatRequest):
graph = request.app.state.graph
q = body.query.strip()
if not q:
raise HTTPException(status_code=400, detail="query must not be empty")
return StreamingResponse(
astream_financial_query(graph, q),
media_type="text/event-stream"
)