Compare commits
6 Commits
main
...
38-news-pr
| Author | SHA1 | Date | |
|---|---|---|---|
| e9024bbf50 | |||
|
|
72dc551862 | ||
|
|
eae14fbde1 | ||
| 39f3b444a7 | |||
| 89bfadf590 | |||
| cb0714177f |
@@ -4,6 +4,18 @@ from typing import TypedDict, Literal
|
|||||||
|
|
||||||
|
|
||||||
class Task(TypedDict):
|
class Task(TypedDict):
|
||||||
|
"""
|
||||||
|
Represents a single task in the execution plan.
|
||||||
|
|
||||||
|
Attributes:
|
||||||
|
name (str): The unique name of the task.
|
||||||
|
status (Literal["pending", "completed", "failed"]): The current status of the task.
|
||||||
|
- "pending": The task is yet to be executed.
|
||||||
|
- "completed": The task has been successfully executed.
|
||||||
|
- "failed": The task execution was unsuccessful.
|
||||||
|
result (str | None): An optional field to store the result or outcome of the task.
|
||||||
|
This could be a summary, an error message, or any relevant information.
|
||||||
|
"""
|
||||||
name: str
|
name: str
|
||||||
status: Literal["pending", "completed", "failed"]
|
status: Literal["pending", "completed", "failed"]
|
||||||
result: str | None
|
result: str | None
|
||||||
@@ -13,6 +25,7 @@ class PlanMemoryTool(Toolkit):
|
|||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.tasks: list[Task] = []
|
self.tasks: list[Task] = []
|
||||||
Toolkit.__init__(self, # type: ignore[call-arg]
|
Toolkit.__init__(self, # type: ignore[call-arg]
|
||||||
|
name="Plan Memory Tool",
|
||||||
instructions="Provides stateful, persistent memory for the Team Leader. " \
|
instructions="Provides stateful, persistent memory for the Team Leader. " \
|
||||||
"This is your primary to-do list and state tracker. " \
|
"This is your primary to-do list and state tracker. " \
|
||||||
"Use it to create, execute step-by-step, and record the results of your execution plan.",
|
"Use it to create, execute step-by-step, and record the results of your execution plan.",
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ class Article(BaseModel):
|
|||||||
time: str = ""
|
time: str = ""
|
||||||
title: str = ""
|
title: str = ""
|
||||||
description: str = ""
|
description: str = ""
|
||||||
|
url: str = ""
|
||||||
|
|
||||||
class NewsWrapper:
|
class NewsWrapper:
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ def extract_articles(response: dict[str, Any]) -> list[Article]:
|
|||||||
article.time = item.get('published_at', '')
|
article.time = item.get('published_at', '')
|
||||||
article.title = item.get('title', '')
|
article.title = item.get('title', '')
|
||||||
article.description = item.get('description', '')
|
article.description = item.get('description', '')
|
||||||
|
article.url = item.get('url', '')
|
||||||
articles.append(article)
|
articles.append(article)
|
||||||
return articles
|
return articles
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import json
|
|
||||||
from typing import Any
|
from typing import Any
|
||||||
from agno.tools.duckduckgo import DuckDuckGoTools
|
from ddgs import DDGS
|
||||||
from app.api.core.news import Article, NewsWrapper
|
from app.api.core.news import Article, NewsWrapper
|
||||||
|
|
||||||
|
|
||||||
@@ -10,6 +9,7 @@ def extract_article(result: dict[str, Any]) -> Article:
|
|||||||
article.time = result.get("date", "")
|
article.time = result.get("date", "")
|
||||||
article.title = result.get("title", "")
|
article.title = result.get("title", "")
|
||||||
article.description = result.get("body", "")
|
article.description = result.get("body", "")
|
||||||
|
article.url = result.get("url", "")
|
||||||
return article
|
return article
|
||||||
|
|
||||||
class DuckDuckGoWrapper(NewsWrapper):
|
class DuckDuckGoWrapper(NewsWrapper):
|
||||||
@@ -19,16 +19,14 @@ class DuckDuckGoWrapper(NewsWrapper):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.tool = DuckDuckGoTools()
|
self.tool = DDGS()
|
||||||
self.query = "crypto"
|
self.query = "crypto"
|
||||||
|
|
||||||
def get_top_headlines(self, limit: int = 100) -> list[Article]:
|
def get_top_headlines(self, limit: int = 100) -> list[Article]:
|
||||||
results = self.tool.duckduckgo_news(self.query, max_results=limit)
|
results = self.tool.news(self.query, max_results=limit)
|
||||||
json_results = json.loads(results)
|
return [extract_article(result) for result in results]
|
||||||
return [extract_article(result) for result in json_results]
|
|
||||||
|
|
||||||
def get_latest_news(self, query: str, limit: int = 100) -> list[Article]:
|
def get_latest_news(self, query: str, limit: int = 100) -> list[Article]:
|
||||||
results = self.tool.duckduckgo_news(query or self.query, max_results=limit)
|
results = self.tool.news(query or self.query, max_results=limit)
|
||||||
json_results = json.loads(results)
|
return [extract_article(result) for result in results]
|
||||||
return [extract_article(result) for result in json_results]
|
|
||||||
|
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ def extract_article(result: dict[str, Any]) -> Article:
|
|||||||
article.time = result.get("publishedAt", "")
|
article.time = result.get("publishedAt", "")
|
||||||
article.title = result.get("title", "")
|
article.title = result.get("title", "")
|
||||||
article.description = result.get("description", "")
|
article.description = result.get("description", "")
|
||||||
|
article.url = result.get("url", "")
|
||||||
return article
|
return article
|
||||||
|
|
||||||
class GoogleNewsWrapper(NewsWrapper):
|
class GoogleNewsWrapper(NewsWrapper):
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ def extract_article(result: dict[str, Any]) -> Article:
|
|||||||
article.time = result.get("publishedAt", "")
|
article.time = result.get("publishedAt", "")
|
||||||
article.title = result.get("title", "")
|
article.title = result.get("title", "")
|
||||||
article.description = result.get("description", "")
|
article.description = result.get("description", "")
|
||||||
|
article.url = result.get("url", "")
|
||||||
return article
|
return article
|
||||||
|
|
||||||
class NewsApiWrapper(NewsWrapper):
|
class NewsApiWrapper(NewsWrapper):
|
||||||
|
|||||||
@@ -13,48 +13,79 @@ logging = logging.getLogger("crypto_symbols")
|
|||||||
|
|
||||||
BASE_URL = "https://finance.yahoo.com/markets/crypto/all/"
|
BASE_URL = "https://finance.yahoo.com/markets/crypto/all/"
|
||||||
|
|
||||||
|
|
||||||
class CryptoSymbolsTools(Toolkit):
|
class CryptoSymbolsTools(Toolkit):
|
||||||
"""
|
"""
|
||||||
Classe per ottenere i simboli delle criptovalute tramite Yahoo Finance.
|
Class for obtaining cryptocurrency symbols via Yahoo Finance.
|
||||||
|
(This class-level docstring is for developers).
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, cache_file: str = 'resources/cryptos.csv'):
|
def __init__(self, cache_file: str = 'resources/cryptos.csv'):
|
||||||
self.cache_file = cache_file
|
self.cache_file = cache_file
|
||||||
self.final_table = pd.read_csv(self.cache_file) if os.path.exists(self.cache_file) else pd.DataFrame() # type: ignore
|
try:
|
||||||
Toolkit.__init__(self, # type: ignore
|
self.final_table = pd.read_csv(self.cache_file) if os.path.exists(self.cache_file) else pd.DataFrame(
|
||||||
name="Crypto Symbols Tool",
|
columns=['Symbol', 'Name'])
|
||||||
instructions="Tool to get cryptocurrency symbols and search them by name.",
|
except Exception:
|
||||||
tools=[
|
self.final_table = pd.DataFrame(columns=['Symbol', 'Name'])
|
||||||
self.get_all_symbols,
|
|
||||||
self.get_symbols_by_name,
|
Toolkit.__init__(self, # type: ignore
|
||||||
],
|
name="Crypto Symbols Tool",
|
||||||
)
|
instructions="A utility tool to find and verify the correct cryptocurrency symbols (tickers). " \
|
||||||
|
"Use this to translate a cryptocurrency name (e.g., 'Bitcoin') into its official symbol " \
|
||||||
|
"(e.g., 'BTC-USD') *before* delegating tasks to the MarketAgent.",
|
||||||
|
tools=[
|
||||||
|
self.get_all_symbols,
|
||||||
|
self.get_symbols_by_name,
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
def get_all_symbols(self) -> list[str]:
|
def get_all_symbols(self) -> list[str]:
|
||||||
"""
|
"""
|
||||||
Restituisce tutti i simboli delle criptovalute.
|
Returns a complete list of all available cryptocurrency symbols (tickers).
|
||||||
|
|
||||||
|
Warning: This list can be very long. Prefer 'get_symbols_by_name'
|
||||||
|
if you are searching for a specific asset.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
list[str]: Lista di tutti i simboli delle criptovalute.
|
list[str]: A comprehensive list of all supported crypto symbols (e.g., "BTC-USD", "ETH-USD").
|
||||||
"""
|
"""
|
||||||
return self.final_table['Symbol'].tolist() if not self.final_table.empty else []
|
return self.final_table['Symbol'].tolist() if not self.final_table.empty else []
|
||||||
|
|
||||||
def get_symbols_by_name(self, query: str) -> list[tuple[str, str]]:
|
def get_symbols_by_name(self, query: str) -> list[tuple[str, str]]:
|
||||||
"""
|
"""
|
||||||
Cerca i simboli che contengono la query.
|
Searches the cryptocurrency database for assets matching a name or symbol.
|
||||||
|
|
||||||
|
Use this to find the exact, correct symbol for a cryptocurrency name.
|
||||||
|
(e.g., query="Bitcoin" might return [("BTC-USD", "Bitcoin USD")]).
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
query (str): Query di ricerca.
|
query (str): The name, partial name, or symbol to search for (e.g., "Bitcoin", "ETH").
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
list[tuple[str, str]]: Lista di tuple (simbolo, nome) che contengono la query.
|
list[tuple[str, str]]: A list of tuples, where each tuple contains
|
||||||
|
the (symbol, full_name) of a matching asset.
|
||||||
|
Returns an empty list if no matches are found.
|
||||||
"""
|
"""
|
||||||
query_lower = query.lower()
|
if self.final_table.empty or 'Name' not in self.final_table.columns or 'Symbol' not in self.final_table.columns:
|
||||||
positions = self.final_table['Name'].str.lower().str.contains(query_lower)
|
return []
|
||||||
return self.final_table[positions][['Symbol', 'Name']].apply(tuple, axis=1).tolist()
|
|
||||||
|
try:
|
||||||
|
# Cerca sia nel nome che nel simbolo, ignorando maiuscole/minuscole
|
||||||
|
mask = self.final_table['Name'].str.contains(query, case=False, na=False) | \
|
||||||
|
self.final_table['Symbol'].str.contains(query, case=False, na=False)
|
||||||
|
|
||||||
|
filtered_df = self.final_table[mask]
|
||||||
|
|
||||||
|
# Converte il risultato in una lista di tuple
|
||||||
|
return list(zip(filtered_df['Symbol'], filtered_df['Name']))
|
||||||
|
except Exception:
|
||||||
|
return []
|
||||||
|
|
||||||
async def fetch_crypto_symbols(self, force_refresh: bool = False) -> None:
|
async def fetch_crypto_symbols(self, force_refresh: bool = False) -> None:
|
||||||
"""
|
"""
|
||||||
Recupera tutti i simboli delle criptovalute da Yahoo Finance e li memorizza in cache.
|
It retrieves all cryptocurrency symbols from Yahoo Finance and caches them.
|
||||||
Args:
|
Args:
|
||||||
force_refresh (bool): Se True, forza il recupero anche se i dati sono già in cache.
|
force_refresh (bool): If True, it forces the retrieval even if the data are already in the cache.
|
||||||
"""
|
"""
|
||||||
if not force_refresh and not self.final_table.empty:
|
if not force_refresh and not self.final_table.empty:
|
||||||
return
|
return
|
||||||
|
|||||||
Reference in New Issue
Block a user