Skip to content

Commit

Permalink
chore: Remove Action enumeration/re-implementation in Framework Exts
Browse files Browse the repository at this point in the history
  • Loading branch information
John-peterson-coinbase committed Nov 7, 2024
1 parent 63eb7cc commit c4aa2f2
Show file tree
Hide file tree
Showing 26 changed files with 427 additions and 654 deletions.
63 changes: 18 additions & 45 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,16 +25,27 @@ git clone git@github.com:coinbase/cdp-agentkit.git

## Adding an Action to Agentkit Core
- Actions are defined in `./cdp_agentkit_core/actions` module. See `./cdp_agentkit_core/actions/mint_nft.py` for an example.
- Actions are created by subclassing `CdpAction`
E.g.
```python
class DeployNftAction(CdpAction):
"""Deploy NFT action."""

name: str = "mint_nft"
description: str = MINT_NFT_PROMPT
args_schema: type[BaseModel] | None = MintNftInput
func: Callable[..., str] = mint_nft
```

### Components of an Agentic Action
Each action will define and export 3 components:
- Prompt - A string that will provide the AI Agent with context on what the function does and a natural language description of the input.
- `name` - Name of the action.
- `description` - A string that will provide the AI Agent with context on what the function does and a natural language description of the input.
- E.g.
```python
MINT_NFT_PROMPT = """
This tool will mint an NFT (ERC-721) to a specified destination address onchain via a contract invocation. It takes the contract address of the NFT onchain and the destination address onchain that will receive the NFT as inputs."""
```
- ArgSchema - A Pydantic Model that defines the input argument schema for the action.
- `arg_schema` - A Pydantic Model that defines the input argument schema for the action.
- E.g.
```python
class MintNftInput(BaseModel):
Expand All @@ -49,7 +60,7 @@ class MintNftInput(BaseModel):
description="The destination address that will receieve the NFT onchain, e.g. `0x036CbD53842c5426634e7929541eC2318f3dCF7e`",
)
```
- Action Callable - A function (or Callable class) that executes the action.
- `func` - A function (or Callable class) that executes the action.
- E.g.
```python
def mint_nft(wallet: Wallet, contract_address: str, destination: str) -> str:
Expand All @@ -74,47 +85,9 @@ def mint_nft(wallet: Wallet, contract_address: str, destination: str) -> str:
```

## Adding an Agentic Action to 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.
1. Ensure the action is implemented in `cdp-agentkit-core` and in a released version.
2. Update the `cdp-agentkit-core` dependency to the latest version.
3. Add the action to the list of tools in the `CdpToolkit` class documentation.

## Adding an Agentic Action to the Twitter Toolkit
1. Ensure the action is implemented in `cdp-agentkit-core/actions/social/twitter`.
Expand Down
114 changes: 34 additions & 80 deletions cdp-agentkit-core/cdp_agentkit_core/actions/__init__.py
Original file line number Diff line number Diff line change
@@ -1,83 +1,37 @@
from cdp_agentkit_core.actions.deploy_nft import (
DEPLOY_NFT_PROMPT,
DeployNftInput,
deploy_nft,
)
from cdp_agentkit_core.actions.deploy_token import (
DEPLOY_TOKEN_PROMPT,
DeployTokenInput,
deploy_token,
)
from cdp_agentkit_core.actions.get_balance import (
GET_BALANCE_PROMPT,
GetBalanceInput,
get_balance,
)
from cdp_agentkit_core.actions.get_wallet_details import (
GET_WALLET_DETAILS_PROMPT,
GetWalletDetailsInput,
get_wallet_details,
)
from cdp_agentkit_core.actions.mint_nft import (
MINT_NFT_PROMPT,
MintNftInput,
mint_nft,
)
from cdp_agentkit_core.actions.register_basename import (
REGISTER_BASENAME_PROMPT,
RegisterBasenameInput,
register_basename,
)
from cdp_agentkit_core.actions.request_faucet_funds import (
REQUEST_FAUCET_FUNDS_PROMPT,
RequestFaucetFundsInput,
request_faucet_funds,
)
from cdp_agentkit_core.actions.trade import (
TRADE_PROMPT,
TradeInput,
trade,
)
from cdp_agentkit_core.actions.transfer import (
TRANSFER_PROMPT,
TransferInput,
transfer,
)
from cdp_agentkit_core.actions.uniswap_v3.create_pool import (
UNISWAP_V3_CREATE_POOL_PROMPT,
UniswapV3CreatePoolInput,
uniswap_v3_create_pool,
)
from cdp_agentkit_core.actions.cdp_action import CdpAction
from cdp_agentkit_core.actions.deploy_nft import DeployNftAction
from cdp_agentkit_core.actions.deploy_token import DeployTokenAction
from cdp_agentkit_core.actions.get_balance import GetBalanceAction
from cdp_agentkit_core.actions.get_wallet_details import GetWalletDetailsAction
from cdp_agentkit_core.actions.mint_nft import MintNftAction
from cdp_agentkit_core.actions.register_basename import RegisterBasenameAction
from cdp_agentkit_core.actions.request_faucet_funds import RequestFaucetFundsAction
from cdp_agentkit_core.actions.trade import TradeAction
from cdp_agentkit_core.actions.transfer import TransferAction
from cdp_agentkit_core.actions.uniswap_v3.create_pool import UniswapV3CreatePoolAction


def get_all_cdp_actions() -> list[type[CdpAction]]:
"""Retrieve all subclasses of CdpAction defined in the package."""
actions = []
for action in CdpAction.__subclasses__():
actions.append(action())
return actions


CDP_ACTIONS = get_all_cdp_actions()

__all__ = [
"UNISWAP_V3_CREATE_POOL_PROMPT",
"UniswapV3CreatePoolInput",
"uniswap_v3_create_pool",
"DEPLOY_NFT_PROMPT",
"DeployNftInput",
"deploy_nft",
"DEPLOY_TOKEN_PROMPT",
"DeployTokenInput",
"deploy_token",
"GET_BALANCE_PROMPT",
"GetBalanceInput",
"get_balance",
"GET_WALLET_DETAILS_PROMPT",
"GetWalletDetailsInput",
"get_wallet_details",
"MINT_NFT_PROMPT",
"MintNftInput",
"mint_nft",
"REGISTER_BASENAME_PROMPT",
"RegisterBasenameInput",
"register_basename",
"REQUEST_FAUCET_FUNDS_PROMPT",
"RequestFaucetFundsInput",
"request_faucet_funds",
"TRADE_PROMPT",
"TradeInput",
"trade",
"TRANSFER_PROMPT",
"TransferInput",
"transfer",
"CdpAction",
"GetWalletDetailsAction",
"DeployNftAction",
"DeployTokenAction",
"GetBalanceAction",
"MintNftAction",
"RegisterBasenameAction",
"RequestFaucetFundsAction",
"TradeAction",
"TransferAction",
"UniswapV3CreatePoolAction",
"CDP_ACTIONS",
]
12 changes: 12 additions & 0 deletions cdp-agentkit-core/cdp_agentkit_core/actions/cdp_action.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
from collections.abc import Callable

from pydantic import BaseModel


class CdpAction(BaseModel):
"""CDP Action Base Class."""

name: str
description: str
args_schema: type[BaseModel] | None = None
func: Callable[..., str]
13 changes: 13 additions & 0 deletions cdp-agentkit-core/cdp_agentkit_core/actions/deploy_nft.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
from collections.abc import Callable

from cdp import Wallet
from pydantic import BaseModel, Field

from cdp_agentkit_core.actions import CdpAction

DEPLOY_NFT_PROMPT = """
This tool will deploy an NFT (ERC-721) contract onchain from the wallet. It takes the name of the NFT collection, the symbol of the NFT collection, and the base URI for the token metadata as inputs."""

Expand Down Expand Up @@ -38,3 +42,12 @@ def deploy_nft(wallet: Wallet, name: str, symbol: str, base_uri: str) -> str:
nft_contract = wallet.deploy_nft(name=name, symbol=symbol, base_uri=base_uri).wait()

return f"Deployed NFT Collection {name} to address {nft_contract.contract_address} on network {wallet.network_id}.\nTransaction hash for the deployment: {nft_contract.transaction.transaction_hash}\nTransaction link for the deployment: {nft_contract.transaction.transaction_link}"


class DeployNftAction(CdpAction):
"""Deploy NFT action."""

name: str = "deploy_nft"
description: str = DEPLOY_NFT_PROMPT
args_schema: type[BaseModel] | None = DeployNftInput
func: Callable[..., str] = deploy_nft
13 changes: 13 additions & 0 deletions cdp-agentkit-core/cdp_agentkit_core/actions/deploy_token.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
from collections.abc import Callable

from cdp import Wallet
from pydantic import BaseModel, Field

from cdp_agentkit_core.actions import CdpAction

DEPLOY_TOKEN_PROMPT = """
This tool will deploy an ERC20 token smart contract. It takes the token name, symbol, and total supply as input. The token will be deployed using the wallet's default address as the owner and initial token holder.
"""
Expand Down Expand Up @@ -34,3 +38,12 @@ def deploy_token(wallet: Wallet, name: str, symbol: str, total_supply: str) -> s
token_contract.wait()

return f"Deployed ERC20 token contract {name} ({symbol}) with total supply of {total_supply} tokens at address {token_contract.contract_address}. Transaction link: {token_contract.transaction.transaction_link}"


class DeployTokenAction(CdpAction):
"""Deploy token action."""

name: str = "deploy_token"
description: str = DEPLOY_TOKEN_PROMPT
args_schema: type[BaseModel] | None = DeployTokenInput
func: Callable[..., str] = deploy_token
13 changes: 13 additions & 0 deletions cdp-agentkit-core/cdp_agentkit_core/actions/get_balance.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
from collections.abc import Callable

from cdp import Wallet
from pydantic import BaseModel, Field

from cdp_agentkit_core.actions import CdpAction

GET_BALANCE_PROMPT = """
This tool will get the balance of all the addresses in the wallet for a given asset. It takes the asset ID as input."""

Expand Down Expand Up @@ -35,3 +39,12 @@ def get_balance(wallet: Wallet, asset_id: str) -> str:
balance_lines = [f" {addr}: {balance}" for addr, balance in balances.items()]
formatted_balances = "\n".join(balance_lines)
return f"Balances for wallet {wallet.id}:\n{formatted_balances}"


class GetBalanceAction(CdpAction):
"""Get wallet balance action."""

name: str = "get_balance"
description: str = GET_BALANCE_PROMPT
args_schema: type[BaseModel] | None = GetBalanceInput
func: Callable[..., str] = get_balance
21 changes: 13 additions & 8 deletions cdp-agentkit-core/cdp_agentkit_core/actions/get_wallet_details.py
Original file line number Diff line number Diff line change
@@ -1,18 +1,14 @@
from collections.abc import Callable

from cdp import Wallet
from pydantic import BaseModel, Field
from pydantic import BaseModel

GET_WALLET_DETAILS_PROMPT = """
This tool will get details about the MPC Wallet."""
from cdp_agentkit_core.actions import CdpAction


class GetWalletDetailsInput(BaseModel):
"""Input argument schema for get wallet details action."""

no_input: str = Field(
"",
description="No input required, e.g. `` (empty string).",
)


def get_wallet_details(wallet: Wallet) -> str:
"""Get a wallet's details.
Expand All @@ -25,3 +21,12 @@ def get_wallet_details(wallet: Wallet) -> str:
"""
return f"Wallet: {wallet.id} on network: {wallet.network_id} with default address: {wallet.default_address.address_id}"


class GetWalletDetailsAction(CdpAction):
"""Get wallet details action."""

name: str = "get_wallet_details"
description: str = "This tool will get details about the MPC Wallet."
args_schema: type[BaseModel] | None = GetWalletDetailsInput
func: Callable[..., str] = get_wallet_details
13 changes: 13 additions & 0 deletions cdp-agentkit-core/cdp_agentkit_core/actions/mint_nft.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
from collections.abc import Callable

from cdp import Wallet
from pydantic import BaseModel, Field

from cdp_agentkit_core.actions import CdpAction

MINT_NFT_PROMPT = """
This tool will mint an NFT (ERC-721) to a specified destination address onchain via a contract invocation. It takes the contract address of the NFT onchain and the destination address onchain that will receive the NFT as inputs."""

Expand Down Expand Up @@ -37,3 +41,12 @@ def mint_nft(wallet: Wallet, contract_address: str, destination: str) -> str:
).wait()

return f"Minted NFT from contract {contract_address} to address {destination} on network {wallet.network_id}.\nTransaction hash for the mint: {mint_invocation.transaction.transaction_hash}\nTransaction link for the mint: {mint_invocation.transaction.transaction_link}"


class MintNftAction(CdpAction):
"""Mint NFT action."""

name: str = "mint_nft"
description: str = MINT_NFT_PROMPT
args_schema: type[BaseModel] | None = MintNftInput
func: Callable[..., str] = mint_nft
17 changes: 15 additions & 2 deletions cdp-agentkit-core/cdp_agentkit_core/actions/register_basename.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
from collections.abc import Callable

from cdp import Wallet
from pydantic import BaseModel, Field
from web3 import Web3
from web3.exceptions import ContractLogicError

from cdp_agentkit_core.actions import CdpAction

# Constants
REGISTER_BASENAME_PROMPT = """
This tool will register a Basename for the agent. The agent should have a wallet associated to register a Basename.
Expand Down Expand Up @@ -30,13 +34,13 @@ class RegisterBasenameInput(BaseModel):
)


def register_basename(wallet: Wallet, basename: str, amount: float | None = 0.002) -> str:
def register_basename(wallet: Wallet, basename: str, amount: str = "0.002") -> str:
"""Register a Basename for the agent.
Args:
wallet (Wallet): The wallet to register the Basename with.
basename (str): The Basename to assign to the agent.
amount (float): The amount of ETH to pay for the registration. The default is set to 0.002.
amount (str): The amount of ETH to pay for the registration. The default is set to 0.002.
Returns:
str: Confirmation message with the basename.
Expand Down Expand Up @@ -158,3 +162,12 @@ def create_register_contract_method_args(base_name: str, address_id: str, is_mai
"type": "function",
}
]


class RegisterBasenameAction(CdpAction):
"""Register Basename action."""

name: str = "register_basename"
description: str = REGISTER_BASENAME_PROMPT
args_schema: type[BaseModel] | None = RegisterBasenameInput
func: Callable[..., str] = register_basename
Loading

0 comments on commit c4aa2f2

Please sign in to comment.