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,436 @@
1
+ from abc import ABC, abstractmethod
2
+ from enum import IntEnum, StrEnum
3
+
4
+ import httpx
5
+
6
+
7
+ class Chain(StrEnum):
8
+ """
9
+ Enum of supported blockchain chains, using QuickNode's naming conventions.
10
+
11
+ This list is based on common chain names used by QuickNode, but it's essential
12
+ to consult the official QuickNode documentation for the most accurate and
13
+ up-to-date list of supported chains and their exact names. Chain names can
14
+ sometimes be slightly different from what you might expect.
15
+ """
16
+
17
+ # EVM Chains
18
+ Ethereum = "eth" # Or "ethereum"
19
+ Avalanche = "avax" # Or "avalanche"
20
+ Binance = "bsc" # BNB Smart Chain
21
+ Polygon = "matic" # Or "polygon"
22
+ Gnosis = "gnosis" # Or "xdai"
23
+ Celo = "celo"
24
+ Fantom = "fantom"
25
+ Moonbeam = "moonbeam"
26
+ Aurora = "aurora"
27
+ Arbitrum = "arbitrum"
28
+ Optimism = "optimism"
29
+ Linea = "linea"
30
+ ZkSync = "zksync"
31
+
32
+ # Base
33
+ Base = "base"
34
+
35
+ # Cosmos Ecosystem
36
+ CosmosHub = "cosmos" # Or "cosmos-hub"
37
+ Osmosis = "osmosis"
38
+ Juno = "juno"
39
+ Evmos = "evmos"
40
+ Kava = "kava"
41
+ Persistence = "persistence"
42
+ Secret = "secret"
43
+ Stargaze = "stargaze"
44
+ Terra = "terra" # Or "terra-classic"
45
+ Axelar = "axelar"
46
+
47
+ # Solana
48
+ Solana = "sol" # Or "solana"
49
+
50
+ # Other Chains
51
+ Sonic = "sonic"
52
+ Bera = "bera"
53
+ Near = "near"
54
+ Frontera = "frontera"
55
+
56
+
57
+ class Network(StrEnum):
58
+ """
59
+ Enum of well-known blockchain network names, based on QuickNode API.
60
+
61
+ This list is not exhaustive and might not be completely up-to-date.
62
+ Always consult the official QuickNode documentation for the most accurate
63
+ and current list of supported networks. Network names can sometimes
64
+ be slightly different from what you might expect.
65
+ """
66
+
67
+ # Ethereum Mainnet and Testnets
68
+ EthereumMainnet = "ethereum-mainnet"
69
+ EthereumGoerli = "ethereum-goerli" # Goerli Testnet (deprecated, Sepolia preferred)
70
+ EthereumSepolia = "ethereum-sepolia"
71
+
72
+ # Layer 2s on Ethereum
73
+ ArbitrumMainnet = "arbitrum-mainnet"
74
+ OptimismMainnet = "optimism-mainnet" # Or just "optimism"
75
+ LineaMainnet = "linea-mainnet"
76
+ ZkSyncMainnet = "zksync-mainnet" # zkSync Era
77
+
78
+ # Other EVM Chains
79
+ AvalancheMainnet = "avalanche-mainnet"
80
+ BinanceMainnet = "bsc" # BNB Smart Chain (BSC)
81
+ PolygonMainnet = "matic" # Or "polygon-mainnet"
82
+ GnosisMainnet = "xdai" # Or "gnosis"
83
+ CeloMainnet = "celo-mainnet"
84
+ FantomMainnet = "fantom-mainnet"
85
+ MoonbeamMainnet = "moonbeam-mainnet"
86
+ AuroraMainnet = "aurora-mainnet"
87
+
88
+ # Base
89
+ BaseMainnet = "base-mainnet"
90
+ BaseSepolia = "base-sepolia"
91
+
92
+ # Cosmos Ecosystem (These can be tricky and may need updates)
93
+ CosmosHubMainnet = "cosmos-hub-mainnet" # Or just "cosmos"
94
+ OsmosisMainnet = "osmosis-mainnet" # Or just "osmosis"
95
+ JunoMainnet = "juno-mainnet" # Or just "juno"
96
+
97
+ # Solana (Note: Solana uses cluster names, not typical network names)
98
+ SolanaMainnet = "solana-mainnet" # Or "solana"
99
+
100
+ # Other Chains
101
+ SonicMainnet = "sonic-mainnet"
102
+ BeraMainnet = "bera-mainnet"
103
+ NearMainnet = "near-mainnet" # Or just "near"
104
+ KavaMainnet = "kava-mainnet" # Or just "kava"
105
+ EvmosMainnet = "evmos-mainnet" # Or just "evmos"
106
+ PersistenceMainnet = "persistence-mainnet" # Or just "persistence"
107
+ SecretMainnet = "secret-mainnet" # Or just "secret"
108
+ StargazeMainnet = "stargaze-mainnet" # Or just "stargaze"
109
+ TerraMainnet = "terra-mainnet" # Or "terra-classic"
110
+ AxelarMainnet = "axelar-mainnet" # Or just "axelar"
111
+ FronteraMainnet = "frontera-mainnet"
112
+
113
+
114
+ class NetworkId(IntEnum):
115
+ """
116
+ Enum of well-known blockchain network IDs.
117
+
118
+ This list is not exhaustive and might not be completely up-to-date.
119
+ Always consult the official documentation for the specific blockchain
120
+ you are working with for the most accurate and current chain ID.
121
+ """
122
+
123
+ # Ethereum Mainnet and Testnets
124
+ EthereumMainnet = 1
125
+ EthereumGoerli = 5 # Goerli Testnet (deprecated, Sepolia is preferred)
126
+ EthereumSepolia = 11155111
127
+
128
+ # Layer 2s on Ethereum
129
+ ArbitrumMainnet = 42161
130
+ OptimismMainnet = 10
131
+ LineaMainnet = 59144
132
+ ZkSyncMainnet = 324 # zkSync Era
133
+
134
+ # Other EVM Chains
135
+ AvalancheMainnet = 43114
136
+ BinanceMainnet = 56 # BNB Smart Chain (BSC)
137
+ PolygonMainnet = 137
138
+ GnosisMainnet = 100 # xDai Chain
139
+ CeloMainnet = 42220
140
+ FantomMainnet = 250
141
+ MoonbeamMainnet = 1284
142
+ AuroraMainnet = 1313161554
143
+
144
+ # Base
145
+ BaseMainnet = 8453
146
+ BaseSepolia = 84532
147
+
148
+ # Other Chains
149
+ SonicMainnet = 146
150
+ BeraMainnet = 80094
151
+
152
+
153
+ # Mapping of Network enum members to their corresponding NetworkId enum members.
154
+ # This dictionary facilitates efficient lookup of network IDs given a network name.
155
+ # Note: SolanaMainnet is intentionally excluded as it does not have a numeric chain ID.
156
+ # Always refer to the official documentation for the most up-to-date mappings.
157
+ network_to_id: dict[Network, NetworkId] = {
158
+ Network.ArbitrumMainnet: NetworkId.ArbitrumMainnet,
159
+ Network.AvalancheMainnet: NetworkId.AvalancheMainnet,
160
+ Network.BaseMainnet: NetworkId.BaseMainnet,
161
+ Network.BaseSepolia: NetworkId.BaseSepolia,
162
+ Network.BeraMainnet: NetworkId.BeraMainnet,
163
+ Network.BinanceMainnet: NetworkId.BinanceMainnet,
164
+ Network.EthereumMainnet: NetworkId.EthereumMainnet,
165
+ Network.EthereumSepolia: NetworkId.EthereumSepolia,
166
+ Network.GnosisMainnet: NetworkId.GnosisMainnet,
167
+ Network.LineaMainnet: NetworkId.LineaMainnet,
168
+ Network.OptimismMainnet: NetworkId.OptimismMainnet,
169
+ Network.PolygonMainnet: NetworkId.PolygonMainnet,
170
+ Network.SonicMainnet: NetworkId.SonicMainnet,
171
+ Network.ZkSyncMainnet: NetworkId.ZkSyncMainnet,
172
+ }
173
+
174
+ # Mapping of NetworkId enum members (chain IDs) to their corresponding
175
+ # Network enum members (network names). This dictionary allows for reverse
176
+ # lookup, enabling retrieval of the network name given a chain ID.
177
+ # Note: Solana is not included here as it does not use a standard numeric
178
+ # chain ID. Always consult official documentation for the most
179
+ # up-to-date mappings.
180
+ id_to_network: dict[NetworkId, Network] = {
181
+ NetworkId.ArbitrumMainnet: Network.ArbitrumMainnet,
182
+ NetworkId.AvalancheMainnet: Network.AvalancheMainnet,
183
+ NetworkId.BaseMainnet: Network.BaseMainnet,
184
+ NetworkId.BaseSepolia: Network.BaseSepolia,
185
+ NetworkId.BeraMainnet: Network.BeraMainnet,
186
+ NetworkId.BinanceMainnet: Network.BinanceMainnet,
187
+ NetworkId.EthereumMainnet: Network.EthereumMainnet,
188
+ NetworkId.EthereumSepolia: Network.EthereumSepolia,
189
+ NetworkId.GnosisMainnet: Network.GnosisMainnet,
190
+ NetworkId.LineaMainnet: Network.LineaMainnet,
191
+ NetworkId.OptimismMainnet: Network.OptimismMainnet,
192
+ NetworkId.PolygonMainnet: Network.PolygonMainnet,
193
+ NetworkId.SonicMainnet: Network.SonicMainnet,
194
+ NetworkId.ZkSyncMainnet: Network.ZkSyncMainnet,
195
+ }
196
+
197
+
198
+ class ChainConfig:
199
+ """
200
+ Configuration class for a specific blockchain chain.
201
+
202
+ This class encapsulates all the necessary information to interact with a
203
+ particular blockchain, including the chain type, network, RPC URLs, and ENS URL.
204
+ """
205
+
206
+ def __init__(
207
+ self,
208
+ chain: Chain,
209
+ network: Network,
210
+ rpc_url: str,
211
+ ens_url: str,
212
+ wss_url: str,
213
+ ):
214
+ """
215
+ Initializes a ChainConfig object.
216
+
217
+ Args:
218
+ chain: The Chain enum member representing the blockchain type (e.g., Ethereum, Solana).
219
+ network: The Network enum member representing the specific network (e.g., EthereumMainnet).
220
+ rpc_url: The URL for the RPC endpoint of the blockchain.
221
+ ens_url: The URL for the ENS (Ethereum Name Service) endpoint (can be None if not applicable).
222
+ wss_url: The URL for the WebSocket endpoint of the blockchain (can be None if not applicable).
223
+ """
224
+
225
+ self._chain = chain
226
+ self._network = network
227
+ self._rpc_url = rpc_url
228
+ self._ens_url = ens_url
229
+ self._wss_url = wss_url
230
+
231
+ @property
232
+ def chain(self) -> Chain:
233
+ """
234
+ Returns the Chain enum member.
235
+ """
236
+ return self._chain
237
+
238
+ @property
239
+ def network(self) -> Network:
240
+ """
241
+ Returns the Network enum member.
242
+ """
243
+ return self._network
244
+
245
+ @property
246
+ def network_id(self) -> int | None:
247
+ """
248
+ Returns the network ID (chain ID) for the configured network, or None if not applicable.
249
+ Uses the global network_to_id mapping to retrieve the ID.
250
+ """
251
+ return network_to_id.get(self._network)
252
+
253
+ @property
254
+ def rpc_url(self) -> str:
255
+ """
256
+ Returns the RPC URL.
257
+ """
258
+ return self._rpc_url
259
+
260
+ @property
261
+ def ens_url(self) -> str:
262
+ """
263
+ Returns the ENS URL, or None if not applicable.
264
+ """
265
+ return self._ens_url
266
+
267
+ @property
268
+ def wss_url(self) -> str:
269
+ """
270
+ Returns the WebSocket URL, or None if not applicable.
271
+ """
272
+ return self._wss_url
273
+
274
+
275
+ class ChainProvider(ABC):
276
+ """
277
+ Abstract base class for providing blockchain chain configurations.
278
+
279
+ This class defines the interface for classes responsible for managing and
280
+ providing access to `ChainConfig` objects. Subclasses *must* implement the
281
+ `init_chain_configs` method to populate the available chain configurations.
282
+ """
283
+
284
+ def __init__(self):
285
+ """
286
+ Initializes the ChainProvider.
287
+
288
+ Sets up an empty dictionary `chain_configs` to store the configurations.
289
+ """
290
+ self.chain_configs: dict[Network, ChainConfig] = {}
291
+
292
+ def get_chain_config(self, network: Network) -> ChainConfig:
293
+ """
294
+ Retrieves the chain configuration for a specific network.
295
+
296
+ Args:
297
+ network: The `Network` enum member representing the desired network.
298
+
299
+ Returns:
300
+ The `ChainConfig` object associated with the given network.
301
+
302
+ Raises:
303
+ Exception: If no chain configuration is found for the specified network.
304
+ """
305
+ chain_config = self.chain_configs.get(network)
306
+ if not chain_config:
307
+ raise Exception(f"chain config for network {network} not found")
308
+ return chain_config
309
+
310
+ def get_chain_config_by_id(self, network_id: NetworkId) -> ChainConfig:
311
+ """
312
+ Retrieves the chain configuration by network ID.
313
+
314
+ This method first looks up the `Network` enum member associated with the
315
+ provided `NetworkId` and then uses `get_chain_config` to retrieve the
316
+ configuration.
317
+
318
+ Args:
319
+ network_id: The `NetworkId` enum member representing the desired network ID.
320
+
321
+ Returns:
322
+ The `ChainConfig` object associated with the network ID.
323
+
324
+ Raises:
325
+ Exception: If no network is found for the given ID or if the
326
+ chain configuration is not found for the resolved network.
327
+ """
328
+ network = id_to_network.get(network_id)
329
+ if not network:
330
+ raise Exception(f"network with id {network_id} not found")
331
+ return self.get_chain_config(network)
332
+
333
+ @abstractmethod
334
+ def init_chain_configs(self, api_key: str) -> dict[Network, ChainConfig]:
335
+ """
336
+ Initializes the chain configurations.
337
+
338
+ This *abstract* method *must* be implemented by subclasses. It is
339
+ responsible for populating the `chain_configs` dictionary with
340
+ `ChainConfig` objects, typically using the provided `api_key` to fetch
341
+ or generate the necessary configuration data.
342
+
343
+ Args:
344
+ api_key: The API key used for initializing chain configurations.
345
+
346
+ Returns:
347
+ A dictionary mapping `Network` enum members to `ChainConfig` objects.
348
+ """
349
+ raise NotImplementedError
350
+
351
+
352
+ class QuicknodeChainProvider(ChainProvider):
353
+ """
354
+ A concrete implementation of `ChainProvider` for QuickNode.
355
+
356
+ This class retrieves chain configuration data from the QuickNode API and
357
+ populates the `chain_configs` dictionary.
358
+ """
359
+
360
+ def __init__(self, api_key):
361
+ """
362
+ Initializes the QuicknodeChainProvider.
363
+
364
+ Args:
365
+ api_key: Your QuickNode API key.
366
+ """
367
+ super().__init__()
368
+ self.api_key = api_key
369
+
370
+ def init_chain_configs(
371
+ self, limit: int = 100, offset: int = 0
372
+ ) -> dict[Network, ChainConfig]:
373
+ """
374
+ Initializes chain configurations by fetching data from the QuickNode API.
375
+
376
+ This method retrieves a list of QuickNode endpoints using the provided
377
+ API key and populates the `chain_configs` dictionary with `ChainConfig`
378
+ objects.
379
+
380
+ Args:
381
+ limit: The maximum number of endpoints to retrieve (default: 100).
382
+ offset: The number of endpoints to skip (default: 0).
383
+
384
+ Returns:
385
+ A dictionary mapping `Network` enum members to `ChainConfig` objects.
386
+
387
+ Raises:
388
+ Exception: If an error occurs during the API request or processing
389
+ the response. More specific exception types are used
390
+ for HTTP errors and request errors.
391
+ """
392
+ url = "https://api.quicknode.com/v0/endpoints"
393
+ headers = {
394
+ "Accept": "application/json",
395
+ "x-api-key": self.api_key,
396
+ }
397
+ params = {
398
+ "limit": limit,
399
+ "offset": offset,
400
+ }
401
+
402
+ with httpx.Client(timeout=30) as client: # Set a timeout for the request
403
+ try:
404
+ response = client.get(url, timeout=30, headers=headers, params=params)
405
+ response.raise_for_status() # Raise HTTPError for bad responses (4xx or 5xx)
406
+ json_dict = response.json()
407
+
408
+ for item in json_dict["data"]:
409
+ # Assuming 'item' contains 'chain', 'network', 'http_url', 'wss_url'
410
+ # and that these values can be used to construct the ChainConfig object
411
+ chain = Chain(item["chain"])
412
+ network = Network(item["network"])
413
+
414
+ self.chain_configs[item["network"]] = ChainConfig(
415
+ chain,
416
+ network,
417
+ item["http_url"],
418
+ item[
419
+ "http_url"
420
+ ], # ens_url is the same as http_url in this case.
421
+ item["wss_url"],
422
+ )
423
+
424
+ except httpx.HTTPStatusError as http_err:
425
+ raise (f"Quicknode API HTTP Error: {http_err}")
426
+ except httpx.RequestError as req_err:
427
+ raise (f"Quicknode API Request Error: {req_err}")
428
+ except (
429
+ KeyError,
430
+ TypeError,
431
+ ) as e: # Handle potential data issues in the API response
432
+ raise Exception(
433
+ f"Error processing QuickNode API response: {e}. Check the API response format."
434
+ )
435
+ except Exception as e:
436
+ raise (f"Quicknode API An unexpected error occurred: {e}")
@@ -0,0 +1,134 @@
1
+ import logging
2
+ from typing import Sequence
3
+
4
+ from fastapi.exceptions import RequestValidationError
5
+ from fastapi.utils import is_body_allowed_for_status_code
6
+ from langchain_core.tools.base import ToolException
7
+ from starlette.exceptions import HTTPException
8
+ from starlette.requests import Request
9
+ from starlette.responses import JSONResponse, Response
10
+ from starlette.status import HTTP_422_UNPROCESSABLE_ENTITY
11
+
12
+ logger = logging.getLogger(__name__)
13
+
14
+
15
+ class IntentKitAPIError(Exception):
16
+ def __init__(self, status_code: int, key: str, message: str):
17
+ self.key = key
18
+ self.message = message
19
+ self.status_code = status_code
20
+
21
+ def __str__(self):
22
+ return f"{self.key}: {self.message}"
23
+
24
+ def __repr__(self):
25
+ return f"IntentKitAPIError({self.key}, {self.message}, {self.status_code})"
26
+
27
+
28
+ async def intentkit_api_error_handler(
29
+ request: Request, exc: IntentKitAPIError
30
+ ) -> Response:
31
+ if exc.status_code >= 500:
32
+ logger.error(f"Internal Server Error for request {request.url}: {str(exc)}")
33
+ else:
34
+ logger.info(f"Bad Request for request {request.url}: {str(exc)}")
35
+ return JSONResponse(
36
+ {"error": exc.key, "msg": exc.message},
37
+ status_code=exc.status_code,
38
+ )
39
+
40
+
41
+ async def intentkit_other_error_handler(request: Request, exc: Exception) -> Response:
42
+ logger.error(f"Internal Server Error for request {request.url}: {str(exc)}")
43
+ return JSONResponse(
44
+ {"error": "ServerError", "msg": "Internal Server Error"},
45
+ status_code=500,
46
+ )
47
+
48
+
49
+ async def http_exception_handler(request: Request, exc: HTTPException) -> Response:
50
+ headers = getattr(exc, "headers", None)
51
+ if not is_body_allowed_for_status_code(exc.status_code):
52
+ return Response(status_code=exc.status_code, headers=headers)
53
+ if exc.status_code >= 500:
54
+ logger.error(f"Internal Server Error for request {request.url}: {str(exc)}")
55
+ return JSONResponse(
56
+ {"error": "ServerError", "msg": "Internal Server Error"},
57
+ status_code=exc.status_code,
58
+ headers=headers,
59
+ )
60
+ logger.info(f"Bad Request for request {request.url}: {str(exc)}")
61
+ return JSONResponse(
62
+ {"error": "BadRequest", "msg": str(exc.detail)},
63
+ status_code=exc.status_code,
64
+ headers=headers,
65
+ )
66
+
67
+
68
+ def format_validation_errors(errors: Sequence) -> str:
69
+ """Format validation errors into a more readable string."""
70
+ formatted_errors = []
71
+
72
+ for error in errors:
73
+ loc = error.get("loc", [])
74
+ msg = error.get("msg", "")
75
+ error_type = error.get("type", "")
76
+
77
+ # Build field path
78
+ field_path = " -> ".join(str(part) for part in loc if part != "body")
79
+
80
+ # Format the error message with type information
81
+ if field_path:
82
+ if error_type:
83
+ formatted_error = f"Field '{field_path}' ({error_type}): {msg}"
84
+ else:
85
+ formatted_error = f"Field '{field_path}': {msg}"
86
+ else:
87
+ formatted_error = msg
88
+
89
+ formatted_errors.append(formatted_error)
90
+
91
+ return "; ".join(formatted_errors)
92
+
93
+
94
+ async def request_validation_exception_handler(
95
+ request: Request, exc: RequestValidationError
96
+ ) -> JSONResponse:
97
+ formatted_msg = format_validation_errors(exc.errors())
98
+ return JSONResponse(
99
+ status_code=HTTP_422_UNPROCESSABLE_ENTITY,
100
+ content={"error": "ValidationError", "msg": formatted_msg},
101
+ )
102
+
103
+
104
+ class IntentKitLookUpError(LookupError):
105
+ """Custom lookup error for IntentKit."""
106
+
107
+ pass
108
+
109
+
110
+ class AgentError(Exception):
111
+ """Custom exception for agent-related errors."""
112
+
113
+ def __init__(self, agent_id: str, message: str | None = None):
114
+ self.agent_id = agent_id
115
+ if message is None:
116
+ message = f"Agent error occurred for agent_id: {agent_id}"
117
+ super().__init__(message)
118
+
119
+ def __str__(self):
120
+ return f"AgentError(agent_id={self.agent_id}): {super().__str__()}"
121
+
122
+
123
+ class SkillError(ToolException):
124
+ """Custom exception for skill-related errors."""
125
+
126
+ def __init__(self, agent_id: str, skill_name: str, message: str | None = None):
127
+ self.agent_id = agent_id
128
+ self.skill_name = skill_name
129
+ if message is None:
130
+ message = f"Skill error occurred for agent_id: {agent_id}, skill_name: {skill_name}"
131
+ super().__init__(message)
132
+
133
+ def __str__(self):
134
+ return f"SkillError(agent_id={self.agent_id}, skill_name={self.skill_name}): {super().__str__()}"
@@ -0,0 +1,70 @@
1
+ """
2
+ Logging configuration module
3
+ """
4
+
5
+ import json
6
+ import logging
7
+ from typing import Callable, Optional
8
+
9
+
10
+ class JsonFormatter(logging.Formatter):
11
+ def __init__(
12
+ self, filter_func: Optional[Callable[[logging.LogRecord], bool]] = None
13
+ ):
14
+ super().__init__()
15
+ self.filter_func = filter_func
16
+
17
+ def format(self, record):
18
+ if self.filter_func and not self.filter_func(record):
19
+ return ""
20
+
21
+ log_obj = {
22
+ "timestamp": self.formatTime(record),
23
+ "name": record.name,
24
+ "level": record.levelname,
25
+ "message": record.getMessage(),
26
+ }
27
+ # Include any extra attributes
28
+ if hasattr(record, "extra"):
29
+ log_obj.update(record.extra)
30
+ elif record.__dict__.get("extra"):
31
+ log_obj.update(record.__dict__["extra"])
32
+ if record.exc_info:
33
+ log_obj["exc_info"] = self.formatException(record.exc_info)
34
+ return json.dumps(log_obj)
35
+
36
+
37
+ def setup_logging(env: str, debug: bool = False):
38
+ """
39
+ Setup global logging configuration.
40
+
41
+ Args:
42
+ env: Environment name ('local', 'prod', etc.)
43
+ debug: Debug mode flag
44
+ """
45
+
46
+ if env == "local" or debug:
47
+ # Set up logging configuration for local/debug
48
+ logging.basicConfig(
49
+ level=logging.DEBUG,
50
+ format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
51
+ handlers=[logging.StreamHandler()],
52
+ )
53
+ # logging.getLogger("openai._base_client").setLevel(logging.INFO)
54
+ # logging.getLogger("httpcore.http11").setLevel(logging.INFO)
55
+ # logging.getLogger("sqlalchemy.engine").setLevel(logging.DEBUG)
56
+ else:
57
+ # For non-local environments, use JSON format
58
+ handler = logging.StreamHandler()
59
+ handler.setFormatter(JsonFormatter())
60
+ logging.basicConfig(level=logging.INFO, handlers=[handler])
61
+ logging.getLogger("sqlalchemy.engine").setLevel(logging.WARNING)
62
+ # fastapi access log
63
+ uvicorn_access = logging.getLogger("uvicorn.access")
64
+ uvicorn_access.handlers = [] # Remove default handlers
65
+ handler = logging.StreamHandler()
66
+ handler.setFormatter(JsonFormatter())
67
+ uvicorn_access.addHandler(handler)
68
+ uvicorn_access.setLevel(logging.WARNING)
69
+ # telegram access log
70
+ logging.getLogger("aiohttp.access").setLevel(logging.WARNING)
@@ -0,0 +1,61 @@
1
+ import logging
2
+ from typing import Optional
3
+
4
+ import jwt
5
+ from fastapi import Depends, HTTPException, Request
6
+ from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer
7
+
8
+ logger = logging.getLogger(__name__)
9
+
10
+ security = HTTPBearer(auto_error=False)
11
+
12
+
13
+ def create_jwt_middleware(enable: bool, jwt_secret: str):
14
+ """Create a JWT verification middleware with configurable enable flag and secret.
15
+
16
+ Args:
17
+ enable: Whether to enable JWT verification
18
+ jwt_secret: Secret key for JWT verification
19
+
20
+ Returns:
21
+ A middleware function that can be used with FastAPI dependencies
22
+ """
23
+
24
+ async def verify_jwt(
25
+ request: Request,
26
+ credentials: Optional[HTTPAuthorizationCredentials] = Depends(security),
27
+ ) -> str:
28
+ """Verify JWT token from Authorization header and return the subject claim.
29
+
30
+ Returns:
31
+ str: The subject claim from the JWT token
32
+ """
33
+ host = request.headers.get("host", "").split(":")[0]
34
+ logger.debug(
35
+ f"verify_jwt: enable={enable}, credentials={credentials}, host={host}"
36
+ )
37
+
38
+ if (
39
+ not enable
40
+ or host == "localhost"
41
+ or host == "127.0.0.1"
42
+ or host == "intent-api"
43
+ or host == "intent-readonly"
44
+ or host == "intent-singleton"
45
+ ):
46
+ return ""
47
+
48
+ if not credentials:
49
+ raise HTTPException(
50
+ status_code=401, detail="Missing authentication credentials"
51
+ )
52
+
53
+ try:
54
+ payload = jwt.decode(
55
+ credentials.credentials, jwt_secret, algorithms=["HS256"]
56
+ )
57
+ return payload.get("sub", "")
58
+ except jwt.InvalidTokenError:
59
+ raise HTTPException(status_code=401, detail="Invalid authentication token")
60
+
61
+ return verify_jwt