clap-agents 0.2.2__py3-none-any.whl → 0.3.1__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/embedding/fastembed_embedding.py +2 -2
- clap/llm_services/__init__.py +0 -1
- clap/llm_services/base.py +0 -1
- clap/llm_services/google_openai_compat_service.py +2 -2
- clap/llm_services/groq_service.py +0 -2
- clap/llm_services/ollama_service.py +8 -1
- clap/mcp_client/client.py +1 -1
- clap/multiagent_pattern/agent.py +9 -4
- clap/react_pattern/react_agent.py +34 -17
- clap/tool_pattern/tool.py +2 -0
- clap/tools/__init__.py +15 -1
- clap/tools/web3_tools.py +301 -0
- clap/vector_stores/chroma_store.py +1 -1
- {clap_agents-0.2.2.dist-info → clap_agents-0.3.1.dist-info}/METADATA +84 -1
- {clap_agents-0.2.2.dist-info → clap_agents-0.3.1.dist-info}/RECORD +17 -16
- {clap_agents-0.2.2.dist-info → clap_agents-0.3.1.dist-info}/WHEEL +0 -0
- {clap_agents-0.2.2.dist-info → clap_agents-0.3.1.dist-info}/licenses/LICENSE +0 -0
@@ -36,7 +36,7 @@ class FastEmbedEmbeddings(EmbeddingFunctionInterface):
|
|
36
36
|
if not _FASTEMBED_LIB_AVAILABLE:
|
37
37
|
raise ImportError(
|
38
38
|
"The 'fastembed' library is required to use FastEmbedEmbeddings. "
|
39
|
-
"Install with 'pip install fastembed'
|
39
|
+
"Install with 'pip install fastembed' "
|
40
40
|
)
|
41
41
|
|
42
42
|
self.model_name = model_name
|
@@ -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:
|
@@ -1,4 +1,3 @@
|
|
1
|
-
# --- START OF agentic_patterns/llm_services/google_openai_compat_service.py ---
|
2
1
|
|
3
2
|
import os
|
4
3
|
import json
|
@@ -56,7 +55,7 @@ class GoogleOpenAICompatService(LLMServiceInterface):
|
|
56
55
|
Sends messages via the OpenAI SDK (to Google's endpoint) and returns a standardized response.
|
57
56
|
|
58
57
|
Args:
|
59
|
-
model: The Google model identifier (e.g., "gemini-
|
58
|
+
model: The Google model identifier (e.g., "gemini-2.0-flash").
|
60
59
|
messages: Chat history in the OpenAI dictionary format.
|
61
60
|
tools: Tool schemas in the OpenAI function format.
|
62
61
|
tool_choice: Tool choice setting ("auto", "none", etc.).
|
@@ -92,6 +91,7 @@ class GoogleOpenAICompatService(LLMServiceInterface):
|
|
92
91
|
for tc in message.tool_calls:
|
93
92
|
tool_call_id = getattr(tc, 'id', None)
|
94
93
|
if not tool_call_id:
|
94
|
+
#raise ValueError("Received a tool call from the Gemini API without a required 'id'.")
|
95
95
|
tool_call_id = f"compat_call_{uuid.uuid4().hex[:6]}"
|
96
96
|
print(f"{Fore.YELLOW}Warning: Tool call from Google compat layer missing ID. Generated fallback: {tool_call_id}{Fore.RESET}")
|
97
97
|
|
@@ -18,7 +18,6 @@ class GroqService(LLMServiceInterface):
|
|
18
18
|
If None, a new client will be created using environment variables.
|
19
19
|
"""
|
20
20
|
self.client = client or AsyncGroq()
|
21
|
-
# Add any other Groq-specific initialization here if needed
|
22
21
|
|
23
22
|
async def get_llm_response(
|
24
23
|
self,
|
@@ -26,7 +25,6 @@ class GroqService(LLMServiceInterface):
|
|
26
25
|
messages: List[Dict[str, Any]],
|
27
26
|
tools: Optional[List[Dict[str, Any]]] = None,
|
28
27
|
tool_choice: str = "auto",
|
29
|
-
# Add other relevant Groq parameters if desired, e.g., temperature, max_tokens
|
30
28
|
# temperature: Optional[float] = None,
|
31
29
|
# max_tokens: Optional[int] = None,
|
32
30
|
) -> 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:
|
@@ -98,4 +106,3 @@ class OllamaOpenAICompatService(LLMServiceInterface):
|
|
98
106
|
elif hasattr(self._client, "_client") and hasattr(self._client._client, "is_closed"): # For httpx client in openai <1.0
|
99
107
|
if not self._client._client.is_closed: await self._client._client.aclose() # type: ignore
|
100
108
|
# print("OllamaService: Client closed.")
|
101
|
-
# --- END OF FILE ---
|
clap/mcp_client/client.py
CHANGED
@@ -39,7 +39,7 @@ class MCPClientManager:
|
|
39
39
|
self._connect_locks: Dict[str, asyncio.Lock] = {
|
40
40
|
name: asyncio.Lock() for name in server_configs
|
41
41
|
}
|
42
|
-
self._manager_lock = asyncio.Lock()
|
42
|
+
self._manager_lock = asyncio.Lock()
|
43
43
|
|
44
44
|
async def _ensure_connected(self, server_name: str):
|
45
45
|
"""
|
clap/multiagent_pattern/agent.py
CHANGED
@@ -56,7 +56,8 @@ class Agent:
|
|
56
56
|
llm_service (Optional[LLMServiceInterface]): Service for LLM calls (defaults to GroqService).
|
57
57
|
mcp_manager (Optional[MCPClientManager]): Shared MCP client manager.
|
58
58
|
mcp_server_names (Optional[List[str]]): MCP servers this agent uses.
|
59
|
-
vector_store (Optional[VectorStoreInterface]): Vector store instance for RAG.
|
59
|
+
vector_store (Optional[VectorStoreInterface]): Vector store instance for RAG.
|
60
|
+
parallel_tool_calls : Determine parallel or sequential execution of agent's tools.
|
60
61
|
# embedding_function(Optional[EmbeddingFunction]): EF if needed by agent.
|
61
62
|
|
62
63
|
"""
|
@@ -71,7 +72,9 @@ class Agent:
|
|
71
72
|
llm_service: Optional[LLMServiceInterface] = None,
|
72
73
|
mcp_manager: Optional[MCPClientManager] = None,
|
73
74
|
mcp_server_names: Optional[List[str]] = None,
|
74
|
-
vector_store: Optional[VectorStoreInterface] = None,
|
75
|
+
vector_store: Optional[VectorStoreInterface] = None,
|
76
|
+
parallel_tool_calls: bool = True ,
|
77
|
+
**kwargs
|
75
78
|
# embedding_function: Optional[EmbeddingFunction] = None,
|
76
79
|
|
77
80
|
):
|
@@ -84,6 +87,7 @@ class Agent:
|
|
84
87
|
self.local_tools = tools or []
|
85
88
|
|
86
89
|
self.vector_store = vector_store
|
90
|
+
self.react_agent_kwargs = kwargs
|
87
91
|
# self.embedding_function = embedding_function
|
88
92
|
|
89
93
|
llm_service_instance = llm_service or GroqService()
|
@@ -98,7 +102,8 @@ class Agent:
|
|
98
102
|
tools=self.local_tools,
|
99
103
|
mcp_manager=self.mcp_manager,
|
100
104
|
mcp_server_names=self.mcp_server_names,
|
101
|
-
vector_store=self.vector_store
|
105
|
+
vector_store=self.vector_store,
|
106
|
+
parallel_tool_calls=parallel_tool_calls
|
102
107
|
)
|
103
108
|
|
104
109
|
self.dependencies: List['Agent'] = []
|
@@ -190,7 +195,7 @@ class Agent:
|
|
190
195
|
self.task_description = original_task_description
|
191
196
|
|
192
197
|
print(f"Agent {self.name}: Running ReactAgent...")
|
193
|
-
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)
|
194
199
|
output_data = {"output": raw_output}
|
195
200
|
|
196
201
|
print(f"Agent {self.name}: Passing context to {len(self.dependents)} dependents...")
|
@@ -54,10 +54,12 @@ class ReactAgent:
|
|
54
54
|
mcp_server_names: Optional[List[str]] = None,
|
55
55
|
vector_store: Optional[VectorStoreInterface] = None,
|
56
56
|
system_prompt: str = "",
|
57
|
+
parallel_tool_calls: bool = True,
|
57
58
|
) -> None:
|
58
59
|
self.llm_service = llm_service
|
59
60
|
self.model = model
|
60
61
|
self.agent_name = agent_name
|
62
|
+
self.parallel_tool_calls = parallel_tool_calls
|
61
63
|
self.system_prompt = (system_prompt + "\n\n" + CORE_SYSTEM_PROMPT).strip()
|
62
64
|
|
63
65
|
|
@@ -275,30 +277,45 @@ class ReactAgent:
|
|
275
277
|
return {tool_call_id: result_str}
|
276
278
|
|
277
279
|
|
278
|
-
|
279
|
-
|
280
280
|
|
281
281
|
async def process_tool_calls(self, tool_calls: List[LLMToolCall]) -> Dict[str, Any]:
|
282
|
-
"""
|
283
|
-
|
282
|
+
"""
|
283
|
+
Processes tool calls using the configured strategy (parallel or sequential).
|
284
|
+
"""
|
284
285
|
if not isinstance(tool_calls, list):
|
285
286
|
print(f"{Fore.RED}Error: Expected a list of LLMToolCall, got {type(tool_calls)}{Fore.RESET}")
|
286
|
-
return
|
287
|
+
return {}
|
287
288
|
|
288
|
-
|
289
|
-
results = await asyncio.gather(*tasks, return_exceptions=True)
|
289
|
+
observations = {}
|
290
290
|
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
291
|
+
if self.parallel_tool_calls:
|
292
|
+
# PARALLEL EXECUTION (FASTER, BUT CAN CAUSE RACE CONDITIONS)
|
293
|
+
print(f"{Fore.BLUE}[{self.agent_name}] Executing {len(tool_calls)} tool calls in PARALLEL...{Fore.RESET}")
|
294
|
+
tasks = [self._execute_single_tool_call(tc) for tc in tool_calls]
|
295
|
+
results = await asyncio.gather(*tasks, return_exceptions=True)
|
296
|
+
|
297
|
+
for result in results:
|
298
|
+
if isinstance(result, dict) and len(result) == 1:
|
299
|
+
observations.update(result)
|
300
|
+
elif isinstance(result, Exception):
|
301
|
+
print(f"{Fore.RED}Error during parallel tool execution gather: {result}{Fore.RESET}")
|
302
|
+
else:
|
303
|
+
print(f"{Fore.RED}Error: Unexpected item in parallel tool execution results: {result}{Fore.RESET}")
|
301
304
|
|
305
|
+
else:
|
306
|
+
# SEQUENTIAL EXECUTION
|
307
|
+
print(f"{Fore.YELLOW}[{self.agent_name}] Executing {len(tool_calls)} tool calls SEQUENTIALLY...{Fore.RESET}")
|
308
|
+
for tool_call in tool_calls:
|
309
|
+
try:
|
310
|
+
result = await self._execute_single_tool_call(tool_call)
|
311
|
+
if isinstance(result, dict) and len(result) == 1:
|
312
|
+
observations.update(result)
|
313
|
+
else:
|
314
|
+
print(f"{Fore.RED}Error: Unexpected item in sequential tool execution result: {result}{Fore.RESET}")
|
315
|
+
except Exception as e:
|
316
|
+
print(f"{Fore.RED}Error during sequential execution of {tool_call.function_name}: {e}{Fore.RESET}")
|
317
|
+
observations[tool_call.id] = f"An unexpected error occurred: {e}"
|
318
|
+
|
302
319
|
return observations
|
303
320
|
|
304
321
|
|
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/__init__.py
CHANGED
@@ -1,4 +1,3 @@
|
|
1
|
-
|
2
1
|
from .web_search import duckduckgo_search
|
3
2
|
from .web_crawler import scrape_url, extract_text_by_query
|
4
3
|
from .email_tools import send_email, fetch_recent_emails
|
@@ -9,5 +8,20 @@ __all__ = [
|
|
9
8
|
"extract_text_by_query",
|
10
9
|
"send_email",
|
11
10
|
"fetch_recent_emails",
|
11
|
+
"get_wallet_balance",
|
12
|
+
"send_eth",
|
13
|
+
"interact_with_contract",
|
14
|
+
'get_erc20_balance'
|
15
|
+
'swap_tokens_for_tokens',
|
16
|
+
'wrap_eth',
|
17
|
+
'get_token_price'
|
12
18
|
]
|
13
19
|
|
20
|
+
try:
|
21
|
+
from .web3_tools import get_wallet_balance, send_eth, interact_with_contract, get_erc20_balance, swap_tokens_for_tokens, wrap_eth, get_token_price
|
22
|
+
__all__.extend([
|
23
|
+
"get_wallet_balance", "send_eth", "interact_with_contract",
|
24
|
+
"get_erc20_balance", "swap_tokens_for_tokens", "wrap_eth", "get_token_price"
|
25
|
+
])
|
26
|
+
except ImportError:
|
27
|
+
pass
|
clap/tools/web3_tools.py
ADDED
@@ -0,0 +1,301 @@
|
|
1
|
+
import os
|
2
|
+
from web3 import Web3
|
3
|
+
from clap.tool_pattern.tool import tool
|
4
|
+
import json
|
5
|
+
|
6
|
+
# Environment variables are loaded by the main application script.
|
7
|
+
|
8
|
+
WEB3_PROVIDER_URL = os.getenv("WEB3_PROVIDER_URL")
|
9
|
+
AGENT_PRIVATE_KEY = os.getenv("AGENT_PRIVATE_KEY")
|
10
|
+
|
11
|
+
# --- Official Testnet Addresses ---
|
12
|
+
WETH_CONTRACT_ADDRESS = "0xfFf9976782d46CC05630D1f6eBAb18b2324d6B14"
|
13
|
+
UNISWAP_ROUTER_ADDRESS = "0x3bFA4769FB09eefC5a399D6D47036A5d3fA67B54"
|
14
|
+
CHAINLINK_ETH_USD_PRICE_FEED_ADDRESS = "0x694AA1769357215DE4FAC081bf1f309aDC325306"
|
15
|
+
|
16
|
+
|
17
|
+
# --- Correct, Centralized ABIs ---
|
18
|
+
CHAINLINK_PRICE_FEED_ABI = """
|
19
|
+
[
|
20
|
+
{
|
21
|
+
"inputs": [],
|
22
|
+
"name": "latestRoundData",
|
23
|
+
"outputs": [
|
24
|
+
{ "internalType": "uint80", "name": "roundId", "type": "uint80" },
|
25
|
+
{ "internalType": "int256", "name": "answer", "type": "int256" },
|
26
|
+
{ "internalType": "uint256", "name": "startedAt", "type": "uint256" },
|
27
|
+
{ "internalType": "uint256", "name": "updatedAt", "type": "uint256" },
|
28
|
+
{ "internalType": "uint80", "name": "answeredInRound", "type": "uint80" }
|
29
|
+
],
|
30
|
+
"stateMutability": "view",
|
31
|
+
"type": "function"
|
32
|
+
},
|
33
|
+
{
|
34
|
+
"inputs": [],
|
35
|
+
"name": "decimals",
|
36
|
+
"outputs": [{ "internalType": "uint8", "name": "", "type": "uint8" }],
|
37
|
+
"stateMutability": "view",
|
38
|
+
"type": "function"
|
39
|
+
}
|
40
|
+
]
|
41
|
+
"""
|
42
|
+
ERC20_STANDARD_ABI = """
|
43
|
+
[
|
44
|
+
{"constant":true,"inputs":[{"name":"_owner","type":"address"}],"name":"balanceOf","outputs":[{"name":"balance","type":"uint256"}],"type":"function"},
|
45
|
+
{"constant":true,"inputs":[],"name":"decimals","outputs":[{"name":"","type":"uint8"}],"type":"function"},
|
46
|
+
{"constant":false,"inputs":[{"name":"_spender","type":"address"},{"name":"_value","type":"uint256"}],"name":"approve","outputs":[{"name":"","type":"bool"}],"type":"function"}
|
47
|
+
]
|
48
|
+
"""
|
49
|
+
WETH_ABI = """
|
50
|
+
[
|
51
|
+
{"constant":false,"inputs":[],"name":"deposit","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},
|
52
|
+
{"constant":true,"inputs":[{"name":"","type":"address"}],"name":"balanceOf","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"}
|
53
|
+
]
|
54
|
+
"""
|
55
|
+
UNISWAP_ABI = """
|
56
|
+
[{"inputs":[{"components":[{"type":"address","name":"tokenIn"},{"type":"address","name":"tokenOut"},{"type":"uint24","name":"fee"},{"type":"address","name":"recipient"},{"type":"uint256","name":"amountIn"},{"type":"uint256","name":"amountOutMinimum"},{"type":"uint160","name":"sqrtPriceLimitX96"}],"type":"tuple","name":"params"}],"name":"exactInputSingle","outputs":[{"type":"uint256","name":"amountOut"}],"stateMutability":"payable","type":"function"}]
|
57
|
+
"""
|
58
|
+
|
59
|
+
|
60
|
+
w3 = None
|
61
|
+
agent_account = None
|
62
|
+
|
63
|
+
def _initialize_web3():
|
64
|
+
"""Initializes Web3 instance and account if they don't exist."""
|
65
|
+
global w3, agent_account, WEB3_PROVIDER_URL, AGENT_PRIVATE_KEY
|
66
|
+
if w3 is None:
|
67
|
+
WEB3_PROVIDER_URL = os.getenv("WEB3_PROVIDER_URL")
|
68
|
+
AGENT_PRIVATE_KEY = os.getenv("AGENT_PRIVATE_KEY")
|
69
|
+
|
70
|
+
if not WEB3_PROVIDER_URL or not AGENT_PRIVATE_KEY:
|
71
|
+
raise ConnectionError("Web3 provider URL or Agent private key not found.")
|
72
|
+
w3 = Web3(Web3.HTTPProvider(WEB3_PROVIDER_URL))
|
73
|
+
agent_account = w3.eth.account.from_key(AGENT_PRIVATE_KEY)
|
74
|
+
|
75
|
+
@tool
|
76
|
+
def get_wallet_balance(address: str) -> str:
|
77
|
+
"""
|
78
|
+
Gets the native token balance (Sepolia ETH) of a given wallet address.
|
79
|
+
"""
|
80
|
+
try:
|
81
|
+
_initialize_web3()
|
82
|
+
balance_wei = w3.eth.get_balance(address)
|
83
|
+
balance_eth = w3.from_wei(balance_wei, 'ether')
|
84
|
+
return f"The balance of address {address} is {balance_eth} ETH."
|
85
|
+
except Exception as e:
|
86
|
+
return f"Error getting balance for address {address}: {e}"
|
87
|
+
|
88
|
+
@tool
|
89
|
+
def get_token_price(token_pair: str) -> str:
|
90
|
+
"""
|
91
|
+
Gets the latest price of a token pair (e.g., 'ETH/USD') from a Chainlink Price Feed.
|
92
|
+
|
93
|
+
Args:
|
94
|
+
token_pair: The token pair to get the price for. Currently supports 'ETH/USD'.
|
95
|
+
|
96
|
+
Returns:
|
97
|
+
A string indicating the latest price of the token pair.
|
98
|
+
"""
|
99
|
+
try:
|
100
|
+
_initialize_web3()
|
101
|
+
if token_pair.upper() != "ETH/USD":
|
102
|
+
return "Error: This tool currently only supports the 'ETH/USD' token pair."
|
103
|
+
|
104
|
+
price_feed_contract = w3.eth.contract(
|
105
|
+
address=CHAINLINK_ETH_USD_PRICE_FEED_ADDRESS,
|
106
|
+
abi=CHAINLINK_PRICE_FEED_ABI
|
107
|
+
)
|
108
|
+
|
109
|
+
# The latestRoundData function returns a tuple of values. The price is the second element.
|
110
|
+
latest_data = price_feed_contract.functions.latestRoundData().call()
|
111
|
+
price_raw = latest_data[1]
|
112
|
+
|
113
|
+
# The price feed also has a 'decimals' function to tell us where to put the decimal point.
|
114
|
+
price_decimals = price_feed_contract.functions.decimals().call()
|
115
|
+
|
116
|
+
# Convert the raw price to a human-readable format
|
117
|
+
price = price_raw / (10 ** price_decimals)
|
118
|
+
|
119
|
+
return f"The latest price for {token_pair} is ${price:.2f}"
|
120
|
+
|
121
|
+
except Exception as e:
|
122
|
+
return f"Error getting token price: {type(e).__name__} - {e}"
|
123
|
+
|
124
|
+
@tool
|
125
|
+
def send_eth(to_address: str, amount_eth: float) -> str:
|
126
|
+
"""
|
127
|
+
Creates, signs, and sends a transaction to transfer Sepolia ETH.
|
128
|
+
"""
|
129
|
+
try:
|
130
|
+
_initialize_web3()
|
131
|
+
nonce = w3.eth.get_transaction_count(agent_account.address)
|
132
|
+
|
133
|
+
tx = {
|
134
|
+
'from': agent_account.address,
|
135
|
+
'to': to_address,
|
136
|
+
'value': w3.to_wei(amount_eth, 'ether'),
|
137
|
+
'gas': 21000,
|
138
|
+
'gasPrice': w3.eth.gas_price,
|
139
|
+
'nonce': nonce,
|
140
|
+
}
|
141
|
+
|
142
|
+
signed_tx = w3.eth.account.sign_transaction(tx,AGENT_PRIVATE_KEY)
|
143
|
+
|
144
|
+
tx_hash = w3.eth.send_raw_transaction(signed_tx.raw_transaction)
|
145
|
+
|
146
|
+
return f"Transaction sent successfully. Transaction hash: {w3.to_hex(tx_hash)}"
|
147
|
+
except Exception as e:
|
148
|
+
return f"Error sending transaction: {type(e).__name__} - {e}"
|
149
|
+
|
150
|
+
|
151
|
+
|
152
|
+
@tool
|
153
|
+
def interact_with_contract(
|
154
|
+
contract_address: str,
|
155
|
+
abi: str,
|
156
|
+
function_name: str,
|
157
|
+
function_args: list,
|
158
|
+
is_write_transaction: bool = False
|
159
|
+
) -> str:
|
160
|
+
"""
|
161
|
+
Interacts with a smart contract by calling one of its functions.
|
162
|
+
Can perform read-only calls (e.g., getting data) or write transactions (e.g., sending tokens).
|
163
|
+
|
164
|
+
Args:
|
165
|
+
contract_address: The address of the smart contract (e.g., "0x...").
|
166
|
+
abi: The contract's Application Binary Interface (ABI) as a JSON string.
|
167
|
+
function_name: The exact name of the function to call.
|
168
|
+
function_args: A list of arguments to pass to the function, in order.
|
169
|
+
is_write_transaction: Set to True if this is a state-changing transaction that requires gas. Defaults to False (read-only call).
|
170
|
+
|
171
|
+
Returns:
|
172
|
+
A string containing the result of the call or a transaction hash if it's a write transaction.
|
173
|
+
"""
|
174
|
+
try:
|
175
|
+
_initialize_web3()
|
176
|
+
|
177
|
+
# 1. Validate inputs
|
178
|
+
if not w3.is_address(contract_address):
|
179
|
+
return "Error: Invalid 'contract_address'."
|
180
|
+
try:
|
181
|
+
abi_json = json.loads(abi)
|
182
|
+
except json.JSONDecodeError:
|
183
|
+
return "Error: The provided 'abi' is not a valid JSON string."
|
184
|
+
|
185
|
+
# 2. Create the contract object
|
186
|
+
contract = w3.eth.contract(address=contract_address, abi=abi_json)
|
187
|
+
|
188
|
+
# 3. Get the function object from the contract
|
189
|
+
func_to_call = getattr(contract.functions, function_name)
|
190
|
+
if not func_to_call:
|
191
|
+
return f"Error: Function '{function_name}' not found in the contract's ABI."
|
192
|
+
|
193
|
+
# 4. Prepare the function call with its arguments
|
194
|
+
prepared_func = func_to_call(*function_args)
|
195
|
+
|
196
|
+
# 5. Execute as either a read-only 'call' or a write 'transaction'
|
197
|
+
if is_write_transaction:
|
198
|
+
# This is a state-changing transaction that costs gas
|
199
|
+
nonce = w3.eth.get_transaction_count(agent_account.address)
|
200
|
+
tx = prepared_func.build_transaction({
|
201
|
+
'from': agent_account.address,
|
202
|
+
'nonce': nonce,
|
203
|
+
'gasPrice': w3.eth.gas_price
|
204
|
+
})
|
205
|
+
signed_tx = w3.eth.account.sign_transaction(tx, private_key=AGENT_PRIVATE_KEY)
|
206
|
+
tx_hash = w3.eth.send_raw_transaction(signed_tx.raw_transaction)
|
207
|
+
return f"Write transaction sent successfully. Transaction hash: {w3.to_hex(tx_hash)}"
|
208
|
+
else:
|
209
|
+
# This is a read-only call that is free and instant
|
210
|
+
result = prepared_func.call()
|
211
|
+
# Convert the result to a JSON string to ensure it's readable by the LLM
|
212
|
+
return json.dumps(result)
|
213
|
+
|
214
|
+
except Exception as e:
|
215
|
+
return f"Error interacting with contract: {type(e).__name__} - {e}"
|
216
|
+
|
217
|
+
@tool
|
218
|
+
def get_erc20_balance(token_address: str, wallet_address: str) -> str:
|
219
|
+
"""Gets the balance of a specific ERC-20 token for a given wallet."""
|
220
|
+
try:
|
221
|
+
_initialize_web3()
|
222
|
+
chk_token_address = Web3.to_checksum_address(token_address)
|
223
|
+
chk_wallet_address = Web3.to_checksum_address(wallet_address)
|
224
|
+
|
225
|
+
token_contract = w3.eth.contract(address=chk_token_address, abi=ERC20_STANDARD_ABI)
|
226
|
+
|
227
|
+
# This will now work correctly with a real contract address
|
228
|
+
decimals = token_contract.functions.decimals().call()
|
229
|
+
raw_balance = token_contract.functions.balanceOf(chk_wallet_address).call()
|
230
|
+
|
231
|
+
balance = raw_balance / (10 ** decimals)
|
232
|
+
return str(balance)
|
233
|
+
|
234
|
+
except Exception as e:
|
235
|
+
# A simple, honest error handler for genuine problems.
|
236
|
+
return f"Error in get_erc20_balance for token {token_address}: {e}"
|
237
|
+
|
238
|
+
@tool
|
239
|
+
def wrap_eth(amount_eth: float) -> str:
|
240
|
+
"""Converts native ETH into WETH (Wrapped ETH) by depositing it into the WETH contract."""
|
241
|
+
try:
|
242
|
+
_initialize_web3()
|
243
|
+
weth_contract = w3.eth.contract(address=WETH_CONTRACT_ADDRESS, abi=WETH_ABI)
|
244
|
+
tx = weth_contract.functions.deposit().build_transaction({
|
245
|
+
'from': agent_account.address,
|
246
|
+
'value': w3.to_wei(amount_eth, 'ether'),
|
247
|
+
'nonce': w3.eth.get_transaction_count(agent_account.address),
|
248
|
+
'gasPrice': w3.eth.gas_price
|
249
|
+
})
|
250
|
+
signed_tx = w3.eth.account.sign_transaction(tx, private_key=AGENT_PRIVATE_KEY)
|
251
|
+
tx_hash = w3.eth.send_raw_transaction(signed_tx.raw_transaction)
|
252
|
+
print(f"Waiting for wrap transaction {w3.to_hex(tx_hash)} to be confirmed...")
|
253
|
+
w3.eth.wait_for_transaction_receipt(tx_hash)
|
254
|
+
|
255
|
+
return f"ETH wrapping successful and confirmed. Hash: {w3.to_hex(tx_hash)}"
|
256
|
+
except Exception as e:
|
257
|
+
return f"Error wrapping ETH: {e}"
|
258
|
+
|
259
|
+
@tool
|
260
|
+
def swap_tokens_for_tokens(token_in_address: str, token_out_address: str, amount_in: float, fee: int = 3000) -> str:
|
261
|
+
"""Swaps an exact amount of an input token for another on Uniswap V3, waiting for confirmations."""
|
262
|
+
try:
|
263
|
+
_initialize_web3()
|
264
|
+
chk_token_in = Web3.to_checksum_address(token_in_address)
|
265
|
+
chk_token_out = Web3.to_checksum_address(token_out_address)
|
266
|
+
chk_router_address = Web3.to_checksum_address(UNISWAP_ROUTER_ADDRESS)
|
267
|
+
|
268
|
+
token_in_contract = w3.eth.contract(address=chk_token_in, abi=ERC20_STANDARD_ABI)
|
269
|
+
decimals = token_in_contract.functions.decimals().call()
|
270
|
+
amount_in_wei = int(amount_in * (10**decimals))
|
271
|
+
|
272
|
+
current_nonce = w3.eth.get_transaction_count(agent_account.address)
|
273
|
+
|
274
|
+
# Step 1: Approve
|
275
|
+
approve_tx = token_in_contract.functions.approve(chk_router_address, amount_in_wei).build_transaction({
|
276
|
+
'from': agent_account.address, 'nonce': current_nonce
|
277
|
+
})
|
278
|
+
signed_approve_tx = w3.eth.account.sign_transaction(approve_tx, private_key=os.getenv("AGENT_PRIVATE_KEY"))
|
279
|
+
approve_tx_hash = w3.eth.send_raw_transaction(signed_approve_tx.raw_transaction)
|
280
|
+
|
281
|
+
# --- THE FIX ---
|
282
|
+
# Wait for the approval transaction to be confirmed.
|
283
|
+
print(f"Waiting for approval transaction {w3.to_hex(approve_tx_hash)} to be confirmed...")
|
284
|
+
w3.eth.wait_for_transaction_receipt(approve_tx_hash)
|
285
|
+
|
286
|
+
# Step 2: Swap
|
287
|
+
uniswap_router = w3.eth.contract(address=chk_router_address, abi=UNISWAP_ABI)
|
288
|
+
swap_params = (chk_token_in, chk_token_out, fee, agent_account.address, amount_in_wei, 0, 0)
|
289
|
+
|
290
|
+
swap_tx = uniswap_router.functions.exactInputSingle(swap_params).build_transaction({
|
291
|
+
'from': agent_account.address, 'nonce': current_nonce + 1
|
292
|
+
})
|
293
|
+
signed_swap_tx = w3.eth.account.sign_transaction(swap_tx, private_key=os.getenv("AGENT_PRIVATE_KEY"))
|
294
|
+
swap_tx_hash = w3.eth.send_raw_transaction(signed_swap_tx.raw_transaction)
|
295
|
+
|
296
|
+
print(f"Waiting for swap transaction {w3.to_hex(swap_tx_hash)} to be confirmed...")
|
297
|
+
w3.eth.wait_for_transaction_receipt(swap_tx_hash)
|
298
|
+
|
299
|
+
return f"Swap successful and confirmed. Swap hash: {w3.to_hex(swap_tx_hash)}"
|
300
|
+
except Exception as e:
|
301
|
+
return f"Error during swap: {e}"
|
@@ -139,4 +139,4 @@ class ChromaStore(VectorStoreInterface):
|
|
139
139
|
documents=results.get("documents"), metadatas=results.get("metadatas"), distances=results.get("distances") )
|
140
140
|
|
141
141
|
async def adelete(self, ids: Optional[List[ID]] = None, where: Optional[Dict[str, Any]] = None, where_document: Optional[Dict[str, Any]] = None) -> None:
|
142
|
-
await self._run_sync(self._collection.delete, ids=ids, where=where, where_document=where_document)
|
142
|
+
await self._run_sync(self._collection.delete, ids=ids, where=where, where_document=where_document)
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: clap-agents
|
3
|
-
Version: 0.
|
3
|
+
Version: 0.3.1
|
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
|
@@ -242,6 +242,8 @@ Requires-Dist: pandas; extra == 'all'
|
|
242
242
|
Requires-Dist: pypdf; extra == 'all'
|
243
243
|
Requires-Dist: qdrant-client[fastembed]>=1.7.0; extra == 'all'
|
244
244
|
Requires-Dist: sentence-transformers; extra == 'all'
|
245
|
+
Requires-Dist: tf-keras; extra == 'all'
|
246
|
+
Requires-Dist: web3<8.0.0,>=7.12.0; extra == 'all'
|
245
247
|
Provides-Extra: chromadb
|
246
248
|
Requires-Dist: chromadb>=0.5.0; extra == 'chromadb'
|
247
249
|
Provides-Extra: fastembed
|
@@ -261,12 +263,16 @@ Requires-Dist: ollama>=0.2.0; extra == 'rag'
|
|
261
263
|
Requires-Dist: pypdf; extra == 'rag'
|
262
264
|
Requires-Dist: qdrant-client[fastembed]>=1.7.0; extra == 'rag'
|
263
265
|
Requires-Dist: sentence-transformers; extra == 'rag'
|
266
|
+
Requires-Dist: tf-keras; extra == 'rag'
|
264
267
|
Provides-Extra: sentence-transformers
|
265
268
|
Requires-Dist: sentence-transformers; extra == 'sentence-transformers'
|
269
|
+
Requires-Dist: tf-keras; extra == 'sentence-transformers'
|
266
270
|
Provides-Extra: standard-tools
|
267
271
|
Requires-Dist: crawl4ai; extra == 'standard-tools'
|
268
272
|
Provides-Extra: viz
|
269
273
|
Requires-Dist: graphviz; extra == 'viz'
|
274
|
+
Provides-Extra: web3
|
275
|
+
Requires-Dist: web3<8.0.0,>=7.12.0; extra == 'web3'
|
270
276
|
Description-Content-Type: text/markdown
|
271
277
|
|
272
278
|
<p align="center">
|
@@ -506,6 +512,83 @@ async def main():
|
|
506
512
|
asyncio.run(main())
|
507
513
|
```
|
508
514
|
|
515
|
+
|
516
|
+
New in v0.3.0: Web3 & On-Chain Agent Capabilities
|
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
|
+
Setup
|
519
|
+
To enable Web3 capabilities, install the web3 extra:
|
520
|
+
pip install "clap-agents[web3]"
|
521
|
+
Use code with caution.
|
522
|
+
Bash
|
523
|
+
You will also need to set the following variables in your .env file:
|
524
|
+
# Your connection to the blockchain (e.g., from Alchemy or Infura)
|
525
|
+
WEB3_PROVIDER_URL="https://sepolia.infura.io/v3/YOUR_API_KEY"
|
526
|
+
|
527
|
+
# The private key for your agent's wallet.
|
528
|
+
# WARNING: For testing only. Do not use a key with real funds.
|
529
|
+
AGENT_PRIVATE_KEY="0xYourTestnetPrivateKeyHere"
|
530
|
+
|
531
|
+
|
532
|
+
|
533
|
+
# Core Web3 Tools
|
534
|
+
The framework now includes a suite of pre-built, robust tools for on-chain interaction:
|
535
|
+
|
536
|
+
get_erc20_balance: Checks the balance of any standard ERC-20 token in a wallet.
|
537
|
+
|
538
|
+
wrap_eth: Converts native ETH into WETH (Wrapped Ether), a necessary step for interacting with many DeFi protocols.
|
539
|
+
|
540
|
+
swap_exact_tokens_for_tokens: Executes trades on Uniswap V3, allowing your agent to autonomously rebalance its portfolio.
|
541
|
+
|
542
|
+
get_token_price: Fetches real-time asset prices from on-chain Chainlink oracles, enabling data-driven decision-making.
|
543
|
+
|
544
|
+
interact_with_contract: A powerful, generic tool to call any function on any smart contract, given its address and ABI.
|
545
|
+
|
546
|
+
|
547
|
+
# Quick Start: A Simple DeFi Agent
|
548
|
+
This example demonstrates an agent that can wrap ETH and then swap it for another token, a common DeFi task.
|
549
|
+
# examples/simple_defi_agent.py
|
550
|
+
import os
|
551
|
+
import asyncio
|
552
|
+
from dotenv import load_dotenv
|
553
|
+
from clap import ReactAgent, GroqService
|
554
|
+
from clap.tools import wrap_eth, swap_exact_tokens_for_tokens
|
555
|
+
|
556
|
+
load_dotenv()
|
557
|
+
|
558
|
+
# --- Configuration ---
|
559
|
+
WETH_ADDRESS = "0xfFf9976782d46CC05630D1f6eBAb18b2324d6B14" # WETH on Sepolia
|
560
|
+
USDC_ADDRESS = "0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7a98" # USDC on Sepolia
|
561
|
+
|
562
|
+
async def main():
|
563
|
+
# We use a ReactAgent for multi-step reasoning
|
564
|
+
agent = ReactAgent(
|
565
|
+
llm_service=GroqService(),
|
566
|
+
tools=[wrap_eth, swap_exact_tokens_for_tokens],
|
567
|
+
model="llama-3.3-70b-versatile",
|
568
|
+
system_prompt="You are a DeFi agent. You execute financial transactions precisely as instructed.",
|
569
|
+
# For on-chain tasks, sequential execution is safer to avoid race conditions
|
570
|
+
parallel_tool_calls=False
|
571
|
+
)
|
572
|
+
|
573
|
+
# A clear, two-step task for the agent
|
574
|
+
user_query = f"""
|
575
|
+
First, wrap 0.01 ETH.
|
576
|
+
Second, after the wrap is successful, swap that 0.01 WETH for USDC.
|
577
|
+
The WETH address is {WETH_ADDRESS} and the USDC address is {USDC_ADDRESS}.
|
578
|
+
"""
|
579
|
+
|
580
|
+
print("--- Running Simple DeFi Agent ---")
|
581
|
+
response = await agent.run(user_msg=user_query, max_rounds=5)
|
582
|
+
|
583
|
+
print("\n--- Agent Final Response ---")
|
584
|
+
print(response)
|
585
|
+
|
586
|
+
if __name__ == "__main__":
|
587
|
+
asyncio.run(main())
|
588
|
+
|
589
|
+
|
590
|
+
This new capability transforms your CLAP agents from simple observers into active participants in the decentralized economy.
|
591
|
+
|
509
592
|
## Exploring Further
|
510
593
|
|
511
594
|
|
@@ -1,26 +1,27 @@
|
|
1
1
|
clap/__init__.py,sha256=rxxESl-xpSZpM4ZIh-GvHYF74CkQdbe-dSLvhMC_2dQ,1069
|
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
|
9
|
-
clap/llm_services/google_openai_compat_service.py,sha256=
|
10
|
-
clap/llm_services/groq_service.py,sha256=
|
11
|
-
clap/llm_services/ollama_service.py,sha256=
|
7
|
+
clap/llm_services/__init__.py,sha256=IBvWmE99PGxHq5Dt4u0G1erZSV80QEC981UULnrD6Tk,496
|
8
|
+
clap/llm_services/base.py,sha256=-XKWd6gLAXedIhUUqM_f7sqkVxdfifP2j-BwmF0hUkI,2183
|
9
|
+
clap/llm_services/google_openai_compat_service.py,sha256=vN0osfCS6DIFHsCCiB03mKUp4n7SkIJd2PAypBAnC30,4552
|
10
|
+
clap/llm_services/groq_service.py,sha256=pcTp24_NgLfp3bGaABzli_Sey7wZsXvFI74VjZ1GvkQ,3051
|
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=IVwtkOAEN7LRaFqw14HQDC7n6OTZ3ciVYWj-lWQddE0,8681
|
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
|
-
clap/react_pattern/react_agent.py,sha256=
|
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
|
-
clap/tools/__init__.py,sha256=
|
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=N5enOoEVMx0T7TjB15NtEYt-_KcAdkm3C-UN0_QLVoo,12894
|
24
25
|
clap/tools/web_crawler.py,sha256=WdFbAKhUsUVveJimmLbzQ6k1BhOdMsg87FjL628HEKM,3542
|
25
26
|
clap/tools/web_search.py,sha256=YT0I1kPrdxMUst-dpsGqqF6aqxMgj3ACwiW_jN9Pu9s,985
|
26
27
|
clap/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
@@ -30,9 +31,9 @@ clap/utils/logging.py,sha256=FfrOHXnVJYCgc3mepIMKcIcGSdSWkMpV7ccKPGFxFiM,727
|
|
30
31
|
clap/utils/rag_utils.py,sha256=Zy-C9l39tHrxYSluaDWcEl1g__uozu4jx5hEGC7h370,6455
|
31
32
|
clap/vector_stores/__init__.py,sha256=H3w5jLdQFbXArVgiidy4RlAalM8a6LAiMlAX0Z-2v7U,404
|
32
33
|
clap/vector_stores/base.py,sha256=nvk8J1oNG3OKFhJfxBGFyVeh9YxoDs9RkB_iOzPBm1w,2853
|
33
|
-
clap/vector_stores/chroma_store.py,sha256=
|
34
|
+
clap/vector_stores/chroma_store.py,sha256=vwkWWGxPwuW45T1PS6D44dXhDG9U_KZWjrMZCOkEXsA,7242
|
34
35
|
clap/vector_stores/qdrant_store.py,sha256=-SwMTb0yaGngpQ9AddDzDIt3x8GZevlFT-0FMkWD28I,9923
|
35
|
-
clap_agents-0.
|
36
|
-
clap_agents-0.
|
37
|
-
clap_agents-0.
|
38
|
-
clap_agents-0.
|
36
|
+
clap_agents-0.3.1.dist-info/METADATA,sha256=4HKAihZchNLte3fWinmWBZAKjDtkpIMYJYLyD6jgfUs,30573
|
37
|
+
clap_agents-0.3.1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
38
|
+
clap_agents-0.3.1.dist-info/licenses/LICENSE,sha256=WNHhf_5RCaeuKWyq_K39vmp9F28LxKsB4SpomwSZ2L0,11357
|
39
|
+
clap_agents-0.3.1.dist-info/RECORD,,
|
File without changes
|
File without changes
|