-
Notifications
You must be signed in to change notification settings - Fork 12
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
21 changed files
with
4,913 additions
and
0 deletions.
There are no files selected for viewing
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
.PHONY: format | ||
format: | ||
ruff format . | ||
|
||
.PHONY: lint | ||
lint: | ||
ruff check . | ||
|
||
.PHONY: lint-fix | ||
lint-fix: | ||
ruff check . --fix |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
# CDP Agentkit Extension - Langchain Toolkit | ||
|
||
## Developing | ||
- Agentkit uses `poetry` for package management and tooling | ||
- [Poetry Installation Instructions](https://python-poetry.org/docs/#installation) | ||
- Run `poetry install` to install `cdp-langchain` dependencies | ||
- Run `poetry shell` to activate the virtual environment | ||
|
||
### Formatting | ||
`make format` | ||
|
||
### Linting | ||
- Check linter | ||
`make lint` | ||
|
||
- Fix linter errors | ||
`make lint-fix` | ||
|
||
## Adding an Agentic Action to the Langchain Toolkit | ||
1. Ensure the action is implemented in `cdp-agentkit-core`. | ||
2. Add a wrapper method to `CdpAgentkitWrapper` in `./cdp_langchain/utils/cdp_agentkit_wrapper.py` | ||
- E.g. | ||
```python | ||
def mint_nft_wrapper(self, contract_address: str, destination: str) -> str: | ||
"""Mint an NFT (ERC-721) to a specified destination address onchain via a contract invocation. | ||
Args: | ||
contract_address (str): "The contract address of the NFT (ERC-721) to mint, e.g. `0x036CbD53842c5426634e7929541eC2318f3dCF7e`". | ||
destination (str): "The destination address that will receieve the NFT onchain, e.g. `0x036CbD53842c5426634e7929541eC2318f3dCF7e`". | ||
Returns: | ||
str: A message containing the NFT mint details. | ||
""" | ||
return mint_nft( | ||
wallet=self.wallet, | ||
contract_address=contract_address, | ||
destination=destination, | ||
) | ||
``` | ||
3. Add call to the wrapper in `CdpAgentkitWrapper.run` in `./cdp_langchain/utils/cdp_agentkit_wrapper.py` | ||
- E.g. | ||
```python | ||
if mode == "mint_nft": | ||
return self.mint_nft_wrapper(**kwargs) | ||
|
||
``` | ||
4. Add the action to the list of available tools in the `CdpToolkit` in `./cdp_langchain/agent_toolkits/cdp_toolkit.py` | ||
- E.g. | ||
```python | ||
actions: List[Dict] = [ | ||
{ | ||
"mode": "mint_nft", | ||
"name": "mint_nft", | ||
"description": MINT_NFT_PROMPT, | ||
"args_schema": MintNftInput, | ||
}, | ||
] | ||
``` | ||
5. Add the action to the list of tools in the `CdpToolkit` class documentation. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
from cdp_langchain.agent_toolkits.cdp_toolkit import CdpToolkit | ||
|
||
__all__ = ["CdpToolkit"] |
215 changes: 215 additions & 0 deletions
215
cdp-langchain/cdp_langchain/agent_toolkits/cdp_toolkit.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,215 @@ | ||
"""CDP Toolkit.""" | ||
|
||
from langchain_core.tools import BaseTool | ||
from langchain_core.tools.base import BaseToolkit | ||
|
||
from cdp_agentkit_core.actions import ( | ||
DEPLOY_NFT_PROMPT, | ||
DEPLOY_TOKEN_PROMPT, | ||
GET_BALANCE_PROMPT, | ||
GET_WALLET_DETAILS_PROMPT, | ||
MINT_NFT_PROMPT, | ||
REGISTER_BASENAME_PROMPT, | ||
REQUEST_FAUCET_FUNDS_PROMPT, | ||
TRADE_PROMPT, | ||
TRANSFER_PROMPT, | ||
DeployNftInput, | ||
DeployTokenInput, | ||
GetBalanceInput, | ||
GetWalletDetailsInput, | ||
MintNftInput, | ||
RegisterBasenameInput, | ||
RequestFaucetFundsInput, | ||
TradeInput, | ||
TransferInput, | ||
) | ||
from cdp_langchain.tools import CdpAction | ||
from cdp_langchain.utils import CdpAgentkitWrapper | ||
|
||
|
||
class CdpToolkit(BaseToolkit): | ||
"""Coinbase Developer Platform (CDP) Toolkit. | ||
*Security Note*: This toolkit contains tools that can read and modify | ||
the state of a service; e.g., by creating, deleting, or updating, | ||
reading underlying data. | ||
For example, this toolkit can be used to create wallets, transactions, | ||
and smart contract invocations on CDP supported blockchains. | ||
See [Security](https://python.langchain.com/docs/security) for more information. | ||
Setup: | ||
See detailed installation instructions here: | ||
https://python.langchain.com/docs/integrations/tools/cdp/#installation | ||
You will need to set the following environment | ||
variables: | ||
.. code-block:: bash | ||
export CDP_API_KEY_NAME="cdp-api-key-name" | ||
export CDP_API_KEY_PRIVATE_KEY="cdp-api-key-private-key" | ||
export NETWORK_ID="network-id" | ||
Instantiate: | ||
.. code-block:: python | ||
from cdp_langchain.agent_toolkits import CdpToolkit | ||
from cdp_langchain.utils import CdpAgentkitWrapper | ||
cdp = CdpAgentkitWrapper() | ||
cdp_toolkit = CdpToolkit.from_cdp_agentkit_wrapper(cdp) | ||
Tools: | ||
.. code-block:: python | ||
tools = cdp_toolkit.get_tools() | ||
for tool in tools: | ||
print(tool.name) | ||
.. code-block:: none | ||
get_wallet_details | ||
get_balance | ||
request_faucet_funds | ||
transfer | ||
trade | ||
deploy_token | ||
mint_nft | ||
deploy_nft | ||
register_basename | ||
Use within an agent: | ||
.. code-block:: python | ||
from langchain_openai import ChatOpenAI | ||
from langgraph.prebuilt import create_react_agent | ||
# Select example tool | ||
tools = [tool for tool in toolkit.get_tools() if tool.name == "get_wallet_details"] | ||
assert len(tools) == 1 | ||
llm = ChatOpenAI(model="gpt-4o-mini") | ||
agent_executor = create_react_agent(llm, tools) | ||
example_query = "Tell me about your wallet" | ||
events = agent_executor.stream( | ||
{"messages": [("user", example_query)]}, | ||
stream_mode="values", | ||
) | ||
for event in events: | ||
event["messages"][-1].pretty_print() | ||
.. code-block:: none | ||
================================[1m Human Message [0m================================= | ||
Tell me about your wallet | ||
==================================[1m Ai Message [0m================================== | ||
Tool Calls: | ||
get_wallet_details (call_iSYJVaM7uchfNHOMJoVPQsOi) | ||
Call ID: call_iSYJVaM7uchfNHOMJoVPQsOi | ||
Args: | ||
no_input: "" | ||
=================================[1m Tool Message [0m================================= | ||
Name: get_wallet_details | ||
... | ||
==================================[1m Ai Message [0m================================== | ||
My wallet is wallet-id-123 on Base Sepolia with default address 0x0123 | ||
Parameters | ||
---------- | ||
tools: List[BaseTool]. The tools in the toolkit. Default is an empty list. | ||
""" | ||
|
||
tools: list[BaseTool] = [] # noqa: RUF012 | ||
|
||
@classmethod | ||
def from_cdp_agentkit_wrapper(cls, cdp_agentkit_wrapper: CdpAgentkitWrapper) -> "CdpToolkit": | ||
"""Create a CdpToolkit from a CdpAgentkitWrapper. | ||
Args: | ||
cdp_agentkit_wrapper: CdpAgentkitWrapper. The CDP Agentkit wrapper. | ||
Returns: | ||
CdpToolkit. The CDP toolkit. | ||
""" | ||
actions: list[dict] = [ | ||
{ | ||
"mode": "get_wallet_details", | ||
"name": "get_wallet_details", | ||
"description": GET_WALLET_DETAILS_PROMPT, | ||
"args_schema": GetWalletDetailsInput, | ||
}, | ||
{ | ||
"mode": "get_balance", | ||
"name": "get_balance", | ||
"description": GET_BALANCE_PROMPT, | ||
"args_schema": GetBalanceInput, | ||
}, | ||
{ | ||
"mode": "request_faucet_funds", | ||
"name": "request_faucet_funds", | ||
"description": REQUEST_FAUCET_FUNDS_PROMPT, | ||
"args_schema": RequestFaucetFundsInput, | ||
}, | ||
{ | ||
"mode": "transfer", | ||
"name": "transfer", | ||
"description": TRANSFER_PROMPT, | ||
"args_schema": TransferInput, | ||
}, | ||
{ | ||
"mode": "trade", | ||
"name": "trade", | ||
"description": TRADE_PROMPT, | ||
"args_schema": TradeInput, | ||
}, | ||
{ | ||
"mode": "deploy_token", | ||
"name": "deploy_token", | ||
"description": DEPLOY_TOKEN_PROMPT, | ||
"args_schema": DeployTokenInput, | ||
}, | ||
{ | ||
"mode": "mint_nft", | ||
"name": "mint_nft", | ||
"description": MINT_NFT_PROMPT, | ||
"args_schema": MintNftInput, | ||
}, | ||
{ | ||
"mode": "deploy_nft", | ||
"name": "deploy_nft", | ||
"description": DEPLOY_NFT_PROMPT, | ||
"args_schema": DeployNftInput, | ||
}, | ||
{ | ||
"mode": "register_basename", | ||
"name": "register_basename", | ||
"description": REGISTER_BASENAME_PROMPT, | ||
"args_schema": RegisterBasenameInput, | ||
}, | ||
] | ||
|
||
tools = [ | ||
CdpAction( | ||
name=action["name"], | ||
description=action["description"], | ||
mode=action["mode"], | ||
cdp_agentkit_wrapper=cdp_agentkit_wrapper, | ||
args_schema=action.get("args_schema", None), | ||
) | ||
for action in actions | ||
] | ||
|
||
return cls(tools=tools) # type: ignore[arg-type] | ||
|
||
def get_tools(self) -> list[BaseTool]: | ||
"""Get the tools in the toolkit.""" | ||
return self.tools |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
"""CDP Tool.""" | ||
|
||
from cdp_langchain.tools.cdp_action import CdpAction | ||
|
||
__all__ = ["CdpAction"] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
"""Tool allows agents to interact with the cdp-sdk library and control an MPC Wallet onchain. | ||
To use this tool, you must first set as environment variables: | ||
CDP_API_KEY_NAME | ||
CDP_API_KEY_PRIVATE_KEY | ||
NETWORK_ID | ||
""" | ||
|
||
from typing import Any | ||
|
||
from langchain_core.callbacks import CallbackManagerForToolRun | ||
from langchain_core.tools import BaseTool | ||
from pydantic import BaseModel | ||
|
||
from cdp_langchain.utils.cdp_agentkit_wrapper import CdpAgentkitWrapper | ||
|
||
|
||
class CdpAction(BaseTool): # type: ignore[override] | ||
"""Tool for interacting with the CDP SDK.""" | ||
|
||
cdp_agentkit_wrapper: CdpAgentkitWrapper | ||
mode: str | ||
name: str = "" | ||
description: str = "" | ||
args_schema: type[BaseModel] | None = None | ||
|
||
def _run( | ||
self, | ||
instructions: str | None = "", | ||
run_manager: CallbackManagerForToolRun | None = None, | ||
**kwargs: Any, | ||
) -> str: | ||
"""Use the CDP SDK to run an operation.""" | ||
if not instructions or instructions == "{}": | ||
# Catch other forms of empty input that GPT-4 likes to send. | ||
instructions = "" | ||
if self.args_schema is not None: | ||
validated_input_data = self.args_schema(**kwargs) | ||
parsed_input_args = validated_input_data.model_dump() | ||
else: | ||
parsed_input_args = {"instructions": instructions} | ||
return self.cdp_agentkit_wrapper.run(self.mode, **parsed_input_args) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
"""**Utilities** are the integration wrappers that LangChain uses to interact with third-party systems and packages.""" | ||
|
||
from cdp_langchain.utils.cdp_agentkit_wrapper import CdpAgentkitWrapper | ||
|
||
__all__ = ["CdpAgentkitWrapper"] |
Oops, something went wrong.