mgbam commited on
Commit
1a96a66
·
verified ·
1 Parent(s): fef2ed9

Update app/gemini_analyzer.py

Browse files
Files changed (1) hide show
  1. app/gemini_analyzer.py +53 -16
app/gemini_analyzer.py CHANGED
@@ -6,22 +6,18 @@ This module provides structured analysis of financial text, including:
6
  - Key entity extraction (e.g., cryptocurrencies).
7
  - Topic classification.
8
  - Potential market impact assessment.
 
9
  """
10
  import os
11
  import logging
12
  import httpx
13
- # ====================================================================
14
- # FIX APPLIED HERE
15
- # ====================================================================
16
- # Import the missing 'json' module
17
  import json
18
- # ====================================================================
19
- from typing import Optional, TypedDict, List
20
 
21
  # Configure logging
22
  logger = logging.getLogger(__name__)
23
 
24
- # --- Pydantic-like models for structured output ---
25
  class AnalysisResult(TypedDict):
26
  sentiment: str
27
  sentiment_score: float
@@ -31,6 +27,7 @@ class AnalysisResult(TypedDict):
31
  impact: str
32
  summary: str
33
  error: Optional[str]
 
34
 
35
  class GeminiAnalyzer:
36
  """Manages interaction with the Google Gemini API for deep text analysis."""
@@ -45,8 +42,8 @@ class GeminiAnalyzer:
45
  self.params = {"key": self.api_key}
46
  self.headers = {"Content-Type": "application/json"}
47
 
48
- def _build_prompt(self, text: str) -> dict:
49
- """Creates the structured JSON prompt for the Gemini API."""
50
  return {
51
  "contents": [{
52
  "parts": [{
@@ -72,24 +69,64 @@ class GeminiAnalyzer:
72
 
73
  async def analyze_text(self, text: str) -> AnalysisResult:
74
  """Sends text to Gemini and returns a structured analysis."""
75
- prompt = self._build_prompt(text)
76
  try:
77
  response = await self.client.post(self.API_URL, headers=self.headers, params=self.params, json=prompt, timeout=60.0)
78
  response.raise_for_status()
79
 
80
- # Extract the JSON content from the response
81
  full_response = response.json()
82
  json_text = full_response["candidates"][0]["content"]["parts"][0]["text"]
83
 
84
- # The output is a JSON string, so we parse it.
85
- analysis = json.loads(json_text)
86
  analysis["error"] = None
87
  return analysis
88
 
89
  except Exception as e:
90
- logger.error(f"❌ Gemini API Error: {e}")
91
  return {
92
  "sentiment": "ERROR", "sentiment_score": 0, "reason": str(e),
93
  "entities": [], "topic": "Unknown", "impact": "Unknown",
94
- "summary": "Failed to analyze text.", "error": str(e)
95
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6
  - Key entity extraction (e.g., cryptocurrencies).
7
  - Topic classification.
8
  - Potential market impact assessment.
9
+ - Synthesis of multiple news items into a daily briefing.
10
  """
11
  import os
12
  import logging
13
  import httpx
 
 
 
 
14
  import json
15
+ from typing import Optional, TypedDict, List, Union
 
16
 
17
  # Configure logging
18
  logger = logging.getLogger(__name__)
19
 
20
+ # --- Type Definitions for Structured Data ---
21
  class AnalysisResult(TypedDict):
22
  sentiment: str
23
  sentiment_score: float
 
27
  impact: str
28
  summary: str
29
  error: Optional[str]
30
+ url: Optional[str] # To store the article URL
31
 
32
  class GeminiAnalyzer:
33
  """Manages interaction with the Google Gemini API for deep text analysis."""
 
42
  self.params = {"key": self.api_key}
43
  self.headers = {"Content-Type": "application/json"}
44
 
45
+ def _build_analysis_prompt(self, text: str) -> dict:
46
+ """Creates the structured JSON prompt for analyzing a single piece of text."""
47
  return {
48
  "contents": [{
49
  "parts": [{
 
69
 
70
  async def analyze_text(self, text: str) -> AnalysisResult:
71
  """Sends text to Gemini and returns a structured analysis."""
72
+ prompt = self._build_analysis_prompt(text)
73
  try:
74
  response = await self.client.post(self.API_URL, headers=self.headers, params=self.params, json=prompt, timeout=60.0)
75
  response.raise_for_status()
76
 
 
77
  full_response = response.json()
78
  json_text = full_response["candidates"][0]["content"]["parts"][0]["text"]
79
 
80
+ analysis: AnalysisResult = json.loads(json_text)
 
81
  analysis["error"] = None
82
  return analysis
83
 
84
  except Exception as e:
85
+ logger.error(f"❌ Gemini Analysis Error: {e}")
86
  return {
87
  "sentiment": "ERROR", "sentiment_score": 0, "reason": str(e),
88
  "entities": [], "topic": "Unknown", "impact": "Unknown",
89
+ "summary": "Failed to analyze text due to an API or parsing error.", "error": str(e)
90
+ }
91
+
92
+ async def generate_daily_briefing(self, analysis_items: List[dict]) -> str:
93
+ """Generates a high-level market briefing from a list of analyzed news items."""
94
+ if not analysis_items:
95
+ return "### Briefing Unavailable\nNo news items were analyzed in the last period."
96
+
97
+ context = "\n".join([f"- {item.get('summary')} (Impact: {item.get('impact')}, Topic: {item.get('topic')})" for item in analysis_items])
98
+
99
+ briefing_prompt = {
100
+ "contents": [{
101
+ "parts": [{
102
+ "text": f"""
103
+ You are a senior crypto market analyst named 'Sentinel'. Your tone is professional, concise, and insightful.
104
+ Based on the following list of analyzed news items from the last 24 hours, write a "Daily Market Briefing".
105
+
106
+ The briefing must have three sections using markdown:
107
+ 1. "### Executive Summary": A single, impactful paragraph summarizing the overall market mood and key events.
108
+ 2. "### Top Bullish Signals": 2-3 bullet points on the most positive developments.
109
+ 3. "### Top Bearish Signals": 2-3 bullet points on the most significant risks or negative news.
110
+
111
+ Here is the data to analyze:
112
+ {context}
113
+ """
114
+ }]
115
+ }],
116
+ "safetySettings": [
117
+ {"category": "HARM_CATEGORY_HARASSMENT", "threshold": "BLOCK_NONE"},
118
+ {"category": "HARM_CATEGORY_HATE_SPEECH", "threshold": "BLOCK_NONE"},
119
+ {"category": "HARM_CATEGORY_SEXUALLY_EXPLICIT", "threshold": "BLOCK_NONE"},
120
+ {"category": "HARM_CATEGORY_DANGEROUS_CONTENT", "threshold": "BLOCK_NONE"},
121
+ ]
122
+ }
123
+
124
+ try:
125
+ response = await self.client.post(self.API_URL, headers=self.headers, params=self.params, json=briefing_prompt, timeout=120.0)
126
+ response.raise_for_status()
127
+ full_response = response.json()
128
+ briefing_text = full_response["candidates"][0]["content"]["parts"][0]["text"]
129
+ return briefing_text
130
+ except Exception as e:
131
+ logger.error(f"❌ Gemini Briefing Error: {e}")
132
+ return "### Briefing Unavailable\nCould not generate the daily market briefing due to a Gemini API error."