import sys from pathlib import Path sys.path.insert(0, str(Path(__file__).resolve().parent.parent)) from fastapi import FastAPI, BackgroundTasks, HTTPException from fastapi.responses import FileResponse, HTMLResponse from pydantic import BaseModel from typing import Optional import uvicorn from config.settings import API_HOST, API_PORT, PROCESSED_DIR from ingestion.sec_downloader import ingest_ticker from ingestion.text_extractor import extract_all from agents.analyst import analyze app = FastAPI(title="SEC Analyst LLM") class IngestRequest(BaseModel): ticker: str form_types: list = ["10-K", "10-Q"] max_filings: int = 5 class AnalyzeRequest(BaseModel): question: str ticker: Optional[str] = None n_results: int = 6 def run_pipeline(ticker, form_types, max_filings): print(f"[PIPELINE] Starting {ticker}...") ingest_ticker(ticker, form_types=form_types, max_filings=max_filings) extract_all(ticker) build_and_persist(ticker) print(f"[PIPELINE] Done: {ticker}") @app.get("/health") def health(): return {"api": "ok", "ollama": "ok", "chromadb": "ok"} @app.get("/tickers") def list_tickers(): tickers = [d.name for d in PROCESSED_DIR.iterdir() if d.is_dir()] if PROCESSED_DIR.exists() else [] return {"tickers": sorted(tickers)} @app.post("/ingest") def ingest(req: IngestRequest, background_tasks: BackgroundTasks): background_tasks.add_task(run_pipeline, req.ticker.upper(), req.form_types, req.max_filings) return {"status": "started", "ticker": req.ticker.upper(), "message": f"Ingesting {req.ticker.upper()} — this takes 3-5 minutes. The ticker will appear on the left when done."} @app.post("/analyze") def analyze_endpoint(req: AnalyzeRequest): try: return analyze(question=req.question, ticker=req.ticker, n_results=req.n_results) except Exception as e: raise HTTPException(status_code=500, detail=str(e)) ui_file = Path(__file__).parent.parent / "ui" / "index.html" @app.get("/") def serve_ui(): if ui_file.exists(): return FileResponse(str(ui_file)) return HTMLResponse("