Spaces:
Running
Running
File size: 6,278 Bytes
ddaf3fc 1a96a66 ddaf3fc 8e4b913 1a96a66 ddaf3fc 1a96a66 ddaf3fc 1a96a66 ddaf3fc 1a96a66 ddaf3fc 1a96a66 ddaf3fc 1a96a66 ddaf3fc 1a96a66 ddaf3fc 1a96a66 |
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 |
"""
A sophisticated analyzer using the Google Gemini Pro API.
This module provides structured analysis of financial text, including:
- Nuanced sentiment with reasoning.
- Key entity extraction (e.g., cryptocurrencies).
- Topic classification.
- Potential market impact assessment.
- Synthesis of multiple news items into a daily briefing.
"""
import os
import logging
import httpx
import json
from typing import Optional, TypedDict, List, Union
# Configure logging
logger = logging.getLogger(__name__)
# --- Type Definitions for Structured Data ---
class AnalysisResult(TypedDict):
sentiment: str
sentiment_score: float
reason: str
entities: List[str]
topic: str
impact: str
summary: str
error: Optional[str]
url: Optional[str] # To store the article URL
class GeminiAnalyzer:
"""Manages interaction with the Google Gemini API for deep text analysis."""
API_URL = "https://generativelanguage.googleapis.com/v1beta/models/gemini-1.5-pro-latest:generateContent"
def __init__(self, client: httpx.AsyncClient, api_key: Optional[str] = None):
self.client = client
self.api_key = api_key or os.getenv("GEMINI_API_KEY")
if not self.api_key:
raise ValueError("GEMINI_API_KEY is not set. Please add it as a repository secret.")
self.params = {"key": self.api_key}
self.headers = {"Content-Type": "application/json"}
def _build_analysis_prompt(self, text: str) -> dict:
"""Creates the structured JSON prompt for analyzing a single piece of text."""
return {
"contents": [{
"parts": [{
"text": f"""
Analyze the following financial text from the cryptocurrency world.
Provide your analysis as a single, minified JSON object with NO markdown formatting.
The JSON object must have these exact keys: "sentiment", "sentiment_score", "reason", "entities", "topic", "impact", "summary".
- "sentiment": MUST be one of "POSITIVE", "NEGATIVE", or "NEUTRAL".
- "sentiment_score": A float between -1.0 (very negative) and 1.0 (very positive).
- "reason": A brief, one-sentence explanation for the sentiment score.
- "entities": A JSON array of strings listing the primary cryptocurrencies or tokens mentioned (e.g., ["Bitcoin", "ETH"]).
- "topic": MUST be one of "Regulation", "Partnership", "Technical Update", "Market Hype", "Security", or "General News".
- "impact": Assess the potential short-term market impact. MUST be one of "LOW", "MEDIUM", or "HIGH".
- "summary": A concise, one-sentence summary of the provided text.
Text to analyze: "{text}"
"""
}]
}]
}
async def analyze_text(self, text: str) -> AnalysisResult:
"""Sends text to Gemini and returns a structured analysis."""
prompt = self._build_analysis_prompt(text)
try:
response = await self.client.post(self.API_URL, headers=self.headers, params=self.params, json=prompt, timeout=60.0)
response.raise_for_status()
full_response = response.json()
json_text = full_response["candidates"][0]["content"]["parts"][0]["text"]
analysis: AnalysisResult = json.loads(json_text)
analysis["error"] = None
return analysis
except Exception as e:
logger.error(f"β Gemini Analysis Error: {e}")
return {
"sentiment": "ERROR", "sentiment_score": 0, "reason": str(e),
"entities": [], "topic": "Unknown", "impact": "Unknown",
"summary": "Failed to analyze text due to an API or parsing error.", "error": str(e)
}
async def generate_daily_briefing(self, analysis_items: List[dict]) -> str:
"""Generates a high-level market briefing from a list of analyzed news items."""
if not analysis_items:
return "### Briefing Unavailable\nNo news items were analyzed in the last period."
context = "\n".join([f"- {item.get('summary')} (Impact: {item.get('impact')}, Topic: {item.get('topic')})" for item in analysis_items])
briefing_prompt = {
"contents": [{
"parts": [{
"text": f"""
You are a senior crypto market analyst named 'Sentinel'. Your tone is professional, concise, and insightful.
Based on the following list of analyzed news items from the last 24 hours, write a "Daily Market Briefing".
The briefing must have three sections using markdown:
1. "### Executive Summary": A single, impactful paragraph summarizing the overall market mood and key events.
2. "### Top Bullish Signals": 2-3 bullet points on the most positive developments.
3. "### Top Bearish Signals": 2-3 bullet points on the most significant risks or negative news.
Here is the data to analyze:
{context}
"""
}]
}],
"safetySettings": [
{"category": "HARM_CATEGORY_HARASSMENT", "threshold": "BLOCK_NONE"},
{"category": "HARM_CATEGORY_HATE_SPEECH", "threshold": "BLOCK_NONE"},
{"category": "HARM_CATEGORY_SEXUALLY_EXPLICIT", "threshold": "BLOCK_NONE"},
{"category": "HARM_CATEGORY_DANGEROUS_CONTENT", "threshold": "BLOCK_NONE"},
]
}
try:
response = await self.client.post(self.API_URL, headers=self.headers, params=self.params, json=briefing_prompt, timeout=120.0)
response.raise_for_status()
full_response = response.json()
briefing_text = full_response["candidates"][0]["content"]["parts"][0]["text"]
return briefing_text
except Exception as e:
logger.error(f"β Gemini Briefing Error: {e}")
return "### Briefing Unavailable\nCould not generate the daily market briefing due to a Gemini API error." |