clap-agents 0.3.0__py3-none-any.whl → 0.3.2__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.
- clap/__init__.py +1 -1
- clap/embedding/fastembed_embedding.py +1 -1
- clap/llm_services/__init__.py +0 -1
- clap/llm_services/base.py +0 -1
- clap/llm_services/ollama_service.py +8 -0
- clap/mcp_client/client.py +94 -49
- clap/multiagent_pattern/agent.py +3 -1
- clap/tool_pattern/tool.py +2 -0
- clap/tools/web3_tools.py +8 -19
- {clap_agents-0.3.0.dist-info → clap_agents-0.3.2.dist-info}/METADATA +12 -10
- {clap_agents-0.3.0.dist-info → clap_agents-0.3.2.dist-info}/RECORD +13 -13
- {clap_agents-0.3.0.dist-info → clap_agents-0.3.2.dist-info}/WHEEL +0 -0
- {clap_agents-0.3.0.dist-info → clap_agents-0.3.2.dist-info}/licenses/LICENSE +0 -0
clap/__init__.py
CHANGED
@@ -12,7 +12,7 @@ from .embedding.base_embedding import EmbeddingFunctionInterface
|
|
12
12
|
|
13
13
|
from .vector_stores.base import VectorStoreInterface, QueryResult
|
14
14
|
|
15
|
-
from .mcp_client.client import MCPClientManager,
|
15
|
+
from .mcp_client.client import MCPClientManager, ServerConfig, TransportType
|
16
16
|
|
17
17
|
from .tools.web_search import duckduckgo_search
|
18
18
|
|
@@ -57,7 +57,7 @@ class FastEmbedEmbeddings(EmbeddingFunctionInterface):
|
|
57
57
|
except Exception as e:
|
58
58
|
raise RuntimeError(f"Failed to initialize fastembed model '{self.model_name}': {e}")
|
59
59
|
|
60
|
-
async def __call__(self, input: List[str]) -> List[List[float]]:
|
60
|
+
async def __call__(self, input: List[str]) -> List[List[float]]:
|
61
61
|
if not input: return []
|
62
62
|
if not _FASTEMBED_LIB_AVAILABLE: raise RuntimeError("FastEmbed library not available.")
|
63
63
|
|
clap/llm_services/__init__.py
CHANGED
clap/llm_services/base.py
CHANGED
@@ -32,7 +32,6 @@ class LLMServiceInterface(abc.ABC):
|
|
32
32
|
messages: List[Dict[str, Any]],
|
33
33
|
tools: Optional[List[Dict[str, Any]]] = None,
|
34
34
|
tool_choice: str = "auto",
|
35
|
-
# Optional:
|
36
35
|
# temperature: Optional[float] = None,
|
37
36
|
# max_tokens: Optional[int] = None,
|
38
37
|
) -> StandardizedLLMResponse:
|
@@ -64,27 +64,35 @@ class OllamaOpenAICompatService(LLMServiceInterface):
|
|
64
64
|
if not request_model: raise ValueError("Ollama model name not specified.")
|
65
65
|
try:
|
66
66
|
api_kwargs: Dict[str, Any] = {"messages": messages, "model": request_model}
|
67
|
+
|
67
68
|
if tools and tool_choice != "none":
|
68
69
|
api_kwargs["tools"] = tools
|
69
70
|
if isinstance(tool_choice, dict) or tool_choice in ["auto", "required", "none"]: api_kwargs["tool_choice"] = tool_choice
|
70
71
|
else: api_kwargs["tools"] = None; api_kwargs["tool_choice"] = None
|
72
|
+
|
71
73
|
if temperature is not None: api_kwargs["temperature"] = temperature
|
72
74
|
if max_tokens is not None: api_kwargs["max_tokens"] = max_tokens
|
73
75
|
api_kwargs = {k: v for k, v in api_kwargs.items() if v is not None}
|
74
76
|
# print(f"OllamaService: Sending request to model '{request_model}'")
|
75
77
|
response = await self._client.chat.completions.create(**api_kwargs)
|
78
|
+
|
76
79
|
message = response.choices[0].message
|
80
|
+
|
77
81
|
text_content = message.content
|
78
82
|
tool_calls_std: List[LLMToolCall] = []
|
83
|
+
|
79
84
|
if message.tool_calls:
|
80
85
|
for tc in message.tool_calls:
|
81
86
|
if tc.id and tc.function and tc.function.name and tc.function.arguments is not None:
|
82
87
|
tool_calls_std.append(LLMToolCall(id=tc.id, function_name=tc.function.name, function_arguments_json_str=tc.function.arguments))
|
83
88
|
else: print(f"{Fore.YELLOW}Warning: Incomplete tool_call from Ollama: {tc}{Fore.RESET}")
|
89
|
+
|
84
90
|
return StandardizedLLMResponse(text_content=text_content, tool_calls=tool_calls_std)
|
91
|
+
|
85
92
|
except _OpenAIError_Placeholder_Type as e: # Use placeholder
|
86
93
|
err_msg = f"Ollama (OpenAI Compat) API Error: {e}"
|
87
94
|
if hasattr(e, 'response') and e.response and hasattr(e.response, 'text'): err_msg += f" - Details: {e.response.text}"
|
95
|
+
|
88
96
|
print(f"{Fore.RED}{err_msg}{Fore.RESET}")
|
89
97
|
return StandardizedLLMResponse(text_content=err_msg)
|
90
98
|
except Exception as e:
|
clap/mcp_client/client.py
CHANGED
@@ -1,35 +1,69 @@
|
|
1
|
-
|
2
1
|
import asyncio
|
3
2
|
import json
|
4
3
|
from contextlib import AsyncExitStack
|
5
4
|
from typing import Any, Dict, List, Optional
|
5
|
+
from enum import Enum
|
6
6
|
|
7
|
-
from pydantic import BaseModel, Field, HttpUrl
|
7
|
+
from pydantic import BaseModel, Field, HttpUrl , model_validator
|
8
|
+
from colorama import Fore
|
8
9
|
|
9
10
|
from mcp import ClientSession, types
|
10
11
|
from mcp.client.sse import sse_client
|
11
|
-
from
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
12
|
+
from mcp.client.streamable_http import streamablehttp_client
|
13
|
+
from mcp.client.stdio import stdio_client, StdioServerParameters
|
14
|
+
|
15
|
+
|
16
|
+
# Define an Enum for transport types for clarity and type safety
|
17
|
+
class TransportType(str, Enum):
|
18
|
+
STREAMABLE_HTTP = "streamable_http"
|
19
|
+
SSE = "sse"
|
20
|
+
STDIO = 'stdio'
|
21
|
+
|
22
|
+
# Rename SseServerConfig to ServerConfig and add the transport field
|
23
|
+
class ServerConfig(BaseModel):
|
24
|
+
"""Configuration for connecting to an MCP server."""
|
25
|
+
transport: TransportType = Field(
|
26
|
+
default=TransportType.STREAMABLE_HTTP,
|
27
|
+
description="The MCP transport to use."
|
28
|
+
)
|
29
|
+
# Fields for HTTP-based transports
|
30
|
+
url: Optional[HttpUrl] = Field(default=None, description="The base URL for HTTP/SSE based MCP servers.")
|
16
31
|
headers: Optional[Dict[str, str]] = Field(default=None, description="Optional headers for the connection.")
|
32
|
+
|
33
|
+
# Fields for STDIO transport
|
34
|
+
command: Optional[str] = Field(default=None, description="The command to execute for stdio-based servers.")
|
35
|
+
args: Optional[List[str]] = Field(default=None, description="A list of arguments for the command.")
|
36
|
+
|
37
|
+
@model_validator(mode='after')
|
38
|
+
def check_transport_params(self) -> 'ServerConfig':
|
39
|
+
"""Ensures the correct parameters are provided for the chosen transport."""
|
40
|
+
if self.transport in [TransportType.STREAMABLE_HTTP, TransportType.SSE]:
|
41
|
+
if not self.url:
|
42
|
+
raise ValueError(f"'url' is required for '{self.transport.value}' transport")
|
43
|
+
elif self.transport == TransportType.STDIO:
|
44
|
+
if not self.command:
|
45
|
+
raise ValueError(f"'command' is required for '{self.transport.value}' transport")
|
46
|
+
|
47
|
+
return self
|
17
48
|
|
49
|
+
# Update the class docstring to be more generic
|
18
50
|
class MCPClientManager:
|
19
51
|
"""
|
20
|
-
Manages connections and interactions with multiple MCP servers via
|
52
|
+
Manages connections and interactions with multiple MCP servers via supported
|
53
|
+
MCP transports (Streamable HTTP, SSE).
|
21
54
|
|
22
55
|
Handles connecting, disconnecting, listing tools, and calling tools on
|
23
56
|
configured MCP servers accessible over HTTP/S.
|
24
57
|
"""
|
25
58
|
|
26
|
-
|
59
|
+
# Update the __init__ method to use the new ServerConfig
|
60
|
+
def __init__(self, server_configs: Dict[str, ServerConfig]):
|
27
61
|
"""
|
28
|
-
Initializes the manager with
|
62
|
+
Initializes the manager with server configurations.
|
29
63
|
|
30
64
|
Args:
|
31
65
|
server_configs: A dictionary where keys are logical server names
|
32
|
-
and values are
|
66
|
+
and values are ServerConfig objects.
|
33
67
|
"""
|
34
68
|
if not isinstance(server_configs, dict):
|
35
69
|
raise TypeError("server_configs must be a dictionary.")
|
@@ -39,11 +73,11 @@ class MCPClientManager:
|
|
39
73
|
self._connect_locks: Dict[str, asyncio.Lock] = {
|
40
74
|
name: asyncio.Lock() for name in server_configs
|
41
75
|
}
|
42
|
-
self._manager_lock = asyncio.Lock()
|
76
|
+
self._manager_lock = asyncio.Lock()
|
43
77
|
|
44
78
|
async def _ensure_connected(self, server_name: str):
|
45
79
|
"""
|
46
|
-
Ensures a connection
|
80
|
+
Ensures a connection to the specified server is active using the configured transport.
|
47
81
|
|
48
82
|
Args:
|
49
83
|
server_name: The logical name of the server to connect to.
|
@@ -67,43 +101,68 @@ class MCPClientManager:
|
|
67
101
|
if not config:
|
68
102
|
raise ValueError(f"Configuration for server '{server_name}' not found.")
|
69
103
|
|
70
|
-
print(f"{Fore.YELLOW}Attempting to connect to MCP server via SSE: {server_name} at {config.url}{Fore.RESET}")
|
71
|
-
|
72
|
-
# Construct the specific SSE endpoint URL (often /sse)
|
73
|
-
sse_url = str(config.url).rstrip('/') + "/sse"
|
74
|
-
|
75
104
|
exit_stack = AsyncExitStack()
|
76
105
|
try:
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
106
|
+
if config.transport == TransportType.STDIO:
|
107
|
+
if not config.command:
|
108
|
+
raise ValueError("Cannot connect to STDIO server without a 'command'.")
|
109
|
+
|
110
|
+
print(f"{Fore.YELLOW}Attempting to connect to MCP server '{server_name}' using STDIO transport...{Fore.RESET}")
|
111
|
+
print(f" Command: {config.command} {' '.join(config.args or [])}")
|
112
|
+
|
113
|
+
server_params = StdioServerParameters(command=config.command, args=config.args or [])
|
114
|
+
stdio_transport_streams = await exit_stack.enter_async_context(
|
115
|
+
stdio_client(server_params)
|
116
|
+
)
|
117
|
+
read_stream, write_stream = stdio_transport_streams
|
118
|
+
transport_name = "STDIO"
|
119
|
+
# Core logic change - branch based on the configured transport
|
120
|
+
elif config.transport == TransportType.STREAMABLE_HTTP:
|
121
|
+
mcp_endpoint_url = str(config.url).rstrip('/')
|
122
|
+
print(f"{Fore.YELLOW}Attempting to connect to MCP server '{server_name}' at {mcp_endpoint_url} using Streamable HTTP transport...{Fore.RESET}")
|
123
|
+
|
124
|
+
# Use the new streamablehttp_client
|
125
|
+
http_transport = await exit_stack.enter_async_context(
|
126
|
+
streamablehttp_client(url=mcp_endpoint_url, headers=config.headers, auth=None)
|
127
|
+
)
|
128
|
+
# It returns three values; we only need the first two.
|
129
|
+
read_stream, write_stream, _ = http_transport
|
130
|
+
transport_name = "Streamable HTTP"
|
131
|
+
|
132
|
+
elif config.transport == TransportType.SSE:
|
133
|
+
# This branch maintains backward compatibility
|
134
|
+
sse_url = str(config.url).rstrip('/') + "/sse"
|
135
|
+
print(f"{Fore.YELLOW}Attempting to connect to MCP server '{server_name}' at {sse_url} using legacy SSE transport...{Fore.RESET}")
|
136
|
+
|
137
|
+
# Use the old sse_client
|
138
|
+
sse_transport = await exit_stack.enter_async_context(
|
139
|
+
sse_client(url=sse_url, headers=config.headers)
|
140
|
+
)
|
141
|
+
read_stream, write_stream = sse_transport
|
142
|
+
transport_name = "legacy SSE"
|
143
|
+
|
144
|
+
else:
|
145
|
+
raise ValueError(f"Unsupported transport type configured for server '{server_name}': {config.transport}")
|
82
146
|
|
83
|
-
|
84
147
|
session = await exit_stack.enter_async_context(
|
85
148
|
ClientSession(read_stream, write_stream)
|
86
149
|
)
|
87
150
|
|
88
|
-
|
89
151
|
await session.initialize()
|
90
152
|
|
91
153
|
async with self._manager_lock:
|
92
154
|
self.sessions[server_name] = session
|
93
155
|
self.exit_stacks[server_name] = exit_stack
|
94
|
-
print(f"{Fore.GREEN}Successfully connected to MCP server via
|
156
|
+
print(f"{Fore.GREEN}Successfully connected to MCP server '{server_name}' via {transport_name}{Fore.RESET}")
|
95
157
|
|
96
158
|
except Exception as e:
|
97
159
|
await exit_stack.aclose()
|
98
|
-
print(f"{Fore.RED}Failed to connect to MCP server '{server_name}'
|
99
|
-
raise RuntimeError(f"
|
160
|
+
print(f"{Fore.RED}Failed to connect to MCP server '{server_name}': {e}{Fore.RESET}")
|
161
|
+
raise RuntimeError(f"Connection to '{server_name}' failed.") from e
|
100
162
|
|
101
163
|
async def disconnect(self, server_name: str):
|
102
164
|
"""
|
103
165
|
Disconnects from a specific server and cleans up resources.
|
104
|
-
|
105
|
-
Args:
|
106
|
-
server_name: The logical name of the server to disconnect from.
|
107
166
|
"""
|
108
167
|
async with self._manager_lock:
|
109
168
|
if server_name in self.sessions:
|
@@ -113,7 +172,7 @@ class MCPClientManager:
|
|
113
172
|
await exit_stack.aclose()
|
114
173
|
print(f"{Fore.GREEN}Disconnected from MCP server: {server_name}{Fore.RESET}")
|
115
174
|
|
116
|
-
|
175
|
+
|
117
176
|
async def disconnect_all(self):
|
118
177
|
server_names = list(self.sessions.keys())
|
119
178
|
print(f"{Fore.YELLOW}MCPClientManager: Disconnecting from all servers ({len(server_names)})...{Fore.RESET}")
|
@@ -126,13 +185,7 @@ class MCPClientManager:
|
|
126
185
|
|
127
186
|
async def list_remote_tools(self, server_name: str) -> List[types.Tool]:
|
128
187
|
"""
|
129
|
-
Lists tools available on a specific connected
|
130
|
-
|
131
|
-
Args:
|
132
|
-
server_name: The logical name of the server.
|
133
|
-
|
134
|
-
Returns:
|
135
|
-
A list of mcp.types.Tool objects provided by the server.
|
188
|
+
Lists tools available on a specific connected server.
|
136
189
|
"""
|
137
190
|
await self._ensure_connected(server_name)
|
138
191
|
session = self.sessions.get(server_name)
|
@@ -152,15 +205,7 @@ class MCPClientManager:
|
|
152
205
|
self, server_name: str, tool_name: str, arguments: Dict[str, Any]
|
153
206
|
) -> str:
|
154
207
|
"""
|
155
|
-
Calls a tool on a specific connected
|
156
|
-
|
157
|
-
Args:
|
158
|
-
server_name: The logical name of the server.
|
159
|
-
tool_name: The name of the tool to call.
|
160
|
-
arguments: A dictionary of arguments for the tool.
|
161
|
-
|
162
|
-
Returns:
|
163
|
-
A string representation of the tool's result content.
|
208
|
+
Calls a tool on a specific connected server.
|
164
209
|
"""
|
165
210
|
await self._ensure_connected(server_name)
|
166
211
|
session = self.sessions.get(server_name)
|
@@ -193,4 +238,4 @@ class MCPClientManager:
|
|
193
238
|
|
194
239
|
except Exception as e:
|
195
240
|
print(f"{Fore.RED}Error calling tool '{tool_name}' on server '{server_name}': {e}{Fore.RESET}")
|
196
|
-
raise RuntimeError(f"Failed to call tool '{tool_name}' on '{server_name}'.") from e
|
241
|
+
raise RuntimeError(f"Failed to call tool '{tool_name}' on '{server_name}'.") from e
|
clap/multiagent_pattern/agent.py
CHANGED
@@ -74,6 +74,7 @@ class Agent:
|
|
74
74
|
mcp_server_names: Optional[List[str]] = None,
|
75
75
|
vector_store: Optional[VectorStoreInterface] = None,
|
76
76
|
parallel_tool_calls: bool = True ,
|
77
|
+
**kwargs
|
77
78
|
# embedding_function: Optional[EmbeddingFunction] = None,
|
78
79
|
|
79
80
|
):
|
@@ -86,6 +87,7 @@ class Agent:
|
|
86
87
|
self.local_tools = tools or []
|
87
88
|
|
88
89
|
self.vector_store = vector_store
|
90
|
+
self.react_agent_kwargs = kwargs
|
89
91
|
# self.embedding_function = embedding_function
|
90
92
|
|
91
93
|
llm_service_instance = llm_service or GroqService()
|
@@ -193,7 +195,7 @@ class Agent:
|
|
193
195
|
self.task_description = original_task_description
|
194
196
|
|
195
197
|
print(f"Agent {self.name}: Running ReactAgent...")
|
196
|
-
raw_output = await self.react_agent.run(user_msg=msg)
|
198
|
+
raw_output = await self.react_agent.run(user_msg=msg,**self.react_agent_kwargs)
|
197
199
|
output_data = {"output": raw_output}
|
198
200
|
|
199
201
|
print(f"Agent {self.name}: Passing context to {len(self.dependents)} dependents...")
|
clap/tool_pattern/tool.py
CHANGED
@@ -16,9 +16,11 @@ def get_fn_signature(fn: Callable) -> dict:
|
|
16
16
|
sig = inspect.signature(fn)
|
17
17
|
for name, type_hint in fn.__annotations__.items():
|
18
18
|
if name == "return": continue
|
19
|
+
|
19
20
|
param_type_name = getattr(type_hint, "__name__", str(type_hint))
|
20
21
|
schema_type = type_mapping.get(param_type_name.lower(), "string")
|
21
22
|
parameters["properties"][name] = {"type": schema_type}
|
23
|
+
|
22
24
|
if sig.parameters[name].default is inspect.Parameter.empty:
|
23
25
|
parameters["required"].append(name)
|
24
26
|
if not parameters.get("required"):
|
clap/tools/web3_tools.py
CHANGED
@@ -3,18 +3,15 @@ from web3 import Web3
|
|
3
3
|
from clap.tool_pattern.tool import tool
|
4
4
|
import json
|
5
5
|
|
6
|
-
# Environment variables are loaded by the main application script.
|
7
6
|
|
8
7
|
WEB3_PROVIDER_URL = os.getenv("WEB3_PROVIDER_URL")
|
9
8
|
AGENT_PRIVATE_KEY = os.getenv("AGENT_PRIVATE_KEY")
|
10
9
|
|
11
|
-
# --- Official Testnet Addresses ---
|
12
10
|
WETH_CONTRACT_ADDRESS = "0xfFf9976782d46CC05630D1f6eBAb18b2324d6B14"
|
13
11
|
UNISWAP_ROUTER_ADDRESS = "0x3bFA4769FB09eefC5a399D6D47036A5d3fA67B54"
|
14
12
|
CHAINLINK_ETH_USD_PRICE_FEED_ADDRESS = "0x694AA1769357215DE4FAC081bf1f309aDC325306"
|
15
13
|
|
16
14
|
|
17
|
-
# --- Correct, Centralized ABIs ---
|
18
15
|
CHAINLINK_PRICE_FEED_ABI = """
|
19
16
|
[
|
20
17
|
{
|
@@ -106,14 +103,11 @@ def get_token_price(token_pair: str) -> str:
|
|
106
103
|
abi=CHAINLINK_PRICE_FEED_ABI
|
107
104
|
)
|
108
105
|
|
109
|
-
# The latestRoundData function returns a tuple of values. The price is the second element.
|
110
106
|
latest_data = price_feed_contract.functions.latestRoundData().call()
|
111
107
|
price_raw = latest_data[1]
|
112
108
|
|
113
|
-
# The price feed also has a 'decimals' function to tell us where to put the decimal point.
|
114
109
|
price_decimals = price_feed_contract.functions.decimals().call()
|
115
110
|
|
116
|
-
# Convert the raw price to a human-readable format
|
117
111
|
price = price_raw / (10 ** price_decimals)
|
118
112
|
|
119
113
|
return f"The latest price for {token_pair} is ${price:.2f}"
|
@@ -174,7 +168,6 @@ def interact_with_contract(
|
|
174
168
|
try:
|
175
169
|
_initialize_web3()
|
176
170
|
|
177
|
-
# 1. Validate inputs
|
178
171
|
if not w3.is_address(contract_address):
|
179
172
|
return "Error: Invalid 'contract_address'."
|
180
173
|
try:
|
@@ -182,20 +175,17 @@ def interact_with_contract(
|
|
182
175
|
except json.JSONDecodeError:
|
183
176
|
return "Error: The provided 'abi' is not a valid JSON string."
|
184
177
|
|
185
|
-
# 2. Create the contract object
|
186
178
|
contract = w3.eth.contract(address=contract_address, abi=abi_json)
|
187
179
|
|
188
|
-
# 3. Get the function object from the contract
|
189
180
|
func_to_call = getattr(contract.functions, function_name)
|
190
181
|
if not func_to_call:
|
191
182
|
return f"Error: Function '{function_name}' not found in the contract's ABI."
|
192
183
|
|
193
|
-
# 4. Prepare the function call with its arguments
|
194
184
|
prepared_func = func_to_call(*function_args)
|
195
185
|
|
196
|
-
|
186
|
+
|
197
187
|
if is_write_transaction:
|
198
|
-
|
188
|
+
|
199
189
|
nonce = w3.eth.get_transaction_count(agent_account.address)
|
200
190
|
tx = prepared_func.build_transaction({
|
201
191
|
'from': agent_account.address,
|
@@ -206,9 +196,9 @@ def interact_with_contract(
|
|
206
196
|
tx_hash = w3.eth.send_raw_transaction(signed_tx.raw_transaction)
|
207
197
|
return f"Write transaction sent successfully. Transaction hash: {w3.to_hex(tx_hash)}"
|
208
198
|
else:
|
209
|
-
|
199
|
+
|
210
200
|
result = prepared_func.call()
|
211
|
-
|
201
|
+
|
212
202
|
return json.dumps(result)
|
213
203
|
|
214
204
|
except Exception as e:
|
@@ -224,7 +214,7 @@ def get_erc20_balance(token_address: str, wallet_address: str) -> str:
|
|
224
214
|
|
225
215
|
token_contract = w3.eth.contract(address=chk_token_address, abi=ERC20_STANDARD_ABI)
|
226
216
|
|
227
|
-
|
217
|
+
|
228
218
|
decimals = token_contract.functions.decimals().call()
|
229
219
|
raw_balance = token_contract.functions.balanceOf(chk_wallet_address).call()
|
230
220
|
|
@@ -232,7 +222,7 @@ def get_erc20_balance(token_address: str, wallet_address: str) -> str:
|
|
232
222
|
return str(balance)
|
233
223
|
|
234
224
|
except Exception as e:
|
235
|
-
|
225
|
+
|
236
226
|
return f"Error in get_erc20_balance for token {token_address}: {e}"
|
237
227
|
|
238
228
|
@tool
|
@@ -278,12 +268,11 @@ def swap_tokens_for_tokens(token_in_address: str, token_out_address: str, amount
|
|
278
268
|
signed_approve_tx = w3.eth.account.sign_transaction(approve_tx, private_key=os.getenv("AGENT_PRIVATE_KEY"))
|
279
269
|
approve_tx_hash = w3.eth.send_raw_transaction(signed_approve_tx.raw_transaction)
|
280
270
|
|
281
|
-
|
282
|
-
# Wait for the approval transaction to be confirmed.
|
271
|
+
|
283
272
|
print(f"Waiting for approval transaction {w3.to_hex(approve_tx_hash)} to be confirmed...")
|
284
273
|
w3.eth.wait_for_transaction_receipt(approve_tx_hash)
|
285
274
|
|
286
|
-
|
275
|
+
|
287
276
|
uniswap_router = w3.eth.contract(address=chk_router_address, abi=UNISWAP_ABI)
|
288
277
|
swap_params = (chk_token_in, chk_token_out, fee, agent_account.address, amount_in_wei, 0, 0)
|
289
278
|
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: clap-agents
|
3
|
-
Version: 0.3.
|
3
|
+
Version: 0.3.2
|
4
4
|
Summary: A Python framework for building cognitive agentic patterns including ReAct agents, Multi-Agent Teams, native tool calling, and MCP client integration.
|
5
5
|
Project-URL: Homepage, https://github.com/MaitreyaM/CLAP-AGENTS.git
|
6
6
|
Project-URL: Repository, https://github.com/MaitreyaM/CLAP-AGENTS.git
|
@@ -513,24 +513,26 @@ asyncio.run(main())
|
|
513
513
|
```
|
514
514
|
|
515
515
|
|
516
|
-
New in v0.3.0: Web3 & On-Chain Agent Capabilities
|
516
|
+
## New in v0.3.0: Web3 & On-Chain Agent Capabilities
|
517
517
|
CLAP now includes a powerful toolkit for building autonomous agents that can interact directly with EVM-compatible blockchains like Ethereum. Your agents can now hold assets, execute transactions, and interact with smart contracts, opening up a new world of possibilities in DeFi, DAOs, and on-chain automation.
|
518
518
|
Setup
|
519
519
|
To enable Web3 capabilities, install the web3 extra:
|
520
|
+
```
|
520
521
|
pip install "clap-agents[web3]"
|
521
|
-
|
522
|
-
|
522
|
+
```
|
523
|
+
|
523
524
|
You will also need to set the following variables in your .env file:
|
525
|
+
```
|
524
526
|
# Your connection to the blockchain (e.g., from Alchemy or Infura)
|
525
527
|
WEB3_PROVIDER_URL="https://sepolia.infura.io/v3/YOUR_API_KEY"
|
526
528
|
|
527
529
|
# The private key for your agent's wallet.
|
528
530
|
# WARNING: For testing only. Do not use a key with real funds.
|
529
531
|
AGENT_PRIVATE_KEY="0xYourTestnetPrivateKeyHere"
|
532
|
+
```
|
530
533
|
|
531
534
|
|
532
|
-
|
533
|
-
# Core Web3 Tools
|
535
|
+
## Core Web3 Tools
|
534
536
|
The framework now includes a suite of pre-built, robust tools for on-chain interaction:
|
535
537
|
|
536
538
|
get_erc20_balance: Checks the balance of any standard ERC-20 token in a wallet.
|
@@ -544,9 +546,10 @@ get_token_price: Fetches real-time asset prices from on-chain Chainlink oracles,
|
|
544
546
|
interact_with_contract: A powerful, generic tool to call any function on any smart contract, given its address and ABI.
|
545
547
|
|
546
548
|
|
547
|
-
|
549
|
+
## Quick Start: A Simple DeFi Agent
|
548
550
|
This example demonstrates an agent that can wrap ETH and then swap it for another token, a common DeFi task.
|
549
|
-
|
551
|
+
```
|
552
|
+
|
550
553
|
import os
|
551
554
|
import asyncio
|
552
555
|
from dotenv import load_dotenv
|
@@ -585,7 +588,7 @@ async def main():
|
|
585
588
|
|
586
589
|
if __name__ == "__main__":
|
587
590
|
asyncio.run(main())
|
588
|
-
|
591
|
+
```
|
589
592
|
|
590
593
|
This new capability transforms your CLAP agents from simple observers into active participants in the decentralized economy.
|
591
594
|
|
@@ -604,4 +607,3 @@ License
|
|
604
607
|
This project is licensed under the terms of the Apache License 2.0. See the LICENSE file for details.
|
605
608
|
|
606
609
|
|
607
|
-
|
@@ -1,27 +1,27 @@
|
|
1
|
-
clap/__init__.py,sha256=
|
1
|
+
clap/__init__.py,sha256=1U3LyIQ60FOOFKLNmdjqNMgN0rbNOO6ggNcDVTMz5SI,1081
|
2
2
|
clap/embedding/__init__.py,sha256=PqnqcSiA_JwEvn69g2DCQHdsffL2l4GNEKo0fAtCqbs,520
|
3
3
|
clap/embedding/base_embedding.py,sha256=0SYicQ-A-rSDqHoFK0IOrRQe0cisOl8OBnis6V43Chs,696
|
4
|
-
clap/embedding/fastembed_embedding.py,sha256=
|
4
|
+
clap/embedding/fastembed_embedding.py,sha256=fUXCRyctPxwinAG2JCkdmlARU945z7dEsXScIkpqwb0,2862
|
5
5
|
clap/embedding/ollama_embedding.py,sha256=s7IYFs4BuM114Md1cqxim5WzCwCjbEJ48wAZZOgR7KQ,3702
|
6
6
|
clap/embedding/sentence_transformer_embedding.py,sha256=0RAqGxDpjZVwerOLmVirqqnCwC07kHdfAPiy2fgOSCk,1798
|
7
|
-
clap/llm_services/__init__.py,sha256=
|
8
|
-
clap/llm_services/base.py,sha256
|
7
|
+
clap/llm_services/__init__.py,sha256=IBvWmE99PGxHq5Dt4u0G1erZSV80QEC981UULnrD6Tk,496
|
8
|
+
clap/llm_services/base.py,sha256=-XKWd6gLAXedIhUUqM_f7sqkVxdfifP2j-BwmF0hUkI,2183
|
9
9
|
clap/llm_services/google_openai_compat_service.py,sha256=vN0osfCS6DIFHsCCiB03mKUp4n7SkIJd2PAypBAnC30,4552
|
10
10
|
clap/llm_services/groq_service.py,sha256=pcTp24_NgLfp3bGaABzli_Sey7wZsXvFI74VjZ1GvkQ,3051
|
11
|
-
clap/llm_services/ollama_service.py,sha256=
|
11
|
+
clap/llm_services/ollama_service.py,sha256=Qh3W2fb-NDMVB8DS9o3q4jisZvK9U6s-r4ATNbAwVLE,5333
|
12
12
|
clap/mcp_client/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
13
|
-
clap/mcp_client/client.py,sha256=
|
13
|
+
clap/mcp_client/client.py,sha256=MqcGEVe04B1vH0EGFaqXAEMCh_0C851MDr9hzGEBIM8,11729
|
14
14
|
clap/multiagent_pattern/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
15
|
-
clap/multiagent_pattern/agent.py,sha256=
|
15
|
+
clap/multiagent_pattern/agent.py,sha256=SDebwxaUquFUN_MMCGSYKLIIA3a6tVIls-IGrnEKqJI,8617
|
16
16
|
clap/multiagent_pattern/team.py,sha256=t8Xru3fVPblw75pyuPT1wmI3jlsrZHD_GKAW5APbpFg,7966
|
17
17
|
clap/react_pattern/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
18
18
|
clap/react_pattern/react_agent.py,sha256=v9JYyIwv0vzkOl6kq8Aua7u50rJyU02NomCHTjt24vo,24596
|
19
19
|
clap/tool_pattern/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
20
|
-
clap/tool_pattern/tool.py,sha256=
|
20
|
+
clap/tool_pattern/tool.py,sha256=Y4Uvu5FsCA3S3BZfjM3OOKfhA-o5Q9SfhCzGWfGIQ6o,5974
|
21
21
|
clap/tool_pattern/tool_agent.py,sha256=VTQv9DNU16zgINZKVcX5oDw1lPfw5Y_8bUnW6wad2vE,14439
|
22
22
|
clap/tools/__init__.py,sha256=8UMtxaPkq-pEOD2C0Qm4WZoyJpMxEOEQSDhWNLwAAiI,822
|
23
23
|
clap/tools/email_tools.py,sha256=18aAlbjcSaOzpf9R3H-EGeRsqL5gdzmcJJcW619xOHU,9729
|
24
|
-
clap/tools/web3_tools.py,sha256=
|
24
|
+
clap/tools/web3_tools.py,sha256=bb9zqhXYima6LzLARh8zgPx_gV0m9g0rf8ZqazCx-og,11852
|
25
25
|
clap/tools/web_crawler.py,sha256=WdFbAKhUsUVveJimmLbzQ6k1BhOdMsg87FjL628HEKM,3542
|
26
26
|
clap/tools/web_search.py,sha256=YT0I1kPrdxMUst-dpsGqqF6aqxMgj3ACwiW_jN9Pu9s,985
|
27
27
|
clap/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
@@ -33,7 +33,7 @@ clap/vector_stores/__init__.py,sha256=H3w5jLdQFbXArVgiidy4RlAalM8a6LAiMlAX0Z-2v7
|
|
33
33
|
clap/vector_stores/base.py,sha256=nvk8J1oNG3OKFhJfxBGFyVeh9YxoDs9RkB_iOzPBm1w,2853
|
34
34
|
clap/vector_stores/chroma_store.py,sha256=vwkWWGxPwuW45T1PS6D44dXhDG9U_KZWjrMZCOkEXsA,7242
|
35
35
|
clap/vector_stores/qdrant_store.py,sha256=-SwMTb0yaGngpQ9AddDzDIt3x8GZevlFT-0FMkWD28I,9923
|
36
|
-
clap_agents-0.3.
|
37
|
-
clap_agents-0.3.
|
38
|
-
clap_agents-0.3.
|
39
|
-
clap_agents-0.3.
|
36
|
+
clap_agents-0.3.2.dist-info/METADATA,sha256=HcN7nnvyCWF3YxREc6BmAoscQ5vpe0UWSRnz99qeKeQ,30541
|
37
|
+
clap_agents-0.3.2.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
38
|
+
clap_agents-0.3.2.dist-info/licenses/LICENSE,sha256=WNHhf_5RCaeuKWyq_K39vmp9F28LxKsB4SpomwSZ2L0,11357
|
39
|
+
clap_agents-0.3.2.dist-info/RECORD,,
|
File without changes
|
File without changes
|