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/
This commit is contained in:
2025-10-04 21:20:21 +02:00
parent 6cd97b2864
commit f0193b94bb
26 changed files with 41 additions and 59 deletions

View File

@@ -1,4 +0,0 @@
from app.chat_manager import ChatManager
from app.pipeline import Pipeline
__all__ = ["ChatManager", "Pipeline"]

View File

@@ -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?")

View File

@@ -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"]

View File

@@ -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:

View File

@@ -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):

0
src/app/base/__init__.py Normal file
View File

View File

@@ -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

View File

@@ -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:

View File

@@ -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:

View File

@@ -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:

View File

@@ -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:

View File

@@ -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

View File

@@ -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):

View File

@@ -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:

View File

@@ -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:

View File

@@ -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:

View File

@@ -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"]

View File

@@ -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

View File

@@ -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"]

View File

@@ -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/<filename>.
"""
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/<filename>.
"""
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()

View File

@@ -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]:

View File

@@ -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]

View File

@@ -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