From f0193b94bb85991d89cd8bd4cbb8e9ad40079c47 Mon Sep 17 00:00:00 2001 From: Berack96 Date: Sat, 4 Oct 2025 21:20:21 +0200 Subject: [PATCH] Refactoring architetturale e spostamento classi base - Eliminazione del file __init__.py obsoleto che importava ChatManager e Pipeline - Spostamento della classe Pipeline in agents/pipeline.py - Spostamento della classe ChatManager in utils/chat_manager.py - Aggiornamento di __main__.py per importare da app.utils e app.agents, e modifica della logica per utilizzare Pipeline invece di chat per la selezione di provider e stile - Creazione della cartella base con classi base comuni: markets.py (ProductInfo, Price, MarketWrapper), news.py (Article, NewsWrapper), social.py (SocialPost, SocialComment, SocialWrapper) - Aggiornamento di tutti gli import nel progetto (markets/, news/, social/, utils/, tests/) per utilizzare la nuova struttura base/ --- src/app/__init__.py | 4 --- src/app/__main__.py | 18 ++++++---- src/app/agents/__init__.py | 3 +- src/app/{ => agents}/pipeline.py | 4 +-- src/app/agents/predictor.py | 2 +- src/app/base/__init__.py | 0 src/app/{markets/base.py => base/markets.py} | 0 src/app/{news/base.py => base/news.py} | 0 src/app/{social/base.py => base/social.py} | 0 src/app/markets/__init__.py | 2 +- src/app/markets/binance.py | 2 +- src/app/markets/coinbase.py | 2 +- src/app/markets/cryptocompare.py | 2 +- src/app/markets/yfinance.py | 2 +- src/app/news/__init__.py | 2 +- src/app/news/cryptopanic_api.py | 2 +- src/app/news/duckduckgo.py | 2 +- src/app/news/googlenews.py | 2 +- src/app/news/news_api.py | 2 +- src/app/social/__init__.py | 2 +- src/app/social/reddit.py | 2 +- src/app/utils/__init__.py | 3 +- src/app/{ => utils}/chat_manager.py | 36 +++++--------------- src/app/utils/market_aggregation.py | 2 +- tests/agents/test_predictor.py | 2 +- tests/utils/test_market_aggregator.py | 2 +- 26 files changed, 41 insertions(+), 59 deletions(-) delete mode 100644 src/app/__init__.py rename src/app/{ => agents}/pipeline.py (97%) create mode 100644 src/app/base/__init__.py rename src/app/{markets/base.py => base/markets.py} (100%) rename src/app/{news/base.py => base/news.py} (100%) rename src/app/{social/base.py => base/social.py} (100%) rename src/app/{ => utils}/chat_manager.py (60%) diff --git a/src/app/__init__.py b/src/app/__init__.py deleted file mode 100644 index e7f5a3d..0000000 --- a/src/app/__init__.py +++ /dev/null @@ -1,4 +0,0 @@ -from app.chat_manager import ChatManager -from app.pipeline import Pipeline - -__all__ = ["ChatManager", "Pipeline"] diff --git a/src/app/__main__.py b/src/app/__main__.py index 5599a1f..578ef35 100644 --- a/src/app/__main__.py +++ b/src/app/__main__.py @@ -1,19 +1,23 @@ import gradio as gr -from agno.utils.log import log_info #type: ignore from dotenv import load_dotenv -from app import ChatManager +from agno.utils.log import log_info #type: ignore +from app.utils import ChatManager +from app.agents import Pipeline if __name__ == "__main__": # Inizializzazioni load_dotenv() + pipeline = Pipeline() chat = ChatManager() ######################################## # Funzioni Gradio ######################################## def respond(message: str, history: list[dict[str, str]]) -> tuple[list[dict[str, str]], list[dict[str, str]], str]: - response = chat.send_message(message) + chat.send_message(message) + response = pipeline.interact(message) + chat.receive_message(response) history.append({"role": "user", "content": message}) history.append({"role": "assistant", "content": response}) return history, history, "" @@ -42,18 +46,18 @@ if __name__ == "__main__": # Dropdown provider e stile with gr.Row(): provider = gr.Dropdown( - choices=chat.list_providers(), + choices=pipeline.list_providers(), type="index", label="Modello da usare" ) - provider.change(fn=chat.choose_provider, inputs=provider, outputs=None) + provider.change(fn=pipeline.choose_predictor, inputs=provider, outputs=None) style = gr.Dropdown( - choices=chat.list_styles(), + choices=pipeline.list_styles(), type="index", label="Stile di investimento" ) - style.change(fn=chat.choose_style, inputs=style, outputs=None) + style.change(fn=pipeline.choose_style, inputs=style, outputs=None) chatbot = gr.Chatbot(label="Conversazione", height=500, type="messages") msg = gr.Textbox(label="Scrivi la tua richiesta", placeholder="Es: Quali sono le crypto interessanti oggi?") diff --git a/src/app/agents/__init__.py b/src/app/agents/__init__.py index 3dcbf9f..a9ec99e 100644 --- a/src/app/agents/__init__.py +++ b/src/app/agents/__init__.py @@ -1,5 +1,6 @@ from app.agents.models import AppModels from app.agents.predictor import PredictorInput, PredictorOutput, PredictorStyle, PREDICTOR_INSTRUCTIONS from app.agents.team import create_team_with +from app.agents.pipeline import Pipeline -__all__ = ["AppModels", "PredictorInput", "PredictorOutput", "PredictorStyle", "PREDICTOR_INSTRUCTIONS", "create_team_with"] +__all__ = ["AppModels", "PredictorInput", "PredictorOutput", "PredictorStyle", "PREDICTOR_INSTRUCTIONS", "create_team_with", "Pipeline"] diff --git a/src/app/pipeline.py b/src/app/agents/pipeline.py similarity index 97% rename from src/app/pipeline.py rename to src/app/agents/pipeline.py index 58819ca..2123296 100644 --- a/src/app/pipeline.py +++ b/src/app/agents/pipeline.py @@ -1,8 +1,8 @@ from agno.run.agent import RunOutput -from app.agents import AppModels +from app.agents.models import AppModels from app.agents.team import create_team_with from app.agents.predictor import PREDICTOR_INSTRUCTIONS, PredictorInput, PredictorOutput, PredictorStyle -from app.markets.base import ProductInfo +from app.base.markets import ProductInfo class Pipeline: diff --git a/src/app/agents/predictor.py b/src/app/agents/predictor.py index c3e3e0a..69a92af 100644 --- a/src/app/agents/predictor.py +++ b/src/app/agents/predictor.py @@ -1,6 +1,6 @@ from enum import Enum from pydantic import BaseModel, Field -from app.markets.base import ProductInfo +from app.base.markets import ProductInfo class PredictorStyle(Enum): diff --git a/src/app/base/__init__.py b/src/app/base/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/app/markets/base.py b/src/app/base/markets.py similarity index 100% rename from src/app/markets/base.py rename to src/app/base/markets.py diff --git a/src/app/news/base.py b/src/app/base/news.py similarity index 100% rename from src/app/news/base.py rename to src/app/base/news.py diff --git a/src/app/social/base.py b/src/app/base/social.py similarity index 100% rename from src/app/social/base.py rename to src/app/base/social.py diff --git a/src/app/markets/__init__.py b/src/app/markets/__init__.py index c6c1e0e..bf2d344 100644 --- a/src/app/markets/__init__.py +++ b/src/app/markets/__init__.py @@ -1,5 +1,5 @@ from agno.tools import Toolkit -from app.markets.base import MarketWrapper, Price, ProductInfo +from app.base.markets import MarketWrapper, Price, ProductInfo from app.markets.binance import BinanceWrapper from app.markets.coinbase import CoinBaseWrapper from app.markets.cryptocompare import CryptoCompareWrapper diff --git a/src/app/markets/binance.py b/src/app/markets/binance.py index 863094a..e72727b 100644 --- a/src/app/markets/binance.py +++ b/src/app/markets/binance.py @@ -1,7 +1,7 @@ import os from typing import Any from binance.client import Client # type: ignore -from app.markets.base import ProductInfo, MarketWrapper, Price +from app.base.markets import ProductInfo, MarketWrapper, Price def extract_product(currency: str, ticker_data: dict[str, Any]) -> ProductInfo: diff --git a/src/app/markets/coinbase.py b/src/app/markets/coinbase.py index 9e3f796..d525d28 100644 --- a/src/app/markets/coinbase.py +++ b/src/app/markets/coinbase.py @@ -3,7 +3,7 @@ from enum import Enum from datetime import datetime, timedelta from coinbase.rest import RESTClient # type: ignore from coinbase.rest.types.product_types import Candle, GetProductResponse, Product # type: ignore -from app.markets.base import ProductInfo, MarketWrapper, Price +from app.base.markets import ProductInfo, MarketWrapper, Price def extract_product(product_data: GetProductResponse | Product) -> ProductInfo: diff --git a/src/app/markets/cryptocompare.py b/src/app/markets/cryptocompare.py index 8c128a5..e3f67d6 100644 --- a/src/app/markets/cryptocompare.py +++ b/src/app/markets/cryptocompare.py @@ -1,7 +1,7 @@ import os from typing import Any import requests -from app.markets.base import ProductInfo, MarketWrapper, Price +from app.base.markets import ProductInfo, MarketWrapper, Price def extract_product(asset_data: dict[str, Any]) -> ProductInfo: diff --git a/src/app/markets/yfinance.py b/src/app/markets/yfinance.py index 03daded..a454ad6 100644 --- a/src/app/markets/yfinance.py +++ b/src/app/markets/yfinance.py @@ -1,6 +1,6 @@ import json from agno.tools.yfinance import YFinanceTools -from app.markets.base import MarketWrapper, ProductInfo, Price +from app.base.markets import MarketWrapper, ProductInfo, Price def extract_product(stock_data: dict[str, str]) -> ProductInfo: diff --git a/src/app/news/__init__.py b/src/app/news/__init__.py index f45eff4..b0cb553 100644 --- a/src/app/news/__init__.py +++ b/src/app/news/__init__.py @@ -1,6 +1,6 @@ from agno.tools import Toolkit from app.utils import WrapperHandler -from app.news.base import NewsWrapper, Article +from app.base.news import NewsWrapper, Article from app.news.news_api import NewsApiWrapper from app.news.googlenews import GoogleNewsWrapper from app.news.cryptopanic_api import CryptoPanicWrapper diff --git a/src/app/news/cryptopanic_api.py b/src/app/news/cryptopanic_api.py index eda854b..1e16078 100644 --- a/src/app/news/cryptopanic_api.py +++ b/src/app/news/cryptopanic_api.py @@ -2,7 +2,7 @@ import os from typing import Any import requests from enum import Enum -from app.news.base import NewsWrapper, Article +from app.base.news import NewsWrapper, Article class CryptoPanicFilter(Enum): diff --git a/src/app/news/duckduckgo.py b/src/app/news/duckduckgo.py index b7e07a6..8108239 100644 --- a/src/app/news/duckduckgo.py +++ b/src/app/news/duckduckgo.py @@ -1,7 +1,7 @@ import json from typing import Any from agno.tools.duckduckgo import DuckDuckGoTools -from app.news.base import Article, NewsWrapper +from app.base.news import Article, NewsWrapper def extract_article(result: dict[str, Any]) -> Article: diff --git a/src/app/news/googlenews.py b/src/app/news/googlenews.py index a1b9414..0041c7f 100644 --- a/src/app/news/googlenews.py +++ b/src/app/news/googlenews.py @@ -1,6 +1,6 @@ from typing import Any from gnews import GNews # type: ignore -from app.news.base import Article, NewsWrapper +from app.base.news import Article, NewsWrapper def extract_article(result: dict[str, Any]) -> Article: diff --git a/src/app/news/news_api.py b/src/app/news/news_api.py index eb88a13..b5bf375 100644 --- a/src/app/news/news_api.py +++ b/src/app/news/news_api.py @@ -1,7 +1,7 @@ import os from typing import Any import newsapi # type: ignore -from app.news.base import Article, NewsWrapper +from app.base.news import Article, NewsWrapper def extract_article(result: dict[str, Any]) -> Article: diff --git a/src/app/social/__init__.py b/src/app/social/__init__.py index ee6b87d..261bcba 100644 --- a/src/app/social/__init__.py +++ b/src/app/social/__init__.py @@ -1,6 +1,6 @@ from agno.tools import Toolkit from app.utils import WrapperHandler -from app.social.base import SocialPost, SocialWrapper +from app.base.social import SocialPost, SocialWrapper from app.social.reddit import RedditWrapper __all__ = ["SocialAPIsTool", "RedditWrapper", "SocialPost"] diff --git a/src/app/social/reddit.py b/src/app/social/reddit.py index 1219e4e..fa2e0e1 100644 --- a/src/app/social/reddit.py +++ b/src/app/social/reddit.py @@ -1,7 +1,7 @@ import os from praw import Reddit # type: ignore from praw.models import Submission, MoreComments # type: ignore -from app.social.base import SocialWrapper, SocialPost, SocialComment +from app.base.social import SocialWrapper, SocialPost, SocialComment MAX_COMMENTS = 5 diff --git a/src/app/utils/__init__.py b/src/app/utils/__init__.py index 30ed839..1a511c1 100644 --- a/src/app/utils/__init__.py +++ b/src/app/utils/__init__.py @@ -1,4 +1,5 @@ from app.utils.market_aggregation import aggregate_history_prices, aggregate_product_info from app.utils.wrapper_handler import WrapperHandler +from app.utils.chat_manager import ChatManager -__all__ = ["aggregate_history_prices", "aggregate_product_info", "WrapperHandler"] +__all__ = ["aggregate_history_prices", "aggregate_product_info", "WrapperHandler", "ChatManager"] diff --git a/src/app/chat_manager.py b/src/app/utils/chat_manager.py similarity index 60% rename from src/app/chat_manager.py rename to src/app/utils/chat_manager.py index d9a0696..d51819d 100644 --- a/src/app/chat_manager.py +++ b/src/app/utils/chat_manager.py @@ -1,10 +1,5 @@ import json import os -from app import Pipeline - -SAVE_DIR = os.path.join(os.path.dirname(__file__), "..", "saves") -os.makedirs(SAVE_DIR, exist_ok=True) - class ChatManager: """ @@ -15,19 +10,19 @@ class ChatManager: """ def __init__(self): - self.pipeline = Pipeline() self.history: list[dict[str, str]] = [] # [{"role": "user"/"assistant", "content": "..."}] - def send_message(self, message: str) -> str: + def send_message(self, message: str) -> None: """ Aggiunge un messaggio utente, chiama la Pipeline e salva la risposta nello storico. """ # Aggiungi messaggio utente allo storico self.history.append({"role": "user", "content": message}) - # Pipeline elabora la query - response = self.pipeline.interact(message) - + def receive_message(self, response: str) -> str: + """ + Riceve un messaggio dalla pipeline e lo aggiunge allo storico. + """ # Aggiungi risposta assistente allo storico self.history.append({"role": "assistant", "content": response}) @@ -37,19 +32,17 @@ class ChatManager: """ Salva la chat corrente in src/saves/. """ - path = os.path.join(SAVE_DIR, filename) - with open(path, "w", encoding="utf-8") as f: + with open(filename, "w", encoding="utf-8") as f: json.dump(self.history, f, ensure_ascii=False, indent=2) def load_chat(self, filename: str = "chat.json") -> None: """ Carica una chat salvata da src/saves/. """ - path = os.path.join(SAVE_DIR, filename) - if not os.path.exists(path): + if not os.path.exists(filename): self.history = [] return - with open(path, "r", encoding="utf-8") as f: + with open(filename, "r", encoding="utf-8") as f: self.history = json.load(f) def reset_chat(self) -> None: @@ -63,16 +56,3 @@ class ChatManager: Restituisce lo storico completo della chat. """ return self.history - - # Facciamo pass-through di provider e style, così Gradio può usarli - def choose_provider(self, index: int): - self.pipeline.choose_predictor(index) - - def choose_style(self, index: int): - self.pipeline.choose_style(index) - - def list_providers(self) -> list[str]: - return self.pipeline.list_providers() - - def list_styles(self) -> list[str]: - return self.pipeline.list_styles() diff --git a/src/app/utils/market_aggregation.py b/src/app/utils/market_aggregation.py index 533f4a7..0fb56f0 100644 --- a/src/app/utils/market_aggregation.py +++ b/src/app/utils/market_aggregation.py @@ -1,5 +1,5 @@ import statistics -from app.markets.base import ProductInfo, Price +from app.base.markets import ProductInfo, Price def aggregate_history_prices(prices: dict[str, list[Price]]) -> list[Price]: diff --git a/tests/agents/test_predictor.py b/tests/agents/test_predictor.py index 62ef1b1..feedb82 100644 --- a/tests/agents/test_predictor.py +++ b/tests/agents/test_predictor.py @@ -1,7 +1,7 @@ import pytest from app.agents import AppModels from app.agents.predictor import PREDICTOR_INSTRUCTIONS, PredictorInput, PredictorOutput, PredictorStyle -from app.markets.base import ProductInfo +from app.base.markets import ProductInfo def unified_checks(model: AppModels, input): llm = model.get_agent(PREDICTOR_INSTRUCTIONS, output=PredictorOutput) # type: ignore[arg-type] diff --git a/tests/utils/test_market_aggregator.py b/tests/utils/test_market_aggregator.py index d7881ef..64d701c 100644 --- a/tests/utils/test_market_aggregator.py +++ b/tests/utils/test_market_aggregator.py @@ -1,5 +1,5 @@ import pytest -from app.markets.base import ProductInfo, Price +from app.base.markets import ProductInfo, Price from app.utils.market_aggregation import aggregate_history_prices, aggregate_product_info