From 02574ca17abfd6c2b0a12a406dce3040ba572492 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 22 Oct 2025 13:57:20 +0000 Subject: [PATCH 1/7] Initial plan -- 2.49.1 From 6e01f1caea67170366b4f33042de513f22755364 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 22 Oct 2025 14:08:37 +0000 Subject: [PATCH 2/7] Implement configurable API providers from configs.yaml Co-authored-by: Berack96 <31776951+Berack96@users.noreply.github.com> --- configs.yaml | 7 +++-- src/app/api/tools/market_tool.py | 42 ++++++++++++++++++++---------- src/app/api/tools/news_tool.py | 44 ++++++++++++++++++++++---------- src/app/api/tools/social_tool.py | 38 ++++++++++++++++++++------- src/app/configs.py | 3 +++ 5 files changed, 94 insertions(+), 40 deletions(-) diff --git a/configs.yaml b/configs.yaml index f83ae9e..1837a30 100644 --- a/configs.yaml +++ b/configs.yaml @@ -32,10 +32,9 @@ models: api: retry_attempts: 3 retry_delay_seconds: 2 - # TODO Magari implementare un sistema per settare i providers - market_providers: [BinanceWrapper, YFinanceWrapper] - news_providers: [GoogleNewsWrapper, DuckDuckGoWrapper] - social_providers: [RedditWrapper] + market_providers: [BinanceWrapper, YFinanceWrapper, CoinBaseWrapper, CryptoCompareWrapper] + news_providers: [GoogleNewsWrapper, DuckDuckGoWrapper, NewsApiWrapper, CryptoPanicWrapper] + social_providers: [RedditWrapper, XWrapper, ChanWrapper] agents: strategy: Conservative diff --git a/src/app/api/tools/market_tool.py b/src/app/api/tools/market_tool.py index 0b92319..e740372 100644 --- a/src/app/api/tools/market_tool.py +++ b/src/app/api/tools/market_tool.py @@ -2,30 +2,46 @@ from agno.tools import Toolkit from app.api.wrapper_handler import WrapperHandler from app.api.core.markets import MarketWrapper, Price, ProductInfo from app.api.markets import BinanceWrapper, CoinBaseWrapper, CryptoCompareWrapper, YFinanceWrapper +from app.configs import AppConfig class MarketAPIsTool(MarketWrapper, Toolkit): """ Class that aggregates multiple market API wrappers and manages them using WrapperHandler. This class supports retrieving product information and historical prices. This class can also aggregate data from multiple sources to provide a more comprehensive view of the market. - The following wrappers are included in this order: - - BinanceWrapper - - YFinanceWrapper - - CoinBaseWrapper - - CryptoCompareWrapper + Providers can be configured in configs.yaml under api.market_providers. """ + # Mapping of wrapper names to wrapper classes + _WRAPPER_MAP = { + 'BinanceWrapper': BinanceWrapper, + 'YFinanceWrapper': YFinanceWrapper, + 'CoinBaseWrapper': CoinBaseWrapper, + 'CryptoCompareWrapper': CryptoCompareWrapper, + } + def __init__(self): """ - Initialize the MarketAPIsTool with multiple market API wrappers. - The following wrappers are included in this order: - - BinanceWrapper - - YFinanceWrapper - - CoinBaseWrapper - - CryptoCompareWrapper + Initialize the MarketAPIsTool with market API wrappers configured in configs.yaml. + The order of wrappers is determined by the api.market_providers list in the configuration. """ - wrappers: list[type[MarketWrapper]] = [BinanceWrapper, YFinanceWrapper, CoinBaseWrapper, CryptoCompareWrapper] - self.handler = WrapperHandler.build_wrappers(wrappers) + config = AppConfig() + + # Get wrapper classes based on configuration + wrappers: list[type[MarketWrapper]] = [] + for provider_name in config.api.market_providers: + if provider_name in self._WRAPPER_MAP: + wrappers.append(self._WRAPPER_MAP[provider_name]) + + # Fallback to all wrappers if none configured + if not wrappers: + wrappers = [BinanceWrapper, YFinanceWrapper, CoinBaseWrapper, CryptoCompareWrapper] + + self.handler = WrapperHandler.build_wrappers( + wrappers, + try_per_wrapper=config.api.retry_attempts, + retry_delay=config.api.retry_delay_seconds + ) Toolkit.__init__( # type: ignore self, diff --git a/src/app/api/tools/news_tool.py b/src/app/api/tools/news_tool.py index ab67f8b..e4733f7 100644 --- a/src/app/api/tools/news_tool.py +++ b/src/app/api/tools/news_tool.py @@ -2,33 +2,49 @@ from agno.tools import Toolkit from app.api.wrapper_handler import WrapperHandler from app.api.core.news import NewsWrapper, Article from app.api.news import NewsApiWrapper, GoogleNewsWrapper, CryptoPanicWrapper, DuckDuckGoWrapper +from app.configs import AppConfig class NewsAPIsTool(NewsWrapper, Toolkit): """ Aggregates multiple news API wrappers and manages them using WrapperHandler. - This class supports retrieving top headlines and latest news articles by querying multiple sources: - - GoogleNewsWrapper - - DuckDuckGoWrapper - - NewsApiWrapper - - CryptoPanicWrapper + This class supports retrieving top headlines and latest news articles by querying multiple sources. + Providers can be configured in configs.yaml under api.news_providers. By default, it returns results from the first successful wrapper. Optionally, it can be configured to collect articles from all wrappers. If no wrapper succeeds, an exception is raised. """ + # Mapping of wrapper names to wrapper classes + _WRAPPER_MAP = { + 'GoogleNewsWrapper': GoogleNewsWrapper, + 'DuckDuckGoWrapper': DuckDuckGoWrapper, + 'NewsApiWrapper': NewsApiWrapper, + 'CryptoPanicWrapper': CryptoPanicWrapper, + } + def __init__(self): """ - Initialize the NewsAPIsTool with multiple news API wrappers. - The tool uses WrapperHandler to manage and invoke the different news API wrappers. - The following wrappers are included in this order: - - GoogleNewsWrapper. - - DuckDuckGoWrapper. - - NewsApiWrapper. - - CryptoPanicWrapper. + Initialize the NewsAPIsTool with news API wrappers configured in configs.yaml. + The order of wrappers is determined by the api.news_providers list in the configuration. """ - wrappers: list[type[NewsWrapper]] = [GoogleNewsWrapper, DuckDuckGoWrapper, NewsApiWrapper, CryptoPanicWrapper] - self.handler = WrapperHandler.build_wrappers(wrappers) + config = AppConfig() + + # Get wrapper classes based on configuration + wrappers: list[type[NewsWrapper]] = [] + for provider_name in config.api.news_providers: + if provider_name in self._WRAPPER_MAP: + wrappers.append(self._WRAPPER_MAP[provider_name]) + + # Fallback to all wrappers if none configured + if not wrappers: + wrappers = [GoogleNewsWrapper, DuckDuckGoWrapper, NewsApiWrapper, CryptoPanicWrapper] + + self.handler = WrapperHandler.build_wrappers( + wrappers, + try_per_wrapper=config.api.retry_attempts, + retry_delay=config.api.retry_delay_seconds + ) Toolkit.__init__( # type: ignore self, diff --git a/src/app/api/tools/social_tool.py b/src/app/api/tools/social_tool.py index c905b5b..e63eb4c 100644 --- a/src/app/api/tools/social_tool.py +++ b/src/app/api/tools/social_tool.py @@ -2,29 +2,49 @@ from agno.tools import Toolkit from app.api.wrapper_handler import WrapperHandler from app.api.core.social import SocialPost, SocialWrapper from app.api.social import * +from app.configs import AppConfig class SocialAPIsTool(SocialWrapper, Toolkit): """ Aggregates multiple social media API wrappers and manages them using WrapperHandler. - This class supports retrieving top crypto-related posts by querying multiple sources: - - RedditWrapper + This class supports retrieving top crypto-related posts by querying multiple sources. + Providers can be configured in configs.yaml under api.social_providers. By default, it returns results from the first successful wrapper. Optionally, it can be configured to collect posts from all wrappers. If no wrapper succeeds, an exception is raised. """ + # Mapping of wrapper names to wrapper classes + _WRAPPER_MAP = { + 'RedditWrapper': RedditWrapper, + 'XWrapper': XWrapper, + 'ChanWrapper': ChanWrapper, + } + def __init__(self): """ - Initialize the SocialAPIsTool with multiple social media API wrappers. - The tool uses WrapperHandler to manage and invoke the different social media API wrappers. - The following wrappers are included in this order: - - RedditWrapper. + Initialize the SocialAPIsTool with social media API wrappers configured in configs.yaml. + The order of wrappers is determined by the api.social_providers list in the configuration. """ - - wrappers: list[type[SocialWrapper]] = [RedditWrapper, XWrapper, ChanWrapper] - self.handler = WrapperHandler.build_wrappers(wrappers) + config = AppConfig() + + # Get wrapper classes based on configuration + wrappers: list[type[SocialWrapper]] = [] + for provider_name in config.api.social_providers: + if provider_name in self._WRAPPER_MAP: + wrappers.append(self._WRAPPER_MAP[provider_name]) + + # Fallback to all wrappers if none configured + if not wrappers: + wrappers = [RedditWrapper, XWrapper, ChanWrapper] + + self.handler = WrapperHandler.build_wrappers( + wrappers, + try_per_wrapper=config.api.retry_attempts, + retry_delay=config.api.retry_delay_seconds + ) Toolkit.__init__( # type: ignore self, diff --git a/src/app/configs.py b/src/app/configs.py index 45b5b01..fab5ad4 100644 --- a/src/app/configs.py +++ b/src/app/configs.py @@ -57,6 +57,9 @@ class AppModel(BaseModel): class APIConfig(BaseModel): retry_attempts: int = 3 retry_delay_seconds: int = 2 + market_providers: list[str] = [] + news_providers: list[str] = [] + social_providers: list[str] = [] class Strategy(BaseModel): name: str = "Conservative" -- 2.49.1 From 93d005c3e5ea3b04481f21f71efcd75169737f86 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 22 Oct 2025 14:49:09 +0000 Subject: [PATCH 3/7] Refactor provider filtering to use WrapperHandler helper function Co-authored-by: Berack96 <31776951+Berack96@users.noreply.github.com> --- configs.yaml | 4 ++-- src/app/api/tools/market_tool.py | 15 ++++++--------- src/app/api/tools/news_tool.py | 15 ++++++--------- src/app/api/tools/social_tool.py | 15 ++++++--------- src/app/api/wrapper_handler.py | 28 ++++++++++++++++++++++++++++ 5 files changed, 48 insertions(+), 29 deletions(-) diff --git a/configs.yaml b/configs.yaml index 1837a30..252c9d1 100644 --- a/configs.yaml +++ b/configs.yaml @@ -32,8 +32,8 @@ models: api: retry_attempts: 3 retry_delay_seconds: 2 - market_providers: [BinanceWrapper, YFinanceWrapper, CoinBaseWrapper, CryptoCompareWrapper] - news_providers: [GoogleNewsWrapper, DuckDuckGoWrapper, NewsApiWrapper, CryptoPanicWrapper] + market_providers: [YFinanceWrapper, BinanceWrapper, CoinBaseWrapper, CryptoCompareWrapper] + news_providers: [DuckDuckGoWrapper, GoogleNewsWrapper, NewsApiWrapper, CryptoPanicWrapper] social_providers: [RedditWrapper, XWrapper, ChanWrapper] agents: diff --git a/src/app/api/tools/market_tool.py b/src/app/api/tools/market_tool.py index e740372..7d7e8cd 100644 --- a/src/app/api/tools/market_tool.py +++ b/src/app/api/tools/market_tool.py @@ -27,15 +27,12 @@ class MarketAPIsTool(MarketWrapper, Toolkit): """ config = AppConfig() - # Get wrapper classes based on configuration - wrappers: list[type[MarketWrapper]] = [] - for provider_name in config.api.market_providers: - if provider_name in self._WRAPPER_MAP: - wrappers.append(self._WRAPPER_MAP[provider_name]) - - # Fallback to all wrappers if none configured - if not wrappers: - wrappers = [BinanceWrapper, YFinanceWrapper, CoinBaseWrapper, CryptoCompareWrapper] + # Get wrapper classes based on configuration using the helper function + wrappers = WrapperHandler.filter_wrappers_by_config( + wrapper_map=self._WRAPPER_MAP, + provider_names=config.api.market_providers, + fallback_wrappers=[BinanceWrapper, YFinanceWrapper, CoinBaseWrapper, CryptoCompareWrapper] + ) self.handler = WrapperHandler.build_wrappers( wrappers, diff --git a/src/app/api/tools/news_tool.py b/src/app/api/tools/news_tool.py index e4733f7..f9558ed 100644 --- a/src/app/api/tools/news_tool.py +++ b/src/app/api/tools/news_tool.py @@ -30,15 +30,12 @@ class NewsAPIsTool(NewsWrapper, Toolkit): """ config = AppConfig() - # Get wrapper classes based on configuration - wrappers: list[type[NewsWrapper]] = [] - for provider_name in config.api.news_providers: - if provider_name in self._WRAPPER_MAP: - wrappers.append(self._WRAPPER_MAP[provider_name]) - - # Fallback to all wrappers if none configured - if not wrappers: - wrappers = [GoogleNewsWrapper, DuckDuckGoWrapper, NewsApiWrapper, CryptoPanicWrapper] + # Get wrapper classes based on configuration using the helper function + wrappers = WrapperHandler.filter_wrappers_by_config( + wrapper_map=self._WRAPPER_MAP, + provider_names=config.api.news_providers, + fallback_wrappers=[GoogleNewsWrapper, DuckDuckGoWrapper, NewsApiWrapper, CryptoPanicWrapper] + ) self.handler = WrapperHandler.build_wrappers( wrappers, diff --git a/src/app/api/tools/social_tool.py b/src/app/api/tools/social_tool.py index e63eb4c..b44a969 100644 --- a/src/app/api/tools/social_tool.py +++ b/src/app/api/tools/social_tool.py @@ -30,15 +30,12 @@ class SocialAPIsTool(SocialWrapper, Toolkit): """ config = AppConfig() - # Get wrapper classes based on configuration - wrappers: list[type[SocialWrapper]] = [] - for provider_name in config.api.social_providers: - if provider_name in self._WRAPPER_MAP: - wrappers.append(self._WRAPPER_MAP[provider_name]) - - # Fallback to all wrappers if none configured - if not wrappers: - wrappers = [RedditWrapper, XWrapper, ChanWrapper] + # Get wrapper classes based on configuration using the helper function + wrappers = WrapperHandler.filter_wrappers_by_config( + wrapper_map=self._WRAPPER_MAP, + provider_names=config.api.social_providers, + fallback_wrappers=[RedditWrapper, XWrapper, ChanWrapper] + ) self.handler = WrapperHandler.build_wrappers( wrappers, diff --git a/src/app/api/wrapper_handler.py b/src/app/api/wrapper_handler.py index 30b3887..1b38f25 100644 --- a/src/app/api/wrapper_handler.py +++ b/src/app/api/wrapper_handler.py @@ -130,6 +130,34 @@ class WrapperHandler(Generic[WrapperType]): last_frame = traceback.extract_tb(e.__traceback__)[-1] return f"{e} [\"{last_frame.filename}\", line {last_frame.lineno}]" + @staticmethod + def filter_wrappers_by_config( + wrapper_map: dict[str, type[WrapperClassType]], + provider_names: list[str], + fallback_wrappers: list[type[WrapperClassType]] | None = None + ) -> list[type[WrapperClassType]]: + """ + Filters wrapper classes based on a list of provider names from configuration. + + Args: + wrapper_map (dict[str, type[W]]): Dictionary mapping provider names to wrapper classes. + provider_names (list[str]): List of provider names from configuration. + fallback_wrappers (list[type[W]] | None): Optional fallback list if no providers configured. + + Returns: + list[type[W]]: List of wrapper classes in the order specified by provider_names. + """ + wrappers: list[type[WrapperClassType]] = [] + for provider_name in provider_names: + if provider_name in wrapper_map: + wrappers.append(wrapper_map[provider_name]) + + # Fallback to all wrappers if none configured + if not wrappers and fallback_wrappers: + wrappers = fallback_wrappers + + return wrappers + @staticmethod def build_wrappers(constructors: list[type[WrapperClassType]], try_per_wrapper: int = 3, retry_delay: int = 2, kwargs: dict[str, Any] | None = None) -> 'WrapperHandler[WrapperClassType]': """ -- 2.49.1 From 803ef22fea93a79527e90deac20abf665686bcc9 Mon Sep 17 00:00:00 2001 From: Berack96 Date: Wed, 22 Oct 2025 17:06:16 +0200 Subject: [PATCH 4/7] Refactor API wrapper initialization to streamline configuration handling --- src/app/api/tools/market_tool.py | 20 +++------------ src/app/api/tools/news_tool.py | 20 +++------------ src/app/api/tools/social_tool.py | 19 +++----------- src/app/api/wrapper_handler.py | 44 ++++++++++---------------------- tests/tools/test_market_tool.py | 8 +++--- tests/tools/test_socials_tool.py | 2 +- 6 files changed, 28 insertions(+), 85 deletions(-) diff --git a/src/app/api/tools/market_tool.py b/src/app/api/tools/market_tool.py index 7d7e8cd..e47fc9f 100644 --- a/src/app/api/tools/market_tool.py +++ b/src/app/api/tools/market_tool.py @@ -12,30 +12,16 @@ class MarketAPIsTool(MarketWrapper, Toolkit): Providers can be configured in configs.yaml under api.market_providers. """ - # Mapping of wrapper names to wrapper classes - _WRAPPER_MAP = { - 'BinanceWrapper': BinanceWrapper, - 'YFinanceWrapper': YFinanceWrapper, - 'CoinBaseWrapper': CoinBaseWrapper, - 'CryptoCompareWrapper': CryptoCompareWrapper, - } - def __init__(self): """ Initialize the MarketAPIsTool with market API wrappers configured in configs.yaml. The order of wrappers is determined by the api.market_providers list in the configuration. """ config = AppConfig() - - # Get wrapper classes based on configuration using the helper function - wrappers = WrapperHandler.filter_wrappers_by_config( - wrapper_map=self._WRAPPER_MAP, - provider_names=config.api.market_providers, - fallback_wrappers=[BinanceWrapper, YFinanceWrapper, CoinBaseWrapper, CryptoCompareWrapper] - ) - + self.handler = WrapperHandler.build_wrappers( - wrappers, + constructors=[BinanceWrapper, YFinanceWrapper, CoinBaseWrapper, CryptoCompareWrapper], + filters=config.api.market_providers, try_per_wrapper=config.api.retry_attempts, retry_delay=config.api.retry_delay_seconds ) diff --git a/src/app/api/tools/news_tool.py b/src/app/api/tools/news_tool.py index f9558ed..eddf48d 100644 --- a/src/app/api/tools/news_tool.py +++ b/src/app/api/tools/news_tool.py @@ -15,30 +15,16 @@ class NewsAPIsTool(NewsWrapper, Toolkit): If no wrapper succeeds, an exception is raised. """ - # Mapping of wrapper names to wrapper classes - _WRAPPER_MAP = { - 'GoogleNewsWrapper': GoogleNewsWrapper, - 'DuckDuckGoWrapper': DuckDuckGoWrapper, - 'NewsApiWrapper': NewsApiWrapper, - 'CryptoPanicWrapper': CryptoPanicWrapper, - } - def __init__(self): """ Initialize the NewsAPIsTool with news API wrappers configured in configs.yaml. The order of wrappers is determined by the api.news_providers list in the configuration. """ config = AppConfig() - - # Get wrapper classes based on configuration using the helper function - wrappers = WrapperHandler.filter_wrappers_by_config( - wrapper_map=self._WRAPPER_MAP, - provider_names=config.api.news_providers, - fallback_wrappers=[GoogleNewsWrapper, DuckDuckGoWrapper, NewsApiWrapper, CryptoPanicWrapper] - ) - + self.handler = WrapperHandler.build_wrappers( - wrappers, + constructors=[NewsApiWrapper, GoogleNewsWrapper, CryptoPanicWrapper, DuckDuckGoWrapper], + filters=config.api.news_providers, try_per_wrapper=config.api.retry_attempts, retry_delay=config.api.retry_delay_seconds ) diff --git a/src/app/api/tools/social_tool.py b/src/app/api/tools/social_tool.py index b44a969..ab346ca 100644 --- a/src/app/api/tools/social_tool.py +++ b/src/app/api/tools/social_tool.py @@ -16,29 +16,16 @@ class SocialAPIsTool(SocialWrapper, Toolkit): If no wrapper succeeds, an exception is raised. """ - # Mapping of wrapper names to wrapper classes - _WRAPPER_MAP = { - 'RedditWrapper': RedditWrapper, - 'XWrapper': XWrapper, - 'ChanWrapper': ChanWrapper, - } - def __init__(self): """ Initialize the SocialAPIsTool with social media API wrappers configured in configs.yaml. The order of wrappers is determined by the api.social_providers list in the configuration. """ config = AppConfig() - - # Get wrapper classes based on configuration using the helper function - wrappers = WrapperHandler.filter_wrappers_by_config( - wrapper_map=self._WRAPPER_MAP, - provider_names=config.api.social_providers, - fallback_wrappers=[RedditWrapper, XWrapper, ChanWrapper] - ) - + self.handler = WrapperHandler.build_wrappers( - wrappers, + constructors=[RedditWrapper, XWrapper, ChanWrapper], + filters=config.api.social_providers, try_per_wrapper=config.api.retry_attempts, retry_delay=config.api.retry_delay_seconds ) diff --git a/src/app/api/wrapper_handler.py b/src/app/api/wrapper_handler.py index 1b38f25..00aafa2 100644 --- a/src/app/api/wrapper_handler.py +++ b/src/app/api/wrapper_handler.py @@ -131,41 +131,19 @@ class WrapperHandler(Generic[WrapperType]): return f"{e} [\"{last_frame.filename}\", line {last_frame.lineno}]" @staticmethod - def filter_wrappers_by_config( - wrapper_map: dict[str, type[WrapperClassType]], - provider_names: list[str], - fallback_wrappers: list[type[WrapperClassType]] | None = None - ) -> list[type[WrapperClassType]]: - """ - Filters wrapper classes based on a list of provider names from configuration. - - Args: - wrapper_map (dict[str, type[W]]): Dictionary mapping provider names to wrapper classes. - provider_names (list[str]): List of provider names from configuration. - fallback_wrappers (list[type[W]] | None): Optional fallback list if no providers configured. - - Returns: - list[type[W]]: List of wrapper classes in the order specified by provider_names. - """ - wrappers: list[type[WrapperClassType]] = [] - for provider_name in provider_names: - if provider_name in wrapper_map: - wrappers.append(wrapper_map[provider_name]) - - # Fallback to all wrappers if none configured - if not wrappers and fallback_wrappers: - wrappers = fallback_wrappers - - return wrappers - - @staticmethod - def build_wrappers(constructors: list[type[WrapperClassType]], try_per_wrapper: int = 3, retry_delay: int = 2, kwargs: dict[str, Any] | None = None) -> 'WrapperHandler[WrapperClassType]': + def build_wrappers( + constructors: list[type[WrapperClassType]], + filters: list[str] | None = None, + try_per_wrapper: int = 3, + retry_delay: int = 2, + kwargs: dict[str, Any] | None = None) -> 'WrapperHandler[WrapperClassType]': """ Builds a WrapperHandler instance with the given wrapper constructors. It attempts to initialize each wrapper and logs a warning if any cannot be initialized. Only successfully initialized wrappers are included in the handler. Args: constructors (list[type[W]]): An iterable of wrapper classes to instantiate. e.g. [WrapperA, WrapperB] + filters (list[str] | None): Optional list of provider names to filter the constructors. try_per_wrapper (int): Number of retries per wrapper before switching to the next. retry_delay (int): Delay in seconds between retries. kwargs (dict | None): Optional dictionary with keyword arguments common to all wrappers. @@ -176,8 +154,14 @@ class WrapperHandler(Generic[WrapperType]): """ assert WrapperHandler.__check(constructors), f"All constructors must be classes. Received: {constructors}" + # Order of wrappers is now determined by the order in filters + filters = filters or [c.__name__ for c in constructors] + wrappers = [c for name in filters for c in constructors if c.__name__ == name] + result: list[WrapperClassType] = [] - for wrapper_class in constructors: + for wrapper_class in wrappers: + if filters and wrapper_class.__name__ not in filters: + continue try: wrapper = wrapper_class(**(kwargs or {})) result.append(wrapper) diff --git a/tests/tools/test_market_tool.py b/tests/tools/test_market_tool.py index ea90bf2..0787a0b 100644 --- a/tests/tools/test_market_tool.py +++ b/tests/tools/test_market_tool.py @@ -7,14 +7,14 @@ from app.api.tools import MarketAPIsTool @pytest.mark.api class TestMarketAPIsTool: def test_wrapper_initialization(self): - market_wrapper = MarketAPIsTool("EUR") + market_wrapper = MarketAPIsTool() assert market_wrapper is not None assert hasattr(market_wrapper, 'get_product') assert hasattr(market_wrapper, 'get_products') assert hasattr(market_wrapper, 'get_historical_prices') def test_wrapper_capabilities(self): - market_wrapper = MarketAPIsTool("EUR") + market_wrapper = MarketAPIsTool() capabilities: list[str] = [] if hasattr(market_wrapper, 'get_product'): capabilities.append('single_product') @@ -25,7 +25,7 @@ class TestMarketAPIsTool: assert len(capabilities) > 0 def test_market_data_retrieval(self): - market_wrapper = MarketAPIsTool("EUR") + market_wrapper = MarketAPIsTool() btc_product = market_wrapper.get_product("BTC") assert btc_product is not None assert hasattr(btc_product, 'symbol') @@ -34,7 +34,7 @@ class TestMarketAPIsTool: def test_error_handling(self): try: - market_wrapper = MarketAPIsTool("EUR") + market_wrapper = MarketAPIsTool() fake_product = market_wrapper.get_product("NONEXISTENT_CRYPTO_SYMBOL_12345") assert fake_product is None or fake_product.price == 0 except Exception as _: diff --git a/tests/tools/test_socials_tool.py b/tests/tools/test_socials_tool.py index c021a90..72c6681 100644 --- a/tests/tools/test_socials_tool.py +++ b/tests/tools/test_socials_tool.py @@ -19,7 +19,7 @@ class TestSocialAPIsTool: assert post.title is not None assert post.time is not None - def test_social_api_tool_get_top__all_results(self): + def test_social_api_tool_get_top_all_results(self): tool = SocialAPIsTool() result = tool.handler.try_call_all(lambda w: w.get_top_crypto_posts(limit=2)) assert isinstance(result, dict) -- 2.49.1 From 2f60f13ef24627c5c2862c65697678fe150deb0f Mon Sep 17 00:00:00 2001 From: Berack96 Date: Wed, 22 Oct 2025 17:08:25 +0200 Subject: [PATCH 5/7] Fix wrapper initialization order to respect filter settings --- src/app/api/wrapper_handler.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/app/api/wrapper_handler.py b/src/app/api/wrapper_handler.py index 00aafa2..d5dfd92 100644 --- a/src/app/api/wrapper_handler.py +++ b/src/app/api/wrapper_handler.py @@ -155,11 +155,11 @@ class WrapperHandler(Generic[WrapperType]): assert WrapperHandler.__check(constructors), f"All constructors must be classes. Received: {constructors}" # Order of wrappers is now determined by the order in filters - filters = filters or [c.__name__ for c in constructors] - wrappers = [c for name in filters for c in constructors if c.__name__ == name] + if filters: + constructors = [c for name in filters for c in constructors if c.__name__ == name] result: list[WrapperClassType] = [] - for wrapper_class in wrappers: + for wrapper_class in constructors: if filters and wrapper_class.__name__ not in filters: continue try: @@ -168,4 +168,4 @@ class WrapperHandler(Generic[WrapperType]): except Exception as e: logging.warning(f"'{wrapper_class.__name__}' cannot be initialized: {e}") - return WrapperHandler(result, try_per_wrapper, retry_delay) \ No newline at end of file + return WrapperHandler(result, try_per_wrapper, retry_delay) -- 2.49.1 From fb6ca5914dd486e8b44d8a7f8c9eec3ccc928566 Mon Sep 17 00:00:00 2001 From: Berack96 Date: Wed, 22 Oct 2025 17:14:09 +0200 Subject: [PATCH 6/7] Refactor agent retrieval to use specific API tools directly --- src/app/agents/core.py | 21 +++------------------ 1 file changed, 3 insertions(+), 18 deletions(-) diff --git a/src/app/agents/core.py b/src/app/agents/core.py index 4a685cb..3dbcb90 100644 --- a/src/app/agents/core.py +++ b/src/app/agents/core.py @@ -89,10 +89,9 @@ class PipelineInputs: # Agent getters # ====================== def get_agent_team(self) -> Team: - market, news, social = self.get_tools() - market_agent = self.team_model.get_agent(MARKET_INSTRUCTIONS, "Market Agent", tools=[market]) - news_agent = self.team_model.get_agent(NEWS_INSTRUCTIONS, "News Agent", tools=[news]) - social_agent = self.team_model.get_agent(SOCIAL_INSTRUCTIONS, "Socials Agent", tools=[social]) + market_agent = self.team_model.get_agent(MARKET_INSTRUCTIONS, "Market Agent", tools=[MarketAPIsTool()]) + news_agent = self.team_model.get_agent(NEWS_INSTRUCTIONS, "News Agent", tools=[NewsAPIsTool()]) + social_agent = self.team_model.get_agent(SOCIAL_INSTRUCTIONS, "Socials Agent", tools=[SocialAPIsTool()]) return Team( model=self.team_leader_model.get_model(TEAM_LEADER_INSTRUCTIONS), name="CryptoAnalysisTeam", @@ -105,17 +104,3 @@ class PipelineInputs: def get_agent_report_generator(self) -> Agent: return self.report_generation_model.get_agent(REPORT_GENERATION_INSTRUCTIONS, "Report Generator Agent") - - def get_tools(self) -> tuple[MarketAPIsTool, NewsAPIsTool, SocialAPIsTool]: - """ - Restituisce la lista di tools disponibili per gli agenti. - """ - api = self.configs.api - - market_tool = MarketAPIsTool() - market_tool.handler.set_retries(api.retry_attempts, api.retry_delay_seconds) - news_tool = NewsAPIsTool() - news_tool.handler.set_retries(api.retry_attempts, api.retry_delay_seconds) - social_tool = SocialAPIsTool() - social_tool.handler.set_retries(api.retry_attempts, api.retry_delay_seconds) - return market_tool, news_tool, social_tool -- 2.49.1 From c1ed1d85f038049305226728f92112d131f66af1 Mon Sep 17 00:00:00 2001 From: Giacomo Bertolazzi <31776951+Berack96@users.noreply.github.com> Date: Wed, 22 Oct 2025 17:16:42 +0200 Subject: [PATCH 7/7] Update src/app/api/wrapper_handler.py Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- src/app/api/wrapper_handler.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/app/api/wrapper_handler.py b/src/app/api/wrapper_handler.py index d5dfd92..0d30e63 100644 --- a/src/app/api/wrapper_handler.py +++ b/src/app/api/wrapper_handler.py @@ -160,8 +160,6 @@ class WrapperHandler(Generic[WrapperType]): result: list[WrapperClassType] = [] for wrapper_class in constructors: - if filters and wrapper_class.__name__ not in filters: - continue try: wrapper = wrapper_class(**(kwargs or {})) result.append(wrapper) -- 2.49.1