From 72dc5518624253c0a67ae7f4b2a5a0c9aeed0827 Mon Sep 17 00:00:00 2001 From: Simone Garau <20005068@studenti.uniupo.it> Date: Wed, 29 Oct 2025 17:58:01 +0100 Subject: [PATCH] refactor: improve documentation and error handling in crypto symbols tools --- configs.yaml.example | 2 - src/app/agents/plan_memory_tool.py | 13 +++++ src/app/agents/prompts/team_leader.md | 14 ++++-- src/app/api/news/duckduckgo.py | 1 - src/app/api/tools/symbols_tool.py | 71 +++++++++++++++++++-------- 5 files changed, 75 insertions(+), 26 deletions(-) diff --git a/configs.yaml.example b/configs.yaml.example index 9591519..49ed160 100644 --- a/configs.yaml.example +++ b/configs.yaml.example @@ -27,8 +27,6 @@ models: - name: mistral-large-latest label: Mistral ollama: - - name: gpt-oss:latest - label: Ollama GPT - name: qwen3:8b label: Qwen 3 (8B) - name: qwen3:4b diff --git a/src/app/agents/plan_memory_tool.py b/src/app/agents/plan_memory_tool.py index 93bbda5..d30573d 100644 --- a/src/app/agents/plan_memory_tool.py +++ b/src/app/agents/plan_memory_tool.py @@ -4,6 +4,18 @@ from typing import TypedDict, Literal 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 status: Literal["pending", "completed", "failed"] result: str | None @@ -13,6 +25,7 @@ class PlanMemoryTool(Toolkit): def __init__(self): self.tasks: list[Task] = [] Toolkit.__init__(self, # type: ignore[call-arg] + name="Plan Memory Tool", instructions="Provides stateful, persistent memory for the Team Leader. " \ "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.", diff --git a/src/app/agents/prompts/team_leader.md b/src/app/agents/prompts/team_leader.md index dc609d2..8b17e3c 100644 --- a/src/app/agents/prompts/team_leader.md +++ b/src/app/agents/prompts/team_leader.md @@ -18,9 +18,17 @@ You orchestrate data retrieval and synthesis using a tool-driven execution plan. - **NewsAgent**: Live news articles with sentiment analysis (NewsAPI, GoogleNews, CryptoPanic) - **SocialAgent**: Current social media discussions (Reddit, X, 4chan) -**YOUR PERSONAL TOOLS (FOR PLANNING & SYNTHESIS):** - - **PlanMemoryTool**: MUST be used to manage your execution plan. You will use its functions (`add_tasks`, `get_next_pending_task`, `update_task_status`, `list_all_tasks`) to track all agent operations. This is your stateful memory. - - **ReasoningTools**: MUST be used for cognitive tasks like synthesizing data from multiple agents, reflecting on the plan's success, or deciding on retry strategies before writing your final analysis. +**YOUR PERSONAL TOOLS (FOR PLANNING, SYNTHESIS & UTILITIES):** +*The framework will provide you with the exact functions for these tools. Your job is to use them according to these strategies.* + +- **Planning & State (`PlanMemoryTool`)**: + This is your stateful memory. You MUST use it to build your plan (`add_tasks`) *before* delegating, execute the plan step-by-step (`get_next_pending_task`), and record all outcomes (`update_task_status`). + +- **Cognition & Synthesis (`ReasoningTools`)**: + You MUST use this tool to reflect on the data gathered from your team and to synthesize the `Analysis` sections of your final report. + +- **Data Utilities (`CryptoSymbolsTools`)**: + You MUST use this tool to find the correct ticker (e.g., "BTC-USD") when the user asks for a name (e.g., "Bitcoin"). Do this *before* you create a task for the `MarketAgent`. **AGENT OUTPUT SCHEMAS (MANDATORY REFERENCE):** You MUST parse the exact structures your agents provide: diff --git a/src/app/api/news/duckduckgo.py b/src/app/api/news/duckduckgo.py index 3bb04bf..cfaf018 100644 --- a/src/app/api/news/duckduckgo.py +++ b/src/app/api/news/duckduckgo.py @@ -5,7 +5,6 @@ from app.api.core.news import Article, NewsWrapper def extract_article(result: dict[str, Any]) -> Article: article = Article() - print(result) article.source = result.get("source", "") article.time = result.get("date", "") article.title = result.get("title", "") diff --git a/src/app/api/tools/symbols_tool.py b/src/app/api/tools/symbols_tool.py index 90cee7e..9bf03e4 100644 --- a/src/app/api/tools/symbols_tool.py +++ b/src/app/api/tools/symbols_tool.py @@ -13,48 +13,79 @@ logging = logging.getLogger("crypto_symbols") BASE_URL = "https://finance.yahoo.com/markets/crypto/all/" + 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'): 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 - Toolkit.__init__(self, # type: ignore - name="Crypto Symbols Tool", - instructions="Tool to get cryptocurrency symbols and search them by name.", - tools=[ - self.get_all_symbols, - self.get_symbols_by_name, - ], - ) + try: + self.final_table = pd.read_csv(self.cache_file) if os.path.exists(self.cache_file) else pd.DataFrame( + columns=['Symbol', 'Name']) + except Exception: + self.final_table = pd.DataFrame(columns=['Symbol', '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]: """ - 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: - 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 [] 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: - query (str): Query di ricerca. + query (str): The name, partial name, or symbol to search for (e.g., "Bitcoin", "ETH"). + 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() - positions = self.final_table['Name'].str.lower().str.contains(query_lower) - return self.final_table[positions][['Symbol', 'Name']].apply(tuple, axis=1).tolist() + if self.final_table.empty or 'Name' not in self.final_table.columns or 'Symbol' not in self.final_table.columns: + return [] + + 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: """ - 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: - 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: return