Creazione branch tool, refactor degli import e soppressione dei warning
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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):
|
||||
"""
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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('"', "'")}\"]")
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)"""
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user