Denisijcu commited on
Commit
b4ba022
·
verified ·
1 Parent(s): 28ced32

Update app/main.py

Browse files
Files changed (1) hide show
  1. app/main.py +141 -145
app/main.py CHANGED
@@ -1,145 +1,141 @@
1
- import json
2
- import os
3
- from fastapi import FastAPI
4
- import uvicorn
5
- from engine.risk_engine import VertexRiskEngine
6
- from engine.semantic import VertexSemanticAgent
7
- from engine.web3_engine import VertexWeb3Engine
8
-
9
- app = FastAPI()
10
-
11
- # Archivo de persistencia de la watchlist
12
- WATCHLIST_FILE = "app/watchlist.json"
13
-
14
- @app.get("/batch_audit")
15
- def run_batch_audit():
16
- """Audita toda la watchlist y devuelve alertas para n8n."""
17
- if not os.path.exists(WATCHLIST_FILE):
18
- return {"error": "Watchlist file not found", "alerts": []}
19
-
20
- try:
21
- with open(WATCHLIST_FILE, "r") as f:
22
- watchlist = json.load(f)
23
- except Exception as e:
24
- return {"error": f"Failed to read watchlist: {e}", "alerts": []}
25
-
26
- results = []
27
- for ticker in watchlist:
28
- try:
29
- # Reutilizamos la lógica del motor Némesis
30
- engine = VertexRiskEngine(ticker.upper())
31
- num_res = engine.run_audit()
32
-
33
- sem_agent = VertexSemanticAgent(ticker.upper())
34
- risk_text = sem_agent.get_sec_risk_factors()
35
- sem_res = sem_agent.judge_risks(risk_text)
36
-
37
- # Blindaje de tipos para evitar errores de comparación
38
- z_score = float(num_res.get('altman_z', 0.0))
39
- m_dsri = float(num_res.get('m_score_dsri', 0.0))
40
- s_score = float(sem_res.get('semantic_score', 0.0))
41
-
42
- # Lógica de Semáforo de Riesgo
43
- if z_score < 1.8 or m_dsri > 1.4 or s_score > 3:
44
- status = "RED"
45
- elif z_score < 3.0:
46
- status = "YELLOW"
47
- else:
48
- status = "GREEN"
49
-
50
- results.append({
51
- "ticker": ticker,
52
- "status": status,
53
- "z_score": z_score,
54
- "summary": sem_res.get("summary", ""),
55
- "alert": True if status == "RED" else False
56
- })
57
- except Exception as ticker_error:
58
- results.append({"ticker": ticker, "status": "ERROR", "msg": str(ticker_error)})
59
-
60
- critical_alerts = [r for r in results if r.get("status") in ["RED", "YELLOW"]]
61
- return {
62
- "total_analyzed": len(watchlist),
63
- "critical_count": len(critical_alerts),
64
- "alerts": critical_alerts,
65
- "full_results": results
66
- }
67
-
68
- @app.get("/audit/{ticker}")
69
- def audit_company(ticker: str):
70
- """Auditoría individual para el tab de Stock Audit."""
71
- try:
72
- engine = VertexRiskEngine(ticker.upper())
73
- num_res = engine.run_audit()
74
-
75
- sem_agent = VertexSemanticAgent(ticker.upper())
76
- risk_text = sem_agent.get_sec_risk_factors()
77
- sem_res = sem_agent.judge_risks(risk_text)
78
-
79
- # Blindaje de tipos
80
- z_score = float(num_res.get('altman_z', 0.0))
81
- m_dsri = float(num_res.get('m_score_dsri', 0.0))
82
- s_score = float(sem_res.get('semantic_score', 0.0))
83
-
84
- if z_score < 1.8 or m_dsri > 1.4 or s_score > 3:
85
- status = "RED"
86
- msg = "CRITICAL RISK: Red flags detected."
87
- elif z_score < 3.0:
88
- status = "YELLOW"
89
- msg = "CAUTION: Monitor closely."
90
- else:
91
- status = "GREEN"
92
- msg = "SAFE: Solid fundamentals."
93
-
94
- return {
95
- "ticker": ticker.upper(),
96
- "status": status,
97
- "numeric_analysis": {"altman_z": z_score, "m_score_dsri": m_dsri},
98
- "semantic_analysis": sem_res,
99
- "msg": msg
100
- }
101
- except Exception as e:
102
- return {"status": "ERROR", "msg": str(e)}
103
-
104
- @app.get("/audit_contract/{address}")
105
- def audit_smart_contract(address: str):
106
- """Auditoría de Web3 usando el nuevo motor V2."""
107
- try:
108
- web3_engine = VertexWeb3Engine(address)
109
- audit_res = web3_engine.get_contract_source()
110
-
111
- if not audit_res["success"]:
112
- return {"status": "ERROR", "msg": audit_res["error"]}
113
-
114
- source = audit_res["source_code"]
115
- vulnerabilities = web3_engine.scan_basic_vulnerabilities(source)
116
-
117
- return {
118
- "address": address,
119
- "status": "DANGER" if len(vulnerabilities) > 0 else "SAFE",
120
- "vulnerabilities": vulnerabilities,
121
- "source_preview": source[:500] + "..." # Ahora source es string y el slice no falla
122
- }
123
- except Exception as e:
124
- return {"status": "ERROR", "msg": str(e)}
125
-
126
- @app.get("/get_settings")
127
- async def get_settings():
128
- try:
129
- path = "app/settings.json"
130
- if os.path.exists(path):
131
- with open(path, "r") as f:
132
- content = f.read().strip()
133
- if not content:
134
- return {"bot_token": "", "chat_id": "", "status": "empty_file"}
135
- return json.loads(content)
136
- return {"bot_token": "", "chat_id": "", "status": "not_found"}
137
- except Exception as e:
138
- # Esto captura el error "Expecting value" y devuelve un JSON válido para n8n
139
- return {"bot_token": "", "chat_id": "", "error": f"JSON Error: {str(e)}"}
140
-
141
- if __name__ == "__main__":
142
- # Hugging Face SIEMPRE usa el puerto 7860 internamente
143
- port = int(os.environ.get("PORT", 7860))
144
- # Importante: host="0.0.0.0" para que sea accesible desde fuera del contenedor
145
- uvicorn.run(app, host="0.0.0.0", port=port)
 
1
+ import json
2
+ import os
3
+ from fastapi import FastAPI
4
+ import uvicorn
5
+ from engine.risk_engine import VertexRiskEngine
6
+ from engine.semantic import VertexSemanticAgent
7
+ from engine.web3_engine import VertexWeb3Engine
8
+
9
+ app = FastAPI()
10
+
11
+ # Archivo de persistencia de la watchlist
12
+ WATCHLIST_FILE = "app/watchlist.json"
13
+
14
+ @app.get("/batch_audit")
15
+ def run_batch_audit():
16
+ """Audita toda la watchlist y devuelve alertas para n8n."""
17
+ if not os.path.exists(WATCHLIST_FILE):
18
+ return {"error": "Watchlist file not found", "alerts": []}
19
+
20
+ try:
21
+ with open(WATCHLIST_FILE, "r") as f:
22
+ watchlist = json.load(f)
23
+ except Exception as e:
24
+ return {"error": f"Failed to read watchlist: {e}", "alerts": []}
25
+
26
+ results = []
27
+ for ticker in watchlist:
28
+ try:
29
+ # Reutilizamos la lógica del motor Némesis
30
+ engine = VertexRiskEngine(ticker.upper())
31
+ num_res = engine.run_audit()
32
+
33
+ sem_agent = VertexSemanticAgent(ticker.upper())
34
+ risk_text = sem_agent.get_sec_risk_factors()
35
+ sem_res = sem_agent.judge_risks(risk_text)
36
+
37
+ # Blindaje de tipos para evitar errores de comparación
38
+ z_score = float(num_res.get('altman_z', 0.0))
39
+ m_dsri = float(num_res.get('m_score_dsri', 0.0))
40
+ s_score = float(sem_res.get('semantic_score', 0.0))
41
+
42
+ # Lógica de Semáforo de Riesgo
43
+ if z_score < 1.8 or m_dsri > 1.4 or s_score > 3:
44
+ status = "RED"
45
+ elif z_score < 3.0:
46
+ status = "YELLOW"
47
+ else:
48
+ status = "GREEN"
49
+
50
+ results.append({
51
+ "ticker": ticker,
52
+ "status": status,
53
+ "z_score": z_score,
54
+ "summary": sem_res.get("summary", ""),
55
+ "alert": True if status == "RED" else False
56
+ })
57
+ except Exception as ticker_error:
58
+ results.append({"ticker": ticker, "status": "ERROR", "msg": str(ticker_error)})
59
+
60
+ critical_alerts = [r for r in results if r.get("status") in ["RED", "YELLOW"]]
61
+ return {
62
+ "total_analyzed": len(watchlist),
63
+ "critical_count": len(critical_alerts),
64
+ "alerts": critical_alerts,
65
+ "full_results": results
66
+ }
67
+
68
+ @app.get("/audit/{ticker}")
69
+ def audit_company(ticker: str):
70
+ """Auditoría individual para el tab de Stock Audit."""
71
+ try:
72
+ engine = VertexRiskEngine(ticker.upper())
73
+ num_res = engine.run_audit()
74
+
75
+ sem_agent = VertexSemanticAgent(ticker.upper())
76
+ risk_text = sem_agent.get_sec_risk_factors()
77
+ sem_res = sem_agent.judge_risks(risk_text)
78
+
79
+ # Blindaje de tipos
80
+ z_score = float(num_res.get('altman_z', 0.0))
81
+ m_dsri = float(num_res.get('m_score_dsri', 0.0))
82
+ s_score = float(sem_res.get('semantic_score', 0.0))
83
+
84
+ if z_score < 1.8 or m_dsri > 1.4 or s_score > 3:
85
+ status = "RED"
86
+ msg = "CRITICAL RISK: Red flags detected."
87
+ elif z_score < 3.0:
88
+ status = "YELLOW"
89
+ msg = "CAUTION: Monitor closely."
90
+ else:
91
+ status = "GREEN"
92
+ msg = "SAFE: Solid fundamentals."
93
+
94
+ return {
95
+ "ticker": ticker.upper(),
96
+ "status": status,
97
+ "numeric_analysis": {"altman_z": z_score, "m_score_dsri": m_dsri},
98
+ "semantic_analysis": sem_res,
99
+ "msg": msg
100
+ }
101
+ except Exception as e:
102
+ return {"status": "ERROR", "msg": str(e)}
103
+
104
+ @app.get("/audit_contract/{address}")
105
+ def audit_smart_contract(address: str):
106
+ """Auditoría de Web3 usando el nuevo motor V2."""
107
+ try:
108
+ web3_engine = VertexWeb3Engine(address)
109
+ audit_res = web3_engine.get_contract_source()
110
+
111
+ if not audit_res["success"]:
112
+ return {"status": "ERROR", "msg": audit_res["error"]}
113
+
114
+ source = audit_res["source_code"]
115
+ vulnerabilities = web3_engine.scan_basic_vulnerabilities(source)
116
+
117
+ return {
118
+ "address": address,
119
+ "status": "DANGER" if len(vulnerabilities) > 0 else "SAFE",
120
+ "vulnerabilities": vulnerabilities,
121
+ "source_preview": source[:500] + "..." # Ahora source es string y el slice no falla
122
+ }
123
+ except Exception as e:
124
+ return {"status": "ERROR", "msg": str(e)}
125
+
126
+ @app.get("/get_settings")
127
+ async def get_settings():
128
+ # Esta línea es la que saca las llaves de los Secrets de Hugging Face
129
+ token = os.environ.get("BOT_TOKEN", "")
130
+ chat = os.environ.get("CHAT_ID", "")
131
+
132
+ if token and chat:
133
+ return {"bot_token": token, "chat_id": chat, "status": "loaded_from_secrets"}
134
+
135
+ return {"bot_token": "", "chat_id": "", "status": "secrets_not_found"}
136
+
137
+ if __name__ == "__main__":
138
+ # Hugging Face SIEMPRE usa el puerto 7860 internamente
139
+ port = int(os.environ.get("PORT", 7860))
140
+ # Importante: host="0.0.0.0" para que sea accesible desde fuera del contenedor
141
+ uvicorn.run(app, host="0.0.0.0", port=port)