File size: 3,017 Bytes
a392f04
 
 
8c79771
a392f04
8c79771
a392f04
 
8c79771
 
 
a392f04
 
 
 
8c79771
a392f04
 
 
 
 
 
 
 
 
 
 
 
8c79771
a392f04
 
626f6c1
8c79771
a392f04
 
 
626f6c1
a392f04
 
 
 
 
 
 
 
626f6c1
 
7598ba6
a392f04
 
 
626f6c1
a392f04
 
 
8c79771
 
 
 
 
 
 
a392f04
6efefd7
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
a392f04
8c79771
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
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("<h1>UI not found</h1>")


@app.get("/test-llm")
def test_llm():
    import requests, os
    from config.settings import HF_TOKEN
    results = {}
    models = [
        "meta-llama/Llama-3.3-70B-Instruct",
        "Qwen/QwQ-32B",
        "meta-llama/Meta-Llama-3-8B-Instruct",
        "HuggingFaceH4/zephyr-7b-beta",
        "mistralai/Mistral-7B-Instruct-v0.2"
    ]
    for model in models:
        try:
            url = f"https://api-inference.huggingface.co/models/{model}"
            r = requests.post(url, headers={"Authorization": f"Bearer {HF_TOKEN}"}, json={"inputs": "say hi", "parameters": {"max_new_tokens": 5}}, timeout=15)
            results[model] = f"{r.status_code}: {r.text[:100]}"
        except Exception as e:
            results[model] = f"ERROR: {str(e)[:100]}"
    return results

if __name__ == "__main__":
    uvicorn.run("api.server:app", host=API_HOST, port=API_PORT, reload=False)