9 enhancement con financialdatasettool e yfinance #11
@@ -16,7 +16,7 @@ dependencies = [
|
|||||||
"dotenv",
|
"dotenv",
|
||||||
# 🟡 per fare scraping di pagine web
|
# 🟡 per fare scraping di pagine web
|
||||||
#"bs4",
|
#"bs4",
|
||||||
# ✅ per fare una UI web semplice con input e output
|
# ✅ per fare una UI web semplice con user_input e output
|
||||||
"gradio",
|
"gradio",
|
||||||
|
|
||||||
# ✅ per costruire agenti (ovvero modelli che possono fare più cose tramite tool) https://github.com/agno-agi/agno
|
# ✅ 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 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
|
# 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
|
# Non so se può essere utile, per ora lo lascio qui
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
from enum import Enum
|
from enum import Enum
|
||||||
from app.markets.base import ProductInfo
|
from src.app.markets.base import ProductInfo
|
||||||
from pydantic import BaseModel, Field
|
from pydantic import BaseModel, Field
|
||||||
|
|
||||||
class PredictorStyle(Enum):
|
class PredictorStyle(Enum):
|
||||||
@@ -23,7 +23,7 @@ class PredictorOutput(BaseModel):
|
|||||||
PREDICTOR_INSTRUCTIONS = """
|
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.
|
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)
|
## Processing Instructions (Absolute Rule)
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
from app.markets.base import BaseWrapper
|
from src.app.markets.base import BaseWrapper
|
||||||
from app.markets.coinbase import CoinBaseWrapper
|
from src.app.markets.coinbase import CoinBaseWrapper
|
||||||
from app.markets.cryptocompare import CryptoCompareWrapper
|
from src.app.markets.cryptocompare import CryptoCompareWrapper
|
||||||
|
|
||||||
from agno.utils.log import log_warning
|
from agno.utils.log import log_warning
|
||||||
|
|
||||||
@@ -30,8 +30,8 @@ class MarketAPIs(BaseWrapper):
|
|||||||
for wrapper in wrapper_builders:
|
for wrapper in wrapper_builders:
|
||||||
try:
|
try:
|
||||||
result.append(wrapper(currency=currency))
|
result.append(wrapper(currency=currency))
|
||||||
except Exception as _:
|
except Exception as e:
|
||||||
log_warning(f"{wrapper} cannot be initialized, maybe missing API key?")
|
log_warning(f"{wrapper} cannot be initialized: {e}")
|
||||||
|
|
||||||
assert result, "No market API keys set in environment variables."
|
assert result, "No market API keys set in environment variables."
|
||||||
return result
|
return result
|
||||||
@@ -39,7 +39,9 @@ class MarketAPIs(BaseWrapper):
|
|||||||
def __init__(self, currency: str = "USD"):
|
def __init__(self, currency: str = "USD"):
|
||||||
"""
|
"""
|
||||||
Inizializza la classe con la valuta di riferimento e la priorità dei provider.
|
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.currency = currency
|
||||||
self.wrappers = MarketAPIs.get_list_available_market_apis(currency=currency)
|
self.wrappers = MarketAPIs.get_list_available_market_apis(currency=currency)
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import os
|
import os
|
||||||
from coinbase.rest import RESTClient
|
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):
|
class CoinBaseWrapper(BaseWrapper):
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import os
|
import os
|
||||||
import requests
|
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"
|
BASE_URL = "https://min-api.cryptocompare.com"
|
||||||
|
|
||||||
@@ -8,7 +8,7 @@ class CryptoCompareWrapper(BaseWrapper):
|
|||||||
"""
|
"""
|
||||||
Wrapper per le API pubbliche di CryptoCompare.
|
Wrapper per le API pubbliche di CryptoCompare.
|
||||||
La documentazione delle API è disponibile qui: https://developers.coindesk.com/documentation/legacy/Price/SingleSymbolPriceEndpoint
|
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'):
|
def __init__(self, api_key:str = None, currency:str='USD'):
|
||||||
if api_key is None:
|
if api_key is None:
|
||||||
|
|||||||
@@ -41,6 +41,7 @@ class AppModels(Enum):
|
|||||||
availables.append(AppModels.OLLAMA_QWEN)
|
availables.append(AppModels.OLLAMA_QWEN)
|
||||||
return availables
|
return availables
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
def availables_online() -> list['AppModels']:
|
def availables_online() -> list['AppModels']:
|
||||||
"""
|
"""
|
||||||
Controlla quali provider di modelli LLM online hanno le loro API keys disponibili
|
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"):
|
if not os.getenv("GOOGLE_API_KEY"):
|
||||||
log_warning("No GOOGLE_API_KEY set in environment variables.")
|
log_warning("No GOOGLE_API_KEY set in environment variables.")
|
||||||
return []
|
return []
|
||||||
availables = []
|
availables = [AppModels.GEMINI, AppModels.GEMINI_PRO]
|
||||||
availables.append(AppModels.GEMINI)
|
|
||||||
availables.append(AppModels.GEMINI_PRO)
|
|
||||||
return availables
|
return availables
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
@@ -75,9 +74,13 @@ class AppModels(Enum):
|
|||||||
def extract_json_str_from_response(response: str) -> str:
|
def extract_json_str_from_response(response: str) -> str:
|
||||||
"""
|
"""
|
||||||
Estrae il JSON dalla risposta del modello.
|
Estrae il JSON dalla risposta del modello.
|
||||||
response: risposta del modello (stringa).
|
Args:
|
||||||
Ritorna la parte JSON della risposta come stringa.
|
response: risposta del modello (stringa).
|
||||||
Se non viene trovato nessun JSON, ritorna una stringa vuota.
|
|
||||||
|
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
|
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
|
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.
|
'{' 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:
|
def get_model(self, instructions:str) -> Model:
|
||||||
"""
|
"""
|
||||||
Restituisce un'istanza del modello specificato.
|
Restituisce un'istanza del modello specificato.
|
||||||
instructions: istruzioni da passare al modello (system prompt).
|
|
||||||
Ritorna un'istanza di BaseModel o una sua sottoclasse.
|
Args:
|
||||||
Raise ValueError se il modello non è supportato.
|
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
|
name = self.value
|
||||||
if self in {AppModels.GEMINI, AppModels.GEMINI_PRO}:
|
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:
|
def get_agent(self, instructions: str, name: str = "", output: BaseModel | None = None) -> Agent:
|
||||||
"""
|
"""
|
||||||
Costruisce un agente con il modello e le istruzioni specificate.
|
Costruisce un agente con il modello e le istruzioni specificate.
|
||||||
instructions: istruzioni da passare al modello (system prompt).
|
Args:
|
||||||
Ritorna un'istanza di Agent.
|
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(
|
return Agent(
|
||||||
model=self.get_model(instructions),
|
model=self.get_model(instructions),
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
from app.agents.news_agent import NewsAgent
|
from src.app.agents.news_agent import NewsAgent
|
||||||
from app.agents.social_agent import SocialAgent
|
from src.app.agents.social_agent import SocialAgent
|
||||||
from app.agents.predictor import PredictorStyle, PredictorInput, PredictorOutput, PREDICTOR_INSTRUCTIONS
|
from src.app.agents.predictor import PredictorStyle, PredictorInput, PredictorOutput, PREDICTOR_INSTRUCTIONS
|
||||||
from app.markets import MarketAPIs
|
from src.app.markets import MarketAPIs
|
||||||
from app.models import AppModels
|
from src.app.models import AppModels
|
||||||
from agno.utils.log import log_info
|
from agno.utils.log import log_info
|
||||||
|
|
||||||
class ToolAgent:
|
class ToolAgent:
|
||||||
@@ -14,6 +14,10 @@ class ToolAgent:
|
|||||||
"""
|
"""
|
||||||
Inizializza l'agente con i modelli disponibili, gli stili e l'API di mercato.
|
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.available_models = AppModels.availables()
|
||||||
self.all_styles = list(PredictorStyle)
|
self.all_styles = list(PredictorStyle)
|
||||||
self.style = self.all_styles[0] # Default to the first style
|
self.style = self.all_styles[0] # Default to the first style
|
||||||
@@ -24,7 +28,9 @@ class ToolAgent:
|
|||||||
def choose_provider(self, index: int):
|
def choose_provider(self, index: int):
|
||||||
"""
|
"""
|
||||||
Sceglie il modello LLM da utilizzare in base all'indice fornito.
|
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 Utilizzare AGNO per gestire i modelli... è molto più semplice e permette di cambiare modello facilmente
|
||||||
# TODO https://docs.agno.com/introduction
|
# TODO https://docs.agno.com/introduction
|
||||||
@@ -37,15 +43,18 @@ class ToolAgent:
|
|||||||
def choose_style(self, index: int):
|
def choose_style(self, index: int):
|
||||||
"""
|
"""
|
||||||
Sceglie lo stile di previsione da utilizzare in base all'indice fornito.
|
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]
|
self.style = self.all_styles[index]
|
||||||
|
|
||||||
def interact(self, query: str) -> str:
|
def interact(self, query: str) -> str:
|
||||||
"""
|
"""
|
||||||
Funzione principale che coordina gli agenti per rispondere alla richiesta dell'utente.
|
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('"', "'")}\"]")
|
log_info(f"[model={self.chosen_model.name}] [style={self.style.name}] [query=\"{query.replace('"', "'")}\"]")
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import statistics
|
import statistics
|
||||||
from typing import Dict, List, Any
|
from typing import Dict, Any
|
||||||
|
|
||||||
class MarketAggregator:
|
class MarketAggregator:
|
||||||
"""
|
"""
|
||||||
@@ -65,6 +65,7 @@ class MarketAggregator:
|
|||||||
return float(v[:-1]) * 1_000
|
return float(v[:-1]) * 1_000
|
||||||
try:
|
try:
|
||||||
return float(v)
|
return float(v)
|
||||||
except Exception:
|
except Exception as e:
|
||||||
|
print(f"Errore nel parsing del volume: {e}")
|
||||||
return 0.0
|
return 0.0
|
||||||
return 0.0
|
return 0.0
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import os
|
import os
|
||||||
import pytest
|
import pytest
|
||||||
from app.agents.market import MarketToolkit
|
from src.app.agents.market import MarketToolkit
|
||||||
from app.markets.base import BaseWrapper
|
from src.app.markets.base import BaseWrapper
|
||||||
from app.markets.coinbase import CoinBaseWrapper
|
from src.app.markets.coinbase import CoinBaseWrapper
|
||||||
from app.markets.cryptocompare import CryptoCompareWrapper
|
from src.app.markets.cryptocompare import CryptoCompareWrapper
|
||||||
from app.markets import MarketAPIs
|
from src.app.markets import MarketAPIs
|
||||||
|
|
||||||
class TestMarketSystem:
|
class TestMarketSystem:
|
||||||
"""Test suite per il sistema di mercato (wrappers + toolkit)"""
|
"""Test suite per il sistema di mercato (wrappers + toolkit)"""
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
import pytest
|
import pytest
|
||||||
from app.agents.predictor import PREDICTOR_INSTRUCTIONS, PredictorInput, PredictorOutput, PredictorStyle
|
from src.app.agents.predictor import PREDICTOR_INSTRUCTIONS, PredictorInput, PredictorOutput, PredictorStyle
|
||||||
from app.markets.base import ProductInfo
|
from src.app.markets.base import ProductInfo
|
||||||
from app.models import AppModels
|
from src.app.models import AppModels
|
||||||
|
|
||||||
def unified_checks(model: AppModels, input):
|
def unified_checks(model: AppModels, user_input):
|
||||||
llm = model.get_agent(PREDICTOR_INSTRUCTIONS, output=PredictorOutput)
|
llm = model.get_agent(PREDICTOR_INSTRUCTIONS, output=PredictorOutput) # type: ignore[arg-type]
|
||||||
result = llm.run(input)
|
result = llm.run(user_input)
|
||||||
content = result.content
|
content = result.content
|
||||||
|
|
||||||
assert isinstance(content, PredictorOutput)
|
assert isinstance(content, PredictorOutput)
|
||||||
|
|||||||
Reference in New Issue
Block a user