Telegram bot support (#23)

* Aggiungi supporto per il bot Telegram: aggiorna .env.example, pyproject.toml e uv.lock

* demo per bot Telegram con gestione comandi e inline keyboard

* Implementazione del bot Telegram con gestione dei comandi e stati di conversazione iniziali

* Aggiorna la gestione delle configurazioni nel bot Telegram: modifica gli stati della conversazione e aggiungi il supporto per la gestione dei messaggi.

* fix static models & readme

* aggiunto il supporto per la query dell'utente e modificata la visualizzazione dei messaggi di stato.

* Aggiunto il supporto per la gestione del bot Telegram e aggiornata la configurazione del pipeline

* Aggiornato .gitignore per includere la cartella .gradio e rimosso chroma_db. Aggiunto il supporto per la generazione di report in PDF utilizzando markdown-pdf nel bot Telegram.

* Refactor pipeline and chat manager for improved structure and functionality

* Better logging

* Aggiornato il comportamento del logging per i logger di agno. Aggiunto il supporto per l'opzione check_for_async nella configurazione di RedditWrapper.

* Rimosso codice commentato e import non utilizzati nella classe Pipeline per semplificare la struttura

* Aggiornata la sezione "Applicazione" nel README & fix main

* Telegram instance instead of static

* Fix logging to use labels for team model, leader model, and strategy

* Rinomina il lock da _lock a __lock per garantire l'incapsulamento nella classe AppConfig

* Rinomina i logger per una migliore identificazione e gestisce le eccezioni nel bot di Telegram

* Aggiorna i messaggi di errore nel gestore Telegram per una migliore chiarezza e modifica il commento nel file di configurazione per riflettere lo stato del modello.

* Aggiungi un messaggio di attesa durante la generazione del report nel bot di Telegram
This commit was merged in pull request #23.
This commit is contained in:
Giacomo Bertolazzi
2025-10-13 10:49:46 +02:00
committed by GitHub
parent 45a17d4570
commit c96617a039
15 changed files with 541 additions and 149 deletions

View File

@@ -1,10 +1,10 @@
from agno.run.agent import RunOutput
import logging
from app.agents.team import create_team_with
from app.agents.predictor import PredictorInput, PredictorOutput
from app.agents.prompts import *
from app.api.core.markets import ProductInfo
from app.configs import AppConfig
logging = logging.getLogger("pipeline")
class Pipeline:
"""
@@ -17,27 +17,30 @@ class Pipeline:
self.configs = configs
# Stato iniziale
self.choose_strategy(0)
self.choose_predictor(0)
self.leader_model = self.configs.get_model_by_name(self.configs.agents.team_leader_model)
self.team_model = self.configs.get_model_by_name(self.configs.agents.team_model)
self.strategy = self.configs.get_strategy_by_name(self.configs.agents.strategy)
# ======================
# Dropdown handlers
# ======================
def choose_predictor(self, index: int):
def choose_leader(self, index: int):
"""
Sceglie il modello LLM da usare per il Predictor.
Sceglie il modello LLM da usare per il Team.
"""
model = self.configs.models.all_models[index]
self.predictor = model.get_agent(
PREDICTOR_INSTRUCTIONS,
output_schema=PredictorOutput,
)
self.leader_model = self.configs.models.all_models[index]
def choose_team(self, index: int):
"""
Sceglie il modello LLM da usare per il Team.
"""
self.team_model = self.configs.models.all_models[index]
def choose_strategy(self, index: int):
"""
Sceglie la strategia da usare per il Predictor.
"""
self.strat = self.configs.strategies[index].description
self.strategy = self.configs.strategies[index]
# ======================
# Helpers
@@ -64,46 +67,18 @@ class Pipeline:
3. Invoca Predictor
4. Restituisce la strategia finale
"""
# Step 1: raccolta output dai membri del Team
team_model = self.configs.get_model_by_name(self.configs.agents.team_model)
leader_model = self.configs.get_model_by_name(self.configs.agents.team_leader_model)
# Step 1: Creazione Team
team = create_team_with(self.configs, self.team_model, self.leader_model)
team = create_team_with(self.configs, team_model, leader_model)
# Step 2: raccolta output dai membri del Team
logging.info(f"Pipeline received query: {query}")
# TODO migliorare prompt (?)
query = f"The user query is: {query}\n\n They requested a {self.strategy.label} investment strategy."
team_outputs = team.run(query) # type: ignore
# Step 2: aggregazione output strutturati
all_products: list[ProductInfo] = []
sentiments: list[str] = []
for agent_output in team_outputs.member_responses:
if isinstance(agent_output, RunOutput) and agent_output.metadata is not None:
keys = agent_output.metadata.keys()
if "products" in keys:
all_products.extend(agent_output.metadata["products"])
if "sentiment_news" in keys:
sentiments.append(agent_output.metadata["sentiment_news"])
if "sentiment_social" in keys:
sentiments.append(agent_output.metadata["sentiment_social"])
aggregated_sentiment = "\n".join(sentiments)
# Step 3: invocazione Predictor
predictor_input = PredictorInput(
data=all_products,
style=self.strat,
sentiment=aggregated_sentiment
)
result = self.predictor.run(predictor_input) # type: ignore
if not isinstance(result.content, PredictorOutput):
return "❌ Errore: il modello non ha restituito un output valido."
prediction: PredictorOutput = result.content
# Step 4: restituzione strategia finale
portfolio_lines = "\n".join(
[f"{item.asset} ({item.percentage}%): {item.motivation}" for item in prediction.portfolio]
)
return (
f"📊 Strategia ({self.strat}): {prediction.strategy}\n\n"
f"💼 Portafoglio consigliato:\n{portfolio_lines}"
)
# Step 3: recupero ouput
if not isinstance(team_outputs.content, str):
logging.error(f"Team output is not a string: {team_outputs.content}")
raise ValueError("Team output is not a string")
logging.info(f"Team finished")
return team_outputs.content