diff --git a/.github/workflows/mor-agents-build-linux.yml b/.github/workflows/mor-agents-build-linux.yml index 3d7dcac..09d09cb 100644 --- a/.github/workflows/mor-agents-build-linux.yml +++ b/.github/workflows/mor-agents-build-linux.yml @@ -2,178 +2,178 @@ name: MOR Agents Build Linux on: push: - branches: [ main ] + branches: [main] pull_request: - branches: [ main ] + branches: [main] workflow_dispatch: jobs: build: 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: Install dependencies - run: | - python -m pip install --upgrade pip - pip install -r requirements.txt - pip install pyinstaller - - - name: Build with PyInstaller - run: | - pyinstaller --name="MORagents" --add-data "images/moragents.png:images" main.py - - - name: Create Debian package - run: | - mkdir -p debian/DEBIAN - mkdir -p debian/usr/bin - mkdir -p debian/usr/share/applications - mkdir -p debian/usr/share/icons/hicolor/256x256/apps - cp -r dist/MORagents/* debian/usr/bin/ - cp images/moragents.png debian/usr/share/icons/hicolor/256x256/apps/moragents.png - echo "[Desktop Entry] - Name=MORagents - Exec=/usr/bin/MORagents - Icon=moragents - Type=Application - Categories=Utility;" > debian/usr/share/applications/moragents.desktop - echo "Package: moragents - Version: 1.0 - Section: utils - Priority: optional - Architecture: amd64 - Maintainer: LachsBagel - Description: MORagents application - MORagents is a desktop application for AI agents." > debian/DEBIAN/control - - dpkg-deb --build debian moragents.deb - - - name: Create setup script - run: | - cat << EOF > moragents-setup.sh - #!/bin/bash - set -e - - # Colors for output - RED='\033[0;31m' - GREEN='\033[0;32m' - YELLOW='\033[0;33m' - NC='\033[0m' # No Color - - # Function to check if a command exists - command_exists() { - command -v "$1" >/dev/null 2>&1 - } - - # Function to add current user to docker group - add_user_to_docker_group() { - local current_user=\$(whoami) - if [ "\$current_user" != "root" ]; then - echo -e "\${YELLOW}Adding current user to docker group...${NC}" - sudo usermod -aG docker "\$current_user" - echo -e "\${GREEN}User added to docker group. Please log out and log back in for changes to take effect.${NC}" - else - echo -e "\${YELLOW}Running as root. Skipping user addition to docker group.${NC}" - fi - } - - # Function to wait for Ollama service to be ready - wait_for_ollama() { - echo -e "\${YELLOW}Waiting for Ollama service to be ready...${NC}" - for i in {1..30}; do - if curl -s http://localhost:11434/api/tags >/dev/null; then - echo -e "\${GREEN}Ollama service is ready.${NC}" - return 0 - fi - sleep 2 - done - echo -e "\${RED}Timed out waiting for Ollama service.${NC}" - return 1 - } - - # Function to pull Ollama model with retries - pull_ollama_model() { - 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 - echo -e "\${GREEN}Successfully pulled \$model.${NC}" - return 0 - fi - echo -e "\${YELLOW}Failed to pull \$model. Retrying...${NC}" - sleep 5 - attempt=\$((attempt + 1)) - done - - echo -e "\${RED}Failed to pull \$model after \$max_attempts attempts.${NC}" - return 1 - } - - # Install curl if not present - if ! command_exists curl; then - echo -e "\${YELLOW}Installing curl...${NC}" - sudo apt-get update - sudo apt-get install -y curl - fi - - # Install Docker if not present - if ! command_exists docker; then - echo -e "\${YELLOW}Installing Docker...${NC}" - curl -fsSL https://get.docker.com -o get-docker.sh - sudo sh get-docker.sh - add_user_to_docker_group - sudo systemctl enable docker - sudo systemctl start docker - else - echo -e "\${GREEN}Docker is already installed.${NC}" - fi - - # Install Ollama - echo -e "\${YELLOW}Installing Ollama...${NC}" - curl -fsSL https://ollama.com/install.sh | sh - - # Start Ollama service - echo -e "\${YELLOW}Starting Ollama service...${NC}" - nohup ollama serve > /dev/null 2>&1 & - - # Wait for Ollama service to be ready - wait_for_ollama - - # Pull Ollama models - echo -e "\${YELLOW}Pulling Ollama models...${NC}" - pull_ollama_model llama3.1 - pull_ollama_model nomic-embed-text - - # Pull necessary Docker images - echo -e "\${YELLOW}Pulling Docker images...${NC}" - sudo docker pull lachsbagel/moragents_dockers-nginx:amd64-0.1.0 - sudo docker pull lachsbagel/moragents_dockers-agents:amd64-0.1.0 - - # Start Docker containers - echo -e "\${YELLOW}Starting Docker containers...${NC}" - sudo docker run -d --name agents -p 8080:5000 --restart always -v /var/lib/agents -v /app/src lachsbagel/moragents_dockers-agents:amd64-0.1.0 - sudo docker run -d --name nginx -p 3333:80 lachsbagel/moragents_dockers-nginx:amd64-0.1.0 - - echo -e "\${GREEN}Setup complete!${NC}" - EOF - - chmod +x moragents-setup.sh - - - name: Upload Debian Package and Setup Script - uses: actions/upload-artifact@v4 - with: - name: MORagentsSetup-Linux - path: | - moragents.deb - moragents-setup.sh \ No newline at end of file + - 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 --name="MORagents" --add-data "images/moragents.png:images" main.py + + - name: Create Debian package + run: | + mkdir -p debian/DEBIAN + mkdir -p debian/usr/bin + mkdir -p debian/usr/share/applications + mkdir -p debian/usr/share/icons/hicolor/256x256/apps + cp -r dist/MORagents/* debian/usr/bin/ + cp images/moragents.png debian/usr/share/icons/hicolor/256x256/apps/moragents.png + echo "[Desktop Entry] + Name=MORagents + Exec=/usr/bin/MORagents + Icon=moragents + Type=Application + Categories=Utility;" > debian/usr/share/applications/moragents.desktop + echo "Package: moragents + Version: 1.0 + Section: utils + Priority: optional + Architecture: amd64 + Maintainer: LachsBagel + Description: MORagents application + MORagents is a desktop application for AI agents." > debian/DEBIAN/control + + dpkg-deb --build debian moragents.deb + + - name: Create setup script + run: | + cat << EOF > moragents-setup.sh + #!/bin/bash + set -e + + # Colors for output + RED='\033[0;31m' + GREEN='\033[0;32m' + YELLOW='\033[0;33m' + NC='\033[0m' # No Color + + # Function to check if a command exists + command_exists() { + command -v "$1" >/dev/null 2>&1 + } + + # Function to add current user to docker group + add_user_to_docker_group() { + local current_user=\$(whoami) + if [ "\$current_user" != "root" ]; then + echo -e "\${YELLOW}Adding current user to docker group...${NC}" + sudo usermod -aG docker "\$current_user" + echo -e "\${GREEN}User added to docker group. Please log out and log back in for changes to take effect.${NC}" + else + echo -e "\${YELLOW}Running as root. Skipping user addition to docker group.${NC}" + fi + } + + # Function to wait for Ollama service to be ready + wait_for_ollama() { + echo -e "\${YELLOW}Waiting for Ollama service to be ready...${NC}" + for i in {1..30}; do + if curl -s http://localhost:11434/api/tags >/dev/null; then + echo -e "\${GREEN}Ollama service is ready.${NC}" + return 0 + fi + sleep 2 + done + echo -e "\${RED}Timed out waiting for Ollama service.${NC}" + return 1 + } + + # Function to pull Ollama model with retries + pull_ollama_model() { + 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 + echo -e "\${GREEN}Successfully pulled \$model.${NC}" + return 0 + fi + echo -e "\${YELLOW}Failed to pull \$model. Retrying...${NC}" + sleep 5 + attempt=\$((attempt + 1)) + done + + echo -e "\${RED}Failed to pull \$model after \$max_attempts attempts.${NC}" + return 1 + } + + # Install curl if not present + if ! command_exists curl; then + echo -e "\${YELLOW}Installing curl...${NC}" + sudo apt-get update + sudo apt-get install -y curl + fi + + # Install Docker if not present + if ! command_exists docker; then + echo -e "\${YELLOW}Installing Docker...${NC}" + curl -fsSL https://get.docker.com -o get-docker.sh + sudo sh get-docker.sh + add_user_to_docker_group + sudo systemctl enable docker + sudo systemctl start docker + else + echo -e "\${GREEN}Docker is already installed.${NC}" + fi + + # Install Ollama + echo -e "\${YELLOW}Installing Ollama...${NC}" + curl -fsSL https://ollama.com/install.sh | sh + + # Start Ollama service + echo -e "\${YELLOW}Starting Ollama service...${NC}" + nohup ollama serve > /dev/null 2>&1 & + + # Wait for Ollama service to be ready + wait_for_ollama + + # Pull Ollama models + echo -e "\${YELLOW}Pulling Ollama models...${NC}" + pull_ollama_model llama3.2:3b + pull_ollama_model nomic-embed-text + + # Pull necessary Docker images + echo -e "\${YELLOW}Pulling Docker images...${NC}" + sudo docker pull lachsbagel/moragents_dockers-nginx:amd64-0.2.0 + sudo docker pull lachsbagel/moragents_dockers-agents:amd64-0.2.0 + + # Start Docker containers + echo -e "\${YELLOW}Starting Docker containers...${NC}" + sudo docker run -d --name agents -p 8080:5000 --restart always -v /var/lib/agents -v /app/src lachsbagel/moragents_dockers-agents:amd64-0.2.0 + sudo docker run -d --name nginx -p 3333:80 lachsbagel/moragents_dockers-nginx:amd64-0.2.0 + + echo -e "\${GREEN}Setup complete!${NC}" + EOF + + chmod +x moragents-setup.sh + + - name: Upload Debian Package and Setup Script + uses: actions/upload-artifact@v4 + with: + name: MORagentsSetup-Linux + path: | + moragents.deb + moragents-setup.sh diff --git a/.github/workflows/security_scan.yml b/.github/workflows/security_scan.yml new file mode 100644 index 0000000..ab7ebac --- /dev/null +++ b/.github/workflows/security_scan.yml @@ -0,0 +1,46 @@ +name: Security Scan + +on: + push: + branches: [ main ] + pull_request: + branches: [ main ] + +jobs: + security_scan: + 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 + with: + scan-type: 'fs' + ignore-unfixed: true + format: 'sarif' + output: 'trivy-results.sarif' + severity: 'CRITICAL' + + - name: Upload Trivy scan results to GitHub Security tab + uses: github/codeql-action/upload-sarif@v3 + with: + sarif_file: 'trivy-results.sarif' diff --git a/AGENTABILITIES.md b/AGENTABILITIES.md index a7e8452..da8a892 100644 --- a/AGENTABILITIES.md +++ b/AGENTABILITIES.md @@ -11,7 +11,7 @@ transaction. ### Current Projects: 1. **lachsbagel on Discord** - [this repo](https://github.com/MorpheusAIs/moragents): - 1. Local install architecture + 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 1. Link to Paper [Hide and Seek: Fingerprinting Large Language Models with Evolutionary Learning](https://www.arxiv.org/abs/2408.02871) @@ -20,27 +20,32 @@ transaction. 1. Windows Build (EXE version of .app) 2. Two installation wizards: 1. Windows - 2. (pending) macOS + 2. macOS 3. CICD build for Windows 4. CICD builds for Linux and macOS (apple and intel) - 4. (pending) Vulnerability scanning of dependencies and code + 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 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 EVM-based wallets. - 3. [Swap agent](submodules/moragents_dockers/agents/src/swap_agent) which can iteratively ask users to provide needed details for disambiguation. + 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. 5. [Local delegating agent](https://github.com/MorpheusAIs/moragents/pull/45) which can maintain user's persona/interests as well as coordinating to task agents and tools. 6. (pending) Agent forge to allow devs to publish their custom agents to the Morpheus/Lumerin Agent Registry 7. **Dan Y.** - 1. [X/Twitter Posting Agent](https://github.com/MorpheusAIs/moragents/pull/57) - an agent which generates spicy tweets with an X integration for one-click posting. - 2. (pending) Inter-agent Delegator which can coordinate between local and decentralized agents + 1. [X/Twitter Posting Agent](https://github.com/MorpheusAIs/moragents/pull/57) - An agent which generates spicy tweets with an X integration for one-click posting. + 2. Real-time search agent + 3. Replaced llama 3.1 and functionary with llama 3.2 to massively increase speed 10X and reduce install footprint 6X + 4. (pending) Tweet maps agent. Will show relevant live tweets with geo-tags based on prompt + 5. (pending) Inter-agent Delegator which can coordinate between local and decentralized agents 7. **Niveshi** - 1. [MOR Rewards agent](https://github.com/MorpheusAIs/moragents/tree/main/submodules/moragents_dockers/agents/src/reward_agent/src). Lets you see how many MOR tokens are claimable for your wallet. + 1. [MOR Rewards agent](https://github.com/MorpheusAIs/moragents/tree/main/submodules/moragents_dockers/agents/src/reward_agent/src). Lets you see how many MOR tokens are claimable for your wallet + 2. Cryto Live News Agent 8. **Dom** - 1. (pending) Coinbase MPC enabled Agent on Base + 1. (pending) Coinbase MPC enabled Agent on Base for dollar cost averaging and gasless sends + 2. (pending) Tweet maps agent. Will show relevant live tweets with geo-tags based on prompt ### Decentralized Inference: #### Non-Local Installation Agents for Permission-less Compute @@ -62,4 +67,4 @@ Pending Lumerin's work. Eventually Agent Builders will be able to permission-les ### Contact Join the [Morpheus Discord](https://discord.com/invite/Dc26EFb6JK) -*Last Updated: September 14, 2024* +*Last Updated: October 20, 2024* diff --git a/README.md b/README.md index 776c89e..7877e17 100644 --- a/README.md +++ b/README.md @@ -7,30 +7,31 @@ Fully Extensible! Add your own agents and have them automatically invoked based ![UI 1](images/tweet_sizzler.png) -![UI 2](images/wallet_integration.png) +![UI 2](images/real-time-info.png) ![UI 3](images/mor_rewards.png) -![UI 4](images/price_fetcher.png) +![UI 4](images/price-fetcher-realtime-news.png) -![UI 5](images/moragents_chatpdf.jpg) +![UI 5](images/moragents_chatpdf.png) --- ### Features -#### Upload a PDF with paperclip icon, then ask questions about the PDF πŸ“„ - - "Can you give me a summary?" - - "What's the main point of the document?" -#### Swap ERC Compatible Tokens πŸͺ™ -> πŸͺ™ - - "Swap 0.01 ETH for USDC" - - **Works with your favorite wallet extensions in your existing browser** +#### Write Sizzling Tweets 🌢️ No Content Moderation πŸ˜… + - "Write a based tweet about Crypto and AI" +#### Real-time Info πŸ•ΈοΈ + - "Real-time info about Company XYZ" +#### Trending Crypto News + - "Latest news for USDC" +#### Check MOR rewards πŸ† + - "How many MOR rewards do I have?" #### Fetch Price, Market Cap, and TVL of coins and tokens supported on CoinGecko πŸ“ˆ - "What's the price of ETH?" - "What's the market cap of BTC?" -#### Check MOR rewards πŸ† - - "How many MOR rewards do I have?" -#### Write Sizzling Tweets 🌢️ No Content Moderation πŸ˜… - - "Write a based tweet about Crypto and AI" +#### Upload a PDF with paperclip icon, then ask questions about the PDF πŸ“„ + - "Can you give me a summary?" + - "What's the main point of the document?" --- @@ -41,14 +42,13 @@ 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. Download and run MORagents installer [MORagents010-apple.pkg](https://drive.proton.me/urls/G9JZYZ508R#gmDk0i6UFLSG) - > SHA256 a4846c83ced5d35740f884a144cf518764edfc17b576b808cd77a8fe2da6ebf2 MORagents010-apple.pkg + 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) - 1. Download and install [Docker Desktop](https://desktop.docker.com/mac/main/amd64/Docker.dmg) - 2. Download and run MORagents installer [MORagents010-intel.pkg](https://drive.proton.me/urls/HPFMSN40GM#Pa90tgOzYn9g) - > SHA256 46b0e927aaca27cf08d9a95b73d01bc07a92cb5a8b05cf69faaf71566712a781 MORagents010-intel.pkg + 1. Download and run MORagents installer [MORagents020-intel.pkg](https://drive.proton.me/urls/1SFGC83RMR#7hdNk4t7bQ0y) + > SHA256 0e3a831f9817ff8fe79cc87d87a60a7884e6752becde20aaa1920133ab2d8036 MORagents020-intel.pkg 2. Wait several minutes for background files to download and then your browser should automatically open to http://localhost:3333 - > Note: After installation is complete, the MORagents app icon will bounce for several minutes on your dock, and then stop. This is normal behavior as it's downloading a couple large 9GB files in the background. You can open "Activity Monitor" and in the Network tab see that it's downloading. + > Note: After installation is complete, the MORagents app icon will bounce for several minutes on your dock, and then stop. This is normal behavior as it's downloading a <7GB of files in the background. You can open "Activity Monitor" and in the Network tab see that it's downloading. #### Future Usage - Open the "MORagents" app from Mac search bar. @@ -66,9 +66,9 @@ Fully Extensible! Add your own agents and have them automatically invoked based >Assumes minimum 16GB RAM #### Steps -1. Download [MORagentsSetupWindows010.zip](https://drive.proton.me/urls/QXRZR77AJ0#U0ZRbd2rDbXT) - > SHA256 0ca1879d3f103938a49852d2d2f82a36bc0ebc44ed94400fcee3b883e2cbb2f6 MORagentsSetupWindows010.zip -2. Go to downloaded **MORagentsSetupWindows010(.zip)** file and double click to open +1. Download [MORagentsSetupWindows020.zip](https://drive.proton.me/urls/CV5NDK88W8#TNGSOxKUbq4b) + > SHA256 1802a941517ad19c732d3a34bfd353fd9a7eb5921ca12210dc5335cf75e34ee8 MORagentsSetupWindows020.zip +2. Go to downloaded **MORagentsSetupWindows020(.zip)** file and double click to open 3. Double click **MORagentsSetup.exe** 1. You may need to click "More info" -> "Run anyway" 2. If that still doesn't work, try temporarily disabling your antivirus and open the .exe again diff --git a/build_assets/macOS/MORagents.app/Contents/Frameworks/Python b/build_assets/macOS/MORagents.app/Contents/Frameworks/Python new file mode 120000 index 0000000..e485e13 --- /dev/null +++ b/build_assets/macOS/MORagents.app/Contents/Frameworks/Python @@ -0,0 +1 @@ +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 new file mode 120000 index 0000000..be75854 --- /dev/null +++ b/build_assets/macOS/MORagents.app/Contents/Frameworks/Python.framework/Python @@ -0,0 +1 @@ +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 new file mode 120000 index 0000000..953ee36 --- /dev/null +++ b/build_assets/macOS/MORagents.app/Contents/Frameworks/Python.framework/Resources @@ -0,0 +1 @@ +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 new file mode 100755 index 0000000..e734fa3 Binary files /dev/null and b/build_assets/macOS/MORagents.app/Contents/Frameworks/Python.framework/Versions/3.11/Python 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 new file mode 100644 index 0000000..653ba7b --- /dev/null +++ b/build_assets/macOS/MORagents.app/Contents/Frameworks/Python.framework/Versions/3.11/Resources/Info.plist @@ -0,0 +1,28 @@ + + + + + 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 new file mode 100644 index 0000000..f5201d5 --- /dev/null +++ b/build_assets/macOS/MORagents.app/Contents/Frameworks/Python.framework/Versions/3.11/_CodeSignature/CodeResources @@ -0,0 +1,128 @@ + + + + + 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 new file mode 120000 index 0000000..902b2c9 --- /dev/null +++ b/build_assets/macOS/MORagents.app/Contents/Frameworks/Python.framework/Versions/Current @@ -0,0 +1 @@ +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 new file mode 120000 index 0000000..89ddc93 --- /dev/null +++ b/build_assets/macOS/MORagents.app/Contents/Frameworks/base_library.zip @@ -0,0 +1 @@ +../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 new file mode 100755 index 0000000..6798a91 Binary files /dev/null and b/build_assets/macOS/MORagents.app/Contents/Frameworks/lib-dynload/_bisect.cpython-311-darwin.so 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 new file mode 100755 index 0000000..54d6cd1 Binary files /dev/null and b/build_assets/macOS/MORagents.app/Contents/Frameworks/lib-dynload/_blake2.cpython-311-darwin.so 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 new file mode 100755 index 0000000..95bc276 Binary files /dev/null and b/build_assets/macOS/MORagents.app/Contents/Frameworks/lib-dynload/_bz2.cpython-311-darwin.so 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 new file mode 100755 index 0000000..ebfeeef Binary files /dev/null and b/build_assets/macOS/MORagents.app/Contents/Frameworks/lib-dynload/_codecs_cn.cpython-311-darwin.so 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 new file mode 100755 index 0000000..984151a Binary files /dev/null and b/build_assets/macOS/MORagents.app/Contents/Frameworks/lib-dynload/_codecs_hk.cpython-311-darwin.so 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 new file mode 100755 index 0000000..dc409ba Binary files /dev/null and b/build_assets/macOS/MORagents.app/Contents/Frameworks/lib-dynload/_codecs_iso2022.cpython-311-darwin.so 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 new file mode 100755 index 0000000..38b2679 Binary files /dev/null and b/build_assets/macOS/MORagents.app/Contents/Frameworks/lib-dynload/_codecs_jp.cpython-311-darwin.so 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 new file mode 100755 index 0000000..b6f7ccb Binary files /dev/null and b/build_assets/macOS/MORagents.app/Contents/Frameworks/lib-dynload/_codecs_kr.cpython-311-darwin.so 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 new file mode 100755 index 0000000..0545a0e Binary files /dev/null and b/build_assets/macOS/MORagents.app/Contents/Frameworks/lib-dynload/_codecs_tw.cpython-311-darwin.so 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 new file mode 100755 index 0000000..fb46fd4 Binary files /dev/null and b/build_assets/macOS/MORagents.app/Contents/Frameworks/lib-dynload/_contextvars.cpython-311-darwin.so 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 new file mode 100755 index 0000000..355db2c Binary files /dev/null and b/build_assets/macOS/MORagents.app/Contents/Frameworks/lib-dynload/_csv.cpython-311-darwin.so 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 new file mode 100755 index 0000000..826edf5 Binary files /dev/null and b/build_assets/macOS/MORagents.app/Contents/Frameworks/lib-dynload/_datetime.cpython-311-darwin.so 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 new file mode 100755 index 0000000..c9a39e1 Binary files /dev/null and b/build_assets/macOS/MORagents.app/Contents/Frameworks/lib-dynload/_decimal.cpython-311-darwin.so 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 new file mode 100755 index 0000000..eadcafe Binary files /dev/null and b/build_assets/macOS/MORagents.app/Contents/Frameworks/lib-dynload/_hashlib.cpython-311-darwin.so 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 new file mode 100755 index 0000000..df8aade Binary files /dev/null and b/build_assets/macOS/MORagents.app/Contents/Frameworks/lib-dynload/_heapq.cpython-311-darwin.so 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 new file mode 100755 index 0000000..adfbb15 Binary files /dev/null and b/build_assets/macOS/MORagents.app/Contents/Frameworks/lib-dynload/_lzma.cpython-311-darwin.so 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 new file mode 100755 index 0000000..6f77cb5 Binary files /dev/null and b/build_assets/macOS/MORagents.app/Contents/Frameworks/lib-dynload/_md5.cpython-311-darwin.so 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 new file mode 100755 index 0000000..db87c2e Binary files /dev/null and b/build_assets/macOS/MORagents.app/Contents/Frameworks/lib-dynload/_multibytecodec.cpython-311-darwin.so 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 new file mode 100755 index 0000000..8d7b2d2 Binary files /dev/null and b/build_assets/macOS/MORagents.app/Contents/Frameworks/lib-dynload/_opcode.cpython-311-darwin.so 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 new file mode 100755 index 0000000..10b16e8 Binary files /dev/null and b/build_assets/macOS/MORagents.app/Contents/Frameworks/lib-dynload/_pickle.cpython-311-darwin.so 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 new file mode 100755 index 0000000..64c4e3e Binary files /dev/null and b/build_assets/macOS/MORagents.app/Contents/Frameworks/lib-dynload/_posixsubprocess.cpython-311-darwin.so 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 new file mode 100755 index 0000000..325efd7 Binary files /dev/null and b/build_assets/macOS/MORagents.app/Contents/Frameworks/lib-dynload/_random.cpython-311-darwin.so 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 new file mode 100755 index 0000000..2c5e57f Binary files /dev/null and b/build_assets/macOS/MORagents.app/Contents/Frameworks/lib-dynload/_scproxy.cpython-311-darwin.so 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 new file mode 100755 index 0000000..184ae13 Binary files /dev/null and b/build_assets/macOS/MORagents.app/Contents/Frameworks/lib-dynload/_sha1.cpython-311-darwin.so 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 new file mode 100755 index 0000000..82aff96 Binary files /dev/null and b/build_assets/macOS/MORagents.app/Contents/Frameworks/lib-dynload/_sha256.cpython-311-darwin.so 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 new file mode 100755 index 0000000..b57973f Binary files /dev/null and b/build_assets/macOS/MORagents.app/Contents/Frameworks/lib-dynload/_sha3.cpython-311-darwin.so 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 new file mode 100755 index 0000000..f595726 Binary files /dev/null and b/build_assets/macOS/MORagents.app/Contents/Frameworks/lib-dynload/_sha512.cpython-311-darwin.so 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 new file mode 100755 index 0000000..5ec2b04 Binary files /dev/null and b/build_assets/macOS/MORagents.app/Contents/Frameworks/lib-dynload/_socket.cpython-311-darwin.so 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 new file mode 100755 index 0000000..71b4f7d Binary files /dev/null and b/build_assets/macOS/MORagents.app/Contents/Frameworks/lib-dynload/_ssl.cpython-311-darwin.so 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 new file mode 100755 index 0000000..5bf100e Binary files /dev/null and b/build_assets/macOS/MORagents.app/Contents/Frameworks/lib-dynload/_statistics.cpython-311-darwin.so 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 new file mode 100755 index 0000000..c2b0e78 Binary files /dev/null and b/build_assets/macOS/MORagents.app/Contents/Frameworks/lib-dynload/_struct.cpython-311-darwin.so 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 new file mode 100755 index 0000000..9a3beaa Binary files /dev/null and b/build_assets/macOS/MORagents.app/Contents/Frameworks/lib-dynload/_typing.cpython-311-darwin.so 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 new file mode 100755 index 0000000..c51e5d9 Binary files /dev/null and b/build_assets/macOS/MORagents.app/Contents/Frameworks/lib-dynload/array.cpython-311-darwin.so 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 new file mode 100755 index 0000000..9bf61c7 Binary files /dev/null and b/build_assets/macOS/MORagents.app/Contents/Frameworks/lib-dynload/binascii.cpython-311-darwin.so 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 new file mode 100755 index 0000000..9fd0042 Binary files /dev/null and b/build_assets/macOS/MORagents.app/Contents/Frameworks/lib-dynload/fcntl.cpython-311-darwin.so 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 new file mode 100755 index 0000000..b874135 Binary files /dev/null and b/build_assets/macOS/MORagents.app/Contents/Frameworks/lib-dynload/grp.cpython-311-darwin.so 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 new file mode 100755 index 0000000..2b22563 Binary files /dev/null and b/build_assets/macOS/MORagents.app/Contents/Frameworks/lib-dynload/math.cpython-311-darwin.so 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 new file mode 100755 index 0000000..9170583 Binary files /dev/null and b/build_assets/macOS/MORagents.app/Contents/Frameworks/lib-dynload/pyexpat.cpython-311-darwin.so 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 new file mode 100755 index 0000000..652b814 Binary files /dev/null and b/build_assets/macOS/MORagents.app/Contents/Frameworks/lib-dynload/resource.cpython-311-darwin.so 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 new file mode 100755 index 0000000..4cce59e Binary files /dev/null and b/build_assets/macOS/MORagents.app/Contents/Frameworks/lib-dynload/select.cpython-311-darwin.so 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 new file mode 100755 index 0000000..6f58b8f Binary files /dev/null and b/build_assets/macOS/MORagents.app/Contents/Frameworks/lib-dynload/termios.cpython-311-darwin.so 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 new file mode 100755 index 0000000..aee61ea Binary files /dev/null and b/build_assets/macOS/MORagents.app/Contents/Frameworks/lib-dynload/unicodedata.cpython-311-darwin.so 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 new file mode 100755 index 0000000..4533871 Binary files /dev/null and b/build_assets/macOS/MORagents.app/Contents/Frameworks/lib-dynload/zlib.cpython-311-darwin.so 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 new file mode 100755 index 0000000..3c841b5 Binary files /dev/null and b/build_assets/macOS/MORagents.app/Contents/Frameworks/libcrypto.3.dylib 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 new file mode 100755 index 0000000..7db7017 Binary files /dev/null and b/build_assets/macOS/MORagents.app/Contents/Frameworks/liblzma.5.dylib 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 new file mode 100755 index 0000000..e356217 Binary files /dev/null and b/build_assets/macOS/MORagents.app/Contents/Frameworks/libmpdec.4.dylib 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 new file mode 100755 index 0000000..85d19a3 Binary files /dev/null and b/build_assets/macOS/MORagents.app/Contents/Frameworks/libssl.3.dylib differ diff --git a/build_assets/macOS/MORagents.app/Contents/Info.plist b/build_assets/macOS/MORagents.app/Contents/Info.plist new file mode 100644 index 0000000..127cf0e --- /dev/null +++ b/build_assets/macOS/MORagents.app/Contents/Info.plist @@ -0,0 +1,24 @@ + + + + + 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 new file mode 100755 index 0000000..402dc34 Binary files /dev/null and b/build_assets/macOS/MORagents.app/Contents/MacOS/MORagents differ diff --git a/build_assets/macOS/MORagents.app/Contents/Resources/Python b/build_assets/macOS/MORagents.app/Contents/Resources/Python new file mode 120000 index 0000000..e485e13 --- /dev/null +++ b/build_assets/macOS/MORagents.app/Contents/Resources/Python @@ -0,0 +1 @@ +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 new file mode 120000 index 0000000..34ca7ea --- /dev/null +++ b/build_assets/macOS/MORagents.app/Contents/Resources/Python.framework @@ -0,0 +1 @@ +../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 new file mode 100644 index 0000000..241b01c Binary files /dev/null and b/build_assets/macOS/MORagents.app/Contents/Resources/base_library.zip differ diff --git a/build_assets/macOS/MORagents.app/Contents/Resources/lib-dynload b/build_assets/macOS/MORagents.app/Contents/Resources/lib-dynload new file mode 120000 index 0000000..5319f7a --- /dev/null +++ b/build_assets/macOS/MORagents.app/Contents/Resources/lib-dynload @@ -0,0 +1 @@ +../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 new file mode 120000 index 0000000..cc14f15 --- /dev/null +++ b/build_assets/macOS/MORagents.app/Contents/Resources/libcrypto.3.dylib @@ -0,0 +1 @@ +../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 new file mode 120000 index 0000000..fbed50f --- /dev/null +++ b/build_assets/macOS/MORagents.app/Contents/Resources/liblzma.5.dylib @@ -0,0 +1 @@ +../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 new file mode 120000 index 0000000..21d144b --- /dev/null +++ b/build_assets/macOS/MORagents.app/Contents/Resources/libmpdec.4.dylib @@ -0,0 +1 @@ +../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 new file mode 120000 index 0000000..4651492 --- /dev/null +++ b/build_assets/macOS/MORagents.app/Contents/Resources/libssl.3.dylib @@ -0,0 +1 @@ +../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 new file mode 100644 index 0000000..bd05d78 Binary files /dev/null and b/build_assets/macOS/MORagents.app/Contents/Resources/moragents.icns differ diff --git a/build_assets/macOS/MORagents.app/Contents/_CodeSignature/CodeResources b/build_assets/macOS/MORagents.app/Contents/_CodeSignature/CodeResources new file mode 100644 index 0000000..2d4b94e --- /dev/null +++ b/build_assets/macOS/MORagents.app/Contents/_CodeSignature/CodeResources @@ -0,0 +1,616 @@ + + + + + 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 b3e596e..fe97b24 100644 --- a/build_assets/macOS/Packaging_Instructions_macOS.md +++ b/build_assets/macOS/Packaging_Instructions_macOS.md @@ -33,12 +33,12 @@ Future usage only requires you to run MORagents from your searchbar. ## Notarize ```sh -xcrun notarytool submit MORagents010-[apple\|intel].pkg --keychain-profile "NotaryProfile" --wait +xcrun notarytool submit MORagents020-[apple\|intel].pkg --keychain-profile "NotaryProfile" --wait ``` ## Staple ```sh -xcrun stapler staple MORagents010-[apple\|intel].pkg +xcrun stapler staple MORagents020-[apple\|intel].pkg ``` --- diff --git a/build_assets/macOS/postinstall.sh b/build_assets/macOS/postinstall.sh old mode 100755 new mode 100644 diff --git a/build_assets/macOS/preinstall.sh b/build_assets/macOS/preinstall.sh old mode 100755 new mode 100644 diff --git a/build_assets/macOS/preinstall_docker.sh b/build_assets/macOS/preinstall_docker.sh old mode 100755 new mode 100644 diff --git a/build_assets/macOS/preinstall_ollama.sh b/build_assets/macOS/preinstall_ollama.sh index dfcb2e7..a74c1d5 100644 --- a/build_assets/macOS/preinstall_ollama.sh +++ b/build_assets/macOS/preinstall_ollama.sh @@ -7,5 +7,5 @@ chmod +x ollama sudo mv ollama /usr/local/bin/ nohup /usr/local/bin/ollama serve > /dev/null 2>&1 & -/usr/local/bin/ollama pull llama3.1 +/usr/local/bin/ollama pull llama3.2:3b /usr/local/bin/ollama pull nomic-embed-text diff --git a/build_assets/macOS/welcome.html b/build_assets/macOS/welcome.html index 18ca86d..a355874 100644 --- a/build_assets/macOS/welcome.html +++ b/build_assets/macOS/welcome.html @@ -1,44 +1,57 @@ - - - - Welcome to MORagents v0.1.0 Installer - - - -

Welcome to MORagents v0.1.0 Installer

-

Thank you for choosing to install MORagents on your system. This installer will guide you through the process of setting up MORagents and its dependencies.

-

The installer will perform the following steps:

- -

Please note that during the installation process, you may be prompted to enter your system password to authorize the installation of required components.

-

Click "Continue" to proceed with the installation.

- + li { + margin-bottom: 10px; + } + + + +

Welcome to MORagents v0.2.0 Installer

+

+ Thank you for choosing to install MORagents on your system. This installer + will guide you through the process of setting up MORagents and its + dependencies. +

+

The installer will perform the following steps:

+ +

+ Please note that during the installation process, you may be prompted to + enter your system password to authorize the installation of required + components. +

+

Click "Continue" to proceed with the installation.

+ diff --git a/config.py b/config.py index 43bae40..1738205 100644 --- a/config.py +++ b/config.py @@ -16,20 +16,20 @@ class AgentDockerConfig: MACOS_APPLE_IMAGE_NAMES = [ - "lachsbagel/moragents_dockers-nginx:apple-0.1.0", - "lachsbagel/moragents_dockers-agents:apple-0.1.0" + "lachsbagel/moragents_dockers-nginx:apple-0.2.0", + "lachsbagel/moragents_dockers-agents:apple-0.2.0" ] MACOS_INTEL_IMAGE_NAMES = [ - "lachsbagel/moragents_dockers-nginx:amd64-0.1.0", - "lachsbagel/moragents_dockers-agents:amd64-0.1.0" + "lachsbagel/moragents_dockers-nginx:amd64-0.2.0", + "lachsbagel/moragents_dockers-agents:amd64-0.2.0" ] WINDOWS_IMAGE_NAMES = [ - "lachsbagel/moragents_dockers-nginx:amd64-0.1.0", - "lachsbagel/moragents_dockers-agents:amd64-0.1.0" + "lachsbagel/moragents_dockers-nginx: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.1.0", - "lachsbagel/moragents_dockers-agents:amd64-0.1.0" + "lachsbagel/moragents_dockers-nginx:amd64-0.2.0", + "lachsbagel/moragents_dockers-agents:amd64-0.2.0" ] @staticmethod @@ -53,5 +53,9 @@ class AgentDockerConfigDeprecate: "lachsbagel/moragents_dockers-nginx:apple-0.0.9", "lachsbagel/moragents_dockers-agents:apple-0.0.9", "lachsbagel/moragents_dockers-nginx:amd64-0.0.9", - "lachsbagel/moragents_dockers-agents:amd64-0.0.9" + "lachsbagel/moragents_dockers-agents:amd64-0.0.9", + "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" ] diff --git a/images/moragents_chatpdf.jpg b/images/moragents_chatpdf.jpg deleted file mode 100644 index a42c7de..0000000 Binary files a/images/moragents_chatpdf.jpg and /dev/null differ diff --git a/images/moragents_chatpdf.png b/images/moragents_chatpdf.png new file mode 100644 index 0000000..1cb159a Binary files /dev/null and b/images/moragents_chatpdf.png differ diff --git a/images/price-fetcher-realtime-news.png b/images/price-fetcher-realtime-news.png new file mode 100644 index 0000000..42ef903 Binary files /dev/null and b/images/price-fetcher-realtime-news.png differ diff --git a/images/price_fetcher.png b/images/price_fetcher.png deleted file mode 100644 index b0336a1..0000000 Binary files a/images/price_fetcher.png and /dev/null differ diff --git a/images/real-time-info.png b/images/real-time-info.png new file mode 100644 index 0000000..2857650 Binary files /dev/null and b/images/real-time-info.png differ diff --git a/images/tweet_sizzler_instructions/dashboard_permissions.png b/images/tweet_sizzler_instructions/dashboard_permissions.png new file mode 100644 index 0000000..9b5a16e Binary files /dev/null and b/images/tweet_sizzler_instructions/dashboard_permissions.png differ diff --git a/images/tweet_sizzler_instructions/user_authentication_settings.png b/images/tweet_sizzler_instructions/user_authentication_settings.png new file mode 100644 index 0000000..60701fd Binary files /dev/null and b/images/tweet_sizzler_instructions/user_authentication_settings.png differ diff --git a/requirements.txt b/requirements.txt index 70969cd..c4a1bda 100644 --- a/requirements.txt +++ b/requirements.txt @@ -5,3 +5,5 @@ requests setuptools pyinstaller torch +safety +bandit diff --git a/submodules/benchmarks/claim_agent_benchmarks/config.py b/submodules/benchmarks/claim_agent_benchmarks/config.py deleted file mode 100644 index 36fa731..0000000 --- a/submodules/benchmarks/claim_agent_benchmarks/config.py +++ /dev/null @@ -1,14 +0,0 @@ -class Config: - URL = 'http://127.0.0.1:5000/' - HEADERS = {'Content-Type': 'application/json'} - - # Test wallet addresses and receiver addresses - WALLET_ADDRESSES = [ - {"wallet": "0x48d0EAc727A7e478f792F16527012452a000f2bd", - "receiver": "0x48d0EAc727A7e478f792F16527012452a000f2bd"} - ] - - PROMPTS = { - "claim_request": "I want to claim my MOR rewards from pool id 1", - "proceed": "proceed" - } diff --git a/submodules/benchmarks/claim_agent_benchmarks/user_flow.py b/submodules/benchmarks/claim_agent_benchmarks/user_flow.py deleted file mode 100644 index f7482df..0000000 --- a/submodules/benchmarks/claim_agent_benchmarks/user_flow.py +++ /dev/null @@ -1,46 +0,0 @@ -import requests -import json - -url = 'http://127.0.0.1:5000/' - -headers = { - 'Content-Type': 'application/json', -} - -def ask_agent(payload): - response = requests.post(url, headers=headers, data=json.dumps(payload)) - - if response.status_code == 200: - print("Raw response data:") - print(response.text) - return response.json() - else: - print("Raw error data:") - print(response.text) - raise Exception(f"Request failed with status code {response.status_code}") - -# Step 1: Request to claim MOR rewards -payload = { - "prompt": {"role": "user", "content": "I want to claim my MOR rewards from pool id 1"}, - "wallet_address": "0x48d0EAc727A7e478f792F16527012452a000f2bd" -} -response = ask_agent(payload) - -# Step 2: Provide the receiver address -receiver_address = "0x48d0EAc727A7e478f792F16527012452a000f2bd" -payload = { - "prompt": {"role": "user", "content": receiver_address}, - "wallet_address": "0x48d0EAc727A7e478f792F16527012452a000f2bd" -} -response = ask_agent(payload) - -# Step 3: Confirm the transaction -payload = { - "prompt": {"role": "user", "content": "proceed"}, - "wallet_address": "0x48d0EAc727A7e478f792F16527012452a000f2bd" -} -response = ask_agent(payload) - -# Final step: Print the final raw response after confirming -print("Final raw response data:") -print(json.dumps(response, indent=2)) diff --git a/submodules/benchmarks/reward_check_agent_benchmarks/adapters/reward_check_adapter.py b/submodules/benchmarks/reward_check_agent_benchmarks/adapters/reward_check_adapter.py deleted file mode 100644 index 538c39f..0000000 --- a/submodules/benchmarks/reward_check_agent_benchmarks/adapters/reward_check_adapter.py +++ /dev/null @@ -1,12 +0,0 @@ -from submodules.moragents_dockers.agents.src.claim_agent.src.tools import get_current_user_reward - -class RewardCheckAdapter: - def __init__(self): - pass - - @property - def name(self) -> str: - return "RewardCheckAdapter" - - def get_reward(self, pool_id: int, wallet_address: str) -> float: - return get_current_user_reward(wallet_address, pool_id) \ No newline at end of file diff --git a/submodules/benchmarks/reward_check_agent_benchmarks/benchmarks.py b/submodules/benchmarks/reward_check_agent_benchmarks/benchmarks.py deleted file mode 100644 index b2d526b..0000000 --- a/submodules/benchmarks/reward_check_agent_benchmarks/benchmarks.py +++ /dev/null @@ -1,47 +0,0 @@ -from helpers import ask_claim_agent, get_current_user_reward, extract_reward_value_from_response -from submodules.benchmarks.reward_check_agent_benchmarks.config import test_cases, reward_check_prompts - - -def run_reward_check_tests(): - total_tests = len(test_cases) - passed_tests = 0 - - for i, test_case in enumerate(test_cases, 1): - pool_id = test_case["pool_id"] - wallet_address = test_case["wallet_address"] - - # Iterate over each prompt - for prompt_template in reward_check_prompts: - prompt = prompt_template.format(wallet_address, pool_id) - print("-" * 100) - print(f"Running test case {i}/{total_tests}: {prompt}") - - # Get the agent's response - agent_response = ask_claim_agent(prompt) - print(f"Agent response: {agent_response}") - - # Extract the reward value from the agent's response - agent_reward_value = extract_reward_value_from_response(agent_response) - print(f"Agent Returned Reward Value: {agent_reward_value}") - - # Get the real reward value from the blockchain - blockchain_reward_value = float(get_current_user_reward(wallet_address, pool_id)) - print(f"Blockchain Returned Reward Value: {blockchain_reward_value}") - - # Compare the values with a tolerance of 10% - tolerance = 0.10 - if abs(agent_reward_value - blockchain_reward_value) / blockchain_reward_value <= tolerance: - print(f"Test case {i} passed.") - passed_tests += 1 - i += 1 - print("-" * 100) - else: - print(f"Test case {i} failed. Agent returned {agent_reward_value}, expected {blockchain_reward_value}.") - print("-" * 100) - i += 1 - - print(f"\n{passed_tests}/{total_tests} test cases passed.") - - -if __name__ == "__main__": - run_reward_check_tests() diff --git a/submodules/moragents_dockers/README.md b/submodules/moragents_dockers/README.md index 38327fc..e07693e 100644 --- a/submodules/moragents_dockers/README.md +++ b/submodules/moragents_dockers/README.md @@ -1,41 +1,44 @@ -# Moragents +# Moragents ## Overview -This project is a Flask-based AI chat application featuring intelligent responses from various language models and embeddings. It includes file uploading, cryptocurrency swapping, and a delegator system to manage multiple agents. The application, along with a dApp for agent interaction, runs locally and is containerized with Docker. +This project is a Flask-based AI chat application featuring intelligent responses from various language models and embeddings. It includes file uploading, cryptocurrency swapping, and a delegator system to manage multiple agents. The application, along with a dApp for agent interaction, runs locally and is containerized with Docker. ## Pre-requisites -* [Download Ollama](https://ollama.com/ )for your operating system -* Then after finishing installation pull these two models: -```ollama pull llama3.1``` +- [Download Ollama](https://ollama.com/)for your operating system +- Then after finishing installation pull these two models: + +`ollama pull llama3.2:3b` -```ollama pull nomic-embed-text``` +`ollama pull nomic-embed-text` ## Run with Docker Compose -Docker compose will build and run two containers. One will be for the agents, the other will be for the UI. +Docker compose will build and run two containers. One will be for the agents, the other will be for the UI. 1. Ensure you're in the submodules/moragents_dockers folder - ```sh - $ cd submodules/moragents_dockers - ``` + + ```sh + $ cd submodules/moragents_dockers + ``` 2. Build Images and Launch Containers: - 1. For Intel / AMD / x86_64 - ```sh - docker-compose up - ``` + 1. For Intel / AMD / x86_64 + ```sh + docker-compose up + ``` 2. For Apple silicon (M1, M2, M3, etc) - ```sh - docker-compose -f docker-compose-apple.yml up - ``` + ```sh + docker-compose -f docker-compose-apple.yml up + ``` -Open in the browser: ```http://localhost:3333/``` +Open in the browser: `http://localhost:3333/` -Docker build will download the model. The first time that one of the agents are called, the model will be loaded into memory and this instance will be shared between all agents. +Docker build will download the model. The first time that one of the agents are called, the model will be loaded into memory and this instance will be shared between all agents. ## Agents + Five agents are included: ### Data Agent @@ -53,6 +56,7 @@ It currently supports the following metrics: It is possible to ask questions about assets by referring to them either by their name or their ticker symbol. ### Swap Agent + This agent will enable you to perform swaps between cryptoassets. It should be used with the accompanying UI which provides a browser-based front-end to chat with the agent, display quotes and sign transactions. A typical flow looks like this: @@ -65,35 +69,43 @@ A typical flow looks like this: - If the allowance for the token being sold is too low, an approval transaction will be generated first ## RAG Agent + This agent will answer questions about an uploaded PDF file. ## Tweet Sizzler Agent -This agent will let you generate tweets, edit with a WSYWIG. -Provided you enter API creds in the Settings you can also directly post to your X account. + +This agent will let you generate tweets, edit with a WSYWIG. +Provided you enter API creds in the Settings you can also directly post to your X account. ## MOR Rewards Agent + Ask the agent to check your MOR rewards and it will retrieve claimable MOR stats from both capital and coder pools. --- # Delegator + The Delegator handles user queries by analyzing the prompt and delegating it to the appropriate agent. ## API Endpoints 1. **Chat Functionality** + - Endpoint: `POST /` - Handles chat interactions, delegating to appropriate agents when necessary. 2. **Message History** + - Endpoint: `GET /messages` - Retrieves chat message history. 3. **Clear Messages** + - Endpoint: `GET /clear_messages` - Clears the chat message history. 4. **Swap Operations** + - Endpoints: - `POST /tx_status`: Check transaction status - `POST /allowance`: Get allowance @@ -129,6 +141,7 @@ This allows the delegator to delegate to the correct task agent based on the use - `upload`: A boolean indicating if the agent requires a file to be uploaded from the front-end before it should be called. #### Example: + ```python:agents/src/config.py DELEGATOR_CONFIG = { "agents": [ @@ -144,13 +157,13 @@ DELEGATOR_CONFIG = { } ``` - ### 3. Implement Agent Logic 1. **Define the agent class** in the specified path. 2. **Ensure the agent can handle the queries** it is designed for. #### Example: + ```python:agents/src/new_agent/src/agent.py class NewAgent: def __init__(self, agent_info, llm, llm_ollama, embeddings, flask_app): @@ -177,12 +190,12 @@ class NewAgent: # Add other methods as needed ``` - ### 4. Handle Multi-Turn Conversations Agents can handle multi-turn conversations by returning a next_turn_agent which indicates the name of the agent that should handle the next turn. #### Example: + ```python class NewAgent: def __init__(self, agent_info, llm, llm_ollama, embeddings, flask_app): @@ -205,7 +218,7 @@ class NewAgent: def chat(self, request, user_id): # Process the query and determine the next agent next_turn_agent = self.agent_info["name"] - + # Generate response where we want to initiate a multi-turn conversation with the same agent. return response, next_turn_agent @@ -215,6 +228,7 @@ class NewAgent: ### 5. Integration The `Delegator` will automatically: + - Import the agent module. - Instantiate the agent class. - Add the agent to its internal dictionary. diff --git a/submodules/benchmarks/__init__.py b/submodules/moragents_dockers/__init__.py similarity index 100% rename from submodules/benchmarks/__init__.py rename to submodules/moragents_dockers/__init__.py diff --git a/submodules/moragents_dockers/agents/Dockerfile b/submodules/moragents_dockers/agents/Dockerfile index 52bcec7..14b0556 100644 --- a/submodules/moragents_dockers/agents/Dockerfile +++ b/submodules/moragents_dockers/agents/Dockerfile @@ -12,20 +12,11 @@ RUN apt-get update && apt-get install -y gcc g++ procps && rm -rf /var/lib/apt/l # Install Python dependencies RUN python3 -m pip install --no-cache-dir --upgrade pip && \ python3 -m pip install --no-cache-dir --upgrade -r requirements.txt - -COPY download_model.py . -COPY model_config.py . - -RUN python3 download_model.py - -copy . . +COPY . . # Expose the port your application listens on EXPOSE 5000 -# Set the environment variable for Flask -ENV FLASK_APP=src/app.py - -# Run the application -CMD ["flask", "run", "--host", "0.0.0.0"] \ No newline at end of file +# 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 diff --git a/submodules/moragents_dockers/agents/Dockerfile-apple b/submodules/moragents_dockers/agents/Dockerfile-apple index ad80504..2e28f3b 100644 --- a/submodules/moragents_dockers/agents/Dockerfile-apple +++ b/submodules/moragents_dockers/agents/Dockerfile-apple @@ -12,21 +12,11 @@ RUN apt-get update && apt-get install -y gcc g++ procps && rm -rf /var/lib/apt/l # Install Python dependencies RUN python3 -m pip install --no-cache-dir --upgrade pip && \ python3 -m pip install --no-cache-dir --upgrade -r requirements.txt - -COPY download_model.py . -COPY model_config.py . - -RUN python3 download_model.py - -copy . . +COPY . . # Expose the port your application listens on EXPOSE 5000 - -# Set the environment variable for Flask -ENV FLASK_APP=src/app.py - -# Run the application -CMD ["flask", "run", "--host", "0.0.0.0"] \ No newline at end of file +# 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 diff --git a/submodules/benchmarks/claim_agent_benchmarks/__init__.py b/submodules/moragents_dockers/agents/__init__.py similarity index 100% rename from submodules/benchmarks/claim_agent_benchmarks/__init__.py rename to submodules/moragents_dockers/agents/__init__.py diff --git a/submodules/moragents_dockers/agents/download_model.py b/submodules/moragents_dockers/agents/download_model.py deleted file mode 100644 index 55f3fd4..0000000 --- a/submodules/moragents_dockers/agents/download_model.py +++ /dev/null @@ -1,29 +0,0 @@ -import os -import shutil -from huggingface_hub import hf_hub_download -from model_config import Config - - -def download_model(model_name, revision): - """Function to download model from the hub""" - model_directory = hf_hub_download(repo_id=model_name, filename=revision) - return model_directory - - -def move_files(src_dir, dest_dir): - """Move files from source to destination directory.""" - for f in os.listdir(src_dir): - src_path = os.path.join(src_dir, f) - dst_path = os.path.join(dest_dir, f) - shutil.copy2(src_path, dst_path) - os.remove(src_path) - - -if __name__ == "__main__": - download_dir = Config.DOWNLOAD_DIR - os.makedirs(download_dir, exist_ok=True) - model_name = Config.MODEL_NAME - revision = Config.MODEL_REVISION - path = download_model(model_name, revision) - model_path = "/".join(path.split("/")[:-1]) + "/" - move_files(model_path, download_dir) diff --git a/submodules/moragents_dockers/agents/model_config.py b/submodules/moragents_dockers/agents/model_config.py deleted file mode 100644 index a5868e4..0000000 --- a/submodules/moragents_dockers/agents/model_config.py +++ /dev/null @@ -1,13 +0,0 @@ -import logging - -# Logging configuration -logging.basicConfig(level=logging.INFO) - - -# Configuration object -class Config: - # Model configuration - MODEL_NAME = "meetkai/functionary-small-v2.4-GGUF" - MODEL_REVISION = "functionary-small-v2.4.Q4_0.gguf" - MODEL_PATH = "model/" + MODEL_REVISION - DOWNLOAD_DIR = "model" diff --git a/submodules/moragents_dockers/agents/requirements.txt b/submodules/moragents_dockers/agents/requirements.txt index e87e422..8542cb4 100644 --- a/submodules/moragents_dockers/agents/requirements.txt +++ b/submodules/moragents_dockers/agents/requirements.txt @@ -1,17 +1,23 @@ llama-cpp-python==0.2.90 -transformers==4.43.3 sentencepiece==0.2.0 protobuf==5.27.2 scikit-learn==1.5.1 -huggingface-hub==0.24.3 -flask==2.2.2 +fastapi==0.115.0 Werkzeug==2.2.2 -flask-cors==4.0.1 web3==6.20.1 pymupdf==1.22.5 faiss-cpu==1.8.0.post1 -langchain-text-splitters==0.2.2 -langchain-core==0.2.24 -langchain-community==0.2.10 +feedparser +langchain-text-splitters==0.3.0 +langchain-core==0.3.9 +langchain-community==0.3.1 +langchain-ollama==0.2.0 +tweepy==4.14.0 +uvicorn==0.31.0 +python-dateutil +python-multipart==0.0.12 +beautifulsoup4==4.12.3 +selenium==4.25.0 torch -tweepy \ No newline at end of file +pytz +pyshorteners diff --git a/submodules/benchmarks/claim_agent_benchmarks/adapters/__init__.py b/submodules/moragents_dockers/agents/src/__init__.py similarity index 100% rename from submodules/benchmarks/claim_agent_benchmarks/adapters/__init__.py rename to submodules/moragents_dockers/agents/src/__init__.py diff --git a/submodules/benchmarks/price_fetching/__init__.py b/submodules/moragents_dockers/agents/src/agents/__init__.py similarity index 100% rename from submodules/benchmarks/price_fetching/__init__.py rename to submodules/moragents_dockers/agents/src/agents/__init__.py diff --git a/submodules/moragents_dockers/agents/src/data_agent/README.md b/submodules/moragents_dockers/agents/src/agents/crypto_data/README.md similarity index 100% rename from submodules/moragents_dockers/agents/src/data_agent/README.md rename to submodules/moragents_dockers/agents/src/agents/crypto_data/README.md diff --git a/submodules/benchmarks/price_fetching/adapters/__init__.py b/submodules/moragents_dockers/agents/src/agents/crypto_data/__init__.py similarity index 100% rename from submodules/benchmarks/price_fetching/adapters/__init__.py rename to submodules/moragents_dockers/agents/src/agents/crypto_data/__init__.py diff --git a/submodules/moragents_dockers/agents/src/agents/crypto_data/agent.py b/submodules/moragents_dockers/agents/src/agents/crypto_data/agent.py new file mode 100644 index 0000000..4240e55 --- /dev/null +++ b/submodules/moragents_dockers/agents/src/agents/crypto_data/agent.py @@ -0,0 +1,93 @@ +import json +import logging + +from src.agents.crypto_data import tools +from src.models.messages import ChatRequest +from src.stores import agent_manager + +logger = logging.getLogger(__name__) + + +class CryptoDataAgent: + def __init__(self, config, llm, embeddings): + self.config = config + self.llm = llm + self.embeddings = embeddings + self.tools_provided = tools.get_tools() + + def get_response(self, message): + system_prompt = ( + "Don't make assumptions about the value of the arguments for the function " + "they should always be supplied by the user and do not alter the value of the arguments. " + "Don't make assumptions about what values to plug into functions. Ask for clarification if a user " + "request is ambiguous." + ) + + messages = [ + {"role": "system", "content": system_prompt}, + ] + messages.extend(message) + + logger.info("Sending request to LLM with %d messages", len(messages)) + + llm_with_tools = self.llm.bind_tools(self.tools_provided) + + try: + result = llm_with_tools.invoke(messages) + logger.info("Received response from LLM: %s", result) + + if result.tool_calls: + tool_call = result.tool_calls[0] + func_name = tool_call.get("name") + args = tool_call.get("args") + logger.info("LLM suggested using tool: %s", func_name) + + if func_name == "get_price": + return tools.get_coin_price_tool(args["coin_name"]), "assistant" + elif func_name == "get_floor_price": + return tools.get_nft_floor_price_tool(args["nft_name"]), "assistant" + elif func_name == "get_fdv": + return ( + tools.get_fully_diluted_valuation_tool(args["coin_name"]), + "assistant", + ) + elif func_name == "get_tvl": + return ( + tools.get_protocol_total_value_locked_tool( + args["protocol_name"] + ), + "assistant", + ) + elif func_name == "get_market_cap": + return ( + tools.get_coin_market_cap_tool(args["coin_name"]), + "assistant", + ) + else: + logger.info("LLM provided a direct response without using tools") + return result.content, "assistant" + except Exception as e: + logger.error(f"Error in get_response: {str(e)}") + return f"An error occurred: {str(e)}", "assistant" + + def generate_response(self, prompt): + response, role = self.get_response([prompt]) + return response, role + + def chat(self, request: ChatRequest): + try: + data = request.dict() + if "prompt" in data: + prompt = data["prompt"] + logger.info( + "Received chat request with prompt: %s", + prompt[:50] + "..." if len(prompt) > 50 else prompt, + ) + response, role = self.generate_response(prompt) + return {"role": role, "content": response} + else: + logger.warning("Received chat request without 'prompt' in data") + return {"error": "Missing required parameters"}, 400 + except Exception as e: + logger.error("Error in chat method: %s", str(e), exc_info=True) + return {"Error": str(e)}, 500 diff --git a/submodules/moragents_dockers/agents/src/data_agent/src/config.py b/submodules/moragents_dockers/agents/src/agents/crypto_data/config.py similarity index 70% rename from submodules/moragents_dockers/agents/src/data_agent/src/config.py rename to submodules/moragents_dockers/agents/src/agents/crypto_data/config.py index 0c916ec..63db777 100644 --- a/submodules/moragents_dockers/agents/src/data_agent/src/config.py +++ b/submodules/moragents_dockers/agents/src/agents/crypto_data/config.py @@ -3,25 +3,25 @@ # Logging configuration logging.basicConfig(level=logging.INFO) + # Configuration object class Config: - # Model configuration - MODEL_NAME = "meetkai/functionary-small-v2.4-GGUF" - MODEL_REVISION = "functionary-small-v2.4.Q4_0.gguf" - MODEL_PATH = "model/"+MODEL_REVISION - DOWNLOAD_DIR = "model" + # API endpoints COINGECKO_BASE_URL = "https://api.coingecko.com/api/v3" DEFILLAMA_BASE_URL = "https://api.llama.fi" 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." - \ No newline at end of file diff --git a/submodules/moragents_dockers/agents/src/agents/crypto_data/routes.py b/submodules/moragents_dockers/agents/src/agents/crypto_data/routes.py new file mode 100644 index 0000000..d8417f5 --- /dev/null +++ b/submodules/moragents_dockers/agents/src/agents/crypto_data/routes.py @@ -0,0 +1,14 @@ +import logging + +from flask import Blueprint, request, jsonify + +crypto_data_agent_bp = Blueprint('crypto_data_agent', __name__) +logger = logging.getLogger(__name__) + +@crypto_data_agent_bp.route('/process_data', methods=['POST']) +def process_data(): + logger.info("Data Agent: Received process_data request") + data = request.get_json() + # Implement your data processing logic here + response = {"status": "success", "message": "Data processed"} + return jsonify(response) diff --git a/submodules/moragents_dockers/agents/src/data_agent/src/tools.py b/submodules/moragents_dockers/agents/src/agents/crypto_data/tools.py similarity index 64% rename from submodules/moragents_dockers/agents/src/data_agent/src/tools.py rename to submodules/moragents_dockers/agents/src/agents/crypto_data/tools.py index e02839a..0b0b100 100644 --- a/submodules/moragents_dockers/agents/src/data_agent/src/tools.py +++ b/submodules/moragents_dockers/agents/src/agents/crypto_data/tools.py @@ -1,9 +1,9 @@ import requests import logging -from data_agent.src.config import Config from sklearn.feature_extraction.text import TfidfVectorizer from sklearn.metrics.pairwise import cosine_similarity +from src.agents.crypto_data.config import Config def get_most_similar(text, data): @@ -13,9 +13,12 @@ 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 + def get_coingecko_id(text, type="coin"): """Get the CoinGecko ID for a given coin or NFT.""" url = f"{Config.COINGECKO_BASE_URL}/search" @@ -25,30 +28,32 @@ def get_coingecko_id(text, type="coin"): response.raise_for_status() data = response.json() if type == "coin": - return data['coins'][0]['id'] if data['coins'] else None + return data["coins"][0]["id"] if data["coins"] else None elif type == "nft": - return data['nfts'][0]['id'] if data.get('nfts') else None + return data["nfts"][0]["id"] if data.get("nfts") else None else: raise ValueError("Invalid type specified") except requests.exceptions.RequestException as e: logging.error(f"API request failed: {str(e)}") raise + def get_price(coin): """Get the price of a coin from CoinGecko API.""" coin_id = get_coingecko_id(coin, type="coin") if not coin_id: return None url = f"{Config.COINGECKO_BASE_URL}/simple/price" - params = {'ids': coin_id, 'vs_currencies': 'USD'} + params = {"ids": coin_id, "vs_currencies": "USD"} try: response = requests.get(url, params=params) response.raise_for_status() - return response.json()[coin_id]['usd'] + return response.json()[coin_id]["usd"] except requests.exceptions.RequestException as e: logging.error(f"Failed to retrieve price: {str(e)}") raise + def get_floor_price(nft): """Get the floor price of an NFT from CoinGecko API.""" nft_id = get_coingecko_id(str(nft), type="nft") @@ -63,6 +68,7 @@ def get_floor_price(nft): logging.error(f"Failed to retrieve floor price: {str(e)}") raise + def get_fdv(coin): """Get the fully diluted valuation of a coin from CoinGecko API.""" coin_id = get_coingecko_id(coin, type="coin") @@ -78,21 +84,23 @@ def get_fdv(coin): logging.error(f"Failed to retrieve FDV: {str(e)}") raise + def get_market_cap(coin): """Get the market cap of a coin from CoinGecko API.""" coin_id = get_coingecko_id(coin, type="coin") if not coin_id: return None url = f"{Config.COINGECKO_BASE_URL}/coins/markets" - params = {'ids': coin_id, 'vs_currency': 'USD'} + params = {"ids": coin_id, "vs_currency": "USD"} try: response = requests.get(url, params=params) response.raise_for_status() - return response.json()[0]['market_cap'] + return response.json()[0]["market_cap"] except requests.exceptions.RequestException as e: logging.error(f"Failed to retrieve market cap: {str(e)}") raise + def get_protocols_list(): """Get the list of protocols from DefiLlama API.""" url = f"{Config.DEFILLAMA_BASE_URL}/protocols" @@ -100,11 +108,16 @@ def get_protocols_list(): response = requests.get(url) response.raise_for_status() data = response.json() - return [item['slug'] for item in data] ,[item['name'] for item in data] ,[item['gecko_id'] for item in data] + return ( + [item["slug"] for item in data], + [item["name"] for item in data], + [item["gecko_id"] for item in data], + ) except requests.exceptions.RequestException as e: logging.error(f"Failed to retrieve protocols list: {str(e)}") raise + def get_tvl_value(protocol_id): """Gets the TVL value using the protocol ID from DefiLlama API.""" url = f"{Config.DEFILLAMA_BASE_URL}/tvl/{protocol_id}" @@ -114,11 +127,12 @@ def get_tvl_value(protocol_id): return response.json() except requests.exceptions.RequestException as e: logging.error(f"Failed to retrieve protocol TVL: {str(e)}") - raise + raise + def get_protocol_tvl(protocol_name): """Get the TVL (Total Value Locked) of a protocol from DefiLlama API.""" - id,name,gecko = get_protocols_list() + id, name, gecko = get_protocols_list() tag = get_coingecko_id(protocol_name) if tag: protocol_id = next((i for i, j in zip(id, gecko) if j == tag), None) @@ -157,7 +171,9 @@ 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 @@ -168,8 +184,10 @@ def get_protocol_total_value_locked_tool(protocol_name): tvl = get_protocol_tvl(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) + 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 + ) except requests.exceptions.RequestException: return Config.API_ERROR_MESSAGE @@ -191,96 +209,99 @@ 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 + def get_tools(): """Return a list of tools for the agent.""" return [ { - "type": "function", - "function": { - "name": "get_price", - "description": "Get the price of a cryptocurrency", - "parameters": { - "type": "object", - "properties": { - "coin_name": { - "type": "string", - "description": "The name of the coin.", - } + "type": "function", + "function": { + "name": "get_price", + "description": "Get the price of a cryptocurrency", + "parameters": { + "type": "object", + "properties": { + "coin_name": { + "type": "string", + "description": "The name of the coin.", + } + }, + "required": ["coin_name"], }, - "required": ["coin_name"], }, }, - }, { - "type": "function", - "function": { - "name": "get_floor_price", - "description": "Get the floor price of an NFT", - "parameters": { - "type": "object", - "properties": { - "nft_name": { - "type": "string", - "description": "Name of the NFT", - } + "type": "function", + "function": { + "name": "get_floor_price", + "description": "Get the floor price of an NFT", + "parameters": { + "type": "object", + "properties": { + "nft_name": { + "type": "string", + "description": "Name of the NFT", + } + }, + "required": ["nft_name"], }, - "required": ["nft_name"], }, }, - }, - { - "type": "function", - "function": { - "name": "get_tvl", - "description": "Get the TVL (Total Value Locked) of a protocol.", - "parameters": { - "type": "object", - "properties": { - "protocol_name": { - "type": "string", - "description": "Name of the protocol", - } + { + "type": "function", + "function": { + "name": "get_tvl", + "description": "Get the TVL (Total Value Locked) of a protocol.", + "parameters": { + "type": "object", + "properties": { + "protocol_name": { + "type": "string", + "description": "Name of the protocol", + } + }, + "required": ["protocol_name"], }, - "required": ["protocol_name"], }, }, - }, - { - "type": "function", - "function": { - "name": "get_fdv", - "description": "Get the fdv or fully diluted valuation of a coin", - "parameters": { - "type": "object", - "properties": { - "coin_name": { - "type": "string", - "description": "Name of the coin", - } + { + "type": "function", + "function": { + "name": "get_fdv", + "description": "Get the fdv or fully diluted valuation of a coin", + "parameters": { + "type": "object", + "properties": { + "coin_name": { + "type": "string", + "description": "Name of the coin", + } + }, + "required": ["coin_name"], }, - "required": ["coin_name"], }, }, - } , - { - "type": "function", - "function": { - "name": "get_market_cap", - "description": "Get the mc or market cap of a coin", - "parameters": { - "type": "object", - "properties": { - "coin_name": { - "type": "string", - "description": "Name of the coin", - } + { + "type": "function", + "function": { + "name": "get_market_cap", + "description": "Get the mc or market cap of a coin", + "parameters": { + "type": "object", + "properties": { + "coin_name": { + "type": "string", + "description": "Name of the coin", + } + }, + "required": ["coin_name"], }, - "required": ["coin_name"], }, }, - } ] diff --git a/submodules/benchmarks/reward_check_agent_benchmarks/__init__.py b/submodules/moragents_dockers/agents/src/agents/mor_claims/__init__.py similarity index 100% rename from submodules/benchmarks/reward_check_agent_benchmarks/__init__.py rename to submodules/moragents_dockers/agents/src/agents/mor_claims/__init__.py diff --git a/submodules/moragents_dockers/agents/src/agents/mor_claims/agent.py b/submodules/moragents_dockers/agents/src/agents/mor_claims/agent.py new file mode 100644 index 0000000..a18f7f4 --- /dev/null +++ b/submodules/moragents_dockers/agents/src/agents/mor_claims/agent.py @@ -0,0 +1,170 @@ +from src.agents.mor_claims import tools +from src.models.messages import ChatRequest +from src.stores import agent_manager + + +class MorClaimsAgent: + def __init__(self, agent_info, llm, embeddings): + self.agent_info = agent_info + self.llm = llm + self.embeddings = embeddings + self.tools_provided = tools.get_tools() + self.conversation_state = {} + + def _get_response(self, message, wallet_address): + if wallet_address not in self.conversation_state: + self.conversation_state[wallet_address] = {"state": "initial"} + + state = self.conversation_state[wallet_address]["state"] + + if state == "initial": + agent_manager.set_active_agent(self.agent_info["name"]) + + rewards = { + 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 + } + + 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" + 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", + self.agent_info["name"], + ) + else: + return ( + f"No rewards found for your wallet address {wallet_address} in either pool. Claim cannot be processed.", + "assistant", + None, + ) + + elif state == "awaiting_confirmation": + user_input = message[-1]["content"].lower() + if any( + word in user_input for word in ["yes", "proceed", "confirm", "claim"] + ): + return self.prepare_transactions(wallet_address) + else: + return ( + "Please confirm if you want to proceed with the claim by saying 'yes', 'proceed', 'confirm', or 'claim'.", + "assistant", + self.agent_info["name"], + ) + + return ( + "I'm sorry, I didn't understand that. Can you please rephrase your request?", + "assistant", + self.agent_info["name"], + ) + + def prepare_transactions(self, wallet_address): + available_rewards = self.conversation_state[wallet_address]["available_rewards"] + receiver_address = self.conversation_state[wallet_address]["receiver_address"] + transactions = [] + + for pool_id in available_rewards.keys(): + try: + tx_data = tools.prepare_claim_transaction(pool_id, receiver_address) + transactions.append({"pool": pool_id, "transaction": tx_data}) + except Exception as e: + return ( + f"Error preparing transaction for pool {pool_id}: {str(e)}", + "assistant", + None, + ) + + self.conversation_state[wallet_address]["transactions"] = transactions + + # Return a structured response + return ( + { + "role": "claim", + "content": {"transactions": transactions, "claim_tx_cb": "/claim"}, + }, + "claim", + None, + ) + + def chat(self, request: ChatRequest): + try: + data = request.dict() + 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 + ) + return { + "role": role, + "content": response, + "next_turn_agent": next_turn_agent, + } + else: + return {"error": "Missing required parameters"}, 400 + except Exception as e: + return {"Error": str(e)}, 500 + + def claim(self, request: ChatRequest): + try: + data = request.dict() + wallet_address = data["wallet_address"] + transactions = self.conversation_state[wallet_address]["transactions"] + agent_manager.clear_active_agent() + return {"transactions": transactions} + except Exception as e: + return {"error": str(e)}, 500 + + def claim_status(self, request: ChatRequest): + try: + data = request.dict() + wallet_address = data.get("wallet_address") + transaction_hash = data.get("transaction_hash") + status = data.get("status") + + if not all([wallet_address, transaction_hash, status]): + return {"error": "Missing required parameters"}, 400 + + # Generate and return the status message + response = self.get_status(status, transaction_hash, "claim") + return response, 200 + except Exception as e: + return {"error": str(e)}, 500 + + def get_status(self, flag, tx_hash, tx_type): + response = "" + + if flag == "cancelled": + response = f"The claim transaction has been cancelled." + elif flag == "success": + response = f"The claim transaction was successful." + 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." + ) + + if tx_hash: + response = ( + response + f" The transaction hash is {tx_hash}. " + f"Here's the link to the Etherscan transaction: " + f"https://etherscan.io/tx/{tx_hash}" + ) + + if flag != "initiated": + response = response + " Is there anything else I can help you with?" + + return {"role": "assistant", "content": response} diff --git a/submodules/moragents_dockers/agents/src/agents/mor_claims/config.py b/submodules/moragents_dockers/agents/src/agents/mor_claims/config.py new file mode 100644 index 0000000..44d7639 --- /dev/null +++ b/submodules/moragents_dockers/agents/src/agents/mor_claims/config.py @@ -0,0 +1,35 @@ +import logging + +# Logging configuration +logging.basicConfig(level=logging.INFO) + + +# Configuration object +class Config: + + WEB3RPCURL = {"1": "https://eth.llamarpc.com/"} + MINT_FEE = 0.001 # in ETH + + DISTRIBUTION_PROXY_ADDRESS = "0x47176B2Af9885dC6C4575d4eFd63895f7Aaa4790" + DISTRIBUTION_ABI = [ + { + "inputs": [ + {"internalType": "uint256", "name": "poolId_", "type": "uint256"}, + {"internalType": "address", "name": "receiver_", "type": "address"}, + ], + "name": "claim", + "outputs": [], + "stateMutability": "payable", + "type": "function", + }, + { + "inputs": [ + {"internalType": "uint256", "name": "poolId_", "type": "uint256"}, + {"internalType": "address", "name": "user_", "type": "address"}, + ], + "name": "getCurrentUserReward", + "outputs": [{"internalType": "uint256", "name": "", "type": "uint256"}], + "stateMutability": "view", + "type": "function", + }, + ] diff --git a/submodules/moragents_dockers/agents/src/claim_agent/src/tools.py b/submodules/moragents_dockers/agents/src/agents/mor_claims/tools.py similarity index 71% rename from submodules/moragents_dockers/agents/src/claim_agent/src/tools.py rename to submodules/moragents_dockers/agents/src/agents/mor_claims/tools.py index bee91c6..e5f0143 100644 --- a/submodules/moragents_dockers/agents/src/claim_agent/src/tools.py +++ b/submodules/moragents_dockers/agents/src/agents/mor_claims/tools.py @@ -1,11 +1,13 @@ from web3 import Web3 -from claim_agent.src.config import Config + +from src.agents.mor_claims.config import Config + 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: @@ -13,37 +15,41 @@ 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)}") + def prepare_claim_transaction(pool_id, wallet_address): try: web3 = Web3(Web3.HTTPProvider(Config.WEB3RPCURL["1"])) contract = web3.eth.contract( address=web3.to_checksum_address(Config.DISTRIBUTION_PROXY_ADDRESS), - abi=Config.DISTRIBUTION_ABI + abi=Config.DISTRIBUTION_ABI, + ) + tx_data = contract.encode_abi( + fn_name="claim", args=[pool_id, web3.to_checksum_address(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} ) - tx_data = contract.encode_abi(fn_name="claim", args=[pool_id, web3.to_checksum_address(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 - }) return { "to": Config.DISTRIBUTION_PROXY_ADDRESS, "data": tx_data, "value": str(mint_fee), "gas": str(estimated_gas), - "chainId": "1" + "chainId": "1", } except Exception as e: raise Exception(f"Failed to prepare claim transaction: {str(e)}") + def get_tools(): return [ { @@ -56,16 +62,16 @@ def get_tools(): "properties": { "wallet_address": { "type": "string", - "description": "The wallet address to check rewards for" + "description": "The wallet address to check rewards for", }, "pool_id": { "type": "integer", - "description": "The ID of the pool to check rewards from" - } + "description": "The ID of the pool to check rewards from", + }, }, - "required": ["wallet_address", "pool_id"] - } - } + "required": ["wallet_address", "pool_id"], + }, + }, }, { "type": "function", @@ -77,15 +83,15 @@ def get_tools(): "properties": { "pool_id": { "type": "integer", - "description": "The ID of the pool to claim from" + "description": "The ID of the pool to claim from", }, "wallet_address": { "type": "string", - "description": "The wallet address to claim rewards for" - } + "description": "The wallet address to claim rewards for", + }, }, - "required": ["pool_id", "wallet_address"] - } - } - } - ] \ No newline at end of file + "required": ["pool_id", "wallet_address"], + }, + }, + }, + ] diff --git a/submodules/benchmarks/reward_check_agent_benchmarks/adapters/__init__.py b/submodules/moragents_dockers/agents/src/agents/mor_rewards/__init__.py similarity index 100% rename from submodules/benchmarks/reward_check_agent_benchmarks/adapters/__init__.py rename to submodules/moragents_dockers/agents/src/agents/mor_rewards/__init__.py diff --git a/submodules/moragents_dockers/agents/src/reward_agent/src/agent.py b/submodules/moragents_dockers/agents/src/agents/mor_rewards/agent.py similarity index 62% rename from submodules/moragents_dockers/agents/src/reward_agent/src/agent.py rename to submodules/moragents_dockers/agents/src/agents/mor_rewards/agent.py index 44555da..d3dfff1 100644 --- a/submodules/moragents_dockers/agents/src/reward_agent/src/agent.py +++ b/submodules/moragents_dockers/agents/src/agents/mor_rewards/agent.py @@ -1,17 +1,16 @@ -import json import logging -from reward_agent.src import tools + +from src.agents.mor_rewards import tools +from src.models.messages import ChatRequest logger = logging.getLogger(__name__) -class RewardAgent: - def __init__(self, agent_info, llm, llm_ollama, embeddings, flask_app): +class MorRewardsAgent: + def __init__(self, agent_info, llm, embeddings): self.agent_info = agent_info self.llm = llm - self.llm_ollama = llm_ollama self.embeddings = embeddings - self.flask_app = flask_app self.tools_provided = tools.get_tools() def get_response(self, message, wallet_address): @@ -20,7 +19,7 @@ def get_response(self, message, wallet_address): try: rewards = { 0: tools.get_current_user_reward(wallet_address, 0), - 1: tools.get_current_user_reward(wallet_address, 1) + 1: tools.get_current_user_reward(wallet_address, 1), } response = f"Your current MOR rewards:\n" @@ -31,16 +30,26 @@ def get_response(self, message, wallet_address): return response, "assistant", None except Exception as e: logger.error(f"Error occurred while checking rewards: {str(e)}") - return f"An error occurred while checking your rewards: {str(e)}", "assistant", None + return ( + f"An error occurred while checking your rewards: {str(e)}", + "assistant", + None, + ) - def chat(self, request): + def chat(self, request: ChatRequest): try: - data = request.get_json() - 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) - return {"role": role, "content": response, "next_turn_agent": next_turn_agent} + data = request.dict() + 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 + ) + return { + "role": role, + "content": response, + "next_turn_agent": next_turn_agent, + } else: logger.warning("Missing required parameters in request") return {"error": "Missing required parameters"}, 400 diff --git a/submodules/moragents_dockers/agents/src/agents/mor_rewards/config.py b/submodules/moragents_dockers/agents/src/agents/mor_rewards/config.py new file mode 100644 index 0000000..e8ac97e --- /dev/null +++ b/submodules/moragents_dockers/agents/src/agents/mor_rewards/config.py @@ -0,0 +1,26 @@ +import logging + +# Logging configuration +logging.basicConfig(level=logging.INFO) + + +# Configuration object +class Config: + + WEB3RPCURL = { + "1": "https://eth.llamarpc.com/", + } + + DISTRIBUTION_PROXY_ADDRESS = "0x47176B2Af9885dC6C4575d4eFd63895f7Aaa4790" + DISTRIBUTION_ABI = [ + { + "inputs": [ + {"internalType": "uint256", "name": "poolId_", "type": "uint256"}, + {"internalType": "address", "name": "user_", "type": "address"}, + ], + "name": "getCurrentUserReward", + "outputs": [{"internalType": "uint256", "name": "", "type": "uint256"}], + "stateMutability": "view", + "type": "function", + } + ] diff --git a/submodules/moragents_dockers/agents/src/reward_agent/src/tools.py b/submodules/moragents_dockers/agents/src/agents/mor_rewards/tools.py similarity index 73% rename from submodules/moragents_dockers/agents/src/reward_agent/src/tools.py rename to submodules/moragents_dockers/agents/src/agents/mor_rewards/tools.py index cbb749f..553041e 100644 --- a/submodules/moragents_dockers/agents/src/reward_agent/src/tools.py +++ b/submodules/moragents_dockers/agents/src/agents/mor_rewards/tools.py @@ -1,11 +1,13 @@ from web3 import Web3 -from reward_agent.src.config import Config + +from src.agents.mor_rewards.config import Config + 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 + address=web3.to_checksum_address(Config.DISTRIBUTION_PROXY_ADDRESS), + abi=Config.DISTRIBUTION_ABI, ) try: @@ -13,14 +15,14 @@ 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)}") + def get_tools(): return [ { @@ -33,15 +35,15 @@ def get_tools(): "properties": { "wallet_address": { "type": "string", - "description": "The wallet address to check rewards for" + "description": "The wallet address to check rewards for", }, "pool_id": { "type": "integer", - "description": "The ID of the pool to check rewards from" - } + "description": "The ID of the pool to check rewards from", + }, }, - "required": ["wallet_address", "pool_id"] - } - } + "required": ["wallet_address", "pool_id"], + }, + }, } - ] \ No newline at end of file + ] diff --git a/submodules/moragents_dockers/agents/src/agents/news_agent/__init__.py b/submodules/moragents_dockers/agents/src/agents/news_agent/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/submodules/moragents_dockers/agents/src/agents/news_agent/agent.py b/submodules/moragents_dockers/agents/src/agents/news_agent/agent.py new file mode 100644 index 0000000..ecbb4d0 --- /dev/null +++ b/submodules/moragents_dockers/agents/src/agents/news_agent/agent.py @@ -0,0 +1,160 @@ +import json +import logging +import re +import urllib.parse +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.models.messages import ChatRequest +import pyshorteners + +logger = logging.getLogger(__name__) + + +class NewsAgent: + def __init__(self, agent_info, llm, embeddings): + self.agent_info = agent_info + self.llm = llm + self.embeddings = embeddings + self.tools_provided = self.get_tools() + self.url_shortener = pyshorteners.Shortener() + logger.info("NewsAgent initialized") + + def get_tools(self): + return [ + { + "type": "function", + "function": { + "name": "fetch_crypto_news", + "description": "Fetch and analyze cryptocurrency news for potential price impacts", + "parameters": { + "type": "object", + "properties": { + "coins": { + "type": "array", + "items": {"type": "string"}, + "description": "List of cryptocurrency symbols to fetch news for", + } + }, + "required": ["coins"], + }, + }, + + } + ] + + def check_relevance_and_summarize(self, title, content, coin): + logger.info(f"Checking relevance for {coin}: {title}") + prompt = Config.RELEVANCE_PROMPT.format(coin=coin, title=title, content=content) + result = self.llm.invoke( + input=[{"role": "user", "content": prompt}], + max_tokens=Config.LLM_MAX_TOKENS, + temperature=Config.LLM_TEMPERATURE, + ) + return result.content.strip() + + def process_rss_feed(self, feed_url, coin): + logger.info(f"Processing RSS feed for {coin}: {feed_url}") + feed = fetch_rss_feed(feed_url) + results = [] + for entry in feed.entries: + published_time = entry.get("published") or entry.get("updated") + if is_within_time_window(published_time): + title = clean_html(entry.title) + content = clean_html(entry.summary) + 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} + ) + if len(results) >= Config.ARTICLES_PER_TOKEN: + break + else: + logger.info( + f"Skipping article: {entry.title} (published: {published_time})" + ) + logger.info(f"Found {len(results)} relevant articles for {coin}") + return results + + def fetch_crypto_news(self, coins): + logger.info(f"Fetching news for coins: {coins}") + all_news = [] + for coin in coins: + logger.info(f"Processing news for {coin}") + coin_name = Config.CRYPTO_DICT.get(coin.upper(), coin) + 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] + ] + ) + + logger.info(f"Total news items fetched: {len(all_news)}") + return all_news + + def chat(self, request: ChatRequest): + try: + data = request.dict() + if "prompt" in data: + prompt = data["prompt"] + if isinstance(prompt, dict) and "content" in prompt: + prompt = prompt["content"] + + # Updated coin detection logic + coins = re.findall( + r"\b(" + + "|".join(re.escape(key) for key in Config.CRYPTO_DICT.keys()) + + r")\b", + prompt.upper(), + ) + + if not coins: + return { + "role": "assistant", + "content": "I couldn't identify any cryptocurrency symbols in your message. Please specify the cryptocurrencies you want news for.", + "next_turn_agent": None, + } + + news = self.fetch_crypto_news(coins) + + if not news: + return { + "role": "assistant", + "content": "No relevant news found for the specified cryptocurrencies in the last 24 hours.", + "next_turn_agent": None, + } + + response = "Here are the latest news items relevant to changes in price movement of the mentioned tokens in the last 24 hours:\n\n" + for index, item in enumerate(news, start=1): + coin_name = Config.CRYPTO_DICT.get(item["Coin"], item["Coin"]) + short_url = self.url_shortener.tinyurl.short(item["Link"]) + response += f"{index}. ***{coin_name} News***:\n" + response += f"{item['Title']}\n" + response += f"{item['Summary']}\n" + response += f"Read more: {short_url}\n\n" + + return { + "role": "assistant", + "content": response, + "next_turn_agent": None, + } + else: + return { + "role": "assistant", + "content": "Missing required parameters", + "next_turn_agent": None, + } + + except Exception as e: + logger.error(f"Error in chat method: {str(e)}", exc_info=True) + return { + "role": "assistant", + "content": f"An error occurred: {str(e)}", + "next_turn_agent": None, + } diff --git a/submodules/moragents_dockers/agents/src/agents/news_agent/config.py b/submodules/moragents_dockers/agents/src/agents/news_agent/config.py new file mode 100644 index 0000000..e9fe7d3 --- /dev/null +++ b/submodules/moragents_dockers/agents/src/agents/news_agent/config.py @@ -0,0 +1,130 @@ +import logging + +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" + + # Time window for news (in hours) + NEWS_TIME_WINDOW = 24 + + # Number of articles to show per token + ARTICLES_PER_TOKEN = 1 + + # LLM configuration + LLM_MAX_TOKENS = 150 + LLM_TEMPERATURE = 0.3 + + # Prompts + RELEVANCE_PROMPT = ( + "Consider the following news article about {coin}:\n\n" + "Title: {title}\n\nContent: {content}\n\n" + "Is this article relevant to potential price impacts on the cryptocurrency? " + "If yes, provide a concise summary focused on how it might impact trading or prices. " + "If it's not relevant or only about price movements, respond with 'NOT RELEVANT'." + ) + + # 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 diff --git a/submodules/moragents_dockers/agents/src/agents/news_agent/tools.py b/submodules/moragents_dockers/agents/src/agents/news_agent/tools.py new file mode 100644 index 0000000..f017b22 --- /dev/null +++ b/submodules/moragents_dockers/agents/src/agents/news_agent/tools.py @@ -0,0 +1,71 @@ +import feedparser +from datetime import datetime, timedelta +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) + cleantext = unescape(cleantext) + cleantext = ' '.join(cleantext.split()) + return cleantext + + +def is_within_time_window(published_time, hours=24): + if not published_time: + return False + try: + pub_date = parser.parse(published_time, fuzzy=True) + now = datetime.now(pytz.UTC) + if pub_date.tzinfo is None: + pub_date = pub_date.replace(tzinfo=pytz.UTC) + return (now - pub_date) <= timedelta(hours=hours) + except Exception as e: + logger.error(f"Error parsing date: {str(e)} for date {published_time}") + return False + + +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']] + encoded_query = urllib.parse.urlencode(query_params, doseq=True) + encoded_url = urllib.parse.urlunparse(parsed_url._replace(query=encoded_query)) + + return feedparser.parse(encoded_url) + + +def get_tools(): + """Return a list of tools for the agent.""" + return [ + { + "type": "function", + "function": { + "name": "fetch_crypto_news", + "description": "Fetch and analyze cryptocurrency news for potential price impacts", + "parameters": { + "type": "object", + "properties": { + "coins": { + "type": "array", + "items": { + "type": "string" + }, + "description": "List of cryptocurrency symbols to fetch news for" + } + }, + "required": ["coins"] + } + } + } + ] \ No newline at end of file diff --git a/submodules/moragents_dockers/agents/src/agents/rag/__init__.py b/submodules/moragents_dockers/agents/src/agents/rag/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/submodules/moragents_dockers/agents/src/agents/rag/agent.py b/submodules/moragents_dockers/agents/src/agents/rag/agent.py new file mode 100644 index 0000000..977de93 --- /dev/null +++ b/submodules/moragents_dockers/agents/src/agents/rag/agent.py @@ -0,0 +1,119 @@ +import os +import logging + +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 + +logger = logging.getLogger(__name__) + +UPLOAD_FOLDER = os.path.join(os.getcwd(), "uploads") + + +class RagAgent: + 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.prompt = ChatPromptTemplate.from_template( + """ + Answer the following question only based on the given context + + + {context} + + + Question: {input} + """ + ) + self.max_size = 5 * 1024 * 1024 + self.retriever = None + + async def handle_file_upload(self, file): + if not os.path.exists(UPLOAD_FOLDER): + os.makedirs(UPLOAD_FOLDER, exist_ok=True) + filename = secure_filename(file.filename) + file_path = os.path.join(UPLOAD_FOLDER, filename) + + # Save the file + with open(file_path, "wb") as buffer: + content = await file.read() + buffer.write(content) + + # DocumentToolsGenerator class instantiation + loader = PyMuPDFLoader(file_path) + docs = loader.load() + text_splitter = RecursiveCharacterTextSplitter( + chunk_size=1024, + chunk_overlap=20, + length_function=len, + is_separator_regex=False, + ) + split_documents = text_splitter.split_documents(docs) + vector_store = FAISS.from_documents(split_documents, self.embedding) + self.retriever = vector_store.as_retriever(search_kwargs={"k": 7}) + + async def upload_file(self, request: Request): + logger.info(f"Received upload request: {request}") + file = request["file"] + if file.filename == "": + return {"error": "No selected file"}, 400 + + # Check file size to ensure it's less than 5 MB + content = await file.read() + await file.seek(0) + if len(content) > self.max_size: + return {"role": "assistant", "content": "Please use a file less than 5 MB"} + + try: + await self.handle_file_upload(file) + chat_manager.set_uploaded_file(True) + return { + "role": "assistant", + "content": "You have successfully uploaded the text", + } + except Exception as e: + logging.error(f"Error during file upload: {str(e)}") + return {"error": str(e)}, 500 + + def _get_rag_response(self, prompt): + retrieved_docs = self.retriever.invoke(prompt) + formatted_context = "\n\n".join(doc.page_content for doc in retrieved_docs) + formatted_prompt = f"Question: {prompt}\n\nContext: {formatted_context}" + messages = [ + { + "role": "system", + "content": "You are a helpful assistant. Use the provided context to respond to the following question.", + }, + {"role": "user", "content": formatted_prompt}, + ] + result = self.llm.invoke(messages) + return result.content.strip() + + def chat(self, request: ChatRequest): + try: + data = request.dict() + if "prompt" in data: + prompt = data["prompt"]["content"] + if chat_manager.get_uploaded_file_status(): + response = self._get_rag_response(prompt) + else: + response = "Please upload a file first" + return {"role": "assistant", "content": response} + + else: + return {"error": "Missing required parameters"}, 400 + except Exception as e: + logging.error(f"Error in chat endpoint: {str(e)}") + return {"Error": str(e)}, 500 diff --git a/submodules/moragents_dockers/agents/src/rag_agent/src/config.py b/submodules/moragents_dockers/agents/src/agents/rag/config.py similarity index 59% rename from submodules/moragents_dockers/agents/src/rag_agent/src/config.py rename to submodules/moragents_dockers/agents/src/agents/rag/config.py index 63983c2..13716f4 100644 --- a/submodules/moragents_dockers/agents/src/rag_agent/src/config.py +++ b/submodules/moragents_dockers/agents/src/agents/rag/config.py @@ -3,7 +3,8 @@ # Logging configuration logging.basicConfig(level=logging.INFO) + # Configuration object class Config: - MAX_FILE_SIZE=5 * 1024 * 1024 # 5 MB - MAX_LENGTH=16 * 1024 * 1024 \ No newline at end of file + MAX_FILE_SIZE = 5 * 1024 * 1024 # 5 MB + MAX_LENGTH = 16 * 1024 * 1024 diff --git a/submodules/moragents_dockers/agents/src/agents/realtime_search/__init__.py b/submodules/moragents_dockers/agents/src/agents/realtime_search/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/submodules/moragents_dockers/agents/src/agents/realtime_search/agent.py b/submodules/moragents_dockers/agents/src/agents/realtime_search/agent.py new file mode 100644 index 0000000..1c098b9 --- /dev/null +++ b/submodules/moragents_dockers/agents/src/agents/realtime_search/agent.py @@ -0,0 +1,137 @@ +import logging +import requests +import time + +from bs4 import BeautifulSoup +from selenium import webdriver +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( + level=logging.INFO, format="%(asctime)s - %(name)s - %(levelname)s - %(message)s" +) +logger = logging.getLogger(__name__) + +USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36" + + +class RealtimeSearchAgent: + def __init__(self, config, llm, embeddings): + self.config = config + self.llm = llm + self.embeddings = embeddings + self.last_search_term = None + + def perform_search_with_web_scraping(self, search_term=None): + if search_term is not None: + self.last_search_term = search_term + elif self.last_search_term is None: + logger.warning("No search term available for web search") + return "Web search failed. Please provide a search term." + else: + search_term = self.last_search_term + + logger.info(f"Performing web search for: {search_term}") + + try: + url = f"https://www.google.com/search?q={search_term}" + headers = {"User-Agent": USER_AGENT} + response = requests.get(url, headers=headers) + response.raise_for_status() + + soup = BeautifulSoup(response.text, "html.parser") + + search_results = soup.find_all("div", class_="g") + + formatted_results = [] + for result in search_results[:5]: + result_text = result.get_text(strip=True) + formatted_results.append(f"Result:\n{result_text}") + + return "\n\n".join(formatted_results) + + except requests.RequestException as e: + logger.error(f"Error performing web search: {str(e)}") + logger.info("Attempting fallback to headless browsing") + return self.perform_search_with_headless_browsing(search_term) + + def perform_search_with_headless_browsing(self, search_term): + chrome_options = Options() + chrome_options.add_argument("--headless") + + driver = webdriver.Chrome(options=chrome_options) + + try: + driver.get("https://www.google.com") + + search_box = driver.find_element(By.NAME, "q") + search_box.send_keys(search_term) + search_box.send_keys(Keys.RETURN) + + time.sleep(2) + + soup = BeautifulSoup(driver.page_source, "html.parser") + + search_results = soup.find_all("div", class_="g") + + formatted_results = [] + for result in search_results[:5]: + result_text = result.get_text(strip=True) + formatted_results.append(f"Result:\n{result_text}") + + return "\n\n".join(formatted_results) + + except Exception as e: + logger.error(f"Error performing headless web search: {str(e)}") + return f"Error performing web search: {str(e)}" + finally: + driver.quit() + + def synthesize_answer(self, search_term, search_results): + logger.info("Synthesizing answer from 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. + Just provide the answer.""", + }, + { + "role": "user", + "content": f"""Based on the following search results for the query '{search_term}', provide a concise and informative answer: {search_results}""", + }, + ] + + try: + result = self.llm.invoke(messages) + logger.info(f"Received response from LLM: {result}") + return result.content.strip() + except Exception as e: + logger.error(f"Error synthesizing answer: {str(e)}") + raise + + def chat(self, request: ChatRequest): + try: + data = request.dict() + logger.info(f"Received chat request: {data}") + if "prompt" in data: + prompt = data["prompt"] + search_term = prompt["content"] + logger.info(f"Performing web search for prompt: {search_term}") + + search_results = self.perform_search_with_web_scraping(search_term) + logger.info(f"Search results obtained") + + synthesized_answer = self.synthesize_answer(search_term, search_results) + logger.info(f"Synthesized answer: {synthesized_answer}") + + return {"role": "assistant", "content": synthesized_answer} + else: + logger.error("Missing 'prompt' in chat request data") + return {"error": "Missing parameters"}, 400 + except Exception as e: + logger.exception(f"Unexpected error in chat method: {str(e)}") + return {"Error": str(e)}, 500 diff --git a/submodules/moragents_dockers/agents/src/swap_agent/README.md b/submodules/moragents_dockers/agents/src/agents/token_swap/README.md similarity index 100% rename from submodules/moragents_dockers/agents/src/swap_agent/README.md rename to submodules/moragents_dockers/agents/src/agents/token_swap/README.md diff --git a/submodules/moragents_dockers/agents/src/agents/token_swap/__init__.py b/submodules/moragents_dockers/agents/src/agents/token_swap/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/submodules/moragents_dockers/agents/src/agents/token_swap/agent.py b/submodules/moragents_dockers/agents/src/agents/token_swap/agent.py new file mode 100644 index 0000000..e99e9b3 --- /dev/null +++ b/submodules/moragents_dockers/agents/src/agents/token_swap/agent.py @@ -0,0 +1,221 @@ +import json +import requests +import logging + +from src.agents.token_swap import tools +from src.agents.token_swap.config import Config +from src.models.messages import ChatRequest +from src.stores import agent_manager + +logger = logging.getLogger(__name__) + + +class TokenSwapAgent: + def __init__(self, config, llm, embeddings): + self.config = config + self.llm = llm + self.embeddings = embeddings + self.tools_provided = tools.get_tools() + self.context = [] + + def api_request_url(self, method_name, query_params, chain_id): + base_url = Config.APIBASEURL + str(chain_id) + return f"{base_url}{method_name}?{'&'.join([f'{key}={value}' for key, value in query_params.items()])}" + + def check_allowance(self, token_address, wallet_address, chain_id): + url = self.api_request_url( + "/approve/allowance", + {"tokenAddress": token_address, "walletAddress": wallet_address}, + chain_id, + ) + response = requests.get(url, headers=Config.HEADERS) + data = response.json() + return data + + def approve_transaction(self, token_address, chain_id, amount=None): + query_params = ( + {"tokenAddress": token_address, "amount": amount} + if amount + else {"tokenAddress": token_address} + ) + url = self.api_request_url("/approve/transaction", query_params, chain_id) + response = requests.get(url, headers=Config.HEADERS) + transaction = response.json() + return transaction + + def build_tx_for_swap(self, swap_params, chain_id): + url = self.api_request_url("/swap", swap_params, chain_id) + swap_transaction = requests.get(url, headers=Config.HEADERS).json() + return swap_transaction + + def get_response(self, message, chain_id, wallet_address): + system_prompt = ( + "Don't make assumptions about the value of the arguments for the function " + "they should always be supplied by the user and do not alter the value of the arguments. " + "Don't make assumptions about what values to plug into functions. Ask for clarification if a user " + "request is ambiguous. you only need the value of token1 we dont need the value of token2. After " + "starting from scratch do not assume the name of token1 or token2" + ) + + messages = [ + {"role": "system", "content": system_prompt}, + ] + messages.extend(message) + + logger.info("Sending request to LLM with %d messages", len(messages)) + + llm_with_tools = self.llm.bind_tools(self.tools_provided) + + try: + result = llm_with_tools.invoke(messages) + logger.info("Received response from LLM: %s", result) + + if result.tool_calls: + tool_call = result.tool_calls[0] + logger.info("Selected tool: %s", tool_call) + func_name = tool_call.get("name") + args = tool_call.get("args") + logger.info("LLM suggested using tool: %s", func_name) + + if func_name == "swap_agent": + tok1 = args["token1"] + tok2 = args["token2"] + value = args["value"] + try: + res, role = tools.swap_coins( + tok1, tok2, float(value), chain_id, wallet_address + ) + except ( + tools.InsufficientFundsError, + tools.TokenNotFoundError, + tools.SwapNotPossibleError, + ) as e: + self.context = [] + return str(e), "assistant", None + return res, role, None + else: + logger.info("LLM provided a direct response without using tools") + return result.content, "assistant", "crypto swap agent" + except Exception as e: + logger.error(f"Error in get_response: {str(e)}") + return f"An error occurred: {str(e)}", "assistant", None + + def get_status(self, flag, tx_hash, tx_type): + response = "" + + if flag == "cancelled": + response = f"The {tx_type} transaction has been cancelled." + elif flag == "success": + response = f"The {tx_type} transaction was successful." + elif flag == "failed": + response = f"The {tx_type} transaction has failed." + elif flag == "initiated": + response = f"Transaction has been sent, please wait for it to be confirmed." + + if tx_hash: + response = response + f" The transaction hash is {tx_hash}." + + if flag == "success" and tx_type == "approve": + response = response + " Please proceed with the swap transaction." + elif flag != "initiated": + response = response + " Is there anything else I can help you with?" + + if flag != "initiated": + self.context = [] + self.context.append({"role": "assistant", "content": response}) + 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 + ) + return response, role, next_turn_agent + + def chat(self, request: ChatRequest): + data = request.dict() + try: + if "prompt" in data: + prompt = data["prompt"] + wallet_address = data["wallet_address"] + chain_id = data["chain_id"] + response, role, next_turn_agent = self.generate_response( + prompt, chain_id, wallet_address + ) + return { + "role": role, + "content": response, + "next_turn_agent": next_turn_agent, + } + else: + return {"error": "Missing required parameters"}, 400 + except Exception as e: + return {"Error": str(e)}, 500 + + def tx_status(self, data): + try: + if "status" in data: + prompt = data["status"] + tx_hash = data.get("tx_hash", "") + tx_type = data.get("tx_type", "") + response = self.get_status(prompt, tx_hash, tx_type) + return response + else: + return {"error": "Missing required parameters"}, 400 + except Exception as e: + return {"Error": str(e)}, 500 + + def get_allowance(self, request_data): + try: + if "tokenAddress" in request_data: + token = request_data["tokenAddress"] + wallet_address = request_data["walletAddress"] + chain_id = request_data["chain_id"] + res = self.check_allowance(token, wallet_address, chain_id) + return {"response": res} + else: + return {"error": "Missing required parameters"}, 400 + except Exception as e: + return {"Error": str(e)}, 500 + + def approve(self, request_data): + try: + if "tokenAddress" in request_data: + token = request_data["tokenAddress"] + chain_id = request_data["chain_id"] + amount = request_data["amount"] + res = self.approve_transaction(token, chain_id, amount) + return {"response": res} + else: + return {"error": "Missing required parameters"}, 400 + except Exception as e: + return {"Error": str(e)}, 500 + + def swap(self, request_data): + try: + if "src" in request_data: + token1 = request_data["src"] + token2 = request_data["dst"] + wallet_address = request_data["walletAddress"] + amount = request_data["amount"] + slippage = request_data["slippage"] + chain_id = request_data["chain_id"] + swap_params = { + "src": token1, + "dst": token2, + "amount": amount, + "from": wallet_address, + "slippage": slippage, + "disableEstimate": False, + "allowPartialFill": False, + } + swap_transaction = self.build_tx_for_swap(swap_params, chain_id) + return swap_transaction + else: + return {"error": "Missing required parameters"}, 400 + except Exception as e: + return {"Error": str(e)}, 500 diff --git a/submodules/moragents_dockers/agents/src/agents/token_swap/config.py b/submodules/moragents_dockers/agents/src/agents/token_swap/config.py new file mode 100644 index 0000000..d479874 --- /dev/null +++ b/submodules/moragents_dockers/agents/src/agents/token_swap/config.py @@ -0,0 +1,54 @@ +import logging + +# Logging configuration +logging.basicConfig(level=logging.INFO) + + +# Configuration object +class Config: + + # API endpoints + INCH_URL = "https://api.1inch.dev/token" + QUOTE_URL = "https://api.1inch.dev/swap" + APIBASEURL = f"https://api.1inch.dev/swap/v6.0/" + HEADERS = { + "Authorization": "Bearer WvQuxaMYpPvDiiOL5RHWUm7OzOd20nt4", + "accept": "application/json", + } + WEB3RPCURL = { + "56": "https://bsc-dataseed.binance.org", + "42161": "https://arb1.arbitrum.io/rpc", + "137": "https://polygon-rpc.com", + "1": "https://eth.llamarpc.com/", + "10": "https://mainnet.optimism.io", + "8453": "https://mainnet.base.org", + } + NATIVE_TOKENS = { + "137": "MATIC", + "56": "BNB", + "1": "ETH", + "42161": "ETH", + "10": "ETH", + "8453": "ETH", + } + ERC20_ABI = [ + { + "constant": True, + "inputs": [], + "name": "decimals", + "outputs": [{"name": "", "type": "uint8"}], + "payable": False, + "stateMutability": "view", + "type": "function", + }, + { + "constant": True, + "inputs": [{"name": "_owner", "type": "address"}], + "name": "balanceOf", + "outputs": [{"name": "balance", "type": "uint256"}], + "payable": False, + "stateMutability": "view", + "type": "function", + }, + ] + INCH_NATIVE_TOKEN_ADDRESS = "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE" diff --git a/submodules/moragents_dockers/agents/src/swap_agent/src/tools.py b/submodules/moragents_dockers/agents/src/agents/token_swap/tools.py similarity index 61% rename from submodules/moragents_dockers/agents/src/swap_agent/src/tools.py rename to submodules/moragents_dockers/agents/src/agents/token_swap/tools.py index 1800dfe..2e2c871 100644 --- a/submodules/moragents_dockers/agents/src/swap_agent/src/tools.py +++ b/submodules/moragents_dockers/agents/src/agents/token_swap/tools.py @@ -2,52 +2,69 @@ import logging import time from web3 import Web3 -from swap_agent.src.config import Config + +from src.agents.token_swap.config import Config class InsufficientFundsError(Exception): pass + class TokenNotFoundError(Exception): pass + class SwapNotPossibleError(Exception): pass 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) + params = {"query": query, "limit": limit, "ignore_listed": ignore_listed} + response = requests.get( + Config.INCH_URL + endpoint, params=params, headers=Config.HEADERS + ) if response.status_code == 200: return response.json() else: logging.error(f"Failed to search tokens. Status code: {response.status_code}") return None -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 + +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: """Convert an amount in ETH to wei.""" return int(amount_in_eth * 10**18) + def validate_swap(web3: Web3, token1, token2, chain_id, amount, wallet_address): native = Config.NATIVE_TOKENS # token1 is the native token if token1.lower() == native[str(chain_id)].lower(): - t1 = [{'symbol': native[str(chain_id)], 'address': Config.INCH_NATIVE_TOKEN_ADDRESS}] - t1_bal = get_token_balance(web3, wallet_address, '', Config.ERC20_ABI) + t1 = [ + { + "symbol": native[str(chain_id)], + "address": Config.INCH_NATIVE_TOKEN_ADDRESS, + } + ] + t1_bal = get_token_balance(web3, wallet_address, "", Config.ERC20_ABI) smallest_amount = eth_to_wei(amount) # token1 is an erc20 token @@ -56,12 +73,19 @@ 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) - smallest_amount = convert_to_smallest_unit(web3, amount, t1[0]['address']) + 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 if token2.lower() == native[str(chain_id)].lower(): - t2 = [{'symbol': native[str(chain_id)], 'address': Config.INCH_NATIVE_TOKEN_ADDRESS}] + t2 = [ + { + "symbol": native[str(chain_id)], + "address": Config.INCH_NATIVE_TOKEN_ADDRESS, + } + ] else: t2 = search_tokens(token2, chain_id) time.sleep(2) @@ -72,54 +96,64 @@ def validate_swap(web3: Web3, token1, token2, chain_id, amount, wallet_address): if t1_bal < smallest_amount: raise InsufficientFundsError(f"Insufficient funds to perform the swap.") - return t1[0]['address'], t1[0]['symbol'], t2[0]['address'], t2[0]['symbol'] + return t1[0]["address"], t1[0]["symbol"], t2[0]["address"], t2[0]["symbol"] + 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) + params = {"src": token1, "dst": token2, "amount": int(amount_in_wei)} + response = requests.get( + Config.QUOTE_URL + endpoint, params=params, headers=Config.HEADERS + ) if response.status_code == 200: return response.json() else: logging.error(f"Failed to get quote. Status code: {response.status_code}") return None + def get_token_decimals(web3: Web3, token_address: str) -> int: if not token_address: return 18 # Assuming 18 decimals for the native gas token else: - contract = web3.eth.contract(address=Web3.to_checksum_address(token_address), abi=Config.ERC20_ABI) + contract = web3.eth.contract( + address=Web3.to_checksum_address(token_address), abi=Config.ERC20_ABI + ) return contract.functions.decimals().call() + def convert_to_smallest_unit(web3: Web3, amount: float, token_address: str) -> int: decimals = get_token_decimals(web3, token_address) - return int(amount * (10 ** decimals)) + 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) + return smallest_unit_amount / (10**decimals) 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 + t1_address = "" if t1_a == Config.INCH_NATIVE_TOKEN_ADDRESS else t1_a smallest_unit_amount = convert_to_smallest_unit(web3, amount, t1_address) result = get_quote(t1_a, t2_a, smallest_unit_amount, chain_id) - + if result: price = result["dstAmount"] - t2_address = '' if t2_a == Config.INCH_NATIVE_TOKEN_ADDRESS else t2_a + t2_address = "" if t2_a == Config.INCH_NATIVE_TOKEN_ADDRESS else t2_a t2_quote = convert_to_readable_unit(web3, int(price), t2_address) else: - raise SwapNotPossibleError("Failed to generate a quote. Please ensure you're on the correct network.") + raise SwapNotPossibleError( + "Failed to generate a quote. Please ensure you're on the correct network." + ) return { "dst": t2_id, @@ -129,9 +163,10 @@ def swap_coins(token1, token2, amount, chain_id, wallet_address): "src_address": t1_a, "src_amount": amount, "approve_tx_cb": "/approve", - "swap_tx_cb": "/swap" + "swap_tx_cb": "/swap", }, "swap" + def get_tools(): """Return a list of tools for the agent.""" return [ @@ -145,19 +180,19 @@ def get_tools(): "properties": { "token1": { "type": "string", - "description": "name or address of the cryptocurrency to sell" + "description": "name or address of the cryptocurrency to sell", }, "token2": { "type": "string", - "description": "name or address of the cryptocurrency to buy" + "description": "name or address of the cryptocurrency to buy", }, "value": { "type": "string", - "description": "Value or amount of the cryptocurrency to sell" - } + "description": "Value or amount of the cryptocurrency to sell", + }, }, - "required": ["token1", "token2", "value"] - } - } + "required": ["token1", "token2", "value"], + }, + }, } - ] \ No newline at end of file + ] diff --git a/submodules/moragents_dockers/agents/src/agents/tweet_sizzler/README.md b/submodules/moragents_dockers/agents/src/agents/tweet_sizzler/README.md new file mode 100644 index 0000000..345781f --- /dev/null +++ b/submodules/moragents_dockers/agents/src/agents/tweet_sizzler/README.md @@ -0,0 +1,73 @@ +# Tweet Sizzler Agent + +This agent enables you to generate and post tweets using AI. It should be used with the accompanying UI, which provides a browser-based front-end to chat with the agent, display generated tweets, and post them to X (formerly Twitter). + +A typical flow looks like this: + +- User prompts for a tweet, e.g., "Generate a tweet about AI in healthcare" +- The agent generates a tweet which is shown to the user, who may either proceed to post it or request a new generation +- If the user accepts the tweet, it can be posted to X (Twitter) + +## Table of Contents + +- [Installation](#installation) +- [Initial Setup for Integration with X Developer API](#initial-setup-for-integration-with-x-developer-api) +- [Usage](#usage) + +## Installation + +## Run with Docker Compose + +Docker compose will build and run two containers. One will be for the agents, the other will be for the UI. + +1. Ensure you're in the submodules/moragents_dockers folder + + ```sh + $ cd submodules/moragents_dockers + ``` + +2. Build Images and Launch Containers: + 1. For Intel / AMD / x86_64 + ```sh + docker-compose up + ``` + 2. For Apple silicon (M1, M2, M3, etc) + ```sh + docker-compose -f docker-compose-apple.yml up + ``` + +Open in the browser: `http://localhost:3333/` + +## Initial Setup for Integration with X Developer API + +To use the Tweet Sizzler Agent, you need to set up a developer account and create an application on the X Developer Portal. Follow these steps: + +1. Sign into the X/Twitter Developer Portal at https://developer.twitter.com/en/portal/dashboard and create a developer account if you haven't already. + +2. Create a new project. This will automatically generate a set of consumer keys and authentication tokens for you. Do not use these because the auto-generated set of access tokens and secrets will not have the appropriate permissions. You should notice the text "Created with Read permissions". For the Tweet Sizzler, we need Write permissions. + +3. To set up Write permissions: + + - Go to the Settings tab of your project + - Find the "User authentication settings" section and click into it + - Under "App permissions (required)", select either "Read and write" or "Read, write, and direct messages" + - For "Type of App", select "Web App, Automated App or Bot" + - For the Callback URI, you can use `http://localhost:5000/callback` + - For the Website URL, use any valid website URL (not localhost) + - Click Save to persist these settings + +![X Developer Portal User Authentication Settings](../../../../../images/tweet_sizzler_instructions/user_authentication_settings.png) +_Figure 2: X Developer Portal User Authentication Settings_ + +Note: Your setup for the user authentication settings should look something like this. Make sure you have selected either "Read and write" or "Read and write and Direct message" under the app permissions section. This configuration is crucial for the Tweet Sizzler agent to function properly and post tweets on your behalf. + +4. Return to the Keys and Tokens tab. You'll notice the Access Token and Secret still say "Created with Read permissions". Click the "Regenerate" button to create a new access token / secret pair with the permissions we set up in step 3. + +5. Double-check that the Access Token and Secret contain a subtitle saying "Created with Read, Write, and Direct Messages permissions". + +6. Enter the consumer keys and authentication tokens from this page into the settings modal of the Morpheus app. + +![X Developer Portal Dashboard](../../../../../images/tweet_sizzler_instructions/dashboard_permissions.png) +_Figure 1: X Developer Portal Dashboard_ + +Note: For the Tweet Sizzler agent to work correctly, you need to ensure that your app permissions are set to "Read and write" or "Read, write, and Direct Messages" as shown in the image above. This level of permission is necessary for the agent to post tweets on your behalf. diff --git a/submodules/moragents_dockers/agents/src/agents/tweet_sizzler/__init__.py b/submodules/moragents_dockers/agents/src/agents/tweet_sizzler/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/submodules/moragents_dockers/agents/src/tweet_sizzler_agent/src/agent.py b/submodules/moragents_dockers/agents/src/agents/tweet_sizzler/agent.py similarity index 63% rename from submodules/moragents_dockers/agents/src/tweet_sizzler_agent/src/agent.py rename to submodules/moragents_dockers/agents/src/agents/tweet_sizzler/agent.py index 08dbf32..2cbd3ff 100644 --- a/submodules/moragents_dockers/agents/src/tweet_sizzler_agent/src/agent.py +++ b/submodules/moragents_dockers/agents/src/agents/tweet_sizzler/agent.py @@ -1,6 +1,8 @@ import logging import tweepy -from .config import Config + +from src.agents.tweet_sizzler.config import Config +from src.models.messages import ChatRequest # Configure logging logging.basicConfig( @@ -10,10 +12,10 @@ class TweetSizzlerAgent: - def __init__(self, config, llm, llm_ollama, embeddings, flask_app): - self.llm = llm - self.flask_app = flask_app + def __init__(self, config, llm, embeddings): self.config = config + self.llm = llm + self.embeddings = embeddings self.x_api_key = None self.last_prompt_content = None self.twitter_client = None @@ -38,20 +40,26 @@ def generate_tweet(self, prompt_content=None): ] try: - result = self.llm.create_chat_completion( - messages=messages, - max_tokens=Config.LLM_MAX_TOKENS, - temperature=Config.LLM_TEMPERATURE, - ) - tweet = result["choices"][0]["message"]["content"] + result = self.llm.invoke(messages) + logger.info(f"Received response from LLM: {result}") + tweet = result.content.strip() + tweet = " ".join(tweet.split()) + + # Remove any dictionary-like formatting, if present + if tweet.startswith("{") and tweet.endswith("}"): + tweet = tweet.strip("{}").split(":", 1)[-1].strip().strip('"') + logger.info(f"Tweet generated successfully: {tweet}") return tweet except Exception as e: logger.error(f"Error generating tweet: {str(e)}") raise - def post_tweet(self, request): - data = request.get_json() + async def post_tweet(self, request): + logger.info(f"Received tweet request: {request}") + data = await request.json() + logger.info(f"Received tweet data: {data}") + tweet_content = data.get("post_content") logger.info(f"Received tweet content: {tweet_content}") @@ -111,33 +119,37 @@ def set_x_api_key(self, request): return {"success": "API credentials saved successfully"}, 200 - def chat(self, request): + def chat(self, chat_request: ChatRequest): try: - data = request.get_json() - logger.info(f"Received chat request: {data}") - if "prompt" in data: - prompt = data["prompt"] - action = data.get("action", Config.DEFAULT_ACTION) - logger.debug(f"Extracted prompt: {prompt}, action: {action}") - - if action == "generate": - logger.info(f"Generating tweet for prompt: {prompt['content']}") - tweet = self.generate_tweet(prompt["content"]) - logger.info(f"Generated tweet: {tweet}") - return {"role": "assistant", "content": tweet} - elif action == "post": - logger.info("Attempting to post tweet") - result, status_code = self.post_tweet(request) - logger.info( - f"Posted tweet result: {result}, status code: {status_code}" - ) + prompt = chat_request.prompt.dict() + logger.info(f"Received chat request: {prompt}") + + action = prompt.get("action", Config.DEFAULT_ACTION) + logger.debug( + f"Extracted prompt content: {prompt['content']}, action: {action}" + ) + + if action == "generate": + logger.info(f"Generating tweet for prompt: {prompt['content']}") + tweet = self.generate_tweet(prompt["content"]) + logger.info(f"Generated tweet: {tweet}") + return {"role": "assistant", "content": tweet} + 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}" + ) + if isinstance(result, dict) and "error" in result: return result, status_code - else: - logger.error(f"Invalid action received: {action}") - return {"error": Config.ERROR_INVALID_ACTION}, 400 + return { + "role": "assistant", + "content": f"Tweet posted successfully: {result.get('tweet', '')}", + }, status_code else: - logger.error("Missing 'prompt' in chat request data") - return {"error": Config.ERROR_MISSING_PARAMETERS}, 400 + logger.error(f"Invalid action received: {action}") + return {"role": "assistant", "content": Config.ERROR_INVALID_ACTION} + except Exception as e: logger.exception(f"Unexpected error in chat method: {str(e)}") - return {"Error": str(e)}, 500 + return {"role": "assistant", "content": f"Error: {str(e)}"} diff --git a/submodules/moragents_dockers/agents/src/tweet_sizzler_agent/src/config.py b/submodules/moragents_dockers/agents/src/agents/tweet_sizzler/config.py similarity index 76% rename from submodules/moragents_dockers/agents/src/tweet_sizzler_agent/src/config.py rename to submodules/moragents_dockers/agents/src/agents/tweet_sizzler/config.py index b7380b7..3b8e37b 100644 --- a/submodules/moragents_dockers/agents/src/tweet_sizzler_agent/src/config.py +++ b/submodules/moragents_dockers/agents/src/agents/tweet_sizzler/config.py @@ -21,9 +21,10 @@ class Config: "attention-grabbing tweets based on the user's prompt. It is CRUCIAL that you " "keep the tweets strictly under 280 characters - this is a hard limit. Make the " "tweets as engaging as possible while adhering to this character limit. Do not " - "surround your tweet with quotes. Do not preface it with any text like 'here is " - "your tweet'. Simply generate and output the tweet, ensuring it is less than " - "280 characters long." + "surround your tweet with quotes or any other formatting. Do not preface it with " + "any text like 'here is your tweet'. Simply generate and output the tweet, ensuring " + "it is less than 280 characters long. Use newlines sparingly. Do not surrounded with " + "quotes or braces. Do not use any other formatting." ) DEFAULT_ACTION = "generate" diff --git a/submodules/moragents_dockers/agents/src/app.py b/submodules/moragents_dockers/agents/src/app.py index a483683..ba9e865 100644 --- a/submodules/moragents_dockers/agents/src/app.py +++ b/submodules/moragents_dockers/agents/src/app.py @@ -1,21 +1,21 @@ import os import logging import time -from functools import wraps -from config import Config -from llama_cpp import Llama -from flask_cors import CORS -from flask import Flask, request, jsonify -from langchain_community.llms import Ollama -from delegator import Delegator -from llama_cpp.llama_tokenizer import LlamaHFTokenizer +import uvicorn + +from fastapi import FastAPI, HTTPException, Request, UploadFile, File +from fastapi.middleware.cors import CORSMiddleware +from pydantic import BaseModel + +from langchain_ollama import ChatOllama from langchain_community.embeddings import OllamaEmbeddings +from src.config import Config +from src.delegator import Delegator +from src.stores import agent_manager, chat_manager +from src.models.messages import ChatRequest + # Constants -INITIAL_MESSAGE = { - "role": "assistant", - "content": "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.", -} UPLOAD_FOLDER = os.path.join(os.getcwd(), "uploads") # Configure logging @@ -27,194 +27,158 @@ ) logger = logging.getLogger(__name__) +app = FastAPI() +app.add_middleware( + CORSMiddleware, + allow_origins=["*"], + allow_credentials=True, + allow_methods=["*"], + allow_headers=["*"], +) -def load_llm(): - logger.info("Loading LLM model") - try: - llm = Llama( - model_path=Config.MODEL_PATH, - chat_format="functionary-v2", - tokenizer=LlamaHFTokenizer.from_pretrained( - "meetkai/functionary-small-v2.4-GGUF" - ), - n_gpu_layers=-1, # Use all available GPU layers - n_batch=1024, # Increase batch size for faster processing - n_ctx=1024, # Increase context size for better performance - verbose=False, # Disable verbose output for speed - use_mlock=True, # Lock memory to prevent swapping - use_mmap=True, # Use memory mapping for faster loading - n_threads=16, # Increase number of threads for more parallel processing - ) - logger.info("LLM model loaded successfully") - return llm - except Exception as e: - logger.error(f"Error loading LLM model: {str(e)}") - raise - - -app = Flask(__name__) -CORS(app) - -upload_state = False os.makedirs(UPLOAD_FOLDER, exist_ok=True) -app.config["UPLOAD_FOLDER"] = UPLOAD_FOLDER -app.config["MAX_CONTENT_LENGTH"] = Config.MAX_UPLOAD_LENGTH -try: - llm = load_llm() -except TimeoutError: - logger.error("LLM loading timed out") - llm = None -except Exception as e: - logger.error(f"Failed to load LLM: {str(e)}") - llm = None - -llm_ollama = Ollama(model="llama3.1", base_url=Config.OLLAMA_URL) -embeddings = OllamaEmbeddings(model="nomic-embed-text", base_url=Config.OLLAMA_URL) +llm = ChatOllama( + model=Config.OLLAMA_MODEL, + base_url=Config.OLLAMA_URL, +) +embeddings = OllamaEmbeddings( + model=Config.OLLAMA_EMBEDDING_MODEL, base_url=Config.OLLAMA_URL +) -delegator = Delegator(Config.DELEGATOR_CONFIG, llm, llm_ollama, embeddings, app) -messages = [INITIAL_MESSAGE] -next_turn_agent = None +delegator = Delegator(Config.DELEGATOR_CONFIG, llm, embeddings) -@app.route("/", methods=["POST"]) -def chat(): - global next_turn_agent, messages - data = request.get_json() - logger.info(f"Received chat request: {data}") +@app.post("/chat") +async def chat(chat_request: ChatRequest): + prompt = chat_request.prompt.dict() + chat_manager.add_message(prompt) try: - current_agent = None - if "prompt" in data: - prompt = data["prompt"] - messages.append(prompt) + active_agent = agent_manager.get_active_agent() - if not next_turn_agent: - logger.info("No next turn agent, getting delegator response") + if not active_agent: + logger.info("No active agent, getting delegator response") start_time = time.time() - result = delegator.get_delegator_response(prompt, upload_state) + result = delegator.get_delegator_response( + prompt["content"], chat_manager.get_uploaded_file_status() + ) + end_time = time.time() + logger.info(f"Delegator response time: {end_time - start_time:.2f} seconds") logger.info(f"Delegator response: {result}") - if "next" not in result: - logger.error(f"Missing 'next' key in delegator response: {result}") - raise ValueError("Invalid delegator response: missing 'next' key") + if "agent" not in result: + logger.error(f"Missing 'agent' key in delegator response: {result}") + raise ValueError("Invalid delegator response: missing 'agent' key") - next_agent = result["next"] - current_agent, response_swap = delegator.delegate_chat(next_agent, request) - else: - logger.info(f"Delegating chat to next turn agent: {next_turn_agent}") - current_agent, response_swap = delegator.delegate_chat( - next_turn_agent, request - ) + active_agent = result["agent"] - # Handle both dictionary and tuple returns from delegate_chat - response, status_code = ( - response_swap if isinstance(response_swap, tuple) else (response_swap, 200) - ) + logger.info(f"Delegating chat to active agent: {active_agent}") + current_agent, response = delegator.delegate_chat(active_agent, chat_request) - # If response_swap is an error, reset next_turn_agent - next_turn_agent = ( - response_swap.get("next_turn_agent") - if isinstance(response_swap, dict) - else None - ) + if isinstance(response, tuple) and len(response) == 2: + error_message, status_code = response + logger.error(f"Error from agent: {error_message}") + raise HTTPException(status_code=status_code, detail=error_message) if isinstance(response, dict) and "role" in response and "content" in response: - response_with_agent = response.copy() - response_with_agent["agentName"] = current_agent or "Unknown" + chat_manager.add_response(response, current_agent or "Unknown") - messages.append(response_with_agent) - - logger.info("Sending response: %s", response_with_agent) - return jsonify(response_with_agent), status_code + logger.info(f"Sending response: {response}") + return response else: logger.error(f"Invalid response format: {response}") - return jsonify({"error": "Invalid response format"}), 500 + raise HTTPException(status_code=500, detail="Invalid response format") except TimeoutError: logger.error("Chat request timed out") - return jsonify({"error": "Request timed out"}), 504 + raise HTTPException(status_code=504, detail="Request timed out") except ValueError as ve: logger.error(f"Input formatting error: {str(ve)}") - return jsonify({"error": str(ve)}), 400 + raise HTTPException(status_code=400, detail=str(ve)) except Exception as e: logger.error(f"Error in chat route: {str(e)}", exc_info=True) - return jsonify({"error": str(e)}), 500 + raise HTTPException(status_code=500, detail=str(e)) -@app.route("/tx_status", methods=["POST"]) -def swap_agent_tx_status(): +@app.post("/tx_status") +async def swap_agent_tx_status(request: Request): logger.info("Received tx_status request") - response = delegator.delegate_route("crypto swap agent", request, "tx_status") - messages.append(response) - return jsonify(response) + response = await delegator.delegate_route("crypto swap agent", request, "tx_status") + chat_manager.add_message(response) + return response -@app.route("/messages", methods=["GET"]) -def get_messages(): +@app.get("/messages") +async def get_messages(): logger.info("Received get_messages request") - return jsonify({"messages": messages}) + return {"messages": chat_manager.get_messages()} -@app.route("/clear_messages", methods=["GET"]) -def clear_messages(): - global messages +@app.get("/clear_messages") +async def clear_messages(): logger.info("Clearing message history") - messages = [INITIAL_MESSAGE] - return jsonify({"response": "successfully cleared message history"}) + chat_manager.clear_messages() + return {"response": "successfully cleared message history"} -@app.route("/allowance", methods=["POST"]) -def swap_agent_allowance(): +@app.post("/allowance") +async def swap_agent_allowance(request: Request): logger.info("Received allowance request") return delegator.delegate_route("crypto swap agent", request, "get_allowance") -@app.route("/approve", methods=["POST"]) -def swap_agent_approve(): +@app.post("/approve") +async def swap_agent_approve(request: Request): logger.info("Received approve request") return delegator.delegate_route("crypto swap agent", request, "approve") -@app.route("/swap", methods=["POST"]) -def swap_agent_swap(): +@app.post("/swap") +async def swap_agent_swap(request: Request): logger.info("Received swap request") return delegator.delegate_route("crypto swap agent", request, "swap") -@app.route("/upload", methods=["POST"]) -def rag_agent_upload(): - global messages, upload_state +@app.post("/upload") +async def rag_agent_upload(file: UploadFile = File(...)): logger.info("Received upload request") - response = delegator.delegate_route( - "general purpose and context-based rag agent", request, "upload_file" + response = await delegator.delegate_route( + + "general purpose and context-based rag agent", {"file": file}, "upload_file" ) - messages.append(response) - upload_state = True - return jsonify(response) + chat_manager.add_message(response) + return response -@app.route("/regenerate_tweet", methods=["POST"]) -def regenerate_tweet(): +@app.post("/regenerate_tweet") +async def regenerate_tweet(): logger.info("Received generate tweet request") return delegator.delegate_route("tweet sizzler agent", None, "generate_tweet") -@app.route("/post_tweet", methods=["POST"]) -def post_tweet(): +@app.post("/post_tweet") +async def post_tweet(request: Request): logger.info("Received x post request") - return delegator.delegate_route("tweet sizzler agent", request, "post_tweet") + return await delegator.delegate_route("tweet sizzler agent", request, "post_tweet") -# TODO: Persist the X API key in the database (once we set this up) -@app.route("/set_x_api_key", methods=["POST"]) -def set_x_api_key(): +@app.post("/set_x_api_key") +async def set_x_api_key(request: Request): logger.info("Received set X API key request") - return 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") +async def claim_agent_claim(request: Request): + logger.info("Received claim request") + return delegator.delegate_route("claim agent", request, "claim") if __name__ == "__main__": - app.run(host="0.0.0.0", port=5000, debug=True) + uvicorn.run(app, host="0.0.0.0", port=5000, reload=True) diff --git a/submodules/moragents_dockers/agents/src/claim_agent/src/agent.py b/submodules/moragents_dockers/agents/src/claim_agent/src/agent.py deleted file mode 100644 index 3538eab..0000000 --- a/submodules/moragents_dockers/agents/src/claim_agent/src/agent.py +++ /dev/null @@ -1,70 +0,0 @@ -import json -from claim_agent.src import tools -from claim_agent.src.config import Config - -class ClaimAgent: - def __init__(self, agent_info, llm, llm_ollama, embeddings, flask_app): - self.agent_info = agent_info - self.llm = llm - self.tools_provided = tools.get_tools() - self.conversation_state = {} - - def get_response(self, message, wallet_address): - if wallet_address not in self.conversation_state: - self.conversation_state[wallet_address] = {"state": "initial"} - - state = self.conversation_state[wallet_address]["state"] - - if state == "initial": - rewards = { - 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} - - 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" - 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", self.agent_info["name"] - else: - return f"No rewards found for your wallet address {wallet_address} in either pool. Claim cannot be processed.", "assistant", None - - elif state == "awaiting_confirmation": - user_input = message[-1]['content'].lower() - if any(word in user_input for word in ['yes', 'proceed', 'confirm', 'claim']): - return self.prepare_transactions(wallet_address) - else: - return "Please confirm if you want to proceed with the claim by saying 'yes', 'proceed', 'confirm', or 'claim'.", "assistant", self.agent_info["name"] - - return "I'm sorry, I didn't understand that. Can you please rephrase your request?", "assistant", self.agent_info["name"] - - def prepare_transactions(self, wallet_address): - available_rewards = self.conversation_state[wallet_address]["available_rewards"] - receiver_address = self.conversation_state[wallet_address]["receiver_address"] - transactions = [] - - for pool_id in available_rewards.keys(): - try: - tx_data = tools.prepare_claim_transaction(pool_id, receiver_address) - transactions.append({"pool": pool_id, "transaction": tx_data}) - except Exception as e: - return f"Error preparing transaction for pool {pool_id}: {str(e)}", "assistant", None - - self.conversation_state[wallet_address]["transactions"] = transactions - tx_data_str = json.dumps(transactions, indent=2) - return f"Transaction data prepared for signing:\n\n{tx_data_str}", "assistant", None - - def chat(self, request): - try: - data = request.get_json() - 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) - return {"role": role, "content": response, "next_turn_agent": next_turn_agent} - else: - return {"error": "Missing required parameters"}, 400 - except Exception as e: - return {"Error": str(e)}, 500 diff --git a/submodules/moragents_dockers/agents/src/claim_agent/src/config.py b/submodules/moragents_dockers/agents/src/claim_agent/src/config.py deleted file mode 100644 index c7bbddc..0000000 --- a/submodules/moragents_dockers/agents/src/claim_agent/src/config.py +++ /dev/null @@ -1,58 +0,0 @@ -import logging - -# Logging configuration -logging.basicConfig(level=logging.INFO) - -# Configuration object -class Config: - - WEB3RPCURL = { - "1": "https://eth.llamarpc.com/" - } - MINT_FEE = 0.001 # in ETH - - DISTRIBUTION_PROXY_ADDRESS = "0x47176B2Af9885dC6C4575d4eFd63895f7Aaa4790" - DISTRIBUTION_ABI = [ - { - "inputs": [ - { - "internalType": "uint256", - "name": "poolId_", - "type": "uint256" - }, - { - "internalType": "address", - "name": "receiver_", - "type": "address" - } - ], - "name": "claim", - "outputs": [], - "stateMutability": "payable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "poolId_", - "type": "uint256" - }, - { - "internalType": "address", - "name": "user_", - "type": "address" - } - ], - "name": "getCurrentUserReward", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - } - ] diff --git a/submodules/moragents_dockers/agents/src/config.py b/submodules/moragents_dockers/agents/src/config.py index 51274c7..05a06ee 100644 --- a/submodules/moragents_dockers/agents/src/config.py +++ b/submodules/moragents_dockers/agents/src/config.py @@ -8,55 +8,68 @@ class Config: # Model configuration - MODEL_NAME = "meetkai/functionary-small-v2.4-GGUF" - MODEL_REVISION = "functionary-small-v2.4.Q4_0.gguf" - MODEL_PATH = "model/" + MODEL_REVISION - DOWNLOAD_DIR = "model" + OLLAMA_MODEL = "llama3.2:3b" + OLLAMA_EMBEDDING_MODEL = "nomic-embed-text" OLLAMA_URL = "http://host.docker.internal:11434" + MAX_UPLOAD_LENGTH = 16 * 1024 * 1024 DELEGATOR_CONFIG = { "agents": [ { - "path": "rag_agent.src.agent", + "path": "src.agents.rag.agent", "class": "RagAgent", - "description": "Handles general queries, information retrieval, and context-based questions. Use for any query that doesn't explicitly match other agents' specialties.", + "description": "Must be used anytime an upload or uploaded document is referred to. Handles general queries, information retrieval, and context-based questions. Use for any query that doesn't explicitly match other agents' specialties.", "name": "general purpose and context-based rag agent", "upload_required": True, }, { - "path": "data_agent.src.agent", - "class": "DataAgent", + "path": "src.agents.crypto_data.agent", + "class": "CryptoDataAgent", "description": "Crypto-specific. Provides real-time cryptocurrency data such as price, market cap, and fully diluted valuation (FDV).", "name": "crypto data agent", "upload_required": False, }, + # { + # "path": "src.agents.token_swap.agent", + # "class": "TokenSwapAgent", + # "description": "Handles cryptocurrency swapping operations. Use when the query explicitly mentions swapping, exchanging, or converting one cryptocurrency to another.", + # "name": "token swap agent", + # "upload_required": False, + # }, { - "path": "swap_agent.src.agent", - "class": "SwapAgent", - "description": "Handles cryptocurrency swapping operations. Use when the query explicitly mentions swapping, exchanging, or converting one cryptocurrency to another.", - "name": "crypto swap agent", - "upload_required": False, - }, - { - "path": "tweet_sizzler_agent.src.agent", + "path": "src.agents.tweet_sizzler.agent", "class": "TweetSizzlerAgent", "description": "Generates and posts engaging tweets. Use when the query explicitly mentions Twitter, tweeting, or X platform.", "name": "tweet sizzler agent", "upload_required": False, }, # { - # "path": "claim_agent.src.agent", - # "class": "ClaimAgent", + # "path": "src.agents.mor_claims.agent", + # "class": "MorClaimsAgent", # "description": "Manages the process of claiming rewards or tokens, specifically MOR rewards. Use when the query explicitly mentions claiming rewards or tokens.", - # "name": "claim agent", + # "name": "mor claims agent", # "upload_required": False, # }, { - "path": "reward_agent.src.agent", - "class": "RewardAgent", + "path": "src.agents.mor_rewards.agent", + "class": "MorRewardsAgent", "description": "Provides information about user's accrued MOR rewards or tokens. Use when the query is about checking or querying reward balances.", - "name": "reward agent", + "name": "mor rewards agent", + "upload_required": False, + }, + { + "path": "src.agents.realtime_search.agent", + "class": "RealtimeSearchAgent", + "description": "Only use this agent for real-time data. This agent is not for general purpose queries. Use when the query is about searching the web for real-time information.", + "name": "realtime search agent", "upload_required": False, }, + { + "path": "src.agents.news_agent.agent", + "class": "NewsAgent", + "description": "Fetches and analyzes cryptocurrency news for potential price impacts.", + "name": "crypto news agent", + "upload_required": False, + } ] } diff --git a/submodules/moragents_dockers/agents/src/data_agent/src/agent.py b/submodules/moragents_dockers/agents/src/data_agent/src/agent.py deleted file mode 100644 index 1f5dd54..0000000 --- a/submodules/moragents_dockers/agents/src/data_agent/src/agent.py +++ /dev/null @@ -1,79 +0,0 @@ -import json -from data_agent.src import tools -import logging - -logger = logging.getLogger(__name__) - - -class DataAgent: - def __init__(self, config, llm, llm_ollama, embeddings, flask_app): - self.llm = llm - self.flask_app = flask_app - self.config = config - self.tools_provided = tools.get_tools() - - def get_response(self, message): - messages = [ - { - "role": "system", - "content": ( - "Don't make assumptions about the value of the arguments for the function " - "they should always be supplied by the user and do not alter the value of the arguments. " - "Don't make assumptions about what values to plug into functions. Ask for clarification if a user " - "request is ambiguous." - ), - } - ] - messages.extend(message) - logger.info("Sending request to LLM with %d messages", len(messages)) - result = self.llm.create_chat_completion( - messages=messages, tools=self.tools_provided, tool_choice="auto" - ) - - logger.info("Received response from LLM: %s", result) - - if "tool_calls" in result["choices"][0]["message"].keys(): - func = result["choices"][0]["message"]["tool_calls"][0]["function"] - logger.info("LLM suggested using tool: %s", func["name"]) - args = json.loads(func["arguments"]) - if func["name"] == "get_price": - return tools.get_coin_price_tool(args["coin_name"]), "assistant" - elif func["name"] == "get_floor_price": - return tools.get_nft_floor_price_tool(args["nft_name"]), "assistant" - elif func["name"] == "get_fdv": - return ( - tools.get_fully_diluted_valuation_tool(args["coin_name"]), - "assistant", - ) - elif func["name"] == "get_tvl": - return ( - tools.get_protocol_total_value_locked_tool(args["protocol_name"]), - "assistant", - ) - elif func["name"] == "get_market_cap": - return tools.get_coin_market_cap_tool(args["coin_name"]), "assistant" - else: - logger.info("LLM provided a direct response without using tools") - return result["choices"][0]["message"]["content"], "assistant" - - def generate_response(self, prompt): - response, role = self.get_response([prompt]) - return response, role - - def chat(self, request): - try: - data = request.get_json() - if "prompt" in data: - prompt = data["prompt"] - logger.info( - "Received chat request with prompt: %s", - prompt[:50] + "..." if len(prompt) > 50 else prompt, - ) - response, role = self.generate_response(prompt) - return {"role": role, "content": response} - else: - logger.warning("Received chat request without 'prompt' in data") - return {"error": "Missing required parameters"}, 400 - except Exception as e: - logger.error("Error in chat method: %s", str(e), exc_info=True) - return {"Error": str(e)}, 500 diff --git a/submodules/moragents_dockers/agents/src/data_agent/src/data_agent_config.py b/submodules/moragents_dockers/agents/src/data_agent/src/data_agent_config.py deleted file mode 100644 index 0c916ec..0000000 --- a/submodules/moragents_dockers/agents/src/data_agent/src/data_agent_config.py +++ /dev/null @@ -1,27 +0,0 @@ -import logging - -# Logging configuration -logging.basicConfig(level=logging.INFO) - -# Configuration object -class Config: - # Model configuration - MODEL_NAME = "meetkai/functionary-small-v2.4-GGUF" - MODEL_REVISION = "functionary-small-v2.4.Q4_0.gguf" - MODEL_PATH = "model/"+MODEL_REVISION - DOWNLOAD_DIR = "model" - # API endpoints - COINGECKO_BASE_URL = "https://api.coingecko.com/api/v3" - DEFILLAMA_BASE_URL = "https://api.llama.fi" - 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." - 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." - API_ERROR_MESSAGE = "I can't seem to access the API at the moment." - \ No newline at end of file diff --git a/submodules/moragents_dockers/agents/src/delegator.py b/submodules/moragents_dockers/agents/src/delegator.py index bc4d605..a0f9b5e 100644 --- a/submodules/moragents_dockers/agents/src/delegator.py +++ b/submodules/moragents_dockers/agents/src/delegator.py @@ -2,6 +2,8 @@ import logging import json +from langchain.schema import SystemMessage, HumanMessage, AIMessage + logger = logging.getLogger(__name__) # Configurable default agent @@ -9,12 +11,10 @@ class Delegator: - def __init__(self, config, llm, llm_ollama, embeddings, flask_app): - self.llm = llm - self.flask_app = flask_app - self.llm_ollama = llm_ollama - self.embeddings = embeddings + def __init__(self, config, llm, embeddings): self.config = config + self.llm = llm # This is now a ChatOllama instance + self.embeddings = embeddings self.agents = self.load_agents(config) logger.info("Delegator initialized with %d agents", len(self.agents)) @@ -27,9 +27,7 @@ def load_agents(self, config): agent_instance = agent_class( agent_info, self.llm, - self.llm_ollama, self.embeddings, - self.flask_app, ) agents[agent_info["name"]] = agent_instance logger.info("Loaded agent: %s", agent_info["name"]) @@ -43,78 +41,57 @@ def get_delegator_response(self, prompt, upload_state): for agent_info in self.config["agents"] if not (agent_info["upload_required"] and not upload_state) ] + logger.info(f"Available agents: {available_agents}") + agent_descriptions = "\n".join( f"- {agent_info['name']}: {agent_info['description']}" for agent_info in self.config["agents"] if agent_info["name"] in available_agents ) - prompt_text = ( - "### Instruction: Your name is Morpheus. " + system_prompt = ( + "Your name is Morpheus. " "Your primary function is to select the correct agent based on the user's input. " - "You MUST use the 'route' function to select an agent. " - "Available agents and their descriptions:\n" - f"{agent_descriptions}\n" + "You MUST use the 'select_agent' function to select an agent. " + f"Available agents and their descriptions: {agent_descriptions}\n" "Analyze the user's input and select the most appropriate agent. " - "Do not respond with any text other than calling the 'route' function. " - "###" ) tools = [ { - "type": "function", - "function": { - "name": "route", - "description": "Choose which agent to run next", - "parameters": { - "type": "object", - "properties": { - "next": { - "type": "string", - "enum": available_agents, - "description": "The name of the next agent to run", - } + "name": "select_agent", + "description": "Choose which agent should be used to respond to the user query", + "parameters": { + "type": "object", + "properties": { + "agent": { + "type": "string", + "enum": available_agents, + "description": "The name of the agent to be used to respond to the user query", }, - "required": ["next"], }, + "required": ["agent"], }, } ] - message_list = [ - {"role": "system", "content": prompt_text}, - prompt, - { - "role": "system", - "content": "Remember, you must use the 'route' function to select an agent.", - }, + agent_selection_llm = self.llm.bind_tools(tools) + + messages = [ + SystemMessage(content=system_prompt), + HumanMessage(content=prompt), ] - logger.info("Sending prompt to LLM: %s", prompt) - result = self.llm.create_chat_completion( - messages=message_list, - tools=tools, - tool_choice="auto", - temperature=0.3, - ) - logger.info("Received response from LLM: %s", result) + result = agent_selection_llm.invoke(messages) + tool_calls = result.tool_calls + if not tool_calls: + raise ValueError("No agent was selected by the model.") - response = result["choices"][0]["message"] + selected_agent = tool_calls[0] + logger.info(f"Selected agent: {selected_agent}") + selected_agent_name = selected_agent.get("args").get("agent") - if response.get("tool_calls"): - try: - function_args = json.loads( - response["tool_calls"][0]["function"]["arguments"] - ) - return {"next": function_args["next"]} - except (json.JSONDecodeError, KeyError) as e: - logger.error(f"Error parsing function call: {e}") - return {"next": DEFAULT_AGENT} - else: - logger.warning( - "No tool calls in LLM response, defaulting to general purpose agent" - ) - return {"next": DEFAULT_AGENT} + return {"agent": selected_agent_name} def delegate_chat(self, agent_name, request): logger.info(f"Attempting to delegate chat to agent: {agent_name}") diff --git a/submodules/moragents_dockers/agents/src/models/__init__.py b/submodules/moragents_dockers/agents/src/models/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/submodules/moragents_dockers/agents/src/models/messages.py b/submodules/moragents_dockers/agents/src/models/messages.py new file mode 100644 index 0000000..e17d693 --- /dev/null +++ b/submodules/moragents_dockers/agents/src/models/messages.py @@ -0,0 +1,12 @@ +from pydantic import BaseModel + + +class ChatMessage(BaseModel): + role: str + content: str + + +class ChatRequest(BaseModel): + prompt: ChatMessage + chain_id: str + wallet_address: str diff --git a/submodules/moragents_dockers/agents/src/rag_agent/src/agent.py b/submodules/moragents_dockers/agents/src/rag_agent/src/agent.py deleted file mode 100644 index 0880cf0..0000000 --- a/submodules/moragents_dockers/agents/src/rag_agent/src/agent.py +++ /dev/null @@ -1,90 +0,0 @@ -import os -import logging -from langchain_community.document_loaders import PyMuPDFLoader -from langchain_community.vectorstores import FAISS -from langchain_core.prompts import ChatPromptTemplate -from langchain_text_splitters import RecursiveCharacterTextSplitter -from langchain.chains.combine_documents import create_stuff_documents_chain -from langchain.chains import create_retrieval_chain -from werkzeug.utils import secure_filename - - -logging.basicConfig(level=logging.DEBUG) - - -class RagAgent: - def __init__(self, config, llm, llm_ollama, embeddings,flask_app): - self.llm = llm_ollama - self.flask_app = flask_app - self.embedding=embeddings - self.config = config - self.agent = None - self.messages = [{'role': "assistant", "content": "Please upload a file to begin"}] - self.upload_state = False - self.prompt = ChatPromptTemplate.from_template( - """ - Answer the following question only based on the given context - - - {context} - - - Question: {input} - """ - ) - self.UPLOAD_FOLDER = flask_app.config['UPLOAD_FOLDER'] - self.max_size = 5 * 1024 * 1024 - self.retriever = None - - - def handle_file_upload(self,file): - if not os.path.exists(self.UPLOAD_FOLDER): - os.makedirs(self.UPLOAD_FOLDER, exist_ok=True) - filename = secure_filename(file.filename) - file.save(os.path.join(self.UPLOAD_FOLDER, filename)) - # DocumentToolsGenerator class instantiation - loader = PyMuPDFLoader(os.path.join(self.UPLOAD_FOLDER,filename)) - docs = loader.load() - text_splitter = RecursiveCharacterTextSplitter(chunk_size=1024,chunk_overlap=20,length_function=len,is_separator_regex=False) - split_documents = text_splitter.split_documents(docs) - vector_store = FAISS.from_documents(split_documents, self.embedding) - self.retriever = vector_store.as_retriever(search_kwargs={"k": 7}) - - - def upload_file(self,request): - if 'file' not in request.files: - return {'error': 'No file part'}, 400 - file = request.files['file'] - if file.filename == '': - return {'error': 'No selected file'}, 400 - # Check file size - file.seek(0, os.SEEK_END) - file_length = file.tell() - file.seek(0, 0) # Reset the file pointer to the beginning - if file_length > self.max_size: - return {"role": "assistant", "content": 'please use a file less than 5 MB'} - try: - self.handle_file_upload(file) - self.upload_state = True - return {"role": "assistant", "content": 'You have successfully uploaded the text'} - except Exception as e: - logging.error(f'Error during file upload: {str(e)}') - return {'error': str(e)}, 500 - - def chat(self,request): - try: - data = request.get_json() - if 'prompt' in data: - prompt = data['prompt']['content'] - role = "assistant" - retrieved_docs = self.retriever.invoke(prompt) - formatted_context = "\n\n".join(doc.page_content for doc in retrieved_docs) - formatted_prompt = f"Question: {prompt}\n\nContext: {formatted_context}" - answer=self.llm(formatted_prompt) - response = answer if self.upload_state else "please upload a file first" - return {"role": role, "content": response} - else: - return {"error": "Missing required parameters"}, 400 - except Exception as e: - logging.error(f'Error in chat endpoint: {str(e)}') - return {"Error": str(e)}, 500 diff --git a/submodules/moragents_dockers/agents/src/reward_agent/src/config.py b/submodules/moragents_dockers/agents/src/reward_agent/src/config.py deleted file mode 100644 index ff7c6d6..0000000 --- a/submodules/moragents_dockers/agents/src/reward_agent/src/config.py +++ /dev/null @@ -1,39 +0,0 @@ -import logging - -# Logging configuration -logging.basicConfig(level=logging.INFO) - -# Configuration object -class Config: - - WEB3RPCURL = { - "1": "https://eth.llamarpc.com/", - } - - DISTRIBUTION_PROXY_ADDRESS = "0x47176B2Af9885dC6C4575d4eFd63895f7Aaa4790" - DISTRIBUTION_ABI = [ - { - "inputs": [ - { - "internalType": "uint256", - "name": "poolId_", - "type": "uint256" - }, - { - "internalType": "address", - "name": "user_", - "type": "address" - } - ], - "name": "getCurrentUserReward", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - } - ] diff --git a/submodules/moragents_dockers/agents/src/stores/__init__.py b/submodules/moragents_dockers/agents/src/stores/__init__.py new file mode 100644 index 0000000..18cc281 --- /dev/null +++ b/submodules/moragents_dockers/agents/src/stores/__init__.py @@ -0,0 +1,5 @@ +from src.stores.agent_manager import AgentManager +from src.stores.chat_manager import ChatManager + +agent_manager = AgentManager() +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 new file mode 100644 index 0000000..82508df --- /dev/null +++ b/submodules/moragents_dockers/agents/src/stores/agent_manager.py @@ -0,0 +1,12 @@ +class AgentManager: + def __init__(self): + self.active_agent = None + + def get_active_agent(self): + return self.active_agent + + def set_active_agent(self, agent_name): + self.active_agent = agent_name + + def clear_active_agent(self): + self.active_agent = None diff --git a/submodules/moragents_dockers/agents/src/stores/chat_manager.py b/submodules/moragents_dockers/agents/src/stores/chat_manager.py new file mode 100644 index 0000000..5bd79eb --- /dev/null +++ b/submodules/moragents_dockers/agents/src/stores/chat_manager.py @@ -0,0 +1,48 @@ +import logging +from typing import List, Dict + +logger = logging.getLogger(__name__) + + +class ChatManager: + def __init__(self): + self.has_uploaded_file = False + self.messages: List[Dict[str, str]] = [ + { + "role": "assistant", + "content": """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.""", + } + ] + + def add_message(self, message: Dict[str, str]): + self.messages.append(message) + logger.info(f"Added message: {message}") + + def get_messages(self) -> List[Dict[str, str]]: + return self.messages + + def set_uploaded_file(self, has_file: bool): + self.has_uploaded_file = has_file + logger.info(f"Set uploaded file status to: {has_file}") + + def get_uploaded_file_status(self) -> bool: + return self.has_uploaded_file + + def clear_messages(self): + self.messages = [self.messages[0]] # Keep the initial message + logger.info("Cleared message history") + + def get_last_message(self) -> Dict[str, str]: + return self.messages[-1] if self.messages else {} + + def add_response(self, response: Dict[str, str], agent_name: str): + response_with_agent = response.copy() + response_with_agent["agentName"] = agent_name + self.add_message(response_with_agent) + logger.info(f"Added response from agent {agent_name}: {response_with_agent}") + + def get_chat_history(self) -> str: + return "\n".join([f"{msg['role']}: {msg['content']}" for msg in self.messages]) diff --git a/submodules/moragents_dockers/agents/src/swap_agent/src/agent.py b/submodules/moragents_dockers/agents/src/swap_agent/src/agent.py deleted file mode 100644 index 73f2694..0000000 --- a/submodules/moragents_dockers/agents/src/swap_agent/src/agent.py +++ /dev/null @@ -1,190 +0,0 @@ -import json -import requests -from flask import jsonify -from swap_agent.src import tools -from swap_agent.src.config import Config - - -class SwapAgent: - def __init__(self, config, llm, llm_ollama, embeddings, flask_app): - self.llm = llm - self.flask_app = flask_app - self.config = config - self.tools_provided = tools.get_tools() - self.context = [] - - def api_request_url(self, method_name, query_params, chain_id): - base_url = Config.APIBASEURL + str(chain_id) - return f"{base_url}{method_name}?{'&'.join([f'{key}={value}' for key, value in query_params.items()])}" - - def check_allowance(self, token_address, wallet_address, chain_id): - url = self.api_request_url( - "/approve/allowance", - {"tokenAddress": token_address, "walletAddress": wallet_address}, - chain_id - ) - response = requests.get(url, headers=Config.HEADERS) - data = response.json() - return data - - def approve_transaction(self, token_address, chain_id, amount=None): - query_params = {"tokenAddress": token_address, "amount": amount} if amount else {"tokenAddress": token_address} - url = self.api_request_url("/approve/transaction", query_params, chain_id) - response = requests.get(url, headers=Config.HEADERS) - transaction = response.json() - return transaction - - def build_tx_for_swap(self, swap_params, chain_id): - url = self.api_request_url("/swap", swap_params, chain_id) - swap_transaction = requests.get(url, headers=Config.HEADERS).json() - return swap_transaction - - def get_response(self, message, chain_id, wallet_address): - prompt = [ - { - "role": "system", - "content": ( - "Don't make assumptions about the value of the arguments for the function " - "they should always be supplied by the user and do not alter the value of the arguments. " - "Don't make assumptions about what values to plug into functions. Ask for clarification if a user " - "request is ambiguous. you only need the value of token1 we dont need the value of token2. After " - "starting from scratch do not assume the name of token1 or token2" - ) - } - ] - prompt.extend(message) - result = self.llm.create_chat_completion( - messages=prompt, - tools=self.tools_provided, - tool_choice="auto", - temperature=0.01 - ) - if "tool_calls" in result["choices"][0]["message"].keys(): - func = result["choices"][0]["message"]["tool_calls"][0]['function'] - if func["name"] == "swap_agent": - args = json.loads(func["arguments"]) - tok1 = args["token1"] - tok2 = args["token2"] - value = args["value"] - try: - res, role = tools.swap_coins(tok1, tok2, float(value), chain_id, wallet_address) - except (tools.InsufficientFundsError, tools.TokenNotFoundError, tools.SwapNotPossibleError) as e: - self.context = [] - return str(e), "assistant", None - return res, role, None - self.context.append({"role": "assistant", "content": result["choices"][0]["message"]['content']}) - return result["choices"][0]["message"]['content'], "assistant", "crypto swap agent" - - def get_status(self, flag, tx_hash, tx_type): - response = '' - - if flag == "cancelled": - response = f"The {tx_type} transaction has been cancelled." - elif flag == "success": - response = f"The {tx_type} transaction was successful." - elif flag == "failed": - response = f"The {tx_type} transaction has failed." - elif flag == "initiated": - response = f"Transaction has been sent, please wait for it to be confirmed." - - if tx_hash: - response = response + f" The transaction hash is {tx_hash}." - - if flag == "success" and tx_type == "approve": - response = response + " Please proceed with the swap transaction." - elif flag != "initiated": - response = response + " Is there anything else I can help you with?" - - if flag != "initiated": - self.context = [] - self.context.append({"role": "assistant", "content": response}) - 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) - return response, role, next_turn_agent - - def chat(self, request): - try: - data = request.get_json() - if 'prompt' in data: - prompt = data['prompt'] - wallet_address = data['wallet_address'] - chain_id = data['chain_id'] - response, role, next_turn_agent = self.generate_response(prompt, chain_id, wallet_address) - return {"role": role, "content": response, "next_turn_agent": next_turn_agent} - else: - return {"error": "Missing required parameters"}, 400 - except Exception as e: - return {"Error": str(e)}, 500 - - def tx_status(self, request): - try: - data = request.get_json() - if 'status' in data: - prompt = data['status'] - tx_hash = data.get('tx_hash', '') - tx_type = data.get('tx_type', '') - response = self.get_status(prompt, tx_hash, tx_type) - return response - else: - return {"error": "Missing required parameters"}, 400 - except Exception as e: - return {"Error": str(e)}, 500 - - def get_allowance(self, request): - try: - data = request.get_json() - if 'tokenAddress' in data: - token = data['tokenAddress'] - wallet_address = data['walletAddress'] - chain_id = data["chain_id"] - res = self.check_allowance(token, wallet_address, chain_id) - return jsonify({"response": res}) - else: - return jsonify({"error": "Missing required parameters"}), 400 - except Exception as e: - return jsonify({"Error": str(e)}), 500 - - def approve(self, request): - try: - data = request.get_json() - if 'tokenAddress' in data: - token = data['tokenAddress'] - chain_id = data['chain_id'] - amount = data['amount'] - res = self.approve_transaction(token, chain_id, amount) - return jsonify({"response": res}) - else: - return jsonify({"error": "Missing required parameters"}), 400 - except Exception as e: - return jsonify({"Error": str(e)}), 500 - - def swap(self, request): - try: - data = request.get_json() - if 'src' in data: - token1 = data['src'] - token2 = data['dst'] - wallet_address = data['walletAddress'] - amount = data['amount'] - slippage = data['slippage'] - chain_id = data['chain_id'] - swap_params = { - "src": token1, - "dst": token2, - "amount": amount, - "from": wallet_address, - "slippage": slippage, - "disableEstimate": False, - "allowPartialFill": False, - } - swap_transaction = self.build_tx_for_swap(swap_params, chain_id) - return swap_transaction - else: - return jsonify({"error": "Missing required parameters"}), 400 - except Exception as e: - return jsonify({"Error": str(e)}), 500 diff --git a/submodules/moragents_dockers/agents/src/swap_agent/src/config.py b/submodules/moragents_dockers/agents/src/swap_agent/src/config.py deleted file mode 100644 index 2e57c81..0000000 --- a/submodules/moragents_dockers/agents/src/swap_agent/src/config.py +++ /dev/null @@ -1,26 +0,0 @@ -import logging - -# Logging configuration -logging.basicConfig(level=logging.INFO) - -# Configuration object -class Config: - # Model configuration - MODEL_NAME = "meetkai/functionary-small-v2.4-GGUF" - MODEL_REVISION = "functionary-small-v2.4.Q4_0.gguf" - MODEL_PATH = "model/"+MODEL_REVISION - DOWNLOAD_DIR = "model" - # API endpoints - INCH_URL = "https://api.1inch.dev/token" - QUOTE_URL = "https://api.1inch.dev/swap" - APIBASEURL = f"https://api.1inch.dev/swap/v6.0/" - HEADERS = { "Authorization": "Bearer WvQuxaMYpPvDiiOL5RHWUm7OzOd20nt4", "accept": "application/json" } - WEB3RPCURL = {"56":"https://bsc-dataseed.binance.org","42161":"https://arb1.arbitrum.io/rpc","137":"https://polygon-rpc.com","1":"https://eth.llamarpc.com/","10":"https://mainnet.optimism.io","8453":"https://mainnet.base.org"} - NATIVE_TOKENS={"137":"MATIC","56":"BNB","1":"ETH","42161":"ETH","10":"ETH","8453":"ETH"} - ERC20_ABI = [ - {"constant": True, "inputs": [], "name": "decimals", "outputs": [{"name": "", "type": "uint8"}], "payable": False, "stateMutability": "view", "type": "function"}, - {"constant": True, "inputs": [{"name": "_owner", "type": "address"}], "name": "balanceOf", "outputs": [{"name": "balance", "type": "uint256"}], "payable": False, "stateMutability": "view", "type": "function"} - ] - INCH_NATIVE_TOKEN_ADDRESS = "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE" - - \ No newline at end of file diff --git a/submodules/moragents_dockers/agents/src/swap_agent/src/swap_agent_config.py b/submodules/moragents_dockers/agents/src/swap_agent/src/swap_agent_config.py deleted file mode 100644 index b91a88f..0000000 --- a/submodules/moragents_dockers/agents/src/swap_agent/src/swap_agent_config.py +++ /dev/null @@ -1,25 +0,0 @@ -import logging - -# Logging configuration -logging.basicConfig(level=logging.INFO) - -# Configuration object -class Config: - # Model configuration - MODEL_NAME = "meetkai/functionary-small-v2.4-GGUF" - MODEL_REVISION = "functionary-small-v2.4.Q4_0.gguf" - MODEL_PATH = "model/"+MODEL_REVISION - DOWNLOAD_DIR = "model" - # API endpoints - INCH_URL = "https://api.1inch.dev/token" - QUOTE_URL = "https://api.1inch.dev/swap" - APIBASEURL = f"https://api.1inch.dev/swap/v6.0/" - HEADERS = { "Authorization": "Bearer WvQuxaMYpPvDiiOL5RHWUm7OzOd20nt4", "accept": "application/json" } - WEB3RPCURL = {"56":"https://bsc-dataseed.binance.org","42161":"https://arb1.arbitrum.io/rpc","137":"https://polygon-rpc.com","1":"https://cloudflare-eth.com","10":"https://mainnet.optimism.io","8453":"https://mainnet.base.org"} - NATIVE_TOKENS={"137":"MATIC","56":"BNB","1":"ETH","42161":"ETH","10":"ETH","8453":"ETH"} - ERC20_ABI = [ - {"constant": True, "inputs": [], "name": "decimals", "outputs": [{"name": "", "type": "uint8"}], "payable": False, "stateMutability": "view", "type": "function"}, - {"constant": True, "inputs": [{"name": "_owner", "type": "address"}], "name": "balanceOf", "outputs": [{"name": "balance", "type": "uint256"}], "payable": False, "stateMutability": "view", "type": "function"} - ] - INCH_NATIVE_TOKEN_ADDRESS = "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE" - \ No newline at end of file diff --git a/submodules/moragents_dockers/agents/tests/__init__.py b/submodules/moragents_dockers/agents/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/submodules/benchmarks/claim_agent_benchmarks/README.md b/submodules/moragents_dockers/agents/tests/claim_agent_benchmarks/README.md similarity index 100% rename from submodules/benchmarks/claim_agent_benchmarks/README.md rename to submodules/moragents_dockers/agents/tests/claim_agent_benchmarks/README.md diff --git a/submodules/moragents_dockers/agents/tests/claim_agent_benchmarks/__init__.py b/submodules/moragents_dockers/agents/tests/claim_agent_benchmarks/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/submodules/moragents_dockers/agents/tests/claim_agent_benchmarks/adapters/__init__.py b/submodules/moragents_dockers/agents/tests/claim_agent_benchmarks/adapters/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/submodules/benchmarks/claim_agent_benchmarks/adapters/claim_adapter.py b/submodules/moragents_dockers/agents/tests/claim_agent_benchmarks/adapters/claim_adapter.py similarity index 100% rename from submodules/benchmarks/claim_agent_benchmarks/adapters/claim_adapter.py rename to submodules/moragents_dockers/agents/tests/claim_agent_benchmarks/adapters/claim_adapter.py diff --git a/submodules/benchmarks/claim_agent_benchmarks/benchmarks.py b/submodules/moragents_dockers/agents/tests/claim_agent_benchmarks/benchmarks.py similarity index 92% rename from submodules/benchmarks/claim_agent_benchmarks/benchmarks.py rename to submodules/moragents_dockers/agents/tests/claim_agent_benchmarks/benchmarks.py index d550827..880365d 100644 --- a/submodules/benchmarks/claim_agent_benchmarks/benchmarks.py +++ b/submodules/moragents_dockers/agents/tests/claim_agent_benchmarks/benchmarks.py @@ -1,6 +1,6 @@ import pytest from helpers import request_claim, provide_receiver_address, confirm_transaction -from submodules.benchmarks.claim_agent_benchmarks.config import Config +from submodules.moragents_dockers.agents.tests.claim_agent_benchmarks.config import Config def test_claim_process(): diff --git a/submodules/moragents_dockers/agents/tests/claim_agent_benchmarks/config.py b/submodules/moragents_dockers/agents/tests/claim_agent_benchmarks/config.py new file mode 100644 index 0000000..e4ed3ea --- /dev/null +++ b/submodules/moragents_dockers/agents/tests/claim_agent_benchmarks/config.py @@ -0,0 +1,13 @@ +class Config: + URL = 'http://127.0.0.1:5000/' + HEADERS = {'Content-Type': 'application/json'} + + # Test wallet addresses + WALLET_ADDRESSES = [ + {"wallet": "0x48d0EAc727A7e478f792F16527012452a000f2bd"} + ] + + PROMPTS = { + "claim_request": "I want to claim my MOR", + "proceed": "yes" + } diff --git a/submodules/benchmarks/claim_agent_benchmarks/helpers.py b/submodules/moragents_dockers/agents/tests/claim_agent_benchmarks/helpers.py similarity index 58% rename from submodules/benchmarks/claim_agent_benchmarks/helpers.py rename to submodules/moragents_dockers/agents/tests/claim_agent_benchmarks/helpers.py index 22a4fef..a319834 100644 --- a/submodules/benchmarks/claim_agent_benchmarks/helpers.py +++ b/submodules/moragents_dockers/agents/tests/claim_agent_benchmarks/helpers.py @@ -1,5 +1,5 @@ -from submodules.benchmarks.claim_agent_benchmarks.config import Config -from 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) @@ -10,13 +10,6 @@ def request_claim(wallet_address): } return claim_adapter.ask_agent(payload) -def provide_receiver_address(wallet_address, receiver_address): - payload = { - "prompt": {"role": "user", "content": receiver_address}, - "wallet_address": wallet_address - } - return claim_adapter.ask_agent(payload) - def confirm_transaction(wallet_address): payload = { "prompt": {"role": "user", "content": Config.PROMPTS["proceed"]}, diff --git a/submodules/moragents_dockers/agents/tests/news_agent_benchmarks/README.md b/submodules/moragents_dockers/agents/tests/news_agent_benchmarks/README.md new file mode 100644 index 0000000..d1c4b90 --- /dev/null +++ b/submodules/moragents_dockers/agents/tests/news_agent_benchmarks/README.md @@ -0,0 +1,20 @@ +# Benchmarking & Testing News Agent Guide + + +## How to Run the Tests: +1) In the parent directory: +- ```cd submodules/moragents_dockers/agents``` + +2) ```docker build -t agent .``` + +2. NOTE: If you are using Apple Silicon then you may experience problems due to the base image not being build for arm64. We have included a separate Dockerfile in order to deal with this issue, run: + +- ```docker build . -t agent -f Dockerfile-apple``` + +3) To run the agent: + +- ```docker run --name agent -p 5000:5000 agent``` + +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 diff --git a/submodules/moragents_dockers/agents/tests/news_agent_benchmarks/__init__.py b/submodules/moragents_dockers/agents/tests/news_agent_benchmarks/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/submodules/moragents_dockers/agents/tests/news_agent_benchmarks/benchmarks.py b/submodules/moragents_dockers/agents/tests/news_agent_benchmarks/benchmarks.py new file mode 100644 index 0000000..7b7df4c --- /dev/null +++ b/submodules/moragents_dockers/agents/tests/news_agent_benchmarks/benchmarks.py @@ -0,0 +1,38 @@ +# submodules/benchmarks/news_agent_benchmarks/benchmarks.py + +import pytest +import logging +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 + +logging.basicConfig(level=logging.INFO) +logger = logging.getLogger(__name__) + + +def test_news_classification(): + for i, test_case in enumerate(Config.TEST_CASES): + article_text = test_case["article_text"] + expected_classification = test_case["expected_classification"] + + logger.info(f"Testing article classification (Test {i + 1})") + + # Ask the news agent to classify the article + response = ask_news_agent(article_text, Config.LOCAL_AGENT_URL) + + # Extract the classification from the response + classification = extract_classification(response) + + if classification == "UNKNOWN": + 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}" + + logger.info(f"Test case {i + 1} passed: Correctly classified as {classification}") + + logger.info("All test cases passed successfully") + + +if __name__ == "__main__": + pytest.main() diff --git a/submodules/moragents_dockers/agents/tests/news_agent_benchmarks/config.py b/submodules/moragents_dockers/agents/tests/news_agent_benchmarks/config.py new file mode 100644 index 0000000..2868b17 --- /dev/null +++ b/submodules/moragents_dockers/agents/tests/news_agent_benchmarks/config.py @@ -0,0 +1,14 @@ +class Config: + TEST_CASES = [ + { + "article_text": "ETH Prices zooms 10 percent today while Bitcoin's price surged to $70,000 today, breaking all previous records. Analysts attribute this to increased institutional adoption and positive regulatory news from several countries.", + "expected_classification": "RELEVANT", + }, + { + "article_text": "A new Tesla facility has opened in Texas, utilizing 100% renewable energy.", + "expected_classification": "NOT RELEVANT", + }, + # Add more test cases as needed + ] + + LOCAL_AGENT_URL = "http://127.0.0.1:5000/" diff --git a/submodules/moragents_dockers/agents/tests/news_agent_benchmarks/helpers.py b/submodules/moragents_dockers/agents/tests/news_agent_benchmarks/helpers.py new file mode 100644 index 0000000..7ce2623 --- /dev/null +++ b/submodules/moragents_dockers/agents/tests/news_agent_benchmarks/helpers.py @@ -0,0 +1,48 @@ +# submodules/benchmarks/news_agent_benchmarks/helpers.py + +import requests +import logging + +logging.basicConfig(level=logging.INFO) +logger = logging.getLogger(__name__) + + +def ask_news_agent(article_text: str, url: str) -> dict: + headers = {'Content-Type': 'application/json'} + payload = { + "prompt": { + "role": "user", + "content": f"Classify if this article is relevant to cryptocurrency price movements: {article_text}" + } + } + response = requests.post(url, headers=headers, json=payload) + if response.status_code == 200: + return response.json() + else: + raise Exception(f"Request failed with status code {response.status_code}: {response.text}") + + +def extract_classification(response: dict) -> str: + if not isinstance(response, dict): + logger.warning(f"Unexpected response type: {type(response)}") + return "UNKNOWN" + + content = response.get('content') + + if content is None: + logger.warning("Response content is None") + return "UNKNOWN" + + if not isinstance(content, str): + logger.warning(f"Unexpected content type: {type(content)}") + return "UNKNOWN" + + content = content.upper() + + if "NOT RELEVANT" in content: + return "NOT RELEVANT" + elif "RELEVANT" in content: + return "RELEVANT" + else: + logger.warning(f"Could not determine relevance from content: {content}") + return "NOT RELEVANT" \ No newline at end of file diff --git a/submodules/moragents_dockers/agents/tests/price_fetching/__init__.py b/submodules/moragents_dockers/agents/tests/price_fetching/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/submodules/moragents_dockers/agents/tests/price_fetching/adapters/__init__.py b/submodules/moragents_dockers/agents/tests/price_fetching/adapters/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/submodules/benchmarks/price_fetching/adapters/base_adapter.py b/submodules/moragents_dockers/agents/tests/price_fetching/adapters/base_adapter.py similarity index 100% rename from submodules/benchmarks/price_fetching/adapters/base_adapter.py rename to submodules/moragents_dockers/agents/tests/price_fetching/adapters/base_adapter.py diff --git a/submodules/benchmarks/price_fetching/adapters/coingecko_adapter.py b/submodules/moragents_dockers/agents/tests/price_fetching/adapters/coingecko_adapter.py similarity index 100% rename from submodules/benchmarks/price_fetching/adapters/coingecko_adapter.py rename to submodules/moragents_dockers/agents/tests/price_fetching/adapters/coingecko_adapter.py diff --git a/submodules/benchmarks/price_fetching/adapters/defillama_adapter.py b/submodules/moragents_dockers/agents/tests/price_fetching/adapters/defillama_adapter.py similarity index 100% rename from submodules/benchmarks/price_fetching/adapters/defillama_adapter.py rename to submodules/moragents_dockers/agents/tests/price_fetching/adapters/defillama_adapter.py diff --git a/submodules/benchmarks/price_fetching/benchmarks.py b/submodules/moragents_dockers/agents/tests/price_fetching/benchmarks.py similarity index 94% rename from submodules/benchmarks/price_fetching/benchmarks.py rename to submodules/moragents_dockers/agents/tests/price_fetching/benchmarks.py index 728a9b4..d376fcd 100644 --- a/submodules/benchmarks/price_fetching/benchmarks.py +++ b/submodules/moragents_dockers/agents/tests/price_fetching/benchmarks.py @@ -1,7 +1,7 @@ import time import argparse from helpers import ask_data_agent, compare_usd_values, extract_agent_usd_value -from submodules.benchmarks.price_fetching.config import coins, price_prompts, mcap_prompts, price_error_tolerance, mcap_error_tolerance, loop_delay +from submodules.moragents_dockers.agents.tests.price_fetching import coins, price_prompts, mcap_prompts, price_error_tolerance, mcap_error_tolerance, loop_delay from adapters.coingecko_adapter import CoingeckoAdapter from adapters.defillama_adapter import DefillamaAdapter diff --git a/submodules/benchmarks/price_fetching/config.py b/submodules/moragents_dockers/agents/tests/price_fetching/config.py similarity index 100% rename from submodules/benchmarks/price_fetching/config.py rename to submodules/moragents_dockers/agents/tests/price_fetching/config.py diff --git a/submodules/benchmarks/price_fetching/helpers.py b/submodules/moragents_dockers/agents/tests/price_fetching/helpers.py similarity index 100% rename from submodules/benchmarks/price_fetching/helpers.py rename to submodules/moragents_dockers/agents/tests/price_fetching/helpers.py diff --git a/submodules/benchmarks/price_fetching/readme.md b/submodules/moragents_dockers/agents/tests/price_fetching/readme.md similarity index 100% rename from submodules/benchmarks/price_fetching/readme.md rename to submodules/moragents_dockers/agents/tests/price_fetching/readme.md diff --git a/submodules/benchmarks/price_fetching/requirements.txt b/submodules/moragents_dockers/agents/tests/price_fetching/requirements.txt similarity index 100% rename from submodules/benchmarks/price_fetching/requirements.txt rename to submodules/moragents_dockers/agents/tests/price_fetching/requirements.txt diff --git a/submodules/benchmarks/reward_check_agent_benchmarks/README.md b/submodules/moragents_dockers/agents/tests/reward_check_agent_benchmarks/README.md similarity index 100% rename from submodules/benchmarks/reward_check_agent_benchmarks/README.md rename to submodules/moragents_dockers/agents/tests/reward_check_agent_benchmarks/README.md diff --git a/submodules/moragents_dockers/agents/tests/reward_check_agent_benchmarks/__init__.py b/submodules/moragents_dockers/agents/tests/reward_check_agent_benchmarks/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/submodules/moragents_dockers/agents/tests/reward_check_agent_benchmarks/adapters/__init__.py b/submodules/moragents_dockers/agents/tests/reward_check_agent_benchmarks/adapters/__init__.py new file mode 100644 index 0000000..e69de29 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 new file mode 100644 index 0000000..0482fc5 --- /dev/null +++ b/submodules/moragents_dockers/agents/tests/reward_check_agent_benchmarks/adapters/reward_check_adapter.py @@ -0,0 +1,14 @@ +from submodules.moragents_dockers.agents.src.claim_agent.src.tools import get_current_user_reward + +class RewardCheckAdapter: + def __init__(self): + pass + + @property + def name(self) -> str: + return "RewardCheckAdapter" + + def get_rewards(self, wallet_address: str) -> dict: + pool_0_reward = get_current_user_reward(wallet_address, 0) + pool_1_reward = get_current_user_reward(wallet_address, 1) + return {"pool_0_reward": pool_0_reward, "pool_1_reward": pool_1_reward} 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 new file mode 100644 index 0000000..03395e7 --- /dev/null +++ b/submodules/moragents_dockers/agents/tests/reward_check_agent_benchmarks/benchmarks.py @@ -0,0 +1,42 @@ +import pytest +from submodules.benchmarks.claim_agent_benchmarks.helpers import request_claim +from submodules.moragents_dockers.agents.tests.claim_agent_benchmarks.config import Config + + +def test_claim_process(): + for i, wallet_data in enumerate(Config.WALLET_ADDRESSES): + wallet_address = wallet_data["wallet"] + + print(f"Testing for wallet {wallet_address} (Test {i + 1})") + + # Step 1: Request to claim rewards + 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 + + 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']) + + # 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'" + + print(f"Step 1 passed for wallet {wallet_address}: Claim process triggered successfully") + + print(f"All steps passed for wallet {wallet_address}") + + +if __name__ == "__main__": + pytest.main() \ No newline at end of file diff --git a/submodules/benchmarks/reward_check_agent_benchmarks/config.py b/submodules/moragents_dockers/agents/tests/reward_check_agent_benchmarks/config.py similarity index 80% rename from submodules/benchmarks/reward_check_agent_benchmarks/config.py rename to submodules/moragents_dockers/agents/tests/reward_check_agent_benchmarks/config.py index e684a3f..2522622 100644 --- a/submodules/benchmarks/reward_check_agent_benchmarks/config.py +++ b/submodules/moragents_dockers/agents/tests/reward_check_agent_benchmarks/config.py @@ -7,7 +7,7 @@ # Configuration object class Config: WEB3RPCURL = { - "1": "https://cloudflare-eth.com", + "1": "https://eth.llamarpc.com/", } DISTRIBUTION_PROXY_ADDRESS = "0x47176B2Af9885dC6C4575d4eFd63895f7Aaa4790" @@ -63,10 +63,9 @@ class Config: ] reward_check_prompts = [ - "Check MOR rewards for wallet_address: {} in pool_id {}", - "Check the MOR rewards for address: {} in pool_id {}", - "Check my MOR Rewards for address: {} for pool_id {}", - "Check the MOR rewards accrued for wallet_address: {} in pool_id {}" - "Check the MOR rewards assigned to wallet_address: {} in pool_id {}", - + "i want to check my mor rewards", + "check my mor rewards", + "check my rewards", + "please check my mor rewards", + "hi, check my mor rewards", ] diff --git a/submodules/benchmarks/reward_check_agent_benchmarks/helpers.py b/submodules/moragents_dockers/agents/tests/reward_check_agent_benchmarks/helpers.py similarity index 59% rename from submodules/benchmarks/reward_check_agent_benchmarks/helpers.py rename to submodules/moragents_dockers/agents/tests/reward_check_agent_benchmarks/helpers.py index 45b0525..9f05361 100644 --- a/submodules/benchmarks/reward_check_agent_benchmarks/helpers.py +++ b/submodules/moragents_dockers/agents/tests/reward_check_agent_benchmarks/helpers.py @@ -9,25 +9,27 @@ 'Content-Type': 'application/json', } -def ask_claim_agent(prompt: str): + +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 } - response = requests.post(url, headers=headers, json=payload) if response.status_code == 200: return response.json()['content'] else: raise Exception(f"Request failed with status code {response.status_code}: {response.text}") + 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 + address=web3.to_checksum_address(Config.DISTRIBUTION_PROXY_ADDRESS), + abi=Config.DISTRIBUTION_ABI ) try: @@ -43,8 +45,19 @@ def get_current_user_reward(wallet_address, pool_id): except Exception as e: raise Exception(f"Error occurred while fetching the reward: {str(e)}") -def extract_reward_value_from_response(response: str) -> float: - match = re.search(r'(\d+\.\d+) MOR', response) - if match: - return float(match.group(1)) - raise ValueError("Could not extract a reward value from the agent's response") + +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', + response, + re.DOTALL + ) + + rewards = {} + if matches: + # Assuming the first match group corresponds to pool 0 and the second to pool 1 + rewards["pool_0_reward"] = float(matches[0][0]) + rewards["pool_1_reward"] = float(matches[0][1]) + + return rewards diff --git a/submodules/moragents_dockers/docker-compose-apple.yml b/submodules/moragents_dockers/docker-compose-apple.yml index 9cf6833..27b8acb 100644 --- a/submodules/moragents_dockers/docker-compose-apple.yml +++ b/submodules/moragents_dockers/docker-compose-apple.yml @@ -2,7 +2,7 @@ version: "3.8" services: agents: - image: lachsbagel/moragents_dockers-agents:apple-0.1.0 + image: lachsbagel/moragents_dockers-agents:apple-0.2.0 build: dockerfile: Dockerfile-apple context: ./agents @@ -18,7 +18,7 @@ services: - BASE_URL=http://host.docker.internal:11434 nginx: - image: lachsbagel/moragents_dockers-nginx:apple-0.1.0 + image: lachsbagel/moragents_dockers-nginx:apple-0.2.0 build: context: ./frontend dockerfile: Dockerfile diff --git a/submodules/moragents_dockers/docker-compose.yml b/submodules/moragents_dockers/docker-compose.yml index 135b524..f9ad6b0 100644 --- a/submodules/moragents_dockers/docker-compose.yml +++ b/submodules/moragents_dockers/docker-compose.yml @@ -2,7 +2,7 @@ version: "3.8" services: agents: - image: lachsbagel/moragents_dockers-agents:amd64-0.1.0 + image: lachsbagel/moragents_dockers-agents:amd64-0.2.0 build: dockerfile: Dockerfile context: ./agents @@ -18,7 +18,7 @@ services: - BASE_URL=http://host.docker.internal:11434 nginx: - image: lachsbagel/moragents_dockers-nginx:amd64-0.1.0 + image: lachsbagel/moragents_dockers-nginx:amd64-0.2.0 build: context: ./frontend dockerfile: Dockerfile diff --git a/submodules/moragents_dockers/frontend/components/Chat/index.tsx b/submodules/moragents_dockers/frontend/components/Chat/index.tsx index d450856..37011db 100644 --- a/submodules/moragents_dockers/frontend/components/Chat/index.tsx +++ b/submodules/moragents_dockers/frontend/components/Chat/index.tsx @@ -2,9 +2,14 @@ import React, { FC, useCallback, useEffect, useState } from "react"; import { Box } from "@chakra-ui/react"; import { ChatMessage, + ClaimMessage, + ClaimMessagePayload, + ClaimTransactionPayload, + sendClaimStatus, sendSwapStatus, getHttpClient, SWAP_STATUS, + CLAIM_STATUS, } from "../../services/backendClient"; import { useAccount, @@ -60,7 +65,7 @@ export const Chat: FC = ({ async (status: string, hash: string, isApprove: number) => { try { const response: ChatMessage = await sendSwapStatus( - getHttpClient(selectedAgent), + getHttpClient(), chainId, address?.toLowerCase() || "0x", status, @@ -153,6 +158,84 @@ export const Chat: FC = ({ [address, handleSwapStatus, sendTransaction] ); + const handleClaimStatus = useCallback( + async (status: string, hash: string) => { + try { + const response: ChatMessage = await sendClaimStatus( + getHttpClient(), + chainId, + address?.toLowerCase() || "0x", + status, + hash + ); + + if ( + response.role === "assistant" && + typeof response.content === "string" + ) { + setMessagesData((prev) => [ + ...prev, + { + role: "assistant", + content: response.content, + } as UserOrAssistantMessage, + ]); + } else if (response.role === "claim") { + setMessagesData((prev) => [...prev, response as ClaimMessage]); + } + + setTxHash(""); + setShowSpinner(false); + } catch (error) { + console.log(`Error sending claim status: ${error}`); + onBackendError(); + setTxHash(""); + setShowSpinner(false); + } + }, + [selectedAgent, chainId, address, onBackendError] + ); + + // Add this near your other useTransactionConfirmations hooks + const claimConfirmations = useTransactionConfirmations({ + hash: (txHash || "0x") as `0x${string}`, + }); + + // Add this effect to watch for claim transaction confirmations + useEffect(() => { + if (txHash && claimConfirmations.data && claimConfirmations.data >= 1) { + handleClaimStatus(CLAIM_STATUS.SUCCESS, txHash); + } + }, [txHash, claimConfirmations.data, handleClaimStatus]); + + // Modify handleClaimSubmit to use the same txHash state + const handleClaimSubmit = useCallback( + (claimTx: ClaimTransactionPayload) => { + setTxHash(""); + console.log("Claim transaction to be sent:", claimTx); + sendTransaction( + { + account: address, + data: claimTx.data as `0x${string}`, + to: claimTx.to as `0x${string}`, + value: BigInt(claimTx.value), + chainId: parseInt(claimTx.chainId), + }, + { + onSuccess: (hash) => { + setTxHash(hash); + handleClaimStatus(CLAIM_STATUS.INIT, hash); + }, + onError: (error) => { + console.log(`Error sending transaction: ${error}`); + handleClaimStatus(CLAIM_STATUS.FAIL, ""); + }, + } + ); + }, + [address, handleClaimStatus, sendTransaction] + ); + return ( = ({ selectedAgent={selectedAgent} onCancelSwap={onCancelSwap} onSwapSubmit={handleSwapSubmit} + onClaimSubmit={handleClaimSubmit} /> {showSpinner && } = ({ const [message, setMessage] = useState(""); const [file, setFile] = useState(null); - const agentSupportsFileUploads = - availableAgents[selectedAgent]?.supportsFiles || false; + const agentSupportsFileUploads = true; const handleSubmit = async () => { if (!message && !file) return; diff --git a/submodules/moragents_dockers/frontend/components/ClaimForm/ClaimForm.tsx b/submodules/moragents_dockers/frontend/components/ClaimForm/ClaimForm.tsx new file mode 100644 index 0000000..ffe6f14 --- /dev/null +++ b/submodules/moragents_dockers/frontend/components/ClaimForm/ClaimForm.tsx @@ -0,0 +1,57 @@ +import React, { FC, useState } from "react"; +import { Button, VStack, Text } from "@chakra-ui/react"; +import { ClaimMessagePayload, ClaimTransactionPayload, getHttpClient } from "../../services/backendClient"; +import { useAccount, useChainId, useSendTransaction } from "wagmi"; +import { parseEther } from "viem"; + +type ClaimFormProps = { + isActive: boolean; + selectedAgent: string; + fromMessage: ClaimMessagePayload; + onSubmitClaim: (claimTx: any) => void; +}; + +export const ClaimForm: FC = ({ + isActive, + selectedAgent, + fromMessage, + onSubmitClaim, +}) => { + console.log("ClaimForm received fromMessage:", fromMessage); + const [isLoading, setIsLoading] = useState(false); + const { address } = useAccount(); + const chainId = useChainId(); + +const handleClaim = async () => { + setIsLoading(true); + try { + if (!fromMessage?.content?.transactions || !Array.isArray(fromMessage.content.transactions)) { + throw new Error("Invalid transaction data"); + } + const transactions: ClaimTransactionPayload[] = fromMessage.content.transactions.map(item => item.transaction); + + console.log("Transactions to be submitted:", transactions); + + // Pass the first transaction to onSubmitClaim + if (transactions.length > 0) { + onSubmitClaim(transactions[0]); + } else { + throw new Error("No transactions to process"); + } + } catch (error) { + console.error("Failed to process claim:", error); + // Handle error (e.g., show error message to user) + } finally { + setIsLoading(false); + } +}; + + return ( + + You have rewards available to claim. Would you like to proceed? + + + ); +}; \ 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 new file mode 100644 index 0000000..f0d88f7 --- /dev/null +++ b/submodules/moragents_dockers/frontend/components/ClaimMessage/ClaimMessage.tsx @@ -0,0 +1,31 @@ +import React, { FC } from "react"; +import { ClaimForm } from "../ClaimForm/ClaimForm"; +import { ClaimMessagePayload } from "../../services/backendClient"; + +type ClaimMessageProps = { + isActive: boolean; + selectedAgent: string; + fromMessage: ClaimMessagePayload; + onSubmitClaim: (claimTx: any) => void; +}; + +export const ClaimMessage: FC = ({ + isActive, + selectedAgent, + fromMessage, + onSubmitClaim, +}) => { + console.log("ClaimMessage received fromMessage:", fromMessage); + if (!fromMessage) { + return
Error: Claim message data is not available.
; + } + + return ( + + ); +}; \ No newline at end of file diff --git a/submodules/moragents_dockers/frontend/components/HeaderBar/index.tsx b/submodules/moragents_dockers/frontend/components/HeaderBar/index.tsx index 14ea0ca..cfc243a 100644 --- a/submodules/moragents_dockers/frontend/components/HeaderBar/index.tsx +++ b/submodules/moragents_dockers/frontend/components/HeaderBar/index.tsx @@ -1,9 +1,14 @@ import React, { FC, ComponentPropsWithoutRef } from "react"; import Image from "next/image"; -import { Box, HStack, Spacer } from "@chakra-ui/react"; +import { Box, HStack, Spacer, Button } from "@chakra-ui/react"; import { ConnectButton } from "@rainbow-me/rainbowkit"; import SettingsButton from "../Settings"; import classes from "./index.module.css"; +import { + getHttpClient, + clearMessagesHistory, +} from "../../services/backendClient"; +import { useRouter } from "next/router"; export interface HeaderBarProps extends ComponentPropsWithoutRef<"div"> { onAgentChanged(agent: string): void; @@ -11,6 +16,18 @@ export interface HeaderBarProps extends ComponentPropsWithoutRef<"div"> { } export const HeaderBar: FC = (props) => { + const backendClient = getHttpClient(); + const router = useRouter(); + + const handleClearChatHistory = async () => { + try { + await clearMessagesHistory(backendClient); + router.reload(); + } catch (error) { + console.error("Failed to clear chat history:", error); + } + }; + return ( @@ -19,6 +36,9 @@ export const HeaderBar: FC = (props) => { + diff --git a/submodules/moragents_dockers/frontend/components/MessageItem/index.tsx b/submodules/moragents_dockers/frontend/components/MessageItem/index.tsx index 0977e12..9e8c537 100644 --- a/submodules/moragents_dockers/frontend/components/MessageItem/index.tsx +++ b/submodules/moragents_dockers/frontend/components/MessageItem/index.tsx @@ -1,13 +1,16 @@ import React, { FC } from "react"; import { Grid, GridItem, Text } from "@chakra-ui/react"; +import ReactMarkdown from "react-markdown"; import { ChatMessage, SwapMessagePayload, UserOrAssistantMessage, + ClaimMessagePayload, } from "../../services/backendClient"; import { Avatar } from "../Avatar"; import { availableAgents } from "../../config"; import { SwapMessage } from "../SwapMessage"; +import { ClaimMessage } from "../ClaimMessage/ClaimMessage"; import { Tweet } from "../Tweet"; import styles from "./index.module.css"; @@ -21,7 +24,9 @@ type MessageItemProps = { selectedAgent: string; onCancelSwap: (fromAction: number) => void; onSwapSubmit: (swapTx: any) => void; + onClaimSubmit: (claimTx: any) => void; isLastSwapMessage: boolean; + isLastClaimMessage: boolean; }; export const MessageItem: FC = ({ @@ -29,7 +34,9 @@ export const MessageItem: FC = ({ selectedAgent, onCancelSwap, onSwapSubmit, + onClaimSubmit, isLastSwapMessage, + isLastClaimMessage, }) => { const agentName = availableAgents[selectedAgent]?.name || UNDEFINED_AGENT; const isUser = message.role === USER_ROLE; @@ -40,7 +47,10 @@ export const MessageItem: FC = ({ if ((message as UserOrAssistantMessage).agentName === TWEET_AGENT) { return ; } - return {content}; + + return ( + {content} + ); } if ((message as UserOrAssistantMessage).agentName === SWAP_AGENT) { @@ -55,6 +65,18 @@ export const MessageItem: FC = ({ ); } + if (message.role === "claim") { + console.log("MessageItem rendering ClaimMessage with content:", message.content); + return ( + + ); + } + return ( {JSON.stringify(content)} ); @@ -75,4 +97,4 @@ export const MessageItem: FC = ({ {renderContent()} ); -}; +}; \ No newline at end of file diff --git a/submodules/moragents_dockers/frontend/components/MessageList/index.tsx b/submodules/moragents_dockers/frontend/components/MessageList/index.tsx index fe03d88..d09f87d 100644 --- a/submodules/moragents_dockers/frontend/components/MessageList/index.tsx +++ b/submodules/moragents_dockers/frontend/components/MessageList/index.tsx @@ -8,6 +8,7 @@ type MessageListProps = { selectedAgent: string; onCancelSwap: (fromAction: number) => void; onSwapSubmit: (swapTx: any) => void; + onClaimSubmit: (claimTx: any) => void; }; export const MessageList: FC = ({ @@ -15,6 +16,7 @@ export const MessageList: FC = ({ selectedAgent, onCancelSwap, onSwapSubmit, + onClaimSubmit, }) => { return ( = ({ selectedAgent={selectedAgent} onCancelSwap={onCancelSwap} onSwapSubmit={onSwapSubmit} + onClaimSubmit={onClaimSubmit} isLastSwapMessage={ index === messages.length - 1 && message.role === "swap" } + isLastClaimMessage={ + index === messages.length - 1 && message.role === "claim" + } /> ))} ); -}; +}; \ No newline at end of file diff --git a/submodules/moragents_dockers/frontend/components/SwapForm/index.tsx b/submodules/moragents_dockers/frontend/components/SwapForm/index.tsx index 5b323d8..1d44164 100644 --- a/submodules/moragents_dockers/frontend/components/SwapForm/index.tsx +++ b/submodules/moragents_dockers/frontend/components/SwapForm/index.tsx @@ -151,7 +151,7 @@ export const SwapForm: FC = ({ } const _payload = await getApprovalTxPayload( - getHttpClient(selectedAgent), + getHttpClient(), chainId, src_address, Number(src_amount), @@ -166,14 +166,7 @@ export const SwapForm: FC = ({ setIsButtonLoading(false); } }, - [ - onSubmitApprove, - isNativeToken, - decimals?.data, - fromMessage, - chainId, - selectedAgent, - ] + [onSubmitApprove, isNativeToken, decimals?.data, fromMessage, chainId] ); const handleSwap = useCallback( @@ -182,7 +175,7 @@ export const SwapForm: FC = ({ try { const _payload = await getSwapTxPayload( - getHttpClient(selectedAgent), + getHttpClient(), fromMessage.src_address, fromMessage.dst_address, address, @@ -202,14 +195,7 @@ export const SwapForm: FC = ({ setIsButtonLoading(false); } }, - [ - fromMessage, - chainId, - selectedAgent, - formData.slippage, - onSubmitSwap, - decimals, - ] + [fromMessage, chainId, formData.slippage, onSubmitSwap, decimals] ); useEffect(() => { diff --git a/submodules/moragents_dockers/frontend/components/Tweet/index.tsx b/submodules/moragents_dockers/frontend/components/Tweet/index.tsx index 4563293..1deab63 100644 --- a/submodules/moragents_dockers/frontend/components/Tweet/index.tsx +++ b/submodules/moragents_dockers/frontend/components/Tweet/index.tsx @@ -54,7 +54,7 @@ export const Tweet: FC = ({ initialContent, selectedAgent }) => { const handleTweet = async () => { setIsTweeting(true); - const backendClient = getHttpClient(selectedAgent); + const backendClient = getHttpClient(); try { await postTweet(backendClient, tweetContent); @@ -72,7 +72,7 @@ export const Tweet: FC = ({ initialContent, selectedAgent }) => { const handleRegenerate = async () => { setIsRegenerating(true); - const backendClient = getHttpClient(selectedAgent); + const backendClient = getHttpClient(); try { const newTweet = await regenerateTweet(backendClient); diff --git a/submodules/moragents_dockers/frontend/config.ts b/submodules/moragents_dockers/frontend/config.ts index 0f4d11a..647d724 100644 --- a/submodules/moragents_dockers/frontend/config.ts +++ b/submodules/moragents_dockers/frontend/config.ts @@ -1,25 +1,25 @@ -export const routerAddress = '0x111111125421cA6dc452d289314280a0f8842A65'; -export const oneInchNativeToken = '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE'; +export const routerAddress = "0x111111125421cA6dc452d289314280a0f8842A65"; +export const oneInchNativeToken = "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE"; export const availableAgents: { - [key: string]: { - name: string, - description: string, - endpoint: string, - requirements: { - connectedWallet: boolean - }, - supportsFiles?: boolean - - } + [key: string]: { + name: string; + description: string; + endpoint: string; + requirements: { + connectedWallet: boolean; + }; + supportsFiles?: boolean; + }; } = { - 'swap-agent': { - 'name': 'Morpheus', - 'description': 'performs multiple tasks crypto data agent,swap agent and rag agent', - 'endpoint': 'http://127.0.0.1:8080', - requirements: { - connectedWallet: true - }, - supportsFiles: true - } -} \ No newline at end of file + "swap-agent": { + name: "Morpheus", + description: + "performs multiple tasks crypto data agent,swap agent and rag agent", + endpoint: "http://127.0.0.1:8080", + requirements: { + connectedWallet: true, + }, + supportsFiles: true, + }, +}; diff --git a/submodules/moragents_dockers/frontend/package-lock.json b/submodules/moragents_dockers/frontend/package-lock.json index 54d296f..0529c6b 100644 --- a/submodules/moragents_dockers/frontend/package-lock.json +++ b/submodules/moragents_dockers/frontend/package-lock.json @@ -1,12 +1,12 @@ { "name": "Morpheus AI", - "version": "0.1.0", + "version": "0.2.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "Morpheus AI", - "version": "0.1.0", + "version": "0.2.0", "license": "MIT", "dependencies": { "@chakra-ui/icons": "^2.1.1", @@ -21,6 +21,7 @@ "react": "^18.2.0", "react-dom": "^18.2.0", "react-icons": "^5.3.0", + "react-markdown": "^9.0.0", "viem": "^2.8.12", "wagmi": "^2.5.11" }, @@ -6364,6 +6365,15 @@ "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==" }, + "node_modules/@types/estree-jsx": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@types/estree-jsx/-/estree-jsx-1.0.5.tgz", + "integrity": "sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg==", + "license": "MIT", + "dependencies": { + "@types/estree": "*" + } + }, "node_modules/@types/filesystem": { "version": "0.0.36", "resolved": "https://registry.npmjs.org/@types/filesystem/-/filesystem-0.0.36.tgz", @@ -6382,6 +6392,15 @@ "resolved": "https://registry.npmjs.org/@types/har-format/-/har-format-1.2.15.tgz", "integrity": "sha512-RpQH4rXLuvTXKR0zqHq3go0RVXYv/YVqv4TnPH95VbwUxZdQlK1EtcMvQvMpDngHbt13Csh9Z4qT9AbkiQH5BA==" }, + "node_modules/@types/hast": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz", + "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==", + "license": "MIT", + "dependencies": { + "@types/unist": "*" + } + }, "node_modules/@types/http-cache-semantics": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.4.tgz", @@ -6445,6 +6464,15 @@ "@types/lodash": "*" } }, + "node_modules/@types/mdast": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.4.tgz", + "integrity": "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==", + "license": "MIT", + "dependencies": { + "@types/unist": "*" + } + }, "node_modules/@types/ms": { "version": "0.7.34", "resolved": "https://registry.npmjs.org/@types/ms/-/ms-0.7.34.tgz", @@ -6454,6 +6482,7 @@ "version": "18.19.49", "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.49.tgz", "integrity": "sha512-ALCeIR6n0nQ7j0FUF1ycOhrp6+XutJWqEu/vtdEqXFUQwkBfgUA5cEg3ZNmjWGF/ZYA/FcF9QMkL55Ar0O6UrA==", + "license": "MIT", "dependencies": { "undici-types": "~5.26.4" } @@ -6466,14 +6495,13 @@ "node_modules/@types/prop-types": { "version": "15.7.12", "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.12.tgz", - "integrity": "sha512-5zvhXYtRNRluoE/jAp4GVsSduVUzNWKkOZrCDBWYtE7biZywwdC2AcEzg+cSMLFRfVgeAFqpfNabiPjxFddV1Q==", - "devOptional": true + "integrity": "sha512-5zvhXYtRNRluoE/jAp4GVsSduVUzNWKkOZrCDBWYtE7biZywwdC2AcEzg+cSMLFRfVgeAFqpfNabiPjxFddV1Q==" }, "node_modules/@types/react": { "version": "18.3.5", "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.5.tgz", "integrity": "sha512-WeqMfGJLGuLCqHGYRGHxnKrXcTitc6L/nBUWfWPcTarG3t9PsquqUMuVeXZeca+mglY4Vo5GZjCi0A3Or2lnxA==", - "devOptional": true, + "license": "MIT", "dependencies": { "@types/prop-types": "*", "csstype": "^3.0.2" @@ -6507,6 +6535,12 @@ "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz", "integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==" }, + "node_modules/@types/unist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", + "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", + "license": "MIT" + }, "node_modules/@types/yargs": { "version": "17.0.32", "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.32.tgz", @@ -6652,8 +6686,7 @@ "node_modules/@ungap/structured-clone": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", - "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", - "dev": true + "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==" }, "node_modules/@vanilla-extract/css": { "version": "1.14.0", @@ -7912,6 +7945,16 @@ "@babel/plugin-syntax-flow": "^7.12.1" } }, + "node_modules/bail": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/bail/-/bail-2.0.2.tgz", + "integrity": "sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", @@ -8432,6 +8475,16 @@ } ] }, + "node_modules/ccount": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/ccount/-/ccount-2.0.1.tgz", + "integrity": "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -8447,6 +8500,46 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, + "node_modules/character-entities": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-2.0.2.tgz", + "integrity": "sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-entities-html4": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/character-entities-html4/-/character-entities-html4-2.1.0.tgz", + "integrity": "sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-entities-legacy": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-3.0.0.tgz", + "integrity": "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-reference-invalid": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/character-reference-invalid/-/character-reference-invalid-2.0.1.tgz", + "integrity": "sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/chokidar": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", @@ -8849,6 +8942,16 @@ "node": ">= 0.8" } }, + "node_modules/comma-separated-tokens": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz", + "integrity": "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/command-exists": { "version": "1.2.9", "resolved": "https://registry.npmjs.org/command-exists/-/command-exists-1.2.9.tgz", @@ -9233,6 +9336,19 @@ "node": ">=0.10.0" } }, + "node_modules/decode-named-character-reference": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/decode-named-character-reference/-/decode-named-character-reference-1.0.2.tgz", + "integrity": "sha512-O8x12RzrUF8xyVcY0KJowWsmaJxQbmy0/EtnNtHRpsOcT7dFk5W598coHqBVpmWo1oQQfsCqfCmkZN5DJrZVdg==", + "license": "MIT", + "dependencies": { + "character-entities": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/decode-uri-component": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.2.tgz", @@ -9395,7 +9511,6 @@ "version": "2.0.3", "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", - "dev": true, "engines": { "node": ">=6" } @@ -9436,6 +9551,19 @@ "resolved": "https://registry.npmjs.org/detect-node-es/-/detect-node-es-1.1.0.tgz", "integrity": "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==" }, + "node_modules/devlop": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/devlop/-/devlop-1.1.0.tgz", + "integrity": "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==", + "license": "MIT", + "dependencies": { + "dequal": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/dijkstrajs": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/dijkstrajs/-/dijkstrajs-1.0.3.tgz", @@ -10270,6 +10398,16 @@ "node": ">=4.0" } }, + "node_modules/estree-util-is-identifier-name": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/estree-util-is-identifier-name/-/estree-util-is-identifier-name-3.0.0.tgz", + "integrity": "sha512-hFtqIDZTIUZ9BXLb8y4pYGyk6+wekIivNVTcmvk8NoOh+VeRn5y6cEHzbURrWbfp1fIqdVipilzj+lfaadNZmg==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/esutils": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", @@ -10519,6 +10657,12 @@ "node": ">=4" } }, + "node_modules/extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "license": "MIT" + }, "node_modules/extension-port-stream": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/extension-port-stream/-/extension-port-stream-2.1.1.tgz", @@ -11374,6 +11518,46 @@ "node": ">= 0.4" } }, + "node_modules/hast-util-to-jsx-runtime": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/hast-util-to-jsx-runtime/-/hast-util-to-jsx-runtime-2.3.0.tgz", + "integrity": "sha512-H/y0+IWPdsLLS738P8tDnrQ8Z+dj12zQQ6WC11TIM21C8WFVoIxcqWXf2H3hiTVZjF1AWqoimGwrTWecWrnmRQ==", + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/unist": "^3.0.0", + "comma-separated-tokens": "^2.0.0", + "devlop": "^1.0.0", + "estree-util-is-identifier-name": "^3.0.0", + "hast-util-whitespace": "^3.0.0", + "mdast-util-mdx-expression": "^2.0.0", + "mdast-util-mdx-jsx": "^3.0.0", + "mdast-util-mdxjs-esm": "^2.0.0", + "property-information": "^6.0.0", + "space-separated-tokens": "^2.0.0", + "style-to-object": "^1.0.0", + "unist-util-position": "^5.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-whitespace": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/hast-util-whitespace/-/hast-util-whitespace-3.0.0.tgz", + "integrity": "sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/hermes-estree": { "version": "0.15.0", "resolved": "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.15.0.tgz", @@ -11441,6 +11625,16 @@ "void-elements": "3.1.0" } }, + "node_modules/html-url-attributes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/html-url-attributes/-/html-url-attributes-3.0.0.tgz", + "integrity": "sha512-/sXbVCWayk6GDVg3ctOX6nxaVj7So40FcFAnWlWGNAB1LpYKcV5Cd10APjPjW80O7zYW2MsjBV4zZ7IZO5fVow==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/http-cache-semantics": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz", @@ -11695,6 +11889,12 @@ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, + "node_modules/inline-style-parser": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.2.4.tgz", + "integrity": "sha512-0aO8FkhNZlj/ZIbNi7Lxxr12obT7cL1moPfE4tg1LkX7LlLfC6DeX4l2ZEud1ukP9jNQyNnfzQVqwbwmAATY4Q==", + "license": "MIT" + }, "node_modules/internal-slot": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.7.tgz", @@ -11733,6 +11933,30 @@ "url": "https://github.com/sponsors/brc-dd" } }, + "node_modules/is-alphabetical": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-2.0.1.tgz", + "integrity": "sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/is-alphanumerical": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-2.0.1.tgz", + "integrity": "sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw==", + "license": "MIT", + "dependencies": { + "is-alphabetical": "^2.0.0", + "is-decimal": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/is-arguments": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", @@ -11875,6 +12099,16 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-decimal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-2.0.1.tgz", + "integrity": "sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/is-directory": { "version": "0.3.1", "resolved": "https://registry.npmjs.org/is-directory/-/is-directory-0.3.1.tgz", @@ -11951,6 +12185,16 @@ "node": ">=0.10.0" } }, + "node_modules/is-hexadecimal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-2.0.1.tgz", + "integrity": "sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/is-inside-container": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-inside-container/-/is-inside-container-1.0.0.tgz", @@ -12964,6 +13208,16 @@ "logkitty": "bin/logkitty.js" } }, + "node_modules/longest-streak": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/longest-streak/-/longest-streak-3.1.0.tgz", + "integrity": "sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/loose-envify": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", @@ -13038,6 +13292,158 @@ "integrity": "sha512-q9JtQJKjpsVxCRVgQ+WapguSbKC3SQ5HEzFGPAJMStgh3QjCawp00UKv3MTTAArTmGmmPUvllHZoNbZ3gs0I+Q==", "peer": true }, + "node_modules/mdast-util-from-markdown": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-2.0.1.tgz", + "integrity": "sha512-aJEUyzZ6TzlsX2s5B4Of7lN7EQtAxvtradMMglCQDyaTFgse6CmtmdJ15ElnVRlCg1vpNyVtbem0PWzlNieZsA==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "@types/unist": "^3.0.0", + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "mdast-util-to-string": "^4.0.0", + "micromark": "^4.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-decode-string": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0", + "unist-util-stringify-position": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-mdx-expression": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mdast-util-mdx-expression/-/mdast-util-mdx-expression-2.0.1.tgz", + "integrity": "sha512-J6f+9hUp+ldTZqKRSg7Vw5V6MqjATc+3E4gf3CFNcuZNWD8XdyI6zQ8GqH7f8169MM6P7hMBRDVGnn7oHB9kXQ==", + "license": "MIT", + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-mdx-jsx": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/mdast-util-mdx-jsx/-/mdast-util-mdx-jsx-3.1.3.tgz", + "integrity": "sha512-bfOjvNt+1AcbPLTFMFWY149nJz0OjmewJs3LQQ5pIyVGxP4CdOqNVJL6kTaM5c68p8q82Xv3nCyFfUnuEcH3UQ==", + "license": "MIT", + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "@types/unist": "^3.0.0", + "ccount": "^2.0.0", + "devlop": "^1.1.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0", + "parse-entities": "^4.0.0", + "stringify-entities": "^4.0.0", + "unist-util-stringify-position": "^4.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-mdxjs-esm": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mdast-util-mdxjs-esm/-/mdast-util-mdxjs-esm-2.0.1.tgz", + "integrity": "sha512-EcmOpxsZ96CvlP03NghtH1EsLtr0n9Tm4lPUJUBccV9RwUOneqSycg19n5HGzCf+10LozMRSObtVr3ee1WoHtg==", + "license": "MIT", + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-phrasing": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/mdast-util-phrasing/-/mdast-util-phrasing-4.1.0.tgz", + "integrity": "sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "unist-util-is": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-hast": { + "version": "13.2.0", + "resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-13.2.0.tgz", + "integrity": "sha512-QGYKEuUsYT9ykKBCMOEDLsU5JRObWQusAolFMeko/tYPufNkRffBAQjIE+99jbA87xv6FgmjLtwjh9wBWajwAA==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "@ungap/structured-clone": "^1.0.0", + "devlop": "^1.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "trim-lines": "^3.0.0", + "unist-util-position": "^5.0.0", + "unist-util-visit": "^5.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-markdown": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mdast-util-to-markdown/-/mdast-util-to-markdown-2.1.0.tgz", + "integrity": "sha512-SR2VnIEdVNCJbP6y7kVTJgPLifdr8WEU440fQec7qHoHOUz/oJ2jmNRqdDQ3rbiStOXb2mCDGTuwsK5OPUgYlQ==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "@types/unist": "^3.0.0", + "longest-streak": "^3.0.0", + "mdast-util-phrasing": "^4.0.0", + "mdast-util-to-string": "^4.0.0", + "micromark-util-decode-string": "^2.0.0", + "unist-util-visit": "^5.0.0", + "zwitch": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-4.0.0.tgz", + "integrity": "sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/media-query-parser": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/media-query-parser/-/media-query-parser-2.0.2.tgz", @@ -13601,26 +14007,468 @@ "resolved": "https://registry.npmjs.org/micro-ftch/-/micro-ftch-0.3.1.tgz", "integrity": "sha512-/0LLxhzP0tfiR5hcQebtudP56gUurs2CLkGarnCiB/OqEyUFQ6U3paQi/tgLv0hBJYt2rnr9MNpxz4fiiugstg==" }, - "node_modules/micromatch": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", - "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "node_modules/micromark": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/micromark/-/micromark-4.0.0.tgz", + "integrity": "sha512-o/sd0nMof8kYff+TqcDx3VSrgBTcZpSvYcAHIfHhv5VAuNmisCxjhx6YmxS8PFEpb9z5WKWKPdzf0jM23ro3RQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", "dependencies": { - "braces": "^3.0.2", - "picomatch": "^2.3.1" - }, - "engines": { - "node": ">=8.6" + "@types/debug": "^4.0.0", + "debug": "^4.0.0", + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "micromark-core-commonmark": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-combine-extensions": "^2.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-encode": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "micromark-util-subtokenize": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-core-commonmark": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-core-commonmark/-/micromark-core-commonmark-2.0.1.tgz", + "integrity": "sha512-CUQyKr1e///ZODyD1U3xit6zXwy1a8q2a1S1HKtIlmgvurrEpaw/Y9y6KSIbF8P59cn/NjzHyO+Q2fAyYLQrAA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "micromark-factory-destination": "^2.0.0", + "micromark-factory-label": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-factory-title": "^2.0.0", + "micromark-factory-whitespace": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-classify-character": "^2.0.0", + "micromark-util-html-tag-name": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-subtokenize": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-destination": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-factory-destination/-/micromark-factory-destination-2.0.0.tgz", + "integrity": "sha512-j9DGrQLm/Uhl2tCzcbLhy5kXsgkHUrjJHg4fFAeoMRwJmJerT9aw4FEhIbZStWN8A3qMwOp1uzHr4UL8AInxtA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" } }, - "node_modules/mime": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz", - "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==", - "peer": true, - "bin": { - "mime": "cli.js" - }, + "node_modules/micromark-factory-label": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-factory-label/-/micromark-factory-label-2.0.0.tgz", + "integrity": "sha512-RR3i96ohZGde//4WSe/dJsxOX6vxIg9TimLAS3i4EhBAFx8Sm5SmqVfR8E87DPSR31nEAjZfbt91OMZWcNgdZw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-space": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.0.tgz", + "integrity": "sha512-TKr+LIDX2pkBJXFLzpyPyljzYK3MtmllMUMODTQJIUfDGncESaqB90db9IAUcz4AZAJFdd8U9zOp9ty1458rxg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-title": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-factory-title/-/micromark-factory-title-2.0.0.tgz", + "integrity": "sha512-jY8CSxmpWLOxS+t8W+FG3Xigc0RDQA9bKMY/EwILvsesiRniiVMejYTE4wumNc2f4UbAa4WsHqe3J1QS1sli+A==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-whitespace": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-factory-whitespace/-/micromark-factory-whitespace-2.0.0.tgz", + "integrity": "sha512-28kbwaBjc5yAI1XadbdPYHX/eDnqaUFVikLwrO7FDnKG7lpgxnvk/XGRhX/PN0mOZ+dBSZ+LgunHS+6tYQAzhA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-character": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.0.tgz", + "integrity": "sha512-KvOVV+X1yLBfs9dCBSopq/+G1PcgT3lAK07mC4BzXi5E7ahzMAF8oIupDDJ6mievI6F+lAATkbQQlQixJfT3aQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-chunked": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-chunked/-/micromark-util-chunked-2.0.0.tgz", + "integrity": "sha512-anK8SWmNphkXdaKgz5hJvGa7l00qmcaUQoMYsBwDlSKFKjc6gjGXPDw3FNL3Nbwq5L8gE+RCbGqTw49FK5Qyvg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-classify-character": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-classify-character/-/micromark-util-classify-character-2.0.0.tgz", + "integrity": "sha512-S0ze2R9GH+fu41FA7pbSqNWObo/kzwf8rN/+IGlW/4tC6oACOs8B++bh+i9bVyNnwCcuksbFwsBme5OCKXCwIw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-combine-extensions": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-combine-extensions/-/micromark-util-combine-extensions-2.0.0.tgz", + "integrity": "sha512-vZZio48k7ON0fVS3CUgFatWHoKbbLTK/rT7pzpJ4Bjp5JjkZeasRfrS9wsBdDJK2cJLHMckXZdzPSSr1B8a4oQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-chunked": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-decode-numeric-character-reference": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-2.0.1.tgz", + "integrity": "sha512-bmkNc7z8Wn6kgjZmVHOX3SowGmVdhYS7yBpMnuMnPzDq/6xwVA604DuOXMZTO1lvq01g+Adfa0pE2UKGlxL1XQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-decode-string": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-decode-string/-/micromark-util-decode-string-2.0.0.tgz", + "integrity": "sha512-r4Sc6leeUTn3P6gk20aFMj2ntPwn6qpDZqWvYmAG6NgvFTIlj4WtrAudLi65qYoaGdXYViXYw2pkmn7QnIFasA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "decode-named-character-reference": "^1.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-encode": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-2.0.0.tgz", + "integrity": "sha512-pS+ROfCXAGLWCOc8egcBvT0kf27GoWMqtdarNfDcjb6YLuV5cM3ioG45Ys2qOVqeqSbjaKg72vU+Wby3eddPsA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-util-html-tag-name": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-html-tag-name/-/micromark-util-html-tag-name-2.0.0.tgz", + "integrity": "sha512-xNn4Pqkj2puRhKdKTm8t1YHC/BAjx6CEwRFXntTaRf/x16aqka6ouVoutm+QdkISTlT7e2zU7U4ZdlDLJd2Mcw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-util-normalize-identifier": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-2.0.0.tgz", + "integrity": "sha512-2xhYT0sfo85FMrUPtHcPo2rrp1lwbDEEzpx7jiH2xXJLqBuy4H0GgXk5ToU8IEwoROtXuL8ND0ttVa4rNqYK3w==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-resolve-all": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-resolve-all/-/micromark-util-resolve-all-2.0.0.tgz", + "integrity": "sha512-6KU6qO7DZ7GJkaCgwBNtplXCvGkJToU86ybBAUdavvgsCiG8lSSvYxr9MhwmQ+udpzywHsl4RpGJsYWG1pDOcA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-sanitize-uri": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.0.tgz", + "integrity": "sha512-WhYv5UEcZrbAtlsnPuChHUAsu/iBPOVaEVsntLBIdpibO0ddy8OzavZz3iL2xVvBZOpolujSliP65Kq0/7KIYw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-encode": "^2.0.0", + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-subtokenize": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-subtokenize/-/micromark-util-subtokenize-2.0.1.tgz", + "integrity": "sha512-jZNtiFl/1aY73yS3UGQkutD0UbhTt68qnRpw2Pifmz5wV9h8gOVsN70v+Lq/f1rKaU/W8pxRe8y8Q9FX1AOe1Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-symbol": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz", + "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-util-types": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.0.tgz", + "integrity": "sha512-oNh6S2WMHWRZrmutsRmDDfkzKtxF+bc2VxLC9dvtrDIRFln627VsFP6fLMgTryGDljgLPjkrzQSDcPrjPyDJ5w==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "dependencies": { + "braces": "^3.0.2", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mime": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz", + "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==", + "peer": true, + "bin": { + "mime": "cli.js" + }, "engines": { "node": ">=4.0.0" } @@ -14424,6 +15272,32 @@ "node": ">=6" } }, + "node_modules/parse-entities": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-4.0.1.tgz", + "integrity": "sha512-SWzvYcSJh4d/SGLIOQfZ/CoNv6BTlI6YEQ7Nj82oDVnRpwe/Z/F1EMx42x3JAOwGBlCjeCH0BRJQbQ/opHL17w==", + "license": "MIT", + "dependencies": { + "@types/unist": "^2.0.0", + "character-entities": "^2.0.0", + "character-entities-legacy": "^3.0.0", + "character-reference-invalid": "^2.0.0", + "decode-named-character-reference": "^1.0.0", + "is-alphanumerical": "^2.0.0", + "is-decimal": "^2.0.0", + "is-hexadecimal": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/parse-entities/node_modules/@types/unist": { + "version": "2.0.11", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.11.tgz", + "integrity": "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==", + "license": "MIT" + }, "node_modules/parse-json": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", @@ -14838,6 +15712,16 @@ "react-is": "^16.13.1" } }, + "node_modules/property-information": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/property-information/-/property-information-6.5.0.tgz", + "integrity": "sha512-PgTgs/BlvHxOu8QuEN7wi5A0OmXaBcHpmCSTehcs6Uuu9IkDIEo13Hy7n898RHfrQ49vKCoGeWZSaAK01nwVig==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/proxy-compare": { "version": "2.5.1", "resolved": "https://registry.npmjs.org/proxy-compare/-/proxy-compare-2.5.1.tgz", @@ -15134,6 +16018,32 @@ "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" }, + "node_modules/react-markdown": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/react-markdown/-/react-markdown-9.0.1.tgz", + "integrity": "sha512-186Gw/vF1uRkydbsOIkcGXw7aHq0sZOCRFFjGrr7b9+nVZg4UfA4enXCaxm4fUzecU38sWfrNDitGhshuU7rdg==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "devlop": "^1.0.0", + "hast-util-to-jsx-runtime": "^2.0.0", + "html-url-attributes": "^3.0.0", + "mdast-util-to-hast": "^13.0.0", + "remark-parse": "^11.0.0", + "remark-rehype": "^11.0.0", + "unified": "^11.0.0", + "unist-util-visit": "^5.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + }, + "peerDependencies": { + "@types/react": ">=18", + "react": ">=18" + } + }, "node_modules/react-native": { "version": "0.73.7", "resolved": "https://registry.npmjs.org/react-native/-/react-native-0.73.7.tgz", @@ -15628,6 +16538,39 @@ "jsesc": "bin/jsesc" } }, + "node_modules/remark-parse": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-11.0.0.tgz", + "integrity": "sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "mdast-util-from-markdown": "^2.0.0", + "micromark-util-types": "^2.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-rehype": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/remark-rehype/-/remark-rehype-11.1.0.tgz", + "integrity": "sha512-z3tJrAs2kIs1AqIIy6pzHmAHlF1hWQ+OdY4/hv+Wxe35EhyLKcajL33iUEn3ScxtFox9nUvRufR/Zre8Q08H/g==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "mdast-util-to-hast": "^13.0.0", + "unified": "^11.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", @@ -16470,6 +17413,16 @@ "node": ">=0.10.0" } }, + "node_modules/space-separated-tokens": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz", + "integrity": "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/split-on-first": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/split-on-first/-/split-on-first-1.1.0.tgz", @@ -16723,6 +17676,20 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/stringify-entities": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/stringify-entities/-/stringify-entities-4.0.4.tgz", + "integrity": "sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==", + "license": "MIT", + "dependencies": { + "character-entities-html4": "^2.0.0", + "character-entities-legacy": "^3.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/strip-ansi": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", @@ -16820,6 +17787,15 @@ "url": "https://github.com/sponsors/Borewit" } }, + "node_modules/style-to-object": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/style-to-object/-/style-to-object-1.0.8.tgz", + "integrity": "sha512-xT47I/Eo0rwJmaXC4oilDGDWLohVhR6o/xAQcPQN8q6QBuZVL8qMYL85kLmST5cPjAorwvqIA4qXTRQoYHaL6g==", + "license": "MIT", + "dependencies": { + "inline-style-parser": "0.2.4" + } + }, "node_modules/styled-jsx": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.1.tgz", @@ -17133,6 +18109,16 @@ "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" }, + "node_modules/trim-lines": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/trim-lines/-/trim-lines-3.0.1.tgz", + "integrity": "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/trim-repeated": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/trim-repeated/-/trim-repeated-2.0.0.tgz", @@ -17157,6 +18143,16 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/trough": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/trough/-/trough-2.2.0.tgz", + "integrity": "sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/ts-api-utils": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.3.0.tgz", @@ -17297,6 +18293,7 @@ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.2.tgz", "integrity": "sha512-+2/g0Fds1ERlP6JsakQQDXjZdZMM+rqpamFZJEKh4kwTIn3iDkgKtby0CeNd5ATNZ4Ry1ax15TMx0W2V+miizQ==", "devOptional": true, + "license": "Apache-2.0", "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -17433,6 +18430,105 @@ "node": ">=4" } }, + "node_modules/unified": { + "version": "11.0.5", + "resolved": "https://registry.npmjs.org/unified/-/unified-11.0.5.tgz", + "integrity": "sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "bail": "^2.0.0", + "devlop": "^1.0.0", + "extend": "^3.0.0", + "is-plain-obj": "^4.0.0", + "trough": "^2.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unified/node_modules/is-plain-obj": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz", + "integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/unist-util-is": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-6.0.0.tgz", + "integrity": "sha512-2qCTHimwdxLfz+YzdGfkqNlH0tLi9xjTnHddPmJwtIG9MGsdbutfTc4P+haPD7l7Cjxf/WZj+we5qfVPvvxfYw==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-position": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/unist-util-position/-/unist-util-position-5.0.0.tgz", + "integrity": "sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-stringify-position": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz", + "integrity": "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-visit": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-5.0.0.tgz", + "integrity": "sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0", + "unist-util-visit-parents": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-visit-parents": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-6.0.1.tgz", + "integrity": "sha512-L/PqWzfTP9lzzEa6CKs0k2nARxTdZduw3zyh8d2NVBnsyvHjSX4TWse388YrrQKbvI8w20fGjGlhgT96WwKykw==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/universalify": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", @@ -17707,6 +18803,34 @@ "node": ">= 0.8" } }, + "node_modules/vfile": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.3.tgz", + "integrity": "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/vfile-message": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-4.0.2.tgz", + "integrity": "sha512-jRDZ1IMLttGj41KcZvlrYAaI3CfqpLpfpf+Mfig13viT6NKvRzWZ+lXz0Y5D60w6uJIBAOGq9mSHf0gktF0duw==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-stringify-position": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/viem": { "version": "2.9.25", "resolved": "https://registry.npmjs.org/viem/-/viem-2.9.25.tgz", @@ -18408,6 +19532,16 @@ "optional": true } } + }, + "node_modules/zwitch": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz", + "integrity": "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } } } } diff --git a/submodules/moragents_dockers/frontend/package.json b/submodules/moragents_dockers/frontend/package.json index 5bb79e6..eca1a8c 100644 --- a/submodules/moragents_dockers/frontend/package.json +++ b/submodules/moragents_dockers/frontend/package.json @@ -1,7 +1,7 @@ { "name": "Morpheus AI", "private": true, - "version": "0.1.0", + "version": "0.2.0", "scripts": { "dev": "next dev", "build": "next build", @@ -21,6 +21,7 @@ "react": "^18.2.0", "react-dom": "^18.2.0", "react-icons": "^5.3.0", + "react-markdown": "^9.0.0", "viem": "^2.8.12", "wagmi": "^2.5.11" }, diff --git a/submodules/moragents_dockers/frontend/pages/index.tsx b/submodules/moragents_dockers/frontend/pages/index.tsx index 8b0d192..d156de9 100644 --- a/submodules/moragents_dockers/frontend/pages/index.tsx +++ b/submodules/moragents_dockers/frontend/pages/index.tsx @@ -1,33 +1,41 @@ -import { ConnectButton } from '@rainbow-me/rainbowkit'; -import type { NextPage } from 'next'; -import { Flex, Grid, GridItem } from '@chakra-ui/react'; -import { LeftSidebar } from '../components/LeftSidebar'; -import { Chat } from '../components/Chat'; -import { writeMessage, getHttpClient, ChatMessage, getMessagesHistory, sendSwapStatus, SWAP_STATUS, uploadFile } from '../services/backendClient'; -import { useEffect, useMemo, useState } from 'react'; -import { useAccount, useChainId, useWalletClient } from 'wagmi'; -import { HeaderBar } from '../components/HeaderBar'; -import { availableAgents } from '../config'; -import { WalletRequiredModal } from '../components/WalletRequiredModal'; -import { ErrorBackendModal } from '../components/ErrorBackendModal'; +import { ConnectButton } from "@rainbow-me/rainbowkit"; +import type { NextPage } from "next"; +import { Flex, Grid, GridItem } from "@chakra-ui/react"; +import { LeftSidebar } from "../components/LeftSidebar"; +import { Chat } from "../components/Chat"; +import { + writeMessage, + getHttpClient, + ChatMessage, + getMessagesHistory, + sendSwapStatus, + SWAP_STATUS, + uploadFile, +} from "../services/backendClient"; +import { useEffect, useMemo, useState } from "react"; +import { useAccount, useChainId, useWalletClient } from "wagmi"; +import { HeaderBar } from "../components/HeaderBar"; +import { availableAgents } from "../config"; +import { WalletRequiredModal } from "../components/WalletRequiredModal"; +import { ErrorBackendModal } from "../components/ErrorBackendModal"; const Home: NextPage = () => { const [chatHistory, setChatHistory] = useState([]); const chainId = useChainId(); const { address } = useAccount(); - const [selectedAgent, setSelectedAgent] = useState('swap-agent'); // default is swap agent for now. + const [selectedAgent, setSelectedAgent] = useState("swap-agent"); // default is swap agent for now. const [showBackendError, setShowBackendError] = useState(false); useEffect(() => { - getMessagesHistory( - getHttpClient(selectedAgent), - ).then((messages: ChatMessage[]) => { - setChatHistory([...messages]) - }).catch((e) => { - console.error(`Failed to get initial messages history. Error: ${e}`); - setShowBackendError(true); - }); - }, [selectedAgent]); + getMessagesHistory(getHttpClient()) + .then((messages: ChatMessage[]) => { + setChatHistory([...messages]); + }) + .catch((e) => { + console.error(`Failed to get initial messages history. Error: ${e}`); + setShowBackendError(true); + }); + }, []); // Empty dependency array to run only on component initialization const isWalletRequired = useMemo(() => { const agent = availableAgents[selectedAgent] || null; @@ -40,34 +48,34 @@ const Home: NextPage = () => { }, [selectedAgent]); return ( -
+
{ setSelectedAgent(agent); }} - currentAgent={selectedAgent || ''} + currentAgent={selectedAgent || ""} /> - - + - - + { // 0 is swap, 1 is approve @@ -76,29 +84,36 @@ const Home: NextPage = () => { } try { - await sendSwapStatus(getHttpClient(selectedAgent), chainId, address, SWAP_STATUS.CANCELLED, '', fromAction); + await sendSwapStatus( + getHttpClient(), + chainId, + address, + SWAP_STATUS.CANCELLED, + "", + fromAction + ); } catch (e) { console.error(`Failed to cancel swap . Error: ${e}`); setShowBackendError(true); - } finally { try { const _updatedMessages = await getMessagesHistory( - getHttpClient(selectedAgent), - ) + getHttpClient() + ); setChatHistory([..._updatedMessages]); } catch (e) { - console.error(`Failed to get messages history after send swap status. Error: ${e}`); + console.error( + `Failed to get messages history after send swap status. Error: ${e}` + ); setShowBackendError(true); } } - - - }} - onSubmitMessage={async (message: string, file: File | null): Promise => { - + onSubmitMessage={async ( + message: string, + file: File | null + ): Promise => { const agent = availableAgents[selectedAgent] || null; if (null !== agent && agent.requirements.connectedWallet) { @@ -107,26 +122,29 @@ const Home: NextPage = () => { } } - setChatHistory([...chatHistory, { - role: 'user', - content: message - } as ChatMessage]); + setChatHistory([ + ...chatHistory, + { + role: "user", + content: message, + } as ChatMessage, + ]); let _newHistory = []; try { if (!file) { - _newHistory = await writeMessage(chatHistory, message, getHttpClient(selectedAgent), chainId, address || ''); - + _newHistory = await writeMessage( + chatHistory, + message, + getHttpClient(), + chainId, + address || "" + ); } else { - await uploadFile( - getHttpClient(selectedAgent), - file, - ) - - _newHistory = await getMessagesHistory( - getHttpClient(selectedAgent), - ) + await uploadFile(getHttpClient(), file); + + _newHistory = await getMessagesHistory(getHttpClient()); } - setChatHistory([..._newHistory]) + setChatHistory([..._newHistory]); } catch (e) { console.error(`Failed to send message. Error: ${e}`); setShowBackendError(true); @@ -142,7 +160,6 @@ const Home: NextPage = () => { {/* */} - {/* { return; } - const _newHistory = await writeMessage(chatHistory, message, getHttpClient(selectedAgent), chainId, address); + const _newHistory = await writeMessage(chatHistory, message, getHttpClient(), chainId, address); setChatHistory([..._newHistory]) }} /> */} -
diff --git a/submodules/moragents_dockers/frontend/services/backendClient.ts b/submodules/moragents_dockers/frontend/services/backendClient.ts index 75369c5..84a03e9 100644 --- a/submodules/moragents_dockers/frontend/services/backendClient.ts +++ b/submodules/moragents_dockers/frontend/services/backendClient.ts @@ -1,8 +1,7 @@ import axios, { Axios } from "axios"; -import { availableAgents } from "../config"; export type ChatMessageBase = { - role: "user" | "assistant" | "swap"; + role: "user" | "assistant" | "swap" | "claim"; }; export type UserOrAssistantMessage = ChatMessageBase & { @@ -18,6 +17,12 @@ export const SWAP_STATUS = { INIT: "initiated", }; +export const CLAIM_STATUS = { + SUCCESS: "success", + FAIL: "failed", + INIT: "initiated", +}; + export type SwapTxPayloadType = { dstAmount: string; tx: { @@ -58,25 +63,45 @@ export type SystemMessage = ChatMessageBase & { content: string; }; -export type ChatMessage = UserOrAssistantMessage | SwapMessage | SystemMessage; +export type ClaimTransactionPayload = { + to: string; + data: string; + value: string; + gas: string; + chainId: string; +}; -export type ChatsListItem = { - index: number; // index at chats array - title: string; // title of the chat (first message content) +export type ClaimMessagePayload = { + content: { + transactions: { + pool: number; + transaction: ClaimTransactionPayload; + }[]; + claim_tx_cb: string; + }; + role: "claim"; }; -export const getHttpClient = (selectedAgent: string) => { - const agentData = availableAgents[selectedAgent]; +export type ClaimMessage = ChatMessageBase & { + role: "claim"; + content: ClaimMessagePayload; +}; - if (typeof agentData === "undefined") { - // if no agent selected lets select by default swap agent for now. - } +// Update the ChatMessage type to include ClaimMessage +export type ChatMessage = + | UserOrAssistantMessage + | SwapMessage + | SystemMessage + | ClaimMessage; - const swapAgentUrl = - agentData?.endpoint || availableAgents["swap-agent"].endpoint; +export type ChatsListItem = { + index: number; // index at chats array + title: string; // title of the chat (first message content) +}; +export const getHttpClient = () => { return axios.create({ - baseURL: swapAgentUrl || "http://localhost:8080", + baseURL: "http://localhost:8080", }); }; @@ -123,7 +148,7 @@ export const getApprovalTxPayload = async ( export const uploadFile = async (backendClient: Axios, file: File) => { const formData = new FormData(); formData.append("file", file); - + console.log("Uploading file:", file); return await backendClient.post("/upload", formData, { headers: { "Content-Type": "multipart/form-data", @@ -188,6 +213,18 @@ export const getMessagesHistory = async ( } as ChatMessage; }); }; + +export const clearMessagesHistory = async ( + backendClient: Axios +): Promise => { + try { + await backendClient.get("/clear_messages"); + } catch (error) { + console.error("Failed to clear message history:", error); + throw error; + } +}; + export const writeMessage = async ( history: ChatMessage[], message: string, @@ -203,7 +240,7 @@ export const writeMessage = async ( history.push(newMessage); let resp; try { - resp = await backendClient.post("/", { + resp = await backendClient.post("/chat", { prompt: { role: "user", content: message, @@ -278,3 +315,32 @@ export const regenerateTweet = async ( throw error; } }; + +export const getClaimTxPayload = async ( + backendClient: Axios, + transactions: ClaimTransactionPayload[] +): Promise => { + const response = await backendClient.post("/claim", { transactions }); + return response.data.transactions; +}; + +export const sendClaimStatus = async ( + backendClient: Axios, + chainId: number, + walletAddress: string, + claimStatus: string, + txHash?: string +): Promise => { + const responseBody = await backendClient.post("/tx_status", { + chain_id: chainId, + wallet_address: walletAddress, + status: claimStatus, + tx_hash: txHash || "", + tx_type: "claim", + }); + + return { + role: responseBody.data.role, + content: responseBody.data.content, + } as ChatMessage; +}; diff --git a/wizard_windows.iss b/wizard_windows.iss index 34d0565..0fdcf7b 100644 --- a/wizard_windows.iss +++ b/wizard_windows.iss @@ -1,6 +1,6 @@ [Setup] AppName=MORagents -AppVersion=0.1.0 +AppVersion=0.2.0 DefaultDirName={commonpf}\MORagents OutputDir=.\MORagentsWindowsInstaller OutputBaseFilename=MORagentsSetup @@ -27,7 +27,7 @@ Filename: "{tmp}\DockerDesktopInstaller.exe"; Parameters: "install"; StatusMsg: Filename: "{tmp}\OllamaSetup.exe"; StatusMsg: "Installing Ollama..."; Flags: waituntilterminated Filename: "{app}\LICENSE.md"; Description: "View License Agreement"; Flags: postinstall shellexec skipifsilent Filename: "{app}\MORagents.exe"; Description: "Launch MORagents"; Flags: postinstall nowait skipifsilent unchecked -Filename: "cmd.exe"; Parameters: "/c ollama pull llama3.1"; StatusMsg: "Pulling llama3.1 model..."; Flags: runhidden waituntilterminated +Filename: "cmd.exe"; Parameters: "/c ollama pull llama3.2:3b"; StatusMsg: "Pulling llama3.2:3b model..."; Flags: runhidden waituntilterminated Filename: "cmd.exe"; Parameters: "/c ollama pull nomic-embed-text"; StatusMsg: "Pulling nomic-embed-text model..."; Flags: runhidden waituntilterminated [Code]