intentkit 0.6.13.dev2__py3-none-any.whl → 0.8.17__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of intentkit might be problematic. Click here for more details.

Files changed (385) hide show
  1. intentkit/__init__.py +1 -1
  2. intentkit/abstracts/agent.py +4 -5
  3. intentkit/abstracts/engine.py +5 -5
  4. intentkit/abstracts/graph.py +14 -7
  5. intentkit/abstracts/skill.py +6 -144
  6. intentkit/abstracts/twitter.py +4 -5
  7. intentkit/clients/__init__.py +5 -2
  8. intentkit/clients/cdp.py +101 -141
  9. intentkit/clients/twitter.py +83 -62
  10. intentkit/clients/web3.py +29 -0
  11. intentkit/config/config.py +8 -5
  12. intentkit/core/agent.py +472 -195
  13. intentkit/core/asset.py +253 -0
  14. intentkit/core/chat.py +51 -0
  15. intentkit/core/client.py +1 -1
  16. intentkit/core/credit.py +460 -130
  17. intentkit/core/engine.py +262 -233
  18. intentkit/core/node.py +15 -16
  19. intentkit/core/prompt.py +62 -28
  20. intentkit/core/scheduler.py +92 -0
  21. intentkit/core/statistics.py +168 -0
  22. intentkit/models/agent.py +1096 -949
  23. intentkit/models/agent_data.py +68 -38
  24. intentkit/models/agent_public.json +98 -0
  25. intentkit/models/agent_schema.json +54 -439
  26. intentkit/models/app_setting.py +96 -33
  27. intentkit/models/chat.py +74 -27
  28. intentkit/models/conversation.py +8 -8
  29. intentkit/models/credit.py +362 -74
  30. intentkit/models/db.py +26 -8
  31. intentkit/models/db_mig.py +2 -2
  32. intentkit/models/llm.csv +28 -0
  33. intentkit/models/llm.py +185 -350
  34. intentkit/models/redis.py +6 -4
  35. intentkit/models/skill.py +186 -72
  36. intentkit/models/skills.csv +174 -0
  37. intentkit/models/user.py +82 -24
  38. intentkit/skills/acolyt/__init__.py +2 -9
  39. intentkit/skills/acolyt/ask.py +3 -4
  40. intentkit/skills/acolyt/base.py +4 -9
  41. intentkit/skills/acolyt/schema.json +4 -3
  42. intentkit/skills/aixbt/__init__.py +2 -13
  43. intentkit/skills/aixbt/base.py +1 -7
  44. intentkit/skills/aixbt/projects.py +14 -15
  45. intentkit/skills/aixbt/schema.json +4 -4
  46. intentkit/skills/allora/__init__.py +2 -9
  47. intentkit/skills/allora/base.py +4 -9
  48. intentkit/skills/allora/price.py +3 -4
  49. intentkit/skills/allora/schema.json +3 -2
  50. intentkit/skills/base.py +248 -85
  51. intentkit/skills/basename/__init__.py +51 -0
  52. intentkit/skills/basename/base.py +11 -0
  53. intentkit/skills/basename/basename.svg +11 -0
  54. intentkit/skills/basename/schema.json +58 -0
  55. intentkit/skills/carv/__init__.py +115 -121
  56. intentkit/skills/carv/base.py +184 -185
  57. intentkit/skills/carv/fetch_news.py +3 -3
  58. intentkit/skills/carv/onchain_query.py +4 -4
  59. intentkit/skills/carv/schema.json +134 -137
  60. intentkit/skills/carv/token_info_and_price.py +5 -5
  61. intentkit/skills/casino/README.md +254 -0
  62. intentkit/skills/casino/__init__.py +86 -0
  63. intentkit/skills/casino/base.py +17 -0
  64. intentkit/skills/casino/casino.png +0 -0
  65. intentkit/skills/casino/deck_draw.py +127 -0
  66. intentkit/skills/casino/deck_shuffle.py +118 -0
  67. intentkit/skills/casino/dice_roll.py +100 -0
  68. intentkit/skills/casino/schema.json +77 -0
  69. intentkit/skills/casino/utils.py +107 -0
  70. intentkit/skills/cdp/__init__.py +22 -84
  71. intentkit/skills/cdp/base.py +1 -7
  72. intentkit/skills/cdp/schema.json +11 -314
  73. intentkit/skills/chainlist/__init__.py +2 -7
  74. intentkit/skills/chainlist/base.py +1 -7
  75. intentkit/skills/chainlist/chain_lookup.py +18 -18
  76. intentkit/skills/chainlist/schema.json +3 -5
  77. intentkit/skills/common/__init__.py +2 -9
  78. intentkit/skills/common/base.py +1 -7
  79. intentkit/skills/common/current_time.py +1 -2
  80. intentkit/skills/common/schema.json +2 -2
  81. intentkit/skills/cookiefun/__init__.py +6 -9
  82. intentkit/skills/cookiefun/base.py +2 -7
  83. intentkit/skills/cookiefun/get_account_details.py +7 -7
  84. intentkit/skills/cookiefun/get_account_feed.py +19 -19
  85. intentkit/skills/cookiefun/get_account_smart_followers.py +7 -7
  86. intentkit/skills/cookiefun/get_sectors.py +3 -3
  87. intentkit/skills/cookiefun/schema.json +1 -3
  88. intentkit/skills/cookiefun/search_accounts.py +9 -9
  89. intentkit/skills/cryptocompare/__init__.py +7 -24
  90. intentkit/skills/cryptocompare/api.py +2 -3
  91. intentkit/skills/cryptocompare/base.py +11 -25
  92. intentkit/skills/cryptocompare/fetch_news.py +4 -5
  93. intentkit/skills/cryptocompare/fetch_price.py +6 -7
  94. intentkit/skills/cryptocompare/fetch_top_exchanges.py +4 -5
  95. intentkit/skills/cryptocompare/fetch_top_market_cap.py +4 -5
  96. intentkit/skills/cryptocompare/fetch_top_volume.py +4 -5
  97. intentkit/skills/cryptocompare/fetch_trading_signals.py +5 -6
  98. intentkit/skills/cryptocompare/schema.json +3 -3
  99. intentkit/skills/cryptopanic/__init__.py +7 -10
  100. intentkit/skills/cryptopanic/base.py +51 -55
  101. intentkit/skills/cryptopanic/fetch_crypto_news.py +4 -8
  102. intentkit/skills/cryptopanic/fetch_crypto_sentiment.py +5 -7
  103. intentkit/skills/cryptopanic/schema.json +105 -103
  104. intentkit/skills/dapplooker/__init__.py +2 -9
  105. intentkit/skills/dapplooker/base.py +4 -9
  106. intentkit/skills/dapplooker/dapplooker_token_data.py +7 -7
  107. intentkit/skills/dapplooker/schema.json +3 -5
  108. intentkit/skills/defillama/__init__.py +24 -74
  109. intentkit/skills/defillama/api.py +6 -9
  110. intentkit/skills/defillama/base.py +11 -21
  111. intentkit/skills/defillama/coins/fetch_batch_historical_prices.py +8 -10
  112. intentkit/skills/defillama/coins/fetch_block.py +6 -8
  113. intentkit/skills/defillama/coins/fetch_current_prices.py +8 -10
  114. intentkit/skills/defillama/coins/fetch_first_price.py +7 -9
  115. intentkit/skills/defillama/coins/fetch_historical_prices.py +9 -11
  116. intentkit/skills/defillama/coins/fetch_price_chart.py +9 -11
  117. intentkit/skills/defillama/coins/fetch_price_percentage.py +7 -9
  118. intentkit/skills/defillama/config/chains.py +1 -3
  119. intentkit/skills/defillama/fees/fetch_fees_overview.py +24 -26
  120. intentkit/skills/defillama/schema.json +5 -1
  121. intentkit/skills/defillama/stablecoins/fetch_stablecoin_chains.py +16 -18
  122. intentkit/skills/defillama/stablecoins/fetch_stablecoin_charts.py +8 -10
  123. intentkit/skills/defillama/stablecoins/fetch_stablecoin_prices.py +5 -7
  124. intentkit/skills/defillama/stablecoins/fetch_stablecoins.py +7 -9
  125. intentkit/skills/defillama/tests/api_integration.test.py +1 -1
  126. intentkit/skills/defillama/tvl/fetch_chain_historical_tvl.py +4 -6
  127. intentkit/skills/defillama/tvl/fetch_chains.py +9 -11
  128. intentkit/skills/defillama/tvl/fetch_historical_tvl.py +4 -6
  129. intentkit/skills/defillama/tvl/fetch_protocol.py +32 -38
  130. intentkit/skills/defillama/tvl/fetch_protocol_current_tvl.py +3 -5
  131. intentkit/skills/defillama/tvl/fetch_protocols.py +37 -45
  132. intentkit/skills/defillama/volumes/fetch_dex_overview.py +42 -48
  133. intentkit/skills/defillama/volumes/fetch_dex_summary.py +35 -37
  134. intentkit/skills/defillama/volumes/fetch_options_overview.py +24 -28
  135. intentkit/skills/defillama/yields/fetch_pool_chart.py +10 -12
  136. intentkit/skills/defillama/yields/fetch_pools.py +26 -30
  137. intentkit/skills/dexscreener/README.md +154 -0
  138. intentkit/skills/dexscreener/__init__.py +97 -93
  139. intentkit/skills/dexscreener/base.py +125 -133
  140. intentkit/skills/dexscreener/get_pair_info.py +158 -0
  141. intentkit/skills/dexscreener/get_token_pairs.py +165 -0
  142. intentkit/skills/dexscreener/get_tokens_info.py +212 -0
  143. intentkit/skills/dexscreener/model/search_token_response.py +80 -82
  144. intentkit/skills/dexscreener/schema.json +91 -48
  145. intentkit/skills/dexscreener/search_token.py +182 -321
  146. intentkit/skills/dexscreener/utils.py +420 -0
  147. intentkit/skills/dune_analytics/__init__.py +7 -9
  148. intentkit/skills/dune_analytics/base.py +48 -52
  149. intentkit/skills/dune_analytics/fetch_kol_buys.py +5 -7
  150. intentkit/skills/dune_analytics/fetch_nation_metrics.py +6 -8
  151. intentkit/skills/dune_analytics/schema.json +104 -99
  152. intentkit/skills/elfa/__init__.py +5 -18
  153. intentkit/skills/elfa/base.py +10 -14
  154. intentkit/skills/elfa/mention.py +19 -21
  155. intentkit/skills/elfa/schema.json +3 -2
  156. intentkit/skills/elfa/stats.py +4 -4
  157. intentkit/skills/elfa/tokens.py +12 -12
  158. intentkit/skills/elfa/utils.py +26 -28
  159. intentkit/skills/enso/__init__.py +11 -31
  160. intentkit/skills/enso/base.py +50 -35
  161. intentkit/skills/enso/best_yield.py +16 -24
  162. intentkit/skills/enso/networks.py +6 -11
  163. intentkit/skills/enso/prices.py +11 -13
  164. intentkit/skills/enso/route.py +34 -38
  165. intentkit/skills/enso/schema.json +3 -2
  166. intentkit/skills/enso/tokens.py +29 -38
  167. intentkit/skills/enso/wallet.py +76 -191
  168. intentkit/skills/erc20/__init__.py +50 -0
  169. intentkit/skills/erc20/base.py +11 -0
  170. intentkit/skills/erc20/erc20.svg +5 -0
  171. intentkit/skills/erc20/schema.json +74 -0
  172. intentkit/skills/erc721/__init__.py +53 -0
  173. intentkit/skills/erc721/base.py +11 -0
  174. intentkit/skills/erc721/erc721.svg +5 -0
  175. intentkit/skills/erc721/schema.json +90 -0
  176. intentkit/skills/firecrawl/README.md +11 -5
  177. intentkit/skills/firecrawl/__init__.py +5 -18
  178. intentkit/skills/firecrawl/base.py +4 -11
  179. intentkit/skills/firecrawl/clear.py +4 -8
  180. intentkit/skills/firecrawl/crawl.py +19 -19
  181. intentkit/skills/firecrawl/query.py +4 -3
  182. intentkit/skills/firecrawl/schema.json +6 -8
  183. intentkit/skills/firecrawl/scrape.py +150 -40
  184. intentkit/skills/firecrawl/utils.py +50 -42
  185. intentkit/skills/github/__init__.py +2 -7
  186. intentkit/skills/github/base.py +1 -7
  187. intentkit/skills/github/github_search.py +1 -2
  188. intentkit/skills/github/schema.json +3 -4
  189. intentkit/skills/heurist/__init__.py +8 -27
  190. intentkit/skills/heurist/base.py +4 -9
  191. intentkit/skills/heurist/image_generation_animagine_xl.py +12 -13
  192. intentkit/skills/heurist/image_generation_arthemy_comics.py +12 -13
  193. intentkit/skills/heurist/image_generation_arthemy_real.py +12 -13
  194. intentkit/skills/heurist/image_generation_braindance.py +12 -13
  195. intentkit/skills/heurist/image_generation_cyber_realistic_xl.py +12 -13
  196. intentkit/skills/heurist/image_generation_flux_1_dev.py +12 -13
  197. intentkit/skills/heurist/image_generation_sdxl.py +12 -13
  198. intentkit/skills/heurist/schema.json +2 -2
  199. intentkit/skills/http/__init__.py +4 -15
  200. intentkit/skills/http/base.py +1 -7
  201. intentkit/skills/http/get.py +21 -16
  202. intentkit/skills/http/post.py +23 -18
  203. intentkit/skills/http/put.py +23 -18
  204. intentkit/skills/http/schema.json +4 -5
  205. intentkit/skills/lifi/__init__.py +8 -13
  206. intentkit/skills/lifi/base.py +1 -7
  207. intentkit/skills/lifi/schema.json +17 -8
  208. intentkit/skills/lifi/token_execute.py +36 -30
  209. intentkit/skills/lifi/token_quote.py +8 -10
  210. intentkit/skills/lifi/utils.py +104 -51
  211. intentkit/skills/moralis/__init__.py +6 -10
  212. intentkit/skills/moralis/api.py +6 -7
  213. intentkit/skills/moralis/base.py +5 -10
  214. intentkit/skills/moralis/fetch_chain_portfolio.py +10 -11
  215. intentkit/skills/moralis/fetch_nft_portfolio.py +22 -22
  216. intentkit/skills/moralis/fetch_solana_portfolio.py +11 -12
  217. intentkit/skills/moralis/fetch_wallet_portfolio.py +8 -9
  218. intentkit/skills/moralis/schema.json +7 -2
  219. intentkit/skills/morpho/__init__.py +52 -0
  220. intentkit/skills/morpho/base.py +11 -0
  221. intentkit/skills/morpho/morpho.svg +12 -0
  222. intentkit/skills/morpho/schema.json +73 -0
  223. intentkit/skills/nation/__init__.py +4 -9
  224. intentkit/skills/nation/base.py +5 -10
  225. intentkit/skills/nation/nft_check.py +3 -4
  226. intentkit/skills/nation/schema.json +4 -3
  227. intentkit/skills/onchain.py +23 -0
  228. intentkit/skills/openai/__init__.py +17 -18
  229. intentkit/skills/openai/base.py +10 -14
  230. intentkit/skills/openai/dalle_image_generation.py +3 -8
  231. intentkit/skills/openai/gpt_avatar_generator.py +102 -0
  232. intentkit/skills/openai/gpt_image_generation.py +4 -8
  233. intentkit/skills/openai/gpt_image_mini_generator.py +91 -0
  234. intentkit/skills/openai/gpt_image_to_image.py +4 -8
  235. intentkit/skills/openai/image_to_text.py +3 -7
  236. intentkit/skills/openai/schema.json +34 -3
  237. intentkit/skills/portfolio/__init__.py +11 -35
  238. intentkit/skills/portfolio/base.py +33 -19
  239. intentkit/skills/portfolio/schema.json +3 -5
  240. intentkit/skills/portfolio/token_balances.py +21 -21
  241. intentkit/skills/portfolio/wallet_approvals.py +17 -18
  242. intentkit/skills/portfolio/wallet_defi_positions.py +3 -3
  243. intentkit/skills/portfolio/wallet_history.py +31 -31
  244. intentkit/skills/portfolio/wallet_net_worth.py +13 -13
  245. intentkit/skills/portfolio/wallet_nfts.py +19 -19
  246. intentkit/skills/portfolio/wallet_profitability.py +18 -18
  247. intentkit/skills/portfolio/wallet_profitability_summary.py +5 -5
  248. intentkit/skills/portfolio/wallet_stats.py +3 -3
  249. intentkit/skills/portfolio/wallet_swaps.py +19 -19
  250. intentkit/skills/pyth/__init__.py +50 -0
  251. intentkit/skills/pyth/base.py +11 -0
  252. intentkit/skills/pyth/pyth.svg +6 -0
  253. intentkit/skills/pyth/schema.json +75 -0
  254. intentkit/skills/skills.toml +40 -0
  255. intentkit/skills/slack/__init__.py +5 -17
  256. intentkit/skills/slack/base.py +3 -9
  257. intentkit/skills/slack/get_channel.py +8 -8
  258. intentkit/skills/slack/get_message.py +9 -9
  259. intentkit/skills/slack/schedule_message.py +5 -5
  260. intentkit/skills/slack/schema.json +2 -2
  261. intentkit/skills/slack/send_message.py +3 -5
  262. intentkit/skills/supabase/__init__.py +7 -23
  263. intentkit/skills/supabase/base.py +9 -13
  264. intentkit/skills/supabase/delete_data.py +5 -6
  265. intentkit/skills/supabase/fetch_data.py +13 -14
  266. intentkit/skills/supabase/insert_data.py +5 -6
  267. intentkit/skills/supabase/invoke_function.py +7 -8
  268. intentkit/skills/supabase/schema.json +2 -3
  269. intentkit/skills/supabase/update_data.py +7 -8
  270. intentkit/skills/supabase/upsert_data.py +5 -6
  271. intentkit/skills/superfluid/__init__.py +53 -0
  272. intentkit/skills/superfluid/base.py +11 -0
  273. intentkit/skills/superfluid/schema.json +89 -0
  274. intentkit/skills/superfluid/superfluid.svg +6 -0
  275. intentkit/skills/system/__init__.py +7 -24
  276. intentkit/skills/system/add_autonomous_task.py +10 -12
  277. intentkit/skills/system/delete_autonomous_task.py +2 -2
  278. intentkit/skills/system/edit_autonomous_task.py +14 -18
  279. intentkit/skills/system/list_autonomous_tasks.py +3 -5
  280. intentkit/skills/system/read_agent_api_key.py +6 -4
  281. intentkit/skills/system/regenerate_agent_api_key.py +6 -4
  282. intentkit/skills/system/schema.json +6 -8
  283. intentkit/skills/tavily/__init__.py +3 -12
  284. intentkit/skills/tavily/base.py +4 -9
  285. intentkit/skills/tavily/schema.json +3 -5
  286. intentkit/skills/tavily/tavily_extract.py +2 -4
  287. intentkit/skills/tavily/tavily_search.py +4 -6
  288. intentkit/skills/token/__init__.py +5 -10
  289. intentkit/skills/token/base.py +7 -11
  290. intentkit/skills/token/erc20_transfers.py +19 -19
  291. intentkit/skills/token/schema.json +3 -6
  292. intentkit/skills/token/token_analytics.py +3 -3
  293. intentkit/skills/token/token_price.py +13 -13
  294. intentkit/skills/token/token_search.py +9 -9
  295. intentkit/skills/twitter/__init__.py +11 -35
  296. intentkit/skills/twitter/base.py +23 -35
  297. intentkit/skills/twitter/follow_user.py +3 -7
  298. intentkit/skills/twitter/get_mentions.py +6 -13
  299. intentkit/skills/twitter/get_timeline.py +5 -13
  300. intentkit/skills/twitter/get_user_by_username.py +3 -7
  301. intentkit/skills/twitter/get_user_tweets.py +6 -14
  302. intentkit/skills/twitter/like_tweet.py +3 -7
  303. intentkit/skills/twitter/post_tweet.py +23 -12
  304. intentkit/skills/twitter/reply_tweet.py +21 -12
  305. intentkit/skills/twitter/retweet.py +3 -7
  306. intentkit/skills/twitter/schema.json +1 -0
  307. intentkit/skills/twitter/search_tweets.py +5 -13
  308. intentkit/skills/unrealspeech/__init__.py +2 -7
  309. intentkit/skills/unrealspeech/base.py +2 -8
  310. intentkit/skills/unrealspeech/schema.json +2 -5
  311. intentkit/skills/unrealspeech/text_to_speech.py +8 -8
  312. intentkit/skills/venice_audio/__init__.py +98 -106
  313. intentkit/skills/venice_audio/base.py +117 -121
  314. intentkit/skills/venice_audio/input.py +41 -41
  315. intentkit/skills/venice_audio/schema.json +151 -152
  316. intentkit/skills/venice_audio/venice_audio.py +38 -21
  317. intentkit/skills/venice_image/__init__.py +147 -154
  318. intentkit/skills/venice_image/api.py +138 -138
  319. intentkit/skills/venice_image/base.py +185 -192
  320. intentkit/skills/venice_image/config.py +33 -35
  321. intentkit/skills/venice_image/image_enhance/image_enhance.py +2 -3
  322. intentkit/skills/venice_image/image_enhance/image_enhance_base.py +21 -23
  323. intentkit/skills/venice_image/image_enhance/image_enhance_input.py +38 -40
  324. intentkit/skills/venice_image/image_generation/image_generation_base.py +9 -9
  325. intentkit/skills/venice_image/image_generation/image_generation_fluently_xl.py +26 -26
  326. intentkit/skills/venice_image/image_generation/image_generation_flux_dev.py +27 -27
  327. intentkit/skills/venice_image/image_generation/image_generation_flux_dev_uncensored.py +26 -26
  328. intentkit/skills/venice_image/image_generation/image_generation_input.py +158 -158
  329. intentkit/skills/venice_image/image_generation/image_generation_lustify_sdxl.py +26 -26
  330. intentkit/skills/venice_image/image_generation/image_generation_pony_realism.py +26 -26
  331. intentkit/skills/venice_image/image_generation/image_generation_stable_diffusion_3_5.py +28 -28
  332. intentkit/skills/venice_image/image_generation/image_generation_venice_sd35.py +28 -28
  333. intentkit/skills/venice_image/image_upscale/image_upscale.py +3 -3
  334. intentkit/skills/venice_image/image_upscale/image_upscale_base.py +21 -23
  335. intentkit/skills/venice_image/image_upscale/image_upscale_input.py +22 -22
  336. intentkit/skills/venice_image/image_vision/image_vision.py +2 -2
  337. intentkit/skills/venice_image/image_vision/image_vision_base.py +17 -17
  338. intentkit/skills/venice_image/image_vision/image_vision_input.py +9 -9
  339. intentkit/skills/venice_image/schema.json +267 -267
  340. intentkit/skills/venice_image/utils.py +77 -78
  341. intentkit/skills/web_scraper/__init__.py +5 -18
  342. intentkit/skills/web_scraper/base.py +21 -7
  343. intentkit/skills/web_scraper/document_indexer.py +7 -6
  344. intentkit/skills/web_scraper/schema.json +2 -6
  345. intentkit/skills/web_scraper/scrape_and_index.py +15 -15
  346. intentkit/skills/web_scraper/utils.py +62 -63
  347. intentkit/skills/web_scraper/website_indexer.py +17 -19
  348. intentkit/skills/weth/__init__.py +49 -0
  349. intentkit/skills/weth/base.py +11 -0
  350. intentkit/skills/weth/schema.json +58 -0
  351. intentkit/skills/weth/weth.svg +6 -0
  352. intentkit/skills/wow/__init__.py +51 -0
  353. intentkit/skills/wow/base.py +11 -0
  354. intentkit/skills/wow/schema.json +89 -0
  355. intentkit/skills/wow/wow.svg +7 -0
  356. intentkit/skills/x402/__init__.py +61 -0
  357. intentkit/skills/x402/ask_agent.py +98 -0
  358. intentkit/skills/x402/base.py +99 -0
  359. intentkit/skills/x402/http_request.py +117 -0
  360. intentkit/skills/x402/schema.json +45 -0
  361. intentkit/skills/x402/x402.webp +0 -0
  362. intentkit/skills/xmtp/__init__.py +4 -15
  363. intentkit/skills/xmtp/base.py +61 -2
  364. intentkit/skills/xmtp/price.py +18 -13
  365. intentkit/skills/xmtp/schema.json +69 -71
  366. intentkit/skills/xmtp/swap.py +22 -25
  367. intentkit/skills/xmtp/transfer.py +71 -32
  368. intentkit/utils/chain.py +3 -3
  369. intentkit/utils/error.py +14 -1
  370. intentkit/utils/logging.py +2 -4
  371. intentkit/utils/s3.py +59 -7
  372. intentkit/utils/schema.py +100 -0
  373. intentkit/utils/slack_alert.py +7 -8
  374. {intentkit-0.6.13.dev2.dist-info → intentkit-0.8.17.dist-info}/METADATA +14 -16
  375. intentkit-0.8.17.dist-info/RECORD +466 -0
  376. intentkit/abstracts/exception.py +0 -9
  377. intentkit/core/skill.py +0 -200
  378. intentkit/models/generator.py +0 -347
  379. intentkit/skills/cdp/get_balance.py +0 -110
  380. intentkit/skills/cdp/swap.py +0 -121
  381. intentkit/skills/moralis/tests/__init__.py +0 -0
  382. intentkit/skills/moralis/tests/test_wallet.py +0 -511
  383. intentkit-0.6.13.dev2.dist-info/RECORD +0 -409
  384. {intentkit-0.6.13.dev2.dist-info → intentkit-0.8.17.dist-info}/WHEEL +0 -0
  385. {intentkit-0.6.13.dev2.dist-info → intentkit-0.8.17.dist-info}/licenses/LICENSE +0 -0
@@ -1,14 +1,15 @@
1
1
  import logging
2
- from datetime import datetime, timezone
2
+ from datetime import UTC, datetime
3
3
  from decimal import ROUND_HALF_UP, Decimal
4
4
  from enum import Enum
5
- from typing import Annotated, Any, Dict, List, Optional, Tuple
5
+ from typing import Annotated, Any
6
6
 
7
7
  from epyxid import XID
8
8
  from fastapi import HTTPException
9
9
  from intentkit.models.app_setting import AppSetting
10
10
  from intentkit.models.base import Base
11
11
  from intentkit.models.db import get_session
12
+ from intentkit.utils.error import IntentKitAPIError
12
13
  from pydantic import BaseModel, ConfigDict, Field, field_validator
13
14
  from sqlalchemy import (
14
15
  ARRAY,
@@ -26,6 +27,9 @@ from sqlalchemy.ext.asyncio import AsyncSession
26
27
 
27
28
  logger = logging.getLogger(__name__)
28
29
 
30
+ # Precision constant for 4 decimal places
31
+ FOURPLACES = Decimal("0.0001")
32
+
29
33
 
30
34
  class CreditType(str, Enum):
31
35
  """Credit type is used in db column names, do not change it."""
@@ -57,6 +61,7 @@ DEFAULT_PLATFORM_ACCOUNT_VOICE = "platform_voice"
57
61
  DEFAULT_PLATFORM_ACCOUNT_KNOWLEDGE = "platform_knowledge"
58
62
  DEFAULT_PLATFORM_ACCOUNT_FEE = "platform_fee"
59
63
  DEFAULT_PLATFORM_ACCOUNT_DEV = "platform_dev"
64
+ DEFAULT_PLATFORM_ACCOUNT_WITHDRAW = "platform_withdraw"
60
65
 
61
66
 
62
67
  class CreditAccountTable(Base):
@@ -114,6 +119,47 @@ class CreditAccountTable(Base):
114
119
  String,
115
120
  nullable=True,
116
121
  )
122
+ # Total statistics fields
123
+ total_income = Column(
124
+ Numeric(22, 4),
125
+ default=0,
126
+ nullable=False,
127
+ )
128
+ total_free_income = Column(
129
+ Numeric(22, 4),
130
+ default=0,
131
+ nullable=False,
132
+ )
133
+ total_reward_income = Column(
134
+ Numeric(22, 4),
135
+ default=0,
136
+ nullable=False,
137
+ )
138
+ total_permanent_income = Column(
139
+ Numeric(22, 4),
140
+ default=0,
141
+ nullable=False,
142
+ )
143
+ total_expense = Column(
144
+ Numeric(22, 4),
145
+ default=0,
146
+ nullable=False,
147
+ )
148
+ total_free_expense = Column(
149
+ Numeric(22, 4),
150
+ default=0,
151
+ nullable=False,
152
+ )
153
+ total_reward_expense = Column(
154
+ Numeric(22, 4),
155
+ default=0,
156
+ nullable=False,
157
+ )
158
+ total_permanent_expense = Column(
159
+ Numeric(22, 4),
160
+ default=0,
161
+ nullable=False,
162
+ )
117
163
  created_at = Column(
118
164
  DateTime(timezone=True),
119
165
  nullable=False,
@@ -123,7 +169,7 @@ class CreditAccountTable(Base):
123
169
  DateTime(timezone=True),
124
170
  nullable=False,
125
171
  server_default=func.now(),
126
- onupdate=lambda: datetime.now(timezone.utc),
172
+ onupdate=lambda: datetime.now(UTC),
127
173
  )
128
174
 
129
175
 
@@ -175,17 +221,74 @@ class CreditAccount(BaseModel):
175
221
  Field(default=Decimal("0"), description="Credits added through top-ups"),
176
222
  ]
177
223
  income_at: Annotated[
178
- Optional[datetime],
224
+ datetime | None,
179
225
  Field(None, description="Timestamp of the last income transaction"),
180
226
  ]
181
227
  expense_at: Annotated[
182
- Optional[datetime],
228
+ datetime | None,
183
229
  Field(None, description="Timestamp of the last expense transaction"),
184
230
  ]
185
231
  last_event_id: Annotated[
186
- Optional[str],
232
+ str | None,
187
233
  Field(None, description="ID of the last event that modified this account"),
188
234
  ]
235
+ # Total statistics fields
236
+ total_income: Annotated[
237
+ Decimal,
238
+ Field(
239
+ default=Decimal("0"),
240
+ description="Total income from all credit transactions",
241
+ ),
242
+ ]
243
+ total_free_income: Annotated[
244
+ Decimal,
245
+ Field(
246
+ default=Decimal("0"),
247
+ description="Total income from free credit transactions",
248
+ ),
249
+ ]
250
+ total_reward_income: Annotated[
251
+ Decimal,
252
+ Field(
253
+ default=Decimal("0"),
254
+ description="Total income from reward credit transactions",
255
+ ),
256
+ ]
257
+ total_permanent_income: Annotated[
258
+ Decimal,
259
+ Field(
260
+ default=Decimal("0"),
261
+ description="Total income from permanent credit transactions",
262
+ ),
263
+ ]
264
+ total_expense: Annotated[
265
+ Decimal,
266
+ Field(
267
+ default=Decimal("0"),
268
+ description="Total expense from all credit transactions",
269
+ ),
270
+ ]
271
+ total_free_expense: Annotated[
272
+ Decimal,
273
+ Field(
274
+ default=Decimal("0"),
275
+ description="Total expense from free credit transactions",
276
+ ),
277
+ ]
278
+ total_reward_expense: Annotated[
279
+ Decimal,
280
+ Field(
281
+ default=Decimal("0"),
282
+ description="Total expense from reward credit transactions",
283
+ ),
284
+ ]
285
+ total_permanent_expense: Annotated[
286
+ Decimal,
287
+ Field(
288
+ default=Decimal("0"),
289
+ description="Total expense from permanent credit transactions",
290
+ ),
291
+ ]
189
292
  created_at: Annotated[
190
293
  datetime, Field(description="Timestamp when this account was created")
191
294
  ]
@@ -194,14 +297,26 @@ class CreditAccount(BaseModel):
194
297
  ]
195
298
 
196
299
  @field_validator(
197
- "free_quota", "refill_amount", "free_credits", "reward_credits", "credits"
300
+ "free_quota",
301
+ "refill_amount",
302
+ "free_credits",
303
+ "reward_credits",
304
+ "credits",
305
+ "total_income",
306
+ "total_free_income",
307
+ "total_reward_income",
308
+ "total_permanent_income",
309
+ "total_expense",
310
+ "total_free_expense",
311
+ "total_reward_expense",
312
+ "total_permanent_expense",
198
313
  )
199
314
  @classmethod
200
315
  def round_decimal(cls, v: Any) -> Decimal:
201
316
  """Round decimal values to 4 decimal places."""
202
317
  if isinstance(v, Decimal):
203
318
  return v.quantize(Decimal("0.0001"), rounding=ROUND_HALF_UP)
204
- elif isinstance(v, (int, float)):
319
+ elif isinstance(v, int | float):
205
320
  return Decimal(str(v)).quantize(Decimal("0.0001"), rounding=ROUND_HALF_UP)
206
321
  return v
207
322
 
@@ -233,7 +348,11 @@ class CreditAccount(BaseModel):
233
348
  )
234
349
  result = await session.scalar(stmt)
235
350
  if not result:
236
- raise HTTPException(status_code=404, detail="Credit account not found")
351
+ raise IntentKitAPIError(
352
+ status_code=404,
353
+ key="CreditAccountNotFound",
354
+ message="Credit account not found",
355
+ )
237
356
  return cls.model_validate(result)
238
357
 
239
358
  @classmethod
@@ -294,19 +413,38 @@ class CreditAccount(BaseModel):
294
413
  owner_id: str,
295
414
  credit_type: CreditType,
296
415
  amount: Decimal,
297
- event_id: Optional[str] = None,
416
+ event_id: str | None = None,
298
417
  ) -> "CreditAccount":
299
418
  """Deduct credits from an account. Not checking balance"""
300
419
  # check first, create if not exists
301
420
  await cls.get_or_create_in_session(session, owner_type, owner_id)
302
421
 
422
+ # Quantize the amount to ensure proper precision
423
+ quantized_amount = amount.quantize(FOURPLACES, rounding=ROUND_HALF_UP)
303
424
  values_dict = {
304
- credit_type.value: getattr(CreditAccountTable, credit_type.value) - amount,
305
- "expense_at": datetime.now(timezone.utc),
425
+ credit_type.value: getattr(CreditAccountTable, credit_type.value)
426
+ - quantized_amount,
427
+ "expense_at": datetime.now(UTC),
428
+ # Update total expense statistics
429
+ "total_expense": CreditAccountTable.total_expense + quantized_amount,
306
430
  }
307
431
  if event_id:
308
432
  values_dict["last_event_id"] = event_id
309
433
 
434
+ # Update corresponding statistics fields based on credit type
435
+ if credit_type == CreditType.FREE:
436
+ values_dict["total_free_expense"] = (
437
+ CreditAccountTable.total_free_expense + quantized_amount
438
+ )
439
+ elif credit_type == CreditType.REWARD:
440
+ values_dict["total_reward_expense"] = (
441
+ CreditAccountTable.total_reward_expense + quantized_amount
442
+ )
443
+ elif credit_type == CreditType.PERMANENT:
444
+ values_dict["total_permanent_expense"] = (
445
+ CreditAccountTable.total_permanent_expense + quantized_amount
446
+ )
447
+
310
448
  stmt = (
311
449
  update(CreditAccountTable)
312
450
  .where(
@@ -318,7 +456,11 @@ class CreditAccount(BaseModel):
318
456
  )
319
457
  res = await session.scalar(stmt)
320
458
  if not res:
321
- raise HTTPException(status_code=500, detail="Failed to expense credits")
459
+ raise IntentKitAPIError(
460
+ status_code=500,
461
+ key="CreditExpenseFailed",
462
+ message="Failed to expense credits",
463
+ )
322
464
  return cls.model_validate(res)
323
465
 
324
466
  @classmethod
@@ -328,8 +470,8 @@ class CreditAccount(BaseModel):
328
470
  owner_type: OwnerType,
329
471
  owner_id: str,
330
472
  amount: Decimal,
331
- event_id: Optional[str] = None,
332
- ) -> Tuple["CreditAccount", Dict[CreditType, Decimal]]:
473
+ event_id: str | None = None,
474
+ ) -> tuple["CreditAccount", dict[CreditType, Decimal]]:
333
475
  """Expense credits and return account and credit type.
334
476
  We are not checking balance here, since a conversation may have
335
477
  multiple expenses, we can't interrupt the conversation.
@@ -348,31 +490,62 @@ class CreditAccount(BaseModel):
348
490
  else:
349
491
  if account.free_credits > 0:
350
492
  details[CreditType.FREE] = account.free_credits
351
- amount_left -= account.free_credits
493
+ amount_left = (amount_left - account.free_credits).quantize(
494
+ FOURPLACES, rounding=ROUND_HALF_UP
495
+ )
352
496
  if amount_left <= account.reward_credits:
353
497
  details[CreditType.REWARD] = amount_left
354
498
  amount_left = Decimal("0")
355
499
  else:
356
500
  if account.reward_credits > 0:
357
501
  details[CreditType.REWARD] = account.reward_credits
358
- amount_left -= account.reward_credits
502
+ amount_left = (amount_left - account.reward_credits).quantize(
503
+ FOURPLACES, rounding=ROUND_HALF_UP
504
+ )
359
505
  details[CreditType.PERMANENT] = amount_left
360
506
 
361
507
  # Create values dict based on what's in details, defaulting to 0 for missing keys
362
508
  values_dict = {
363
- "expense_at": datetime.now(timezone.utc),
509
+ "expense_at": datetime.now(UTC),
364
510
  }
365
511
  if event_id:
366
512
  values_dict["last_event_id"] = event_id
367
513
 
514
+ # Calculate total expense for statistics
515
+ total_expense_amount = Decimal("0")
516
+
368
517
  # Add credit type values only if they exist in details
369
518
  for credit_type in [CreditType.FREE, CreditType.REWARD, CreditType.PERMANENT]:
370
519
  if credit_type in details:
520
+ # Quantize the amount to ensure proper precision
521
+ quantized_amount = details[credit_type].quantize(
522
+ FOURPLACES, rounding=ROUND_HALF_UP
523
+ )
371
524
  values_dict[credit_type.value] = (
372
- getattr(CreditAccountTable, credit_type.value)
373
- - details[credit_type]
525
+ getattr(CreditAccountTable, credit_type.value) - quantized_amount
374
526
  )
375
527
 
528
+ # Update corresponding statistics fields
529
+ total_expense_amount += quantized_amount
530
+ if credit_type == CreditType.FREE:
531
+ values_dict["total_free_expense"] = (
532
+ CreditAccountTable.total_free_expense + quantized_amount
533
+ )
534
+ elif credit_type == CreditType.REWARD:
535
+ values_dict["total_reward_expense"] = (
536
+ CreditAccountTable.total_reward_expense + quantized_amount
537
+ )
538
+ elif credit_type == CreditType.PERMANENT:
539
+ values_dict["total_permanent_expense"] = (
540
+ CreditAccountTable.total_permanent_expense + quantized_amount
541
+ )
542
+
543
+ # Update total expense if there was any expense
544
+ if total_expense_amount > 0:
545
+ values_dict["total_expense"] = (
546
+ CreditAccountTable.total_expense + total_expense_amount
547
+ )
548
+
376
549
  stmt = (
377
550
  update(CreditAccountTable)
378
551
  .where(
@@ -384,7 +557,11 @@ class CreditAccount(BaseModel):
384
557
  )
385
558
  res = await session.scalar(stmt)
386
559
  if not res:
387
- raise HTTPException(status_code=500, detail="Failed to expense credits")
560
+ raise IntentKitAPIError(
561
+ status_code=500,
562
+ key="CreditExpenseFailed",
563
+ message="Failed to expense credits",
564
+ )
388
565
  return cls.model_validate(res), details
389
566
 
390
567
  def has_sufficient_credits(self, amount: Decimal) -> bool:
@@ -404,20 +581,51 @@ class CreditAccount(BaseModel):
404
581
  session: AsyncSession,
405
582
  owner_type: OwnerType,
406
583
  owner_id: str,
407
- amount: Decimal,
408
- credit_type: CreditType,
409
- event_id: Optional[str] = None,
584
+ amount_details: dict[CreditType, Decimal],
585
+ event_id: str | None = None,
410
586
  ) -> "CreditAccount":
411
587
  # check first, create if not exists
412
588
  await cls.get_or_create_in_session(session, owner_type, owner_id)
413
589
  # income
414
590
  values_dict = {
415
- credit_type.value: getattr(CreditAccountTable, credit_type.value) + amount,
416
- "income_at": datetime.now(timezone.utc),
591
+ "income_at": datetime.now(UTC),
417
592
  }
418
593
  if event_id:
419
594
  values_dict["last_event_id"] = event_id
420
595
 
596
+ # Calculate total income for statistics
597
+ total_income_amount = Decimal("0")
598
+
599
+ # Add credit type values based on amount_details
600
+ for credit_type, amount in amount_details.items():
601
+ if amount > 0:
602
+ # Quantize the amount to ensure 4 decimal places precision
603
+ quantized_amount = amount.quantize(FOURPLACES, rounding=ROUND_HALF_UP)
604
+ values_dict[credit_type.value] = (
605
+ getattr(CreditAccountTable, credit_type.value) + quantized_amount
606
+ )
607
+
608
+ # Update corresponding statistics fields
609
+ total_income_amount += quantized_amount
610
+ if credit_type == CreditType.FREE:
611
+ values_dict["total_free_income"] = (
612
+ CreditAccountTable.total_free_income + quantized_amount
613
+ )
614
+ elif credit_type == CreditType.REWARD:
615
+ values_dict["total_reward_income"] = (
616
+ CreditAccountTable.total_reward_income + quantized_amount
617
+ )
618
+ elif credit_type == CreditType.PERMANENT:
619
+ values_dict["total_permanent_income"] = (
620
+ CreditAccountTable.total_permanent_income + quantized_amount
621
+ )
622
+
623
+ # Update total income if there was any income
624
+ if total_income_amount > 0:
625
+ values_dict["total_income"] = (
626
+ CreditAccountTable.total_income + total_income_amount
627
+ )
628
+
421
629
  stmt = (
422
630
  update(CreditAccountTable)
423
631
  .where(
@@ -438,8 +646,8 @@ class CreditAccount(BaseModel):
438
646
  session: AsyncSession,
439
647
  owner_type: OwnerType,
440
648
  owner_id: str,
441
- free_quota: Optional[Decimal] = None,
442
- refill_amount: Optional[Decimal] = None,
649
+ free_quota: Decimal | None = None,
650
+ refill_amount: Decimal | None = None,
443
651
  ) -> "CreditAccount":
444
652
  """Get an existing credit account or create a new one if it doesn't exist.
445
653
 
@@ -479,9 +687,19 @@ class CreditAccount(BaseModel):
479
687
  free_credits=free_quota,
480
688
  reward_credits=0.0,
481
689
  credits=0.0,
482
- income_at=datetime.now(timezone.utc),
690
+ income_at=datetime.now(UTC),
483
691
  expense_at=None,
484
692
  last_event_id=event_id if owner_type == OwnerType.USER else None,
693
+ # Initialize new statistics fields
694
+ # For USER accounts, initial free_quota counts as income
695
+ total_income=free_quota,
696
+ total_free_income=free_quota,
697
+ total_reward_income=0.0,
698
+ total_permanent_income=0.0,
699
+ total_expense=0.0,
700
+ total_free_expense=0.0,
701
+ total_reward_expense=0.0,
702
+ total_permanent_expense=0.0,
485
703
  )
486
704
  # Platform virtual accounts have fixed IDs, same as owner_id
487
705
  if owner_type == OwnerType.PLATFORM:
@@ -515,6 +733,7 @@ class CreditAccount(BaseModel):
515
733
  balance_after=free_quota,
516
734
  base_amount=free_quota,
517
735
  base_original_amount=free_quota,
736
+ base_free_amount=free_quota,
518
737
  free_amount=free_quota, # Set free_amount since this is a free credit refill
519
738
  reward_amount=Decimal("0"), # No reward credits involved
520
739
  permanent_amount=Decimal("0"), # No permanent credits involved
@@ -530,10 +749,13 @@ class CreditAccount(BaseModel):
530
749
  id=str(XID()),
531
750
  account_id=account.id,
532
751
  event_id=event_id,
533
- tx_type=TransactionType.RECHARGE,
752
+ tx_type=TransactionType.REFILL,
534
753
  credit_debit=CreditDebit.CREDIT,
535
754
  change_amount=free_quota,
536
755
  credit_type=CreditType.FREE,
756
+ free_amount=free_quota,
757
+ reward_amount=Decimal("0"),
758
+ permanent_amount=Decimal("0"),
537
759
  )
538
760
  session.add(user_tx)
539
761
 
@@ -546,6 +768,9 @@ class CreditAccount(BaseModel):
546
768
  credit_debit=CreditDebit.DEBIT,
547
769
  change_amount=free_quota,
548
770
  credit_type=CreditType.FREE,
771
+ free_amount=free_quota,
772
+ reward_amount=Decimal("0"),
773
+ permanent_amount=Decimal("0"),
549
774
  )
550
775
  session.add(platform_tx)
551
776
 
@@ -556,8 +781,8 @@ class CreditAccount(BaseModel):
556
781
  cls,
557
782
  session: AsyncSession,
558
783
  user_id: str,
559
- free_quota: Optional[Decimal] = None,
560
- refill_amount: Optional[Decimal] = None,
784
+ free_quota: Decimal | None = None,
785
+ refill_amount: Decimal | None = None,
561
786
  upstream_tx_id: str = "",
562
787
  note: str = "",
563
788
  ) -> "CreditAccount":
@@ -609,6 +834,10 @@ class CreditAccount(BaseModel):
609
834
  if not note:
610
835
  raise ValueError("Quota update requires a note explaining the reason")
611
836
 
837
+ # Quantize values to ensure proper precision (4 decimal places)
838
+ free_quota = free_quota.quantize(FOURPLACES, rounding=ROUND_HALF_UP)
839
+ refill_amount = refill_amount.quantize(FOURPLACES, rounding=ROUND_HALF_UP)
840
+
612
841
  # Update the free_quota field
613
842
  stmt = (
614
843
  update(CreditAccountTable)
@@ -650,6 +879,7 @@ class EventType(str, Enum):
650
879
  REFUND = "refund"
651
880
  ADJUSTMENT = "adjustment"
652
881
  REFILL = "refill"
882
+ WITHDRAW = "withdraw"
653
883
  # Sync with RewardType values
654
884
  REWARD = "reward"
655
885
  EVENT_REWARD = "event_reward"
@@ -794,6 +1024,21 @@ class CreditEventTable(Base):
794
1024
  default=0,
795
1025
  nullable=True,
796
1026
  )
1027
+ base_free_amount = Column(
1028
+ Numeric(22, 4),
1029
+ default=0,
1030
+ nullable=True,
1031
+ )
1032
+ base_reward_amount = Column(
1033
+ Numeric(22, 4),
1034
+ default=0,
1035
+ nullable=True,
1036
+ )
1037
+ base_permanent_amount = Column(
1038
+ Numeric(22, 4),
1039
+ default=0,
1040
+ nullable=True,
1041
+ )
797
1042
  fee_platform_amount = Column(
798
1043
  Numeric(22, 4),
799
1044
  default=0,
@@ -902,34 +1147,34 @@ class CreditEvent(BaseModel):
902
1147
  ]
903
1148
  event_type: Annotated[EventType, Field(description="Type of the event")]
904
1149
  user_id: Annotated[
905
- Optional[str], Field(None, description="ID of the user if applicable")
1150
+ str | None, Field(None, description="ID of the user if applicable")
906
1151
  ]
907
1152
  upstream_type: Annotated[
908
1153
  UpstreamType, Field(description="Type of upstream transaction")
909
1154
  ]
910
1155
  upstream_tx_id: Annotated[str, Field(description="Upstream transaction ID if any")]
911
1156
  agent_id: Annotated[
912
- Optional[str], Field(None, description="ID of the agent if applicable")
1157
+ str | None, Field(None, description="ID of the agent if applicable")
913
1158
  ]
914
1159
  agent_wallet_address: Annotated[
915
- Optional[str],
1160
+ str | None,
916
1161
  Field(None, description="Wallet address of the agent if applicable"),
917
1162
  ]
918
1163
  start_message_id: Annotated[
919
- Optional[str],
1164
+ str | None,
920
1165
  Field(None, description="ID of the starting message if applicable"),
921
1166
  ]
922
1167
  message_id: Annotated[
923
- Optional[str], Field(None, description="ID of the message if applicable")
1168
+ str | None, Field(None, description="ID of the message if applicable")
924
1169
  ]
925
1170
  model: Annotated[
926
- Optional[str], Field(None, description="LLM model used if applicable")
1171
+ str | None, Field(None, description="LLM model used if applicable")
927
1172
  ]
928
1173
  skill_call_id: Annotated[
929
- Optional[str], Field(None, description="ID of the skill call if applicable")
1174
+ str | None, Field(None, description="ID of the skill call if applicable")
930
1175
  ]
931
1176
  skill_name: Annotated[
932
- Optional[str], Field(None, description="Name of the skill if applicable")
1177
+ str | None, Field(None, description="Name of the skill if applicable")
933
1178
  ]
934
1179
  direction: Annotated[Direction, Field(description="Direction of the credit flow")]
935
1180
  total_amount: Annotated[
@@ -941,11 +1186,11 @@ class CreditEvent(BaseModel):
941
1186
  ]
942
1187
  credit_type: Annotated[CreditType, Field(description="Type of credits involved")]
943
1188
  credit_types: Annotated[
944
- Optional[List[CreditType]],
1189
+ list[CreditType] | None,
945
1190
  Field(default=None, description="Array of credit types involved"),
946
1191
  ]
947
1192
  balance_after: Annotated[
948
- Optional[Decimal],
1193
+ Decimal | None,
949
1194
  Field(None, description="Account total balance after the transaction"),
950
1195
  ]
951
1196
  base_amount: Annotated[
@@ -953,103 +1198,115 @@ class CreditEvent(BaseModel):
953
1198
  Field(default=Decimal("0"), description="Base amount of credits involved"),
954
1199
  ]
955
1200
  base_discount_amount: Annotated[
956
- Optional[Decimal],
1201
+ Decimal | None,
957
1202
  Field(default=Decimal("0"), description="Base discount amount"),
958
1203
  ]
959
1204
  base_original_amount: Annotated[
960
- Optional[Decimal],
1205
+ Decimal | None,
961
1206
  Field(default=Decimal("0"), description="Base original amount"),
962
1207
  ]
963
1208
  base_llm_amount: Annotated[
964
- Optional[Decimal],
1209
+ Decimal | None,
965
1210
  Field(default=Decimal("0"), description="Base LLM cost amount"),
966
1211
  ]
967
1212
  base_skill_amount: Annotated[
968
- Optional[Decimal],
1213
+ Decimal | None,
969
1214
  Field(default=Decimal("0"), description="Base skill cost amount"),
970
1215
  ]
1216
+ base_free_amount: Annotated[
1217
+ Decimal | None,
1218
+ Field(default=Decimal("0"), description="Base free credit amount"),
1219
+ ]
1220
+ base_reward_amount: Annotated[
1221
+ Decimal | None,
1222
+ Field(default=Decimal("0"), description="Base reward credit amount"),
1223
+ ]
1224
+ base_permanent_amount: Annotated[
1225
+ Decimal | None,
1226
+ Field(default=Decimal("0"), description="Base permanent credit amount"),
1227
+ ]
971
1228
  fee_platform_amount: Annotated[
972
- Optional[Decimal],
1229
+ Decimal | None,
973
1230
  Field(default=Decimal("0"), description="Platform fee amount"),
974
1231
  ]
975
1232
  fee_platform_free_amount: Annotated[
976
- Optional[Decimal],
1233
+ Decimal | None,
977
1234
  Field(
978
1235
  default=Decimal("0"), description="Platform fee amount from free credits"
979
1236
  ),
980
1237
  ]
981
1238
  fee_platform_reward_amount: Annotated[
982
- Optional[Decimal],
1239
+ Decimal | None,
983
1240
  Field(
984
1241
  default=Decimal("0"), description="Platform fee amount from reward credits"
985
1242
  ),
986
1243
  ]
987
1244
  fee_platform_permanent_amount: Annotated[
988
- Optional[Decimal],
1245
+ Decimal | None,
989
1246
  Field(
990
1247
  default=Decimal("0"),
991
1248
  description="Platform fee amount from permanent credits",
992
1249
  ),
993
1250
  ]
994
1251
  fee_dev_account: Annotated[
995
- Optional[str], Field(None, description="Developer account ID receiving fee")
1252
+ str | None, Field(None, description="Developer account ID receiving fee")
996
1253
  ]
997
1254
  fee_dev_amount: Annotated[
998
- Optional[Decimal],
1255
+ Decimal | None,
999
1256
  Field(default=Decimal("0"), description="Developer fee amount"),
1000
1257
  ]
1001
1258
  fee_dev_free_amount: Annotated[
1002
- Optional[Decimal],
1259
+ Decimal | None,
1003
1260
  Field(
1004
1261
  default=Decimal("0"), description="Developer fee amount from free credits"
1005
1262
  ),
1006
1263
  ]
1007
1264
  fee_dev_reward_amount: Annotated[
1008
- Optional[Decimal],
1265
+ Decimal | None,
1009
1266
  Field(
1010
1267
  default=Decimal("0"), description="Developer fee amount from reward credits"
1011
1268
  ),
1012
1269
  ]
1013
1270
  fee_dev_permanent_amount: Annotated[
1014
- Optional[Decimal],
1271
+ Decimal | None,
1015
1272
  Field(
1016
1273
  default=Decimal("0"),
1017
1274
  description="Developer fee amount from permanent credits",
1018
1275
  ),
1019
1276
  ]
1020
1277
  fee_agent_account: Annotated[
1021
- Optional[str], Field(None, description="Agent account ID receiving fee")
1278
+ str | None, Field(None, description="Agent account ID receiving fee")
1022
1279
  ]
1023
1280
  fee_agent_amount: Annotated[
1024
- Optional[Decimal], Field(default=Decimal("0"), description="Agent fee amount")
1281
+ Decimal | None, Field(default=Decimal("0"), description="Agent fee amount")
1025
1282
  ]
1026
1283
  fee_agent_free_amount: Annotated[
1027
- Optional[Decimal],
1284
+ Decimal | None,
1028
1285
  Field(default=Decimal("0"), description="Agent fee amount from free credits"),
1029
1286
  ]
1030
1287
  fee_agent_reward_amount: Annotated[
1031
- Optional[Decimal],
1288
+ Decimal | None,
1032
1289
  Field(default=Decimal("0"), description="Agent fee amount from reward credits"),
1033
1290
  ]
1034
1291
  fee_agent_permanent_amount: Annotated[
1035
- Optional[Decimal],
1292
+ Decimal | None,
1036
1293
  Field(
1037
1294
  default=Decimal("0"), description="Agent fee amount from permanent credits"
1038
1295
  ),
1039
1296
  ]
1040
1297
  free_amount: Annotated[
1041
- Optional[Decimal],
1298
+ Decimal | None,
1042
1299
  Field(default=Decimal("0"), description="Free credit amount involved"),
1043
1300
  ]
1044
1301
  reward_amount: Annotated[
1045
- Optional[Decimal],
1302
+ Decimal | None,
1046
1303
  Field(default=Decimal("0"), description="Reward credit amount involved"),
1047
1304
  ]
1048
1305
  permanent_amount: Annotated[
1049
- Optional[Decimal],
1306
+ Decimal | None,
1050
1307
  Field(default=Decimal("0"), description="Permanent credit amount involved"),
1051
1308
  ]
1052
- note: Annotated[Optional[str], Field(None, description="Additional notes")]
1309
+ note: Annotated[str | None, Field(None, description="Additional notes")]
1053
1310
  created_at: Annotated[
1054
1311
  datetime, Field(description="Timestamp when this event was created")
1055
1312
  ]
@@ -1062,6 +1319,9 @@ class CreditEvent(BaseModel):
1062
1319
  "base_original_amount",
1063
1320
  "base_llm_amount",
1064
1321
  "base_skill_amount",
1322
+ "base_free_amount",
1323
+ "base_reward_amount",
1324
+ "base_permanent_amount",
1065
1325
  "fee_platform_amount",
1066
1326
  "fee_platform_free_amount",
1067
1327
  "fee_platform_reward_amount",
@@ -1079,13 +1339,13 @@ class CreditEvent(BaseModel):
1079
1339
  "permanent_amount",
1080
1340
  )
1081
1341
  @classmethod
1082
- def round_decimal(cls, v: Any) -> Optional[Decimal]:
1342
+ def round_decimal(cls, v: Any) -> Decimal | None:
1083
1343
  """Round decimal values to 4 decimal places."""
1084
1344
  if v is None:
1085
1345
  return None
1086
1346
  if isinstance(v, Decimal):
1087
1347
  return v.quantize(Decimal("0.0001"), rounding=ROUND_HALF_UP)
1088
- elif isinstance(v, (int, float)):
1348
+ elif isinstance(v, int | float):
1089
1349
  return Decimal(str(v)).quantize(Decimal("0.0001"), rounding=ROUND_HALF_UP)
1090
1350
  return v
1091
1351
 
@@ -1133,6 +1393,7 @@ class TransactionType(str, Enum):
1133
1393
  REFUND = "refund"
1134
1394
  ADJUSTMENT = "adjustment"
1135
1395
  REFILL = "refill"
1396
+ WITHDRAW = "withdraw"
1136
1397
  # Sync with RewardType values
1137
1398
  REWARD = "reward"
1138
1399
  EVENT_REWARD = "event_reward"
@@ -1183,6 +1444,21 @@ class CreditTransactionTable(Base):
1183
1444
  default=0,
1184
1445
  nullable=False,
1185
1446
  )
1447
+ free_amount = Column(
1448
+ Numeric(22, 4),
1449
+ default=0,
1450
+ nullable=False,
1451
+ )
1452
+ reward_amount = Column(
1453
+ Numeric(22, 4),
1454
+ default=0,
1455
+ nullable=False,
1456
+ )
1457
+ permanent_amount = Column(
1458
+ Numeric(22, 4),
1459
+ default=0,
1460
+ nullable=False,
1461
+ )
1186
1462
  credit_type = Column(
1187
1463
  String,
1188
1464
  nullable=False,
@@ -1223,14 +1499,28 @@ class CreditTransaction(BaseModel):
1223
1499
  change_amount: Annotated[
1224
1500
  Decimal, Field(default=Decimal("0"), description="Amount of credits changed")
1225
1501
  ]
1502
+ free_amount: Annotated[
1503
+ Decimal,
1504
+ Field(default=Decimal("0"), description="Amount of free credits changed"),
1505
+ ]
1506
+ reward_amount: Annotated[
1507
+ Decimal,
1508
+ Field(default=Decimal("0"), description="Amount of reward credits changed"),
1509
+ ]
1510
+ permanent_amount: Annotated[
1511
+ Decimal,
1512
+ Field(default=Decimal("0"), description="Amount of permanent credits changed"),
1513
+ ]
1226
1514
 
1227
- @field_validator("change_amount")
1515
+ @field_validator(
1516
+ "change_amount", "free_amount", "reward_amount", "permanent_amount"
1517
+ )
1228
1518
  @classmethod
1229
1519
  def round_decimal(cls, v: Any) -> Decimal:
1230
1520
  """Round decimal values to 4 decimal places."""
1231
1521
  if isinstance(v, Decimal):
1232
1522
  return v.quantize(Decimal("0.0001"), rounding=ROUND_HALF_UP)
1233
- elif isinstance(v, (int, float)):
1523
+ elif isinstance(v, int | float):
1234
1524
  return Decimal(str(v)).quantize(Decimal("0.0001"), rounding=ROUND_HALF_UP)
1235
1525
  return v
1236
1526
 
@@ -1295,7 +1585,7 @@ class CreditPriceTable(Base):
1295
1585
  DateTime(timezone=True),
1296
1586
  nullable=False,
1297
1587
  server_default=func.now(),
1298
- onupdate=lambda: datetime.now(timezone.utc),
1588
+ onupdate=lambda: datetime.now(UTC),
1299
1589
  )
1300
1590
 
1301
1591
 
@@ -1333,7 +1623,7 @@ class CreditPrice(BaseModel):
1333
1623
  """Round decimal values to 4 decimal places."""
1334
1624
  if isinstance(v, Decimal):
1335
1625
  return v.quantize(Decimal("0.0001"), rounding=ROUND_HALF_UP)
1336
- elif isinstance(v, (int, float)):
1626
+ elif isinstance(v, int | float):
1337
1627
  return Decimal(str(v)).quantize(Decimal("0.0001"), rounding=ROUND_HALF_UP)
1338
1628
  return v
1339
1629
 
@@ -1410,13 +1700,11 @@ class CreditPriceLog(BaseModel):
1410
1700
  """Round decimal values to 4 decimal places."""
1411
1701
  if isinstance(v, Decimal):
1412
1702
  return v.quantize(Decimal("0.0001"), rounding=ROUND_HALF_UP)
1413
- elif isinstance(v, (int, float)):
1703
+ elif isinstance(v, int | float):
1414
1704
  return Decimal(str(v)).quantize(Decimal("0.0001"), rounding=ROUND_HALF_UP)
1415
1705
  return v
1416
1706
 
1417
- note: Annotated[
1418
- Optional[str], Field(None, description="Note about the modification")
1419
- ]
1707
+ note: Annotated[str | None, Field(None, description="Note about the modification")]
1420
1708
  modified_by: Annotated[
1421
1709
  str, Field(description="ID of the user who made the modification")
1422
1710
  ]