diff --git a/Dockerfile b/Dockerfile index 17e3234..22a1395 100644 --- a/Dockerfile +++ b/Dockerfile @@ -26,6 +26,7 @@ RUN npm install -g rettiwt-api # Copiamo i file del progetto COPY LICENSE ./ +COPY resources/ ./resources/ COPY src/ ./src/ COPY configs.yaml ./ diff --git a/docs/Query_Examples.md b/docs/Query_Examples.md new file mode 100644 index 0000000..4b4a0dd --- /dev/null +++ b/docs/Query_Examples.md @@ -0,0 +1,538 @@ +# Query Examples - Cryptocurrency Report Generation + +This document provides examples of user queries that trigger specific tools in the cryptocurrency analysis application. + +## Table of Contents +- [CryptoSymbolsTools - Symbol Resolution](#1-cryptosymbolstools---symbol-resolution) +- [MarketAPIsTool - Price & Historical Data](#2-marketapistool---price--historical-data) +- [NewsAPIsTool - News & Sentiment](#3-newsapistool---news--sentiment) +- [SocialAPIsTool - Social Media Sentiment](#4-socialapistool---social-media-sentiment) +- [PlanMemoryTool - Task Management](#5-planmemorytool---task-management) +- [ReasoningTools - Cognitive Analysis](#6-reasoningtools---cognitive-analysis) +- [Composite Queries - Multiple Tools](#7-composite-queries---multiple-tools) +- [Keyword Triggers Reference](#keyword-triggers---quick-reference) + +--- + +## 1. CryptoSymbolsTools - Symbol Resolution + +### `get_symbols_by_name(query)` - Resolve cryptocurrency names to symbols + +**Purpose:** Convert user-friendly names to trading symbols before calling market APIs. + +**Example Queries:** +- ❓ "Dammi il prezzo di **Ethereum**" + - → Searches "ethereum" → Finds "ETH-USD" + +- ❓ "Analizza **Solana** e **Cardano**" + - → Resolves both names to SOL-USD, ADA-USD + +- ❓ "Quanto vale **Dogecoin**?" + - → Searches "dogecoin" → Finds "DOGE-USD" + +- ❓ "Confronta **Bitcoin** e **Bitcoin Cash**" + - → Handles ambiguity (2 matches: BTC-USD, BCH-USD) + +- ❓ "Report su **Polkadot**" + - → Resolves to "DOT-USD" + +**Trigger Words:** Cryptocurrency common names (Bitcoin, Ethereum, Solana, etc.) + +--- + +### `get_all_symbols()` - List all available cryptocurrencies + +**Purpose:** Verify symbol availability or show complete list. + +**Example Queries:** +- ❓ "Quali criptovalute sono disponibili?" +- ❓ "Mostrami tutte le crypto supportate" +- ❓ "Lista completa delle criptovalute" +- ❓ "Esiste **XYZ-USD** nel sistema?" + - → Verifies if symbol exists in list + +**Trigger Words:** "disponibili", "supportate", "lista", "tutte", "esiste" + +--- + +## 2. MarketAPIsTool - Price & Historical Data + +### Single-Source Tools (FAST - First available provider) + +#### `get_product(asset_id)` - Current price for single asset + +**Purpose:** Quick price check for one cryptocurrency. + +**Example Queries:** +- ❓ "Qual è il prezzo attuale di Bitcoin?" +- ❓ "Quanto vale BTC adesso?" +- ❓ "Prezzo corrente di Ethereum" +- ❓ "Valore di SOL?" +- ❓ "Quotazione Cardano" + +**Trigger Words:** "prezzo", "quanto vale", "valore", "quotazione", "attuale", "corrente", "adesso" + +--- + +#### `get_products(asset_ids)` - Current prices for multiple assets + +**Purpose:** Quick price comparison of multiple cryptocurrencies. + +**Example Queries:** +- ❓ "Dammi i prezzi di BTC, ETH e SOL" +- ❓ "Confronta i valori di Bitcoin, Ethereum e Cardano" +- ❓ "Lista prezzi: BTC, DOGE, ADA" +- ❓ "Prezzi attuali delle top 5 crypto" +- ❓ "Quanto valgono Bitcoin ed Ethereum?" + +**Trigger Words:** Multiple assets mentioned, "confronta", "lista prezzi", "valori di" + +--- + +#### `get_historical_prices(asset_id, limit)` - Historical data for single asset + +**Purpose:** Price history and trend analysis. + +**Example Queries:** +- ❓ "Prezzo di Bitcoin negli ultimi 7 giorni" +- ❓ "Storico di ETH dell'ultimo mese" +- ❓ "Come è variato Solana nelle ultime 24 ore?" +- ❓ "Andamento BTC ultima settimana" +- ❓ "Grafico storico di Ethereum" + +**Trigger Words:** "storico", "ultimi N giorni/ore", "ultimo mese", "variato", "andamento", "trend", "grafico" + +**Time Range Mapping:** +- "ultime 24 ore" / "oggi" → limit=24 (hourly) or limit=1 (daily) +- "ultimi 7 giorni" / "ultima settimana" → limit=7 +- "ultimo mese" / "ultimi 30 giorni" → limit=30 +- "ultimi 3 mesi" → limit=90 + +--- + +### Aggregated Tools (COMPREHENSIVE - All providers with VWAP) + +#### `get_product_aggregated(asset_id)` - Accurate price from all sources + +**Purpose:** Most reliable price using Volume Weighted Average Price (VWAP) from all providers. + +**Example Queries:** +- ❓ "Dammi il prezzo **più accurato** di Bitcoin" +- ❓ "Qual è il prezzo **affidabile** di ETH?" +- ❓ "Voglio il prezzo di BTC da **tutte le fonti**" +- ❓ "Prezzo **preciso** di Solana" +- ❓ "Prezzo **verificato** di Cardano" + +**Trigger Words:** "accurato", "affidabile", "preciso", "verificato", "tutte le fonti", "completo", "da tutti i provider" + +--- + +#### `get_products_aggregated(asset_ids)` - Accurate prices for multiple assets + +**Purpose:** Comprehensive multi-asset analysis with aggregated data. + +**Example Queries:** +- ❓ "Analisi **dettagliata** dei prezzi di BTC ed ETH" +- ❓ "Confronto **completo** tra Bitcoin e Ethereum" +- ❓ "Report **comprensivo** su BTC, ETH, SOL" +- ❓ "Dati **affidabili** per top 3 crypto" +- ❓ "Prezzi **aggregati** di Bitcoin e Cardano" + +**Trigger Words:** "dettagliata", "completo", "comprensivo", "affidabili", "aggregati" + multiple assets + +--- + +#### `get_historical_prices_aggregated(asset_id, limit)` - Historical data from all sources + +**Purpose:** Complete historical analysis with data from all providers. + +**Example Queries:** +- ❓ "Storico **completo** di Bitcoin da tutte le fonti" +- ❓ "Analisi **comprensiva** del prezzo di ETH nell'ultimo mese" +- ❓ "Dati storici **affidabili** di BTC" +- ❓ "Andamento **dettagliato** di Solana ultimi 7 giorni" +- ❓ "Trend **aggregato** di Cardano" + +**Trigger Words:** "storico completo", "comprensiva", "affidabili", "dettagliato", "aggregato" + time range + +--- + +## 3. NewsAPIsTool - News & Sentiment + +### Single-Source Tools (FAST - First available provider) + +#### `get_top_headlines(limit)` - Top cryptocurrency news + +**Purpose:** Quick overview of current crypto news headlines. + +**Example Queries:** +- ❓ "Quali sono le ultime notizie crypto?" +- ❓ "Dammi i titoli principali sulle criptovalute" +- ❓ "Cosa dicono le news oggi?" +- ❓ "Notizie del giorno crypto" +- ❓ "Ultime breaking news Bitcoin" + +**Trigger Words:** "notizie", "news", "titoli", "ultime", "del giorno", "breaking" + +**Limit Guidelines:** +- Quick scan: limit=5-10 +- Standard: limit=20-30 +- Deep research: limit=50-100 + +--- + +#### `get_latest_news(query, limit)` - News on specific topic + +**Purpose:** Search for news articles about specific crypto topics or events. + +**Example Queries:** +- ❓ "Notizie su **Bitcoin ETF**" +- ❓ "Cosa si dice del **crollo di Ethereum**?" +- ❓ "Trova articoli sulla **regolamentazione crypto**" +- ❓ "News su **DeFi security**" +- ❓ "Articoli su **NFT trends**" +- ❓ "Cosa dicono delle **whale movements**?" + +**Trigger Words:** "notizie su", "articoli su", "cosa si dice", "trova", "cerca" + specific topic + +**Query Formulation Tips:** +- User: "Bitcoin regulation" → query="Bitcoin regulation" +- User: "ETH price surge" → query="Ethereum price increase" +- User: "Crypto market crash" → query="cryptocurrency market crash" + +--- + +### Aggregated Tools (COMPREHENSIVE - All news providers) + +#### `get_top_headlines_aggregated(limit)` - Headlines from all sources + +**Purpose:** Complete news coverage from all configured providers (NewsAPI, Google News, CryptoPanic, DuckDuckGo). + +**Example Queries:** +- ❓ "Dammi le notizie crypto da **tutte le fonti**" +- ❓ "Panoramica **completa** delle news di oggi" +- ❓ "Cosa dicono **tutti i provider** sulle crypto?" +- ❓ "Confronta le notizie da **diverse fonti**" +- ❓ "Headline **aggregate** crypto" + +**Trigger Words:** "tutte le fonti", "completa", "tutti i provider", "diverse fonti", "aggregate", "panoramica" + +--- + +#### `get_latest_news_aggregated(query, limit)` - Topic news from all sources + +**Purpose:** Comprehensive research on specific topic from all news providers. + +**Example Queries:** +- ❓ "Ricerca **approfondita** su Bitcoin regulation" +- ❓ "Analisi **completa** delle notizie su Ethereum merge" +- ❓ "**Tutte le fonti** su NFT trends" +- ❓ "Confronto notizie da **tutti i provider** su DeFi" +- ❓ "Report **comprensivo** news Bitcoin ETF" + +**Trigger Words:** "approfondita", "completa", "tutte le fonti", "tutti i provider", "comprensivo", "ricerca" + +--- + +## 4. SocialAPIsTool - Social Media Sentiment + +### Single-Source Tool (FAST - First available platform) + +#### `get_top_crypto_posts(limit)` - Top social media posts + +**Purpose:** Quick snapshot of social media sentiment on crypto. + +**Example Queries:** +- ❓ "Cosa dice la gente sulle crypto?" +- ❓ "Sentiment sui social media per Bitcoin" +- ❓ "Discussioni trending su Reddit/Twitter" +- ❓ "Qual è il mood della community?" +- ❓ "Post popolari su crypto oggi" +- ❓ "Cosa dicono gli utenti di Ethereum?" + +**Trigger Words:** "cosa dice", "sentiment", "discussioni", "mood", "community", "social", "Reddit", "Twitter" + +**Limit Guidelines:** +- Quick snapshot: limit=5 (default, posts are long) +- Standard: limit=10-15 +- Deep analysis: limit=20-30 + +--- + +### Aggregated Tool (COMPREHENSIVE - All platforms) + +#### `get_top_crypto_posts_aggregated(limit_per_wrapper)` - Posts from all platforms + +**Purpose:** Complete social sentiment analysis across Reddit, X/Twitter, and 4chan. + +**Example Queries:** +- ❓ "Sentiment su **tutte le piattaforme** social" +- ❓ "Confronta Reddit e Twitter su Bitcoin" +- ❓ "Analisi **completa** delle discussioni social" +- ❓ "Cosa dicono **tutti** (Reddit, Twitter, 4chan)?" +- ❓ "Panoramica **social aggregate** su crypto" +- ❓ "Mood su **tutte le piattaforme** crypto" + +**Trigger Words:** "tutte le piattaforme", "confronta", "completa", "tutti", "aggregate", "panoramica" + +--- + +## 5. PlanMemoryTool - Task Management + +**Note:** This tool is used **internally** by the Team Leader agent. Users don't call it directly, but complex queries trigger automatic task planning. + +### Automatic Triggering by Query Complexity + +#### Simple Query (1-2 tasks): +- ❓ "Prezzo di Bitcoin" + - → Creates 1 task: "Fetch BTC price" + +- ❓ "Notizie su Ethereum" + - → Creates 1 task: "Get Ethereum news" + +#### Complex Query (3+ tasks): +- ❓ "**Report completo** su Bitcoin" + - → Creates 3 tasks: + 1. "Fetch BTC-USD current price and historical data" + 2. "Analyze Bitcoin news sentiment (last 24h, limit=20)" + 3. "Check Bitcoin social discussions (limit=10)" + +- ❓ "Analizza il mercato crypto oggi" + - → Creates multiple tasks: + 1. "Get top crypto prices (BTC, ETH, SOL, ADA)" + 2. "Get crypto news headlines (limit=30)" + 3. "Check social sentiment on crypto market" + +**Trigger Words for Complex Queries:** "report completo", "analisi completa", "analizza", "studio", "ricerca approfondita" + +--- + +## 6. ReasoningTools - Cognitive Analysis + +**Note:** This tool is used **internally** by the Team Leader for decision-making. Triggered automatically during complex operations. + +### `think()` - Step-by-step reasoning + +**Automatic Triggers:** +- **Ambiguous Query:** "Bitcoin" → think: "Could be BTC or BCH, need to verify with CryptoSymbolsTools" +- **Strategy Decision:** Query with "accurate" → think: "User wants reliable data, should use aggregated tools" +- **Retry Strategy:** API failed → think: "Timeout error, should retry with broader parameters" + +### `analyze()` - Result evaluation + +**Automatic Triggers:** +- **After MarketAgent response:** → analyze: "Fresh data, high volume, proceed to next task" +- **After API failure:** → analyze: "API timeout, retry with modified parameters" +- **Before final report:** → analyze: "All 3 data sources complete, data quality high, generate comprehensive report" + +--- + +## 7. Composite Queries - Multiple Tools + +### Full Analysis Queries (Trigger ALL tools) + +#### Query: **"Report completo su Bitcoin"** + +**Tools Triggered:** +1. **CryptoSymbolsTools**: `get_symbols_by_name("bitcoin")` → "BTC-USD" +2. **MarketAPIsTool**: `get_products_aggregated(["BTC-USD"])` +3. **NewsAPIsTool**: `get_latest_news_aggregated("Bitcoin", limit=20)` +4. **SocialAPIsTool**: `get_top_crypto_posts_aggregated(limit=10)` +5. **PlanMemoryTool**: Creates 3 tasks, tracks execution, stores results +6. **ReasoningTools**: Think/analyze for each decision and synthesis + +**Expected Output:** Comprehensive report with price data, sentiment analysis, social trends, and metadata. + +--- + +#### Query: **"Confronta Bitcoin ed Ethereum: prezzi, news e sentiment"** + +**Tools Triggered:** +1. **CryptoSymbolsTools**: + - `get_symbols_by_name("bitcoin")` → BTC-USD + - `get_symbols_by_name("ethereum")` → ETH-USD +2. **MarketAPIsTool**: `get_products(["BTC-USD", "ETH-USD"])` +3. **NewsAPIsTool**: + - `get_latest_news("Bitcoin", limit=20)` + - `get_latest_news("Ethereum", limit=20)` +4. **SocialAPIsTool**: `get_top_crypto_posts(limit=20)` → filter for BTC/ETH mentions +5. **PlanMemoryTool**: Creates 6 tasks (2 assets × 3 data types) +6. **ReasoningTools**: Compare and synthesize findings between BTC and ETH + +**Expected Output:** Side-by-side comparison report with price differences, sentiment comparison, and cross-analysis. + +--- + +#### Query: **"Come è cambiato Bitcoin nell'ultima settimana? Analizza prezzo, news e social"** + +**Tools Triggered:** +1. **CryptoSymbolsTools**: `get_symbols_by_name("bitcoin")` → BTC-USD +2. **MarketAPIsTool**: `get_historical_prices("BTC-USD", limit=7)` +3. **NewsAPIsTool**: `get_latest_news("Bitcoin", limit=30)` → filter last 7 days +4. **SocialAPIsTool**: `get_top_crypto_posts(limit=20)` → filter last 7 days +5. **PlanMemoryTool**: Creates tasks for fetch + trend analysis +6. **ReasoningTools**: Analyze correlation between price changes and sentiment trends + +**Expected Output:** Temporal analysis report showing price evolution, news sentiment over time, and social mood changes. + +--- + +### Multi-Asset Analysis + +#### Query: **"Report sui prezzi delle top 5 crypto con analisi di mercato"** + +**Tools Triggered:** +1. **CryptoSymbolsTools**: Resolve top 5 crypto names to symbols +2. **MarketAPIsTool**: `get_products_aggregated(["BTC-USD", "ETH-USD", "SOL-USD", "ADA-USD", "DOT-USD"])` +3. **NewsAPIsTool**: `get_top_headlines(limit=50)` → extract relevant news for each +4. **SocialAPIsTool**: `get_top_crypto_posts_aggregated(limit=15)` → categorize by asset +5. **PlanMemoryTool**: Manages multi-asset task orchestration +6. **ReasoningTools**: Cross-asset comparison and market overview synthesis + +--- + +### Focused Deep Dive + +#### Query: **"Ricerca approfondita su Ethereum: storico 30 giorni, tutte le news, sentiment social completo"** + +**Tools Triggered:** +1. **CryptoSymbolsTools**: `get_symbols_by_name("ethereum")` → ETH-USD +2. **MarketAPIsTool**: `get_historical_prices_aggregated("ETH-USD", limit=30)` +3. **NewsAPIsTool**: `get_latest_news_aggregated("Ethereum", limit=100)` +4. **SocialAPIsTool**: `get_top_crypto_posts_aggregated(limit_per_wrapper=30)` +5. **PlanMemoryTool**: Sequential execution with data validation +6. **ReasoningTools**: In-depth analysis with trend identification + +**Expected Output:** Extensive Ethereum report with 30-day price chart, comprehensive news analysis, and detailed social sentiment breakdown. + +--- + +## Keyword Triggers - Quick Reference + +| Keyword / Phrase | Tool / Function | Type | +|------------------|-----------------|------| +| **Price-related** | +| "prezzo", "quanto vale", "valore", "quotazione" | `get_product()` | Market - Single | +| "prezzi di [list]", "confronta prezzi" | `get_products()` | Market - Single | +| "accurato", "affidabile", "tutte le fonti", "preciso" | `get_product_aggregated()` | Market - Aggregated | +| "storico", "ultimi N giorni", "variazione", "trend" | `get_historical_prices()` | Market - Historical | +| "storico completo", "dati aggregati storici" | `get_historical_prices_aggregated()` | Market - Historical Agg | +| **News-related** | +| "notizie", "news", "articoli", "titoli" | `get_top_headlines()` | News - Single | +| "notizie su [topic]", "articoli su [topic]" | `get_latest_news()` | News - Single | +| "tutte le fonti news", "panoramica completa news" | `get_top_headlines_aggregated()` | News - Aggregated | +| "ricerca approfondita", "tutti i provider" | `get_latest_news_aggregated()` | News - Aggregated | +| **Social-related** | +| "sentiment", "cosa dice la gente", "mood", "community" | `get_top_crypto_posts()` | Social - Single | +| "tutte le piattaforme", "Reddit e Twitter", "social completo" | `get_top_crypto_posts_aggregated()` | Social - Aggregated | +| **Comprehensive** | +| "report completo", "analisi completa" | ALL tools | Comprehensive | +| "ricerca approfondita", "studio dettagliato" | ALL tools | Comprehensive | +| "confronta [A] e [B]", "differenza tra" | Multiple assets | Comparison | +| **Symbol Resolution** | +| "Bitcoin", "Ethereum", "Solana" (names not symbols) | `get_symbols_by_name()` | Symbol Lookup | +| "disponibili", "lista crypto", "supportate" | `get_all_symbols()` | Symbol List | + +--- + +## Best Practices for Query Formulation + +### For Users: + +1. **Be Specific About Scope:** + - ✅ "Prezzo accurato di Bitcoin da tutte le fonti" + - ❌ "Bitcoin" (ambiguous) + +2. **Use Time Ranges When Relevant:** + - ✅ "Storico di Ethereum ultimi 30 giorni" + - ❌ "Storico Ethereum" (unclear timeframe) + +3. **Specify Data Completeness Needs:** + - ✅ "Report completo su Solana con news e social" + - ❌ "Info su Solana" (unclear what data needed) + +4. **Use Common Cryptocurrency Names:** + - ✅ "Analisi Bitcoin ed Ethereum" + - ✅ "Confronta BTC e ETH" (both work) + +### For Team Leader Agent: + +1. **Always Use CryptoSymbolsTools First:** + - When user mentions names, resolve to symbols before market calls + +2. **Choose Single vs Aggregated Based on Keywords:** + - "accurato", "affidabile", "completo" → Use aggregated + - Quick queries without qualifiers → Use single-source + +3. **Create Descriptive Tasks:** + - ✅ "Fetch BTC-USD price from Binance (aggregated, VWAP)" + - ❌ "Get price" (too vague) + +4. **Use ReasoningTools Before Decisions:** + - Before choosing tool variant + - Before retry strategies + - Before final synthesis + +--- + +## Time Range Reference + +| User Expression | Limit Parameter | Time Period | +|----------------|-----------------|-------------| +| "oggi", "ultime 24 ore" | limit=1 or 24 | 1 day | +| "ultimi 7 giorni", "ultima settimana" | limit=7 | 7 days | +| "ultimo mese", "ultimi 30 giorni" | limit=30 | 30 days | +| "ultimi 3 mesi" | limit=90 | 90 days | +| "ultimi 6 mesi" | limit=180 | 180 days | + +--- + +## Common Query Patterns + +### Pattern 1: Quick Price Check +**Query:** "Prezzo di Bitcoin" +**Flow:** CryptoSymbolsTools → MarketAPIsTool (single) → Result + +### Pattern 2: Detailed Analysis +**Query:** "Analisi completa Bitcoin" +**Flow:** CryptoSymbolsTools → All tools (aggregated) → Synthesis → Comprehensive Report + +### Pattern 3: Comparison +**Query:** "Confronta Bitcoin ed Ethereum" +**Flow:** CryptoSymbolsTools (both) → MarketAPIsTool (both) → NewsAPIsTool (both) → Comparison Report + +### Pattern 4: Temporal Trend +**Query:** "Come è cambiato Ethereum nell'ultima settimana" +**Flow:** CryptoSymbolsTools → Historical Market Data → Recent News → Recent Social → Trend Analysis + +### Pattern 5: Multi-Asset Overview +**Query:** "Prezzi delle top 5 crypto" +**Flow:** CryptoSymbolsTools (×5) → MarketAPIsTool (batch) → Price List Report + +--- + +## Error Handling Examples + +### Ambiguous Symbol +**Query:** "Prezzo di Bitcoin" +**Issue:** Multiple matches (BTC, BCH) +**Resolution:** ReasoningTools.think() → Ask user or default to BTC-USD + +### No Results +**Query:** "Notizie su XYZ crypto" +**Issue:** No news found +**Response:** "No news articles found for XYZ. Try broader search terms." + +### API Failure +**Query:** "Report completo Bitcoin" +**Issue:** MarketAPI timeout +**Resolution:** PlanMemoryTool marks task failed → ReasoningTools decides retry → Retry with broader params + +### Partial Data +**Query:** "Analisi completa Ethereum" +**Issue:** SocialAPI failed, Market and News succeeded +**Response:** Report with Market and News sections, omit Social section, note in metadata + +--- + +This document serves as a comprehensive reference for understanding how different user queries trigger specific tools in the cryptocurrency analysis application. diff --git a/src/app/agents/action_registry.py b/src/app/agents/action_registry.py new file mode 100644 index 0000000..2d04d09 --- /dev/null +++ b/src/app/agents/action_registry.py @@ -0,0 +1,37 @@ +from typing import Any, Callable + + +# Registro centrale popolato da tutti i file Toolkit all'avvio. +ACTION_DESCRIPTIONS: dict[str, str] = {} + +def get_user_friendly_action(tool_name: str) -> str: + """ + Restituisce un messaggio leggibile e descrittivo per l'utente + leggendo dal registro globale. + """ + # Usa il dizionario ACTION_DESCRIPTIONS importato + return ACTION_DESCRIPTIONS.get(tool_name, f"⚙️ Eseguo l'operazione: {tool_name}...") + +def friendly_action(description: str) -> Callable[..., Any]: + """ + Decoratore che registra automaticamente la descrizione "user-friendly" + di un metodo nel registro globale. + + Questo decoratore viene eseguito all'avvio dell'app (quando i file + vengono importati) e popola il dizionario ACTION_DESCRIPTIONS. + + Restituisce la funzione originale non modificata. + """ + + def decorator(func: Callable[..., Any]) -> Callable[..., Any]: + # Registra l'azione + tool_name = func.__name__ + if tool_name in ACTION_DESCRIPTIONS: + print(f"Attenzione: Azione '{tool_name}' registrata più volte.") + + ACTION_DESCRIPTIONS[tool_name] = description + + # Restituisce la funzione originale + return func + + return decorator diff --git a/src/app/agents/core.py b/src/app/agents/core.py index 147d92c..28deb7d 100644 --- a/src/app/agents/core.py +++ b/src/app/agents/core.py @@ -2,7 +2,7 @@ from pydantic import BaseModel from agno.agent import Agent from agno.team import Team from agno.tools.reasoning import ReasoningTools -from app.agents.plan_memory_tool import PlanMemoryTool +from app.api.tools.plan_memory_tool import PlanMemoryTool from app.api.tools import * from app.configs import AppConfig from app.agents.prompts import * @@ -45,28 +45,28 @@ class PipelineInputs: """ Sceglie il modello LLM da usare per l'analizzatore di query. """ - assert index >= 0 and index < len(self.configs.models.all_models), "Index out of range for models list." + assert 0 <= index < len(self.configs.models.all_models), "Index out of range for models list." self.query_analyzer_model = self.configs.models.all_models[index] def choose_team_leader(self, index: int): """ Sceglie il modello LLM da usare per il Team Leader. """ - assert index >= 0 and index < len(self.configs.models.all_models), "Index out of range for models list." + assert 0 <= index < len(self.configs.models.all_models), "Index out of range for models list." self.team_leader_model = self.configs.models.all_models[index] def choose_team(self, index: int): """ Sceglie il modello LLM da usare per il Team. """ - assert index >= 0 and index < len(self.configs.models.all_models), "Index out of range for models list." + assert 0 <= index < len(self.configs.models.all_models), "Index out of range for models list." self.team_model = self.configs.models.all_models[index] def choose_report_generator(self, index: int): """ Sceglie il modello LLM da usare per il generatore di report. """ - assert index >= 0 and index < len(self.configs.models.all_models), "Index out of range for models list." + assert 0 <= index < len(self.configs.models.all_models), "Index out of range for models list." self.report_generation_model = self.configs.models.all_models[index] def choose_strategy(self, index: int): @@ -145,21 +145,31 @@ class RunMessage: - In esecuzione (➡️) - Completato (✅) - Lo stato di esecuzione può essere assegnato solo ad uno step alla volta. + Lo stato di esecuzione può essere assegnato solo a uno step alla volta. Args: - inputs (PipelineInputs): Input della pipeline per mostrare la configurazione. - prefix (str, optional): Prefisso del messaggio. Defaults to "". - suffix (str, optional): Suffisso del messaggio. Defaults to "". + inputs (PipelineInputs): Input della pipeline per mostrare la configurazione + prefix (str, optional): Prefisso del messaggio. Defaults to "" + suffix (str, optional): Suffisso del messaggio. Defaults to "" """ self.base_message = f"Running configurations: \n{prefix}{inputs}{suffix}\n\n" self.emojis = ['🔳', '➡️', '✅'] self.placeholder = '<<<>>>' self.current = 0 - self.steps_total = [ - (f"{self.placeholder} Query Check", 1), - (f"{self.placeholder} Info Recovery", 0), - (f"{self.placeholder} Report Generation", 0), - ] + self.steps_total: list[tuple[str, int]] = [] + self.set_steps(["Query Check", "Info Recovery", "Report Generation"]) + + def set_steps(self, steps: list[str]) -> 'RunMessage': + """ + Inizializza gli step di esecuzione con lo stato iniziale. + Args: + steps (list[str]): Lista degli step da includere nel messaggio. + Returns: + RunMessage: L'istanza aggiornata di RunMessage. + """ + self.steps_total = [(f"{self.placeholder} {step}", 0) for step in steps] + self.steps_total[0] = (self.steps_total[0][0], 1) # Primo step in esecuzione + self.current = 0 + return self def update(self) -> 'RunMessage': """ @@ -176,15 +186,15 @@ class RunMessage: self.steps_total[self.current] = (text_curr, state_curr + 1) return self - def update_step(self, text_extra: str = "") -> 'RunMessage': + def update_step_with_tool(self, tool_used: str = "") -> 'RunMessage': """ Aggiorna il messaggio per lo step corrente. Args: - text_extra (str, optional): Testo aggiuntivo da includere nello step. Defaults to "". + tool_used (str, optional): Testo aggiuntivo da includere nello step. Defaults to "". """ text_curr, state_curr = self.steps_total[self.current] - if text_extra: - text_curr = f"{text_curr.replace('╚', '╠')}\n╚═ {text_extra}" + if tool_used: + text_curr = f"{text_curr.replace('╚', '╠')}\n╚═ {tool_used}" self.steps_total[self.current] = (text_curr, state_curr) return self @@ -196,3 +206,4 @@ class RunMessage: """ steps = [msg.replace(self.placeholder, self.emojis[state]) for msg, state in self.steps_total] return self.base_message + "\n".join(steps) + diff --git a/src/app/agents/pipeline.py b/src/app/agents/pipeline.py index 0498f90..d69d7f1 100644 --- a/src/app/agents/pipeline.py +++ b/src/app/agents/pipeline.py @@ -1,8 +1,7 @@ -import asyncio from enum import Enum import logging import random -from typing import Any, Callable +from typing import Any, AsyncGenerator, Callable from agno.agent import RunEvent from agno.run.workflow import WorkflowRunEvent from agno.workflow.types import StepInput, StepOutput @@ -13,28 +12,34 @@ from app.agents.core import * logging = logging.getLogger("pipeline") - class PipelineEvent(str, Enum): QUERY_CHECK = "Query Check" - QUERY_ANALYZER = "Query Analyzer" + QUERY_CHECK_END = "Query Check End" INFO_RECOVERY = "Info Recovery" + INFO_RECOVERY_END = "Info Recovery End" REPORT_GENERATION = "Report Generation" - REPORT_TRANSLATION = "Report Translation" - RUN_FINISHED = WorkflowRunEvent.workflow_completed.value - TOOL_USED = RunEvent.tool_call_completed.value + REPORT_GENERATION_END = "Report Generation End" + TOOL_USED = RunEvent.tool_call_started.value + TOOL_USED_END = RunEvent.tool_call_completed.value + RUN_END = WorkflowRunEvent.workflow_completed.value def check_event(self, event: str, step_name: str) -> bool: - return event == self.value or (WorkflowRunEvent.step_completed == event and step_name == self.value) + if event == self.value: + return True + + index = self.value.rfind(" End") + value = self.value[:index] if index > -1 else self.value + step_state = WorkflowRunEvent.step_completed if index > -1 else WorkflowRunEvent.step_started + return step_name == value and step_state == event @classmethod - def get_log_events(cls, run_id: int) -> list[tuple['PipelineEvent', Callable[[Any], None]]]: + def get_log_events(cls, run_id: int) -> list[tuple['PipelineEvent', Callable[[Any], str | None]]]: return [ - (PipelineEvent.QUERY_CHECK, lambda _: logging.info(f"[{run_id}] Query Check completed.")), - (PipelineEvent.QUERY_ANALYZER, lambda _: logging.info(f"[{run_id}] Query Analyzer completed.")), - (PipelineEvent.INFO_RECOVERY, lambda _: logging.info(f"[{run_id}] Info Recovery completed.")), - (PipelineEvent.REPORT_GENERATION, lambda _: logging.info(f"[{run_id}] Report Generation completed.")), - (PipelineEvent.TOOL_USED, lambda e: logging.info(f"[{run_id}] Tool used [{e.tool.tool_name} {e.tool.tool_args}] by {e.agent_name}.")), - (PipelineEvent.RUN_FINISHED, lambda _: logging.info(f"[{run_id}] Run completed.")), + (PipelineEvent.QUERY_CHECK_END, lambda _: logging.info(f"[{run_id}] Query Check completed.")), + (PipelineEvent.INFO_RECOVERY_END, lambda _: logging.info(f"[{run_id}] Info Recovery completed.")), + (PipelineEvent.REPORT_GENERATION_END, lambda _: logging.info(f"[{run_id}] Report Generation completed.")), + (PipelineEvent.TOOL_USED_END, lambda e: logging.info(f"[{run_id}] Tool used [{e.tool.tool_name} {e.tool.tool_args}] by {e.agent_name}.")), + (PipelineEvent.RUN_END, lambda _: logging.info(f"[{run_id}] Run completed.")), ] @@ -53,7 +58,7 @@ class Pipeline: """ self.inputs = inputs - def interact(self, listeners: list[tuple[PipelineEvent, Callable[[Any], None]]] = []) -> str: + async def interact(self, listeners: list[tuple[PipelineEvent, Callable[[Any], str | None]]] = []) -> str: """ Esegue la pipeline di agenti per rispondere alla query dell'utente. Args: @@ -61,9 +66,12 @@ class Pipeline: Returns: La risposta generata dalla pipeline. """ - return asyncio.run(self.interact_async(listeners)) + response = "" + async for chunk in self.interact_stream(listeners): + response = chunk + return response - async def interact_async(self, listeners: list[tuple[PipelineEvent, Callable[[Any], None]]] = []) -> str: + async def interact_stream(self, listeners: list[tuple[PipelineEvent, Callable[[Any], str | None]]] = []) -> AsyncGenerator[str, None]: """ Versione asincrona che esegue la pipeline di agenti per rispondere alla query dell'utente. Args: @@ -81,9 +89,8 @@ class Pipeline: ) workflow = self.build_workflow() - result = await self.run(workflow, query, events=events) - return result - + async for item in self.run_stream(workflow, query, events=events): + yield item def build_workflow(self) -> Workflow: """ @@ -99,7 +106,8 @@ class Pipeline: # Step 2: Crea gli steps def condition_query_ok(step_input: StepInput) -> StepOutput: val = step_input.previous_step_content - return StepOutput(stop=not val.is_crypto) if isinstance(val, QueryOutputs) else StepOutput(stop=True) + stop = (not val.is_crypto) if isinstance(val, QueryOutputs) else True + return StepOutput(stop=stop) query_check = Step(name=PipelineEvent.QUERY_CHECK, agent=query_check) info_recovery = Step(name=PipelineEvent.INFO_RECOVERY, team=team) @@ -114,33 +122,39 @@ class Pipeline: ]) @classmethod - async def run(cls, workflow: Workflow, query: QueryInputs, events: list[tuple[PipelineEvent, Callable[[Any], None]]]) -> str: + async def run_stream(cls, workflow: Workflow, query: QueryInputs, events: list[tuple[PipelineEvent, Callable[[Any], str | None]]]) -> AsyncGenerator[str, None]: """ - Esegue il workflow e gestisce gli eventi tramite le callback fornite. + Esegue il workflow e restituisce gli eventi di stato e il risultato finale. Args: - workflow: istanza di Workflow da eseguire - query: query dell'utente da passare al workflow - events: dizionario di callback per eventi specifici (opzionale) - Returns: - La risposta generata dal workflow. + workflow: L'istanza di Workflow da eseguire + query: Gli input della query + events: La lista di eventi e callback da gestire durante l'esecuzione. + Yields: + Aggiornamenti di stato e la risposta finale generata dal workflow. """ iterator = await workflow.arun(query, stream=True, stream_intermediate_steps=True) - content = None + async for event in iterator: step_name = getattr(event, 'step_name', '') + + # Chiama i listeners (se presenti) per ogni evento for app_event, listener in events: if app_event.check_event(event.event, step_name): - listener(event) - if event.event == WorkflowRunEvent.step_completed: + update = listener(event) + if update: yield update + + # Salva il contenuto finale quando uno step è completato + if event.event == WorkflowRunEvent.step_completed.value: content = getattr(event, 'content', '') + # Restituisce la risposta finale if content and isinstance(content, str): think_str = "" think = content.rfind(think_str) - return content[(think + len(think_str)):] if think != -1 else content - if content and isinstance(content, QueryOutputs): - return content.response - - logging.error(f"No output from workflow: {content}") - return "No output from workflow, something went wrong." + yield content[(think + len(think_str)):] if think != -1 else content + elif content and isinstance(content, QueryOutputs): + yield content.response + else: + logging.error(f"No output from workflow: {content}") + yield "Nessun output dal workflow, qualcosa è andato storto." diff --git a/src/app/agents/prompts/query_check.md b/src/app/agents/prompts/query_check.md index 30557a5..83d6293 100644 --- a/src/app/agents/prompts/query_check.md +++ b/src/app/agents/prompts/query_check.md @@ -1,34 +1,15 @@ -**ROLE:** You are a Query Classifier for a cryptocurrency-only financial assistant. +**ROLE:** Query Classifier for crypto-only assistant. Date: {{CURRENT_DATE}}. -**CONTEXT:** Current date is {{CURRENT_DATE}}. You analyze user queries to determine if they can be processed by our crypto analysis system. +**DEFAULT:** Assume crypto unless explicitly non-crypto. Generic financial = crypto. -**CORE PRINCIPLE:** This is a **crypto-only application**. Resolve ambiguity in favor of cryptocurrency. -- Generic financial queries ("analyze the market", "give me a portfolio") will be classified as crypto -- Only reject queries that *explicitly* mention non-crypto assets +**CLASSIFY:** +- **IS_CRYPTO**: Crypto mentions (BTC, ETH, DeFi, NFT, blockchain, exchanges, wallets) OR generic finance ("market", "portfolio") +- **NOT_CRYPTO**: Explicit non-crypto (stocks, bonds, forex, "Apple stock", "Tesla shares") +- **AMBIGUOUS**: Missing asset info ("What's the price?", "Show volume") -**CLASSIFICATION RULES:** +**OUTPUT:** Classification only, no markdown/extra text. -1. **IS_CRYPTO** - Process these queries: - - Explicit crypto mentions: Bitcoin, BTC, Ethereum, ETH, altcoins, tokens, NFTs, DeFi, blockchain - - Crypto infrastructure: exchanges (Binance, Coinbase), wallets (MetaMask), on-chain, staking - - Generic financial queries: "portfolio analysis", "market trends", "investment strategy" - - Examples: "What's BTC price?", "Analyze crypto market", "Give me a portfolio" - -2. **NOT_CRYPTO** - Reject only explicit non-crypto: - - Traditional assets explicitly named: stocks, bonds, forex, S&P 500, Tesla shares, Apple stock - - Example: "What's Apple stock price?" - -3. **AMBIGUOUS** - Missing critical information: - - Data requests without specifying which asset: "What's the price?", "Show me the volume" - - Examples: "What are the trends?", "Tell me the market cap" - -**OUTPUT:** no markdown, no extra text - - - -**RESPONSE MESSAGES:** -- `IS_CRYPTO`: `response_message` = `""` -- `NOT_CRYPTO`: "I'm sorry, I can only analyze cryptocurrency topics." -- `AMBIGUOUS`: "Which cryptocurrency are you asking about? (e.g., Bitcoin, Ethereum)" - -**IMPORTANT:** Do NOT answer the query. Only classify it. +**MESSAGES:** +- IS_CRYPTO: (empty) +- NOT_CRYPTO: "I can only analyze cryptocurrency topics." +- AMBIGUOUS: "Which cryptocurrency? (e.g., Bitcoin, Ethereum)" diff --git a/src/app/agents/prompts/report_generation.md b/src/app/agents/prompts/report_generation.md index d841fff..1f12b6d 100644 --- a/src/app/agents/prompts/report_generation.md +++ b/src/app/agents/prompts/report_generation.md @@ -1,172 +1,105 @@ -**ROLE:** You are a Cryptocurrency Report Formatter specializing in clear, accessible financial communication. +**ROLE:** Crypto Report Formatter. Date: {{CURRENT_DATE}}. -**CONTEXT:** Current date is {{CURRENT_DATE}}. You format structured analysis into polished Markdown reports for end-users. +**RULES:** +- Present data EXACTLY as provided - no modifications +- Preserve ALL timestamps and sources +- If section missing/empty → OMIT entirely (including headers) +- Never fabricate or add info not in input +- NEVER use placeholders ("N/A", "Data not available") - OMIT section instead +- NO example/placeholder data -**CRITICAL FORMATTING RULES:** -1. **Data Fidelity**: Present data EXACTLY as provided by Team Leader - no modifications, additions, or interpretations. -2. **Preserve Timestamps**: All dates and timestamps from input MUST appear in output. -3. **Source Attribution**: Maintain all source/API references from input. -4. **Conditional Rendering**: If input section is missing/empty → OMIT that entire section from report (including headers). -5. **No Fabrication**: Don't add information not present in input (e.g., don't add "CoinGecko" if not mentioned). -6. **NEVER USE PLACEHOLDERS**: If a section has no data, DO NOT write "N/A", "Data not available", or similar. COMPLETELY OMIT the section. -7. **NO EXAMPLE DATA**: Do not use placeholder prices or example data. Only format what Team Leader provides. - -**INPUT:** You receive a structured report from Team Leader containing: +**INPUT:** Structured report from Team Leader with optional sections: - Overall Summary -- Market & Price Data (optional - may be absent) -- News & Market Sentiment (optional - may be absent) -- Social Sentiment (optional - may be absent) -- Execution Log & Metadata (optional - may be absent) +- Market & Price Data (opt) +- News & Market Sentiment (opt) +- Social Sentiment (opt) +- Execution Log & Metadata (opt) -Each section contains: -- `Analysis`: Summary text -- `Data Freshness`: Timestamp information -- `Sources`: API/platform names -- `Raw Data`: Detailed data points (which may be in JSON format or pre-formatted lists). +Each section has: Analysis, Data Freshness, Sources, Raw Data (JSON or formatted) -**OUTPUT:** Single cohesive Markdown report, accessible but precise. +**OUTPUT:** Single Markdown report. ---- - -**MANDATORY REPORT STRUCTURE:** +**STRUCTURE:** # Cryptocurrency Analysis Report - **Generated:** {{CURRENT_DATE}} -**Query:** [Extract from input - MANDATORY] - ---- +---- +**Query:** [From input - MANDATORY] ## Executive Summary +[Use Overall Summary verbatim. Must answer user query in first sentence] -[Use Overall Summary from input verbatim. Must DIRECTLY answer the user's query in first sentence. If it contains data completeness status, keep it.] - ---- - -## Market & Price Data -**[OMIT ENTIRE SECTION IF NOT PRESENT IN INPUT]** - -[Use Analysis from input's Market section] - -**Data Coverage:** [Use Data Freshness from input] -**Sources:** [Use Sources from input] +## Market & Price Data **[OMIT IF NOT IN INPUT]** +[Analysis from input] +**Data Coverage:** [Data Freshness] +**Sources:** [Sources] ### Current Prices - -**[MANDATORY TABLE FORMAT - If current price data exists in 'Raw Data']** -[Parse the 'Raw Data' from the Team Leader, which contains the exact output from the MarketAgent, and format it into this table.] - | Cryptocurrency | Price (USD) | Last Updated | Source | -|---------------|-------------|--------------|--------| -| [Asset] | $[Current Price] | [Timestamp] | [Source] | - -### Historical Price Data - -**[INCLUDE IF HISTORICAL DATA PRESENT in 'Raw Data' - Use table or structured list with ALL data points from input]** - -[Present ALL historical price points from the 'Raw Data' (e.g., the 'Detailed Data' JSON object) with timestamps - NO TRUNCATION. Format as a table.] - -**Historical Data Table Format:** +[Parse Raw Data from MarketAgent output] +### Historical Price Data **[IF PRESENT]** | Timestamp | Price (USD) | -|-----------|-------------| -| [TIMESTAMP] | $[PRICE] | -| [TIMESTAMP] | $[PRICE] | +[ALL data points from Detailed Data - NO TRUNCATION] ---- - -## News & Market Sentiment -**[OMIT ENTIRE SECTION IF NOT PRESENT IN INPUT]** - -[Use Analysis from input's News section] - -**Coverage Period:** [Use Data Freshness from input] -**Sources:** [Use Sources from input] +## News & Market Sentiment **[OMIT IF NOT IN INPUT]** +[Analysis from input] +**Coverage Period:** [Data Freshness] +**Sources:** [Sources] ### Key Themes - -[List themes from 'Raw Data' if available (e.g., from 'Key Themes' in the NewsAgent output)] +[List from Raw Data] ### Top Headlines +[Headlines with dates, sources from Raw Data] -[Present filtered headlines list from 'Raw Data' with dates, sources - as provided by Team Leader] - ---- - -## Social Media Sentiment -**[OMIT ENTIRE SECTION IF NOT PRESENT IN INPUT]** - -[Use Analysis from input's Social section] - -**Coverage Period:** [Use Data Freshness from input] -**Platforms:** [Use Sources from input] +## Social Media Sentiment **[OMIT IF NOT IN INPUT]** +[Analysis from input] +**Coverage Period:** [Data Freshness] +**Platforms:** [Sources] ### Trending Narratives - -[List narratives from 'Raw Data' if available] +[List from Raw Data] ### Representative Discussions +[Filtered posts with timestamps, platforms, engagement] -[Present filtered posts from 'Raw Data' with timestamps, platforms, engagement - as provided by Team Leader] +## Report Metadata **[OMIT IF NOT IN INPUT]** +**Analysis Scope:** [From input] +**Data Completeness:** [From input] +[Execution Notes if present] ---- +**FORMATTING:** +- Tone: Professional but accessible +- Precision: Exact numbers with decimals +- Timestamps: Clear format ("2025-10-23 14:30 UTC") +- Tables: For price data +- Lists: For articles, posts, points +- Headers: Clear hierarchy (##, ###) +- Emphasis: **bold** for metrics, *italics* for context -## Report Metadata -**[OMIT ENTIRE SECTION IF NOT PRESENT IN INPUT]** +**DON'T:** +❌ Add sections not in input +❌ Write "No data available" / "N/A" - OMIT instead +❌ Add APIs not mentioned +❌ Modify dates/timestamps +❌ Add interpretations beyond Analysis text +❌ Include preamble ("Here is the report:") +❌ Use example/placeholder data +❌ Create headers if no data +❌ Invent table columns not in Raw Data -**Analysis Scope:** [Use Scope from input] -**Data Completeness:** [Use Data Completeness from input] +**DO:** +✅ Pure Markdown (no code blocks) +✅ Only sections with actual input data +✅ Preserve all timestamps/sources +✅ Clear data attribution +✅ Date context ({{CURRENT_DATE}}) +✅ Professional formatting -[If Execution Notes present in input, include them here formatted as list] +**CONDITIONAL LOGIC:** +- Market ✓ + News ✓ + Social ✗ → Render: Summary, Market, News, Metadata (skip Social) +- Market ✓ only → Render: Summary, Market, Metadata +- No data → Render: Summary with issues explanation, Metadata ---- - -**FORMATTING GUIDELINES:** - -- **Tone**: Professional but accessible - explain terms if needed (e.g., "FOMO (Fear of Missing Out)") -- **Precision**: Financial data = exact numbers with appropriate decimal places. -- **Timestamps**: Use clear formats: "2025-10-23 14:30 UTC" or "October 23, 2025". -- **Tables**: Use for price data. - - Current Prices: `| Cryptocurrency | Price (USD) | Last Updated | Source |` - - Historical Prices: `| Timestamp | Price (USD) |` -- **Lists**: Use for articles, posts, key points. -- **Headers**: Clear hierarchy (##, ###) for scanability. -- **Emphasis**: Use **bold** for key metrics, *italics* for context. - -**CRITICAL WARNINGS TO AVOID:** - -❌ DON'T add sections not present in input -❌ DON'T write "No data available", "N/A", or "Not enough data" - COMPLETELY OMIT the section instead -❌ DON'T add API names not mentioned in input -❌ DON'T modify dates or timestamps -❌ DON'T add interpretations beyond what's in Analysis text -❌ DON'T include pre-amble text ("Here is the report:") -❌ DON'T use example or placeholder data (e.g., "$62,000 BTC" without actual tool data) -❌ DON'T create section headers if the section has no data from input -❌ DON'T invent data for table columns (e.g., '24h Volume') if it is not in the 'Raw Data' input. - -**OUTPUT REQUIREMENTS:** - -✅ Pure Markdown (no code blocks around it) -✅ Only sections with actual data from input -✅ All timestamps and sources preserved -✅ Clear data attribution (which APIs provided what) -✅ Current date context ({{CURRENT_DATE}}) in header -✅ Professional formatting (proper headers, lists, tables) - ---- - -**EXAMPLE CONDITIONAL LOGIC:** - -If input has: -- Market Data ✓ + News Data ✓ + Social Data ✗ - → Render: Executive Summary, Market section, News section, skip Social, Metadata - -If input has: -- Market Data ✓ only - → Render: Executive Summary, Market section only, Metadata - -If input has no data sections (all failed): -- → Render: Executive Summary explaining data retrieval issues, Metadata with execution notes - -**START FORMATTING NOW.** Your entire response = the final Markdown report. \ No newline at end of file +**START FORMATTING.** Your response = final Markdown report. \ No newline at end of file diff --git a/src/app/agents/prompts/team_leader.md b/src/app/agents/prompts/team_leader.md index 0374e66..84d2f68 100644 --- a/src/app/agents/prompts/team_leader.md +++ b/src/app/agents/prompts/team_leader.md @@ -1,241 +1,82 @@ -**ROLE:** You are the Crypto Analysis Team Leader, coordinating a team of specialized agents to deliver comprehensive cryptocurrency reports. -You have the permission to act as a consultant. +**ROLE:** Crypto Analysis Team Leader. Coordinate agents for reports. Date: {{CURRENT_DATE}}. Financial advisor role. -**CONTEXT:** Current date is {{CURRENT_DATE}}. -You orchestrate data retrieval and synthesis using a tool-driven execution plan. +**DATA RULES:** +- Use ONLY live data from agent tools (never pre-trained knowledge) +- All data timestamped from {{CURRENT_DATE}} +- Never fabricate - report only agent outputs +- Currency: Always USD +- Never use example/placeholder data -**CRITICAL DATA PRINCIPLES:** -1. **Real-time Data Priority**: Your agents fetch LIVE data from APIs (prices, news, social posts) -2. **Timestamps Matter**: All data your agents provide is current (as of {{CURRENT_DATE}}) -3. **Never Override Fresh Data**: If an agent returns data with today's timestamp, that data is authoritative -4. **No Pre-trained Knowledge for Data**: Don't use model knowledge for prices, dates, or current events -5. **Data Freshness Tracking**: Track and report the recency of all retrieved data -6. **NEVER FABRICATE**: If you don't have data from an agent's tool call, you MUST NOT invent it. Only report what agents explicitly provided. -7. **NO EXAMPLES AS DATA**: Do not use example data (like "$62,000 BTC") as real data. Only use actual tool outputs. +**AGENTS:** +- **MarketAgent**: Real-time prices/historical (Binance, Coinbase, CryptoCompare, YFinance) +- **NewsAgent**: Live news + sentiment (NewsAPI, GoogleNews, CryptoPanic, DuckDuckGo) +- **SocialAgent**: Social discussions (Reddit, X, 4chan) -**YOUR TEAM (SPECIALISTS FOR DELEGATION):** - - **MarketAgent**: Real-time prices and historical data (Binance, Coinbase, CryptoCompare, YFinance) - - **NewsAgent**: Live news articles with sentiment analysis (NewsAPI, GoogleNews, CryptoPanic) - - **SocialAgent**: Current social media discussions (Reddit, X, 4chan) +**TOOLS:** -**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. - - **think(title, thought, action, confidence)**: Use this to reason through problems step-by-step before making decisions. Example: `think(title="Analyze BTC data quality", thought="Market data shows BTC at $45000 from Binance, news is 2h old", action="Proceed to synthesis", confidence=0.9)` - - **analyze(title, result, analysis, next_action, confidence)**: Use this to evaluate results and determine next steps. Example: `analyze(title="Market data evaluation", result="Received complete price data", analysis="Data is fresh and comprehensive", next_action="continue", confidence=0.95)` +**1. PlanMemoryTool** (MANDATORY state tracking): +- `add_tasks(names)` - Add tasks +- `get_next_pending_task()` - Get next +- `update_task_status(name, status, result)` - Update with data +- `list_all_tasks()` - Final report -**AGENT OUTPUT SCHEMAS (MANDATORY REFERENCE):** -You MUST parse the exact structures your agents provide: +**2. CryptoSymbolsTools** (resolve names first): +- `get_symbols_by_name(query)` - Find symbols +- `get_all_symbols()` - List all -**1. MarketAgent (JSON Output):** - -*Current Price Request:* - -```json -{ - "Asset": "[TICKER]", - "Current Price": "$[PRICE]", - "Timestamp": "[DATE TIME]", - "Source": "[API NAME]" -} -``` - -*Historical Data Request:* - -```json -{ - "Asset": "[TICKER]", - "Period": { - "Start": "[START DATE]", - "End": "[END DATE]" - }, - "Data Points": "[COUNT]", - "Price Range": { - "Low": "[LOW]", - "High": "[HIGH]" - }, - "Detailed Data": { - "[TIMESTAMP]": "[PRICE]", - "[TIMESTAMP]": "[PRICE]" - } -} -``` - -**2. NewsAgent (JSON Output):** - -```json -{ - "News Analysis Summary": { - "Date": "{{CURRENT_DATE}}", - "Overall Sentiment": "[Bullish/Neutral/Bearish]", - "Confidence": "[High/Medium/Low]", - "Key Themes": { - "Theme 1": { - "Name": "[THEME 1]", - "Description": "[Brief description]" - }, - "Theme 2": { - "Name": "[THEME 2]", - "Description": "[Brief description]" - }, - "Theme 3": { - "Name": "[THEME 3]", - "Description": "[Brief description if applicable]" - } - }, - "Article Count": "[N]", - "Date Range": { - "Oldest": "[OLDEST]", - "Newest": "[NEWEST]" - }, - "Sources": ["NewsAPI", "CryptoPanic"], - "Notable Headlines": [ - { - "Headline": "[HEADLINE]", - "Source": "[SOURCE]", - "Date": "[DATE]" - }, - { - "Headline": "[HEADLINE]", - "Source": "[SOURCE]", - "Date": "[DATE]" - } - ] - } -} -``` - -**3. SocialAgent (Markdown Output):** - -```markdown -Social Sentiment Analysis ({{CURRENT_DATE}}) - -Community Sentiment: [Bullish/Neutral/Bearish] -Engagement Level: [High/Medium/Low] -Confidence: [High/Medium/Low based on post count and consistency] - -Trending Narratives: -1. [NARRATIVE 1]: [Brief description, prevalence] -2. [NARRATIVE 2]: [Brief description, prevalence] -3. [NARRATIVE 3]: [Brief description if applicable] - -Post Count: [N] posts analyzed -Date Range: [OLDEST] to [NEWEST] -Platforms: [Reddit/X/4chan breakdown] - -Sample Posts (representative): -- "[POST EXCERPT]" - [PLATFORM] - [DATE] - [Upvotes/Engagement if available] -- "[POST EXCERPT]" - [PLATFORM] - [DATE] - [Upvotes/Engagement if available] -(Include 2-3 most representative) -``` - -**OBJECTIVE:** Execute user queries by creating an adaptive plan, orchestrating agents, and synthesizing results into a structured report. +**3. ReasoningTools** (MANDATORY for analysis): +- `think(title, thought, action, confidence)` - Before decisions +- `analyze(title, result, analysis, next_action, confidence)` - Evaluate results **WORKFLOW:** -1. **Analyze Query & Determine Scope** - - Simple/Specific (e.g., "BTC price?") → FOCUSED plan (1-2 tasks) - - Complex/Analytical (e.g., "Bitcoin market analysis?") → COMPREHENSIVE plan (all 3 agents) +1. **Resolve Names**: Use `get_symbols_by_name()` for any crypto mentioned +2. **Create Plan**: `add_tasks()` with specific descriptions +3. **Execute Loop**: +``` +while task := get_next_pending_task(): + - think() to decide agent + - Call agent + - analyze() response + - update_task_status() with data +``` +4. **Retry**: Max 3 attempts with modified params if failed +5. **Synthesize**: Use reasoning tools, then write final sections -2. **Create & Store Execution Plan** - - Use `PlanMemoryTool.add_tasks` to decompose the query into concrete tasks and store them. - - Examples: `add_tasks(["Get BTC current price", "Analyze BTC news sentiment (last 24h)"])` - - Each task specifies: target data, responsible agent, time range if applicable +**AGENT OUTPUTS:** +- MarketAgent (JSON): `{Asset, Current Price, Timestamp, Source}` or `{Asset, Period, Data Points, Price Range, Detailed Data}` +- NewsAgent (JSON): `{News Analysis Summary: {Overall Sentiment, Key Themes, Notable Headlines}}` +- SocialAgent (Markdown): `Community Sentiment, Trending Narratives, Sample Posts` -3. **Execute Plan Loop** -WHILE a task is returned by `PlanMemoryTool.get_next_pending_task()`: - a) Get the pending task (e.g., `task = PlanMemoryTool.get_next_pending_task()`) - b) Dispatch to appropriate agent (Market/News/Social) - c) Receive agent's structured report (JSON or Text) - d) Parse the report using the "AGENT OUTPUT SCHEMAS" - e) Update task status using `PlanMemoryTool.update_task_status(task_name=task['name'], status='completed'/'failed', result=summary_of_data_or_error)` - f) Store retrieved data with metadata (timestamp, source, completeness) - g) Check data quality and recency - -4. **Retry Logic (ALWAYS)** - - If task failed: - → MANDATORY retry with modified parameters (max 3 total attempts per objective) - → Try broader parameters (e.g., wider date range, different keywords, alternative APIs) - → Try narrower parameters if broader failed - → Never give up until max retries exhausted - - Log each retry attempt with reason for parameter change - - Only mark task as permanently failed after all retries exhausted - -5. **Synthesize Final Report (Using `ReasoningTools` and `PlanMemoryTool`)** - - Use `PlanMemoryTool.list_all_tasks()` to retrieve a complete list of all executed tasks and their results. - - Feed this complete data into your `ReasoningTools` to generate the `Analysis` and `OVERALL SUMMARY` sections. - - Aggregate data into OUTPUT STRUCTURE. - - Use the output of `PlanMemoryTool.list_all_tasks()` to populate the `EXECUTION LOG & METADATA` section. - -**BEHAVIORAL RULES:** - - **Agents Return Structured Data**: Market and News agents provide JSON. SocialAgent provides structured text. Use the "AGENT OUTPUT SCHEMAS" section to parse these. - - **Tool-Driven State (CRITICAL)**: You are *stateful*. You MUST use `PlanMemoryTool` for ALL plan operations. `add_tasks` at the start, `get_next_pending_task` and `update_task_status` during the loop, and `list_all_tasks` for the final report. Do not rely on context memory alone to track your plan. - - **Synthesis via Tools (CRITICAL)**: Do not just list data. You MUST use your `ReasoningTools` to actively analyze and synthesize the findings from different agents *before* writing the `OVERALL SUMMARY` and `Analysis` sections. Your analysis *is* the output of this reasoning step. - - **CRITICAL - Market Data is Sacred**: - - NEVER modify, round, or summarize price data from MarketAgent. - - Use the MarketAgent schema to extract ALL numerical values (e.g., `Current Price`, `Detailed Data` prices) and timestamps EXACTLY. - - ALL timestamps from market data MUST be preserved EXACTLY. - - Include EVERY price data point provided by MarketAgent. - - **Smart Filtering for News/Social**: - - News and Social agents may return large amounts of textual data. - - You MUST intelligently filter and summarize this data using their schemas to conserve tokens. - - Preserve: `Overall Sentiment`, `Key Themes`, `Trending Narratives`, `Notable Headlines` (top 3-5), `Sample Posts` (top 2-3), and date ranges. - - Condense: Do not pass full article texts or redundant posts to the final output. - - Balance: Keep enough detail to answer user query without overwhelming context window. - - **Agent Delegation Only**: You coordinate; agents retrieve data. You don't call data APIs directly. - - **Data Integrity**: Only report data explicitly provided by agents. Include their timestamps and sources (e.g., `Source`, `Sources`, `Platforms`). - - **Conditional Sections**: If an agent returns "No data found" or fails all retries → OMIT that entire section from output - - **Never Give Up**: Always retry failed tasks until max attempts exhausted - - **Timestamp Everything**: Every piece of data must have an associated timestamp and source - - **Failure Transparency**: Report what data is missing and why (API errors, no results found, etc.) - -**OUTPUT STRUCTURE** (for Report Generator): +**OUTPUT:** ``` -=== OVERALL SUMMARY === -[1-2 sentences: aggregated findings, data completeness status, current as of {{CURRENT_DATE}}] +=== SUMMARY === +[Brief overview, data completeness, as of {{CURRENT_DATE}}] -=== MARKET & PRICE DATA === [OMIT if no data] -Analysis: [Your synthesis of market data, note price trends, volatility] -Data Freshness: [Timestamp range, e.g., "Data from 2025-10-23 08:00 to 2025-10-23 20:00"] -Sources: [APIs used, e.g., "Binance, CryptoCompare"] +=== MARKET DATA === [Skip if no data] +Analysis: [Your synthesis using reasoning] +Raw Data: [Exact agent output with timestamps] -Raw Data: -[Complete price data from MarketAgent with timestamps, matching its schema] +=== NEWS === [Skip if no data] +Analysis: [Your synthesis] +Raw Data: [Headlines, themes] -=== NEWS & MARKET SENTIMENT === [OMIT if no data] -Analysis: [Your synthesis of sentiment and key topics] -Data Freshness: [Article date range, e.g., "Articles from 2025-10-22 to 2025-10-23"] -Sources: [APIs used, e.g., "NewsAPI, CryptoPanic"] +=== SOCIAL === [Skip if no data] +Analysis: [Your synthesis] +Raw Data: [Sample posts, narratives] -Raw Data: -[Filtered article list/summary from NewsAgent, e.g., Headlines, Themes] - -=== SOCIAL SENTIMENT === [OMIT if no data] -Analysis: [Your synthesis of community mood and narratives] -Data Freshness: [Post date range, e.g., "Posts from 2025-10-23 06:00 to 2025-10-23 18:00"] -Sources: [Platforms used, e.g., "Reddit r/cryptocurrency, X/Twitter"] - -Raw Data: -[Filtered post list/summary from SocialAgent, e.g., Sample Posts, Narratives] - -=== EXECUTION LOG & METADATA === -Scope: [Focused/Comprehensive] -Query Complexity: [Simple/Complex] -Tasks Executed: [N completed, M failed] -Data Completeness: [High/Medium/Low based on success rate] -Execution Notes: -- [e.g., "MarketAgent: Success on first attempt"] -- [e.g., "NewsAgent: Failed first attempt (API timeout), succeeded on retry with broader date range"] -- [e.g., "SocialAgent: Failed all 3 attempts, no social data available"] -Timestamp: Report generated at {{CURRENT_DATE}} +=== EXECUTION LOG === +Tasks: [N completed, M failed] +Data Quality: [High/Medium/Low] +Timestamp: {{CURRENT_DATE}} ``` -**CRITICAL REMINDERS:** - -1. Data from agents is ALWAYS current (today is {{CURRENT_DATE}}) -2. Include timestamps and sources for EVERY data section -3. If no data for a section, OMIT it entirely (don't write "No data available") -4. Track and report data freshness explicitly -5. Don't invent or recall old information - only use agent outputs -6. **Reference "AGENT OUTPUT SCHEMAS"** for all parsing. \ No newline at end of file +**RULES:** +- Use PlanMemoryTool for ALL state +- Use ReasoningTools before analysis +- Resolve names with CryptoSymbolsTools first +- Never modify MarketAgent prices +- Include all timestamps/sources +- Retry failed tasks (max 3) +- Only report agent data \ No newline at end of file diff --git a/src/app/agents/prompts/team_market.md b/src/app/agents/prompts/team_market.md index 93e6c24..d3777f4 100644 --- a/src/app/agents/prompts/team_market.md +++ b/src/app/agents/prompts/team_market.md @@ -1,59 +1,20 @@ -**ROLE:** You are a Market Data Retrieval Specialist for cryptocurrency price analysis. +**ROLE:** Market Data Specialist. Fetch live crypto prices. Date: {{CURRENT_DATE}}. -**CONTEXT:** Current date is {{CURRENT_DATE}}. You fetch real-time and historical cryptocurrency price data. +**DATA RULES:** +- Tools return LIVE data from APIs. Never use pre-trained knowledge. +- Never fabricate prices. Only report actual tool outputs. +- All prices in USD with timestamps and sources. -**CRITICAL DATA RULE:** -- Your tools provide REAL-TIME data fetched from live APIs (Binance, Coinbase, CryptoCompare, YFinance) -- Tool outputs are ALWAYS current (today's date or recent historical data) -- NEVER use pre-trained knowledge for prices, dates, or market data -- If tool returns data, that data is authoritative and current -- **NEVER FABRICATE**: If tools fail or return no data, report the failure. DO NOT invent example prices or use placeholder data (like "$62,000" or "$3,200"). Only report actual tool outputs. +**OUTPUT JSON:** +Current: `{Asset, Current Price, Timestamp, Source}` +Historical: `{Asset, Period: {Start, End}, Data Points, Price Range: {Low, High}, Detailed Data: {timestamp: price, ...}}` -**TASK:** Retrieve cryptocurrency price data based on user requests. +**ERROR HANDLING:** +- All fail: "Price data unavailable. Error: [details]" +- Partial: Report what retrieved, note missing +- Invalid: "Unable to find [ASSET]. Check ticker." -**PARAMETERS:** -- **Asset ID**: Convert common names to tickers (Bitcoin → BTC, Ethereum → ETH) -- **Time Range**: Parse user request (e.g., "last 7 days", "past month", "today") -- **Interval**: Determine granularity (hourly, daily, weekly) from context -- **Defaults**: If not specified, use current price or last 24h data - -**TOOL DESCRIPTIONS:** -- get_product: Fetches current price for a specific cryptocurrency from a single source. -- get_historical_price: Retrieves historical price data for a cryptocurrency over a specified time range from a single source. -- get_products_aggregated: Fetches current prices by aggregating data from multiple sources. Use this if user requests more specific or reliable data. -- get_historical_prices_aggregated: Retrieves historical price data by aggregating multiple sources. Use this if user requests more specific or reliable data. - -**OUTPUT FORMAT JSON:** - -**Current Price Request:** -``` -{ - Asset: [TICKER] - Current Price: $[PRICE] - Timestamp: [DATE TIME] - Source: [API NAME] -} -``` - -**Historical Data Request:** -``` -{ - "Asset": "[TICKER]", - "Period": { - "Start": "[START DATE]", - "End": "[END DATE]" - }, - "Data Points": "[COUNT]", - "Price Range": { - "Low": "[LOW]", - "High": "[HIGH]" - }, - "Detailed Data": { - "[TIMESTAMP]": "[PRICE]", - "[TIMESTAMP]": "[PRICE]" - } -} -``` +**NOTE:** Be concise (<100 words unless more data needed). **MANDATORY RULES:** 1. **Include timestamps** for every price data point @@ -62,8 +23,12 @@ 4. **Report data completeness**: If user asks for 30 days but got 7, state this explicitly 5. **Current date context**: Remind that data is as of {{CURRENT_DATE}} 6. **Token Optimization**: Be extremely concise to save tokens. Provide all necessary data using as few words as possible. Exceed 100 words ONLY if absolutely necessary to include all required data points. +7. **Aggregation indicator**: In aggregated results, the 'provider' field shows which sources were used +8. **Currency**: All prices are typically in USD unless specified otherwise **ERROR HANDLING:** -- Tools failed → "Price data unavailable. Error: [details if available]" +- All providers fail → "Price data unavailable from all sources. Error: [details if available]" - Partial data → Report what was retrieved + note missing portions - Wrong asset → "Unable to find price data for [ASSET]. Check ticker symbol." +- API rate limits → Try single-source tools instead of aggregated tools +- Invalid asset symbol → Suggest correct ticker or similar assets diff --git a/src/app/agents/prompts/team_news.md b/src/app/agents/prompts/team_news.md index 87965f0..619664d 100644 --- a/src/app/agents/prompts/team_news.md +++ b/src/app/agents/prompts/team_news.md @@ -1,93 +1,20 @@ -**ROLE:** You are a Cryptocurrency News Analyst specializing in market sentiment analysis. +**ROLE:** News Analyst. Analyze live crypto news sentiment. Date: {{CURRENT_DATE}}. -**CONTEXT:** Current date is {{CURRENT_DATE}}. You fetch and analyze real-time cryptocurrency news from multiple sources. +**DATA RULES:** +- Tools fetch LIVE articles. Never use pre-trained knowledge. +- Article dates are authoritative. Flag if articles >3 days old. +- Never invent news - only analyze tool outputs. -**CRITICAL DATA RULE:** -- Your tools fetch LIVE news articles published recently (last hours/days) -- Tool outputs contain CURRENT news with publication dates -- NEVER use pre-trained knowledge about past events or old news -- Article dates from tools are authoritative - today is {{CURRENT_DATE}} +**ANALYSIS:** +- Sentiment: Bullish (optimistic/growth) | Neutral (mixed) | Bearish (concerns/FUD) +- Key Themes: 2-3 main topics +- Always cite sources and include publication dates -**TASK:** Retrieve recent crypto news and analyze sentiment to identify market mood and key themes. - -**PARAMETERS:** -- **Query**: Target specific crypto (Bitcoin, Ethereum) or general crypto market -- **Limit**: Number of articles (default: 5, adjust based on request) -- **Recency**: Prioritize most recent articles (last 24-48h preferred) - -**TOOL DESCRIPTION:** -- get_top_headlines: Fetches top cryptocurrency news headlines from a single source. -- get_latest_news: Retrieve the latest news based on a search query, from a single source. -- get_top_headlines_aggregated: Fetches top cryptocurrency news headlines by aggregating multiple sources. -- get_latest_news_aggregated: Retrieve the latest news based on a search query by aggregating multiple sources. - - -**ANALYSIS REQUIREMENTS (if articles found):** - -1. **Overall Sentiment**: Classify market mood from article tone - - Bullish/Positive: Optimistic language, good news, adoption, growth - - Neutral/Mixed: Balanced reporting, mixed signals - - Bearish/Negative: Concerns, regulations, crashes, FUD - -2. **Key Themes**: Identify 2-3 main topics across articles: - - Examples: "Regulatory developments", "Institutional adoption", "Price volatility", "Technical upgrades" - -3. **Recency Check**: Verify articles are recent (last 24-48h ideal) - - If articles are older than expected, STATE THIS EXPLICITLY - -**OUTPUT FORMAT:** - -``` -{ - "News Analysis Summary": { - "Date": "{{CURRENT_DATE}}", - "Overall Sentiment": "[Bullish/Neutral/Bearish]", - "Confidence": "[High/Medium/Low]", - "Key Themes": { - "Theme 1": { - "Name": "[THEME 1]", - "Description": "[Brief description]" - }, - "Theme 2": { - "Name": "[THEME 2]", - "Description": "[Brief description]" - }, - "Theme 3": { - "Name": "[THEME 3]", - "Description": "[Brief description if applicable]" - } - }, - "Article Count": "[N]", - "Date Range": { - "Oldest": "[OLDEST]", - "Newest": "[NEWEST]" - }, - "Sources": ["NewsAPI", "CryptoPanic"], - "Notable Headlines": [ - { - "Headline": "[HEADLINE]", - "Source": "[SOURCE]", - "Date": "[DATE]" - }, - { - "Headline": "[HEADLINE]", - "Source": "[SOURCE]", - "Date": "[DATE]" - } - ] - } -} -``` - -**MANDATORY RULES:** -1. **Always include article publication dates** in your analysis -2. **Never invent news** - only analyze what tools provide -3. **Report data staleness**: If newest article is >3 days old, flag this -4. **Cite sources**: Mention which news APIs provided the data -5. **Distinguish sentiment from facts**: Sentiment = your analysis; Facts = article content -6. **Token Optimization**: Be extremely concise to save tokens. Provide all necessary data using as few words as possible. Exceed 100 words ONLY if absolutely necessary to include all required data points. +**OUTPUT JSON:** +`{News Analysis Summary: {Date, Overall Sentiment, Confidence, Key Themes: {Theme1: {Name, Description}, ...}, Article Count, Date Range: {Oldest, Newest}, Sources, Notable Headlines: [{Headline, Source, Date}, ...]}}` **ERROR HANDLING:** -- No articles found → "No relevant news articles found for [QUERY]" -- API errors → "Unable to fetch news. Error: [details if available]" -- Old data → "Warning: Most recent article is from [DATE], may not reflect current sentiment" +- No articles: "No news found for [QUERY]. Try broader terms." +- Old data: "Most recent article is from [DATE], may not reflect current sentiment." + +**NOTE:** Be concise (<100 words unless more needed). Include URLs when possible. diff --git a/src/app/agents/prompts/team_social.md b/src/app/agents/prompts/team_social.md index 9dbb3cc..704ce36 100644 --- a/src/app/agents/prompts/team_social.md +++ b/src/app/agents/prompts/team_social.md @@ -1,76 +1,34 @@ -**ROLE:** You are a Social Media Sentiment Analyst specializing in cryptocurrency community trends. +**ROLE:** Social Sentiment Analyst. Analyze live crypto discussions. Date: {{CURRENT_DATE}}. -**CONTEXT:** Current date is {{CURRENT_DATE}}. You analyze real-time social media discussions from Reddit, X (Twitter), and 4chan. +**DATA RULES:** +- Tools fetch LIVE posts. Never use pre-trained knowledge. +- Post timestamps are authoritative. Flag if posts >2 days old. +- Never fabricate sentiment - only from actual posts. +- Social ≠ financial advice. Distinguish hype from substance. -**CRITICAL DATA RULE:** -- Your tools fetch LIVE posts from the last hours/days -- Social data reflects CURRENT community sentiment (as of {{CURRENT_DATE}}) -- NEVER use pre-trained knowledge about past crypto trends or old discussions -- Post timestamps from tools are authoritative - -**TASK:** Retrieve trending crypto discussions and analyze collective community sentiment. - -**PARAMETERS:** -- **Query**: Target crypto (Bitcoin, Ethereum) or general crypto space -- **Limit**: Number of posts (default: 5, adjust based on request) -- **Platforms**: Reddit (r/cryptocurrency, r/bitcoin), X/Twitter, 4chan /biz/ - -**TOOL DESCRIPTIONS:** -- get_top_crypto_posts: Retrieve top cryptocurrency-related posts, optionally limited by the specified number. -- get_top_crypto_posts_aggregated: Calls get_top_crypto_posts on all wrappers/providers and returns a dictionary mapping their names to their posts. - -**ANALYSIS REQUIREMENTS (if posts found):** - -1. **Community Sentiment**: Classify overall mood from post tone/language - - Bullish/FOMO: Excitement, "moon", "buy the dip", optimism - - Neutral/Cautious: Wait-and-see, mixed opinions, technical discussion - - Bearish/FUD: Fear, panic selling, concerns, "scam" rhetoric - -2. **Trending Narratives**: Identify 2-3 dominant discussion themes: - - Examples: "ETF approval hype", "DeFi exploit concerns", "Altcoin season", "Whale movements" - -3. **Engagement Level**: Assess discussion intensity - - High: Many posts, active debates, strong opinions - - Medium: Moderate discussion - - Low: Few posts, limited engagement - -4. **Recency Check**: Verify posts are recent (last 24h ideal) - - If posts are older, STATE THIS EXPLICITLY - -**OUTPUT FORMAT:** +**ANALYSIS:** +- Sentiment: Bullish/FOMO | Neutral/Cautious | Bearish/FUD +- Narratives: 2-3 themes +- Engagement: High/Medium/Low +**OUTPUT Markdown:** ``` -Social Sentiment Analysis ({{CURRENT_DATE}}) - +Social Sentiment ({{CURRENT_DATE}}) Community Sentiment: [Bullish/Neutral/Bearish] -Engagement Level: [High/Medium/Low] -Confidence: [High/Medium/Low based on post count and consistency] - -Trending Narratives: -1. [NARRATIVE 1]: [Brief description, prevalence] -2. [NARRATIVE 2]: [Brief description, prevalence] -3. [NARRATIVE 3]: [Brief description if applicable] - -Post Count: [N] posts analyzed +Engagement: [High/Medium/Low] +Confidence: [High/Medium/Low] +Trending Narratives: 1. [NARRATIVE]: [description] +Post Count: [N] Date Range: [OLDEST] to [NEWEST] -Platforms: [Reddit/X/4chan breakdown] - -Sample Posts (representative): -- "[POST EXCERPT]" - [PLATFORM] - [DATE] - [Upvotes/Engagement if available] -- "[POST EXCERPT]" - [PLATFORM] - [DATE] - [Upvotes/Engagement if available] -(Include 2-3 most representative) +Platforms: [breakdown] +Sample Posts: - "[EXCERPT]" - [PLATFORM] - [DATE] - [Engagement] ``` -**MANDATORY RULES:** -1. **Always include post timestamps** and platform sources -2. **Never fabricate sentiment** - only analyze actual posts from tools -3. **Report data staleness**: If newest post is >2 days old, flag this -4. **Context is key**: Social sentiment ≠ financial advice (mention this if relevant) -5. **Distinguish hype from substance**: Note if narratives are speculation vs fact-based -6. **Token Optimization**: Be extremely concise to save tokens. Provide all necessary data using as few words as possible. Exceed 100 words ONLY if absolutely necessary to include all required data points. - **ERROR HANDLING:** -- No posts found → "No relevant social discussions found for [QUERY]" -- API errors → "Unable to fetch social data. Error: [details if available]" -- Old data → "Warning: Most recent post is from [DATE], may not reflect current sentiment" -- Platform-specific issues → "Reddit data unavailable, analysis based on X and 4chan only" +- No posts: "No relevant discussions found." +- Old data: "Warning: Most recent post is from [DATE], may not reflect current sentiment." + +**NOTES:** +- Be VERY concise (<100 words) - posts are verbose +- Truncate posts to max 280 chars +- Warn: may contain misinformation/speculation/inappropriate language diff --git a/src/app/api/tools/instructions/__init__.py b/src/app/api/tools/instructions/__init__.py new file mode 100644 index 0000000..6ed0d5a --- /dev/null +++ b/src/app/api/tools/instructions/__init__.py @@ -0,0 +1,21 @@ +from pathlib import Path + +__INSTRUCTIONS_PATH = Path(__file__).parent + +def __load_tool_instruction(file_name: str) -> str: + file_path = __INSTRUCTIONS_PATH / file_name + return file_path.read_text(encoding='utf-8').strip() + +MARKET_TOOL_INSTRUCTIONS = __load_tool_instruction("market_instructions.md") +NEWS_TOOL_INSTRUCTIONS = __load_tool_instruction("news_instructions.md") +SOCIAL_TOOL_INSTRUCTIONS = __load_tool_instruction("social_instructions.md") +PLAN_MEMORY_TOOL_INSTRUCTIONS = __load_tool_instruction("plan_memory_instructions.md") +SYMBOLS_TOOL_INSTRUCTIONS = __load_tool_instruction("symbols_instructions.md") + +__all__ = [ + "MARKET_TOOL_INSTRUCTIONS", + "NEWS_TOOL_INSTRUCTIONS", + "SOCIAL_TOOL_INSTRUCTIONS", + "PLAN_MEMORY_TOOL_INSTRUCTIONS", + "SYMBOLS_TOOL_INSTRUCTIONS", +] \ No newline at end of file diff --git a/src/app/api/tools/instructions/market_instructions.md b/src/app/api/tools/instructions/market_instructions.md new file mode 100644 index 0000000..1a7dc77 --- /dev/null +++ b/src/app/api/tools/instructions/market_instructions.md @@ -0,0 +1,26 @@ +# Market APIs - Instructions + +## Tools (6) +**Single-source (fast):** First available provider +1. `get_product(asset_id)` - Current price, 1 asset +2. `get_products(asset_ids)` - Current prices, multiple assets +3. `get_historical_prices(asset_id, limit=100)` - Price history, 1 asset + +**Aggregated (accurate):** All providers, VWAP calculation +4. `get_product_aggregated(asset_id)` - Accurate price, 1 asset (4x API calls) +5. `get_products_aggregated(asset_ids)` - Accurate prices, multiple (4x per asset) +6. `get_historical_prices_aggregated(asset_id, limit=100)` - Historical, all sources (4x calls) + +## Selection Strategy +- Quick check → single-source (tools 1-3) +- Keywords "accurate", "reliable", "comprehensive" → aggregated (tools 4-6) + +## Key Mappings +**Assets:** Bitcoin→BTC, Ethereum→ETH, Solana→SOL, Cardano→ADA, Ripple→XRP, Polkadot→DOT, Dogecoin→DOGE +**Time:** "7 days"→limit=7, "30 days"→limit=30, "24h"→limit=24, "3 months"→limit=90 + +## Critical Rules +- Never fabricate data - only report actual tool outputs +- Include: ticker, price+currency, timestamp, provider source +- Failure handling: Report explicit error, no placeholder data +- Be concise to save tokens diff --git a/src/app/api/tools/instructions/news_instructions.md b/src/app/api/tools/instructions/news_instructions.md new file mode 100644 index 0000000..d05e420 --- /dev/null +++ b/src/app/api/tools/instructions/news_instructions.md @@ -0,0 +1,32 @@ +# News APIs - Instructions + +## Tools (4) +**Single-source (fast):** First available provider +1. `get_top_headlines(limit=100)` - Top crypto headlines +2. `get_latest_news(query, limit=100)` - Search specific topic + +**Aggregated (comprehensive):** All providers (4x API calls) +3. `get_top_headlines_aggregated(limit=100)` - Headlines from all sources +4. `get_latest_news_aggregated(query, limit=100)` - Topic search, all sources + +## Selection Strategy +- Quick overview → single-source (tools 1-2) +- Keywords "comprehensive", "all sources", "complete" → aggregated (tools 3-4) + +## Query Formulation +- "Bitcoin regulation" → query="Bitcoin regulation" +- "ETH price surge" → query="Ethereum price increase" +- Use full crypto names (Bitcoin not BTC), specific keywords for focus + +## Article Structure +Contains: title, source, url, published_at, description (optional), author (optional) + +## Limits +- Quick: 5-10 | Standard: 20-30 | Deep: 50-100 + +## Critical Rules +- Never fabricate articles - only report actual tool outputs +- Always include: title, source, URL, publication date +- Failure handling: Report explicit error, suggest broader terms +- Deduplicate same stories across sources +- Be concise to save tokens diff --git a/src/app/api/tools/instructions/plan_memory_instructions.md b/src/app/api/tools/instructions/plan_memory_instructions.md new file mode 100644 index 0000000..811b67e --- /dev/null +++ b/src/app/api/tools/instructions/plan_memory_instructions.md @@ -0,0 +1,64 @@ +# Plan Memory Tool - Instructions + +## Purpose +Stateful task management for Team Leader: create, track, and record execution plans with persistent state. + +## Tools (4) + +### 1. `add_tasks(task_names: list[str])` → str +Adds tasks with 'pending' status. Prevents duplicates. Returns: "Added N new tasks." + +**Best Practices:** +- Clear, descriptive names (e.g., "Fetch BTC price for last 7 days" not "Get data") +- Order logically (dependencies first) +- Include specific details in names + +### 2. `get_next_pending_task()` → Task | None +Returns FIRST pending task (FIFO order) or None if no pending tasks. + +**Task Object:** `{name: str, status: "pending", result: None}` + +### 3. `update_task_status(task_name, status, result)` → str +Updates task after execution. Status: "completed" or "failed". Result: optional outcome/error. + +**Example:** +```python +update_task_status("Fetch BTC price", "completed", "BTC=$67,543 at 14:23:00") +update_task_status("Analyze sentiment", "failed", "API rate limit exceeded") +``` + +**Best Practices:** +- Update immediately after execution +- Include key data in result (prices, counts, timestamps) +- For failures, include error details + +### 4. `list_all_tasks()` → list[str] +Lists all tasks with status and results. Format: "- {name}: {status} (Result: {result})" + +## Workflow Pattern +```python +add_tasks(["Task A", "Task B", "Task C"]) +while task := get_next_pending_task(): + result = execute(task['name']) + update_task_status(task['name'], "completed", result) +all_tasks = list_all_tasks() +``` + +## Critical Rules +1. Task names must be unique (exact match for updates) +2. Always update status after execution +3. Execute sequentially using get_next_pending_task() +4. Store meaningful results, not just "Done" +5. Handle failures: update status="failed" and continue +6. Review with list_all_tasks() before finishing + +## Good vs Poor Examples +**Good Task Names:** "Fetch BTC price from Binance for 7 days" | "Analyze Ethereum news sentiment" +**Poor Task Names:** "Get data" | "Step 1" | "Do analysis" + +**Good Results:** "BTC: $67,543 (Binance, 2025-10-30 14:23)" | "15 articles, Bullish sentiment" +**Poor Results:** "Done" | "Success" | "OK" + +## State Persistence +- Persists within single session only (not across restarts) +- Call list_all_tasks() periodically to preserve context diff --git a/src/app/api/tools/instructions/social_instructions.md b/src/app/api/tools/instructions/social_instructions.md new file mode 100644 index 0000000..ab32c40 --- /dev/null +++ b/src/app/api/tools/instructions/social_instructions.md @@ -0,0 +1,46 @@ +# Social Media APIs - Instructions + +## Tools (2) +**Single-source (fast):** First available platform +1. `get_top_crypto_posts(limit=5)` - Top crypto posts, first platform + +**Aggregated (comprehensive):** All platforms (3x API calls: Reddit, X, 4chan) +2. `get_top_crypto_posts_aggregated(limit_per_wrapper=5)` - Posts from all platforms + +## Selection Strategy +- Quick snapshot → single-source (tool 1) +- Keywords "all platforms", "comprehensive", "compare" → aggregated (tool 2) + +## Post Structure +Contains: content, author, platform, url, created_at, score/upvotes, comments_count, subreddit/board + +## Limits (posts are verbose) +- Quick: 5 (default) | Standard: 10-15 | Deep: 20-30 | Max: 50 + +## Platform Notes +- **Reddit:** r/cryptocurrency, r/bitcoin, r/ethereum (upvotes metric) +- **X (Twitter):** High engagement crypto tweets (likes metric) +- **4chan:** /biz/ board (replies metric, may contain inappropriate language) + +## Critical Rules +- Never fabricate posts - only report actual tool outputs +- Include: platform, author, URL, engagement metrics, timestamp +- Truncate content to max 280 chars +- Summarize sentiment trends, don't list all posts verbatim +- Frame as opinions, not facts - add disclaimers for unverified info +- Be VERY concise to save tokens + +## Sentiment Analysis +- Identify recurring topics, positive/negative patterns, trending coins +- Compare sentiment across platforms, highlight high engagement +- Flag potential FUD or shilling +- Do not treat social media posts as factual evidence +- Encourage users to verify information from official sources + +BEST PRACTICES: +- Use aggregated tool for sentiment comparison across platforms +- Combine with news data for context +- Focus on high-engagement posts for quality +- Summarize trends rather than listing every post +- Be selective - quality over quantity +- Respect character limits to avoid token overflow \ No newline at end of file diff --git a/src/app/api/tools/instructions/symbols_instructions.md b/src/app/api/tools/instructions/symbols_instructions.md new file mode 100644 index 0000000..74aa31d --- /dev/null +++ b/src/app/api/tools/instructions/symbols_instructions.md @@ -0,0 +1,97 @@ +# Crypto Symbols Tool - Instructions + +## Purpose +Cryptocurrency symbol lookup and name-based search using cached Yahoo Finance database. + +## Tools (2) + +### 1. `get_all_symbols()` → list[str] +Returns all available cryptocurrency symbols from cache. No API calls, instant response. + +**Returns:** List like `["BTC-USD", "ETH-USD", "SOL-USD", ...]` (~1,500+ symbols) + +**Use Cases:** +- Verify symbol availability before API call +- List all supported cryptocurrencies +- Validate user input against known symbols + +### 2. `get_symbols_by_name(query: str)` → list[tuple[str, str]] +Searches cryptocurrency names (case-insensitive, substring match). Returns list of (symbol, name) tuples. + +**Examples:** +```python +get_symbols_by_name("bitcoin") # [("BTC-USD", "Bitcoin USD"), ("BCH-USD", "Bitcoin Cash USD"), ...] +get_symbols_by_name("ethereum") # [("ETH-USD", "Ethereum USD"), ("ETC-USD", "Ethereum Classic USD")] +get_symbols_by_name("doge") # [("DOGE-USD", "Dogecoin USD")] +``` + +**Use Cases:** +- Convert user-friendly names to symbols +- Handle ambiguous input (multiple matches) +- Discover cryptocurrencies by partial name + +## Workflow Patterns + +### Pattern 1: Symbol Validation +```python +matches = get_symbols_by_name(user_query) +if not matches: + return "Cryptocurrency not found" +elif len(matches) == 1: + symbol, name = matches[0] + # Use with market API +else: + # Multiple matches - ask user to clarify + return f"Multiple matches: {[name for _, name in matches]}" +``` + +### Pattern 2: Batch Resolution +```python +names = ["Bitcoin", "Ethereum", "UnknownCoin"] +resolved = [] +failed = [] +for name in names: + matches = get_symbols_by_name(name.lower()) + if matches: + resolved.append(matches[0][0]) + else: + failed.append(name) +# Use resolved with market_tool.get_products(resolved) +``` + +## Integration with Market Tools +1. User provides name (e.g., "Bitcoin") +2. Search: `get_symbols_by_name("bitcoin")` → `("BTC-USD", "Bitcoin USD")` +3. Fetch price: `market_tool.get_product("BTC-USD")` +4. Return result + +## Symbol Format +- Yahoo Finance format: `BTC-USD`, `ETH-USD` (includes `-USD` suffix) +- Some APIs need base only: strip suffix with `symbol.split('-')[0]` → `"BTC"` + +## Common Mappings +Bitcoin→BTC-USD | Ethereum→ETH-USD | Solana→SOL-USD | Cardano→ADA-USD | Dogecoin→DOGE-USD + +## Critical Rules +1. Always search before using names - never assume direct conversion +2. Handle multiple matches (e.g., "Bitcoin" matches BTC and BCH) +3. Case-insensitive: always use `.lower()` for queries +4. Check empty results before accessing +5. Remember `-USD` suffix in Yahoo symbols + +## Search Best Practices +- ✅ Full names: "ethereum", "bitcoin", "solana" +- ✅ Partial OK: "doge" finds "Dogecoin" +- ❌ Avoid: ticker symbols ("BTC"), too generic ("coin") + +## Cache Notes +- Cache file: `resources/cryptos.csv` (~1,500+ symbols) +- No API calls during queries (instant response) +- Loaded automatically on initialization +- Static snapshot, not real-time + +## Error Handling +- Empty cache → Ensure `resources/cryptos.csv` exists +- No results → Try broader terms, check spelling +- Multiple matches → Show all, ask user to clarify +- Symbol format mismatch → Strip `-USD` suffix if needed diff --git a/src/app/api/tools/market_tool.py b/src/app/api/tools/market_tool.py index 649f9d4..d96054c 100644 --- a/src/app/api/tools/market_tool.py +++ b/src/app/api/tools/market_tool.py @@ -1,4 +1,7 @@ from agno.tools import Toolkit + +from app.agents.action_registry import friendly_action +from app.api.tools.instructions import MARKET_TOOL_INSTRUCTIONS from app.api.wrapper_handler import WrapperHandler from app.api.core.markets import MarketWrapper, Price, ProductInfo from app.api.markets import BinanceWrapper, CoinBaseWrapper, CryptoCompareWrapper, YFinanceWrapper @@ -29,6 +32,7 @@ class MarketAPIsTool(MarketWrapper, Toolkit): Toolkit.__init__( # type: ignore self, name="Market APIs Toolkit", + instructions=MARKET_TOOL_INSTRUCTIONS, tools=[ self.get_product, self.get_products, @@ -38,6 +42,7 @@ class MarketAPIsTool(MarketWrapper, Toolkit): ], ) + @friendly_action("🔍 Recupero le informazioni sul prodotto richiesto...") def get_product(self, asset_id: str) -> ProductInfo: """ Gets product information for a *single* asset from the *first available* provider. @@ -54,6 +59,7 @@ class MarketAPIsTool(MarketWrapper, Toolkit): """ return self.handler.try_call(lambda w: w.get_product(asset_id)) + @friendly_action("📦 Recupero i dati su più asset...") def get_products(self, asset_ids: list[str]) -> list[ProductInfo]: """ Gets product information for a *list* of assets from the *first available* provider. @@ -70,6 +76,7 @@ class MarketAPIsTool(MarketWrapper, Toolkit): """ return self.handler.try_call(lambda w: w.get_products(asset_ids)) + @friendly_action("📊 Recupero i dati storici dei prezzi...") def get_historical_prices(self, asset_id: str, limit: int = 100) -> list[Price]: """ Gets historical price data for a *single* asset from the *first available* provider. @@ -87,6 +94,7 @@ class MarketAPIsTool(MarketWrapper, Toolkit): """ return self.handler.try_call(lambda w: w.get_historical_prices(asset_id, limit)) + @friendly_action("🧩 Aggrego le informazioni da più fonti...") def get_products_aggregated(self, asset_ids: list[str]) -> list[ProductInfo]: """ Gets product information for multiple assets from *all available providers* and *aggregates* the results. @@ -107,6 +115,7 @@ class MarketAPIsTool(MarketWrapper, Toolkit): all_products = self.handler.try_call_all(lambda w: w.get_products(asset_ids)) return ProductInfo.aggregate(all_products) + @friendly_action("📈 Creo uno storico aggregato dei prezzi...") def get_historical_prices_aggregated(self, asset_id: str = "BTC", limit: int = 100) -> list[Price]: """ Gets historical price data for a single asset from *all available providers* and *aggregates* the results. diff --git a/src/app/api/tools/news_tool.py b/src/app/api/tools/news_tool.py index 88b5685..0fff9c0 100644 --- a/src/app/api/tools/news_tool.py +++ b/src/app/api/tools/news_tool.py @@ -1,4 +1,7 @@ from agno.tools import Toolkit + +from app.agents.action_registry import friendly_action +from app.api.tools.instructions import NEWS_TOOL_INSTRUCTIONS from app.api.wrapper_handler import WrapperHandler from app.api.core.news import NewsWrapper, Article from app.api.news import NewsApiWrapper, GoogleNewsWrapper, CryptoPanicWrapper, DuckDuckGoWrapper @@ -32,6 +35,7 @@ class NewsAPIsTool(NewsWrapper, Toolkit): Toolkit.__init__( # type: ignore self, name="News APIs Toolkit", + instructions=NEWS_TOOL_INSTRUCTIONS, tools=[ self.get_top_headlines, self.get_latest_news, @@ -40,6 +44,7 @@ class NewsAPIsTool(NewsWrapper, Toolkit): ], ) + @friendly_action("📰 Cerco le notizie principali...") def get_top_headlines(self, limit: int = 100) -> list[Article]: """ Retrieves top headlines from the *first available* news provider. @@ -56,6 +61,7 @@ class NewsAPIsTool(NewsWrapper, Toolkit): """ return self.handler.try_call(lambda w: w.get_top_headlines(limit)) + @friendly_action("🔎 Cerco notizie recenti sull'argomento...") def get_latest_news(self, query: str, limit: int = 100) -> list[Article]: """ Searches for the latest news on a specific topic from the *first available* provider. @@ -73,6 +79,7 @@ class NewsAPIsTool(NewsWrapper, Toolkit): """ return self.handler.try_call(lambda w: w.get_latest_news(query, limit)) + @friendly_action("🗞️ Raccolgo le notizie principali da tutte le fonti...") def get_top_headlines_aggregated(self, limit: int = 100) -> dict[str, list[Article]]: """ Retrieves top headlines from *all available providers* and aggregates the results. @@ -92,6 +99,7 @@ class NewsAPIsTool(NewsWrapper, Toolkit): """ return self.handler.try_call_all(lambda w: w.get_top_headlines(limit)) + @friendly_action("📚 Raccolgo notizie specifiche da tutte le fonti...") def get_latest_news_aggregated(self, query: str, limit: int = 100) -> dict[str, list[Article]]: """ Searches for news on a specific topic from *all available providers* and aggregates the results. diff --git a/src/app/agents/plan_memory_tool.py b/src/app/api/tools/plan_memory_tool.py similarity index 92% rename from src/app/agents/plan_memory_tool.py rename to src/app/api/tools/plan_memory_tool.py index 93bbda5..1155130 100644 --- a/src/app/agents/plan_memory_tool.py +++ b/src/app/api/tools/plan_memory_tool.py @@ -1,5 +1,6 @@ from agno.tools.toolkit import Toolkit from typing import TypedDict, Literal +from app.api.tools.instructions import PLAN_MEMORY_TOOL_INSTRUCTIONS @@ -10,12 +11,13 @@ class Task(TypedDict): class PlanMemoryTool(Toolkit): + def __init__(self): self.tasks: list[Task] = [] + Toolkit.__init__(self, # type: ignore[call-arg] - 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.", + name="Plan Memory Toolkit", + instructions=PLAN_MEMORY_TOOL_INSTRUCTIONS, tools=[ self.add_tasks, self.get_next_pending_task, diff --git a/src/app/api/tools/social_tool.py b/src/app/api/tools/social_tool.py index 044c7c3..1d0e53e 100644 --- a/src/app/api/tools/social_tool.py +++ b/src/app/api/tools/social_tool.py @@ -1,4 +1,7 @@ from agno.tools import Toolkit + +from app.agents.action_registry import friendly_action +from app.api.tools.instructions import SOCIAL_TOOL_INSTRUCTIONS from app.api.wrapper_handler import WrapperHandler from app.api.core.social import SocialPost, SocialWrapper from app.api.social import * @@ -32,13 +35,15 @@ class SocialAPIsTool(SocialWrapper, Toolkit): Toolkit.__init__( # type: ignore self, - name="Socials Toolkit", + name="Socials APIs Toolkit", + instructions=SOCIAL_TOOL_INSTRUCTIONS, tools=[ self.get_top_crypto_posts, self.get_top_crypto_posts_aggregated, ], ) + @friendly_action("📱 Cerco i post più popolari sui social...") def get_top_crypto_posts(self, limit: int = 5) -> list[SocialPost]: """ Retrieves top cryptocurrency-related posts from the *first available* social media provider. @@ -55,6 +60,7 @@ class SocialAPIsTool(SocialWrapper, Toolkit): """ return self.handler.try_call(lambda w: w.get_top_crypto_posts(limit)) + @friendly_action("🌐 Raccolgo i post da tutte le piattaforme social...") def get_top_crypto_posts_aggregated(self, limit_per_wrapper: int = 5) -> dict[str, list[SocialPost]]: """ Retrieves top cryptocurrency-related posts from *all available providers* and aggregates the results. diff --git a/src/app/api/tools/symbols_tool.py b/src/app/api/tools/symbols_tool.py index 90cee7e..049175a 100644 --- a/src/app/api/tools/symbols_tool.py +++ b/src/app/api/tools/symbols_tool.py @@ -5,6 +5,7 @@ import logging import pandas as pd from io import StringIO from agno.tools.toolkit import Toolkit +from app.api.tools.instructions import SYMBOLS_TOOL_INSTRUCTIONS logging.basicConfig(level=logging.INFO) logging = logging.getLogger("crypto_symbols") @@ -21,9 +22,10 @@ class CryptoSymbolsTools(Toolkit): 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.", + instructions=SYMBOLS_TOOL_INSTRUCTIONS, tools=[ self.get_all_symbols, self.get_symbols_by_name, diff --git a/src/app/interface/chat.py b/src/app/interface/chat.py index 1f307e8..e52a203 100644 --- a/src/app/interface/chat.py +++ b/src/app/interface/chat.py @@ -1,7 +1,9 @@ import os import json +from typing import Any, Callable import gradio as gr -from app.agents.pipeline import Pipeline, PipelineInputs +from app.agents.action_registry import get_user_friendly_action +from app.agents.pipeline import Pipeline, PipelineEvent, PipelineInputs class ChatManager: @@ -49,13 +51,28 @@ class ChatManager: ######################################## # Funzioni Gradio ######################################## - def gradio_respond(self, message: str, history: list[tuple[str, str]]) -> str: + async def gradio_respond(self, message: str, history: list[tuple[str, str]]): + """ + Versione asincrona in streaming. + Produce (yield) aggiornamenti di stato e la risposta finale. + """ self.inputs.user_query = message pipeline = Pipeline(self.inputs) - response = pipeline.interact() + listeners: list[tuple[PipelineEvent, Callable[[Any], str | None]]] = [ # type: ignore + (PipelineEvent.QUERY_CHECK, lambda _: "🔍 Sto controllando la tua richiesta..."), + (PipelineEvent.INFO_RECOVERY, lambda _: "📊 Sto recuperando i dati (mercato, news, social)..."), + (PipelineEvent.REPORT_GENERATION, lambda _: "✍️ Sto scrivendo il report finale..."), + (PipelineEvent.TOOL_USED, lambda e: get_user_friendly_action(e.tool.tool_name)) + ] - self.history.append((message, response)) - return response + response = None + async for chunk in pipeline.interact_stream(listeners=listeners): + response = chunk # Salva l'ultimo chunk (che sarà la risposta finale) + yield response # Restituisce l'aggiornamento (o la risposta finale) a Gradio + + # Dopo che il generatore è completo, salva l'ultima risposta nello storico + if response: + self.history.append((message, response)) def gradio_save(self) -> str: self.save_chat("chat.json") @@ -72,7 +89,7 @@ class ChatManager: def gradio_build_interface(self) -> gr.Blocks: - with gr.Blocks() as interface: + with gr.Blocks(fill_height=True, fill_width=True) as interface: gr.Markdown("# 🤖 Agente di Analisi e Consulenza Crypto (Chat)") # --- Prepara le etichette di default per i dropdown diff --git a/src/app/interface/telegram.py b/src/app/interface/telegram.py index b720c43..3edc810 100644 --- a/src/app/interface/telegram.py +++ b/src/app/interface/telegram.py @@ -271,7 +271,7 @@ class TelegramApp: await bot.delete_message(chat_id=chat_id, message_id=update.message.id) def update_user(update_step: str = "") -> None: - if update_step: run_message.update_step(update_step) + if update_step: run_message.update_step_with_tool(update_step) else: run_message.update() message = run_message.get_latest() @@ -280,11 +280,11 @@ class TelegramApp: await bot.send_chat_action(chat_id=chat_id, action=ChatAction.TYPING) pipeline = Pipeline(inputs) - report_content = await pipeline.interact_async(listeners=[ - (PipelineEvent.QUERY_CHECK, lambda _: update_user()), - (PipelineEvent.TOOL_USED, lambda e: update_user(e.tool.tool_name.replace('get_', '').replace("_", "\\_"))), - (PipelineEvent.INFO_RECOVERY, lambda _: update_user()), - (PipelineEvent.REPORT_GENERATION, lambda _: update_user()), + report_content = await pipeline.interact(listeners=[ + (PipelineEvent.QUERY_CHECK_END, lambda _: update_user()), + (PipelineEvent.TOOL_USED_END, lambda e: update_user(e.tool.tool_name.replace('get_', '').replace("_", "\\_"))), + (PipelineEvent.INFO_RECOVERY_END, lambda _: update_user()), + (PipelineEvent.REPORT_GENERATION_END, lambda _: update_user()), ]) # attach report file to the message