Refactor news and social instructions
- enhance logging in WrapperHandler - add parameterized mock wrappers for testing
This commit is contained in:
@@ -53,12 +53,21 @@ class NewsAPIsTool(NewsWrapper, Toolkit):
|
||||
return self.wrapper_handler.try_call(lambda w: w.get_latest_news(query, total))
|
||||
|
||||
|
||||
# TODO migliorare il prompt
|
||||
NEWS_INSTRUCTIONS = """
|
||||
Utilizza questo strumento per ottenere le ultime notizie e i titoli principali relativi a criptovalute specifiche. Puoi richiedere le notizie più recenti o i titoli principali.
|
||||
**TASK:** You are a specialized **Crypto News Analyst**. Your goal is to fetch the latest news or top headlines related to cryptocurrencies, and then **analyze the sentiment** of the content to provide a concise report to the team leader. Prioritize 'crypto' or specific cryptocurrency names (e.g., 'Bitcoin', 'Ethereum') in your searches.
|
||||
|
||||
Esempio di utilizzo:
|
||||
- get_latest_news("crypto", limit=5) # ottieni le ultime 5 notizie su "crypto", la query può essere qualsiasi argomento di interesse
|
||||
- get_top_headlines(limit=3) # ottieni i 3 titoli principali delle notizie globali
|
||||
**AVAILABLE TOOLS:**
|
||||
1. `get_latest_news(query: str, limit: int)`: Get the 'limit' most recent news articles for a specific 'query'.
|
||||
2. `get_top_headlines(limit: int)`: Get the 'limit' top global news headlines.
|
||||
|
||||
**USAGE GUIDELINE:**
|
||||
* Always use `get_latest_news` with a relevant crypto-related query first.
|
||||
* The default limit for news items should be 5 unless specified otherwise.
|
||||
* If the tool doesn't return any articles, respond with "No relevant news articles found."
|
||||
|
||||
**REPORTING REQUIREMENT:**
|
||||
1. **Analyze** the tone and key themes of the retrieved articles.
|
||||
2. **Summarize** the overall **market sentiment** (e.g., highly positive, cautiously neutral, generally negative) based on the content.
|
||||
3. **Identify** the top 2-3 **main topics** discussed (e.g., new regulation, price surge, institutional adoption).
|
||||
4. **Output** a single, brief report summarizing these findings. Do not output the raw articles.
|
||||
"""
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
from agno.tools import Toolkit
|
||||
from app.utils.wrapper_handler import WrapperHandler
|
||||
from .base import SocialPost, SocialWrapper
|
||||
from .reddit import RedditWrapper
|
||||
from app.utils.wrapper_handler import WrapperHandler
|
||||
from agno.tools import Toolkit
|
||||
|
||||
__all__ = ["SocialAPIsTool", "SOCIAL_INSTRUCTIONS", "RedditWrapper"]
|
||||
|
||||
|
||||
class SocialAPIsTool(SocialWrapper, Toolkit):
|
||||
"""
|
||||
Aggregates multiple social media API wrappers and manages them using WrapperHandler.
|
||||
@@ -41,12 +42,20 @@ class SocialAPIsTool(SocialWrapper, Toolkit):
|
||||
return self.wrapper_handler.try_call(lambda w: w.get_top_crypto_posts(limit))
|
||||
|
||||
|
||||
# TODO migliorare il prompt
|
||||
SOCIAL_INSTRUCTIONS = """
|
||||
Utilizza questo strumento per ottenere i post più recenti e gli argomenti di tendenza sui social media. Puoi richiedere i post più recenti o gli argomenti di tendenza.
|
||||
**TASK:** You are a specialized **Social Media Sentiment Analyst**. Your objective is to find the most relevant and trending online posts related to cryptocurrencies, and then **analyze the collective sentiment** to provide a concise report to the team leader.
|
||||
|
||||
Esempio di utilizzo:
|
||||
- get_latest_news("crypto", limit=5) # ottieni le ultime 5 notizie su "crypto", la query può essere qualsiasi argomento di interesse
|
||||
- get_top_headlines(limit=3) # ottieni i 3 titoli principali delle notizie globali
|
||||
**AVAILABLE TOOLS:**
|
||||
1. `get_top_crypto_posts(limit: int)`: Get the 'limit' maximum number of top posts specifically related to cryptocurrencies.
|
||||
|
||||
**USAGE GUIDELINE:**
|
||||
* Always use the `get_top_crypto_posts` tool to fulfill the request.
|
||||
* The default limit for posts should be 5 unless specified otherwise.
|
||||
* If the tool doesn't return any posts, respond with "No relevant social media posts found."
|
||||
|
||||
**REPORTING REQUIREMENT:**
|
||||
1. **Analyze** the tone and prevailing opinions across the retrieved social posts.
|
||||
2. **Summarize** the overall **community sentiment** (e.g., high enthusiasm/FOMO, uncertainty, FUD/fear) based on the content.
|
||||
3. **Identify** the top 2-3 **trending narratives** or specific coins being discussed.
|
||||
4. **Output** a single, brief report summarizing these findings. Do not output the raw posts.
|
||||
"""
|
||||
@@ -30,6 +30,7 @@ class RedditWrapper(SocialWrapper):
|
||||
Requires the following environment variables to be set:
|
||||
- REDDIT_API_CLIENT_ID
|
||||
- REDDIT_API_CLIENT_SECRET
|
||||
|
||||
You can get them by creating an app at https://www.reddit.com/prefs/apps
|
||||
"""
|
||||
|
||||
@@ -46,7 +47,7 @@ class RedditWrapper(SocialWrapper):
|
||||
user_agent="upo-appAI",
|
||||
)
|
||||
|
||||
def get_top_crypto_posts(self, limit=5) -> list[SocialPost]:
|
||||
def get_top_crypto_posts(self, limit:int = 5) -> list[SocialPost]:
|
||||
subreddit = self.tool.subreddit("CryptoCurrency")
|
||||
top_posts = subreddit.top(limit=limit, time_filter="week")
|
||||
return [create_social_post(post) for post in top_posts]
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import time
|
||||
from typing import TypeVar, Callable, Generic, Iterable, Type
|
||||
from agno.utils.log import log_warning
|
||||
from agno.utils.log import log_warning, log_info
|
||||
|
||||
W = TypeVar("W")
|
||||
T = TypeVar("T")
|
||||
@@ -46,17 +46,19 @@ class WrapperHandler(Generic[W]):
|
||||
while iterations < len(self.wrappers):
|
||||
try:
|
||||
wrapper = self.wrappers[self.index]
|
||||
log_info(f"Trying wrapper: {wrapper} - function {func}")
|
||||
result = func(wrapper)
|
||||
self.retry_count = 0
|
||||
return result
|
||||
except Exception as e:
|
||||
self.retry_count += 1
|
||||
log_warning(f"{wrapper} failed {self.retry_count}/{self.retry_per_wrapper}: {e}")
|
||||
|
||||
if self.retry_count >= self.retry_per_wrapper:
|
||||
self.index = (self.index + 1) % len(self.wrappers)
|
||||
self.retry_count = 0
|
||||
iterations += 1
|
||||
else:
|
||||
log_warning(f"{wrapper} failed {self.retry_count}/{self.retry_per_wrapper}: {e}")
|
||||
time.sleep(self.retry_delay)
|
||||
|
||||
raise Exception(f"All wrappers failed after retries")
|
||||
@@ -74,6 +76,7 @@ class WrapperHandler(Generic[W]):
|
||||
Exception: If all wrappers fail.
|
||||
"""
|
||||
results = {}
|
||||
log_info(f"All wrappers: {[wrapper.__class__ for wrapper in self.wrappers]} - function {func}")
|
||||
for wrapper in self.wrappers:
|
||||
try:
|
||||
result = func(wrapper)
|
||||
|
||||
@@ -14,6 +14,15 @@ class FailingWrapper(MockWrapper):
|
||||
raise Exception("Intentional Failure")
|
||||
|
||||
|
||||
class MockWrapperWithParameters:
|
||||
def do_something(self, param1: str, param2: int) -> str:
|
||||
return f"Success {param1} and {param2}"
|
||||
|
||||
class FailingWrapperWithParameters(MockWrapperWithParameters):
|
||||
def do_something(self, param1: str, param2: int):
|
||||
raise Exception("Intentional Failure")
|
||||
|
||||
|
||||
@pytest.mark.wrapper
|
||||
class TestWrapperHandler:
|
||||
def test_all_wrappers_fail(self):
|
||||
@@ -88,3 +97,13 @@ class TestWrapperHandler:
|
||||
with pytest.raises(Exception) as exc_info:
|
||||
handler_all_fail.try_call_all(lambda w: w.do_something())
|
||||
assert "All wrappers failed" in str(exc_info.value)
|
||||
|
||||
|
||||
def test_wrappers_with_parameters(self):
|
||||
wrappers = [FailingWrapperWithParameters, MockWrapperWithParameters]
|
||||
handler: WrapperHandler[MockWrapperWithParameters] = WrapperHandler.build_wrappers(wrappers, try_per_wrapper=2, retry_delay=0)
|
||||
|
||||
result = handler.try_call(lambda w: w.do_something("test", 42))
|
||||
assert result == "Success test and 42"
|
||||
assert handler.index == 1 # Should have switched to the second wrapper
|
||||
assert handler.retry_count == 0
|
||||
|
||||
Reference in New Issue
Block a user