intentkit 0.5.0__py3-none-any.whl → 0.5.2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of intentkit might be problematic. Click here for more details.
- intentkit/__init__.py +17 -0
- intentkit/abstracts/__init__.py +0 -0
- intentkit/abstracts/agent.py +60 -0
- intentkit/abstracts/api.py +4 -0
- intentkit/abstracts/engine.py +38 -0
- intentkit/abstracts/exception.py +9 -0
- intentkit/abstracts/graph.py +25 -0
- intentkit/abstracts/skill.py +129 -0
- intentkit/abstracts/twitter.py +54 -0
- intentkit/clients/__init__.py +14 -0
- intentkit/clients/cdp.py +53 -0
- intentkit/clients/twitter.py +445 -0
- intentkit/config/__init__.py +0 -0
- intentkit/config/config.py +164 -0
- intentkit/core/__init__.py +0 -0
- intentkit/core/agent.py +191 -0
- intentkit/core/api.py +40 -0
- intentkit/core/client.py +45 -0
- intentkit/core/credit.py +1767 -0
- intentkit/core/engine.py +1018 -0
- intentkit/core/node.py +223 -0
- intentkit/core/prompt.py +58 -0
- intentkit/core/skill.py +124 -0
- intentkit/models/agent.py +1689 -0
- intentkit/models/agent_data.py +810 -0
- intentkit/models/agent_schema.json +733 -0
- intentkit/models/app_setting.py +156 -0
- intentkit/models/base.py +9 -0
- intentkit/models/chat.py +581 -0
- intentkit/models/conversation.py +286 -0
- intentkit/models/credit.py +1406 -0
- intentkit/models/db.py +120 -0
- intentkit/models/db_mig.py +102 -0
- intentkit/models/generator.py +347 -0
- intentkit/models/llm.py +746 -0
- intentkit/models/redis.py +132 -0
- intentkit/models/skill.py +466 -0
- intentkit/models/user.py +243 -0
- intentkit/skills/__init__.py +12 -0
- intentkit/skills/acolyt/__init__.py +83 -0
- intentkit/skills/acolyt/acolyt.jpg +0 -0
- intentkit/skills/acolyt/ask.py +128 -0
- intentkit/skills/acolyt/base.py +28 -0
- intentkit/skills/acolyt/schema.json +89 -0
- intentkit/skills/aixbt/README.md +71 -0
- intentkit/skills/aixbt/__init__.py +73 -0
- intentkit/skills/aixbt/aixbt.jpg +0 -0
- intentkit/skills/aixbt/base.py +21 -0
- intentkit/skills/aixbt/projects.py +153 -0
- intentkit/skills/aixbt/schema.json +99 -0
- intentkit/skills/allora/__init__.py +83 -0
- intentkit/skills/allora/allora.jpeg +0 -0
- intentkit/skills/allora/base.py +28 -0
- intentkit/skills/allora/price.py +130 -0
- intentkit/skills/allora/schema.json +89 -0
- intentkit/skills/base.py +174 -0
- intentkit/skills/carv/README.md +95 -0
- intentkit/skills/carv/__init__.py +121 -0
- intentkit/skills/carv/base.py +183 -0
- intentkit/skills/carv/carv.webp +0 -0
- intentkit/skills/carv/fetch_news.py +92 -0
- intentkit/skills/carv/onchain_query.py +164 -0
- intentkit/skills/carv/schema.json +137 -0
- intentkit/skills/carv/token_info_and_price.py +110 -0
- intentkit/skills/cdp/__init__.py +137 -0
- intentkit/skills/cdp/base.py +21 -0
- intentkit/skills/cdp/cdp.png +0 -0
- intentkit/skills/cdp/get_balance.py +81 -0
- intentkit/skills/cdp/schema.json +473 -0
- intentkit/skills/chainlist/README.md +38 -0
- intentkit/skills/chainlist/__init__.py +54 -0
- intentkit/skills/chainlist/base.py +21 -0
- intentkit/skills/chainlist/chain_lookup.py +208 -0
- intentkit/skills/chainlist/chainlist.png +0 -0
- intentkit/skills/chainlist/schema.json +47 -0
- intentkit/skills/common/__init__.py +82 -0
- intentkit/skills/common/base.py +21 -0
- intentkit/skills/common/common.jpg +0 -0
- intentkit/skills/common/current_time.py +84 -0
- intentkit/skills/common/schema.json +57 -0
- intentkit/skills/cookiefun/README.md +121 -0
- intentkit/skills/cookiefun/__init__.py +78 -0
- intentkit/skills/cookiefun/base.py +41 -0
- intentkit/skills/cookiefun/constants.py +18 -0
- intentkit/skills/cookiefun/cookiefun.png +0 -0
- intentkit/skills/cookiefun/get_account_details.py +171 -0
- intentkit/skills/cookiefun/get_account_feed.py +282 -0
- intentkit/skills/cookiefun/get_account_smart_followers.py +181 -0
- intentkit/skills/cookiefun/get_sectors.py +128 -0
- intentkit/skills/cookiefun/schema.json +155 -0
- intentkit/skills/cookiefun/search_accounts.py +225 -0
- intentkit/skills/cryptocompare/__init__.py +130 -0
- intentkit/skills/cryptocompare/api.py +159 -0
- intentkit/skills/cryptocompare/base.py +303 -0
- intentkit/skills/cryptocompare/cryptocompare.png +0 -0
- intentkit/skills/cryptocompare/fetch_news.py +96 -0
- intentkit/skills/cryptocompare/fetch_price.py +99 -0
- intentkit/skills/cryptocompare/fetch_top_exchanges.py +113 -0
- intentkit/skills/cryptocompare/fetch_top_market_cap.py +109 -0
- intentkit/skills/cryptocompare/fetch_top_volume.py +108 -0
- intentkit/skills/cryptocompare/fetch_trading_signals.py +107 -0
- intentkit/skills/cryptocompare/schema.json +168 -0
- intentkit/skills/cryptopanic/__init__.py +108 -0
- intentkit/skills/cryptopanic/base.py +51 -0
- intentkit/skills/cryptopanic/cryptopanic.png +0 -0
- intentkit/skills/cryptopanic/fetch_crypto_news.py +153 -0
- intentkit/skills/cryptopanic/fetch_crypto_sentiment.py +136 -0
- intentkit/skills/cryptopanic/schema.json +103 -0
- intentkit/skills/dapplooker/README.md +92 -0
- intentkit/skills/dapplooker/__init__.py +83 -0
- intentkit/skills/dapplooker/base.py +26 -0
- intentkit/skills/dapplooker/dapplooker.jpg +0 -0
- intentkit/skills/dapplooker/dapplooker_token_data.py +476 -0
- intentkit/skills/dapplooker/schema.json +91 -0
- intentkit/skills/defillama/__init__.py +323 -0
- intentkit/skills/defillama/api.py +315 -0
- intentkit/skills/defillama/base.py +135 -0
- intentkit/skills/defillama/coins/__init__.py +0 -0
- intentkit/skills/defillama/coins/fetch_batch_historical_prices.py +116 -0
- intentkit/skills/defillama/coins/fetch_block.py +98 -0
- intentkit/skills/defillama/coins/fetch_current_prices.py +105 -0
- intentkit/skills/defillama/coins/fetch_first_price.py +100 -0
- intentkit/skills/defillama/coins/fetch_historical_prices.py +110 -0
- intentkit/skills/defillama/coins/fetch_price_chart.py +109 -0
- intentkit/skills/defillama/coins/fetch_price_percentage.py +93 -0
- intentkit/skills/defillama/config/__init__.py +0 -0
- intentkit/skills/defillama/config/chains.py +433 -0
- intentkit/skills/defillama/defillama.jpeg +0 -0
- intentkit/skills/defillama/fees/__init__.py +0 -0
- intentkit/skills/defillama/fees/fetch_fees_overview.py +130 -0
- intentkit/skills/defillama/schema.json +383 -0
- intentkit/skills/defillama/stablecoins/__init__.py +0 -0
- intentkit/skills/defillama/stablecoins/fetch_stablecoin_chains.py +100 -0
- intentkit/skills/defillama/stablecoins/fetch_stablecoin_charts.py +129 -0
- intentkit/skills/defillama/stablecoins/fetch_stablecoin_prices.py +83 -0
- intentkit/skills/defillama/stablecoins/fetch_stablecoins.py +126 -0
- intentkit/skills/defillama/tests/__init__.py +0 -0
- intentkit/skills/defillama/tests/api_integration.test.py +192 -0
- intentkit/skills/defillama/tests/api_unit.test.py +583 -0
- intentkit/skills/defillama/tvl/__init__.py +0 -0
- intentkit/skills/defillama/tvl/fetch_chain_historical_tvl.py +106 -0
- intentkit/skills/defillama/tvl/fetch_chains.py +107 -0
- intentkit/skills/defillama/tvl/fetch_historical_tvl.py +91 -0
- intentkit/skills/defillama/tvl/fetch_protocol.py +207 -0
- intentkit/skills/defillama/tvl/fetch_protocol_current_tvl.py +93 -0
- intentkit/skills/defillama/tvl/fetch_protocols.py +196 -0
- intentkit/skills/defillama/volumes/__init__.py +0 -0
- intentkit/skills/defillama/volumes/fetch_dex_overview.py +157 -0
- intentkit/skills/defillama/volumes/fetch_dex_summary.py +123 -0
- intentkit/skills/defillama/volumes/fetch_options_overview.py +131 -0
- intentkit/skills/defillama/yields/__init__.py +0 -0
- intentkit/skills/defillama/yields/fetch_pool_chart.py +100 -0
- intentkit/skills/defillama/yields/fetch_pools.py +126 -0
- intentkit/skills/dexscreener/__init__.py +93 -0
- intentkit/skills/dexscreener/base.py +133 -0
- intentkit/skills/dexscreener/dexscreener.png +0 -0
- intentkit/skills/dexscreener/model/__init__.py +0 -0
- intentkit/skills/dexscreener/model/search_token_response.py +82 -0
- intentkit/skills/dexscreener/schema.json +48 -0
- intentkit/skills/dexscreener/search_token.py +321 -0
- intentkit/skills/dune_analytics/__init__.py +103 -0
- intentkit/skills/dune_analytics/base.py +46 -0
- intentkit/skills/dune_analytics/dune.png +0 -0
- intentkit/skills/dune_analytics/fetch_kol_buys.py +128 -0
- intentkit/skills/dune_analytics/fetch_nation_metrics.py +237 -0
- intentkit/skills/dune_analytics/schema.json +99 -0
- intentkit/skills/elfa/README.md +100 -0
- intentkit/skills/elfa/__init__.py +123 -0
- intentkit/skills/elfa/base.py +28 -0
- intentkit/skills/elfa/elfa.jpg +0 -0
- intentkit/skills/elfa/mention.py +504 -0
- intentkit/skills/elfa/schema.json +153 -0
- intentkit/skills/elfa/stats.py +118 -0
- intentkit/skills/elfa/tokens.py +126 -0
- intentkit/skills/enso/README.md +75 -0
- intentkit/skills/enso/__init__.py +114 -0
- intentkit/skills/enso/abi/__init__.py +0 -0
- intentkit/skills/enso/abi/approval.py +279 -0
- intentkit/skills/enso/abi/erc20.py +14 -0
- intentkit/skills/enso/abi/route.py +129 -0
- intentkit/skills/enso/base.py +44 -0
- intentkit/skills/enso/best_yield.py +286 -0
- intentkit/skills/enso/enso.jpg +0 -0
- intentkit/skills/enso/networks.py +105 -0
- intentkit/skills/enso/prices.py +93 -0
- intentkit/skills/enso/route.py +300 -0
- intentkit/skills/enso/schema.json +212 -0
- intentkit/skills/enso/tokens.py +223 -0
- intentkit/skills/enso/wallet.py +381 -0
- intentkit/skills/github/README.md +63 -0
- intentkit/skills/github/__init__.py +54 -0
- intentkit/skills/github/base.py +21 -0
- intentkit/skills/github/github.jpg +0 -0
- intentkit/skills/github/github_search.py +183 -0
- intentkit/skills/github/schema.json +59 -0
- intentkit/skills/heurist/__init__.py +143 -0
- intentkit/skills/heurist/base.py +26 -0
- intentkit/skills/heurist/heurist.png +0 -0
- intentkit/skills/heurist/image_generation_animagine_xl.py +162 -0
- intentkit/skills/heurist/image_generation_arthemy_comics.py +162 -0
- intentkit/skills/heurist/image_generation_arthemy_real.py +162 -0
- intentkit/skills/heurist/image_generation_braindance.py +162 -0
- intentkit/skills/heurist/image_generation_cyber_realistic_xl.py +162 -0
- intentkit/skills/heurist/image_generation_flux_1_dev.py +162 -0
- intentkit/skills/heurist/image_generation_sdxl.py +161 -0
- intentkit/skills/heurist/schema.json +196 -0
- intentkit/skills/lifi/README.md +294 -0
- intentkit/skills/lifi/__init__.py +141 -0
- intentkit/skills/lifi/base.py +21 -0
- intentkit/skills/lifi/lifi.png +0 -0
- intentkit/skills/lifi/schema.json +89 -0
- intentkit/skills/lifi/token_execute.py +472 -0
- intentkit/skills/lifi/token_quote.py +190 -0
- intentkit/skills/lifi/utils.py +656 -0
- intentkit/skills/moralis/README.md +490 -0
- intentkit/skills/moralis/__init__.py +110 -0
- intentkit/skills/moralis/api.py +281 -0
- intentkit/skills/moralis/base.py +55 -0
- intentkit/skills/moralis/fetch_chain_portfolio.py +191 -0
- intentkit/skills/moralis/fetch_nft_portfolio.py +284 -0
- intentkit/skills/moralis/fetch_solana_portfolio.py +331 -0
- intentkit/skills/moralis/fetch_wallet_portfolio.py +301 -0
- intentkit/skills/moralis/moralis.png +0 -0
- intentkit/skills/moralis/schema.json +156 -0
- intentkit/skills/moralis/tests/__init__.py +0 -0
- intentkit/skills/moralis/tests/test_wallet.py +511 -0
- intentkit/skills/nation/__init__.py +62 -0
- intentkit/skills/nation/base.py +31 -0
- intentkit/skills/nation/nation.png +0 -0
- intentkit/skills/nation/nft_check.py +106 -0
- intentkit/skills/nation/schema.json +58 -0
- intentkit/skills/openai/__init__.py +107 -0
- intentkit/skills/openai/base.py +32 -0
- intentkit/skills/openai/dalle_image_generation.py +128 -0
- intentkit/skills/openai/gpt_image_generation.py +152 -0
- intentkit/skills/openai/gpt_image_to_image.py +186 -0
- intentkit/skills/openai/image_to_text.py +126 -0
- intentkit/skills/openai/openai.png +0 -0
- intentkit/skills/openai/schema.json +139 -0
- intentkit/skills/portfolio/README.md +55 -0
- intentkit/skills/portfolio/__init__.py +151 -0
- intentkit/skills/portfolio/base.py +107 -0
- intentkit/skills/portfolio/constants.py +9 -0
- intentkit/skills/portfolio/moralis.png +0 -0
- intentkit/skills/portfolio/schema.json +237 -0
- intentkit/skills/portfolio/token_balances.py +155 -0
- intentkit/skills/portfolio/wallet_approvals.py +102 -0
- intentkit/skills/portfolio/wallet_defi_positions.py +80 -0
- intentkit/skills/portfolio/wallet_history.py +155 -0
- intentkit/skills/portfolio/wallet_net_worth.py +112 -0
- intentkit/skills/portfolio/wallet_nfts.py +139 -0
- intentkit/skills/portfolio/wallet_profitability.py +101 -0
- intentkit/skills/portfolio/wallet_profitability_summary.py +91 -0
- intentkit/skills/portfolio/wallet_stats.py +79 -0
- intentkit/skills/portfolio/wallet_swaps.py +147 -0
- intentkit/skills/skills.toml +103 -0
- intentkit/skills/slack/__init__.py +98 -0
- intentkit/skills/slack/base.py +55 -0
- intentkit/skills/slack/get_channel.py +109 -0
- intentkit/skills/slack/get_message.py +136 -0
- intentkit/skills/slack/schedule_message.py +92 -0
- intentkit/skills/slack/schema.json +135 -0
- intentkit/skills/slack/send_message.py +81 -0
- intentkit/skills/slack/slack.jpg +0 -0
- intentkit/skills/system/__init__.py +90 -0
- intentkit/skills/system/base.py +22 -0
- intentkit/skills/system/read_agent_api_key.py +87 -0
- intentkit/skills/system/regenerate_agent_api_key.py +77 -0
- intentkit/skills/system/schema.json +53 -0
- intentkit/skills/system/system.svg +76 -0
- intentkit/skills/tavily/README.md +86 -0
- intentkit/skills/tavily/__init__.py +91 -0
- intentkit/skills/tavily/base.py +27 -0
- intentkit/skills/tavily/schema.json +119 -0
- intentkit/skills/tavily/tavily.jpg +0 -0
- intentkit/skills/tavily/tavily_extract.py +147 -0
- intentkit/skills/tavily/tavily_search.py +139 -0
- intentkit/skills/token/README.md +89 -0
- intentkit/skills/token/__init__.py +107 -0
- intentkit/skills/token/base.py +154 -0
- intentkit/skills/token/constants.py +9 -0
- intentkit/skills/token/erc20_transfers.py +145 -0
- intentkit/skills/token/moralis.png +0 -0
- intentkit/skills/token/schema.json +141 -0
- intentkit/skills/token/token_analytics.py +81 -0
- intentkit/skills/token/token_price.py +132 -0
- intentkit/skills/token/token_search.py +121 -0
- intentkit/skills/twitter/__init__.py +146 -0
- intentkit/skills/twitter/base.py +68 -0
- intentkit/skills/twitter/follow_user.py +69 -0
- intentkit/skills/twitter/get_mentions.py +124 -0
- intentkit/skills/twitter/get_timeline.py +111 -0
- intentkit/skills/twitter/get_user_by_username.py +84 -0
- intentkit/skills/twitter/get_user_tweets.py +123 -0
- intentkit/skills/twitter/like_tweet.py +65 -0
- intentkit/skills/twitter/post_tweet.py +90 -0
- intentkit/skills/twitter/reply_tweet.py +98 -0
- intentkit/skills/twitter/retweet.py +76 -0
- intentkit/skills/twitter/schema.json +258 -0
- intentkit/skills/twitter/search_tweets.py +115 -0
- intentkit/skills/twitter/twitter.png +0 -0
- intentkit/skills/unrealspeech/__init__.py +55 -0
- intentkit/skills/unrealspeech/base.py +21 -0
- intentkit/skills/unrealspeech/schema.json +100 -0
- intentkit/skills/unrealspeech/text_to_speech.py +177 -0
- intentkit/skills/unrealspeech/unrealspeech.jpg +0 -0
- intentkit/skills/venice_audio/__init__.py +106 -0
- intentkit/skills/venice_audio/base.py +119 -0
- intentkit/skills/venice_audio/input.py +41 -0
- intentkit/skills/venice_audio/schema.json +152 -0
- intentkit/skills/venice_audio/venice_audio.py +240 -0
- intentkit/skills/venice_audio/venice_logo.jpg +0 -0
- intentkit/skills/venice_image/README.md +119 -0
- intentkit/skills/venice_image/__init__.py +154 -0
- intentkit/skills/venice_image/api.py +138 -0
- intentkit/skills/venice_image/base.py +188 -0
- intentkit/skills/venice_image/config.py +35 -0
- intentkit/skills/venice_image/image_enhance/README.md +119 -0
- intentkit/skills/venice_image/image_enhance/__init__.py +0 -0
- intentkit/skills/venice_image/image_enhance/image_enhance.py +80 -0
- intentkit/skills/venice_image/image_enhance/image_enhance_base.py +23 -0
- intentkit/skills/venice_image/image_enhance/image_enhance_input.py +40 -0
- intentkit/skills/venice_image/image_generation/README.md +144 -0
- intentkit/skills/venice_image/image_generation/__init__.py +0 -0
- intentkit/skills/venice_image/image_generation/image_generation_base.py +117 -0
- intentkit/skills/venice_image/image_generation/image_generation_fluently_xl.py +26 -0
- intentkit/skills/venice_image/image_generation/image_generation_flux_dev.py +27 -0
- intentkit/skills/venice_image/image_generation/image_generation_flux_dev_uncensored.py +26 -0
- intentkit/skills/venice_image/image_generation/image_generation_input.py +158 -0
- intentkit/skills/venice_image/image_generation/image_generation_lustify_sdxl.py +26 -0
- intentkit/skills/venice_image/image_generation/image_generation_pony_realism.py +26 -0
- intentkit/skills/venice_image/image_generation/image_generation_stable_diffusion_3_5.py +28 -0
- intentkit/skills/venice_image/image_generation/image_generation_venice_sd35.py +28 -0
- intentkit/skills/venice_image/image_upscale/README.md +111 -0
- intentkit/skills/venice_image/image_upscale/__init__.py +0 -0
- intentkit/skills/venice_image/image_upscale/image_upscale.py +90 -0
- intentkit/skills/venice_image/image_upscale/image_upscale_base.py +23 -0
- intentkit/skills/venice_image/image_upscale/image_upscale_input.py +22 -0
- intentkit/skills/venice_image/image_vision/README.md +112 -0
- intentkit/skills/venice_image/image_vision/__init__.py +0 -0
- intentkit/skills/venice_image/image_vision/image_vision.py +100 -0
- intentkit/skills/venice_image/image_vision/image_vision_base.py +17 -0
- intentkit/skills/venice_image/image_vision/image_vision_input.py +9 -0
- intentkit/skills/venice_image/schema.json +267 -0
- intentkit/skills/venice_image/utils.py +78 -0
- intentkit/skills/venice_image/venice_image.jpg +0 -0
- intentkit/skills/web_scraper/README.md +82 -0
- intentkit/skills/web_scraper/__init__.py +92 -0
- intentkit/skills/web_scraper/base.py +21 -0
- intentkit/skills/web_scraper/langchain.png +0 -0
- intentkit/skills/web_scraper/schema.json +115 -0
- intentkit/skills/web_scraper/scrape_and_index.py +327 -0
- intentkit/utils/__init__.py +1 -0
- intentkit/utils/chain.py +436 -0
- intentkit/utils/error.py +134 -0
- intentkit/utils/logging.py +70 -0
- intentkit/utils/middleware.py +61 -0
- intentkit/utils/random.py +16 -0
- intentkit/utils/s3.py +267 -0
- intentkit/utils/slack_alert.py +79 -0
- intentkit/utils/tx.py +37 -0
- {intentkit-0.5.0.dist-info → intentkit-0.5.2.dist-info}/METADATA +1 -1
- intentkit-0.5.2.dist-info/RECORD +365 -0
- intentkit-0.5.0.dist-info/RECORD +0 -4
- {intentkit-0.5.0.dist-info → intentkit-0.5.2.dist-info}/WHEEL +0 -0
- {intentkit-0.5.0.dist-info → intentkit-0.5.2.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,511 @@
|
|
|
1
|
+
"""Tests for the Moralis Wallet Portfolio skills."""
|
|
2
|
+
|
|
3
|
+
import asyncio
|
|
4
|
+
import json
|
|
5
|
+
import unittest
|
|
6
|
+
from unittest.mock import AsyncMock, MagicMock, patch
|
|
7
|
+
|
|
8
|
+
from intentkit.skills.moralis import (
|
|
9
|
+
FetchChainPortfolio,
|
|
10
|
+
FetchSolanaPortfolio,
|
|
11
|
+
FetchWalletPortfolio,
|
|
12
|
+
get_skills,
|
|
13
|
+
)
|
|
14
|
+
from intentkit.skills.moralis.api import (
|
|
15
|
+
fetch_moralis_data,
|
|
16
|
+
fetch_wallet_balances,
|
|
17
|
+
get_solana_portfolio,
|
|
18
|
+
)
|
|
19
|
+
from intentkit.skills.moralis.base import WalletBaseTool
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class DummyResponse:
|
|
23
|
+
"""Mock HTTP response for testing."""
|
|
24
|
+
|
|
25
|
+
def __init__(self, status_code, json_data):
|
|
26
|
+
self.status_code = status_code
|
|
27
|
+
self._json_data = json_data
|
|
28
|
+
self.text = json.dumps(json_data) if json_data else ""
|
|
29
|
+
|
|
30
|
+
def json(self):
|
|
31
|
+
return self._json_data
|
|
32
|
+
|
|
33
|
+
async def raise_for_status(self):
|
|
34
|
+
if self.status_code >= 400:
|
|
35
|
+
raise Exception(f"HTTP Error: {self.status_code}")
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class TestWalletBaseClass(unittest.TestCase):
|
|
39
|
+
"""Test the base wallet portfolio tool class."""
|
|
40
|
+
|
|
41
|
+
def setUp(self):
|
|
42
|
+
self.loop = asyncio.new_event_loop()
|
|
43
|
+
asyncio.set_event_loop(self.loop)
|
|
44
|
+
|
|
45
|
+
self.mock_skill_store = MagicMock()
|
|
46
|
+
|
|
47
|
+
def tearDown(self):
|
|
48
|
+
self.loop.close()
|
|
49
|
+
|
|
50
|
+
def test_base_class_init(self):
|
|
51
|
+
"""Test base class initialization."""
|
|
52
|
+
|
|
53
|
+
# Create a concrete subclass for testing
|
|
54
|
+
class TestTool(WalletBaseTool):
|
|
55
|
+
async def _arun(self, *args, **kwargs):
|
|
56
|
+
return "test"
|
|
57
|
+
|
|
58
|
+
tool = TestTool(
|
|
59
|
+
name="test_tool",
|
|
60
|
+
description="Test tool",
|
|
61
|
+
args_schema=MagicMock(),
|
|
62
|
+
api_key="test_key",
|
|
63
|
+
skill_store=self.mock_skill_store,
|
|
64
|
+
agent_id="test_agent",
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
self.assertEqual(tool.api_key, "test_key")
|
|
68
|
+
self.assertEqual(tool.agent_id, "test_agent")
|
|
69
|
+
self.assertEqual(tool.skill_store, self.mock_skill_store)
|
|
70
|
+
self.assertEqual(tool.category, "moralis")
|
|
71
|
+
|
|
72
|
+
def test_get_chain_name(self):
|
|
73
|
+
"""Test chain name conversion."""
|
|
74
|
+
|
|
75
|
+
class TestTool(WalletBaseTool):
|
|
76
|
+
async def _arun(self, *args, **kwargs):
|
|
77
|
+
return "test"
|
|
78
|
+
|
|
79
|
+
tool = TestTool(
|
|
80
|
+
name="test_tool",
|
|
81
|
+
description="Test tool",
|
|
82
|
+
args_schema=MagicMock(),
|
|
83
|
+
api_key="test_key",
|
|
84
|
+
skill_store=self.mock_skill_store,
|
|
85
|
+
agent_id="test_agent",
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
# Test with known chain IDs
|
|
89
|
+
self.assertEqual(tool._get_chain_name(1), "eth")
|
|
90
|
+
self.assertEqual(tool._get_chain_name(56), "bsc")
|
|
91
|
+
self.assertEqual(tool._get_chain_name(137), "polygon")
|
|
92
|
+
|
|
93
|
+
# Test with unknown chain ID
|
|
94
|
+
self.assertEqual(tool._get_chain_name(999999), "eth")
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
class TestAPIFunctions(unittest.IsolatedAsyncioTestCase):
|
|
98
|
+
"""Test the API interaction functions."""
|
|
99
|
+
|
|
100
|
+
async def test_fetch_moralis_data(self):
|
|
101
|
+
"""Test the base Moralis API function."""
|
|
102
|
+
with patch("httpx.AsyncClient") as MockClient:
|
|
103
|
+
client_instance = AsyncMock()
|
|
104
|
+
client_instance.get.return_value = DummyResponse(
|
|
105
|
+
200, {"success": True, "data": "test_data"}
|
|
106
|
+
)
|
|
107
|
+
MockClient.return_value.__aenter__.return_value = client_instance
|
|
108
|
+
|
|
109
|
+
result = await fetch_moralis_data(
|
|
110
|
+
"test_api_key", "test_endpoint", "0xAddress", 1
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
self.assertEqual(result, {"success": True, "data": "test_data"})
|
|
114
|
+
|
|
115
|
+
# Test error handling
|
|
116
|
+
client_instance.get.return_value = DummyResponse(404, None)
|
|
117
|
+
client_instance.get.return_value.raise_for_status = AsyncMock(
|
|
118
|
+
side_effect=Exception("HTTP error 404")
|
|
119
|
+
)
|
|
120
|
+
|
|
121
|
+
result = await fetch_moralis_data(
|
|
122
|
+
"test_api_key", "test_endpoint", "0xAddress", 1
|
|
123
|
+
)
|
|
124
|
+
self.assertIn("error", result)
|
|
125
|
+
|
|
126
|
+
async def test_fetch_wallet_balances(self):
|
|
127
|
+
"""Test fetching wallet balances."""
|
|
128
|
+
with patch("skills.moralis.api.fetch_moralis_data") as mock_fetch:
|
|
129
|
+
mock_fetch.return_value = {
|
|
130
|
+
"result": [
|
|
131
|
+
{
|
|
132
|
+
"token_address": "0x123",
|
|
133
|
+
"symbol": "TEST",
|
|
134
|
+
"balance": "1000000",
|
|
135
|
+
"usd_value": 100,
|
|
136
|
+
}
|
|
137
|
+
]
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
result = await fetch_wallet_balances("test_api_key", "0xAddress", 1)
|
|
141
|
+
|
|
142
|
+
self.assertEqual(result["result"][0]["symbol"], "TEST")
|
|
143
|
+
mock_fetch.assert_called_once_with(
|
|
144
|
+
"test_api_key", "wallets/{address}/tokens", "0xAddress", 1, None
|
|
145
|
+
)
|
|
146
|
+
|
|
147
|
+
async def test_get_solana_portfolio(self):
|
|
148
|
+
"""Test getting Solana portfolio."""
|
|
149
|
+
with patch("skills.moralis.api.fetch_solana_api") as mock_fetch:
|
|
150
|
+
mock_fetch.return_value = {
|
|
151
|
+
"nativeBalance": {"solana": 1.5, "lamports": 1500000000},
|
|
152
|
+
"tokens": [
|
|
153
|
+
{
|
|
154
|
+
"symbol": "TEST",
|
|
155
|
+
"name": "Test Token",
|
|
156
|
+
"mint": "TokenMintAddress",
|
|
157
|
+
"associatedTokenAddress": "AssocTokenAddress",
|
|
158
|
+
"amount": 10,
|
|
159
|
+
"decimals": 9,
|
|
160
|
+
"amountRaw": "10000000000",
|
|
161
|
+
}
|
|
162
|
+
],
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
result = await get_solana_portfolio("test_api_key", "SolAddress", "mainnet")
|
|
166
|
+
|
|
167
|
+
mock_fetch.assert_called_once_with(
|
|
168
|
+
"test_api_key", "/account/mainnet/SolAddress/portfolio"
|
|
169
|
+
)
|
|
170
|
+
self.assertEqual(result["nativeBalance"]["solana"], 1.5)
|
|
171
|
+
self.assertEqual(len(result["tokens"]), 1)
|
|
172
|
+
self.assertEqual(result["tokens"][0]["symbol"], "TEST")
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
class TestFetchWalletPortfolio(unittest.IsolatedAsyncioTestCase):
|
|
176
|
+
"""Test the FetchWalletPortfolio skill."""
|
|
177
|
+
|
|
178
|
+
async def test_wallet_portfolio_success(self):
|
|
179
|
+
"""Test successful wallet portfolio fetch."""
|
|
180
|
+
mock_skill_store = MagicMock()
|
|
181
|
+
|
|
182
|
+
with (
|
|
183
|
+
patch(
|
|
184
|
+
"skills.moralis.moralis_fetch_wallet_portfolio.fetch_wallet_balances"
|
|
185
|
+
) as mock_balances,
|
|
186
|
+
patch(
|
|
187
|
+
"skills.moralis.moralis_fetch_wallet_portfolio.fetch_net_worth"
|
|
188
|
+
) as mock_net_worth,
|
|
189
|
+
):
|
|
190
|
+
# Mock successful responses
|
|
191
|
+
mock_balances.return_value = {
|
|
192
|
+
"result": [
|
|
193
|
+
{
|
|
194
|
+
"token_address": "0x123",
|
|
195
|
+
"symbol": "TEST",
|
|
196
|
+
"name": "Test Token",
|
|
197
|
+
"balance": "1000000000000000000",
|
|
198
|
+
"balance_formatted": "1.0",
|
|
199
|
+
"usd_value": 100,
|
|
200
|
+
}
|
|
201
|
+
]
|
|
202
|
+
}
|
|
203
|
+
mock_net_worth.return_value = {"result": {"total_networth_usd": 1000}}
|
|
204
|
+
|
|
205
|
+
tool = FetchWalletPortfolio(
|
|
206
|
+
name="fetch_wallet_portfolio",
|
|
207
|
+
description="Test description",
|
|
208
|
+
args_schema=MagicMock(),
|
|
209
|
+
api_key="test_key",
|
|
210
|
+
skill_store=mock_skill_store,
|
|
211
|
+
agent_id="test_agent",
|
|
212
|
+
)
|
|
213
|
+
|
|
214
|
+
result = await tool._arun(address="0xAddress")
|
|
215
|
+
|
|
216
|
+
self.assertEqual(result.address, "0xAddress")
|
|
217
|
+
self.assertEqual(result.total_net_worth, 1000)
|
|
218
|
+
self.assertEqual(len(result.tokens), 1)
|
|
219
|
+
self.assertEqual(result.tokens[0].symbol, "TEST")
|
|
220
|
+
|
|
221
|
+
async def test_wallet_portfolio_with_solana(self):
|
|
222
|
+
"""Test wallet portfolio with Solana support."""
|
|
223
|
+
mock_skill_store = MagicMock()
|
|
224
|
+
|
|
225
|
+
with (
|
|
226
|
+
patch(
|
|
227
|
+
"skills.moralis.moralis_fetch_wallet_portfolio.fetch_wallet_balances"
|
|
228
|
+
) as mock_evm_balances,
|
|
229
|
+
patch(
|
|
230
|
+
"skills.moralis.moralis_fetch_wallet_portfolio.fetch_net_worth"
|
|
231
|
+
) as mock_net_worth,
|
|
232
|
+
patch(
|
|
233
|
+
"skills.moralis.moralis_fetch_wallet_portfolio.get_solana_portfolio"
|
|
234
|
+
) as mock_sol_portfolio,
|
|
235
|
+
patch(
|
|
236
|
+
"skills.moralis.moralis_fetch_wallet_portfolio.get_token_price"
|
|
237
|
+
) as mock_token_price,
|
|
238
|
+
):
|
|
239
|
+
# Mock EVM responses
|
|
240
|
+
mock_evm_balances.return_value = {
|
|
241
|
+
"result": [
|
|
242
|
+
{
|
|
243
|
+
"token_address": "0x123",
|
|
244
|
+
"symbol": "ETH",
|
|
245
|
+
"name": "Ethereum",
|
|
246
|
+
"balance": "1000000000000000000",
|
|
247
|
+
"balance_formatted": "1.0",
|
|
248
|
+
"usd_value": 2000,
|
|
249
|
+
}
|
|
250
|
+
]
|
|
251
|
+
}
|
|
252
|
+
mock_net_worth.return_value = {"result": {"total_networth_usd": 3000}}
|
|
253
|
+
|
|
254
|
+
# Mock Solana responses
|
|
255
|
+
mock_sol_portfolio.return_value = {
|
|
256
|
+
"nativeBalance": {"solana": 2.0, "lamports": 2000000000},
|
|
257
|
+
"tokens": [
|
|
258
|
+
{
|
|
259
|
+
"symbol": "SOL",
|
|
260
|
+
"name": "Solana",
|
|
261
|
+
"mint": "So11111111111111111111111111111111111111112",
|
|
262
|
+
"associatedTokenAddress": "AssocTokenAddress",
|
|
263
|
+
"amount": 2.0,
|
|
264
|
+
"decimals": 9,
|
|
265
|
+
"amountRaw": "2000000000",
|
|
266
|
+
}
|
|
267
|
+
],
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
mock_token_price.return_value = {"usdPrice": 500}
|
|
271
|
+
|
|
272
|
+
tool = FetchWalletPortfolio(
|
|
273
|
+
name="fetch_wallet_portfolio",
|
|
274
|
+
description="Test description",
|
|
275
|
+
args_schema=MagicMock(),
|
|
276
|
+
api_key="test_key",
|
|
277
|
+
skill_store=mock_skill_store,
|
|
278
|
+
agent_id="test_agent",
|
|
279
|
+
)
|
|
280
|
+
|
|
281
|
+
result = await tool._arun(address="0xAddress", include_solana=True)
|
|
282
|
+
|
|
283
|
+
self.assertEqual(result.address, "0xAddress")
|
|
284
|
+
self.assertEqual(
|
|
285
|
+
result.total_net_worth, 3000
|
|
286
|
+
) # Using the net worth from mock
|
|
287
|
+
self.assertIn("eth", result.chains)
|
|
288
|
+
self.assertIn("solana", result.chains)
|
|
289
|
+
|
|
290
|
+
# Check that we have both EVM and Solana tokens
|
|
291
|
+
token_symbols = [token.symbol for token in result.tokens]
|
|
292
|
+
self.assertIn("ETH", token_symbols)
|
|
293
|
+
self.assertIn("SOL", token_symbols)
|
|
294
|
+
|
|
295
|
+
|
|
296
|
+
class TestFetchSolanaPortfolio(unittest.IsolatedAsyncioTestCase):
|
|
297
|
+
"""Test the FetchSolanaPortfolio skill."""
|
|
298
|
+
|
|
299
|
+
async def test_solana_portfolio_success(self):
|
|
300
|
+
"""Test successful Solana portfolio fetch."""
|
|
301
|
+
mock_skill_store = MagicMock()
|
|
302
|
+
|
|
303
|
+
with (
|
|
304
|
+
patch(
|
|
305
|
+
"skills.moralis.moralis_fetch_solana_portfolio.get_solana_portfolio"
|
|
306
|
+
) as mock_portfolio,
|
|
307
|
+
patch(
|
|
308
|
+
"skills.moralis.moralis_fetch_solana_portfolio.get_solana_nfts"
|
|
309
|
+
) as mock_nfts,
|
|
310
|
+
patch(
|
|
311
|
+
"skills.moralis.moralis_fetch_solana_portfolio.get_token_price"
|
|
312
|
+
) as mock_token_price,
|
|
313
|
+
):
|
|
314
|
+
# Mock successful responses
|
|
315
|
+
mock_portfolio.return_value = {
|
|
316
|
+
"nativeBalance": {"solana": 1.5, "lamports": 1500000000},
|
|
317
|
+
"tokens": [
|
|
318
|
+
{
|
|
319
|
+
"symbol": "TEST",
|
|
320
|
+
"name": "Test Token",
|
|
321
|
+
"mint": "TokenMintAddress",
|
|
322
|
+
"associatedTokenAddress": "AssocTokenAddress",
|
|
323
|
+
"amount": 10,
|
|
324
|
+
"decimals": 9,
|
|
325
|
+
"amountRaw": "10000000000",
|
|
326
|
+
}
|
|
327
|
+
],
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
mock_nfts.return_value = [
|
|
331
|
+
{
|
|
332
|
+
"mint": "NFTMintAddress",
|
|
333
|
+
"name": "Test NFT",
|
|
334
|
+
"symbol": "TNFT",
|
|
335
|
+
"associatedTokenAddress": "AssocTokenAddress",
|
|
336
|
+
"metadata": {"name": "Test NFT", "image": "image.png"},
|
|
337
|
+
}
|
|
338
|
+
]
|
|
339
|
+
|
|
340
|
+
mock_token_price.return_value = {"usdPrice": 25}
|
|
341
|
+
|
|
342
|
+
tool = FetchSolanaPortfolio(
|
|
343
|
+
name="fetch_solana_portfolio",
|
|
344
|
+
description="Test description",
|
|
345
|
+
args_schema=MagicMock(),
|
|
346
|
+
api_key="test_key",
|
|
347
|
+
skill_store=mock_skill_store,
|
|
348
|
+
agent_id="test_agent",
|
|
349
|
+
)
|
|
350
|
+
|
|
351
|
+
result = await tool._arun(address="SolanaAddress", include_nfts=True)
|
|
352
|
+
|
|
353
|
+
self.assertEqual(result.address, "SolanaAddress")
|
|
354
|
+
self.assertEqual(result.sol_balance, 1.5)
|
|
355
|
+
self.assertEqual(len(result.tokens), 1)
|
|
356
|
+
self.assertEqual(result.tokens[0].token_info.symbol, "TEST")
|
|
357
|
+
self.assertEqual(len(result.nfts), 1)
|
|
358
|
+
self.assertEqual(result.nfts[0].name, "Test NFT")
|
|
359
|
+
self.assertEqual(result.sol_price_usd, 25)
|
|
360
|
+
self.assertEqual(result.sol_value_usd, 37.5) # 1.5 SOL * $25
|
|
361
|
+
|
|
362
|
+
|
|
363
|
+
class TestFetchChainPortfolio(unittest.IsolatedAsyncioTestCase):
|
|
364
|
+
"""Test the FetchChainPortfolio skill."""
|
|
365
|
+
|
|
366
|
+
async def test_chain_portfolio_success(self):
|
|
367
|
+
"""Test successful chain portfolio fetch."""
|
|
368
|
+
mock_skill_store = MagicMock()
|
|
369
|
+
|
|
370
|
+
with patch(
|
|
371
|
+
"skills.moralis.moralis_fetch_chain_portfolio.fetch_wallet_balances"
|
|
372
|
+
) as mock_balances:
|
|
373
|
+
# Mock successful responses
|
|
374
|
+
mock_balances.return_value = {
|
|
375
|
+
"result": [
|
|
376
|
+
{
|
|
377
|
+
"token_address": "0x123",
|
|
378
|
+
"symbol": "ETH",
|
|
379
|
+
"name": "Ethereum",
|
|
380
|
+
"logo": "logo.png",
|
|
381
|
+
"decimals": 18,
|
|
382
|
+
"balance": "1000000000000000000",
|
|
383
|
+
"balance_formatted": "1.0",
|
|
384
|
+
"usd_value": 2000,
|
|
385
|
+
"native_token": True,
|
|
386
|
+
},
|
|
387
|
+
{
|
|
388
|
+
"token_address": "0x456",
|
|
389
|
+
"symbol": "TOKEN",
|
|
390
|
+
"name": "Test Token",
|
|
391
|
+
"logo": "logo2.png",
|
|
392
|
+
"decimals": 18,
|
|
393
|
+
"balance": "2000000000000000000",
|
|
394
|
+
"balance_formatted": "2.0",
|
|
395
|
+
"usd_value": 200,
|
|
396
|
+
"native_token": False,
|
|
397
|
+
},
|
|
398
|
+
]
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
tool = FetchChainPortfolio(
|
|
402
|
+
name="fetch_chain_portfolio",
|
|
403
|
+
description="Test description",
|
|
404
|
+
args_schema=MagicMock(),
|
|
405
|
+
api_key="test_key",
|
|
406
|
+
skill_store=mock_skill_store,
|
|
407
|
+
agent_id="test_agent",
|
|
408
|
+
)
|
|
409
|
+
|
|
410
|
+
result = await tool._arun(address="0xAddress", chain_id=1)
|
|
411
|
+
|
|
412
|
+
self.assertEqual(result.address, "0xAddress")
|
|
413
|
+
self.assertEqual(result.chain_id, 1)
|
|
414
|
+
self.assertEqual(result.chain_name, "eth")
|
|
415
|
+
self.assertEqual(result.total_usd_value, 2200) # 2000 + 200
|
|
416
|
+
self.assertEqual(len(result.tokens), 1) # Regular tokens, not native
|
|
417
|
+
self.assertIsNotNone(result.native_token)
|
|
418
|
+
self.assertEqual(result.native_token.symbol, "ETH")
|
|
419
|
+
self.assertEqual(result.tokens[0].symbol, "TOKEN")
|
|
420
|
+
|
|
421
|
+
|
|
422
|
+
class TestSkillInitialization(unittest.TestCase):
|
|
423
|
+
"""Test skill initialization and configuration."""
|
|
424
|
+
|
|
425
|
+
def setUp(self):
|
|
426
|
+
self.mock_skill_store = MagicMock()
|
|
427
|
+
|
|
428
|
+
def test_get_skills(self):
|
|
429
|
+
"""Test getting multiple skills from config."""
|
|
430
|
+
config = {
|
|
431
|
+
"api_key": "test_api_key",
|
|
432
|
+
"states": {
|
|
433
|
+
"fetch_wallet_portfolio": "public",
|
|
434
|
+
"fetch_chain_portfolio": "public",
|
|
435
|
+
"fetch_nft_portfolio": "private",
|
|
436
|
+
"fetch_transaction_history": "private",
|
|
437
|
+
"fetch_solana_portfolio": "public",
|
|
438
|
+
},
|
|
439
|
+
"supported_chains": {"evm": True, "solana": True},
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
# Test with mock implementation
|
|
443
|
+
with patch("skills.moralis.base.WalletBaseTool") as mock_tool:
|
|
444
|
+
mock_tool.return_value = MagicMock()
|
|
445
|
+
|
|
446
|
+
# This is just a test structure - actual implementation would create the skills
|
|
447
|
+
skills = get_skills(
|
|
448
|
+
config,
|
|
449
|
+
is_private=False, # Only get public skills
|
|
450
|
+
skill_store=self.mock_skill_store,
|
|
451
|
+
agent_id="test_agent",
|
|
452
|
+
)
|
|
453
|
+
|
|
454
|
+
# In a real implementation, we'd test that the correct skills were returned
|
|
455
|
+
# For now, we just verify the function exists
|
|
456
|
+
self.assertIsNotNone(skills)
|
|
457
|
+
|
|
458
|
+
|
|
459
|
+
class TestIntegration(unittest.TestCase):
|
|
460
|
+
"""Integration tests for wallet skills."""
|
|
461
|
+
|
|
462
|
+
def test_wallet_skill_configuration(self):
|
|
463
|
+
"""Test wallet skill configuration in agent config."""
|
|
464
|
+
# Example agent configuration
|
|
465
|
+
agent_config = {
|
|
466
|
+
"id": "crypto-agent",
|
|
467
|
+
"skills": {
|
|
468
|
+
"moralis": {
|
|
469
|
+
"api_key": "test_api_key",
|
|
470
|
+
"states": {
|
|
471
|
+
"fetch_wallet_portfolio": "public",
|
|
472
|
+
"fetch_chain_portfolio": "public",
|
|
473
|
+
"fetch_nft_portfolio": "private",
|
|
474
|
+
"fetch_transaction_history": "private",
|
|
475
|
+
"fetch_solana_portfolio": "public",
|
|
476
|
+
},
|
|
477
|
+
"supported_chains": {"evm": True, "solana": True},
|
|
478
|
+
}
|
|
479
|
+
},
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
# Verify the configuration structure is valid
|
|
483
|
+
moralis_config = agent_config["skills"]["moralis"]
|
|
484
|
+
self.assertIn("api_key", moralis_config)
|
|
485
|
+
self.assertIn("states", moralis_config)
|
|
486
|
+
self.assertIn("supported_chains", moralis_config)
|
|
487
|
+
|
|
488
|
+
# Check that all required skills are configured
|
|
489
|
+
states = moralis_config["states"]
|
|
490
|
+
required_skills = [
|
|
491
|
+
"fetch_wallet_portfolio",
|
|
492
|
+
"fetch_chain_portfolio",
|
|
493
|
+
"fetch_nft_portfolio",
|
|
494
|
+
"fetch_transaction_history",
|
|
495
|
+
"fetch_solana_portfolio",
|
|
496
|
+
]
|
|
497
|
+
|
|
498
|
+
for skill in required_skills:
|
|
499
|
+
self.assertIn(skill, states)
|
|
500
|
+
self.assertIn(states[skill], ["public", "private", "disabled"])
|
|
501
|
+
|
|
502
|
+
# Check chain configuration
|
|
503
|
+
chains = moralis_config["supported_chains"]
|
|
504
|
+
self.assertIn("evm", chains)
|
|
505
|
+
self.assertIn("solana", chains)
|
|
506
|
+
self.assertTrue(isinstance(chains["evm"], bool))
|
|
507
|
+
self.assertTrue(isinstance(chains["solana"], bool))
|
|
508
|
+
|
|
509
|
+
|
|
510
|
+
if __name__ == "__main__":
|
|
511
|
+
unittest.main()
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
from typing import Optional, TypedDict
|
|
3
|
+
|
|
4
|
+
from intentkit.abstracts.skill import SkillStoreABC
|
|
5
|
+
from intentkit.skills.base import SkillConfig, SkillState
|
|
6
|
+
from intentkit.skills.nation.base import NationBaseTool
|
|
7
|
+
from intentkit.skills.nation.nft_check import NftCheck
|
|
8
|
+
|
|
9
|
+
logger = logging.getLogger(__name__)
|
|
10
|
+
|
|
11
|
+
# Cache skills at the system level, because they are stateless
|
|
12
|
+
_cache: dict[str, NationBaseTool] = {}
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class SkillStates(TypedDict):
|
|
16
|
+
nft_check: SkillState
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class Config(SkillConfig):
|
|
20
|
+
"""Configuration for nation skills."""
|
|
21
|
+
|
|
22
|
+
states: SkillStates
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
async def get_skills(
|
|
26
|
+
config: "Config",
|
|
27
|
+
is_private: bool,
|
|
28
|
+
store: SkillStoreABC,
|
|
29
|
+
**_,
|
|
30
|
+
) -> list[NationBaseTool]:
|
|
31
|
+
"""Get all nation skills."""
|
|
32
|
+
available_skills = []
|
|
33
|
+
|
|
34
|
+
# Include skills based on their state
|
|
35
|
+
for skill_name, state in config["states"].items():
|
|
36
|
+
if state == "disabled":
|
|
37
|
+
continue
|
|
38
|
+
elif state == "public" or (state == "private" and is_private):
|
|
39
|
+
available_skills.append(skill_name)
|
|
40
|
+
|
|
41
|
+
# Get each skill using the cached getter
|
|
42
|
+
return [
|
|
43
|
+
skill
|
|
44
|
+
for name in available_skills
|
|
45
|
+
if (skill := get_nation_skill(name, store)) is not None
|
|
46
|
+
]
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def get_nation_skill(
|
|
50
|
+
name: str,
|
|
51
|
+
store: SkillStoreABC,
|
|
52
|
+
) -> Optional[NationBaseTool]:
|
|
53
|
+
"""Get a nation skill by name."""
|
|
54
|
+
if name == "nft_check":
|
|
55
|
+
if name not in _cache:
|
|
56
|
+
_cache[name] = NftCheck(
|
|
57
|
+
skill_store=store,
|
|
58
|
+
)
|
|
59
|
+
return _cache[name]
|
|
60
|
+
else:
|
|
61
|
+
logger.error(f"Unknown Nation skill: {name}")
|
|
62
|
+
return None
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
from typing import Type
|
|
2
|
+
|
|
3
|
+
from pydantic import BaseModel, Field
|
|
4
|
+
|
|
5
|
+
from intentkit.abstracts.skill import SkillStoreABC
|
|
6
|
+
from intentkit.skills.base import IntentKitSkill
|
|
7
|
+
|
|
8
|
+
default_nation_api_url = "http://backend-api"
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class NationBaseTool(IntentKitSkill):
|
|
12
|
+
"""Base class for GitHub tools."""
|
|
13
|
+
|
|
14
|
+
name: str = Field(description="The name of the tool")
|
|
15
|
+
description: str = Field(description="A description of what the tool does")
|
|
16
|
+
args_schema: Type[BaseModel]
|
|
17
|
+
skill_store: SkillStoreABC = Field(
|
|
18
|
+
description="The skill store for persisting data"
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
def get_api_key(self) -> str:
|
|
22
|
+
return self.skill_store.get_system_config("nation_api_key")
|
|
23
|
+
|
|
24
|
+
def get_base_url(self) -> str:
|
|
25
|
+
if self.skill_store.get_system_config("nation_api_url"):
|
|
26
|
+
return self.skill_store.get_system_config("nation_api_url")
|
|
27
|
+
return default_nation_api_url
|
|
28
|
+
|
|
29
|
+
@property
|
|
30
|
+
def category(self) -> str:
|
|
31
|
+
return "nation"
|
|
Binary file
|