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
@@ -1,321 +1,182 @@
1
- import json
2
- import logging
3
- from enum import Enum
4
- from typing import (
5
- Any,
6
- Callable,
7
- Literal,
8
- Optional,
9
- Type,
10
- )
11
-
12
- from pydantic import BaseModel, Field, ValidationError
13
-
14
- from intentkit.skills.dexscreener.base import DexScreenerBaseTool
15
- from intentkit.skills.dexscreener.model.search_token_response import (
16
- PairModel,
17
- SearchTokenResponseModel,
18
- )
19
-
20
- logger = logging.getLogger(__name__)
21
-
22
- MAX_RESULTS_LIMIT = 25 # limit to 25 pair entries
23
- SEARCH_TOKEN_API_PATH = "/latest/dex/search"
24
-
25
- # Define the allowed sort options, including multiple volume types
26
- SortByOption = Literal["liquidity", "volume"]
27
- VolumeTimeframeOption = Literal["24_hour", "6_hour", "1_hour", "5_minutes"]
28
-
29
-
30
- class QueryType(str, Enum):
31
- TEXT = "TEXT"
32
- TICKER = "TICKER"
33
- ADDRESS = "ADDRESS"
34
-
35
-
36
- # this will bring aloside with pairs information
37
- DISCLAIMER_TEXT = {
38
- "disclaimer": (
39
- "Search results may include unofficial, duplicate, or potentially malicious tokens. "
40
- "If multiple unrelated tokens share a similar name or ticker, ask the user for the exact token address. "
41
- "If the correct token is not found, re-run the tool using the provided address. "
42
- "Also advise the user to verify the token's legitimacy via its official social links included in the result."
43
- )
44
- }
45
-
46
-
47
- class SearchTokenInput(BaseModel):
48
- """Input schema for the DexScreener search_token tool."""
49
-
50
- query: str = Field(
51
- description="The search query string (e.g., token symbol 'WIF', pair address, token address '0x...', token name 'Dogwifhat', or ticker '$WIF'). Prefixing with '$' filters results to match the base token symbol exactly (case-insensitive)."
52
- )
53
- sort_by: Optional[SortByOption] = Field(
54
- default="liquidity",
55
- description="Sort preference for the results. Options: 'liquidity' (default) or 'volume'",
56
- )
57
- volume_timeframe: Optional[VolumeTimeframeOption] = Field(
58
- default="24_hour",
59
- description=f"define which timeframe should we use if the 'sort_by' is `volume` avalable options are {VolumeTimeframeOption}",
60
- )
61
-
62
-
63
- class SearchToken(DexScreenerBaseTool):
64
- """
65
- Tool to search for token pairs on DexScreener based on a query string.
66
- """
67
-
68
- name: str = "dexscreener_search_token"
69
- description: str = (
70
- f"Searches DexScreener for token pairs matching the provided query string "
71
- f"(e.g., token symbol like 'WIF', pair address, token name like 'Dogwifhat', or ticker like '$WIF'). "
72
- f"If the query starts with '$', it filters results to only include pairs where the base token symbol exactly matches the ticker (case-insensitive). "
73
- f"Returns a list of matching pairs with details like price, volume, liquidity, etc., "
74
- f"sorted by the specified criteria (via 'sort_by': 'liquidity', 'volume'; defaults to 'liquidity'), "
75
- f"limited to the top {MAX_RESULTS_LIMIT}. "
76
- f"Use this tool to find token information based on user queries."
77
- )
78
- args_schema: Type[BaseModel] = SearchTokenInput
79
-
80
- async def _arun(
81
- self,
82
- query: str,
83
- sort_by: Optional[SortByOption] = "liquidity",
84
- volume_timeframe: Optional[VolumeTimeframeOption] = "24_hour",
85
- **kwargs: Any,
86
- ) -> str:
87
- """Implementation to search token, with filtering based on query type."""
88
-
89
- # dexscreener 300 request per minute (across all user) based on dexscreener docs
90
- # https://docs.dexscreener.com/api/reference#get-latest-dex-search
91
- await self.user_rate_limit_by_category(
92
- # using hardcoded user_id to make sure it limit across all users
93
- user_id=f"{self.category}{self.name}",
94
- limit=300,
95
- minutes=1,
96
- )
97
-
98
- sort_by = sort_by or "liquidity"
99
- volume_timeframe = volume_timeframe or "24_hour"
100
-
101
- # Determine query type
102
- query_type = self.get_query_type(query)
103
-
104
- # Process query based on type
105
- if query_type == QueryType.TICKER:
106
- search_query = query[1:] # Remove the '$' prefix
107
- target_ticker = search_query.upper()
108
- else:
109
- search_query = query
110
- target_ticker = None
111
-
112
- logger.info(
113
- f"Executing DexScreener search_token tool with query: '{query}' "
114
- f"(interpreted as {query_type.value} search for '{search_query}'), "
115
- f"sort_by: {sort_by}"
116
- )
117
-
118
- ### --- sort functions ---
119
- def get_liquidity_usd(pair: PairModel) -> float:
120
- return (
121
- pair.liquidity.usd
122
- if pair.liquidity and pair.liquidity.usd is not None
123
- else 0.0
124
- )
125
-
126
- def get_volume(pair: PairModel) -> float:
127
- if not pair.volume:
128
- return 0.0
129
- return {
130
- "24_hour": pair.volume.h24,
131
- "6_hour": pair.volume.h6,
132
- "1_hour": pair.volume.h1,
133
- "5_minutes": pair.volume.m5,
134
- }.get(volume_timeframe, 0.0) or 0.0
135
-
136
- def get_sort_key_func() -> Callable[[PairModel], float]:
137
- if sort_by == "liquidity":
138
- return get_liquidity_usd
139
- if sort_by == "volume":
140
- return get_volume
141
- logger.warning(
142
- f"Invalid sort_by value '{sort_by}', defaulting to liquidity."
143
- )
144
- return get_liquidity_usd
145
-
146
- ### --- END sort functions ---
147
-
148
- try:
149
- data, error_details = await self._get(
150
- path=SEARCH_TOKEN_API_PATH, params={"q": search_query}
151
- )
152
-
153
- if error_details:
154
- return await self._handle_error_response(error_details)
155
- if not data:
156
- logger.error(f"No data or error details returned for query '{query}'")
157
- return json.dumps(
158
- {
159
- "error": "API call returned empty success response.",
160
- "error_type": "empty_success",
161
- },
162
- indent=2,
163
- )
164
-
165
- try:
166
- result = SearchTokenResponseModel.model_validate(data)
167
- except ValidationError as e:
168
- return await self._handle_validation_error(e, query, data)
169
-
170
- if not result.pairs:
171
- return await self._no_pairs_found_response(
172
- query, reason="returned null or empty for pairs"
173
- )
174
-
175
- pairs_list = [p for p in result.pairs if p is not None]
176
-
177
- # Apply filtering based on query type
178
- if query_type == QueryType.TICKER and target_ticker:
179
- pairs_list = [
180
- p
181
- for p in pairs_list
182
- if p.baseToken
183
- and p.baseToken.symbol
184
- and p.baseToken.symbol.upper() == target_ticker
185
- ]
186
- if not pairs_list:
187
- return await self._no_pairs_found_response(
188
- query, reason=f"no match for ticker '${target_ticker}'"
189
- )
190
- elif query_type == QueryType.ADDRESS:
191
- # Filter by address (checking pairAddress, baseToken.address, quoteToken.address)
192
- pairs_list = [
193
- p
194
- for p in pairs_list
195
- if (p.pairAddress and p.pairAddress.lower() == search_query.lower())
196
- or (
197
- p.baseToken
198
- and p.baseToken.address
199
- and p.baseToken.address.lower() == search_query.lower()
200
- )
201
- or (
202
- p.quoteToken
203
- and p.quoteToken.address
204
- and p.quoteToken.address.lower() == search_query.lower()
205
- )
206
- ]
207
- if not pairs_list:
208
- return await self._no_pairs_found_response(
209
- query, reason=f"no match for address '{search_query}'"
210
- )
211
-
212
- try:
213
- sort_func = get_sort_key_func()
214
- pairs_list.sort(key=sort_func, reverse=True)
215
- except Exception as sort_err:
216
- logger.error(f"Sorting failed: {sort_err}", exc_info=True)
217
- return json.dumps(
218
- {
219
- "error": "Failed to sort results.",
220
- "error_type": "sorting_error",
221
- "details": str(sort_err),
222
- "unsorted_results": [
223
- p.model_dump() for p in pairs_list[:MAX_RESULTS_LIMIT]
224
- ],
225
- **DISCLAIMER_TEXT,
226
- },
227
- indent=2,
228
- )
229
-
230
- final_count = min(len(pairs_list), MAX_RESULTS_LIMIT)
231
- logger.info(f"Returning {final_count} pairs for query '{query}'")
232
- return json.dumps(
233
- {
234
- **DISCLAIMER_TEXT,
235
- "pairs": [p.model_dump() for p in pairs_list[:MAX_RESULTS_LIMIT]],
236
- },
237
- indent=2,
238
- )
239
- except Exception as e:
240
- return await self._handle_unexpected_runtime_error(e, query)
241
-
242
- def get_query_type(self, query: str) -> QueryType:
243
- """
244
- Determine whether the query is a TEXT, TICKER, or ADDRESS.
245
-
246
- TICKER: starts with '$'
247
- ADDRESS: starts with '0x'.
248
- TEXT: anything else.
249
- """
250
- if query.startswith("0x"):
251
- return QueryType.ADDRESS
252
- if query.startswith("$"):
253
- return QueryType.TICKER
254
- return QueryType.TEXT
255
-
256
- async def _handle_error_response(self, error_details: dict) -> str:
257
- """Formats error details (from _get) into a JSON string."""
258
- if error_details.get("error_type") in [
259
- "connection_error",
260
- "parsing_error",
261
- "unexpected_error",
262
- ]:
263
- logger.error(f"DexScreener tool encountered an error: {error_details}")
264
- else: # api_error
265
- logger.warning(f"DexScreener API returned an error: {error_details}")
266
-
267
- # Truncate potentially large fields before returning to user/LLM
268
- for key in ["details", "response_body"]:
269
- if (
270
- isinstance(error_details.get(key), str)
271
- and len(error_details[key]) > 500
272
- ):
273
- error_details[key] = error_details[key][:500] + "... (truncated)"
274
-
275
- return json.dumps(error_details, indent=2)
276
-
277
- async def _handle_validation_error(
278
- self, e: ValidationError, query: str, data: Any
279
- ) -> str:
280
- """Formats validation error details into a JSON string."""
281
- logger.error(
282
- f"Failed to validate DexScreener response structure for query '{query}'. Error: {e}. Raw data length: {len(str(data))}",
283
- exc_info=True,
284
- )
285
- # Avoid sending potentially huge raw data back
286
- return json.dumps(
287
- {
288
- "error": "Failed to parse successful DexScreener API response",
289
- "error_type": "validation_error",
290
- "details": e.errors(),
291
- },
292
- indent=2,
293
- )
294
-
295
- async def _handle_unexpected_runtime_error(self, e: Exception, query: str) -> str:
296
- """Formats unexpected runtime exception details into a JSON string."""
297
- logger.exception(
298
- f"An unexpected runtime error occurred in search_token tool _arun method for query '{query}': {e}"
299
- )
300
- return json.dumps(
301
- {
302
- "error": "An unexpected internal error occurred processing the search request",
303
- "error_type": "runtime_error",
304
- "details": str(e),
305
- },
306
- indent=2,
307
- )
308
-
309
- async def _no_pairs_found_response(
310
- self, query: str, reason: str = "returned no matching pairs"
311
- ) -> str:
312
- """Generates the standard 'no pairs found' JSON response."""
313
- logger.info(f"DexScreener search for query '{query}': {reason}.")
314
- return json.dumps(
315
- {
316
- "message": f"No matching pairs found for the query '{query}'. Reason: {reason}.",
317
- "query": query,
318
- "pairs": [],
319
- },
320
- indent=2,
321
- )
1
+ import logging
2
+ from typing import Any
3
+
4
+ from pydantic import BaseModel, Field, ValidationError
5
+
6
+ from intentkit.skills.dexscreener.base import DexScreenerBaseTool
7
+ from intentkit.skills.dexscreener.model.search_token_response import (
8
+ SearchTokenResponseModel,
9
+ )
10
+ from intentkit.skills.dexscreener.utils import (
11
+ API_ENDPOINTS,
12
+ MAX_SEARCH_RESULTS,
13
+ SEARCH_DISCLAIMER,
14
+ QueryType,
15
+ SortBy,
16
+ VolumeTimeframe,
17
+ create_error_response,
18
+ create_no_results_response,
19
+ determine_query_type,
20
+ filter_address_pairs,
21
+ filter_ticker_pairs,
22
+ format_success_response,
23
+ handle_validation_error,
24
+ sort_pairs_by_criteria,
25
+ truncate_large_fields,
26
+ )
27
+
28
+ logger = logging.getLogger(__name__)
29
+
30
+
31
+ class SearchTokenInput(BaseModel):
32
+ """Input schema for the DexScreener search_token tool."""
33
+
34
+ query: str = Field(
35
+ description="The search query string (e.g., token symbol 'WIF', pair address, token address '0x...', token name 'Dogwifhat', or ticker '$WIF'). Prefixing with '$' filters results to match the base token symbol exactly (case-insensitive)."
36
+ )
37
+ sort_by: SortBy | None = Field(
38
+ default=SortBy.LIQUIDITY,
39
+ description="Sort preference for the results. Options: 'liquidity' (default) or 'volume'",
40
+ )
41
+ volume_timeframe: VolumeTimeframe | None = Field(
42
+ default=VolumeTimeframe.TWENTY_FOUR_HOUR,
43
+ description="Define which timeframe should we use if the 'sort_by' is 'volume'. Available options: '5_minutes', '1_hour', '6_hour', '24_hour'",
44
+ )
45
+
46
+
47
+ class SearchToken(DexScreenerBaseTool):
48
+ """
49
+ Tool to search for token pairs on DexScreener based on a query string.
50
+ """
51
+
52
+ name: str = "dexscreener_search_token"
53
+ description: str = (
54
+ f"Searches DexScreener for token pairs matching the provided query string "
55
+ f"(e.g., token symbol like 'WIF', pair address, token name like 'Dogwifhat', or ticker like '$WIF'). "
56
+ f"If the query starts with '$', it filters results to only include pairs where the base token symbol exactly matches the ticker (case-insensitive). "
57
+ f"Returns a list of matching pairs with details like price, volume, liquidity, etc., "
58
+ f"sorted by the specified criteria (via 'sort_by': 'liquidity', 'volume'; defaults to 'liquidity'), "
59
+ f"limited to the top {MAX_SEARCH_RESULTS}. "
60
+ f"Use this tool to find token information based on user queries."
61
+ )
62
+ args_schema: type[BaseModel] = SearchTokenInput
63
+
64
+ async def _arun(
65
+ self,
66
+ query: str,
67
+ sort_by: SortBy | None = SortBy.LIQUIDITY,
68
+ volume_timeframe: VolumeTimeframe | None = VolumeTimeframe.TWENTY_FOUR_HOUR,
69
+ **kwargs: Any,
70
+ ) -> str:
71
+ """Implementation to search token, with filtering based on query type."""
72
+
73
+ # dexscreener 300 request per minute (across all user) based on dexscreener docs
74
+ # https://docs.dexscreener.com/api/reference#get-latest-dex-search
75
+ await self.global_rate_limit_by_skill(
76
+ limit=300,
77
+ seconds=60,
78
+ )
79
+
80
+ sort_by = sort_by or SortBy.LIQUIDITY
81
+ volume_timeframe = volume_timeframe or VolumeTimeframe.TWENTY_FOUR_HOUR
82
+
83
+ # Determine query type
84
+ query_type = determine_query_type(query)
85
+
86
+ # Process query based on type
87
+ if query_type == QueryType.TICKER:
88
+ search_query = query[1:] # Remove the '$' prefix
89
+ target_ticker = search_query.upper()
90
+ else:
91
+ search_query = query
92
+ target_ticker = None
93
+
94
+ logger.info(
95
+ f"Executing DexScreener search_token tool with query: '{query}' "
96
+ f"(interpreted as {query_type.value} search for '{search_query}'), "
97
+ f"sort_by: {sort_by}"
98
+ )
99
+
100
+ try:
101
+ data, error_details = await self._get(
102
+ path=API_ENDPOINTS["search"], params={"q": search_query}
103
+ )
104
+
105
+ if error_details:
106
+ return await self._handle_error_response(error_details)
107
+ if not data:
108
+ logger.error(f"No data or error details returned for query '{query}'")
109
+ return create_error_response(
110
+ error_type="empty_success",
111
+ message="API call returned empty success response.",
112
+ additional_data={"query": query},
113
+ )
114
+
115
+ try:
116
+ result = SearchTokenResponseModel.model_validate(data)
117
+ except ValidationError as e:
118
+ return handle_validation_error(e, query, len(str(data)))
119
+
120
+ if not result.pairs:
121
+ return create_no_results_response(
122
+ query, reason="returned null or empty for pairs"
123
+ )
124
+
125
+ pairs_list = [p for p in result.pairs if p is not None]
126
+
127
+ # Apply filtering based on query type
128
+ if query_type == QueryType.TICKER and target_ticker:
129
+ pairs_list = filter_ticker_pairs(pairs_list, target_ticker)
130
+ if not pairs_list:
131
+ return create_no_results_response(
132
+ query, reason=f"no match for ticker '${target_ticker}'"
133
+ )
134
+ elif query_type == QueryType.ADDRESS:
135
+ pairs_list = filter_address_pairs(pairs_list, search_query)
136
+ if not pairs_list:
137
+ return create_no_results_response(
138
+ query, reason=f"no match for address '{search_query}'"
139
+ )
140
+
141
+ # Sort pairs by specified criteria
142
+ pairs_list = sort_pairs_by_criteria(pairs_list, sort_by, volume_timeframe)
143
+
144
+ # If sorting failed, pairs_list will be returned unchanged by the utility function
145
+
146
+ final_count = min(len(pairs_list), MAX_SEARCH_RESULTS)
147
+ logger.info(f"Returning {final_count} pairs for query '{query}'")
148
+ return format_success_response(
149
+ {
150
+ **SEARCH_DISCLAIMER,
151
+ "pairs": [p.model_dump() for p in pairs_list[:MAX_SEARCH_RESULTS]],
152
+ }
153
+ )
154
+ except Exception as e:
155
+ return await self._handle_unexpected_runtime_error(e, query)
156
+
157
+ async def _handle_error_response(self, error_details: dict) -> str:
158
+ """Formats error details (from _get) into a JSON string."""
159
+ if error_details.get("error_type") in [
160
+ "connection_error",
161
+ "parsing_error",
162
+ "unexpected_error",
163
+ ]:
164
+ logger.error(f"DexScreener tool encountered an error: {error_details}")
165
+ else: # api_error
166
+ logger.warning(f"DexScreener API returned an error: {error_details}")
167
+
168
+ # Truncate potentially large fields before returning to user/LLM
169
+ truncated_details = truncate_large_fields(error_details)
170
+ return format_success_response(truncated_details)
171
+
172
+ async def _handle_unexpected_runtime_error(self, e: Exception, query: str) -> str:
173
+ """Formats unexpected runtime exception details into a JSON string."""
174
+ logger.exception(
175
+ f"An unexpected runtime error occurred in search_token tool _arun method for query '{query}': {e}"
176
+ )
177
+ return create_error_response(
178
+ error_type="runtime_error",
179
+ message="An unexpected internal error occurred processing the search request",
180
+ details=str(e),
181
+ additional_data={"query": query},
182
+ )