intentkit 0.5.0__py3-none-any.whl → 0.5.2__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 (366) hide show
  1. intentkit/__init__.py +17 -0
  2. intentkit/abstracts/__init__.py +0 -0
  3. intentkit/abstracts/agent.py +60 -0
  4. intentkit/abstracts/api.py +4 -0
  5. intentkit/abstracts/engine.py +38 -0
  6. intentkit/abstracts/exception.py +9 -0
  7. intentkit/abstracts/graph.py +25 -0
  8. intentkit/abstracts/skill.py +129 -0
  9. intentkit/abstracts/twitter.py +54 -0
  10. intentkit/clients/__init__.py +14 -0
  11. intentkit/clients/cdp.py +53 -0
  12. intentkit/clients/twitter.py +445 -0
  13. intentkit/config/__init__.py +0 -0
  14. intentkit/config/config.py +164 -0
  15. intentkit/core/__init__.py +0 -0
  16. intentkit/core/agent.py +191 -0
  17. intentkit/core/api.py +40 -0
  18. intentkit/core/client.py +45 -0
  19. intentkit/core/credit.py +1767 -0
  20. intentkit/core/engine.py +1018 -0
  21. intentkit/core/node.py +223 -0
  22. intentkit/core/prompt.py +58 -0
  23. intentkit/core/skill.py +124 -0
  24. intentkit/models/agent.py +1689 -0
  25. intentkit/models/agent_data.py +810 -0
  26. intentkit/models/agent_schema.json +733 -0
  27. intentkit/models/app_setting.py +156 -0
  28. intentkit/models/base.py +9 -0
  29. intentkit/models/chat.py +581 -0
  30. intentkit/models/conversation.py +286 -0
  31. intentkit/models/credit.py +1406 -0
  32. intentkit/models/db.py +120 -0
  33. intentkit/models/db_mig.py +102 -0
  34. intentkit/models/generator.py +347 -0
  35. intentkit/models/llm.py +746 -0
  36. intentkit/models/redis.py +132 -0
  37. intentkit/models/skill.py +466 -0
  38. intentkit/models/user.py +243 -0
  39. intentkit/skills/__init__.py +12 -0
  40. intentkit/skills/acolyt/__init__.py +83 -0
  41. intentkit/skills/acolyt/acolyt.jpg +0 -0
  42. intentkit/skills/acolyt/ask.py +128 -0
  43. intentkit/skills/acolyt/base.py +28 -0
  44. intentkit/skills/acolyt/schema.json +89 -0
  45. intentkit/skills/aixbt/README.md +71 -0
  46. intentkit/skills/aixbt/__init__.py +73 -0
  47. intentkit/skills/aixbt/aixbt.jpg +0 -0
  48. intentkit/skills/aixbt/base.py +21 -0
  49. intentkit/skills/aixbt/projects.py +153 -0
  50. intentkit/skills/aixbt/schema.json +99 -0
  51. intentkit/skills/allora/__init__.py +83 -0
  52. intentkit/skills/allora/allora.jpeg +0 -0
  53. intentkit/skills/allora/base.py +28 -0
  54. intentkit/skills/allora/price.py +130 -0
  55. intentkit/skills/allora/schema.json +89 -0
  56. intentkit/skills/base.py +174 -0
  57. intentkit/skills/carv/README.md +95 -0
  58. intentkit/skills/carv/__init__.py +121 -0
  59. intentkit/skills/carv/base.py +183 -0
  60. intentkit/skills/carv/carv.webp +0 -0
  61. intentkit/skills/carv/fetch_news.py +92 -0
  62. intentkit/skills/carv/onchain_query.py +164 -0
  63. intentkit/skills/carv/schema.json +137 -0
  64. intentkit/skills/carv/token_info_and_price.py +110 -0
  65. intentkit/skills/cdp/__init__.py +137 -0
  66. intentkit/skills/cdp/base.py +21 -0
  67. intentkit/skills/cdp/cdp.png +0 -0
  68. intentkit/skills/cdp/get_balance.py +81 -0
  69. intentkit/skills/cdp/schema.json +473 -0
  70. intentkit/skills/chainlist/README.md +38 -0
  71. intentkit/skills/chainlist/__init__.py +54 -0
  72. intentkit/skills/chainlist/base.py +21 -0
  73. intentkit/skills/chainlist/chain_lookup.py +208 -0
  74. intentkit/skills/chainlist/chainlist.png +0 -0
  75. intentkit/skills/chainlist/schema.json +47 -0
  76. intentkit/skills/common/__init__.py +82 -0
  77. intentkit/skills/common/base.py +21 -0
  78. intentkit/skills/common/common.jpg +0 -0
  79. intentkit/skills/common/current_time.py +84 -0
  80. intentkit/skills/common/schema.json +57 -0
  81. intentkit/skills/cookiefun/README.md +121 -0
  82. intentkit/skills/cookiefun/__init__.py +78 -0
  83. intentkit/skills/cookiefun/base.py +41 -0
  84. intentkit/skills/cookiefun/constants.py +18 -0
  85. intentkit/skills/cookiefun/cookiefun.png +0 -0
  86. intentkit/skills/cookiefun/get_account_details.py +171 -0
  87. intentkit/skills/cookiefun/get_account_feed.py +282 -0
  88. intentkit/skills/cookiefun/get_account_smart_followers.py +181 -0
  89. intentkit/skills/cookiefun/get_sectors.py +128 -0
  90. intentkit/skills/cookiefun/schema.json +155 -0
  91. intentkit/skills/cookiefun/search_accounts.py +225 -0
  92. intentkit/skills/cryptocompare/__init__.py +130 -0
  93. intentkit/skills/cryptocompare/api.py +159 -0
  94. intentkit/skills/cryptocompare/base.py +303 -0
  95. intentkit/skills/cryptocompare/cryptocompare.png +0 -0
  96. intentkit/skills/cryptocompare/fetch_news.py +96 -0
  97. intentkit/skills/cryptocompare/fetch_price.py +99 -0
  98. intentkit/skills/cryptocompare/fetch_top_exchanges.py +113 -0
  99. intentkit/skills/cryptocompare/fetch_top_market_cap.py +109 -0
  100. intentkit/skills/cryptocompare/fetch_top_volume.py +108 -0
  101. intentkit/skills/cryptocompare/fetch_trading_signals.py +107 -0
  102. intentkit/skills/cryptocompare/schema.json +168 -0
  103. intentkit/skills/cryptopanic/__init__.py +108 -0
  104. intentkit/skills/cryptopanic/base.py +51 -0
  105. intentkit/skills/cryptopanic/cryptopanic.png +0 -0
  106. intentkit/skills/cryptopanic/fetch_crypto_news.py +153 -0
  107. intentkit/skills/cryptopanic/fetch_crypto_sentiment.py +136 -0
  108. intentkit/skills/cryptopanic/schema.json +103 -0
  109. intentkit/skills/dapplooker/README.md +92 -0
  110. intentkit/skills/dapplooker/__init__.py +83 -0
  111. intentkit/skills/dapplooker/base.py +26 -0
  112. intentkit/skills/dapplooker/dapplooker.jpg +0 -0
  113. intentkit/skills/dapplooker/dapplooker_token_data.py +476 -0
  114. intentkit/skills/dapplooker/schema.json +91 -0
  115. intentkit/skills/defillama/__init__.py +323 -0
  116. intentkit/skills/defillama/api.py +315 -0
  117. intentkit/skills/defillama/base.py +135 -0
  118. intentkit/skills/defillama/coins/__init__.py +0 -0
  119. intentkit/skills/defillama/coins/fetch_batch_historical_prices.py +116 -0
  120. intentkit/skills/defillama/coins/fetch_block.py +98 -0
  121. intentkit/skills/defillama/coins/fetch_current_prices.py +105 -0
  122. intentkit/skills/defillama/coins/fetch_first_price.py +100 -0
  123. intentkit/skills/defillama/coins/fetch_historical_prices.py +110 -0
  124. intentkit/skills/defillama/coins/fetch_price_chart.py +109 -0
  125. intentkit/skills/defillama/coins/fetch_price_percentage.py +93 -0
  126. intentkit/skills/defillama/config/__init__.py +0 -0
  127. intentkit/skills/defillama/config/chains.py +433 -0
  128. intentkit/skills/defillama/defillama.jpeg +0 -0
  129. intentkit/skills/defillama/fees/__init__.py +0 -0
  130. intentkit/skills/defillama/fees/fetch_fees_overview.py +130 -0
  131. intentkit/skills/defillama/schema.json +383 -0
  132. intentkit/skills/defillama/stablecoins/__init__.py +0 -0
  133. intentkit/skills/defillama/stablecoins/fetch_stablecoin_chains.py +100 -0
  134. intentkit/skills/defillama/stablecoins/fetch_stablecoin_charts.py +129 -0
  135. intentkit/skills/defillama/stablecoins/fetch_stablecoin_prices.py +83 -0
  136. intentkit/skills/defillama/stablecoins/fetch_stablecoins.py +126 -0
  137. intentkit/skills/defillama/tests/__init__.py +0 -0
  138. intentkit/skills/defillama/tests/api_integration.test.py +192 -0
  139. intentkit/skills/defillama/tests/api_unit.test.py +583 -0
  140. intentkit/skills/defillama/tvl/__init__.py +0 -0
  141. intentkit/skills/defillama/tvl/fetch_chain_historical_tvl.py +106 -0
  142. intentkit/skills/defillama/tvl/fetch_chains.py +107 -0
  143. intentkit/skills/defillama/tvl/fetch_historical_tvl.py +91 -0
  144. intentkit/skills/defillama/tvl/fetch_protocol.py +207 -0
  145. intentkit/skills/defillama/tvl/fetch_protocol_current_tvl.py +93 -0
  146. intentkit/skills/defillama/tvl/fetch_protocols.py +196 -0
  147. intentkit/skills/defillama/volumes/__init__.py +0 -0
  148. intentkit/skills/defillama/volumes/fetch_dex_overview.py +157 -0
  149. intentkit/skills/defillama/volumes/fetch_dex_summary.py +123 -0
  150. intentkit/skills/defillama/volumes/fetch_options_overview.py +131 -0
  151. intentkit/skills/defillama/yields/__init__.py +0 -0
  152. intentkit/skills/defillama/yields/fetch_pool_chart.py +100 -0
  153. intentkit/skills/defillama/yields/fetch_pools.py +126 -0
  154. intentkit/skills/dexscreener/__init__.py +93 -0
  155. intentkit/skills/dexscreener/base.py +133 -0
  156. intentkit/skills/dexscreener/dexscreener.png +0 -0
  157. intentkit/skills/dexscreener/model/__init__.py +0 -0
  158. intentkit/skills/dexscreener/model/search_token_response.py +82 -0
  159. intentkit/skills/dexscreener/schema.json +48 -0
  160. intentkit/skills/dexscreener/search_token.py +321 -0
  161. intentkit/skills/dune_analytics/__init__.py +103 -0
  162. intentkit/skills/dune_analytics/base.py +46 -0
  163. intentkit/skills/dune_analytics/dune.png +0 -0
  164. intentkit/skills/dune_analytics/fetch_kol_buys.py +128 -0
  165. intentkit/skills/dune_analytics/fetch_nation_metrics.py +237 -0
  166. intentkit/skills/dune_analytics/schema.json +99 -0
  167. intentkit/skills/elfa/README.md +100 -0
  168. intentkit/skills/elfa/__init__.py +123 -0
  169. intentkit/skills/elfa/base.py +28 -0
  170. intentkit/skills/elfa/elfa.jpg +0 -0
  171. intentkit/skills/elfa/mention.py +504 -0
  172. intentkit/skills/elfa/schema.json +153 -0
  173. intentkit/skills/elfa/stats.py +118 -0
  174. intentkit/skills/elfa/tokens.py +126 -0
  175. intentkit/skills/enso/README.md +75 -0
  176. intentkit/skills/enso/__init__.py +114 -0
  177. intentkit/skills/enso/abi/__init__.py +0 -0
  178. intentkit/skills/enso/abi/approval.py +279 -0
  179. intentkit/skills/enso/abi/erc20.py +14 -0
  180. intentkit/skills/enso/abi/route.py +129 -0
  181. intentkit/skills/enso/base.py +44 -0
  182. intentkit/skills/enso/best_yield.py +286 -0
  183. intentkit/skills/enso/enso.jpg +0 -0
  184. intentkit/skills/enso/networks.py +105 -0
  185. intentkit/skills/enso/prices.py +93 -0
  186. intentkit/skills/enso/route.py +300 -0
  187. intentkit/skills/enso/schema.json +212 -0
  188. intentkit/skills/enso/tokens.py +223 -0
  189. intentkit/skills/enso/wallet.py +381 -0
  190. intentkit/skills/github/README.md +63 -0
  191. intentkit/skills/github/__init__.py +54 -0
  192. intentkit/skills/github/base.py +21 -0
  193. intentkit/skills/github/github.jpg +0 -0
  194. intentkit/skills/github/github_search.py +183 -0
  195. intentkit/skills/github/schema.json +59 -0
  196. intentkit/skills/heurist/__init__.py +143 -0
  197. intentkit/skills/heurist/base.py +26 -0
  198. intentkit/skills/heurist/heurist.png +0 -0
  199. intentkit/skills/heurist/image_generation_animagine_xl.py +162 -0
  200. intentkit/skills/heurist/image_generation_arthemy_comics.py +162 -0
  201. intentkit/skills/heurist/image_generation_arthemy_real.py +162 -0
  202. intentkit/skills/heurist/image_generation_braindance.py +162 -0
  203. intentkit/skills/heurist/image_generation_cyber_realistic_xl.py +162 -0
  204. intentkit/skills/heurist/image_generation_flux_1_dev.py +162 -0
  205. intentkit/skills/heurist/image_generation_sdxl.py +161 -0
  206. intentkit/skills/heurist/schema.json +196 -0
  207. intentkit/skills/lifi/README.md +294 -0
  208. intentkit/skills/lifi/__init__.py +141 -0
  209. intentkit/skills/lifi/base.py +21 -0
  210. intentkit/skills/lifi/lifi.png +0 -0
  211. intentkit/skills/lifi/schema.json +89 -0
  212. intentkit/skills/lifi/token_execute.py +472 -0
  213. intentkit/skills/lifi/token_quote.py +190 -0
  214. intentkit/skills/lifi/utils.py +656 -0
  215. intentkit/skills/moralis/README.md +490 -0
  216. intentkit/skills/moralis/__init__.py +110 -0
  217. intentkit/skills/moralis/api.py +281 -0
  218. intentkit/skills/moralis/base.py +55 -0
  219. intentkit/skills/moralis/fetch_chain_portfolio.py +191 -0
  220. intentkit/skills/moralis/fetch_nft_portfolio.py +284 -0
  221. intentkit/skills/moralis/fetch_solana_portfolio.py +331 -0
  222. intentkit/skills/moralis/fetch_wallet_portfolio.py +301 -0
  223. intentkit/skills/moralis/moralis.png +0 -0
  224. intentkit/skills/moralis/schema.json +156 -0
  225. intentkit/skills/moralis/tests/__init__.py +0 -0
  226. intentkit/skills/moralis/tests/test_wallet.py +511 -0
  227. intentkit/skills/nation/__init__.py +62 -0
  228. intentkit/skills/nation/base.py +31 -0
  229. intentkit/skills/nation/nation.png +0 -0
  230. intentkit/skills/nation/nft_check.py +106 -0
  231. intentkit/skills/nation/schema.json +58 -0
  232. intentkit/skills/openai/__init__.py +107 -0
  233. intentkit/skills/openai/base.py +32 -0
  234. intentkit/skills/openai/dalle_image_generation.py +128 -0
  235. intentkit/skills/openai/gpt_image_generation.py +152 -0
  236. intentkit/skills/openai/gpt_image_to_image.py +186 -0
  237. intentkit/skills/openai/image_to_text.py +126 -0
  238. intentkit/skills/openai/openai.png +0 -0
  239. intentkit/skills/openai/schema.json +139 -0
  240. intentkit/skills/portfolio/README.md +55 -0
  241. intentkit/skills/portfolio/__init__.py +151 -0
  242. intentkit/skills/portfolio/base.py +107 -0
  243. intentkit/skills/portfolio/constants.py +9 -0
  244. intentkit/skills/portfolio/moralis.png +0 -0
  245. intentkit/skills/portfolio/schema.json +237 -0
  246. intentkit/skills/portfolio/token_balances.py +155 -0
  247. intentkit/skills/portfolio/wallet_approvals.py +102 -0
  248. intentkit/skills/portfolio/wallet_defi_positions.py +80 -0
  249. intentkit/skills/portfolio/wallet_history.py +155 -0
  250. intentkit/skills/portfolio/wallet_net_worth.py +112 -0
  251. intentkit/skills/portfolio/wallet_nfts.py +139 -0
  252. intentkit/skills/portfolio/wallet_profitability.py +101 -0
  253. intentkit/skills/portfolio/wallet_profitability_summary.py +91 -0
  254. intentkit/skills/portfolio/wallet_stats.py +79 -0
  255. intentkit/skills/portfolio/wallet_swaps.py +147 -0
  256. intentkit/skills/skills.toml +103 -0
  257. intentkit/skills/slack/__init__.py +98 -0
  258. intentkit/skills/slack/base.py +55 -0
  259. intentkit/skills/slack/get_channel.py +109 -0
  260. intentkit/skills/slack/get_message.py +136 -0
  261. intentkit/skills/slack/schedule_message.py +92 -0
  262. intentkit/skills/slack/schema.json +135 -0
  263. intentkit/skills/slack/send_message.py +81 -0
  264. intentkit/skills/slack/slack.jpg +0 -0
  265. intentkit/skills/system/__init__.py +90 -0
  266. intentkit/skills/system/base.py +22 -0
  267. intentkit/skills/system/read_agent_api_key.py +87 -0
  268. intentkit/skills/system/regenerate_agent_api_key.py +77 -0
  269. intentkit/skills/system/schema.json +53 -0
  270. intentkit/skills/system/system.svg +76 -0
  271. intentkit/skills/tavily/README.md +86 -0
  272. intentkit/skills/tavily/__init__.py +91 -0
  273. intentkit/skills/tavily/base.py +27 -0
  274. intentkit/skills/tavily/schema.json +119 -0
  275. intentkit/skills/tavily/tavily.jpg +0 -0
  276. intentkit/skills/tavily/tavily_extract.py +147 -0
  277. intentkit/skills/tavily/tavily_search.py +139 -0
  278. intentkit/skills/token/README.md +89 -0
  279. intentkit/skills/token/__init__.py +107 -0
  280. intentkit/skills/token/base.py +154 -0
  281. intentkit/skills/token/constants.py +9 -0
  282. intentkit/skills/token/erc20_transfers.py +145 -0
  283. intentkit/skills/token/moralis.png +0 -0
  284. intentkit/skills/token/schema.json +141 -0
  285. intentkit/skills/token/token_analytics.py +81 -0
  286. intentkit/skills/token/token_price.py +132 -0
  287. intentkit/skills/token/token_search.py +121 -0
  288. intentkit/skills/twitter/__init__.py +146 -0
  289. intentkit/skills/twitter/base.py +68 -0
  290. intentkit/skills/twitter/follow_user.py +69 -0
  291. intentkit/skills/twitter/get_mentions.py +124 -0
  292. intentkit/skills/twitter/get_timeline.py +111 -0
  293. intentkit/skills/twitter/get_user_by_username.py +84 -0
  294. intentkit/skills/twitter/get_user_tweets.py +123 -0
  295. intentkit/skills/twitter/like_tweet.py +65 -0
  296. intentkit/skills/twitter/post_tweet.py +90 -0
  297. intentkit/skills/twitter/reply_tweet.py +98 -0
  298. intentkit/skills/twitter/retweet.py +76 -0
  299. intentkit/skills/twitter/schema.json +258 -0
  300. intentkit/skills/twitter/search_tweets.py +115 -0
  301. intentkit/skills/twitter/twitter.png +0 -0
  302. intentkit/skills/unrealspeech/__init__.py +55 -0
  303. intentkit/skills/unrealspeech/base.py +21 -0
  304. intentkit/skills/unrealspeech/schema.json +100 -0
  305. intentkit/skills/unrealspeech/text_to_speech.py +177 -0
  306. intentkit/skills/unrealspeech/unrealspeech.jpg +0 -0
  307. intentkit/skills/venice_audio/__init__.py +106 -0
  308. intentkit/skills/venice_audio/base.py +119 -0
  309. intentkit/skills/venice_audio/input.py +41 -0
  310. intentkit/skills/venice_audio/schema.json +152 -0
  311. intentkit/skills/venice_audio/venice_audio.py +240 -0
  312. intentkit/skills/venice_audio/venice_logo.jpg +0 -0
  313. intentkit/skills/venice_image/README.md +119 -0
  314. intentkit/skills/venice_image/__init__.py +154 -0
  315. intentkit/skills/venice_image/api.py +138 -0
  316. intentkit/skills/venice_image/base.py +188 -0
  317. intentkit/skills/venice_image/config.py +35 -0
  318. intentkit/skills/venice_image/image_enhance/README.md +119 -0
  319. intentkit/skills/venice_image/image_enhance/__init__.py +0 -0
  320. intentkit/skills/venice_image/image_enhance/image_enhance.py +80 -0
  321. intentkit/skills/venice_image/image_enhance/image_enhance_base.py +23 -0
  322. intentkit/skills/venice_image/image_enhance/image_enhance_input.py +40 -0
  323. intentkit/skills/venice_image/image_generation/README.md +144 -0
  324. intentkit/skills/venice_image/image_generation/__init__.py +0 -0
  325. intentkit/skills/venice_image/image_generation/image_generation_base.py +117 -0
  326. intentkit/skills/venice_image/image_generation/image_generation_fluently_xl.py +26 -0
  327. intentkit/skills/venice_image/image_generation/image_generation_flux_dev.py +27 -0
  328. intentkit/skills/venice_image/image_generation/image_generation_flux_dev_uncensored.py +26 -0
  329. intentkit/skills/venice_image/image_generation/image_generation_input.py +158 -0
  330. intentkit/skills/venice_image/image_generation/image_generation_lustify_sdxl.py +26 -0
  331. intentkit/skills/venice_image/image_generation/image_generation_pony_realism.py +26 -0
  332. intentkit/skills/venice_image/image_generation/image_generation_stable_diffusion_3_5.py +28 -0
  333. intentkit/skills/venice_image/image_generation/image_generation_venice_sd35.py +28 -0
  334. intentkit/skills/venice_image/image_upscale/README.md +111 -0
  335. intentkit/skills/venice_image/image_upscale/__init__.py +0 -0
  336. intentkit/skills/venice_image/image_upscale/image_upscale.py +90 -0
  337. intentkit/skills/venice_image/image_upscale/image_upscale_base.py +23 -0
  338. intentkit/skills/venice_image/image_upscale/image_upscale_input.py +22 -0
  339. intentkit/skills/venice_image/image_vision/README.md +112 -0
  340. intentkit/skills/venice_image/image_vision/__init__.py +0 -0
  341. intentkit/skills/venice_image/image_vision/image_vision.py +100 -0
  342. intentkit/skills/venice_image/image_vision/image_vision_base.py +17 -0
  343. intentkit/skills/venice_image/image_vision/image_vision_input.py +9 -0
  344. intentkit/skills/venice_image/schema.json +267 -0
  345. intentkit/skills/venice_image/utils.py +78 -0
  346. intentkit/skills/venice_image/venice_image.jpg +0 -0
  347. intentkit/skills/web_scraper/README.md +82 -0
  348. intentkit/skills/web_scraper/__init__.py +92 -0
  349. intentkit/skills/web_scraper/base.py +21 -0
  350. intentkit/skills/web_scraper/langchain.png +0 -0
  351. intentkit/skills/web_scraper/schema.json +115 -0
  352. intentkit/skills/web_scraper/scrape_and_index.py +327 -0
  353. intentkit/utils/__init__.py +1 -0
  354. intentkit/utils/chain.py +436 -0
  355. intentkit/utils/error.py +134 -0
  356. intentkit/utils/logging.py +70 -0
  357. intentkit/utils/middleware.py +61 -0
  358. intentkit/utils/random.py +16 -0
  359. intentkit/utils/s3.py +267 -0
  360. intentkit/utils/slack_alert.py +79 -0
  361. intentkit/utils/tx.py +37 -0
  362. {intentkit-0.5.0.dist-info → intentkit-0.5.2.dist-info}/METADATA +1 -1
  363. intentkit-0.5.2.dist-info/RECORD +365 -0
  364. intentkit-0.5.0.dist-info/RECORD +0 -4
  365. {intentkit-0.5.0.dist-info → intentkit-0.5.2.dist-info}/WHEEL +0 -0
  366. {intentkit-0.5.0.dist-info → intentkit-0.5.2.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,583 @@
1
+ import unittest
2
+ from unittest.mock import AsyncMock, patch
3
+
4
+ # Import the endpoints from your module.
5
+ # Adjust the import path if your module has a different name or location.
6
+ from intentkit.skills.defillama.api import (
7
+ fetch_batch_historical_prices,
8
+ fetch_block,
9
+ fetch_chain_historical_tvl,
10
+ fetch_chains,
11
+ fetch_current_prices,
12
+ # Volume related functions
13
+ fetch_dex_overview,
14
+ fetch_dex_summary,
15
+ # Fees related functions
16
+ fetch_fees_overview,
17
+ fetch_first_price,
18
+ fetch_historical_prices,
19
+ fetch_historical_tvl,
20
+ fetch_options_overview,
21
+ fetch_pool_chart,
22
+ # Yields related functions
23
+ fetch_pools,
24
+ # Price related functions
25
+ fetch_price_chart,
26
+ fetch_price_percentage,
27
+ fetch_protocol,
28
+ fetch_protocol_current_tvl,
29
+ # Original functions
30
+ fetch_protocols,
31
+ fetch_stablecoin_chains,
32
+ fetch_stablecoin_charts,
33
+ fetch_stablecoin_prices,
34
+ # Stablecoin related functions
35
+ fetch_stablecoins,
36
+ )
37
+
38
+
39
+ # Dummy response to simulate httpx responses.
40
+ class DummyResponse:
41
+ def __init__(self, status_code, json_data):
42
+ self.status_code = status_code
43
+ self._json_data = json_data
44
+
45
+ def json(self):
46
+ return self._json_data
47
+
48
+
49
+ class TestDefiLlamaAPI(unittest.IsolatedAsyncioTestCase):
50
+ @classmethod
51
+ def setUpClass(cls):
52
+ # Set up a fixed timestamp that all tests will use
53
+ cls.mock_timestamp = 1677648000 # Fixed timestamp
54
+
55
+ async def asyncSetUp(self):
56
+ # Start the patcher before each test
57
+ self.datetime_patcher = patch("skills.defillama.api.datetime")
58
+ self.mock_datetime = self.datetime_patcher.start()
59
+ # Configure the mock to return our fixed timestamp
60
+ self.mock_datetime.now.return_value.timestamp.return_value = self.mock_timestamp
61
+
62
+ async def asyncTearDown(self):
63
+ # Stop the patcher after each test
64
+ self.datetime_patcher.stop()
65
+
66
+ # Helper method to patch httpx.AsyncClient and set up the dummy client.
67
+ async def _run_with_dummy(
68
+ self, func, expected_url, dummy_response, *args, expected_kwargs=None
69
+ ):
70
+ if expected_kwargs is None:
71
+ expected_kwargs = {}
72
+ with patch("httpx.AsyncClient") as MockClient:
73
+ client_instance = AsyncMock()
74
+ client_instance.get.return_value = dummy_response
75
+ # Ensure that __aenter__ returns our dummy client.
76
+ MockClient.return_value.__aenter__.return_value = client_instance
77
+ result = await func(*args)
78
+ # Check that the get call was made with the expected URL (and parameters, if any).
79
+ client_instance.get.assert_called_once_with(expected_url, **expected_kwargs)
80
+ return result
81
+
82
+ # --- Tests for fetch_protocols ---
83
+ async def test_fetch_protocols_success(self):
84
+ dummy = DummyResponse(200, {"protocols": []})
85
+ result = await self._run_with_dummy(
86
+ fetch_protocols,
87
+ "https://api.llama.fi/protocols",
88
+ dummy,
89
+ )
90
+ self.assertEqual(result, {"protocols": []})
91
+
92
+ async def test_fetch_protocols_error(self):
93
+ dummy = DummyResponse(404, None)
94
+ result = await self._run_with_dummy(
95
+ fetch_protocols,
96
+ "https://api.llama.fi/protocols",
97
+ dummy,
98
+ )
99
+ self.assertEqual(result, {"error": "API returned status code 404"})
100
+
101
+ # --- Tests for fetch_protocol ---
102
+ async def test_fetch_protocol_success(self):
103
+ protocol = "testprotocol"
104
+ dummy = DummyResponse(200, {"protocol": protocol})
105
+ expected_url = f"https://api.llama.fi/protocol/{protocol}"
106
+ result = await self._run_with_dummy(
107
+ fetch_protocol, expected_url, dummy, protocol
108
+ )
109
+ self.assertEqual(result, {"protocol": protocol})
110
+
111
+ async def test_fetch_protocol_error(self):
112
+ protocol = "testprotocol"
113
+ dummy = DummyResponse(500, None)
114
+ expected_url = f"https://api.llama.fi/protocol/{protocol}"
115
+ result = await self._run_with_dummy(
116
+ fetch_protocol, expected_url, dummy, protocol
117
+ )
118
+ self.assertEqual(result, {"error": "API returned status code 500"})
119
+
120
+ # --- Tests for fetch_historical_tvl ---
121
+ async def test_fetch_historical_tvl_success(self):
122
+ dummy = DummyResponse(200, {"historical": "data"})
123
+ expected_url = "https://api.llama.fi/v2/historicalChainTvl"
124
+ result = await self._run_with_dummy(
125
+ fetch_historical_tvl,
126
+ expected_url,
127
+ dummy,
128
+ )
129
+ self.assertEqual(result, {"historical": "data"})
130
+
131
+ async def test_fetch_historical_tvl_error(self):
132
+ dummy = DummyResponse(400, None)
133
+ expected_url = "https://api.llama.fi/v2/historicalChainTvl"
134
+ result = await self._run_with_dummy(
135
+ fetch_historical_tvl,
136
+ expected_url,
137
+ dummy,
138
+ )
139
+ self.assertEqual(result, {"error": "API returned status code 400"})
140
+
141
+ # --- Tests for fetch_chain_historical_tvl ---
142
+ async def test_fetch_chain_historical_tvl_success(self):
143
+ chain = "ethereum"
144
+ dummy = DummyResponse(200, {"chain": chain})
145
+ expected_url = f"https://api.llama.fi/v2/historicalChainTvl/{chain}"
146
+ result = await self._run_with_dummy(
147
+ fetch_chain_historical_tvl, expected_url, dummy, chain
148
+ )
149
+ self.assertEqual(result, {"chain": chain})
150
+
151
+ async def test_fetch_chain_historical_tvl_error(self):
152
+ chain = "ethereum"
153
+ dummy = DummyResponse(503, None)
154
+ expected_url = f"https://api.llama.fi/v2/historicalChainTvl/{chain}"
155
+ result = await self._run_with_dummy(
156
+ fetch_chain_historical_tvl, expected_url, dummy, chain
157
+ )
158
+ self.assertEqual(result, {"error": "API returned status code 503"})
159
+
160
+ # --- Tests for fetch_protocol_current_tvl ---
161
+ async def test_fetch_protocol_current_tvl_success(self):
162
+ protocol = "testprotocol"
163
+ dummy = DummyResponse(200, {"current_tvl": 12345})
164
+ expected_url = f"https://api.llama.fi/tvl/{protocol}"
165
+ result = await self._run_with_dummy(
166
+ fetch_protocol_current_tvl, expected_url, dummy, protocol
167
+ )
168
+ self.assertEqual(result, {"current_tvl": 12345})
169
+
170
+ async def test_fetch_protocol_current_tvl_error(self):
171
+ protocol = "testprotocol"
172
+ dummy = DummyResponse(418, None)
173
+ expected_url = f"https://api.llama.fi/tvl/{protocol}"
174
+ result = await self._run_with_dummy(
175
+ fetch_protocol_current_tvl, expected_url, dummy, protocol
176
+ )
177
+ self.assertEqual(result, {"error": "API returned status code 418"})
178
+
179
+ # --- Tests for fetch_chains ---
180
+ async def test_fetch_chains_success(self):
181
+ dummy = DummyResponse(200, {"chains": ["eth", "bsc"]})
182
+ expected_url = "https://api.llama.fi/v2/chains"
183
+ result = await self._run_with_dummy(
184
+ fetch_chains,
185
+ expected_url,
186
+ dummy,
187
+ )
188
+ self.assertEqual(result, {"chains": ["eth", "bsc"]})
189
+
190
+ async def test_fetch_chains_error(self):
191
+ dummy = DummyResponse(404, None)
192
+ expected_url = "https://api.llama.fi/v2/chains"
193
+ result = await self._run_with_dummy(
194
+ fetch_chains,
195
+ expected_url,
196
+ dummy,
197
+ )
198
+ self.assertEqual(result, {"error": "API returned status code 404"})
199
+
200
+ # --- Tests for fetch_current_prices ---
201
+ async def test_fetch_current_prices_success(self):
202
+ coins = ["coin1", "coin2"]
203
+ coins_str = ",".join(coins)
204
+ dummy = DummyResponse(200, {"prices": "data"})
205
+ expected_url = f"https://api.llama.fi/prices/current/{coins_str}?searchWidth=4h"
206
+ result = await self._run_with_dummy(
207
+ fetch_current_prices, expected_url, dummy, coins
208
+ )
209
+ self.assertEqual(result, {"prices": "data"})
210
+
211
+ async def test_fetch_current_prices_error(self):
212
+ coins = ["coin1", "coin2"]
213
+ coins_str = ",".join(coins)
214
+ dummy = DummyResponse(500, None)
215
+ expected_url = f"https://api.llama.fi/prices/current/{coins_str}?searchWidth=4h"
216
+ result = await self._run_with_dummy(
217
+ fetch_current_prices, expected_url, dummy, coins
218
+ )
219
+ self.assertEqual(result, {"error": "API returned status code 500"})
220
+
221
+ # --- Tests for fetch_historical_prices ---
222
+ async def test_fetch_historical_prices_success(self):
223
+ timestamp = 1609459200
224
+ coins = ["coin1", "coin2"]
225
+ coins_str = ",".join(coins)
226
+ dummy = DummyResponse(200, {"historical_prices": "data"})
227
+ expected_url = f"https://api.llama.fi/prices/historical/{timestamp}/{coins_str}?searchWidth=4h"
228
+ result = await self._run_with_dummy(
229
+ fetch_historical_prices, expected_url, dummy, timestamp, coins
230
+ )
231
+ self.assertEqual(result, {"historical_prices": "data"})
232
+
233
+ async def test_fetch_historical_prices_error(self):
234
+ timestamp = 1609459200
235
+ coins = ["coin1", "coin2"]
236
+ coins_str = ",".join(coins)
237
+ dummy = DummyResponse(400, None)
238
+ expected_url = f"https://api.llama.fi/prices/historical/{timestamp}/{coins_str}?searchWidth=4h"
239
+ result = await self._run_with_dummy(
240
+ fetch_historical_prices, expected_url, dummy, timestamp, coins
241
+ )
242
+ self.assertEqual(result, {"error": "API returned status code 400"})
243
+
244
+ # --- Tests for fetch_batch_historical_prices ---
245
+ async def test_fetch_batch_historical_prices_success(self):
246
+ coins_timestamps = {"coin1": [1609459200, 1609545600], "coin2": [1609459200]}
247
+ dummy = DummyResponse(200, {"batch": "data"})
248
+ expected_url = "https://api.llama.fi/batchHistorical"
249
+ # For this endpoint, a params dict is sent.
250
+ expected_params = {"coins": coins_timestamps, "searchWidth": "600"}
251
+ with patch("httpx.AsyncClient") as MockClient:
252
+ client_instance = AsyncMock()
253
+ client_instance.get.return_value = dummy
254
+ MockClient.return_value.__aenter__.return_value = client_instance
255
+ result = await fetch_batch_historical_prices(coins_timestamps)
256
+ client_instance.get.assert_called_once_with(
257
+ expected_url, params=expected_params
258
+ )
259
+ self.assertEqual(result, {"batch": "data"})
260
+
261
+ async def test_fetch_batch_historical_prices_error(self):
262
+ coins_timestamps = {"coin1": [1609459200], "coin2": [1609459200]}
263
+ dummy = DummyResponse(503, None)
264
+ expected_url = "https://api.llama.fi/batchHistorical"
265
+ expected_params = {"coins": coins_timestamps, "searchWidth": "600"}
266
+ with patch("httpx.AsyncClient") as MockClient:
267
+ client_instance = AsyncMock()
268
+ client_instance.get.return_value = dummy
269
+ MockClient.return_value.__aenter__.return_value = client_instance
270
+ result = await fetch_batch_historical_prices(coins_timestamps)
271
+ client_instance.get.assert_called_once_with(
272
+ expected_url, params=expected_params
273
+ )
274
+ self.assertEqual(result, {"error": "API returned status code 503"})
275
+
276
+ async def test_fetch_price_chart_success(self):
277
+ coins = ["bitcoin", "ethereum"]
278
+ coins_str = ",".join(coins)
279
+ dummy = DummyResponse(200, {"chart": "data"})
280
+ expected_url = f"https://api.llama.fi/chart/{coins_str}"
281
+
282
+ # Calculate start time based on mock timestamp
283
+ start_time = self.mock_timestamp - 86400 # mock timestamp - 1 day
284
+ expected_params = {
285
+ "start": start_time,
286
+ "span": 10,
287
+ "period": "2d",
288
+ "searchWidth": "600",
289
+ }
290
+
291
+ with patch("httpx.AsyncClient") as MockClient:
292
+ client_instance = AsyncMock()
293
+ client_instance.get.return_value = dummy
294
+ MockClient.return_value.__aenter__.return_value = client_instance
295
+ result = await fetch_price_chart(coins)
296
+ client_instance.get.assert_called_once_with(
297
+ expected_url, params=expected_params
298
+ )
299
+ self.assertEqual(result, {"chart": "data"})
300
+
301
+ async def test_fetch_price_chart_error(self):
302
+ coins = ["bitcoin", "ethereum"]
303
+ coins_str = ",".join(coins)
304
+ dummy = DummyResponse(500, None)
305
+ expected_url = f"https://api.llama.fi/chart/{coins_str}"
306
+
307
+ # Calculate start time based on mock timestamp
308
+ start_time = self.mock_timestamp - 86400 # mock timestamp - 1 day
309
+ expected_params = {
310
+ "start": start_time,
311
+ "span": 10,
312
+ "period": "2d",
313
+ "searchWidth": "600",
314
+ }
315
+
316
+ with patch("httpx.AsyncClient") as MockClient:
317
+ client_instance = AsyncMock()
318
+ client_instance.get.return_value = dummy
319
+ MockClient.return_value.__aenter__.return_value = client_instance
320
+ result = await fetch_price_chart(coins)
321
+ client_instance.get.assert_called_once_with(
322
+ expected_url, params=expected_params
323
+ )
324
+ self.assertEqual(result, {"error": "API returned status code 500"})
325
+
326
+ # --- Tests for fetch_price_percentage ---
327
+ async def test_fetch_price_percentage_success(self):
328
+ coins = ["bitcoin", "ethereum"]
329
+ coins_str = ",".join(coins)
330
+ dummy = DummyResponse(200, {"percentage": "data"})
331
+ expected_url = f"https://api.llama.fi/percentage/{coins_str}"
332
+
333
+ mock_timestamp = 1677648000 # Fixed timestamp
334
+ with patch("skills.defillama.api.datetime") as mock_datetime:
335
+ mock_datetime.now.return_value.timestamp.return_value = mock_timestamp
336
+ expected_params = {
337
+ "timestamp": mock_timestamp,
338
+ "lookForward": "false",
339
+ "period": "24h",
340
+ }
341
+
342
+ with patch("httpx.AsyncClient") as MockClient:
343
+ client_instance = AsyncMock()
344
+ client_instance.get.return_value = dummy
345
+ MockClient.return_value.__aenter__.return_value = client_instance
346
+ result = await fetch_price_percentage(coins)
347
+ client_instance.get.assert_called_once_with(
348
+ expected_url, params=expected_params
349
+ )
350
+ self.assertEqual(result, {"percentage": "data"})
351
+
352
+ async def test_fetch_price_percentage_error(self):
353
+ coins = ["bitcoin", "ethereum"]
354
+ coins_str = ",".join(coins)
355
+ dummy = DummyResponse(404, None)
356
+ expected_url = f"https://api.llama.fi/percentage/{coins_str}"
357
+
358
+ expected_params = {
359
+ "timestamp": self.mock_timestamp,
360
+ "lookForward": "false",
361
+ "period": "24h",
362
+ }
363
+
364
+ with patch("httpx.AsyncClient") as MockClient:
365
+ client_instance = AsyncMock()
366
+ client_instance.get.return_value = dummy
367
+ MockClient.return_value.__aenter__.return_value = client_instance
368
+ result = await fetch_price_percentage(coins)
369
+ client_instance.get.assert_called_once_with(
370
+ expected_url, params=expected_params
371
+ )
372
+ self.assertEqual(result, {"error": "API returned status code 404"})
373
+
374
+ async def test_fetch_price_percentage_error2(self):
375
+ coins = ["bitcoin", "ethereum"]
376
+ coins_str = ",".join(coins)
377
+ dummy = DummyResponse(404, None)
378
+ expected_url = f"https://api.llama.fi/percentage/{coins_str}"
379
+
380
+ with patch("datetime.datetime") as mock_datetime:
381
+ mock_datetime.now.return_value.timestamp.return_value = 1677648000
382
+ expected_params = {
383
+ "timestamp": 1677648000,
384
+ "lookForward": "false",
385
+ "period": "24h",
386
+ }
387
+
388
+ with patch("httpx.AsyncClient") as MockClient:
389
+ client_instance = AsyncMock()
390
+ client_instance.get.return_value = dummy
391
+ MockClient.return_value.__aenter__.return_value = client_instance
392
+ result = await fetch_price_percentage(coins)
393
+ client_instance.get.assert_called_once_with(
394
+ expected_url, params=expected_params
395
+ )
396
+ self.assertEqual(result, {"error": "API returned status code 404"})
397
+
398
+ # --- Tests for fetch_first_price ---
399
+ async def test_fetch_first_price_success(self):
400
+ coins = ["bitcoin", "ethereum"]
401
+ coins_str = ",".join(coins)
402
+ dummy = DummyResponse(200, {"first_prices": "data"})
403
+ expected_url = f"https://api.llama.fi/prices/first/{coins_str}"
404
+ result = await self._run_with_dummy(
405
+ fetch_first_price, expected_url, dummy, coins
406
+ )
407
+ self.assertEqual(result, {"first_prices": "data"})
408
+
409
+ async def test_fetch_first_price_error(self):
410
+ coins = ["bitcoin", "ethereum"]
411
+ coins_str = ",".join(coins)
412
+ dummy = DummyResponse(500, None)
413
+ expected_url = f"https://api.llama.fi/prices/first/{coins_str}"
414
+ result = await self._run_with_dummy(
415
+ fetch_first_price, expected_url, dummy, coins
416
+ )
417
+ self.assertEqual(result, {"error": "API returned status code 500"})
418
+
419
+ # --- Tests for fetch_block ---
420
+ async def test_fetch_block_success(self):
421
+ chain = "ethereum"
422
+ dummy = DummyResponse(200, {"block": 123456})
423
+ mock_timestamp = 1677648000 # Fixed timestamp
424
+
425
+ with patch("skills.defillama.api.datetime") as mock_datetime:
426
+ mock_datetime.now.return_value.timestamp.return_value = mock_timestamp
427
+ expected_url = f"https://api.llama.fi/block/{chain}/{mock_timestamp}"
428
+ result = await self._run_with_dummy(fetch_block, expected_url, dummy, chain)
429
+ self.assertEqual(result, {"block": 123456})
430
+
431
+ async def test_fetch_block_error(self):
432
+ chain = "ethereum"
433
+ dummy = DummyResponse(404, None)
434
+ mock_timestamp = 1677648000 # Fixed timestamp
435
+
436
+ with patch("skills.defillama.api.datetime") as mock_datetime:
437
+ mock_datetime.now.return_value.timestamp.return_value = mock_timestamp
438
+ expected_url = f"https://api.llama.fi/block/{chain}/{mock_timestamp}"
439
+ result = await self._run_with_dummy(fetch_block, expected_url, dummy, chain)
440
+ self.assertEqual(result, {"error": "API returned status code 404"})
441
+
442
+ # --- Tests for Stablecoins API ---
443
+ async def test_fetch_stablecoins_success(self):
444
+ dummy = DummyResponse(200, {"stablecoins": "data"})
445
+ expected_url = "https://api.llama.fi/stablecoins"
446
+ expected_params = {"includePrices": "true"}
447
+
448
+ with patch("httpx.AsyncClient") as MockClient:
449
+ client_instance = AsyncMock()
450
+ client_instance.get.return_value = dummy
451
+ MockClient.return_value.__aenter__.return_value = client_instance
452
+ result = await fetch_stablecoins()
453
+ client_instance.get.assert_called_once_with(
454
+ expected_url, params=expected_params
455
+ )
456
+ self.assertEqual(result, {"stablecoins": "data"})
457
+
458
+ async def test_fetch_stablecoin_charts_success(self):
459
+ stablecoin_id = "USDT"
460
+ chain = "ethereum"
461
+ dummy = DummyResponse(200, {"charts": "data"})
462
+ expected_url = (
463
+ f"https://api.llama.fi/stablecoincharts/{chain}?stablecoin={stablecoin_id}"
464
+ )
465
+ result = await self._run_with_dummy(
466
+ fetch_stablecoin_charts, expected_url, dummy, stablecoin_id, chain
467
+ )
468
+ self.assertEqual(result, {"charts": "data"})
469
+
470
+ async def test_fetch_stablecoin_chains_success(self):
471
+ dummy = DummyResponse(200, {"chains": "data"})
472
+ expected_url = "https://api.llama.fi/stablecoinchains"
473
+ result = await self._run_with_dummy(
474
+ fetch_stablecoin_chains, expected_url, dummy
475
+ )
476
+ self.assertEqual(result, {"chains": "data"})
477
+
478
+ async def test_fetch_stablecoin_prices_success(self):
479
+ dummy = DummyResponse(200, {"prices": "data"})
480
+ expected_url = "https://api.llama.fi/stablecoinprices"
481
+ result = await self._run_with_dummy(
482
+ fetch_stablecoin_prices, expected_url, dummy
483
+ )
484
+ self.assertEqual(result, {"prices": "data"})
485
+
486
+ # --- Tests for Yields API ---
487
+ async def test_fetch_pools_success(self):
488
+ dummy = DummyResponse(200, {"pools": "data"})
489
+ expected_url = "https://api.llama.fi/pools"
490
+ result = await self._run_with_dummy(fetch_pools, expected_url, dummy)
491
+ self.assertEqual(result, {"pools": "data"})
492
+
493
+ async def test_fetch_pool_chart_success(self):
494
+ pool_id = "compound-usdc"
495
+ dummy = DummyResponse(200, {"chart": "data"})
496
+ expected_url = f"https://api.llama.fi/chart/{pool_id}"
497
+ result = await self._run_with_dummy(
498
+ fetch_pool_chart, expected_url, dummy, pool_id
499
+ )
500
+ self.assertEqual(result, {"chart": "data"})
501
+
502
+ # --- Tests for Volumes API ---
503
+ async def test_fetch_dex_overview_success(self):
504
+ dummy = DummyResponse(200, {"overview": "data"})
505
+ expected_url = "https://api.llama.fi/overview/dexs"
506
+ expected_params = {
507
+ "excludeTotalDataChart": "true",
508
+ "excludeTotalDataChartBreakdown": "true",
509
+ "dataType": "dailyVolume",
510
+ }
511
+
512
+ with patch("httpx.AsyncClient") as MockClient:
513
+ client_instance = AsyncMock()
514
+ client_instance.get.return_value = dummy
515
+ MockClient.return_value.__aenter__.return_value = client_instance
516
+ result = await fetch_dex_overview()
517
+ client_instance.get.assert_called_once_with(
518
+ expected_url, params=expected_params
519
+ )
520
+ self.assertEqual(result, {"overview": "data"})
521
+
522
+ async def test_fetch_dex_summary_success(self):
523
+ protocol = "uniswap"
524
+ dummy = DummyResponse(200, {"summary": "data"})
525
+ expected_url = f"https://api.llama.fi/summary/dexs/{protocol}"
526
+ expected_params = {
527
+ "excludeTotalDataChart": "true",
528
+ "excludeTotalDataChartBreakdown": "true",
529
+ "dataType": "dailyVolume",
530
+ }
531
+
532
+ with patch("httpx.AsyncClient") as MockClient:
533
+ client_instance = AsyncMock()
534
+ client_instance.get.return_value = dummy
535
+ MockClient.return_value.__aenter__.return_value = client_instance
536
+ result = await fetch_dex_summary(protocol)
537
+ client_instance.get.assert_called_once_with(
538
+ expected_url, params=expected_params
539
+ )
540
+ self.assertEqual(result, {"summary": "data"})
541
+
542
+ async def test_fetch_options_overview_success(self):
543
+ dummy = DummyResponse(200, {"options": "data"})
544
+ expected_url = "https://api.llama.fi/overview/options"
545
+ expected_params = {
546
+ "excludeTotalDataChart": "true",
547
+ "excludeTotalDataChartBreakdown": "true",
548
+ "dataType": "dailyPremiumVolume",
549
+ }
550
+
551
+ with patch("httpx.AsyncClient") as MockClient:
552
+ client_instance = AsyncMock()
553
+ client_instance.get.return_value = dummy
554
+ MockClient.return_value.__aenter__.return_value = client_instance
555
+ result = await fetch_options_overview()
556
+ client_instance.get.assert_called_once_with(
557
+ expected_url, params=expected_params
558
+ )
559
+ self.assertEqual(result, {"options": "data"})
560
+
561
+ # --- Tests for Fees API ---
562
+ async def test_fetch_fees_overview_success(self):
563
+ dummy = DummyResponse(200, {"fees": "data"})
564
+ expected_url = "https://api.llama.fi/overview/fees"
565
+ expected_params = {
566
+ "excludeTotalDataChart": "true",
567
+ "excludeTotalDataChartBreakdown": "true",
568
+ "dataType": "dailyFees",
569
+ }
570
+
571
+ with patch("httpx.AsyncClient") as MockClient:
572
+ client_instance = AsyncMock()
573
+ client_instance.get.return_value = dummy
574
+ MockClient.return_value.__aenter__.return_value = client_instance
575
+ result = await fetch_fees_overview()
576
+ client_instance.get.assert_called_once_with(
577
+ expected_url, params=expected_params
578
+ )
579
+ self.assertEqual(result, {"fees": "data"})
580
+
581
+
582
+ if __name__ == "__main__":
583
+ unittest.main()
File without changes
@@ -0,0 +1,106 @@
1
+ """Tool for fetching chain historical TVL via DeFiLlama API."""
2
+
3
+ from typing import List, Type
4
+
5
+ from langchain.schema.runnable import RunnableConfig
6
+ from pydantic import BaseModel, Field
7
+
8
+ from intentkit.skills.defillama.api import fetch_chain_historical_tvl
9
+ from intentkit.skills.defillama.base import DefiLlamaBaseTool
10
+
11
+ FETCH_HISTORICAL_TVL_PROMPT = """
12
+ This tool fetches historical Total Value Locked (TVL) data for a specific blockchain.
13
+ Provide the chain name (e.g., "ethereum", "solana") to get its TVL history.
14
+ Returns a time series of TVL values with their corresponding dates.
15
+ """
16
+
17
+
18
+ class HistoricalTVLDataPoint(BaseModel):
19
+ """Model representing a single TVL data point."""
20
+
21
+ date: int = Field(..., description="Unix timestamp of the TVL measurement")
22
+ tvl: float = Field(..., description="Total Value Locked in USD at this timestamp")
23
+
24
+
25
+ class FetchChainHistoricalTVLInput(BaseModel):
26
+ """Input schema for fetching chain-specific historical TVL data."""
27
+
28
+ chain: str = Field(
29
+ ..., description="Chain name to fetch TVL for (e.g., 'ethereum', 'solana')"
30
+ )
31
+
32
+
33
+ class FetchChainHistoricalTVLResponse(BaseModel):
34
+ """Response schema for chain-specific historical TVL data."""
35
+
36
+ chain: str = Field(..., description="Normalized chain name")
37
+ data: List[HistoricalTVLDataPoint] = Field(
38
+ default_factory=list, description="List of historical TVL data points"
39
+ )
40
+ error: str | None = Field(default=None, description="Error message if any")
41
+
42
+
43
+ class DefiLlamaFetchChainHistoricalTvl(DefiLlamaBaseTool):
44
+ """Tool for fetching historical TVL data for a specific blockchain.
45
+
46
+ This tool fetches the complete Total Value Locked (TVL) history for a given
47
+ blockchain using the DeFiLlama API. It includes rate limiting and chain
48
+ validation to ensure reliable data retrieval.
49
+
50
+ Example:
51
+ tvl_tool = DefiLlamaFetchChainHistoricalTvl(
52
+ skill_store=store,
53
+ agent_id="agent_123",
54
+ agent_store=agent_store
55
+ )
56
+ result = await tvl_tool._arun(chain="ethereum")
57
+ """
58
+
59
+ name: str = "defillama_fetch_chain_historical_tvl"
60
+ description: str = FETCH_HISTORICAL_TVL_PROMPT
61
+ args_schema: Type[BaseModel] = FetchChainHistoricalTVLInput
62
+
63
+ async def _arun(
64
+ self, config: RunnableConfig, chain: str
65
+ ) -> FetchChainHistoricalTVLResponse:
66
+ """Fetch historical TVL data for the given chain.
67
+
68
+ Args:
69
+ config: Runnable configuration
70
+ chain: Blockchain name (e.g., "ethereum", "solana")
71
+
72
+ Returns:
73
+ FetchChainHistoricalTVLResponse containing chain name, TVL history or error
74
+ """
75
+ try:
76
+ # Check rate limiting
77
+ context = self.context_from_config(config)
78
+ is_rate_limited, error_msg = await self.check_rate_limit(context)
79
+ if is_rate_limited:
80
+ return FetchChainHistoricalTVLResponse(chain=chain, error=error_msg)
81
+
82
+ # Validate chain parameter
83
+ is_valid, normalized_chain = await self.validate_chain(chain)
84
+ if not is_valid or normalized_chain is None:
85
+ return FetchChainHistoricalTVLResponse(
86
+ chain=chain, error=f"Invalid chain: {chain}"
87
+ )
88
+
89
+ # Fetch TVL history from API
90
+ result = await fetch_chain_historical_tvl(normalized_chain)
91
+
92
+ # Check for API errors
93
+ if isinstance(result, dict) and "error" in result:
94
+ return FetchChainHistoricalTVLResponse(
95
+ chain=normalized_chain, error=result["error"]
96
+ )
97
+
98
+ # Parse response into our schema
99
+ data_points = [HistoricalTVLDataPoint(**point) for point in result]
100
+
101
+ return FetchChainHistoricalTVLResponse(
102
+ chain=normalized_chain, data=data_points
103
+ )
104
+
105
+ except Exception as e:
106
+ return FetchChainHistoricalTVLResponse(chain=chain, error=str(e))