Spaces:
Running
Running
Update app/app.py
Browse files- app/app.py +10 -15
app/app.py
CHANGED
@@ -18,11 +18,14 @@ from fastapi import FastAPI, Request
|
|
18 |
from fastapi.responses import HTMLResponse, StreamingResponse
|
19 |
from fastapi.templating import Jinja2Templates
|
20 |
|
21 |
-
#
|
22 |
-
#
|
23 |
-
|
24 |
-
|
|
|
|
|
25 |
from newsapi import NewsApiClient
|
|
|
26 |
|
27 |
|
28 |
# --- Application Lifespan for Resource Management ---
|
@@ -39,17 +42,15 @@ async def lifespan(app: FastAPI):
|
|
39 |
app.state.gemini_analyzer = GeminiAnalyzer(client=client)
|
40 |
app.state.news_api = NewsApiClient(api_key=os.getenv("NEWS_API_KEY"))
|
41 |
|
42 |
-
#
|
43 |
-
# We only need ONE queue for the autonomous signal stream.
|
44 |
app.state.signal_queue: asyncio.Queue = asyncio.Queue()
|
45 |
-
# =============================================================
|
46 |
|
47 |
# Create cancellable background tasks for periodic updates.
|
48 |
price_task = asyncio.create_task(
|
49 |
run_periodic_updates(app.state.price_fetcher, interval_seconds=60)
|
50 |
)
|
51 |
news_task = asyncio.create_task(
|
52 |
-
run_periodic_news_analysis(app, interval_seconds=900) #
|
53 |
)
|
54 |
|
55 |
print("π Sentinel TradeFlow Protocol started successfully.")
|
@@ -86,17 +87,13 @@ async def run_periodic_news_analysis(app: FastAPI, interval_seconds: int):
|
|
86 |
title = article.get('title')
|
87 |
if title and "[Removed]" not in title:
|
88 |
analysis = await analyzer.analyze_text(title)
|
89 |
-
# Don't proceed if Gemini returned an error
|
90 |
if analysis.get("error"):
|
91 |
print(f"Skipping article due to Gemini error: {analysis['reason']}")
|
92 |
continue
|
93 |
|
94 |
analysis['url'] = article.get('url')
|
95 |
-
# =================== FIX APPLIED HERE (2/2) ===================
|
96 |
-
# Ensure the result is put into the one and only signal_queue.
|
97 |
await app.state.signal_queue.put(analysis)
|
98 |
print(f"β
Signal generated and queued for: {title}")
|
99 |
-
# =============================================================
|
100 |
|
101 |
except Exception as e:
|
102 |
print(f"β Error during news fetching or analysis: {e}")
|
@@ -106,6 +103,7 @@ async def run_periodic_news_analysis(app: FastAPI, interval_seconds: int):
|
|
106 |
# --- FastAPI App Initialization ---
|
107 |
|
108 |
app = FastAPI(title="Sentinel TradeFlow Protocol", lifespan=lifespan)
|
|
|
109 |
templates = Jinja2Templates(directory="templates")
|
110 |
|
111 |
# --- HTML Rendering Helper ---
|
@@ -144,11 +142,8 @@ async def serve_dashboard(request: Request):
|
|
144 |
@app.get("/api/signals/stream")
|
145 |
async def signal_stream(request: Request):
|
146 |
"""SSE stream for the automated Signal Stream."""
|
147 |
-
# This was already correct from the last fix, but we confirm it points to signal_queue.
|
148 |
queue: asyncio.Queue = request.app.state.signal_queue
|
149 |
async def event_generator():
|
150 |
-
# Let's send a confirmation that the stream is connected and ready
|
151 |
-
yield f"event: message\ndata: <div hx-swap-oob='innerHTML' id='signal-stream-container'><p>Status: ONLINE - Listening for new market signals...</p></div>\n\n"
|
152 |
while True:
|
153 |
payload = await queue.get()
|
154 |
html = render_signal_card(payload)
|
|
|
18 |
from fastapi.responses import HTMLResponse, StreamingResponse
|
19 |
from fastapi.templating import Jinja2Templates
|
20 |
|
21 |
+
# ====================================================================
|
22 |
+
# FIX APPLIED HERE
|
23 |
+
# ====================================================================
|
24 |
+
# Use relative imports because these modules are in the same 'app' package.
|
25 |
+
from .price_fetcher import PriceFetcher
|
26 |
+
from .gemini_analyzer import GeminiAnalyzer
|
27 |
from newsapi import NewsApiClient
|
28 |
+
# ====================================================================
|
29 |
|
30 |
|
31 |
# --- Application Lifespan for Resource Management ---
|
|
|
42 |
app.state.gemini_analyzer = GeminiAnalyzer(client=client)
|
43 |
app.state.news_api = NewsApiClient(api_key=os.getenv("NEWS_API_KEY"))
|
44 |
|
45 |
+
# Create a queue for the real-time signal feed
|
|
|
46 |
app.state.signal_queue: asyncio.Queue = asyncio.Queue()
|
|
|
47 |
|
48 |
# Create cancellable background tasks for periodic updates.
|
49 |
price_task = asyncio.create_task(
|
50 |
run_periodic_updates(app.state.price_fetcher, interval_seconds=60)
|
51 |
)
|
52 |
news_task = asyncio.create_task(
|
53 |
+
run_periodic_news_analysis(app, interval_seconds=900) # Run every 15 minutes
|
54 |
)
|
55 |
|
56 |
print("π Sentinel TradeFlow Protocol started successfully.")
|
|
|
87 |
title = article.get('title')
|
88 |
if title and "[Removed]" not in title:
|
89 |
analysis = await analyzer.analyze_text(title)
|
|
|
90 |
if analysis.get("error"):
|
91 |
print(f"Skipping article due to Gemini error: {analysis['reason']}")
|
92 |
continue
|
93 |
|
94 |
analysis['url'] = article.get('url')
|
|
|
|
|
95 |
await app.state.signal_queue.put(analysis)
|
96 |
print(f"β
Signal generated and queued for: {title}")
|
|
|
97 |
|
98 |
except Exception as e:
|
99 |
print(f"β Error during news fetching or analysis: {e}")
|
|
|
103 |
# --- FastAPI App Initialization ---
|
104 |
|
105 |
app = FastAPI(title="Sentinel TradeFlow Protocol", lifespan=lifespan)
|
106 |
+
# This path is relative to the root where the uvicorn command is run
|
107 |
templates = Jinja2Templates(directory="templates")
|
108 |
|
109 |
# --- HTML Rendering Helper ---
|
|
|
142 |
@app.get("/api/signals/stream")
|
143 |
async def signal_stream(request: Request):
|
144 |
"""SSE stream for the automated Signal Stream."""
|
|
|
145 |
queue: asyncio.Queue = request.app.state.signal_queue
|
146 |
async def event_generator():
|
|
|
|
|
147 |
while True:
|
148 |
payload = await queue.get()
|
149 |
html = render_signal_card(payload)
|