intentkit 0.6.9.dev2__py3-none-any.whl → 0.6.10.dev2__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 (168) hide show
  1. intentkit/__init__.py +1 -1
  2. intentkit/abstracts/graph.py +17 -2
  3. intentkit/core/engine.py +49 -30
  4. intentkit/core/node.py +10 -20
  5. intentkit/models/agent.py +3 -0
  6. intentkit/models/chat.py +9 -1
  7. intentkit/skills/acolyt/ask.py +2 -5
  8. intentkit/skills/acolyt/base.py +16 -6
  9. intentkit/skills/aixbt/__init__.py +3 -7
  10. intentkit/skills/aixbt/projects.py +12 -36
  11. intentkit/skills/allora/base.py +16 -6
  12. intentkit/skills/allora/price.py +2 -4
  13. intentkit/skills/base.py +8 -1
  14. intentkit/skills/carv/base.py +12 -10
  15. intentkit/skills/carv/fetch_news.py +90 -92
  16. intentkit/skills/carv/onchain_query.py +162 -164
  17. intentkit/skills/carv/token_info_and_price.py +108 -110
  18. intentkit/skills/chainlist/chain_lookup.py +1 -2
  19. intentkit/skills/common/current_time.py +1 -2
  20. intentkit/skills/cookiefun/base.py +20 -12
  21. intentkit/skills/cookiefun/get_account_details.py +1 -3
  22. intentkit/skills/cookiefun/get_account_feed.py +1 -3
  23. intentkit/skills/cookiefun/get_account_smart_followers.py +1 -3
  24. intentkit/skills/cookiefun/get_sectors.py +2 -3
  25. intentkit/skills/cookiefun/search_accounts.py +1 -3
  26. intentkit/skills/cryptocompare/fetch_news.py +3 -4
  27. intentkit/skills/cryptocompare/fetch_price.py +3 -4
  28. intentkit/skills/cryptocompare/fetch_top_exchanges.py +3 -4
  29. intentkit/skills/cryptocompare/fetch_top_market_cap.py +3 -4
  30. intentkit/skills/cryptocompare/fetch_top_volume.py +3 -4
  31. intentkit/skills/cryptocompare/fetch_trading_signals.py +3 -4
  32. intentkit/skills/cryptopanic/base.py +13 -9
  33. intentkit/skills/cryptopanic/fetch_crypto_news.py +150 -153
  34. intentkit/skills/cryptopanic/fetch_crypto_sentiment.py +133 -136
  35. intentkit/skills/dapplooker/base.py +16 -6
  36. intentkit/skills/dapplooker/dapplooker_token_data.py +2 -4
  37. intentkit/skills/defillama/coins/fetch_batch_historical_prices.py +2 -3
  38. intentkit/skills/defillama/coins/fetch_block.py +2 -3
  39. intentkit/skills/defillama/coins/fetch_current_prices.py +2 -5
  40. intentkit/skills/defillama/coins/fetch_first_price.py +2 -5
  41. intentkit/skills/defillama/coins/fetch_historical_prices.py +2 -3
  42. intentkit/skills/defillama/coins/fetch_price_chart.py +2 -5
  43. intentkit/skills/defillama/coins/fetch_price_percentage.py +2 -5
  44. intentkit/skills/defillama/fees/fetch_fees_overview.py +2 -3
  45. intentkit/skills/defillama/stablecoins/fetch_stablecoin_chains.py +2 -3
  46. intentkit/skills/defillama/stablecoins/fetch_stablecoin_charts.py +2 -3
  47. intentkit/skills/defillama/stablecoins/fetch_stablecoin_prices.py +2 -3
  48. intentkit/skills/defillama/stablecoins/fetch_stablecoins.py +2 -3
  49. intentkit/skills/defillama/tvl/fetch_chain_historical_tvl.py +2 -5
  50. intentkit/skills/defillama/tvl/fetch_chains.py +2 -3
  51. intentkit/skills/defillama/tvl/fetch_historical_tvl.py +2 -3
  52. intentkit/skills/defillama/tvl/fetch_protocol.py +2 -5
  53. intentkit/skills/defillama/tvl/fetch_protocol_current_tvl.py +2 -5
  54. intentkit/skills/defillama/tvl/fetch_protocols.py +2 -3
  55. intentkit/skills/defillama/volumes/fetch_dex_overview.py +2 -3
  56. intentkit/skills/defillama/volumes/fetch_dex_summary.py +2 -5
  57. intentkit/skills/defillama/volumes/fetch_options_overview.py +2 -3
  58. intentkit/skills/defillama/yields/fetch_pool_chart.py +2 -5
  59. intentkit/skills/defillama/yields/fetch_pools.py +2 -3
  60. intentkit/skills/dune_analytics/base.py +15 -9
  61. intentkit/skills/dune_analytics/fetch_kol_buys.py +125 -128
  62. intentkit/skills/dune_analytics/fetch_nation_metrics.py +234 -237
  63. intentkit/skills/elfa/base.py +16 -6
  64. intentkit/skills/elfa/mention.py +2 -7
  65. intentkit/skills/elfa/stats.py +2 -6
  66. intentkit/skills/elfa/tokens.py +1 -4
  67. intentkit/skills/enso/base.py +25 -13
  68. intentkit/skills/enso/best_yield.py +1 -4
  69. intentkit/skills/enso/networks.py +2 -5
  70. intentkit/skills/enso/prices.py +1 -5
  71. intentkit/skills/enso/route.py +2 -5
  72. intentkit/skills/enso/tokens.py +1 -4
  73. intentkit/skills/enso/wallet.py +3 -9
  74. intentkit/skills/firecrawl/base.py +16 -6
  75. intentkit/skills/firecrawl/clear.py +1 -3
  76. intentkit/skills/firecrawl/crawl.py +7 -8
  77. intentkit/skills/firecrawl/query.py +7 -9
  78. intentkit/skills/firecrawl/scrape.py +7 -8
  79. intentkit/skills/github/github_search.py +1 -3
  80. intentkit/skills/heurist/base.py +15 -0
  81. intentkit/skills/heurist/image_generation_animagine_xl.py +3 -4
  82. intentkit/skills/heurist/image_generation_arthemy_comics.py +3 -4
  83. intentkit/skills/heurist/image_generation_arthemy_real.py +3 -4
  84. intentkit/skills/heurist/image_generation_braindance.py +3 -4
  85. intentkit/skills/heurist/image_generation_cyber_realistic_xl.py +3 -4
  86. intentkit/skills/heurist/image_generation_flux_1_dev.py +3 -4
  87. intentkit/skills/heurist/image_generation_sdxl.py +3 -4
  88. intentkit/skills/http/get.py +0 -2
  89. intentkit/skills/http/post.py +0 -2
  90. intentkit/skills/http/put.py +0 -2
  91. intentkit/skills/lifi/token_execute.py +1 -3
  92. intentkit/skills/lifi/token_quote.py +0 -2
  93. intentkit/skills/moralis/base.py +15 -1
  94. intentkit/skills/nation/nft_check.py +2 -5
  95. intentkit/skills/openai/base.py +14 -5
  96. intentkit/skills/openai/dalle_image_generation.py +6 -5
  97. intentkit/skills/openai/gpt_image_generation.py +6 -5
  98. intentkit/skills/openai/gpt_image_to_image.py +6 -5
  99. intentkit/skills/openai/image_to_text.py +6 -6
  100. intentkit/skills/portfolio/base.py +4 -3
  101. intentkit/skills/portfolio/token_balances.py +2 -4
  102. intentkit/skills/portfolio/wallet_approvals.py +2 -4
  103. intentkit/skills/portfolio/wallet_defi_positions.py +3 -4
  104. intentkit/skills/portfolio/wallet_history.py +2 -4
  105. intentkit/skills/portfolio/wallet_net_worth.py +2 -4
  106. intentkit/skills/portfolio/wallet_nfts.py +2 -4
  107. intentkit/skills/portfolio/wallet_profitability.py +2 -4
  108. intentkit/skills/portfolio/wallet_profitability_summary.py +2 -4
  109. intentkit/skills/portfolio/wallet_stats.py +2 -4
  110. intentkit/skills/portfolio/wallet_swaps.py +2 -4
  111. intentkit/skills/slack/base.py +18 -0
  112. intentkit/skills/slack/get_channel.py +3 -4
  113. intentkit/skills/slack/get_message.py +3 -4
  114. intentkit/skills/slack/schedule_message.py +3 -4
  115. intentkit/skills/slack/send_message.py +3 -4
  116. intentkit/skills/supabase/delete_data.py +3 -6
  117. intentkit/skills/supabase/fetch_data.py +3 -6
  118. intentkit/skills/supabase/insert_data.py +3 -6
  119. intentkit/skills/supabase/invoke_function.py +3 -6
  120. intentkit/skills/supabase/update_data.py +3 -6
  121. intentkit/skills/supabase/upsert_data.py +3 -6
  122. intentkit/skills/system/add_autonomous_task.py +1 -3
  123. intentkit/skills/system/delete_autonomous_task.py +1 -3
  124. intentkit/skills/system/edit_autonomous_task.py +1 -3
  125. intentkit/skills/system/list_autonomous_tasks.py +1 -3
  126. intentkit/skills/system/read_agent_api_key.py +2 -3
  127. intentkit/skills/system/regenerate_agent_api_key.py +2 -5
  128. intentkit/skills/tavily/base.py +14 -5
  129. intentkit/skills/tavily/tavily_extract.py +7 -8
  130. intentkit/skills/tavily/tavily_search.py +11 -9
  131. intentkit/skills/token/base.py +4 -6
  132. intentkit/skills/token/erc20_transfers.py +2 -4
  133. intentkit/skills/token/token_analytics.py +2 -4
  134. intentkit/skills/token/token_price.py +2 -4
  135. intentkit/skills/token/token_search.py +2 -4
  136. intentkit/skills/twitter/base.py +41 -0
  137. intentkit/skills/twitter/follow_user.py +4 -4
  138. intentkit/skills/twitter/get_mentions.py +4 -4
  139. intentkit/skills/twitter/get_timeline.py +4 -4
  140. intentkit/skills/twitter/get_user_by_username.py +4 -4
  141. intentkit/skills/twitter/get_user_tweets.py +4 -4
  142. intentkit/skills/twitter/like_tweet.py +4 -4
  143. intentkit/skills/twitter/post_tweet.py +3 -4
  144. intentkit/skills/twitter/reply_tweet.py +3 -4
  145. intentkit/skills/twitter/retweet.py +4 -4
  146. intentkit/skills/twitter/search_tweets.py +4 -4
  147. intentkit/skills/unrealspeech/base.py +16 -0
  148. intentkit/skills/unrealspeech/text_to_speech.py +4 -4
  149. intentkit/skills/venice_audio/base.py +11 -9
  150. intentkit/skills/venice_audio/venice_audio.py +238 -240
  151. intentkit/skills/venice_image/base.py +23 -19
  152. intentkit/skills/venice_image/image_enhance/image_enhance.py +78 -80
  153. intentkit/skills/venice_image/image_generation/image_generation_base.py +115 -117
  154. intentkit/skills/venice_image/image_upscale/image_upscale.py +88 -90
  155. intentkit/skills/venice_image/image_vision/image_vision.py +98 -100
  156. intentkit/skills/web_scraper/document_indexer.py +3 -5
  157. intentkit/skills/web_scraper/scrape_and_index.py +14 -17
  158. intentkit/skills/web_scraper/website_indexer.py +8 -10
  159. intentkit/skills/xmtp/README.md +110 -0
  160. intentkit/skills/xmtp/__init__.py +82 -0
  161. intentkit/skills/xmtp/base.py +15 -0
  162. intentkit/skills/xmtp/schema.json +41 -0
  163. intentkit/skills/xmtp/transfer.py +155 -0
  164. intentkit/skills/xmtp/xmtp.svg +26 -0
  165. {intentkit-0.6.9.dev2.dist-info → intentkit-0.6.10.dev2.dist-info}/METADATA +3 -3
  166. {intentkit-0.6.9.dev2.dist-info → intentkit-0.6.10.dev2.dist-info}/RECORD +168 -162
  167. {intentkit-0.6.9.dev2.dist-info → intentkit-0.6.10.dev2.dist-info}/WHEEL +0 -0
  168. {intentkit-0.6.9.dev2.dist-info → intentkit-0.6.10.dev2.dist-info}/licenses/LICENSE +0 -0
@@ -1,100 +1,98 @@
1
- import logging
2
- from typing import Any, Type
3
-
4
- from langchain_core.runnables import RunnableConfig
5
- from pydantic import BaseModel, HttpUrl
6
-
7
- from intentkit.skills.base import ToolException
8
- from intentkit.skills.venice_image.image_vision.image_vision_base import (
9
- VeniceImageVisionBaseTool,
10
- )
11
- from intentkit.skills.venice_image.image_vision.image_vision_input import (
12
- VeniceImageVision,
13
- )
14
- from intentkit.skills.venice_image.utils import fetch_image_as_base64
15
-
16
- logger = logging.getLogger(__name__)
17
-
18
-
19
- class ImageVision(VeniceImageVisionBaseTool):
20
- """
21
- Describes an image provided via URL using the Venice AI API.
22
- Ideal for understanding the content of an existing image.
23
- """
24
-
25
- name: str = "venice_image_vision"
26
- description: str = (
27
- "Describes an image from a URL using Venice AI.\n"
28
- "Provide the public URL of the image to describe.\n"
29
- "Returns a descriptive text of the image."
30
- )
31
- args_schema: Type[BaseModel] = VeniceImageVision
32
- # No model_id needed for the generic vision endpoint currently
33
-
34
- async def _arun(
35
- self,
36
- image_url: HttpUrl,
37
- config: RunnableConfig = None,
38
- **kwargs,
39
- ) -> dict[str, Any]:
40
- try:
41
- context = self.context_from_config(config)
42
-
43
- await self.apply_venice_rate_limit(context)
44
-
45
- image_base64 = await fetch_image_as_base64(image_url)
46
- if not image_base64:
47
- error_msg = f"Failed to fetch or validate image from URL: {image_url}"
48
- logger.error(error_msg)
49
- return {"success": False, "error": error_msg, "result": None}
50
-
51
- payload = {
52
- "model": "qwen-2.5-vl",
53
- "messages": [
54
- {
55
- "role": "system",
56
- "content": [
57
- {
58
- "type": "text",
59
- "text": (
60
- "You are an AI model that provides detailed descriptions of images. "
61
- "When given an image, you must respond with a description that is as comprehensive and detailed as possible. "
62
- "Focus on identifying all objects, colors, textures, and any other relevant features present in the image. "
63
- "Provide a thorough and exhaustive account of what is visible in the image."
64
- ),
65
- }
66
- ],
67
- },
68
- {
69
- "role": "user",
70
- "content": [
71
- {
72
- "type": "text",
73
- "text": (
74
- "Provide an extremely detailed description of the image, focusing on every discernible aspect. "
75
- "Include information about objects, colors, textures, lighting conditions, artistic style (if applicable), "
76
- "composition, and any other relevant details that would allow someone to accurately understand and potentially "
77
- "recreate the image. Be as thorough and comprehensive as possible."
78
- ),
79
- },
80
- {"type": "image_url", "image_url": {"url": str(image_url)}},
81
- ],
82
- },
83
- ],
84
- }
85
-
86
- result, error = await self.post("api/v1/chat/completions", payload, context)
87
- if error:
88
- raise ToolException(f"Venice Image Vision API error: {error}")
89
- return result
90
- except ToolException as e:
91
- return {
92
- "success": False,
93
- "error": f"An unexpected error occurred: {str(e)}",
94
- }
95
- except Exception as e:
96
- logger.error(f"Error in {self.name}: {str(e)}")
97
- return {
98
- "success": False,
99
- "error": f"An unexpected error occurred: {str(e)}",
100
- }
1
+ import logging
2
+ from typing import Any, Type
3
+
4
+ from pydantic import BaseModel, HttpUrl
5
+
6
+ from intentkit.skills.base import ToolException
7
+ from intentkit.skills.venice_image.image_vision.image_vision_base import (
8
+ VeniceImageVisionBaseTool,
9
+ )
10
+ from intentkit.skills.venice_image.image_vision.image_vision_input import (
11
+ VeniceImageVision,
12
+ )
13
+ from intentkit.skills.venice_image.utils import fetch_image_as_base64
14
+
15
+ logger = logging.getLogger(__name__)
16
+
17
+
18
+ class ImageVision(VeniceImageVisionBaseTool):
19
+ """
20
+ Describes an image provided via URL using the Venice AI API.
21
+ Ideal for understanding the content of an existing image.
22
+ """
23
+
24
+ name: str = "venice_image_vision"
25
+ description: str = (
26
+ "Describes an image from a URL using Venice AI.\n"
27
+ "Provide the public URL of the image to describe.\n"
28
+ "Returns a descriptive text of the image."
29
+ )
30
+ args_schema: Type[BaseModel] = VeniceImageVision
31
+ # No model_id needed for the generic vision endpoint currently
32
+
33
+ async def _arun(
34
+ self,
35
+ image_url: HttpUrl,
36
+ **kwargs,
37
+ ) -> dict[str, Any]:
38
+ try:
39
+ context = self.get_context()
40
+
41
+ await self.apply_venice_rate_limit(context)
42
+
43
+ image_base64 = await fetch_image_as_base64(image_url)
44
+ if not image_base64:
45
+ error_msg = f"Failed to fetch or validate image from URL: {image_url}"
46
+ logger.error(error_msg)
47
+ return {"success": False, "error": error_msg, "result": None}
48
+
49
+ payload = {
50
+ "model": "qwen-2.5-vl",
51
+ "messages": [
52
+ {
53
+ "role": "system",
54
+ "content": [
55
+ {
56
+ "type": "text",
57
+ "text": (
58
+ "You are an AI model that provides detailed descriptions of images. "
59
+ "When given an image, you must respond with a description that is as comprehensive and detailed as possible. "
60
+ "Focus on identifying all objects, colors, textures, and any other relevant features present in the image. "
61
+ "Provide a thorough and exhaustive account of what is visible in the image."
62
+ ),
63
+ }
64
+ ],
65
+ },
66
+ {
67
+ "role": "user",
68
+ "content": [
69
+ {
70
+ "type": "text",
71
+ "text": (
72
+ "Provide an extremely detailed description of the image, focusing on every discernible aspect. "
73
+ "Include information about objects, colors, textures, lighting conditions, artistic style (if applicable), "
74
+ "composition, and any other relevant details that would allow someone to accurately understand and potentially "
75
+ "recreate the image. Be as thorough and comprehensive as possible."
76
+ ),
77
+ },
78
+ {"type": "image_url", "image_url": {"url": str(image_url)}},
79
+ ],
80
+ },
81
+ ],
82
+ }
83
+
84
+ result, error = await self.post("api/v1/chat/completions", payload, context)
85
+ if error:
86
+ raise ToolException(f"Venice Image Vision API error: {error}")
87
+ return result
88
+ except ToolException as e:
89
+ return {
90
+ "success": False,
91
+ "error": f"An unexpected error occurred: {str(e)}",
92
+ }
93
+ except Exception as e:
94
+ logger.error(f"Error in {self.name}: {str(e)}")
95
+ return {
96
+ "success": False,
97
+ "error": f"An unexpected error occurred: {str(e)}",
98
+ }
@@ -1,7 +1,6 @@
1
1
  import logging
2
2
  from typing import Type
3
3
 
4
- from langchain_core.runnables import RunnableConfig
5
4
  from pydantic import BaseModel, Field
6
5
 
7
6
  from intentkit.skills.web_scraper.base import WebScraperBaseTool
@@ -76,15 +75,14 @@ class DocumentIndexer(WebScraperBaseTool):
76
75
  chunk_size: int = 1000,
77
76
  chunk_overlap: int = 200,
78
77
  tags: str = "",
79
- config: RunnableConfig = None,
80
78
  **kwargs,
81
79
  ) -> str:
82
80
  """Add text content to the vector database."""
83
81
  # Get agent context - throw error if not available
84
- if not config:
85
- raise ValueError("Configuration is required but not provided")
82
+ # Configuration is always available in new runtime
83
+ pass
86
84
 
87
- context = self.context_from_config(config)
85
+ context = self.get_context()
88
86
  if not context or not context.agent_id:
89
87
  raise ValueError("Agent ID is required but not found in configuration")
90
88
 
@@ -1,7 +1,6 @@
1
1
  import logging
2
2
  from typing import List, Type
3
3
 
4
- from langchain_core.runnables import RunnableConfig
5
4
  from pydantic import BaseModel, Field
6
5
 
7
6
  from intentkit.skills.web_scraper.base import WebScraperBaseTool
@@ -75,16 +74,15 @@ class ScrapeAndIndex(WebScraperBaseTool):
75
74
  urls: List[str],
76
75
  chunk_size: int = DEFAULT_CHUNK_SIZE,
77
76
  chunk_overlap: int = DEFAULT_CHUNK_OVERLAP,
78
- config: RunnableConfig = None,
79
77
  **kwargs,
80
78
  ) -> str:
81
79
  """Scrape URLs and index content into vector store."""
82
80
  try:
83
81
  # Get agent context - throw error if not available
84
- if not config:
85
- raise ValueError("Configuration is required but not provided")
82
+ # Configuration is always available in new runtime
83
+ pass
86
84
 
87
- context = self.context_from_config(config)
85
+ context = self.get_context()
88
86
  if not context or not context.agent_id:
89
87
  raise ValueError("Agent ID is required but not found in configuration")
90
88
 
@@ -147,10 +145,10 @@ class ScrapeAndIndex(WebScraperBaseTool):
147
145
  # Extract agent_id for error logging if possible
148
146
  agent_id = "UNKNOWN"
149
147
  try:
150
- if config:
151
- context = self.context_from_config(config)
152
- if context and context.agent_id:
153
- agent_id = context.agent_id
148
+ # TODO: Fix config reference
149
+ context = self.get_context()
150
+ if context and context.agent_id:
151
+ agent_id = context.agent_id
154
152
  except Exception:
155
153
  pass
156
154
 
@@ -177,16 +175,15 @@ class QueryIndexedContent(WebScraperBaseTool):
177
175
  self,
178
176
  query: str,
179
177
  max_results: int = 4,
180
- config: RunnableConfig = None,
181
178
  **kwargs,
182
179
  ) -> str:
183
180
  """Query the indexed content."""
184
181
  try:
185
182
  # Get agent context - throw error if not available
186
- if not config:
187
- raise ValueError("Configuration is required but not provided")
183
+ # Configuration is always available in new runtime
184
+ pass
188
185
 
189
- context = self.context_from_config(config)
186
+ context = self.get_context()
190
187
  if not context or not context.agent_id:
191
188
  raise ValueError("Agent ID is required but not found in configuration")
192
189
 
@@ -249,10 +246,10 @@ class QueryIndexedContent(WebScraperBaseTool):
249
246
  # Extract agent_id for error logging if possible
250
247
  agent_id = "UNKNOWN"
251
248
  try:
252
- if config:
253
- context = self.context_from_config(config)
254
- if context and context.agent_id:
255
- agent_id = context.agent_id
249
+ # TODO: Fix config reference
250
+ context = self.get_context()
251
+ if context and context.agent_id:
252
+ agent_id = context.agent_id
256
253
  except Exception:
257
254
  pass
258
255
 
@@ -4,7 +4,6 @@ from urllib.parse import urljoin, urlparse
4
4
 
5
5
  import httpx
6
6
  import openai
7
- from langchain_core.runnables import RunnableConfig
8
7
  from pydantic import BaseModel, Field
9
8
 
10
9
  from intentkit.skills.web_scraper.base import WebScraperBaseTool
@@ -254,7 +253,7 @@ Extract the URLs now:"""
254
253
  from intentkit.skills.openai.base import OpenAIBaseTool
255
254
 
256
255
  temp_tool = OpenAIBaseTool(skill_store=self.skill_store)
257
- api_key = temp_tool.get_api_key(context)
256
+ api_key = temp_tool.get_api_key()
258
257
 
259
258
  # Initialize OpenAI client
260
259
  client = openai.AsyncOpenAI(api_key=api_key)
@@ -287,7 +286,6 @@ Extract the URLs now:"""
287
286
  chunk_overlap: int = DEFAULT_CHUNK_OVERLAP,
288
287
  include_patterns: List[str] = None,
289
288
  exclude_patterns: List[str] = None,
290
- config: RunnableConfig = None,
291
289
  **kwargs,
292
290
  ) -> str:
293
291
  """Discover website sitemaps, extract URLs with AI, and delegate to scrape_and_index."""
@@ -302,11 +300,11 @@ Extract the URLs now:"""
302
300
  if not parsed_url.netloc:
303
301
  return "Error: Invalid base URL provided. Please provide a valid URL (e.g., https://example.com)"
304
302
 
305
- # Get agent context - throw error if not available
306
- if not config:
303
+ # Get agent context - throw error if not available
304
+ # TODO: Fix config reference
307
305
  raise ValueError("Configuration is required but not provided")
308
306
 
309
- context = self.context_from_config(config)
307
+ context = self.get_context()
310
308
  if not context or not context.agent_id:
311
309
  raise ValueError("Agent ID is required but not found in configuration")
312
310
 
@@ -445,10 +443,10 @@ Extract the URLs now:"""
445
443
  # Extract agent_id for error logging if possible
446
444
  agent_id = "UNKNOWN"
447
445
  try:
448
- if config:
449
- context = self.context_from_config(config)
450
- if context and context.agent_id:
451
- agent_id = context.agent_id
446
+ # TODO: Fix config reference
447
+ context = self.get_context()
448
+ if context and context.agent_id:
449
+ agent_id = context.agent_id
452
450
  except Exception:
453
451
  pass
454
452
 
@@ -0,0 +1,110 @@
1
+ # XMTP Skills
2
+
3
+ This skill category provides capabilities for creating XMTP protocol transaction requests that can be sent to users for signing.
4
+
5
+ ## Features
6
+
7
+ - **xmtp_transfer**: Create ETH or ERC20 token transfer transactions on Base mainnet using XMTP protocol
8
+
9
+ ## Key Innovations
10
+
11
+ This skill category uses a new response format mechanism:
12
+
13
+ - `response_format = "content_and_artifact"` in the base class
14
+ - Skills return a tuple `(content_message, List[ChatMessageAttachment])` instead of just a string
15
+ - The `content_message` is sent to the user as conversational text
16
+ - The `ChatMessageAttachment` list contains XMTP transaction data with type "xmtp"
17
+
18
+ ## Requirements
19
+
20
+ - Agent must be configured for Base mainnet (`network_id: "base-mainnet"`)
21
+ - Agent must have an EVM wallet address configured
22
+ - Only supports Base mainnet (Chain ID: 8453)
23
+
24
+ ## Supported Transfer Types
25
+
26
+ ### ETH Transfers
27
+ - Direct ETH transfers using transaction value
28
+ - No token contract address required
29
+
30
+ ### ERC20 Token Transfers
31
+ - Supports any ERC20 token on Base mainnet
32
+ - Uses `transfer(address,uint256)` function call
33
+ - Requires token contract address
34
+ - Automatically encodes function call data
35
+
36
+ ## Transaction Format
37
+
38
+ ### ETH Transfer Example
39
+ ```json
40
+ {
41
+ "version": "1.0",
42
+ "from": "0x...",
43
+ "chainId": "0x2105",
44
+ "calls": [{
45
+ "to": "0x...",
46
+ "value": "0x16345785d8a0000",
47
+ "data": "0x",
48
+ "metadata": {
49
+ "description": "Send 0.1 ETH to address",
50
+ "transactionType": "transfer",
51
+ "currency": "ETH",
52
+ "amount": 100000000000000000,
53
+ "decimals": 18,
54
+ "toAddress": "0x..."
55
+ }
56
+ }]
57
+ }
58
+ ```
59
+
60
+ ### ERC20 Transfer Example
61
+ ```json
62
+ {
63
+ "version": "1.0",
64
+ "from": "0x...",
65
+ "chainId": "0x2105",
66
+ "calls": [{
67
+ "to": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
68
+ "value": "0x0",
69
+ "data": "0xa9059cbb000000000000000000000000...",
70
+ "metadata": {
71
+ "description": "Send 100 USDC to address",
72
+ "transactionType": "erc20_transfer",
73
+ "currency": "USDC",
74
+ "amount": 100000000,
75
+ "decimals": 6,
76
+ "toAddress": "0x...",
77
+ "tokenContract": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913"
78
+ }
79
+ }]
80
+ }
81
+ ```
82
+
83
+ ## Parameters
84
+
85
+ - `from_address`: Sender's wallet address (must match agent's EVM wallet)
86
+ - `to_address`: Recipient's wallet address
87
+ - `amount`: Amount to transfer (in human-readable format, e.g., "1.5" for 1.5 tokens)
88
+ - `decimals`: Token decimals (18 for ETH, 6 for USDC, etc.)
89
+ - `currency`: Currency symbol ("ETH", "USDC", "DAI", etc.)
90
+ - `token_contract_address`: Token contract address (optional, leave empty for ETH)
91
+
92
+ ## Usage
93
+
94
+ ```python
95
+ # ETH Transfer:
96
+ # from_address: agent's EVM wallet
97
+ # to_address: recipient address
98
+ # amount: "0.1"
99
+ # decimals: 18
100
+ # currency: "ETH"
101
+ # token_contract_address: None
102
+
103
+ # USDC Transfer:
104
+ # from_address: agent's EVM wallet
105
+ # to_address: recipient address
106
+ # amount: "100"
107
+ # decimals: 6
108
+ # currency: "USDC"
109
+ # token_contract_address: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913"
110
+ ```
@@ -0,0 +1,82 @@
1
+ """XMTP skills."""
2
+
3
+ import logging
4
+ from typing import TypedDict
5
+
6
+ from intentkit.abstracts.skill import SkillStoreABC
7
+ from intentkit.skills.base import SkillConfig, SkillOwnerState
8
+ from intentkit.skills.xmtp.base import XmtpBaseTool
9
+ from intentkit.skills.xmtp.transfer import XmtpTransfer
10
+
11
+ # Cache skills at the module level, because they are stateless
12
+ _cache: dict[str, XmtpBaseTool] = {}
13
+
14
+ logger = logging.getLogger(__name__)
15
+
16
+
17
+ class SkillStates(TypedDict):
18
+ xmtp_transfer: SkillOwnerState
19
+
20
+
21
+ class Config(SkillConfig):
22
+ """Configuration for XMTP skills."""
23
+
24
+ states: SkillStates
25
+
26
+
27
+ async def get_skills(
28
+ config: "Config",
29
+ is_private: bool,
30
+ store: SkillStoreABC,
31
+ **_,
32
+ ) -> list[XmtpBaseTool]:
33
+ """Get all XMTP skills.
34
+
35
+ Args:
36
+ config: The configuration for XMTP skills.
37
+ is_private: Whether to include private skills.
38
+ store: The skill store for persisting data.
39
+
40
+ Returns:
41
+ A list of XMTP skills.
42
+ """
43
+ available_skills = []
44
+
45
+ # Include skills based on their state
46
+ for skill_name, state in config["states"].items():
47
+ if state == "disabled":
48
+ continue
49
+ elif state == "public" or (state == "private" and is_private):
50
+ available_skills.append(skill_name)
51
+
52
+ # Get each skill using the cached getter
53
+ result = []
54
+ for name in available_skills:
55
+ skill = get_xmtp_skill(name, store)
56
+ if skill:
57
+ result.append(skill)
58
+ return result
59
+
60
+
61
+ def get_xmtp_skill(
62
+ name: str,
63
+ store: SkillStoreABC,
64
+ ) -> XmtpBaseTool:
65
+ """Get an XMTP skill by name.
66
+
67
+ Args:
68
+ name: The name of the skill to get
69
+ store: The skill store for persisting data
70
+
71
+ Returns:
72
+ The requested XMTP skill
73
+ """
74
+ if name == "xmtp_transfer":
75
+ if name not in _cache:
76
+ _cache[name] = XmtpTransfer(
77
+ skill_store=store,
78
+ )
79
+ return _cache[name]
80
+ else:
81
+ logger.warning(f"Unknown XMTP skill: {name}")
82
+ return None
@@ -0,0 +1,15 @@
1
+ from typing import Literal
2
+
3
+ from intentkit.skills.base import IntentKitSkill
4
+
5
+
6
+ class XmtpBaseTool(IntentKitSkill):
7
+ """Base class for XMTP-related skills."""
8
+
9
+ # Set response format to content_and_artifact for returning tuple
10
+ response_format: Literal["content", "content_and_artifact"] = "content_and_artifact"
11
+
12
+ @property
13
+ def category(self) -> str:
14
+ """Return the skill category."""
15
+ return "xmtp"
@@ -0,0 +1,41 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "type": "object",
4
+ "title": "XMTP",
5
+ "description": "XMTP protocol skills for creating blockchain transaction requests that can be sent to users for signing",
6
+ "x-icon": "https://ai.service.crestal.dev/skills/xmtp/xmtp.svg",
7
+ "x-tags": [
8
+ "XMTP",
9
+ "Blockchain",
10
+ "Transactions",
11
+ "Web3",
12
+ "Base"
13
+ ],
14
+ "properties": {
15
+ "enabled": {
16
+ "type": "boolean",
17
+ "title": "Enabled",
18
+ "description": "Whether this skill is enabled",
19
+ "default": false
20
+ },
21
+ "states": {
22
+ "type": "object",
23
+ "properties": {
24
+ "xmtp_transfer": {
25
+ "type": "string",
26
+ "title": "XMTP Transfer",
27
+ "enum": [
28
+ "disabled",
29
+ "private"
30
+ ],
31
+ "x-enum-title": [
32
+ "Disabled",
33
+ "Agent Owner Only"
34
+ ],
35
+ "description": "Create XMTP transaction requests for transferring ETH or ERC20 tokens on Base mainnet. Supports both native ETH transfers and ERC20 token transfers. Generates wallet_sendCalls transaction data that users can sign.",
36
+ "default": "disabled"
37
+ }
38
+ }
39
+ }
40
+ }
41
+ }