intentkit 0.6.11.dev2__py3-none-any.whl → 0.6.11.dev4__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.

intentkit/__init__.py CHANGED
@@ -3,7 +3,7 @@
3
3
  A powerful platform for building AI agents with blockchain and cryptocurrency capabilities.
4
4
  """
5
5
 
6
- __version__ = "0.6.11-dev2"
6
+ __version__ = "0.6.11-dev4"
7
7
  __author__ = "hyacinthus"
8
8
  __email__ = "hyacinthus@gmail.com"
9
9
 
intentkit/clients/cdp.py CHANGED
@@ -17,6 +17,7 @@ from intentkit.models.agent import Agent
17
17
  from intentkit.models.agent_data import AgentData
18
18
 
19
19
  _clients: Dict[str, "CdpClient"] = {}
20
+ _origin_cdp_client: Optional[OriginCdpClient] = None
20
21
 
21
22
  logger = logging.getLogger(__name__)
22
23
 
@@ -56,6 +57,24 @@ def bip39_seed_to_eth_keys(seed_hex: str) -> Dict[str, str]:
56
57
  }
57
58
 
58
59
 
60
+ def get_origin_cdp_client(skill_store: SkillStoreABC) -> OriginCdpClient:
61
+ global _origin_cdp_client
62
+ if _origin_cdp_client:
63
+ return _origin_cdp_client
64
+
65
+ # Get credentials from skill store system config
66
+ api_key_id = skill_store.get_system_config("cdp_api_key_id")
67
+ api_key_secret = skill_store.get_system_config("cdp_api_key_secret")
68
+ wallet_secret = skill_store.get_system_config("cdp_wallet_secret")
69
+
70
+ _origin_cdp_client = OriginCdpClient(
71
+ api_key_id=api_key_id,
72
+ api_key_secret=api_key_secret,
73
+ wallet_secret=wallet_secret,
74
+ )
75
+ return _origin_cdp_client
76
+
77
+
59
78
  class CdpClient:
60
79
  def __init__(self, agent_id: str, skill_store: SkillStoreABC) -> None:
61
80
  self._agent_id = agent_id
@@ -81,11 +100,7 @@ class CdpClient:
81
100
  # new agent or address not migrated yet
82
101
  if not address:
83
102
  # create cdp client for later use
84
- cdp_client = OriginCdpClient(
85
- api_key_id=api_key_id,
86
- api_key_secret=api_key_secret,
87
- wallet_secret=wallet_secret,
88
- )
103
+ cdp_client = get_origin_cdp_client(self._skill_store)
89
104
  # try migrating from v1 cdp_wallet_data
90
105
  if agent_data.cdp_wallet_data:
91
106
  wallet_data = json.loads(agent_data.cdp_wallet_data)
@@ -115,8 +130,7 @@ class CdpClient:
115
130
  address = new_account.address
116
131
  logger.info("Created new wallet: %s", address)
117
132
 
118
- # close client
119
- await cdp_client.close()
133
+ # do not close cached global client
120
134
  # now it should be created or migrated, store it
121
135
  agent_data.evm_wallet_address = address
122
136
  await agent_data.save()
intentkit/core/engine.py CHANGED
@@ -26,12 +26,10 @@ from langchain_core.messages import (
26
26
  BaseMessage,
27
27
  HumanMessage,
28
28
  )
29
- from langchain_core.prompts import ChatPromptTemplate
30
29
  from langchain_core.tools import BaseTool
31
30
  from langgraph.errors import GraphRecursionError
32
31
  from langgraph.graph.state import CompiledStateGraph
33
32
  from langgraph.prebuilt import create_react_agent
34
- from langgraph.runtime import Runtime
35
33
  from sqlalchemy import func, update
36
34
  from sqlalchemy.exc import SQLAlchemyError
37
35
 
@@ -39,7 +37,10 @@ from intentkit.abstracts.graph import AgentContext, AgentError, AgentState
39
37
  from intentkit.config.config import config
40
38
  from intentkit.core.credit import expense_message, expense_skill
41
39
  from intentkit.core.node import PreModelNode, post_model_node
42
- from intentkit.core.prompt import agent_prompt
40
+ from intentkit.core.prompt import (
41
+ create_formatted_prompt_function,
42
+ explain_prompt,
43
+ )
43
44
  from intentkit.core.skill import skill_store
44
45
  from intentkit.models.agent import Agent, AgentTable
45
46
  from intentkit.models.agent_data import AgentData, AgentQuota
@@ -53,51 +54,13 @@ from intentkit.models.chat import (
53
54
  from intentkit.models.credit import CreditAccount, OwnerType
54
55
  from intentkit.models.db import get_langgraph_checkpointer, get_session
55
56
  from intentkit.models.llm import LLMModelInfo, LLMProvider
56
- from intentkit.models.skill import AgentSkillData, Skill, ThreadSkillData
57
+ from intentkit.models.skill import AgentSkillData, ThreadSkillData
57
58
  from intentkit.models.user import User
58
59
  from intentkit.utils.error import IntentKitAPIError
59
60
 
60
61
  logger = logging.getLogger(__name__)
61
62
 
62
63
 
63
- async def explain_prompt(message: str) -> str:
64
- """
65
- Process message to replace @skill:*:* patterns with (call skill xxxxx) format.
66
-
67
- Args:
68
- message (str): The input message to process
69
-
70
- Returns:
71
- str: The processed message with @skill patterns replaced
72
- """
73
- # Pattern to match @skill:category:config_name with word boundaries
74
- pattern = r"\b@skill:([^:]+):([^\s]+)\b"
75
-
76
- async def replace_skill_pattern(match):
77
- category = match.group(1)
78
- config_name = match.group(2)
79
-
80
- # Get skill by category and config_name
81
- skill = await Skill.get_by_config_name(category, config_name)
82
-
83
- if skill:
84
- return f"(call skill {skill.name})"
85
- else:
86
- # If skill not found, keep original pattern
87
- return match.group(0)
88
-
89
- # Find all matches
90
- matches = list(re.finditer(pattern, message))
91
-
92
- # Process matches in reverse order to maintain string positions
93
- result = message
94
- for match in reversed(matches):
95
- replacement = await replace_skill_pattern(match)
96
- result = result[: match.start()] + replacement + result[match.end() :]
97
-
98
- return result
99
-
100
-
101
64
  # Global variable to cache all agent executors
102
65
  _agents: dict[str, CompiledStateGraph] = {}
103
66
  _private_agents: dict[str, CompiledStateGraph] = {}
@@ -176,106 +139,15 @@ async def create_agent(
176
139
  has_search
177
140
  and llm_model.info.provider == LLMProvider.OPENAI
178
141
  and llm_model.info.supports_search
142
+ and not agent.model.contains(
143
+ "gpt-5"
144
+ ) # tmp disable gpt-5 search since package bugs
179
145
  ):
180
146
  tools.append({"type": "web_search_preview"})
181
147
 
182
- # finally, set up the system prompt
183
- prompt = agent_prompt(agent, agent_data)
184
- # Escape curly braces in the prompt
185
- escaped_prompt = prompt.replace("{", "{{").replace("}", "}}")
186
- # Process message to handle @skill patterns
187
- if config.admin_llm_skill_control:
188
- escaped_prompt = await explain_prompt(escaped_prompt)
189
- prompt_array = [
190
- ("placeholder", "{system_prompt}"),
191
- ("placeholder", "{messages}"),
192
- ]
193
- if agent.prompt_append:
194
- # Escape any curly braces in prompt_append
195
- escaped_append = agent.prompt_append.replace("{", "{{").replace("}", "}}")
196
- # Process message to handle @skill patterns
197
- if config.admin_llm_skill_control:
198
- escaped_append = await explain_prompt(escaped_append)
199
- prompt_array.append(("system", escaped_append))
200
-
201
- prompt_temp = ChatPromptTemplate.from_messages(prompt_array)
202
-
203
- async def formatted_prompt(
204
- state: AgentState, runtime: Runtime[AgentContext]
205
- ) -> list[BaseMessage]:
206
- final_system_prompt = escaped_prompt
207
- context = runtime.context
208
- logger.debug(f"formatted_prompt, context: {context}")
209
- if context.entrypoint:
210
- entrypoint = context.entrypoint
211
- entrypoint_prompt = None
212
- if (
213
- agent.twitter_entrypoint_enabled
214
- and agent.twitter_entrypoint_prompt
215
- and entrypoint == AuthorType.TWITTER.value
216
- ):
217
- entrypoint_prompt = agent.twitter_entrypoint_prompt
218
- logger.debug("twitter entrypoint prompt added")
219
- elif (
220
- agent.telegram_entrypoint_enabled
221
- and agent.telegram_entrypoint_prompt
222
- and entrypoint == AuthorType.TELEGRAM.value
223
- ):
224
- entrypoint_prompt = agent.telegram_entrypoint_prompt
225
- logger.debug("telegram entrypoint prompt added")
226
- elif entrypoint == AuthorType.TRIGGER.value:
227
- task_id = context.chat_id.removeprefix("autonomous-")
228
- # Find the autonomous task by task_id
229
- autonomous_task = None
230
- if agent.autonomous:
231
- for task in agent.autonomous:
232
- if task.id == task_id:
233
- autonomous_task = task
234
- break
235
-
236
- if autonomous_task:
237
- # Build detailed task info - always include task_id
238
- if autonomous_task.name:
239
- task_info = f"You are running an autonomous task '{autonomous_task.name}' (ID: {task_id})"
240
- else:
241
- task_info = (
242
- f"You are running an autonomous task (ID: {task_id})"
243
- )
244
-
245
- # Add description if available
246
- if autonomous_task.description:
247
- task_info += f": {autonomous_task.description}"
248
-
249
- # Add cycle info
250
- if autonomous_task.minutes:
251
- task_info += f". This task runs every {autonomous_task.minutes} minute(s)"
252
- elif autonomous_task.cron:
253
- task_info += (
254
- f". This task runs on schedule: {autonomous_task.cron}"
255
- )
148
+ # Create the formatted_prompt function using the refactored prompt module
149
+ formatted_prompt = create_formatted_prompt_function(agent, agent_data)
256
150
 
257
- entrypoint_prompt = f"{task_info}. "
258
- else:
259
- # Fallback if task not found
260
- entrypoint_prompt = f"You are running an autonomous task. The task id is {task_id}. "
261
- if entrypoint_prompt:
262
- entrypoint_prompt = await explain_prompt(entrypoint_prompt)
263
- final_system_prompt = f"{final_system_prompt}## Entrypoint rules\n\n{entrypoint_prompt}\n\n"
264
- final_system_prompt = f"{final_system_prompt}## Internal Info\n\n"
265
- "These are for your internal use. You can use them when querying or storing data, "
266
- "but please do not directly share this information with users.\n\n"
267
- final_system_prompt = f"{final_system_prompt}chat_id: {context.chat_id}\n\n"
268
- if context.user_id:
269
- final_system_prompt = f"{final_system_prompt}user_id: {context.user_id}\n\n"
270
- system_prompt = [("system", final_system_prompt)]
271
- return prompt_temp.invoke(
272
- {"messages": state["messages"], "system_prompt": system_prompt}
273
- )
274
-
275
- # log final prompt and all skills
276
- logger.debug(
277
- f"[{agent.id}{'-private' if is_private else ''}] init prompt: {escaped_prompt}"
278
- )
279
151
  for tool in tools:
280
152
  logger.info(
281
153
  f"[{agent.id}{'-private' if is_private else ''}] loaded tool: {tool.name if isinstance(tool, BaseTool) else tool}"
intentkit/core/prompt.py CHANGED
@@ -1,57 +1,438 @@
1
+ import re
2
+ from typing import Callable, Optional
3
+
4
+ from eth_utils import is_address
5
+ from langchain_core.messages import BaseMessage
6
+ from langchain_core.prompts import ChatPromptTemplate
7
+ from langgraph.runtime import Runtime
8
+
9
+ from intentkit.abstracts.graph import AgentContext, AgentState
1
10
  from intentkit.config.config import config
2
11
  from intentkit.models.agent import Agent
3
12
  from intentkit.models.agent_data import AgentData
13
+ from intentkit.models.chat import AuthorType
14
+ from intentkit.models.skill import Skill
4
15
 
16
+ # ============================================================================
17
+ # CONSTANTS AND CONFIGURATION
18
+ # ============================================================================
5
19
 
6
- def agent_prompt(agent: Agent, agent_data: AgentData) -> str:
20
+ # Base system prompt components
21
+ BASE_SYSTEM_PROMPT = """You are an AI agent built using IntentKit.
22
+ Your tools are called 'skills'.
23
+ If your skill fails to execute due to a technical error ask the user to try again later, don't retry by yourself. If someone asks you to do something you can't do with your currently available skills, you must say so, recommend them to submit their feedback to the IntentKit team at https://github.com/crestalnetwork/intentkit. Be concise and helpful with your responses."""
24
+
25
+ ENSO_SKILLS_GUIDE = """## ENSO Skills Guide
26
+
27
+ You are integrated with the Enso API. You can use enso_get_tokens to retrieve token information,
28
+ including APY, Protocol Slug, Symbol, Address, Decimals, and underlying tokens. When interacting with token amounts,
29
+ ensure to multiply input amounts by the token's decimal places and divide output amounts by the token's decimals.
30
+ Utilize enso_route_shortcut to find the best swap or deposit route. Set broadcast_request to True only when the
31
+ user explicitly requests a transaction broadcast. Insufficient funds or insufficient spending approval can cause
32
+ Route Shortcut broadcasts to fail. To avoid this, use the enso_broadcast_wallet_approve tool that requires explicit
33
+ user confirmation before broadcasting any approval transactions for security reasons.
34
+
35
+ """
36
+
37
+
38
+ # ============================================================================
39
+ # CORE PROMPT BUILDING FUNCTIONS
40
+ # ============================================================================
41
+
42
+
43
+ def _build_system_header() -> str:
44
+ """Build the system prompt header."""
7
45
  prompt = "# SYSTEM PROMPT\n\n"
8
46
  if config.system_prompt:
9
47
  prompt += config.system_prompt + "\n\n"
10
- prompt += "You are an AI agent built using IntentKit.\n"
11
- prompt += "Your tools are called 'skills'.\n"
12
- prompt += "If your skill fails to execute due to a technical error ask the user to try again later, don't retry by yourself. If someone asks you to do something you can't do with your currently available skills, you must say so, recommend them to submit their feedback to the IntentKit team at https://github.com/crestalnetwork/intentkit. Be concise and helpful with your responses.\n"
48
+ prompt += BASE_SYSTEM_PROMPT + "\n"
49
+ return prompt
50
+
51
+
52
+ def _build_agent_identity_section(agent: Agent) -> str:
53
+ """Build agent identity information section."""
54
+ identity_parts = []
55
+
13
56
  if agent.name:
14
- prompt += f"Your name is {agent.name}.\n"
57
+ identity_parts.append(f"Your name is {agent.name}.")
15
58
  if agent.ticker:
16
- prompt += f"Your ticker symbol is {agent.ticker}.\n"
17
- if agent_data:
18
- if agent_data.twitter_id:
19
- prompt += f"Your twitter id is {agent_data.twitter_id}, never reply or retweet yourself.\n"
20
- if agent_data.twitter_username:
21
- prompt += f"Your twitter username is {agent_data.twitter_username}.\n"
22
- if agent_data.twitter_name:
23
- prompt += f"Your twitter name is {agent_data.twitter_name}.\n"
24
- if agent_data.twitter_is_verified:
25
- prompt += "Your twitter account is verified.\n"
26
- else:
27
- prompt += "Your twitter account is not verified.\n"
28
- if agent_data.telegram_id:
29
- prompt += f"Your telegram bot id is {agent_data.telegram_id}.\n"
30
- if agent_data.telegram_username:
31
- prompt += f"Your telegram bot username is {agent_data.telegram_username}.\n"
32
- if agent_data.telegram_name:
33
- prompt += f"Your telegram bot name is {agent_data.telegram_name}.\n"
34
- # CDP
35
- network_id = agent.network_id or agent.cdp_network_id
36
- if agent_data.evm_wallet_address and network_id != "solana":
37
- prompt += f"Your wallet address in {network_id} is {agent_data.evm_wallet_address} .\n"
38
- if agent_data.solana_wallet_address and network_id == "solana":
39
- prompt += f"Your wallet address in {network_id} is {agent_data.solana_wallet_address} .\n"
40
- prompt += "\n"
59
+ identity_parts.append(f"Your ticker symbol is {agent.ticker}.")
60
+
61
+ return "\n".join(identity_parts) + ("\n" if identity_parts else "")
62
+
63
+
64
+ def _build_social_accounts_section(agent_data: AgentData) -> str:
65
+ """Build social accounts information section."""
66
+ if not agent_data:
67
+ return ""
68
+
69
+ social_parts = []
70
+
71
+ # Twitter info
72
+ if agent_data.twitter_id:
73
+ social_parts.append(
74
+ f"Your twitter id is {agent_data.twitter_id}, never reply or retweet yourself."
75
+ )
76
+ if agent_data.twitter_username:
77
+ social_parts.append(f"Your twitter username is {agent_data.twitter_username}.")
78
+ if agent_data.twitter_name:
79
+ social_parts.append(f"Your twitter name is {agent_data.twitter_name}.")
80
+
81
+ # Twitter verification status
82
+ if agent_data.twitter_is_verified:
83
+ social_parts.append("Your twitter account is verified.")
84
+ else:
85
+ social_parts.append("Your twitter account is not verified.")
86
+
87
+ # Telegram info
88
+ if agent_data.telegram_id:
89
+ social_parts.append(f"Your telegram bot id is {agent_data.telegram_id}.")
90
+ if agent_data.telegram_username:
91
+ social_parts.append(
92
+ f"Your telegram bot username is {agent_data.telegram_username}."
93
+ )
94
+ if agent_data.telegram_name:
95
+ social_parts.append(f"Your telegram bot name is {agent_data.telegram_name}.")
96
+
97
+ return "\n".join(social_parts) + ("\n" if social_parts else "")
98
+
99
+
100
+ def _build_wallet_section(agent: Agent, agent_data: AgentData) -> str:
101
+ """Build wallet information section."""
102
+ if not agent_data:
103
+ return ""
104
+
105
+ wallet_parts = []
106
+ network_id = agent.network_id or agent.cdp_network_id
107
+
108
+ if agent_data.evm_wallet_address and network_id != "solana":
109
+ wallet_parts.append(
110
+ f"Your wallet address in {network_id} is {agent_data.evm_wallet_address}."
111
+ )
112
+ if agent_data.solana_wallet_address and network_id == "solana":
113
+ wallet_parts.append(
114
+ f"Your wallet address in {network_id} is {agent_data.solana_wallet_address}."
115
+ )
116
+
117
+ return "\n".join(wallet_parts) + ("\n" if wallet_parts else "")
118
+
119
+
120
+ def _build_user_info_section(context: AgentContext) -> str:
121
+ """Build user information section when user_id is a valid EVM wallet address."""
122
+ if not context.user_id:
123
+ return ""
124
+
125
+ # Check if user_id is a valid EVM wallet address
126
+ try:
127
+ if is_address(context.user_id):
128
+ return f"## User Info\n\nThe person you are talking to has wallet address: {context.user_id}\n\n"
129
+ except Exception:
130
+ # If validation fails, don't include the section
131
+ pass
132
+
133
+ return ""
134
+
135
+
136
+ def _build_agent_characteristics_section(agent: Agent) -> str:
137
+ """Build agent characteristics section (purpose, personality, principles, etc.)."""
138
+ sections = []
139
+
41
140
  if agent.purpose:
42
- prompt += f"## Purpose\n\n{agent.purpose}\n\n"
141
+ sections.append(f"## Purpose\n\n{agent.purpose}")
43
142
  if agent.personality:
44
- prompt += f"## Personality\n\n{agent.personality}\n\n"
143
+ sections.append(f"## Personality\n\n{agent.personality}")
45
144
  if agent.principles:
46
- prompt += f"## Principles\n\n{agent.principles}\n\n"
145
+ sections.append(f"## Principles\n\n{agent.principles}")
47
146
  if agent.prompt:
48
- prompt += f"## Initial Rules\n\n{agent.prompt}\n\n"
147
+ sections.append(f"## Initial Rules\n\n{agent.prompt}")
148
+
149
+ return "\n\n".join(sections) + ("\n\n" if sections else "")
150
+
151
+
152
+ def _build_skills_guides_section(agent: Agent) -> str:
153
+ """Build skills-specific guides section."""
154
+ guides = []
155
+
156
+ # ENSO skills guide
49
157
  if agent.skills and "enso" in agent.skills and agent.skills["enso"].get("enabled"):
50
- prompt += """## ENSO Skills Guide\n\nYou are integrated with the Enso API. You can use enso_get_tokens to retrieve token information,
51
- including APY, Protocol Slug, Symbol, Address, Decimals, and underlying tokens. When interacting with token amounts,
52
- ensure to multiply input amounts by the token's decimal places and divide output amounts by the token's decimals.
53
- Utilize enso_route_shortcut to find the best swap or deposit route. Set broadcast_request to True only when the
54
- user explicitly requests a transaction broadcast. Insufficient funds or insufficient spending approval can cause
55
- Route Shortcut broadcasts to fail. To avoid this, use the enso_broadcast_wallet_approve tool that requires explicit
56
- user confirmation before broadcasting any approval transactions for security reasons.\n\n"""
57
- return prompt
158
+ guides.append(ENSO_SKILLS_GUIDE)
159
+
160
+ return "".join(guides)
161
+
162
+
163
+ def build_agent_prompt(agent: Agent, agent_data: AgentData) -> str:
164
+ """
165
+ Build the complete agent system prompt.
166
+
167
+ This function orchestrates the building of different prompt sections:
168
+ - System header and base prompt
169
+ - Agent identity (name, ticker)
170
+ - Social accounts (Twitter, Telegram)
171
+ - Wallet information
172
+ - Agent characteristics (purpose, personality, principles)
173
+ - Skills-specific guides
174
+
175
+ Args:
176
+ agent: The agent configuration
177
+ agent_data: The agent's runtime data
178
+
179
+ Returns:
180
+ str: The complete system prompt
181
+ """
182
+ prompt_sections = [
183
+ _build_system_header(),
184
+ _build_agent_identity_section(agent),
185
+ _build_social_accounts_section(agent_data),
186
+ _build_wallet_section(agent, agent_data),
187
+ "\n", # Add spacing before characteristics
188
+ _build_agent_characteristics_section(agent),
189
+ _build_skills_guides_section(agent),
190
+ ]
191
+
192
+ return "".join(section for section in prompt_sections if section)
193
+
194
+
195
+ # Legacy function name for backward compatibility
196
+ def agent_prompt(agent: Agent, agent_data: AgentData) -> str:
197
+ """Legacy function name. Use build_agent_prompt instead."""
198
+ return build_agent_prompt(agent, agent_data)
199
+
200
+
201
+ async def explain_prompt(message: str) -> str:
202
+ """
203
+ Process message to replace @skill:*:* patterns with (call skill xxxxx) format.
204
+ This function is used when admin_llm_skill_control is enabled.
205
+
206
+ Args:
207
+ message (str): The input message to process
208
+
209
+ Returns:
210
+ str: The processed message with @skill patterns replaced
211
+ """
212
+ # Pattern to match @skill:category:config_name with word boundaries
213
+ pattern = r"\b@skill:([^:]+):([^\s]+)\b"
214
+
215
+ async def replace_skill_pattern(match):
216
+ category = match.group(1)
217
+ config_name = match.group(2)
218
+
219
+ # Get skill by category and config_name
220
+ skill = await Skill.get_by_config_name(category, config_name)
221
+
222
+ if skill:
223
+ return f"(call skill {skill.name})"
224
+ else:
225
+ # If skill not found, keep original pattern
226
+ return match.group(0)
227
+
228
+ # Find all matches
229
+ matches = list(re.finditer(pattern, message))
230
+
231
+ # Process matches in reverse order to maintain string positions
232
+ result = message
233
+ for match in reversed(matches):
234
+ replacement = await replace_skill_pattern(match)
235
+ result = result[: match.start()] + replacement + result[match.end() :]
236
+
237
+ return result
238
+
239
+
240
+ # ============================================================================
241
+ # UTILITY FUNCTIONS
242
+ # ============================================================================
243
+
244
+
245
+ def escape_prompt(prompt: str) -> str:
246
+ """Escape curly braces in the prompt for template processing."""
247
+ return prompt.replace("{", "{{").replace("}", "}}")
248
+
249
+
250
+ # ============================================================================
251
+ # ENTRYPOINT PROCESSING FUNCTIONS
252
+ # ============================================================================
253
+
254
+
255
+ def _build_social_entrypoint_prompt(agent: Agent, entrypoint: str) -> Optional[str]:
256
+ """Build prompt for social media entrypoints (Twitter, Telegram)."""
257
+ if (
258
+ agent.twitter_entrypoint_enabled
259
+ and agent.twitter_entrypoint_prompt
260
+ and entrypoint == AuthorType.TWITTER.value
261
+ ):
262
+ return agent.twitter_entrypoint_prompt
263
+ elif (
264
+ agent.telegram_entrypoint_enabled
265
+ and agent.telegram_entrypoint_prompt
266
+ and entrypoint == AuthorType.TELEGRAM.value
267
+ ):
268
+ return agent.telegram_entrypoint_prompt
269
+ return None
270
+
271
+
272
+ def _build_autonomous_task_prompt(agent: Agent, context: AgentContext) -> str:
273
+ """Build prompt for autonomous task entrypoint."""
274
+ task_id = context.chat_id.removeprefix("autonomous-")
275
+
276
+ # Find the autonomous task by task_id
277
+ autonomous_task = None
278
+ if agent.autonomous:
279
+ for task in agent.autonomous:
280
+ if task.id == task_id:
281
+ autonomous_task = task
282
+ break
283
+
284
+ if not autonomous_task:
285
+ # Fallback if task not found
286
+ return f"You are running an autonomous task. The task id is {task_id}. "
287
+
288
+ # Build detailed task info - always include task_id
289
+ if autonomous_task.name:
290
+ task_info = f"You are running an autonomous task '{autonomous_task.name}' (ID: {task_id})"
291
+ else:
292
+ task_info = f"You are running an autonomous task (ID: {task_id})"
293
+
294
+ # Add description if available
295
+ if autonomous_task.description:
296
+ task_info += f": {autonomous_task.description}"
297
+
298
+ # Add cycle info
299
+ if autonomous_task.minutes:
300
+ task_info += f". This task runs every {autonomous_task.minutes} minute(s)"
301
+ elif autonomous_task.cron:
302
+ task_info += f". This task runs on schedule: {autonomous_task.cron}"
303
+
304
+ return f"{task_info}. "
305
+
306
+
307
+ async def build_entrypoint_prompt(agent: Agent, context: AgentContext) -> Optional[str]:
308
+ """
309
+ Build entrypoint-specific prompt based on context.
310
+
311
+ Supports different entrypoint types:
312
+ - Twitter: Uses agent.twitter_entrypoint_prompt
313
+ - Telegram: Uses agent.telegram_entrypoint_prompt
314
+ - Autonomous tasks: Builds task-specific prompt with scheduling info
315
+
316
+ Args:
317
+ agent: The agent configuration
318
+ context: The agent context containing entrypoint information
319
+
320
+ Returns:
321
+ Optional[str]: The entrypoint-specific prompt, or None if no entrypoint
322
+ """
323
+ if not context.entrypoint:
324
+ return None
325
+
326
+ entrypoint = context.entrypoint
327
+ entrypoint_prompt = None
328
+
329
+ # Handle social media entrypoints
330
+ entrypoint_prompt = _build_social_entrypoint_prompt(agent, entrypoint)
331
+
332
+ # Handle autonomous task entrypoint
333
+ if not entrypoint_prompt and entrypoint == AuthorType.TRIGGER.value:
334
+ entrypoint_prompt = _build_autonomous_task_prompt(agent, context)
335
+
336
+ # Process with admin LLM skill control if enabled
337
+ if entrypoint_prompt and config.admin_llm_skill_control:
338
+ entrypoint_prompt = await explain_prompt(entrypoint_prompt)
339
+
340
+ return entrypoint_prompt
341
+
342
+
343
+ def build_internal_info_prompt(context: AgentContext) -> str:
344
+ """Build internal info prompt with context information."""
345
+ internal_info = "## Internal Info\n\n"
346
+ internal_info += "These are for your internal use. You can use them when querying or storing data, "
347
+ internal_info += "but please do not directly share this information with users.\n\n"
348
+ internal_info += f"chat_id: {context.chat_id}\n\n"
349
+ if context.user_id:
350
+ internal_info += f"user_id: {context.user_id}\n\n"
351
+ return internal_info
352
+
353
+
354
+ # ============================================================================
355
+ # MAIN PROMPT FACTORY FUNCTION
356
+ # ============================================================================
357
+
358
+
359
+ def create_formatted_prompt_function(agent: Agent, agent_data: AgentData) -> Callable:
360
+ """
361
+ Create the formatted_prompt function with agent-specific configuration.
362
+
363
+ This is the main factory function that creates a prompt formatting function
364
+ tailored to a specific agent. The returned function will be used by the
365
+ agent's runtime to format prompts for each conversation.
366
+
367
+ Args:
368
+ agent: The agent configuration
369
+ agent_data: The agent's runtime data
370
+
371
+ Returns:
372
+ Callable: An async function that formats prompts based on agent state and context
373
+ """
374
+ # Build base prompt using the new function name
375
+ prompt = build_agent_prompt(agent, agent_data)
376
+ escaped_prompt = escape_prompt(prompt)
377
+
378
+ # Process with admin LLM skill control if enabled
379
+ async def get_base_prompt():
380
+ if config.admin_llm_skill_control:
381
+ return await explain_prompt(escaped_prompt)
382
+ return escaped_prompt
383
+
384
+ # Build prompt array
385
+ prompt_array = [
386
+ ("placeholder", "{system_prompt}"),
387
+ ("placeholder", "{messages}"),
388
+ ]
389
+
390
+ if agent.prompt_append:
391
+ # Escape any curly braces in prompt_append
392
+ escaped_append = escape_prompt(agent.prompt_append)
393
+ prompt_array.append(("system", escaped_append))
394
+
395
+ prompt_temp = ChatPromptTemplate.from_messages(prompt_array)
396
+
397
+ async def formatted_prompt(
398
+ state: AgentState, runtime: Runtime[AgentContext]
399
+ ) -> list[BaseMessage]:
400
+ # Get base prompt (with potential admin LLM skill control processing)
401
+ final_system_prompt = await get_base_prompt()
402
+
403
+ context = runtime.context
404
+
405
+ # Add entrypoint prompt if applicable
406
+ entrypoint_prompt = await build_entrypoint_prompt(agent, context)
407
+ if entrypoint_prompt:
408
+ final_system_prompt = (
409
+ f"{final_system_prompt}## Entrypoint rules\n\n{entrypoint_prompt}\n\n"
410
+ )
411
+
412
+ # Add user info if user_id is a valid EVM wallet address
413
+ user_info = _build_user_info_section(context)
414
+ if user_info:
415
+ final_system_prompt = f"{final_system_prompt}{user_info}"
416
+
417
+ # Add internal info
418
+ internal_info = build_internal_info_prompt(context)
419
+ final_system_prompt = f"{final_system_prompt}{internal_info}"
420
+
421
+ # Process prompt_append with admin LLM skill control if needed
422
+ if agent.prompt_append and config.admin_llm_skill_control:
423
+ # Find the system message in prompt_array and process it
424
+ for i, (role, content) in enumerate(prompt_array):
425
+ if role == "system":
426
+ processed_append = await explain_prompt(content)
427
+ prompt_array[i] = ("system", processed_append)
428
+ break
429
+
430
+ system_prompt = [("system", final_system_prompt)]
431
+ return prompt_temp.invoke(
432
+ {
433
+ "messages": state["messages"],
434
+ "system_prompt": system_prompt,
435
+ }
436
+ )
437
+
438
+ return formatted_prompt
intentkit/models/llm.py CHANGED
@@ -623,14 +623,15 @@ class DeepseekLLM(LLMModel):
623
623
  async def create_instance(self, config: Any) -> LanguageModelLike:
624
624
  """Create and return a ChatDeepseek instance."""
625
625
 
626
- from langchain_openai import ChatOpenAI
626
+ from langchain_deepseek import ChatDeepSeek
627
627
 
628
628
  info = await self.model_info()
629
629
 
630
630
  kwargs = {
631
- "model_name": self.model_name,
632
- "openai_api_key": config.deepseek_api_key,
631
+ "model": self.model_name,
632
+ "api_key": config.deepseek_api_key,
633
633
  "timeout": info.timeout,
634
+ "max_retries": 3,
634
635
  }
635
636
 
636
637
  # Add optional parameters based on model support
@@ -644,9 +645,9 @@ class DeepseekLLM(LLMModel):
644
645
  kwargs["presence_penalty"] = self.presence_penalty
645
646
 
646
647
  if info.api_base:
647
- kwargs["openai_api_base"] = info.api_base
648
+ kwargs["api_base"] = info.api_base
648
649
 
649
- return ChatOpenAI(**kwargs)
650
+ return ChatDeepSeek(**kwargs)
650
651
 
651
652
 
652
653
  class XAILLM(LLMModel):
@@ -6,6 +6,8 @@ from typing import TypedDict
6
6
  from intentkit.abstracts.skill import SkillStoreABC
7
7
  from intentkit.skills.base import SkillConfig, SkillState
8
8
  from intentkit.skills.xmtp.base import XmtpBaseTool
9
+ from intentkit.skills.xmtp.price import XmtpGetSwapPrice
10
+ from intentkit.skills.xmtp.swap import XmtpSwap
9
11
  from intentkit.skills.xmtp.transfer import XmtpTransfer
10
12
 
11
13
  # Cache skills at the module level, because they are stateless
@@ -16,6 +18,8 @@ logger = logging.getLogger(__name__)
16
18
 
17
19
  class SkillStates(TypedDict):
18
20
  xmtp_transfer: SkillState
21
+ xmtp_swap: SkillState
22
+ xmtp_get_swap_price: SkillState
19
23
 
20
24
 
21
25
  class Config(SkillConfig):
@@ -77,6 +81,18 @@ def get_xmtp_skill(
77
81
  skill_store=store,
78
82
  )
79
83
  return _cache[name]
84
+ elif name == "xmtp_swap":
85
+ if name not in _cache:
86
+ _cache[name] = XmtpSwap(
87
+ skill_store=store,
88
+ )
89
+ return _cache[name]
90
+ elif name == "xmtp_get_swap_price":
91
+ if name not in _cache:
92
+ _cache[name] = XmtpGetSwapPrice(
93
+ skill_store=store,
94
+ )
95
+ return _cache[name]
80
96
  else:
81
97
  logger.warning(f"Unknown XMTP skill: {name}")
82
98
  return None
@@ -0,0 +1,72 @@
1
+ from typing import Literal, Type
2
+
3
+ from pydantic import BaseModel, Field
4
+
5
+ from intentkit.clients.cdp import get_origin_cdp_client
6
+ from intentkit.skills.xmtp.base import XmtpBaseTool
7
+
8
+
9
+ class SwapPriceInput(BaseModel):
10
+ """Input for querying swap price via CDP."""
11
+
12
+ from_token: str = Field(description="The contract address to swap from")
13
+ to_token: str = Field(description="The contract address to swap to")
14
+ from_amount: str = Field(description="Input amount in smallest units (as string)")
15
+ from_address: str = Field(
16
+ description="The address where the from_token balance is located"
17
+ )
18
+
19
+
20
+ class XmtpGetSwapPrice(XmtpBaseTool):
21
+ """Skill for fetching indicative swap price using CDP SDK."""
22
+
23
+ name: str = "xmtp_get_swap_price"
24
+ description: str = "Get an indicative swap price/quote for token pair and amount on Base networks using CDP."
25
+ response_format: Literal["content", "content_and_artifact"] = "content"
26
+ args_schema: Type[BaseModel] = SwapPriceInput
27
+
28
+ async def _arun(
29
+ self,
30
+ from_token: str,
31
+ to_token: str,
32
+ from_amount: str,
33
+ from_address: str,
34
+ ) -> str:
35
+ context = self.get_context()
36
+ agent = context.agent
37
+
38
+ if agent.network_id not in ("base-mainnet", "base-sepolia"):
39
+ raise ValueError(
40
+ f"Swap price only supported on base-mainnet or base-sepolia. Current: {agent.network_id}"
41
+ )
42
+
43
+ network_for_cdp = {
44
+ "base-mainnet": "base",
45
+ "base-sepolia": "base-sepolia",
46
+ }[agent.network_id]
47
+
48
+ cdp_client = get_origin_cdp_client(self.skill_store)
49
+ # Note: Don't use async with context manager as get_origin_cdp_client returns a managed global client
50
+ price = await cdp_client.evm.get_swap_price(
51
+ from_token=from_token,
52
+ to_token=to_token,
53
+ from_amount=str(from_amount),
54
+ network=network_for_cdp,
55
+ taker=from_address,
56
+ )
57
+
58
+ # Try to format a readable message from typical fields
59
+ try:
60
+ amount_out = getattr(price, "to_amount", None) or (
61
+ price.get("to_amount") if isinstance(price, dict) else None
62
+ )
63
+ route = getattr(price, "route", None) or (
64
+ price.get("route") if isinstance(price, dict) else None
65
+ )
66
+ route_str = f" via {route}" if route else ""
67
+ if amount_out:
68
+ return f"Estimated output: {amount_out} units of {to_token}{route_str} on {agent.network_id}."
69
+ except Exception:
70
+ pass
71
+
72
+ return f"Swap price result (raw): {price}"
@@ -0,0 +1,204 @@
1
+ from typing import List, Tuple, Type
2
+
3
+ from pydantic import BaseModel, Field
4
+
5
+ from intentkit.clients.cdp import get_origin_cdp_client
6
+ from intentkit.models.chat import ChatMessageAttachment, ChatMessageAttachmentType
7
+ from intentkit.skills.xmtp.base import XmtpBaseTool
8
+
9
+
10
+ class SwapInput(BaseModel):
11
+ """Input for XMTP swap skill.
12
+
13
+ This creates an unsigned swap transaction attachment using CDP swap quote
14
+ that a user can review and sign via XMTP wallet_sendCalls.
15
+ """
16
+
17
+ from_address: str = Field(description="The sender address for the swap")
18
+ from_token: str = Field(
19
+ description="The contract address of the token to swap from"
20
+ )
21
+ to_token: str = Field(description="The contract address of the token to swap to")
22
+ from_amount: str = Field(
23
+ description="The input amount in the smallest unit of from_token (as string)"
24
+ )
25
+ slippage_bps: int = Field(
26
+ default=100,
27
+ description="Maximum slippage in basis points (100 = 1%). Defaults to 100.",
28
+ )
29
+
30
+
31
+ class XmtpSwap(XmtpBaseTool):
32
+ """Skill for creating XMTP swap transactions using CDP swap quote.
33
+
34
+ Generates a wallet_sendCalls transaction request to perform a token swap.
35
+ May include an ERC20 approval call followed by the router swap call.
36
+ Supports Base mainnet and Base Sepolia testnet.
37
+ """
38
+
39
+ name: str = "xmtp_swap"
40
+ description: str = (
41
+ "Create an XMTP transaction request for swapping tokens on Base using CDP swap quote. "
42
+ "Returns a wallet_sendCalls payload that can include an optional approval call and the swap call. "
43
+ "Only supports base-mainnet and base-sepolia."
44
+ )
45
+ args_schema: Type[BaseModel] = SwapInput
46
+
47
+ async def _arun(
48
+ self,
49
+ from_address: str,
50
+ from_token: str,
51
+ to_token: str,
52
+ from_amount: str,
53
+ slippage_bps: int = 100,
54
+ ) -> Tuple[str, List[ChatMessageAttachment]]:
55
+ # Resolve agent context and target network
56
+ context = self.get_context()
57
+ agent = context.agent
58
+
59
+ # ChainId mapping for XMTP wallet_sendCalls
60
+ chain_id_hex_by_network = {
61
+ "base-mainnet": "0x2105", # 8453
62
+ "base-sepolia": "0x14A34", # 84532
63
+ }
64
+
65
+ if agent.network_id not in chain_id_hex_by_network:
66
+ raise ValueError(
67
+ f"XMTP swap only supports base-mainnet or base-sepolia. Current agent network: {agent.network_id}"
68
+ )
69
+
70
+ chain_id_hex = chain_id_hex_by_network[agent.network_id]
71
+
72
+ # CDP network mapping for swap quote API
73
+ # Reference: CDP SDK examples for swap quote and price
74
+ # https://github.com/coinbase/cdp-sdk/blob/main/examples/python/evm/swaps/create_swap_quote.py
75
+ network_for_cdp = {
76
+ "base-mainnet": "base",
77
+ "base-sepolia": "base-sepolia",
78
+ }[agent.network_id]
79
+
80
+ # Get CDP client from global origin helper (server-side credentials)
81
+ cdp_client = get_origin_cdp_client(self.skill_store)
82
+
83
+ # Call CDP to create swap quote and extract call datas
84
+ # Be permissive with response shape across SDK versions
85
+ try:
86
+ # Attempt the canonical method per CDP SDK examples
87
+ # create_swap_quote(from_token, to_token, from_amount, network, taker, slippage_bps, signer_address)
88
+ # Note: Don't use async with context manager as get_origin_cdp_client returns a managed global client
89
+ quote = await cdp_client.evm.create_swap_quote(
90
+ from_token=from_token,
91
+ to_token=to_token,
92
+ from_amount=str(from_amount),
93
+ network=network_for_cdp,
94
+ taker=from_address,
95
+ slippage_bps=slippage_bps,
96
+ signer_address=from_address,
97
+ )
98
+ except Exception as e: # pragma: no cover - defensive
99
+ raise ValueError(f"Failed to create swap quote via CDP: {e!s}")
100
+
101
+ # Extract approval and swap calls if present (prefer QuoteSwapResult canonical fields)
102
+ calls: list[dict] = []
103
+
104
+ def to_xmtp_call(call_like, description: str) -> dict | None:
105
+ if not call_like:
106
+ return None
107
+ # Attributes on QuoteSwapResult call-like objects
108
+ to_value = getattr(call_like, "to", None) or getattr(
109
+ call_like, "target", None
110
+ )
111
+ data_value = getattr(call_like, "data", None) or getattr(
112
+ call_like, "calldata", None
113
+ )
114
+ value_value = getattr(call_like, "value", None)
115
+ # Dict fallback
116
+ if isinstance(call_like, dict):
117
+ to_value = to_value or call_like.get("to") or call_like.get("target")
118
+ data_value = (
119
+ data_value or call_like.get("data") or call_like.get("calldata")
120
+ )
121
+ value_value = value_value or call_like.get("value")
122
+ if not to_value or not data_value:
123
+ return None
124
+ value_hex = (
125
+ value_value
126
+ if isinstance(value_value, str) and value_value.startswith("0x")
127
+ else (hex(int(value_value)) if value_value is not None else "0x0")
128
+ )
129
+ data_hex = (
130
+ data_value if str(data_value).startswith("0x") else f"0x{data_value}"
131
+ )
132
+ return {
133
+ "to": to_value,
134
+ "value": value_hex,
135
+ "data": data_hex,
136
+ "metadata": {
137
+ "description": description,
138
+ "transactionType": "swap_step",
139
+ "fromToken": from_token,
140
+ "toToken": to_token,
141
+ "amountIn": from_amount,
142
+ "slippageBps": slippage_bps,
143
+ },
144
+ }
145
+
146
+ # Heuristics for various response shapes
147
+ approval = (
148
+ getattr(quote, "approval", None)
149
+ or getattr(quote, "approval_call_data", None)
150
+ or (quote.get("approval") if isinstance(quote, dict) else None)
151
+ or (quote.get("approval_call_data") if isinstance(quote, dict) else None)
152
+ )
153
+ approval_xmtp = to_xmtp_call(approval, "Approve token spending if required")
154
+ if approval_xmtp:
155
+ calls.append(approval_xmtp)
156
+
157
+ swap_call = (
158
+ getattr(quote, "swap", None)
159
+ or getattr(quote, "swap_call_data", None)
160
+ or (quote.get("swap") if isinstance(quote, dict) else None)
161
+ or (quote.get("swap_call_data") if isinstance(quote, dict) else None)
162
+ )
163
+ swap_xmtp = to_xmtp_call(swap_call, "Execute token swap")
164
+ if swap_xmtp:
165
+ calls.append(swap_xmtp)
166
+
167
+ if not calls:
168
+ # As a final fallback, some responses may provide a generic 'calls' list
169
+ raw_calls = getattr(quote, "calls", None) or (
170
+ quote.get("calls") if isinstance(quote, dict) else None
171
+ )
172
+ if isinstance(raw_calls, list):
173
+ for idx, c in enumerate(raw_calls):
174
+ x = to_xmtp_call(c, f"Swap step {idx + 1}")
175
+ if x:
176
+ calls.append(x)
177
+
178
+ if not calls:
179
+ raise ValueError(
180
+ "CDP swap quote did not return callable steps compatible with wallet_sendCalls"
181
+ )
182
+
183
+ # Build XMTP wallet_sendCalls payload
184
+ wallet_send_calls = {
185
+ "version": "1.0",
186
+ "from": from_address,
187
+ "chainId": chain_id_hex,
188
+ "calls": calls,
189
+ }
190
+
191
+ # Attachment for chat
192
+ attachment: ChatMessageAttachment = {
193
+ "type": ChatMessageAttachmentType.XMTP,
194
+ "url": None,
195
+ "json": wallet_send_calls,
196
+ }
197
+
198
+ # Human-friendly message
199
+ content_message = (
200
+ f"I created a swap transaction request to exchange {from_amount} units of {from_token} "
201
+ f"for {to_token} on {agent.network_id}. Review and sign to execute."
202
+ )
203
+
204
+ return content_message, [attachment]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: intentkit
3
- Version: 0.6.11.dev2
3
+ Version: 0.6.11.dev4
4
4
  Summary: Intent-based AI Agent Platform - Core Package
5
5
  Project-URL: Homepage, https://github.com/crestal-network/intentkit
6
6
  Project-URL: Repository, https://github.com/crestal-network/intentkit
@@ -58,6 +58,7 @@ Requires-Dist: httpx>=0.28.1
58
58
  Requires-Dist: jsonref>=1.1.0
59
59
  Requires-Dist: langchain-community>=0.3.19
60
60
  Requires-Dist: langchain-core>=0.3.43
61
+ Requires-Dist: langchain-deepseek>=0.1.4
61
62
  Requires-Dist: langchain-mcp-adapters>=0.0.11
62
63
  Requires-Dist: langchain-openai>=0.3.8
63
64
  Requires-Dist: langchain-text-splitters>=0.3.8
@@ -1,4 +1,4 @@
1
- intentkit/__init__.py,sha256=bHsUzcFd-XdSi9M2iFW-p1x3-hntZ5xAoxpEA4kIadU,384
1
+ intentkit/__init__.py,sha256=-53Aff4TSlAzOvvA-Mqb5kjEZiXmV8lAoboQ8zJCUm0,384
2
2
  intentkit/abstracts/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
3
3
  intentkit/abstracts/agent.py,sha256=108gb5W8Q1Sy4G55F2_ZFv2-_CnY76qrBtpIr0Oxxqk,1489
4
4
  intentkit/abstracts/api.py,sha256=ZUc24vaQvQVbbjznx7bV0lbbQxdQPfEV8ZxM2R6wZWo,166
@@ -8,7 +8,7 @@ intentkit/abstracts/graph.py,sha256=8jkQnm6pnUAyiU7w5dpe2RuSLvpXBN17NGqZGEuc0ys,
8
8
  intentkit/abstracts/skill.py,sha256=cIJ6BkASD31U1IEkE8rdAawq99w_xsg0lt3oalqa1ZA,5071
9
9
  intentkit/abstracts/twitter.py,sha256=cEtP7ygR_b-pHdc9i8kBuyooz1cPoGUGwsBHDpowJyY,1262
10
10
  intentkit/clients/__init__.py,sha256=sQ_6_bRC2MPWLPH-skQ3qsEe8ce-dUGL7i8VJOautHg,298
11
- intentkit/clients/cdp.py,sha256=_CkvnBkzdq7-sFMGct4lz85FpaOoHxOGstWubhClzrA,5921
11
+ intentkit/clients/cdp.py,sha256=VaIFzgfqpq5H4bHbFBe8UPabhkwe8s5pge_P5FssGqU,6453
12
12
  intentkit/clients/twitter.py,sha256=Lfa7srHOFnY96SXcElW0jfg7XKS_WliWnXjPZEe6SQc,18976
13
13
  intentkit/config/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
14
14
  intentkit/config/config.py,sha256=Q3xsxRvn0RxcPpAOHbHcPumTRlJQMcE_pfnPfAl6aXw,8553
@@ -17,9 +17,9 @@ intentkit/core/agent.py,sha256=GIKDn1dTenIHWMRxe-ud7hd1cQaHzbTDdypy5IAgPfU,16658
17
17
  intentkit/core/api.py,sha256=3GIMJpwduLUSbVPNW6mQVxZncYHH3OlLwdiqFtYtEAE,1208
18
18
  intentkit/core/client.py,sha256=rIwtJVVm-7piXtFNDbeykt9vWdNTecgjW0aA3N-lHnM,1495
19
19
  intentkit/core/credit.py,sha256=Y5cjShFFqGBhz7Uc_ziqejyW-2FP58TYxsSNc4N_6hI,63039
20
- intentkit/core/engine.py,sha256=ibaPSez5IuNys5AT3xifBsKUwNElFiJMmsXCjuziRdE,43555
20
+ intentkit/core/engine.py,sha256=qfxcpNHZu8PxdiCFyQueK2zkr8w1FQ58jShUzAxrl1M,38018
21
21
  intentkit/core/node.py,sha256=7h9zgDSd928bzUi3m3EZnKkhbwqlbRAQUr_uz7gKB5Y,8880
22
- intentkit/core/prompt.py,sha256=RfLhlUktkB2kCr3wfldqq6ZP2l8heZIMc8jVp31KIyQ,3631
22
+ intentkit/core/prompt.py,sha256=eaa2FyFptpsWlEGWCL8jVmZMGB6Pl9hBnt-y8Yq37RU,16041
23
23
  intentkit/core/skill.py,sha256=vPK37sDRT9kzkMBymPwqZ5uEdxTTRtb_DfREIeyz-Xw,5788
24
24
  intentkit/models/agent.py,sha256=pKeafRhmFMJwuIEBJkBEIl_oEu_CQ5AkvsBVfNfoxuQ,67168
25
25
  intentkit/models/agent_data.py,sha256=mVsiK8TziYa1W1ujU1KwI9osIVIeSM7XJEogGRL1WVU,28263
@@ -32,7 +32,7 @@ intentkit/models/credit.py,sha256=bcasHyrCwforLGrH8ZWEvN6y6ml7NeAFrGl8cfqvqbI,42
32
32
  intentkit/models/db.py,sha256=nuDX6NEtnfD5YLr2iVpAAXsgHbSpG5diqfLC-PkHsA4,4406
33
33
  intentkit/models/db_mig.py,sha256=vT6Tanm-BHC2T7dTztuB1UG494EFBAlHADKsNzR6xaQ,3577
34
34
  intentkit/models/generator.py,sha256=lyZu9U9rZUGkqd_QT5SAhay9DY358JJY8EhDSpN8I1M,10298
35
- intentkit/models/llm.py,sha256=TTTuea6f9aFxmS1X1SVUuMhvTh5rvIiqMn9A14lFxPY,26911
35
+ intentkit/models/llm.py,sha256=BgmtW6Vq7ZLZN33HG37VWGg_e8YlF8GxrO9WEvKCE-A,26928
36
36
  intentkit/models/redis.py,sha256=UoN8jqLREO1VO9_w6m-JhldpP19iEHj4TiGVCMutQW4,3702
37
37
  intentkit/models/skill.py,sha256=h_2wtKEbYE29TLsMdaSnjfOv6vXY6GwMU_abw-ONX28,16374
38
38
  intentkit/models/user.py,sha256=P7l6LOsZmXZ5tDPTczTbqDtDB_MKc_9_ddZkAB2npPk,9288
@@ -388,9 +388,11 @@ intentkit/skills/web_scraper/scrape_and_index.py,sha256=Xi1BmUd-LgmJXnQOgSz82baq
388
388
  intentkit/skills/web_scraper/utils.py,sha256=feGBTMWqpkoY7RFy2xDHVs5y8c2h8-XZ111jRo6cC3k,23349
389
389
  intentkit/skills/web_scraper/website_indexer.py,sha256=rTqCx-XzJtMlZnyGImPGWRdLpS13_exXc3lQu9EDjQM,17925
390
390
  intentkit/skills/xmtp/README.md,sha256=7y3ny77l5WUI758Q3xip1akFEgBdbibkwZjeJDu5MwE,2963
391
- intentkit/skills/xmtp/__init__.py,sha256=pG56rMjtC85x_R_YadlKyTnWYJe-jgwW4xolilcVCkA,2003
391
+ intentkit/skills/xmtp/__init__.py,sha256=inpuVqA9i98g8VIqqfC28PbfveozSukbEHOLbOMYv14,2538
392
392
  intentkit/skills/xmtp/base.py,sha256=85ZEuNLJmI_NmBPkbvDXQrNvJNG8dp9MbcbQYQQ3QZ8,430
393
+ intentkit/skills/xmtp/price.py,sha256=LqM3tWiW42bYIRqfvsZUvYpG5H5ife3WUhR-pxiS9I8,2648
393
394
  intentkit/skills/xmtp/schema.json,sha256=p8lPnzAzsW4yrPcuEv48Tv6qj3ouSNEFjw7YSxDZ3ok,1558
395
+ intentkit/skills/xmtp/swap.py,sha256=f_cl8NMAWJ3q1t8_y1Bs8P6o78MzWb7Z85sIqLfAz-A,8082
394
396
  intentkit/skills/xmtp/transfer.py,sha256=hlkUu2UkTKx1Y7mUrBT4OjkWuzDK8DwfNpxYEwdOFhc,5860
395
397
  intentkit/skills/xmtp/xmtp.png,sha256=vQzT-71zIb8aPodg-GkGSQbBnjGAPczWGm3es2ZkJe8,6681
396
398
  intentkit/utils/__init__.py,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
@@ -401,7 +403,7 @@ intentkit/utils/random.py,sha256=DymMxu9g0kuQLgJUqalvgksnIeLdS-v0aRk5nQU0mLI,452
401
403
  intentkit/utils/s3.py,sha256=9trQNkKQ5VgxWsewVsV8Y0q_pXzGRvsCYP8xauyUYkg,8549
402
404
  intentkit/utils/slack_alert.py,sha256=s7UpRgyzLW7Pbmt8cKzTJgMA9bm4EP-1rQ5KXayHu6E,2264
403
405
  intentkit/utils/tx.py,sha256=2yLLGuhvfBEY5n_GJ8wmIWLCzn0FsYKv5kRNzw_sLUI,1454
404
- intentkit-0.6.11.dev2.dist-info/METADATA,sha256=0u5l0TiIRv2CAvfKEQAO0L6iDKysSuuH7TkrivT2-Gg,6373
405
- intentkit-0.6.11.dev2.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
406
- intentkit-0.6.11.dev2.dist-info/licenses/LICENSE,sha256=Bln6DhK-LtcO4aXy-PBcdZv2f24MlJFm_qn222biJtE,1071
407
- intentkit-0.6.11.dev2.dist-info/RECORD,,
406
+ intentkit-0.6.11.dev4.dist-info/METADATA,sha256=-KB_L3wx69B4fvZdiDdpS3adppof0sR-G8-xDU_m6kM,6414
407
+ intentkit-0.6.11.dev4.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
408
+ intentkit-0.6.11.dev4.dist-info/licenses/LICENSE,sha256=Bln6DhK-LtcO4aXy-PBcdZv2f24MlJFm_qn222biJtE,1071
409
+ intentkit-0.6.11.dev4.dist-info/RECORD,,