Spaces:
Runtime error
Runtime error
File size: 5,717 Bytes
29d192d 3a3a58f 6de9d51 3a3a58f 29d192d 3a3a58f 29d192d 3a3a58f 29d192d 3a3a58f 29d192d 3a3a58f 29d192d 3a3a58f 6de9d51 3a3a58f 29d192d 3a3a58f 29d192d 3a3a58f 29d192d 3a3a58f 29d192d 3a3a58f 29d192d 3a3a58f 29d192d 3a3a58f 6de9d51 3a3a58f 29d192d 3a3a58f 29d192d 3a3a58f | 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 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 | """
Eugene Intelligence v0.4 — Unified SEC Data Server
FastAPI + MCP in one file.
Usage:
python eugene_server.py → FastAPI on port 8000
python eugene_server.py --mode mcp → MCP stdio server
"""
import sys
import os
from eugene.router import query, capabilities, VALID_EXTRACTS
from eugene.sources.fred import get_category, get_series, get_all, FRED_SERIES
from eugene.sources.fmp import get_price, get_profile, get_earnings, get_estimates, get_news
from eugene.concepts import VALID_CONCEPTS
# ---------------------------------------------------------------------------
# MCP MODE
# ---------------------------------------------------------------------------
def run_mcp():
from mcp.server.fastmcp import FastMCP
mcp = FastMCP("eugene-intelligence")
@mcp.tool()
def sec(
identifier: str,
extract: str = "financials",
period: str = "FY",
concept: str = None,
form: str = None,
section: str = None,
date_from: str = None,
date_to: str = None,
limit: int = 10,
) -> dict:
"""All SEC EDGAR data in one tool.
identifier: ticker (AAPL), CIK (320193), or accession number
extract: profile|filings|financials|concepts|insiders|ownership|events|sections|exhibits (comma-separated)
period: FY|Q (for financials)
concept: canonical concept (revenue,net_income,...) or raw XBRL tag
form: 10-K|10-Q|8-K|4|13F-HR (filter)
section: mdna|risk_factors|business|legal (for sections)
limit: max results (default 10)
"""
params = {
"period": period, "concept": concept, "form": form,
"section": section, "from": date_from, "to": date_to,
"limit": limit,
}
return query(identifier, extract, **{k: v for k, v in params.items() if v is not None})
@mcp.tool()
def economics(category: str = "all", series: str = None) -> dict:
"""All economic data from FRED.
category: inflation|employment|gdp|housing|consumer|manufacturing|rates|money|treasury|all
series: specific FRED series ID (e.g. CPIAUCSL)
"""
if series:
return get_series(series)
if category == "all":
return get_all()
return get_category(category)
@mcp.tool()
def caps() -> dict:
"""List all available Eugene tools, extracts, and capabilities."""
return capabilities()
mcp.run()
# ---------------------------------------------------------------------------
# FASTAPI MODE
# ---------------------------------------------------------------------------
def run_api():
from fastapi import FastAPI, Request
from fastapi.responses import JSONResponse
import uvicorn
app = FastAPI(
title="Eugene Intelligence",
description="Financial context for AI. SEC EDGAR + FRED. Every number traced to source.",
version="0.4.0",
docs_url="/",
redoc_url="/redoc",
)
@app.get("/health")
def health():
return {"status": "ok", "version": "0.4.0"}
@app.get("/v1/capabilities")
def caps_endpoint():
return capabilities()
@app.get("/v1/concepts")
def concepts_list():
from eugene.concepts import CANONICAL_CONCEPTS
return {
"concepts": {
name: {
"description": c.get("description", ""),
"statement": c.get("statement", ""),
"derived": c.get("derived", False),
}
for name, c in CANONICAL_CONCEPTS.items()
}
}
@app.get("/v1/sec/{identifier}")
def sec_endpoint(
identifier: str,
extract: str = "financials",
period: str = "FY",
concept: str = None,
form: str = None,
section: str = None,
limit: int = 10,
):
params = {
"period": period, "concept": concept, "form": form,
"section": section, "limit": limit,
}
return query(identifier, extract, **{k: v for k, v in params.items() if v is not None})
@app.get("/v1/economics/{category}")
def economics_endpoint(category: str = "all", series: str = None):
if series:
return get_series(series)
if category == "all":
return get_all()
return get_category(category)
# Keep v0.3 routes for backward compatibility
@app.get("/v1/sec/{ticker}/prices")
def prices_compat(ticker: str):
return get_price(ticker)
@app.get("/v1/sec/{ticker}/profile")
def profile_compat(ticker: str):
return get_profile(ticker)
@app.get("/v1/sec/{ticker}/earnings")
def earnings_compat(ticker: str):
return get_earnings(ticker)
@app.get("/v1/sec/{ticker}/estimates")
def estimates_compat(ticker: str):
return get_estimates(ticker)
@app.get("/v1/sec/{ticker}/news")
def news_compat(ticker: str):
return get_news(ticker)
port = int(os.environ.get("PORT", 8000))
import logging
logging.basicConfig(level=logging.INFO)
logging.info(f"Starting Eugene v0.4 on port {port}")
try:
from eugene.router import query
logging.info("Router imported OK")
except Exception as e:
logging.error(f"Import failed: {e}")
uvicorn.run(app, host="0.0.0.0", port=port)
# ---------------------------------------------------------------------------
# ENTRY POINT
# ---------------------------------------------------------------------------
if __name__ == "__main__":
if "--mode" in sys.argv and "mcp" in sys.argv:
run_mcp()
else:
run_api()
|