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,9 @@
1
1
  """Utility functions for Elfa skills."""
2
2
 
3
- from typing import Any, Dict, Optional
3
+ from typing import Any
4
4
 
5
5
  import httpx
6
- from langchain.tools.base import ToolException
6
+ from langchain_core.tools.base import ToolException
7
7
  from pydantic import BaseModel, Field
8
8
 
9
9
  from .base import base_url
@@ -14,13 +14,13 @@ class ElfaResponse(BaseModel):
14
14
 
15
15
  success: bool
16
16
  data: Any = None
17
- metadata: Optional[Dict[str, Any]] = None
17
+ metadata: dict[str, Any] | None = None
18
18
 
19
19
 
20
20
  async def make_elfa_request(
21
21
  endpoint: str,
22
22
  api_key: str,
23
- params: Optional[Dict[str, Any]] = None,
23
+ params: dict[str, Any] | None = None,
24
24
  timeout: int = 30,
25
25
  ) -> ElfaResponse:
26
26
  """
@@ -82,32 +82,32 @@ async def make_elfa_request(
82
82
  class RepostBreakdown(BaseModel):
83
83
  """Repost breakdown data."""
84
84
 
85
- smart: Optional[int] = None
86
- ct: Optional[int] = None
85
+ smart: int | None = None
86
+ ct: int | None = None
87
87
 
88
88
 
89
89
  class Account(BaseModel):
90
90
  """Account information."""
91
91
 
92
- username: Optional[str] = None
93
- isVerified: Optional[bool] = None
92
+ username: str | None = None
93
+ isVerified: bool | None = None
94
94
 
95
95
 
96
96
  class MentionData(BaseModel):
97
97
  """Base mention data structure used across multiple endpoints."""
98
98
 
99
- tweetId: Optional[str] = Field(None, description="Tweet ID")
100
- link: Optional[str] = Field(None, description="Link to the tweet")
101
- likeCount: Optional[int] = Field(None, description="Number of likes")
102
- repostCount: Optional[int] = Field(None, description="Number of reposts")
103
- viewCount: Optional[int] = Field(None, description="Number of views")
104
- quoteCount: Optional[int] = Field(None, description="Number of quotes")
105
- replyCount: Optional[int] = Field(None, description="Number of replies")
106
- bookmarkCount: Optional[int] = Field(None, description="Number of bookmarks")
107
- mentionedAt: Optional[str] = Field(None, description="When mentioned")
108
- type: Optional[str] = Field(None, description="Post type")
109
- account: Optional[Account] = Field(None, description="Account information")
110
- repostBreakdown: Optional[RepostBreakdown] = Field(
99
+ tweetId: str | None = Field(None, description="Tweet ID")
100
+ link: str | None = Field(None, description="Link to the tweet")
101
+ likeCount: int | None = Field(None, description="Number of likes")
102
+ repostCount: int | None = Field(None, description="Number of reposts")
103
+ viewCount: int | None = Field(None, description="Number of views")
104
+ quoteCount: int | None = Field(None, description="Number of quotes")
105
+ replyCount: int | None = Field(None, description="Number of replies")
106
+ bookmarkCount: int | None = Field(None, description="Number of bookmarks")
107
+ mentionedAt: str | None = Field(None, description="When mentioned")
108
+ type: str | None = Field(None, description="Post type")
109
+ account: Account | None = Field(None, description="Account information")
110
+ repostBreakdown: RepostBreakdown | None = Field(
111
111
  None, description="Repost breakdown"
112
112
  )
113
113
 
@@ -115,15 +115,13 @@ class MentionData(BaseModel):
115
115
  class SmartStatsData(BaseModel):
116
116
  """Smart stats data structure."""
117
117
 
118
- smartFollowingCount: Optional[int] = Field(
119
- None, description="Smart following count"
120
- )
121
- averageEngagement: Optional[float] = Field(None, description="Average engagement")
122
- averageReach: Optional[float] = Field(None, description="Average reach")
123
- smartFollowerCount: Optional[int] = Field(None, description="Smart follower count")
124
- followerCount: Optional[int] = Field(None, description="Total follower count")
118
+ smartFollowingCount: int | None = Field(None, description="Smart following count")
119
+ averageEngagement: float | None = Field(None, description="Average engagement")
120
+ averageReach: float | None = Field(None, description="Average reach")
121
+ smartFollowerCount: int | None = Field(None, description="Smart follower count")
122
+ followerCount: int | None = Field(None, description="Total follower count")
125
123
 
126
124
 
127
- def clean_params(params: Dict[str, Any]) -> Dict[str, Any]:
125
+ def clean_params(params: dict[str, Any]) -> dict[str, Any]:
128
126
  """Remove None values from parameters dict."""
129
127
  return {k: v for k, v in params.items() if v is not None}
@@ -1,9 +1,8 @@
1
1
  """Enso skills."""
2
2
 
3
3
  import logging
4
- from typing import List, NotRequired, TypedDict
4
+ from typing import NotRequired, TypedDict
5
5
 
6
- from intentkit.abstracts.skill import SkillStoreABC
7
6
  from intentkit.skills.base import SkillConfig, SkillState
8
7
  from intentkit.skills.enso.base import EnsoBaseTool
9
8
  from intentkit.skills.enso.best_yield import EnsoGetBestYield
@@ -36,13 +35,12 @@ class Config(SkillConfig):
36
35
 
37
36
  states: SkillStates
38
37
  api_token: NotRequired[str]
39
- main_tokens: List[str]
38
+ main_tokens: NotRequired[list[str]]
40
39
 
41
40
 
42
41
  async def get_skills(
43
42
  config: Config,
44
43
  is_private: bool,
45
- store: SkillStoreABC,
46
44
  **_,
47
45
  ) -> list[EnsoBaseTool]:
48
46
  """Get all Enso skills."""
@@ -58,7 +56,7 @@ async def get_skills(
58
56
  # Get each skill using the cached getter
59
57
  result = []
60
58
  for name in available_skills:
61
- skill = get_enso_skill(name, store)
59
+ skill = get_enso_skill(name)
62
60
  if skill:
63
61
  result.append(skill)
64
62
  return result
@@ -66,49 +64,31 @@ async def get_skills(
66
64
 
67
65
  def get_enso_skill(
68
66
  name: str,
69
- skill_store: SkillStoreABC,
70
67
  ) -> EnsoBaseTool:
71
68
  """Get an Enso skill by name.
72
69
 
73
70
  Args:
74
71
  name: The name of the skill to get
75
- skill_store: The skill store for persisting data
76
72
 
77
73
  Returns:
78
74
  The requested Enso skill
79
75
  """
80
76
  if name == "get_networks":
81
- return EnsoGetNetworks(
82
- skill_store=skill_store,
83
- )
77
+ return EnsoGetNetworks()
84
78
  if name == "get_tokens":
85
- return EnsoGetTokens(
86
- skill_store=skill_store,
87
- )
79
+ return EnsoGetTokens()
88
80
  if name == "get_prices":
89
- return EnsoGetPrices(
90
- skill_store=skill_store,
91
- )
81
+ return EnsoGetPrices()
92
82
  if name == "get_wallet_approvals":
93
- return EnsoGetWalletApprovals(
94
- skill_store=skill_store,
95
- )
83
+ return EnsoGetWalletApprovals()
96
84
  if name == "get_wallet_balances":
97
- return EnsoGetWalletBalances(
98
- skill_store=skill_store,
99
- )
85
+ return EnsoGetWalletBalances()
100
86
  if name == "wallet_approve":
101
- return EnsoWalletApprove(
102
- skill_store=skill_store,
103
- )
87
+ return EnsoWalletApprove()
104
88
  if name == "route_shortcut":
105
- return EnsoRouteShortcut(
106
- skill_store=skill_store,
107
- )
89
+ return EnsoRouteShortcut()
108
90
  if name == "get_best_yield":
109
- return EnsoGetBestYield(
110
- skill_store=skill_store,
111
- )
91
+ return EnsoGetBestYield()
112
92
  else:
113
93
  logger.warning(f"Unknown Enso skill: {name}")
114
94
  return None
@@ -1,18 +1,20 @@
1
- from typing import Optional, Type
1
+ from decimal import Decimal
2
2
 
3
- from cdp import EvmServerAccount
4
- from coinbase_agentkit import CdpEvmServerWalletProvider
5
- from langchain.tools.base import ToolException
3
+ from coinbase_agentkit import CdpEvmWalletProvider
4
+ from langchain_core.tools.base import ToolException
6
5
  from pydantic import BaseModel, Field
7
6
 
8
7
  from intentkit.abstracts.graph import AgentContext
9
- from intentkit.abstracts.skill import SkillStoreABC
10
- from intentkit.clients import CdpClient, get_cdp_client
8
+ from intentkit.clients import get_wallet_provider as get_agent_wallet_provider
9
+ from intentkit.config.config import config
11
10
  from intentkit.skills.base import IntentKitSkill
12
- from intentkit.utils.chain import ChainProvider, NetworkId
11
+ from intentkit.utils.chain import (
12
+ ChainProvider,
13
+ network_to_id,
14
+ resolve_quicknode_network,
15
+ )
13
16
 
14
17
  base_url = "https://api.enso.finance"
15
- default_chain_id = int(NetworkId.BaseMainnet)
16
18
 
17
19
 
18
20
  class EnsoBaseTool(IntentKitSkill):
@@ -20,39 +22,25 @@ class EnsoBaseTool(IntentKitSkill):
20
22
 
21
23
  name: str = Field(description="The name of the tool")
22
24
  description: str = Field(description="A description of what the tool does")
23
- args_schema: Type[BaseModel]
24
- skill_store: SkillStoreABC = Field(
25
- description="The skill store for persisting data"
26
- )
25
+ args_schema: type[BaseModel]
27
26
 
28
- async def get_account(self, context: AgentContext) -> Optional[EvmServerAccount]:
29
- """Get the account object from the CDP client.
30
-
31
- Args:
32
- context: The skill context containing agent information.
33
-
34
- Returns:
35
- Optional[EvmServerAccount]: The account object if available.
36
- """
37
- client: CdpClient = await get_cdp_client(context.agent.id, self.skill_store)
38
- return await client.get_account()
39
-
40
- async def get_wallet_provider(
41
- self, context: AgentContext
42
- ) -> Optional[CdpEvmServerWalletProvider]:
27
+ async def get_wallet_provider(self, context: AgentContext) -> CdpEvmWalletProvider:
43
28
  """Get the wallet provider from the CDP client.
44
29
 
45
30
  Args:
46
31
  context: The skill context containing agent information.
47
32
 
48
33
  Returns:
49
- Optional[CdpEvmServerWalletProvider]: The wallet provider if available.
34
+ CdpEvmWalletProvider | None: The wallet provider if available.
50
35
  """
51
- client: CdpClient = await get_cdp_client(context.agent.id, self.skill_store)
52
- return await client.get_wallet_provider()
36
+ return await get_agent_wallet_provider(context.agent)
53
37
 
54
- def get_chain_provider(self, context: AgentContext) -> Optional[ChainProvider]:
55
- return self.skill_store.get_system_config("chain_provider")
38
+ async def get_wallet_address(self, context: AgentContext) -> str:
39
+ provider: CdpEvmWalletProvider = await self.get_wallet_provider(context)
40
+ return provider.get_address()
41
+
42
+ def get_chain_provider(self, context: AgentContext) -> ChainProvider | None:
43
+ return config.chain_provider
56
44
 
57
45
  def get_main_tokens(self, context: AgentContext) -> list[str]:
58
46
  skill_config = context.agent.skill_config(self.category)
@@ -60,12 +48,11 @@ class EnsoBaseTool(IntentKitSkill):
60
48
  return skill_config["main_tokens"]
61
49
  return []
62
50
 
63
- def get_api_key(self) -> str:
64
- context = self.get_context()
51
+ def get_api_token(self, context: AgentContext) -> str:
65
52
  skill_config = context.agent.skill_config(self.category)
66
53
  api_key_provider = skill_config.get("api_key_provider")
67
54
  if api_key_provider == "platform":
68
- return self.skill_store.get_system_config("enso_api_token")
55
+ return config.enso_api_token
69
56
  # for backward compatibility, may only have api_token in skill_config
70
57
  elif skill_config.get("api_token"):
71
58
  return skill_config.get("api_token")
@@ -74,6 +61,38 @@ class EnsoBaseTool(IntentKitSkill):
74
61
  f"Invalid API key provider: {api_key_provider}, or no api_token in config"
75
62
  )
76
63
 
64
+ def resolve_chain_id(
65
+ self, context: AgentContext, chain_id: int | None = None
66
+ ) -> int:
67
+ if chain_id:
68
+ return chain_id
69
+
70
+ agent = context.agent
71
+ try:
72
+ network = resolve_quicknode_network(agent.network_id)
73
+ except ValueError as exc: # pragma: no cover - defensive
74
+ raise ToolException(
75
+ f"Unsupported network configured for agent: {agent.network_id}"
76
+ ) from exc
77
+
78
+ network_id = network_to_id.get(network)
79
+ if network_id is None:
80
+ raise ToolException(
81
+ f"Unable to determine chain id for network: {agent.network_id}"
82
+ )
83
+ return int(network_id)
84
+
77
85
  @property
78
86
  def category(self) -> str:
79
87
  return "enso"
88
+
89
+
90
+ def format_amount_with_decimals(amount: object, decimals: int | None) -> str | None:
91
+ if amount is None or decimals is None:
92
+ return None
93
+
94
+ try:
95
+ value = Decimal(str(amount)) / (Decimal(10) ** decimals)
96
+ return format(value, "f")
97
+ except Exception: # pragma: no cover - defensive
98
+ return None
@@ -1,17 +1,8 @@
1
- from typing import List, Optional, Type
2
-
3
1
  import httpx
4
- from langchain.tools.base import ToolException
2
+ from langchain_core.tools.base import ToolException
5
3
  from pydantic import BaseModel, Field
6
4
 
7
- from intentkit.skills.enso.base import (
8
- EnsoBaseTool,
9
- base_url,
10
- )
11
- from intentkit.utils.chain import NetworkId
12
-
13
- # Chain ID for Base Mainnet
14
- BASE_CHAIN_ID = int(NetworkId.BaseMainnet)
5
+ from intentkit.skills.enso.base import EnsoBaseTool, base_url
15
6
 
16
7
 
17
8
  class EnsoGetBestYieldInput(BaseModel):
@@ -21,9 +12,9 @@ class EnsoGetBestYieldInput(BaseModel):
21
12
  "USDC",
22
13
  description="Symbol of the token to find the best yield for (e.g., 'USDC', 'ETH', 'USDT')",
23
14
  )
24
- chain_id: int = Field(
25
- BASE_CHAIN_ID,
26
- description="The blockchain chain ID. Default is Base Mainnet (8453)",
15
+ chain_id: int | None = Field(
16
+ None,
17
+ description="The blockchain chain ID. Defaults to the agent's configured network.",
27
18
  )
28
19
  top_n: int = Field(
29
20
  5,
@@ -47,8 +38,8 @@ class YieldOption(BaseModel):
47
38
  None, description="Primary contract address for interacting with the protocol"
48
39
  )
49
40
  apy: float = Field(None, description="Annual Percentage Yield")
50
- tvl: Optional[float] = Field(None, description="Total Value Locked in the protocol")
51
- underlying_tokens: List[str] = Field(
41
+ tvl: float | None = Field(None, description="Total Value Locked in the protocol")
42
+ underlying_tokens: list[str] = Field(
52
43
  [], description="List of underlying token symbols"
53
44
  )
54
45
 
@@ -56,7 +47,7 @@ class YieldOption(BaseModel):
56
47
  class EnsoGetBestYieldOutput(BaseModel):
57
48
  """Output containing the best yield options."""
58
49
 
59
- best_options: List[YieldOption] = Field(
50
+ best_options: list[YieldOption] = Field(
60
51
  [], description="List of best yield options sorted by APY (descending)"
61
52
  )
62
53
  token_symbol: str = Field(None, description="Symbol of the token searched for")
@@ -75,12 +66,12 @@ class EnsoGetBestYield(EnsoBaseTool):
75
66
  "Find the best yield options for a specific token (default: USDC) across all protocols "
76
67
  "on a blockchain network (default: Base). Results are sorted by APY in descending order."
77
68
  )
78
- args_schema: Type[BaseModel] = EnsoGetBestYieldInput
69
+ args_schema: type[BaseModel] = EnsoGetBestYieldInput
79
70
 
80
71
  async def _arun(
81
72
  self,
82
73
  token_symbol: str = "USDC",
83
- chain_id: int = BASE_CHAIN_ID,
74
+ chain_id: int | None = None,
84
75
  top_n: int = 5,
85
76
  **kwargs,
86
77
  ) -> EnsoGetBestYieldOutput:
@@ -89,7 +80,7 @@ class EnsoGetBestYield(EnsoBaseTool):
89
80
 
90
81
  Args:
91
82
  token_symbol (str): Symbol of the token to find the best yield for (default: USDC)
92
- chain_id (int): The chain id of the network (default: Base Mainnet)
83
+ chain_id (int | None): The chain id of the network. Defaults to the agent's configured network.
93
84
  top_n (int): Number of top yield options to return
94
85
 
95
86
  Returns:
@@ -99,16 +90,17 @@ class EnsoGetBestYield(EnsoBaseTool):
99
90
  ToolException: If there's an error accessing the Enso API.
100
91
  """
101
92
  context = self.get_context()
93
+ resolved_chain_id = self.resolve_chain_id(context, chain_id)
102
94
  api_token = self.get_api_token(context)
103
95
 
104
96
  if not api_token:
105
97
  raise ToolException("No API token found for Enso Finance")
106
98
 
107
99
  # Get the chain name for the given chain ID
108
- chain_name = await self._get_chain_name(api_token, chain_id)
100
+ chain_name = await self._get_chain_name(api_token, resolved_chain_id)
109
101
 
110
102
  # Get all protocols on the specified chain
111
- protocols = await self._get_protocols(api_token, chain_id)
103
+ protocols = await self._get_protocols(api_token, resolved_chain_id)
112
104
 
113
105
  # Collect all yield options from all protocols
114
106
  all_yield_options = []
@@ -119,7 +111,7 @@ class EnsoGetBestYield(EnsoBaseTool):
119
111
 
120
112
  # Get yield-bearing tokens for this protocol
121
113
  tokens = await self._get_protocol_tokens(
122
- api_token, chain_id, protocol_slug, token_symbol
114
+ api_token, resolved_chain_id, protocol_slug, token_symbol
123
115
  )
124
116
 
125
117
  # Process tokens to extract yield options
@@ -170,7 +162,7 @@ class EnsoGetBestYield(EnsoBaseTool):
170
162
  return EnsoGetBestYieldOutput(
171
163
  best_options=top_options,
172
164
  token_symbol=token_symbol,
173
- chain_id=chain_id,
165
+ chain_id=resolved_chain_id,
174
166
  chain_name=chain_name,
175
167
  )
176
168
 
@@ -1,14 +1,11 @@
1
1
  import logging
2
- from typing import Type
3
2
 
4
3
  import httpx
5
- from langchain.tools.base import ToolException
4
+ from langchain_core.tools.base import ToolException
6
5
  from pydantic import BaseModel, Field
7
6
 
8
7
  from .base import EnsoBaseTool, base_url
9
8
 
10
- logger = logging.getLogger(__name__)
11
-
12
9
 
13
10
  class EnsoGetNetworksInput(BaseModel):
14
11
  """
@@ -38,6 +35,9 @@ class EnsoGetNetworksOutput(BaseModel):
38
35
  )
39
36
 
40
37
 
38
+ logger = logging.getLogger(__name__)
39
+
40
+
41
41
  class EnsoGetNetworks(EnsoBaseTool):
42
42
  """
43
43
  Tool for retrieving networks and their corresponding chainId, the output should be kept.
@@ -45,7 +45,7 @@ class EnsoGetNetworks(EnsoBaseTool):
45
45
 
46
46
  name: str = "enso_get_networks"
47
47
  description: str = "Retrieve networks supported by the Enso API"
48
- args_schema: Type[BaseModel] = EnsoGetNetworksInput
48
+ args_schema: type[BaseModel] = EnsoGetNetworksInput
49
49
 
50
50
  async def _arun(self, **kwargs) -> EnsoGetNetworksOutput:
51
51
  """
@@ -82,12 +82,7 @@ class EnsoGetNetworks(EnsoBaseTool):
82
82
  exclude_none=True
83
83
  )
84
84
 
85
- await self.skill_store.save_agent_skill_data(
86
- context.agent_id,
87
- "enso_get_networks",
88
- "networks",
89
- networks_memory,
90
- )
85
+ await self.save_agent_skill_data("networks", networks_memory)
91
86
 
92
87
  return EnsoGetNetworksOutput(res=networks)
93
88
  except httpx.RequestError as req_err:
@@ -1,16 +1,12 @@
1
- from typing import Type
2
-
3
1
  import httpx
4
- from langchain.tools.base import ToolException
2
+ from langchain_core.tools.base import ToolException
5
3
  from pydantic import BaseModel, Field
6
4
 
7
- from .base import EnsoBaseTool, base_url, default_chain_id
5
+ from .base import EnsoBaseTool, base_url
8
6
 
9
7
 
10
8
  class EnsoGetPricesInput(BaseModel):
11
- chainId: int = Field(
12
- default_chain_id, description="Blockchain chain ID of the token"
13
- )
9
+ chainId: int | None = Field(None, description="Blockchain chain ID of the token")
14
10
  address: str = Field(
15
11
  "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee",
16
12
  description="Contract address of the token",
@@ -38,27 +34,26 @@ class EnsoGetPrices(EnsoBaseTool):
38
34
 
39
35
  name: str = "enso_get_prices"
40
36
  description: str = "Retrieve the price of a token by chain ID and contract address"
41
- args_schema: Type[BaseModel] = EnsoGetPricesInput
37
+ args_schema: type[BaseModel] = EnsoGetPricesInput
42
38
 
43
39
  async def _arun(
44
40
  self,
45
41
  address: str,
46
- chainId: int = default_chain_id,
42
+ chainId: int | None = None,
47
43
  **kwargs,
48
44
  ) -> EnsoGetPricesOutput:
49
45
  """
50
46
  Asynchronous function to request the token price from the API.
51
47
 
52
48
  Args:
53
- chainId (int): The blockchain's chain ID.
49
+ chainId (int | None): The blockchain's chain ID. Defaults to the agent's configured network.
54
50
  address (str): Contract address of the token.
55
51
 
56
52
  Returns:
57
53
  EnsoGetPricesOutput: Token price response or error message.
58
54
  """
59
- url = f"{base_url}/api/v1/prices/{str(chainId)}/{address}"
60
-
61
55
  context = self.get_context()
56
+ resolved_chain_id = self.resolve_chain_id(context, chainId)
62
57
  api_token = self.get_api_token(context)
63
58
 
64
59
  headers = {
@@ -68,7 +63,10 @@ class EnsoGetPrices(EnsoBaseTool):
68
63
 
69
64
  async with httpx.AsyncClient() as client:
70
65
  try:
71
- response = await client.get(url, headers=headers)
66
+ response = await client.get(
67
+ f"{base_url}/api/v1/prices/{str(resolved_chain_id)}/{address}",
68
+ headers=headers,
69
+ )
72
70
  response.raise_for_status()
73
71
  json_dict = response.json()
74
72