intentkit 0.6.13.dev2__py3-none-any.whl → 0.8.17__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.

Files changed (385) hide show
  1. intentkit/__init__.py +1 -1
  2. intentkit/abstracts/agent.py +4 -5
  3. intentkit/abstracts/engine.py +5 -5
  4. intentkit/abstracts/graph.py +14 -7
  5. intentkit/abstracts/skill.py +6 -144
  6. intentkit/abstracts/twitter.py +4 -5
  7. intentkit/clients/__init__.py +5 -2
  8. intentkit/clients/cdp.py +101 -141
  9. intentkit/clients/twitter.py +83 -62
  10. intentkit/clients/web3.py +29 -0
  11. intentkit/config/config.py +8 -5
  12. intentkit/core/agent.py +472 -195
  13. intentkit/core/asset.py +253 -0
  14. intentkit/core/chat.py +51 -0
  15. intentkit/core/client.py +1 -1
  16. intentkit/core/credit.py +460 -130
  17. intentkit/core/engine.py +262 -233
  18. intentkit/core/node.py +15 -16
  19. intentkit/core/prompt.py +62 -28
  20. intentkit/core/scheduler.py +92 -0
  21. intentkit/core/statistics.py +168 -0
  22. intentkit/models/agent.py +1096 -949
  23. intentkit/models/agent_data.py +68 -38
  24. intentkit/models/agent_public.json +98 -0
  25. intentkit/models/agent_schema.json +54 -439
  26. intentkit/models/app_setting.py +96 -33
  27. intentkit/models/chat.py +74 -27
  28. intentkit/models/conversation.py +8 -8
  29. intentkit/models/credit.py +362 -74
  30. intentkit/models/db.py +26 -8
  31. intentkit/models/db_mig.py +2 -2
  32. intentkit/models/llm.csv +28 -0
  33. intentkit/models/llm.py +185 -350
  34. intentkit/models/redis.py +6 -4
  35. intentkit/models/skill.py +186 -72
  36. intentkit/models/skills.csv +174 -0
  37. intentkit/models/user.py +82 -24
  38. intentkit/skills/acolyt/__init__.py +2 -9
  39. intentkit/skills/acolyt/ask.py +3 -4
  40. intentkit/skills/acolyt/base.py +4 -9
  41. intentkit/skills/acolyt/schema.json +4 -3
  42. intentkit/skills/aixbt/__init__.py +2 -13
  43. intentkit/skills/aixbt/base.py +1 -7
  44. intentkit/skills/aixbt/projects.py +14 -15
  45. intentkit/skills/aixbt/schema.json +4 -4
  46. intentkit/skills/allora/__init__.py +2 -9
  47. intentkit/skills/allora/base.py +4 -9
  48. intentkit/skills/allora/price.py +3 -4
  49. intentkit/skills/allora/schema.json +3 -2
  50. intentkit/skills/base.py +248 -85
  51. intentkit/skills/basename/__init__.py +51 -0
  52. intentkit/skills/basename/base.py +11 -0
  53. intentkit/skills/basename/basename.svg +11 -0
  54. intentkit/skills/basename/schema.json +58 -0
  55. intentkit/skills/carv/__init__.py +115 -121
  56. intentkit/skills/carv/base.py +184 -185
  57. intentkit/skills/carv/fetch_news.py +3 -3
  58. intentkit/skills/carv/onchain_query.py +4 -4
  59. intentkit/skills/carv/schema.json +134 -137
  60. intentkit/skills/carv/token_info_and_price.py +5 -5
  61. intentkit/skills/casino/README.md +254 -0
  62. intentkit/skills/casino/__init__.py +86 -0
  63. intentkit/skills/casino/base.py +17 -0
  64. intentkit/skills/casino/casino.png +0 -0
  65. intentkit/skills/casino/deck_draw.py +127 -0
  66. intentkit/skills/casino/deck_shuffle.py +118 -0
  67. intentkit/skills/casino/dice_roll.py +100 -0
  68. intentkit/skills/casino/schema.json +77 -0
  69. intentkit/skills/casino/utils.py +107 -0
  70. intentkit/skills/cdp/__init__.py +22 -84
  71. intentkit/skills/cdp/base.py +1 -7
  72. intentkit/skills/cdp/schema.json +11 -314
  73. intentkit/skills/chainlist/__init__.py +2 -7
  74. intentkit/skills/chainlist/base.py +1 -7
  75. intentkit/skills/chainlist/chain_lookup.py +18 -18
  76. intentkit/skills/chainlist/schema.json +3 -5
  77. intentkit/skills/common/__init__.py +2 -9
  78. intentkit/skills/common/base.py +1 -7
  79. intentkit/skills/common/current_time.py +1 -2
  80. intentkit/skills/common/schema.json +2 -2
  81. intentkit/skills/cookiefun/__init__.py +6 -9
  82. intentkit/skills/cookiefun/base.py +2 -7
  83. intentkit/skills/cookiefun/get_account_details.py +7 -7
  84. intentkit/skills/cookiefun/get_account_feed.py +19 -19
  85. intentkit/skills/cookiefun/get_account_smart_followers.py +7 -7
  86. intentkit/skills/cookiefun/get_sectors.py +3 -3
  87. intentkit/skills/cookiefun/schema.json +1 -3
  88. intentkit/skills/cookiefun/search_accounts.py +9 -9
  89. intentkit/skills/cryptocompare/__init__.py +7 -24
  90. intentkit/skills/cryptocompare/api.py +2 -3
  91. intentkit/skills/cryptocompare/base.py +11 -25
  92. intentkit/skills/cryptocompare/fetch_news.py +4 -5
  93. intentkit/skills/cryptocompare/fetch_price.py +6 -7
  94. intentkit/skills/cryptocompare/fetch_top_exchanges.py +4 -5
  95. intentkit/skills/cryptocompare/fetch_top_market_cap.py +4 -5
  96. intentkit/skills/cryptocompare/fetch_top_volume.py +4 -5
  97. intentkit/skills/cryptocompare/fetch_trading_signals.py +5 -6
  98. intentkit/skills/cryptocompare/schema.json +3 -3
  99. intentkit/skills/cryptopanic/__init__.py +7 -10
  100. intentkit/skills/cryptopanic/base.py +51 -55
  101. intentkit/skills/cryptopanic/fetch_crypto_news.py +4 -8
  102. intentkit/skills/cryptopanic/fetch_crypto_sentiment.py +5 -7
  103. intentkit/skills/cryptopanic/schema.json +105 -103
  104. intentkit/skills/dapplooker/__init__.py +2 -9
  105. intentkit/skills/dapplooker/base.py +4 -9
  106. intentkit/skills/dapplooker/dapplooker_token_data.py +7 -7
  107. intentkit/skills/dapplooker/schema.json +3 -5
  108. intentkit/skills/defillama/__init__.py +24 -74
  109. intentkit/skills/defillama/api.py +6 -9
  110. intentkit/skills/defillama/base.py +11 -21
  111. intentkit/skills/defillama/coins/fetch_batch_historical_prices.py +8 -10
  112. intentkit/skills/defillama/coins/fetch_block.py +6 -8
  113. intentkit/skills/defillama/coins/fetch_current_prices.py +8 -10
  114. intentkit/skills/defillama/coins/fetch_first_price.py +7 -9
  115. intentkit/skills/defillama/coins/fetch_historical_prices.py +9 -11
  116. intentkit/skills/defillama/coins/fetch_price_chart.py +9 -11
  117. intentkit/skills/defillama/coins/fetch_price_percentage.py +7 -9
  118. intentkit/skills/defillama/config/chains.py +1 -3
  119. intentkit/skills/defillama/fees/fetch_fees_overview.py +24 -26
  120. intentkit/skills/defillama/schema.json +5 -1
  121. intentkit/skills/defillama/stablecoins/fetch_stablecoin_chains.py +16 -18
  122. intentkit/skills/defillama/stablecoins/fetch_stablecoin_charts.py +8 -10
  123. intentkit/skills/defillama/stablecoins/fetch_stablecoin_prices.py +5 -7
  124. intentkit/skills/defillama/stablecoins/fetch_stablecoins.py +7 -9
  125. intentkit/skills/defillama/tests/api_integration.test.py +1 -1
  126. intentkit/skills/defillama/tvl/fetch_chain_historical_tvl.py +4 -6
  127. intentkit/skills/defillama/tvl/fetch_chains.py +9 -11
  128. intentkit/skills/defillama/tvl/fetch_historical_tvl.py +4 -6
  129. intentkit/skills/defillama/tvl/fetch_protocol.py +32 -38
  130. intentkit/skills/defillama/tvl/fetch_protocol_current_tvl.py +3 -5
  131. intentkit/skills/defillama/tvl/fetch_protocols.py +37 -45
  132. intentkit/skills/defillama/volumes/fetch_dex_overview.py +42 -48
  133. intentkit/skills/defillama/volumes/fetch_dex_summary.py +35 -37
  134. intentkit/skills/defillama/volumes/fetch_options_overview.py +24 -28
  135. intentkit/skills/defillama/yields/fetch_pool_chart.py +10 -12
  136. intentkit/skills/defillama/yields/fetch_pools.py +26 -30
  137. intentkit/skills/dexscreener/README.md +154 -0
  138. intentkit/skills/dexscreener/__init__.py +97 -93
  139. intentkit/skills/dexscreener/base.py +125 -133
  140. intentkit/skills/dexscreener/get_pair_info.py +158 -0
  141. intentkit/skills/dexscreener/get_token_pairs.py +165 -0
  142. intentkit/skills/dexscreener/get_tokens_info.py +212 -0
  143. intentkit/skills/dexscreener/model/search_token_response.py +80 -82
  144. intentkit/skills/dexscreener/schema.json +91 -48
  145. intentkit/skills/dexscreener/search_token.py +182 -321
  146. intentkit/skills/dexscreener/utils.py +420 -0
  147. intentkit/skills/dune_analytics/__init__.py +7 -9
  148. intentkit/skills/dune_analytics/base.py +48 -52
  149. intentkit/skills/dune_analytics/fetch_kol_buys.py +5 -7
  150. intentkit/skills/dune_analytics/fetch_nation_metrics.py +6 -8
  151. intentkit/skills/dune_analytics/schema.json +104 -99
  152. intentkit/skills/elfa/__init__.py +5 -18
  153. intentkit/skills/elfa/base.py +10 -14
  154. intentkit/skills/elfa/mention.py +19 -21
  155. intentkit/skills/elfa/schema.json +3 -2
  156. intentkit/skills/elfa/stats.py +4 -4
  157. intentkit/skills/elfa/tokens.py +12 -12
  158. intentkit/skills/elfa/utils.py +26 -28
  159. intentkit/skills/enso/__init__.py +11 -31
  160. intentkit/skills/enso/base.py +50 -35
  161. intentkit/skills/enso/best_yield.py +16 -24
  162. intentkit/skills/enso/networks.py +6 -11
  163. intentkit/skills/enso/prices.py +11 -13
  164. intentkit/skills/enso/route.py +34 -38
  165. intentkit/skills/enso/schema.json +3 -2
  166. intentkit/skills/enso/tokens.py +29 -38
  167. intentkit/skills/enso/wallet.py +76 -191
  168. intentkit/skills/erc20/__init__.py +50 -0
  169. intentkit/skills/erc20/base.py +11 -0
  170. intentkit/skills/erc20/erc20.svg +5 -0
  171. intentkit/skills/erc20/schema.json +74 -0
  172. intentkit/skills/erc721/__init__.py +53 -0
  173. intentkit/skills/erc721/base.py +11 -0
  174. intentkit/skills/erc721/erc721.svg +5 -0
  175. intentkit/skills/erc721/schema.json +90 -0
  176. intentkit/skills/firecrawl/README.md +11 -5
  177. intentkit/skills/firecrawl/__init__.py +5 -18
  178. intentkit/skills/firecrawl/base.py +4 -11
  179. intentkit/skills/firecrawl/clear.py +4 -8
  180. intentkit/skills/firecrawl/crawl.py +19 -19
  181. intentkit/skills/firecrawl/query.py +4 -3
  182. intentkit/skills/firecrawl/schema.json +6 -8
  183. intentkit/skills/firecrawl/scrape.py +150 -40
  184. intentkit/skills/firecrawl/utils.py +50 -42
  185. intentkit/skills/github/__init__.py +2 -7
  186. intentkit/skills/github/base.py +1 -7
  187. intentkit/skills/github/github_search.py +1 -2
  188. intentkit/skills/github/schema.json +3 -4
  189. intentkit/skills/heurist/__init__.py +8 -27
  190. intentkit/skills/heurist/base.py +4 -9
  191. intentkit/skills/heurist/image_generation_animagine_xl.py +12 -13
  192. intentkit/skills/heurist/image_generation_arthemy_comics.py +12 -13
  193. intentkit/skills/heurist/image_generation_arthemy_real.py +12 -13
  194. intentkit/skills/heurist/image_generation_braindance.py +12 -13
  195. intentkit/skills/heurist/image_generation_cyber_realistic_xl.py +12 -13
  196. intentkit/skills/heurist/image_generation_flux_1_dev.py +12 -13
  197. intentkit/skills/heurist/image_generation_sdxl.py +12 -13
  198. intentkit/skills/heurist/schema.json +2 -2
  199. intentkit/skills/http/__init__.py +4 -15
  200. intentkit/skills/http/base.py +1 -7
  201. intentkit/skills/http/get.py +21 -16
  202. intentkit/skills/http/post.py +23 -18
  203. intentkit/skills/http/put.py +23 -18
  204. intentkit/skills/http/schema.json +4 -5
  205. intentkit/skills/lifi/__init__.py +8 -13
  206. intentkit/skills/lifi/base.py +1 -7
  207. intentkit/skills/lifi/schema.json +17 -8
  208. intentkit/skills/lifi/token_execute.py +36 -30
  209. intentkit/skills/lifi/token_quote.py +8 -10
  210. intentkit/skills/lifi/utils.py +104 -51
  211. intentkit/skills/moralis/__init__.py +6 -10
  212. intentkit/skills/moralis/api.py +6 -7
  213. intentkit/skills/moralis/base.py +5 -10
  214. intentkit/skills/moralis/fetch_chain_portfolio.py +10 -11
  215. intentkit/skills/moralis/fetch_nft_portfolio.py +22 -22
  216. intentkit/skills/moralis/fetch_solana_portfolio.py +11 -12
  217. intentkit/skills/moralis/fetch_wallet_portfolio.py +8 -9
  218. intentkit/skills/moralis/schema.json +7 -2
  219. intentkit/skills/morpho/__init__.py +52 -0
  220. intentkit/skills/morpho/base.py +11 -0
  221. intentkit/skills/morpho/morpho.svg +12 -0
  222. intentkit/skills/morpho/schema.json +73 -0
  223. intentkit/skills/nation/__init__.py +4 -9
  224. intentkit/skills/nation/base.py +5 -10
  225. intentkit/skills/nation/nft_check.py +3 -4
  226. intentkit/skills/nation/schema.json +4 -3
  227. intentkit/skills/onchain.py +23 -0
  228. intentkit/skills/openai/__init__.py +17 -18
  229. intentkit/skills/openai/base.py +10 -14
  230. intentkit/skills/openai/dalle_image_generation.py +3 -8
  231. intentkit/skills/openai/gpt_avatar_generator.py +102 -0
  232. intentkit/skills/openai/gpt_image_generation.py +4 -8
  233. intentkit/skills/openai/gpt_image_mini_generator.py +91 -0
  234. intentkit/skills/openai/gpt_image_to_image.py +4 -8
  235. intentkit/skills/openai/image_to_text.py +3 -7
  236. intentkit/skills/openai/schema.json +34 -3
  237. intentkit/skills/portfolio/__init__.py +11 -35
  238. intentkit/skills/portfolio/base.py +33 -19
  239. intentkit/skills/portfolio/schema.json +3 -5
  240. intentkit/skills/portfolio/token_balances.py +21 -21
  241. intentkit/skills/portfolio/wallet_approvals.py +17 -18
  242. intentkit/skills/portfolio/wallet_defi_positions.py +3 -3
  243. intentkit/skills/portfolio/wallet_history.py +31 -31
  244. intentkit/skills/portfolio/wallet_net_worth.py +13 -13
  245. intentkit/skills/portfolio/wallet_nfts.py +19 -19
  246. intentkit/skills/portfolio/wallet_profitability.py +18 -18
  247. intentkit/skills/portfolio/wallet_profitability_summary.py +5 -5
  248. intentkit/skills/portfolio/wallet_stats.py +3 -3
  249. intentkit/skills/portfolio/wallet_swaps.py +19 -19
  250. intentkit/skills/pyth/__init__.py +50 -0
  251. intentkit/skills/pyth/base.py +11 -0
  252. intentkit/skills/pyth/pyth.svg +6 -0
  253. intentkit/skills/pyth/schema.json +75 -0
  254. intentkit/skills/skills.toml +40 -0
  255. intentkit/skills/slack/__init__.py +5 -17
  256. intentkit/skills/slack/base.py +3 -9
  257. intentkit/skills/slack/get_channel.py +8 -8
  258. intentkit/skills/slack/get_message.py +9 -9
  259. intentkit/skills/slack/schedule_message.py +5 -5
  260. intentkit/skills/slack/schema.json +2 -2
  261. intentkit/skills/slack/send_message.py +3 -5
  262. intentkit/skills/supabase/__init__.py +7 -23
  263. intentkit/skills/supabase/base.py +9 -13
  264. intentkit/skills/supabase/delete_data.py +5 -6
  265. intentkit/skills/supabase/fetch_data.py +13 -14
  266. intentkit/skills/supabase/insert_data.py +5 -6
  267. intentkit/skills/supabase/invoke_function.py +7 -8
  268. intentkit/skills/supabase/schema.json +2 -3
  269. intentkit/skills/supabase/update_data.py +7 -8
  270. intentkit/skills/supabase/upsert_data.py +5 -6
  271. intentkit/skills/superfluid/__init__.py +53 -0
  272. intentkit/skills/superfluid/base.py +11 -0
  273. intentkit/skills/superfluid/schema.json +89 -0
  274. intentkit/skills/superfluid/superfluid.svg +6 -0
  275. intentkit/skills/system/__init__.py +7 -24
  276. intentkit/skills/system/add_autonomous_task.py +10 -12
  277. intentkit/skills/system/delete_autonomous_task.py +2 -2
  278. intentkit/skills/system/edit_autonomous_task.py +14 -18
  279. intentkit/skills/system/list_autonomous_tasks.py +3 -5
  280. intentkit/skills/system/read_agent_api_key.py +6 -4
  281. intentkit/skills/system/regenerate_agent_api_key.py +6 -4
  282. intentkit/skills/system/schema.json +6 -8
  283. intentkit/skills/tavily/__init__.py +3 -12
  284. intentkit/skills/tavily/base.py +4 -9
  285. intentkit/skills/tavily/schema.json +3 -5
  286. intentkit/skills/tavily/tavily_extract.py +2 -4
  287. intentkit/skills/tavily/tavily_search.py +4 -6
  288. intentkit/skills/token/__init__.py +5 -10
  289. intentkit/skills/token/base.py +7 -11
  290. intentkit/skills/token/erc20_transfers.py +19 -19
  291. intentkit/skills/token/schema.json +3 -6
  292. intentkit/skills/token/token_analytics.py +3 -3
  293. intentkit/skills/token/token_price.py +13 -13
  294. intentkit/skills/token/token_search.py +9 -9
  295. intentkit/skills/twitter/__init__.py +11 -35
  296. intentkit/skills/twitter/base.py +23 -35
  297. intentkit/skills/twitter/follow_user.py +3 -7
  298. intentkit/skills/twitter/get_mentions.py +6 -13
  299. intentkit/skills/twitter/get_timeline.py +5 -13
  300. intentkit/skills/twitter/get_user_by_username.py +3 -7
  301. intentkit/skills/twitter/get_user_tweets.py +6 -14
  302. intentkit/skills/twitter/like_tweet.py +3 -7
  303. intentkit/skills/twitter/post_tweet.py +23 -12
  304. intentkit/skills/twitter/reply_tweet.py +21 -12
  305. intentkit/skills/twitter/retweet.py +3 -7
  306. intentkit/skills/twitter/schema.json +1 -0
  307. intentkit/skills/twitter/search_tweets.py +5 -13
  308. intentkit/skills/unrealspeech/__init__.py +2 -7
  309. intentkit/skills/unrealspeech/base.py +2 -8
  310. intentkit/skills/unrealspeech/schema.json +2 -5
  311. intentkit/skills/unrealspeech/text_to_speech.py +8 -8
  312. intentkit/skills/venice_audio/__init__.py +98 -106
  313. intentkit/skills/venice_audio/base.py +117 -121
  314. intentkit/skills/venice_audio/input.py +41 -41
  315. intentkit/skills/venice_audio/schema.json +151 -152
  316. intentkit/skills/venice_audio/venice_audio.py +38 -21
  317. intentkit/skills/venice_image/__init__.py +147 -154
  318. intentkit/skills/venice_image/api.py +138 -138
  319. intentkit/skills/venice_image/base.py +185 -192
  320. intentkit/skills/venice_image/config.py +33 -35
  321. intentkit/skills/venice_image/image_enhance/image_enhance.py +2 -3
  322. intentkit/skills/venice_image/image_enhance/image_enhance_base.py +21 -23
  323. intentkit/skills/venice_image/image_enhance/image_enhance_input.py +38 -40
  324. intentkit/skills/venice_image/image_generation/image_generation_base.py +9 -9
  325. intentkit/skills/venice_image/image_generation/image_generation_fluently_xl.py +26 -26
  326. intentkit/skills/venice_image/image_generation/image_generation_flux_dev.py +27 -27
  327. intentkit/skills/venice_image/image_generation/image_generation_flux_dev_uncensored.py +26 -26
  328. intentkit/skills/venice_image/image_generation/image_generation_input.py +158 -158
  329. intentkit/skills/venice_image/image_generation/image_generation_lustify_sdxl.py +26 -26
  330. intentkit/skills/venice_image/image_generation/image_generation_pony_realism.py +26 -26
  331. intentkit/skills/venice_image/image_generation/image_generation_stable_diffusion_3_5.py +28 -28
  332. intentkit/skills/venice_image/image_generation/image_generation_venice_sd35.py +28 -28
  333. intentkit/skills/venice_image/image_upscale/image_upscale.py +3 -3
  334. intentkit/skills/venice_image/image_upscale/image_upscale_base.py +21 -23
  335. intentkit/skills/venice_image/image_upscale/image_upscale_input.py +22 -22
  336. intentkit/skills/venice_image/image_vision/image_vision.py +2 -2
  337. intentkit/skills/venice_image/image_vision/image_vision_base.py +17 -17
  338. intentkit/skills/venice_image/image_vision/image_vision_input.py +9 -9
  339. intentkit/skills/venice_image/schema.json +267 -267
  340. intentkit/skills/venice_image/utils.py +77 -78
  341. intentkit/skills/web_scraper/__init__.py +5 -18
  342. intentkit/skills/web_scraper/base.py +21 -7
  343. intentkit/skills/web_scraper/document_indexer.py +7 -6
  344. intentkit/skills/web_scraper/schema.json +2 -6
  345. intentkit/skills/web_scraper/scrape_and_index.py +15 -15
  346. intentkit/skills/web_scraper/utils.py +62 -63
  347. intentkit/skills/web_scraper/website_indexer.py +17 -19
  348. intentkit/skills/weth/__init__.py +49 -0
  349. intentkit/skills/weth/base.py +11 -0
  350. intentkit/skills/weth/schema.json +58 -0
  351. intentkit/skills/weth/weth.svg +6 -0
  352. intentkit/skills/wow/__init__.py +51 -0
  353. intentkit/skills/wow/base.py +11 -0
  354. intentkit/skills/wow/schema.json +89 -0
  355. intentkit/skills/wow/wow.svg +7 -0
  356. intentkit/skills/x402/__init__.py +61 -0
  357. intentkit/skills/x402/ask_agent.py +98 -0
  358. intentkit/skills/x402/base.py +99 -0
  359. intentkit/skills/x402/http_request.py +117 -0
  360. intentkit/skills/x402/schema.json +45 -0
  361. intentkit/skills/x402/x402.webp +0 -0
  362. intentkit/skills/xmtp/__init__.py +4 -15
  363. intentkit/skills/xmtp/base.py +61 -2
  364. intentkit/skills/xmtp/price.py +18 -13
  365. intentkit/skills/xmtp/schema.json +69 -71
  366. intentkit/skills/xmtp/swap.py +22 -25
  367. intentkit/skills/xmtp/transfer.py +71 -32
  368. intentkit/utils/chain.py +3 -3
  369. intentkit/utils/error.py +14 -1
  370. intentkit/utils/logging.py +2 -4
  371. intentkit/utils/s3.py +59 -7
  372. intentkit/utils/schema.py +100 -0
  373. intentkit/utils/slack_alert.py +7 -8
  374. {intentkit-0.6.13.dev2.dist-info → intentkit-0.8.17.dist-info}/METADATA +14 -16
  375. intentkit-0.8.17.dist-info/RECORD +466 -0
  376. intentkit/abstracts/exception.py +0 -9
  377. intentkit/core/skill.py +0 -200
  378. intentkit/models/generator.py +0 -347
  379. intentkit/skills/cdp/get_balance.py +0 -110
  380. intentkit/skills/cdp/swap.py +0 -121
  381. intentkit/skills/moralis/tests/__init__.py +0 -0
  382. intentkit/skills/moralis/tests/test_wallet.py +0 -511
  383. intentkit-0.6.13.dev2.dist-info/RECORD +0 -409
  384. {intentkit-0.6.13.dev2.dist-info → intentkit-0.8.17.dist-info}/WHEEL +0 -0
  385. {intentkit-0.6.13.dev2.dist-info → intentkit-0.8.17.dist-info}/licenses/LICENSE +0 -0
@@ -3,12 +3,13 @@
3
3
  import asyncio
4
4
  import logging
5
5
  from abc import ABC
6
- from typing import Any, Dict, Type
6
+ from typing import Any
7
7
 
8
8
  import aiohttp
9
+ from langchain_core.tools import ToolException
9
10
  from pydantic import BaseModel, Field
10
11
 
11
- from intentkit.abstracts.skill import SkillStoreABC
12
+ from intentkit.config.config import config
12
13
  from intentkit.skills.base import IntentKitSkill
13
14
  from intentkit.skills.portfolio.constants import MORALIS_API_BASE_URL
14
15
 
@@ -20,23 +21,26 @@ class PortfolioBaseTool(IntentKitSkill, ABC):
20
21
 
21
22
  name: str = Field(description="The name of the tool")
22
23
  description: str = Field(description="A description of what the tool does")
23
- args_schema: Type[BaseModel]
24
- skill_store: SkillStoreABC = Field(
25
- description="The skill store for persisting data"
26
- )
24
+ args_schema: type[BaseModel]
27
25
 
28
26
  def get_api_key(self) -> str:
29
27
  context = self.get_context()
30
28
  skill_config = context.agent.skill_config(self.category)
31
29
  if skill_config.get("api_key_provider") == "agent_owner":
32
- return skill_config.get("api_key")
33
- return self.skill_store.get_system_config("moralis_api_key")
30
+ api_key = skill_config.get("api_key")
31
+ else:
32
+ api_key = config.moralis_api_key
33
+
34
+ if not api_key:
35
+ raise ToolException("Moralis API key is not configured.")
36
+
37
+ return api_key
34
38
 
35
39
  @property
36
40
  def category(self) -> str:
37
41
  return "portfolio"
38
42
 
39
- def _prepare_params(self, params: Dict[str, Any]) -> Dict[str, Any]:
43
+ def _prepare_params(self, params: dict[str, Any]) -> dict[str, Any]:
40
44
  """Convert boolean values to lowercase strings for API compatibility.
41
45
 
42
46
  Args:
@@ -61,9 +65,9 @@ class PortfolioBaseTool(IntentKitSkill, ABC):
61
65
  method: str,
62
66
  endpoint: str,
63
67
  api_key: str,
64
- params: Dict[str, Any] = None,
65
- data: Dict[str, Any] = None,
66
- ) -> Dict[str, Any]:
68
+ params: dict[str, Any] = None,
69
+ data: dict[str, Any] = None,
70
+ ) -> dict[str, Any]:
67
71
  """Make a request to the Moralis API.
68
72
 
69
73
  Args:
@@ -95,13 +99,23 @@ class PortfolioBaseTool(IntentKitSkill, ABC):
95
99
  ) as response:
96
100
  if response.status >= 400:
97
101
  error_text = await response.text()
98
- logger.error(f"portfolio/base.py: API error: {error_text}")
99
- return {
100
- "error": f"API error: {response.status}",
101
- "details": error_text,
102
- }
103
-
104
- return await response.json()
102
+ logger.error(
103
+ "portfolio/base.py: API error %s for %s", response.status, url
104
+ )
105
+ raise ToolException(
106
+ f"Moralis API error: {response.status} - {error_text}"
107
+ )
108
+
109
+ try:
110
+ return await response.json()
111
+ except aiohttp.ContentTypeError as exc:
112
+ await response.text()
113
+ logger.error(
114
+ "portfolio/base.py: Failed to decode JSON response from %s", url
115
+ )
116
+ raise ToolException(
117
+ "Moralis API returned invalid JSON payload."
118
+ ) from exc
105
119
 
106
120
  def _run(self, *args: Any, **kwargs: Any) -> Any:
107
121
  """Execute the tool synchronously by running the async version in a loop."""
@@ -5,18 +5,16 @@
5
5
  "description": "Access blockchain wallet data and analytics through Moralis APIs for portfolio tracking, token balances, and investment performance",
6
6
  "x-icon": "https://ai.service.crestal.dev/skills/portfolio/moralis.png",
7
7
  "x-tags": [
8
- "Blockchain",
9
- "Web3",
8
+ "Analytics",
10
9
  "Crypto",
11
- "Portfolio",
12
- "Wallet"
10
+ "DeFi"
13
11
  ],
14
12
  "properties": {
15
13
  "enabled": {
16
14
  "type": "boolean",
17
15
  "title": "Enabled",
18
16
  "description": "Whether this skill is enabled",
19
- "default": true
17
+ "default": false
20
18
  },
21
19
  "states": {
22
20
  "type": "object",
@@ -1,5 +1,5 @@
1
1
  import logging
2
- from typing import Any, Dict, List, Optional, Type
2
+ from typing import Any
3
3
 
4
4
  from pydantic import BaseModel, Field
5
5
 
@@ -20,39 +20,39 @@ class TokenBalancesInput(BaseModel):
20
20
  description="The chain to query (e.g., 'eth', 'bsc', 'polygon').",
21
21
  default=DEFAULT_CHAIN,
22
22
  )
23
- to_block: Optional[int] = Field(
23
+ to_block: int | None = Field(
24
24
  description="The block number up to which the balances will be checked.",
25
25
  default=None,
26
26
  )
27
- token_addresses: Optional[List[str]] = Field(
27
+ token_addresses: list[str] | None = Field(
28
28
  description="The specific token addresses to get balances for.",
29
29
  default=None,
30
30
  )
31
- exclude_spam: Optional[bool] = Field(
31
+ exclude_spam: bool | None = Field(
32
32
  description="Exclude spam tokens from the result.",
33
33
  default=True,
34
34
  )
35
- exclude_unverified_contracts: Optional[bool] = Field(
35
+ exclude_unverified_contracts: bool | None = Field(
36
36
  description="Exclude unverified contracts from the result.",
37
37
  default=True,
38
38
  )
39
- cursor: Optional[str] = Field(
39
+ cursor: str | None = Field(
40
40
  description="The cursor for pagination.",
41
41
  default=None,
42
42
  )
43
- limit: Optional[int] = Field(
43
+ limit: int | None = Field(
44
44
  description="The number of results per page.",
45
45
  default=DEFAULT_LIMIT,
46
46
  )
47
- exclude_native: Optional[bool] = Field(
47
+ exclude_native: bool | None = Field(
48
48
  description="Exclude native balance from the result.",
49
49
  default=None,
50
50
  )
51
- max_token_inactivity: Optional[int] = Field(
51
+ max_token_inactivity: int | None = Field(
52
52
  description="Exclude tokens inactive for more than the given amount of days.",
53
53
  default=None,
54
54
  )
55
- min_pair_side_liquidity_usd: Optional[float] = Field(
55
+ min_pair_side_liquidity_usd: float | None = Field(
56
56
  description="Exclude tokens with liquidity less than the specified amount in USD.",
57
57
  default=None,
58
58
  )
@@ -70,23 +70,23 @@ class TokenBalances(PortfolioBaseTool):
70
70
  "Get token balances for a specific wallet address and their token prices in USD. "
71
71
  "Includes options to exclude spam and unverified contracts."
72
72
  )
73
- args_schema: Type[BaseModel] = TokenBalancesInput
73
+ args_schema: type[BaseModel] = TokenBalancesInput
74
74
 
75
75
  async def _arun(
76
76
  self,
77
77
  address: str,
78
78
  chain: str = DEFAULT_CHAIN,
79
- to_block: Optional[int] = None,
80
- token_addresses: Optional[List[str]] = None,
81
- exclude_spam: Optional[bool] = True,
82
- exclude_unverified_contracts: Optional[bool] = True,
83
- cursor: Optional[str] = None,
84
- limit: Optional[int] = DEFAULT_LIMIT,
85
- exclude_native: Optional[bool] = None,
86
- max_token_inactivity: Optional[int] = None,
87
- min_pair_side_liquidity_usd: Optional[float] = None,
79
+ to_block: int | None = None,
80
+ token_addresses: list[str] | None = None,
81
+ exclude_spam: bool | None = True,
82
+ exclude_unverified_contracts: bool | None = True,
83
+ cursor: str | None = None,
84
+ limit: int | None = DEFAULT_LIMIT,
85
+ exclude_native: bool | None = None,
86
+ max_token_inactivity: int | None = None,
87
+ min_pair_side_liquidity_usd: float | None = None,
88
88
  **kwargs,
89
- ) -> Dict[str, Any]:
89
+ ) -> dict[str, Any]:
90
90
  """Fetch token balances from Moralis.
91
91
 
92
92
  Args:
@@ -1,6 +1,7 @@
1
1
  import logging
2
- from typing import Any, Dict, Optional, Type
2
+ from typing import Any
3
3
 
4
+ from langchain_core.tools import ToolException
4
5
  from pydantic import BaseModel, Field
5
6
 
6
7
  from intentkit.skills.portfolio.base import PortfolioBaseTool
@@ -20,11 +21,11 @@ class WalletApprovalsInput(BaseModel):
20
21
  description="The chain to query (e.g., 'eth', 'bsc', 'polygon').",
21
22
  default=DEFAULT_CHAIN,
22
23
  )
23
- cursor: Optional[str] = Field(
24
+ cursor: str | None = Field(
24
25
  description="The cursor for pagination.",
25
26
  default=None,
26
27
  )
27
- limit: Optional[int] = Field(
28
+ limit: int | None = Field(
28
29
  description="The number of results per page.",
29
30
  default=DEFAULT_LIMIT,
30
31
  )
@@ -42,16 +43,16 @@ class WalletApprovals(PortfolioBaseTool):
42
43
  "Retrieve active ERC20 token approvals for the specified wallet address. "
43
44
  "This helps identify which contracts have permission to spend tokens."
44
45
  )
45
- args_schema: Type[BaseModel] = WalletApprovalsInput
46
+ args_schema: type[BaseModel] = WalletApprovalsInput
46
47
 
47
48
  async def _arun(
48
49
  self,
49
50
  address: str,
50
51
  chain: str = DEFAULT_CHAIN,
51
- cursor: Optional[str] = None,
52
- limit: Optional[int] = DEFAULT_LIMIT,
52
+ cursor: str | None = None,
53
+ limit: int | None = DEFAULT_LIMIT,
53
54
  **kwargs,
54
- ) -> Dict[str, Any]:
55
+ ) -> dict[str, Any]:
55
56
  """Fetch wallet token approvals from Moralis.
56
57
 
57
58
  Args:
@@ -69,11 +70,6 @@ class WalletApprovals(PortfolioBaseTool):
69
70
  f"wallet_approvals.py: Fetching wallet approvals with context {context}"
70
71
  )
71
72
 
72
- # Get the API key from the agent's configuration
73
- api_key = self.get_api_key()
74
- if not api_key:
75
- return {"error": "No Moralis API key provided in the configuration."}
76
-
77
73
  # Build query parameters
78
74
  params = {
79
75
  "chain": chain,
@@ -85,16 +81,19 @@ class WalletApprovals(PortfolioBaseTool):
85
81
  params["cursor"] = cursor
86
82
 
87
83
  # Call Moralis API
84
+ api_key = self.get_api_key()
85
+
88
86
  try:
89
87
  endpoint = f"/wallets/{address}/approvals"
90
88
  return await self._make_request(
91
89
  method="GET", endpoint=endpoint, api_key=api_key, params=params
92
90
  )
93
- except Exception as e:
91
+ except ToolException:
92
+ raise
93
+ except Exception as exc: # noqa: BLE001
94
94
  logger.error(
95
- f"wallet_approvals.py: Error fetching wallet approvals: {e}",
96
- exc_info=True,
95
+ "wallet_approvals.py: Error fetching wallet approvals", exc_info=exc
97
96
  )
98
- return {
99
- "error": "An error occurred while fetching wallet approvals. Please try again later."
100
- }
97
+ raise ToolException(
98
+ "An unexpected error occurred while fetching wallet approvals."
99
+ ) from exc
@@ -1,5 +1,5 @@
1
1
  import logging
2
- from typing import Any, Dict, Type
2
+ from typing import Any
3
3
 
4
4
  from pydantic import BaseModel, Field
5
5
 
@@ -31,14 +31,14 @@ class WalletDefiPositions(PortfolioBaseTool):
31
31
  "Get the DeFi positions summary of a wallet address. "
32
32
  "Returns information about liquidity positions, staking, lending, and other DeFi activities."
33
33
  )
34
- args_schema: Type[BaseModel] = WalletDefiPositionsInput
34
+ args_schema: type[BaseModel] = WalletDefiPositionsInput
35
35
 
36
36
  async def _arun(
37
37
  self,
38
38
  address: str,
39
39
  chain: str = DEFAULT_CHAIN,
40
40
  **kwargs,
41
- ) -> Dict[str, Any]:
41
+ ) -> dict[str, Any]:
42
42
  """Fetch wallet DeFi positions from Moralis.
43
43
 
44
44
  Args:
@@ -1,6 +1,7 @@
1
1
  import logging
2
- from typing import Any, Dict, Optional, Type
2
+ from typing import Any
3
3
 
4
+ from langchain_core.tools import ToolException
4
5
  from pydantic import BaseModel, Field
5
6
 
6
7
  from intentkit.skills.portfolio.base import PortfolioBaseTool
@@ -23,39 +24,39 @@ class WalletHistoryInput(BaseModel):
23
24
  description="The chain to query (e.g., 'eth', 'bsc', 'polygon').",
24
25
  default=DEFAULT_CHAIN,
25
26
  )
26
- limit: Optional[int] = Field(
27
+ limit: int | None = Field(
27
28
  description="The desired page size of the result.",
28
29
  default=DEFAULT_LIMIT,
29
30
  )
30
- cursor: Optional[str] = Field(
31
+ cursor: str | None = Field(
31
32
  description="The cursor returned in the previous response (for pagination).",
32
33
  default=None,
33
34
  )
34
- from_block: Optional[int] = Field(
35
+ from_block: int | None = Field(
35
36
  description="The minimum block number to get transactions from.",
36
37
  default=None,
37
38
  )
38
- to_block: Optional[int] = Field(
39
+ to_block: int | None = Field(
39
40
  description="The maximum block number to get transactions from.",
40
41
  default=None,
41
42
  )
42
- from_date: Optional[str] = Field(
43
+ from_date: str | None = Field(
43
44
  description="The start date to get transactions from (format in seconds or datestring).",
44
45
  default=None,
45
46
  )
46
- to_date: Optional[str] = Field(
47
+ to_date: str | None = Field(
47
48
  description="The end date to get transactions from (format in seconds or datestring).",
48
49
  default=None,
49
50
  )
50
- include_internal_transactions: Optional[bool] = Field(
51
+ include_internal_transactions: bool | None = Field(
51
52
  description="If the result should contain the internal transactions.",
52
53
  default=None,
53
54
  )
54
- nft_metadata: Optional[bool] = Field(
55
+ nft_metadata: bool | None = Field(
55
56
  description="If the result should contain the NFT metadata.",
56
57
  default=None,
57
58
  )
58
- order: Optional[str] = Field(
59
+ order: str | None = Field(
59
60
  description="The order of the result, in ascending (ASC) or descending (DESC).",
60
61
  default=DEFAULT_ORDER,
61
62
  )
@@ -73,23 +74,23 @@ class WalletHistory(PortfolioBaseTool):
73
74
  "Retrieve the full transaction history of a specified wallet address, including sends, "
74
75
  "receives, token and NFT transfers, and contract interactions."
75
76
  )
76
- args_schema: Type[BaseModel] = WalletHistoryInput
77
+ args_schema: type[BaseModel] = WalletHistoryInput
77
78
 
78
79
  async def _arun(
79
80
  self,
80
81
  address: str,
81
82
  chain: str = DEFAULT_CHAIN,
82
- limit: Optional[int] = DEFAULT_LIMIT,
83
- cursor: Optional[str] = None,
84
- from_block: Optional[int] = None,
85
- to_block: Optional[int] = None,
86
- from_date: Optional[str] = None,
87
- to_date: Optional[str] = None,
88
- include_internal_transactions: Optional[bool] = None,
89
- nft_metadata: Optional[bool] = None,
90
- order: Optional[str] = DEFAULT_ORDER,
83
+ limit: int | None = DEFAULT_LIMIT,
84
+ cursor: str | None = None,
85
+ from_block: int | None = None,
86
+ to_block: int | None = None,
87
+ from_date: str | None = None,
88
+ to_date: str | None = None,
89
+ include_internal_transactions: bool | None = None,
90
+ nft_metadata: bool | None = None,
91
+ order: str | None = DEFAULT_ORDER,
91
92
  **kwargs,
92
- ) -> Dict[str, Any]:
93
+ ) -> dict[str, Any]:
93
94
  """Fetch wallet transaction history from Moralis.
94
95
 
95
96
  Args:
@@ -114,11 +115,6 @@ class WalletHistory(PortfolioBaseTool):
114
115
  f"wallet_history.py: Fetching wallet history with context {context}"
115
116
  )
116
117
 
117
- # Get the API key from the agent's configuration
118
- api_key = self.get_api_key()
119
- if not api_key:
120
- return {"error": "No Moralis API key provided in the configuration."}
121
-
122
118
  # Build query parameters
123
119
  params = {"chain": chain, "limit": limit, "order": order}
124
120
 
@@ -139,15 +135,19 @@ class WalletHistory(PortfolioBaseTool):
139
135
  params["nft_metadata"] = nft_metadata
140
136
 
141
137
  # Call Moralis API
138
+ api_key = self.get_api_key()
139
+
142
140
  try:
143
141
  endpoint = f"/wallets/{address}/history"
144
142
  return await self._make_request(
145
143
  method="GET", endpoint=endpoint, api_key=api_key, params=params
146
144
  )
147
- except Exception as e:
145
+ except ToolException:
146
+ raise
147
+ except Exception as exc: # noqa: BLE001
148
148
  logger.error(
149
- f"wallet_history.py: Error fetching wallet history: {e}", exc_info=True
149
+ "wallet_history.py: Error fetching wallet history", exc_info=exc
150
150
  )
151
- return {
152
- "error": "An error occurred while fetching wallet history. Please try again later."
153
- }
151
+ raise ToolException(
152
+ "An unexpected error occurred while fetching wallet history."
153
+ ) from exc
@@ -1,5 +1,5 @@
1
1
  import logging
2
- from typing import Any, Dict, List, Optional, Type
2
+ from typing import Any
3
3
 
4
4
  from pydantic import BaseModel, Field
5
5
 
@@ -12,23 +12,23 @@ class WalletNetWorthInput(BaseModel):
12
12
  """Input for wallet net worth tool."""
13
13
 
14
14
  address: str = Field(description="The wallet address to calculate net worth for.")
15
- chains: Optional[List[str]] = Field(
15
+ chains: list[str] | None = Field(
16
16
  description="The chains to query (e.g., ['eth', 'bsc', 'polygon']).",
17
17
  default=None,
18
18
  )
19
- exclude_spam: Optional[bool] = Field(
19
+ exclude_spam: bool | None = Field(
20
20
  description="Exclude spam tokens from the result.",
21
21
  default=True,
22
22
  )
23
- exclude_unverified_contracts: Optional[bool] = Field(
23
+ exclude_unverified_contracts: bool | None = Field(
24
24
  description="Exclude unverified contracts from the result.",
25
25
  default=True,
26
26
  )
27
- max_token_inactivity: Optional[int] = Field(
27
+ max_token_inactivity: int | None = Field(
28
28
  description="Exclude tokens inactive for more than the given amount of days.",
29
29
  default=1,
30
30
  )
31
- min_pair_side_liquidity_usd: Optional[float] = Field(
31
+ min_pair_side_liquidity_usd: float | None = Field(
32
32
  description="Exclude tokens with liquidity less than the specified amount in USD.",
33
33
  default=1000,
34
34
  )
@@ -46,18 +46,18 @@ class WalletNetWorth(PortfolioBaseTool):
46
46
  "Get the net worth of a wallet in USD across multiple chains. "
47
47
  "Filters out spam tokens and low-liquidity assets for more accurate results."
48
48
  )
49
- args_schema: Type[BaseModel] = WalletNetWorthInput
49
+ args_schema: type[BaseModel] = WalletNetWorthInput
50
50
 
51
51
  async def _arun(
52
52
  self,
53
53
  address: str,
54
- chains: Optional[List[str]] = None,
55
- exclude_spam: Optional[bool] = True,
56
- exclude_unverified_contracts: Optional[bool] = True,
57
- max_token_inactivity: Optional[int] = 1,
58
- min_pair_side_liquidity_usd: Optional[float] = 1000,
54
+ chains: list[str] | None = None,
55
+ exclude_spam: bool | None = True,
56
+ exclude_unverified_contracts: bool | None = True,
57
+ max_token_inactivity: int | None = 1,
58
+ min_pair_side_liquidity_usd: float | None = 1000,
59
59
  **kwargs,
60
- ) -> Dict[str, Any]:
60
+ ) -> dict[str, Any]:
61
61
  """Calculate wallet net worth from Moralis.
62
62
 
63
63
  Args:
@@ -1,5 +1,5 @@
1
1
  import logging
2
- from typing import Any, Dict, List, Optional, Type
2
+ from typing import Any
3
3
 
4
4
  from pydantic import BaseModel, Field
5
5
 
@@ -17,35 +17,35 @@ class WalletNFTsInput(BaseModel):
17
17
  description="The chain to query (e.g., 'eth', 'base', 'polygon').",
18
18
  default=DEFAULT_CHAIN,
19
19
  )
20
- format: Optional[str] = Field(
20
+ format: str | None = Field(
21
21
  description="The format of the token ID ('decimal' or 'hex').",
22
22
  default="decimal",
23
23
  )
24
- limit: Optional[int] = Field(
24
+ limit: int | None = Field(
25
25
  description="The desired page size of the result.",
26
26
  default=DEFAULT_LIMIT,
27
27
  )
28
- exclude_spam: Optional[bool] = Field(
28
+ exclude_spam: bool | None = Field(
29
29
  description="Should spam NFTs be excluded from the result?",
30
30
  default=True,
31
31
  )
32
- token_addresses: Optional[List[str]] = Field(
32
+ token_addresses: list[str] | None = Field(
33
33
  description="The non-fungible token (NFT) addresses to get balances for.",
34
34
  default=None,
35
35
  )
36
- cursor: Optional[str] = Field(
36
+ cursor: str | None = Field(
37
37
  description="The cursor returned in the previous response (for pagination).",
38
38
  default=None,
39
39
  )
40
- normalize_metadata: Optional[bool] = Field(
40
+ normalize_metadata: bool | None = Field(
41
41
  description="The option to enable metadata normalization.",
42
42
  default=True,
43
43
  )
44
- media_items: Optional[bool] = Field(
44
+ media_items: bool | None = Field(
45
45
  description="Should preview media data be returned?",
46
46
  default=False,
47
47
  )
48
- include_prices: Optional[bool] = Field(
48
+ include_prices: bool | None = Field(
49
49
  description="Should NFT last sale prices be included in the result?",
50
50
  default=False,
51
51
  )
@@ -63,22 +63,22 @@ class WalletNFTs(PortfolioBaseTool):
63
63
  "Get NFTs owned by a given wallet address. Results include token details, "
64
64
  "metadata, collection information, and optionally prices."
65
65
  )
66
- args_schema: Type[BaseModel] = WalletNFTsInput
66
+ args_schema: type[BaseModel] = WalletNFTsInput
67
67
 
68
68
  async def _arun(
69
69
  self,
70
70
  address: str,
71
71
  chain: str = DEFAULT_CHAIN,
72
- format: Optional[str] = "decimal",
73
- limit: Optional[int] = DEFAULT_LIMIT,
74
- exclude_spam: Optional[bool] = True,
75
- token_addresses: Optional[List[str]] = None,
76
- cursor: Optional[str] = None,
77
- normalize_metadata: Optional[bool] = True,
78
- media_items: Optional[bool] = False,
79
- include_prices: Optional[bool] = False,
72
+ format: str | None = "decimal",
73
+ limit: int | None = DEFAULT_LIMIT,
74
+ exclude_spam: bool | None = True,
75
+ token_addresses: list[str] | None = None,
76
+ cursor: str | None = None,
77
+ normalize_metadata: bool | None = True,
78
+ media_items: bool | None = False,
79
+ include_prices: bool | None = False,
80
80
  **kwargs,
81
- ) -> Dict[str, Any]:
81
+ ) -> dict[str, Any]:
82
82
  """Fetch NFTs owned by a wallet from Moralis.
83
83
 
84
84
  Args:
@@ -1,6 +1,7 @@
1
1
  import logging
2
- from typing import Any, Dict, List, Optional, Type
2
+ from typing import Any
3
3
 
4
+ from langchain_core.tools import ToolException
4
5
  from pydantic import BaseModel, Field
5
6
 
6
7
  from intentkit.skills.portfolio.base import PortfolioBaseTool
@@ -19,11 +20,11 @@ class WalletProfitabilityInput(BaseModel):
19
20
  description="The chain to query (e.g., 'eth', 'bsc', 'polygon').",
20
21
  default=DEFAULT_CHAIN,
21
22
  )
22
- days: Optional[str] = Field(
23
+ days: str | None = Field(
23
24
  description="Timeframe in days for which profitability is calculated. Options: 'all', '7', '30', '60', '90'.",
24
25
  default="all",
25
26
  )
26
- token_addresses: Optional[List[str]] = Field(
27
+ token_addresses: list[str] | None = Field(
27
28
  description="The token addresses list to filter the result with.",
28
29
  default=None,
29
30
  )
@@ -41,16 +42,16 @@ class WalletProfitability(PortfolioBaseTool):
41
42
  "Retrieve detailed profitability breakdown for a wallet, including profit/loss per token, "
42
43
  "average buy/sell prices, and realized profits. Can be filtered by specific tokens."
43
44
  )
44
- args_schema: Type[BaseModel] = WalletProfitabilityInput
45
+ args_schema: type[BaseModel] = WalletProfitabilityInput
45
46
 
46
47
  async def _arun(
47
48
  self,
48
49
  address: str,
49
50
  chain: str = DEFAULT_CHAIN,
50
- days: Optional[str] = "all",
51
- token_addresses: Optional[List[str]] = None,
51
+ days: str | None = "all",
52
+ token_addresses: list[str] | None = None,
52
53
  **kwargs,
53
- ) -> Dict[str, Any]:
54
+ ) -> dict[str, Any]:
54
55
  """Fetch detailed wallet profitability from Moralis.
55
56
 
56
57
  Args:
@@ -68,11 +69,6 @@ class WalletProfitability(PortfolioBaseTool):
68
69
  f"wallet_profitability.py: Fetching profitability breakdown with context {context}"
69
70
  )
70
71
 
71
- # Get the API key from the agent's configuration
72
- api_key = self.get_api_key()
73
- if not api_key:
74
- return {"error": "No Moralis API key provided in the configuration."}
75
-
76
72
  # Build query parameters
77
73
  params = {
78
74
  "chain": chain,
@@ -84,16 +80,20 @@ class WalletProfitability(PortfolioBaseTool):
84
80
  params["token_addresses"] = token_addresses
85
81
 
86
82
  # Call Moralis API
83
+ api_key = self.get_api_key()
84
+
87
85
  try:
88
86
  endpoint = f"/wallets/{address}/profitability"
89
87
  return await self._make_request(
90
88
  method="GET", endpoint=endpoint, api_key=api_key, params=params
91
89
  )
92
- except Exception as e:
90
+ except ToolException:
91
+ raise
92
+ except Exception as exc: # noqa: BLE001
93
93
  logger.error(
94
- f"wallet_profitability.py: Error fetching profitability breakdown: {e}",
95
- exc_info=True,
94
+ "wallet_profitability.py: Error fetching profitability breakdown",
95
+ exc_info=exc,
96
96
  )
97
- return {
98
- "error": "An error occurred while fetching profitability breakdown. Please try again later."
99
- }
97
+ raise ToolException(
98
+ "An unexpected error occurred while fetching profitability breakdown."
99
+ ) from exc