intentkit 0.7.5.dev3__py3-none-any.whl → 0.8.34.dev7__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (393) hide show
  1. intentkit/MANIFEST.in +14 -0
  2. intentkit/README.md +88 -0
  3. intentkit/__init__.py +6 -4
  4. intentkit/abstracts/agent.py +4 -5
  5. intentkit/abstracts/engine.py +5 -5
  6. intentkit/abstracts/graph.py +15 -8
  7. intentkit/abstracts/skill.py +6 -144
  8. intentkit/abstracts/twitter.py +4 -5
  9. intentkit/clients/__init__.py +9 -2
  10. intentkit/clients/cdp.py +129 -153
  11. intentkit/{utils → clients}/s3.py +109 -34
  12. intentkit/clients/twitter.py +83 -62
  13. intentkit/clients/web3.py +4 -7
  14. intentkit/config/config.py +123 -90
  15. intentkit/core/account_checking.py +802 -0
  16. intentkit/core/agent.py +313 -498
  17. intentkit/core/asset.py +267 -0
  18. intentkit/core/chat.py +5 -3
  19. intentkit/core/client.py +1 -1
  20. intentkit/core/credit.py +49 -41
  21. intentkit/core/draft.py +201 -0
  22. intentkit/core/draft_chat.py +118 -0
  23. intentkit/core/engine.py +378 -287
  24. intentkit/core/manager/__init__.py +25 -0
  25. intentkit/core/manager/engine.py +220 -0
  26. intentkit/core/manager/service.py +172 -0
  27. intentkit/core/manager/skills.py +178 -0
  28. intentkit/core/middleware.py +231 -0
  29. intentkit/core/prompt.py +74 -114
  30. intentkit/core/scheduler.py +143 -0
  31. intentkit/core/statistics.py +168 -0
  32. intentkit/models/agent.py +931 -518
  33. intentkit/models/agent_data.py +165 -106
  34. intentkit/models/agent_schema.json +38 -251
  35. intentkit/models/app_setting.py +15 -13
  36. intentkit/models/chat.py +86 -140
  37. intentkit/models/credit.py +182 -162
  38. intentkit/models/db.py +42 -23
  39. intentkit/models/db_mig.py +120 -3
  40. intentkit/models/draft.py +222 -0
  41. intentkit/models/llm.csv +31 -0
  42. intentkit/models/llm.py +262 -370
  43. intentkit/models/redis.py +6 -4
  44. intentkit/models/skill.py +222 -101
  45. intentkit/models/skills.csv +173 -0
  46. intentkit/models/team.py +189 -0
  47. intentkit/models/user.py +103 -31
  48. intentkit/skills/acolyt/__init__.py +2 -9
  49. intentkit/skills/acolyt/ask.py +3 -4
  50. intentkit/skills/acolyt/base.py +4 -9
  51. intentkit/skills/acolyt/schema.json +4 -3
  52. intentkit/skills/aixbt/__init__.py +2 -13
  53. intentkit/skills/aixbt/base.py +1 -7
  54. intentkit/skills/aixbt/projects.py +14 -15
  55. intentkit/skills/aixbt/schema.json +4 -4
  56. intentkit/skills/allora/__init__.py +2 -9
  57. intentkit/skills/allora/base.py +4 -9
  58. intentkit/skills/allora/price.py +3 -4
  59. intentkit/skills/allora/schema.json +3 -2
  60. intentkit/skills/base.py +241 -41
  61. intentkit/skills/basename/__init__.py +51 -0
  62. intentkit/skills/basename/base.py +11 -0
  63. intentkit/skills/basename/basename.svg +11 -0
  64. intentkit/skills/basename/schema.json +58 -0
  65. intentkit/skills/carv/__init__.py +115 -121
  66. intentkit/skills/carv/base.py +184 -185
  67. intentkit/skills/carv/fetch_news.py +3 -3
  68. intentkit/skills/carv/onchain_query.py +4 -4
  69. intentkit/skills/carv/schema.json +134 -137
  70. intentkit/skills/carv/token_info_and_price.py +6 -6
  71. intentkit/skills/casino/__init__.py +4 -15
  72. intentkit/skills/casino/base.py +1 -7
  73. intentkit/skills/casino/deck_draw.py +5 -8
  74. intentkit/skills/casino/deck_shuffle.py +6 -6
  75. intentkit/skills/casino/dice_roll.py +2 -4
  76. intentkit/skills/casino/schema.json +0 -1
  77. intentkit/skills/cdp/__init__.py +22 -84
  78. intentkit/skills/cdp/base.py +1 -7
  79. intentkit/skills/cdp/schema.json +11 -314
  80. intentkit/skills/chainlist/__init__.py +2 -7
  81. intentkit/skills/chainlist/base.py +1 -7
  82. intentkit/skills/chainlist/chain_lookup.py +18 -18
  83. intentkit/skills/chainlist/schema.json +3 -5
  84. intentkit/skills/common/__init__.py +2 -9
  85. intentkit/skills/common/base.py +1 -7
  86. intentkit/skills/common/current_time.py +1 -2
  87. intentkit/skills/common/schema.json +2 -2
  88. intentkit/skills/cookiefun/__init__.py +6 -9
  89. intentkit/skills/cookiefun/base.py +2 -7
  90. intentkit/skills/cookiefun/get_account_details.py +7 -7
  91. intentkit/skills/cookiefun/get_account_feed.py +19 -19
  92. intentkit/skills/cookiefun/get_account_smart_followers.py +7 -7
  93. intentkit/skills/cookiefun/get_sectors.py +3 -3
  94. intentkit/skills/cookiefun/schema.json +1 -3
  95. intentkit/skills/cookiefun/search_accounts.py +9 -9
  96. intentkit/skills/cryptocompare/__init__.py +7 -24
  97. intentkit/skills/cryptocompare/api.py +2 -3
  98. intentkit/skills/cryptocompare/base.py +10 -24
  99. intentkit/skills/cryptocompare/fetch_news.py +4 -5
  100. intentkit/skills/cryptocompare/fetch_price.py +6 -7
  101. intentkit/skills/cryptocompare/fetch_top_exchanges.py +4 -5
  102. intentkit/skills/cryptocompare/fetch_top_market_cap.py +4 -5
  103. intentkit/skills/cryptocompare/fetch_top_volume.py +4 -5
  104. intentkit/skills/cryptocompare/fetch_trading_signals.py +5 -6
  105. intentkit/skills/cryptocompare/schema.json +3 -3
  106. intentkit/skills/cryptopanic/__init__.py +7 -10
  107. intentkit/skills/cryptopanic/base.py +51 -55
  108. intentkit/skills/cryptopanic/fetch_crypto_news.py +4 -8
  109. intentkit/skills/cryptopanic/fetch_crypto_sentiment.py +5 -7
  110. intentkit/skills/cryptopanic/schema.json +105 -103
  111. intentkit/skills/dapplooker/__init__.py +2 -9
  112. intentkit/skills/dapplooker/base.py +4 -9
  113. intentkit/skills/dapplooker/dapplooker_token_data.py +7 -7
  114. intentkit/skills/dapplooker/schema.json +3 -5
  115. intentkit/skills/defillama/__init__.py +24 -74
  116. intentkit/skills/defillama/api.py +6 -9
  117. intentkit/skills/defillama/base.py +8 -19
  118. intentkit/skills/defillama/coins/fetch_batch_historical_prices.py +8 -10
  119. intentkit/skills/defillama/coins/fetch_block.py +6 -8
  120. intentkit/skills/defillama/coins/fetch_current_prices.py +8 -10
  121. intentkit/skills/defillama/coins/fetch_first_price.py +7 -9
  122. intentkit/skills/defillama/coins/fetch_historical_prices.py +9 -11
  123. intentkit/skills/defillama/coins/fetch_price_chart.py +9 -11
  124. intentkit/skills/defillama/coins/fetch_price_percentage.py +7 -9
  125. intentkit/skills/defillama/config/chains.py +1 -3
  126. intentkit/skills/defillama/fees/fetch_fees_overview.py +24 -26
  127. intentkit/skills/defillama/schema.json +5 -1
  128. intentkit/skills/defillama/stablecoins/fetch_stablecoin_chains.py +16 -18
  129. intentkit/skills/defillama/stablecoins/fetch_stablecoin_charts.py +8 -10
  130. intentkit/skills/defillama/stablecoins/fetch_stablecoin_prices.py +5 -7
  131. intentkit/skills/defillama/stablecoins/fetch_stablecoins.py +7 -9
  132. intentkit/skills/defillama/tests/api_integration.test.py +1 -1
  133. intentkit/skills/defillama/tvl/fetch_chain_historical_tvl.py +4 -6
  134. intentkit/skills/defillama/tvl/fetch_chains.py +9 -11
  135. intentkit/skills/defillama/tvl/fetch_historical_tvl.py +4 -6
  136. intentkit/skills/defillama/tvl/fetch_protocol.py +32 -38
  137. intentkit/skills/defillama/tvl/fetch_protocol_current_tvl.py +3 -5
  138. intentkit/skills/defillama/tvl/fetch_protocols.py +37 -45
  139. intentkit/skills/defillama/volumes/fetch_dex_overview.py +42 -48
  140. intentkit/skills/defillama/volumes/fetch_dex_summary.py +35 -37
  141. intentkit/skills/defillama/volumes/fetch_options_overview.py +24 -28
  142. intentkit/skills/defillama/yields/fetch_pool_chart.py +10 -12
  143. intentkit/skills/defillama/yields/fetch_pools.py +26 -30
  144. intentkit/skills/dexscreener/__init__.py +97 -102
  145. intentkit/skills/dexscreener/base.py +125 -130
  146. intentkit/skills/dexscreener/get_pair_info.py +4 -5
  147. intentkit/skills/dexscreener/get_token_pairs.py +4 -5
  148. intentkit/skills/dexscreener/get_tokens_info.py +7 -8
  149. intentkit/skills/dexscreener/model/search_token_response.py +80 -82
  150. intentkit/skills/dexscreener/schema.json +91 -93
  151. intentkit/skills/dexscreener/search_token.py +182 -184
  152. intentkit/skills/dexscreener/utils.py +15 -14
  153. intentkit/skills/dune_analytics/__init__.py +7 -9
  154. intentkit/skills/dune_analytics/base.py +48 -52
  155. intentkit/skills/dune_analytics/fetch_kol_buys.py +5 -7
  156. intentkit/skills/dune_analytics/fetch_nation_metrics.py +6 -8
  157. intentkit/skills/dune_analytics/schema.json +104 -99
  158. intentkit/skills/elfa/__init__.py +5 -18
  159. intentkit/skills/elfa/base.py +10 -14
  160. intentkit/skills/elfa/mention.py +19 -21
  161. intentkit/skills/elfa/schema.json +3 -2
  162. intentkit/skills/elfa/stats.py +4 -4
  163. intentkit/skills/elfa/tokens.py +12 -12
  164. intentkit/skills/elfa/utils.py +26 -28
  165. intentkit/skills/enso/__init__.py +11 -31
  166. intentkit/skills/enso/base.py +54 -35
  167. intentkit/skills/enso/best_yield.py +16 -24
  168. intentkit/skills/enso/networks.py +6 -11
  169. intentkit/skills/enso/prices.py +11 -13
  170. intentkit/skills/enso/route.py +34 -38
  171. intentkit/skills/enso/schema.json +3 -2
  172. intentkit/skills/enso/tokens.py +29 -38
  173. intentkit/skills/enso/wallet.py +76 -191
  174. intentkit/skills/erc20/__init__.py +50 -0
  175. intentkit/skills/erc20/base.py +11 -0
  176. intentkit/skills/erc20/erc20.svg +5 -0
  177. intentkit/skills/erc20/schema.json +74 -0
  178. intentkit/skills/erc721/__init__.py +53 -0
  179. intentkit/skills/erc721/base.py +11 -0
  180. intentkit/skills/erc721/erc721.svg +5 -0
  181. intentkit/skills/erc721/schema.json +90 -0
  182. intentkit/skills/firecrawl/__init__.py +5 -18
  183. intentkit/skills/firecrawl/base.py +4 -9
  184. intentkit/skills/firecrawl/clear.py +4 -8
  185. intentkit/skills/firecrawl/crawl.py +19 -19
  186. intentkit/skills/firecrawl/query.py +4 -3
  187. intentkit/skills/firecrawl/schema.json +2 -6
  188. intentkit/skills/firecrawl/scrape.py +17 -22
  189. intentkit/skills/firecrawl/utils.py +50 -42
  190. intentkit/skills/github/__init__.py +2 -7
  191. intentkit/skills/github/base.py +1 -7
  192. intentkit/skills/github/github_search.py +1 -2
  193. intentkit/skills/github/schema.json +3 -4
  194. intentkit/skills/heurist/__init__.py +8 -27
  195. intentkit/skills/heurist/base.py +4 -9
  196. intentkit/skills/heurist/image_generation_animagine_xl.py +13 -15
  197. intentkit/skills/heurist/image_generation_arthemy_comics.py +13 -15
  198. intentkit/skills/heurist/image_generation_arthemy_real.py +13 -15
  199. intentkit/skills/heurist/image_generation_braindance.py +13 -15
  200. intentkit/skills/heurist/image_generation_cyber_realistic_xl.py +13 -15
  201. intentkit/skills/heurist/image_generation_flux_1_dev.py +13 -15
  202. intentkit/skills/heurist/image_generation_sdxl.py +13 -15
  203. intentkit/skills/heurist/schema.json +2 -2
  204. intentkit/skills/http/__init__.py +4 -15
  205. intentkit/skills/http/base.py +1 -7
  206. intentkit/skills/http/get.py +21 -16
  207. intentkit/skills/http/post.py +23 -18
  208. intentkit/skills/http/put.py +23 -18
  209. intentkit/skills/http/schema.json +4 -5
  210. intentkit/skills/lifi/__init__.py +8 -13
  211. intentkit/skills/lifi/base.py +3 -9
  212. intentkit/skills/lifi/schema.json +17 -8
  213. intentkit/skills/lifi/token_execute.py +150 -60
  214. intentkit/skills/lifi/token_quote.py +8 -10
  215. intentkit/skills/lifi/utils.py +104 -51
  216. intentkit/skills/moralis/__init__.py +6 -10
  217. intentkit/skills/moralis/api.py +6 -7
  218. intentkit/skills/moralis/base.py +5 -10
  219. intentkit/skills/moralis/fetch_chain_portfolio.py +10 -11
  220. intentkit/skills/moralis/fetch_nft_portfolio.py +22 -22
  221. intentkit/skills/moralis/fetch_solana_portfolio.py +11 -12
  222. intentkit/skills/moralis/fetch_wallet_portfolio.py +8 -9
  223. intentkit/skills/moralis/schema.json +7 -2
  224. intentkit/skills/morpho/__init__.py +52 -0
  225. intentkit/skills/morpho/base.py +11 -0
  226. intentkit/skills/morpho/morpho.svg +12 -0
  227. intentkit/skills/morpho/schema.json +73 -0
  228. intentkit/skills/nation/__init__.py +4 -9
  229. intentkit/skills/nation/base.py +5 -10
  230. intentkit/skills/nation/nft_check.py +3 -4
  231. intentkit/skills/nation/schema.json +4 -3
  232. intentkit/skills/onchain.py +30 -0
  233. intentkit/skills/openai/__init__.py +17 -18
  234. intentkit/skills/openai/base.py +10 -14
  235. intentkit/skills/openai/dalle_image_generation.py +4 -9
  236. intentkit/skills/openai/gpt_avatar_generator.py +102 -0
  237. intentkit/skills/openai/gpt_image_generation.py +5 -9
  238. intentkit/skills/openai/gpt_image_mini_generator.py +92 -0
  239. intentkit/skills/openai/gpt_image_to_image.py +5 -9
  240. intentkit/skills/openai/image_to_text.py +3 -7
  241. intentkit/skills/openai/schema.json +34 -3
  242. intentkit/skills/portfolio/__init__.py +11 -35
  243. intentkit/skills/portfolio/base.py +33 -19
  244. intentkit/skills/portfolio/schema.json +3 -5
  245. intentkit/skills/portfolio/token_balances.py +21 -21
  246. intentkit/skills/portfolio/wallet_approvals.py +17 -18
  247. intentkit/skills/portfolio/wallet_defi_positions.py +3 -3
  248. intentkit/skills/portfolio/wallet_history.py +31 -31
  249. intentkit/skills/portfolio/wallet_net_worth.py +13 -13
  250. intentkit/skills/portfolio/wallet_nfts.py +19 -19
  251. intentkit/skills/portfolio/wallet_profitability.py +18 -18
  252. intentkit/skills/portfolio/wallet_profitability_summary.py +5 -5
  253. intentkit/skills/portfolio/wallet_stats.py +3 -3
  254. intentkit/skills/portfolio/wallet_swaps.py +19 -19
  255. intentkit/skills/pyth/__init__.py +50 -0
  256. intentkit/skills/pyth/base.py +11 -0
  257. intentkit/skills/pyth/pyth.svg +6 -0
  258. intentkit/skills/pyth/schema.json +75 -0
  259. intentkit/skills/skills.toml +36 -0
  260. intentkit/skills/slack/__init__.py +5 -17
  261. intentkit/skills/slack/base.py +3 -9
  262. intentkit/skills/slack/get_channel.py +8 -8
  263. intentkit/skills/slack/get_message.py +9 -9
  264. intentkit/skills/slack/schedule_message.py +5 -5
  265. intentkit/skills/slack/schema.json +2 -2
  266. intentkit/skills/slack/send_message.py +3 -5
  267. intentkit/skills/supabase/__init__.py +7 -23
  268. intentkit/skills/supabase/base.py +1 -7
  269. intentkit/skills/supabase/delete_data.py +4 -4
  270. intentkit/skills/supabase/fetch_data.py +12 -12
  271. intentkit/skills/supabase/insert_data.py +4 -4
  272. intentkit/skills/supabase/invoke_function.py +6 -6
  273. intentkit/skills/supabase/schema.json +2 -3
  274. intentkit/skills/supabase/update_data.py +6 -6
  275. intentkit/skills/supabase/upsert_data.py +4 -4
  276. intentkit/skills/superfluid/__init__.py +53 -0
  277. intentkit/skills/superfluid/base.py +11 -0
  278. intentkit/skills/superfluid/schema.json +89 -0
  279. intentkit/skills/superfluid/superfluid.svg +6 -0
  280. intentkit/skills/system/__init__.py +7 -24
  281. intentkit/skills/system/add_autonomous_task.py +10 -12
  282. intentkit/skills/system/delete_autonomous_task.py +2 -2
  283. intentkit/skills/system/edit_autonomous_task.py +14 -18
  284. intentkit/skills/system/list_autonomous_tasks.py +3 -5
  285. intentkit/skills/system/read_agent_api_key.py +6 -4
  286. intentkit/skills/system/regenerate_agent_api_key.py +6 -4
  287. intentkit/skills/system/schema.json +6 -8
  288. intentkit/skills/tavily/__init__.py +3 -12
  289. intentkit/skills/tavily/base.py +4 -9
  290. intentkit/skills/tavily/schema.json +3 -5
  291. intentkit/skills/tavily/tavily_extract.py +2 -4
  292. intentkit/skills/tavily/tavily_search.py +4 -6
  293. intentkit/skills/token/__init__.py +5 -10
  294. intentkit/skills/token/base.py +7 -11
  295. intentkit/skills/token/erc20_transfers.py +19 -19
  296. intentkit/skills/token/schema.json +3 -6
  297. intentkit/skills/token/token_analytics.py +3 -3
  298. intentkit/skills/token/token_price.py +13 -13
  299. intentkit/skills/token/token_search.py +9 -9
  300. intentkit/skills/twitter/__init__.py +11 -35
  301. intentkit/skills/twitter/base.py +22 -34
  302. intentkit/skills/twitter/follow_user.py +2 -6
  303. intentkit/skills/twitter/get_mentions.py +5 -12
  304. intentkit/skills/twitter/get_timeline.py +4 -12
  305. intentkit/skills/twitter/get_user_by_username.py +2 -6
  306. intentkit/skills/twitter/get_user_tweets.py +5 -13
  307. intentkit/skills/twitter/like_tweet.py +2 -6
  308. intentkit/skills/twitter/post_tweet.py +6 -9
  309. intentkit/skills/twitter/reply_tweet.py +6 -9
  310. intentkit/skills/twitter/retweet.py +2 -6
  311. intentkit/skills/twitter/schema.json +1 -0
  312. intentkit/skills/twitter/search_tweets.py +4 -12
  313. intentkit/skills/unrealspeech/__init__.py +2 -7
  314. intentkit/skills/unrealspeech/base.py +2 -8
  315. intentkit/skills/unrealspeech/schema.json +2 -5
  316. intentkit/skills/unrealspeech/text_to_speech.py +8 -8
  317. intentkit/skills/venice_audio/__init__.py +98 -106
  318. intentkit/skills/venice_audio/base.py +117 -121
  319. intentkit/skills/venice_audio/input.py +41 -41
  320. intentkit/skills/venice_audio/schema.json +151 -152
  321. intentkit/skills/venice_audio/venice_audio.py +38 -21
  322. intentkit/skills/venice_image/__init__.py +147 -154
  323. intentkit/skills/venice_image/api.py +138 -138
  324. intentkit/skills/venice_image/base.py +185 -192
  325. intentkit/skills/venice_image/config.py +33 -35
  326. intentkit/skills/venice_image/image_enhance/image_enhance.py +2 -3
  327. intentkit/skills/venice_image/image_enhance/image_enhance_base.py +21 -23
  328. intentkit/skills/venice_image/image_enhance/image_enhance_input.py +38 -40
  329. intentkit/skills/venice_image/image_generation/image_generation_base.py +11 -10
  330. intentkit/skills/venice_image/image_generation/image_generation_fluently_xl.py +26 -26
  331. intentkit/skills/venice_image/image_generation/image_generation_flux_dev.py +27 -27
  332. intentkit/skills/venice_image/image_generation/image_generation_flux_dev_uncensored.py +26 -26
  333. intentkit/skills/venice_image/image_generation/image_generation_input.py +158 -158
  334. intentkit/skills/venice_image/image_generation/image_generation_lustify_sdxl.py +26 -26
  335. intentkit/skills/venice_image/image_generation/image_generation_pony_realism.py +26 -26
  336. intentkit/skills/venice_image/image_generation/image_generation_stable_diffusion_3_5.py +28 -28
  337. intentkit/skills/venice_image/image_generation/image_generation_venice_sd35.py +28 -28
  338. intentkit/skills/venice_image/image_upscale/image_upscale.py +3 -3
  339. intentkit/skills/venice_image/image_upscale/image_upscale_base.py +21 -23
  340. intentkit/skills/venice_image/image_upscale/image_upscale_input.py +22 -22
  341. intentkit/skills/venice_image/image_vision/image_vision.py +2 -2
  342. intentkit/skills/venice_image/image_vision/image_vision_base.py +17 -17
  343. intentkit/skills/venice_image/image_vision/image_vision_input.py +9 -9
  344. intentkit/skills/venice_image/schema.json +267 -267
  345. intentkit/skills/venice_image/utils.py +77 -78
  346. intentkit/skills/web_scraper/__init__.py +5 -18
  347. intentkit/skills/web_scraper/base.py +21 -7
  348. intentkit/skills/web_scraper/document_indexer.py +7 -6
  349. intentkit/skills/web_scraper/schema.json +2 -6
  350. intentkit/skills/web_scraper/scrape_and_index.py +15 -15
  351. intentkit/skills/web_scraper/utils.py +62 -63
  352. intentkit/skills/web_scraper/website_indexer.py +17 -19
  353. intentkit/skills/weth/__init__.py +49 -0
  354. intentkit/skills/weth/base.py +11 -0
  355. intentkit/skills/weth/schema.json +58 -0
  356. intentkit/skills/weth/weth.svg +6 -0
  357. intentkit/skills/wow/__init__.py +51 -0
  358. intentkit/skills/wow/base.py +11 -0
  359. intentkit/skills/wow/schema.json +89 -0
  360. intentkit/skills/wow/wow.svg +7 -0
  361. intentkit/skills/x402/__init__.py +58 -0
  362. intentkit/skills/x402/base.py +99 -0
  363. intentkit/skills/x402/http_request.py +117 -0
  364. intentkit/skills/x402/schema.json +40 -0
  365. intentkit/skills/x402/x402.webp +0 -0
  366. intentkit/skills/xmtp/__init__.py +4 -15
  367. intentkit/skills/xmtp/base.py +5 -5
  368. intentkit/skills/xmtp/price.py +7 -6
  369. intentkit/skills/xmtp/schema.json +69 -71
  370. intentkit/skills/xmtp/swap.py +6 -8
  371. intentkit/skills/xmtp/transfer.py +4 -6
  372. intentkit/utils/__init__.py +4 -0
  373. intentkit/utils/chain.py +198 -96
  374. intentkit/utils/ens.py +135 -0
  375. intentkit/utils/error.py +5 -2
  376. intentkit/utils/logging.py +9 -11
  377. intentkit/utils/schema.py +100 -0
  378. intentkit/utils/slack_alert.py +8 -8
  379. intentkit/utils/tx.py +16 -8
  380. intentkit/uv.lock +3377 -0
  381. {intentkit-0.7.5.dev3.dist-info → intentkit-0.8.34.dev7.dist-info}/METADATA +13 -15
  382. intentkit-0.8.34.dev7.dist-info/RECORD +478 -0
  383. intentkit-0.8.34.dev7.dist-info/licenses/LICENSE +21 -0
  384. intentkit/core/node.py +0 -215
  385. intentkit/models/conversation.py +0 -286
  386. intentkit/models/generator.py +0 -347
  387. intentkit/skills/cdp/get_balance.py +0 -110
  388. intentkit/skills/cdp/swap.py +0 -121
  389. intentkit/skills/moralis/tests/__init__.py +0 -0
  390. intentkit/skills/moralis/tests/test_wallet.py +0 -511
  391. intentkit-0.7.5.dev3.dist-info/RECORD +0 -424
  392. {intentkit-0.7.5.dev3.dist-info/licenses → intentkit}/LICENSE +0 -0
  393. {intentkit-0.7.5.dev3.dist-info → intentkit-0.8.34.dev7.dist-info}/WHEEL +0 -0
intentkit/core/engine.py CHANGED
@@ -17,32 +17,33 @@ import textwrap
17
17
  import time
18
18
  import traceback
19
19
  from datetime import datetime
20
- from typing import Optional
21
20
 
22
21
  import sqlalchemy
23
22
  from epyxid import XID
24
- from fastapi import HTTPException
23
+ from langchain.agents import create_agent as create_langchain_agent
25
24
  from langchain_core.messages import (
26
25
  BaseMessage,
27
26
  HumanMessage,
28
27
  )
29
28
  from langchain_core.tools import BaseTool
29
+ from langgraph.checkpoint.memory import InMemorySaver
30
30
  from langgraph.errors import GraphRecursionError
31
31
  from langgraph.graph.state import CompiledStateGraph
32
- from langgraph.prebuilt import create_react_agent
33
32
  from sqlalchemy import func, update
34
33
  from sqlalchemy.exc import SQLAlchemyError
35
34
 
36
35
  from intentkit.abstracts.graph import AgentContext, AgentError, AgentState
37
36
  from intentkit.config.config import config
38
- from intentkit.core.agent import agent_store
39
37
  from intentkit.core.chat import clear_thread_memory
40
38
  from intentkit.core.credit import expense_message, expense_skill
41
- from intentkit.core.node import PreModelNode, post_model_node
42
- from intentkit.core.prompt import (
43
- create_formatted_prompt_function,
44
- explain_prompt,
39
+ from intentkit.core.middleware import (
40
+ CreditCheckMiddleware,
41
+ DynamicPromptMiddleware,
42
+ SummarizationMiddleware,
43
+ ToolBindingMiddleware,
44
+ TrimMessagesMiddleware,
45
45
  )
46
+ from intentkit.core.prompt import explain_prompt
46
47
  from intentkit.models.agent import Agent, AgentTable
47
48
  from intentkit.models.agent_data import AgentData, AgentQuota
48
49
  from intentkit.models.app_setting import AppSetting, SystemMessageType
@@ -53,26 +54,51 @@ from intentkit.models.chat import (
53
54
  ChatMessageSkillCall,
54
55
  )
55
56
  from intentkit.models.credit import CreditAccount, OwnerType
56
- from intentkit.models.db import get_langgraph_checkpointer, get_session
57
- from intentkit.models.llm import LLMModelInfo, LLMProvider, create_llm_model
58
- from intentkit.models.skill import AgentSkillData, ThreadSkillData
57
+ from intentkit.models.db import (
58
+ get_checkpointer,
59
+ get_session,
60
+ )
61
+ from intentkit.models.llm import LLMModelInfo, create_llm_model
62
+ from intentkit.models.skill import AgentSkillData, ChatSkillData, Skill
59
63
  from intentkit.models.user import User
60
64
  from intentkit.utils.error import IntentKitAPIError
61
65
 
62
66
  logger = logging.getLogger(__name__)
63
67
 
64
-
65
68
  # Global variable to cache all agent executors
66
69
  _agents: dict[str, CompiledStateGraph] = {}
67
- _private_agents: dict[str, CompiledStateGraph] = {}
68
70
 
69
71
  # Global dictionaries to cache agent update times
70
72
  _agents_updated: dict[str, datetime] = {}
71
- _private_agents_updated: dict[str, datetime] = {}
72
73
 
73
74
 
74
- async def create_agent(agent: Agent, is_private: bool = False) -> CompiledStateGraph:
75
- """Create an AI agent with specified configuration and tools.
75
+ def _extract_text_content(content: object) -> str:
76
+ if isinstance(content, list):
77
+ texts: list[str] = []
78
+ for item in content:
79
+ if isinstance(item, dict):
80
+ t = item.get("text")
81
+ ty = item.get("type")
82
+ if t is not None and (ty == "text" or ty is None):
83
+ texts.append(t)
84
+ elif isinstance(item, str):
85
+ texts.append(item)
86
+ return "".join(texts)
87
+ if isinstance(content, dict):
88
+ if content.get("type") == "text" and "text" in content:
89
+ return content["text"]
90
+ if "text" in content:
91
+ return content["text"]
92
+ return ""
93
+ if isinstance(content, str):
94
+ return content
95
+ return ""
96
+
97
+
98
+ async def build_agent(
99
+ agent: Agent, agent_data: AgentData, custom_skills: list[BaseTool] = []
100
+ ) -> CompiledStateGraph:
101
+ """Build an AI agent with specified configuration and tools.
76
102
 
77
103
  This function:
78
104
  1. Initializes LLM with specified model
@@ -82,13 +108,13 @@ async def create_agent(agent: Agent, is_private: bool = False) -> CompiledStateG
82
108
 
83
109
  Args:
84
110
  agent (Agent): Agent configuration object
85
- is_private (bool, optional): Flag indicating whether the agent is private. Defaults to False.
86
- has_search (bool, optional): Flag indicating whether to include search tools. Defaults to False.
111
+ agent_data (AgentData): Agent data object
112
+ custom_skills (list[BaseTool], optional): Designed for advanced user who directly
113
+ call this function to inject custom skills into the agent tool node.
87
114
 
88
115
  Returns:
89
116
  CompiledStateGraph: Initialized LangChain agent
90
117
  """
91
- agent_data = await AgentData.get(agent.id)
92
118
 
93
119
  # Create the LLM model instance
94
120
  llm_model = await create_llm_model(
@@ -98,17 +124,15 @@ async def create_agent(agent: Agent, is_private: bool = False) -> CompiledStateG
98
124
  presence_penalty=agent.presence_penalty,
99
125
  )
100
126
 
101
- # Get the LLM instance
102
- llm = await llm_model.create_instance(config)
103
-
104
- # Get the token limit from the model info
105
- input_token_limit = min(config.input_token_limit, llm_model.info.context_length)
106
-
107
127
  # ==== Store buffered conversation history in memory.
108
- memory = get_langgraph_checkpointer()
128
+ try:
129
+ checkpointer = get_checkpointer()
130
+ except RuntimeError:
131
+ checkpointer = InMemorySaver()
109
132
 
110
133
  # ==== Load skills
111
134
  tools: list[BaseTool | dict] = []
135
+ private_tools: list[BaseTool | dict] = []
112
136
 
113
137
  if agent.skills:
114
138
  for k, v in agent.skills.items():
@@ -117,55 +141,65 @@ async def create_agent(agent: Agent, is_private: bool = False) -> CompiledStateG
117
141
  try:
118
142
  skill_module = importlib.import_module(f"intentkit.skills.{k}")
119
143
  if hasattr(skill_module, "get_skills"):
144
+ # all
120
145
  skill_tools = await skill_module.get_skills(
121
- v, is_private, agent_store, agent_id=agent.id
146
+ v, False, agent_id=agent.id, agent=agent
122
147
  )
123
148
  if skill_tools and len(skill_tools) > 0:
124
149
  tools.extend(skill_tools)
150
+ # private
151
+ skill_private_tools = await skill_module.get_skills(
152
+ v, True, agent_id=agent.id, agent=agent
153
+ )
154
+ if skill_private_tools and len(skill_private_tools) > 0:
155
+ private_tools.extend(skill_private_tools)
125
156
  else:
126
157
  logger.error(f"Skill {k} does not have get_skills function")
127
158
  except ImportError as e:
128
159
  logger.error(f"Could not import skill module: {k} ({e})")
129
160
 
161
+ # add custom skills to private tools
162
+ if custom_skills and len(custom_skills) > 0:
163
+ private_tools.extend(custom_skills)
164
+
130
165
  # filter the duplicate tools
131
166
  tools = list({tool.name: tool for tool in tools}.values())
167
+ private_tools = list({tool.name: tool for tool in private_tools}.values())
132
168
 
133
- # Add search tools if requested
134
- if (
135
- llm_model.info.provider == LLMProvider.OPENAI
136
- and llm_model.info.supports_search
137
- and not agent.model.startswith(
138
- "gpt-5"
139
- ) # tmp disable gpt-5 search since package bugs
140
- ):
141
- tools.append({"type": "web_search_preview"})
169
+ for tool in private_tools:
170
+ logger.info(
171
+ f"[{agent.id}] loaded tool: {tool.name if isinstance(tool, BaseTool) else tool}"
172
+ )
142
173
 
143
- # Create the formatted_prompt function using the refactored prompt module
144
- formatted_prompt = create_formatted_prompt_function(agent, agent_data)
174
+ base_model = await llm_model.create_instance()
145
175
 
146
- for tool in tools:
147
- logger.info(
148
- f"[{agent.id}{'-private' if is_private else ''}] loaded tool: {tool.name if isinstance(tool, BaseTool) else tool}"
176
+ middleware = [
177
+ ToolBindingMiddleware(llm_model, tools, private_tools),
178
+ DynamicPromptMiddleware(agent, agent_data),
179
+ ]
180
+
181
+ if agent.short_term_memory_strategy == "trim":
182
+ middleware.append(TrimMessagesMiddleware(max_summary_tokens=2048))
183
+ elif agent.short_term_memory_strategy == "summarize":
184
+ summarize_llm = await create_llm_model(model_name="gpt-5-mini")
185
+ summarize_model = await summarize_llm.create_instance()
186
+ middleware.append(
187
+ SummarizationMiddleware(
188
+ model=summarize_model,
189
+ max_tokens_before_summary=llm_model.info.context_length // 2,
190
+ )
149
191
  )
150
192
 
151
- # Pre model hook
152
- pre_model_hook = PreModelNode(
153
- model=llm,
154
- short_term_memory_strategy=agent.short_term_memory_strategy,
155
- max_tokens=input_token_limit // 2,
156
- max_summary_tokens=2048,
157
- )
193
+ if config.payment_enabled:
194
+ middleware.append(CreditCheckMiddleware())
158
195
 
159
- # Create ReAct Agent using the LLM and CDP Agentkit tools.
160
- executor = create_react_agent(
161
- model=llm,
162
- tools=tools,
163
- prompt=formatted_prompt,
164
- pre_model_hook=pre_model_hook,
165
- post_model_hook=post_model_node if config.payment_enabled else None,
196
+ executor = create_langchain_agent(
197
+ model=base_model,
198
+ tools=private_tools,
199
+ middleware=middleware,
166
200
  state_schema=AgentState,
167
201
  context_schema=AgentContext,
168
- checkpointer=memory,
202
+ checkpointer=checkpointer,
169
203
  debug=config.debug_checkpoint,
170
204
  name=agent.id,
171
205
  )
@@ -173,7 +207,23 @@ async def create_agent(agent: Agent, is_private: bool = False) -> CompiledStateG
173
207
  return executor
174
208
 
175
209
 
176
- async def initialize_agent(aid, is_private=False):
210
+ async def create_agent(agent: Agent) -> CompiledStateGraph:
211
+ """Create an AI agent with specified configuration and tools.
212
+
213
+ This function maintains backward compatibility by calling build_agent internally.
214
+
215
+ Args:
216
+ agent (Agent): Agent configuration object
217
+ is_private (bool, optional): Flag indicating whether the agent is private. Defaults to False.
218
+
219
+ Returns:
220
+ CompiledStateGraph: Initialized LangChain agent
221
+ """
222
+ agent_data = await AgentData.get(agent.id)
223
+ return await build_agent(agent, agent_data)
224
+
225
+
226
+ async def initialize_agent(aid):
177
227
  """Initialize an AI agent with specified configuration and tools.
178
228
 
179
229
  This function:
@@ -192,50 +242,41 @@ async def initialize_agent(aid, is_private=False):
192
242
  HTTPException: If agent not found (404) or database error (500)
193
243
  """
194
244
  # get the agent from the database
195
- agent: Optional[Agent] = await Agent.get(aid)
245
+ agent: Agent | None = await Agent.get(aid)
196
246
  if not agent:
197
- raise HTTPException(status_code=404, detail="Agent not found")
247
+ raise IntentKitAPIError(
248
+ status_code=404, key="AgentNotFound", message="Agent not found"
249
+ )
198
250
 
199
251
  # Create the agent using the new create_agent function
200
- executor = await create_agent(agent, is_private)
252
+ executor = await create_agent(agent)
201
253
 
202
254
  # Cache the agent executor
203
- if is_private:
204
- _private_agents[aid] = executor
205
- _private_agents_updated[aid] = agent.updated_at
206
- else:
207
- _agents[aid] = executor
208
- _agents_updated[aid] = agent.updated_at
255
+ _agents[aid] = executor
256
+ _agents_updated[aid] = agent.deployed_at if agent.deployed_at else agent.updated_at
209
257
 
210
258
 
211
- async def agent_executor(
212
- agent_id: str, is_private: bool
213
- ) -> (CompiledStateGraph, float):
259
+ async def agent_executor(agent_id: str) -> tuple[CompiledStateGraph, float]:
214
260
  start = time.perf_counter()
215
261
  agent = await Agent.get(agent_id)
216
262
  if not agent:
217
- raise HTTPException(status_code=404, detail="Agent not found")
218
- agents = _private_agents if is_private else _agents
219
- agents_updated = _private_agents_updated if is_private else _agents_updated
220
-
263
+ raise IntentKitAPIError(
264
+ status_code=404, key="AgentNotFound", message="Agent not found"
265
+ )
266
+ updated_at = agent.deployed_at if agent.deployed_at else agent.updated_at
221
267
  # Check if agent needs reinitialization due to updates
222
268
  needs_reinit = False
223
- if agent_id in agents:
224
- if (
225
- agent_id not in agents_updated
226
- or agent.updated_at != agents_updated[agent_id]
227
- ):
269
+ if agent_id in _agents:
270
+ if agent_id not in _agents_updated or updated_at != _agents_updated[agent_id]:
228
271
  needs_reinit = True
229
- logger.info(
230
- f"Reinitializing agent {agent_id} due to updates, private mode: {is_private}"
231
- )
272
+ logger.info(f"Reinitializing agent {agent_id} due to updates")
232
273
 
233
274
  # cold start or needs reinitialization
234
275
  cold_start_cost = 0.0
235
- if (agent_id not in agents) or needs_reinit:
236
- await initialize_agent(agent_id, is_private)
276
+ if (agent_id not in _agents) or needs_reinit:
277
+ await initialize_agent(agent_id)
237
278
  cold_start_cost = time.perf_counter() - start
238
- return agents[agent_id], cold_start_cost
279
+ return _agents[agent_id], cold_start_cost
239
280
 
240
281
 
241
282
  async def stream_agent(message: ChatMessageCreate):
@@ -254,24 +295,60 @@ async def stream_agent(message: ChatMessageCreate):
254
295
  Yields:
255
296
  ChatMessage: Individual response messages including timing information
256
297
  """
298
+ agent = await Agent.get(message.agent_id)
299
+ executor, cold_start_cost = await agent_executor(message.agent_id)
300
+ message.cold_start_cost = cold_start_cost
301
+ async for chat_message in stream_agent_raw(message, agent, executor):
302
+ yield chat_message
303
+
304
+
305
+ async def stream_agent_raw(
306
+ message: ChatMessageCreate, agent: Agent, executor: CompiledStateGraph
307
+ ):
257
308
  start = time.perf_counter()
258
309
  # make sure reply_to is set
259
310
  message.reply_to = message.id
260
311
 
261
312
  # save input message first
262
- input = await message.save()
313
+ user_message = await message.save()
263
314
 
264
- # agent
265
- agent = await Agent.get(input.agent_id)
315
+ # temporary debug logging for telegram messages
316
+ if user_message.author_type == AuthorType.TELEGRAM:
317
+ logger.info(
318
+ f"[TELEGRAM DEBUG] Agent: {user_message.agent_id} | Chat: {user_message.chat_id} | Message: {user_message.message}"
319
+ )
320
+
321
+ if re.search(
322
+ r"(@clear|/clear)(?!\w)",
323
+ user_message.message.strip(),
324
+ re.IGNORECASE,
325
+ ):
326
+ await clear_thread_memory(user_message.agent_id, user_message.chat_id)
327
+
328
+ confirmation_message = ChatMessageCreate(
329
+ id=str(XID()),
330
+ agent_id=user_message.agent_id,
331
+ chat_id=user_message.chat_id,
332
+ user_id=user_message.user_id,
333
+ author_id=user_message.agent_id,
334
+ author_type=AuthorType.AGENT,
335
+ model=agent.model,
336
+ thread_type=user_message.author_type,
337
+ reply_to=user_message.id,
338
+ message="Memory in context has been cleared.",
339
+ time_cost=time.perf_counter() - start,
340
+ )
341
+
342
+ yield await confirmation_message.save()
343
+ return
266
344
 
267
- # model
268
345
  model = await LLMModelInfo.get(agent.model)
269
346
 
270
347
  payment_enabled = config.payment_enabled
271
348
 
272
349
  # check user balance
273
350
  if payment_enabled:
274
- if not input.user_id or not agent.owner:
351
+ if not user_message.user_id or not agent.owner:
275
352
  raise IntentKitAPIError(
276
353
  500,
277
354
  "PaymentError",
@@ -282,23 +359,25 @@ async def stream_agent(message: ChatMessageCreate):
282
359
  if owner and agent.fee_percentage > 100 + owner.nft_count * 10:
283
360
  error_message_create = await ChatMessageCreate.from_system_message(
284
361
  SystemMessageType.SERVICE_FEE_ERROR,
285
- agent_id=input.agent_id,
286
- chat_id=input.chat_id,
287
- user_id=input.user_id,
288
- author_id=input.agent_id,
289
- thread_type=input.author_type,
290
- reply_to=input.id,
362
+ agent_id=user_message.agent_id,
363
+ chat_id=user_message.chat_id,
364
+ user_id=user_message.user_id,
365
+ author_id=user_message.agent_id,
366
+ thread_type=user_message.author_type,
367
+ reply_to=user_message.id,
291
368
  time_cost=time.perf_counter() - start,
292
369
  )
293
370
  error_message = await error_message_create.save()
294
371
  yield error_message
295
372
  return
296
373
  # payer
297
- payer = input.user_id
298
- if input.author_type in [
374
+ payer = user_message.user_id
375
+ if user_message.author_type in [
299
376
  AuthorType.TELEGRAM,
377
+ AuthorType.DISCORD,
300
378
  AuthorType.TWITTER,
301
379
  AuthorType.API,
380
+ AuthorType.X402,
302
381
  ]:
303
382
  payer = agent.owner
304
383
  # user account
@@ -318,12 +397,12 @@ async def stream_agent(message: ChatMessageCreate):
318
397
  if quota and quota.free_income_daily > 24000:
319
398
  error_message_create = await ChatMessageCreate.from_system_message(
320
399
  SystemMessageType.DAILY_USAGE_LIMIT_EXCEEDED,
321
- agent_id=input.agent_id,
322
- chat_id=input.chat_id,
323
- user_id=input.user_id,
324
- author_id=input.agent_id,
325
- thread_type=input.author_type,
326
- reply_to=input.id,
400
+ agent_id=user_message.agent_id,
401
+ chat_id=user_message.chat_id,
402
+ user_id=user_message.user_id,
403
+ author_id=user_message.agent_id,
404
+ thread_type=user_message.author_type,
405
+ reply_to=user_message.id,
327
406
  time_cost=time.perf_counter() - start,
328
407
  )
329
408
  error_message = await error_message_create.save()
@@ -336,12 +415,12 @@ async def stream_agent(message: ChatMessageCreate):
336
415
  if not user_account.has_sufficient_credits(avg_count):
337
416
  error_message_create = await ChatMessageCreate.from_system_message(
338
417
  SystemMessageType.INSUFFICIENT_BALANCE,
339
- agent_id=input.agent_id,
340
- chat_id=input.chat_id,
341
- user_id=input.user_id,
342
- author_id=input.agent_id,
343
- thread_type=input.author_type,
344
- reply_to=input.id,
418
+ agent_id=user_message.agent_id,
419
+ chat_id=user_message.chat_id,
420
+ user_id=user_message.user_id,
421
+ author_id=user_message.agent_id,
422
+ thread_type=user_message.author_type,
423
+ reply_to=user_message.id,
345
424
  time_cost=time.perf_counter() - start,
346
425
  )
347
426
  error_message = await error_message_create.save()
@@ -349,56 +428,41 @@ async def stream_agent(message: ChatMessageCreate):
349
428
  return
350
429
 
351
430
  is_private = False
352
- if input.user_id == agent.owner:
431
+ if user_message.user_id == agent.owner:
353
432
  is_private = True
354
433
 
355
- executor, cold_start_cost = await agent_executor(input.agent_id, is_private)
356
- last = start + cold_start_cost
434
+ last = start
357
435
 
358
436
  # Extract images from attachments
359
437
  image_urls = []
360
- if input.attachments:
438
+ if user_message.attachments:
361
439
  image_urls = [
362
440
  att["url"]
363
- for att in input.attachments
441
+ for att in user_message.attachments
364
442
  if "type" in att and att["type"] == "image" and "url" in att
365
443
  ]
366
444
 
367
445
  # Process input message to handle @skill patterns
368
- if config.admin_llm_skill_control:
369
- input_message = await explain_prompt(input.message)
370
- else:
371
- input_message = input.message
446
+ input_message = await explain_prompt(user_message.message)
372
447
 
373
448
  # super mode
374
449
  recursion_limit = 30
375
- if re.search(r"\b@super\b", input_message):
450
+ if (
451
+ re.search(r"@super\b", input_message)
452
+ or user_message.super_mode
453
+ or agent.has_super()
454
+ ):
376
455
  recursion_limit = 300
377
- # Remove @super from the message
378
- input_message = re.sub(r"\b@super\b", "", input_message).strip()
456
+ input_message = re.sub(r"@super\b", "", input_message).strip()
379
457
 
380
458
  # llm native search
381
- if re.search(r"\b@search\b", input_message) or re.search(
382
- r"\b@web\b", input_message
383
- ):
384
- if model.supports_search:
385
- input_message = re.sub(
386
- r"\b@search\b",
387
- "(You have native search tool, you can use it to get more recent information)",
388
- input_message,
389
- ).strip()
390
- input_message = re.sub(
391
- r"\b@web\b",
392
- "(You have native search tool, you can use it to get more recent information)",
393
- input_message,
394
- ).strip()
395
- else:
396
- input_message = re.sub(r"\b@search\b", "", input_message).strip()
397
- input_message = re.sub(r"\b@web\b", "", input_message).strip()
459
+ search = user_message.search_mode if user_message.search_mode is not None else False
460
+ if re.search(r"@search\b", input_message) or re.search(r"@web\b", input_message):
461
+ search = True
398
462
 
399
463
  # content to llm
400
- content = [
401
- {"type": "text", "text": input_message},
464
+ messages = [
465
+ HumanMessage(content=input_message),
402
466
  ]
403
467
  # if the model doesn't natively support image parsing, add the image URLs to the message
404
468
  if image_urls:
@@ -406,25 +470,24 @@ async def stream_agent(message: ChatMessageCreate):
406
470
  agent.has_image_parser_skill(is_private=is_private)
407
471
  and not model.supports_image_input
408
472
  ):
409
- input_message += f"\n\nImages:\n{'\n'.join(image_urls)}"
410
- content = [
411
- {"type": "text", "text": input_message},
473
+ image_urls_text = "\n".join(image_urls)
474
+ input_message += f"\n\nImages:\n{image_urls_text}"
475
+ messages = [
476
+ HumanMessage(content=input_message),
412
477
  ]
413
478
  else:
414
479
  # anyway, pass it directly to LLM
415
- content.extend(
480
+ messages.extend(
416
481
  [
417
- {"type": "image_url", "image_url": {"url": image_url}}
482
+ HumanMessage(
483
+ content={"type": "image_url", "image_url": {"url": image_url}}
484
+ )
418
485
  for image_url in image_urls
419
486
  ]
420
487
  )
421
488
 
422
- messages = [
423
- HumanMessage(content=content),
424
- ]
425
-
426
489
  # stream config
427
- thread_id = f"{input.agent_id}-{input.chat_id}"
490
+ thread_id = f"{user_message.agent_id}-{user_message.chat_id}"
428
491
  stream_config = {
429
492
  "configurable": {
430
493
  "thread_id": thread_id,
@@ -432,13 +495,18 @@ async def stream_agent(message: ChatMessageCreate):
432
495
  "recursion_limit": recursion_limit,
433
496
  }
434
497
 
498
+ def get_agent() -> Agent:
499
+ return agent
500
+
435
501
  context = AgentContext(
436
- agent_id=input.agent_id,
437
- chat_id=input.chat_id,
438
- user_id=input.user_id,
439
- app_id=input.app_id,
440
- entrypoint=input.author_type,
502
+ agent_id=user_message.agent_id,
503
+ get_agent=get_agent,
504
+ chat_id=user_message.chat_id,
505
+ user_id=user_message.user_id,
506
+ app_id=user_message.app_id,
507
+ entrypoint=user_message.author_type,
441
508
  is_private=is_private,
509
+ search=search,
442
510
  payer=payer if payment_enabled else None,
443
511
  )
444
512
 
@@ -446,42 +514,84 @@ async def stream_agent(message: ChatMessageCreate):
446
514
  cached_tool_step = None
447
515
  try:
448
516
  async for chunk in executor.astream(
449
- {"messages": messages}, context=context, config=stream_config
517
+ {"messages": messages},
518
+ context=context,
519
+ config=stream_config,
520
+ stream_mode=["updates", "custom"],
450
521
  ):
451
522
  this_time = time.perf_counter()
452
523
  logger.debug(f"stream chunk: {chunk}", extra={"thread_id": thread_id})
453
- if "agent" in chunk and "messages" in chunk["agent"]:
454
- if len(chunk["agent"]["messages"]) != 1:
524
+
525
+ if isinstance(chunk, tuple) and len(chunk) == 2:
526
+ event_kind, payload = chunk
527
+ chunk = payload
528
+
529
+ if isinstance(chunk, dict) and "credit_check" in chunk:
530
+ credit_payload = chunk.get("credit_check", {})
531
+ content = credit_payload.get("message")
532
+ if content:
533
+ credit_message_create = ChatMessageCreate(
534
+ id=str(XID()),
535
+ agent_id=user_message.agent_id,
536
+ chat_id=user_message.chat_id,
537
+ user_id=user_message.user_id,
538
+ author_id=user_message.agent_id,
539
+ author_type=AuthorType.AGENT,
540
+ model=agent.model,
541
+ thread_type=user_message.author_type,
542
+ reply_to=user_message.id,
543
+ message=content,
544
+ input_tokens=0,
545
+ output_tokens=0,
546
+ time_cost=this_time - last,
547
+ )
548
+ last = this_time
549
+ credit_message = await credit_message_create.save()
550
+ yield credit_message
551
+
552
+ error_message_create = await ChatMessageCreate.from_system_message(
553
+ SystemMessageType.INSUFFICIENT_BALANCE,
554
+ agent_id=user_message.agent_id,
555
+ chat_id=user_message.chat_id,
556
+ user_id=user_message.user_id,
557
+ author_id=user_message.agent_id,
558
+ thread_type=user_message.author_type,
559
+ reply_to=user_message.id,
560
+ time_cost=0,
561
+ )
562
+ error_message = await error_message_create.save()
563
+ yield error_message
564
+ return
565
+
566
+ if not isinstance(chunk, dict):
567
+ continue
568
+
569
+ if "model" in chunk and "messages" in chunk["model"]:
570
+ if len(chunk["model"]["messages"]) != 1:
455
571
  logger.error(
456
- "unexpected agent message: " + str(chunk["agent"]["messages"]),
572
+ "unexpected model message: " + str(chunk["model"]["messages"]),
457
573
  extra={"thread_id": thread_id},
458
574
  )
459
- msg = chunk["agent"]["messages"][0]
460
- if hasattr(msg, "tool_calls") and msg.tool_calls:
461
- # tool calls, save for later use, if it is deleted by post_model_hook, will not be used.
575
+ msg = chunk["model"]["messages"][0]
576
+ has_tools = hasattr(msg, "tool_calls") and bool(msg.tool_calls)
577
+ if has_tools:
462
578
  cached_tool_step = msg
463
- if hasattr(msg, "content") and msg.content:
464
- content = msg.content
465
- if isinstance(msg.content, list):
466
- # in new version, content item maybe a list
467
- content = msg.content[0]
468
- if isinstance(content, dict):
469
- if "text" in content:
470
- content = content["text"]
471
- else:
472
- content = str(content)
473
- logger.error(f"unexpected content type: {content}")
474
- # agent message
579
+ content = (
580
+ _extract_text_content(msg.content)
581
+ if hasattr(msg, "content")
582
+ else ""
583
+ )
584
+ if content and not has_tools:
475
585
  chat_message_create = ChatMessageCreate(
476
586
  id=str(XID()),
477
- agent_id=input.agent_id,
478
- chat_id=input.chat_id,
479
- user_id=input.user_id,
480
- author_id=input.agent_id,
587
+ agent_id=user_message.agent_id,
588
+ chat_id=user_message.chat_id,
589
+ user_id=user_message.user_id,
590
+ author_id=user_message.agent_id,
481
591
  author_type=AuthorType.AGENT,
482
592
  model=agent.model,
483
- thread_type=input.author_type,
484
- reply_to=input.id,
593
+ thread_type=user_message.author_type,
594
+ reply_to=user_message.id,
485
595
  message=content,
486
596
  input_tokens=(
487
597
  msg.usage_metadata.get("input_tokens", 0)
@@ -496,19 +606,13 @@ async def stream_agent(message: ChatMessageCreate):
496
606
  time_cost=this_time - last,
497
607
  )
498
608
  last = this_time
499
- if cold_start_cost > 0:
500
- chat_message_create.cold_start_cost = cold_start_cost
501
- cold_start_cost = 0
502
- # handle message and payment in one transaction
503
609
  async with get_session() as session:
504
- # payment
505
610
  if payment_enabled:
506
611
  amount = await model.calculate_cost(
507
612
  chat_message_create.input_tokens,
508
613
  chat_message_create.output_tokens,
509
614
  )
510
615
 
511
- # Check for web_search_call in additional_kwargs
512
616
  if (
513
617
  hasattr(msg, "additional_kwargs")
514
618
  and msg.additional_kwargs
@@ -519,7 +623,7 @@ async def stream_agent(message: ChatMessageCreate):
519
623
  for tool_output in tool_outputs:
520
624
  if tool_output.get("type") == "web_search_call":
521
625
  logger.info(
522
- f"[{input.agent_id}] Found web_search_call in additional_kwargs"
626
+ f"[{user_message.agent_id}] Found web_search_call in additional_kwargs"
523
627
  )
524
628
  amount += 35
525
629
  break
@@ -527,11 +631,13 @@ async def stream_agent(message: ChatMessageCreate):
527
631
  session,
528
632
  payer,
529
633
  chat_message_create.id,
530
- input.id,
634
+ user_message.id,
531
635
  amount,
532
636
  agent,
533
637
  )
534
- logger.info(f"[{input.agent_id}] expense message: {amount}")
638
+ logger.info(
639
+ f"[{user_message.agent_id}] expense message: {amount}"
640
+ )
535
641
  chat_message_create.credit_event_id = credit_event.id
536
642
  chat_message_create.credit_cost = credit_event.total_amount
537
643
  chat_message = await chat_message_create.save_in_session(
@@ -566,7 +672,8 @@ async def stream_agent(message: ChatMessageCreate):
566
672
  "parameters": call["args"],
567
673
  "success": True,
568
674
  }
569
- if msg.status == "error":
675
+ status = getattr(msg, "status", None)
676
+ if status == "error":
570
677
  skill_call["success"] = False
571
678
  skill_call["error_message"] = str(msg.content)
572
679
  else:
@@ -576,20 +683,21 @@ async def stream_agent(message: ChatMessageCreate):
576
683
  skill_call["response"] = textwrap.shorten(
577
684
  str(msg.content), width=1000, placeholder="..."
578
685
  )
579
- if msg.artifact:
580
- cached_attachments.extend(msg.artifact)
686
+ artifact = getattr(msg, "artifact", None)
687
+ if artifact:
688
+ cached_attachments.extend(artifact)
581
689
  skill_calls.append(skill_call)
582
690
  break
583
691
  skill_message_create = ChatMessageCreate(
584
692
  id=str(XID()),
585
- agent_id=input.agent_id,
586
- chat_id=input.chat_id,
587
- user_id=input.user_id,
588
- author_id=input.agent_id,
693
+ agent_id=user_message.agent_id,
694
+ chat_id=user_message.chat_id,
695
+ user_id=user_message.user_id,
696
+ author_id=user_message.agent_id,
589
697
  author_type=AuthorType.SKILL,
590
698
  model=agent.model,
591
- thread_type=input.author_type,
592
- reply_to=input.id,
699
+ thread_type=user_message.author_type,
700
+ reply_to=user_message.id,
593
701
  message="",
594
702
  skill_calls=skill_calls,
595
703
  attachments=cached_attachments,
@@ -610,13 +718,8 @@ async def stream_agent(message: ChatMessageCreate):
610
718
  time_cost=this_time - last,
611
719
  )
612
720
  last = this_time
613
- if cold_start_cost > 0:
614
- skill_message_create.cold_start_cost = cold_start_cost
615
- cold_start_cost = 0
616
- # save message and credit in one transaction
617
721
  async with get_session() as session:
618
722
  if payment_enabled:
619
- # message payment, only first call in a group has message bill
620
723
  if have_first_call_in_cache:
621
724
  message_amount = await model.calculate_cost(
622
725
  skill_message_create.input_tokens,
@@ -626,7 +729,7 @@ async def stream_agent(message: ChatMessageCreate):
626
729
  session,
627
730
  payer,
628
731
  skill_message_create.id,
629
- input.id,
732
+ user_message.id,
630
733
  message_amount,
631
734
  agent,
632
735
  )
@@ -636,15 +739,17 @@ async def stream_agent(message: ChatMessageCreate):
636
739
  skill_message_create.credit_cost = (
637
740
  message_payment_event.total_amount
638
741
  )
639
- # skill payment
640
742
  for skill_call in skill_calls:
641
743
  if not skill_call["success"]:
642
744
  continue
745
+ skill = await Skill.get(skill_call["name"])
746
+ if not skill:
747
+ continue
643
748
  payment_event = await expense_skill(
644
749
  session,
645
750
  payer,
646
751
  skill_message_create.id,
647
- input.id,
752
+ user_message.id,
648
753
  skill_call["id"],
649
754
  skill_call["name"],
650
755
  agent,
@@ -652,73 +757,61 @@ async def stream_agent(message: ChatMessageCreate):
652
757
  skill_call["credit_event_id"] = payment_event.id
653
758
  skill_call["credit_cost"] = payment_event.total_amount
654
759
  logger.info(
655
- f"[{input.agent_id}] skill payment: {skill_call}"
760
+ f"[{user_message.agent_id}] skill payment: {skill_call}"
656
761
  )
657
762
  skill_message_create.skill_calls = skill_calls
658
763
  skill_message = await skill_message_create.save_in_session(session)
659
764
  await session.commit()
660
765
  yield skill_message
661
- elif "pre_model_hook" in chunk:
662
- pass
663
- elif "post_model_hook" in chunk:
664
- logger.debug(
665
- f"post_model_hook: {chunk}",
666
- extra={"thread_id": thread_id},
667
- )
668
- if chunk["post_model_hook"] and "error" in chunk["post_model_hook"]:
766
+ else:
767
+ for node_name, update in chunk.items():
669
768
  if (
670
- chunk["post_model_hook"]["error"]
671
- == AgentError.INSUFFICIENT_CREDITS
769
+ node_name.endswith("CreditCheckMiddleware.after_model")
770
+ and isinstance(update, dict)
771
+ and update.get("error") == AgentError.INSUFFICIENT_CREDITS
672
772
  ):
673
- if "messages" in chunk["post_model_hook"]:
674
- msg = chunk["post_model_hook"]["messages"][-1]
675
- content = msg.content
676
- if isinstance(msg.content, list):
677
- # in new version, content item maybe a list
678
- content = msg.content[0]
679
- post_model_message_create = ChatMessageCreate(
680
- id=str(XID()),
681
- agent_id=input.agent_id,
682
- chat_id=input.chat_id,
683
- user_id=input.user_id,
684
- author_id=input.agent_id,
685
- author_type=AuthorType.AGENT,
686
- model=agent.model,
687
- thread_type=input.author_type,
688
- reply_to=input.id,
689
- message=content,
690
- input_tokens=0,
691
- output_tokens=0,
692
- time_cost=this_time - last,
693
- )
694
- last = this_time
695
- if cold_start_cost > 0:
696
- post_model_message_create.cold_start_cost = (
697
- cold_start_cost
698
- )
699
- cold_start_cost = 0
700
- post_model_message = await post_model_message_create.save()
701
- yield post_model_message
773
+ ai_messages = [
774
+ message
775
+ for message in update.get("messages", [])
776
+ if isinstance(message, BaseMessage)
777
+ ]
778
+ content = ""
779
+ if ai_messages:
780
+ content = _extract_text_content(ai_messages[-1].content)
781
+ post_model_message_create = ChatMessageCreate(
782
+ id=str(XID()),
783
+ agent_id=user_message.agent_id,
784
+ chat_id=user_message.chat_id,
785
+ user_id=user_message.user_id,
786
+ author_id=user_message.agent_id,
787
+ author_type=AuthorType.AGENT,
788
+ model=agent.model,
789
+ thread_type=user_message.author_type,
790
+ reply_to=user_message.id,
791
+ message=content,
792
+ input_tokens=0,
793
+ output_tokens=0,
794
+ time_cost=this_time - last,
795
+ )
796
+ last = this_time
797
+ post_model_message = await post_model_message_create.save()
798
+ yield post_model_message
799
+
702
800
  error_message_create = (
703
801
  await ChatMessageCreate.from_system_message(
704
802
  SystemMessageType.INSUFFICIENT_BALANCE,
705
- agent_id=input.agent_id,
706
- chat_id=input.chat_id,
707
- user_id=input.user_id,
708
- author_id=input.agent_id,
709
- thread_type=input.author_type,
710
- reply_to=input.id,
803
+ agent_id=user_message.agent_id,
804
+ chat_id=user_message.chat_id,
805
+ user_id=user_message.user_id,
806
+ author_id=user_message.agent_id,
807
+ thread_type=user_message.author_type,
808
+ reply_to=user_message.id,
711
809
  time_cost=0,
712
810
  )
713
811
  )
714
812
  error_message = await error_message_create.save()
715
813
  yield error_message
716
- else:
717
- error_traceback = traceback.format_exc()
718
- logger.error(
719
- f"unexpected message type: {str(chunk)}\n{error_traceback}",
720
- extra={"thread_id": thread_id},
721
- )
814
+ return
722
815
  except SQLAlchemyError as e:
723
816
  error_traceback = traceback.format_exc()
724
817
  logger.error(
@@ -727,12 +820,12 @@ async def stream_agent(message: ChatMessageCreate):
727
820
  )
728
821
  error_message_create = await ChatMessageCreate.from_system_message(
729
822
  SystemMessageType.AGENT_INTERNAL_ERROR,
730
- agent_id=input.agent_id,
731
- chat_id=input.chat_id,
732
- user_id=input.user_id,
733
- author_id=input.agent_id,
734
- thread_type=input.author_type,
735
- reply_to=input.id,
823
+ agent_id=user_message.agent_id,
824
+ chat_id=user_message.chat_id,
825
+ user_id=user_message.user_id,
826
+ author_id=user_message.agent_id,
827
+ thread_type=user_message.author_type,
828
+ reply_to=user_message.id,
736
829
  time_cost=time.perf_counter() - start,
737
830
  )
738
831
  error_message = await error_message_create.save()
@@ -742,16 +835,16 @@ async def stream_agent(message: ChatMessageCreate):
742
835
  error_traceback = traceback.format_exc()
743
836
  logger.error(
744
837
  f"reached recursion limit: {str(e)}\n{error_traceback}",
745
- extra={"thread_id": thread_id, "agent_id": input.agent_id},
838
+ extra={"thread_id": thread_id, "agent_id": user_message.agent_id},
746
839
  )
747
840
  error_message_create = await ChatMessageCreate.from_system_message(
748
841
  SystemMessageType.STEP_LIMIT_EXCEEDED,
749
- agent_id=input.agent_id,
750
- chat_id=input.chat_id,
751
- user_id=input.user_id,
752
- author_id=input.agent_id,
753
- thread_type=input.author_type,
754
- reply_to=input.id,
842
+ agent_id=user_message.agent_id,
843
+ chat_id=user_message.chat_id,
844
+ user_id=user_message.user_id,
845
+ author_id=user_message.agent_id,
846
+ thread_type=user_message.author_type,
847
+ reply_to=user_message.id,
755
848
  time_cost=time.perf_counter() - start,
756
849
  )
757
850
  error_message = await error_message_create.save()
@@ -761,21 +854,21 @@ async def stream_agent(message: ChatMessageCreate):
761
854
  error_traceback = traceback.format_exc()
762
855
  logger.error(
763
856
  f"failed to execute agent: {str(e)}\n{error_traceback}",
764
- extra={"thread_id": thread_id, "agent_id": input.agent_id},
857
+ extra={"thread_id": thread_id, "agent_id": user_message.agent_id},
765
858
  )
766
859
  error_message_create = await ChatMessageCreate.from_system_message(
767
860
  SystemMessageType.AGENT_INTERNAL_ERROR,
768
- agent_id=input.agent_id,
769
- chat_id=input.chat_id,
770
- user_id=input.user_id,
771
- author_id=input.agent_id,
772
- thread_type=input.author_type,
773
- reply_to=input.id,
861
+ agent_id=user_message.agent_id,
862
+ chat_id=user_message.chat_id,
863
+ user_id=user_message.user_id,
864
+ author_id=user_message.agent_id,
865
+ thread_type=user_message.author_type,
866
+ reply_to=user_message.id,
774
867
  time_cost=time.perf_counter() - start,
775
868
  )
776
869
  error_message = await error_message_create.save()
777
870
  yield error_message
778
- await clear_thread_memory(input.agent_id, input.chat_id)
871
+ await clear_thread_memory(user_message.agent_id, user_message.chat_id)
779
872
  return
780
873
 
781
874
 
@@ -830,14 +923,15 @@ async def clean_agent_memory(
830
923
  # get the agent from the database
831
924
  try:
832
925
  if not clean_skill and not clean_agent:
833
- raise HTTPException(
926
+ raise IntentKitAPIError(
834
927
  status_code=400,
835
- detail="at least one of skills data or agent memory should be true.",
928
+ key="InvalidCleanupParameters",
929
+ message="at least one of skills data or agent memory should be true",
836
930
  )
837
931
 
838
932
  if clean_skill:
839
933
  await AgentSkillData.clean_data(agent_id)
840
- await ThreadSkillData.clean_data(agent_id, chat_id)
934
+ await ChatSkillData.clean_data(agent_id, chat_id)
841
935
 
842
936
  async with get_session() as db:
843
937
  if clean_agent:
@@ -879,7 +973,7 @@ async def clean_agent_memory(
879
973
  except SQLAlchemyError as e:
880
974
  # Handle other SQLAlchemy-related errors
881
975
  logger.error(e)
882
- raise HTTPException(status_code=500, detail=str(e))
976
+ raise IntentKitAPIError(status_code=500, key="DatabaseError", message=str(e))
883
977
  except Exception as e:
884
978
  logger.error("failed to cleanup the agent memory: " + str(e))
885
979
  raise e
@@ -888,10 +982,7 @@ async def clean_agent_memory(
888
982
  async def thread_stats(agent_id: str, chat_id: str) -> list[BaseMessage]:
889
983
  thread_id = f"{agent_id}-{chat_id}"
890
984
  stream_config = {"configurable": {"thread_id": thread_id}}
891
- is_private = False
892
- if chat_id.startswith("owner") or chat_id.startswith("autonomous"):
893
- is_private = True
894
- executor, _ = await agent_executor(agent_id, is_private)
985
+ executor, _ = await agent_executor(agent_id)
895
986
  snap = await executor.aget_state(stream_config)
896
987
  if snap.values and "messages" in snap.values:
897
988
  return snap.values["messages"]