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
@@ -0,0 +1,420 @@
1
+ """
2
+ Utility functions and constants for DexScreener skills.
3
+ """
4
+
5
+ import json
6
+ import logging
7
+ from collections.abc import Callable
8
+ from enum import Enum
9
+ from typing import Any
10
+
11
+ from pydantic import ValidationError
12
+
13
+ from intentkit.skills.dexscreener.model.search_token_response import PairModel
14
+
15
+ logger = logging.getLogger(__name__)
16
+
17
+ # API Base URL
18
+ DEXSCREENER_BASE_URL = "https://api.dexscreener.com"
19
+
20
+ # API Endpoints
21
+ API_ENDPOINTS = {
22
+ "search": "/latest/dex/search",
23
+ "pairs": "/latest/dex/pairs",
24
+ "token_pairs": "/token-pairs/v1",
25
+ "tokens": "/tokens/v1",
26
+ "token_profiles": "/token-profiles/latest/v1",
27
+ "token_boosts_latest": "/token-boosts/latest/v1",
28
+ "token_boosts_top": "/token-boosts/top/v1",
29
+ "orders": "/orders/v1",
30
+ }
31
+
32
+ # Rate Limits (requests per minute)
33
+ RATE_LIMITS = {
34
+ "search": 300,
35
+ "pairs": 300,
36
+ "token_pairs": 300,
37
+ "tokens": 300,
38
+ "token_profiles": 60,
39
+ "token_boosts": 60,
40
+ "orders": 60,
41
+ }
42
+
43
+ # Limits
44
+ MAX_SEARCH_RESULTS = 25
45
+ MAX_TOKENS_BATCH = 30
46
+
47
+ # Common disclaimer for search results
48
+ SEARCH_DISCLAIMER = {
49
+ "disclaimer": (
50
+ "Search results may include unofficial, duplicate, or potentially malicious tokens. "
51
+ "If multiple unrelated tokens share a similar name or ticker, ask the user for the exact token address. "
52
+ "If the correct token is not found, re-run the tool using the provided address. "
53
+ "Also advise the user to verify the token's legitimacy via its official social links included in the result."
54
+ )
55
+ }
56
+
57
+
58
+ # Query Types
59
+ class QueryType(str, Enum):
60
+ TEXT = "TEXT"
61
+ TICKER = "TICKER"
62
+ ADDRESS = "ADDRESS"
63
+
64
+
65
+ # Sort Options
66
+ class SortBy(str, Enum):
67
+ LIQUIDITY = "liquidity"
68
+ VOLUME = "volume"
69
+
70
+
71
+ # Volume Timeframes
72
+ class VolumeTimeframe(str, Enum):
73
+ FIVE_MINUTES = "5_minutes"
74
+ ONE_HOUR = "1_hour"
75
+ SIX_HOUR = "6_hour"
76
+ TWENTY_FOUR_HOUR = "24_hour"
77
+
78
+
79
+ # Supported Chain IDs
80
+ SUPPORTED_CHAINS = [
81
+ "ethereum",
82
+ "bsc",
83
+ "polygon",
84
+ "avalanche",
85
+ "fantom",
86
+ "cronos",
87
+ "arbitrum",
88
+ "optimism",
89
+ "base",
90
+ "solana",
91
+ "sui",
92
+ "tron",
93
+ "ton",
94
+ ]
95
+
96
+
97
+ def determine_query_type(query: str) -> QueryType:
98
+ """
99
+ Determine whether the query is a TEXT, TICKER, or ADDRESS.
100
+
101
+ Args:
102
+ query: The search query string
103
+
104
+ Returns:
105
+ QueryType enum value
106
+ """
107
+ if query.startswith("0x"):
108
+ return QueryType.ADDRESS
109
+ if query.startswith("$"):
110
+ return QueryType.TICKER
111
+ return QueryType.TEXT
112
+
113
+
114
+ def get_liquidity_value(pair: PairModel) -> float:
115
+ """
116
+ Extract liquidity USD value from a pair, defaulting to 0.0 if not available.
117
+
118
+ Args:
119
+ pair: PairModel instance
120
+
121
+ Returns:
122
+ Liquidity value in USD as float
123
+ """
124
+ return (
125
+ pair.liquidity.usd if pair.liquidity and pair.liquidity.usd is not None else 0.0
126
+ )
127
+
128
+
129
+ def get_volume_value(
130
+ pair: PairModel, timeframe: VolumeTimeframe = VolumeTimeframe.TWENTY_FOUR_HOUR
131
+ ) -> float:
132
+ """
133
+ Extract volume value from a pair for the specified timeframe.
134
+
135
+ Args:
136
+ pair: PairModel instance
137
+ timeframe: VolumeTimeframe enum value
138
+
139
+ Returns:
140
+ Volume value as float
141
+ """
142
+ if not pair.volume:
143
+ return 0.0
144
+
145
+ volume_map = {
146
+ VolumeTimeframe.FIVE_MINUTES: pair.volume.m5,
147
+ VolumeTimeframe.ONE_HOUR: pair.volume.h1,
148
+ VolumeTimeframe.SIX_HOUR: pair.volume.h6,
149
+ VolumeTimeframe.TWENTY_FOUR_HOUR: pair.volume.h24,
150
+ }
151
+
152
+ return volume_map.get(timeframe, 0.0) or 0.0
153
+
154
+
155
+ def get_sort_function(
156
+ sort_by: SortBy,
157
+ volume_timeframe: VolumeTimeframe = VolumeTimeframe.TWENTY_FOUR_HOUR,
158
+ ) -> Callable[[PairModel], float]:
159
+ """
160
+ Get the appropriate sorting function based on sort criteria.
161
+
162
+ Args:
163
+ sort_by: SortBy enum value
164
+ volume_timeframe: VolumeTimeframe enum value (used when sorting by volume)
165
+
166
+ Returns:
167
+ Callable function that takes a PairModel and returns a float for sorting
168
+ """
169
+ if sort_by == SortBy.LIQUIDITY:
170
+ return get_liquidity_value
171
+ elif sort_by == SortBy.VOLUME:
172
+ return lambda pair: get_volume_value(pair, volume_timeframe)
173
+ else:
174
+ logger.warning(f"Invalid sort_by value '{sort_by}', defaulting to liquidity.")
175
+ return get_liquidity_value
176
+
177
+
178
+ def sort_pairs_by_criteria(
179
+ pairs: list[PairModel],
180
+ sort_by: SortBy = SortBy.LIQUIDITY,
181
+ volume_timeframe: VolumeTimeframe = VolumeTimeframe.TWENTY_FOUR_HOUR,
182
+ reverse: bool = True,
183
+ ) -> list[PairModel]:
184
+ """
185
+ Sort pairs by the specified criteria.
186
+
187
+ Args:
188
+ pairs: List of PairModel instances to sort
189
+ sort_by: Sorting criteria (liquidity or volume)
190
+ volume_timeframe: Timeframe for volume sorting
191
+ reverse: Sort in descending order if True
192
+
193
+ Returns:
194
+ Sorted list of PairModel instances
195
+ """
196
+ try:
197
+ sort_func = get_sort_function(sort_by, volume_timeframe)
198
+ return sorted(pairs, key=sort_func, reverse=reverse)
199
+ except Exception as e:
200
+ logger.error(f"Failed to sort pairs: {e}", exc_info=True)
201
+ return pairs # Return original list if sorting fails
202
+
203
+
204
+ def filter_ticker_pairs(pairs: list[PairModel], target_ticker: str) -> list[PairModel]:
205
+ """
206
+ Filter pairs to only include those where base token symbol matches target ticker.
207
+
208
+ Args:
209
+ pairs: List of PairModel instances
210
+ target_ticker: Target ticker symbol (case-insensitive)
211
+
212
+ Returns:
213
+ Filtered list of PairModel instances
214
+ """
215
+ target_ticker_upper = target_ticker.upper()
216
+ return [
217
+ p
218
+ for p in pairs
219
+ if p.baseToken
220
+ and p.baseToken.symbol
221
+ and p.baseToken.symbol.upper() == target_ticker_upper
222
+ ]
223
+
224
+
225
+ def filter_address_pairs(
226
+ pairs: list[PairModel], target_address: str
227
+ ) -> list[PairModel]:
228
+ """
229
+ Filter pairs to only include those matching the target address.
230
+ Checks pairAddress, baseToken.address, and quoteToken.address.
231
+
232
+ Args:
233
+ pairs: List of PairModel instances
234
+ target_address: Target address (case-insensitive)
235
+
236
+ Returns:
237
+ Filtered list of PairModel instances
238
+ """
239
+ target_address_lower = target_address.lower()
240
+ return [
241
+ p
242
+ for p in pairs
243
+ if (p.pairAddress and p.pairAddress.lower() == target_address_lower)
244
+ or (
245
+ p.baseToken
246
+ and p.baseToken.address
247
+ and p.baseToken.address.lower() == target_address_lower
248
+ )
249
+ or (
250
+ p.quoteToken
251
+ and p.quoteToken.address
252
+ and p.quoteToken.address.lower() == target_address_lower
253
+ )
254
+ ]
255
+
256
+
257
+ def create_error_response(
258
+ error_type: str,
259
+ message: str,
260
+ details: str | None = None,
261
+ additional_data: dict[str, Any] | None = None,
262
+ ) -> str:
263
+ """
264
+ Create a standardized error response in JSON format.
265
+
266
+ Args:
267
+ error_type: Type/category of error
268
+ message: Human-readable error message
269
+ details: Optional additional details about the error
270
+ additional_data: Optional dictionary of additional data to include
271
+
272
+ Returns:
273
+ JSON string containing error information
274
+ """
275
+ response = {
276
+ "error": message,
277
+ "error_type": error_type,
278
+ }
279
+
280
+ if details:
281
+ response["details"] = details
282
+
283
+ if additional_data:
284
+ response.update(additional_data)
285
+
286
+ return json.dumps(response, indent=2)
287
+
288
+
289
+ def create_no_results_response(
290
+ query_info: str,
291
+ reason: str = "no results found",
292
+ additional_data: dict[str, Any] | None = None,
293
+ ) -> str:
294
+ """
295
+ Create a standardized "no results found" response.
296
+
297
+ Args:
298
+ query_info: Information about the query that was performed
299
+ reason: Reason why no results were found
300
+ additional_data: Optional additional data to include
301
+
302
+ Returns:
303
+ JSON string containing no results information
304
+ """
305
+ response = {
306
+ "message": f"No results found for the query. Reason: {reason}.",
307
+ "query_info": query_info,
308
+ "pairs": [],
309
+ }
310
+
311
+ if additional_data:
312
+ response.update(additional_data)
313
+
314
+ return json.dumps(response, indent=2)
315
+
316
+
317
+ def handle_validation_error(
318
+ error: ValidationError, query_info: str, data_length: int | None = None
319
+ ) -> str:
320
+ """
321
+ Handle validation errors in a standardized way.
322
+
323
+ Args:
324
+ error: The ValidationError that occurred
325
+ query_info: Information about the query being processed
326
+ data_length: Optional length of the data that failed validation
327
+
328
+ Returns:
329
+ JSON error response string
330
+ """
331
+ log_message = f"Failed to validate DexScreener response structure for {query_info}. Error: {error}"
332
+ if data_length:
333
+ log_message += f". Raw data length: {data_length}"
334
+
335
+ logger.error(log_message, exc_info=True)
336
+
337
+ return create_error_response(
338
+ error_type="validation_error",
339
+ message="Failed to parse successful DexScreener API response",
340
+ details=str(error.errors()),
341
+ additional_data={"query_info": query_info},
342
+ )
343
+
344
+
345
+ def truncate_large_fields(
346
+ data: dict[str, Any], max_length: int = 500
347
+ ) -> dict[str, Any]:
348
+ """
349
+ Truncate large string fields in error response data to avoid overwhelming the LLM.
350
+
351
+ Args:
352
+ data: Dictionary potentially containing large string fields
353
+ max_length: Maximum length for string fields before truncation
354
+
355
+ Returns:
356
+ Dictionary with truncated fields
357
+ """
358
+ truncated = data.copy()
359
+
360
+ for key in ["details", "response_body"]:
361
+ if isinstance(truncated.get(key), str) and len(truncated[key]) > max_length:
362
+ truncated[key] = truncated[key][:max_length] + "... (truncated)"
363
+
364
+ return truncated
365
+
366
+
367
+ def group_pairs_by_token(pairs: list[PairModel]) -> dict[str, list[PairModel]]:
368
+ """
369
+ Group pairs by token address for better organization in multi-token responses.
370
+
371
+ Args:
372
+ pairs: List of PairModel instances
373
+
374
+ Returns:
375
+ Dictionary mapping lowercase token addresses to lists of pairs
376
+ """
377
+ tokens_data = {}
378
+
379
+ for pair in pairs:
380
+ # Group by base token address
381
+ if pair.baseToken and pair.baseToken.address:
382
+ base_addr = pair.baseToken.address.lower()
383
+ if base_addr not in tokens_data:
384
+ tokens_data[base_addr] = []
385
+ tokens_data[base_addr].append(pair)
386
+
387
+ # Group by quote token address
388
+ if pair.quoteToken and pair.quoteToken.address:
389
+ quote_addr = pair.quoteToken.address.lower()
390
+ if quote_addr not in tokens_data:
391
+ tokens_data[quote_addr] = []
392
+ tokens_data[quote_addr].append(pair)
393
+
394
+ return tokens_data
395
+
396
+
397
+ def validate_chain_id(chain_id: str) -> bool:
398
+ """
399
+ Validate if the chain ID is supported.
400
+
401
+ Args:
402
+ chain_id: Chain ID to validate
403
+
404
+ Returns:
405
+ True if chain ID is supported, False otherwise
406
+ """
407
+ return chain_id.lower() in SUPPORTED_CHAINS
408
+
409
+
410
+ def format_success_response(data: dict[str, Any]) -> str:
411
+ """
412
+ Format a successful response as JSON string.
413
+
414
+ Args:
415
+ data: Response data dictionary
416
+
417
+ Returns:
418
+ JSON formatted string
419
+ """
420
+ return json.dumps(data, indent=2)
@@ -4,16 +4,15 @@ Loads and initializes skills for fetching data from Dune Analytics API.
4
4
  """
5
5
 
6
6
  import logging
7
- from typing import Dict, List, Optional, TypedDict
7
+ from typing import TypedDict
8
8
 
9
- from intentkit.abstracts.skill import SkillStoreABC
10
9
  from intentkit.skills.base import SkillConfig, SkillState
11
10
  from intentkit.skills.dune_analytics.base import DuneBaseTool
12
11
 
13
12
  logger = logging.getLogger(__name__)
14
13
 
15
14
  # Cache for skill instances
16
- _skill_cache: Dict[str, DuneBaseTool] = {}
15
+ _skill_cache: dict[str, DuneBaseTool] = {}
17
16
 
18
17
 
19
18
  class SkillStates(TypedDict):
@@ -33,9 +32,8 @@ class Config(SkillConfig):
33
32
  async def get_skills(
34
33
  config: Config,
35
34
  is_private: bool,
36
- store: SkillStoreABC,
37
35
  **kwargs,
38
- ) -> List[DuneBaseTool]:
36
+ ) -> list[DuneBaseTool]:
39
37
  """Load Dune Analytics skills based on configuration.
40
38
 
41
39
  Args:
@@ -59,7 +57,7 @@ async def get_skills(
59
57
 
60
58
  loaded_skills = []
61
59
  for name in available_skills:
62
- skill = get_dune_skill(name, store)
60
+ skill = get_dune_skill(name)
63
61
  if skill:
64
62
  logger.info("Successfully loaded skill: %s", name)
65
63
  loaded_skills.append(skill)
@@ -69,7 +67,7 @@ async def get_skills(
69
67
  return loaded_skills
70
68
 
71
69
 
72
- def get_dune_skill(name: str, store: SkillStoreABC) -> Optional[DuneBaseTool]:
70
+ def get_dune_skill(name: str) -> DuneBaseTool | None:
73
71
  """Retrieve a Dune Analytics skill instance by name.
74
72
 
75
73
  Args:
@@ -87,11 +85,11 @@ def get_dune_skill(name: str, store: SkillStoreABC) -> Optional[DuneBaseTool]:
87
85
  if name == "fetch_nation_metrics":
88
86
  from .fetch_nation_metrics import FetchNationMetrics
89
87
 
90
- _skill_cache[name] = FetchNationMetrics(skill_store=store)
88
+ _skill_cache[name] = FetchNationMetrics()
91
89
  elif name == "fetch_kol_buys":
92
90
  from .fetch_kol_buys import FetchKOLBuys
93
91
 
94
- _skill_cache[name] = FetchKOLBuys(skill_store=store)
92
+ _skill_cache[name] = FetchKOLBuys()
95
93
  else:
96
94
  logger.warning("Unknown Dune Analytics skill: %s", name)
97
95
  return None
@@ -1,52 +1,48 @@
1
- """Base module for Dune Analytics skills.
2
-
3
- Provides shared functionality for interacting with the Dune Analytics API.
4
- """
5
-
6
- from typing import Type
7
-
8
- from langchain.tools.base import ToolException
9
- from pydantic import BaseModel, Field
10
-
11
- from intentkit.abstracts.skill import SkillStoreABC
12
- from intentkit.skills.base import IntentKitSkill
13
-
14
-
15
- class DuneBaseTool(IntentKitSkill):
16
- """Base class for Dune Analytics skills.
17
-
18
- Offers common functionality like API key retrieval and Dune API interaction.
19
- """
20
-
21
- name: str = Field(description="Tool name")
22
- description: str = Field(description="Tool description")
23
- args_schema: Type[BaseModel]
24
- skill_store: SkillStoreABC = Field(description="Skill store for data persistence")
25
-
26
- def get_api_key(self) -> str:
27
- """Retrieve the Dune Analytics API key from context.
28
-
29
- Returns:
30
- API key string.
31
-
32
- Raises:
33
- ToolException: If the API key is not found.
34
- """
35
- context = self.get_context()
36
- skill_config = context.agent.skill_config(self.category)
37
- api_key_provider = skill_config.get("api_key_provider")
38
- if api_key_provider == "agent_owner":
39
- api_key = skill_config.get("api_key")
40
- if api_key:
41
- return api_key
42
- else:
43
- raise ToolException("No api_key found in agent_owner configuration")
44
- else:
45
- raise ToolException(
46
- f"Invalid API key provider: {api_key_provider}. Only 'agent_owner' is supported for Dune Analytics."
47
- )
48
-
49
- @property
50
- def category(self) -> str:
51
- """Category of the skill."""
52
- return "dune_analytics"
1
+ """Base module for Dune Analytics skills.
2
+
3
+ Provides shared functionality for interacting with the Dune Analytics API.
4
+ """
5
+
6
+ from langchain_core.tools.base import ToolException
7
+ from pydantic import BaseModel, Field
8
+
9
+ from intentkit.skills.base import IntentKitSkill
10
+
11
+
12
+ class DuneBaseTool(IntentKitSkill):
13
+ """Base class for Dune Analytics skills.
14
+
15
+ Offers common functionality like API key retrieval and Dune API interaction.
16
+ """
17
+
18
+ name: str = Field(description="Tool name")
19
+ description: str = Field(description="Tool description")
20
+ args_schema: type[BaseModel]
21
+
22
+ def get_api_key(self) -> str:
23
+ """Retrieve the Dune Analytics API key from context.
24
+
25
+ Returns:
26
+ API key string.
27
+
28
+ Raises:
29
+ ToolException: If the API key is not found.
30
+ """
31
+ context = self.get_context()
32
+ skill_config = context.agent.skill_config(self.category)
33
+ api_key_provider = skill_config.get("api_key_provider")
34
+ if api_key_provider == "agent_owner":
35
+ api_key = skill_config.get("api_key")
36
+ if api_key:
37
+ return api_key
38
+ else:
39
+ raise ToolException("No api_key found in agent_owner configuration")
40
+ else:
41
+ raise ToolException(
42
+ f"Invalid API key provider: {api_key_provider}. Only 'agent_owner' is supported for Dune Analytics."
43
+ )
44
+
45
+ @property
46
+ def category(self) -> str:
47
+ """Category of the skill."""
48
+ return "dune_analytics"
@@ -3,13 +3,12 @@
3
3
  Uses query ID 4832844 to retrieve a list of KOL buy transactions.
4
4
  """
5
5
 
6
- from typing import Any, Dict, Type
6
+ from typing import Any
7
7
 
8
8
  import httpx
9
9
  from pydantic import BaseModel, Field
10
10
  from tenacity import retry, stop_after_attempt, wait_exponential
11
11
 
12
- from intentkit.abstracts.skill import SkillStoreABC
13
12
  from intentkit.skills.dune_analytics.base import DuneBaseTool
14
13
 
15
14
  BASE_URL = "https://api.dune.com/api/v1/query"
@@ -29,7 +28,7 @@ class KOLBuysInput(BaseModel):
29
28
  class KOLBuyData(BaseModel):
30
29
  """Data model for KOL buy results."""
31
30
 
32
- data: Dict[str, Any] = Field(description="KOL buy data from Dune API")
31
+ data: dict[str, Any] = Field(description="KOL buy data from Dune API")
33
32
  error: str = Field(default="", description="Error message if fetch failed")
34
33
 
35
34
 
@@ -48,15 +47,14 @@ class FetchKOLBuys(DuneBaseTool):
48
47
  "Fetches a list of KOL memecoin buy transactions on Solana from Dune Analytics API using query ID 4832844. "
49
48
  "Supports a configurable limit for the number of results. Handles rate limits with retries."
50
49
  )
51
- args_schema: Type[BaseModel] = KOLBuysInput
52
- skill_store: SkillStoreABC = Field(description="Skill store for data persistence")
50
+ args_schema: type[BaseModel] = KOLBuysInput
53
51
 
54
52
  @retry(
55
53
  stop=stop_after_attempt(3), wait=wait_exponential(multiplier=5, min=5, max=60)
56
54
  )
57
55
  async def fetch_data(
58
56
  self, query_id: int, api_key: str, limit: int = 10
59
- ) -> Dict[str, Any]:
57
+ ) -> dict[str, Any]:
60
58
  """Fetch data for a specific Dune query.
61
59
 
62
60
  Args:
@@ -70,7 +68,7 @@ class FetchKOLBuys(DuneBaseTool):
70
68
  Raises:
71
69
  ToolException: If the API request fails.
72
70
  """
73
- from langchain.tools.base import ToolException
71
+ from langchain_core.tools.base import ToolException
74
72
 
75
73
  url = f"{BASE_URL}/{query_id}/results?limit={limit}"
76
74
  headers = {"X-Dune-API-Key": api_key}
@@ -5,13 +5,12 @@ Supports predefined metrics (e.g., total_users, unique_ai_citizens) or direct qu
5
5
 
6
6
  import difflib
7
7
  import re
8
- from typing import Any, Dict, Type
8
+ from typing import Any
9
9
 
10
10
  import httpx
11
11
  from pydantic import BaseModel, Field
12
12
  from tenacity import retry, stop_after_attempt, wait_exponential
13
13
 
14
- from intentkit.abstracts.skill import SkillStoreABC
15
14
  from intentkit.skills.dune_analytics.base import DuneBaseTool
16
15
 
17
16
  SUPPORTED_QUERIES = {
@@ -63,14 +62,14 @@ class MetricData(BaseModel):
63
62
  """Data model for a single metric result."""
64
63
 
65
64
  metric: str = Field(description="Metric name or query ID")
66
- data: Dict[str, Any] = Field(description="Metric data from Dune API")
65
+ data: dict[str, Any] = Field(description="Metric data from Dune API")
67
66
  error: str = Field(default="", description="Error message if fetch failed")
68
67
 
69
68
 
70
69
  class NationMetricsOutput(BaseModel):
71
70
  """Output schema for Crestal Nation metrics."""
72
71
 
73
- metrics: Dict[str, MetricData] = Field(
72
+ metrics: dict[str, MetricData] = Field(
74
73
  description="Dictionary of metric names or query IDs to their data"
75
74
  )
76
75
  summary: str = Field(description="Summary of fetched metrics")
@@ -85,8 +84,7 @@ class FetchNationMetrics(DuneBaseTool):
85
84
  "Supports predefined metrics, direct query IDs, or all configured metrics if none specified. "
86
85
  "Handles rate limits with retries."
87
86
  )
88
- args_schema: Type[BaseModel] = NationMetricsInput
89
- skill_store: SkillStoreABC = Field(description="Skill store for data persistence")
87
+ args_schema: type[BaseModel] = NationMetricsInput
90
88
 
91
89
  def normalize_metric(self, metric: str) -> str:
92
90
  """Normalize a metric string for matching.
@@ -124,7 +122,7 @@ class FetchNationMetrics(DuneBaseTool):
124
122
  )
125
123
  async def fetch_data(
126
124
  self, query_id: int, api_key: str, limit: int = 1000
127
- ) -> Dict[str, Any]:
125
+ ) -> dict[str, Any]:
128
126
  """Fetch data for a specific Dune query.
129
127
 
130
128
  Args:
@@ -138,7 +136,7 @@ class FetchNationMetrics(DuneBaseTool):
138
136
  Raises:
139
137
  ToolException: If the API request fails.
140
138
  """
141
- from langchain.tools.base import ToolException
139
+ from langchain_core.tools.base import ToolException
142
140
 
143
141
  url = f"{BASE_URL}/{query_id}/results?limit={limit}"
144
142
  headers = {"X-Dune-API-Key": api_key}