intentkit 0.7.5.dev17__py3-none-any.whl → 0.7.5.dev18__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.7.5-dev17"
6
+ __version__ = "0.7.5-dev18"
7
7
  __author__ = "hyacinthus"
8
8
  __email__ = "hyacinthus@gmail.com"
9
9
 
@@ -1,5 +1,3 @@
1
- # app/config.py
2
- import asyncio
3
1
  import json
4
2
  import logging
5
3
  import os
@@ -11,9 +9,6 @@ from intentkit.utils.logging import setup_logging
11
9
  from intentkit.utils.s3 import init_s3
12
10
  from intentkit.utils.slack_alert import init_slack
13
11
 
14
- # hack for cdp bug
15
- asyncio.set_event_loop_policy(asyncio.DefaultEventLoopPolicy())
16
-
17
12
  # Load environment variables from .env file
18
13
  load_dotenv()
19
14
 
intentkit/core/engine.py CHANGED
@@ -21,6 +21,7 @@ from typing import Optional, Tuple
21
21
 
22
22
  import sqlalchemy
23
23
  from epyxid import XID
24
+ from langchain_core.language_models import BaseChatModel
24
25
  from langchain_core.messages import (
25
26
  BaseMessage,
26
27
  HumanMessage,
@@ -29,6 +30,7 @@ from langchain_core.tools import BaseTool
29
30
  from langgraph.errors import GraphRecursionError
30
31
  from langgraph.graph.state import CompiledStateGraph
31
32
  from langgraph.prebuilt import create_react_agent
33
+ from langgraph.runtime import Runtime
32
34
  from sqlalchemy import func, update
33
35
  from sqlalchemy.exc import SQLAlchemyError
34
36
 
@@ -70,8 +72,8 @@ _agents_updated: dict[str, datetime] = {}
70
72
  _private_agents_updated: dict[str, datetime] = {}
71
73
 
72
74
 
73
- async def create_agent(agent: Agent, is_private: bool = False) -> CompiledStateGraph:
74
- """Create an AI agent with specified configuration and tools.
75
+ async def build_agent(agent: Agent, agent_data: AgentData) -> CompiledStateGraph:
76
+ """Build an AI agent with specified configuration and tools.
75
77
 
76
78
  This function:
77
79
  1. Initializes LLM with specified model
@@ -81,13 +83,12 @@ async def create_agent(agent: Agent, is_private: bool = False) -> CompiledStateG
81
83
 
82
84
  Args:
83
85
  agent (Agent): Agent configuration object
86
+ agent_data (AgentData): Agent data object
84
87
  is_private (bool, optional): Flag indicating whether the agent is private. Defaults to False.
85
- has_search (bool, optional): Flag indicating whether to include search tools. Defaults to False.
86
88
 
87
89
  Returns:
88
90
  CompiledStateGraph: Initialized LangChain agent
89
91
  """
90
- agent_data = await AgentData.get(agent.id)
91
92
 
92
93
  # Create the LLM model instance
93
94
  llm_model = await create_llm_model(
@@ -108,6 +109,7 @@ async def create_agent(agent: Agent, is_private: bool = False) -> CompiledStateG
108
109
 
109
110
  # ==== Load skills
110
111
  tools: list[BaseTool | dict] = []
112
+ private_tools: list[BaseTool | dict] = []
111
113
 
112
114
  if agent.skills:
113
115
  for k, v in agent.skills.items():
@@ -116,11 +118,18 @@ async def create_agent(agent: Agent, is_private: bool = False) -> CompiledStateG
116
118
  try:
117
119
  skill_module = importlib.import_module(f"intentkit.skills.{k}")
118
120
  if hasattr(skill_module, "get_skills"):
121
+ # all
119
122
  skill_tools = await skill_module.get_skills(
120
- v, is_private, agent_store, agent_id=agent.id
123
+ v, False, agent_store, agent_id=agent.id
121
124
  )
122
125
  if skill_tools and len(skill_tools) > 0:
123
126
  tools.extend(skill_tools)
127
+ # private
128
+ skill_private_tools = await skill_module.get_skills(
129
+ v, True, agent_store, agent_id=agent.id
130
+ )
131
+ if skill_private_tools and len(skill_private_tools) > 0:
132
+ private_tools.extend(skill_private_tools)
124
133
  else:
125
134
  logger.error(f"Skill {k} does not have get_skills function")
126
135
  except ImportError as e:
@@ -128,23 +137,33 @@ async def create_agent(agent: Agent, is_private: bool = False) -> CompiledStateG
128
137
 
129
138
  # filter the duplicate tools
130
139
  tools = list({tool.name: tool for tool in tools}.values())
140
+ private_tools = list({tool.name: tool for tool in private_tools}.values())
131
141
 
132
142
  # Add search tools if requested
133
143
  if (
134
- llm_model.info.provider == LLMProvider.OPENAI
135
- and llm_model.info.supports_search
136
- and not agent.model.startswith(
137
- "gpt-5"
138
- ) # tmp disable gpt-5 search since package bugs
144
+ llm_model.info.provider == LLMProvider.OPENAI and llm_model.info.supports_search
145
+ # and not agent.model.startswith(
146
+ # "gpt-5"
147
+ # ) # tmp disable gpt-5 search since package bugs
139
148
  ):
140
149
  tools.append({"type": "web_search_preview"})
150
+ private_tools.append({"type": "web_search_preview"})
141
151
 
142
152
  # Create the formatted_prompt function using the refactored prompt module
143
153
  formatted_prompt = create_formatted_prompt_function(agent, agent_data)
144
154
 
155
+ # bind tools to llm
156
+ def select_model(
157
+ state: AgentState, runtime: Runtime[AgentContext]
158
+ ) -> BaseChatModel:
159
+ context = runtime.context
160
+ if context.is_private:
161
+ return llm.bind_tools(private_tools)
162
+ return llm.bind_tools(tools)
163
+
145
164
  for tool in tools:
146
165
  logger.info(
147
- f"[{agent.id}{'-private' if is_private else ''}] loaded tool: {tool.name if isinstance(tool, BaseTool) else tool}"
166
+ f"[{agent.id}] loaded tool: {tool.name if isinstance(tool, BaseTool) else tool}"
148
167
  )
149
168
 
150
169
  # Pre model hook
@@ -157,7 +176,7 @@ async def create_agent(agent: Agent, is_private: bool = False) -> CompiledStateG
157
176
 
158
177
  # Create ReAct Agent using the LLM and CDP Agentkit tools.
159
178
  executor = create_react_agent(
160
- model=llm,
179
+ model=select_model,
161
180
  tools=tools,
162
181
  prompt=formatted_prompt,
163
182
  pre_model_hook=pre_model_hook,
@@ -172,7 +191,23 @@ async def create_agent(agent: Agent, is_private: bool = False) -> CompiledStateG
172
191
  return executor
173
192
 
174
193
 
175
- async def initialize_agent(aid, is_private=False):
194
+ async def create_agent(agent: Agent) -> CompiledStateGraph:
195
+ """Create an AI agent with specified configuration and tools.
196
+
197
+ This function maintains backward compatibility by calling build_agent internally.
198
+
199
+ Args:
200
+ agent (Agent): Agent configuration object
201
+ is_private (bool, optional): Flag indicating whether the agent is private. Defaults to False.
202
+
203
+ Returns:
204
+ CompiledStateGraph: Initialized LangChain agent
205
+ """
206
+ agent_data = await AgentData.get(agent.id)
207
+ return await build_agent(agent, agent_data)
208
+
209
+
210
+ async def initialize_agent(aid):
176
211
  """Initialize an AI agent with specified configuration and tools.
177
212
 
178
213
  This function:
@@ -198,47 +233,34 @@ async def initialize_agent(aid, is_private=False):
198
233
  )
199
234
 
200
235
  # Create the agent using the new create_agent function
201
- executor = await create_agent(agent, is_private)
236
+ executor = await create_agent(agent)
202
237
 
203
238
  # Cache the agent executor
204
- if is_private:
205
- _private_agents[aid] = executor
206
- _private_agents_updated[aid] = agent.updated_at
207
- else:
208
- _agents[aid] = executor
209
- _agents_updated[aid] = agent.updated_at
239
+ _agents[aid] = executor
240
+ _agents_updated[aid] = agent.deployed_at if agent.deployed_at else agent.updated_at
210
241
 
211
242
 
212
- async def agent_executor(
213
- agent_id: str, is_private: bool
214
- ) -> Tuple[CompiledStateGraph, float]:
243
+ async def agent_executor(agent_id: str) -> Tuple[CompiledStateGraph, float]:
215
244
  start = time.perf_counter()
216
245
  agent = await Agent.get(agent_id)
217
246
  if not agent:
218
247
  raise IntentKitAPIError(
219
248
  status_code=404, key="AgentNotFound", message="Agent not found"
220
249
  )
221
- agents = _private_agents if is_private else _agents
222
- agents_updated = _private_agents_updated if is_private else _agents_updated
223
-
250
+ updated_at = agent.deployed_at if agent.deployed_at else agent.updated_at
224
251
  # Check if agent needs reinitialization due to updates
225
252
  needs_reinit = False
226
- if agent_id in agents:
227
- if (
228
- agent_id not in agents_updated
229
- or agent.updated_at != agents_updated[agent_id]
230
- ):
253
+ if agent_id in _agents:
254
+ if agent_id not in _agents_updated or updated_at != _agents_updated[agent_id]:
231
255
  needs_reinit = True
232
- logger.info(
233
- f"Reinitializing agent {agent_id} due to updates, private mode: {is_private}"
234
- )
256
+ logger.info(f"Reinitializing agent {agent_id} due to updates")
235
257
 
236
258
  # cold start or needs reinitialization
237
259
  cold_start_cost = 0.0
238
- if (agent_id not in agents) or needs_reinit:
239
- await initialize_agent(agent_id, is_private)
260
+ if (agent_id not in _agents) or needs_reinit:
261
+ await initialize_agent(agent_id)
240
262
  cold_start_cost = time.perf_counter() - start
241
- return agents[agent_id], cold_start_cost
263
+ return _agents[agent_id], cold_start_cost
242
264
 
243
265
 
244
266
  async def stream_agent(message: ChatMessageCreate):
@@ -355,7 +377,7 @@ async def stream_agent(message: ChatMessageCreate):
355
377
  if input.user_id == agent.owner:
356
378
  is_private = True
357
379
 
358
- executor, cold_start_cost = await agent_executor(input.agent_id, is_private)
380
+ executor, cold_start_cost = await agent_executor(input.agent_id)
359
381
  last = start + cold_start_cost
360
382
 
361
383
  # Extract images from attachments
@@ -889,10 +911,7 @@ async def clean_agent_memory(
889
911
  async def thread_stats(agent_id: str, chat_id: str) -> list[BaseMessage]:
890
912
  thread_id = f"{agent_id}-{chat_id}"
891
913
  stream_config = {"configurable": {"thread_id": thread_id}}
892
- is_private = False
893
- if chat_id.startswith("owner") or chat_id.startswith("autonomous"):
894
- is_private = True
895
- executor, _ = await agent_executor(agent_id, is_private)
914
+ executor, _ = await agent_executor(agent_id)
896
915
  snap = await executor.aget_state(stream_config)
897
916
  if snap.values and "messages" in snap.values:
898
917
  return snap.values["messages"]
intentkit/models/llm.py CHANGED
@@ -12,7 +12,7 @@ from intentkit.models.base import Base
12
12
  from intentkit.models.db import get_session
13
13
  from intentkit.models.redis import get_redis
14
14
  from intentkit.utils.error import IntentKitLookUpError
15
- from langchain_core.language_models import LanguageModelLike
15
+ from langchain.chat_models.base import BaseChatModel
16
16
  from pydantic import BaseModel, ConfigDict, Field
17
17
  from sqlalchemy import Boolean, Column, DateTime, Integer, Numeric, String, func, select
18
18
  from sqlalchemy.ext.asyncio import AsyncSession
@@ -344,7 +344,7 @@ class LLMModel(BaseModel):
344
344
  return model_info
345
345
 
346
346
  # This will be implemented by subclasses to return the appropriate LLM instance
347
- async def create_instance(self, config: Any) -> LanguageModelLike:
347
+ async def create_instance(self, config: Any) -> BaseChatModel:
348
348
  """Create and return the LLM instance based on the configuration."""
349
349
  raise NotImplementedError("Subclasses must implement create_instance")
350
350
 
@@ -362,7 +362,7 @@ class LLMModel(BaseModel):
362
362
  class OpenAILLM(LLMModel):
363
363
  """OpenAI LLM configuration."""
364
364
 
365
- async def create_instance(self, config: Any) -> LanguageModelLike:
365
+ async def create_instance(self, config: Any) -> BaseChatModel:
366
366
  """Create and return a ChatOpenAI instance."""
367
367
  from langchain_openai import ChatOpenAI
368
368
 
@@ -398,7 +398,7 @@ class OpenAILLM(LLMModel):
398
398
  class DeepseekLLM(LLMModel):
399
399
  """Deepseek LLM configuration."""
400
400
 
401
- async def create_instance(self, config: Any) -> LanguageModelLike:
401
+ async def create_instance(self, config: Any) -> BaseChatModel:
402
402
  """Create and return a ChatDeepseek instance."""
403
403
 
404
404
  from langchain_deepseek import ChatDeepSeek
@@ -431,7 +431,7 @@ class DeepseekLLM(LLMModel):
431
431
  class XAILLM(LLMModel):
432
432
  """XAI (Grok) LLM configuration."""
433
433
 
434
- async def create_instance(self, config: Any) -> LanguageModelLike:
434
+ async def create_instance(self, config: Any) -> BaseChatModel:
435
435
  """Create and return a ChatXAI instance."""
436
436
 
437
437
  from langchain_xai import ChatXAI
@@ -463,7 +463,7 @@ class XAILLM(LLMModel):
463
463
  class EternalLLM(LLMModel):
464
464
  """Eternal AI LLM configuration."""
465
465
 
466
- async def create_instance(self, config: Any) -> LanguageModelLike:
466
+ async def create_instance(self, config: Any) -> BaseChatModel:
467
467
  """Create and return a ChatOpenAI instance configured for Eternal AI."""
468
468
  from langchain_openai import ChatOpenAI
469
469
 
@@ -495,7 +495,7 @@ class EternalLLM(LLMModel):
495
495
  class ReigentLLM(LLMModel):
496
496
  """Reigent LLM configuration."""
497
497
 
498
- async def create_instance(self, config: Any) -> LanguageModelLike:
498
+ async def create_instance(self, config: Any) -> BaseChatModel:
499
499
  """Create and return a ChatOpenAI instance configured for Reigent."""
500
500
  from langchain_openai import ChatOpenAI
501
501
 
@@ -517,7 +517,7 @@ class ReigentLLM(LLMModel):
517
517
  class VeniceLLM(LLMModel):
518
518
  """Venice LLM configuration."""
519
519
 
520
- async def create_instance(self, config: Any) -> LanguageModelLike:
520
+ async def create_instance(self, config: Any) -> BaseChatModel:
521
521
  """Create and return a ChatOpenAI instance configured for Venice."""
522
522
  from langchain_openai import ChatOpenAI
523
523
 
@@ -36,7 +36,7 @@ class Config(SkillConfig):
36
36
 
37
37
  states: SkillStates
38
38
  api_token: NotRequired[str]
39
- main_tokens: List[str]
39
+ main_tokens: NotRequired[List[str]]
40
40
 
41
41
 
42
42
  async def get_skills(
@@ -1,6 +1,6 @@
1
+ from decimal import Decimal
1
2
  from typing import Optional, Type
2
3
 
3
- from cdp import EvmServerAccount
4
4
  from coinbase_agentkit import CdpEvmWalletProvider
5
5
  from langchain.tools.base import ToolException
6
6
  from pydantic import BaseModel, Field
@@ -9,10 +9,9 @@ from intentkit.abstracts.graph import AgentContext
9
9
  from intentkit.abstracts.skill import SkillStoreABC
10
10
  from intentkit.clients import CdpClient, get_cdp_client
11
11
  from intentkit.skills.base import IntentKitSkill
12
- from intentkit.utils.chain import ChainProvider, NetworkId
12
+ from intentkit.utils.chain import ChainProvider, Network, network_to_id
13
13
 
14
14
  base_url = "https://api.enso.finance"
15
- default_chain_id = int(NetworkId.BaseMainnet)
16
15
 
17
16
 
18
17
  class EnsoBaseTool(IntentKitSkill):
@@ -25,18 +24,6 @@ class EnsoBaseTool(IntentKitSkill):
25
24
  description="The skill store for persisting data"
26
25
  )
27
26
 
28
- async def get_account(self, context: AgentContext) -> Optional[EvmServerAccount]:
29
- """Get the account object from the CDP client.
30
-
31
- Args:
32
- context: The skill context containing agent information.
33
-
34
- Returns:
35
- Optional[EvmServerAccount]: The account object if available.
36
- """
37
- client: CdpClient = await get_cdp_client(context.agent.id, self.skill_store)
38
- return await client.get_account()
39
-
40
27
  async def get_wallet_provider(
41
28
  self, context: AgentContext
42
29
  ) -> Optional[CdpEvmWalletProvider]:
@@ -51,6 +38,13 @@ class EnsoBaseTool(IntentKitSkill):
51
38
  client: CdpClient = await get_cdp_client(context.agent.id, self.skill_store)
52
39
  return await client.get_wallet_provider()
53
40
 
41
+ async def get_wallet_address(self, context: AgentContext) -> str:
42
+ client: CdpClient = await get_cdp_client(context.agent.id, self.skill_store)
43
+ provider_config = await client.get_provider_config()
44
+ if not provider_config.address:
45
+ raise ToolException("wallet address not found for agent")
46
+ return provider_config.address
47
+
54
48
  def get_chain_provider(self, context: AgentContext) -> Optional[ChainProvider]:
55
49
  return self.skill_store.get_system_config("chain_provider")
56
50
 
@@ -60,8 +54,7 @@ class EnsoBaseTool(IntentKitSkill):
60
54
  return skill_config["main_tokens"]
61
55
  return []
62
56
 
63
- def get_api_key(self) -> str:
64
- context = self.get_context()
57
+ def get_api_token(self, context: AgentContext) -> str:
65
58
  skill_config = context.agent.skill_config(self.category)
66
59
  api_key_provider = skill_config.get("api_key_provider")
67
60
  if api_key_provider == "platform":
@@ -74,6 +67,40 @@ class EnsoBaseTool(IntentKitSkill):
74
67
  f"Invalid API key provider: {api_key_provider}, or no api_token in config"
75
68
  )
76
69
 
70
+ def resolve_chain_id(
71
+ self, context: AgentContext, chain_id: Optional[int] = None
72
+ ) -> int:
73
+ if chain_id:
74
+ return chain_id
75
+
76
+ agent = context.agent
77
+ try:
78
+ network = Network(agent.network_id)
79
+ except ValueError as exc: # pragma: no cover - defensive
80
+ raise ToolException(
81
+ f"Unsupported network configured for agent: {agent.network_id}"
82
+ ) from exc
83
+
84
+ network_id = network_to_id.get(network)
85
+ if network_id is None:
86
+ raise ToolException(
87
+ f"Unable to determine chain id for network: {agent.network_id}"
88
+ )
89
+ return int(network_id)
90
+
77
91
  @property
78
92
  def category(self) -> str:
79
93
  return "enso"
94
+
95
+
96
+ def format_amount_with_decimals(
97
+ amount: object, decimals: Optional[int]
98
+ ) -> Optional[str]:
99
+ if amount is None or decimals is None:
100
+ return None
101
+
102
+ try:
103
+ value = Decimal(str(amount)) / (Decimal(10) ** decimals)
104
+ return format(value, "f")
105
+ except Exception: # pragma: no cover - defensive
106
+ return None
@@ -4,14 +4,7 @@ import httpx
4
4
  from langchain.tools.base import ToolException
5
5
  from pydantic import BaseModel, Field
6
6
 
7
- from intentkit.skills.enso.base import (
8
- EnsoBaseTool,
9
- base_url,
10
- )
11
- from intentkit.utils.chain import NetworkId
12
-
13
- # Chain ID for Base Mainnet
14
- BASE_CHAIN_ID = int(NetworkId.BaseMainnet)
7
+ from intentkit.skills.enso.base import EnsoBaseTool, base_url
15
8
 
16
9
 
17
10
  class EnsoGetBestYieldInput(BaseModel):
@@ -21,9 +14,9 @@ class EnsoGetBestYieldInput(BaseModel):
21
14
  "USDC",
22
15
  description="Symbol of the token to find the best yield for (e.g., 'USDC', 'ETH', 'USDT')",
23
16
  )
24
- chain_id: int = Field(
25
- BASE_CHAIN_ID,
26
- description="The blockchain chain ID. Default is Base Mainnet (8453)",
17
+ chain_id: int | None = Field(
18
+ None,
19
+ description="The blockchain chain ID. Defaults to the agent's configured network.",
27
20
  )
28
21
  top_n: int = Field(
29
22
  5,
@@ -80,7 +73,7 @@ class EnsoGetBestYield(EnsoBaseTool):
80
73
  async def _arun(
81
74
  self,
82
75
  token_symbol: str = "USDC",
83
- chain_id: int = BASE_CHAIN_ID,
76
+ chain_id: int | None = None,
84
77
  top_n: int = 5,
85
78
  **kwargs,
86
79
  ) -> EnsoGetBestYieldOutput:
@@ -89,7 +82,7 @@ class EnsoGetBestYield(EnsoBaseTool):
89
82
 
90
83
  Args:
91
84
  token_symbol (str): Symbol of the token to find the best yield for (default: USDC)
92
- chain_id (int): The chain id of the network (default: Base Mainnet)
85
+ chain_id (int | None): The chain id of the network. Defaults to the agent's configured network.
93
86
  top_n (int): Number of top yield options to return
94
87
 
95
88
  Returns:
@@ -99,16 +92,17 @@ class EnsoGetBestYield(EnsoBaseTool):
99
92
  ToolException: If there's an error accessing the Enso API.
100
93
  """
101
94
  context = self.get_context()
95
+ resolved_chain_id = self.resolve_chain_id(context, chain_id)
102
96
  api_token = self.get_api_token(context)
103
97
 
104
98
  if not api_token:
105
99
  raise ToolException("No API token found for Enso Finance")
106
100
 
107
101
  # Get the chain name for the given chain ID
108
- chain_name = await self._get_chain_name(api_token, chain_id)
102
+ chain_name = await self._get_chain_name(api_token, resolved_chain_id)
109
103
 
110
104
  # Get all protocols on the specified chain
111
- protocols = await self._get_protocols(api_token, chain_id)
105
+ protocols = await self._get_protocols(api_token, resolved_chain_id)
112
106
 
113
107
  # Collect all yield options from all protocols
114
108
  all_yield_options = []
@@ -119,7 +113,7 @@ class EnsoGetBestYield(EnsoBaseTool):
119
113
 
120
114
  # Get yield-bearing tokens for this protocol
121
115
  tokens = await self._get_protocol_tokens(
122
- api_token, chain_id, protocol_slug, token_symbol
116
+ api_token, resolved_chain_id, protocol_slug, token_symbol
123
117
  )
124
118
 
125
119
  # Process tokens to extract yield options
@@ -170,7 +164,7 @@ class EnsoGetBestYield(EnsoBaseTool):
170
164
  return EnsoGetBestYieldOutput(
171
165
  best_options=top_options,
172
166
  token_symbol=token_symbol,
173
- chain_id=chain_id,
167
+ chain_id=resolved_chain_id,
174
168
  chain_name=chain_name,
175
169
  )
176
170
 
@@ -7,8 +7,6 @@ from pydantic import BaseModel, Field
7
7
 
8
8
  from .base import EnsoBaseTool, base_url
9
9
 
10
- logger = logging.getLogger(__name__)
11
-
12
10
 
13
11
  class EnsoGetNetworksInput(BaseModel):
14
12
  """
@@ -38,6 +36,9 @@ class EnsoGetNetworksOutput(BaseModel):
38
36
  )
39
37
 
40
38
 
39
+ logger = logging.getLogger(__name__)
40
+
41
+
41
42
  class EnsoGetNetworks(EnsoBaseTool):
42
43
  """
43
44
  Tool for retrieving networks and their corresponding chainId, the output should be kept.
@@ -4,13 +4,11 @@ import httpx
4
4
  from langchain.tools.base import ToolException
5
5
  from pydantic import BaseModel, Field
6
6
 
7
- from .base import EnsoBaseTool, base_url, default_chain_id
7
+ from .base import EnsoBaseTool, base_url
8
8
 
9
9
 
10
10
  class EnsoGetPricesInput(BaseModel):
11
- chainId: int = Field(
12
- default_chain_id, description="Blockchain chain ID of the token"
13
- )
11
+ chainId: int | None = Field(None, description="Blockchain chain ID of the token")
14
12
  address: str = Field(
15
13
  "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee",
16
14
  description="Contract address of the token",
@@ -43,22 +41,21 @@ class EnsoGetPrices(EnsoBaseTool):
43
41
  async def _arun(
44
42
  self,
45
43
  address: str,
46
- chainId: int = default_chain_id,
44
+ chainId: int | None = None,
47
45
  **kwargs,
48
46
  ) -> EnsoGetPricesOutput:
49
47
  """
50
48
  Asynchronous function to request the token price from the API.
51
49
 
52
50
  Args:
53
- chainId (int): The blockchain's chain ID.
51
+ chainId (int | None): The blockchain's chain ID. Defaults to the agent's configured network.
54
52
  address (str): Contract address of the token.
55
53
 
56
54
  Returns:
57
55
  EnsoGetPricesOutput: Token price response or error message.
58
56
  """
59
- url = f"{base_url}/api/v1/prices/{str(chainId)}/{address}"
60
-
61
57
  context = self.get_context()
58
+ resolved_chain_id = self.resolve_chain_id(context, chainId)
62
59
  api_token = self.get_api_token(context)
63
60
 
64
61
  headers = {
@@ -68,7 +65,10 @@ class EnsoGetPrices(EnsoBaseTool):
68
65
 
69
66
  async with httpx.AsyncClient() as client:
70
67
  try:
71
- response = await client.get(url, headers=headers)
68
+ response = await client.get(
69
+ f"{base_url}/api/v1/prices/{str(resolved_chain_id)}/{address}",
70
+ headers=headers,
71
+ )
72
72
  response.raise_for_status()
73
73
  json_dict = response.json()
74
74