Creazione branch tool, refactor degli import e soppressione dei warning

This commit is contained in:
trojanhorse47
2025-09-29 12:22:02 +02:00
parent 4615ebe63e
commit c82f10b32c
11 changed files with 72 additions and 46 deletions

View File

@@ -16,7 +16,7 @@ dependencies = [
"dotenv",
# 🟡 per fare scraping di pagine web
#"bs4",
# ✅ per fare una UI web semplice con input e output
# ✅ per fare una UI web semplice con user_input e output
"gradio",
# ✅ per costruire agenti (ovvero modelli che possono fare più cose tramite tool) https://github.com/agno-agi/agno

View File

@@ -1,5 +1,5 @@
from agno.tools import Toolkit
from app.markets import MarketAPIs
from src.app.markets import MarketAPIs
# TODO (?) in futuro fare in modo che la LLM faccia da sé per il mercato
# Non so se può essere utile, per ora lo lascio qui

View File

@@ -1,5 +1,5 @@
from enum import Enum
from app.markets.base import ProductInfo
from src.app.markets.base import ProductInfo
from pydantic import BaseModel, Field
class PredictorStyle(Enum):
@@ -23,7 +23,7 @@ class PredictorOutput(BaseModel):
PREDICTOR_INSTRUCTIONS = """
You are an **Allocation Algorithm (Crypto-Algo)** specialized in analyzing market data and sentiment to generate an investment strategy and a target portfolio.
Your sole objective is to process the input data and generate the strictly structured output as required by the response format. **You MUST NOT provide introductions, preambles, explanations, conclusions, or any additional comments that are not strictly required.**
Your sole objective is to process the user_input data and generate the strictly structured output as required by the response format. **You MUST NOT provide introductions, preambles, explanations, conclusions, or any additional comments that are not strictly required.**
## Processing Instructions (Absolute Rule)

View File

@@ -1,6 +1,6 @@
from app.markets.base import BaseWrapper
from app.markets.coinbase import CoinBaseWrapper
from app.markets.cryptocompare import CryptoCompareWrapper
from src.app.markets.base import BaseWrapper
from src.app.markets.coinbase import CoinBaseWrapper
from src.app.markets.cryptocompare import CryptoCompareWrapper
from agno.utils.log import log_warning
@@ -30,8 +30,8 @@ class MarketAPIs(BaseWrapper):
for wrapper in wrapper_builders:
try:
result.append(wrapper(currency=currency))
except Exception as _:
log_warning(f"{wrapper} cannot be initialized, maybe missing API key?")
except Exception as e:
log_warning(f"{wrapper} cannot be initialized: {e}")
assert result, "No market API keys set in environment variables."
return result
@@ -39,7 +39,9 @@ class MarketAPIs(BaseWrapper):
def __init__(self, currency: str = "USD"):
"""
Inizializza la classe con la valuta di riferimento e la priorità dei provider.
:param currency: Valuta di riferimento (default "USD")
Args:
currency: Valuta di riferimento (default "USD")
"""
self.currency = currency
self.wrappers = MarketAPIs.get_list_available_market_apis(currency=currency)

View File

@@ -1,6 +1,6 @@
import os
from coinbase.rest import RESTClient
from app.markets.base import ProductInfo, BaseWrapper, Price
from src.app.markets.base import ProductInfo, BaseWrapper, Price
class CoinBaseWrapper(BaseWrapper):
"""

View File

@@ -1,6 +1,6 @@
import os
import requests
from app.markets.base import ProductInfo, BaseWrapper, Price
from src.app.markets.base import ProductInfo, BaseWrapper, Price
BASE_URL = "https://min-api.cryptocompare.com"
@@ -8,7 +8,7 @@ class CryptoCompareWrapper(BaseWrapper):
"""
Wrapper per le API pubbliche di CryptoCompare.
La documentazione delle API è disponibile qui: https://developers.coindesk.com/documentation/legacy/Price/SingleSymbolPriceEndpoint
!!ATTENZIONE!! sembra essere una API legacy e potrebbe essere deprecata in futuro.
!ATTENZIONE! Sembra essere una API legacy e potrebbe essere deprecata in futuro.
"""
def __init__(self, api_key:str = None, currency:str='USD'):
if api_key is None:

View File

@@ -41,6 +41,7 @@ class AppModels(Enum):
availables.append(AppModels.OLLAMA_QWEN)
return availables
@staticmethod
def availables_online() -> list['AppModels']:
"""
Controlla quali provider di modelli LLM online hanno le loro API keys disponibili
@@ -49,9 +50,7 @@ class AppModels(Enum):
if not os.getenv("GOOGLE_API_KEY"):
log_warning("No GOOGLE_API_KEY set in environment variables.")
return []
availables = []
availables.append(AppModels.GEMINI)
availables.append(AppModels.GEMINI_PRO)
availables = [AppModels.GEMINI, AppModels.GEMINI_PRO]
return availables
@staticmethod
@@ -75,9 +74,13 @@ class AppModels(Enum):
def extract_json_str_from_response(response: str) -> str:
"""
Estrae il JSON dalla risposta del modello.
response: risposta del modello (stringa).
Ritorna la parte JSON della risposta come stringa.
Se non viene trovato nessun JSON, ritorna una stringa vuota.
Args:
response: risposta del modello (stringa).
Returns:
La parte JSON della risposta come stringa.
Se non viene trovato nessun JSON, ritorna una stringa vuota.
ATTENZIONE: questa funzione è molto semplice e potrebbe non funzionare
in tutti i casi. Si assume che il JSON sia ben formato e che inizi con
'{' e finisca con '}'. Quindi anche solo un json array farà fallire questa funzione.
@@ -98,9 +101,15 @@ class AppModels(Enum):
def get_model(self, instructions:str) -> Model:
"""
Restituisce un'istanza del modello specificato.
instructions: istruzioni da passare al modello (system prompt).
Ritorna un'istanza di BaseModel o una sua sottoclasse.
Raise ValueError se il modello non è supportato.
Args:
instructions: istruzioni da passare al modello (system prompt).
Returns:
Un'istanza di BaseModel o una sua sottoclasse.
Raise:
ValueError se il modello non è supportato.
"""
name = self.value
if self in {AppModels.GEMINI, AppModels.GEMINI_PRO}:
@@ -113,8 +122,13 @@ class AppModels(Enum):
def get_agent(self, instructions: str, name: str = "", output: BaseModel | None = None) -> Agent:
"""
Costruisce un agente con il modello e le istruzioni specificate.
instructions: istruzioni da passare al modello (system prompt).
Ritorna un'istanza di Agent.
Args:
instructions: istruzioni da passare al modello (system prompt)
name: nome dell'agente (opzionale)
output: schema di output opzionale (Pydantic BaseModel)
Returns:
Un'istanza di Agent.
"""
return Agent(
model=self.get_model(instructions),

View File

@@ -1,8 +1,8 @@
from app.agents.news_agent import NewsAgent
from app.agents.social_agent import SocialAgent
from app.agents.predictor import PredictorStyle, PredictorInput, PredictorOutput, PREDICTOR_INSTRUCTIONS
from app.markets import MarketAPIs
from app.models import AppModels
from src.app.agents.news_agent import NewsAgent
from src.app.agents.social_agent import SocialAgent
from src.app.agents.predictor import PredictorStyle, PredictorInput, PredictorOutput, PREDICTOR_INSTRUCTIONS
from src.app.markets import MarketAPIs
from src.app.models import AppModels
from agno.utils.log import log_info
class ToolAgent:
@@ -14,6 +14,10 @@ class ToolAgent:
"""
Inizializza l'agente con i modelli disponibili, gli stili e l'API di mercato.
"""
self.social_agent = None
self.news_agent = None
self.predictor = None
self.chosen_model = None
self.available_models = AppModels.availables()
self.all_styles = list(PredictorStyle)
self.style = self.all_styles[0] # Default to the first style
@@ -24,7 +28,9 @@ class ToolAgent:
def choose_provider(self, index: int):
"""
Sceglie il modello LLM da utilizzare in base all'indice fornito.
index: indice del modello nella lista available_models.
Args:
index: indice del modello nella lista available_models.
"""
# TODO Utilizzare AGNO per gestire i modelli... è molto più semplice e permette di cambiare modello facilmente
# TODO https://docs.agno.com/introduction
@@ -37,15 +43,18 @@ class ToolAgent:
def choose_style(self, index: int):
"""
Sceglie lo stile di previsione da utilizzare in base all'indice fornito.
index: indice dello stile nella lista all_styles.
Args:
index: indice dello stile nella lista all_styles.
"""
self.style = self.all_styles[index]
def interact(self, query: str) -> str:
"""
Funzione principale che coordina gli agenti per rispondere alla richiesta dell'utente.
query: richiesta dell'utente (es. "Qual è la previsione per Bitcoin?")
style_index: indice dello stile di previsione nella lista all_styles.
Args:
query: richiesta dell'utente (es. "Qual è la previsione per Bitcoin?")
"""
log_info(f"[model={self.chosen_model.name}] [style={self.style.name}] [query=\"{query.replace('"', "'")}\"]")

View File

@@ -1,5 +1,5 @@
import statistics
from typing import Dict, List, Any
from typing import Dict, Any
class MarketAggregator:
"""
@@ -65,6 +65,7 @@ class MarketAggregator:
return float(v[:-1]) * 1_000
try:
return float(v)
except Exception:
except Exception as e:
print(f"Errore nel parsing del volume: {e}")
return 0.0
return 0.0

View File

@@ -1,10 +1,10 @@
import os
import pytest
from app.agents.market import MarketToolkit
from app.markets.base import BaseWrapper
from app.markets.coinbase import CoinBaseWrapper
from app.markets.cryptocompare import CryptoCompareWrapper
from app.markets import MarketAPIs
from src.app.agents.market import MarketToolkit
from src.app.markets.base import BaseWrapper
from src.app.markets.coinbase import CoinBaseWrapper
from src.app.markets.cryptocompare import CryptoCompareWrapper
from src.app.markets import MarketAPIs
class TestMarketSystem:
"""Test suite per il sistema di mercato (wrappers + toolkit)"""

View File

@@ -1,11 +1,11 @@
import pytest
from app.agents.predictor import PREDICTOR_INSTRUCTIONS, PredictorInput, PredictorOutput, PredictorStyle
from app.markets.base import ProductInfo
from app.models import AppModels
from src.app.agents.predictor import PREDICTOR_INSTRUCTIONS, PredictorInput, PredictorOutput, PredictorStyle
from src.app.markets.base import ProductInfo
from src.app.models import AppModels
def unified_checks(model: AppModels, input):
llm = model.get_agent(PREDICTOR_INSTRUCTIONS, output=PredictorOutput)
result = llm.run(input)
def unified_checks(model: AppModels, user_input):
llm = model.get_agent(PREDICTOR_INSTRUCTIONS, output=PredictorOutput) # type: ignore[arg-type]
result = llm.run(user_input)
content = result.content
assert isinstance(content, PredictorOutput)