Denisijcu's picture
Update app/main.py
b4ba022 verified
import json
import os
from fastapi import FastAPI
import uvicorn
from engine.risk_engine import VertexRiskEngine
from engine.semantic import VertexSemanticAgent
from engine.web3_engine import VertexWeb3Engine
app = FastAPI()
# Archivo de persistencia de la watchlist
WATCHLIST_FILE = "app/watchlist.json"
@app.get("/batch_audit")
def run_batch_audit():
"""Audita toda la watchlist y devuelve alertas para n8n."""
if not os.path.exists(WATCHLIST_FILE):
return {"error": "Watchlist file not found", "alerts": []}
try:
with open(WATCHLIST_FILE, "r") as f:
watchlist = json.load(f)
except Exception as e:
return {"error": f"Failed to read watchlist: {e}", "alerts": []}
results = []
for ticker in watchlist:
try:
# Reutilizamos la lógica del motor Némesis
engine = VertexRiskEngine(ticker.upper())
num_res = engine.run_audit()
sem_agent = VertexSemanticAgent(ticker.upper())
risk_text = sem_agent.get_sec_risk_factors()
sem_res = sem_agent.judge_risks(risk_text)
# Blindaje de tipos para evitar errores de comparación
z_score = float(num_res.get('altman_z', 0.0))
m_dsri = float(num_res.get('m_score_dsri', 0.0))
s_score = float(sem_res.get('semantic_score', 0.0))
# Lógica de Semáforo de Riesgo
if z_score < 1.8 or m_dsri > 1.4 or s_score > 3:
status = "RED"
elif z_score < 3.0:
status = "YELLOW"
else:
status = "GREEN"
results.append({
"ticker": ticker,
"status": status,
"z_score": z_score,
"summary": sem_res.get("summary", ""),
"alert": True if status == "RED" else False
})
except Exception as ticker_error:
results.append({"ticker": ticker, "status": "ERROR", "msg": str(ticker_error)})
critical_alerts = [r for r in results if r.get("status") in ["RED", "YELLOW"]]
return {
"total_analyzed": len(watchlist),
"critical_count": len(critical_alerts),
"alerts": critical_alerts,
"full_results": results
}
@app.get("/audit/{ticker}")
def audit_company(ticker: str):
"""Auditoría individual para el tab de Stock Audit."""
try:
engine = VertexRiskEngine(ticker.upper())
num_res = engine.run_audit()
sem_agent = VertexSemanticAgent(ticker.upper())
risk_text = sem_agent.get_sec_risk_factors()
sem_res = sem_agent.judge_risks(risk_text)
# Blindaje de tipos
z_score = float(num_res.get('altman_z', 0.0))
m_dsri = float(num_res.get('m_score_dsri', 0.0))
s_score = float(sem_res.get('semantic_score', 0.0))
if z_score < 1.8 or m_dsri > 1.4 or s_score > 3:
status = "RED"
msg = "CRITICAL RISK: Red flags detected."
elif z_score < 3.0:
status = "YELLOW"
msg = "CAUTION: Monitor closely."
else:
status = "GREEN"
msg = "SAFE: Solid fundamentals."
return {
"ticker": ticker.upper(),
"status": status,
"numeric_analysis": {"altman_z": z_score, "m_score_dsri": m_dsri},
"semantic_analysis": sem_res,
"msg": msg
}
except Exception as e:
return {"status": "ERROR", "msg": str(e)}
@app.get("/audit_contract/{address}")
def audit_smart_contract(address: str):
"""Auditoría de Web3 usando el nuevo motor V2."""
try:
web3_engine = VertexWeb3Engine(address)
audit_res = web3_engine.get_contract_source()
if not audit_res["success"]:
return {"status": "ERROR", "msg": audit_res["error"]}
source = audit_res["source_code"]
vulnerabilities = web3_engine.scan_basic_vulnerabilities(source)
return {
"address": address,
"status": "DANGER" if len(vulnerabilities) > 0 else "SAFE",
"vulnerabilities": vulnerabilities,
"source_preview": source[:500] + "..." # Ahora source es string y el slice no falla
}
except Exception as e:
return {"status": "ERROR", "msg": str(e)}
@app.get("/get_settings")
async def get_settings():
# Esta línea es la que saca las llaves de los Secrets de Hugging Face
token = os.environ.get("BOT_TOKEN", "")
chat = os.environ.get("CHAT_ID", "")
if token and chat:
return {"bot_token": token, "chat_id": chat, "status": "loaded_from_secrets"}
return {"bot_token": "", "chat_id": "", "status": "secrets_not_found"}
if __name__ == "__main__":
# Hugging Face SIEMPRE usa el puerto 7860 internamente
port = int(os.environ.get("PORT", 7860))
# Importante: host="0.0.0.0" para que sea accesible desde fuera del contenedor
uvicorn.run(app, host="0.0.0.0", port=port)