diff --git a/.flake8 b/.flake8 new file mode 100644 index 0000000..4ac4516 --- /dev/null +++ b/.flake8 @@ -0,0 +1,43 @@ +[flake8] +max-line-length = 100 +max-complexity = 10 +exclude = + .git, + __pycache__, + build, + dist, + *.egg-info, + venv, + .venv +extend-ignore = + # Black conflicts with E203 (whitespace before ':') + E203, + # Line break before binary operator (Black formats this way) + W503, + # Lambda assignments (used in routes) + E731, + # Bugbear errors that might cause recursion + B023, + # Ignore nested function definition + B006, + # Ignore missing docstring in public module/package/class/function/method + D100, + D101, + D104, + D102, + D103, + D105, + D106, + D107, + +per-file-ignores = + # Allow unused imports in __init__.py + __init__.py: F401 + # Allow longer lines in config files + config.py: E501 + +# Limit the plugins to avoid recursion issues +enable-extensions = B + +# Increased recursion depth adjustment for bugbear +max-recursion-depth = 1000 diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml new file mode 100644 index 0000000..2aa40b4 --- /dev/null +++ b/.github/workflows/lint.yml @@ -0,0 +1,37 @@ +# .github/workflows/lint.yml +name: Lint + +on: + push: + branches: [ main ] + pull_request: + branches: [ main ] + +jobs: + lint: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + submodules: 'recursive' + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: '3.12' + + - name: Set up Node.js + uses: actions/setup-node@v4 + with: + node-version: '20' + + - name: Install Python linting dependencies + run: | + python -m pip install --upgrade pip + pip install flake8 black isort + pip install -r requirements.txt + + - name: Run Python linters + run: | + black . --check + isort . --check-only diff --git a/.github/workflows/mor-agents-build-linux.yml b/.github/workflows/mor-agents-build-linux.yml index 09d09cb..cdc44ae 100644 --- a/.github/workflows/mor-agents-build-linux.yml +++ b/.github/workflows/mor-agents-build-linux.yml @@ -104,7 +104,7 @@ jobs: local model=\$1 local max_attempts=3 local attempt=1 - + while [ \$attempt -le \$max_attempts ]; do echo -e "\${YELLOW}Pulling Ollama model \$model (Attempt \$attempt)...${NC}" if ollama pull \$model; then @@ -115,7 +115,7 @@ jobs: sleep 5 attempt=\$((attempt + 1)) done - + echo -e "\${RED}Failed to pull \$model after \$max_attempts attempts.${NC}" return 1 } diff --git a/.github/workflows/mor-agents-build-mac-arm.yml b/.github/workflows/mor-agents-build-mac-arm.yml index 0c9ea7f..644e82a 100644 --- a/.github/workflows/mor-agents-build-mac-arm.yml +++ b/.github/workflows/mor-agents-build-mac-arm.yml @@ -10,45 +10,45 @@ on: jobs: build: runs-on: macos-latest - + steps: - uses: actions/checkout@v4 with: submodules: 'recursive' - + - name: Set up Python uses: actions/setup-python@v5 with: python-version: '3.12' - + - name: Install dependencies run: | python -m pip install --upgrade pip pip install -r requirements.txt pip install pyinstaller - + - name: Build with PyInstaller run: | pyinstaller --windowed --name="MORagents" --icon="images/moragents.icns" --osx-entitlements-file "build_assets/macOS/MORagents.entitlements" main.py - + - name: Move .app to expected location run: | mv dist/MORagents.app build_assets/macOS/ - + - name: Install Packages app run: | wget http://s.sudre.free.fr/files/Packages_1211_dev.dmg hdiutil attach Packages_1211_dev.dmg sudo installer -pkg /Volumes/Packages\ 1.2.11/packages/Packages.pkg -target / hdiutil detach /Volumes/Packages\ 1.2.11 - + - name: Create installer package run: | cd build_assets/macOS /usr/local/bin/packagesbuild --verbose --project MorpheusPackagesSudre.pkgproj - + - name: Upload Installer uses: actions/upload-artifact@v4 with: name: MORagentsSetup-macOS - path: ./build_assets/macOS/MORAgentsInstaller.pkg \ No newline at end of file + path: ./build_assets/macOS/MORAgentsInstaller.pkg diff --git a/.github/workflows/mor-agents-build-mac-intel.yml b/.github/workflows/mor-agents-build-mac-intel.yml index caa9124..a3677a4 100644 --- a/.github/workflows/mor-agents-build-mac-intel.yml +++ b/.github/workflows/mor-agents-build-mac-intel.yml @@ -10,45 +10,45 @@ on: jobs: build: runs-on: macos-latest - + steps: - uses: actions/checkout@v4 with: submodules: 'recursive' - + - name: Set up Python uses: actions/setup-python@v5 with: python-version: '3.12' - + - name: Install dependencies run: | python -m pip install --upgrade pip pip install -r requirements.txt pip install pyinstaller - + - name: Build with PyInstaller run: | pyinstaller --windowed --name="MORagents" --icon="images/moragents.icns" --osx-entitlements-file "build_assets/macOS/MORagents.entitlements" main.py - + - name: Move .app to expected location run: | mv dist/MORagents.app build_assets/macOS/ - + - name: Install Packages app run: | wget http://s.sudre.free.fr/files/Packages_1211_dev.dmg hdiutil attach Packages_1211_dev.dmg sudo installer -pkg /Volumes/Packages\ 1.2.11/packages/Packages.pkg -target / hdiutil detach /Volumes/Packages\ 1.2.11 - + - name: Create installer package run: | cd build_assets/macOS /usr/local/bin/packagesbuild --verbose --project MorpheusPackagesSudreIntel.pkgproj - + - name: Upload Installer uses: actions/upload-artifact@v4 with: name: MORagentsSetup-macOS - path: ./build_assets/macOS/MORAgentsInstaller.pkg \ No newline at end of file + path: ./build_assets/macOS/MORAgentsInstaller.pkg diff --git a/.github/workflows/mor-agents-build-windows.yml b/.github/workflows/mor-agents-build-windows.yml index ddc0597..cb65dd8 100644 --- a/.github/workflows/mor-agents-build-windows.yml +++ b/.github/workflows/mor-agents-build-windows.yml @@ -10,7 +10,7 @@ on: jobs: build: runs-on: windows-latest - + steps: - uses: actions/checkout@v4 diff --git a/.github/workflows/security_scan.yml b/.github/workflows/security_scan.yml index ab7ebac..f89d1f1 100644 --- a/.github/workflows/security_scan.yml +++ b/.github/workflows/security_scan.yml @@ -1,4 +1,7 @@ name: Security Scan +permissions: + security-events: write + contents: write on: push: @@ -11,36 +14,31 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - - name: Set up Python uses: actions/setup-python@v4 with: python-version: '3.x' - - name: Install dependencies run: | python -m pip install --upgrade pip pip install -r requirements.txt pip install safety bandit - - name: Run Safety check run: safety check -r requirements.txt continue-on-error: true - - name: Run Bandit run: bandit -r . -f custom continue-on-error: true - - - name: Run Trivy vulnerability scanner in repo mode - uses: aquasecurity/trivy-action@0.20.0 + - name: Run Trivy vulnerability scanner + uses: aquasecurity/trivy-action@master with: scan-type: 'fs' ignore-unfixed: true format: 'sarif' output: 'trivy-results.sarif' severity: 'CRITICAL' - - - name: Upload Trivy scan results to GitHub Security tab + scanners: 'vuln,secret,config' + - name: Upload Trivy scan results uses: github/codeql-action/upload-sarif@v3 with: sarif_file: 'trivy-results.sarif' diff --git a/.gitignore b/.gitignore index 5caedf3..f9c8485 100644 --- a/.gitignore +++ b/.gitignore @@ -56,3 +56,9 @@ resources/* ## installer MORagentsWindowsInstaller MORagentsWindowsInstaller.zip + +## app +MORagents.app + +## lint +.pytest_cache/ diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..c85ea8a --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,22 @@ +repos: +- repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.5.0 + hooks: + - id: trailing-whitespace + - id: end-of-file-fixer + - id: check-yaml + - id: check-json + - id: check-added-large-files + - id: detect-private-key + - id: check-merge-conflict + +- repo: https://github.com/psf/black + rev: 24.2.0 + hooks: + - id: black + language_version: python3.12 + +- repo: https://github.com/pycqa/isort + rev: 5.13.2 + hooks: + - id: isort diff --git a/AGENTABILITIES.md b/AGENTABILITIES.md index da8a892..ae3c7e2 100644 --- a/AGENTABILITIES.md +++ b/AGENTABILITIES.md @@ -1,16 +1,16 @@ # MORagents -Welcome to the world of Web3 agents! If you're interested in building and using agents locally, this document will guide you through the principles and +Welcome to the world of Web3 agents! If you're interested in building and using agents locally, this document will guide you through the principles and current projects underway. ### Principles: -1. **Agents cannot execute decisions**: Agents should not be given private keys or allowed to make transactions on their own. They can only construct transaction +1. **Agents cannot execute decisions**: Agents should not be given private keys or allowed to make transactions on their own. They can only construct transaction payloads for a user's approval. This is due to the limitations of current LLMs in understanding complex transactions and the risk of [gaslighting](https://arxiv.org/abs/2311.04235). 2. **Local installation**: Agents should run on the user's laptop, typically with 8-16 GB of RAM. This allows for faster execution and better performance. -3. **No private keys**: Agents must not have access to private keys or be able to execute transactions independently. User's cryptographic approval is essential for any +3. **No private keys**: Agents must not have access to private keys or be able to execute transactions independently. User's cryptographic approval is essential for any transaction. ### Current Projects: -1. **lachsbagel on Discord** - [this repo](https://github.com/MorpheusAIs/moragents): +1. **lachsbagel on Discord** - [this repo](https://github.com/MorpheusAIs/moragents): 1. Architecture 2. **IODmitri, SanatSharma, LachsBagel on GitHhub** 1. [HideNSeek](https://github.com/MorpheusAIs/HideNSeek): An algorithm for verifying and fingerprinting which model a compute provider is actually running @@ -25,10 +25,10 @@ transaction. 4. CICD builds for Linux and macOS (apple and intel) 5. Vulnerability scanning of dependencies and code 4. GenLayer - 1. (pending) [FeedBuzz](https://github.com/yeagerai/feedbuzz-contracts) - AI filtered logging system to surface user demand and failure modes for new functionality + 1. (pending) [FeedBuzz](https://github.com/yeagerai/feedbuzz-contracts) - AI filtered logging system to surface user demand and failure modes for new functionality 5. **CliffordAttractor on Discord** - Following Assume 16GB+ RAM: 1. Developed a [price fetcher agent](submodules/moragents_dockers/agents/src/data_agent) using CoinGecko. - 2. A [web interface](submodules/moragents_dockers/frontend) which is served by the local Docker installation and integrated with Rainbow, enabling the use of MetaMask, WalletConnect, and other + 2. A [web interface](submodules/moragents_dockers/frontend) which is served by the local Docker installation and integrated with Rainbow, enabling the use of MetaMask, WalletConnect, and other EVM-based wallets. 3. (NEEDS REFACTORING DUE TO 1INCH CHANGE) [Swap agent](submodules/moragents_dockers/agents/src/swap_agent) which can iteratively ask users to provide needed details for disambiguation. 4. [General-purpose agent](https://github.com/MorpheusAIs/moragents/pull/34) that can ingest arbitrary documents, such as PDFs, for basic document QA and text generation. @@ -57,7 +57,7 @@ Pending Lumerin's work. Eventually Agent Builders will be able to permission-les ### How to Contribute: -- If you are working on an agent which can provide value through open models and relies on processing public data, please reach out to lachsbagel on Discord (link below) +- If you are working on an agent which can provide value through open models and relies on processing public data, please reach out to lachsbagel on Discord (link below) - Otherwise, you are more than welcome to publish your agent to the registry when it goes live pending Lumerin's work and any other necessary pieces which come up to better ensure security and verifiability of models in non-local execution environments. - If you are working on security and/or verifiability of models and the runtime, please reach out to LachsBagel on the Morpheus Discord. - Currently looking at [Hyperbolic.xyz](https://hyperbolic.xyz) and [6079](https://docs.6079.ai/technology/6079-proof-of-inference-protocol). See more ecosystem members [here](https://mor.org/ecosystem). diff --git a/DISCLAIMER.md b/DISCLAIMER.md index 92739bc..82a12da 100644 --- a/DISCLAIMER.md +++ b/DISCLAIMER.md @@ -1,2 +1,2 @@ -This highly experimental chatbot is not intended for making important decisions, and its responses are generated based on incomplete data and algorithms that may evolve +This highly experimental chatbot is not intended for making important decisions, and its responses are generated based on incomplete data and algorithms that may evolve rapidly. By using this chatbot, you acknowledge that you use it at your own discretion and assume all risks associated with its limitations and potential errors. diff --git a/README.md b/README.md index c4bb82f..8f89249 100644 --- a/README.md +++ b/README.md @@ -41,7 +41,7 @@ Fully Extensible! Add your own agents and have them automatically invoked based #### Steps to Install 1. Download Installer - 1. For Mac on Apple Silicon M1/2/3 etc. (arm64) + 1. For Mac on Apple Silicon M1/2/3 etc. (arm64) 1. Download and run MORagents installer [MORagents020-apple.pkg](https://drive.proton.me/urls/20ENWS94AW#Kols2sA9mWLf) > SHA256 e65e11719a24ca9a00545443a35cda3b6d78f756b8e2ba535db00399ef75168f MORagents020-apple.pkg 2. For Mac on Intel (x86_64) @@ -52,11 +52,11 @@ Fully Extensible! Add your own agents and have them automatically invoked based #### Future Usage - Open the "MORagents" app from Mac search bar. - - For easier access: Right-click MORagents icon on dock -> Options -> Keep in Dock + - For easier access: Right-click MORagents icon on dock -> Options -> Keep in Dock #### Troubleshooting - If the app shows connections errors in connecting to agents. Please ensure Docker Desktop is running, then close and reopen **MORagents** from desktop. -- If installation is unsuccessful, run the following in your Terminal and open the MORagents....pkg again +- If installation is unsuccessful, run the following in your Terminal and open the MORagents....pkg again ```shell $ xcode-select --install ``` @@ -74,7 +74,7 @@ Fully Extensible! Add your own agents and have them automatically invoked based 2. If that still doesn't work, try temporarily disabling your antivirus and open the .exe again 4. Click and Run **MORagentsSetup.exe** 1. This will auto-install Docker and Ollama dependencies. Those will ask you for confirmation. -5. Open **MORagents** from Desktop +5. Open **MORagents** from Desktop 1. Wait for Docker engine to start... 2. If you see any errors or if anything hangs for >10min, please try opening the MORagents app again from the Desktop @@ -100,4 +100,3 @@ This will allow you to add custom agents which will be automatically invoked bas #### Build instructions: 1. [macOS](build_assets/macOS/README_MACOS_DEV_BUILD.md) 2. [Windows](build_assets/windows/README_WINDOWS_DEV_BUILD.md) - \ No newline at end of file diff --git a/build_assets/linux/install_moragents.sh b/build_assets/linux/install_moragents.sh index 6aa8e87..316d269 100644 --- a/build_assets/linux/install_moragents.sh +++ b/build_assets/linux/install_moragents.sh @@ -25,4 +25,4 @@ echo -e "${YELLOW}Running MORagents setup...${NC}" echo -e "${GREEN}Installation complete!${NC}" echo "You can now start MORagents from your application menu or by running 'MORagents' in the terminal." -echo -e "${YELLOW}NOTE: Please log out and log back in for Docker group changes to take effect.${NC}" \ No newline at end of file +echo -e "${YELLOW}NOTE: Please log out and log back in for Docker group changes to take effect.${NC}" diff --git a/build_assets/macOS/MORagents.app/Contents/Frameworks/Python b/build_assets/macOS/MORagents.app/Contents/Frameworks/Python deleted file mode 120000 index e485e13..0000000 --- a/build_assets/macOS/MORagents.app/Contents/Frameworks/Python +++ /dev/null @@ -1 +0,0 @@ -Python.framework/Versions/3.11/Python \ No newline at end of file diff --git a/build_assets/macOS/MORagents.app/Contents/Frameworks/Python.framework/Python b/build_assets/macOS/MORagents.app/Contents/Frameworks/Python.framework/Python deleted file mode 120000 index be75854..0000000 --- a/build_assets/macOS/MORagents.app/Contents/Frameworks/Python.framework/Python +++ /dev/null @@ -1 +0,0 @@ -Versions/Current/Python \ No newline at end of file diff --git a/build_assets/macOS/MORagents.app/Contents/Frameworks/Python.framework/Resources b/build_assets/macOS/MORagents.app/Contents/Frameworks/Python.framework/Resources deleted file mode 120000 index 953ee36..0000000 --- a/build_assets/macOS/MORagents.app/Contents/Frameworks/Python.framework/Resources +++ /dev/null @@ -1 +0,0 @@ -Versions/Current/Resources \ No newline at end of file diff --git a/build_assets/macOS/MORagents.app/Contents/Frameworks/Python.framework/Versions/3.11/Python b/build_assets/macOS/MORagents.app/Contents/Frameworks/Python.framework/Versions/3.11/Python deleted file mode 100755 index e734fa3..0000000 Binary files a/build_assets/macOS/MORagents.app/Contents/Frameworks/Python.framework/Versions/3.11/Python and /dev/null differ diff --git a/build_assets/macOS/MORagents.app/Contents/Frameworks/Python.framework/Versions/3.11/Resources/Info.plist b/build_assets/macOS/MORagents.app/Contents/Frameworks/Python.framework/Versions/3.11/Resources/Info.plist deleted file mode 100644 index 653ba7b..0000000 --- a/build_assets/macOS/MORagents.app/Contents/Frameworks/Python.framework/Versions/3.11/Resources/Info.plist +++ /dev/null @@ -1,28 +0,0 @@ - - - - - CFBundleDevelopmentRegion - English - CFBundleExecutable - Python - CFBundleGetInfoString - Python Runtime and Library - CFBundleIdentifier - org.python.python - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - Python - CFBundlePackageType - FMWK - CFBundleShortVersionString - 3.11.9, (c) 2001-2023 Python Software Foundation. - CFBundleLongVersionString - 3.11.9, (c) 2001-2023 Python Software Foundation. - CFBundleSignature - ???? - CFBundleVersion - 3.11.9 - - diff --git a/build_assets/macOS/MORagents.app/Contents/Frameworks/Python.framework/Versions/3.11/_CodeSignature/CodeResources b/build_assets/macOS/MORagents.app/Contents/Frameworks/Python.framework/Versions/3.11/_CodeSignature/CodeResources deleted file mode 100644 index f5201d5..0000000 --- a/build_assets/macOS/MORagents.app/Contents/Frameworks/Python.framework/Versions/3.11/_CodeSignature/CodeResources +++ /dev/null @@ -1,128 +0,0 @@ - - - - - files - - Resources/Info.plist - - V34aaXzEpRJ2DIzEhy/IxEvsXqA= - - - files2 - - Resources/Info.plist - - hash2 - - TdALsl+p8JFfb+D7L2lIlHLO1ByMBpeQxHv1z1Rn5L8= - - - - rules - - ^Resources/ - - ^Resources/.*\.lproj/ - - optional - - weight - 1000 - - ^Resources/.*\.lproj/locversion.plist$ - - omit - - weight - 1100 - - ^Resources/Base\.lproj/ - - weight - 1010 - - ^version.plist$ - - - rules2 - - .*\.dSYM($|/) - - weight - 11 - - ^(.*/)?\.DS_Store$ - - omit - - weight - 2000 - - ^(Frameworks|SharedFrameworks|PlugIns|Plug-ins|XPCServices|Helpers|MacOS|Library/(Automator|Spotlight|LoginItems))/ - - nested - - weight - 10 - - ^.* - - ^Info\.plist$ - - omit - - weight - 20 - - ^PkgInfo$ - - omit - - weight - 20 - - ^Resources/ - - weight - 20 - - ^Resources/.*\.lproj/ - - optional - - weight - 1000 - - ^Resources/.*\.lproj/locversion.plist$ - - omit - - weight - 1100 - - ^Resources/Base\.lproj/ - - weight - 1010 - - ^[^/]+$ - - nested - - weight - 10 - - ^embedded\.provisionprofile$ - - weight - 20 - - ^version\.plist$ - - weight - 20 - - - - diff --git a/build_assets/macOS/MORagents.app/Contents/Frameworks/Python.framework/Versions/Current b/build_assets/macOS/MORagents.app/Contents/Frameworks/Python.framework/Versions/Current deleted file mode 120000 index 902b2c9..0000000 --- a/build_assets/macOS/MORagents.app/Contents/Frameworks/Python.framework/Versions/Current +++ /dev/null @@ -1 +0,0 @@ -3.11 \ No newline at end of file diff --git a/build_assets/macOS/MORagents.app/Contents/Frameworks/base_library.zip b/build_assets/macOS/MORagents.app/Contents/Frameworks/base_library.zip deleted file mode 120000 index 89ddc93..0000000 --- a/build_assets/macOS/MORagents.app/Contents/Frameworks/base_library.zip +++ /dev/null @@ -1 +0,0 @@ -../Resources/base_library.zip \ No newline at end of file diff --git a/build_assets/macOS/MORagents.app/Contents/Frameworks/lib-dynload/_bisect.cpython-311-darwin.so b/build_assets/macOS/MORagents.app/Contents/Frameworks/lib-dynload/_bisect.cpython-311-darwin.so deleted file mode 100755 index 6798a91..0000000 Binary files a/build_assets/macOS/MORagents.app/Contents/Frameworks/lib-dynload/_bisect.cpython-311-darwin.so and /dev/null differ diff --git a/build_assets/macOS/MORagents.app/Contents/Frameworks/lib-dynload/_blake2.cpython-311-darwin.so b/build_assets/macOS/MORagents.app/Contents/Frameworks/lib-dynload/_blake2.cpython-311-darwin.so deleted file mode 100755 index 54d6cd1..0000000 Binary files a/build_assets/macOS/MORagents.app/Contents/Frameworks/lib-dynload/_blake2.cpython-311-darwin.so and /dev/null differ diff --git a/build_assets/macOS/MORagents.app/Contents/Frameworks/lib-dynload/_bz2.cpython-311-darwin.so b/build_assets/macOS/MORagents.app/Contents/Frameworks/lib-dynload/_bz2.cpython-311-darwin.so deleted file mode 100755 index 95bc276..0000000 Binary files a/build_assets/macOS/MORagents.app/Contents/Frameworks/lib-dynload/_bz2.cpython-311-darwin.so and /dev/null differ diff --git a/build_assets/macOS/MORagents.app/Contents/Frameworks/lib-dynload/_codecs_cn.cpython-311-darwin.so b/build_assets/macOS/MORagents.app/Contents/Frameworks/lib-dynload/_codecs_cn.cpython-311-darwin.so deleted file mode 100755 index ebfeeef..0000000 Binary files a/build_assets/macOS/MORagents.app/Contents/Frameworks/lib-dynload/_codecs_cn.cpython-311-darwin.so and /dev/null differ diff --git a/build_assets/macOS/MORagents.app/Contents/Frameworks/lib-dynload/_codecs_hk.cpython-311-darwin.so b/build_assets/macOS/MORagents.app/Contents/Frameworks/lib-dynload/_codecs_hk.cpython-311-darwin.so deleted file mode 100755 index 984151a..0000000 Binary files a/build_assets/macOS/MORagents.app/Contents/Frameworks/lib-dynload/_codecs_hk.cpython-311-darwin.so and /dev/null differ diff --git a/build_assets/macOS/MORagents.app/Contents/Frameworks/lib-dynload/_codecs_iso2022.cpython-311-darwin.so b/build_assets/macOS/MORagents.app/Contents/Frameworks/lib-dynload/_codecs_iso2022.cpython-311-darwin.so deleted file mode 100755 index dc409ba..0000000 Binary files a/build_assets/macOS/MORagents.app/Contents/Frameworks/lib-dynload/_codecs_iso2022.cpython-311-darwin.so and /dev/null differ diff --git a/build_assets/macOS/MORagents.app/Contents/Frameworks/lib-dynload/_codecs_jp.cpython-311-darwin.so b/build_assets/macOS/MORagents.app/Contents/Frameworks/lib-dynload/_codecs_jp.cpython-311-darwin.so deleted file mode 100755 index 38b2679..0000000 Binary files a/build_assets/macOS/MORagents.app/Contents/Frameworks/lib-dynload/_codecs_jp.cpython-311-darwin.so and /dev/null differ diff --git a/build_assets/macOS/MORagents.app/Contents/Frameworks/lib-dynload/_codecs_kr.cpython-311-darwin.so b/build_assets/macOS/MORagents.app/Contents/Frameworks/lib-dynload/_codecs_kr.cpython-311-darwin.so deleted file mode 100755 index b6f7ccb..0000000 Binary files a/build_assets/macOS/MORagents.app/Contents/Frameworks/lib-dynload/_codecs_kr.cpython-311-darwin.so and /dev/null differ diff --git a/build_assets/macOS/MORagents.app/Contents/Frameworks/lib-dynload/_codecs_tw.cpython-311-darwin.so b/build_assets/macOS/MORagents.app/Contents/Frameworks/lib-dynload/_codecs_tw.cpython-311-darwin.so deleted file mode 100755 index 0545a0e..0000000 Binary files a/build_assets/macOS/MORagents.app/Contents/Frameworks/lib-dynload/_codecs_tw.cpython-311-darwin.so and /dev/null differ diff --git a/build_assets/macOS/MORagents.app/Contents/Frameworks/lib-dynload/_contextvars.cpython-311-darwin.so b/build_assets/macOS/MORagents.app/Contents/Frameworks/lib-dynload/_contextvars.cpython-311-darwin.so deleted file mode 100755 index fb46fd4..0000000 Binary files a/build_assets/macOS/MORagents.app/Contents/Frameworks/lib-dynload/_contextvars.cpython-311-darwin.so and /dev/null differ diff --git a/build_assets/macOS/MORagents.app/Contents/Frameworks/lib-dynload/_csv.cpython-311-darwin.so b/build_assets/macOS/MORagents.app/Contents/Frameworks/lib-dynload/_csv.cpython-311-darwin.so deleted file mode 100755 index 355db2c..0000000 Binary files a/build_assets/macOS/MORagents.app/Contents/Frameworks/lib-dynload/_csv.cpython-311-darwin.so and /dev/null differ diff --git a/build_assets/macOS/MORagents.app/Contents/Frameworks/lib-dynload/_datetime.cpython-311-darwin.so b/build_assets/macOS/MORagents.app/Contents/Frameworks/lib-dynload/_datetime.cpython-311-darwin.so deleted file mode 100755 index 826edf5..0000000 Binary files a/build_assets/macOS/MORagents.app/Contents/Frameworks/lib-dynload/_datetime.cpython-311-darwin.so and /dev/null differ diff --git a/build_assets/macOS/MORagents.app/Contents/Frameworks/lib-dynload/_decimal.cpython-311-darwin.so b/build_assets/macOS/MORagents.app/Contents/Frameworks/lib-dynload/_decimal.cpython-311-darwin.so deleted file mode 100755 index c9a39e1..0000000 Binary files a/build_assets/macOS/MORagents.app/Contents/Frameworks/lib-dynload/_decimal.cpython-311-darwin.so and /dev/null differ diff --git a/build_assets/macOS/MORagents.app/Contents/Frameworks/lib-dynload/_hashlib.cpython-311-darwin.so b/build_assets/macOS/MORagents.app/Contents/Frameworks/lib-dynload/_hashlib.cpython-311-darwin.so deleted file mode 100755 index eadcafe..0000000 Binary files a/build_assets/macOS/MORagents.app/Contents/Frameworks/lib-dynload/_hashlib.cpython-311-darwin.so and /dev/null differ diff --git a/build_assets/macOS/MORagents.app/Contents/Frameworks/lib-dynload/_heapq.cpython-311-darwin.so b/build_assets/macOS/MORagents.app/Contents/Frameworks/lib-dynload/_heapq.cpython-311-darwin.so deleted file mode 100755 index df8aade..0000000 Binary files a/build_assets/macOS/MORagents.app/Contents/Frameworks/lib-dynload/_heapq.cpython-311-darwin.so and /dev/null differ diff --git a/build_assets/macOS/MORagents.app/Contents/Frameworks/lib-dynload/_lzma.cpython-311-darwin.so b/build_assets/macOS/MORagents.app/Contents/Frameworks/lib-dynload/_lzma.cpython-311-darwin.so deleted file mode 100755 index adfbb15..0000000 Binary files a/build_assets/macOS/MORagents.app/Contents/Frameworks/lib-dynload/_lzma.cpython-311-darwin.so and /dev/null differ diff --git a/build_assets/macOS/MORagents.app/Contents/Frameworks/lib-dynload/_md5.cpython-311-darwin.so b/build_assets/macOS/MORagents.app/Contents/Frameworks/lib-dynload/_md5.cpython-311-darwin.so deleted file mode 100755 index 6f77cb5..0000000 Binary files a/build_assets/macOS/MORagents.app/Contents/Frameworks/lib-dynload/_md5.cpython-311-darwin.so and /dev/null differ diff --git a/build_assets/macOS/MORagents.app/Contents/Frameworks/lib-dynload/_multibytecodec.cpython-311-darwin.so b/build_assets/macOS/MORagents.app/Contents/Frameworks/lib-dynload/_multibytecodec.cpython-311-darwin.so deleted file mode 100755 index db87c2e..0000000 Binary files a/build_assets/macOS/MORagents.app/Contents/Frameworks/lib-dynload/_multibytecodec.cpython-311-darwin.so and /dev/null differ diff --git a/build_assets/macOS/MORagents.app/Contents/Frameworks/lib-dynload/_opcode.cpython-311-darwin.so b/build_assets/macOS/MORagents.app/Contents/Frameworks/lib-dynload/_opcode.cpython-311-darwin.so deleted file mode 100755 index 8d7b2d2..0000000 Binary files a/build_assets/macOS/MORagents.app/Contents/Frameworks/lib-dynload/_opcode.cpython-311-darwin.so and /dev/null differ diff --git a/build_assets/macOS/MORagents.app/Contents/Frameworks/lib-dynload/_pickle.cpython-311-darwin.so b/build_assets/macOS/MORagents.app/Contents/Frameworks/lib-dynload/_pickle.cpython-311-darwin.so deleted file mode 100755 index 10b16e8..0000000 Binary files a/build_assets/macOS/MORagents.app/Contents/Frameworks/lib-dynload/_pickle.cpython-311-darwin.so and /dev/null differ diff --git a/build_assets/macOS/MORagents.app/Contents/Frameworks/lib-dynload/_posixsubprocess.cpython-311-darwin.so b/build_assets/macOS/MORagents.app/Contents/Frameworks/lib-dynload/_posixsubprocess.cpython-311-darwin.so deleted file mode 100755 index 64c4e3e..0000000 Binary files a/build_assets/macOS/MORagents.app/Contents/Frameworks/lib-dynload/_posixsubprocess.cpython-311-darwin.so and /dev/null differ diff --git a/build_assets/macOS/MORagents.app/Contents/Frameworks/lib-dynload/_random.cpython-311-darwin.so b/build_assets/macOS/MORagents.app/Contents/Frameworks/lib-dynload/_random.cpython-311-darwin.so deleted file mode 100755 index 325efd7..0000000 Binary files a/build_assets/macOS/MORagents.app/Contents/Frameworks/lib-dynload/_random.cpython-311-darwin.so and /dev/null differ diff --git a/build_assets/macOS/MORagents.app/Contents/Frameworks/lib-dynload/_scproxy.cpython-311-darwin.so b/build_assets/macOS/MORagents.app/Contents/Frameworks/lib-dynload/_scproxy.cpython-311-darwin.so deleted file mode 100755 index 2c5e57f..0000000 Binary files a/build_assets/macOS/MORagents.app/Contents/Frameworks/lib-dynload/_scproxy.cpython-311-darwin.so and /dev/null differ diff --git a/build_assets/macOS/MORagents.app/Contents/Frameworks/lib-dynload/_sha1.cpython-311-darwin.so b/build_assets/macOS/MORagents.app/Contents/Frameworks/lib-dynload/_sha1.cpython-311-darwin.so deleted file mode 100755 index 184ae13..0000000 Binary files a/build_assets/macOS/MORagents.app/Contents/Frameworks/lib-dynload/_sha1.cpython-311-darwin.so and /dev/null differ diff --git a/build_assets/macOS/MORagents.app/Contents/Frameworks/lib-dynload/_sha256.cpython-311-darwin.so b/build_assets/macOS/MORagents.app/Contents/Frameworks/lib-dynload/_sha256.cpython-311-darwin.so deleted file mode 100755 index 82aff96..0000000 Binary files a/build_assets/macOS/MORagents.app/Contents/Frameworks/lib-dynload/_sha256.cpython-311-darwin.so and /dev/null differ diff --git a/build_assets/macOS/MORagents.app/Contents/Frameworks/lib-dynload/_sha3.cpython-311-darwin.so b/build_assets/macOS/MORagents.app/Contents/Frameworks/lib-dynload/_sha3.cpython-311-darwin.so deleted file mode 100755 index b57973f..0000000 Binary files a/build_assets/macOS/MORagents.app/Contents/Frameworks/lib-dynload/_sha3.cpython-311-darwin.so and /dev/null differ diff --git a/build_assets/macOS/MORagents.app/Contents/Frameworks/lib-dynload/_sha512.cpython-311-darwin.so b/build_assets/macOS/MORagents.app/Contents/Frameworks/lib-dynload/_sha512.cpython-311-darwin.so deleted file mode 100755 index f595726..0000000 Binary files a/build_assets/macOS/MORagents.app/Contents/Frameworks/lib-dynload/_sha512.cpython-311-darwin.so and /dev/null differ diff --git a/build_assets/macOS/MORagents.app/Contents/Frameworks/lib-dynload/_socket.cpython-311-darwin.so b/build_assets/macOS/MORagents.app/Contents/Frameworks/lib-dynload/_socket.cpython-311-darwin.so deleted file mode 100755 index 5ec2b04..0000000 Binary files a/build_assets/macOS/MORagents.app/Contents/Frameworks/lib-dynload/_socket.cpython-311-darwin.so and /dev/null differ diff --git a/build_assets/macOS/MORagents.app/Contents/Frameworks/lib-dynload/_ssl.cpython-311-darwin.so b/build_assets/macOS/MORagents.app/Contents/Frameworks/lib-dynload/_ssl.cpython-311-darwin.so deleted file mode 100755 index 71b4f7d..0000000 Binary files a/build_assets/macOS/MORagents.app/Contents/Frameworks/lib-dynload/_ssl.cpython-311-darwin.so and /dev/null differ diff --git a/build_assets/macOS/MORagents.app/Contents/Frameworks/lib-dynload/_statistics.cpython-311-darwin.so b/build_assets/macOS/MORagents.app/Contents/Frameworks/lib-dynload/_statistics.cpython-311-darwin.so deleted file mode 100755 index 5bf100e..0000000 Binary files a/build_assets/macOS/MORagents.app/Contents/Frameworks/lib-dynload/_statistics.cpython-311-darwin.so and /dev/null differ diff --git a/build_assets/macOS/MORagents.app/Contents/Frameworks/lib-dynload/_struct.cpython-311-darwin.so b/build_assets/macOS/MORagents.app/Contents/Frameworks/lib-dynload/_struct.cpython-311-darwin.so deleted file mode 100755 index c2b0e78..0000000 Binary files a/build_assets/macOS/MORagents.app/Contents/Frameworks/lib-dynload/_struct.cpython-311-darwin.so and /dev/null differ diff --git a/build_assets/macOS/MORagents.app/Contents/Frameworks/lib-dynload/_typing.cpython-311-darwin.so b/build_assets/macOS/MORagents.app/Contents/Frameworks/lib-dynload/_typing.cpython-311-darwin.so deleted file mode 100755 index 9a3beaa..0000000 Binary files a/build_assets/macOS/MORagents.app/Contents/Frameworks/lib-dynload/_typing.cpython-311-darwin.so and /dev/null differ diff --git a/build_assets/macOS/MORagents.app/Contents/Frameworks/lib-dynload/array.cpython-311-darwin.so b/build_assets/macOS/MORagents.app/Contents/Frameworks/lib-dynload/array.cpython-311-darwin.so deleted file mode 100755 index c51e5d9..0000000 Binary files a/build_assets/macOS/MORagents.app/Contents/Frameworks/lib-dynload/array.cpython-311-darwin.so and /dev/null differ diff --git a/build_assets/macOS/MORagents.app/Contents/Frameworks/lib-dynload/binascii.cpython-311-darwin.so b/build_assets/macOS/MORagents.app/Contents/Frameworks/lib-dynload/binascii.cpython-311-darwin.so deleted file mode 100755 index 9bf61c7..0000000 Binary files a/build_assets/macOS/MORagents.app/Contents/Frameworks/lib-dynload/binascii.cpython-311-darwin.so and /dev/null differ diff --git a/build_assets/macOS/MORagents.app/Contents/Frameworks/lib-dynload/fcntl.cpython-311-darwin.so b/build_assets/macOS/MORagents.app/Contents/Frameworks/lib-dynload/fcntl.cpython-311-darwin.so deleted file mode 100755 index 9fd0042..0000000 Binary files a/build_assets/macOS/MORagents.app/Contents/Frameworks/lib-dynload/fcntl.cpython-311-darwin.so and /dev/null differ diff --git a/build_assets/macOS/MORagents.app/Contents/Frameworks/lib-dynload/grp.cpython-311-darwin.so b/build_assets/macOS/MORagents.app/Contents/Frameworks/lib-dynload/grp.cpython-311-darwin.so deleted file mode 100755 index b874135..0000000 Binary files a/build_assets/macOS/MORagents.app/Contents/Frameworks/lib-dynload/grp.cpython-311-darwin.so and /dev/null differ diff --git a/build_assets/macOS/MORagents.app/Contents/Frameworks/lib-dynload/math.cpython-311-darwin.so b/build_assets/macOS/MORagents.app/Contents/Frameworks/lib-dynload/math.cpython-311-darwin.so deleted file mode 100755 index 2b22563..0000000 Binary files a/build_assets/macOS/MORagents.app/Contents/Frameworks/lib-dynload/math.cpython-311-darwin.so and /dev/null differ diff --git a/build_assets/macOS/MORagents.app/Contents/Frameworks/lib-dynload/pyexpat.cpython-311-darwin.so b/build_assets/macOS/MORagents.app/Contents/Frameworks/lib-dynload/pyexpat.cpython-311-darwin.so deleted file mode 100755 index 9170583..0000000 Binary files a/build_assets/macOS/MORagents.app/Contents/Frameworks/lib-dynload/pyexpat.cpython-311-darwin.so and /dev/null differ diff --git a/build_assets/macOS/MORagents.app/Contents/Frameworks/lib-dynload/resource.cpython-311-darwin.so b/build_assets/macOS/MORagents.app/Contents/Frameworks/lib-dynload/resource.cpython-311-darwin.so deleted file mode 100755 index 652b814..0000000 Binary files a/build_assets/macOS/MORagents.app/Contents/Frameworks/lib-dynload/resource.cpython-311-darwin.so and /dev/null differ diff --git a/build_assets/macOS/MORagents.app/Contents/Frameworks/lib-dynload/select.cpython-311-darwin.so b/build_assets/macOS/MORagents.app/Contents/Frameworks/lib-dynload/select.cpython-311-darwin.so deleted file mode 100755 index 4cce59e..0000000 Binary files a/build_assets/macOS/MORagents.app/Contents/Frameworks/lib-dynload/select.cpython-311-darwin.so and /dev/null differ diff --git a/build_assets/macOS/MORagents.app/Contents/Frameworks/lib-dynload/termios.cpython-311-darwin.so b/build_assets/macOS/MORagents.app/Contents/Frameworks/lib-dynload/termios.cpython-311-darwin.so deleted file mode 100755 index 6f58b8f..0000000 Binary files a/build_assets/macOS/MORagents.app/Contents/Frameworks/lib-dynload/termios.cpython-311-darwin.so and /dev/null differ diff --git a/build_assets/macOS/MORagents.app/Contents/Frameworks/lib-dynload/unicodedata.cpython-311-darwin.so b/build_assets/macOS/MORagents.app/Contents/Frameworks/lib-dynload/unicodedata.cpython-311-darwin.so deleted file mode 100755 index aee61ea..0000000 Binary files a/build_assets/macOS/MORagents.app/Contents/Frameworks/lib-dynload/unicodedata.cpython-311-darwin.so and /dev/null differ diff --git a/build_assets/macOS/MORagents.app/Contents/Frameworks/lib-dynload/zlib.cpython-311-darwin.so b/build_assets/macOS/MORagents.app/Contents/Frameworks/lib-dynload/zlib.cpython-311-darwin.so deleted file mode 100755 index 4533871..0000000 Binary files a/build_assets/macOS/MORagents.app/Contents/Frameworks/lib-dynload/zlib.cpython-311-darwin.so and /dev/null differ diff --git a/build_assets/macOS/MORagents.app/Contents/Frameworks/libcrypto.3.dylib b/build_assets/macOS/MORagents.app/Contents/Frameworks/libcrypto.3.dylib deleted file mode 100755 index 3c841b5..0000000 Binary files a/build_assets/macOS/MORagents.app/Contents/Frameworks/libcrypto.3.dylib and /dev/null differ diff --git a/build_assets/macOS/MORagents.app/Contents/Frameworks/liblzma.5.dylib b/build_assets/macOS/MORagents.app/Contents/Frameworks/liblzma.5.dylib deleted file mode 100755 index 7db7017..0000000 Binary files a/build_assets/macOS/MORagents.app/Contents/Frameworks/liblzma.5.dylib and /dev/null differ diff --git a/build_assets/macOS/MORagents.app/Contents/Frameworks/libmpdec.4.dylib b/build_assets/macOS/MORagents.app/Contents/Frameworks/libmpdec.4.dylib deleted file mode 100755 index e356217..0000000 Binary files a/build_assets/macOS/MORagents.app/Contents/Frameworks/libmpdec.4.dylib and /dev/null differ diff --git a/build_assets/macOS/MORagents.app/Contents/Frameworks/libssl.3.dylib b/build_assets/macOS/MORagents.app/Contents/Frameworks/libssl.3.dylib deleted file mode 100755 index 85d19a3..0000000 Binary files a/build_assets/macOS/MORagents.app/Contents/Frameworks/libssl.3.dylib and /dev/null differ diff --git a/build_assets/macOS/MORagents.app/Contents/Info.plist b/build_assets/macOS/MORagents.app/Contents/Info.plist deleted file mode 100644 index 127cf0e..0000000 --- a/build_assets/macOS/MORagents.app/Contents/Info.plist +++ /dev/null @@ -1,24 +0,0 @@ - - - - - CFBundleDisplayName - MORagents - CFBundleExecutable - MORagents - CFBundleIconFile - moragents.icns - CFBundleIdentifier - com.liquidtensor.moragents - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - MORagents - CFBundlePackageType - APPL - CFBundleShortVersionString - 0.0.0 - NSHighResolutionCapable - - - diff --git a/build_assets/macOS/MORagents.app/Contents/MacOS/MORagents b/build_assets/macOS/MORagents.app/Contents/MacOS/MORagents deleted file mode 100755 index 402dc34..0000000 Binary files a/build_assets/macOS/MORagents.app/Contents/MacOS/MORagents and /dev/null differ diff --git a/build_assets/macOS/MORagents.app/Contents/Resources/Python b/build_assets/macOS/MORagents.app/Contents/Resources/Python deleted file mode 120000 index e485e13..0000000 --- a/build_assets/macOS/MORagents.app/Contents/Resources/Python +++ /dev/null @@ -1 +0,0 @@ -Python.framework/Versions/3.11/Python \ No newline at end of file diff --git a/build_assets/macOS/MORagents.app/Contents/Resources/Python.framework b/build_assets/macOS/MORagents.app/Contents/Resources/Python.framework deleted file mode 120000 index 34ca7ea..0000000 --- a/build_assets/macOS/MORagents.app/Contents/Resources/Python.framework +++ /dev/null @@ -1 +0,0 @@ -../Frameworks/Python.framework \ No newline at end of file diff --git a/build_assets/macOS/MORagents.app/Contents/Resources/base_library.zip b/build_assets/macOS/MORagents.app/Contents/Resources/base_library.zip deleted file mode 100644 index 241b01c..0000000 Binary files a/build_assets/macOS/MORagents.app/Contents/Resources/base_library.zip and /dev/null differ diff --git a/build_assets/macOS/MORagents.app/Contents/Resources/lib-dynload b/build_assets/macOS/MORagents.app/Contents/Resources/lib-dynload deleted file mode 120000 index 5319f7a..0000000 --- a/build_assets/macOS/MORagents.app/Contents/Resources/lib-dynload +++ /dev/null @@ -1 +0,0 @@ -../Frameworks/lib-dynload \ No newline at end of file diff --git a/build_assets/macOS/MORagents.app/Contents/Resources/libcrypto.3.dylib b/build_assets/macOS/MORagents.app/Contents/Resources/libcrypto.3.dylib deleted file mode 120000 index cc14f15..0000000 --- a/build_assets/macOS/MORagents.app/Contents/Resources/libcrypto.3.dylib +++ /dev/null @@ -1 +0,0 @@ -../Frameworks/libcrypto.3.dylib \ No newline at end of file diff --git a/build_assets/macOS/MORagents.app/Contents/Resources/liblzma.5.dylib b/build_assets/macOS/MORagents.app/Contents/Resources/liblzma.5.dylib deleted file mode 120000 index fbed50f..0000000 --- a/build_assets/macOS/MORagents.app/Contents/Resources/liblzma.5.dylib +++ /dev/null @@ -1 +0,0 @@ -../Frameworks/liblzma.5.dylib \ No newline at end of file diff --git a/build_assets/macOS/MORagents.app/Contents/Resources/libmpdec.4.dylib b/build_assets/macOS/MORagents.app/Contents/Resources/libmpdec.4.dylib deleted file mode 120000 index 21d144b..0000000 --- a/build_assets/macOS/MORagents.app/Contents/Resources/libmpdec.4.dylib +++ /dev/null @@ -1 +0,0 @@ -../Frameworks/libmpdec.4.dylib \ No newline at end of file diff --git a/build_assets/macOS/MORagents.app/Contents/Resources/libssl.3.dylib b/build_assets/macOS/MORagents.app/Contents/Resources/libssl.3.dylib deleted file mode 120000 index 4651492..0000000 --- a/build_assets/macOS/MORagents.app/Contents/Resources/libssl.3.dylib +++ /dev/null @@ -1 +0,0 @@ -../Frameworks/libssl.3.dylib \ No newline at end of file diff --git a/build_assets/macOS/MORagents.app/Contents/Resources/moragents.icns b/build_assets/macOS/MORagents.app/Contents/Resources/moragents.icns deleted file mode 100644 index bd05d78..0000000 Binary files a/build_assets/macOS/MORagents.app/Contents/Resources/moragents.icns and /dev/null differ diff --git a/build_assets/macOS/MORagents.app/Contents/_CodeSignature/CodeResources b/build_assets/macOS/MORagents.app/Contents/_CodeSignature/CodeResources deleted file mode 100644 index 2d4b94e..0000000 --- a/build_assets/macOS/MORagents.app/Contents/_CodeSignature/CodeResources +++ /dev/null @@ -1,616 +0,0 @@ - - - - - files - - Resources/base_library.zip - - IS1VtlvSrysxLHoNY08e2LTAths= - - Resources/moragents.icns - - 9nAN5Ww73gWkts/lCfj9eohp/Ww= - - - files2 - - Frameworks/Python - - symlink - Python.framework/Versions/3.11/Python - - Frameworks/Python.framework - - cdhash - - A1ZSa7QwzxfBtgARhMzuuvRDJsA= - - requirement - identifier "org.python.python" and anchor apple generic and certificate 1[field.1.2.840.113635.100.6.2.6] /* exists */ and certificate leaf[field.1.2.840.113635.100.6.1.13] /* exists */ and certificate leaf[subject.OU] = ZQN244GMTD - - Frameworks/base_library.zip - - symlink - ../Resources/base_library.zip - - Frameworks/lib-dynload/_bisect.cpython-311-darwin.so - - cdhash - - a9L5PZ7D3tS7mEXjX3go+xBBBzU= - - requirement - identifier "_bisect.cpython-311-darwin" and anchor apple generic and certificate 1[field.1.2.840.113635.100.6.2.6] /* exists */ and certificate leaf[field.1.2.840.113635.100.6.1.13] /* exists */ and certificate leaf[subject.OU] = ZQN244GMTD - - Frameworks/lib-dynload/_blake2.cpython-311-darwin.so - - cdhash - - JO+rqD4/5sCJ49BI8PfthSRQPoY= - - requirement - identifier "_blake2.cpython-311-darwin" and anchor apple generic and certificate 1[field.1.2.840.113635.100.6.2.6] /* exists */ and certificate leaf[field.1.2.840.113635.100.6.1.13] /* exists */ and certificate leaf[subject.OU] = ZQN244GMTD - - Frameworks/lib-dynload/_bz2.cpython-311-darwin.so - - cdhash - - 2zPo0SGvr3iCt42LyeMbyZ9PRbA= - - requirement - identifier "_bz2.cpython-311-darwin" and anchor apple generic and certificate 1[field.1.2.840.113635.100.6.2.6] /* exists */ and certificate leaf[field.1.2.840.113635.100.6.1.13] /* exists */ and certificate leaf[subject.OU] = ZQN244GMTD - - Frameworks/lib-dynload/_codecs_cn.cpython-311-darwin.so - - cdhash - - 0aZ6HxwXyQ0YjKpNIMAWvf1TmGk= - - requirement - identifier "_codecs_cn.cpython-311-darwin" and anchor apple generic and certificate 1[field.1.2.840.113635.100.6.2.6] /* exists */ and certificate leaf[field.1.2.840.113635.100.6.1.13] /* exists */ and certificate leaf[subject.OU] = ZQN244GMTD - - Frameworks/lib-dynload/_codecs_hk.cpython-311-darwin.so - - cdhash - - WTOUUaxw0S5CYaa6gNrmbHoDJlI= - - requirement - identifier "_codecs_hk.cpython-311-darwin" and anchor apple generic and certificate 1[field.1.2.840.113635.100.6.2.6] /* exists */ and certificate leaf[field.1.2.840.113635.100.6.1.13] /* exists */ and certificate leaf[subject.OU] = ZQN244GMTD - - Frameworks/lib-dynload/_codecs_iso2022.cpython-311-darwin.so - - cdhash - - fjPbCTgvW7zInPk48/nX1td9tcg= - - requirement - identifier "_codecs_iso2022.cpython-311-darwin" and anchor apple generic and certificate 1[field.1.2.840.113635.100.6.2.6] /* exists */ and certificate leaf[field.1.2.840.113635.100.6.1.13] /* exists */ and certificate leaf[subject.OU] = ZQN244GMTD - - Frameworks/lib-dynload/_codecs_jp.cpython-311-darwin.so - - cdhash - - +paked5eaY0QSBYCnxJG334zBsg= - - requirement - identifier "_codecs_jp.cpython-311-darwin" and anchor apple generic and certificate 1[field.1.2.840.113635.100.6.2.6] /* exists */ and certificate leaf[field.1.2.840.113635.100.6.1.13] /* exists */ and certificate leaf[subject.OU] = ZQN244GMTD - - Frameworks/lib-dynload/_codecs_kr.cpython-311-darwin.so - - cdhash - - XT6BaBL42ZccSn3m800DWIYBRFs= - - requirement - identifier "_codecs_kr.cpython-311-darwin" and anchor apple generic and certificate 1[field.1.2.840.113635.100.6.2.6] /* exists */ and certificate leaf[field.1.2.840.113635.100.6.1.13] /* exists */ and certificate leaf[subject.OU] = ZQN244GMTD - - Frameworks/lib-dynload/_codecs_tw.cpython-311-darwin.so - - cdhash - - M7E9qAv9gmULGwJxLjp0auXtJiQ= - - requirement - identifier "_codecs_tw.cpython-311-darwin" and anchor apple generic and certificate 1[field.1.2.840.113635.100.6.2.6] /* exists */ and certificate leaf[field.1.2.840.113635.100.6.1.13] /* exists */ and certificate leaf[subject.OU] = ZQN244GMTD - - Frameworks/lib-dynload/_contextvars.cpython-311-darwin.so - - cdhash - - FKrEpOUaDpv7b35q9+AU/aMDA+c= - - requirement - identifier "_contextvars.cpython-311-darwin" and anchor apple generic and certificate 1[field.1.2.840.113635.100.6.2.6] /* exists */ and certificate leaf[field.1.2.840.113635.100.6.1.13] /* exists */ and certificate leaf[subject.OU] = ZQN244GMTD - - Frameworks/lib-dynload/_csv.cpython-311-darwin.so - - cdhash - - sQjwzfAcRL4Qrnl/3LFjuS3Crjo= - - requirement - identifier "_csv.cpython-311-darwin" and anchor apple generic and certificate 1[field.1.2.840.113635.100.6.2.6] /* exists */ and certificate leaf[field.1.2.840.113635.100.6.1.13] /* exists */ and certificate leaf[subject.OU] = ZQN244GMTD - - Frameworks/lib-dynload/_datetime.cpython-311-darwin.so - - cdhash - - sgzkBEmqvuC6s+W9npnyEVCcW9w= - - requirement - identifier "_datetime.cpython-311-darwin" and anchor apple generic and certificate 1[field.1.2.840.113635.100.6.2.6] /* exists */ and certificate leaf[field.1.2.840.113635.100.6.1.13] /* exists */ and certificate leaf[subject.OU] = ZQN244GMTD - - Frameworks/lib-dynload/_decimal.cpython-311-darwin.so - - cdhash - - NM64mhqiVLs3XIbP04hnO77kKaw= - - requirement - identifier "_decimal.cpython-311-darwin" and anchor apple generic and certificate 1[field.1.2.840.113635.100.6.2.6] /* exists */ and certificate leaf[field.1.2.840.113635.100.6.1.13] /* exists */ and certificate leaf[subject.OU] = ZQN244GMTD - - Frameworks/lib-dynload/_hashlib.cpython-311-darwin.so - - cdhash - - w+2+oGeYUZPyoeNH29J9eF7JMbc= - - requirement - identifier "_hashlib.cpython-311-darwin" and anchor apple generic and certificate 1[field.1.2.840.113635.100.6.2.6] /* exists */ and certificate leaf[field.1.2.840.113635.100.6.1.13] /* exists */ and certificate leaf[subject.OU] = ZQN244GMTD - - Frameworks/lib-dynload/_heapq.cpython-311-darwin.so - - cdhash - - Rwj4koCBDkIe55Ue8Z6A5Ml0yd8= - - requirement - identifier "_heapq.cpython-311-darwin" and anchor apple generic and certificate 1[field.1.2.840.113635.100.6.2.6] /* exists */ and certificate leaf[field.1.2.840.113635.100.6.1.13] /* exists */ and certificate leaf[subject.OU] = ZQN244GMTD - - Frameworks/lib-dynload/_lzma.cpython-311-darwin.so - - cdhash - - kiRv0/MdHrgtmhXFdOCHgWJ2uJU= - - requirement - identifier "_lzma.cpython-311-darwin" and anchor apple generic and certificate 1[field.1.2.840.113635.100.6.2.6] /* exists */ and certificate leaf[field.1.2.840.113635.100.6.1.13] /* exists */ and certificate leaf[subject.OU] = ZQN244GMTD - - Frameworks/lib-dynload/_md5.cpython-311-darwin.so - - cdhash - - uCE0tAVZGWYhp0Oe4MaBxrq51zQ= - - requirement - identifier "_md5.cpython-311-darwin" and anchor apple generic and certificate 1[field.1.2.840.113635.100.6.2.6] /* exists */ and certificate leaf[field.1.2.840.113635.100.6.1.13] /* exists */ and certificate leaf[subject.OU] = ZQN244GMTD - - Frameworks/lib-dynload/_multibytecodec.cpython-311-darwin.so - - cdhash - - 0DqK6kkrSLtL+VMTlMaUsdr2k1g= - - requirement - identifier "_multibytecodec.cpython-311-darwin" and anchor apple generic and certificate 1[field.1.2.840.113635.100.6.2.6] /* exists */ and certificate leaf[field.1.2.840.113635.100.6.1.13] /* exists */ and certificate leaf[subject.OU] = ZQN244GMTD - - Frameworks/lib-dynload/_opcode.cpython-311-darwin.so - - cdhash - - NgXWdadJDL0FHKEh11iP95PL9ZQ= - - requirement - identifier "_opcode.cpython-311-darwin" and anchor apple generic and certificate 1[field.1.2.840.113635.100.6.2.6] /* exists */ and certificate leaf[field.1.2.840.113635.100.6.1.13] /* exists */ and certificate leaf[subject.OU] = ZQN244GMTD - - Frameworks/lib-dynload/_pickle.cpython-311-darwin.so - - cdhash - - 4D658Yw3LJ77hQemSSHlDrdMHjY= - - requirement - identifier "_pickle.cpython-311-darwin" and anchor apple generic and certificate 1[field.1.2.840.113635.100.6.2.6] /* exists */ and certificate leaf[field.1.2.840.113635.100.6.1.13] /* exists */ and certificate leaf[subject.OU] = ZQN244GMTD - - Frameworks/lib-dynload/_posixsubprocess.cpython-311-darwin.so - - cdhash - - HIebyK2xiqYwgs272vOCqP/Nurc= - - requirement - identifier "_posixsubprocess.cpython-311-darwin" and anchor apple generic and certificate 1[field.1.2.840.113635.100.6.2.6] /* exists */ and certificate leaf[field.1.2.840.113635.100.6.1.13] /* exists */ and certificate leaf[subject.OU] = ZQN244GMTD - - Frameworks/lib-dynload/_random.cpython-311-darwin.so - - cdhash - - sE7ItPIFe6TUZejMiWDCTVN70ng= - - requirement - identifier "_random.cpython-311-darwin" and anchor apple generic and certificate 1[field.1.2.840.113635.100.6.2.6] /* exists */ and certificate leaf[field.1.2.840.113635.100.6.1.13] /* exists */ and certificate leaf[subject.OU] = ZQN244GMTD - - Frameworks/lib-dynload/_scproxy.cpython-311-darwin.so - - cdhash - - YcaQQ6IhFEmcgdiHjGmlr6hRQIM= - - requirement - identifier "_scproxy.cpython-311-darwin" and anchor apple generic and certificate 1[field.1.2.840.113635.100.6.2.6] /* exists */ and certificate leaf[field.1.2.840.113635.100.6.1.13] /* exists */ and certificate leaf[subject.OU] = ZQN244GMTD - - Frameworks/lib-dynload/_sha1.cpython-311-darwin.so - - cdhash - - J9whwvzD7VnmseSFhV8fp9OA/VI= - - requirement - identifier "_sha1.cpython-311-darwin" and anchor apple generic and certificate 1[field.1.2.840.113635.100.6.2.6] /* exists */ and certificate leaf[field.1.2.840.113635.100.6.1.13] /* exists */ and certificate leaf[subject.OU] = ZQN244GMTD - - Frameworks/lib-dynload/_sha256.cpython-311-darwin.so - - cdhash - - E/pmbqfpLEuyYM8aMBftZRh5dw4= - - requirement - identifier "_sha256.cpython-311-darwin" and anchor apple generic and certificate 1[field.1.2.840.113635.100.6.2.6] /* exists */ and certificate leaf[field.1.2.840.113635.100.6.1.13] /* exists */ and certificate leaf[subject.OU] = ZQN244GMTD - - Frameworks/lib-dynload/_sha3.cpython-311-darwin.so - - cdhash - - NmWij7YVE0M2nUs9XZGzv0OWzj0= - - requirement - identifier "_sha3.cpython-311-darwin" and anchor apple generic and certificate 1[field.1.2.840.113635.100.6.2.6] /* exists */ and certificate leaf[field.1.2.840.113635.100.6.1.13] /* exists */ and certificate leaf[subject.OU] = ZQN244GMTD - - Frameworks/lib-dynload/_sha512.cpython-311-darwin.so - - cdhash - - lPEAnBiLvZY9DCXykfu9d0xfgfU= - - requirement - identifier "_sha512.cpython-311-darwin" and anchor apple generic and certificate 1[field.1.2.840.113635.100.6.2.6] /* exists */ and certificate leaf[field.1.2.840.113635.100.6.1.13] /* exists */ and certificate leaf[subject.OU] = ZQN244GMTD - - Frameworks/lib-dynload/_socket.cpython-311-darwin.so - - cdhash - - y1BXtpEw9aOYtFT6l9ZlAoiDqVo= - - requirement - identifier "_socket.cpython-311-darwin" and anchor apple generic and certificate 1[field.1.2.840.113635.100.6.2.6] /* exists */ and certificate leaf[field.1.2.840.113635.100.6.1.13] /* exists */ and certificate leaf[subject.OU] = ZQN244GMTD - - Frameworks/lib-dynload/_ssl.cpython-311-darwin.so - - cdhash - - Kjzabf6+maOvZUTWsBWKrYAzkO4= - - requirement - identifier "_ssl.cpython-311-darwin" and anchor apple generic and certificate 1[field.1.2.840.113635.100.6.2.6] /* exists */ and certificate leaf[field.1.2.840.113635.100.6.1.13] /* exists */ and certificate leaf[subject.OU] = ZQN244GMTD - - Frameworks/lib-dynload/_statistics.cpython-311-darwin.so - - cdhash - - JW2gZbV+8dRoqgHMtj1NL8dnE80= - - requirement - identifier "_statistics.cpython-311-darwin" and anchor apple generic and certificate 1[field.1.2.840.113635.100.6.2.6] /* exists */ and certificate leaf[field.1.2.840.113635.100.6.1.13] /* exists */ and certificate leaf[subject.OU] = ZQN244GMTD - - Frameworks/lib-dynload/_struct.cpython-311-darwin.so - - cdhash - - 5UH/M0Yt24N3swD/rBmT5lKrOe0= - - requirement - identifier "_struct.cpython-311-darwin" and anchor apple generic and certificate 1[field.1.2.840.113635.100.6.2.6] /* exists */ and certificate leaf[field.1.2.840.113635.100.6.1.13] /* exists */ and certificate leaf[subject.OU] = ZQN244GMTD - - Frameworks/lib-dynload/_typing.cpython-311-darwin.so - - cdhash - - vCUR84fM32WUlFK6j/xxHbUezcA= - - requirement - identifier "_typing.cpython-311-darwin" and anchor apple generic and certificate 1[field.1.2.840.113635.100.6.2.6] /* exists */ and certificate leaf[field.1.2.840.113635.100.6.1.13] /* exists */ and certificate leaf[subject.OU] = ZQN244GMTD - - Frameworks/lib-dynload/array.cpython-311-darwin.so - - cdhash - - AeQhgF1JetTJ/C0B19NItsbatVA= - - requirement - identifier "array.cpython-311-darwin" and anchor apple generic and certificate 1[field.1.2.840.113635.100.6.2.6] /* exists */ and certificate leaf[field.1.2.840.113635.100.6.1.13] /* exists */ and certificate leaf[subject.OU] = ZQN244GMTD - - Frameworks/lib-dynload/binascii.cpython-311-darwin.so - - cdhash - - NTr0oJt/gKDQzGRJvl70sKYu47g= - - requirement - identifier "binascii.cpython-311-darwin" and anchor apple generic and certificate 1[field.1.2.840.113635.100.6.2.6] /* exists */ and certificate leaf[field.1.2.840.113635.100.6.1.13] /* exists */ and certificate leaf[subject.OU] = ZQN244GMTD - - Frameworks/lib-dynload/fcntl.cpython-311-darwin.so - - cdhash - - DgnOnUXfuucRMHSmMmWGZs0EX5s= - - requirement - identifier "fcntl.cpython-311-darwin" and anchor apple generic and certificate 1[field.1.2.840.113635.100.6.2.6] /* exists */ and certificate leaf[field.1.2.840.113635.100.6.1.13] /* exists */ and certificate leaf[subject.OU] = ZQN244GMTD - - Frameworks/lib-dynload/grp.cpython-311-darwin.so - - cdhash - - b+yp3rebVFRT+JNvlmXPrEq6GKc= - - requirement - identifier "grp.cpython-311-darwin" and anchor apple generic and certificate 1[field.1.2.840.113635.100.6.2.6] /* exists */ and certificate leaf[field.1.2.840.113635.100.6.1.13] /* exists */ and certificate leaf[subject.OU] = ZQN244GMTD - - Frameworks/lib-dynload/math.cpython-311-darwin.so - - cdhash - - OFGpnDuFpWztXTjGM209yAzo6Jg= - - requirement - identifier "math.cpython-311-darwin" and anchor apple generic and certificate 1[field.1.2.840.113635.100.6.2.6] /* exists */ and certificate leaf[field.1.2.840.113635.100.6.1.13] /* exists */ and certificate leaf[subject.OU] = ZQN244GMTD - - Frameworks/lib-dynload/pyexpat.cpython-311-darwin.so - - cdhash - - wCvLfssi5QJEw/8NnASa9k5IhDw= - - requirement - identifier "pyexpat.cpython-311-darwin" and anchor apple generic and certificate 1[field.1.2.840.113635.100.6.2.6] /* exists */ and certificate leaf[field.1.2.840.113635.100.6.1.13] /* exists */ and certificate leaf[subject.OU] = ZQN244GMTD - - Frameworks/lib-dynload/resource.cpython-311-darwin.so - - cdhash - - 9Uf/mb4y7Y2K3jC0TpT55m5rD4g= - - requirement - identifier "resource.cpython-311-darwin" and anchor apple generic and certificate 1[field.1.2.840.113635.100.6.2.6] /* exists */ and certificate leaf[field.1.2.840.113635.100.6.1.13] /* exists */ and certificate leaf[subject.OU] = ZQN244GMTD - - Frameworks/lib-dynload/select.cpython-311-darwin.so - - cdhash - - bp80lEsUnzkBfBPXujY2lwAFylg= - - requirement - identifier "select.cpython-311-darwin" and anchor apple generic and certificate 1[field.1.2.840.113635.100.6.2.6] /* exists */ and certificate leaf[field.1.2.840.113635.100.6.1.13] /* exists */ and certificate leaf[subject.OU] = ZQN244GMTD - - Frameworks/lib-dynload/termios.cpython-311-darwin.so - - cdhash - - uhXx4nr9PYxtLQI1MRfZlMkUl6g= - - requirement - identifier "termios.cpython-311-darwin" and anchor apple generic and certificate 1[field.1.2.840.113635.100.6.2.6] /* exists */ and certificate leaf[field.1.2.840.113635.100.6.1.13] /* exists */ and certificate leaf[subject.OU] = ZQN244GMTD - - Frameworks/lib-dynload/unicodedata.cpython-311-darwin.so - - cdhash - - LlGTLpWCJ0uFY4omEfZHkB5N2hw= - - requirement - identifier "unicodedata.cpython-311-darwin" and anchor apple generic and certificate 1[field.1.2.840.113635.100.6.2.6] /* exists */ and certificate leaf[field.1.2.840.113635.100.6.1.13] /* exists */ and certificate leaf[subject.OU] = ZQN244GMTD - - Frameworks/lib-dynload/zlib.cpython-311-darwin.so - - cdhash - - n5WmiLtWUnu3a2gV2tvW2yW4l7I= - - requirement - identifier "zlib.cpython-311-darwin" and anchor apple generic and certificate 1[field.1.2.840.113635.100.6.2.6] /* exists */ and certificate leaf[field.1.2.840.113635.100.6.1.13] /* exists */ and certificate leaf[subject.OU] = ZQN244GMTD - - Frameworks/libcrypto.3.dylib - - cdhash - - bmegX+B7PeaeEGpFI5BDVptfecI= - - requirement - identifier "libcrypto.3" and anchor apple generic and certificate 1[field.1.2.840.113635.100.6.2.6] /* exists */ and certificate leaf[field.1.2.840.113635.100.6.1.13] /* exists */ and certificate leaf[subject.OU] = ZQN244GMTD - - Frameworks/liblzma.5.dylib - - cdhash - - 1FRS30QBmju5vzIHqDNcw1OYULk= - - requirement - identifier "liblzma.5" and anchor apple generic and certificate 1[field.1.2.840.113635.100.6.2.6] /* exists */ and certificate leaf[field.1.2.840.113635.100.6.1.13] /* exists */ and certificate leaf[subject.OU] = ZQN244GMTD - - Frameworks/libmpdec.4.dylib - - cdhash - - fm06OLkQd4GUI2GhR3ynkD0leiY= - - requirement - identifier "libmpdec.4" and anchor apple generic and certificate 1[field.1.2.840.113635.100.6.2.6] /* exists */ and certificate leaf[field.1.2.840.113635.100.6.1.13] /* exists */ and certificate leaf[subject.OU] = ZQN244GMTD - - Frameworks/libssl.3.dylib - - cdhash - - ZD9iKktH53KOE7Ky/cyQmFhYnxA= - - requirement - identifier "libssl.3" and anchor apple generic and certificate 1[field.1.2.840.113635.100.6.2.6] /* exists */ and certificate leaf[field.1.2.840.113635.100.6.1.13] /* exists */ and certificate leaf[subject.OU] = ZQN244GMTD - - Resources/Python - - symlink - Python.framework/Versions/3.11/Python - - Resources/Python.framework - - symlink - ../Frameworks/Python.framework - - Resources/base_library.zip - - hash2 - - LM2oh/dWfTQvy0sewywdjHjTxh73eG2ySkklFF7SC/4= - - - Resources/lib-dynload - - symlink - ../Frameworks/lib-dynload - - Resources/libcrypto.3.dylib - - symlink - ../Frameworks/libcrypto.3.dylib - - Resources/liblzma.5.dylib - - symlink - ../Frameworks/liblzma.5.dylib - - Resources/libmpdec.4.dylib - - symlink - ../Frameworks/libmpdec.4.dylib - - Resources/libssl.3.dylib - - symlink - ../Frameworks/libssl.3.dylib - - Resources/moragents.icns - - hash2 - - AXbbYYlsdnXtGQLSAQiKk4a1WD/JMby6OiXtkN/AmjI= - - - - rules - - ^Resources/ - - ^Resources/.*\.lproj/ - - optional - - weight - 1000 - - ^Resources/.*\.lproj/locversion.plist$ - - omit - - weight - 1100 - - ^Resources/Base\.lproj/ - - weight - 1010 - - ^version.plist$ - - - rules2 - - .*\.dSYM($|/) - - weight - 11 - - ^(.*/)?\.DS_Store$ - - omit - - weight - 2000 - - ^(Frameworks|SharedFrameworks|PlugIns|Plug-ins|XPCServices|Helpers|MacOS|Library/(Automator|Spotlight|LoginItems))/ - - nested - - weight - 10 - - ^.* - - ^Info\.plist$ - - omit - - weight - 20 - - ^PkgInfo$ - - omit - - weight - 20 - - ^Resources/ - - weight - 20 - - ^Resources/.*\.lproj/ - - optional - - weight - 1000 - - ^Resources/.*\.lproj/locversion.plist$ - - omit - - weight - 1100 - - ^Resources/Base\.lproj/ - - weight - 1010 - - ^[^/]+$ - - nested - - weight - 10 - - ^embedded\.provisionprofile$ - - weight - 20 - - ^version\.plist$ - - weight - 20 - - - - diff --git a/build_assets/macOS/Packaging_Instructions_macOS.md b/build_assets/macOS/Packaging_Instructions_macOS.md index fe97b24..5cfb715 100644 --- a/build_assets/macOS/Packaging_Instructions_macOS.md +++ b/build_assets/macOS/Packaging_Instructions_macOS.md @@ -15,7 +15,7 @@ NOTE: you will need to use your own codesigning identity if you intend to distri 2. Under the Packages, either edit the existing package or create a new one. Set "Identifier" to com.morpheus.pkg.MORAgents, set the Payload to have MORAgents.app under /Applications, then under Scripts, add the preinstall.sh and postinstall.sh from the downloaded files.\ 3. Create a new Package, Set "Identifier" to com.morpheus.pkg.DockerDesktop, then under Payload, add the Docker Desktop Mac Install under /Applications\ 4. Create a new Package, Set "Identifier" to com.morpheus.pkg.Ollama, then under preinstall scripts, add the preinstall_ollama.sh script from the downloaded files.\ - 5. Navigate to Project, then in Presentation click the topright dropdown and select Introduction choose the welcome.html file, add a License section and choose license.html. + 5. Navigate to Project, then in Presentation click the topright dropdown and select Introduction choose the welcome.html file, add a License section and choose license.html. 6. In the upmost toolbar click Build -> Build to generate a .pkg file in the directory you saved the MORagents Packages package --- @@ -27,17 +27,17 @@ Future usage only requires you to run MORagents from your searchbar. --- ## Signing -```sh +```sh productsign --sign "Developer ID Installer: Liquid Tensor LLC (ZQN244GMTD)" MORagents.pkg MORagents010-[apple\|intel].pkg ``` -## Notarize -```sh +## Notarize +```sh xcrun notarytool submit MORagents020-[apple\|intel].pkg --keychain-profile "NotaryProfile" --wait ``` ## Staple -```sh +```sh xcrun stapler staple MORagents020-[apple\|intel].pkg ``` @@ -47,7 +47,7 @@ xcrun stapler staple MORagents020-[apple\|intel].pkg ### Verify notarization ```sh -xcrun notarytool info "" --keychain-profile "NotaryProfile" +xcrun notarytool info "" --keychain-profile "NotaryProfile" ``` ### Verify codesign @@ -55,4 +55,4 @@ xcrun notarytool info "" --keychain-profile "NotaryProfile" codesign --verify --verbose=4 MORagents.app codesign -dv --verbose=4 MORagents.app -``` \ No newline at end of file +``` diff --git a/build_assets/macOS/README_MACOS_DEV_BUILD.md b/build_assets/macOS/README_MACOS_DEV_BUILD.md index 223cea9..29d355c 100644 --- a/build_assets/macOS/README_MACOS_DEV_BUILD.md +++ b/build_assets/macOS/README_MACOS_DEV_BUILD.md @@ -16,7 +16,7 @@ You may instead simply download the [pre-built app](../../README.md) 3. Build Docker Image for Local Agent Execution ```shell -For ARM (M1, M2, M3) +For ARM (M1, M2, M3) $ docker-compose -f submodules/moragents_dockers/docker-compose-apple.yml up For Intel (x86_64) @@ -38,7 +38,7 @@ For Intel (x86_64) # If you have issues, try python -m PyInstaller --windowed --name="MORagents" --icon="images/moragents.icns" --osx-entitlements-file "build_assets/macOS/MORagents.entitlements" main.py -6. Install Application +6. Install Application ```shell $ cp dist/MORagents.app /Applications ``` diff --git a/build_assets/macOS/license.html b/build_assets/macOS/license.html index 2d8999a..f50d127 100644 --- a/build_assets/macOS/license.html +++ b/build_assets/macOS/license.html @@ -53,4 +53,4 @@

License Agreement

By clicking Continue, installing, and/or using the MORagents software you accept the terms of the License agreement.

- \ No newline at end of file + diff --git a/build_assets/macOS/postinstall.sh b/build_assets/macOS/postinstall.sh index e6c4e53..68cced5 100644 --- a/build_assets/macOS/postinstall.sh +++ b/build_assets/macOS/postinstall.sh @@ -55,12 +55,12 @@ start_time=$(date +%s) while ! is_app_running "Docker Desktop"; do current_time=$(date +%s) elapsed_time=$((current_time - start_time)) - + if [ $elapsed_time -ge $timeout ]; then log_message "Warning: Docker Desktop did not start within the specified timeout." break fi - + sleep 5 done @@ -82,4 +82,4 @@ else fi log_message "Post-install script completed." -exit 0 \ No newline at end of file +exit 0 diff --git a/build_assets/macOS/postinstall_intel.sh b/build_assets/macOS/postinstall_intel.sh index 0dab78e..c78a2e2 100644 --- a/build_assets/macOS/postinstall_intel.sh +++ b/build_assets/macOS/postinstall_intel.sh @@ -61,12 +61,12 @@ start_time=$(date +%s) while ! is_app_running "Docker Desktop"; do current_time=$(date +%s) elapsed_time=$((current_time - start_time)) - + if [ $elapsed_time -ge $timeout ]; then log_message "Warning: Docker Desktop did not start within the specified timeout." break fi - + sleep 5 done @@ -88,4 +88,4 @@ else fi log_message "Post-install script completed." -exit 0 \ No newline at end of file +exit 0 diff --git a/build_assets/macOS/preinstall_docker.sh b/build_assets/macOS/preinstall_docker.sh index f110bbd..57b541a 100644 --- a/build_assets/macOS/preinstall_docker.sh +++ b/build_assets/macOS/preinstall_docker.sh @@ -52,4 +52,4 @@ log_message "Cleaning up..." rm -f "$DOCKER_DMG" log_message "Docker preinstall script completed successfully." -exit 0 \ No newline at end of file +exit 0 diff --git a/build_assets/macOS/preinstall_docker_intel.sh b/build_assets/macOS/preinstall_docker_intel.sh index 544f0c8..1ef67c5 100644 --- a/build_assets/macOS/preinstall_docker_intel.sh +++ b/build_assets/macOS/preinstall_docker_intel.sh @@ -58,4 +58,4 @@ log_message "Cleaning up..." rm -f "$DOCKER_DMG" log_message "Docker preinstall script completed successfully." -exit 0 \ No newline at end of file +exit 0 diff --git a/config.py b/config.py index 1738205..907d0c2 100644 --- a/config.py +++ b/config.py @@ -1,5 +1,6 @@ import os import sys + from utils.host_utils import get_os_and_arch os_name, arch = get_os_and_arch() @@ -8,28 +9,29 @@ repo_root = os.path.dirname(__file__) elif os_name == "Windows": # Run as bundled executable if condition is met, else run as regular Python script - repo_root = sys._MEIPASS if getattr(sys, 'frozen', False) else os.path.dirname(__file__) + repo_root = sys._MEIPASS if getattr(sys, "frozen", False) else os.path.dirname(__file__) elif os_name == "Linux": repo_root = os.path.dirname(__file__) else: raise RuntimeError(f"Unsupported OS: {os_name}") + class AgentDockerConfig: MACOS_APPLE_IMAGE_NAMES = [ "lachsbagel/moragents_dockers-nginx:apple-0.2.0", - "lachsbagel/moragents_dockers-agents:apple-0.2.0" + "lachsbagel/moragents_dockers-agents:apple-0.2.0", ] MACOS_INTEL_IMAGE_NAMES = [ "lachsbagel/moragents_dockers-nginx:amd64-0.2.0", - "lachsbagel/moragents_dockers-agents:amd64-0.2.0" + "lachsbagel/moragents_dockers-agents:amd64-0.2.0", ] WINDOWS_IMAGE_NAMES = [ "lachsbagel/moragents_dockers-nginx:amd64-0.2.0", - "lachsbagel/moragents_dockers-agents:amd64-0.2.0" + "lachsbagel/moragents_dockers-agents:amd64-0.2.0", ] LINUX_IMAGE_NAMES = [ # TODO, may need linux specific tagged images "lachsbagel/moragents_dockers-nginx:amd64-0.2.0", - "lachsbagel/moragents_dockers-agents:amd64-0.2.0" + "lachsbagel/moragents_dockers-agents:amd64-0.2.0", ] @staticmethod @@ -45,6 +47,7 @@ def get_current_image_names(): else: raise RuntimeError(f"Unsupported OS: {os_name}") + class AgentDockerConfigDeprecate: OLD_IMAGE_NAMES = [ "morpheus/price_fetcher_agent:latest", @@ -57,5 +60,5 @@ class AgentDockerConfigDeprecate: "lachsbagel/moragents_dockers-nginx:apple-0.1.0", "lachsbagel/moragents_dockers-agents:apple-0.1.0", "lachsbagel/moragents_dockers-nginx:amd64-0.1.0", - "lachsbagel/moragents_dockers-agents:amd64-0.1.0" + "lachsbagel/moragents_dockers-agents:amd64-0.1.0", ] diff --git a/main.py b/main.py index e69c7fb..148f39e 100644 --- a/main.py +++ b/main.py @@ -1,12 +1,12 @@ -import time import logging +import time import webbrowser +from runtime_setup_linux import main as linux_setup from runtime_setup_macos import main as macos_setup from runtime_setup_windows import main as windows_setup -from runtime_setup_linux import main as linux_setup -from utils.logger_config import setup_logger from utils.host_utils import get_os_and_arch +from utils.logger_config import setup_logger # Configure logging logger = setup_logger(__name__) diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..8d47df5 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,26 @@ +[tool.black] +line-length = 100 +include = '\.pyi?$' +exclude = ''' +/( + \.git + | \.hg + | \.tox + | \.venv + | _build + | buck-out + | build + | dist + | \.eggs + | venv + | .venv +)/ +''' + +[tool.isort] +profile = "black" +multi_line_output = 3 +include_trailing_comma = true +force_grid_wrap = 0 +use_parentheses = true +line_length = 100 diff --git a/requirements.txt b/requirements.txt index 62809e8..1d41ac5 100644 --- a/requirements.txt +++ b/requirements.txt @@ -8,3 +8,14 @@ cdp-sdk torch safety bandit + +# Linting dependencies +pre-commit==3.5.0 +flake8==7.0.0 +black==24.2.0 +isort==5.13.2 +types-requests==2.31.0.20240311 +types-setuptools==69.2.0.20240316 +flake8-bugbear==24.2.6 +flake8-comprehensions==3.14.0 +flake8-docstrings==1.7.0 diff --git a/runtime_setup_linux.py b/runtime_setup_linux.py index 3439dd4..e1484f6 100644 --- a/runtime_setup_linux.py +++ b/runtime_setup_linux.py @@ -2,16 +2,17 @@ import shutil import subprocess -from utils.logger_config import setup_logger from config import AgentDockerConfig, AgentDockerConfigDeprecate +from utils.logger_config import setup_logger logger = setup_logger(__name__) + def get_docker_path(): docker_paths = [ - '/usr/bin/docker', # Common Linux path - '/usr/local/bin/docker', # Alternative Linux path - shutil.which('docker') + "/usr/bin/docker", # Common Linux path + "/usr/local/bin/docker", # Alternative Linux path + shutil.which("docker"), ] for docker_path in docker_paths: if docker_path and os.path.exists(docker_path): @@ -20,16 +21,22 @@ def get_docker_path(): logger.error("Docker executable not found in PATH.") return None + def check_docker_installed(docker_path): try: - subprocess.run([docker_path, "--version"], - check=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) + subprocess.run( + [docker_path, "--version"], + check=True, + stdout=subprocess.DEVNULL, + stderr=subprocess.DEVNULL, + ) logger.info(f"Docker was found at {docker_path}") return True except (subprocess.CalledProcessError, TypeError) as e: logger.error(f"Error checking Docker installation: {str(e)}") return False + def delete_docker_image(docker_path, image_name): try: # List all images @@ -49,37 +56,51 @@ def delete_docker_image(docker_path, image_name): except subprocess.CalledProcessError as e: logger.warning(f"Error deleting image: {e}") + def list_containers_for_image(docker_path, image_name): try: output = subprocess.check_output( - [docker_path, "ps", "-a", "--filter", f"ancestor={image_name}", "--format", "{{.ID}}"]) + [docker_path, "ps", "-a", "--filter", f"ancestor={image_name}", "--format", "{{.ID}}"] + ) containers = output.decode().strip().split("\n") return [container for container in containers if container] except subprocess.CalledProcessError as e: logger.error(f"Failed to list containers for image '{image_name}': {e}") return [] + def remove_container(docker_path, container): try: - subprocess.run([docker_path, "rm", "-f", container], check=True, stdout=subprocess.DEVNULL, - stderr=subprocess.DEVNULL) + subprocess.run( + [docker_path, "rm", "-f", container], + check=True, + stdout=subprocess.DEVNULL, + stderr=subprocess.DEVNULL, + ) except subprocess.CalledProcessError as e: logger.error(f"Failed to remove container '{container}': {e}") + def docker_image_present_on_host(docker_path, image_name): try: - subprocess.run([docker_path, "inspect", image_name], check=True, stdout=subprocess.DEVNULL, - stderr=subprocess.DEVNULL) + subprocess.run( + [docker_path, "inspect", image_name], + check=True, + stdout=subprocess.DEVNULL, + stderr=subprocess.DEVNULL, + ) return True except (subprocess.CalledProcessError, TypeError) as e: return False + def remove_containers_for_image(docker_path, image_name): containers = list_containers_for_image(docker_path, image_name) for container in containers: remove_container(docker_path, container) logger.info(f"Removed container '{container}' for image '{image_name}'") + def remove_containers_by_name(docker_path, container_name): try: list_command = [docker_path, "ps", "-a", "--format", "{{.Names}}"] @@ -95,12 +116,14 @@ def remove_containers_by_name(docker_path, container_name): except subprocess.CalledProcessError as e: logger.error(f"Error removing container '{container_name}': {str(e)}") + def migration_remove_old_images(docker_path): for image_name in AgentDockerConfigDeprecate.OLD_IMAGE_NAMES: if docker_image_present_on_host(docker_path, image_name): delete_docker_image(docker_path, image_name) logger.info(f"Deleted image '{image_name} from previous release") + def pull_docker_images(docker_path): for image in AgentDockerConfig.get_current_image_names(): try: @@ -110,18 +133,22 @@ def pull_docker_images(docker_path): logger.error(f"Failed to pull image {image}: {e}") raise + def start_ollama_server(): - ollama_path = '/usr/local/bin/ollama' # This path might need to be adjusted for Linux + ollama_path = "/usr/local/bin/ollama" # This path might need to be adjusted for Linux try: # Start Ollama server logger.info("Starting Ollama server...") - subprocess.Popen([ollama_path, "serve"], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) + subprocess.Popen( + [ollama_path, "serve"], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL + ) logger.info("Ollama server started successfully.") except Exception as e: logger.info("Failed to start Ollama server.") logger.error(f"Failed to start Ollama server: {e}") + def docker_setup(): docker_path = get_docker_path() logger.info(f"Docker path: {docker_path}") @@ -144,18 +171,41 @@ def docker_setup(): pull_docker_images(docker_path) # Spin up Agent container - subprocess.run([ - docker_path, "run", "-d", "--name", "agents", - "-p", "8080:5000", "--restart", "always", - "-v", "/var/lib/agents:/var/lib/agents", "-v", "/app/src:/app/src", # Adjusted volume paths for Linux - AgentDockerConfig.get_current_image_names()[1] # agents image - ], check=True) + subprocess.run( + [ + docker_path, + "run", + "-d", + "--name", + "agents", + "-p", + "8080:5000", + "--restart", + "always", + "-v", + "/var/lib/agents:/var/lib/agents", + "-v", + "/app/src:/app/src", # Adjusted volume paths for Linux + AgentDockerConfig.get_current_image_names()[1], # agents image + ], + check=True, + ) # Spin up Nginx container - subprocess.run([ - docker_path, "run", "-d", "--name", "nginx", "-p", "3333:80", - AgentDockerConfig.get_current_image_names()[0] # nginx image - ], check=True) + subprocess.run( + [ + docker_path, + "run", + "-d", + "--name", + "nginx", + "-p", + "3333:80", + AgentDockerConfig.get_current_image_names()[0], # nginx image + ], + check=True, + ) + def main(): # main() called every time the app is opened (from main.py). Put all app open code here. @@ -163,5 +213,6 @@ def main(): start_ollama_server() docker_setup() + if __name__ == "__main__": - main() \ No newline at end of file + main() diff --git a/runtime_setup_macos.py b/runtime_setup_macos.py index fda8f2e..26cfc1f 100644 --- a/runtime_setup_macos.py +++ b/runtime_setup_macos.py @@ -2,13 +2,17 @@ import shutil import subprocess -from utils.logger_config import setup_logger from config import AgentDockerConfig, AgentDockerConfigDeprecate +from utils.logger_config import setup_logger logger = setup_logger(__name__) + def get_docker_path(): - docker_paths = ['/Applications/Docker.app/Contents/Resources/bin/docker', shutil.which('docker')] + docker_paths = [ + "/Applications/Docker.app/Contents/Resources/bin/docker", + shutil.which("docker"), + ] for docker_path in docker_paths: if os.path.exists(docker_path): return docker_path @@ -19,14 +23,19 @@ def get_docker_path(): def check_docker_installed(docker_path): try: - subprocess.run([docker_path, "--version"], - check=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) + subprocess.run( + [docker_path, "--version"], + check=True, + stdout=subprocess.DEVNULL, + stderr=subprocess.DEVNULL, + ) logger.info(f"Docker was found at {docker_path}") return True except (subprocess.CalledProcessError, TypeError) as e: logger.error(f"Error checking Docker installation: {str(e)}") return False + def delete_docker_image(docker_path, image_name): try: # List all images @@ -46,37 +55,51 @@ def delete_docker_image(docker_path, image_name): except subprocess.CalledProcessError as e: logger.warning(f"Error deleting image: {e}") + def list_containers_for_image(docker_path, image_name): try: output = subprocess.check_output( - [docker_path, "ps", "-a", "--filter", f"ancestor={image_name}", "--format", "{{.ID}}"]) + [docker_path, "ps", "-a", "--filter", f"ancestor={image_name}", "--format", "{{.ID}}"] + ) containers = output.decode().strip().split("\n") return [container for container in containers if container] except subprocess.CalledProcessError as e: logger.error(f"Failed to list containers for image '{image_name}': {e}") return [] + def remove_container(docker_path, container): try: - subprocess.run([docker_path, "rm", "-f", container], check=True, stdout=subprocess.DEVNULL, - stderr=subprocess.DEVNULL) + subprocess.run( + [docker_path, "rm", "-f", container], + check=True, + stdout=subprocess.DEVNULL, + stderr=subprocess.DEVNULL, + ) except subprocess.CalledProcessError as e: logger.error(f"Failed to remove container '{container}': {e}") + def docker_image_present_on_host(docker_path, image_name): try: - subprocess.run([docker_path, "inspect", image_name], check=True, stdout=subprocess.DEVNULL, - stderr=subprocess.DEVNULL) + subprocess.run( + [docker_path, "inspect", image_name], + check=True, + stdout=subprocess.DEVNULL, + stderr=subprocess.DEVNULL, + ) return True except (subprocess.CalledProcessError, TypeError) as e: return False + def remove_containers_for_image(docker_path, image_name): containers = list_containers_for_image(docker_path, image_name) for container in containers: remove_container(docker_path, container) logger.info(f"Removed container '{container}' for image '{image_name}'") + def remove_containers_by_name(docker_path, container_name): try: list_command = [docker_path, "ps", "-a", "--format", "{{.Names}}"] @@ -92,12 +115,14 @@ def remove_containers_by_name(docker_path, container_name): except subprocess.CalledProcessError as e: logger.error(f"Error removing container '{container_name}': {str(e)}") + def migration_remove_old_images(docker_path): for image_name in AgentDockerConfigDeprecate.OLD_IMAGE_NAMES: if docker_image_present_on_host(docker_path, image_name): delete_docker_image(docker_path, image_name) logger.info(f"Deleted image '{image_name} from previous release") + def pull_docker_images(docker_path): for image in AgentDockerConfig.get_current_image_names(): try: @@ -107,18 +132,22 @@ def pull_docker_images(docker_path): logger.error(f"Failed to pull image {image}: {e}") raise + def start_ollama_server(): - ollama_path = '/usr/local/bin/ollama' + ollama_path = "/usr/local/bin/ollama" try: # Start Ollama server logger.info("Starting Ollama server...") - subprocess.Popen([ollama_path, "serve"], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) + subprocess.Popen( + [ollama_path, "serve"], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL + ) logger.info("Ollama server started successfully.") except Exception as e: logger.info("Failed to start Ollama server.") logger.error(f"Failed to start Ollama server: {e}") + def docker_setup(): docker_path = get_docker_path() logger.info(f"Docker path: {docker_path}") @@ -141,18 +170,41 @@ def docker_setup(): pull_docker_images(docker_path) # Spin up Agent container - subprocess.run([ - docker_path, "run", "-d", "--name", "agents", - "-p", "8080:5000", "--restart", "always", - "-v", "/var/lib/agents", "-v", "/app/src", - AgentDockerConfig.get_current_image_names()[1] # agents image - ], check=True) + subprocess.run( + [ + docker_path, + "run", + "-d", + "--name", + "agents", + "-p", + "8080:5000", + "--restart", + "always", + "-v", + "/var/lib/agents", + "-v", + "/app/src", + AgentDockerConfig.get_current_image_names()[1], # agents image + ], + check=True, + ) # Spin up Nginx container - subprocess.run([ - docker_path, "run", "-d", "--name", "nginx", "-p", "3333:80", - AgentDockerConfig.get_current_image_names()[0] # nginx image - ], check=True) + subprocess.run( + [ + docker_path, + "run", + "-d", + "--name", + "nginx", + "-p", + "3333:80", + AgentDockerConfig.get_current_image_names()[0], # nginx image + ], + check=True, + ) + def main(): # main() called every time the app is opened (from main.py). Put all app open code here. @@ -160,5 +212,6 @@ def main(): start_ollama_server() docker_setup() + if __name__ == "__main__": - main() \ No newline at end of file + main() diff --git a/runtime_setup_windows.py b/runtime_setup_windows.py index 33de5d9..43024fc 100644 --- a/runtime_setup_windows.py +++ b/runtime_setup_windows.py @@ -1,20 +1,24 @@ import subprocess import time -from utils.logger_config import setup_logger from config import AgentDockerConfig, AgentDockerConfigDeprecate +from utils.logger_config import setup_logger logger = setup_logger(__name__) docker_path = "docker" + def check_docker_installed(): try: - subprocess.run([docker_path, "--version"], check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + subprocess.run( + [docker_path, "--version"], check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE + ) return True except (subprocess.CalledProcessError, FileNotFoundError): return False + def start_docker(): try: subprocess.run(["C:\\Program Files\\Docker\\Docker\\Docker Desktop.exe"]) @@ -33,6 +37,7 @@ def start_docker(): logger.info("Waiting for Docker engine to start...") time.sleep(2) + def delete_docker_image(image_name): try: list_command = [docker_path, "images", "--format", "{{.Repository}}:{{.Tag}}"] @@ -46,37 +51,51 @@ def delete_docker_image(image_name): except subprocess.CalledProcessError as e: logger.warning(f"Error deleting image: {e}") + def list_containers_for_image(image_name): try: output = subprocess.check_output( - [docker_path, "ps", "-a", "--filter", f"ancestor={image_name}", "--format", "{{.ID}}"]) + [docker_path, "ps", "-a", "--filter", f"ancestor={image_name}", "--format", "{{.ID}}"] + ) containers = output.decode().strip().split("\n") return [container for container in containers if container] except subprocess.CalledProcessError as e: logger.error(f"Failed to list containers for image '{image_name}': {e}") return [] + def remove_container(container): try: - subprocess.run([docker_path, "rm", "-f", container], check=True, stdout=subprocess.DEVNULL, - stderr=subprocess.DEVNULL) + subprocess.run( + [docker_path, "rm", "-f", container], + check=True, + stdout=subprocess.DEVNULL, + stderr=subprocess.DEVNULL, + ) except subprocess.CalledProcessError as e: logger.error(f"Failed to remove container '{container}': {e}") + def docker_image_present_on_host(image_name): try: - subprocess.run([docker_path, "inspect", image_name], check=True, stdout=subprocess.DEVNULL, - stderr=subprocess.DEVNULL) + subprocess.run( + [docker_path, "inspect", image_name], + check=True, + stdout=subprocess.DEVNULL, + stderr=subprocess.DEVNULL, + ) return True except (subprocess.CalledProcessError, TypeError): return False + def remove_containers_for_image(image_name): containers = list_containers_for_image(image_name) for container in containers: remove_container(container) logger.info(f"Removed container '{container}' for image '{image_name}'") + def remove_containers_by_name(container_name): try: list_command = [docker_path, "ps", "-a", "--format", "{{.Names}}"] @@ -92,12 +111,14 @@ def remove_containers_by_name(container_name): except subprocess.CalledProcessError as e: logger.error(f"Error removing container '{container_name}': {str(e)}") + def migration_remove_old_images(): for image_name in AgentDockerConfigDeprecate.OLD_IMAGE_NAMES: if docker_image_present_on_host(image_name): delete_docker_image(image_name) logger.info(f"Deleted image '{image_name}' from previous release") + def pull_docker_images(): for image_name in AgentDockerConfig.get_current_image_names(): try: @@ -107,15 +128,18 @@ def pull_docker_images(): logger.error(f"Failed to pull image {image_name}: {e}") raise + def start_ollama_server(): ollama_path = "ollama" try: print(f"Attempting to start Ollama server using: {ollama_path}") - subprocess.Popen([ollama_path, "serve"], - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, - creationflags=subprocess.CREATE_NO_WINDOW) + subprocess.Popen( + [ollama_path, "serve"], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + creationflags=subprocess.CREATE_NO_WINDOW, + ) print("Ollama server started successfully.") except Exception as e: print(f"Failed to start Ollama server: {e}") @@ -140,23 +164,47 @@ def docker_setup(): remove_containers_by_name("nginx") # Spin up Agent container - subprocess.run([ - docker_path, "run", "-d", "--name", "agents", - "-p", "8080:5000", "--restart", "always", - "-v", "/var/lib/agents", "-v", "/app/src", - AgentDockerConfig.get_current_image_names()[1] # agents image - ], check=True) + subprocess.run( + [ + docker_path, + "run", + "-d", + "--name", + "agents", + "-p", + "8080:5000", + "--restart", + "always", + "-v", + "/var/lib/agents", + "-v", + "/app/src", + AgentDockerConfig.get_current_image_names()[1], # agents image + ], + check=True, + ) # Spin up Nginx container - subprocess.run([ - docker_path, "run", "-d", "--name", "nginx", "-p", "3333:80", - AgentDockerConfig.get_current_image_names()[0] # nginx image - ], check=True) + subprocess.run( + [ + docker_path, + "run", + "-d", + "--name", + "nginx", + "-p", + "3333:80", + AgentDockerConfig.get_current_image_names()[0], # nginx image + ], + check=True, + ) + def main(): # main() called every time the app is opened (from main.py). Put all app open code here. start_ollama_server() docker_setup() + if __name__ == "__main__": - docker_setup() \ No newline at end of file + docker_setup() diff --git a/submodules/moragents_dockers/LICENSE.md b/submodules/moragents_dockers/LICENSE.md index 5885ae8..149c400 100644 --- a/submodules/moragents_dockers/LICENSE.md +++ b/submodules/moragents_dockers/LICENSE.md @@ -1,5 +1,5 @@ -This repository is provided under the following MIT License subject to -it's continued status as a contribution to the Morpheus project with +This repository is provided under the following MIT License subject to +it's continued status as a contribution to the Morpheus project with weights as accepted in the initial contribution pull request. MIT License diff --git a/submodules/moragents_dockers/README.md b/submodules/moragents_dockers/README.md index e07693e..d8b3780 100644 --- a/submodules/moragents_dockers/README.md +++ b/submodules/moragents_dockers/README.md @@ -237,3 +237,50 @@ The `Delegator` will automatically: 1. **Ensure the `Delegator` can properly route requests** to the new agent. 2. **Test the agent's functionality** through the chat interface. + + +### 6. Code Quality & Linting + +We use several tools to maintain code quality: + +#### Python (Root & Agents) +1. **black** - Code formatting +2. **isort** - Import sorting +3. **flake8** - Style guide enforcement + +#### Frontend +1. **eslint** - JavaScript/TypeScript linting + +--- + +#### Installing & Running Linters + +Linters will automatically run on staged files when you commit changes. You can also run them manually: + +```bash +# In root directory +pip install -r requirements.txt +pre-commit install + +# Run all pre-commit hooks on staged files +pre-commit run + +# Run specific hooks on staged files +pre-commit run black +pre-commit run isort +pre-commit run flake8 + +# Run on specific files +pre-commit run --files path/to/file.py + +# Run all hooks on all files (useful for initial setup) +pre-commit run --all-files +``` + +#### Skipping Hooks + +In rare cases where you need to skip pre-commit hooks (not recommended): + +```bash +git commit -m "message" --no-verify +``` diff --git a/submodules/moragents_dockers/agents/Dockerfile b/submodules/moragents_dockers/agents/Dockerfile index 14b0556..ec3801e 100644 --- a/submodules/moragents_dockers/agents/Dockerfile +++ b/submodules/moragents_dockers/agents/Dockerfile @@ -19,4 +19,4 @@ COPY . . EXPOSE 5000 # Run the application using uvicorn with auto-reload enabled -CMD ["uvicorn", "src.app:app", "--host", "0.0.0.0", "--port", "5000", "--reload"] \ No newline at end of file +CMD ["uvicorn", "src.app:app", "--host", "0.0.0.0", "--port", "5000", "--reload"] diff --git a/submodules/moragents_dockers/agents/Dockerfile-apple b/submodules/moragents_dockers/agents/Dockerfile-apple index 5a1e59b..86d8989 100644 --- a/submodules/moragents_dockers/agents/Dockerfile-apple +++ b/submodules/moragents_dockers/agents/Dockerfile-apple @@ -41,4 +41,4 @@ COPY . . EXPOSE 5000 # Run the application using uvicorn with auto-reload enabled -CMD ["uvicorn", "src.app:app", "--host", "0.0.0.0", "--port", "5000", "--reload"] \ No newline at end of file +CMD ["uvicorn", "src.app:app", "--host", "0.0.0.0", "--port", "5000", "--reload"] diff --git a/submodules/moragents_dockers/agents/get-pip.py b/submodules/moragents_dockers/agents/get-pip.py index 688652d..59e5680 100644 --- a/submodules/moragents_dockers/agents/get-pip.py +++ b/submodules/moragents_dockers/agents/get-pip.py @@ -34,12 +34,12 @@ sys.exit(1) +import argparse +import importlib import os.path import pkgutil import shutil import tempfile -import argparse -import importlib from base64 import b85decode @@ -115,6 +115,7 @@ def bootstrap(tmpdir): # Execute the included pip and use it to install the latest pip and # any user-requested packages from PyPI. from pip._internal.cli.main import main as pip_entry_point + args = determine_pip_install_arguments() sys.exit(pip_entry_point(args)) diff --git a/submodules/moragents_dockers/agents/pytest.ini b/submodules/moragents_dockers/agents/pytest.ini index 72a01cc..a7f7118 100644 --- a/submodules/moragents_dockers/agents/pytest.ini +++ b/submodules/moragents_dockers/agents/pytest.ini @@ -1,3 +1,3 @@ [pytest] pythonpath = . -addopts = --import-mode=importlib -p no:warnings \ No newline at end of file +addopts = --import-mode=importlib -p no:warnings diff --git a/submodules/moragents_dockers/agents/src/agents/base_agent/agent.py b/submodules/moragents_dockers/agents/src/agents/base_agent/agent.py index 6cfd992..9d2a832 100644 --- a/submodules/moragents_dockers/agents/src/agents/base_agent/agent.py +++ b/submodules/moragents_dockers/agents/src/agents/base_agent/agent.py @@ -1,12 +1,12 @@ import json -import threading import logging -from cdp import Cdp, Wallet +import threading from datetime import datetime -from typing import Dict, Any +from typing import Any, Dict -from src.agents.base_agent.config import Config +from cdp import Cdp, Wallet from src.agents.base_agent import tools +from src.agents.base_agent.config import Config # Configure logging logging.basicConfig( @@ -51,9 +51,7 @@ def chat(self, request): else: return {"error": "Missing required parameters"}, 400 except Exception as e: - logger.error( - f"Error in chat method: {str(e)}, agent: {self.agent_info['name']}" - ) + logger.error(f"Error in chat method: {str(e)}, agent: {self.agent_info['name']}") raise e def handle_request(self, message, chain_id, wallet_address): @@ -108,9 +106,7 @@ def handle_request(self, message, chain_id, wallet_address): logger.info(f"Arguments: {args}") # Call the appropriate handler - return self.handle_function_call( - func_name, args, chain_id, wallet_address - ) + return self.handle_function_call(func_name, args, chain_id, wallet_address) else: # No function call; return the assistant's message content = choice.get("content", "") @@ -133,14 +129,10 @@ def handle_gasless_usdc_transfer(self, args, chain_id, wallet_address): toAddress = args.get("toAddress") amount = args.get("amount") if not toAddress or not amount: - logger.error( - "Missing 'toAddress' or 'amount' in gasless_usdc_transfer arguments." - ) + logger.error("Missing 'toAddress' or 'amount' in gasless_usdc_transfer arguments.") return "Error: Missing 'toAddress' or 'amount'.", "assistant", None - logger.info( - f"Initiating gasless USDC transfer to {toAddress} of amount {amount}." - ) + logger.info(f"Initiating gasless USDC transfer to {toAddress} of amount {amount}.") try: res, role = tools.send_gasless_usdc_transaction(toAddress, amount) diff --git a/submodules/moragents_dockers/agents/src/agents/base_agent/config.py b/submodules/moragents_dockers/agents/src/agents/base_agent/config.py index d283936..09f1db0 100644 --- a/submodules/moragents_dockers/agents/src/agents/base_agent/config.py +++ b/submodules/moragents_dockers/agents/src/agents/base_agent/config.py @@ -12,13 +12,14 @@ "class": "BaseAgent", "description": "If the user wants to send a transaction on Base", "name": "Base Agent", - "upload": False + "upload": False, } ] } + class Config: CDP_API_KEY = "" CDP_API_SECRET = "" - DEFAULT_ACTION = "eth_transfer" \ No newline at end of file + DEFAULT_ACTION = "eth_transfer" diff --git a/submodules/moragents_dockers/agents/src/agents/base_agent/tools.py b/submodules/moragents_dockers/agents/src/agents/base_agent/tools.py index 2449f7c..a51f1ae 100644 --- a/submodules/moragents_dockers/agents/src/agents/base_agent/tools.py +++ b/submodules/moragents_dockers/agents/src/agents/base_agent/tools.py @@ -1,18 +1,23 @@ -import requests import logging import time + +import requests +from cdp import Cdp, Transaction, Wallet from src.agents.base_agent.config import Config -from cdp import Cdp, Wallet, Transaction -logging.basicConfig(level=logging.INFO, format="%(asctime)s - %(name)s - %(levelname)s - %(message)s") +logging.basicConfig( + level=logging.INFO, format="%(asctime)s - %(name)s - %(levelname)s - %(message)s" +) logger = logging.getLogger(__name__) + class InsufficientFundsError(Exception): pass + def send_gasless_usdc_transaction(toAddress, amount): - - client = Cdp.configure('','') + + client = Cdp.configure("", "") logger.info(f"Client successfully configured: {client}") @@ -22,7 +27,7 @@ def send_gasless_usdc_transaction(toAddress, amount): logger.info(f"Wallet address: {wallet1.default_address}") eth_faucet_tx = wallet1.faucet() - + usdc_faucet_tx = wallet1.faucet(asset_id="usdc") logger.info(f"Faucet transaction successfully sent: {eth_faucet_tx}") @@ -41,13 +46,12 @@ def send_gasless_usdc_transaction(toAddress, amount): logger.info(f"Transfer transaction: {transfer}") - return { - 'success': 'Transfer transaction successful' - }, "gasless_usdc_transfer" + return {"success": "Transfer transaction successful"}, "gasless_usdc_transfer" + def send_eth_transaction(toAddress, amount): - - client = Cdp.configure('','') + + client = Cdp.configure("", "") logger.info(f"Client successfully configured: {client}") @@ -72,10 +76,7 @@ def send_eth_transaction(toAddress, amount): logger.info(f"Transfer transaction: {transfer}") - return { - 'success': 'Transfer transaction successful' - }, "eth_transfer" - + return {"success": "Transfer transaction successful"}, "eth_transfer" def get_tools(): @@ -89,11 +90,11 @@ def get_tools(): "type": "object", "properties": { "toAddress": {"type": "string", "description": "Recipient's address."}, - "amount": {"type": "string", "description": "Amount of USDC to transfer."} + "amount": {"type": "string", "description": "Amount of USDC to transfer."}, }, - "required": ["toAddress", "amount"] - } - } + "required": ["toAddress", "amount"], + }, + }, }, { "type": "function", @@ -104,10 +105,10 @@ def get_tools(): "type": "object", "properties": { "toAddress": {"type": "string", "description": "Recipient's address."}, - "amount": {"type": "string", "description": "Amount of ETH to transfer."} + "amount": {"type": "string", "description": "Amount of ETH to transfer."}, }, - "required": ["toAddress", "amount"] - } - } - } + "required": ["toAddress", "amount"], + }, + }, + }, ] diff --git a/submodules/moragents_dockers/agents/src/agents/crypto_data/README.md b/submodules/moragents_dockers/agents/src/agents/crypto_data/README.md index 65c447b..ca5c34d 100644 --- a/submodules/moragents_dockers/agents/src/agents/crypto_data/README.md +++ b/submodules/moragents_dockers/agents/src/agents/crypto_data/README.md @@ -56,7 +56,7 @@ A notebook has been provided to run test queries against the API: ### Without Docker -To use the API run +To use the API run ```python agent.py``` To use an interactive CLI prompt to test the agent run: @@ -84,4 +84,4 @@ This project uses the openhermes-2.5-mistral-7b GGUF model and performs well on The CoinGecko search API is used to find the asset that is being referenced. In case multiple matching assets are found, the agent will select the one with the largest market cap -When consuming this API as part of a larger agent, care should be taken to ensure that responses do not pass thorugh an LLM that hallucinates the number before the response is sent to the user. \ No newline at end of file +When consuming this API as part of a larger agent, care should be taken to ensure that responses do not pass thorugh an LLM that hallucinates the number before the response is sent to the user. diff --git a/submodules/moragents_dockers/agents/src/agents/crypto_data/agent.py b/submodules/moragents_dockers/agents/src/agents/crypto_data/agent.py index 4e2d29b..9d77053 100644 --- a/submodules/moragents_dockers/agents/src/agents/crypto_data/agent.py +++ b/submodules/moragents_dockers/agents/src/agents/crypto_data/agent.py @@ -50,9 +50,7 @@ def get_response(self, message): ) return response_data, "assistant" elif func_name == "get_floor_price": - response_data["data"] = tools.get_nft_floor_price_tool( - args["nft_name"] - ) + response_data["data"] = tools.get_nft_floor_price_tool(args["nft_name"]) return response_data, "assistant" elif func_name == "get_fdv": response_data["data"] = tools.get_fully_diluted_valuation_tool( @@ -65,9 +63,7 @@ def get_response(self, message): ) return response_data, "assistant" elif func_name == "get_market_cap": - response_data["data"] = tools.get_coin_market_cap_tool( - args["coin_name"] - ) + response_data["data"] = tools.get_coin_market_cap_tool(args["coin_name"]) return response_data, "assistant" else: logger.info("LLM provided a direct response without using tools") diff --git a/submodules/moragents_dockers/agents/src/agents/crypto_data/config.py b/submodules/moragents_dockers/agents/src/agents/crypto_data/config.py index 63db777..c189a55 100644 --- a/submodules/moragents_dockers/agents/src/agents/crypto_data/config.py +++ b/submodules/moragents_dockers/agents/src/agents/crypto_data/config.py @@ -13,15 +13,11 @@ class Config: PRICE_SUCCESS_MESSAGE = "The price of {coin_name} is ${price:,}" PRICE_FAILURE_MESSAGE = "Failed to retrieve price. Please enter a valid coin name." FLOOR_PRICE_SUCCESS_MESSAGE = "The floor price of {nft_name} is ${floor_price:,}" - FLOOR_PRICE_FAILURE_MESSAGE = ( - "Failed to retrieve floor price. Please enter a valid NFT name." - ) + FLOOR_PRICE_FAILURE_MESSAGE = "Failed to retrieve floor price. Please enter a valid NFT name." TVL_SUCCESS_MESSAGE = "The TVL of {protocol_name} is ${tvl:,}" TVL_FAILURE_MESSAGE = "Failed to retrieve TVL. Please enter a valid protocol name." FDV_SUCCESS_MESSAGE = "The fully diluted valuation of {coin_name} is ${fdv:,}" FDV_FAILURE_MESSAGE = "Failed to retrieve FDV. Please enter a valid coin name." MARKET_CAP_SUCCESS_MESSAGE = "The market cap of {coin_name} is ${market_cap:,}" - MARKET_CAP_FAILURE_MESSAGE = ( - "Failed to retrieve market cap. Please enter a valid coin name." - ) + MARKET_CAP_FAILURE_MESSAGE = "Failed to retrieve market cap. Please enter a valid coin name." API_ERROR_MESSAGE = "I can't seem to access the API at the moment." diff --git a/submodules/moragents_dockers/agents/src/agents/crypto_data/routes.py b/submodules/moragents_dockers/agents/src/agents/crypto_data/routes.py index d8417f5..1f347a7 100644 --- a/submodules/moragents_dockers/agents/src/agents/crypto_data/routes.py +++ b/submodules/moragents_dockers/agents/src/agents/crypto_data/routes.py @@ -1,11 +1,12 @@ import logging -from flask import Blueprint, request, jsonify +from flask import Blueprint, jsonify, request -crypto_data_agent_bp = Blueprint('crypto_data_agent', __name__) +crypto_data_agent_bp = Blueprint("crypto_data_agent", __name__) logger = logging.getLogger(__name__) -@crypto_data_agent_bp.route('/process_data', methods=['POST']) + +@crypto_data_agent_bp.route("/process_data", methods=["POST"]) def process_data(): logger.info("Data Agent: Received process_data request") data = request.get_json() diff --git a/submodules/moragents_dockers/agents/src/agents/crypto_data/tools.py b/submodules/moragents_dockers/agents/src/agents/crypto_data/tools.py index 648f8ff..4fc8306 100644 --- a/submodules/moragents_dockers/agents/src/agents/crypto_data/tools.py +++ b/submodules/moragents_dockers/agents/src/agents/crypto_data/tools.py @@ -1,8 +1,8 @@ -import requests import logging + +import requests from sklearn.feature_extraction.text import TfidfVectorizer from sklearn.metrics.pairwise import cosine_similarity - from src.agents.crypto_data.config import Config @@ -13,9 +13,7 @@ def get_most_similar(text, data): text_vector = vectorizer.transform([text]) similarity_scores = cosine_similarity(text_vector, sentence_vectors) top_indices = similarity_scores.argsort()[0][-20:] - top_matches = [ - data[item] for item in top_indices if similarity_scores[0][item] > 0.5 - ] + top_matches = [data[item] for item in top_indices if similarity_scores[0][item] > 0.5] return top_matches @@ -185,9 +183,7 @@ def get_nft_floor_price_tool(nft_name): floor_price = get_floor_price(nft_name) if floor_price is None: return Config.FLOOR_PRICE_FAILURE_MESSAGE - return Config.FLOOR_PRICE_SUCCESS_MESSAGE.format( - nft_name=nft_name, floor_price=floor_price - ) + return Config.FLOOR_PRICE_SUCCESS_MESSAGE.format(nft_name=nft_name, floor_price=floor_price) except requests.exceptions.RequestException: return Config.API_ERROR_MESSAGE @@ -199,9 +195,7 @@ def get_protocol_total_value_locked_tool(protocol_name): if tvl is None: return Config.TVL_FAILURE_MESSAGE protocol, tvl_value = list(tvl.items())[0][0], list(tvl.items())[0][1] - return Config.TVL_SUCCESS_MESSAGE.format( - protocol_name=protocol_name, tvl=tvl_value - ) + return Config.TVL_SUCCESS_MESSAGE.format(protocol_name=protocol_name, tvl=tvl_value) except requests.exceptions.RequestException: return Config.API_ERROR_MESSAGE @@ -223,9 +217,7 @@ def get_coin_market_cap_tool(coin_name): market_cap = get_market_cap(coin_name) if market_cap is None: return Config.MARKET_CAP_FAILURE_MESSAGE - return Config.MARKET_CAP_SUCCESS_MESSAGE.format( - coin_name=coin_name, market_cap=market_cap - ) + return Config.MARKET_CAP_SUCCESS_MESSAGE.format(coin_name=coin_name, market_cap=market_cap) except requests.exceptions.RequestException: return Config.API_ERROR_MESSAGE diff --git a/submodules/moragents_dockers/agents/src/agents/dca_agent/agent.py b/submodules/moragents_dockers/agents/src/agents/dca_agent/agent.py index 5a6890f..08ad094 100644 --- a/submodules/moragents_dockers/agents/src/agents/dca_agent/agent.py +++ b/submodules/moragents_dockers/agents/src/agents/dca_agent/agent.py @@ -2,7 +2,8 @@ import threading import time from datetime import datetime -from typing import Dict, Any +from typing import Any, Dict + from cdp import Wallet logger = logging.getLogger(__name__) @@ -50,19 +51,13 @@ def chat(self, request: Dict[str, Any], user_id: str) -> tuple[str, str]: next_turn_agent = self.agent_info["name"] return response, next_turn_agent except ValueError as e: - logger.error( - f"Validation error in chat: {str(e)}, agent: {self.agent_info['name']}" - ) + logger.error(f"Validation error in chat: {str(e)}, agent: {self.agent_info['name']}") raise e except Exception as e: - logger.error( - f"Unexpected error in chat: {str(e)}, agent: {self.agent_info['name']}" - ) + logger.error(f"Unexpected error in chat: {str(e)}, agent: {self.agent_info['name']}") raise e - def schedule_purchase( - self, user_id: str, token: str, amount: float, interval: int - ) -> str: + def schedule_purchase(self, user_id: str, token: str, amount: float, interval: int) -> str: """ Schedule a recurring purchase task. diff --git a/submodules/moragents_dockers/agents/src/agents/dca_agent/config.py b/submodules/moragents_dockers/agents/src/agents/dca_agent/config.py index ba75875..5a8996b 100644 --- a/submodules/moragents_dockers/agents/src/agents/dca_agent/config.py +++ b/submodules/moragents_dockers/agents/src/agents/dca_agent/config.py @@ -10,11 +10,12 @@ "class": "DCAAgent", "description": "If the user wants to set up a dollar-cost averaging strategy for crypto purchases.", "name": "DCA Agent", - "upload": False + "upload": False, } ] } + class Config: CDP_API_KEY = "cdp_api_key" diff --git a/submodules/moragents_dockers/agents/src/agents/default/agent.py b/submodules/moragents_dockers/agents/src/agents/default/agent.py index 523d2f0..418019d 100644 --- a/submodules/moragents_dockers/agents/src/agents/default/agent.py +++ b/submodules/moragents_dockers/agents/src/agents/default/agent.py @@ -25,9 +25,7 @@ def chat(self, request: ChatRequest): for agent in available_agents: if agent["name"] in selected_agent_names: human_name = agent.get("human_readable_name", agent["name"]) - selected_agents_info.append( - f"- {human_name}: {agent['description']}" - ) + selected_agents_info.append(f"- {human_name}: {agent['description']}") system_prompt = f""" You are a helpful assistant. Use the context provided to respond to the user's question. diff --git a/submodules/moragents_dockers/agents/src/agents/imagen/agent.py b/submodules/moragents_dockers/agents/src/agents/imagen/agent.py index 4a63344..e9f29d7 100644 --- a/submodules/moragents_dockers/agents/src/agents/imagen/agent.py +++ b/submodules/moragents_dockers/agents/src/agents/imagen/agent.py @@ -1,17 +1,15 @@ -import logging import base64 -import requests - +import logging from io import BytesIO -from PIL import Image +import requests +from PIL import Image from selenium import webdriver -from selenium.webdriver.common.by import By from selenium.webdriver.chrome.options import Options -from selenium.webdriver.support.ui import WebDriverWait -from selenium.webdriver.support import expected_conditions as EC from selenium.webdriver.chrome.service import Service - +from selenium.webdriver.common.by import By +from selenium.webdriver.support import expected_conditions as EC +from selenium.webdriver.support.ui import WebDriverWait from src.models.messages import ChatRequest logging.basicConfig( @@ -65,9 +63,7 @@ def _generate_with_fluxai(self, prompt): # Find textarea and enter the prompt wait = WebDriverWait(driver, 30) - textarea = wait.until( - EC.presence_of_element_located((By.TAG_NAME, "textarea")) - ) + textarea = wait.until(EC.presence_of_element_located((By.TAG_NAME, "textarea"))) textarea.clear() textarea.send_keys(prompt) @@ -157,7 +153,5 @@ def chat(self, request: ChatRequest): logger.error("Missing 'prompt' in chat request data") return {"error": "Missing parameters"}, 400 except Exception as e: - logger.error( - f"Unexpected error in chat method: {str(e)}, request: {request}" - ) + logger.error(f"Unexpected error in chat method: {str(e)}, request: {request}") raise e diff --git a/submodules/moragents_dockers/agents/src/agents/mor_claims/agent.py b/submodules/moragents_dockers/agents/src/agents/mor_claims/agent.py index 964ed7f..327d585 100644 --- a/submodules/moragents_dockers/agents/src/agents/mor_claims/agent.py +++ b/submodules/moragents_dockers/agents/src/agents/mor_claims/agent.py @@ -28,21 +28,15 @@ def _get_response(self, message, wallet_address): 0: tools.get_current_user_reward(wallet_address, 0), 1: tools.get_current_user_reward(wallet_address, 1), } - available_rewards = { - pool: amount for pool, amount in rewards.items() if amount > 0 - } + available_rewards = {pool: amount for pool, amount in rewards.items() if amount > 0} if available_rewards: selected_pool = max(available_rewards, key=available_rewards.get) self.conversation_state[wallet_address]["available_rewards"] = { selected_pool: available_rewards[selected_pool] } - self.conversation_state[wallet_address][ - "receiver_address" - ] = wallet_address - self.conversation_state[wallet_address][ - "state" - ] = "awaiting_confirmation" + self.conversation_state[wallet_address]["receiver_address"] = wallet_address + self.conversation_state[wallet_address]["state"] = "awaiting_confirmation" return ( f"You have {available_rewards[selected_pool]} MOR rewards available in pool {selected_pool}. Would you like to proceed with claiming these rewards?", "assistant", @@ -57,9 +51,7 @@ def _get_response(self, message, wallet_address): elif state == "awaiting_confirmation": user_input = message[-1]["content"].lower() - if any( - word in user_input for word in ["yes", "proceed", "confirm", "claim"] - ): + if any(word in user_input for word in ["yes", "proceed", "confirm", "claim"]): return self.prepare_transactions(wallet_address) else: return ( @@ -108,9 +100,7 @@ def chat(self, request: ChatRequest): if "prompt" in data and "wallet_address" in data: prompt = data["prompt"] wallet_address = data["wallet_address"] - response, role, next_turn_agent = self._get_response( - [prompt], wallet_address - ) + response, role, next_turn_agent = self._get_response([prompt], wallet_address) return { "role": role, "content": response, @@ -119,9 +109,7 @@ def chat(self, request: ChatRequest): else: return {"error": "Missing required parameters"}, 400 except Exception as e: - logger.error( - f"Unexpected error in chat method: {str(e)}, request: {request}" - ) + logger.error(f"Unexpected error in chat method: {str(e)}, request: {request}") raise e def claim(self, request: ChatRequest): @@ -160,9 +148,7 @@ def get_status(self, flag, tx_hash, tx_type): elif flag == "failed": response = f"The claim transaction has failed." elif flag == "initiated": - response = ( - f"Claim transaction has been sent, please wait for it to be confirmed." - ) + response = f"Claim transaction has been sent, please wait for it to be confirmed." if tx_hash: response = ( diff --git a/submodules/moragents_dockers/agents/src/agents/mor_claims/tools.py b/submodules/moragents_dockers/agents/src/agents/mor_claims/tools.py index e5f0143..f921140 100644 --- a/submodules/moragents_dockers/agents/src/agents/mor_claims/tools.py +++ b/submodules/moragents_dockers/agents/src/agents/mor_claims/tools.py @@ -1,6 +1,5 @@ -from web3 import Web3 - from src.agents.mor_claims.config import Config +from web3 import Web3 def get_current_user_reward(wallet_address, pool_id): @@ -36,9 +35,7 @@ def prepare_claim_transaction(pool_id, wallet_address): mint_fee = web3.to_wei(Config.MINT_FEE, "ether") estimated_gas = contract.functions.claim( pool_id, web3.to_checksum_address(wallet_address) - ).estimate_gas( - {"from": web3.to_checksum_address(wallet_address), "value": mint_fee} - ) + ).estimate_gas({"from": web3.to_checksum_address(wallet_address), "value": mint_fee}) return { "to": Config.DISTRIBUTION_PROXY_ADDRESS, "data": tx_data, diff --git a/submodules/moragents_dockers/agents/src/agents/mor_rewards/agent.py b/submodules/moragents_dockers/agents/src/agents/mor_rewards/agent.py index 583e440..ecd21d6 100644 --- a/submodules/moragents_dockers/agents/src/agents/mor_rewards/agent.py +++ b/submodules/moragents_dockers/agents/src/agents/mor_rewards/agent.py @@ -42,9 +42,7 @@ def chat(self, request: ChatRequest): if "prompt" in data and "wallet_address" in data: prompt = data["prompt"] wallet_address = data["wallet_address"] - response, role, next_turn_agent = self.get_response( - prompt, wallet_address - ) + response, role, next_turn_agent = self.get_response(prompt, wallet_address) return { "role": role, "content": response, diff --git a/submodules/moragents_dockers/agents/src/agents/mor_rewards/tools.py b/submodules/moragents_dockers/agents/src/agents/mor_rewards/tools.py index 553041e..a674810 100644 --- a/submodules/moragents_dockers/agents/src/agents/mor_rewards/tools.py +++ b/submodules/moragents_dockers/agents/src/agents/mor_rewards/tools.py @@ -1,6 +1,5 @@ -from web3 import Web3 - from src.agents.mor_rewards.config import Config +from web3 import Web3 def get_current_user_reward(wallet_address, pool_id): diff --git a/submodules/moragents_dockers/agents/src/agents/news_agent/agent.py b/submodules/moragents_dockers/agents/src/agents/news_agent/agent.py index d00f8c3..0bfb03c 100644 --- a/submodules/moragents_dockers/agents/src/agents/news_agent/agent.py +++ b/submodules/moragents_dockers/agents/src/agents/news_agent/agent.py @@ -1,13 +1,9 @@ import logging import re + from src.agents.news_agent.config import Config -from src.agents.news_agent.tools import ( - clean_html, - is_within_time_window, - fetch_rss_feed, -) +from src.agents.news_agent.tools import clean_html, fetch_rss_feed, is_within_time_window from src.models.messages import ChatRequest -import pyshorteners logger = logging.getLogger(__name__) @@ -64,15 +60,11 @@ def process_rss_feed(self, feed_url, coin): logger.info(f"Checking relevance for article: {title}") result = self.check_relevance_and_summarize(title, content, coin) if not result.upper().startswith("NOT RELEVANT"): - results.append( - {"Title": title, "Summary": result, "Link": entry.link} - ) + results.append({"Title": title, "Summary": result, "Link": entry.link}) if len(results) >= Config.ARTICLES_PER_TOKEN: break else: - logger.info( - f"Skipping article: {entry.title} (published: {published_time})" - ) + logger.info(f"Skipping article: {entry.title} (published: {published_time})") logger.info(f"Found {len(results)} relevant articles for {coin}") return results @@ -85,10 +77,7 @@ def fetch_crypto_news(self, coins): google_news_url = Config.GOOGLE_NEWS_BASE_URL.format(coin_name) results = self.process_rss_feed(google_news_url, coin_name) all_news.extend( - [ - {"Coin": coin, **result} - for result in results[: Config.ARTICLES_PER_TOKEN] - ] + [{"Coin": coin, **result} for result in results[: Config.ARTICLES_PER_TOKEN]] ) logger.info(f"Total news items fetched: {len(all_news)}") @@ -104,9 +93,7 @@ def chat(self, request: ChatRequest): # Updated coin detection logic coins = re.findall( - r"\b(" - + "|".join(re.escape(key) for key in Config.CRYPTO_DICT.keys()) - + r")\b", + r"\b(" + "|".join(re.escape(key) for key in Config.CRYPTO_DICT.keys()) + r")\b", prompt.upper(), ) diff --git a/submodules/moragents_dockers/agents/src/agents/news_agent/config.py b/submodules/moragents_dockers/agents/src/agents/news_agent/config.py index e9fe7d3..6db7ac0 100644 --- a/submodules/moragents_dockers/agents/src/agents/news_agent/config.py +++ b/submodules/moragents_dockers/agents/src/agents/news_agent/config.py @@ -2,6 +2,7 @@ logging.basicConfig(level=logging.INFO) + class Config: # RSS Feed URL GOOGLE_NEWS_BASE_URL = "https://news.google.com/rss/search?q={}&hl=en-US&gl=US&ceid=US:en" @@ -27,104 +28,104 @@ class Config: # Dictionary of top 100 popular tickers and their crypto names CRYPTO_DICT = { - 'BTC': 'Bitcoin', - 'ETH': 'Ethereum', - 'USDT': 'Tether', - 'BNB': 'BNB', - 'SOL': 'Solana', - 'USDC': 'USDC', - 'XRP': 'XRP', - 'STETH': 'Lido Staked Ether', - 'DOGE': 'Dogecoin', - 'TON': 'Toncoin', - 'ADA': 'Cardano', - 'TRX': 'TRON', - 'AVAX': 'Avalanche', - 'WSTETH': 'Wrapped stETH', - 'SHIB': 'Shiba Inu', - 'WBTC': 'Wrapped Bitcoin', - 'WETH': 'Binance-Peg WETH', - 'LINK': 'Chainlink', - 'BCH': 'Bitcoin Cash', - 'DOT': 'Polkadot', - 'NEAR': 'NEAR Protocol', - 'UNI': 'Uniswap', - 'LEO': 'LEO Token', - 'DAI': 'Dai', - 'SUI': 'Sui', - 'LTC': 'Litecoin', - 'PEPE': 'Pepe', - 'ICP': 'Internet Computer', - 'WEETH': 'Wrapped eETH', - 'TAO': 'Bittensor', - 'FET': 'Artificial Superintelligence Alliance', - 'APT': 'Aptos', - 'KAS': 'Kaspa', - 'POL': 'POL (ex-MATIC)', - 'XLM': 'Stellar', - 'ETC': 'Ethereum Classic', - 'STX': 'Stacks', - 'FDUSD': 'First Digital USD', - 'IMX': 'Immutable', - 'XMR': 'Monero', - 'RENDER': 'Render', - 'WIF': 'dogwifhat', - 'USDE': 'Ethena USDe', - 'OKB': 'OKB', - 'AAVE': 'Aave', - 'INJ': 'Injective', - 'OP': 'Optimism', - 'FIL': 'Filecoin', - 'CRO': 'Cronos', - 'ARB': 'Arbitrum', - 'HBAR': 'Hedera', - 'FTM': 'Fantom', - 'MNT': 'Mantle', - 'VET': 'VeChain', - 'ATOM': 'Cosmos Hub', - 'RUNE': 'THORChain', - 'BONK': 'Bonk', - 'GRT': 'The Graph', - 'SEI': 'Sei', - 'WBT': 'WhiteBIT Coin', - 'FLOKI': 'FLOKI', - 'AR': 'Arweave', - 'THETA': 'Theta Network', - 'RETH': 'Rocket Pool ETH', - 'BGB': 'Bitget Token', - 'MKR': 'Maker', - 'HNT': 'Helium', - 'METH': 'Mantle Staked Ether', - 'SOLVBTC': 'Solv Protocol SolvBTC', - 'PYTH': 'Pyth Network', - 'TIA': 'Celestia', - 'JUP': 'Jupiter', - 'LDO': 'Lido DAO', - 'MATIC': 'Polygon', - 'ONDO': 'Ondo', - 'ALGO': 'Algorand', - 'GT': 'Gate', - 'JASMY': 'JasmyCoin', - 'QNT': 'Quant', - 'OM': 'MANTRA', - 'BEAM': 'Beam', - 'POPCAT': 'Popcat', - 'BSV': 'Bitcoin SV', - 'KCS': 'KuCoin', - 'EZETH': 'Renzo Restaked ETH', - 'CORE': 'Core', - 'BRETT': 'Brett', - 'WLD': 'Worldcoin', - 'GALA': 'GALA', - 'BTT': 'BitTorrent', - 'FLOW': 'Flow', - 'NOT': 'Notcoin', - 'STRK': 'Starknet', - 'EETH': 'ether.fi Staked ETH', - 'MSOL': 'Marinade Staked SOL', - 'EIGEN': 'Eigenlayer', - 'ORDI': 'ORDI', - 'CFX': 'Conflux', - 'W': 'Wormhole', - 'MOR': 'Morpheus AI' - } \ No newline at end of file + "BTC": "Bitcoin", + "ETH": "Ethereum", + "USDT": "Tether", + "BNB": "BNB", + "SOL": "Solana", + "USDC": "USDC", + "XRP": "XRP", + "STETH": "Lido Staked Ether", + "DOGE": "Dogecoin", + "TON": "Toncoin", + "ADA": "Cardano", + "TRX": "TRON", + "AVAX": "Avalanche", + "WSTETH": "Wrapped stETH", + "SHIB": "Shiba Inu", + "WBTC": "Wrapped Bitcoin", + "WETH": "Binance-Peg WETH", + "LINK": "Chainlink", + "BCH": "Bitcoin Cash", + "DOT": "Polkadot", + "NEAR": "NEAR Protocol", + "UNI": "Uniswap", + "LEO": "LEO Token", + "DAI": "Dai", + "SUI": "Sui", + "LTC": "Litecoin", + "PEPE": "Pepe", + "ICP": "Internet Computer", + "WEETH": "Wrapped eETH", + "TAO": "Bittensor", + "FET": "Artificial Superintelligence Alliance", + "APT": "Aptos", + "KAS": "Kaspa", + "POL": "POL (ex-MATIC)", + "XLM": "Stellar", + "ETC": "Ethereum Classic", + "STX": "Stacks", + "FDUSD": "First Digital USD", + "IMX": "Immutable", + "XMR": "Monero", + "RENDER": "Render", + "WIF": "dogwifhat", + "USDE": "Ethena USDe", + "OKB": "OKB", + "AAVE": "Aave", + "INJ": "Injective", + "OP": "Optimism", + "FIL": "Filecoin", + "CRO": "Cronos", + "ARB": "Arbitrum", + "HBAR": "Hedera", + "FTM": "Fantom", + "MNT": "Mantle", + "VET": "VeChain", + "ATOM": "Cosmos Hub", + "RUNE": "THORChain", + "BONK": "Bonk", + "GRT": "The Graph", + "SEI": "Sei", + "WBT": "WhiteBIT Coin", + "FLOKI": "FLOKI", + "AR": "Arweave", + "THETA": "Theta Network", + "RETH": "Rocket Pool ETH", + "BGB": "Bitget Token", + "MKR": "Maker", + "HNT": "Helium", + "METH": "Mantle Staked Ether", + "SOLVBTC": "Solv Protocol SolvBTC", + "PYTH": "Pyth Network", + "TIA": "Celestia", + "JUP": "Jupiter", + "LDO": "Lido DAO", + "MATIC": "Polygon", + "ONDO": "Ondo", + "ALGO": "Algorand", + "GT": "Gate", + "JASMY": "JasmyCoin", + "QNT": "Quant", + "OM": "MANTRA", + "BEAM": "Beam", + "POPCAT": "Popcat", + "BSV": "Bitcoin SV", + "KCS": "KuCoin", + "EZETH": "Renzo Restaked ETH", + "CORE": "Core", + "BRETT": "Brett", + "WLD": "Worldcoin", + "GALA": "GALA", + "BTT": "BitTorrent", + "FLOW": "Flow", + "NOT": "Notcoin", + "STRK": "Starknet", + "EETH": "ether.fi Staked ETH", + "MSOL": "Marinade Staked SOL", + "EIGEN": "Eigenlayer", + "ORDI": "ORDI", + "CFX": "Conflux", + "W": "Wormhole", + "MOR": "Morpheus AI", + } diff --git a/submodules/moragents_dockers/agents/src/agents/news_agent/tools.py b/submodules/moragents_dockers/agents/src/agents/news_agent/tools.py index f017b22..ac874d2 100644 --- a/submodules/moragents_dockers/agents/src/agents/news_agent/tools.py +++ b/submodules/moragents_dockers/agents/src/agents/news_agent/tools.py @@ -1,21 +1,22 @@ -import feedparser +import logging +import re +import urllib.parse from datetime import datetime, timedelta +from html import unescape + +import feedparser import pytz from dateutil import parser -import re -from html import unescape from src.agents.news_agent.config import Config -import logging -import urllib.parse logger = logging.getLogger(__name__) def clean_html(raw_html): - cleanr = re.compile('<.*?>') - cleantext = re.sub(cleanr, '', raw_html) + cleanr = re.compile("<.*?>") + cleantext = re.sub(cleanr, "", raw_html) cleantext = unescape(cleantext) - cleantext = ' '.join(cleantext.split()) + cleantext = " ".join(cleantext.split()) return cleantext @@ -37,8 +38,8 @@ def fetch_rss_feed(feed_url): # URL encode the query parameter parsed_url = urllib.parse.urlparse(feed_url) query_params = urllib.parse.parse_qs(parsed_url.query) - if 'q' in query_params: - query_params['q'] = [urllib.parse.quote(q) for q in query_params['q']] + if "q" in query_params: + query_params["q"] = [urllib.parse.quote(q) for q in query_params["q"]] encoded_query = urllib.parse.urlencode(query_params, doseq=True) encoded_url = urllib.parse.urlunparse(parsed_url._replace(query=encoded_query)) @@ -58,14 +59,12 @@ def get_tools(): "properties": { "coins": { "type": "array", - "items": { - "type": "string" - }, - "description": "List of cryptocurrency symbols to fetch news for" + "items": {"type": "string"}, + "description": "List of cryptocurrency symbols to fetch news for", } }, - "required": ["coins"] - } - } + "required": ["coins"], + }, + }, } - ] \ No newline at end of file + ] diff --git a/submodules/moragents_dockers/agents/src/agents/rag/agent.py b/submodules/moragents_dockers/agents/src/agents/rag/agent.py index 17b05fe..c164bf6 100644 --- a/submodules/moragents_dockers/agents/src/agents/rag/agent.py +++ b/submodules/moragents_dockers/agents/src/agents/rag/agent.py @@ -1,16 +1,13 @@ -import os import logging +import os from fastapi import Request -from werkzeug.utils import secure_filename - from langchain_community.document_loaders import PyMuPDFLoader from langchain_community.vectorstores import FAISS from langchain_core.prompts import ChatPromptTemplate from langchain_text_splitters.character import RecursiveCharacterTextSplitter - from src.models.messages import ChatRequest -from src.stores import chat_manager, agent_manager +from src.stores import agent_manager, chat_manager logger = logging.getLogger(__name__) @@ -22,18 +19,16 @@ def __init__(self, config, llm, embeddings): self.config = config self.llm = llm self.embedding = embeddings - self.messages = [ - {"role": "assistant", "content": "Please upload a file to begin"} - ] + self.messages = [{"role": "assistant", "content": "Please upload a file to begin"}] self.prompt = ChatPromptTemplate.from_template( """ Answer the following question only based on the given context - + {context} - + Question: {input} """ ) @@ -92,9 +87,7 @@ def _get_rag_response(self, prompt): formatted_context = "\n\n".join(doc.page_content for doc in retrieved_docs) formatted_prompt = f"Question: {prompt}\n\nContext: {formatted_context}" selected_agents = agent_manager.get_selected_agents() - agent_descriptions = "\n".join( - [f"- {agent['description']}" for agent in selected_agents] - ) + agent_descriptions = "\n".join([f"- {agent['description']}" for agent in selected_agents]) system_prompt = f""" You are a helpful assistant. Use the provided context to respond to the following question. The following agents are currently available: diff --git a/submodules/moragents_dockers/agents/src/agents/realtime_search/agent.py b/submodules/moragents_dockers/agents/src/agents/realtime_search/agent.py index 7e6ab8c..c022186 100644 --- a/submodules/moragents_dockers/agents/src/agents/realtime_search/agent.py +++ b/submodules/moragents_dockers/agents/src/agents/realtime_search/agent.py @@ -1,13 +1,12 @@ import logging -import requests import time +import requests from bs4 import BeautifulSoup from selenium import webdriver +from selenium.webdriver.chrome.options import Options from selenium.webdriver.common.by import By from selenium.webdriver.common.keys import Keys -from selenium.webdriver.chrome.options import Options - from src.models.messages import ChatRequest logging.basicConfig( @@ -95,8 +94,8 @@ def synthesize_answer(self, search_term, search_results): messages = [ { "role": "system", - "content": """You are a helpful assistant that synthesizes information from web search results to answer user queries. - Do not preface your answer with 'Based on the search results, I can tell you that:' or anything similar. + "content": """You are a helpful assistant that synthesizes information from web search results to answer user queries. + Do not preface your answer with 'Based on the search results, I can tell you that:' or anything similar. Just provide the answer.""", }, { @@ -133,7 +132,5 @@ def chat(self, request: ChatRequest): logger.error("Missing 'prompt' in chat request data") return {"error": "Missing parameters"}, 400 except Exception as e: - logger.error( - f"Unexpected error in chat method: {str(e)}, request: {request}" - ) + logger.error(f"Unexpected error in chat method: {str(e)}, request: {request}") raise e diff --git a/submodules/moragents_dockers/agents/src/agents/token_swap/README.md b/submodules/moragents_dockers/agents/src/agents/token_swap/README.md index d22f59e..daa7bd4 100644 --- a/submodules/moragents_dockers/agents/src/agents/token_swap/README.md +++ b/submodules/moragents_dockers/agents/src/agents/token_swap/README.md @@ -50,9 +50,9 @@ To run: Your agent should expose the following endpoints: -### 1. Chat -This is the main endpoint for chatting with the agent. - +### 1. Chat +This is the main endpoint for chatting with the agent. + ```http://127.0.0.1:5000/``` The chat API accepts inputs in OpenAI chat completion format - see the example below: @@ -65,14 +65,14 @@ data = {'prompt':message,'chain_id':56} response = requests.post(url, json=data) ``` -The response will also be in this format. +The response will also be in this format. ```sh -response = {"role":"assistant","content":"To proceed with the swap, I need to know which crypto currency you want to +response = {"role":"assistant","content":"To proceed with the swap, I need to know which crypto currency you want to buy in exchange for 1 ETH. Could you please specify the target crypto currency?"} ``` -If the agent has enough information (buy token, sell token, amount) it will then look up the token addresses on the current chain. +If the agent has enough information (buy token, sell token, amount) it will then look up the token addresses on the current chain. If the token symbols are valid, it will then check the user has sufficient balance of the sell token. @@ -91,12 +91,12 @@ response = {"role": "swap", If the user wants to perform a swap based on the quote, the following steps are required: - 1) Check allowance + 1) Check allowance 2) If allowance < swap amount, send an approve transaction 3) If allowance >= swap amount, send a swap transaction -### 2. Check Allowance +### 2. Check Allowance ```http://127.0.0.1:5000/allowance``` @@ -130,7 +130,7 @@ url='http://127.0.0.1:5000/approve ``` -### 4. Generate Swap tx +### 4. Generate Swap tx ```http://127.0.0.1:5000/swap``` @@ -156,7 +156,7 @@ url='http://127.0.0.1:5000/swap ```http://127.0.0.1:5000/tx_status``` This endpoint is used to inform the back-end of the status of transactions that have been signed by the user's wallet on the front-end. - + Status values: * "initiated" = tx has been sent to the wallet @@ -207,4 +207,4 @@ result=response.text ``` url = http://127.0.0.1:5000/clear_messages response = requests.get(url, json=data) -``` \ No newline at end of file +``` diff --git a/submodules/moragents_dockers/agents/src/agents/token_swap/agent.py b/submodules/moragents_dockers/agents/src/agents/token_swap/agent.py index 1488412..cf7eea0 100644 --- a/submodules/moragents_dockers/agents/src/agents/token_swap/agent.py +++ b/submodules/moragents_dockers/agents/src/agents/token_swap/agent.py @@ -1,7 +1,7 @@ import json -import requests import logging +import requests from src.agents.token_swap import tools from src.agents.token_swap.config import Config from src.models.messages import ChatRequest @@ -123,17 +123,13 @@ def get_status(self, flag, tx_hash, tx_type): if flag != "initiated": self.context = [] self.context.append({"role": "assistant", "content": response}) - self.context.append( - {"role": "user", "content": "okay lets start again from scratch"} - ) + self.context.append({"role": "user", "content": "okay lets start again from scratch"}) return {"role": "assistant", "content": response} def generate_response(self, prompt, chain_id, wallet_address): self.context.append(prompt) - response, role, next_turn_agent = self.get_response( - self.context, chain_id, wallet_address - ) + response, role, next_turn_agent = self.get_response(self.context, chain_id, wallet_address) return response, role, next_turn_agent def chat(self, request: ChatRequest): diff --git a/submodules/moragents_dockers/agents/src/agents/token_swap/tools.py b/submodules/moragents_dockers/agents/src/agents/token_swap/tools.py index 2e2c871..40f4bb9 100644 --- a/submodules/moragents_dockers/agents/src/agents/token_swap/tools.py +++ b/submodules/moragents_dockers/agents/src/agents/token_swap/tools.py @@ -1,9 +1,9 @@ -import requests import logging import time -from web3 import Web3 +import requests from src.agents.token_swap.config import Config +from web3 import Web3 class InsufficientFundsError(Exception): @@ -21,9 +21,7 @@ class SwapNotPossibleError(Exception): def search_tokens(query, chain_id, limit=1, ignore_listed="false"): endpoint = f"/v1.2/{chain_id}/search" params = {"query": query, "limit": limit, "ignore_listed": ignore_listed} - response = requests.get( - Config.INCH_URL + endpoint, params=params, headers=Config.HEADERS - ) + response = requests.get(Config.INCH_URL + endpoint, params=params, headers=Config.HEADERS) if response.status_code == 200: return response.json() else: @@ -31,21 +29,15 @@ def search_tokens(query, chain_id, limit=1, ignore_listed="false"): return None -def get_token_balance( - web3: Web3, wallet_address: str, token_address: str, abi: list -) -> int: +def get_token_balance(web3: Web3, wallet_address: str, token_address: str, abi: list) -> int: """Get the balance of an ERC-20 token for a given wallet address.""" if ( not token_address ): # If no token address is provided, assume checking ETH or native token balance return web3.eth.get_balance(web3.to_checksum_address(wallet_address)) else: - contract = web3.eth.contract( - address=web3.to_checksum_address(token_address), abi=abi - ) - return contract.functions.balanceOf( - web3.to_checksum_address(wallet_address) - ).call() + contract = web3.eth.contract(address=web3.to_checksum_address(token_address), abi=abi) + return contract.functions.balanceOf(web3.to_checksum_address(wallet_address)).call() def eth_to_wei(amount_in_eth: float) -> int: @@ -73,9 +65,7 @@ def validate_swap(web3: Web3, token1, token2, chain_id, amount, wallet_address): time.sleep(2) if not t1: raise TokenNotFoundError(f"Token {token1} not found.") - t1_bal = get_token_balance( - web3, wallet_address, t1[0]["address"], Config.ERC20_ABI - ) + t1_bal = get_token_balance(web3, wallet_address, t1[0]["address"], Config.ERC20_ABI) smallest_amount = convert_to_smallest_unit(web3, amount, t1[0]["address"]) # Check if token2 is the native token @@ -102,9 +92,7 @@ def validate_swap(web3: Web3, token1, token2, chain_id, amount, wallet_address): def get_quote(token1, token2, amount_in_wei, chain_id): endpoint = f"/v6.0/{chain_id}/quote" params = {"src": token1, "dst": token2, "amount": int(amount_in_wei)} - response = requests.get( - Config.QUOTE_URL + endpoint, params=params, headers=Config.HEADERS - ) + response = requests.get(Config.QUOTE_URL + endpoint, params=params, headers=Config.HEADERS) if response.status_code == 200: return response.json() else: @@ -127,9 +115,7 @@ def convert_to_smallest_unit(web3: Web3, amount: float, token_address: str) -> i return int(amount * (10**decimals)) -def convert_to_readable_unit( - web3: Web3, smallest_unit_amount: int, token_address: str -) -> float: +def convert_to_readable_unit(web3: Web3, smallest_unit_amount: int, token_address: str) -> float: decimals = get_token_decimals(web3, token_address) return smallest_unit_amount / (10**decimals) @@ -137,9 +123,7 @@ def convert_to_readable_unit( def swap_coins(token1, token2, amount, chain_id, wallet_address): """Swap two crypto coins with each other""" web3 = Web3(Web3.HTTPProvider(Config.WEB3RPCURL[str(chain_id)])) - t1_a, t1_id, t2_a, t2_id = validate_swap( - web3, token1, token2, chain_id, amount, wallet_address - ) + t1_a, t1_id, t2_a, t2_id = validate_swap(web3, token1, token2, chain_id, amount, wallet_address) time.sleep(2) t1_address = "" if t1_a == Config.INCH_NATIVE_TOKEN_ADDRESS else t1_a diff --git a/submodules/moragents_dockers/agents/src/agents/tweet_sizzler/agent.py b/submodules/moragents_dockers/agents/src/agents/tweet_sizzler/agent.py index affbb4b..f1d4cf2 100644 --- a/submodules/moragents_dockers/agents/src/agents/tweet_sizzler/agent.py +++ b/submodules/moragents_dockers/agents/src/agents/tweet_sizzler/agent.py @@ -1,6 +1,6 @@ import logging -import tweepy +import tweepy from src.agents.tweet_sizzler.config import Config from src.models.messages import ChatRequest @@ -125,9 +125,7 @@ def chat(self, chat_request: ChatRequest): logger.info(f"Received chat request: {prompt}") action = prompt.get("action", Config.DEFAULT_ACTION) - logger.debug( - f"Extracted prompt content: {prompt['content']}, action: {action}" - ) + logger.debug(f"Extracted prompt content: {prompt['content']}, action: {action}") if action == "generate": logger.info(f"Generating tweet for prompt: {prompt['content']}") @@ -137,9 +135,7 @@ def chat(self, chat_request: ChatRequest): elif action == "post": logger.info("Attempting to post tweet") result, status_code = self.post_tweet(chat_request) - logger.info( - f"Posted tweet result: {result}, status code: {status_code}" - ) + logger.info(f"Posted tweet result: {result}, status code: {status_code}") if isinstance(result, dict) and "error" in result: return result, status_code return { @@ -151,7 +147,5 @@ def chat(self, chat_request: ChatRequest): return {"role": "assistant", "content": Config.ERROR_INVALID_ACTION} except Exception as e: - logger.error( - f"Unexpected error in chat method: {str(e)}, request: {chat_request}" - ) + logger.error(f"Unexpected error in chat method: {str(e)}, request: {chat_request}") raise e diff --git a/submodules/moragents_dockers/agents/src/app.py b/submodules/moragents_dockers/agents/src/app.py index cb7c4a2..81e00a6 100644 --- a/submodules/moragents_dockers/agents/src/app.py +++ b/submodules/moragents_dockers/agents/src/app.py @@ -1,19 +1,17 @@ -import os import logging +import os import time -import uvicorn -from fastapi import FastAPI, HTTPException, Request, UploadFile, File +import uvicorn +from fastapi import FastAPI, File, HTTPException, Request, UploadFile from fastapi.middleware.cors import CORSMiddleware -from pydantic import BaseModel - -from langchain_ollama import ChatOllama from langchain_community.embeddings import OllamaEmbeddings - +from langchain_ollama import ChatOllama +from pydantic import BaseModel from src.config import Config from src.delegator import Delegator -from src.stores import agent_manager, chat_manager from src.models.messages import ChatRequest +from src.stores import agent_manager, chat_manager # Constants UPLOAD_FOLDER = os.path.join(os.getcwd(), "uploads") @@ -42,9 +40,7 @@ model=Config.OLLAMA_MODEL, base_url=Config.OLLAMA_URL, ) -embeddings = OllamaEmbeddings( - model=Config.OLLAMA_EMBEDDING_MODEL, base_url=Config.OLLAMA_URL -) +embeddings = OllamaEmbeddings(model=Config.OLLAMA_EMBEDDING_MODEL, base_url=Config.OLLAMA_URL) delegator = Delegator(agent_manager, llm, embeddings) @@ -154,9 +150,7 @@ async def swap_agent_swap(request: Request): @app.post("/upload") async def rag_agent_upload(file: UploadFile = File(...)): logger.info("Received upload request") - response = await delegator.delegate_route( - "rag agent", {"file": file}, "upload_file" - ) + response = await delegator.delegate_route("rag agent", {"file": file}, "upload_file") chat_manager.add_message(response) return response @@ -176,9 +170,7 @@ async def post_tweet(request: Request): @app.post("/set_x_api_key") async def set_x_api_key(request: Request): logger.info("Received set X API key request") - return await delegator.delegate_route( - "tweet sizzler agent", request, "set_x_api_key" - ) + return await delegator.delegate_route("tweet sizzler agent", request, "set_x_api_key") @app.post("/claim") diff --git a/submodules/moragents_dockers/agents/src/delegator.py b/submodules/moragents_dockers/agents/src/delegator.py index b020050..1969098 100644 --- a/submodules/moragents_dockers/agents/src/delegator.py +++ b/submodules/moragents_dockers/agents/src/delegator.py @@ -1,8 +1,8 @@ import importlib import logging -from typing import Dict, Optional, Tuple, Any, List +from typing import Any, Dict, List, Optional, Tuple -from langchain.schema import SystemMessage, HumanMessage, AIMessage +from langchain.schema import AIMessage, HumanMessage, SystemMessage from src.stores import chat_manager logger = logging.getLogger(__name__) @@ -63,8 +63,7 @@ def get_available_unattempted_agents(self) -> List[Dict]: and agent_config["name"] not in self.attempted_agents and agent_config["name"] != "default agent" # Exclude default agent and not ( - agent_config["upload_required"] - and not chat_manager.get_uploaded_file_status() + agent_config["upload_required"] and not chat_manager.get_uploaded_file_status() ) ] @@ -85,10 +84,7 @@ def get_delegator_response(self, prompt: Dict) -> Dict[str, str]: "Your primary function is to select the correct agent from the list of available agents based on the user's input. " "You MUST use the 'select_agent' function to select an agent. " "Available agents and their descriptions:\n" - + "\n".join( - f"- {agent['name']}: {agent['description']}" - for agent in available_agents - ) + + "\n".join(f"- {agent['name']}: {agent['description']}" for agent in available_agents) ) tools = [ @@ -133,9 +129,7 @@ def get_delegator_response(self, prompt: Dict) -> Dict[str, str]: return {"agent": selected_agent_name} - def delegate_chat( - self, agent_name: str, chat_request: Any - ) -> Tuple[Optional[str], Any]: + def delegate_chat(self, agent_name: str, chat_request: Any) -> Tuple[Optional[str], Any]: """Delegate chat to specific agent with cascading fallback""" logger.info(f"Attempting to delegate chat to agent: {agent_name}") @@ -172,13 +166,9 @@ def _try_next_agent(self, chat_request: Any) -> Tuple[Optional[str], Any]: except ValueError as ve: # No more agents available logger.error(f"No more agents available: {str(ve)}") - return None, { - "error": "All available agents have been attempted without success" - } + return None, {"error": "All available agents have been attempted without success"} - def delegate_route( - self, agent_name: str, request: Any, method_name: str - ) -> Tuple[Any, int]: + def delegate_route(self, agent_name: str, request: Any, method_name: str) -> Tuple[Any, int]: agent = self.agents.get(agent_name) if agent: if hasattr(agent, method_name): @@ -186,11 +176,7 @@ def delegate_route( method = getattr(agent, method_name) return method(request) else: - logger.warning( - "Method %s not found in agent %s", method_name, agent_name - ) - return { - "error": f"No such method '{method_name}' in agent '{agent_name}'" - }, 400 + logger.warning("Method %s not found in agent %s", method_name, agent_name) + return {"error": f"No such method '{method_name}' in agent '{agent_name}'"}, 400 logger.warning("Attempted to delegate to non-existent agent: %s", agent_name) return {"error": "No such agent registered"}, 400 diff --git a/submodules/moragents_dockers/agents/src/stores/__init__.py b/submodules/moragents_dockers/agents/src/stores/__init__.py index 97b67da..a8e35f4 100644 --- a/submodules/moragents_dockers/agents/src/stores/__init__.py +++ b/submodules/moragents_dockers/agents/src/stores/__init__.py @@ -1,6 +1,6 @@ +from src.config import Config from src.stores.agent_manager import AgentManager from src.stores.chat_manager import ChatManager -from src.config import Config agent_manager = AgentManager(Config.AGENTS_CONFIG) chat_manager = ChatManager() diff --git a/submodules/moragents_dockers/agents/src/stores/agent_manager.py b/submodules/moragents_dockers/agents/src/stores/agent_manager.py index c1c8075..873d364 100644 --- a/submodules/moragents_dockers/agents/src/stores/agent_manager.py +++ b/submodules/moragents_dockers/agents/src/stores/agent_manager.py @@ -1,5 +1,5 @@ import logging -from typing import List, Dict, Optional +from typing import Dict, List, Optional logger = logging.getLogger(__name__) @@ -13,9 +13,7 @@ def __init__(self, config: Dict): # Initialize with first 6 agents selected default_agents = [agent["name"] for agent in config["agents"][:6]] self.set_selected_agents(default_agents) - logger.info( - f"AgentManager initialized with {len(default_agents)} default selected agents" - ) + logger.info(f"AgentManager initialized with {len(default_agents)} default selected agents") def get_active_agent(self) -> Optional[str]: return self.active_agent diff --git a/submodules/moragents_dockers/agents/src/stores/chat_manager.py b/submodules/moragents_dockers/agents/src/stores/chat_manager.py index 5bd79eb..6532a57 100644 --- a/submodules/moragents_dockers/agents/src/stores/chat_manager.py +++ b/submodules/moragents_dockers/agents/src/stores/chat_manager.py @@ -1,5 +1,5 @@ import logging -from typing import List, Dict +from typing import Dict, List logger = logging.getLogger(__name__) diff --git a/submodules/moragents_dockers/agents/tests/claim_agent_benchmarks/README.md b/submodules/moragents_dockers/agents/tests/claim_agent_benchmarks/README.md index 16c72ce..bb8c3ac 100644 --- a/submodules/moragents_dockers/agents/tests/claim_agent_benchmarks/README.md +++ b/submodules/moragents_dockers/agents/tests/claim_agent_benchmarks/README.md @@ -20,4 +20,4 @@ NOTE: this is made for the router compatible moragents repo 5) If it is running, navigate to `submodules/claim_agent_benchmarks` 6) run `pytest benchmarks.py` -NOTE: If needed use your own alchemy mainnet RPC instead of the default cloudflare one in `config.py` and please `pip install pytest web3` \ No newline at end of file +NOTE: If needed use your own alchemy mainnet RPC instead of the default cloudflare one in `config.py` and please `pip install pytest web3` diff --git a/submodules/moragents_dockers/agents/tests/claim_agent_benchmarks/adapters/claim_adapter.py b/submodules/moragents_dockers/agents/tests/claim_agent_benchmarks/adapters/claim_adapter.py index 97afe42..a9bed8a 100644 --- a/submodules/moragents_dockers/agents/tests/claim_agent_benchmarks/adapters/claim_adapter.py +++ b/submodules/moragents_dockers/agents/tests/claim_agent_benchmarks/adapters/claim_adapter.py @@ -1,6 +1,8 @@ -import requests import json +import requests + + class ClaimAdapter: def __init__(self, url, headers): self.url = url @@ -11,4 +13,6 @@ def ask_agent(self, payload): if response.status_code == 200: return response.json() else: - raise Exception(f"Request failed with status code {response.status_code}: {response.text}") + raise Exception( + f"Request failed with status code {response.status_code}: {response.text}" + ) diff --git a/submodules/moragents_dockers/agents/tests/claim_agent_benchmarks/benchmarks.py b/submodules/moragents_dockers/agents/tests/claim_agent_benchmarks/benchmarks.py index 880365d..af675a5 100644 --- a/submodules/moragents_dockers/agents/tests/claim_agent_benchmarks/benchmarks.py +++ b/submodules/moragents_dockers/agents/tests/claim_agent_benchmarks/benchmarks.py @@ -1,5 +1,6 @@ import pytest -from helpers import request_claim, provide_receiver_address, confirm_transaction +from helpers import confirm_transaction, provide_receiver_address, request_claim + from submodules.moragents_dockers.agents.tests.claim_agent_benchmarks.config import Config @@ -12,17 +13,17 @@ def test_claim_process(): # Step 1: Request to claim rewards response = request_claim(wallet_address) - assert "Please provide the receiver address" in response['content'] + assert "Please provide the receiver address" in response["content"] print(f"Step 1 passed for wallet {wallet_address}: Request to claim MOR rewards") # Step 2: Provide the receiver address response = provide_receiver_address(wallet_address, receiver_address) - assert "Please confirm if you want to proceed" in response['content'] + assert "Please confirm if you want to proceed" in response["content"] print(f"Step 2 passed for wallet {wallet_address}: Provided receiver address") # Step 3: Confirm the transaction response = confirm_transaction(wallet_address) - assert "Transaction data prepared" in response['content'] + assert "Transaction data prepared" in response["content"] print(f"Step 3 passed for wallet {wallet_address}: Transaction confirmed") diff --git a/submodules/moragents_dockers/agents/tests/claim_agent_benchmarks/config.py b/submodules/moragents_dockers/agents/tests/claim_agent_benchmarks/config.py index e4ed3ea..d15c74d 100644 --- a/submodules/moragents_dockers/agents/tests/claim_agent_benchmarks/config.py +++ b/submodules/moragents_dockers/agents/tests/claim_agent_benchmarks/config.py @@ -1,13 +1,8 @@ class Config: - URL = 'http://127.0.0.1:5000/' - HEADERS = {'Content-Type': 'application/json'} + URL = "http://127.0.0.1:5000/" + HEADERS = {"Content-Type": "application/json"} # Test wallet addresses - WALLET_ADDRESSES = [ - {"wallet": "0x48d0EAc727A7e478f792F16527012452a000f2bd"} - ] + WALLET_ADDRESSES = [{"wallet": "0x48d0EAc727A7e478f792F16527012452a000f2bd"}] - PROMPTS = { - "claim_request": "I want to claim my MOR", - "proceed": "yes" - } + PROMPTS = {"claim_request": "I want to claim my MOR", "proceed": "yes"} diff --git a/submodules/moragents_dockers/agents/tests/claim_agent_benchmarks/helpers.py b/submodules/moragents_dockers/agents/tests/claim_agent_benchmarks/helpers.py index a319834..a2a8653 100644 --- a/submodules/moragents_dockers/agents/tests/claim_agent_benchmarks/helpers.py +++ b/submodules/moragents_dockers/agents/tests/claim_agent_benchmarks/helpers.py @@ -1,18 +1,22 @@ +from submodules.moragents_dockers.agents.tests.claim_agent_benchmarks.adapters.claim_adapter import ( + ClaimAdapter, +) from submodules.moragents_dockers.agents.tests.claim_agent_benchmarks.config import Config -from submodules.moragents_dockers.agents.tests.claim_agent_benchmarks.adapters.claim_adapter import ClaimAdapter claim_adapter = ClaimAdapter(Config.URL, Config.HEADERS) + def request_claim(wallet_address): payload = { "prompt": {"role": "user", "content": Config.PROMPTS["claim_request"]}, - "wallet_address": wallet_address + "wallet_address": wallet_address, } return claim_adapter.ask_agent(payload) + def confirm_transaction(wallet_address): payload = { "prompt": {"role": "user", "content": Config.PROMPTS["proceed"]}, - "wallet_address": wallet_address + "wallet_address": wallet_address, } return claim_adapter.ask_agent(payload) diff --git a/submodules/moragents_dockers/agents/tests/data_agent/test_agent.py b/submodules/moragents_dockers/agents/tests/data_agent/test_agent.py index b44936f..13db7e4 100644 --- a/submodules/moragents_dockers/agents/tests/data_agent/test_agent.py +++ b/submodules/moragents_dockers/agents/tests/data_agent/test_agent.py @@ -1,95 +1,105 @@ -import sys import os -sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..', 'src', 'data_agent', 'src'))) +import sys + +sys.path.append( + os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "..", "src", "data_agent", "src")) +) # The above sys/os lines are needed because the agents have identical namings (i.e. agent.py, tools.py) +from unittest.mock import MagicMock, patch + import pytest -from unittest.mock import patch, MagicMock -from src.data_agent.src.agent import get_response, generate_response, chat, get_messages, clear_messages from flask import Flask, request +from src.data_agent.src.agent import ( + chat, + clear_messages, + generate_response, + get_messages, + get_response, +) + @pytest.fixture def mock_llm(): mock = MagicMock() mock.create_chat_completion.return_value = { - "choices": [ - { - "message": { - "content": "This is a test response" - } - } - ] + "choices": [{"message": {"content": "This is a test response"}}] } return mock + @pytest.fixture def app(): app = Flask(__name__) return app + def test_get_response(mock_llm): message = [{"role": "user", "content": "Hello"}] response, role = get_response(message, mock_llm) assert response == "This is a test response" assert role == "assistant" + def test_get_response_with_tool_call(mock_llm): mock_llm.create_chat_completion.return_value = { "choices": [ { "message": { "tool_calls": [ - { - "function": { - "name": "get_price", - "arguments": '{"coin_name": "bitcoin"}' - } - } + {"function": {"name": "get_price", "arguments": '{"coin_name": "bitcoin"}'}} ] } } ] } - - with patch('agent.tools.get_coin_price_tool') as mock_price_tool: + + with patch("agent.tools.get_coin_price_tool") as mock_price_tool: mock_price_tool.return_value = "The price of bitcoin is $50,000" message = [{"role": "user", "content": "What's the price of Bitcoin?"}] response, role = get_response(message, mock_llm) - + assert response == "The price of bitcoin is $50,000" assert role == "assistant" + def test_generate_response(mock_llm): prompt = {"role": "user", "content": "Hello"} response, role = generate_response(prompt, mock_llm) assert response == "This is a test response" assert role == "assistant" + def test_chat(app, mock_llm): with app.test_request_context(json={"prompt": "Hello"}): - with patch('agent.generate_response', return_value=("This is a test response", "assistant")): + with patch( + "agent.generate_response", return_value=("This is a test response", "assistant") + ): response, status_code = chat(request, mock_llm) - + assert status_code == 200 assert response.json == {"role": "assistant", "content": "This is a test response"} + def test_chat_missing_prompt(app): with app.test_request_context(json={}): response, status_code = chat(request, None) - + assert status_code == 400 assert "error" in response.json + def test_get_messages(app): with app.test_request_context(): response = get_messages() - + assert response.status_code == 200 assert "messages" in response.json + def test_clear_messages(app): with app.test_request_context(): response = clear_messages() - + assert response.status_code == 200 - assert response.json["response"] == "successfully cleared message history" \ No newline at end of file + assert response.json["response"] == "successfully cleared message history" diff --git a/submodules/moragents_dockers/agents/tests/data_agent/test_tools.py b/submodules/moragents_dockers/agents/tests/data_agent/test_tools.py index 2cbce79..4c51fcc 100644 --- a/submodules/moragents_dockers/agents/tests/data_agent/test_tools.py +++ b/submodules/moragents_dockers/agents/tests/data_agent/test_tools.py @@ -1,82 +1,105 @@ -import sys import os -sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..', 'src', 'data_agent', 'src'))) +import sys + +sys.path.append( + os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "..", "src", "data_agent", "src")) +) # The above sys/os lines are needed because the agents have identical namings (i.e. agent.py, tools.py) -import pytest -from unittest.mock import patch, MagicMock +from unittest.mock import MagicMock, patch +import pytest from src.data_agent.src.tools import ( + get_coin_market_cap_tool, + get_coin_price_tool, get_coingecko_id, - get_price, - get_floor_price, get_fdv, + get_floor_price, + get_fully_diluted_valuation_tool, get_market_cap, - get_protocol_tvl, - get_coin_price_tool, get_nft_floor_price_tool, - get_fully_diluted_valuation_tool, - get_coin_market_cap_tool, + get_price, + get_protocol_tvl, ) # Mock responses -mock_coingecko_search = { - 'coins': [{'id': 'bitcoin'}], - 'nfts': [{'id': 'bored-ape-yacht-club'}] -} -mock_price_response = {'bitcoin': {'usd': 50000}} -mock_floor_price_response = {'floor_price': {'usd': 100000}} -mock_fdv_response = {'market_data': {'fully_diluted_valuation': {'usd': 1000000000000}}} -mock_market_cap_response = [{'market_cap': 500000000000}] -mock_tvl_response = {'tvl': 10000000000} +mock_coingecko_search = {"coins": [{"id": "bitcoin"}], "nfts": [{"id": "bored-ape-yacht-club"}]} +mock_price_response = {"bitcoin": {"usd": 50000}} +mock_floor_price_response = {"floor_price": {"usd": 100000}} +mock_fdv_response = {"market_data": {"fully_diluted_valuation": {"usd": 1000000000000}}} +mock_market_cap_response = [{"market_cap": 500000000000}] +mock_tvl_response = {"tvl": 10000000000} + @pytest.fixture def mock_requests_get(): - with patch('requests.get') as mock_get: + with patch("requests.get") as mock_get: mock_get.return_value = MagicMock() mock_get.return_value.json.return_value = {} yield mock_get + def test_get_coingecko_id_coin(mock_requests_get): mock_requests_get.return_value.json.return_value = mock_coingecko_search - assert get_coingecko_id('bitcoin', type='coin') == 'bitcoin' + assert get_coingecko_id("bitcoin", type="coin") == "bitcoin" + def test_get_coingecko_id_nft(mock_requests_get): mock_requests_get.return_value.json.return_value = mock_coingecko_search - assert get_coingecko_id('bored ape', type='nft') == 'bored-ape-yacht-club' + assert get_coingecko_id("bored ape", type="nft") == "bored-ape-yacht-club" + def test_get_price(mock_requests_get): mock_requests_get.return_value.json.side_effect = [mock_coingecko_search, mock_price_response] - assert get_price('bitcoin') == 50000 + assert get_price("bitcoin") == 50000 + def test_get_floor_price(mock_requests_get): - mock_requests_get.return_value.json.side_effect = [mock_coingecko_search, mock_floor_price_response] - assert get_floor_price('bored ape') == 100000 + mock_requests_get.return_value.json.side_effect = [ + mock_coingecko_search, + mock_floor_price_response, + ] + assert get_floor_price("bored ape") == 100000 + def test_get_fdv(mock_requests_get): mock_requests_get.return_value.json.side_effect = [mock_coingecko_search, mock_fdv_response] - assert get_fdv('bitcoin') == 1000000000000 + assert get_fdv("bitcoin") == 1000000000000 + def test_get_market_cap(mock_requests_get): - mock_requests_get.return_value.json.side_effect = [mock_coingecko_search, mock_market_cap_response] - assert get_market_cap('bitcoin') == 500000000000 + mock_requests_get.return_value.json.side_effect = [ + mock_coingecko_search, + mock_market_cap_response, + ] + assert get_market_cap("bitcoin") == 500000000000 + def test_get_coin_price_tool(mock_requests_get): mock_requests_get.return_value.json.side_effect = [mock_coingecko_search, mock_price_response] - result = get_coin_price_tool('bitcoin') + result = get_coin_price_tool("bitcoin") assert "The price of bitcoin is $50,000" in result + def test_get_nft_floor_price_tool(mock_requests_get): - mock_requests_get.return_value.json.side_effect = [mock_coingecko_search, mock_floor_price_response] - result = get_nft_floor_price_tool('bored ape') + mock_requests_get.return_value.json.side_effect = [ + mock_coingecko_search, + mock_floor_price_response, + ] + result = get_nft_floor_price_tool("bored ape") assert "The floor price of bored ape is $100,000" in result + def test_get_fully_diluted_valuation_tool(mock_requests_get): mock_requests_get.return_value.json.side_effect = [mock_coingecko_search, mock_fdv_response] - result = get_fully_diluted_valuation_tool('bitcoin') + result = get_fully_diluted_valuation_tool("bitcoin") assert "The fully diluted valuation of bitcoin is $1,000,000,000,000" in result + def test_get_coin_market_cap_tool(mock_requests_get): - mock_requests_get.return_value.json.side_effect = [mock_coingecko_search, mock_market_cap_response] - result = get_coin_market_cap_tool('bitcoin') - assert "The market cap of bitcoin is $500,000,000,000" in result \ No newline at end of file + mock_requests_get.return_value.json.side_effect = [ + mock_coingecko_search, + mock_market_cap_response, + ] + result = get_coin_market_cap_tool("bitcoin") + assert "The market cap of bitcoin is $500,000,000,000" in result diff --git a/submodules/moragents_dockers/agents/tests/news_agent_benchmarks/README.md b/submodules/moragents_dockers/agents/tests/news_agent_benchmarks/README.md index d1c4b90..2e7e805 100644 --- a/submodules/moragents_dockers/agents/tests/news_agent_benchmarks/README.md +++ b/submodules/moragents_dockers/agents/tests/news_agent_benchmarks/README.md @@ -17,4 +17,4 @@ 4) Check if the agent is up and running on docker or not 5) If it is running, navigate to `submodules/news_agent_benchmarks` -6) run `pytest benchmarks.py` \ No newline at end of file +6) run `pytest benchmarks.py` diff --git a/submodules/moragents_dockers/agents/tests/news_agent_benchmarks/benchmarks.py b/submodules/moragents_dockers/agents/tests/news_agent_benchmarks/benchmarks.py index 7b7df4c..f5662b5 100644 --- a/submodules/moragents_dockers/agents/tests/news_agent_benchmarks/benchmarks.py +++ b/submodules/moragents_dockers/agents/tests/news_agent_benchmarks/benchmarks.py @@ -1,9 +1,14 @@ # submodules/benchmarks/news_agent_benchmarks/benchmarks.py -import pytest import logging + +import pytest + from submodules.moragents_dockers.agents.tests.news_agent_benchmarks.config import Config -from submodules.moragents_dockers.agents.tests.news_agent_benchmarks.helpers import ask_news_agent, extract_classification +from submodules.moragents_dockers.agents.tests.news_agent_benchmarks.helpers import ( + ask_news_agent, + extract_classification, +) logging.basicConfig(level=logging.INFO) logger = logging.getLogger(__name__) @@ -23,11 +28,15 @@ def test_news_classification(): classification = extract_classification(response) if classification == "UNKNOWN": - logger.warning(f"Test case {i + 1} resulted in UNKNOWN classification. Response: {response}") + logger.warning( + f"Test case {i + 1} resulted in UNKNOWN classification. Response: {response}" + ) assert False, f"Test case {i + 1} failed: Could not determine classification" else: # Check if the classification matches the expected classification - assert classification == expected_classification, f"Test case {i + 1} failed: Expected {expected_classification}, but got {classification}" + assert ( + classification == expected_classification + ), f"Test case {i + 1} failed: Expected {expected_classification}, but got {classification}" logger.info(f"Test case {i + 1} passed: Correctly classified as {classification}") diff --git a/submodules/moragents_dockers/agents/tests/news_agent_benchmarks/helpers.py b/submodules/moragents_dockers/agents/tests/news_agent_benchmarks/helpers.py index 7ce2623..d5eba22 100644 --- a/submodules/moragents_dockers/agents/tests/news_agent_benchmarks/helpers.py +++ b/submodules/moragents_dockers/agents/tests/news_agent_benchmarks/helpers.py @@ -1,18 +1,19 @@ # submodules/benchmarks/news_agent_benchmarks/helpers.py -import requests import logging +import requests + logging.basicConfig(level=logging.INFO) logger = logging.getLogger(__name__) def ask_news_agent(article_text: str, url: str) -> dict: - headers = {'Content-Type': 'application/json'} + headers = {"Content-Type": "application/json"} payload = { "prompt": { "role": "user", - "content": f"Classify if this article is relevant to cryptocurrency price movements: {article_text}" + "content": f"Classify if this article is relevant to cryptocurrency price movements: {article_text}", } } response = requests.post(url, headers=headers, json=payload) @@ -27,7 +28,7 @@ def extract_classification(response: dict) -> str: logger.warning(f"Unexpected response type: {type(response)}") return "UNKNOWN" - content = response.get('content') + content = response.get("content") if content is None: logger.warning("Response content is None") @@ -45,4 +46,4 @@ def extract_classification(response: dict) -> str: return "RELEVANT" else: logger.warning(f"Could not determine relevance from content: {content}") - return "NOT RELEVANT" \ No newline at end of file + return "NOT RELEVANT" diff --git a/submodules/moragents_dockers/agents/tests/price_fetching/adapters/base_adapter.py b/submodules/moragents_dockers/agents/tests/price_fetching/adapters/base_adapter.py index 323f1be..48816c7 100644 --- a/submodules/moragents_dockers/agents/tests/price_fetching/adapters/base_adapter.py +++ b/submodules/moragents_dockers/agents/tests/price_fetching/adapters/base_adapter.py @@ -1,5 +1,6 @@ from abc import ABC, abstractmethod + class BaseAdapter(ABC): @property diff --git a/submodules/moragents_dockers/agents/tests/price_fetching/adapters/coingecko_adapter.py b/submodules/moragents_dockers/agents/tests/price_fetching/adapters/coingecko_adapter.py index 9327118..6103722 100644 --- a/submodules/moragents_dockers/agents/tests/price_fetching/adapters/coingecko_adapter.py +++ b/submodules/moragents_dockers/agents/tests/price_fetching/adapters/coingecko_adapter.py @@ -1,6 +1,8 @@ import requests + from .base_adapter import BaseAdapter + # defillama and coingecko share the same identifiers class CoingeckoAdapter(BaseAdapter): @@ -10,18 +12,18 @@ def name(self) -> str: def get_price(self, coingecko_id: str) -> float: url = f"https://api.coingecko.com/api/v3/simple/price" - params = {'ids': coingecko_id, 'vs_currencies': 'usd'} + params = {"ids": coingecko_id, "vs_currencies": "usd"} response = requests.get(url, params=params) response.raise_for_status() - return response.json()[coingecko_id]['usd'] + return response.json()[coingecko_id]["usd"] def get_marketcap(self, coin_id: str) -> float: - request_url = f"https://api.coingecko.com/api/v3/coins/{coin_id}" - response = requests.get(request_url) - response.raise_for_status() - data = response.json() - marketcap = data['market_data']['market_cap']['usd'] - return marketcap + request_url = f"https://api.coingecko.com/api/v3/coins/{coin_id}" + response = requests.get(request_url) + response.raise_for_status() + data = response.json() + marketcap = data["market_data"]["market_cap"]["usd"] + return marketcap def has_get_marketcap(self) -> bool: return True diff --git a/submodules/moragents_dockers/agents/tests/price_fetching/adapters/defillama_adapter.py b/submodules/moragents_dockers/agents/tests/price_fetching/adapters/defillama_adapter.py index 21bac2f..e98990d 100644 --- a/submodules/moragents_dockers/agents/tests/price_fetching/adapters/defillama_adapter.py +++ b/submodules/moragents_dockers/agents/tests/price_fetching/adapters/defillama_adapter.py @@ -1,22 +1,24 @@ import requests + from .base_adapter import BaseAdapter + class DefillamaAdapter(BaseAdapter): @property def name(self) -> str: return "Defillama" - + def get_price(self, coingecko_id: str) -> float: request_url = f"https://coins.llama.fi/prices/current/coingecko:{coingecko_id}" response = requests.get(request_url) response.raise_for_status() data = response.json() - price = data['coins'][f'coingecko:{coingecko_id}']['price'] + price = data["coins"][f"coingecko:{coingecko_id}"]["price"] return price def has_get_marketcap(self) -> bool: return False def has_get_price(self) -> bool: - return True \ No newline at end of file + return True diff --git a/submodules/moragents_dockers/agents/tests/price_fetching/benchmarks.py b/submodules/moragents_dockers/agents/tests/price_fetching/benchmarks.py index d376fcd..c665393 100644 --- a/submodules/moragents_dockers/agents/tests/price_fetching/benchmarks.py +++ b/submodules/moragents_dockers/agents/tests/price_fetching/benchmarks.py @@ -1,25 +1,31 @@ -import time import argparse -from helpers import ask_data_agent, compare_usd_values, extract_agent_usd_value -from submodules.moragents_dockers.agents.tests.price_fetching import coins, price_prompts, mcap_prompts, price_error_tolerance, mcap_error_tolerance, loop_delay +import time + from adapters.coingecko_adapter import CoingeckoAdapter from adapters.defillama_adapter import DefillamaAdapter +from helpers import ask_data_agent, compare_usd_values, extract_agent_usd_value + +from submodules.moragents_dockers.agents.tests.price_fetching import ( + coins, + loop_delay, + mcap_error_tolerance, + mcap_prompts, + price_error_tolerance, + price_prompts, +) -all_adapters = [ - DefillamaAdapter(), - CoingeckoAdapter() -] +all_adapters = [DefillamaAdapter(), CoingeckoAdapter()] parser = argparse.ArgumentParser(description="Specify the type of prompts to use (price or mcap).") -parser.add_argument('type', choices=['price', 'mcap'], help="Type of prompts to use") +parser.add_argument("type", choices=["price", "mcap"], help="Type of prompts to use") args = parser.parse_args() benchmark_type = args.type -if benchmark_type == 'price': +if benchmark_type == "price": prompts = price_prompts error_tolerance = price_error_tolerance -elif benchmark_type == 'mcap': +elif benchmark_type == "mcap": prompts = mcap_prompts error_tolerance = mcap_error_tolerance else: @@ -28,7 +34,7 @@ total_checks = 0 failures = [] -print('benchmark type', benchmark_type) +print("benchmark type", benchmark_type) try: print() for prompt in prompts: @@ -41,7 +47,9 @@ agent_response = ask_data_agent(prompt.format(name_variation)) agent_usd_value = extract_agent_usd_value(agent_response) print(f"{agent_usd_value}") - time.sleep(loop_delay) # the agent gets rate limitted by coingecko if we call it too fast + time.sleep( + loop_delay + ) # the agent gets rate limitted by coingecko if we call it too fast except: result = f"FAIL DataAgent: {agent_prompt}" print(result) @@ -59,7 +67,15 @@ benchmark_value = adapter.get_marketcap(coingecko_id) else: continue - result = compare_usd_values(agent_usd_value, adapter, coingecko_id, name_variation, benchmark_value, error_tolerance, failures) + result = compare_usd_values( + agent_usd_value, + adapter, + coingecko_id, + name_variation, + benchmark_value, + error_tolerance, + failures, + ) except Exception as e: result = f"FAIL {adapter.name}: {coingecko_id} ({e})" failures.append(result) diff --git a/submodules/moragents_dockers/agents/tests/price_fetching/config.py b/submodules/moragents_dockers/agents/tests/price_fetching/config.py index 34a764e..5b3bf19 100644 --- a/submodules/moragents_dockers/agents/tests/price_fetching/config.py +++ b/submodules/moragents_dockers/agents/tests/price_fetching/config.py @@ -1,252 +1,182 @@ -loop_delay = 10 # prevent rate limiting +loop_delay = 10 # prevent rate limiting -price_error_tolerance = 0.01 # 1% -mcap_error_tolerance = 0.01 # 1% +price_error_tolerance = 0.01 # 1% +mcap_error_tolerance = 0.01 # 1% coins = [ - { - "coingecko_id": "bitcoin", - "name_variations": ["Bitcoin", "Bitcoins", "BITcoin", "BTC"] - }, - { - "coingecko_id": "ethereum", - "name_variations": ["Ether", "Ethereum", "Ethers", "ETH"] - }, - { - "coingecko_id": "tether", - "name_variations": ["Tether", "USDT", "USDTether", "TetherUSD"] - }, + {"coingecko_id": "bitcoin", "name_variations": ["Bitcoin", "Bitcoins", "BITcoin", "BTC"]}, + {"coingecko_id": "ethereum", "name_variations": ["Ether", "Ethereum", "Ethers", "ETH"]}, + {"coingecko_id": "tether", "name_variations": ["Tether", "USDT", "USDTether", "TetherUSD"]}, { "coingecko_id": "binancecoin", - "name_variations": ["BNB", "Binance Smart Chain", "Binancecoin", "Binance coin"] - }, - { - "coingecko_id": "solana", - "name_variations": ["Solana", "SOL", "Solanacoin"] - }, - { - "coingecko_id": "usd-coin", - "name_variations": ["USDC", "USDCoin", "USD coin", "CUSD"] + "name_variations": ["BNB", "Binance Smart Chain", "Binancecoin", "Binance coin"], }, + {"coingecko_id": "solana", "name_variations": ["Solana", "SOL", "Solanacoin"]}, + {"coingecko_id": "usd-coin", "name_variations": ["USDC", "USDCoin", "USD coin", "CUSD"]}, { "coingecko_id": "staked-ether", - "name_variations": ["Lido Staked Ether", "Lido Ether", "Lido Eth", "Staked Ether"] - }, - { - "coingecko_id": "ripple", - "name_variations": ["XRP", "Ripple", "XRPcoin", "XRP coin"] + "name_variations": ["Lido Staked Ether", "Lido Ether", "Lido Eth", "Staked Ether"], }, + {"coingecko_id": "ripple", "name_variations": ["XRP", "Ripple", "XRPcoin", "XRP coin"]}, { "coingecko_id": "the-open-network", - "name_variations": ["Toncoin", "TON", "the open network", "open network coin"] - }, - { - "coingecko_id": "dogecoin", - "name_variations": ["Dogecoin", "DOGE", "dogcoin", "doge coin"] - }, - { - "coingecko_id": "cardano", - "name_variations": ["Cardano", "ADA", "cardano coin"] - }, - { - "coingecko_id": "tron", - "name_variations": ["Tron", "TRX", "troncoin", "trxcoin"] + "name_variations": ["Toncoin", "TON", "the open network", "open network coin"], }, + {"coingecko_id": "dogecoin", "name_variations": ["Dogecoin", "DOGE", "dogcoin", "doge coin"]}, + {"coingecko_id": "cardano", "name_variations": ["Cardano", "ADA", "cardano coin"]}, + {"coingecko_id": "tron", "name_variations": ["Tron", "TRX", "troncoin", "trxcoin"]}, { "coingecko_id": "avalanche-2", - "name_variations": ["Avalanche", "AVAX", "Avalancecoin", "Avaxcoin"] + "name_variations": ["Avalanche", "AVAX", "Avalancecoin", "Avaxcoin"], }, { "coingecko_id": "shiba-inu", - "name_variations": ["Shiba Inu", "Shiba", "SHIB", "shibacoin", "Shiba inu coin"] + "name_variations": ["Shiba Inu", "Shiba", "SHIB", "shibacoin", "Shiba inu coin"], }, { "coingecko_id": "wrapped-bitcoin", - "name_variations": ["Wrapped Bitcoin", "WBTC", "wrapped btc", "wrapped BITcoin"] + "name_variations": ["Wrapped Bitcoin", "WBTC", "wrapped btc", "wrapped BITcoin"], }, { "coingecko_id": "polkadot", - "name_variations": ["Polkadot", "DOT", "polkadotcoin", "polka dot coin", "polka coin"] + "name_variations": ["Polkadot", "DOT", "polkadotcoin", "polka dot coin", "polka coin"], }, { "coingecko_id": "chainlink", - "name_variations": ["Chainlink", "LINK", "Chainlinkcoin", "Linkcoin"] + "name_variations": ["Chainlink", "LINK", "Chainlinkcoin", "Linkcoin"], }, { "coingecko_id": "bitcoin-cash", - "name_variations": ["Bitcoin Cash", "BCH", "BTC cash", "BCHcoin"] - }, - { - "coingecko_id": "uniswap", - "name_variations": ["Uniswap", "UNI", "Unicoin", "Uniswap coin"] - }, - { - "coingecko_id": "leo-token", - "name_variations": ["LEO token", "LEO", "leotoken", "leocoin"] - }, - { - "coingecko_id": "dai", - "name_variations": ["Dai", "Daicoin", "DaiUSD"] - }, - { - "coingecko_id": "near", - "name_variations": ["Near Protocol", "NEAR", "Nearcoin"] - }, - { - "coingecko_id": "litecoin", - "name_variations": ["Litecoin", "LTC", "LTCcoin", "lightcoin"] + "name_variations": ["Bitcoin Cash", "BCH", "BTC cash", "BCHcoin"], }, + {"coingecko_id": "uniswap", "name_variations": ["Uniswap", "UNI", "Unicoin", "Uniswap coin"]}, + {"coingecko_id": "leo-token", "name_variations": ["LEO token", "LEO", "leotoken", "leocoin"]}, + {"coingecko_id": "dai", "name_variations": ["Dai", "Daicoin", "DaiUSD"]}, + {"coingecko_id": "near", "name_variations": ["Near Protocol", "NEAR", "Nearcoin"]}, + {"coingecko_id": "litecoin", "name_variations": ["Litecoin", "LTC", "LTCcoin", "lightcoin"]}, { "coingecko_id": "matic-network", - "name_variations": ["Polygon", "Matic", "MATIC", "Polygoncoin", "Maticcoin"] + "name_variations": ["Polygon", "Matic", "MATIC", "Polygoncoin", "Maticcoin"], }, { "coingecko_id": "wrapped-eeth", - "name_variations": ["Wrapped eETH", "eETH", "WEETH", "eETHcoin", "WEETHcoin"] - }, - { - "coingecko_id": "kaspa", - "name_variations": ["Kaspa", "KAS", "Kaspacoin", "Kascoin"] - }, - { - "coingecko_id": "pepe", - "name_variations": ["Pepe", "Pepecoin"] + "name_variations": ["Wrapped eETH", "eETH", "WEETH", "eETHcoin", "WEETHcoin"], }, + {"coingecko_id": "kaspa", "name_variations": ["Kaspa", "KAS", "Kaspacoin", "Kascoin"]}, + {"coingecko_id": "pepe", "name_variations": ["Pepe", "Pepecoin"]}, { "coingecko_id": "ethena-usde", - "name_variations": ["Ethena", "USDE", "Ethena USD", "Ethenacoin", "USDEcoin"] + "name_variations": ["Ethena", "USDE", "Ethena USD", "Ethenacoin", "USDEcoin"], }, { "coingecko_id": "internet-computer", - "name_variations": ["Internet Computer", "ICP", "ICPcoin", "InternetComputercoin"] + "name_variations": ["Internet Computer", "ICP", "ICPcoin", "InternetComputercoin"], }, { "coingecko_id": "renzo-restaked-eth", - "name_variations": ["Renzo Restaked ETH", "Renzo Restaked Ethereum", "Renzocoin", "RenzoEth"] + "name_variations": [ + "Renzo Restaked ETH", + "Renzo Restaked Ethereum", + "Renzocoin", + "RenzoEth", + ], }, { "coingecko_id": "ethereum-classic", - "name_variations": ["Ethereum Classic", "Ether Classic", "ETH Classic", "ETC"] + "name_variations": ["Ethereum Classic", "Ether Classic", "ETH Classic", "ETC"], }, { "coingecko_id": "fetch-ai", "name_variations": [ - "Artificial Superintelligence Alliance", "FET", "FETcoin", "Fetch", - "Ocean Protocol", "Oceancoin", "Singularity", "Singularitycoin" - ] - }, - { - "coingecko_id": "monero", - "name_variations": ["Monero", "XMR", "Monerocoin", "XMRcoin"] - }, - { - "coingecko_id": "aptos", - "name_variations": ["Aptos", "APT", "Aptoscoin", "APTcoin"] - }, + "Artificial Superintelligence Alliance", + "FET", + "FETcoin", + "Fetch", + "Ocean Protocol", + "Oceancoin", + "Singularity", + "Singularitycoin", + ], + }, + {"coingecko_id": "monero", "name_variations": ["Monero", "XMR", "Monerocoin", "XMRcoin"]}, + {"coingecko_id": "aptos", "name_variations": ["Aptos", "APT", "Aptoscoin", "APTcoin"]}, { "coingecko_id": "render-token", - "name_variations": ["Render", "RNDR", "Rendercoin", "RNDRcoin", "Render token"] - }, - { - "coingecko_id": "stellar", - "name_variations": ["Stellar", "XLM", "Stellarcoin", "XLMcoin"] + "name_variations": ["Render", "RNDR", "Rendercoin", "RNDRcoin", "Render token"], }, + {"coingecko_id": "stellar", "name_variations": ["Stellar", "XLM", "Stellarcoin", "XLMcoin"]}, { "coingecko_id": "hedera-hashgraph", - "name_variations": ["Hedera", "HBAR", "Hederacoin", "HBARcoin", "Hashgraph"] + "name_variations": ["Hedera", "HBAR", "Hederacoin", "HBARcoin", "Hashgraph"], }, { "coingecko_id": "cosmos", - "name_variations": ["Cosmos", "Cosmoshub", "ATOM", "Cosmoscoin", "ATOMCoin"] - }, - { - "coingecko_id": "arbitrum", - "name_variations": ["Arbitrum", "ARB", "Arbitrumcoin", "ARBCoin"] + "name_variations": ["Cosmos", "Cosmoshub", "ATOM", "Cosmoscoin", "ATOMCoin"], }, + {"coingecko_id": "arbitrum", "name_variations": ["Arbitrum", "ARB", "Arbitrumcoin", "ARBCoin"]}, { "coingecko_id": "crypto-com-chain", - "name_variations": ["Cronos", "CRO", "Cronoscoin", "CROCoin", "Crypto.com"] - }, - { - "coingecko_id": "mantle", - "name_variations": ["Mantle", "MNT", "Mantlecoin", "MNTCoin"] + "name_variations": ["Cronos", "CRO", "Cronoscoin", "CROCoin", "Crypto.com"], }, + {"coingecko_id": "mantle", "name_variations": ["Mantle", "MNT", "Mantlecoin", "MNTCoin"]}, { "coingecko_id": "blockstack", - "name_variations": ["Stacks", "STX", "Stackscoin", "STXCoin", "Blockstack"] - }, - { - "coingecko_id": "filecoin", - "name_variations": ["Filecoin", "FIL", "FILCoin", "File coin"] - }, - { - "coingecko_id": "okb", - "name_variations": ["OKB", "OKBCoin"] + "name_variations": ["Stacks", "STX", "Stackscoin", "STXCoin", "Blockstack"], }, + {"coingecko_id": "filecoin", "name_variations": ["Filecoin", "FIL", "FILCoin", "File coin"]}, + {"coingecko_id": "okb", "name_variations": ["OKB", "OKBCoin"]}, { "coingecko_id": "maker", - "name_variations": ["Maker", "MKR", "MakerDAO", "Makercoin", "MRKCoin"] - }, - { - "coingecko_id": "vechain", - "name_variations": ["VeChain", "VET", "VeChaincoin", "VETCoin"] + "name_variations": ["Maker", "MKR", "MakerDAO", "Makercoin", "MRKCoin"], }, + {"coingecko_id": "vechain", "name_variations": ["VeChain", "VET", "VeChaincoin", "VETCoin"]}, { "coingecko_id": "injective-protocol", - "name_variations": ["Injective", "INJ", "Injectivecoin", "INJCoin", "Injective Protocol"] + "name_variations": ["Injective", "INJ", "Injectivecoin", "INJCoin", "Injective Protocol"], }, { "coingecko_id": "immutable-x", - "name_variations": ["Immutable", "IMX", "Immutablecoin", "IMXCoin", "ImmutableX"] + "name_variations": ["Immutable", "IMX", "Immutablecoin", "IMXCoin", "ImmutableX"], }, { "coingecko_id": "first-digital-usd", - "name_variations": ["First Digital USD", "FDUSD", "FirstDigitalUSD", "FDUSDCoin"] - }, - { - "coingecko_id": "optimism", - "name_variations": ["Optimism", "OP", "Optimismcoin", "OPCoin"] - }, - { - "coingecko_id": "morpheusai", - "name_variations": ["Morpheus", "MorpheusAI", "MOR", "MORCoin"] - }, - { - "coingecko_id": "aave", - "name_variations": ["Aave", "Aavecoin"] + "name_variations": ["First Digital USD", "FDUSD", "FirstDigitalUSD", "FDUSDCoin"], }, + {"coingecko_id": "optimism", "name_variations": ["Optimism", "OP", "Optimismcoin", "OPCoin"]}, + {"coingecko_id": "morpheusai", "name_variations": ["Morpheus", "MorpheusAI", "MOR", "MORCoin"]}, + {"coingecko_id": "aave", "name_variations": ["Aave", "Aavecoin"]}, { "coingecko_id": "aavegotchi", - "name_variations": ["Aavegotchi", "Ghost", "Aavegotchicoin", "Ghostcoin", "GHST"] + "name_variations": ["Aavegotchi", "Ghost", "Aavegotchicoin", "Ghostcoin", "GHST"], }, { "coingecko_id": "thorchain", - "name_variations": ["Thor", "THORChain", "RUNE", "Thorcoin", "Runecoin"] + "name_variations": ["Thor", "THORChain", "RUNE", "Thorcoin", "Runecoin"], }, { "coingecko_id": "ethereum-name-service", - "name_variations": ["Ethereum Name Service", "ENS", "ENScoin"] + "name_variations": ["Ethereum Name Service", "ENS", "ENScoin"], }, { "coingecko_id": "axie-infinity", - "name_variations": ["Axie Infinity", "AXS", "Axiecoin", "Axscoin"] - }, - { - "coingecko_id": "zombiecoin", - "name_variations": ["ZombieCoin", "Zombie", "ZMBCoin"] + "name_variations": ["Axie Infinity", "AXS", "Axiecoin", "Axscoin"], }, + {"coingecko_id": "zombiecoin", "name_variations": ["ZombieCoin", "Zombie", "ZMBCoin"]}, { "coingecko_id": "elon-xmas", - "name_variations": ["Elon Xmas", "XMAS", "ElonXmascoin", "XMASCoin"] - }, - { - "coingecko_id": "neblio", - "name_variations": ["Neblio", "NEBL", "Nebliocoin", "NEBLCoin"] + "name_variations": ["Elon Xmas", "XMAS", "ElonXmascoin", "XMASCoin"], }, + {"coingecko_id": "neblio", "name_variations": ["Neblio", "NEBL", "Nebliocoin", "NEBLCoin"]}, { "coingecko_id": "shapeshift-fox-token", - "name_variations": ["ShapeShift FOX", "ShapeShift", "ShapeShiftCoin", "FOXCoin", "FOXToken"] - } + "name_variations": [ + "ShapeShift FOX", + "ShapeShift", + "ShapeShiftCoin", + "FOXCoin", + "FOXToken", + ], + }, ] price_prompts = [ @@ -267,4 +197,4 @@ tvl_prompts = [ "What is the TVL of {}?", "How much value is locked in {}?", -] \ No newline at end of file +] diff --git a/submodules/moragents_dockers/agents/tests/price_fetching/helpers.py b/submodules/moragents_dockers/agents/tests/price_fetching/helpers.py index e50c767..7712caa 100644 --- a/submodules/moragents_dockers/agents/tests/price_fetching/helpers.py +++ b/submodules/moragents_dockers/agents/tests/price_fetching/helpers.py @@ -1,47 +1,54 @@ -import requests import json import re + +import requests from adapters.base_adapter import BaseAdapter -url = 'http://127.0.0.1:8080/data_agent/' +url = "http://127.0.0.1:8080/data_agent/" headers = { - 'Accept': 'application/json, text/plain, */*', - 'Accept-Language': 'en-US,en;q=0.9', - 'Connection': 'keep-alive', - 'Content-Type': 'application/json', - 'Origin': 'http://localhost:3333', - 'Referer': 'http://localhost:3333/', - 'Sec-Fetch-Dest': 'empty', - 'Sec-Fetch-Mode': 'cors', - 'Sec-Fetch-Site': 'cross-site', - 'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36', - 'sec-ch-ua': '"Not/A)Brand";v="8", "Chromium";v="126", "Google Chrome";v="126"', - 'sec-ch-ua-mobile': '?0', - 'sec-ch-ua-platform': '"macOS"', + "Accept": "application/json, text/plain, */*", + "Accept-Language": "en-US,en;q=0.9", + "Connection": "keep-alive", + "Content-Type": "application/json", + "Origin": "http://localhost:3333", + "Referer": "http://localhost:3333/", + "Sec-Fetch-Dest": "empty", + "Sec-Fetch-Mode": "cors", + "Sec-Fetch-Site": "cross-site", + "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36", + "sec-ch-ua": '"Not/A)Brand";v="8", "Chromium";v="126", "Google Chrome";v="126"', + "sec-ch-ua-mobile": "?0", + "sec-ch-ua-platform": '"macOS"', } + def ask_data_agent(prompt: str): - payload = { - "prompt": { - "role": "user", - "content": prompt - } - } + payload = {"prompt": {"role": "user", "content": prompt}} response = requests.post(url, headers=headers, data=json.dumps(payload)) - - result_content = response.json()['content'] + + result_content = response.json()["content"] return result_content + def extract_agent_usd_value(content: str): - match = re.search(r'\$\d+(?:,\d{3})*(?:\.\d{1,8})?', content) # 8 usd digits should be plenty + match = re.search(r"\$\d+(?:,\d{3})*(?:\.\d{1,8})?", content) # 8 usd digits should be plenty if match: - price_str = match.group(0).replace('$', '').replace(',', '') + price_str = match.group(0).replace("$", "").replace(",", "") return float(price_str) raise ValueError("Could not extract a price from the agent response") -def compare_usd_values(agent_value: float, adapter: BaseAdapter, coingecko_id: str, name_variation: str, benchmark_value: float, error_tolerance: float, failures: list): + +def compare_usd_values( + agent_value: float, + adapter: BaseAdapter, + coingecko_id: str, + name_variation: str, + benchmark_value: float, + error_tolerance: float, + failures: list, +): difference = abs(agent_value - benchmark_value) percent_difference = (difference / benchmark_value) * 100 result_value = f"${benchmark_value:.8f}, {percent_difference:.2f}% off" @@ -49,6 +56,6 @@ def compare_usd_values(agent_value: float, adapter: BaseAdapter, coingecko_id: s result_message = f"PASS {adapter.name}. {result_value}" else: result_message = f"FAIL {adapter.name}. {result_value}" - failure_message = f"FAIL {adapter.name}. {result_value}. {coingecko_id}. {name_variation}" # so we have more information to summarize failures at the end + failure_message = f"FAIL {adapter.name}. {result_value}. {coingecko_id}. {name_variation}" # so we have more information to summarize failures at the end failures.append(result_message) return result_message diff --git a/submodules/moragents_dockers/agents/tests/price_fetching/requirements.txt b/submodules/moragents_dockers/agents/tests/price_fetching/requirements.txt index 663bd1f..f229360 100644 --- a/submodules/moragents_dockers/agents/tests/price_fetching/requirements.txt +++ b/submodules/moragents_dockers/agents/tests/price_fetching/requirements.txt @@ -1 +1 @@ -requests \ No newline at end of file +requests diff --git a/submodules/moragents_dockers/agents/tests/reward_check_agent_benchmarks/README.md b/submodules/moragents_dockers/agents/tests/reward_check_agent_benchmarks/README.md index 9469618..3baf18d 100644 --- a/submodules/moragents_dockers/agents/tests/reward_check_agent_benchmarks/README.md +++ b/submodules/moragents_dockers/agents/tests/reward_check_agent_benchmarks/README.md @@ -20,4 +20,4 @@ NOTE: this is made for the router compatible moragents repo 5) If it is running, navigate to `submodules/reward_check_agent_benchmarks` 6) run `benchmarks.py` -NOTE: If needed use your own alchemy mainnet RPC instead of the default cloudflare one in `config.py` and please `pip install pytest web3` \ No newline at end of file +NOTE: If needed use your own alchemy mainnet RPC instead of the default cloudflare one in `config.py` and please `pip install pytest web3` diff --git a/submodules/moragents_dockers/agents/tests/reward_check_agent_benchmarks/adapters/reward_check_adapter.py b/submodules/moragents_dockers/agents/tests/reward_check_agent_benchmarks/adapters/reward_check_adapter.py index 0482fc5..ce68f12 100644 --- a/submodules/moragents_dockers/agents/tests/reward_check_agent_benchmarks/adapters/reward_check_adapter.py +++ b/submodules/moragents_dockers/agents/tests/reward_check_agent_benchmarks/adapters/reward_check_adapter.py @@ -1,5 +1,6 @@ from submodules.moragents_dockers.agents.src.claim_agent.src.tools import get_current_user_reward + class RewardCheckAdapter: def __init__(self): pass diff --git a/submodules/moragents_dockers/agents/tests/reward_check_agent_benchmarks/benchmarks.py b/submodules/moragents_dockers/agents/tests/reward_check_agent_benchmarks/benchmarks.py index 03395e7..85915fb 100644 --- a/submodules/moragents_dockers/agents/tests/reward_check_agent_benchmarks/benchmarks.py +++ b/submodules/moragents_dockers/agents/tests/reward_check_agent_benchmarks/benchmarks.py @@ -1,4 +1,5 @@ import pytest + from submodules.benchmarks.claim_agent_benchmarks.helpers import request_claim from submodules.moragents_dockers.agents.tests.claim_agent_benchmarks.config import Config @@ -13,25 +14,27 @@ def test_claim_process(): response = request_claim(wallet_address) # Check if the response contains the expected structure - assert 'role' in response - assert response['role'] == 'claim' - assert 'content' in response - assert isinstance(response['content'], dict) - assert 'content' in response['content'] - assert 'transactions' in response['content']['content'] - assert len(response['content']['content']['transactions']) > 0 + assert "role" in response + assert response["role"] == "claim" + assert "content" in response + assert isinstance(response["content"], dict) + assert "content" in response["content"] + assert "transactions" in response["content"]["content"] + assert len(response["content"]["content"]["transactions"]) > 0 - transaction = response['content']['content']['transactions'][0] - assert 'pool' in transaction - assert 'transaction' in transaction + transaction = response["content"]["content"]["transactions"][0] + assert "pool" in transaction + assert "transaction" in transaction - tx_data = transaction['transaction'] - assert all(key in tx_data for key in ['to', 'data', 'value', 'gas', 'chainId']) + tx_data = transaction["transaction"] + assert all(key in tx_data for key in ["to", "data", "value", "gas", "chainId"]) # Additional specific checks - assert tx_data['to'] == '0x47176B2Af9885dC6C4575d4eFd63895f7Aaa4790', "Incorrect 'to' address" - assert tx_data['value'] == '1000000000000000', "Incorrect 'value'" - assert tx_data['chainId'] == '1', "Incorrect 'chainId'" + assert ( + tx_data["to"] == "0x47176B2Af9885dC6C4575d4eFd63895f7Aaa4790" + ), "Incorrect 'to' address" + assert tx_data["value"] == "1000000000000000", "Incorrect 'value'" + assert tx_data["chainId"] == "1", "Incorrect 'chainId'" print(f"Step 1 passed for wallet {wallet_address}: Claim process triggered successfully") @@ -39,4 +42,4 @@ def test_claim_process(): if __name__ == "__main__": - pytest.main() \ No newline at end of file + pytest.main() diff --git a/submodules/moragents_dockers/agents/tests/reward_check_agent_benchmarks/config.py b/submodules/moragents_dockers/agents/tests/reward_check_agent_benchmarks/config.py index 2522622..a48d8bb 100644 --- a/submodules/moragents_dockers/agents/tests/reward_check_agent_benchmarks/config.py +++ b/submodules/moragents_dockers/agents/tests/reward_check_agent_benchmarks/config.py @@ -14,27 +14,13 @@ class Config: DISTRIBUTION_ABI = [ { "inputs": [ - { - "internalType": "uint256", - "name": "poolId_", - "type": "uint256" - }, - { - "internalType": "address", - "name": "user_", - "type": "address" - } + {"internalType": "uint256", "name": "poolId_", "type": "uint256"}, + {"internalType": "address", "name": "user_", "type": "address"}, ], "name": "getCurrentUserReward", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], + "outputs": [{"internalType": "uint256", "name": "", "type": "uint256"}], "stateMutability": "view", - "type": "function" + "type": "function", } ] @@ -59,7 +45,7 @@ class Config: { "pool_id": 1, "wallet_address": "0x5CD4C60f0e566dCa1Ae8456C36a63bc7A8D803de", - } + }, ] reward_check_prompts = [ diff --git a/submodules/moragents_dockers/agents/tests/reward_check_agent_benchmarks/helpers.py b/submodules/moragents_dockers/agents/tests/reward_check_agent_benchmarks/helpers.py index 9f05361..7d010e4 100644 --- a/submodules/moragents_dockers/agents/tests/reward_check_agent_benchmarks/helpers.py +++ b/submodules/moragents_dockers/agents/tests/reward_check_agent_benchmarks/helpers.py @@ -1,26 +1,25 @@ -import requests import re + +import requests from web3 import Web3 + from submodules.benchmarks.reward_check_agent_benchmarks.config import Config -url = 'http://127.0.0.1:5000/' +url = "http://127.0.0.1:5000/" headers = { - 'Content-Type': 'application/json', + "Content-Type": "application/json", } def ask_claim_agent(prompt: str, wallet_address: str): payload = { - "prompt": { - "role": "user", - "content": prompt - }, - "wallet_address": wallet_address # Adding the wallet address in the payload + "prompt": {"role": "user", "content": prompt}, + "wallet_address": wallet_address, # Adding the wallet address in the payload } response = requests.post(url, headers=headers, json=payload) if response.status_code == 200: - return response.json()['content'] + return response.json()["content"] else: raise Exception(f"Request failed with status code {response.status_code}: {response.text}") @@ -29,7 +28,7 @@ def get_current_user_reward(wallet_address, pool_id): web3 = Web3(Web3.HTTPProvider(Config.WEB3RPCURL["1"])) distribution_contract = web3.eth.contract( address=web3.to_checksum_address(Config.DISTRIBUTION_PROXY_ADDRESS), - abi=Config.DISTRIBUTION_ABI + abi=Config.DISTRIBUTION_ABI, ) try: @@ -37,10 +36,9 @@ def get_current_user_reward(wallet_address, pool_id): raise Exception("Unable to connect to Ethereum network") reward = distribution_contract.functions.getCurrentUserReward( - pool_id, - web3.to_checksum_address(wallet_address) + pool_id, web3.to_checksum_address(wallet_address) ).call() - formatted_reward = web3.from_wei(reward, 'ether') + formatted_reward = web3.from_wei(reward, "ether") return round(formatted_reward, 4) except Exception as e: raise Exception(f"Error occurred while fetching the reward: {str(e)}") @@ -49,9 +47,9 @@ def get_current_user_reward(wallet_address, pool_id): def extract_reward_value_from_response(response: str) -> dict: # Regex to extract rewards for both pools; adjusted to be more flexible matches = re.findall( - r'Capital Providers Pool \(Pool 0\):\s*([\d.]+)\s*MOR.*?Code Providers Pool \(Pool 1\):\s*([\d.]+)\s*MOR', + r"Capital Providers Pool \(Pool 0\):\s*([\d.]+)\s*MOR.*?Code Providers Pool \(Pool 1\):\s*([\d.]+)\s*MOR", response, - re.DOTALL + re.DOTALL, ) rewards = {} diff --git a/submodules/moragents_dockers/agents/tests/swap_agent/test_agent.py b/submodules/moragents_dockers/agents/tests/swap_agent/test_agent.py index 82f0fab..a1dd527 100644 --- a/submodules/moragents_dockers/agents/tests/swap_agent/test_agent.py +++ b/submodules/moragents_dockers/agents/tests/swap_agent/test_agent.py @@ -1,38 +1,49 @@ -import sys import os -sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..', 'src', 'swap_agent', 'src'))) +import sys + +sys.path.append( + os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "..", "src", "swap_agent", "src")) +) # The above sys/os lines are needed because the agents have identical namings (i.e. agent.py, tools.py) +from unittest.mock import MagicMock, patch + import pytest -from unittest.mock import patch, MagicMock from flask import Flask, request -from src.swap_agent.src.agent import get_response, generate_response, chat, get_messages, clear_messages, get_allowance, approve, swap +from src.swap_agent.src.agent import ( + approve, + chat, + clear_messages, + generate_response, + get_allowance, + get_messages, + get_response, + swap, +) + @pytest.fixture def mock_llm(): mock = MagicMock() mock.create_chat_completion.return_value = { - "choices": [ - { - "message": { - "content": "This is a test response" - } - } - ] + "choices": [{"message": {"content": "This is a test response"}}] } return mock + @pytest.fixture def app(): app = Flask(__name__) return app + def test_get_response(mock_llm): message = [{"role": "user", "content": "Hello"}] response, role = get_response(message, "chain_id", "wallet_address", mock_llm) assert response == "This is a test response" assert role == "assistant" + def test_get_response_with_tool_call(mock_llm): mock_llm.create_chat_completion.return_value = { "choices": [ @@ -42,7 +53,7 @@ def test_get_response_with_tool_call(mock_llm): { "function": { "name": "swap_agent", - "arguments": '{"token1": "ETH", "token2": "USDT", "value": "1.0"}' + "arguments": '{"token1": "ETH", "token2": "USDT", "value": "1.0"}', } } ] @@ -50,66 +61,85 @@ def test_get_response_with_tool_call(mock_llm): } ] } - - with patch('src.swap_agent.src.tools.swap_coins') as mock_swap_coins: + + with patch("src.swap_agent.src.tools.swap_coins") as mock_swap_coins: mock_swap_coins.return_value = ("Swap successful", "assistant") message = [{"role": "user", "content": "Swap 1 ETH for USDT"}] response, role = get_response(message, "chain_id", "wallet_address", mock_llm) - + assert response == "Swap successful" assert role == "assistant" + def test_generate_response(mock_llm): prompt = {"role": "user", "content": "Hello"} response, role = generate_response(prompt, "chain_id", "wallet_address", mock_llm) assert response == "This is a test response" assert role == "assistant" + def test_chat(app, mock_llm): - with app.test_request_context(json={"prompt": "Hello", "wallet_address": "0x123", "chain_id": "1"}): - with patch('src.swap_agent.src.agent.generate_response', return_value=("This is a test response", "assistant")): + with app.test_request_context( + json={"prompt": "Hello", "wallet_address": "0x123", "chain_id": "1"} + ): + with patch( + "src.swap_agent.src.agent.generate_response", + return_value=("This is a test response", "assistant"), + ): response = chat(request, mock_llm) - + assert response.status_code == 200 assert response.json == {"role": "assistant", "content": "This is a test response"} + def test_chat_missing_prompt(app): with app.test_request_context(json={}): response, status_code = chat(request, None) - + assert status_code == 400 assert "error" in response.json + def test_get_messages(app): with app.test_request_context(): response = get_messages() - + assert response.status_code == 200 assert "messages" in response.json + def test_clear_messages(app): with app.test_request_context(): response = clear_messages() - + assert response.status_code == 200 assert response.json["response"] == "successfully cleared message history" + def test_get_allowance(app): - with app.test_request_context(json={"tokenAddress": "0x123", "walletAddress": "0x456", "chain_id": "1"}): - with patch('src.swap_agent.src.agent.check_allowance', return_value={"allowance": "1000"}): + with app.test_request_context( + json={"tokenAddress": "0x123", "walletAddress": "0x456", "chain_id": "1"} + ): + with patch("src.swap_agent.src.agent.check_allowance", return_value={"allowance": "1000"}): response = get_allowance(request) - + assert response.status_code == 200 assert "response" in response.json + def test_approve(app): - with app.test_request_context(json={"tokenAddress": "0x123", "chain_id": "1", "amount": "1000"}): - with patch('src.swap_agent.src.agent.approve_transaction', return_value={"txHash": "0x789"}): + with app.test_request_context( + json={"tokenAddress": "0x123", "chain_id": "1", "amount": "1000"} + ): + with patch( + "src.swap_agent.src.agent.approve_transaction", return_value={"txHash": "0x789"} + ): response = approve(request) - + assert response.status_code == 200 assert "response" in response.json + def test_swap(app): swap_params = { "src": "ETH", @@ -117,10 +147,10 @@ def test_swap(app): "walletAddress": "0x123", "amount": "1.0", "slippage": "1", - "chain_id": "1" + "chain_id": "1", } with app.test_request_context(json=swap_params): - with patch('src.swap_agent.src.agent.build_tx_for_swap', return_value={"txHash": "0x789"}): + with patch("src.swap_agent.src.agent.build_tx_for_swap", return_value={"txHash": "0x789"}): response = swap(request) - - assert {'txHash': '0x789'} == response + + assert {"txHash": "0x789"} == response diff --git a/submodules/moragents_dockers/agents/tests/swap_agent/test_tools.py b/submodules/moragents_dockers/agents/tests/swap_agent/test_tools.py index 66b8004..1450f81 100644 --- a/submodules/moragents_dockers/agents/tests/swap_agent/test_tools.py +++ b/submodules/moragents_dockers/agents/tests/swap_agent/test_tools.py @@ -1,17 +1,30 @@ -import sys import os -sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..', 'src', 'swap_agent', 'src'))) +import sys + +sys.path.append( + os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "..", "src", "swap_agent", "src")) +) # The above sys/os lines are needed because the agents have identical namings (i.e. agent.py, tools.py) +from unittest.mock import MagicMock, patch + import pytest -from unittest.mock import patch, MagicMock -from web3 import Web3 from src.swap_agent.src.tools import ( - search_tokens, get_token_balance, eth_to_wei, validate_swap, - get_quote, get_token_decimals, convert_to_smallest_unit, - convert_to_readable_unit, swap_coins, InsufficientFundsError, - TokenNotFoundError, SwapNotPossibleError + InsufficientFundsError, + SwapNotPossibleError, + TokenNotFoundError, + convert_to_readable_unit, + convert_to_smallest_unit, + eth_to_wei, + get_quote, + get_token_balance, + get_token_decimals, + search_tokens, + swap_coins, + validate_swap, ) +from web3 import Web3 + @pytest.fixture def mock_web3(): @@ -21,94 +34,108 @@ def mock_web3(): mock.eth.contract = MagicMock() return mock + def test_search_tokens(): - with patch('requests.get') as mock_get: + with patch("requests.get") as mock_get: mock_get.return_value.status_code = 200 mock_get.return_value.json.return_value = [{"symbol": "ETH", "address": "0x123"}] result = search_tokens("ETH", 1) assert result[0]["symbol"] == "ETH" + def test_get_token_balance(mock_web3): mock_web3.eth.get_balance.return_value = 1000 balance = get_token_balance(mock_web3, "0x456", "", []) assert balance == 1000 + def test_eth_to_wei(): assert eth_to_wei(1) == 10**18 + def test_validate_swap(mock_web3): - with patch('src.swap_agent.src.tools.search_tokens') as mock_search: + with patch("src.swap_agent.src.tools.search_tokens") as mock_search: mock_search.side_effect = [ [{"symbol": "ETH", "address": "0x0000000000000000000000000000000000000000"}], - [{"symbol": "DAI", "address": "0x123"}] + [{"symbol": "DAI", "address": "0x123"}], ] mock_web3.eth.get_balance.return_value = 10**18 result = validate_swap(mock_web3, "ETH", "DAI", 1, 1, "0x456") assert result[1] == "ETH" + def test_get_quote(): - with patch('requests.get') as mock_get: + with patch("requests.get") as mock_get: mock_get.return_value.status_code = 200 mock_get.return_value.json.return_value = {"dstAmount": "1000000000000000000"} result = get_quote("0x123", "0x456", 10**18, 1) assert result["dstAmount"] == "1000000000000000000" + def test_get_token_decimals(mock_web3): mock_contract = MagicMock() mock_contract.functions.decimals.return_value.call.return_value = 18 mock_web3.eth.contract.return_value = mock_contract assert get_token_decimals(mock_web3, "0x1234567890123456789012345678901234567890") == 18 + def test_convert_to_smallest_unit(mock_web3): - with patch('src.swap_agent.src.tools.get_token_decimals', return_value=18): + with patch("src.swap_agent.src.tools.get_token_decimals", return_value=18): assert convert_to_smallest_unit(mock_web3, 1, "0x123") == 10**18 + def test_convert_to_readable_unit(mock_web3): - with patch('src.swap_agent.src.tools.get_token_decimals', return_value=18): + with patch("src.swap_agent.src.tools.get_token_decimals", return_value=18): assert convert_to_readable_unit(mock_web3, 10**18, "0x123") == 1 + def test_swap_coins(): - with patch('src.swap_agent.src.tools.Web3') as mock_web3, \ - patch('src.swap_agent.src.tools.validate_swap') as mock_validate, \ - patch('src.swap_agent.src.tools.get_quote') as mock_quote: - + with patch("src.swap_agent.src.tools.Web3") as mock_web3, patch( + "src.swap_agent.src.tools.validate_swap" + ) as mock_validate, patch("src.swap_agent.src.tools.get_quote") as mock_quote: + mock_web3.return_value = MagicMock() mock_validate.return_value = ("0x123", "ETH", "0x456", "DAI") mock_quote.return_value = {"dstAmount": "1000000000000000000"} - + result, role = swap_coins("ETH", "DAI", 1, 1, "0x789") assert result["src"] == "ETH" assert result["dst"] == "DAI" assert role == "swap" + def test_swap_coins_insufficient_funds(): - with patch('src.swap_agent.src.tools.Web3') as mock_web3, \ - patch('src.swap_agent.src.tools.validate_swap') as mock_validate: - + with patch("src.swap_agent.src.tools.Web3") as mock_web3, patch( + "src.swap_agent.src.tools.validate_swap" + ) as mock_validate: + mock_web3.return_value = MagicMock() mock_validate.side_effect = InsufficientFundsError("Not enough funds") - + with pytest.raises(InsufficientFundsError): swap_coins("ETH", "DAI", 1000, 1, "0x789") + def test_swap_coins_token_not_found(): - with patch('src.swap_agent.src.tools.Web3') as mock_web3, \ - patch('src.swap_agent.src.tools.validate_swap') as mock_validate: - + with patch("src.swap_agent.src.tools.Web3") as mock_web3, patch( + "src.swap_agent.src.tools.validate_swap" + ) as mock_validate: + mock_web3.return_value = MagicMock() mock_validate.side_effect = TokenNotFoundError("Token not found") - + with pytest.raises(TokenNotFoundError): swap_coins("UNKNOWN", "DAI", 1, 1, "0x789") + def test_swap_coins_swap_not_possible(): - with patch('src.swap_agent.src.tools.Web3') as mock_web3, \ - patch('src.swap_agent.src.tools.validate_swap') as mock_validate, \ - patch('src.swap_agent.src.tools.get_quote') as mock_quote: - + with patch("src.swap_agent.src.tools.Web3") as mock_web3, patch( + "src.swap_agent.src.tools.validate_swap" + ) as mock_validate, patch("src.swap_agent.src.tools.get_quote") as mock_quote: + mock_web3.return_value = MagicMock() mock_validate.return_value = ("0x123", "ETH", "0x456", "DAI") mock_quote.return_value = None - + with pytest.raises(SwapNotPossibleError): - swap_coins("ETH", "DAI", 1, 1, "0x789") \ No newline at end of file + swap_coins("ETH", "DAI", 1, 1, "0x789") diff --git a/submodules/moragents_dockers/frontend/README.md b/submodules/moragents_dockers/frontend/README.md index 91eda58..606d7e3 100644 --- a/submodules/moragents_dockers/frontend/README.md +++ b/submodules/moragents_dockers/frontend/README.md @@ -10,7 +10,7 @@ This is a front-end for the Morpheus local install. It connects directly to agen ### Adding a new agent -Edit ```frontend/config.ts``` +Edit ```frontend/config.ts``` ## Usage @@ -29,9 +29,9 @@ Open [http://localhost:3000](http://localhost:3000) with your browser to see the Your agent should expose the following endpoints: -### 1. Chat -This is the main endpoint for chatting with the agent. - +### 1. Chat +This is the main endpoint for chatting with the agent. + ```http://127.0.0.1:5000/``` The chat API accepts inputs in OpenAI chat completion format - see the example below: @@ -44,14 +44,14 @@ data = {'prompt':message,'chain_id':56} response = requests.post(url, json=data) ``` -The response will also be in this format. +The response will also be in this format. ```sh -response = {"role":"assistant","content":"To proceed with the swap, I need to know which crypto currency you want to +response = {"role":"assistant","content":"To proceed with the swap, I need to know which crypto currency you want to buy in exchange for 1 ETH. Could you please specify the target crypto currency?"} ``` -If the agent has enough information (buy token, sell token, amount) it will then look up the token addresses on the current chain. +If the agent has enough information (buy token, sell token, amount) it will then look up the token addresses on the current chain. If the token symbols are valid, it will then check the user has sufficient balance of the sell token. @@ -70,12 +70,12 @@ response = {"role": "swap", If the user wants to perform a swap based on the quote, the following steps are required: - 1) Check allowance + 1) Check allowance 2) If allowance < swap amount, send an approve transaction 3) If allowance >= swap amount, send a swap transaction -### 2. Check Allowance +### 2. Check Allowance ```http://127.0.0.1:5000/allowance``` @@ -109,7 +109,7 @@ url='http://127.0.0.1:5000/approve ``` -### 4. Generate Swap tx +### 4. Generate Swap tx ```http://127.0.0.1:5000/swap``` @@ -135,7 +135,7 @@ url='http://127.0.0.1:5000/swap ```http://127.0.0.1:5000/tx_status``` This endpoint is used to inform the back-end of the status of transactions that have been signed by the user's wallet on the front-end. - + Status values: * "initiated" = tx has been sent to the wallet @@ -199,5 +199,3 @@ To learn more about this stack, take a look at the following resources: - [RainbowKit Documentation](https://rainbowkit.com) - Learn how to customize your wallet connection flow. - [wagmi Documentation](https://wagmi.sh) - Learn how to interact with Ethereum. - [Next.js Documentation](https://nextjs.org/docs) - Learn how to build a Next.js application - - diff --git a/submodules/moragents_dockers/frontend/components/Avatar/index.tsx b/submodules/moragents_dockers/frontend/components/Avatar/index.tsx index b3acdf2..059da05 100644 --- a/submodules/moragents_dockers/frontend/components/Avatar/index.tsx +++ b/submodules/moragents_dockers/frontend/components/Avatar/index.tsx @@ -18,4 +18,4 @@ export const Avatar: FC = ({ isAgent, agentName }: AvatarProps) => return ( ); -} \ No newline at end of file +} diff --git a/submodules/moragents_dockers/frontend/components/ClaimForm/ClaimForm.tsx b/submodules/moragents_dockers/frontend/components/ClaimForm/ClaimForm.tsx index ffe6f14..6826cdd 100644 --- a/submodules/moragents_dockers/frontend/components/ClaimForm/ClaimForm.tsx +++ b/submodules/moragents_dockers/frontend/components/ClaimForm/ClaimForm.tsx @@ -54,4 +54,4 @@ const handleClaim = async () => { ); -}; \ No newline at end of file +}; diff --git a/submodules/moragents_dockers/frontend/components/ClaimMessage/ClaimMessage.tsx b/submodules/moragents_dockers/frontend/components/ClaimMessage/ClaimMessage.tsx index f0d88f7..5e9ecdb 100644 --- a/submodules/moragents_dockers/frontend/components/ClaimMessage/ClaimMessage.tsx +++ b/submodules/moragents_dockers/frontend/components/ClaimMessage/ClaimMessage.tsx @@ -28,4 +28,4 @@ export const ClaimMessage: FC = ({ onSubmitClaim={onSubmitClaim} /> ); -}; \ No newline at end of file +}; diff --git a/submodules/moragents_dockers/frontend/components/CustomIcon/SendIcon.tsx b/submodules/moragents_dockers/frontend/components/CustomIcon/SendIcon.tsx index d74cc30..a981e83 100644 --- a/submodules/moragents_dockers/frontend/components/CustomIcon/SendIcon.tsx +++ b/submodules/moragents_dockers/frontend/components/CustomIcon/SendIcon.tsx @@ -5,4 +5,4 @@ export const SendIcon = (props: any) => ( -) \ No newline at end of file +) diff --git a/submodules/moragents_dockers/frontend/components/ErrorBackendModal/index.tsx b/submodules/moragents_dockers/frontend/components/ErrorBackendModal/index.tsx index 3b141e1..e80b9d0 100644 --- a/submodules/moragents_dockers/frontend/components/ErrorBackendModal/index.tsx +++ b/submodules/moragents_dockers/frontend/components/ErrorBackendModal/index.tsx @@ -76,4 +76,4 @@ export const ErrorBackendModal: FC = ({ show }) => { ); -} \ No newline at end of file +} diff --git a/submodules/moragents_dockers/frontend/components/LeftSidebar/index.tsx b/submodules/moragents_dockers/frontend/components/LeftSidebar/index.tsx index 95cfa57..8d77c47 100644 --- a/submodules/moragents_dockers/frontend/components/LeftSidebar/index.tsx +++ b/submodules/moragents_dockers/frontend/components/LeftSidebar/index.tsx @@ -21,4 +21,4 @@ export const LeftSidebar: FC = () => { ); -} \ No newline at end of file +} diff --git a/submodules/moragents_dockers/frontend/components/Loader/styles.module.css b/submodules/moragents_dockers/frontend/components/Loader/styles.module.css index f8f1732..75315c6 100644 --- a/submodules/moragents_dockers/frontend/components/Loader/styles.module.css +++ b/submodules/moragents_dockers/frontend/components/Loader/styles.module.css @@ -26,4 +26,4 @@ 30% { transform: translateY(-15px); } -} \ No newline at end of file +} diff --git a/submodules/moragents_dockers/frontend/components/SwapAgentModal/index.tsx b/submodules/moragents_dockers/frontend/components/SwapAgentModal/index.tsx index 70fcd72..eaa62d5 100644 --- a/submodules/moragents_dockers/frontend/components/SwapAgentModal/index.tsx +++ b/submodules/moragents_dockers/frontend/components/SwapAgentModal/index.tsx @@ -26,4 +26,4 @@ export const SwapAgentModal: FC = ({ isOpen, onClose }) => ); -}; \ No newline at end of file +}; diff --git a/submodules/moragents_dockers/frontend/components/WalletRequiredModal/index.tsx b/submodules/moragents_dockers/frontend/components/WalletRequiredModal/index.tsx index 0b43bea..16f895a 100644 --- a/submodules/moragents_dockers/frontend/components/WalletRequiredModal/index.tsx +++ b/submodules/moragents_dockers/frontend/components/WalletRequiredModal/index.tsx @@ -76,4 +76,4 @@ export const WalletRequiredModal: FC = ({ agentRequiresWall ); -} \ No newline at end of file +} diff --git a/submodules/moragents_dockers/frontend/public/assets/ellipse.svg b/submodules/moragents_dockers/frontend/public/assets/ellipse.svg index 81a6f4b..9a223cd 100644 --- a/submodules/moragents_dockers/frontend/public/assets/ellipse.svg +++ b/submodules/moragents_dockers/frontend/public/assets/ellipse.svg @@ -1,3 +1,3 @@ - \ No newline at end of file + diff --git a/submodules/moragents_dockers/frontend/public/assets/me.svg b/submodules/moragents_dockers/frontend/public/assets/me.svg index b381288..1624a09 100644 --- a/submodules/moragents_dockers/frontend/public/assets/me.svg +++ b/submodules/moragents_dockers/frontend/public/assets/me.svg @@ -1,4 +1,4 @@ - \ No newline at end of file + diff --git a/submodules/moragents_dockers/frontend/public/assets/send.svg b/submodules/moragents_dockers/frontend/public/assets/send.svg index 45122ff..724d93e 100644 --- a/submodules/moragents_dockers/frontend/public/assets/send.svg +++ b/submodules/moragents_dockers/frontend/public/assets/send.svg @@ -1,3 +1,3 @@ - \ No newline at end of file + diff --git a/submodules/moragents_dockers/frontend/styles/globals.css b/submodules/moragents_dockers/frontend/styles/globals.css index d42b61a..be1c58c 100644 --- a/submodules/moragents_dockers/frontend/styles/globals.css +++ b/submodules/moragents_dockers/frontend/styles/globals.css @@ -1 +1 @@ -@import url('https://fonts.googleapis.com/css2?family=Inter:wght@100..900&display=swap'); \ No newline at end of file +@import url('https://fonts.googleapis.com/css2?family=Inter:wght@100..900&display=swap'); diff --git a/utils/docker_utils.py b/utils/docker_utils.py index 2edd323..d78920f 100644 --- a/utils/docker_utils.py +++ b/utils/docker_utils.py @@ -8,7 +8,7 @@ def find_unused_port() -> int: while True: port = random.randint(49152, 65535) with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock: - res = sock.connect_ex(('localhost', port)) + res = sock.connect_ex(("localhost", port)) if res != 0: return port @@ -18,12 +18,21 @@ def build_image_if_not_present(image_name, dockerfile_path) -> None: context_dir = os.path.dirname(dockerfile_path) try: - subprocess.run(f"docker inspect {image_name}", shell=True, check=True, stdout=subprocess.DEVNULL, - stderr=subprocess.DEVNULL) + subprocess.run( + f"docker inspect {image_name}", + shell=True, + check=True, + stdout=subprocess.DEVNULL, + stderr=subprocess.DEVNULL, + ) print(f"Docker image '{image_name}' already exists.") except subprocess.CalledProcessError: print(f"Docker image '{image_name}' not found. Building the image...") - subprocess.run(f"docker build -t {image_name} -f {dockerfile_path} {context_dir}", shell=True, check=True) + subprocess.run( + f"docker build -t {image_name} -f {dockerfile_path} {context_dir}", + shell=True, + check=True, + ) print(f"Docker image '{image_name}' built successfully.") @@ -57,6 +66,8 @@ def launch_container(image_name, internal_port, dockerfile_path) -> int: docker_command = f"docker run -d -p {host_port}:{internal_port} {image_name}" subprocess.run(docker_command, shell=True, check=True) - print(f"Docker container of image {image_name} launched with port mapping: {host_port}:{internal_port}") + print( + f"Docker container of image {image_name} launched with port mapping: {host_port}:{internal_port}" + ) return host_port diff --git a/utils/host_utils.py b/utils/host_utils.py index 01d88aa..cc4a286 100644 --- a/utils/host_utils.py +++ b/utils/host_utils.py @@ -1,24 +1,24 @@ -import sys import platform +import sys def get_os_and_arch(): os_name = "Unknown" arch = "Unknown" - if sys.platform.startswith('darwin'): + if sys.platform.startswith("darwin"): os_name = "macOS" - elif sys.platform.startswith('win'): + elif sys.platform.startswith("win"): os_name = "Windows" - elif sys.platform.startswith('linux'): + elif sys.platform.startswith("linux"): os_name = "Linux" machine = platform.machine().lower() - if machine == 'x86_64' or machine == 'amd64': + if machine == "x86_64" or machine == "amd64": arch = "x86_64" - elif machine.startswith('arm') or machine.startswith('aarch'): + elif machine.startswith("arm") or machine.startswith("aarch"): arch = "ARM64" - elif machine == 'i386': + elif machine == "i386": arch = "x86" return os_name, arch diff --git a/utils/logger_config.py b/utils/logger_config.py index 8e0a049..d64946b 100644 --- a/utils/logger_config.py +++ b/utils/logger_config.py @@ -8,7 +8,7 @@ def setup_logger(name, level=logging.INFO): console_handler = logging.StreamHandler() console_handler.setLevel(level) - formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') + formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s") console_handler.setFormatter(formatter) logger.addHandler(console_handler) diff --git a/wizard_windows.iss b/wizard_windows.iss index 0fdcf7b..1f417b8 100644 --- a/wizard_windows.iss +++ b/wizard_windows.iss @@ -98,7 +98,7 @@ end; function ShouldSkipPage(PageID: Integer): Boolean; begin Result := False; - + { Skip EULA page if already accepted } if (PageID = EULAPage.ID) and EULAAccepted then Result := True;