eliza icon indicating copy to clipboard operation
eliza copied to clipboard

fix: remove problematic redundant uuid conversion and add api input param validations to api server

Open jonathanykh opened this issue 11 months ago • 1 comments

Relates to

N/A - this is an issue and will be described below in the Background section

Risks

Low - This is a bug fix and a defensive programming change that adds more robust input validation.

Background

What does this PR do?

  1. Removes incorrect usage of stringToUuid for API parameters that should already be in UUID format
  2. Adds proper UUID validation for API input parameters (agentId, roomId)
  3. Implements consistent error handling with appropriate HTTP status codes and messages
  4. Centralizes UUID validation logic in a reusable function

What kind of change is this?

Bug fixes (non-breaking change which fixes an issue) and Improvements (misc. changes to existing features)

Why are we doing this? Any context or related work?

We're making these changes to address two issues:

  1. Bug Fix: There was a bug in the /agents/:agentId/:roomId/memories endpoint at client-direct where stringToUuid() was being incorrectly applied to the roomId parameter. This caused:
  • The original valid UUID to be unnecessarily converted
  • Generation of a new, different UUID
  • Failed memory retrievals due to UUID mismatch

image To further show case the problem, orginal room ID: ad22e47a-3fd0-05dc-8791-8fba9e475378 was altered by stringToUuid() to 8e9b5550-ca28-0f3c-a1f4-f13c2cdf3dca, leading to memories failed to be fetched by runtime.messageManager.getMemories.

Response:

{
  "agentId": "b850bc30-45f8-0041-a00a-83df46d8555d",
  "roomId": "8e9b5550-ca28-0f3c-a1f4-f13c2cdf3dca",
  "memories": []
}
  1. Defensive Programming: The API server was not validating UUID parameters, which led to:
  • No validation of input format
  • Silent failures when invalid UUIDs were provided

This PR implements proper input validation and error handling, making the API more robust and developer-friendly.

Documentation changes needed?

My changes do not require a change to the project documentation.

Testing

Where should a reviewer start?

  1. Review the UUID type validation function introduced at packages/core/src/uuid.ts
  2. Review the new validateUUIDParams function in packages/client-direct/src/api.ts
  3. Check the implementation across all API endpoints that use agentId or roomId

Detailed testing steps

  1. Test /agents/:agentId/:roomId/memories endpoint:
  • With valid UUIDs - should work normally
  • With invalid agentId - should return 400 with appropriate error message
  • With invalid roomId - should return 400 with appropriate error message
  1. Test other endpoints with agentId parameter:
  • /agents/:agentId
  • /agents/:agentId/set
  • /agents/:agentId/channels
  1. Verify error messages from 1. are clear and helpful.

jonathanykh avatar Jan 09 '25 06:01 jonathanykh

Regarding testing, there is this simple Python script that I can provide to facilitate calls to the API server (normally at localhost:3000) and test the results of different inputs (e.g. invalid UUID):

import requests
import json
from typing import Dict, Any

BASE_URL = "http://localhost:3000"


def print_menu():
    print("\n=== Eliza API Client ===")
    print("1. Welcome message")
    print("2. Hello world")
    print("3. List all agents")
    print("4. Get agent details")
    print("5. Set agent character")
    print("6. Get agent's Discord channels")
    print("7. Get agent's memories")
    print("8. Exit")
    return input("Choose an option (1-8): ")


def pretty_print_response(response: Dict[str, Any]):
    print("\nResponse:")
    print(json.dumps(response, indent=2))


def get_welcome():
    response = requests.get(f"{BASE_URL}/")
    return response.text


def get_hello():
    response = requests.get(f"{BASE_URL}/hello")
    return response.json()


def list_agents():
    response = requests.get(f"{BASE_URL}/agents")
    return response.json()


def get_agent_details():
    agent_id = input("Enter agent ID: ")
    response = requests.get(f"{BASE_URL}/agents/{agent_id}")
    return response.json()


def set_agent_character():
    agent_id = input("Enter agent ID: ")
    print("Enter character configuration (JSON format):")
    character_json = input()
    try:
        character_data = json.loads(character_json)
        response = requests.post(
            f"{BASE_URL}/agents/{agent_id}/set", json=character_data
        )
        return response.json()
    except json.JSONDecodeError:
        return {"error": "Invalid JSON format"}


def get_agent_channels():
    agent_id = input("Enter agent ID: ")
    response = requests.get(f"{BASE_URL}/agents/{agent_id}/channels")
    return response.json()


def get_agent_memories():
    agent_id = input("Enter agent ID: ")
    room_id = input("Enter room ID: ")
    response = requests.get(f"{BASE_URL}/agents/{agent_id}/{room_id}/memories")
    return response.json()


def main():
    while True:
        choice = print_menu()

        try:
            if choice == "1":
                result = get_welcome()
                print("\nResponse:", result)

            elif choice == "2":
                result = get_hello()
                pretty_print_response(result)

            elif choice == "3":
                result = list_agents()
                pretty_print_response(result)

            elif choice == "4":
                result = get_agent_details()
                pretty_print_response(result)

            elif choice == "5":
                result = set_agent_character()
                pretty_print_response(result)

            elif choice == "6":
                result = get_agent_channels()
                pretty_print_response(result)

            elif choice == "7":
                result = get_agent_memories()
                pretty_print_response(result)

            elif choice == "8":
                print("Goodbye!")
                break

            else:
                print("Invalid option, please try again")

        except requests.exceptions.RequestException as e:
            print(f"\nError making request: {e}")
        except Exception as e:
            print(f"\nUnexpected error: {e}")


if __name__ == "__main__":
    main()

jonathanykh avatar Jan 09 '25 06:01 jonathanykh

Hello @jonathanykh could you update pnpm-lock.yaml

tcm390 avatar Jan 11 '25 03:01 tcm390

Thanks @tcm390, I have updated the pnpm-lock.yaml, please take a look! :)

jonathanykh avatar Jan 11 '25 10:01 jonathanykh