cicboy commited on
Commit
708f080
·
1 Parent(s): 3a209c3

update sentiment_tool.py

Browse files
Files changed (1) hide show
  1. tools/sentiment_tool.py +107 -48
tools/sentiment_tool.py CHANGED
@@ -5,77 +5,136 @@ from openai import OpenAI
5
  from typing import Type
6
  from pydantic import BaseModel, Field
7
 
8
- SERPER_API_KEY = os.getenv(key="SERPER_API_KEY")
9
- OPENAI_API_KEY = os.getenv(key = "OPENAI_API_KEY")
 
10
 
11
  client = OpenAI(api_key=OPENAI_API_KEY)
12
 
 
 
 
 
13
  class SentimentInput(BaseModel):
14
- query: str = Field(default="bitcoin", description="Cryptocurrency name to evaluate sentiment for.")
 
 
 
 
15
 
 
 
 
16
  class SentimentTool(BaseTool):
17
  name: str = "get_crypto_sentiment"
18
  description: str = (
19
- "Fetches recent cryptocurrency news and Reddit discussions,"
20
- "then analyses sentiment as bullish, bearish or neutral."
 
21
  )
22
- arg_schema: Type[BaseModel] = SentimentInput
 
 
 
 
 
 
 
 
 
 
 
 
 
 
23
 
24
- def _run(self, query: str = "bitcoin") -> str:
 
 
 
25
  try:
26
- #Google news sentiment
27
- headlines = []
28
- try:
29
- news_url = "https://google.serper.dev/news"
30
- headers = {"X-API-KEY": SERPER_API_KEY, "Content-Type": "application/json"}
31
- payload = {"q": f"{query} crypto", "num": 5}
32
- news_response = requests.post(news_url, headers=headers, json=payload, timeout=10)
33
- news_response.raise_for_status()
34
- news = news_response.json().get("news", [])
35
- headlines = [n["title"] for n in news[:5]]
36
- except Exception:
37
- pass # silent fail
38
-
39
- #Reddit sentiment
 
 
 
 
 
 
 
 
 
 
 
40
  reddit_titles = []
41
- try:
42
- reddit_url = f"https://www.reddit.com/search.json?q={query}&sort=new&limit=5"
43
- reddit_headers = {"User-Agent": "CryptoAgent/1.0"}
44
- reddit_response = requests.get(reddit_url,headers=reddit_headers, timeout=10)
45
- reddit_response.raise_for_status()
46
- reddit_posts = reddit_response.json().get("data", {}).get("children", [])
47
- reddit_titles = [p["data"]["title"] for p in reddit_posts[:5]]
48
- except Exception:
49
- pass
50
-
51
- combined_text = "News: " + " | ".join(headlines) + "\nReddit: " + " | ".join(reddit_titles)
52
-
53
- #analyze sentiment
 
 
 
 
 
 
 
 
 
 
54
  sentiment_prompt = f"""
55
- You are a crypto sentiment analyst. Based on the following headlines and Reddit posts,
56
- intuitively determine if the overall sentiment towards "{query}" is bullish, bearish, or neutral.
57
- Respond with a short summary and a one-word sentiment classification.
58
 
59
- {combined_text}
 
 
 
60
 
61
- Return ONLY valid JSON in the format:
62
  {{
63
  "sentiment": "bullish/bearish/neutral",
64
- "reasoning": "short explanation",
65
  "news_headlines": [...],
66
- "reddit_titles": [...]
 
 
67
  }}
68
- """
 
 
 
 
 
 
 
69
  completion = client.chat.completions.create(
70
  model="gpt-4.1",
71
  messages=[
72
  {"role": "system", "content": "You are a precise sentiment classifier."},
73
  {"role": "user", "content": sentiment_prompt}
74
- ],
75
- temperature=0.2
76
  )
77
- sentiment_result = completion.choices[0].message.content
78
- return sentiment_result
79
-
80
  except Exception as e:
81
- return f"Error fetching sentiment data: {str(e)}"
 
5
  from typing import Type
6
  from pydantic import BaseModel, Field
7
 
8
+ # Environment variables
9
+ SERPER_API_KEY = os.getenv("SERPER_API_KEY")
10
+ OPENAI_API_KEY = os.getenv("OPENAI_API_KEY")
11
 
12
  client = OpenAI(api_key=OPENAI_API_KEY)
13
 
14
+
15
+ # -----------------------------
16
+ # Pydantic Input Schema
17
+ # -----------------------------
18
  class SentimentInput(BaseModel):
19
+ query: str = Field(
20
+ default="bitcoin",
21
+ description="Cryptocurrency to evaluate sentiment for."
22
+ )
23
+
24
 
25
+ # -----------------------------
26
+ # Sentiment Tool (Serper-only)
27
+ # -----------------------------
28
  class SentimentTool(BaseTool):
29
  name: str = "get_crypto_sentiment"
30
  description: str = (
31
+ "Fetches recent cryptocurrency sentiment using Serper.dev, including "
32
+ "Google News headlines and Reddit discussions, then classifies overall "
33
+ "sentiment as bullish, bearish, or neutral."
34
  )
35
+ args_schema: Type[BaseModel] = SentimentInput
36
+
37
+ # -----------------------------
38
+ # Helper: call Serper API safely
39
+ # -----------------------------
40
+ def _serper_post(self, endpoint: str, payload: dict):
41
+ url = f"https://google.serper.dev/{endpoint}"
42
+ headers = {"X-API-KEY": SERPER_API_KEY, "Content-Type": "application/json"}
43
+
44
+ try:
45
+ response = requests.post(url, headers=headers, json=payload, timeout=10)
46
+ response.raise_for_status()
47
+ return response.json()
48
+ except Exception as e:
49
+ return {"error": str(e)}
50
 
51
+ # -----------------------------
52
+ # Main execution
53
+ # -----------------------------
54
+ def _run(self, query: str = "bitcoin"):
55
  try:
56
+ # -------------------------
57
+ # 1️⃣ Fetch Google News
58
+ # -------------------------
59
+ news_result = self._serper_post(
60
+ "news",
61
+ {"q": f"{query} crypto", "num": 5}
62
+ )
63
+
64
+ news_headlines = []
65
+ news_error = None
66
+
67
+ if "error" in news_result:
68
+ news_error = news_result["error"]
69
+ else:
70
+ raw_news = news_result.get("news", [])
71
+ news_headlines = [item.get("title", "") for item in raw_news[:5]]
72
+
73
+ # -------------------------
74
+ # 2️⃣ Fetch Reddit Discussions
75
+ # -------------------------
76
+ reddit_result = self._serper_post(
77
+ "reddit",
78
+ {"q": query, "num": 5}
79
+ )
80
+
81
  reddit_titles = []
82
+ reddit_error = None
83
+
84
+ if "error" in reddit_result:
85
+ reddit_error = reddit_result["error"]
86
+ else:
87
+ raw_reddit = reddit_result.get("organic", [])
88
+ reddit_titles = [item.get("title", "") for item in raw_reddit[:5]]
89
+
90
+ # -------------------------
91
+ # 3️⃣ Create LLM prompt
92
+ # -------------------------
93
+ combined_text = f"""
94
+ News Headlines:
95
+ {news_headlines}
96
+
97
+ Reddit Posts:
98
+ {reddit_titles}
99
+
100
+ Errors:
101
+ news_error={news_error}
102
+ reddit_error={reddit_error}
103
+ """
104
+
105
  sentiment_prompt = f"""
106
+ You are a cryptocurrency sentiment analyst.
 
 
107
 
108
+ Given the following news and Reddit discussions for "{query}", classify overall sentiment
109
+ as **bullish**, **bearish**, or **neutral**.
110
+
111
+ Return ONLY valid JSON in the exact structure below:
112
 
 
113
  {{
114
  "sentiment": "bullish/bearish/neutral",
115
+ "reasoning": "Short explanation",
116
  "news_headlines": [...],
117
+ "reddit_titles": [...],
118
+ "news_error": "... or null",
119
+ "reddit_error": "... or null"
120
  }}
121
+
122
+ Here is the data to analyze:
123
+ {combined_text}
124
+ """
125
+
126
+ # -------------------------
127
+ # 4️⃣ Call OpenAI GPT-4.1
128
+ # -------------------------
129
  completion = client.chat.completions.create(
130
  model="gpt-4.1",
131
  messages=[
132
  {"role": "system", "content": "You are a precise sentiment classifier."},
133
  {"role": "user", "content": sentiment_prompt}
134
+ ]
 
135
  )
136
+
137
+ return completion.choices[0].message.content
138
+
139
  except Exception as e:
140
+ return {"error": f"SentimentTool failed: {str(e)}"}