intentkit 0.8.12rc0__py3-none-any.whl → 0.8.13__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 (188) hide show
  1. intentkit/__init__.py +1 -1
  2. intentkit/abstracts/skill.py +2 -59
  3. intentkit/clients/__init__.py +3 -2
  4. intentkit/clients/cdp.py +63 -44
  5. intentkit/clients/twitter.py +35 -28
  6. intentkit/config/config.py +1 -0
  7. intentkit/core/agent.py +2 -279
  8. intentkit/core/asset.py +63 -16
  9. intentkit/core/engine.py +9 -5
  10. intentkit/core/scheduler.py +8 -8
  11. intentkit/models/agent.py +138 -94
  12. intentkit/models/agent_schema.json +6 -9
  13. intentkit/models/chat.py +1 -0
  14. intentkit/models/llm.csv +15 -12
  15. intentkit/models/skills.csv +5 -4
  16. intentkit/skills/acolyt/__init__.py +2 -9
  17. intentkit/skills/acolyt/base.py +2 -5
  18. intentkit/skills/aixbt/__init__.py +2 -13
  19. intentkit/skills/aixbt/base.py +0 -4
  20. intentkit/skills/aixbt/projects.py +1 -2
  21. intentkit/skills/allora/__init__.py +2 -9
  22. intentkit/skills/allora/base.py +2 -5
  23. intentkit/skills/base.py +101 -37
  24. intentkit/skills/basename/__init__.py +1 -3
  25. intentkit/skills/carv/__init__.py +116 -121
  26. intentkit/skills/carv/base.py +184 -185
  27. intentkit/skills/casino/__init__.py +4 -15
  28. intentkit/skills/casino/base.py +0 -4
  29. intentkit/skills/casino/deck_draw.py +1 -2
  30. intentkit/skills/casino/deck_shuffle.py +1 -2
  31. intentkit/skills/casino/dice_roll.py +1 -2
  32. intentkit/skills/cdp/__init__.py +0 -5
  33. intentkit/skills/cdp/base.py +0 -4
  34. intentkit/skills/cdp/schema.json +1 -17
  35. intentkit/skills/chainlist/__init__.py +2 -7
  36. intentkit/skills/chainlist/base.py +0 -4
  37. intentkit/skills/common/__init__.py +2 -9
  38. intentkit/skills/common/base.py +0 -4
  39. intentkit/skills/cookiefun/__init__.py +6 -9
  40. intentkit/skills/cookiefun/base.py +0 -4
  41. intentkit/skills/cryptocompare/__init__.py +7 -24
  42. intentkit/skills/cryptocompare/base.py +0 -5
  43. intentkit/skills/cryptopanic/__init__.py +3 -6
  44. intentkit/skills/cryptopanic/base.py +53 -55
  45. intentkit/skills/cryptopanic/fetch_crypto_news.py +0 -2
  46. intentkit/skills/cryptopanic/fetch_crypto_sentiment.py +1 -3
  47. intentkit/skills/dapplooker/__init__.py +2 -9
  48. intentkit/skills/dapplooker/base.py +2 -5
  49. intentkit/skills/defillama/__init__.py +24 -74
  50. intentkit/skills/defillama/base.py +0 -4
  51. intentkit/skills/defillama/coins/fetch_batch_historical_prices.py +2 -2
  52. intentkit/skills/defillama/coins/fetch_block.py +2 -2
  53. intentkit/skills/defillama/coins/fetch_current_prices.py +2 -2
  54. intentkit/skills/defillama/coins/fetch_first_price.py +2 -2
  55. intentkit/skills/defillama/coins/fetch_historical_prices.py +2 -2
  56. intentkit/skills/defillama/coins/fetch_price_chart.py +2 -2
  57. intentkit/skills/defillama/coins/fetch_price_percentage.py +2 -2
  58. intentkit/skills/defillama/fees/fetch_fees_overview.py +2 -2
  59. intentkit/skills/defillama/stablecoins/fetch_stablecoin_chains.py +2 -2
  60. intentkit/skills/defillama/stablecoins/fetch_stablecoin_charts.py +2 -2
  61. intentkit/skills/defillama/stablecoins/fetch_stablecoin_prices.py +2 -2
  62. intentkit/skills/defillama/stablecoins/fetch_stablecoins.py +2 -2
  63. intentkit/skills/defillama/tvl/fetch_chain_historical_tvl.py +2 -2
  64. intentkit/skills/defillama/tvl/fetch_chains.py +2 -2
  65. intentkit/skills/defillama/tvl/fetch_historical_tvl.py +2 -2
  66. intentkit/skills/defillama/tvl/fetch_protocol.py +2 -2
  67. intentkit/skills/defillama/tvl/fetch_protocol_current_tvl.py +2 -2
  68. intentkit/skills/defillama/tvl/fetch_protocols.py +2 -2
  69. intentkit/skills/defillama/volumes/fetch_dex_overview.py +2 -2
  70. intentkit/skills/defillama/volumes/fetch_dex_summary.py +2 -2
  71. intentkit/skills/defillama/volumes/fetch_options_overview.py +2 -2
  72. intentkit/skills/defillama/yields/fetch_pool_chart.py +2 -2
  73. intentkit/skills/defillama/yields/fetch_pools.py +2 -2
  74. intentkit/skills/dexscreener/__init__.py +97 -102
  75. intentkit/skills/dexscreener/base.py +125 -130
  76. intentkit/skills/dexscreener/get_pair_info.py +2 -3
  77. intentkit/skills/dexscreener/get_token_pairs.py +2 -3
  78. intentkit/skills/dexscreener/get_tokens_info.py +2 -3
  79. intentkit/skills/dexscreener/search_token.py +2 -4
  80. intentkit/skills/dune_analytics/__init__.py +4 -6
  81. intentkit/skills/dune_analytics/base.py +50 -52
  82. intentkit/skills/dune_analytics/fetch_kol_buys.py +0 -2
  83. intentkit/skills/dune_analytics/fetch_nation_metrics.py +0 -2
  84. intentkit/skills/elfa/__init__.py +5 -18
  85. intentkit/skills/elfa/base.py +8 -10
  86. intentkit/skills/enso/__init__.py +9 -29
  87. intentkit/skills/enso/base.py +3 -6
  88. intentkit/skills/enso/route.py +1 -3
  89. intentkit/skills/erc20/__init__.py +1 -5
  90. intentkit/skills/erc721/__init__.py +1 -3
  91. intentkit/skills/firecrawl/__init__.py +5 -18
  92. intentkit/skills/firecrawl/base.py +2 -5
  93. intentkit/skills/firecrawl/crawl.py +10 -9
  94. intentkit/skills/firecrawl/query.py +3 -1
  95. intentkit/skills/firecrawl/scrape.py +8 -10
  96. intentkit/skills/firecrawl/utils.py +25 -26
  97. intentkit/skills/github/__init__.py +2 -7
  98. intentkit/skills/github/base.py +0 -4
  99. intentkit/skills/heurist/__init__.py +8 -27
  100. intentkit/skills/heurist/base.py +2 -5
  101. intentkit/skills/heurist/image_generation_animagine_xl.py +5 -5
  102. intentkit/skills/heurist/image_generation_arthemy_comics.py +5 -5
  103. intentkit/skills/heurist/image_generation_arthemy_real.py +5 -5
  104. intentkit/skills/heurist/image_generation_braindance.py +5 -5
  105. intentkit/skills/heurist/image_generation_cyber_realistic_xl.py +5 -5
  106. intentkit/skills/heurist/image_generation_flux_1_dev.py +5 -5
  107. intentkit/skills/heurist/image_generation_sdxl.py +5 -5
  108. intentkit/skills/http/__init__.py +4 -15
  109. intentkit/skills/http/base.py +0 -4
  110. intentkit/skills/lifi/__init__.py +1 -6
  111. intentkit/skills/lifi/base.py +0 -4
  112. intentkit/skills/lifi/token_execute.py +1 -4
  113. intentkit/skills/lifi/token_quote.py +1 -3
  114. intentkit/skills/moralis/__init__.py +3 -7
  115. intentkit/skills/moralis/base.py +2 -5
  116. intentkit/skills/morpho/__init__.py +1 -3
  117. intentkit/skills/nation/__init__.py +2 -7
  118. intentkit/skills/nation/base.py +4 -7
  119. intentkit/skills/onchain.py +27 -0
  120. intentkit/skills/openai/__init__.py +5 -18
  121. intentkit/skills/openai/base.py +8 -10
  122. intentkit/skills/openai/dalle_image_generation.py +2 -5
  123. intentkit/skills/openai/gpt_image_generation.py +2 -5
  124. intentkit/skills/openai/gpt_image_to_image.py +2 -5
  125. intentkit/skills/openai/image_to_text.py +2 -5
  126. intentkit/skills/portfolio/__init__.py +11 -35
  127. intentkit/skills/portfolio/base.py +2 -5
  128. intentkit/skills/pyth/__init__.py +1 -5
  129. intentkit/skills/skills.toml +4 -0
  130. intentkit/skills/slack/__init__.py +5 -17
  131. intentkit/skills/slack/base.py +0 -4
  132. intentkit/skills/supabase/__init__.py +7 -23
  133. intentkit/skills/supabase/base.py +0 -4
  134. intentkit/skills/superfluid/__init__.py +1 -3
  135. intentkit/skills/system/__init__.py +7 -24
  136. intentkit/skills/system/add_autonomous_task.py +2 -2
  137. intentkit/skills/system/delete_autonomous_task.py +2 -2
  138. intentkit/skills/system/edit_autonomous_task.py +2 -4
  139. intentkit/skills/system/list_autonomous_tasks.py +2 -2
  140. intentkit/skills/system/read_agent_api_key.py +6 -4
  141. intentkit/skills/system/regenerate_agent_api_key.py +6 -4
  142. intentkit/skills/tavily/__init__.py +3 -12
  143. intentkit/skills/tavily/base.py +2 -5
  144. intentkit/skills/tavily/tavily_extract.py +1 -2
  145. intentkit/skills/tavily/tavily_search.py +3 -3
  146. intentkit/skills/token/__init__.py +5 -10
  147. intentkit/skills/token/base.py +2 -6
  148. intentkit/skills/twitter/__init__.py +11 -35
  149. intentkit/skills/twitter/base.py +14 -16
  150. intentkit/skills/twitter/follow_user.py +0 -1
  151. intentkit/skills/twitter/get_mentions.py +0 -1
  152. intentkit/skills/twitter/get_timeline.py +0 -1
  153. intentkit/skills/twitter/get_user_by_username.py +0 -1
  154. intentkit/skills/twitter/get_user_tweets.py +0 -1
  155. intentkit/skills/twitter/like_tweet.py +0 -1
  156. intentkit/skills/twitter/post_tweet.py +2 -2
  157. intentkit/skills/twitter/reply_tweet.py +2 -2
  158. intentkit/skills/twitter/retweet.py +0 -1
  159. intentkit/skills/twitter/search_tweets.py +0 -1
  160. intentkit/skills/unrealspeech/__init__.py +2 -7
  161. intentkit/skills/unrealspeech/base.py +0 -4
  162. intentkit/skills/venice_audio/__init__.py +99 -106
  163. intentkit/skills/venice_audio/base.py +118 -121
  164. intentkit/skills/venice_audio/venice_audio.py +1 -5
  165. intentkit/skills/venice_image/__init__.py +147 -154
  166. intentkit/skills/venice_image/base.py +185 -192
  167. intentkit/skills/web_scraper/__init__.py +5 -18
  168. intentkit/skills/web_scraper/base.py +20 -4
  169. intentkit/skills/web_scraper/document_indexer.py +6 -4
  170. intentkit/skills/web_scraper/scrape_and_index.py +11 -8
  171. intentkit/skills/web_scraper/utils.py +31 -27
  172. intentkit/skills/web_scraper/website_indexer.py +7 -8
  173. intentkit/skills/weth/__init__.py +1 -5
  174. intentkit/skills/wow/__init__.py +1 -5
  175. intentkit/skills/x402/__init__.py +53 -0
  176. intentkit/skills/x402/ask_agent.py +141 -0
  177. intentkit/skills/x402/base.py +9 -0
  178. intentkit/skills/x402/schema.json +40 -0
  179. intentkit/skills/x402/x402.png +0 -0
  180. intentkit/skills/xmtp/__init__.py +4 -15
  181. intentkit/skills/xmtp/base.py +2 -2
  182. intentkit/skills/xmtp/price.py +3 -3
  183. intentkit/skills/xmtp/swap.py +4 -4
  184. intentkit/utils/schema.py +100 -0
  185. {intentkit-0.8.12rc0.dist-info → intentkit-0.8.13.dist-info}/METADATA +1 -1
  186. {intentkit-0.8.12rc0.dist-info → intentkit-0.8.13.dist-info}/RECORD +188 -181
  187. {intentkit-0.8.12rc0.dist-info → intentkit-0.8.13.dist-info}/WHEEL +0 -0
  188. {intentkit-0.8.12rc0.dist-info → intentkit-0.8.13.dist-info}/licenses/LICENSE +0 -0
intentkit/core/agent.py CHANGED
@@ -2,21 +2,19 @@ import logging
2
2
  import time
3
3
  from datetime import datetime, timedelta, timezone
4
4
  from decimal import Decimal
5
- from typing import Any, AsyncGenerator, Dict, List, Optional, Tuple
5
+ from typing import AsyncGenerator, Dict, Optional, Tuple
6
6
 
7
7
  from sqlalchemy import func, select, text, update
8
8
 
9
- from intentkit.abstracts.skill import SkillStoreABC
10
9
  from intentkit.clients.cdp import get_wallet_provider
11
10
  from intentkit.config.config import config
12
11
  from intentkit.models.agent import (
13
12
  Agent,
14
- AgentAutonomous,
15
13
  AgentCreate,
16
14
  AgentTable,
17
15
  AgentUpdate,
18
16
  )
19
- from intentkit.models.agent_data import AgentData, AgentQuota, AgentQuotaTable
17
+ from intentkit.models.agent_data import AgentData, AgentQuotaTable
20
18
  from intentkit.models.credit import (
21
19
  CreditAccount,
22
20
  CreditEventTable,
@@ -486,93 +484,6 @@ async def agent_action_cost(agent_id: str) -> Dict[str, Decimal]:
486
484
  return result
487
485
 
488
486
 
489
- class AgentStore(SkillStoreABC):
490
- """Implementation of skill data storage operations.
491
-
492
- This class provides concrete implementations for storing and retrieving
493
- skill-related data for both agents and threads.
494
- """
495
-
496
- @staticmethod
497
- def get_system_config(key: str) -> Any:
498
- # TODO: maybe need a whitelist here
499
- if hasattr(config, key):
500
- return getattr(config, key)
501
- return None
502
-
503
- @staticmethod
504
- async def get_agent_config(agent_id: str) -> Optional[Agent]:
505
- return await Agent.get(agent_id)
506
-
507
- @staticmethod
508
- async def get_agent_data(agent_id: str) -> AgentData:
509
- return await AgentData.get(agent_id)
510
-
511
- @staticmethod
512
- async def set_agent_data(agent_id: str, data: Dict) -> AgentData:
513
- return await AgentData.patch(agent_id, data)
514
-
515
- @staticmethod
516
- async def get_agent_quota(agent_id: str) -> AgentQuota:
517
- return await AgentQuota.get(agent_id)
518
-
519
- @staticmethod
520
- async def list_autonomous_tasks(agent_id: str) -> List[AgentAutonomous]:
521
- """List all autonomous tasks for an agent.
522
-
523
- Args:
524
- agent_id: ID of the agent
525
-
526
- Returns:
527
- List[AgentAutonomous]: List of autonomous task configurations
528
- """
529
- return await list_autonomous_tasks(agent_id)
530
-
531
- @staticmethod
532
- async def add_autonomous_task(
533
- agent_id: str, task: AgentAutonomous
534
- ) -> AgentAutonomous:
535
- """Add a new autonomous task to an agent.
536
-
537
- Args:
538
- agent_id: ID of the agent
539
- task: Autonomous task configuration
540
-
541
- Returns:
542
- AgentAutonomous: The created task
543
- """
544
- return await add_autonomous_task(agent_id, task)
545
-
546
- @staticmethod
547
- async def delete_autonomous_task(agent_id: str, task_id: str) -> None:
548
- """Delete an autonomous task from an agent.
549
-
550
- Args:
551
- agent_id: ID of the agent
552
- task_id: ID of the task to delete
553
- """
554
- await delete_autonomous_task(agent_id, task_id)
555
-
556
- @staticmethod
557
- async def update_autonomous_task(
558
- agent_id: str, task_id: str, task_updates: dict
559
- ) -> AgentAutonomous:
560
- """Update an autonomous task for an agent.
561
-
562
- Args:
563
- agent_id: ID of the agent
564
- task_id: ID of the task to update
565
- task_updates: Dictionary containing fields to update
566
-
567
- Returns:
568
- AgentAutonomous: The updated task
569
- """
570
- return await update_autonomous_task(agent_id, task_id, task_updates)
571
-
572
-
573
- agent_store = AgentStore()
574
-
575
-
576
487
  async def _iterate_agent_id_batches(
577
488
  batch_size: int = 100,
578
489
  ) -> AsyncGenerator[list[str], None]:
@@ -828,191 +739,3 @@ async def update_agents_statistics(
828
739
  total_updated,
829
740
  total_time,
830
741
  )
831
-
832
-
833
- async def list_autonomous_tasks(agent_id: str) -> List[AgentAutonomous]:
834
- """
835
- List all autonomous tasks for an agent.
836
-
837
- Args:
838
- agent_id: ID of the agent
839
-
840
- Returns:
841
- List[AgentAutonomous]: List of autonomous task configurations
842
-
843
- Raises:
844
- IntentKitAPIError: If agent is not found
845
- """
846
- agent = await Agent.get(agent_id)
847
- if not agent:
848
- raise IntentKitAPIError(
849
- 400, "AgentNotFound", f"Agent with ID {agent_id} does not exist."
850
- )
851
-
852
- if not agent.autonomous:
853
- return []
854
-
855
- return agent.autonomous
856
-
857
-
858
- async def add_autonomous_task(agent_id: str, task: AgentAutonomous) -> AgentAutonomous:
859
- """
860
- Add a new autonomous task to an agent.
861
-
862
- Args:
863
- agent_id: ID of the agent
864
- task: Autonomous task configuration (id will be generated if not provided)
865
-
866
- Returns:
867
- AgentAutonomous: The created task with generated ID
868
-
869
- Raises:
870
- IntentKitAPIError: If agent is not found
871
- """
872
- agent = await Agent.get(agent_id)
873
- if not agent:
874
- raise IntentKitAPIError(
875
- 400, "AgentNotFound", f"Agent with ID {agent_id} does not exist."
876
- )
877
-
878
- # Get current autonomous tasks
879
- current_tasks = agent.autonomous or []
880
- if not isinstance(current_tasks, list):
881
- current_tasks = []
882
-
883
- # Add the new task
884
- current_tasks.append(task)
885
-
886
- # Convert all AgentAutonomous objects to dictionaries for JSON serialization
887
- serializable_tasks = [task_item.model_dump() for task_item in current_tasks]
888
-
889
- # Update the agent in the database
890
- async with get_session() as session:
891
- update_stmt = (
892
- update(AgentTable)
893
- .where(AgentTable.id == agent_id)
894
- .values(autonomous=serializable_tasks)
895
- )
896
- await session.execute(update_stmt)
897
- await session.commit()
898
-
899
- logger.info(f"Added autonomous task {task.id} to agent {agent_id}")
900
- return task
901
-
902
-
903
- async def delete_autonomous_task(agent_id: str, task_id: str) -> None:
904
- """
905
- Delete an autonomous task from an agent.
906
-
907
- Args:
908
- agent_id: ID of the agent
909
- task_id: ID of the task to delete
910
-
911
- Raises:
912
- IntentKitAPIError: If agent is not found or task is not found
913
- """
914
- agent = await Agent.get(agent_id)
915
- if not agent:
916
- raise IntentKitAPIError(
917
- 400, "AgentNotFound", f"Agent with ID {agent_id} does not exist."
918
- )
919
-
920
- # Get current autonomous tasks
921
- current_tasks = agent.autonomous or []
922
- if not isinstance(current_tasks, list):
923
- current_tasks = []
924
-
925
- # Find and remove the task
926
- task_found = False
927
- updated_tasks = []
928
- for task_data in current_tasks:
929
- if task_data.id == task_id:
930
- task_found = True
931
- continue
932
- updated_tasks.append(task_data)
933
-
934
- if not task_found:
935
- raise IntentKitAPIError(
936
- 404, "TaskNotFound", f"Autonomous task with ID {task_id} not found."
937
- )
938
-
939
- # Convert remaining AgentAutonomous objects to dictionaries for JSON serialization
940
- serializable_tasks = [task_item.model_dump() for task_item in updated_tasks]
941
-
942
- # Update the agent in the database
943
- async with get_session() as session:
944
- update_stmt = (
945
- update(AgentTable)
946
- .where(AgentTable.id == agent_id)
947
- .values(autonomous=serializable_tasks)
948
- )
949
- await session.execute(update_stmt)
950
- await session.commit()
951
-
952
- logger.info(f"Deleted autonomous task {task_id} from agent {agent_id}")
953
-
954
-
955
- async def update_autonomous_task(
956
- agent_id: str, task_id: str, task_updates: dict
957
- ) -> AgentAutonomous:
958
- """
959
- Update an autonomous task for an agent.
960
-
961
- Args:
962
- agent_id: ID of the agent
963
- task_id: ID of the task to update
964
- task_updates: Dictionary containing fields to update
965
-
966
- Returns:
967
- AgentAutonomous: The updated task
968
-
969
- Raises:
970
- IntentKitAPIError: If agent is not found or task is not found
971
- """
972
- agent = await Agent.get(agent_id)
973
- if not agent:
974
- raise IntentKitAPIError(
975
- 400, "AgentNotFound", f"Agent with ID {agent_id} does not exist."
976
- )
977
-
978
- # Get current autonomous tasks
979
- current_tasks: List[AgentAutonomous] = agent.autonomous or []
980
-
981
- # Find and update the task
982
- task_found = False
983
- updated_tasks: List[AgentAutonomous] = []
984
- updated_task = None
985
-
986
- for task_data in current_tasks:
987
- if task_data.id == task_id:
988
- task_found = True
989
- # Create a dictionary with current task data
990
- task_dict = task_data.model_dump()
991
- # Update with provided fields
992
- task_dict.update(task_updates)
993
- # Create new AgentAutonomous instance
994
- updated_task = AgentAutonomous.model_validate(task_dict)
995
- updated_tasks.append(updated_task)
996
- else:
997
- updated_tasks.append(task_data)
998
-
999
- if not task_found:
1000
- raise IntentKitAPIError(
1001
- 404, "TaskNotFound", f"Autonomous task with ID {task_id} not found."
1002
- )
1003
-
1004
- # Convert all AgentAutonomous objects to dictionaries for JSON serialization
1005
- serializable_tasks = [task_item.model_dump() for task_item in updated_tasks]
1006
-
1007
- # Update the agent in the database
1008
- async with get_session() as session:
1009
- update_stmt = (
1010
- update(AgentTable)
1011
- .where(AgentTable.id == agent_id)
1012
- .values(autonomous=serializable_tasks)
1013
- )
1014
- await session.execute(update_stmt)
1015
- await session.commit()
1016
-
1017
- logger.info(f"Updated autonomous task {task_id} for agent {agent_id}")
1018
- return updated_task
intentkit/core/asset.py CHANGED
@@ -2,18 +2,22 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
+ import json
5
6
  import logging
6
7
  from decimal import Decimal
7
8
  from typing import Optional
8
9
 
9
10
  import httpx
10
11
  from pydantic import BaseModel, Field
12
+ from sqlalchemy import update
11
13
  from web3 import Web3
12
14
 
13
15
  from intentkit.clients.web3 import get_web3_client
14
16
  from intentkit.config.config import config
15
- from intentkit.models.agent import Agent
17
+ from intentkit.models.agent import Agent, AgentTable
16
18
  from intentkit.models.agent_data import AgentData
19
+ from intentkit.models.db import get_session
20
+ from intentkit.models.redis import get_redis
17
21
  from intentkit.utils.error import IntentKitAPIError
18
22
 
19
23
  logger = logging.getLogger(__name__)
@@ -171,29 +175,72 @@ async def _build_assets_list(
171
175
 
172
176
  async def agent_asset(agent_id: str) -> AgentAssets:
173
177
  """Fetch wallet net worth and token balances for an agent."""
178
+
179
+ cache_key = f"intentkit:agent_assets:{agent_id}"
180
+ redis_client = None
181
+
182
+ try:
183
+ redis_client = get_redis()
184
+ except Exception as exc: # pragma: no cover - best effort fallback
185
+ logger.debug("Redis unavailable for agent assets: %s", exc)
186
+
174
187
  agent = await Agent.get(agent_id)
175
188
  if not agent:
176
189
  raise IntentKitAPIError(404, "AgentNotFound", "Agent not found")
177
190
 
191
+ if redis_client:
192
+ try:
193
+ cached_raw = await redis_client.get(cache_key)
194
+ if cached_raw:
195
+ cached_data = json.loads(cached_raw)
196
+ cached_assets = AgentAssets.model_validate(cached_data)
197
+ return cached_assets
198
+ except Exception as exc: # pragma: no cover - cache read path only
199
+ logger.debug("Failed to read agent asset cache for %s: %s", agent_id, exc)
200
+
178
201
  agent_data = await AgentData.get(agent_id)
179
202
  if not agent_data or not agent_data.evm_wallet_address:
180
- return AgentAssets(net_worth="0", tokens=[])
181
-
182
- if not agent.network_id:
183
- return AgentAssets(net_worth="0", tokens=[])
203
+ assets_result = AgentAssets(net_worth="0", tokens=[])
204
+ elif not agent.network_id:
205
+ assets_result = AgentAssets(net_worth="0", tokens=[])
206
+ else:
207
+ try:
208
+ web3_client = get_web3_client(str(agent.network_id))
209
+ tokens = await _build_assets_list(agent, agent_data, web3_client)
210
+ net_worth = await _get_wallet_net_worth(agent_data.evm_wallet_address)
211
+ assets_result = AgentAssets(net_worth=net_worth, tokens=tokens)
212
+ except IntentKitAPIError:
213
+ raise
214
+ except Exception as exc:
215
+ logger.error("Error getting agent assets for %s: %s", agent_id, exc)
216
+ raise IntentKitAPIError(
217
+ 500, "AgentAssetError", "Failed to retrieve agent assets"
218
+ ) from exc
219
+
220
+ assets_payload = assets_result.model_dump(mode="json")
221
+
222
+ if redis_client:
223
+ try:
224
+ await redis_client.set(
225
+ cache_key,
226
+ json.dumps(assets_payload),
227
+ ex=3600,
228
+ )
229
+ except Exception as exc: # pragma: no cover - cache write path only
230
+ logger.debug("Failed to write agent asset cache for %s: %s", agent_id, exc)
184
231
 
185
232
  try:
186
- web3_client = get_web3_client(str(agent.network_id))
187
- tokens = await _build_assets_list(agent, agent_data, web3_client)
188
- net_worth = await _get_wallet_net_worth(agent_data.evm_wallet_address)
189
- return AgentAssets(net_worth=net_worth, tokens=tokens)
190
- except IntentKitAPIError:
191
- raise
192
- except Exception as exc:
193
- logger.error("Error getting agent assets for %s: %s", agent_id, exc)
194
- raise IntentKitAPIError(
195
- 500, "AgentAssetError", "Failed to retrieve agent assets"
196
- ) from exc
233
+ async with get_session() as session:
234
+ await session.execute(
235
+ update(AgentTable)
236
+ .where(AgentTable.id == agent_id)
237
+ .values(assets=assets_payload)
238
+ )
239
+ await session.commit()
240
+ except Exception as exc: # pragma: no cover - db persistence path only
241
+ logger.error("Error updating agent assets cache for %s: %s", agent_id, exc)
242
+
243
+ return assets_result
197
244
 
198
245
 
199
246
  __all__ = [
intentkit/core/engine.py CHANGED
@@ -21,7 +21,6 @@ from typing import Optional, Tuple
21
21
 
22
22
  import sqlalchemy
23
23
  from epyxid import XID
24
- from langchain.agents import create_agent as create_react_agent
25
24
  from langchain_core.language_models import BaseChatModel
26
25
  from langchain_core.messages import (
27
26
  BaseMessage,
@@ -30,13 +29,13 @@ from langchain_core.messages import (
30
29
  from langchain_core.tools import BaseTool
31
30
  from langgraph.errors import GraphRecursionError
32
31
  from langgraph.graph.state import CompiledStateGraph
32
+ from langgraph.prebuilt import create_react_agent
33
33
  from langgraph.runtime import Runtime
34
34
  from sqlalchemy import func, update
35
35
  from sqlalchemy.exc import SQLAlchemyError
36
36
 
37
37
  from intentkit.abstracts.graph import AgentContext, AgentError, AgentState
38
38
  from intentkit.config.config import config
39
- from intentkit.core.agent import agent_store
40
39
  from intentkit.core.chat import clear_thread_memory
41
40
  from intentkit.core.credit import expense_message, expense_skill
42
41
  from intentkit.core.node import PreModelNode, post_model_node
@@ -56,7 +55,7 @@ from intentkit.models.chat import (
56
55
  from intentkit.models.credit import CreditAccount, OwnerType
57
56
  from intentkit.models.db import get_langgraph_checkpointer, get_session
58
57
  from intentkit.models.llm import LLMModelInfo, LLMProvider, create_llm_model
59
- from intentkit.models.skill import AgentSkillData, ChatSkillData
58
+ from intentkit.models.skill import AgentSkillData, ChatSkillData, Skill
60
59
  from intentkit.models.user import User
61
60
  from intentkit.utils.error import IntentKitAPIError
62
61
 
@@ -115,13 +114,13 @@ async def build_agent(
115
114
  if hasattr(skill_module, "get_skills"):
116
115
  # all
117
116
  skill_tools = await skill_module.get_skills(
118
- v, False, agent_store, agent_id=agent.id, agent=agent
117
+ v, False, agent_id=agent.id, agent=agent
119
118
  )
120
119
  if skill_tools and len(skill_tools) > 0:
121
120
  tools.extend(skill_tools)
122
121
  # private
123
122
  skill_private_tools = await skill_module.get_skills(
124
- v, True, agent_store, agent_id=agent.id, agent=agent
123
+ v, True, agent_id=agent.id, agent=agent
125
124
  )
126
125
  if skill_private_tools and len(skill_private_tools) > 0:
127
126
  private_tools.extend(skill_private_tools)
@@ -333,6 +332,8 @@ async def stream_agent_raw(
333
332
  model = await LLMModelInfo.get(agent.model)
334
333
 
335
334
  payment_enabled = config.payment_enabled
335
+ if user_message.author_type == AuthorType.X402:
336
+ payment_enabled = False
336
337
 
337
338
  # check user balance
338
339
  if payment_enabled:
@@ -703,6 +704,9 @@ async def stream_agent_raw(
703
704
  for skill_call in skill_calls:
704
705
  if not skill_call["success"]:
705
706
  continue
707
+ skill = await Skill.get(skill_call["name"])
708
+ if not skill:
709
+ continue
706
710
  payment_event = await expense_skill(
707
711
  session,
708
712
  payer,
@@ -11,7 +11,6 @@ from apscheduler.triggers.cron import CronTrigger
11
11
  from intentkit.core.agent import (
12
12
  update_agent_action_cost,
13
13
  update_agents_account_snapshot,
14
- update_agents_assets,
15
14
  update_agents_statistics,
16
15
  )
17
16
  from intentkit.core.credit import refill_all_free_credits
@@ -63,13 +62,14 @@ def create_scheduler(
63
62
  )
64
63
 
65
64
  # Update agent assets daily at UTC midnight
66
- scheduler.add_job(
67
- update_agents_assets,
68
- trigger=CronTrigger(hour=0, minute=0, timezone="UTC"),
69
- id="update_agent_assets",
70
- name="Update agent assets",
71
- replace_existing=True,
72
- )
65
+ # This is too expensive to run daily, so it will only be triggered when detail page is visited
66
+ # scheduler.add_job(
67
+ # update_agents_assets,
68
+ # trigger=CronTrigger(hour=0, minute=0, timezone="UTC"),
69
+ # id="update_agent_assets",
70
+ # name="Update agent assets",
71
+ # replace_existing=True,
72
+ # )
73
73
 
74
74
  # Update agent action costs hourly at minute 40
75
75
  scheduler.add_job(