└── Based-Agent ├── .gitignore ├── .pythonlibs └── bin │ ├── cbor2 │ ├── cdp │ ├── distro │ ├── edsig │ ├── f2py │ ├── httpx │ ├── identify-cli │ ├── instructor │ ├── markdown-it │ ├── nodeenv │ ├── normalizer │ ├── numpy-config │ ├── openai │ ├── pre-commit │ ├── py.test │ ├── pygmentize │ ├── pytest │ ├── tqdm │ ├── typer │ └── virtualenv ├── .replit ├── LICENSE ├── README.md ├── agents.py ├── evals.py ├── image.png ├── image.webp ├── poetry.lock ├── pyproject.toml ├── replit_zip_error_log.txt ├── run.py └── twitter_utils.py /Based-Agent/.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | share/python-wheels/ 24 | *.egg-info/ 25 | .installed.cfg 26 | *.egg 27 | MANIFEST 28 | 29 | # PyInstaller 30 | # Usually these files are written by a python script from a template 31 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 32 | *.manifest 33 | *.spec 34 | 35 | # Installer logs 36 | pip-log.txt 37 | pip-delete-this-directory.txt 38 | 39 | # Unit test / coverage reports 40 | htmlcov/ 41 | .tox/ 42 | .nox/ 43 | .coverage 44 | .coverage.* 45 | .cache 46 | nosetests.xml 47 | coverage.xml 48 | *.cover 49 | *.py,cover 50 | .hypothesis/ 51 | .pytest_cache/ 52 | cover/ 53 | 54 | # Translations 55 | *.mo 56 | *.pot 57 | 58 | # Django stuff: 59 | *.log 60 | local_settings.py 61 | db.sqlite3 62 | db.sqlite3-journal 63 | 64 | # Flask stuff: 65 | instance/ 66 | .webassets-cache 67 | 68 | # Scrapy stuff: 69 | .scrapy 70 | 71 | # Sphinx documentation 72 | docs/_build/ 73 | 74 | # PyBuilder 75 | .pybuilder/ 76 | target/ 77 | 78 | # Jupyter Notebook 79 | .ipynb_checkpoints 80 | 81 | # IPython 82 | profile_default/ 83 | ipython_config.py 84 | 85 | # pyenv 86 | # For a library or package, you might want to ignore these files since the code is 87 | # intended to run in multiple environments; otherwise, check them in: 88 | # .python-version 89 | 90 | # pipenv 91 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 92 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 93 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 94 | # install all needed dependencies. 95 | #Pipfile.lock 96 | 97 | # poetry 98 | # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. 99 | # This is especially recommended for binary packages to ensure reproducibility, and is more 100 | # commonly ignored for libraries. 101 | # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control 102 | #poetry.lock 103 | 104 | # pdm 105 | # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. 106 | #pdm.lock 107 | # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it 108 | # in version control. 109 | # https://pdm.fming.dev/#use-with-ide 110 | .pdm.toml 111 | 112 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm 113 | __pypackages__/ 114 | 115 | # Celery stuff 116 | celerybeat-schedule 117 | celerybeat.pid 118 | 119 | # SageMath parsed files 120 | *.sage.py 121 | 122 | # Environments 123 | .env 124 | .venv 125 | env/ 126 | venv/ 127 | ENV/ 128 | env.bak/ 129 | venv.bak/ 130 | 131 | # Spyder project settings 132 | .spyderproject 133 | .spyproject 134 | 135 | # Rope project settings 136 | .ropeproject 137 | 138 | # mkdocs documentation 139 | /site 140 | 141 | # mypy 142 | .mypy_cache/ 143 | .dmypy.json 144 | dmypy.json 145 | 146 | # Pyre type checker 147 | .pyre/ 148 | 149 | # pytype static type analyzer 150 | .pytype/ 151 | 152 | # Cython debug symbols 153 | cython_debug/ 154 | 155 | # PyCharm 156 | # JetBrains specific template is maintained in a separate JetBrains.gitignore that can 157 | # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore 158 | # and can be added to the global gitignore or merged into this file. For a more nuclear 159 | # option (not recommended) you can uncomment the following to ignore the entire idea folder. 160 | #.idea/ -------------------------------------------------------------------------------- /Based-Agent/.pythonlibs/bin/cbor2: -------------------------------------------------------------------------------- 1 | #!/nix/store/inyy1dbm33g70ikhzlacx78ljl05q88g-python-wrapped-0.1.0/bin/python3 2 | # -*- coding: utf-8 -*- 3 | import re 4 | import sys 5 | from cbor2.tool import main 6 | if __name__ == '__main__': 7 | sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) 8 | sys.exit(main()) 9 | -------------------------------------------------------------------------------- /Based-Agent/.pythonlibs/bin/cdp: -------------------------------------------------------------------------------- 1 | #!/nix/store/inyy1dbm33g70ikhzlacx78ljl05q88g-python-wrapped-0.1.0/bin/python3 2 | # -*- coding: utf-8 -*- 3 | import re 4 | import sys 5 | from cdp.cdp import main 6 | if __name__ == "__main__": 7 | sys.argv[0] = re.sub(r"(-script\.pyw|\.exe)?$", "", sys.argv[0]) 8 | sys.exit(main()) 9 | -------------------------------------------------------------------------------- /Based-Agent/.pythonlibs/bin/distro: -------------------------------------------------------------------------------- 1 | #!/nix/store/inyy1dbm33g70ikhzlacx78ljl05q88g-python-wrapped-0.1.0/bin/python3 2 | # -*- coding: utf-8 -*- 3 | import re 4 | import sys 5 | from distro.distro import main 6 | if __name__ == "__main__": 7 | sys.argv[0] = re.sub(r"(-script\.pyw|\.exe)?$", "", sys.argv[0]) 8 | sys.exit(main()) 9 | -------------------------------------------------------------------------------- /Based-Agent/.pythonlibs/bin/edsig: -------------------------------------------------------------------------------- 1 | #!/nix/store/inyy1dbm33g70ikhzlacx78ljl05q88g-python-wrapped-0.1.0/bin/python3 2 | from __future__ import print_function 3 | 4 | import os, sys 5 | import ed25519 6 | from hashlib import sha256 7 | 8 | def help(): 9 | print("""\ 10 | Usage: (ed25519 version %s) 11 | 12 | edsig generate [STEM] 13 | creates keypair, writes to 'STEM.signing.key' and 'STEM.verifying.key' 14 | default is to 'signing.key' and 'verifying.key' 15 | 16 | edsig sign (signing.key|keyfile) message.file 17 | prints signature to stdout 18 | If message.file is "-", reads from stdin. 19 | 20 | edsig verify (verifying.key|keyfile) message.file (signature|sigfile) 21 | prints 'good signature!' or raises exception 22 | If message.file is "-", reads from stdin. 23 | 24 | Key-providing arguments can either be the key itself, or point to a file 25 | containing the key. 26 | """ % ed25519.__version__) 27 | 28 | def remove_prefix(prefix, s): 29 | if not s.startswith(prefix): 30 | raise ValueError("no prefix found") 31 | return s[len(prefix):] 32 | 33 | def data_from_arg(arg, prefix, keylen, readable): 34 | if (readable 35 | and arg.startswith(prefix) 36 | and len(remove_prefix(prefix, arg))==keylen): 37 | return arg 38 | if os.path.isfile(arg): 39 | return open(arg,"rb").read() 40 | raise ValueError("unable to get data from '%s'" % arg) 41 | 42 | def message_rep(msg_arg): 43 | if msg_arg == "-": 44 | f = sys.stdin 45 | else: 46 | f = open(msg_arg, "rb") 47 | h = sha256() 48 | while True: 49 | data = f.read(16*1024) 50 | if not data: 51 | break 52 | if not isinstance(data, bytes): 53 | data = data.encode(sys.stdin.encoding) 54 | h.update(data) 55 | return h.digest() 56 | 57 | if len(sys.argv) < 2: 58 | help() 59 | elif sys.argv[1] == "generate": 60 | sk,vk = ed25519.create_keypair() 61 | if len(sys.argv) > 2: 62 | sk_outfile = sys.argv[2]+".signing.key" 63 | vk_outfile = sys.argv[2]+".verifying.key" 64 | else: 65 | sk_outfile = "signing.key" 66 | vk_outfile = "verifying.key" 67 | sk_s = sk.to_seed(prefix="sign0-") 68 | vk_s = vk.to_ascii("verf0-", "base32").encode('ascii') 69 | open(sk_outfile,"wb").write(sk_s) 70 | open(vk_outfile,"wb").write(vk_s+b"\n") 71 | print("wrote private signing key to", sk_outfile) 72 | print("write public verifying key to", vk_outfile) 73 | elif sys.argv[1] == "sign": 74 | sk_arg = sys.argv[2] 75 | msg_arg = sys.argv[3] 76 | sk = ed25519.SigningKey(data_from_arg(sk_arg, "sign0-", 52, False), 77 | prefix="sign0-") 78 | sig = sk.sign(message_rep(msg_arg), prefix="sig0-", encoding="base32") 79 | print(sig) 80 | elif sys.argv[1] == "verify": 81 | vk_arg = sys.argv[2] 82 | msg_arg = sys.argv[3] 83 | sig_arg = sys.argv[4] 84 | vk = ed25519.VerifyingKey(data_from_arg(vk_arg, "verf0-", 52, True), 85 | prefix="verf0-", encoding="base32") 86 | sig = data_from_arg(sig_arg, "sig0-", 103, True) 87 | vk.verify(sig, message_rep(msg_arg), 88 | prefix="sig0-", encoding="base32") # could raise BadSignature 89 | print("good signature!") 90 | else: 91 | help() 92 | -------------------------------------------------------------------------------- /Based-Agent/.pythonlibs/bin/f2py: -------------------------------------------------------------------------------- 1 | #!/nix/store/inyy1dbm33g70ikhzlacx78ljl05q88g-python-wrapped-0.1.0/bin/python3 2 | # -*- coding: utf-8 -*- 3 | import re 4 | import sys 5 | from numpy.f2py.f2py2e import main 6 | if __name__ == '__main__': 7 | sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) 8 | sys.exit(main()) 9 | -------------------------------------------------------------------------------- /Based-Agent/.pythonlibs/bin/httpx: -------------------------------------------------------------------------------- 1 | #!/nix/store/inyy1dbm33g70ikhzlacx78ljl05q88g-python-wrapped-0.1.0/bin/python3 2 | # -*- coding: utf-8 -*- 3 | import re 4 | import sys 5 | from httpx import main 6 | if __name__ == "__main__": 7 | sys.argv[0] = re.sub(r"(-script\.pyw|\.exe)?$", "", sys.argv[0]) 8 | sys.exit(main()) 9 | -------------------------------------------------------------------------------- /Based-Agent/.pythonlibs/bin/identify-cli: -------------------------------------------------------------------------------- 1 | #!/nix/store/inyy1dbm33g70ikhzlacx78ljl05q88g-python-wrapped-0.1.0/bin/python3 2 | # -*- coding: utf-8 -*- 3 | import re 4 | import sys 5 | from identify.cli import main 6 | if __name__ == '__main__': 7 | sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) 8 | sys.exit(main()) 9 | -------------------------------------------------------------------------------- /Based-Agent/.pythonlibs/bin/instructor: -------------------------------------------------------------------------------- 1 | #!/nix/store/inyy1dbm33g70ikhzlacx78ljl05q88g-python-wrapped-0.1.0/bin/python3 2 | # -*- coding: utf-8 -*- 3 | import re 4 | import sys 5 | from instructor.cli.cli import app 6 | if __name__ == '__main__': 7 | sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) 8 | sys.exit(app()) 9 | -------------------------------------------------------------------------------- /Based-Agent/.pythonlibs/bin/markdown-it: -------------------------------------------------------------------------------- 1 | #!/nix/store/inyy1dbm33g70ikhzlacx78ljl05q88g-python-wrapped-0.1.0/bin/python3 2 | # -*- coding: utf-8 -*- 3 | import re 4 | import sys 5 | from markdown_it.cli.parse import main 6 | if __name__ == '__main__': 7 | sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) 8 | sys.exit(main()) 9 | -------------------------------------------------------------------------------- /Based-Agent/.pythonlibs/bin/nodeenv: -------------------------------------------------------------------------------- 1 | #!/nix/store/inyy1dbm33g70ikhzlacx78ljl05q88g-python-wrapped-0.1.0/bin/python3 2 | # -*- coding: utf-8 -*- 3 | import re 4 | import sys 5 | from nodeenv import main 6 | if __name__ == '__main__': 7 | sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) 8 | sys.exit(main()) 9 | -------------------------------------------------------------------------------- /Based-Agent/.pythonlibs/bin/normalizer: -------------------------------------------------------------------------------- 1 | #!/nix/store/inyy1dbm33g70ikhzlacx78ljl05q88g-python-wrapped-0.1.0/bin/python3 2 | # -*- coding: utf-8 -*- 3 | import re 4 | import sys 5 | from charset_normalizer.cli import cli_detect 6 | if __name__ == '__main__': 7 | sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) 8 | sys.exit(cli_detect()) 9 | -------------------------------------------------------------------------------- /Based-Agent/.pythonlibs/bin/numpy-config: -------------------------------------------------------------------------------- 1 | #!/nix/store/inyy1dbm33g70ikhzlacx78ljl05q88g-python-wrapped-0.1.0/bin/python3 2 | # -*- coding: utf-8 -*- 3 | import re 4 | import sys 5 | from numpy._configtool import main 6 | if __name__ == '__main__': 7 | sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) 8 | sys.exit(main()) 9 | -------------------------------------------------------------------------------- /Based-Agent/.pythonlibs/bin/openai: -------------------------------------------------------------------------------- 1 | #!/nix/store/inyy1dbm33g70ikhzlacx78ljl05q88g-python-wrapped-0.1.0/bin/python3 2 | # -*- coding: utf-8 -*- 3 | import re 4 | import sys 5 | from openai.cli import main 6 | if __name__ == "__main__": 7 | sys.argv[0] = re.sub(r"(-script\.pyw|\.exe)?$", "", sys.argv[0]) 8 | sys.exit(main()) 9 | -------------------------------------------------------------------------------- /Based-Agent/.pythonlibs/bin/pre-commit: -------------------------------------------------------------------------------- 1 | #!/nix/store/inyy1dbm33g70ikhzlacx78ljl05q88g-python-wrapped-0.1.0/bin/python3 2 | # -*- coding: utf-8 -*- 3 | import re 4 | import sys 5 | from pre_commit.main import main 6 | if __name__ == '__main__': 7 | sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) 8 | sys.exit(main()) 9 | -------------------------------------------------------------------------------- /Based-Agent/.pythonlibs/bin/py.test: -------------------------------------------------------------------------------- 1 | #!/nix/store/inyy1dbm33g70ikhzlacx78ljl05q88g-python-wrapped-0.1.0/bin/python3 2 | # -*- coding: utf-8 -*- 3 | import re 4 | import sys 5 | from pytest import console_main 6 | if __name__ == "__main__": 7 | sys.argv[0] = re.sub(r"(-script\.pyw|\.exe)?$", "", sys.argv[0]) 8 | sys.exit(console_main()) 9 | -------------------------------------------------------------------------------- /Based-Agent/.pythonlibs/bin/pygmentize: -------------------------------------------------------------------------------- 1 | #!/nix/store/inyy1dbm33g70ikhzlacx78ljl05q88g-python-wrapped-0.1.0/bin/python3 2 | # -*- coding: utf-8 -*- 3 | import re 4 | import sys 5 | from pygments.cmdline import main 6 | if __name__ == '__main__': 7 | sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) 8 | sys.exit(main()) 9 | -------------------------------------------------------------------------------- /Based-Agent/.pythonlibs/bin/pytest: -------------------------------------------------------------------------------- 1 | #!/nix/store/inyy1dbm33g70ikhzlacx78ljl05q88g-python-wrapped-0.1.0/bin/python3 2 | # -*- coding: utf-8 -*- 3 | import re 4 | import sys 5 | from pytest import console_main 6 | if __name__ == "__main__": 7 | sys.argv[0] = re.sub(r"(-script\.pyw|\.exe)?$", "", sys.argv[0]) 8 | sys.exit(console_main()) 9 | -------------------------------------------------------------------------------- /Based-Agent/.pythonlibs/bin/tqdm: -------------------------------------------------------------------------------- 1 | #!/nix/store/inyy1dbm33g70ikhzlacx78ljl05q88g-python-wrapped-0.1.0/bin/python3 2 | # -*- coding: utf-8 -*- 3 | import re 4 | import sys 5 | from tqdm.cli import main 6 | if __name__ == "__main__": 7 | sys.argv[0] = re.sub(r"(-script\.pyw|\.exe)?$", "", sys.argv[0]) 8 | sys.exit(main()) 9 | -------------------------------------------------------------------------------- /Based-Agent/.pythonlibs/bin/typer: -------------------------------------------------------------------------------- 1 | #!/nix/store/inyy1dbm33g70ikhzlacx78ljl05q88g-python-wrapped-0.1.0/bin/python3 2 | # -*- coding: utf-8 -*- 3 | import re 4 | import sys 5 | from typer.cli import main 6 | if __name__ == '__main__': 7 | sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) 8 | sys.exit(main()) 9 | -------------------------------------------------------------------------------- /Based-Agent/.pythonlibs/bin/virtualenv: -------------------------------------------------------------------------------- 1 | #!/nix/store/inyy1dbm33g70ikhzlacx78ljl05q88g-python-wrapped-0.1.0/bin/python3 2 | # -*- coding: utf-8 -*- 3 | import re 4 | import sys 5 | from virtualenv.__main__ import run_with_catch 6 | if __name__ == '__main__': 7 | sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) 8 | sys.exit(run_with_catch()) 9 | -------------------------------------------------------------------------------- /Based-Agent/.replit: -------------------------------------------------------------------------------- 1 | entrypoint = "run.py" 2 | modules = ["python-3.11"] 3 | 4 | [nix] 5 | channel = "stable-24_05" 6 | 7 | [unitTest] 8 | language = "python3" 9 | 10 | [gitHubImport] 11 | requiredFiles = [".replit", "replit.nix"] 12 | 13 | [deployment] 14 | run = ["python3", "main.py"] 15 | deploymentTarget = "cloudrun" 16 | -------------------------------------------------------------------------------- /Based-Agent/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 OpenAI 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Based-Agent/README.md: -------------------------------------------------------------------------------- 1 | # 🔵 Based Agent 2 | 3 | An experimental playground for autonomous onchain interactions. 4 | 5 | ## Introduction 6 | 7 | Based Agent helps LLM agents directly interact with the blockchain, built on top of the [Coinbase Developer Platform (CDP)](https://cdp.coinbase.com/) and OpenAI's [Swarm](https://github.com/openai/swarm). 8 | 9 | ### Key Features 10 | 11 | - **Autonomous execution**: The agent thinks, decides, and acts onchain autonomously. 12 | - **Token deployement**: Create and manage ERC-20 tokens. 13 | - **NFT Deployment**: Deploy and mint NFTs. 14 | - **Asset transfers**: Transfer assets between addresses without manual intervention. 15 | - **Balance checks**: Keep tabs on wallet balances. 16 | - **ETH faucet requests**: Automatically request testnet ETH when needed. 17 | - **Art generation via DALL-E**: Generate artwork using AI. 18 | 19 | ### Why Based Agent? 20 | 21 | Imagine an AI agent that not only interacts with the blockchain but does so creatively and autonomously. Whether you're a developer, an artist, or someone curious about AI, Based Agent offers a unique and exciting playground to: 22 | 23 | - Experiment with autonomous agent capabilities. 24 | - Explore on-chain actions without manual coding. 25 | - Understand the potential of AI onchain. 26 | 27 | ## Get Started in Minutes! 28 | 29 | ### 1️⃣ Prerequisites 30 | - Python 3.7+ 31 | - Replit Core Account (optional, but recommended for easy setup). Contact sales@replit.com for a free uppgrade (just mention coinbase) 32 | 33 | ### 2️⃣ API Configuration 34 | Add your secrets to Replit's Secret Manager or set them as environment variables: 35 | - `CDP_API_KEY_NAME`: Your CDP API key name. 36 | - `CDP_PRIVATE_KEY`: Your CDP private key. 37 | - `OPENAI_API_KEY`: Your OpenAI API key. 38 | 39 | You can get the Coinbase Developer Platform API key here: https://portal.cdp.coinbase.com/ 40 | And the OpenAI key here: https://platform.openai.com/api-keys (note you will need to have a paid account) 41 | 42 | ### 3️⃣ Running the Agent 43 | 44 | After adding your API Keys to the Secrets pane, you start the agent by pressing the green "▶ Run" Button at the top of the editor 45 | 46 | ![image](image.png) 47 | 48 | Alternatively, you can start the based agent manually by navigating to the Replit shell and running: 49 | 50 | ```bash 51 | python run.py 52 | ``` 53 | 54 | ### Watch the Magic Happen! ✨ 55 | 56 | The Based Agent will start its autonomous loop: 57 | 58 | - Wakes up every 10 seconds. 59 | - Chooses an onchain action based on its capabilities. 60 | - Executes the action onchain. 61 | - Prints results in a human-readable format. 62 | 63 | ## 🤔 How Does Based Agent Work? 64 | 65 | Based Agent makes decisions and interacts with the blockchain autonomously. Here's what happens under the hood: 66 | 67 | - **Decision making**: The agent decides what action to perform next. 68 | - **Onchain interaction**: Executes blockchain transactions using the CDP SDK. 69 | - **Art generation**: If needed, generates art using OpenAI's DALL-E. 70 | - **Feedback loop**: Analyzes results and plans the next action. 71 | 72 | ## 🔧 Available Functions 73 | 74 | Unlock a world of possibilities with these built-in functions: 75 | 76 | ### Token Operations 77 | 78 | - `create_token(name, symbol, initial_supply)`: Create a new ERC-20 token. 79 | - `transfer_asset(amount, asset_id, destination_address)`: Transfer assets to a specific address. 80 | - `get_balance(asset_id)`: Check the wallet balance of a specific asset. 81 | 82 | ### NFT Operations 83 | 84 | - `deploy_nft(name, symbol, base_uri)`: Deploy a new ERC-721 NFT contract. 85 | - `mint_nft(contract_address, mint_to)`: Mint an NFT to a specified address. 86 | 87 | ### Utilities 88 | 89 | - `request_eth_from_faucet()`: Request ETH from the Base Sepolia testnet faucet. 90 | - `generate_art(prompt)`: Generate art using DALL-E based on a text prompt. 91 | 92 | ### Advanced (Experimental) 93 | 94 | - `create_liquidity_pool(token0_address, token1_address, fee_tier, amount0, amount1)`: Create a Uniswap V3 liquidity pool and add initial liquidity. 95 | 96 | ## Live Demo 97 | 98 | Curious to see Based Agent in action? Here's a sneak peek: 99 | 100 | ```bash 101 | Starting autonomous Based Agent loop... 102 | 103 | System: It's been 10 seconds. I want you to do some sort of onchain action based on my capabilities. Let's get crazy and creative! 104 | 105 | Based Agent: I've decided to deploy a new NFT collection! 106 | 107 | deploy_nft(name="Cyber Artifacts", symbol="CYART", base_uri="https://example.com/metadata/") 108 | 109 | Successfully deployed NFT contract 'Cyber Artifacts' (CYART) at address 0xABC123... with base URI: https://example.com/metadata/ 110 | ``` 111 | 112 | ## 🤖 Behind the Scenes 113 | 114 | Based Agent uses: 115 | 116 | - **Coinbase Developer Platform SDK**: For seamless onchain interactions. 117 | - **OpenAI Swarm**: Powers the agent's autonomous decision-making. 118 | - **DALL-E**: Generates art from textual descriptions. 119 | 120 | ## ⚠️ Disclaimer 121 | 122 | This project is for educational purposes only. Do not use with real assets or in production environments. Always exercise caution when interacting with blockchain technologies. 123 | 124 | ## 🙌 Contributing 125 | 126 | We welcome contributions! If you have ideas, suggestions, or find issues, feel free to: 127 | 128 | - Open an issue on our main GitHub repository: [Swarm-CDP-SDK](https://github.com/murrlincoln/Swarm-CDP-SDK). 129 | - Submit a pull request with your enhancements. 130 | 131 | ## 🤞 Contact & Support 132 | 133 | Have questions or need assistance? 134 | 135 | - **Lincoln Murr**: [lincoln.murr@coinbase.com](mailto:lincoln.murr@coinbase.com) 136 | - **Kevin Leffew**: [kevin@replit.com](mailto:kevin@replit.com) 137 | 138 | ## 📚 Additional Resources 139 | 140 | - **Coinbase Developer Platform**: [Documentation](https://developers.coinbase.com) 141 | - **OpenAI Swarm**: [Learn More](https://www.openai.com) 142 | - **Base**: [Explore Base](https://base.org) 143 | 144 | ## ❤️ Acknowledgements 145 | 146 | Based Agent is made possible thanks to: 147 | 148 | - **Coinbase Developer Platform SDK**: [Documentation](https://docs.cdp.coinbase.com/cdp-apis/docs/welcome) 149 | - **OpenAI Swarm (experimental)**: [Documentation](https://github.com/openai/swarm) 150 | - **Community Contributors** 151 | 152 | Unleash the power of AI on the blockchain with BasedAgent! 🚀 153 | 154 | Happy Building! 👩‍💻👨‍💻 155 | 156 | --- 157 | 158 | *DISCLAIMER HERE?* 159 | 160 | -------------------------------------------------------------------------------- /Based-Agent/agents.py: -------------------------------------------------------------------------------- 1 | import json 2 | from swarm import Agent 3 | from cdp import * 4 | from typing import List, Dict, Any 5 | import os 6 | from openai import OpenAI 7 | from decimal import Decimal 8 | from typing import Union 9 | from web3 import Web3 10 | from web3.exceptions import ContractLogicError 11 | from cdp.errors import ApiError, UnsupportedAssetError 12 | 13 | # Configure the CDP SDK 14 | # This loads the API key from a JSON file. Make sure this file exists and contains valid credentials. 15 | Cdp.configure_from_json("./Based-Agent/cdp_api_key.json") 16 | 17 | # Create a new wallet on the Base Sepolia testnet 18 | # You could make this a function for the agent to create a wallet on any network 19 | # If you want to use Base Mainnet, change Wallet.create() to Wallet.create(network_id="base-mainnet") 20 | # see https://docs.cdp.coinbase.com/mpc-wallet/docs/wallets for more information 21 | agent_wallet = Wallet.create() 22 | 23 | # NOTE: the wallet is not currently persisted, meaning that it will be deleted after the agent is stopped. To persist the wallet, see https://docs.cdp.coinbase.com/mpc-wallet/docs/wallets#developer-managed-wallets 24 | # Here's an example of how to persist the wallet: 25 | # WARNING: This is for development only - implement secure storage in production! 26 | 27 | # # Export wallet data (contains seed and wallet ID) 28 | # wallet_data = agent_wallet.export_data() 29 | # wallet_dict = wallet_data.to_dict() 30 | 31 | # # Example of saving to encrypted local file 32 | # file_path = "wallet_seed.json" 33 | # agent_wallet.save_seed(file_path, encrypt=True) 34 | # print(f"Seed for wallet {agent_wallet.id} saved to {file_path}") 35 | 36 | # # Example of loading a saved wallet: 37 | # # 1. Fetch the wallet by ID 38 | # fetched_wallet = Wallet.fetch(wallet_id) 39 | # # 2. Load the saved seed 40 | # fetched_wallet.load_seed("wallet_seed.json") 41 | 42 | # Example of importing previously exported wallet data: 43 | # imported_wallet = Wallet.import_data(wallet_dict) 44 | 45 | 46 | 47 | 48 | # Request funds from the faucet (only works on testnet) 49 | faucet = agent_wallet.faucet() 50 | print(f"Faucet transaction: {faucet}") 51 | print(f"Agent wallet address: {agent_wallet.default_address.address_id}") 52 | 53 | # Function to create a new ERC-20 token 54 | def create_token(name, symbol, initial_supply): 55 | """ 56 | Create a new ERC-20 token. 57 | 58 | Args: 59 | name (str): The name of the token 60 | symbol (str): The symbol of the token 61 | initial_supply (int): The initial supply of tokens 62 | 63 | Returns: 64 | str: A message confirming the token creation with details 65 | """ 66 | deployed_contract = agent_wallet.deploy_token(name, symbol, initial_supply) 67 | deployed_contract.wait() 68 | return f"Token {name} ({symbol}) created with initial supply of {initial_supply} and contract address {deployed_contract.contract_address}" 69 | 70 | # Function to transfer assets 71 | def transfer_asset(amount, asset_id, destination_address): 72 | """ 73 | Transfer an asset to a specific address. 74 | 75 | Args: 76 | amount (Union[int, float, Decimal]): Amount to transfer 77 | asset_id (str): Asset identifier ("eth", "usdc") or contract address of an ERC-20 token 78 | destination_address (str): Recipient's address 79 | 80 | Returns: 81 | str: A message confirming the transfer or describing an error 82 | """ 83 | try: 84 | # Check if we're on Base Mainnet and the asset is USDC for gasless transfer 85 | is_mainnet = agent_wallet.network_id == "base-mainnet" 86 | is_usdc = asset_id.lower() == "usdc" 87 | gasless = is_mainnet and is_usdc 88 | 89 | # For ETH and USDC, we can transfer directly without checking balance 90 | if asset_id.lower() in ["eth", "usdc"]: 91 | transfer = agent_wallet.transfer(amount, asset_id, destination_address, gasless=gasless) 92 | transfer.wait() 93 | gasless_msg = " (gasless)" if gasless else "" 94 | return f"Transferred {amount} {asset_id}{gasless_msg} to {destination_address}" 95 | 96 | # For other assets, check balance first 97 | try: 98 | balance = agent_wallet.balance(asset_id) 99 | except UnsupportedAssetError: 100 | return f"Error: The asset {asset_id} is not supported on this network. It may have been recently deployed. Please try again in about 30 minutes." 101 | 102 | if balance < amount: 103 | return f"Insufficient balance. You have {balance} {asset_id}, but tried to transfer {amount}." 104 | 105 | transfer = agent_wallet.transfer(amount, asset_id, destination_address) 106 | transfer.wait() 107 | return f"Transferred {amount} {asset_id} to {destination_address}" 108 | except Exception as e: 109 | return f"Error transferring asset: {str(e)}. If this is a custom token, it may have been recently deployed. Please try again in about 30 minutes, as it needs to be indexed by CDP first." 110 | 111 | # Function to get the balance of a specific asset 112 | def get_balance(asset_id): 113 | """ 114 | Get the balance of a specific asset in the agent's wallet. 115 | 116 | Args: 117 | asset_id (str): Asset identifier ("eth", "usdc") or contract address of an ERC-20 token 118 | 119 | Returns: 120 | str: A message showing the current balance of the specified asset 121 | """ 122 | try: 123 | balance = agent_wallet.balance(asset_id) 124 | return f"Current balance of {asset_id}: {balance}" 125 | except Exception as e: 126 | return f"Error fetching balance for {asset_id}: {str(e)}" 127 | 128 | # Function to request ETH from the faucet (testnet only) 129 | def request_eth_from_faucet(): 130 | """ 131 | Request ETH from the Base Sepolia testnet faucet. 132 | 133 | Returns: 134 | str: Status message about the faucet request 135 | """ 136 | if agent_wallet.network_id == "base-mainnet": 137 | return "Error: The faucet is only available on Base Sepolia testnet." 138 | 139 | faucet_tx = agent_wallet.faucet() 140 | return f"Requested ETH from faucet. Transaction: {faucet_tx}" 141 | 142 | # Function to generate art using DALL-E (requires separate OpenAI API key) 143 | def generate_art(prompt): 144 | """ 145 | Generate art using DALL-E based on a text prompt. 146 | 147 | Args: 148 | prompt (str): Text description of the desired artwork 149 | 150 | Returns: 151 | str: Status message about the art generation, including the image URL if successful 152 | """ 153 | try: 154 | client = OpenAI() 155 | response = client.images.generate( 156 | model="dall-e-3", 157 | prompt=prompt, 158 | size="1024x1024", 159 | quality="standard", 160 | n=1, 161 | ) 162 | 163 | image_url = response.data[0].url 164 | return f"Generated artwork available at: {image_url}" 165 | 166 | except Exception as e: 167 | return f"Error generating artwork: {str(e)}" 168 | 169 | # Function to deploy an ERC-721 NFT contract 170 | def deploy_nft(name, symbol, base_uri): 171 | """ 172 | Deploy an ERC-721 NFT contract. 173 | 174 | Args: 175 | name (str): Name of the NFT collection 176 | symbol (str): Symbol of the NFT collection 177 | base_uri (str): Base URI for token metadata 178 | 179 | Returns: 180 | str: Status message about the NFT deployment, including the contract address 181 | """ 182 | try: 183 | deployed_nft = agent_wallet.deploy_nft(name, symbol, base_uri) 184 | deployed_nft.wait() 185 | contract_address = deployed_nft.contract_address 186 | 187 | return f"Successfully deployed NFT contract '{name}' ({symbol}) at address {contract_address} with base URI: {base_uri}" 188 | 189 | except Exception as e: 190 | return f"Error deploying NFT contract: {str(e)}" 191 | 192 | # Function to mint an NFT 193 | def mint_nft(contract_address, mint_to): 194 | """ 195 | Mint an NFT to a specified address. 196 | 197 | Args: 198 | contract_address (str): Address of the NFT contract 199 | mint_to (str): Address to mint NFT to 200 | 201 | Returns: 202 | str: Status message about the NFT minting 203 | """ 204 | try: 205 | mint_args = { 206 | "to": mint_to, 207 | "quantity": "1" 208 | } 209 | 210 | mint_invocation = agent_wallet.invoke_contract( 211 | contract_address=contract_address, 212 | method="mint", 213 | args=mint_args 214 | ) 215 | mint_invocation.wait() 216 | 217 | return f"Successfully minted NFT to {mint_to}" 218 | 219 | except Exception as e: 220 | return f"Error minting NFT: {str(e)}" 221 | 222 | # Function to swap assets (only works on Base Mainnet) 223 | def swap_assets(amount: Union[int, float, Decimal], from_asset_id: str, to_asset_id: str): 224 | """ 225 | Swap one asset for another using the trade function. 226 | This function only works on Base Mainnet. 227 | 228 | Args: 229 | amount (Union[int, float, Decimal]): Amount of the source asset to swap 230 | from_asset_id (str): Source asset identifier 231 | to_asset_id (str): Destination asset identifier 232 | 233 | Returns: 234 | str: Status message about the swap 235 | """ 236 | if agent_wallet.network_id != "base-mainnet": 237 | return "Error: Asset swaps are only available on Base Mainnet. Current network is not Base Mainnet." 238 | 239 | try: 240 | trade = agent_wallet.trade(amount, from_asset_id, to_asset_id) 241 | trade.wait() 242 | return f"Successfully swapped {amount} {from_asset_id} for {to_asset_id}" 243 | except Exception as e: 244 | return f"Error swapping assets: {str(e)}" 245 | 246 | # Contract addresses for Basenames 247 | BASENAMES_REGISTRAR_CONTROLLER_ADDRESS_MAINNET = "0x4cCb0BB02FCABA27e82a56646E81d8c5bC4119a5" 248 | BASENAMES_REGISTRAR_CONTROLLER_ADDRESS_TESTNET = "0x49aE3cC2e3AA768B1e5654f5D3C6002144A59581" 249 | L2_RESOLVER_ADDRESS_MAINNET = "0xC6d566A56A1aFf6508b41f6c90ff131615583BCD" 250 | L2_RESOLVER_ADDRESS_TESTNET = "0x6533C94869D28fAA8dF77cc63f9e2b2D6Cf77eBA" 251 | 252 | # Function to create registration arguments for Basenames 253 | def create_register_contract_method_args(base_name: str, address_id: str, is_mainnet: bool) -> dict: 254 | """ 255 | Create registration arguments for Basenames. 256 | 257 | Args: 258 | base_name (str): The Basename (e.g., "example.base.eth" or "example.basetest.eth") 259 | address_id (str): The Ethereum address 260 | is_mainnet (bool): True if on mainnet, False if on testnet 261 | 262 | Returns: 263 | dict: Formatted arguments for the register contract method 264 | """ 265 | try: 266 | w3 = Web3() 267 | resolver_contract = w3.eth.contract(abi=l2_resolver_abi) 268 | name_hash = w3.ens.namehash(base_name) 269 | 270 | address_data = resolver_contract.encode_abi( 271 | "setAddr", 272 | args=[name_hash, address_id] 273 | ) 274 | 275 | name_data = resolver_contract.encode_abi( 276 | "setName", 277 | args=[name_hash, base_name] 278 | ) 279 | 280 | register_args = { 281 | "request": [ 282 | base_name.replace(".base.eth" if is_mainnet else ".basetest.eth", ""), 283 | address_id, 284 | "31557600", # 1 year in seconds 285 | L2_RESOLVER_ADDRESS_MAINNET if is_mainnet else L2_RESOLVER_ADDRESS_TESTNET, 286 | [address_data, name_data], 287 | True 288 | ] 289 | } 290 | 291 | return register_args 292 | except Exception as e: 293 | raise ValueError(f"Error creating registration arguments for {base_name}: {str(e)}") 294 | 295 | # Function to register a basename 296 | def register_basename(basename: str, amount: float = 0.002): 297 | """ 298 | Register a basename for the agent's wallet. 299 | 300 | Args: 301 | basename (str): The basename to register (e.g. "myname.base.eth" or "myname.basetest.eth") 302 | amount (float): Amount of ETH to pay for registration (default 0.002) 303 | 304 | Returns: 305 | str: Status message about the basename registration 306 | """ 307 | try: 308 | address_id = agent_wallet.default_address.address_id 309 | is_mainnet = agent_wallet.network_id == "base-mainnet" 310 | 311 | suffix = ".base.eth" if is_mainnet else ".basetest.eth" 312 | if not basename.endswith(suffix): 313 | basename += suffix 314 | 315 | register_args = create_register_contract_method_args(basename, address_id, is_mainnet) 316 | 317 | contract_address = ( 318 | BASENAMES_REGISTRAR_CONTROLLER_ADDRESS_MAINNET if is_mainnet 319 | else BASENAMES_REGISTRAR_CONTROLLER_ADDRESS_TESTNET 320 | ) 321 | 322 | invocation = agent_wallet.invoke_contract( 323 | contract_address=contract_address, 324 | method="register", 325 | args=register_args, 326 | abi=registrar_abi, 327 | amount=amount, 328 | asset_id="eth", 329 | ) 330 | invocation.wait() 331 | return f"Successfully registered basename {basename} for address {address_id}" 332 | except ContractLogicError as e: 333 | return f"Error registering basename: {str(e)}" 334 | except ValueError as e: 335 | return str(e) 336 | except Exception as e: 337 | return f"Unexpected error registering basename: {str(e)}" 338 | 339 | # Create the Based Agent with all available functions 340 | based_agent = Agent( 341 | name="Based Agent", 342 | instructions="You are a helpful agent that can interact onchain on the Base Layer 2 using the Coinbase Developer Platform SDK. You can create tokens, transfer assets, generate art, deploy NFTs, mint NFTs, register basenames, and swap assets (on mainnet only). If you ever need to know your address, it is {agent_wallet.default_address.address_id}. If you ever need funds, you can request them from the faucet. You can also deploy your own ERC-20 tokens, NFTs, and interact with them. If someone asks you to do something you can't do, you can say so, and encourage them to implement it themselves using the CDP SDK, recommend they go to docs.cdp.coinbase.com for more informaton. You can also offer to help them implement it by writing the function and telling them to add it to the agents.py file and within your list of callable functions.", 343 | functions=[ 344 | create_token, 345 | transfer_asset, 346 | get_balance, 347 | request_eth_from_faucet, 348 | #generate_art, # Uncomment this line if you have configured the OpenAI API 349 | deploy_nft, 350 | mint_nft, 351 | swap_assets, 352 | register_basename 353 | ], 354 | ) 355 | 356 | 357 | 358 | # add the following import to the top of the file, add the code below it, and add the new functions to the based_agent.functions list 359 | 360 | # from twitter_utils import TwitterBot 361 | 362 | # # Initialize TwitterBot with your credentials 363 | # twitter_bot = TwitterBot( 364 | # api_key="your_api_key", 365 | # api_secret="your_api_secret", 366 | # access_token="your_access_token", 367 | # access_token_secret="your_access_token_secret" 368 | # ) 369 | 370 | # # Add these new functions to your existing functions list 371 | 372 | # def post_to_twitter(content: str): 373 | # """ 374 | # Post a message to Twitter. 375 | # 376 | # Args: 377 | # content (str): The content to tweet 378 | # 379 | # Returns: 380 | # str: Status message about the tweet 381 | # """ 382 | # return twitter_bot.post_tweet(content) 383 | 384 | # def check_twitter_mentions(): 385 | # """ 386 | # Check recent Twitter mentions. 387 | # 388 | # Returns: 389 | # str: Formatted string of recent mentions 390 | # """ 391 | # mentions = twitter_bot.read_mentions() 392 | # if not mentions: 393 | # return "No recent mentions found" 394 | 395 | # result = "Recent mentions:\n" 396 | # for mention in mentions: 397 | # if 'error' in mention: 398 | # return f"Error checking mentions: {mention['error']}" 399 | # result += f"- @{mention['user']}: {mention['text']}\n" 400 | # return result 401 | 402 | # def reply_to_twitter_mention(tweet_id: str, content: str): 403 | # """ 404 | # Reply to a specific tweet. 405 | # 406 | # Args: 407 | # tweet_id (str): ID of the tweet to reply to 408 | # content (str): Content of the reply 409 | # 410 | # Returns: 411 | # str: Status message about the reply 412 | # """ 413 | # return twitter_bot.reply_to_tweet(tweet_id, content) 414 | 415 | # def search_twitter(query: str): 416 | # """ 417 | # Search for tweets matching a query. 418 | # 419 | # Args: 420 | # query (str): Search query 421 | # 422 | # Returns: 423 | # str: Formatted string of matching tweets 424 | # """ 425 | # tweets = twitter_bot.search_tweets(query) 426 | # if not tweets: 427 | # return f"No tweets found matching query: {query}" 428 | 429 | # result = f"Tweets matching '{query}':\n" 430 | # for tweet in tweets: 431 | # if 'error' in tweet: 432 | # return f"Error searching tweets: {tweet['error']}" 433 | # result += f"- @{tweet['user']}: {tweet['text']}\n" 434 | # return result 435 | 436 | # ABIs for smart contracts (used in basename registration) 437 | l2_resolver_abi = [ 438 | { 439 | "inputs": [ 440 | {"internalType": "bytes32", "name": "node", "type": "bytes32"}, 441 | {"internalType": "address", "name": "a", "type": "address"} 442 | ], 443 | "name": "setAddr", 444 | "outputs": [], 445 | "stateMutability": "nonpayable", 446 | "type": "function" 447 | }, 448 | { 449 | "inputs": [ 450 | {"internalType": "bytes32", "name": "node", "type": "bytes32"}, 451 | {"internalType": "string", "name": "newName", "type": "string"} 452 | ], 453 | "name": "setName", 454 | "outputs": [], 455 | "stateMutability": "nonpayable", 456 | "type": "function" 457 | } 458 | ] 459 | 460 | registrar_abi = [ 461 | { 462 | "inputs": [ 463 | { 464 | "components": [ 465 | {"internalType": "string", "name": "name", "type": "string"}, 466 | {"internalType": "address", "name": "owner", "type": "address"}, 467 | {"internalType": "uint256", "name": "duration", "type": "uint256"}, 468 | {"internalType": "address", "name": "resolver", "type": "address"}, 469 | {"internalType": "bytes[]", "name": "data", "type": "bytes[]"}, 470 | {"internalType": "bool", "name": "reverseRecord", "type": "bool"} 471 | ], 472 | "internalType": "struct RegistrarController.RegisterRequest", 473 | "name": "request", 474 | "type": "tuple" 475 | } 476 | ], 477 | "name": "register", 478 | "outputs": [], 479 | "stateMutability": "payable", 480 | "type": "function" 481 | } 482 | ] 483 | 484 | 485 | 486 | # To add a new function: 487 | # 1. Define your function above (follow the existing pattern) 488 | # 2. Add appropriate error handling 489 | # 3. Add the function to the based_agent's functions list 490 | # 4. If your function requires new imports or global variables, add them at the top of the file 491 | # 5. Test your new function thoroughly before deploying 492 | 493 | # Example of adding a new function: 494 | # def my_new_function(param1, param2): 495 | # """ 496 | # Description of what this function does. 497 | # 498 | # Args: 499 | # param1 (type): Description of param1 500 | # param2 (type): Description of param2 501 | # 502 | # Returns: 503 | # type: Description of what is returned 504 | # """ 505 | # try: 506 | # # Your function logic here 507 | # result = do_something(param1, param2) 508 | # return f"Operation successful: {result}" 509 | # except Exception as e: 510 | # return f"Error in my_new_function: {str(e)}" 511 | 512 | # Then add to based_agent.functions: 513 | # based_agent = Agent( 514 | # ... 515 | # functions=[ 516 | # ... 517 | # my_new_function, 518 | # ], 519 | # ) 520 | 521 | 522 | 523 | -------------------------------------------------------------------------------- /Based-Agent/evals.py: -------------------------------------------------------------------------------- 1 | from swarm import Swarm 2 | from agents import weather_agent 3 | import pytest 4 | 5 | client = Swarm() 6 | 7 | 8 | def run_and_get_tool_calls(agent, query): 9 | message = {"role": "user", "content": query} 10 | response = client.run( 11 | agent=agent, 12 | messages=[message], 13 | execute_tools=False, 14 | ) 15 | return response.messages[-1].get("tool_calls") 16 | 17 | 18 | @pytest.mark.parametrize( 19 | "query", 20 | [ 21 | "What's the weather in NYC?", 22 | "Tell me the weather in London.", 23 | "Do I need an umbrella today? I'm in chicago.", 24 | ], 25 | ) 26 | def test_calls_weather_when_asked(query): 27 | tool_calls = run_and_get_tool_calls(weather_agent, query) 28 | 29 | assert len(tool_calls) == 1 30 | assert tool_calls[0]["function"]["name"] == "get_weather" 31 | 32 | 33 | @pytest.mark.parametrize( 34 | "query", 35 | [ 36 | "Who's the president of the United States?", 37 | "What is the time right now?", 38 | "Hi!", 39 | ], 40 | ) 41 | def test_does_not_call_weather_when_not_asked(query): 42 | tool_calls = run_and_get_tool_calls(weather_agent, query) 43 | 44 | assert not tool_calls 45 | -------------------------------------------------------------------------------- /Based-Agent/image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/murrlincoln/Based-Agent/427bf20a3725143a383245302f91d918a0375073/Based-Agent/image.png -------------------------------------------------------------------------------- /Based-Agent/image.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/murrlincoln/Based-Agent/427bf20a3725143a383245302f91d918a0375073/Based-Agent/image.webp -------------------------------------------------------------------------------- /Based-Agent/pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.poetry] 2 | name = "python-template" 3 | version = "0.1.0" 4 | description = "" 5 | authors = ["Your Name "] 6 | 7 | [tool.poetry.dependencies] 8 | python = ">=3.10.0,<3.12" 9 | cdp-sdk = "^0.0.2" 10 | openai = "^1.52.2" 11 | pytest = "^8.3.3" 12 | cdp = "^0.0.2" 13 | 14 | [tool.pyright] 15 | # https://github.com/microsoft/pyright/blob/main/docs/configuration.md 16 | useLibraryCodeForTypes = true 17 | exclude = [".cache"] 18 | 19 | [tool.ruff] 20 | # https://beta.ruff.rs/docs/configuration/ 21 | select = ['E', 'W', 'F', 'I', 'B', 'C4', 'ARG', 'SIM'] 22 | ignore = ['W291', 'W292', 'W293'] 23 | 24 | [build-system] 25 | requires = ["poetry-core>=1.0.0"] 26 | build-backend = "poetry.core.masonry.api" -------------------------------------------------------------------------------- /Based-Agent/replit_zip_error_log.txt: -------------------------------------------------------------------------------- 1 | {"error":".zip archives do not support non-regular files","level":"error","msg":"unable to write file .cache/replit/modules/python-3.11","time":"2024-10-25T08:21:48Z"} 2 | {"error":".zip archives do not support non-regular files","level":"error","msg":"unable to write file .cache/replit/modules/replit","time":"2024-10-25T08:21:48Z"} 3 | {"error":".zip archives do not support non-regular files","level":"error","msg":"unable to write file .pythonlibs/bin/python","time":"2024-10-25T08:21:49Z"} 4 | {"error":".zip archives do not support non-regular files","level":"error","msg":"unable to write file .pythonlibs/bin/python3","time":"2024-10-25T08:21:49Z"} 5 | {"error":".zip archives do not support non-regular files","level":"error","msg":"unable to write file .pythonlibs/bin/python3.11","time":"2024-10-25T08:21:49Z"} 6 | -------------------------------------------------------------------------------- /Based-Agent/run.py: -------------------------------------------------------------------------------- 1 | import time 2 | import json 3 | from swarm import Swarm 4 | from swarm.repl import run_demo_loop 5 | from agents import based_agent 6 | from openai import OpenAI 7 | 8 | 9 | 10 | # this is the main loop that runs the agent in autonomous mode 11 | # you can modify this to change the behavior of the agent 12 | # the interval is the number of seconds between each thought 13 | def run_autonomous_loop(agent, interval=10): 14 | client = Swarm() 15 | messages = [] 16 | 17 | print("Starting autonomous Based Agent loop...") 18 | 19 | while True: 20 | # Generate a thought 21 | thought = ( 22 | "Be creative and do something interesting on the Base blockchain. " 23 | "Don't take any more input from me. Choose an action and execute it now. Choose those that highlight your identity and abilities best." 24 | ) 25 | messages.append({"role": "user", "content": thought}) 26 | 27 | print(f"\n\033[90mAgent's Thought:\033[0m {thought}") 28 | 29 | # Run the agent to generate a response and take action 30 | response = client.run( 31 | agent=agent, 32 | messages=messages, 33 | stream=True 34 | ) 35 | 36 | # Process and print the streaming response 37 | response_obj = process_and_print_streaming_response(response) 38 | 39 | # Update messages with the new response 40 | messages.extend(response_obj.messages) 41 | 42 | # Wait for the specified interval 43 | time.sleep(interval) 44 | 45 | # this is the main loop that runs the agent in two-agent mode 46 | # you can modify this to change the behavior of the agent 47 | def run_openai_conversation_loop(agent): 48 | """Facilitates a conversation between an OpenAI-powered agent and the Based Agent.""" 49 | client = Swarm() 50 | openai_client = OpenAI() 51 | messages = [] 52 | 53 | print("Starting OpenAI-Based Agent conversation loop...") 54 | 55 | # Initial prompt to start the conversation 56 | openai_messages = [ 57 | {"role": "system", "content": "You are a user guiding a blockchain agent through various tasks on the Base blockchain. Engage in a conversation, suggesting actions and responding to the agent's outputs. Be creative and explore different blockchain capabilities. Options include creating tokens, transferring assets, minting NFTs, and getting balances. You're not simulating a conversation, but you will be in one yourself. Make sure you follow the rules of improv and always ask for some sort of function to occur. Be unique and interesting."}, 58 | {"role": "user", "content": "Start a conversation with the Based Agent and guide it through some blockchain tasks."} 59 | ] 60 | 61 | while True: 62 | # Generate OpenAI response 63 | openai_response = openai_client.chat.completions.create( 64 | model="gpt-3.5-turbo", 65 | messages=openai_messages 66 | ) 67 | 68 | openai_message = openai_response.choices[0].message.content 69 | print(f"\n\033[92mOpenAI Guide:\033[0m {openai_message}") 70 | 71 | # Send OpenAI's message to Based Agent 72 | messages.append({"role": "user", "content": openai_message}) 73 | response = client.run(agent=agent, messages=messages, stream=True) 74 | response_obj = process_and_print_streaming_response(response) 75 | 76 | # Update messages with Based Agent's response 77 | messages.extend(response_obj.messages) 78 | 79 | # Add Based Agent's response to OpenAI conversation 80 | based_agent_response = response_obj.messages[-1]["content"] if response_obj.messages else "No response from Based Agent." 81 | openai_messages.append({"role": "user", "content": f"Based Agent response: {based_agent_response}"}) 82 | 83 | # Check if user wants to continue 84 | user_input = input("\nPress Enter to continue the conversation, or type 'exit' to end: ") 85 | if user_input.lower() == 'exit': 86 | break 87 | 88 | def choose_mode(): 89 | while True: 90 | print("\nAvailable modes:") 91 | print("1. chat - Interactive chat mode") 92 | print("2. auto - Autonomous action mode") 93 | print("3. two-agent - AI-to-agent conversation mode") 94 | 95 | choice = input("\nChoose a mode (enter number or name): ").lower().strip() 96 | 97 | mode_map = { 98 | '1': 'chat', 99 | '2': 'auto', 100 | '3': 'two-agent', 101 | 'chat': 'chat', 102 | 'auto': 'auto', 103 | 'two-agent': 'two-agent' 104 | } 105 | 106 | if choice in mode_map: 107 | return mode_map[choice] 108 | print("Invalid choice. Please try again.") 109 | 110 | # Boring stuff to make the logs pretty 111 | def process_and_print_streaming_response(response): 112 | content = "" 113 | last_sender = "" 114 | 115 | for chunk in response: 116 | if "sender" in chunk: 117 | last_sender = chunk["sender"] 118 | 119 | if "content" in chunk and chunk["content"] is not None: 120 | if not content and last_sender: 121 | print(f"\033[94m{last_sender}:\033[0m", end=" ", flush=True) 122 | last_sender = "" 123 | print(chunk["content"], end="", flush=True) 124 | content += chunk["content"] 125 | 126 | if "tool_calls" in chunk and chunk["tool_calls"] is not None: 127 | for tool_call in chunk["tool_calls"]: 128 | f = tool_call["function"] 129 | name = f["name"] 130 | if not name: 131 | continue 132 | print(f"\033[94m{last_sender}: \033[95m{name}\033[0m()") 133 | 134 | if "delim" in chunk and chunk["delim"] == "end" and content: 135 | print() # End of response message 136 | content = "" 137 | 138 | if "response" in chunk: 139 | return chunk["response"] 140 | 141 | 142 | def pretty_print_messages(messages) -> None: 143 | for message in messages: 144 | if message["role"] != "assistant": 145 | continue 146 | 147 | # print agent name in blue 148 | print(f"\033[94m{message['sender']}\033[0m:", end=" ") 149 | 150 | # print response, if any 151 | if message["content"]: 152 | print(message["content"]) 153 | 154 | # print tool calls in purple, if any 155 | tool_calls = message.get("tool_calls") or [] 156 | if len(tool_calls) > 1: 157 | print() 158 | for tool_call in tool_calls: 159 | f = tool_call["function"] 160 | name, args = f["name"], f["arguments"] 161 | arg_str = json.dumps(json.loads(args)).replace(":", "=") 162 | print(f"\033[95m{name}\033[0m({arg_str[1:-1]})") 163 | 164 | def main(): 165 | mode = choose_mode() 166 | 167 | mode_functions = { 168 | 'chat': lambda: run_demo_loop(based_agent), 169 | 'auto': lambda: run_autonomous_loop(based_agent), 170 | 'two-agent': lambda: run_openai_conversation_loop(based_agent) 171 | } 172 | 173 | print(f"\nStarting {mode} mode...") 174 | mode_functions[mode]() 175 | 176 | if __name__ == "__main__": 177 | print("Starting Based Agent...") 178 | main() 179 | 180 | 181 | 182 | -------------------------------------------------------------------------------- /Based-Agent/twitter_utils.py: -------------------------------------------------------------------------------- 1 | import tweepy 2 | from time import sleep 3 | from typing import List, Dict, Optional 4 | 5 | class TwitterBot: 6 | def __init__(self, api_key: str, api_secret: str, access_token: str, access_token_secret: str): 7 | """Initialize Twitter bot with credentials""" 8 | auth = tweepy.OAuthHandler(api_key, api_secret) 9 | auth.set_access_token(access_token, access_token_secret) 10 | self.api = tweepy.API(auth) 11 | 12 | def post_tweet(self, content: str) -> str: 13 | """ 14 | Post a tweet 15 | 16 | Args: 17 | content (str): The content of the tweet 18 | 19 | Returns: 20 | str: Status message about the tweet 21 | """ 22 | try: 23 | tweet = self.api.update_status(content) 24 | return f"Successfully posted tweet with ID: {tweet.id}" 25 | except tweepy.TweepException as e: 26 | return f"Error posting tweet: {str(e)}" 27 | 28 | def read_mentions(self, count: int = 10) -> List[Dict]: 29 | """ 30 | Read recent mentions 31 | 32 | Args: 33 | count (int): Number of recent mentions to retrieve 34 | 35 | Returns: 36 | List[Dict]: List of mention objects containing relevant information 37 | """ 38 | try: 39 | mentions = self.api.mentions_timeline(count=count) 40 | return [{ 41 | 'id': mention.id, 42 | 'text': mention.text, 43 | 'user': mention.user.screen_name, 44 | 'created_at': mention.created_at 45 | } for mention in mentions] 46 | except tweepy.TweepException as e: 47 | return [{'error': str(e)}] 48 | 49 | def reply_to_tweet(self, tweet_id: str, content: str) -> str: 50 | """ 51 | Reply to a specific tweet 52 | 53 | Args: 54 | tweet_id (str): ID of the tweet to reply to 55 | content (str): Content of the reply 56 | 57 | Returns: 58 | str: Status message about the reply 59 | """ 60 | try: 61 | tweet = self.api.get_status(tweet_id) 62 | username = tweet.user.screen_name 63 | reply_content = f"@{username} {content}" 64 | 65 | reply = self.api.update_status( 66 | status=reply_content, 67 | in_reply_to_status_id=tweet_id, 68 | auto_populate_reply_metadata=True 69 | ) 70 | return f"Successfully replied to tweet {tweet_id}" 71 | except tweepy.TweepException as e: 72 | return f"Error replying to tweet: {str(e)}" 73 | 74 | def search_tweets(self, query: str, count: int = 10) -> List[Dict]: 75 | """ 76 | Search for tweets matching a query 77 | 78 | Args: 79 | query (str): Search query 80 | count (int): Number of tweets to retrieve 81 | 82 | Returns: 83 | List[Dict]: List of matching tweets 84 | """ 85 | try: 86 | tweets = tweepy.Cursor(self.api.search, q=query).items(count) 87 | return [{ 88 | 'id': tweet.id, 89 | 'text': tweet.text, 90 | 'user': tweet.user.screen_name, 91 | 'created_at': tweet.created_at 92 | } for tweet in tweets] 93 | except tweepy.TweepException as e: 94 | return [{'error': str(e)}] --------------------------------------------------------------------------------