intentkit 0.7.5.dev3__py3-none-any.whl → 0.8.34.dev7__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.
Files changed (393) hide show
  1. intentkit/MANIFEST.in +14 -0
  2. intentkit/README.md +88 -0
  3. intentkit/__init__.py +6 -4
  4. intentkit/abstracts/agent.py +4 -5
  5. intentkit/abstracts/engine.py +5 -5
  6. intentkit/abstracts/graph.py +15 -8
  7. intentkit/abstracts/skill.py +6 -144
  8. intentkit/abstracts/twitter.py +4 -5
  9. intentkit/clients/__init__.py +9 -2
  10. intentkit/clients/cdp.py +129 -153
  11. intentkit/{utils → clients}/s3.py +109 -34
  12. intentkit/clients/twitter.py +83 -62
  13. intentkit/clients/web3.py +4 -7
  14. intentkit/config/config.py +123 -90
  15. intentkit/core/account_checking.py +802 -0
  16. intentkit/core/agent.py +313 -498
  17. intentkit/core/asset.py +267 -0
  18. intentkit/core/chat.py +5 -3
  19. intentkit/core/client.py +1 -1
  20. intentkit/core/credit.py +49 -41
  21. intentkit/core/draft.py +201 -0
  22. intentkit/core/draft_chat.py +118 -0
  23. intentkit/core/engine.py +378 -287
  24. intentkit/core/manager/__init__.py +25 -0
  25. intentkit/core/manager/engine.py +220 -0
  26. intentkit/core/manager/service.py +172 -0
  27. intentkit/core/manager/skills.py +178 -0
  28. intentkit/core/middleware.py +231 -0
  29. intentkit/core/prompt.py +74 -114
  30. intentkit/core/scheduler.py +143 -0
  31. intentkit/core/statistics.py +168 -0
  32. intentkit/models/agent.py +931 -518
  33. intentkit/models/agent_data.py +165 -106
  34. intentkit/models/agent_schema.json +38 -251
  35. intentkit/models/app_setting.py +15 -13
  36. intentkit/models/chat.py +86 -140
  37. intentkit/models/credit.py +182 -162
  38. intentkit/models/db.py +42 -23
  39. intentkit/models/db_mig.py +120 -3
  40. intentkit/models/draft.py +222 -0
  41. intentkit/models/llm.csv +31 -0
  42. intentkit/models/llm.py +262 -370
  43. intentkit/models/redis.py +6 -4
  44. intentkit/models/skill.py +222 -101
  45. intentkit/models/skills.csv +173 -0
  46. intentkit/models/team.py +189 -0
  47. intentkit/models/user.py +103 -31
  48. intentkit/skills/acolyt/__init__.py +2 -9
  49. intentkit/skills/acolyt/ask.py +3 -4
  50. intentkit/skills/acolyt/base.py +4 -9
  51. intentkit/skills/acolyt/schema.json +4 -3
  52. intentkit/skills/aixbt/__init__.py +2 -13
  53. intentkit/skills/aixbt/base.py +1 -7
  54. intentkit/skills/aixbt/projects.py +14 -15
  55. intentkit/skills/aixbt/schema.json +4 -4
  56. intentkit/skills/allora/__init__.py +2 -9
  57. intentkit/skills/allora/base.py +4 -9
  58. intentkit/skills/allora/price.py +3 -4
  59. intentkit/skills/allora/schema.json +3 -2
  60. intentkit/skills/base.py +241 -41
  61. intentkit/skills/basename/__init__.py +51 -0
  62. intentkit/skills/basename/base.py +11 -0
  63. intentkit/skills/basename/basename.svg +11 -0
  64. intentkit/skills/basename/schema.json +58 -0
  65. intentkit/skills/carv/__init__.py +115 -121
  66. intentkit/skills/carv/base.py +184 -185
  67. intentkit/skills/carv/fetch_news.py +3 -3
  68. intentkit/skills/carv/onchain_query.py +4 -4
  69. intentkit/skills/carv/schema.json +134 -137
  70. intentkit/skills/carv/token_info_and_price.py +6 -6
  71. intentkit/skills/casino/__init__.py +4 -15
  72. intentkit/skills/casino/base.py +1 -7
  73. intentkit/skills/casino/deck_draw.py +5 -8
  74. intentkit/skills/casino/deck_shuffle.py +6 -6
  75. intentkit/skills/casino/dice_roll.py +2 -4
  76. intentkit/skills/casino/schema.json +0 -1
  77. intentkit/skills/cdp/__init__.py +22 -84
  78. intentkit/skills/cdp/base.py +1 -7
  79. intentkit/skills/cdp/schema.json +11 -314
  80. intentkit/skills/chainlist/__init__.py +2 -7
  81. intentkit/skills/chainlist/base.py +1 -7
  82. intentkit/skills/chainlist/chain_lookup.py +18 -18
  83. intentkit/skills/chainlist/schema.json +3 -5
  84. intentkit/skills/common/__init__.py +2 -9
  85. intentkit/skills/common/base.py +1 -7
  86. intentkit/skills/common/current_time.py +1 -2
  87. intentkit/skills/common/schema.json +2 -2
  88. intentkit/skills/cookiefun/__init__.py +6 -9
  89. intentkit/skills/cookiefun/base.py +2 -7
  90. intentkit/skills/cookiefun/get_account_details.py +7 -7
  91. intentkit/skills/cookiefun/get_account_feed.py +19 -19
  92. intentkit/skills/cookiefun/get_account_smart_followers.py +7 -7
  93. intentkit/skills/cookiefun/get_sectors.py +3 -3
  94. intentkit/skills/cookiefun/schema.json +1 -3
  95. intentkit/skills/cookiefun/search_accounts.py +9 -9
  96. intentkit/skills/cryptocompare/__init__.py +7 -24
  97. intentkit/skills/cryptocompare/api.py +2 -3
  98. intentkit/skills/cryptocompare/base.py +10 -24
  99. intentkit/skills/cryptocompare/fetch_news.py +4 -5
  100. intentkit/skills/cryptocompare/fetch_price.py +6 -7
  101. intentkit/skills/cryptocompare/fetch_top_exchanges.py +4 -5
  102. intentkit/skills/cryptocompare/fetch_top_market_cap.py +4 -5
  103. intentkit/skills/cryptocompare/fetch_top_volume.py +4 -5
  104. intentkit/skills/cryptocompare/fetch_trading_signals.py +5 -6
  105. intentkit/skills/cryptocompare/schema.json +3 -3
  106. intentkit/skills/cryptopanic/__init__.py +7 -10
  107. intentkit/skills/cryptopanic/base.py +51 -55
  108. intentkit/skills/cryptopanic/fetch_crypto_news.py +4 -8
  109. intentkit/skills/cryptopanic/fetch_crypto_sentiment.py +5 -7
  110. intentkit/skills/cryptopanic/schema.json +105 -103
  111. intentkit/skills/dapplooker/__init__.py +2 -9
  112. intentkit/skills/dapplooker/base.py +4 -9
  113. intentkit/skills/dapplooker/dapplooker_token_data.py +7 -7
  114. intentkit/skills/dapplooker/schema.json +3 -5
  115. intentkit/skills/defillama/__init__.py +24 -74
  116. intentkit/skills/defillama/api.py +6 -9
  117. intentkit/skills/defillama/base.py +8 -19
  118. intentkit/skills/defillama/coins/fetch_batch_historical_prices.py +8 -10
  119. intentkit/skills/defillama/coins/fetch_block.py +6 -8
  120. intentkit/skills/defillama/coins/fetch_current_prices.py +8 -10
  121. intentkit/skills/defillama/coins/fetch_first_price.py +7 -9
  122. intentkit/skills/defillama/coins/fetch_historical_prices.py +9 -11
  123. intentkit/skills/defillama/coins/fetch_price_chart.py +9 -11
  124. intentkit/skills/defillama/coins/fetch_price_percentage.py +7 -9
  125. intentkit/skills/defillama/config/chains.py +1 -3
  126. intentkit/skills/defillama/fees/fetch_fees_overview.py +24 -26
  127. intentkit/skills/defillama/schema.json +5 -1
  128. intentkit/skills/defillama/stablecoins/fetch_stablecoin_chains.py +16 -18
  129. intentkit/skills/defillama/stablecoins/fetch_stablecoin_charts.py +8 -10
  130. intentkit/skills/defillama/stablecoins/fetch_stablecoin_prices.py +5 -7
  131. intentkit/skills/defillama/stablecoins/fetch_stablecoins.py +7 -9
  132. intentkit/skills/defillama/tests/api_integration.test.py +1 -1
  133. intentkit/skills/defillama/tvl/fetch_chain_historical_tvl.py +4 -6
  134. intentkit/skills/defillama/tvl/fetch_chains.py +9 -11
  135. intentkit/skills/defillama/tvl/fetch_historical_tvl.py +4 -6
  136. intentkit/skills/defillama/tvl/fetch_protocol.py +32 -38
  137. intentkit/skills/defillama/tvl/fetch_protocol_current_tvl.py +3 -5
  138. intentkit/skills/defillama/tvl/fetch_protocols.py +37 -45
  139. intentkit/skills/defillama/volumes/fetch_dex_overview.py +42 -48
  140. intentkit/skills/defillama/volumes/fetch_dex_summary.py +35 -37
  141. intentkit/skills/defillama/volumes/fetch_options_overview.py +24 -28
  142. intentkit/skills/defillama/yields/fetch_pool_chart.py +10 -12
  143. intentkit/skills/defillama/yields/fetch_pools.py +26 -30
  144. intentkit/skills/dexscreener/__init__.py +97 -102
  145. intentkit/skills/dexscreener/base.py +125 -130
  146. intentkit/skills/dexscreener/get_pair_info.py +4 -5
  147. intentkit/skills/dexscreener/get_token_pairs.py +4 -5
  148. intentkit/skills/dexscreener/get_tokens_info.py +7 -8
  149. intentkit/skills/dexscreener/model/search_token_response.py +80 -82
  150. intentkit/skills/dexscreener/schema.json +91 -93
  151. intentkit/skills/dexscreener/search_token.py +182 -184
  152. intentkit/skills/dexscreener/utils.py +15 -14
  153. intentkit/skills/dune_analytics/__init__.py +7 -9
  154. intentkit/skills/dune_analytics/base.py +48 -52
  155. intentkit/skills/dune_analytics/fetch_kol_buys.py +5 -7
  156. intentkit/skills/dune_analytics/fetch_nation_metrics.py +6 -8
  157. intentkit/skills/dune_analytics/schema.json +104 -99
  158. intentkit/skills/elfa/__init__.py +5 -18
  159. intentkit/skills/elfa/base.py +10 -14
  160. intentkit/skills/elfa/mention.py +19 -21
  161. intentkit/skills/elfa/schema.json +3 -2
  162. intentkit/skills/elfa/stats.py +4 -4
  163. intentkit/skills/elfa/tokens.py +12 -12
  164. intentkit/skills/elfa/utils.py +26 -28
  165. intentkit/skills/enso/__init__.py +11 -31
  166. intentkit/skills/enso/base.py +54 -35
  167. intentkit/skills/enso/best_yield.py +16 -24
  168. intentkit/skills/enso/networks.py +6 -11
  169. intentkit/skills/enso/prices.py +11 -13
  170. intentkit/skills/enso/route.py +34 -38
  171. intentkit/skills/enso/schema.json +3 -2
  172. intentkit/skills/enso/tokens.py +29 -38
  173. intentkit/skills/enso/wallet.py +76 -191
  174. intentkit/skills/erc20/__init__.py +50 -0
  175. intentkit/skills/erc20/base.py +11 -0
  176. intentkit/skills/erc20/erc20.svg +5 -0
  177. intentkit/skills/erc20/schema.json +74 -0
  178. intentkit/skills/erc721/__init__.py +53 -0
  179. intentkit/skills/erc721/base.py +11 -0
  180. intentkit/skills/erc721/erc721.svg +5 -0
  181. intentkit/skills/erc721/schema.json +90 -0
  182. intentkit/skills/firecrawl/__init__.py +5 -18
  183. intentkit/skills/firecrawl/base.py +4 -9
  184. intentkit/skills/firecrawl/clear.py +4 -8
  185. intentkit/skills/firecrawl/crawl.py +19 -19
  186. intentkit/skills/firecrawl/query.py +4 -3
  187. intentkit/skills/firecrawl/schema.json +2 -6
  188. intentkit/skills/firecrawl/scrape.py +17 -22
  189. intentkit/skills/firecrawl/utils.py +50 -42
  190. intentkit/skills/github/__init__.py +2 -7
  191. intentkit/skills/github/base.py +1 -7
  192. intentkit/skills/github/github_search.py +1 -2
  193. intentkit/skills/github/schema.json +3 -4
  194. intentkit/skills/heurist/__init__.py +8 -27
  195. intentkit/skills/heurist/base.py +4 -9
  196. intentkit/skills/heurist/image_generation_animagine_xl.py +13 -15
  197. intentkit/skills/heurist/image_generation_arthemy_comics.py +13 -15
  198. intentkit/skills/heurist/image_generation_arthemy_real.py +13 -15
  199. intentkit/skills/heurist/image_generation_braindance.py +13 -15
  200. intentkit/skills/heurist/image_generation_cyber_realistic_xl.py +13 -15
  201. intentkit/skills/heurist/image_generation_flux_1_dev.py +13 -15
  202. intentkit/skills/heurist/image_generation_sdxl.py +13 -15
  203. intentkit/skills/heurist/schema.json +2 -2
  204. intentkit/skills/http/__init__.py +4 -15
  205. intentkit/skills/http/base.py +1 -7
  206. intentkit/skills/http/get.py +21 -16
  207. intentkit/skills/http/post.py +23 -18
  208. intentkit/skills/http/put.py +23 -18
  209. intentkit/skills/http/schema.json +4 -5
  210. intentkit/skills/lifi/__init__.py +8 -13
  211. intentkit/skills/lifi/base.py +3 -9
  212. intentkit/skills/lifi/schema.json +17 -8
  213. intentkit/skills/lifi/token_execute.py +150 -60
  214. intentkit/skills/lifi/token_quote.py +8 -10
  215. intentkit/skills/lifi/utils.py +104 -51
  216. intentkit/skills/moralis/__init__.py +6 -10
  217. intentkit/skills/moralis/api.py +6 -7
  218. intentkit/skills/moralis/base.py +5 -10
  219. intentkit/skills/moralis/fetch_chain_portfolio.py +10 -11
  220. intentkit/skills/moralis/fetch_nft_portfolio.py +22 -22
  221. intentkit/skills/moralis/fetch_solana_portfolio.py +11 -12
  222. intentkit/skills/moralis/fetch_wallet_portfolio.py +8 -9
  223. intentkit/skills/moralis/schema.json +7 -2
  224. intentkit/skills/morpho/__init__.py +52 -0
  225. intentkit/skills/morpho/base.py +11 -0
  226. intentkit/skills/morpho/morpho.svg +12 -0
  227. intentkit/skills/morpho/schema.json +73 -0
  228. intentkit/skills/nation/__init__.py +4 -9
  229. intentkit/skills/nation/base.py +5 -10
  230. intentkit/skills/nation/nft_check.py +3 -4
  231. intentkit/skills/nation/schema.json +4 -3
  232. intentkit/skills/onchain.py +30 -0
  233. intentkit/skills/openai/__init__.py +17 -18
  234. intentkit/skills/openai/base.py +10 -14
  235. intentkit/skills/openai/dalle_image_generation.py +4 -9
  236. intentkit/skills/openai/gpt_avatar_generator.py +102 -0
  237. intentkit/skills/openai/gpt_image_generation.py +5 -9
  238. intentkit/skills/openai/gpt_image_mini_generator.py +92 -0
  239. intentkit/skills/openai/gpt_image_to_image.py +5 -9
  240. intentkit/skills/openai/image_to_text.py +3 -7
  241. intentkit/skills/openai/schema.json +34 -3
  242. intentkit/skills/portfolio/__init__.py +11 -35
  243. intentkit/skills/portfolio/base.py +33 -19
  244. intentkit/skills/portfolio/schema.json +3 -5
  245. intentkit/skills/portfolio/token_balances.py +21 -21
  246. intentkit/skills/portfolio/wallet_approvals.py +17 -18
  247. intentkit/skills/portfolio/wallet_defi_positions.py +3 -3
  248. intentkit/skills/portfolio/wallet_history.py +31 -31
  249. intentkit/skills/portfolio/wallet_net_worth.py +13 -13
  250. intentkit/skills/portfolio/wallet_nfts.py +19 -19
  251. intentkit/skills/portfolio/wallet_profitability.py +18 -18
  252. intentkit/skills/portfolio/wallet_profitability_summary.py +5 -5
  253. intentkit/skills/portfolio/wallet_stats.py +3 -3
  254. intentkit/skills/portfolio/wallet_swaps.py +19 -19
  255. intentkit/skills/pyth/__init__.py +50 -0
  256. intentkit/skills/pyth/base.py +11 -0
  257. intentkit/skills/pyth/pyth.svg +6 -0
  258. intentkit/skills/pyth/schema.json +75 -0
  259. intentkit/skills/skills.toml +36 -0
  260. intentkit/skills/slack/__init__.py +5 -17
  261. intentkit/skills/slack/base.py +3 -9
  262. intentkit/skills/slack/get_channel.py +8 -8
  263. intentkit/skills/slack/get_message.py +9 -9
  264. intentkit/skills/slack/schedule_message.py +5 -5
  265. intentkit/skills/slack/schema.json +2 -2
  266. intentkit/skills/slack/send_message.py +3 -5
  267. intentkit/skills/supabase/__init__.py +7 -23
  268. intentkit/skills/supabase/base.py +1 -7
  269. intentkit/skills/supabase/delete_data.py +4 -4
  270. intentkit/skills/supabase/fetch_data.py +12 -12
  271. intentkit/skills/supabase/insert_data.py +4 -4
  272. intentkit/skills/supabase/invoke_function.py +6 -6
  273. intentkit/skills/supabase/schema.json +2 -3
  274. intentkit/skills/supabase/update_data.py +6 -6
  275. intentkit/skills/supabase/upsert_data.py +4 -4
  276. intentkit/skills/superfluid/__init__.py +53 -0
  277. intentkit/skills/superfluid/base.py +11 -0
  278. intentkit/skills/superfluid/schema.json +89 -0
  279. intentkit/skills/superfluid/superfluid.svg +6 -0
  280. intentkit/skills/system/__init__.py +7 -24
  281. intentkit/skills/system/add_autonomous_task.py +10 -12
  282. intentkit/skills/system/delete_autonomous_task.py +2 -2
  283. intentkit/skills/system/edit_autonomous_task.py +14 -18
  284. intentkit/skills/system/list_autonomous_tasks.py +3 -5
  285. intentkit/skills/system/read_agent_api_key.py +6 -4
  286. intentkit/skills/system/regenerate_agent_api_key.py +6 -4
  287. intentkit/skills/system/schema.json +6 -8
  288. intentkit/skills/tavily/__init__.py +3 -12
  289. intentkit/skills/tavily/base.py +4 -9
  290. intentkit/skills/tavily/schema.json +3 -5
  291. intentkit/skills/tavily/tavily_extract.py +2 -4
  292. intentkit/skills/tavily/tavily_search.py +4 -6
  293. intentkit/skills/token/__init__.py +5 -10
  294. intentkit/skills/token/base.py +7 -11
  295. intentkit/skills/token/erc20_transfers.py +19 -19
  296. intentkit/skills/token/schema.json +3 -6
  297. intentkit/skills/token/token_analytics.py +3 -3
  298. intentkit/skills/token/token_price.py +13 -13
  299. intentkit/skills/token/token_search.py +9 -9
  300. intentkit/skills/twitter/__init__.py +11 -35
  301. intentkit/skills/twitter/base.py +22 -34
  302. intentkit/skills/twitter/follow_user.py +2 -6
  303. intentkit/skills/twitter/get_mentions.py +5 -12
  304. intentkit/skills/twitter/get_timeline.py +4 -12
  305. intentkit/skills/twitter/get_user_by_username.py +2 -6
  306. intentkit/skills/twitter/get_user_tweets.py +5 -13
  307. intentkit/skills/twitter/like_tweet.py +2 -6
  308. intentkit/skills/twitter/post_tweet.py +6 -9
  309. intentkit/skills/twitter/reply_tweet.py +6 -9
  310. intentkit/skills/twitter/retweet.py +2 -6
  311. intentkit/skills/twitter/schema.json +1 -0
  312. intentkit/skills/twitter/search_tweets.py +4 -12
  313. intentkit/skills/unrealspeech/__init__.py +2 -7
  314. intentkit/skills/unrealspeech/base.py +2 -8
  315. intentkit/skills/unrealspeech/schema.json +2 -5
  316. intentkit/skills/unrealspeech/text_to_speech.py +8 -8
  317. intentkit/skills/venice_audio/__init__.py +98 -106
  318. intentkit/skills/venice_audio/base.py +117 -121
  319. intentkit/skills/venice_audio/input.py +41 -41
  320. intentkit/skills/venice_audio/schema.json +151 -152
  321. intentkit/skills/venice_audio/venice_audio.py +38 -21
  322. intentkit/skills/venice_image/__init__.py +147 -154
  323. intentkit/skills/venice_image/api.py +138 -138
  324. intentkit/skills/venice_image/base.py +185 -192
  325. intentkit/skills/venice_image/config.py +33 -35
  326. intentkit/skills/venice_image/image_enhance/image_enhance.py +2 -3
  327. intentkit/skills/venice_image/image_enhance/image_enhance_base.py +21 -23
  328. intentkit/skills/venice_image/image_enhance/image_enhance_input.py +38 -40
  329. intentkit/skills/venice_image/image_generation/image_generation_base.py +11 -10
  330. intentkit/skills/venice_image/image_generation/image_generation_fluently_xl.py +26 -26
  331. intentkit/skills/venice_image/image_generation/image_generation_flux_dev.py +27 -27
  332. intentkit/skills/venice_image/image_generation/image_generation_flux_dev_uncensored.py +26 -26
  333. intentkit/skills/venice_image/image_generation/image_generation_input.py +158 -158
  334. intentkit/skills/venice_image/image_generation/image_generation_lustify_sdxl.py +26 -26
  335. intentkit/skills/venice_image/image_generation/image_generation_pony_realism.py +26 -26
  336. intentkit/skills/venice_image/image_generation/image_generation_stable_diffusion_3_5.py +28 -28
  337. intentkit/skills/venice_image/image_generation/image_generation_venice_sd35.py +28 -28
  338. intentkit/skills/venice_image/image_upscale/image_upscale.py +3 -3
  339. intentkit/skills/venice_image/image_upscale/image_upscale_base.py +21 -23
  340. intentkit/skills/venice_image/image_upscale/image_upscale_input.py +22 -22
  341. intentkit/skills/venice_image/image_vision/image_vision.py +2 -2
  342. intentkit/skills/venice_image/image_vision/image_vision_base.py +17 -17
  343. intentkit/skills/venice_image/image_vision/image_vision_input.py +9 -9
  344. intentkit/skills/venice_image/schema.json +267 -267
  345. intentkit/skills/venice_image/utils.py +77 -78
  346. intentkit/skills/web_scraper/__init__.py +5 -18
  347. intentkit/skills/web_scraper/base.py +21 -7
  348. intentkit/skills/web_scraper/document_indexer.py +7 -6
  349. intentkit/skills/web_scraper/schema.json +2 -6
  350. intentkit/skills/web_scraper/scrape_and_index.py +15 -15
  351. intentkit/skills/web_scraper/utils.py +62 -63
  352. intentkit/skills/web_scraper/website_indexer.py +17 -19
  353. intentkit/skills/weth/__init__.py +49 -0
  354. intentkit/skills/weth/base.py +11 -0
  355. intentkit/skills/weth/schema.json +58 -0
  356. intentkit/skills/weth/weth.svg +6 -0
  357. intentkit/skills/wow/__init__.py +51 -0
  358. intentkit/skills/wow/base.py +11 -0
  359. intentkit/skills/wow/schema.json +89 -0
  360. intentkit/skills/wow/wow.svg +7 -0
  361. intentkit/skills/x402/__init__.py +58 -0
  362. intentkit/skills/x402/base.py +99 -0
  363. intentkit/skills/x402/http_request.py +117 -0
  364. intentkit/skills/x402/schema.json +40 -0
  365. intentkit/skills/x402/x402.webp +0 -0
  366. intentkit/skills/xmtp/__init__.py +4 -15
  367. intentkit/skills/xmtp/base.py +5 -5
  368. intentkit/skills/xmtp/price.py +7 -6
  369. intentkit/skills/xmtp/schema.json +69 -71
  370. intentkit/skills/xmtp/swap.py +6 -8
  371. intentkit/skills/xmtp/transfer.py +4 -6
  372. intentkit/utils/__init__.py +4 -0
  373. intentkit/utils/chain.py +198 -96
  374. intentkit/utils/ens.py +135 -0
  375. intentkit/utils/error.py +5 -2
  376. intentkit/utils/logging.py +9 -11
  377. intentkit/utils/schema.py +100 -0
  378. intentkit/utils/slack_alert.py +8 -8
  379. intentkit/utils/tx.py +16 -8
  380. intentkit/uv.lock +3377 -0
  381. {intentkit-0.7.5.dev3.dist-info → intentkit-0.8.34.dev7.dist-info}/METADATA +13 -15
  382. intentkit-0.8.34.dev7.dist-info/RECORD +478 -0
  383. intentkit-0.8.34.dev7.dist-info/licenses/LICENSE +21 -0
  384. intentkit/core/node.py +0 -215
  385. intentkit/models/conversation.py +0 -286
  386. intentkit/models/generator.py +0 -347
  387. intentkit/skills/cdp/get_balance.py +0 -110
  388. intentkit/skills/cdp/swap.py +0 -121
  389. intentkit/skills/moralis/tests/__init__.py +0 -0
  390. intentkit/skills/moralis/tests/test_wallet.py +0 -511
  391. intentkit-0.7.5.dev3.dist-info/RECORD +0 -424
  392. {intentkit-0.7.5.dev3.dist-info/licenses → intentkit}/LICENSE +0 -0
  393. {intentkit-0.7.5.dev3.dist-info → intentkit-0.8.34.dev7.dist-info}/WHEEL +0 -0
@@ -1,9 +1,8 @@
1
- from typing import Any, Dict, List, Optional, Type
1
+ from typing import Any
2
2
 
3
3
  import httpx
4
4
  from pydantic import BaseModel, Field
5
5
 
6
- from intentkit.abstracts.skill import SkillStoreABC
7
6
  from intentkit.skills.lifi.base import LiFiBaseTool
8
7
  from intentkit.skills.lifi.utils import (
9
8
  LIFI_API_URL,
@@ -53,21 +52,20 @@ class TokenQuote(LiFiBaseTool):
53
52
  "Use this tool to check rates, fees, and estimated time for token transfers without executing them.\n"
54
53
  "Supports all major chains like Ethereum, Polygon, Arbitrum, Optimism, Base, and more."
55
54
  )
56
- args_schema: Type[BaseModel] = TokenQuoteInput
55
+ args_schema: type[BaseModel] = TokenQuoteInput
57
56
  api_url: str = LIFI_API_URL
58
57
 
59
58
  # Configuration options
60
59
  default_slippage: float = 0.03
61
- allowed_chains: Optional[List[str]] = None
60
+ allowed_chains: list[str] | None = None
62
61
 
63
62
  def __init__(
64
63
  self,
65
- skill_store: SkillStoreABC,
66
64
  default_slippage: float = 0.03,
67
- allowed_chains: Optional[List[str]] = None,
68
- ):
65
+ allowed_chains: list[str] | None = None,
66
+ ) -> None:
69
67
  """Initialize the TokenQuote skill with configuration options."""
70
- super().__init__(skill_store=skill_store)
68
+ super().__init__()
71
69
  self.default_slippage = default_slippage
72
70
  self.allowed_chains = allowed_chains
73
71
 
@@ -78,7 +76,7 @@ class TokenQuote(LiFiBaseTool):
78
76
  from_token: str,
79
77
  to_token: str,
80
78
  from_amount: str,
81
- slippage: float = None,
79
+ slippage: float | None = None,
82
80
  **kwargs,
83
81
  ) -> str:
84
82
  """Get a quote for token transfer."""
@@ -140,7 +138,7 @@ class TokenQuote(LiFiBaseTool):
140
138
  self.logger.error("LiFi_Error: %s", str(e))
141
139
  return f"An unexpected error occurred: {str(e)}"
142
140
 
143
- def _format_quote_result(self, data: Dict[str, Any]) -> str:
141
+ def _format_quote_result(self, data: dict[str, Any]) -> str:
144
142
  """Format quote result into human-readable text."""
145
143
  try:
146
144
  # Get basic info
@@ -4,7 +4,8 @@ LiFi Skills Utilities
4
4
  Common utilities and helper functions for LiFi token transfer skills.
5
5
  """
6
6
 
7
- from typing import Any, Dict, List, Optional, Tuple
7
+ from decimal import ROUND_DOWN, Decimal, InvalidOperation
8
+ from typing import Any
8
9
 
9
10
  import httpx
10
11
  from web3 import Web3
@@ -71,8 +72,8 @@ def validate_inputs(
71
72
  to_token: str,
72
73
  from_amount: str,
73
74
  slippage: float,
74
- allowed_chains: Optional[List[str]] = None,
75
- ) -> Optional[str]:
75
+ allowed_chains: list[str] | None = None,
76
+ ) -> str | None:
76
77
  """
77
78
  Validate all input parameters for LiFi operations.
78
79
 
@@ -179,7 +180,7 @@ def handle_api_response(
179
180
  from_chain: str,
180
181
  to_token: str,
181
182
  to_chain: str,
182
- ) -> Tuple[Optional[Dict], Optional[str]]:
183
+ ) -> tuple[dict[str, Any] | None, str | None]:
183
184
  """
184
185
  Handle LiFi API response and return data or error message.
185
186
 
@@ -293,17 +294,20 @@ def convert_chain_to_id(chain: str) -> int:
293
294
 
294
295
 
295
296
  def convert_amount_to_wei(amount: str, token_symbol: str = "ETH") -> str:
296
- """
297
- Convert human-readable amount to wei format for LiFi API.
297
+ """Convert a token amount into the smallest denomination expected by LiFi."""
298
298
 
299
- Args:
300
- amount: Amount in human readable format (e.g., "0.0015")
301
- token_symbol: Token symbol to determine decimals
299
+ if amount is None:
300
+ raise ValueError("Amount is required")
301
+
302
+ normalized_amount = amount.strip()
303
+ if not normalized_amount:
304
+ raise ValueError("Amount cannot be empty")
305
+
306
+ # If the user already provided an integer amount without a decimal point,
307
+ # assume it is already in the token's smallest denomination.
308
+ if normalized_amount.isdigit():
309
+ return normalized_amount
302
310
 
303
- Returns:
304
- Amount in wei format as string
305
- """
306
- # Default decimals for common tokens
307
311
  token_decimals = {
308
312
  "ETH": 18,
309
313
  "USDC": 6,
@@ -318,13 +322,16 @@ def convert_amount_to_wei(amount: str, token_symbol: str = "ETH") -> str:
318
322
  decimals = token_decimals.get(token_symbol.upper(), 18)
319
323
 
320
324
  try:
321
- # Convert string to float, then to wei
322
- amount_float = float(amount)
323
- amount_wei = int(amount_float * (10**decimals))
324
- return str(amount_wei)
325
- except (ValueError, TypeError):
326
- # If conversion fails, return original amount
327
- return amount
325
+ decimal_amount = Decimal(normalized_amount)
326
+ scaled_amount = (decimal_amount * (Decimal(10) ** decimals)).quantize(
327
+ Decimal("1"),
328
+ rounding=ROUND_DOWN,
329
+ )
330
+ return str(int(scaled_amount))
331
+ except (InvalidOperation, ValueError, TypeError):
332
+ # If conversion fails, fall back to the original value to avoid
333
+ # accidentally submitting an incorrect amount.
334
+ return normalized_amount
328
335
 
329
336
 
330
337
  def build_quote_params(
@@ -334,8 +341,8 @@ def build_quote_params(
334
341
  to_token: str,
335
342
  from_amount: str,
336
343
  slippage: float,
337
- from_address: Optional[str] = None,
338
- ) -> Dict[str, Any]:
344
+ from_address: str | None = None,
345
+ ) -> dict[str, Any]:
339
346
  """
340
347
  Build parameters for LiFi quote API request.
341
348
 
@@ -350,15 +357,12 @@ def build_quote_params(
350
357
  Raises:
351
358
  ValueError: If chain identifiers are not recognized
352
359
  """
353
- # Convert amount to wei format for API
354
- wei_amount = convert_amount_to_wei(from_amount, from_token)
355
-
356
360
  return {
357
361
  "fromChain": convert_chain_to_id(from_chain),
358
362
  "toChain": convert_chain_to_id(to_chain),
359
363
  "fromToken": from_token,
360
364
  "toToken": to_token,
361
- "fromAmount": wei_amount,
365
+ "fromAmount": convert_amount_to_wei(from_amount, from_token),
362
366
  "fromAddress": from_address or DUMMY_ADDRESS,
363
367
  "slippage": slippage,
364
368
  }
@@ -381,38 +385,87 @@ def is_native_token(token_address: str) -> bool:
381
385
  )
382
386
 
383
387
 
384
- def prepare_transaction_params(transaction_request: Dict[str, Any]) -> Dict[str, Any]:
385
- """
386
- Prepare transaction parameters for CDP wallet provider.
388
+ def _convert_hex_or_decimal(value: Any) -> int | None:
389
+ """Convert LiFi transaction numeric values into integers."""
387
390
 
388
- Args:
389
- transaction_request: Transaction request from LiFi API
391
+ if value is None:
392
+ return None
390
393
 
391
- Returns:
392
- Formatted transaction parameters
394
+ if isinstance(value, int):
395
+ return value
396
+
397
+ if isinstance(value, str):
398
+ stripped = value.strip()
399
+ if not stripped:
400
+ return None
401
+ if stripped.startswith("0x"):
402
+ return int(stripped, 16)
403
+ try:
404
+ return int(Decimal(stripped))
405
+ except (InvalidOperation, ValueError):
406
+ return None
407
+
408
+ return None
409
+
410
+
411
+ def prepare_transaction_params(
412
+ transaction_request: dict[str, Any],
413
+ wallet_address: str | None = None,
414
+ ) -> dict[str, Any]:
415
+ """Prepare transaction parameters for the CDP wallet provider."""
393
416
 
394
- Raises:
395
- Exception: If required parameters are missing
396
- """
397
417
  to_address = transaction_request.get("to")
398
- value = transaction_request.get("value", "0")
418
+ value = transaction_request.get("value", "0x0")
399
419
  data = transaction_request.get("data", "0x")
400
420
 
401
421
  if not to_address:
402
- raise Exception("No destination address in transaction request")
403
-
404
- # Convert value to integer if it's a string
405
- if isinstance(value, str):
406
- value = int(value, 16) if value.startswith("0x") else int(value)
422
+ raise Exception("Transaction request is missing destination address")
407
423
 
408
- return {
424
+ tx_params: dict[str, Any] = {
409
425
  "to": Web3.to_checksum_address(to_address),
410
- "value": value,
411
426
  "data": data,
412
427
  }
413
428
 
429
+ int_value = _convert_hex_or_decimal(value)
430
+ if int_value is not None:
431
+ tx_params["value"] = int_value
432
+
433
+ chain_id = _convert_hex_or_decimal(transaction_request.get("chainId"))
434
+ if chain_id is not None:
435
+ tx_params["chainId"] = chain_id
436
+
437
+ gas_limit = _convert_hex_or_decimal(
438
+ transaction_request.get("gasLimit") or transaction_request.get("gas")
439
+ )
440
+ if gas_limit is not None:
441
+ tx_params["gas"] = gas_limit
442
+
443
+ gas_price = _convert_hex_or_decimal(transaction_request.get("gasPrice"))
444
+ if gas_price is not None:
445
+ tx_params["gasPrice"] = gas_price
446
+
447
+ max_fee_per_gas = _convert_hex_or_decimal(transaction_request.get("maxFeePerGas"))
448
+ if max_fee_per_gas is not None:
449
+ tx_params["maxFeePerGas"] = max_fee_per_gas
450
+
451
+ max_priority_fee_per_gas = _convert_hex_or_decimal(
452
+ transaction_request.get("maxPriorityFeePerGas")
453
+ )
454
+ if max_priority_fee_per_gas is not None:
455
+ tx_params["maxPriorityFeePerGas"] = max_priority_fee_per_gas
456
+
457
+ nonce = _convert_hex_or_decimal(transaction_request.get("nonce"))
458
+ if nonce is not None:
459
+ tx_params["nonce"] = nonce
460
+
461
+ from_address = transaction_request.get("from") or wallet_address
462
+ if from_address:
463
+ tx_params["from"] = Web3.to_checksum_address(from_address)
464
+
465
+ return tx_params
466
+
414
467
 
415
- def format_quote_basic_info(data: Dict[str, Any]) -> Dict[str, Any]:
468
+ def format_quote_basic_info(data: dict[str, Any]) -> dict[str, Any]:
416
469
  """
417
470
  Extract and format basic quote information.
418
471
 
@@ -450,7 +503,7 @@ def format_quote_basic_info(data: Dict[str, Any]) -> Dict[str, Any]:
450
503
  }
451
504
 
452
505
 
453
- def format_fees_and_gas(data: Dict[str, Any]) -> Tuple[str, str]:
506
+ def format_fees_and_gas(data: dict[str, Any]) -> tuple[str, str]:
454
507
  """
455
508
  Format fee and gas cost information from quote data.
456
509
 
@@ -464,7 +517,7 @@ def format_fees_and_gas(data: Dict[str, Any]) -> Tuple[str, str]:
464
517
 
465
518
  # Extract gas and fee costs
466
519
  gas_costs = estimate.get("gasCosts", [])
467
- fee_costs = []
520
+ fee_costs: list[dict[str, Any]] = []
468
521
 
469
522
  # Collect fee information from included steps
470
523
  for step in data.get("includedSteps", []):
@@ -476,7 +529,7 @@ def format_fees_and_gas(data: Dict[str, Any]) -> Tuple[str, str]:
476
529
  fees_text = ""
477
530
  if fee_costs:
478
531
  fees_text = "**Fees:**\n"
479
- total_fee_usd = 0
532
+ total_fee_usd = 0.0
480
533
  for fee in fee_costs:
481
534
  fee_name = fee.get("name", "Unknown fee")
482
535
  fee_amount = fee.get("amount", "0")
@@ -508,7 +561,7 @@ def format_fees_and_gas(data: Dict[str, Any]) -> Tuple[str, str]:
508
561
  gas_text = ""
509
562
  if gas_costs:
510
563
  gas_text = "**Gas Cost:**\n"
511
- total_gas_usd = 0
564
+ total_gas_usd = 0.0
512
565
  for gas in gas_costs:
513
566
  gas_amount = gas.get("amount", "0")
514
567
  gas_token = gas.get("token", {}).get("symbol", "ETH")
@@ -531,7 +584,7 @@ def format_fees_and_gas(data: Dict[str, Any]) -> Tuple[str, str]:
531
584
  return fees_text, gas_text
532
585
 
533
586
 
534
- def format_route_info(data: Dict[str, Any]) -> str:
587
+ def format_route_info(data: dict[str, Any]) -> str:
535
588
  """
536
589
  Format routing information from quote data.
537
590
 
@@ -628,7 +681,7 @@ def get_explorer_url(chain_id: int, tx_hash: str) -> str:
628
681
 
629
682
 
630
683
  def format_transaction_result(
631
- tx_hash: str, chain_id: int, token_info: dict = None
684
+ tx_hash: str, chain_id: int, token_info: dict[str, str] | None = None
632
685
  ) -> str:
633
686
  """
634
687
  Format transaction result with explorer link.
@@ -1,9 +1,9 @@
1
1
  """Wallet Portfolio Skills for IntentKit."""
2
2
 
3
3
  import logging
4
- from typing import Dict, List, NotRequired, TypedDict
4
+ from typing import NotRequired, TypedDict
5
5
 
6
- from intentkit.abstracts.skill import SkillStoreABC
6
+ from intentkit.config.config import config as system_config
7
7
  from intentkit.skills.base import SkillConfig, SkillState
8
8
  from intentkit.skills.moralis.base import WalletBaseTool
9
9
  from intentkit.skills.moralis.fetch_chain_portfolio import FetchChainPortfolio
@@ -28,21 +28,19 @@ class Config(SkillConfig):
28
28
 
29
29
  api_key: str
30
30
  states: SkillStates
31
- supported_chains: NotRequired[Dict[str, bool]] = {"evm": True, "solana": True}
31
+ supported_chains: NotRequired[dict[str, bool]] = {"evm": True, "solana": True}
32
32
 
33
33
 
34
34
  async def get_skills(
35
35
  config: "Config",
36
36
  is_private: bool,
37
- store: SkillStoreABC,
38
37
  **_,
39
- ) -> List[WalletBaseTool]:
38
+ ) -> list[WalletBaseTool]:
40
39
  """Get all Wallet Portfolio skills.
41
40
 
42
41
  Args:
43
42
  config: Skill configuration
44
43
  is_private: Whether the request is from an authenticated user
45
- store: Skill store for persistence
46
44
  chain_provider: Optional chain provider for blockchain interactions
47
45
  **_: Additional arguments
48
46
 
@@ -67,12 +65,12 @@ async def get_skills(
67
65
  if config.get("api_key_provider") == "agent_owner":
68
66
  api_key = config.get("api_key")
69
67
  else:
70
- api_key = store.get_system_config("moralis_api_key")
68
+ api_key = system_config.moralis_api_key
71
69
 
72
70
  # Get each skill using the getter
73
71
  result = []
74
72
  for name in available_skills:
75
- skill = get_wallet_skill(name, api_key, store)
73
+ skill = get_wallet_skill(name, api_key)
76
74
  if skill:
77
75
  result.append(skill)
78
76
  return result
@@ -81,7 +79,6 @@ async def get_skills(
81
79
  def get_wallet_skill(
82
80
  name: str,
83
81
  api_key: str,
84
- store: SkillStoreABC,
85
82
  ) -> WalletBaseTool:
86
83
  """Get a specific Wallet Portfolio skill by name.
87
84
 
@@ -106,5 +103,4 @@ def get_wallet_skill(
106
103
 
107
104
  return skill_classes[name](
108
105
  api_key=api_key,
109
- skill_store=store,
110
106
  )
@@ -1,7 +1,6 @@
1
1
  """API interface for wallet data providers (EVM chains and Solana)."""
2
2
 
3
3
  import logging
4
- from typing import Dict
5
4
 
6
5
  import httpx
7
6
 
@@ -163,7 +162,7 @@ async def fetch_net_worth(api_key: str, address: str) -> dict:
163
162
  #############################################
164
163
 
165
164
 
166
- async def fetch_solana_api(api_key: str, endpoint: str, params: Dict = None) -> Dict:
165
+ async def fetch_solana_api(api_key: str, endpoint: str, params: dict = None) -> dict:
167
166
  """Base function for Solana API calls using Moralis.
168
167
 
169
168
  Args:
@@ -200,7 +199,7 @@ async def fetch_solana_api(api_key: str, endpoint: str, params: Dict = None) ->
200
199
 
201
200
  async def get_solana_portfolio(
202
201
  api_key: str, address: str, network: str = "mainnet"
203
- ) -> Dict:
202
+ ) -> dict:
204
203
  """Get complete portfolio for a Solana wallet.
205
204
 
206
205
  Args:
@@ -217,7 +216,7 @@ async def get_solana_portfolio(
217
216
 
218
217
  async def get_solana_balance(
219
218
  api_key: str, address: str, network: str = "mainnet"
220
- ) -> Dict:
219
+ ) -> dict:
221
220
  """Get native SOL balance.
222
221
 
223
222
  Args:
@@ -234,7 +233,7 @@ async def get_solana_balance(
234
233
 
235
234
  async def get_solana_spl_tokens(
236
235
  api_key: str, address: str, network: str = "mainnet"
237
- ) -> Dict:
236
+ ) -> dict:
238
237
  """Get SPL token balances.
239
238
 
240
239
  Args:
@@ -249,7 +248,7 @@ async def get_solana_spl_tokens(
249
248
  return await fetch_solana_api(api_key, endpoint)
250
249
 
251
250
 
252
- async def get_solana_nfts(api_key: str, address: str, network: str = "mainnet") -> Dict:
251
+ async def get_solana_nfts(api_key: str, address: str, network: str = "mainnet") -> dict:
253
252
  """Get NFTs owned by a Solana wallet.
254
253
 
255
254
  Args:
@@ -266,7 +265,7 @@ async def get_solana_nfts(api_key: str, address: str, network: str = "mainnet")
266
265
 
267
266
  async def get_token_price(
268
267
  api_key: str, token_address: str, network: str = "mainnet"
269
- ) -> Dict:
268
+ ) -> dict:
270
269
  """Get token price by mint address.
271
270
 
272
271
  Args:
@@ -1,11 +1,9 @@
1
1
  """Base class for Wallet Portfolio tools."""
2
2
 
3
- from typing import List, Optional, Type
4
-
5
- from langchain.tools.base import ToolException
3
+ from langchain_core.tools.base import ToolException
6
4
  from pydantic import BaseModel, Field
7
5
 
8
- from intentkit.abstracts.skill import SkillStoreABC
6
+ from intentkit.config.config import config
9
7
  from intentkit.skills.base import IntentKitSkill
10
8
 
11
9
  # Chain ID to chain name mapping for EVM chains
@@ -29,13 +27,10 @@ class WalletBaseTool(IntentKitSkill):
29
27
 
30
28
  name: str = Field(description="The name of the tool")
31
29
  description: str = Field(description="A description of what the tool does")
32
- args_schema: Type[BaseModel]
33
- skill_store: SkillStoreABC = Field(
34
- description="The skill store for persisting data"
35
- )
30
+ args_schema: type[BaseModel]
36
31
 
37
32
  # Optional fields for blockchain providers
38
- solana_networks: Optional[List[str]] = Field(
33
+ solana_networks: list[str] | None = Field(
39
34
  default=SOLANA_NETWORKS, description="Supported Solana networks"
40
35
  )
41
36
 
@@ -44,7 +39,7 @@ class WalletBaseTool(IntentKitSkill):
44
39
  skill_config = context.agent.skill_config(self.category)
45
40
  api_key_provider = skill_config.get("api_key_provider")
46
41
  if api_key_provider == "platform":
47
- return self.skill_store.get_system_config("moralis_api_key")
42
+ return config.moralis_api_key
48
43
  # for backward compatibility, may only have api_key in skill_config
49
44
  elif skill_config.get("api_key"):
50
45
  return skill_config.get("api_key")
@@ -1,7 +1,6 @@
1
1
  """fetching wallet portfolio for a specific blockchain."""
2
2
 
3
3
  import logging
4
- from typing import List, Optional, Type
5
4
 
6
5
  from pydantic import BaseModel, Field
7
6
 
@@ -27,7 +26,7 @@ class ChainTokenBalance(BaseModel):
27
26
  contract_address: str = Field(..., description="Token contract address")
28
27
  symbol: str = Field(..., description="Token symbol")
29
28
  name: str = Field(..., description="Token name")
30
- logo: Optional[str] = Field(None, description="Token logo URL")
29
+ logo: str | None = Field(None, description="Token logo URL")
31
30
  decimals: int = Field(..., description="Token decimals")
32
31
  balance: float = Field(..., description="Token balance")
33
32
  balance_raw: str = Field(..., description="Raw token balance")
@@ -38,12 +37,12 @@ class TokenApproval(BaseModel):
38
37
  """Model for token approval."""
39
38
 
40
39
  token_address: str = Field(..., description="Token contract address")
41
- token_symbol: Optional[str] = Field(None, description="Token symbol")
42
- token_name: Optional[str] = Field(None, description="Token name")
40
+ token_symbol: str | None = Field(None, description="Token symbol")
41
+ token_name: str | None = Field(None, description="Token name")
43
42
  spender: str = Field(..., description="Spender address (contract)")
44
- spender_name: Optional[str] = Field(None, description="Spender name if known")
43
+ spender_name: str | None = Field(None, description="Spender name if known")
45
44
  allowance: str = Field(..., description="Raw approval amount")
46
- allowance_formatted: Optional[float] = Field(
45
+ allowance_formatted: float | None = Field(
47
46
  None, description="Formatted approval amount"
48
47
  )
49
48
  unlimited: bool = Field(False, description="Whether the approval is unlimited")
@@ -55,17 +54,17 @@ class ChainPortfolioOutput(BaseModel):
55
54
  address: str = Field(..., description="Wallet address")
56
55
  chain_id: int = Field(..., description="Chain ID")
57
56
  chain_name: str = Field(..., description="Chain name")
58
- native_token: Optional[ChainTokenBalance] = Field(
57
+ native_token: ChainTokenBalance | None = Field(
59
58
  None, description="Native token balance"
60
59
  )
61
- tokens: List[ChainTokenBalance] = Field(
60
+ tokens: list[ChainTokenBalance] = Field(
62
61
  default_factory=list, description="List of token balances"
63
62
  )
64
63
  total_usd_value: float = Field(0.0, description="Total USD value on this chain")
65
- approvals: Optional[List[TokenApproval]] = Field(
64
+ approvals: list[TokenApproval] | None = Field(
66
65
  None, description="Token approvals if requested"
67
66
  )
68
- error: Optional[str] = Field(None, description="Error message if any")
67
+ error: str | None = Field(None, description="Error message if any")
69
68
 
70
69
 
71
70
  class FetchChainPortfolio(WalletBaseTool):
@@ -87,7 +86,7 @@ class FetchChainPortfolio(WalletBaseTool):
87
86
  "- Token approvals (optional)\n"
88
87
  "Use this tool whenever a user wants to see their holdings on a specific blockchain."
89
88
  )
90
- args_schema: Type[BaseModel] = FetchChainPortfolioInput
89
+ args_schema: type[BaseModel] = FetchChainPortfolioInput
91
90
 
92
91
  async def _arun(
93
92
  self, address: str, chain_id: int, include_approvals: bool = False, **kwargs
@@ -2,7 +2,7 @@
2
2
 
3
3
  import json
4
4
  import logging
5
- from typing import Any, Dict, List, Optional, Type
5
+ from typing import Any
6
6
 
7
7
  from pydantic import BaseModel, Field
8
8
 
@@ -16,7 +16,7 @@ class FetchNftPortfolioInput(BaseModel):
16
16
  """Input for FetchNftPortfolio tool."""
17
17
 
18
18
  address: str = Field(..., description="Wallet address")
19
- chain_id: Optional[int] = Field(
19
+ chain_id: int | None = Field(
20
20
  None,
21
21
  description="Chain ID (if not specified, fetches from all supported chains)",
22
22
  )
@@ -26,7 +26,7 @@ class FetchNftPortfolioInput(BaseModel):
26
26
  solana_network: str = Field(
27
27
  default="mainnet", description="Solana network to use (mainnet or devnet)"
28
28
  )
29
- limit: Optional[int] = Field(100, description="Maximum number of NFTs to return")
29
+ limit: int | None = Field(100, description="Maximum number of NFTs to return")
30
30
  normalize_metadata: bool = Field(
31
31
  True, description="Whether to normalize metadata across different standards"
32
32
  )
@@ -35,12 +35,12 @@ class FetchNftPortfolioInput(BaseModel):
35
35
  class NftMetadata(BaseModel):
36
36
  """Model for NFT metadata."""
37
37
 
38
- name: Optional[str] = Field(None, description="NFT name")
39
- description: Optional[str] = Field(None, description="NFT description")
40
- image: Optional[str] = Field(None, description="NFT image URL")
41
- animation_url: Optional[str] = Field(None, description="NFT animation URL")
42
- attributes: Optional[List[Dict]] = Field(None, description="NFT attributes/traits")
43
- external_url: Optional[str] = Field(None, description="External URL")
38
+ name: str | None = Field(None, description="NFT name")
39
+ description: str | None = Field(None, description="NFT description")
40
+ image: str | None = Field(None, description="NFT image URL")
41
+ animation_url: str | None = Field(None, description="NFT animation URL")
42
+ attributes: list[dict] | None = Field(None, description="NFT attributes/traits")
43
+ external_url: str | None = Field(None, description="External URL")
44
44
 
45
45
 
46
46
  class NftItem(BaseModel):
@@ -48,14 +48,14 @@ class NftItem(BaseModel):
48
48
 
49
49
  token_id: str = Field(..., description="NFT token ID")
50
50
  token_address: str = Field(..., description="NFT contract address")
51
- contract_type: Optional[str] = Field(
51
+ contract_type: str | None = Field(
52
52
  None, description="NFT contract type (ERC721, ERC1155, etc.)"
53
53
  )
54
- name: Optional[str] = Field(None, description="NFT name")
55
- symbol: Optional[str] = Field(None, description="NFT symbol")
54
+ name: str | None = Field(None, description="NFT name")
55
+ symbol: str | None = Field(None, description="NFT symbol")
56
56
  owner_of: str = Field(..., description="Owner address")
57
- metadata: Optional[NftMetadata] = Field(None, description="NFT metadata")
58
- floor_price: Optional[float] = Field(None, description="Floor price if available")
57
+ metadata: NftMetadata | None = Field(None, description="NFT metadata")
58
+ floor_price: float | None = Field(None, description="Floor price if available")
59
59
  chain: str = Field("eth", description="Blockchain network")
60
60
 
61
61
 
@@ -63,13 +63,13 @@ class NftPortfolioOutput(BaseModel):
63
63
  """Output for FetchNftPortfolio tool."""
64
64
 
65
65
  address: str = Field(..., description="Wallet address")
66
- nfts: List[NftItem] = Field(default_factory=list, description="List of NFT items")
66
+ nfts: list[NftItem] = Field(default_factory=list, description="List of NFT items")
67
67
  total_count: int = Field(0, description="Total count of NFTs")
68
- chains: List[str] = Field(
68
+ chains: list[str] = Field(
69
69
  default_factory=list, description="Chains included in the response"
70
70
  )
71
- cursor: Optional[str] = Field(None, description="Cursor for pagination")
72
- error: Optional[str] = Field(None, description="Error message if any")
71
+ cursor: str | None = Field(None, description="Cursor for pagination")
72
+ error: str | None = Field(None, description="Error message if any")
73
73
 
74
74
 
75
75
  class FetchNftPortfolio(WalletBaseTool):
@@ -90,12 +90,12 @@ class FetchNftPortfolio(WalletBaseTool):
90
90
  "- Floor prices if available\n"
91
91
  "Use this tool whenever a user asks about their NFTs or digital collectibles."
92
92
  )
93
- args_schema: Type[BaseModel] = FetchNftPortfolioInput
93
+ args_schema: type[BaseModel] = FetchNftPortfolioInput
94
94
 
95
95
  async def _arun(
96
96
  self,
97
97
  address: str,
98
- chain_id: Optional[int] = None,
98
+ chain_id: int | None = None,
99
99
  include_solana: bool = False,
100
100
  solana_network: str = "mainnet",
101
101
  limit: int = 100,
@@ -156,7 +156,7 @@ class FetchNftPortfolio(WalletBaseTool):
156
156
  chain_id: int,
157
157
  limit: int,
158
158
  normalize_metadata: bool,
159
- result: Dict[str, Any],
159
+ result: dict[str, Any],
160
160
  ) -> None:
161
161
  """Fetch NFTs from an EVM chain.
162
162
 
@@ -222,7 +222,7 @@ class FetchNftPortfolio(WalletBaseTool):
222
222
  result["nfts"].append(nft_item)
223
223
 
224
224
  async def _fetch_solana_nfts(
225
- self, address: str, network: str, limit: int, result: Dict[str, Any]
225
+ self, address: str, network: str, limit: int, result: dict[str, Any]
226
226
  ) -> None:
227
227
  """Fetch NFTs from Solana.
228
228