Repository: agora-protocol/paper-demo Branch: main Commit: 9dfe6837ee1e Files: 38 Total size: 374.5 KB Directory structure: gitextract_gcfiyf96/ ├── .gitignore ├── README.md ├── actions.json ├── agents/ │ ├── common/ │ │ └── core.py │ ├── protocol_db/ │ │ └── main.py │ ├── server/ │ │ ├── config.py │ │ ├── main.py │ │ └── memory.py │ └── user/ │ ├── config.py │ ├── main.py │ ├── memory.py │ └── protocol_management.py ├── compute_costs.py ├── config.json ├── generate_screenplay.py ├── generate_users.py ├── mocks/ │ ├── mock_tasks.py │ └── mock_tools.py ├── models/ │ └── openai_model.py ├── orchestrator.py ├── requirements.txt ├── specialized_toolformers/ │ ├── negotiator.py │ ├── programmer.py │ ├── protocol_checker.py │ ├── querier.py │ └── responder.py ├── toolformers/ │ ├── __init__.py │ ├── base.py │ ├── camel.py │ ├── gemini.py │ ├── llama/ │ │ ├── __init__.py │ │ ├── api_gateway.py │ │ ├── function_calling.py │ │ ├── llama.py │ │ ├── sambanova_langchain.py │ │ └── utils.py │ └── unified.py └── utils.py ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ /**/__pycache__/** venv/** .env past_experiments/** new_tests/** script_utils/** storage/** logs/** databases/** ================================================ FILE: README.md ================================================ # Agora - A Scalable Communication Protocol for Networks of LLMs [Paper](https://arxiv.org/abs/2410.11905) | [Website](https://agoraprotocol.org) | [Twitter](https://x.com/Agora_Protocol) | [Discord](https://discord.gg/MXmfhwQ4FB) | [Mailing List](https://forms.gle/KeCMveoRGx2S3i5CA) ## **Important**: This demo was originally made for the paper. Check out our [updated HuggingFace demo](https://huggingface.co/spaces/agora-protocol/agora-demo) and our [Python library](https://github.com/agora-protocol/python). Agora is a simple cross-platform protocol that allows heterogeneous LLMs to communicate efficienly with each other. This is achieved through the power of **negotiation**. In particular, Agora agents operate as follows: 1. For rare communications, they use LLMs to talk with each other in natural language 2. For frequent communications, they use LLMs to negotiate a protocol for communication, usually involving structured data (e.g. JSON) 3. Once a protocol is finalized, they use LLMs to implement _routines_, simple scripts (e.g. in Python) that send or receive data 4. Future communications are handled using the routines, which means that LLMs aren't required anymore Since natural language is supported, very different agents that have never interacted before can communicate with each other, but once a common ground is established they just use routines, which are way more efficient. This enables agents to achieve at the same time **efficiency**, **versatility** and **portability**. ## The Demo This demo showcases a network of 100 agents interacting with each other. The agents have different LLMs (OpenAI GPT-4o, Llama 3 405b, Gemini 1.5 Pro) and different DB technologies (MongoDB, SQL), but they still complete complex, multi-agent tasks with way lower costs. In a picture: You might also be interested in our [HuggingFace demo](https://huggingface.co/spaces/agora-protocol/agora-demo). ## Running the Demo 1. `mv .env.template .env` 2. Add the corresponding fields to the `.env` file 3. `pip install -r requirements.txt` 4. `python orchestrator.py` ## Contributing We're building the next iteration of Agora, with more features for real-world use cases. If you're interested in contributing or simply want to stay updated about Agora, check out our [Discord](https://discord.gg/MXmfhwQ4FB) or subscribe to our [Mailing List](https://forms.gle/KeCMveoRGx2S3i5CA) ================================================ FILE: actions.json ================================================ [ [ "phenex", [ "restaurant2", "orderEverything" ], { "deliveryAddress": "68 Wilson Street" } ], [ "vassago", [ "restaurant2", "orderEverything" ], { "deliveryAddress": "21 Taylor Street" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2023-11-04", "type": "backcountry" } ], [ "bael", [ "skiResort2", "suggestRestaurant" ], { "date": "2024-09-14" } ], [ "bune", [ "restaurant2", "orderEverything" ], { "deliveryAddress": "9 Davis Street" } ], [ "valefor", [ "skiResort2", "suggestRestaurant" ], { "date": "2024-09-05" } ], [ "agares", [ "taxi2", "callTaxi" ], { "address": "2 Brown Street", "time": "20:57" } ], [ "marbas", [ "cinema1", "buyTickets" ], { "date": [ "2024-03-16" ], "hour": 18, "numTickets": 2, "movie": "Forrest Gump" } ], [ "vassago", [ "restaurant2", "orderEverything" ], { "deliveryAddress": "62 Davis Street" } ], [ "bael", [ "skiResort2", "suggestRestaurant" ], { "date": "2024-06-19" } ], [ "protocolDb1", [ null, "synchronization" ], null ], [ "vassago", [ "restaurant2", "orderEverything" ], { "deliveryAddress": "62 Johnson Street" } ], [ "paimon", [ "restaurant1", "openingTimes" ], {} ], [ "sitri", [ "hotel1", "bookRoom" ], { "startDate": "2024-08-11", "endDate": "2024-08-28" } ], [ "valefor", [ "skiResort2", "suggestRestaurant" ], { "date": "2024-01-06" } ], [ "naberius", [ "cinema1", "availableMovies" ], { "date": [ "2024-03-18" ] } ], [ "bael", [ "taxi2", "callTaxi" ], { "address": "71 Johnson Street", "time": "2:15" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-01-21", "type": "carving" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-02-03", "type": "racing" } ], [ "agares", [ "taxi2", "callTaxi" ], { "address": "10 Smith Street", "time": "0:49" } ], [ "agares", [ "taxi2", "callTaxi" ], { "address": "83 Jones Street", "time": "20:1" } ], [ "protocolDb2", [ null, "synchronization" ], null ], [ "amon", [ "skiResort1", "rentSki" ], { "date": "2024-09-17", "type": "carving" } ], [ "bael", [ "skiResort2", "suggestRestaurant" ], { "date": "2024-08-24" } ], [ "bael", [ "skiResort2", "suggestRestaurant" ], { "date": "2024-04-20" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-01-17", "type": "carving" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-01-05", "type": "carving" } ], [ "ipos", [ "taxi2", "callTaxi" ], { "address": "60 Williams Street", "time": "10:11" } ], [ "bael", [ "taxi2", "callTaxi" ], { "address": "75 Brown Street", "time": "5:59" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-06-23", "type": "racing" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2023-11-14", "type": "carving" } ], [ "valefor", [ "restaurant3", "bookTable" ], { "fullName": "Toby Williams", "date": "2024-07-21", "numPeople": 1, "hour": 19 } ], [ "protocolDb3", [ null, "synchronization" ], null ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-02-22", "type": "racing" } ], [ "agares", [ "cinema2", "availableMovies" ], { "date": [ "2024-03-16" ] } ], [ "agares", [ "taxi2", "callTaxi" ], { "address": "88 Williams Street", "time": "13:56" } ], [ "agares", [ "restaurant3", "bookTable" ], { "fullName": "Laurel Miller", "date": "2024-03-07", "numPeople": 8, "hour": 20 } ], [ "caim", [ "taxi2", "callTaxi" ], { "address": "9 Johnson Street", "time": "18:38" } ], [ "bael", [ "skiResort2", "suggestRestaurant" ], { "date": "2024-07-25" } ], [ "agares", [ "taxi2", "callTaxi" ], { "address": "31 Johnson Street", "time": "18:46" } ], [ "marbas", [ "restaurant1", "bookTable" ], { "fullName": "Brooke Brown", "date": "2024-07-09", "numPeople": 10, "hour": 21 } ], [ "bael", [ "skiResort2", "suggestRestaurant" ], { "date": "2023-12-14" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2023-11-27", "type": "backcountry" } ], [ "protocolDb1", [ null, "synchronization" ], null ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2023-11-18", "type": "racing" } ], [ "agares", [ "restaurant3", "bookTable" ], { "fullName": "Maxwell Davis", "date": "2024-08-31", "numPeople": 7, "hour": 18 } ], [ "samigina", [ "restaurant3", "bookTable" ], { "fullName": "Maxwell Smith", "date": "2024-05-21", "numPeople": 5, "hour": 18 } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-04-25", "type": "carving" } ], [ "leraje", [ "hotel3", "bookRoom" ], { "startDate": "2024-08-23", "endDate": "2024-08-31" } ], [ "beleth", [ "restaurant1", "openingTimes" ], {} ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2023-10-14", "type": "carving" } ], [ "agares", [ "restaurant3", "bookTable" ], { "fullName": "Madge Miller", "date": "2024-09-11", "numPeople": 1, "hour": 17 } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2023-10-30", "type": "carving" } ], [ "bifrons", [ "taxi1", "callTaxi" ], { "address": "79 Miller Street", "time": "11:28" } ], [ "protocolDb2", [ null, "synchronization" ], null ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-06-20", "type": "racing" } ], [ "bael", [ "skiResort2", "suggestRestaurant" ], { "date": "2024-09-07" } ], [ "agares", [ "cinema2", "availableMovies" ], { "date": [ "2024-03-17" ] } ], [ "agares", [ "taxi2", "callTaxi" ], { "address": "100 Miller Street", "time": "23:58" } ], [ "purson", [ "trafficServer", "getTraffic" ], { "date": "2024-03-05", "location": "Zurich" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-04-22", "type": "backcountry" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-04-15", "type": "carving" } ], [ "vassago", [ "skiResort1", "bookRoom" ], { "startDate": "2024-05-04", "endDate": "2024-05-05" } ], [ "andras", [ "restaurant2", "bookTable" ], { "fullName": "Nina Johnson", "date": "2023-10-02", "numPeople": 6, "hour": 16 } ], [ "agares", [ "taxi2", "callTaxi" ], { "address": "87 Johnson Street", "time": "19:1" } ], [ "protocolDb3", [ null, "synchronization" ], null ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-05-24", "type": "racing" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-04-02", "type": "carving" } ], [ "bael", [ "skiResort2", "suggestRestaurant" ], { "date": "2024-01-25" } ], [ "sitri", [ "skiResort1", "bookRoom" ], { "startDate": "2024-06-20", "endDate": "2024-06-26" } ], [ "bael", [ "taxi2", "callTaxi" ], { "address": "20 Wilson Street", "time": "18:44" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2023-11-12", "type": "racing" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2023-10-05", "type": "backcountry" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-01-25", "type": "carving" } ], [ "vassago", [ "restaurant2", "orderEverything" ], { "deliveryAddress": "45 Miller Street" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-02-04", "type": "carving" } ], [ "protocolDb1", [ null, "synchronization" ], null ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2023-11-04", "type": "racing" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-01-27", "type": "carving" } ], [ "buer", [ "skiResort2", "suggestRestaurant" ], { "date": "2024-07-25" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-09-07", "type": "racing" } ], [ "halphas", [ "hotel1", "bookRoom" ], { "startDate": "2024-02-18", "endDate": "2024-02-26" } ], [ "amon", [ "skiResort1", "rentSki" ], { "date": "2023-10-11", "type": "carving" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-05-26", "type": "carving" } ], [ "bael", [ "skiResort2", "suggestRestaurant" ], { "date": "2024-02-07" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-04-19", "type": "carving" } ], [ "marchosias", [ "skiResort2", "bookRoom" ], { "startDate": "2024-01-16", "endDate": "2024-01-21" } ], [ "protocolDb2", [ null, "synchronization" ], null ], [ "samigina", [ "hotel3", "suggestRestaurant" ], { "date": "2024-03-21" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-05-23", "type": "backcountry" } ], [ "bael", [ "skiResort2", "suggestRestaurant" ], { "date": "2024-03-07" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-05-24", "type": "backcountry" } ], [ "agares", [ "taxi2", "callTaxi" ], { "address": "36 Wilson Street", "time": "20:41" } ], [ "bael", [ "skiResort2", "suggestRestaurant" ], { "date": "2024-07-29" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2023-09-23", "type": "racing" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2023-11-19", "type": "backcountry" } ], [ "guison", [ "cinema1", "availableMovies" ], { "date": [ "2024-03-16" ] } ], [ "bael", [ "skiResort2", "suggestRestaurant" ], { "date": "2024-07-19" } ], [ "protocolDb3", [ null, "synchronization" ], null ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-02-06", "type": "backcountry" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2023-10-30", "type": "backcountry" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-03-23", "type": "carving" } ], [ "vassago", [ "skiResort1", "bookRoom" ], { "startDate": "2024-03-20", "endDate": "2024-04-07" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-08-13", "type": "backcountry" } ], [ "vassago", [ "restaurant2", "orderEverything" ], { "deliveryAddress": "81 Davis Street" } ], [ "buer", [ "skiResort1", "rentSki" ], { "date": "2024-04-15", "type": "carving" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2023-10-01", "type": "racing" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-03-01", "type": "carving" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2023-11-08", "type": "racing" } ], [ "protocolDb1", [ null, "synchronization" ], null ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2023-12-23", "type": "backcountry" } ], [ "guison", [ "taxi2", "callTaxi" ], { "address": "31 White Street", "time": "21:15" } ], [ "agares", [ "taxi2", "callTaxi" ], { "address": "11 Wilson Street", "time": "18:40" } ], [ "samigina", [ "cinema1", "buyTickets" ], { "date": [ "2024-03-16" ], "hour": 18, "numTickets": 3, "movie": "Stand by Me" } ], [ "amon", [ "skiResort1", "rentSki" ], { "date": "2023-09-27", "type": "backcountry" } ], [ "agares", [ "restaurant3", "bookTable" ], { "fullName": "Cliff Miller", "date": "2023-10-26", "numPeople": 10, "hour": 17 } ], [ "samigina", [ "cinema1", "buyTickets" ], { "date": [ "2024-03-18" ], "hour": 18, "numTickets": 2, "movie": "Forrest Gump" } ], [ "vassago", [ "skiResort1", "bookRoom" ], { "startDate": "2024-07-03", "endDate": "2024-07-08" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-03-27", "type": "racing" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2023-11-21", "type": "backcountry" } ], [ "protocolDb2", [ null, "synchronization" ], null ], [ "marbas", [ "skiResort1", "bookRoom" ], { "startDate": "2024-08-21", "endDate": "2024-08-23" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-08-05", "type": "backcountry" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2023-09-28", "type": "racing" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-01-02", "type": "racing" } ], [ "agares", [ "taxi2", "callTaxi" ], { "address": "33 Williams Street", "time": "4:17" } ], [ "valefor", [ "restaurant3", "bookTable" ], { "fullName": "Cliff Jones", "date": "2024-02-18", "numPeople": 9, "hour": 23 } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-06-14", "type": "racing" } ], [ "ronove", [ "cinema1", "buyTickets" ], { "date": [ "2024-03-16" ], "hour": 18, "numTickets": 1, "movie": "Stand by Me" } ], [ "vassago", [ "restaurant2", "bookTable" ], { "fullName": "Toby Davis", "date": "2024-05-22", "numPeople": 8, "hour": 21 } ], [ "botis", [ "skiResort2", "rentSki" ], { "date": "2024-08-27", "type": "racing" } ], [ "protocolDb3", [ null, "synchronization" ], null ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-01-23", "type": "racing" } ], [ "agares", [ "taxi2", "callTaxi" ], { "address": "39 Jones Street", "time": "7:57" } ], [ "agares", [ "taxi2", "callTaxi" ], { "address": "16 Smith Street", "time": "21:57" } ], [ "leraje", [ "hotel3", "bookRoom" ], { "startDate": "2024-07-02", "endDate": "2024-07-06" } ], [ "bael", [ "skiResort2", "suggestRestaurant" ], { "date": "2023-09-28" } ], [ "bael", [ "skiResort2", "suggestRestaurant" ], { "date": "2024-05-16" } ], [ "bael", [ "skiResort2", "suggestRestaurant" ], { "date": "2024-08-08" } ], [ "agares", [ "taxi2", "callTaxi" ], { "address": "70 Davis Street", "time": "8:53" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2023-10-11", "type": "carving" } ], [ "agares", [ "taxi2", "callTaxi" ], { "address": "41 Jones Street", "time": "20:58" } ], [ "protocolDb1", [ null, "synchronization" ], null ], [ "agares", [ "taxi2", "callTaxi" ], { "address": "31 Williams Street", "time": "10:56" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-03-10", "type": "racing" } ], [ "amon", [ "hotel1", "bookRoom" ], { "startDate": "2024-08-08", "endDate": "2024-08-13" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-09-20", "type": "carving" } ], [ "samigina", [ "cinema1", "buyTickets" ], { "date": [ "2024-03-15" ], "hour": 20, "numTickets": 1, "movie": "The Silence of the Lambs" } ], [ "agares", [ "taxi2", "callTaxi" ], { "address": "100 White Street", "time": "7:59" } ], [ "samigina", [ "hotel3", "suggestRestaurant" ], { "date": "2023-11-25" } ], [ "marbas", [ "skiResort1", "bookRoom" ], { "startDate": "2023-11-22", "endDate": "2023-11-30" } ], [ "marbas", [ "restaurant1", "bookTable" ], { "fullName": "Kara Smith", "date": "2024-06-02", "numPeople": 10, "hour": 21 } ], [ "bael", [ "skiResort2", "suggestRestaurant" ], { "date": "2023-12-26" } ], [ "protocolDb2", [ null, "synchronization" ], null ], [ "stolas", [ "restaurant2", "orderEverything" ], { "deliveryAddress": "49 Jones Street" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2023-10-30", "type": "racing" } ], [ "amon", [ "skiResort1", "rentSki" ], { "date": "2024-08-31", "type": "racing" } ], [ "agares", [ "taxi2", "callTaxi" ], { "address": "28 Davis Street", "time": "18:4" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2023-10-15", "type": "backcountry" } ], [ "bael", [ "skiResort2", "suggestRestaurant" ], { "date": "2024-05-23" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2023-12-12", "type": "backcountry" } ], [ "marax", [ "trafficServer", "getTraffic" ], { "date": "2023-09-29", "location": "London" } ], [ "bael", [ "skiResort2", "suggestRestaurant" ], { "date": "2024-08-12" } ], [ "paimon", [ "skiResort2", "suggestRestaurant" ], { "date": "2024-07-21" } ], [ "protocolDb3", [ null, "synchronization" ], null ], [ "vassago", [ "restaurant2", "orderEverything" ], { "deliveryAddress": "55 Williams Street" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2023-09-25", "type": "backcountry" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-08-26", "type": "backcountry" } ], [ "agares", [ "taxi2", "callTaxi" ], { "address": "11 Taylor Street", "time": "13:35" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-02-21", "type": "racing" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2023-12-27", "type": "racing" } ], [ "vassago", [ "restaurant2", "orderEverything" ], { "deliveryAddress": "48 Wilson Street" } ], [ "valefor", [ "skiResort2", "rentSki" ], { "date": "2024-08-05", "type": "racing" } ], [ "agares", [ "taxi2", "callTaxi" ], { "address": "5 Taylor Street", "time": "4:42" } ], [ "vassago", [ "restaurant2", "orderEverything" ], { "deliveryAddress": "67 Wilson Street" } ], [ "protocolDb1", [ null, "synchronization" ], null ], [ "paimon", [ "restaurant1", "openingTimes" ], {} ], [ "zepar", [ "cinema1", "buyTickets" ], { "date": [ "2024-03-15" ], "hour": 18, "numTickets": 6, "movie": "Forrest Gump" } ], [ "naberius", [ "cinema1", "availableMovies" ], { "date": [ "2024-03-16" ] } ], [ "agares", [ "taxi2", "callTaxi" ], { "address": "11 White Street", "time": "2:8" } ], [ "aim", [ "cinema1", "buyTickets" ], { "date": [ "2024-03-18" ], "hour": 20, "numTickets": 4, "movie": "Forrest Gump" } ], [ "paimon", [ "skiResort2", "suggestRestaurant" ], { "date": "2023-10-15" } ], [ "agares", [ "taxi2", "callTaxi" ], { "address": "28 Jones Street", "time": "0:24" } ], [ "valefor", [ "skiResort2", "rentSki" ], { "date": "2024-06-26", "type": "backcountry" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-08-03", "type": "carving" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-07-19", "type": "carving" } ], [ "protocolDb2", [ null, "synchronization" ], null ], [ "marbas", [ "restaurant1", "bookTable" ], { "fullName": "Nina Johnson", "date": "2023-12-01", "numPeople": 6, "hour": 19 } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-05-18", "type": "backcountry" } ], [ "samigina", [ "hotel3", "suggestRestaurant" ], { "date": "2024-02-04" } ], [ "bael", [ "taxi2", "callTaxi" ], { "address": "75 Taylor Street", "time": "9:58" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-04-12", "type": "carving" } ], [ "asmoday", [ "cinema1", "buyTickets" ], { "date": [ "2024-03-15" ], "hour": 20, "numTickets": 2, "movie": "Stand by Me" } ], [ "marbas", [ "restaurant1", "bookTable" ], { "fullName": "Valerie Williams", "date": "2024-01-29", "numPeople": 1, "hour": 21 } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-01-09", "type": "backcountry" } ], [ "agares", [ "cinema2", "availableMovies" ], { "date": [ "2024-03-15" ] } ], [ "agares", [ "taxi2", "callTaxi" ], { "address": "51 Taylor Street", "time": "22:38" } ], [ "protocolDb3", [ null, "synchronization" ], null ], [ "agares", [ "taxi2", "callTaxi" ], { "address": "81 Johnson Street", "time": "8:53" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-02-25", "type": "backcountry" } ], [ "orobas", [ "skiResort2", "bookRoom" ], { "startDate": "2024-03-02", "endDate": "2024-03-21" } ], [ "botis", [ "skiResort2", "rentSki" ], { "date": "2024-03-24", "type": "carving" } ], [ "berith", [ "skiResort2", "rentSki" ], { "date": "2023-10-05", "type": "carving" } ], [ "agares", [ "taxi2", "callTaxi" ], { "address": "87 Wilson Street", "time": "17:58" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-08-09", "type": "carving" } ], [ "bael", [ "skiResort2", "suggestRestaurant" ], { "date": "2023-10-07" } ], [ "agares", [ "taxi2", "callTaxi" ], { "address": "20 White Street", "time": "8:51" } ], [ "bael", [ "taxi2", "callTaxi" ], { "address": "17 Davis Street", "time": "17:0" } ], [ "protocolDb1", [ null, "synchronization" ], null ], [ "valefor", [ "restaurant3", "bookTable" ], { "fullName": "Nina Jones", "date": "2024-09-21", "numPeople": 3, "hour": 17 } ], [ "bael", [ "skiResort2", "suggestRestaurant" ], { "date": "2024-07-05" } ], [ "vassago", [ "restaurant2", "orderEverything" ], { "deliveryAddress": "95 Johnson Street" } ], [ "valefor", [ "restaurant3", "bookTable" ], { "fullName": "Nina Williams", "date": "2024-09-19", "numPeople": 1, "hour": 16 } ], [ "bael", [ "skiResort2", "suggestRestaurant" ], { "date": "2024-08-12" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-09-17", "type": "backcountry" } ], [ "vassago", [ "skiResort1", "bookRoom" ], { "startDate": "2024-03-30", "endDate": "2024-04-04" } ], [ "vassago", [ "restaurant2", "orderEverything" ], { "deliveryAddress": "55 Davis Street" } ], [ "agares", [ "taxi2", "callTaxi" ], { "address": "100 Jones Street", "time": "9:38" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-06-14", "type": "racing" } ], [ "protocolDb2", [ null, "synchronization" ], null ], [ "marbas", [ "restaurant1", "bookTable" ], { "fullName": "Toby Miller", "date": "2023-10-25", "numPeople": 2, "hour": 21 } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2023-10-17", "type": "racing" } ], [ "bael", [ "skiResort2", "suggestRestaurant" ], { "date": "2024-01-10" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-03-21", "type": "backcountry" } ], [ "marchosias", [ "cinema1", "availableMovies" ], { "date": [ "2024-03-18" ] } ], [ "sallos", [ "restaurant2", "openingTimes" ], {} ], [ "valefor", [ "skiResort2", "rentSki" ], { "date": "2024-09-01", "type": "carving" } ], [ "purson", [ "skiResort2", "rentSki" ], { "date": "2024-07-25", "type": "backcountry" } ], [ "buer", [ "restaurant3", "openingTimes" ], {} ], [ "barbatos", [ "skiResort2", "bookRoom" ], { "startDate": "2024-06-04", "endDate": "2024-06-18" } ], [ "protocolDb3", [ null, "synchronization" ], null ], [ "agares", [ "cinema2", "availableMovies" ], { "date": [ "2024-03-15" ] } ], [ "vassago", [ "restaurant2", "orderEverything" ], { "deliveryAddress": "35 Wilson Street" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-08-26", "type": "racing" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-01-30", "type": "backcountry" } ], [ "vassago", [ "skiResort1", "bookRoom" ], { "startDate": "2024-07-04", "endDate": "2024-07-14" } ], [ "bael", [ "skiResort2", "suggestRestaurant" ], { "date": "2024-01-06" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2023-11-24", "type": "carving" } ], [ "agares", [ "taxi2", "callTaxi" ], { "address": "79 Wilson Street", "time": "13:18" } ], [ "marbas", [ "restaurant1", "bookTable" ], { "fullName": "Valerie Smith", "date": "2024-07-26", "numPeople": 7, "hour": 16 } ], [ "vassago", [ "restaurant2", "bookTable" ], { "fullName": "Maxwell Brown", "date": "2024-08-10", "numPeople": 7, "hour": 16 } ], [ "protocolDb1", [ null, "synchronization" ], null ], [ "agares", [ "taxi2", "callTaxi" ], { "address": "4 Jones Street", "time": "17:12" } ], [ "bael", [ "skiResort2", "suggestRestaurant" ], { "date": "2024-01-19" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2023-11-13", "type": "racing" } ], [ "agares", [ "taxi2", "callTaxi" ], { "address": "1 Brown Street", "time": "13:1" } ], [ "guison", [ "cinema1", "availableMovies" ], { "date": [ "2024-03-17" ] } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-09-16", "type": "backcountry" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-09-11", "type": "carving" } ], [ "agares", [ "taxi2", "callTaxi" ], { "address": "39 Miller Street", "time": "5:8" } ], [ "leraje", [ "cinema2", "availableMovies" ], { "date": [ "2024-03-17" ] } ], [ "samigina", [ "hotel3", "suggestRestaurant" ], { "date": "2024-06-15" } ], [ "protocolDb2", [ null, "synchronization" ], null ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-08-08", "type": "backcountry" } ], [ "cimeies", [ "skiResort2", "rentSki" ], { "date": "2023-10-04", "type": "racing" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-08-07", "type": "racing" } ], [ "agares", [ "taxi2", "callTaxi" ], { "address": "27 Williams Street", "time": "23:39" } ], [ "buer", [ "skiResort1", "rentSki" ], { "date": "2024-06-11", "type": "carving" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-08-31", "type": "backcountry" } ], [ "vassago", [ "restaurant2", "orderEverything" ], { "deliveryAddress": "51 White Street" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2023-12-09", "type": "racing" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-01-21", "type": "racing" } ], [ "agares", [ "taxi2", "callTaxi" ], { "address": "32 Johnson Street", "time": "6:9" } ], [ "protocolDb3", [ null, "synchronization" ], null ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-05-05", "type": "racing" } ], [ "agares", [ "taxi2", "callTaxi" ], { "address": "90 Williams Street", "time": "0:23" } ], [ "buer", [ "skiResort2", "suggestRestaurant" ], { "date": "2024-08-11" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-03-14", "type": "carving" } ], [ "furcas", [ "cinema2", "buyTickets" ], { "date": [ "2024-03-15" ], "hour": 20, "numTickets": 4, "movie": "Forrest Gump" } ], [ "agares", [ "restaurant3", "bookTable" ], { "fullName": "Brooke Brown", "date": "2024-03-03", "numPeople": 1, "hour": 23 } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-02-13", "type": "racing" } ], [ "amon", [ "skiResort1", "rentSki" ], { "date": "2024-03-07", "type": "carving" } ], [ "eligos", [ "restaurant1", "orderEverything" ], { "deliveryAddress": "79 White Street" } ], [ "agares", [ "taxi2", "callTaxi" ], { "address": "41 White Street", "time": "0:4" } ], [ "protocolDb1", [ null, "synchronization" ], null ], [ "zepar", [ "weatherServer", "queryWeather" ], { "date": "2024-05-11", "location": "New York, USA" } ], [ "vassago", [ "restaurant2", "orderEverything" ], { "deliveryAddress": "8 Jones Street" } ], [ "agares", [ "taxi2", "callTaxi" ], { "address": "56 Smith Street", "time": "1:11" } ], [ "buer", [ "skiResort1", "rentSki" ], { "date": "2024-05-25", "type": "carving" } ], [ "marbas", [ "restaurant1", "bookTable" ], { "fullName": "Cliff Johnson", "date": "2024-03-08", "numPeople": 1, "hour": 20 } ], [ "bael", [ "skiResort2", "suggestRestaurant" ], { "date": "2024-08-07" } ], [ "barbatos", [ "skiResort2", "bookRoom" ], { "startDate": "2024-08-07", "endDate": "2024-08-25" } ], [ "bael", [ "skiResort2", "suggestRestaurant" ], { "date": "2024-03-09" } ], [ "samigina", [ "hotel3", "suggestRestaurant" ], { "date": "2024-08-30" } ], [ "gaap", [ "restaurant2", "openingTimes" ], {} ], [ "protocolDb2", [ null, "synchronization" ], null ], [ "valefor", [ "restaurant3", "bookTable" ], { "fullName": "Cliff Johnson", "date": "2024-05-07", "numPeople": 8, "hour": 23 } ], [ "agares", [ "taxi2", "callTaxi" ], { "address": "71 Jones Street", "time": "3:45" } ], [ "marbas", [ "restaurant1", "bookTable" ], { "fullName": "Toby Johnson", "date": "2023-11-11", "numPeople": 8, "hour": 18 } ], [ "vepar", [ "cinema1", "buyTickets" ], { "date": [ "2024-03-17" ], "hour": 18, "numTickets": 5, "movie": "Forrest Gump" } ], [ "barbatos", [ "skiResort2", "bookRoom" ], { "startDate": "2024-02-15", "endDate": "2024-02-25" } ], [ "agares", [ "taxi2", "callTaxi" ], { "address": "51 Wilson Street", "time": "17:3" } ], [ "vassago", [ "restaurant2", "orderEverything" ], { "deliveryAddress": "52 Brown Street" } ], [ "agares", [ "cinema2", "availableMovies" ], { "date": [ "2024-03-17" ] } ], [ "haures", [ "restaurant2", "orderEverything" ], { "deliveryAddress": "63 Williams Street" } ], [ "leraje", [ "hotel3", "bookRoom" ], { "startDate": "2024-04-01", "endDate": "2024-04-05" } ], [ "protocolDb3", [ null, "synchronization" ], null ], [ "vassago", [ "restaurant2", "orderEverything" ], { "deliveryAddress": "6 Smith Street" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-07-18", "type": "backcountry" } ], [ "agares", [ "taxi2", "callTaxi" ], { "address": "45 Johnson Street", "time": "20:57" } ], [ "samigina", [ "hotel3", "suggestRestaurant" ], { "date": "2024-01-24" } ], [ "bael", [ "taxi2", "callTaxi" ], { "address": "30 Brown Street", "time": "11:39" } ], [ "vassago", [ "restaurant2", "orderEverything" ], { "deliveryAddress": "19 Williams Street" } ], [ "vassago", [ "restaurant2", "orderEverything" ], { "deliveryAddress": "49 Miller Street" } ], [ "astaroth", [ "restaurant3", "openingTimes" ], {} ], [ "bael", [ "skiResort2", "suggestRestaurant" ], { "date": "2024-03-25" } ], [ "seere", [ "restaurant3", "openingTimes" ], {} ], [ "protocolDb1", [ null, "synchronization" ], null ], [ "beleth", [ "hotel1", "bookRoom" ], { "startDate": "2023-11-21", "endDate": "2023-11-27" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-02-17", "type": "carving" } ], [ "amon", [ "hotel1", "bookRoom" ], { "startDate": "2024-03-16", "endDate": "2024-03-20" } ], [ "vassago", [ "restaurant2", "orderEverything" ], { "deliveryAddress": "33 Smith Street" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-01-21", "type": "carving" } ], [ "samigina", [ "cinema1", "buyTickets" ], { "date": [ "2024-03-18" ], "hour": 18, "numTickets": 2, "movie": "Forrest Gump" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-01-06", "type": "backcountry" } ], [ "agares", [ "restaurant3", "bookTable" ], { "fullName": "Valerie Miller", "date": "2024-09-06", "numPeople": 4, "hour": 19 } ], [ "vassago", [ "restaurant2", "bookTable" ], { "fullName": "Nina Wilson", "date": "2024-07-31", "numPeople": 2, "hour": 16 } ], [ "vassago", [ "restaurant2", "orderEverything" ], { "deliveryAddress": "85 Smith Street" } ], [ "protocolDb2", [ null, "synchronization" ], null ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-07-30", "type": "carving" } ], [ "bune", [ "skiResort1", "rentSki" ], { "date": "2024-07-10", "type": "racing" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-04-17", "type": "backcountry" } ], [ "bael", [ "taxi2", "callTaxi" ], { "address": "47 White Street", "time": "22:4" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-02-13", "type": "carving" } ], [ "botis", [ "skiResort2", "rentSki" ], { "date": "2024-05-01", "type": "carving" } ], [ "bael", [ "skiResort2", "suggestRestaurant" ], { "date": "2024-07-26" } ], [ "guison", [ "cinema1", "availableMovies" ], { "date": [ "2024-03-18" ] } ], [ "samigina", [ "hotel3", "suggestRestaurant" ], { "date": "2024-08-29" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2023-12-06", "type": "carving" } ], [ "protocolDb3", [ null, "synchronization" ], null ], [ "vassago", [ "restaurant2", "orderEverything" ], { "deliveryAddress": "6 Williams Street" } ], [ "paimon", [ "restaurant1", "openingTimes" ], {} ], [ "agares", [ "taxi2", "callTaxi" ], { "address": "79 Wilson Street", "time": "1:41" } ], [ "agares", [ "taxi2", "callTaxi" ], { "address": "59 Jones Street", "time": "5:49" } ], [ "bael", [ "taxi2", "callTaxi" ], { "address": "54 Brown Street", "time": "13:8" } ], [ "botis", [ "skiResort2", "rentSki" ], { "date": "2024-02-02", "type": "racing" } ], [ "agares", [ "taxi2", "callTaxi" ], { "address": "26 Johnson Street", "time": "5:11" } ], [ "samigina", [ "hotel3", "suggestRestaurant" ], { "date": "2024-03-12" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-03-06", "type": "carving" } ], [ "agares", [ "taxi2", "callTaxi" ], { "address": "98 Williams Street", "time": "4:9" } ], [ "protocolDb1", [ null, "synchronization" ], null ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-08-18", "type": "backcountry" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2023-10-16", "type": "racing" } ], [ "bael", [ "skiResort2", "suggestRestaurant" ], { "date": "2024-06-26" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-08-13", "type": "backcountry" } ], [ "paimon", [ "skiResort2", "suggestRestaurant" ], { "date": "2024-07-19" } ], [ "valefor", [ "restaurant3", "bookTable" ], { "fullName": "Kara Davis", "date": "2024-07-19", "numPeople": 9, "hour": 16 } ], [ "asmoday", [ "skiResort2", "suggestRestaurant" ], { "date": "2024-08-28" } ], [ "samigina", [ "hotel3", "suggestRestaurant" ], { "date": "2024-02-06" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-07-19", "type": "backcountry" } ], [ "enepsigon", [ "restaurant3", "bookTable" ], { "fullName": "Brooke Wilson", "date": "2024-03-15", "numPeople": 10, "hour": 18 } ], [ "protocolDb2", [ null, "synchronization" ], null ], [ "agares", [ "taxi2", "callTaxi" ], { "address": "96 Jones Street", "time": "22:28" } ], [ "naberius", [ "skiResort2", "bookRoom" ], { "startDate": "2024-01-19", "endDate": "2024-01-28" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-08-25", "type": "backcountry" } ], [ "valefor", [ "skiResort2", "rentSki" ], { "date": "2024-09-06", "type": "backcountry" } ], [ "zagan", [ "hotel2", "bookRoom" ], { "startDate": "2024-04-23", "endDate": "2024-05-13" } ], [ "bael", [ "skiResort2", "suggestRestaurant" ], { "date": "2024-05-17" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-08-01", "type": "racing" } ], [ "forneus", [ "skiResort2", "suggestRestaurant" ], { "date": "2023-12-10" } ], [ "vassago", [ "restaurant2", "bookTable" ], { "fullName": "Laurel Johnson", "date": "2024-07-10", "numPeople": 9, "hour": 16 } ], [ "agares", [ "taxi2", "callTaxi" ], { "address": "3 Wilson Street", "time": "23:51" } ], [ "protocolDb3", [ null, "synchronization" ], null ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-07-06", "type": "carving" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-05-11", "type": "backcountry" } ], [ "agares", [ "cinema2", "availableMovies" ], { "date": [ "2024-03-15" ] } ], [ "eligos", [ "trafficServer", "getTraffic" ], { "date": "2024-03-14", "location": "Zurich" } ], [ "haagenti", [ "cinema1", "availableMovies" ], { "date": [ "2024-03-17" ] } ], [ "samigina", [ "hotel3", "suggestRestaurant" ], { "date": "2024-02-12" } ], [ "bael", [ "taxi2", "callTaxi" ], { "address": "90 Williams Street", "time": "13:16" } ], [ "envy", [ "skiResort1", "rentSki" ], { "date": "2024-09-06", "type": "backcountry" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-07-17", "type": "backcountry" } ], [ "amon", [ "skiResort1", "rentSki" ], { "date": "2023-10-23", "type": "carving" } ], [ "protocolDb1", [ null, "synchronization" ], null ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-04-23", "type": "carving" } ], [ "vassago", [ "restaurant2", "orderEverything" ], { "deliveryAddress": "94 Wilson Street" } ], [ "marbas", [ "restaurant1", "bookTable" ], { "fullName": "Valerie White", "date": "2024-03-29", "numPeople": 4, "hour": 21 } ], [ "vassago", [ "restaurant2", "bookTable" ], { "fullName": "Valerie Brown", "date": "2024-07-15", "numPeople": 4, "hour": 18 } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-04-11", "type": "racing" } ], [ "agares", [ "cinema2", "availableMovies" ], { "date": [ "2024-03-17" ] } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-03-03", "type": "racing" } ], [ "leraje", [ "hotel3", "bookRoom" ], { "startDate": "2023-12-03", "endDate": "2023-12-23" } ], [ "agares", [ "taxi2", "callTaxi" ], { "address": "79 Brown Street", "time": "2:36" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-03-13", "type": "racing" } ], [ "protocolDb2", [ null, "synchronization" ], null ], [ "glasya-labolas", [ "cinema2", "availableMovies" ], { "date": [ "2024-03-17" ] } ], [ "barbatos", [ "skiResort2", "bookRoom" ], { "startDate": "2024-04-25", "endDate": "2024-04-26" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-02-20", "type": "racing" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2023-12-30", "type": "backcountry" } ], [ "bael", [ "skiResort2", "suggestRestaurant" ], { "date": "2024-07-27" } ], [ "bael", [ "skiResort2", "suggestRestaurant" ], { "date": "2024-02-06" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2023-10-29", "type": "carving" } ], [ "vassago", [ "restaurant2", "orderEverything" ], { "deliveryAddress": "60 Miller Street" } ], [ "bael", [ "skiResort2", "suggestRestaurant" ], { "date": "2024-09-02" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-06-23", "type": "carving" } ], [ "protocolDb3", [ null, "synchronization" ], null ], [ "valefor", [ "restaurant3", "bookTable" ], { "fullName": "Kara Davis", "date": "2024-05-12", "numPeople": 9, "hour": 16 } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-03-07", "type": "backcountry" } ], [ "agares", [ "taxi2", "callTaxi" ], { "address": "82 Williams Street", "time": "10:17" } ], [ "bael", [ "skiResort2", "suggestRestaurant" ], { "date": "2024-04-30" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-05-27", "type": "backcountry" } ], [ "vassago", [ "restaurant2", "bookTable" ], { "fullName": "Manfred White", "date": "2024-04-12", "numPeople": 2, "hour": 16 } ], [ "beleth", [ "hotel1", "bookRoom" ], { "startDate": "2024-01-17", "endDate": "2024-01-30" } ], [ "bael", [ "skiResort2", "suggestRestaurant" ], { "date": "2023-10-01" } ], [ "raum", [ "hotel3", "bookRoom" ], { "startDate": "2023-10-05", "endDate": "2023-10-07" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-09-07", "type": "carving" } ], [ "protocolDb1", [ null, "synchronization" ], null ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2023-11-02", "type": "racing" } ], [ "amon", [ "skiResort1", "rentSki" ], { "date": "2024-07-14", "type": "carving" } ], [ "bael", [ "skiResort2", "suggestRestaurant" ], { "date": "2024-09-07" } ], [ "agares", [ "restaurant3", "bookTable" ], { "fullName": "Nina White", "date": "2023-11-27", "numPeople": 1, "hour": 19 } ], [ "agares", [ "taxi2", "callTaxi" ], { "address": "28 Johnson Street", "time": "23:49" } ], [ "raum", [ "restaurant2", "bookTable" ], { "fullName": "Madge Taylor", "date": "2023-12-12", "numPeople": 2, "hour": 21 } ], [ "agares", [ "taxi2", "callTaxi" ], { "address": "30 Davis Street", "time": "13:37" } ], [ "barbatos", [ "skiResort2", "bookRoom" ], { "startDate": "2024-08-09", "endDate": "2024-08-13" } ], [ "guison", [ "skiResort1", "rentSki" ], { "date": "2024-05-20", "type": "racing" } ], [ "bael", [ "skiResort2", "suggestRestaurant" ], { "date": "2024-06-10" } ], [ "protocolDb2", [ null, "synchronization" ], null ], [ "bael", [ "skiResort2", "suggestRestaurant" ], { "date": "2024-02-26" } ], [ "agares", [ "taxi2", "callTaxi" ], { "address": "73 Miller Street", "time": "3:46" } ], [ "zepar", [ "weatherServer", "queryWeather" ], { "date": "2024-01-11", "location": "London, UK" } ], [ "marbas", [ "restaurant1", "bookTable" ], { "fullName": "Kara Jones", "date": "2024-05-07", "numPeople": 4, "hour": 16 } ], [ "barbatos", [ "skiResort2", "bookRoom" ], { "startDate": "2024-02-21", "endDate": "2024-03-04" } ], [ "vassago", [ "restaurant2", "orderEverything" ], { "deliveryAddress": "37 Miller Street" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-09-02", "type": "racing" } ], [ "astaroth", [ "hotel3", "bookRoom" ], { "startDate": "2023-10-08", "endDate": "2023-10-13" } ], [ "agares", [ "restaurant3", "bookTable" ], { "fullName": "Cliff Jones", "date": "2024-08-01", "numPeople": 10, "hour": 22 } ], [ "bael", [ "skiResort2", "suggestRestaurant" ], { "date": "2024-06-01" } ], [ "protocolDb3", [ null, "synchronization" ], null ], [ "buer", [ "skiResort1", "rentSki" ], { "date": "2024-05-06", "type": "backcountry" } ], [ "paimon", [ "skiResort2", "suggestRestaurant" ], { "date": "2024-01-05" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2023-12-01", "type": "backcountry" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-08-07", "type": "racing" } ], [ "paimon", [ "skiResort2", "suggestRestaurant" ], { "date": "2024-05-19" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-03-06", "type": "racing" } ], [ "sitri", [ "skiResort1", "bookRoom" ], { "startDate": "2024-03-21", "endDate": "2024-03-23" } ], [ "pruflas", [ "restaurant1", "orderEverything" ], { "deliveryAddress": "94 Miller Street" } ], [ "agares", [ "taxi2", "callTaxi" ], { "address": "61 White Street", "time": "7:15" } ], [ "botis", [ "skiResort1", "rentSki" ], { "date": "2024-06-25", "type": "racing" } ], [ "protocolDb1", [ null, "synchronization" ], null ], [ "stolas", [ "skiResort1", "rentSki" ], { "date": "2024-08-07", "type": "carving" } ], [ "sitri", [ "skiResort1", "bookRoom" ], { "startDate": "2023-12-09", "endDate": "2023-12-26" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-04-12", "type": "racing" } ], [ "vassago", [ "restaurant2", "orderEverything" ], { "deliveryAddress": "65 Jones Street" } ], [ "bael", [ "taxi2", "callTaxi" ], { "address": "15 Brown Street", "time": "0:53" } ], [ "sitri", [ "skiResort1", "bookRoom" ], { "startDate": "2023-11-17", "endDate": "2023-12-03" } ], [ "rath", [ "restaurant2", "bookTable" ], { "fullName": "Brooke White", "date": "2024-03-13", "numPeople": 9, "hour": 23 } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-08-15", "type": "backcountry" } ], [ "barbatos", [ "cinema2", "buyTickets" ], { "date": [ "2024-03-18" ], "hour": 20, "numTickets": 2, "movie": "Forrest Gump" } ], [ "samigina", [ "hotel3", "suggestRestaurant" ], { "date": "2024-09-18" } ], [ "protocolDb2", [ null, "synchronization" ], null ], [ "zepar", [ "weatherServer", "queryWeather" ], { "date": "2024-06-30", "location": "New York, USA" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-07-15", "type": "racing" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-06-21", "type": "backcountry" } ], [ "beleth", [ "restaurant1", "openingTimes" ], {} ], [ "vassago", [ "skiResort1", "bookRoom" ], { "startDate": "2024-05-24", "endDate": "2024-05-31" } ], [ "crocell", [ "trafficServer", "getTraffic" ], { "date": "2024-02-24", "location": "Zurich" } ], [ "bael", [ "taxi2", "callTaxi" ], { "address": "57 Davis Street", "time": "3:8" } ], [ "amon", [ "skiResort1", "rentSki" ], { "date": "2024-02-12", "type": "carving" } ], [ "vassago", [ "restaurant2", "orderEverything" ], { "deliveryAddress": "31 Miller Street" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-01-01", "type": "racing" } ], [ "protocolDb3", [ null, "synchronization" ], null ], [ "agares", [ "taxi2", "callTaxi" ], { "address": "79 Jones Street", "time": "22:12" } ], [ "bael", [ "taxi2", "callTaxi" ], { "address": "53 Brown Street", "time": "9:35" } ], [ "andrealphus", [ "restaurant2", "bookTable" ], { "fullName": "Maxwell Wilson", "date": "2024-03-22", "numPeople": 5, "hour": 21 } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-02-21", "type": "racing" } ], [ "bael", [ "skiResort2", "suggestRestaurant" ], { "date": "2024-09-09" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-01-22", "type": "carving" } ], [ "bael", [ "skiResort2", "suggestRestaurant" ], { "date": "2023-11-18" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2023-10-17", "type": "carving" } ], [ "agares", [ "taxi2", "callTaxi" ], { "address": "24 Jones Street", "time": "6:16" } ], [ "bael", [ "taxi2", "callTaxi" ], { "address": "11 Wilson Street", "time": "22:44" } ], [ "protocolDb1", [ null, "synchronization" ], null ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-09-08", "type": "backcountry" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-01-22", "type": "carving" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-09-21", "type": "carving" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2023-12-08", "type": "carving" } ], [ "vine", [ "weatherServer", "queryWeather" ], { "date": "2024-06-20", "location": "Paris, France" } ], [ "agares", [ "taxi2", "callTaxi" ], { "address": "41 Williams Street", "time": "23:53" } ], [ "amy", [ "skiResort1", "rentSki" ], { "date": "2024-03-12", "type": "carving" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-05-12", "type": "racing" } ], [ "paimon", [ "skiResort2", "suggestRestaurant" ], { "date": "2024-08-04" } ], [ "bael", [ "taxi2", "callTaxi" ], { "address": "51 Miller Street", "time": "5:50" } ], [ "protocolDb2", [ null, "synchronization" ], null ], [ "barbatos", [ "skiResort2", "bookRoom" ], { "startDate": "2023-12-15", "endDate": "2023-12-20" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-03-05", "type": "backcountry" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-05-02", "type": "carving" } ], [ "bael", [ "skiResort2", "suggestRestaurant" ], { "date": "2024-07-23" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-02-11", "type": "carving" } ], [ "vine", [ "restaurant2", "bookTable" ], { "fullName": "Nina Miller", "date": "2024-08-29", "numPeople": 4, "hour": 17 } ], [ "agares", [ "taxi2", "callTaxi" ], { "address": "90 Davis Street", "time": "14:57" } ], [ "agares", [ "cinema2", "availableMovies" ], { "date": [ "2024-03-16" ] } ], [ "bael", [ "taxi2", "callTaxi" ], { "address": "42 Miller Street", "time": "13:22" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-07-23", "type": "backcountry" } ], [ "protocolDb3", [ null, "synchronization" ], null ], [ "rabdos", [ "cinema1", "availableMovies" ], { "date": [ "2024-03-18" ] } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2023-11-05", "type": "racing" } ], [ "agares", [ "cinema2", "availableMovies" ], { "date": [ "2024-03-18" ] } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-06-30", "type": "backcountry" } ], [ "glasya-labolas", [ "cinema2", "availableMovies" ], { "date": [ "2024-03-17" ] } ], [ "valefor", [ "restaurant3", "bookTable" ], { "fullName": "Manfred Brown", "date": "2024-08-13", "numPeople": 4, "hour": 18 } ], [ "bael", [ "skiResort2", "suggestRestaurant" ], { "date": "2023-09-29" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-08-28", "type": "racing" } ], [ "agares", [ "taxi2", "callTaxi" ], { "address": "20 Taylor Street", "time": "12:12" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2023-10-26", "type": "carving" } ], [ "protocolDb1", [ null, "synchronization" ], null ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-03-06", "type": "racing" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2023-12-02", "type": "carving" } ], [ "marbas", [ "restaurant1", "bookTable" ], { "fullName": "Toby Brown", "date": "2024-01-31", "numPeople": 1, "hour": 23 } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-06-14", "type": "racing" } ], [ "bael", [ "taxi2", "callTaxi" ], { "address": "93 Brown Street", "time": "2:14" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-04-16", "type": "racing" } ], [ "vassago", [ "restaurant2", "orderEverything" ], { "deliveryAddress": "71 White Street" } ], [ "shax", [ "restaurant3", "openingTimes" ], {} ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-02-14", "type": "carving" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-04-08", "type": "carving" } ], [ "protocolDb2", [ null, "synchronization" ], null ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-05-05", "type": "racing" } ], [ "agares", [ "taxi2", "callTaxi" ], { "address": "15 Williams Street", "time": "11:8" } ], [ "marbas", [ "restaurant1", "bookTable" ], { "fullName": "Laurel Johnson", "date": "2023-12-25", "numPeople": 7, "hour": 21 } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-03-27", "type": "carving" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-05-02", "type": "backcountry" } ], [ "amon", [ "skiResort1", "rentSki" ], { "date": "2023-11-07", "type": "carving" } ], [ "samigina", [ "cinema1", "buyTickets" ], { "date": [ "2024-03-16" ], "hour": 20, "numTickets": 1, "movie": "Stand by Me" } ], [ "agares", [ "restaurant3", "bookTable" ], { "fullName": "Toby White", "date": "2024-03-24", "numPeople": 1, "hour": 21 } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-03-10", "type": "racing" } ], [ "bael", [ "skiResort2", "suggestRestaurant" ], { "date": "2023-12-20" } ], [ "protocolDb3", [ null, "synchronization" ], null ], [ "bael", [ "skiResort2", "suggestRestaurant" ], { "date": "2024-07-13" } ], [ "sallos", [ "restaurant2", "openingTimes" ], {} ], [ "agares", [ "taxi2", "callTaxi" ], { "address": "90 Brown Street", "time": "9:43" } ], [ "agares", [ "taxi2", "callTaxi" ], { "address": "6 Williams Street", "time": "17:50" } ], [ "agares", [ "restaurant3", "bookTable" ], { "fullName": "Toby White", "date": "2023-10-01", "numPeople": 6, "hour": 21 } ], [ "bael", [ "skiResort2", "suggestRestaurant" ], { "date": "2024-09-02" } ], [ "ronove", [ "skiResort2", "suggestRestaurant" ], { "date": "2023-10-23" } ], [ "marbas", [ "restaurant1", "bookTable" ], { "fullName": "Cliff Taylor", "date": "2024-06-23", "numPeople": 2, "hour": 23 } ], [ "bael", [ "skiResort2", "suggestRestaurant" ], { "date": "2024-01-21" } ], [ "balam", [ "restaurant3", "bookTable" ], { "fullName": "Toby Smith", "date": "2023-10-01", "numPeople": 4, "hour": 18 } ], [ "protocolDb1", [ null, "synchronization" ], null ], [ "beelzebub", [ "cinema1", "buyTickets" ], { "date": [ "2024-03-16" ], "hour": 18, "numTickets": 5, "movie": "The Silence of the Lambs" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-05-07", "type": "racing" } ], [ "bael", [ "skiResort2", "suggestRestaurant" ], { "date": "2023-12-31" } ], [ "foras", [ "cinema1", "availableMovies" ], { "date": [ "2024-03-15" ] } ], [ "agares", [ "taxi2", "callTaxi" ], { "address": "61 Miller Street", "time": "5:22" } ], [ "furcas", [ "skiResort2", "rentSki" ], { "date": "2023-10-09", "type": "racing" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2023-10-16", "type": "carving" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-09-15", "type": "backcountry" } ], [ "agares", [ "taxi2", "callTaxi" ], { "address": "69 White Street", "time": "8:48" } ], [ "bael", [ "skiResort2", "suggestRestaurant" ], { "date": "2023-10-25" } ], [ "protocolDb2", [ null, "synchronization" ], null ], [ "alloces", [ "skiResort2", "suggestRestaurant" ], { "date": "2024-01-17" } ], [ "glasya-labolas", [ "restaurant1", "bookTable" ], { "fullName": "Laurel Taylor", "date": "2024-09-01", "numPeople": 1, "hour": 20 } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2023-12-17", "type": "carving" } ], [ "marbas", [ "restaurant1", "bookTable" ], { "fullName": "Toby Smith", "date": "2023-12-29", "numPeople": 10, "hour": 21 } ], [ "samigina", [ "hotel3", "suggestRestaurant" ], { "date": "2024-07-23" } ], [ "vassago", [ "restaurant2", "bookTable" ], { "fullName": "Maxwell Jones", "date": "2024-04-05", "numPeople": 3, "hour": 20 } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2023-11-17", "type": "racing" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2023-11-04", "type": "carving" } ], [ "barbatos", [ "skiResort2", "bookRoom" ], { "startDate": "2024-07-09", "endDate": "2024-07-14" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-09-01", "type": "racing" } ], [ "protocolDb3", [ null, "synchronization" ], null ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2023-12-29", "type": "backcountry" } ], [ "samigina", [ "hotel3", "suggestRestaurant" ], { "date": "2023-09-24" } ], [ "bael", [ "taxi2", "callTaxi" ], { "address": "70 Johnson Street", "time": "7:5" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-08-10", "type": "backcountry" } ], [ "agares", [ "taxi2", "callTaxi" ], { "address": "12 Johnson Street", "time": "21:32" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2023-10-14", "type": "carving" } ], [ "furfur", [ "restaurant3", "openingTimes" ], {} ], [ "vassago", [ "restaurant2", "orderEverything" ], { "deliveryAddress": "54 Johnson Street" } ], [ "agares", [ "taxi2", "callTaxi" ], { "address": "94 Brown Street", "time": "3:11" } ], [ "vassago", [ "restaurant2", "orderEverything" ], { "deliveryAddress": "70 White Street" } ], [ "protocolDb1", [ null, "synchronization" ], null ], [ "leraje", [ "hotel3", "bookRoom" ], { "startDate": "2024-04-25", "endDate": "2024-05-02" } ], [ "vassago", [ "restaurant2", "orderEverything" ], { "deliveryAddress": "8 Miller Street" } ], [ "buer", [ "skiResort1", "rentSki" ], { "date": "2024-01-31", "type": "backcountry" } ], [ "bael", [ "skiResort2", "suggestRestaurant" ], { "date": "2024-07-30" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-07-09", "type": "racing" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-09-15", "type": "backcountry" } ], [ "eligos", [ "restaurant1", "orderEverything" ], { "deliveryAddress": "50 Wilson Street" } ], [ "purson", [ "trafficServer", "getTraffic" ], { "date": "2023-10-20", "location": "Los Angeles" } ], [ "bael", [ "taxi2", "callTaxi" ], { "address": "46 White Street", "time": "23:45" } ], [ "tephras", [ "cinema2", "availableMovies" ], { "date": [ "2024-03-17" ] } ], [ "protocolDb2", [ null, "synchronization" ], null ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-02-18", "type": "backcountry" } ], [ "kunopaston", [ "skiResort2", "suggestRestaurant" ], { "date": "2024-07-06" } ], [ "samigina", [ "cinema1", "buyTickets" ], { "date": [ "2024-03-15" ], "hour": 20, "numTickets": 6, "movie": "Stand by Me" } ], [ "decarabia", [ "restaurant1", "bookTable" ], { "fullName": "Manfred Miller", "date": "2023-12-13", "numPeople": 7, "hour": 22 } ], [ "bael", [ "skiResort2", "suggestRestaurant" ], { "date": "2024-01-07" } ], [ "bael", [ "taxi2", "callTaxi" ], { "address": "18 Johnson Street", "time": "20:0" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-08-23", "type": "backcountry" } ], [ "bael", [ "skiResort2", "suggestRestaurant" ], { "date": "2024-04-18" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-06-18", "type": "racing" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-03-24", "type": "carving" } ], [ "protocolDb3", [ null, "synchronization" ], null ], [ "bael", [ "skiResort2", "suggestRestaurant" ], { "date": "2023-12-29" } ], [ "beleth", [ "hotel1", "bookRoom" ], { "startDate": "2024-05-15", "endDate": "2024-05-31" } ], [ "agares", [ "cinema2", "availableMovies" ], { "date": [ "2024-03-16" ] } ], [ "samigina", [ "hotel3", "suggestRestaurant" ], { "date": "2023-10-05" } ], [ "eligos", [ "restaurant1", "orderEverything" ], { "deliveryAddress": "21 Brown Street" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-03-17", "type": "carving" } ], [ "vassago", [ "skiResort1", "bookRoom" ], { "startDate": "2024-06-22", "endDate": "2024-07-07" } ], [ "amon", [ "cinema1", "availableMovies" ], { "date": [ "2024-03-17" ] } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2023-10-20", "type": "carving" } ], [ "eligos", [ "restaurant1", "orderEverything" ], { "deliveryAddress": "53 Williams Street" } ], [ "protocolDb1", [ null, "synchronization" ], null ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2023-10-20", "type": "backcountry" } ], [ "samigina", [ "hotel3", "suggestRestaurant" ], { "date": "2023-12-18" } ], [ "foras", [ "taxi2", "callTaxi" ], { "address": "42 Brown Street", "time": "12:22" } ], [ "agares", [ "taxi2", "callTaxi" ], { "address": "26 Williams Street", "time": "1:36" } ], [ "barbatos", [ "hotel3", "suggestRestaurant" ], { "date": "2024-09-11" } ], [ "beleth", [ "hotel1", "bookRoom" ], { "startDate": "2024-08-24", "endDate": "2024-09-01" } ], [ "valefor", [ "restaurant3", "bookTable" ], { "fullName": "Valerie Miller", "date": "2024-06-09", "numPeople": 6, "hour": 18 } ], [ "guison", [ "cinema1", "availableMovies" ], { "date": [ "2024-03-18" ] } ], [ "guison", [ "taxi2", "callTaxi" ], { "address": "96 White Street", "time": "8:11" } ], [ "vassago", [ "restaurant2", "orderEverything" ], { "deliveryAddress": "68 Miller Street" } ], [ "protocolDb2", [ null, "synchronization" ], null ], [ "malphas", [ "skiResort2", "rentSki" ], { "date": "2024-02-25", "type": "racing" } ], [ "bael", [ "skiResort2", "suggestRestaurant" ], { "date": "2024-06-22" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2023-10-15", "type": "racing" } ], [ "samigina", [ "hotel3", "suggestRestaurant" ], { "date": "2023-11-07" } ], [ "leraje", [ "hotel3", "bookRoom" ], { "startDate": "2024-06-16", "endDate": "2024-06-26" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-05-01", "type": "carving" } ], [ "agares", [ "taxi2", "callTaxi" ], { "address": "54 Johnson Street", "time": "21:14" } ], [ "bael", [ "skiResort2", "suggestRestaurant" ], { "date": "2024-09-17" } ], [ "agares", [ "taxi2", "callTaxi" ], { "address": "63 Brown Street", "time": "17:0" } ], [ "agares", [ "taxi2", "callTaxi" ], { "address": "49 Miller Street", "time": "12:55" } ], [ "protocolDb3", [ null, "synchronization" ], null ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-03-12", "type": "backcountry" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-03-18", "type": "racing" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-01-31", "type": "racing" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-08-12", "type": "backcountry" } ], [ "agares", [ "cinema2", "availableMovies" ], { "date": [ "2024-03-18" ] } ], [ "valefor", [ "restaurant3", "bookTable" ], { "fullName": "Brooke Taylor", "date": "2024-09-07", "numPeople": 6, "hour": 22 } ], [ "marbas", [ "restaurant1", "bookTable" ], { "fullName": "Manfred Williams", "date": "2024-07-07", "numPeople": 6, "hour": 21 } ], [ "agares", [ "taxi2", "callTaxi" ], { "address": "25 Taylor Street", "time": "20:51" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-08-11", "type": "backcountry" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-04-13", "type": "racing" } ], [ "protocolDb1", [ null, "synchronization" ], null ], [ "bael", [ "skiResort2", "suggestRestaurant" ], { "date": "2024-06-05" } ], [ "vassago", [ "restaurant2", "orderEverything" ], { "deliveryAddress": "42 Jones Street" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-02-27", "type": "backcountry" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-05-10", "type": "racing" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2023-10-15", "type": "racing" } ], [ "agares", [ "taxi2", "callTaxi" ], { "address": "77 Jones Street", "time": "3:2" } ], [ "guison", [ "cinema1", "availableMovies" ], { "date": [ "2024-03-18" ] } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-04-25", "type": "backcountry" } ], [ "forneus", [ "skiResort1", "rentSki" ], { "date": "2024-05-29", "type": "backcountry" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-05-12", "type": "backcountry" } ], [ "protocolDb2", [ null, "synchronization" ], null ], [ "agares", [ "taxi2", "callTaxi" ], { "address": "2 Miller Street", "time": "10:54" } ], [ "agares", [ "cinema2", "availableMovies" ], { "date": [ "2024-03-16" ] } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2023-12-23", "type": "carving" } ], [ "samigina", [ "hotel3", "suggestRestaurant" ], { "date": "2023-10-20" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2023-11-03", "type": "carving" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-02-05", "type": "carving" } ], [ "samigina", [ "hotel3", "suggestRestaurant" ], { "date": "2023-10-27" } ], [ "foras", [ "restaurant1", "orderEverything" ], { "deliveryAddress": "4 White Street" } ], [ "barbatos", [ "hotel3", "suggestRestaurant" ], { "date": "2023-10-22" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-08-02", "type": "carving" } ], [ "protocolDb3", [ null, "synchronization" ], null ], [ "valefor", [ "restaurant3", "bookTable" ], { "fullName": "Kara Wilson", "date": "2023-11-24", "numPeople": 9, "hour": 20 } ], [ "vassago", [ "restaurant2", "orderEverything" ], { "deliveryAddress": "66 Davis Street" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2023-10-19", "type": "backcountry" } ], [ "bathin", [ "taxi2", "callTaxi" ], { "address": "59 Taylor Street", "time": "9:14" } ], [ "valefor", [ "restaurant3", "bookTable" ], { "fullName": "Kara Williams", "date": "2024-05-04", "numPeople": 9, "hour": 19 } ], [ "bael", [ "taxi2", "callTaxi" ], { "address": "10 Taylor Street", "time": "21:32" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2023-10-29", "type": "racing" } ], [ "orias", [ "cinema2", "availableMovies" ], { "date": [ "2024-03-16" ] } ], [ "vassago", [ "restaurant2", "orderEverything" ], { "deliveryAddress": "23 Davis Street" } ], [ "andromalius", [ "restaurant2", "openingTimes" ], {} ], [ "protocolDb1", [ null, "synchronization" ], null ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-08-20", "type": "racing" } ], [ "agares", [ "taxi2", "callTaxi" ], { "address": "7 Smith Street", "time": "0:40" } ], [ "paimon", [ "skiResort2", "suggestRestaurant" ], { "date": "2024-04-23" } ], [ "samigina", [ "hotel3", "suggestRestaurant" ], { "date": "2024-06-29" } ], [ "agares", [ "taxi2", "callTaxi" ], { "address": "68 Taylor Street", "time": "2:24" } ], [ "agares", [ "taxi2", "callTaxi" ], { "address": "64 White Street", "time": "7:26" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-02-08", "type": "carving" } ], [ "samigina", [ "restaurant3", "bookTable" ], { "fullName": "Madge Wilson", "date": "2024-08-18", "numPeople": 8, "hour": 19 } ], [ "agares", [ "taxi2", "callTaxi" ], { "address": "39 Brown Street", "time": "20:6" } ], [ "samigina", [ "hotel3", "suggestRestaurant" ], { "date": "2023-10-31" } ], [ "protocolDb2", [ null, "synchronization" ], null ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-09-07", "type": "carving" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-05-16", "type": "carving" } ], [ "agares", [ "taxi2", "callTaxi" ], { "address": "21 Williams Street", "time": "2:11" } ], [ "bael", [ "taxi2", "callTaxi" ], { "address": "91 White Street", "time": "8:59" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-03-26", "type": "racing" } ], [ "vassago", [ "restaurant2", "orderEverything" ], { "deliveryAddress": "6 Miller Street" } ], [ "leraje", [ "hotel3", "suggestRestaurant" ], { "date": "2024-04-28" } ], [ "marbas", [ "cinema1", "buyTickets" ], { "date": [ "2024-03-18" ], "hour": 18, "numTickets": 3, "movie": "Forrest Gump" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-04-16", "type": "racing" } ], [ "marbas", [ "restaurant1", "bookTable" ], { "fullName": "Cliff White", "date": "2023-11-12", "numPeople": 5, "hour": 20 } ], [ "protocolDb3", [ null, "synchronization" ], null ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-05-18", "type": "racing" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2023-12-14", "type": "carving" } ], [ "sitri", [ "skiResort1", "bookRoom" ], { "startDate": "2024-01-21", "endDate": "2024-02-09" } ], [ "amon", [ "skiResort1", "rentSki" ], { "date": "2024-03-28", "type": "racing" } ], [ "bael", [ "taxi2", "callTaxi" ], { "address": "37 Williams Street", "time": "5:13" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-04-25", "type": "racing" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-01-09", "type": "racing" } ], [ "vassago", [ "skiResort1", "bookRoom" ], { "startDate": "2024-05-30", "endDate": "2024-06-06" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-05-16", "type": "carving" } ], [ "agares", [ "taxi2", "callTaxi" ], { "address": "43 Johnson Street", "time": "13:13" } ], [ "protocolDb1", [ null, "synchronization" ], null ], [ "agares", [ "cinema2", "availableMovies" ], { "date": [ "2024-03-16" ] } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2023-10-21", "type": "backcountry" } ], [ "samigina", [ "cinema1", "buyTickets" ], { "date": [ "2024-03-18" ], "hour": 18, "numTickets": 5, "movie": "The Silence of the Lambs" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-05-26", "type": "backcountry" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2023-10-29", "type": "carving" } ], [ "samigina", [ "hotel3", "suggestRestaurant" ], { "date": "2024-04-27" } ], [ "samigina", [ "hotel3", "suggestRestaurant" ], { "date": "2024-09-10" } ], [ "buer", [ "skiResort1", "rentSki" ], { "date": "2024-02-07", "type": "carving" } ], [ "dantalion", [ "weatherServer", "queryWeather" ], { "date": "2023-11-06", "location": "Paris, France" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2023-10-12", "type": "backcountry" } ], [ "protocolDb2", [ null, "synchronization" ], null ], [ "bael", [ "skiResort2", "suggestRestaurant" ], { "date": "2024-02-23" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-04-21", "type": "racing" } ], [ "bael", [ "taxi2", "callTaxi" ], { "address": "48 Taylor Street", "time": "6:26" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2023-10-10", "type": "racing" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-05-29", "type": "backcountry" } ], [ "samigina", [ "hotel3", "suggestRestaurant" ], { "date": "2023-10-02" } ], [ "amon", [ "skiResort1", "rentSki" ], { "date": "2024-09-05", "type": "carving" } ], [ "murmur", [ "cinema1", "buyTickets" ], { "date": [ "2024-03-17" ], "hour": 18, "numTickets": 5, "movie": "The Silence of the Lambs" } ], [ "vassago", [ "restaurant2", "orderEverything" ], { "deliveryAddress": "30 Brown Street" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-01-23", "type": "backcountry" } ], [ "protocolDb3", [ null, "synchronization" ], null ], [ "vassago", [ "restaurant2", "orderEverything" ], { "deliveryAddress": "36 Johnson Street" } ], [ "vassago", [ "skiResort1", "bookRoom" ], { "startDate": "2024-03-05", "endDate": "2024-03-11" } ], [ "bael", [ "skiResort2", "suggestRestaurant" ], { "date": "2024-02-08" } ], [ "agares", [ "taxi2", "callTaxi" ], { "address": "37 Brown Street", "time": "9:28" } ], [ "agares", [ "cinema2", "availableMovies" ], { "date": [ "2024-03-17" ] } ], [ "aim", [ "hotel3", "bookRoom" ], { "startDate": "2024-09-04", "endDate": "2024-09-05" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-08-18", "type": "carving" } ], [ "tribolaios", [ "restaurant3", "openingTimes" ], {} ], [ "vassago", [ "restaurant2", "orderEverything" ], { "deliveryAddress": "59 Taylor Street" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2023-11-06", "type": "racing" } ], [ "protocolDb1", [ null, "synchronization" ], null ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-07-08", "type": "racing" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-04-11", "type": "racing" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-05-19", "type": "backcountry" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2023-11-15", "type": "backcountry" } ], [ "bael", [ "skiResort2", "suggestRestaurant" ], { "date": "2024-05-10" } ], [ "ipos", [ "hotel3", "bookRoom" ], { "startDate": "2024-05-09", "endDate": "2024-05-20" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-01-02", "type": "racing" } ], [ "bael", [ "taxi2", "callTaxi" ], { "address": "51 Taylor Street", "time": "5:9" } ], [ "bael", [ "taxi2", "callTaxi" ], { "address": "60 Miller Street", "time": "22:28" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-04-02", "type": "backcountry" } ], [ "protocolDb2", [ null, "synchronization" ], null ], [ "vassago", [ "restaurant2", "orderEverything" ], { "deliveryAddress": "17 Davis Street" } ], [ "amon", [ "skiResort1", "rentSki" ], { "date": "2024-07-28", "type": "backcountry" } ], [ "marbas", [ "cinema1", "buyTickets" ], { "date": [ "2024-03-17" ], "hour": 20, "numTickets": 5, "movie": "Stand by Me" } ], [ "obizuth", [ "skiResort2", "rentSki" ], { "date": "2024-06-09", "type": "carving" } ], [ "sabnock", [ "skiResort1", "bookRoom" ], { "startDate": "2024-05-04", "endDate": "2024-05-19" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-07-08", "type": "backcountry" } ], [ "vassago", [ "restaurant2", "orderEverything" ], { "deliveryAddress": "84 Wilson Street" } ], [ "paimon", [ "restaurant3", "bookTable" ], { "fullName": "Laurel Brown", "date": "2024-03-10", "numPeople": 9, "hour": 17 } ], [ "agares", [ "cinema2", "availableMovies" ], { "date": [ "2024-03-15" ] } ], [ "amon", [ "cinema1", "availableMovies" ], { "date": [ "2024-03-18" ] } ], [ "protocolDb3", [ null, "synchronization" ], null ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-09-12", "type": "backcountry" } ], [ "beleth", [ "skiResort2", "rentSki" ], { "date": "2024-07-16", "type": "racing" } ], [ "marbas", [ "restaurant1", "bookTable" ], { "fullName": "Brooke Wilson", "date": "2024-04-29", "numPeople": 8, "hour": 20 } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-07-24", "type": "carving" } ], [ "bael", [ "skiResort2", "suggestRestaurant" ], { "date": "2024-07-04" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-08-13", "type": "carving" } ], [ "samigina", [ "hotel3", "suggestRestaurant" ], { "date": "2024-04-04" } ], [ "agares", [ "taxi2", "callTaxi" ], { "address": "99 Jones Street", "time": "6:39" } ], [ "agares", [ "taxi2", "callTaxi" ], { "address": "11 Miller Street", "time": "6:39" } ], [ "agares", [ "taxi2", "callTaxi" ], { "address": "48 Jones Street", "time": "2:4" } ], [ "protocolDb1", [ null, "synchronization" ], null ], [ "bael", [ "taxi2", "callTaxi" ], { "address": "96 Johnson Street", "time": "8:59" } ], [ "vassago", [ "restaurant2", "orderEverything" ], { "deliveryAddress": "32 Jones Street" } ], [ "sallos", [ "restaurant3", "openingTimes" ], {} ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-04-30", "type": "carving" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-08-16", "type": "backcountry" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-07-01", "type": "backcountry" } ], [ "gaap", [ "hotel1", "bookRoom" ], { "startDate": "2024-03-15", "endDate": "2024-03-16" } ], [ "berith", [ "skiResort2", "bookRoom" ], { "startDate": "2023-10-23", "endDate": "2023-11-10" } ], [ "vassago", [ "restaurant2", "orderEverything" ], { "deliveryAddress": "38 Davis Street" } ], [ "agares", [ "taxi2", "callTaxi" ], { "address": "31 Wilson Street", "time": "17:42" } ], [ "protocolDb2", [ null, "synchronization" ], null ], [ "samigina", [ "hotel3", "suggestRestaurant" ], { "date": "2024-01-24" } ], [ "bathin", [ "taxi2", "callTaxi" ], { "address": "25 Smith Street", "time": "20:23" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2023-12-16", "type": "carving" } ], [ "vassago", [ "restaurant2", "orderEverything" ], { "deliveryAddress": "94 Taylor Street" } ], [ "ronove", [ "restaurant1", "bookTable" ], { "fullName": "Valerie Jones", "date": "2024-06-13", "numPeople": 4, "hour": 18 } ], [ "amon", [ "skiResort1", "rentSki" ], { "date": "2024-04-23", "type": "carving" } ], [ "agares", [ "restaurant3", "bookTable" ], { "fullName": "Brooke Wilson", "date": "2023-10-23", "numPeople": 10, "hour": 18 } ], [ "amon", [ "hotel1", "bookRoom" ], { "startDate": "2024-01-05", "endDate": "2024-01-23" } ], [ "agares", [ "cinema2", "availableMovies" ], { "date": [ "2024-03-15" ] } ], [ "vassago", [ "restaurant2", "orderEverything" ], { "deliveryAddress": "79 Taylor Street" } ], [ "protocolDb3", [ null, "synchronization" ], null ], [ "vapula", [ "cinema2", "availableMovies" ], { "date": [ "2024-03-17" ] } ], [ "samigina", [ "hotel3", "suggestRestaurant" ], { "date": "2024-08-25" } ], [ "sitri", [ "restaurant1", "openingTimes" ], {} ], [ "samigina", [ "hotel3", "suggestRestaurant" ], { "date": "2023-12-04" } ], [ "samigina", [ "hotel3", "suggestRestaurant" ], { "date": "2024-04-03" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-02-18", "type": "carving" } ], [ "guison", [ "cinema1", "availableMovies" ], { "date": [ "2024-03-17" ] } ], [ "marbas", [ "restaurant1", "bookTable" ], { "fullName": "Toby Smith", "date": "2024-05-14", "numPeople": 4, "hour": 17 } ], [ "vassago", [ "restaurant2", "orderEverything" ], { "deliveryAddress": "75 Brown Street" } ], [ "marbas", [ "restaurant1", "bookTable" ], { "fullName": "Manfred Taylor", "date": "2024-09-16", "numPeople": 9, "hour": 19 } ], [ "protocolDb1", [ null, "synchronization" ], null ], [ "sitri", [ "skiResort1", "bookRoom" ], { "startDate": "2024-02-28", "endDate": "2024-03-08" } ], [ "bathin", [ "taxi2", "callTaxi" ], { "address": "63 White Street", "time": "15:3" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-07-21", "type": "racing" } ], [ "agares", [ "taxi2", "callTaxi" ], { "address": "91 Taylor Street", "time": "22:14" } ], [ "marbas", [ "restaurant1", "bookTable" ], { "fullName": "Cliff Jones", "date": "2024-05-07", "numPeople": 6, "hour": 19 } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-05-09", "type": "backcountry" } ], [ "samigina", [ "cinema1", "buyTickets" ], { "date": [ "2024-03-17" ], "hour": 20, "numTickets": 5, "movie": "Stand by Me" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-05-08", "type": "backcountry" } ], [ "marbas", [ "restaurant1", "bookTable" ], { "fullName": "Cliff Taylor", "date": "2024-02-28", "numPeople": 4, "hour": 22 } ], [ "bael", [ "taxi2", "callTaxi" ], { "address": "54 Miller Street", "time": "17:20" } ], [ "protocolDb2", [ null, "synchronization" ], null ], [ "amon", [ "skiResort1", "rentSki" ], { "date": "2024-09-03", "type": "backcountry" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-03-04", "type": "racing" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-03-10", "type": "racing" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2023-09-29", "type": "backcountry" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-02-18", "type": "racing" } ], [ "samigina", [ "hotel3", "suggestRestaurant" ], { "date": "2023-10-19" } ], [ "sitri", [ "skiResort1", "bookRoom" ], { "startDate": "2023-10-27", "endDate": "2023-10-31" } ], [ "buer", [ "skiResort1", "rentSki" ], { "date": "2024-01-24", "type": "racing" } ], [ "crocell", [ "cinema1", "availableMovies" ], { "date": [ "2024-03-17" ] } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-02-10", "type": "backcountry" } ], [ "protocolDb3", [ null, "synchronization" ], null ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-06-16", "type": "backcountry" } ], [ "purson", [ "restaurant1", "orderEverything" ], { "deliveryAddress": "10 Williams Street" } ], [ "sitri", [ "hotel1", "bookRoom" ], { "startDate": "2024-04-13", "endDate": "2024-05-03" } ], [ "paimon", [ "skiResort2", "suggestRestaurant" ], { "date": "2024-01-26" } ], [ "shax", [ "skiResort1", "bookRoom" ], { "startDate": "2024-02-12", "endDate": "2024-03-01" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-08-01", "type": "carving" } ], [ "vassago", [ "skiResort1", "bookRoom" ], { "startDate": "2024-02-26", "endDate": "2024-03-04" } ], [ "vassago", [ "skiResort1", "bookRoom" ], { "startDate": "2024-09-05", "endDate": "2024-09-13" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-05-24", "type": "racing" } ], [ "marbas", [ "cinema1", "buyTickets" ], { "date": [ "2024-03-18" ], "hour": 18, "numTickets": 1, "movie": "The Silence of the Lambs" } ], [ "protocolDb1", [ null, "synchronization" ], null ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-03-31", "type": "racing" } ], [ "agares", [ "cinema2", "availableMovies" ], { "date": [ "2024-03-15" ] } ], [ "bael", [ "skiResort2", "suggestRestaurant" ], { "date": "2024-02-04" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2023-10-18", "type": "racing" } ], [ "agares", [ "taxi2", "callTaxi" ], { "address": "33 Williams Street", "time": "9:46" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-06-05", "type": "carving" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-03-21", "type": "carving" } ], [ "bael", [ "skiResort2", "suggestRestaurant" ], { "date": "2023-10-09" } ], [ "ose", [ "skiResort1", "rentSki" ], { "date": "2023-12-26", "type": "backcountry" } ], [ "bune", [ "skiResort2", "rentSki" ], { "date": "2024-08-13", "type": "racing" } ], [ "protocolDb2", [ null, "synchronization" ], null ], [ "sallos", [ "skiResort2", "rentSki" ], { "date": "2024-05-25", "type": "racing" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-07-12", "type": "carving" } ], [ "bael", [ "taxi2", "callTaxi" ], { "address": "40 Williams Street", "time": "18:28" } ], [ "agares", [ "taxi2", "callTaxi" ], { "address": "76 Miller Street", "time": "0:10" } ], [ "vassago", [ "restaurant2", "orderEverything" ], { "deliveryAddress": "65 Johnson Street" } ], [ "vassago", [ "restaurant2", "orderEverything" ], { "deliveryAddress": "62 Johnson Street" } ], [ "vassago", [ "skiResort1", "bookRoom" ], { "startDate": "2024-01-21", "endDate": "2024-02-09" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-07-03", "type": "racing" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-02-04", "type": "racing" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-08-20", "type": "racing" } ], [ "protocolDb3", [ null, "synchronization" ], null ], [ "alloces", [ "cinema1", "availableMovies" ], { "date": [ "2024-03-16" ] } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-01-15", "type": "carving" } ], [ "marbas", [ "restaurant1", "bookTable" ], { "fullName": "Brooke Jones", "date": "2023-12-15", "numPeople": 7, "hour": 23 } ], [ "vassago", [ "restaurant2", "orderEverything" ], { "deliveryAddress": "38 Jones Street" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-04-15", "type": "racing" } ], [ "bael", [ "skiResort2", "suggestRestaurant" ], { "date": "2023-12-16" } ], [ "marax", [ "hotel2", "bookRoom" ], { "startDate": "2024-07-22", "endDate": "2024-08-05" } ], [ "agares", [ "taxi2", "callTaxi" ], { "address": "5 Jones Street", "time": "1:28" } ], [ "valefor", [ "skiResort2", "rentSki" ], { "date": "2024-05-02", "type": "racing" } ], [ "phenex", [ "cinema1", "buyTickets" ], { "date": [ "2024-03-17" ], "hour": 18, "numTickets": 1, "movie": "Stand by Me" } ], [ "protocolDb1", [ null, "synchronization" ], null ], [ "valefor", [ "restaurant3", "bookTable" ], { "fullName": "Valerie Williams", "date": "2024-04-10", "numPeople": 1, "hour": 18 } ], [ "bael", [ "skiResort2", "suggestRestaurant" ], { "date": "2023-12-20" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-06-08", "type": "backcountry" } ], [ "focalor", [ "restaurant2", "bookTable" ], { "fullName": "Toby Williams", "date": "2024-03-27", "numPeople": 6, "hour": 19 } ], [ "paimon", [ "skiResort2", "suggestRestaurant" ], { "date": "2023-12-30" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-07-30", "type": "racing" } ], [ "bathin", [ "trafficServer", "getTraffic" ], { "date": "2024-05-29", "location": "Cairo" } ], [ "vassago", [ "restaurant2", "orderEverything" ], { "deliveryAddress": "60 White Street" } ], [ "valefor", [ "restaurant3", "bookTable" ], { "fullName": "Cliff Smith", "date": "2024-04-02", "numPeople": 6, "hour": 16 } ], [ "agares", [ "taxi2", "callTaxi" ], { "address": "26 White Street", "time": "20:41" } ], [ "protocolDb2", [ null, "synchronization" ], null ], [ "vassago", [ "restaurant2", "orderEverything" ], { "deliveryAddress": "46 Jones Street" } ], [ "agares", [ "taxi2", "callTaxi" ], { "address": "68 Miller Street", "time": "11:40" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-05-22", "type": "carving" } ], [ "agares", [ "taxi2", "callTaxi" ], { "address": "89 Johnson Street", "time": "8:48" } ], [ "bathin", [ "skiResort2", "rentSki" ], { "date": "2024-05-20", "type": "backcountry" } ], [ "agares", [ "taxi2", "callTaxi" ], { "address": "100 Jones Street", "time": "14:20" } ], [ "barbatos", [ "skiResort2", "bookRoom" ], { "startDate": "2023-10-26", "endDate": "2023-11-10" } ], [ "vassago", [ "restaurant2", "orderEverything" ], { "deliveryAddress": "2 Brown Street" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-05-04", "type": "backcountry" } ], [ "bael", [ "taxi2", "callTaxi" ], { "address": "6 Brown Street", "time": "18:33" } ], [ "protocolDb3", [ null, "synchronization" ], null ], [ "bael", [ "skiResort2", "suggestRestaurant" ], { "date": "2024-05-09" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2023-11-05", "type": "backcountry" } ], [ "aim", [ "skiResort2", "rentSki" ], { "date": "2024-04-15", "type": "carving" } ], [ "beleth", [ "hotel1", "bookRoom" ], { "startDate": "2024-03-28", "endDate": "2024-04-05" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-06-02", "type": "backcountry" } ], [ "botis", [ "restaurant2", "orderEverything" ], { "deliveryAddress": "71 Jones Street" } ], [ "marbas", [ "restaurant1", "bookTable" ], { "fullName": "Brooke Miller", "date": "2024-06-04", "numPeople": 3, "hour": 19 } ], [ "marbas", [ "cinema1", "buyTickets" ], { "date": [ "2024-03-16" ], "hour": 20, "numTickets": 1, "movie": "Forrest Gump" } ], [ "marbas", [ "restaurant1", "bookTable" ], { "fullName": "Cliff Davis", "date": "2024-09-15", "numPeople": 9, "hour": 21 } ], [ "samigina", [ "hotel3", "suggestRestaurant" ], { "date": "2024-05-08" } ], [ "protocolDb1", [ null, "synchronization" ], null ], [ "valac", [ "restaurant1", "orderEverything" ], { "deliveryAddress": "70 Miller Street" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-04-12", "type": "racing" } ], [ "samigina", [ "hotel3", "suggestRestaurant" ], { "date": "2024-04-27" } ], [ "valefor", [ "restaurant3", "bookTable" ], { "fullName": "Cliff Smith", "date": "2024-09-19", "numPeople": 6, "hour": 17 } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-08-16", "type": "racing" } ], [ "agares", [ "restaurant3", "bookTable" ], { "fullName": "Laurel Jones", "date": "2024-04-10", "numPeople": 4, "hour": 22 } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-08-02", "type": "backcountry" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2023-10-06", "type": "racing" } ], [ "marax", [ "skiResort1", "bookRoom" ], { "startDate": "2023-11-22", "endDate": "2023-12-01" } ], [ "vassago", [ "skiResort1", "bookRoom" ], { "startDate": "2024-07-06", "endDate": "2024-07-25" } ], [ "protocolDb2", [ null, "synchronization" ], null ], [ "valefor", [ "restaurant3", "bookTable" ], { "fullName": "Brooke Williams", "date": "2023-12-14", "numPeople": 10, "hour": 23 } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2023-10-03", "type": "racing" } ], [ "buer", [ "skiResort1", "rentSki" ], { "date": "2024-01-22", "type": "backcountry" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-07-17", "type": "backcountry" } ], [ "agares", [ "taxi2", "callTaxi" ], { "address": "72 Brown Street", "time": "8:0" } ], [ "valefor", [ "restaurant3", "bookTable" ], { "fullName": "Maxwell Davis", "date": "2024-04-19", "numPeople": 5, "hour": 22 } ], [ "barbatos", [ "skiResort2", "bookRoom" ], { "startDate": "2023-09-30", "endDate": "2023-10-18" } ], [ "vepar", [ "skiResort2", "suggestRestaurant" ], { "date": "2023-11-20" } ], [ "vassago", [ "restaurant2", "orderEverything" ], { "deliveryAddress": "73 Taylor Street" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-08-21", "type": "backcountry" } ], [ "protocolDb3", [ null, "synchronization" ], null ], [ "bael", [ "taxi2", "callTaxi" ], { "address": "70 Smith Street", "time": "23:40" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-08-21", "type": "carving" } ], [ "sabnock", [ "taxi1", "callTaxi" ], { "address": "49 Smith Street", "time": "0:20" } ], [ "amon", [ "hotel1", "bookRoom" ], { "startDate": "2024-05-30", "endDate": "2024-06-03" } ], [ "vassago", [ "restaurant2", "orderEverything" ], { "deliveryAddress": "89 Brown Street" } ], [ "vassago", [ "restaurant2", "orderEverything" ], { "deliveryAddress": "51 Davis Street" } ], [ "agares", [ "taxi2", "callTaxi" ], { "address": "85 Williams Street", "time": "21:56" } ], [ "amdusias", [ "cinema2", "availableMovies" ], { "date": [ "2024-03-18" ] } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-01-04", "type": "racing" } ], [ "valefor", [ "restaurant3", "bookTable" ], { "fullName": "Cliff White", "date": "2024-03-03", "numPeople": 9, "hour": 18 } ], [ "protocolDb1", [ null, "synchronization" ], null ], [ "naberius", [ "cinema2", "availableMovies" ], { "date": [ "2024-03-15" ] } ], [ "samigina", [ "hotel3", "suggestRestaurant" ], { "date": "2023-12-10" } ], [ "glasya-labolas", [ "restaurant3", "bookTable" ], { "fullName": "Brooke Wilson", "date": "2024-06-04", "numPeople": 8, "hour": 23 } ], [ "agares", [ "taxi2", "callTaxi" ], { "address": "12 Johnson Street", "time": "11:39" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-09-21", "type": "carving" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-05-22", "type": "backcountry" } ], [ "samigina", [ "hotel3", "suggestRestaurant" ], { "date": "2024-02-13" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-09-02", "type": "racing" } ], [ "gaap", [ "hotel3", "bookRoom" ], { "startDate": "2024-02-15", "endDate": "2024-02-29" } ], [ "ipos", [ "restaurant1", "openingTimes" ], {} ], [ "protocolDb2", [ null, "synchronization" ], null ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2023-11-24", "type": "racing" } ], [ "agares", [ "cinema2", "availableMovies" ], { "date": [ "2024-03-18" ] } ], [ "eligos", [ "taxi1", "callTaxi" ], { "address": "82 Johnson Street", "time": "19:31" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-08-28", "type": "backcountry" } ], [ "agares", [ "cinema2", "availableMovies" ], { "date": [ "2024-03-18" ] } ], [ "paimon", [ "skiResort2", "suggestRestaurant" ], { "date": "2024-07-16" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-02-18", "type": "backcountry" } ], [ "bael", [ "skiResort2", "suggestRestaurant" ], { "date": "2024-07-12" } ], [ "beleth", [ "hotel1", "bookRoom" ], { "startDate": "2024-08-15", "endDate": "2024-08-21" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2023-11-03", "type": "racing" } ], [ "protocolDb3", [ null, "synchronization" ], null ], [ "bael", [ "skiResort2", "suggestRestaurant" ], { "date": "2024-01-27" } ], [ "marbas", [ "cinema1", "buyTickets" ], { "date": [ "2024-03-15" ], "hour": 20, "numTickets": 2, "movie": "Forrest Gump" } ], [ "marbas", [ "restaurant1", "bookTable" ], { "fullName": "Brooke Miller", "date": "2024-04-02", "numPeople": 1, "hour": 23 } ], [ "guison", [ "cinema1", "availableMovies" ], { "date": [ "2024-03-17" ] } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-02-02", "type": "carving" } ], [ "samigina", [ "hotel3", "suggestRestaurant" ], { "date": "2023-10-05" } ], [ "malphas", [ "cinema2", "availableMovies" ], { "date": [ "2024-03-15" ] } ], [ "vassago", [ "restaurant2", "orderEverything" ], { "deliveryAddress": "81 Jones Street" } ], [ "agares", [ "cinema2", "availableMovies" ], { "date": [ "2024-03-15" ] } ], [ "vassago", [ "restaurant2", "orderEverything" ], { "deliveryAddress": "58 Williams Street" } ], [ "protocolDb1", [ null, "synchronization" ], null ], [ "samigina", [ "cinema1", "buyTickets" ], { "date": [ "2024-03-16" ], "hour": 18, "numTickets": 4, "movie": "The Silence of the Lambs" } ], [ "bael", [ "skiResort2", "suggestRestaurant" ], { "date": "2023-12-11" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-05-06", "type": "racing" } ], [ "vassago", [ "skiResort1", "bookRoom" ], { "startDate": "2024-05-12", "endDate": "2024-05-26" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-04-04", "type": "racing" } ], [ "agares", [ "cinema2", "availableMovies" ], { "date": [ "2024-03-18" ] } ], [ "barbatos", [ "skiResort2", "bookRoom" ], { "startDate": "2024-06-12", "endDate": "2024-06-24" } ], [ "agares", [ "taxi2", "callTaxi" ], { "address": "47 Davis Street", "time": "15:9" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-02-04", "type": "backcountry" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-03-23", "type": "racing" } ], [ "protocolDb2", [ null, "synchronization" ], null ], [ "bael", [ "taxi2", "callTaxi" ], { "address": "57 Brown Street", "time": "1:22" } ], [ "vual", [ "restaurant2", "bookTable" ], { "fullName": "Laurel Davis", "date": "2024-06-21", "numPeople": 4, "hour": 19 } ], [ "bael", [ "skiResort2", "suggestRestaurant" ], { "date": "2023-10-07" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-06-27", "type": "backcountry" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-05-24", "type": "racing" } ], [ "agares", [ "cinema2", "availableMovies" ], { "date": [ "2024-03-18" ] } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-09-15", "type": "backcountry" } ], [ "samigina", [ "hotel3", "suggestRestaurant" ], { "date": "2024-07-28" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-03-24", "type": "carving" } ], [ "buer", [ "skiResort1", "rentSki" ], { "date": "2024-03-20", "type": "racing" } ], [ "protocolDb3", [ null, "synchronization" ], null ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2023-11-29", "type": "backcountry" } ], [ "amon", [ "skiResort1", "rentSki" ], { "date": "2024-05-30", "type": "racing" } ], [ "marbas", [ "skiResort1", "bookRoom" ], { "startDate": "2024-04-18", "endDate": "2024-04-24" } ], [ "agares", [ "taxi2", "callTaxi" ], { "address": "80 Jones Street", "time": "12:22" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-01-07", "type": "racing" } ], [ "bael", [ "skiResort2", "suggestRestaurant" ], { "date": "2024-06-17" } ], [ "berith", [ "restaurant1", "orderEverything" ], { "deliveryAddress": "47 Brown Street" } ], [ "guison", [ "cinema1", "availableMovies" ], { "date": [ "2024-03-15" ] } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-04-17", "type": "racing" } ], [ "samigina", [ "restaurant3", "bookTable" ], { "fullName": "Manfred Johnson", "date": "2024-04-24", "numPeople": 5, "hour": 21 } ], [ "protocolDb1", [ null, "synchronization" ], null ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-03-13", "type": "racing" } ], [ "aim", [ "skiResort2", "rentSki" ], { "date": "2024-08-17", "type": "carving" } ], [ "agares", [ "cinema2", "availableMovies" ], { "date": [ "2024-03-15" ] } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2023-12-21", "type": "backcountry" } ], [ "zepar", [ "restaurant1", "openingTimes" ], {} ], [ "bifrons", [ "skiResort1", "bookRoom" ], { "startDate": "2024-08-10", "endDate": "2024-08-20" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-02-26", "type": "backcountry" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-03-08", "type": "backcountry" } ], [ "ephippas", [ "cinema1", "buyTickets" ], { "date": [ "2024-03-15" ], "hour": 20, "numTickets": 4, "movie": "Stand by Me" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2023-11-28", "type": "backcountry" } ], [ "protocolDb2", [ null, "synchronization" ], null ], [ "sallos", [ "restaurant2", "openingTimes" ], {} ], [ "agares", [ "taxi2", "callTaxi" ], { "address": "45 White Street", "time": "18:2" } ], [ "ipos", [ "hotel3", "bookRoom" ], { "startDate": "2024-02-27", "endDate": "2024-03-15" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-02-07", "type": "backcountry" } ], [ "barbatos", [ "cinema2", "buyTickets" ], { "date": [ "2024-03-18" ], "hour": 20, "numTickets": 5, "movie": "Forrest Gump" } ], [ "balam", [ "cinema1", "availableMovies" ], { "date": [ "2024-03-16" ] } ], [ "bathin", [ "taxi2", "callTaxi" ], { "address": "84 White Street", "time": "3:18" } ], [ "bael", [ "skiResort2", "suggestRestaurant" ], { "date": "2024-07-03" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2023-12-24", "type": "racing" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-04-30", "type": "backcountry" } ], [ "protocolDb3", [ null, "synchronization" ], null ], [ "marbas", [ "cinema1", "buyTickets" ], { "date": [ "2024-03-16" ], "hour": 18, "numTickets": 4, "movie": "Forrest Gump" } ], [ "samigina", [ "hotel3", "suggestRestaurant" ], { "date": "2023-11-28" } ], [ "bael", [ "taxi2", "callTaxi" ], { "address": "2 Smith Street", "time": "18:43" } ], [ "bael", [ "skiResort2", "suggestRestaurant" ], { "date": "2024-04-29" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-02-15", "type": "racing" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-07-23", "type": "racing" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-08-30", "type": "backcountry" } ], [ "agares", [ "taxi2", "callTaxi" ], { "address": "7 Smith Street", "time": "8:55" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2023-12-18", "type": "carving" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-05-31", "type": "carving" } ], [ "protocolDb1", [ null, "synchronization" ], null ], [ "agares", [ "taxi2", "callTaxi" ], { "address": "87 Williams Street", "time": "3:53" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2023-10-28", "type": "racing" } ], [ "focalor", [ "skiResort2", "suggestRestaurant" ], { "date": "2024-02-11" } ], [ "haagenti", [ "trafficServer", "getTraffic" ], { "date": "2024-03-30", "location": "Cairo" } ], [ "ornias", [ "hotel2", "bookRoom" ], { "startDate": "2024-06-25", "endDate": "2024-07-13" } ], [ "marbas", [ "restaurant1", "bookTable" ], { "fullName": "Kara Brown", "date": "2024-04-21", "numPeople": 10, "hour": 21 } ], [ "bael", [ "skiResort2", "suggestRestaurant" ], { "date": "2024-01-23" } ], [ "forneus", [ "cinema1", "availableMovies" ], { "date": [ "2024-03-17" ] } ], [ "purson", [ "trafficServer", "getTraffic" ], { "date": "2023-10-11", "location": "Rome" } ], [ "agares", [ "taxi2", "callTaxi" ], { "address": "15 Taylor Street", "time": "12:51" } ], [ "protocolDb2", [ null, "synchronization" ], null ], [ "valefor", [ "restaurant3", "bookTable" ], { "fullName": "Madge Williams", "date": "2023-12-21", "numPeople": 8, "hour": 19 } ], [ "agares", [ "cinema2", "availableMovies" ], { "date": [ "2024-03-18" ] } ], [ "agares", [ "taxi2", "callTaxi" ], { "address": "36 Jones Street", "time": "2:9" } ], [ "agares", [ "taxi2", "callTaxi" ], { "address": "99 Davis Street", "time": "18:2" } ], [ "furfur", [ "cinema1", "availableMovies" ], { "date": [ "2024-03-16" ] } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2023-10-30", "type": "racing" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-01-31", "type": "backcountry" } ], [ "samigina", [ "hotel3", "suggestRestaurant" ], { "date": "2024-04-11" } ], [ "buer", [ "skiResort1", "rentSki" ], { "date": "2024-03-14", "type": "racing" } ], [ "vassago", [ "restaurant2", "orderEverything" ], { "deliveryAddress": "86 Davis Street" } ], [ "protocolDb3", [ null, "synchronization" ], null ], [ "agares", [ "taxi2", "callTaxi" ], { "address": "51 Wilson Street", "time": "19:32" } ], [ "barbatos", [ "cinema2", "buyTickets" ], { "date": [ "2024-03-16" ], "hour": 20, "numTickets": 6, "movie": "Forrest Gump" } ], [ "amon", [ "skiResort1", "rentSki" ], { "date": "2024-05-09", "type": "backcountry" } ], [ "agares", [ "taxi2", "callTaxi" ], { "address": "42 Taylor Street", "time": "10:42" } ], [ "vassago", [ "restaurant2", "orderEverything" ], { "deliveryAddress": "85 Miller Street" } ], [ "eligos", [ "restaurant1", "orderEverything" ], { "deliveryAddress": "12 Brown Street" } ], [ "onoskelis", [ "skiResort2", "suggestRestaurant" ], { "date": "2024-01-10" } ], [ "belial", [ "restaurant3", "openingTimes" ], {} ], [ "agares", [ "taxi2", "callTaxi" ], { "address": "56 Jones Street", "time": "19:24" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-03-25", "type": "racing" } ], [ "protocolDb1", [ null, "synchronization" ], null ], [ "agares", [ "taxi2", "callTaxi" ], { "address": "98 Brown Street", "time": "17:46" } ], [ "barbatos", [ "skiResort2", "bookRoom" ], { "startDate": "2024-02-27", "endDate": "2024-02-29" } ], [ "bael", [ "skiResort2", "suggestRestaurant" ], { "date": "2024-05-29" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-02-02", "type": "carving" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-07-24", "type": "backcountry" } ], [ "astaroth", [ "weatherServer", "queryWeather" ], { "date": "2024-03-28", "location": "London, UK" } ], [ "valefor", [ "restaurant3", "bookTable" ], { "fullName": "Brooke Davis", "date": "2024-01-22", "numPeople": 8, "hour": 18 } ], [ "agares", [ "taxi2", "callTaxi" ], { "address": "96 Smith Street", "time": "19:24" } ], [ "gremory", [ "restaurant1", "orderEverything" ], { "deliveryAddress": "16 Davis Street" } ], [ "agares", [ "cinema2", "availableMovies" ], { "date": [ "2024-03-15" ] } ], [ "protocolDb2", [ null, "synchronization" ], null ], [ "agares", [ "taxi2", "callTaxi" ], { "address": "49 Miller Street", "time": "5:41" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2023-10-16", "type": "carving" } ], [ "paimon", [ "skiResort2", "suggestRestaurant" ], { "date": "2023-11-15" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-02-08", "type": "racing" } ], [ "halphas", [ "skiResort2", "rentSki" ], { "date": "2024-08-25", "type": "racing" } ], [ "samigina", [ "restaurant3", "bookTable" ], { "fullName": "Maxwell Miller", "date": "2023-09-29", "numPeople": 4, "hour": 21 } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-02-26", "type": "racing" } ], [ "bael", [ "taxi2", "callTaxi" ], { "address": "71 Johnson Street", "time": "16:6" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-07-20", "type": "racing" } ], [ "marax", [ "trafficServer", "getTraffic" ], { "date": "2024-04-26", "location": "Zurich" } ], [ "protocolDb3", [ null, "synchronization" ], null ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-04-08", "type": "carving" } ], [ "vual", [ "trafficServer", "getTraffic" ], { "date": "2023-10-29", "location": "Tokyo" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2023-11-10", "type": "carving" } ], [ "asmoday", [ "skiResort1", "rentSki" ], { "date": "2024-01-23", "type": "backcountry" } ], [ "zepar", [ "weatherServer", "queryWeather" ], { "date": "2023-09-24", "location": "Paris, France" } ], [ "bael", [ "skiResort2", "suggestRestaurant" ], { "date": "2023-10-27" } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2024-03-16", "type": "carving" } ], [ "bael", [ "taxi2", "callTaxi" ], { "address": "45 Jones Street", "time": "3:56" } ], [ "agares", [ "cinema2", "availableMovies" ], { "date": [ "2024-03-15" ] } ], [ "bael", [ "skiResort2", "rentSki" ], { "date": "2023-10-30", "type": "backcountry" } ], [ "protocolDb1", [ null, "synchronization" ], null ] ] ================================================ FILE: agents/common/core.py ================================================ from enum import Enum class Suitability(str, Enum): ADEQUATE = 'adequate' INADEQUATE = 'inadequate' PROBABLY_INADEQUATE = 'probably_inadequate' UNKNOWN = 'unknown' ================================================ FILE: agents/protocol_db/main.py ================================================ import sys sys.path.append('.') import dotenv dotenv.load_dotenv() import os from pathlib import Path if os.environ.get('STORAGE_PATH') is None: os.environ['STORAGE_PATH'] = str(Path().parent / 'storage' / 'protocol_db') from flask import Flask, request import requests as request_manager import json from utils import compute_hash, shared_config app = Flask(__name__) OTHER_DBS = [] PROTOCOLS = {} @app.route('/', methods=['GET']) def main(): return json.dumps({ 'status': 'success', 'protocols' : [ { 'id': protocol_id, 'name': protocol_data['name'], 'description': protocol_data['description'], } for protocol_id, protocol_data in PROTOCOLS.items() ] }) @app.route('/', methods=['POST']) def add_protocol(): data = request.get_json() if 'protocol' not in data: return json.dumps({ 'status': 'error', 'message': 'No protocol provided.' }) protocol = data['protocol'] hashed_protocol = compute_hash(protocol) if hashed_protocol in PROTOCOLS: # No-op return json.dumps({ 'status' : 'success' }) PROTOCOLS[hashed_protocol] = data print('Added protocol:', hashed_protocol) save_memory() return json.dumps({ 'status': 'success' }) # route for each protocol @app.route('/protocol', methods=['GET']) def get_protocol(): protocol_id = request.args.get('id') print('Received request for protocol:', protocol_id) print('Current keys:', PROTOCOLS.keys()) print('Do I have the protocol?', protocol_id in PROTOCOLS) if protocol_id not in PROTOCOLS: return json.dumps({ 'status': 'error', 'message': 'Protocol not found.' }) # No JSON return PROTOCOLS[protocol_id]['protocol'] @app.route('/metadata', methods=['GET']) def get_metadata(): protocol_id = request.args.get('id') print('Received request for metadata:', protocol_id) print('Current keys:', PROTOCOLS.keys()) print('Do I have the protocol?', protocol_id in PROTOCOLS) if protocol_id not in PROTOCOLS: return json.dumps({ 'status': 'error', 'message': 'Protocol not found.' }) return json.dumps({ 'status': 'success', 'metadata': { 'name': PROTOCOLS[protocol_id]['name'], 'description': PROTOCOLS[protocol_id]['description'] } }) @app.route('/synchronize', methods=['POST']) def trigger_share(): for other_db in OTHER_DBS: print('Checking known protocols of:', other_db) known_protocols = request_manager.get(f'{other_db}/', timeout=shared_config('timeout')).json() for protocol_id, protocol_data in PROTOCOLS.items(): if protocol_id not in known_protocols: print('Sharing protocol:', protocol_id) request_manager.post(f'{other_db}/', json=protocol_data, timeout=shared_config('timeout')) return json.dumps({ 'status': 'success' }) def load_config(): with open('config.json') as f: config = json.load(f) with open('node_urls.json') as f: node_urls = json.load(f) agent_id = os.environ.get('AGENT_ID') db_config = config['protocolDbs'][agent_id] for peer_id in db_config['peers']: OTHER_DBS.append(node_urls[peer_id]) def load_memory(): PROTOCOLS.clear() storage_path = Path(os.environ.get('STORAGE_PATH')) memory_file = storage_path / 'memory.json' if not memory_file.exists(): print('No memory file found, using empty memory') return with open(memory_file, 'r') as f: memory = json.load(f) for protocol_id, protocol_data in memory.items(): PROTOCOLS[protocol_id] = protocol_data print('Loaded memory:', PROTOCOLS) def save_memory(): storage_path = Path(os.environ.get('STORAGE_PATH')) if not storage_path.exists(): storage_path.mkdir(parents=True) memory_file = storage_path / 'memory.json' with open(memory_file, 'w') as f: json.dump(PROTOCOLS, f) def init(): load_config() load_memory() init() ================================================ FILE: agents/server/config.py ================================================ import json import os import random import requests as request_manager import databases.mongo as mongo import databases.sql as sql from toolformers.base import Tool, StringParameter, EnumParameter import mocks.mock_tools as mock_tools from utils import get_query_id, shared_config TOOLS = [] ADDITIONAL_INFO = '' NODE_URLS = {} def get_additional_info(): return ADDITIONAL_INFO def add_mongo_database(external_name, schema): global ADDITIONAL_INFO # TODO: Support hot-loading? mongo.create_database_from_schema(external_name, schema) ADDITIONAL_INFO += f'You have access to a MongoDB database.\n' ADDITIONAL_INFO += 'The database has the following collections (with schemas):\n\n' for collection_name, collection_schema in schema['collections'].items(): ADDITIONAL_INFO += f'=={collection_name}==\n' if 'initialValues' in collection_schema: del collection_schema['initialValues'] ADDITIONAL_INFO += json.dumps(collection_schema, indent=2) + '\n\n' def add_mongo_tools(server_name): def insert_element(collection, doc): doc = json.loads(doc) mongo.insert_one(server_name, collection, doc) return 'Done' insert_element_tool = Tool('insert_into_database', 'Insert into a database (MongoDB).', [ StringParameter('collection', 'The collection to insert into', True), StringParameter('doc', 'The document to insert, as a formatted JSON string for MongoDB', True) ], insert_element) def query_database(collection, query): query = json.loads(query) output = mongo.query_database(server_name, collection, query) print('MongoDB query database output:', output) return json.dumps(output) find_in_database_tool = Tool('find_in_database', 'Find in a database (MongoDB). Returns a JSON formatted string with the result.', [ StringParameter('collection', 'The collection to query', True), StringParameter('query', 'The query to run, as a formatted JSON string for MongoDB', True) ], query_database) def update_element(collection, query, update): query = json.loads(query) update = json.loads(update) mongo.update_one(server_name, collection, query, update) return 'Done' update_element_tool = Tool('update_one_in_database', 'Update an element in a database (MongoDB).', [ StringParameter('collection', 'The collection to query', True), StringParameter('query', 'The query to run, as a formatted JSON string for MongoDB', True), StringParameter('update', 'The update to run, as a formatted JSON string for MongoDB. Remember the $ operator (e.g. {$set : {...}})', True) ], update_element) def delete_element(collection, query): query = json.loads(query) mongo.delete_one(server_name, collection, query) return 'Done' delete_element_tool = Tool('delete_element_in_database', 'Delete an element in a database (MongoDB).', [ StringParameter('database', 'The database to query', True), StringParameter('collection', 'The collection to query', True), StringParameter('query', 'The query to run, as a formatted JSON string for MongoDB', True) ], delete_element) TOOLS.append(insert_element_tool) TOOLS.append(find_in_database_tool) TOOLS.append(update_element_tool) TOOLS.append(delete_element_tool) # TODO: Test insert and delete tools def add_sql_database(table_schemas, server_name): global ADDITIONAL_INFO name_mappings = sql.create_database_from_schema(table_schemas, server_name) global ADDITIONAL_INFO ADDITIONAL_INFO += f'\n\nYou have access to a Microsoft SQL Server database. You can run SQL queries on the database. The database has the following tables:\n\n' for table_name, table_schema in table_schemas.items(): ADDITIONAL_INFO += f'=={table_name}==\n' ADDITIONAL_INFO += f'Description: {table_schema["description"]}\n' ADDITIONAL_INFO += 'Columns:\n' for column_name, column_data in table_schema['columns'].items(): ADDITIONAL_INFO += f'\n- {column_name}: {column_data}' constraints = table_schema.get('constraints', []) if len(constraints) > 0: print('Constraints:', constraints) ADDITIONAL_INFO += '\n\nConstraints:\n' for constraint in constraints: ADDITIONAL_INFO += f'\n- {constraint}' extra_table_schema_info = table_schema.get('extraInfo', '') if extra_table_schema_info: ADDITIONAL_INFO += '\n\nOther info:' + extra_table_schema_info for starting_value in table_schema.get('initialValues', []): sql.insert(table_name, server_name, starting_value) ADDITIONAL_INFO += '\n\n========\n\n' ADDITIONAL_INFO += '\n' ADDITIONAL_INFO += 'For security reasons, you are not allowed to create other tables or modify the schema of the existing tables.\n' ADDITIONAL_INFO += 'Remember: Microsoft SQL Server uses single quotes (\') for literals!' return name_mappings def add_sql_tools(name_mappings): def run_query(query): for internal_table_name, external_table_name in name_mappings.items(): query = query.replace(internal_table_name, external_table_name) # Some models (especially Gemini) really struggle with escaping, adding escaping where it's not necessary query = query.replace("\\'", "'") print('Running SQL query:', query) response = sql.run_query(query) print('SQL Response:', response) return response tool_description = 'Run a Microsoft SQL Server query. Returns a list of results, where each element is a dictionary ' \ 'with the column names as keys. If the query does not return any results, an empty list is returned.' query_tool = Tool('run_sql_query', tool_description, [ StringParameter('query', 'The query to run', True) ], run_query) TOOLS.append(query_tool) def prepare_mock_tool(tool_schema, internal_name, schema_name): print('Internal name:', internal_name) print('Tool schema:', tool_schema) input_schema = tool_schema['input'] required_parameter_names = input_schema['required'] parameters = [] for parameter_name, parameter_data in input_schema['properties'].items(): if 'enum' in parameter_data: parameters.append(EnumParameter(parameter_name, parameter_data['description'], parameter_data['enum'], parameter_name in required_parameter_names)) elif parameter_data['type'] == 'string': parameters.append(StringParameter(parameter_name, parameter_data['description'], parameter_name in required_parameter_names)) else: raise ValueError('Unknown parameter type:', parameter_data['type']) def run_mock_tool(*args, **kwargs): print('Running mock tool:', internal_name, 'Schema name:', schema_name, args, kwargs) if schema_name not in mock_tools.__dict__: raise ValueError('Unknown mock tool schema:', schema_name) response = mock_tools.__dict__[schema_name](*args, **kwargs) print(f'Mock tool {internal_name} response:', response) return response mock_tool = Tool(internal_name, tool_schema['description'], parameters, run_mock_tool, output_schema=tool_schema['output']) return mock_tool def prepare_external_tool(tool_schema, internal_name, external_server_name): input_schema = tool_schema['input'] required_parameter_names = input_schema['required'] parameters = [] for parameter_name, parameter_data in input_schema['properties'].items(): if 'enum' in parameter_data: parameters.append(EnumParameter(parameter_name, parameter_data['description'], parameter_data['enum'], parameter_name in required_parameter_names)) elif parameter_data['type'] == 'string': parameters.append(StringParameter(parameter_name, parameter_data['description'], parameter_name in required_parameter_names)) else: raise ValueError('Unknown parameter type:', parameter_data['type']) helper_id = os.environ.get('AGENT_ID') + '_helper' helper_url = NODE_URLS[helper_id] def run_external_tool(*args, **kwargs): for i, arg in enumerate(args): argument_name = parameters[i].name kwargs[argument_name] = arg # TODO: Check that the parameter names are correct? print('Running external tool:', internal_name, kwargs) query_parameters = { 'type' : internal_name, # TODO: Use the schema name instead? Must be matched on the other side 'data' : kwargs, 'targetServer' : external_server_name, 'queryId': get_query_id() } response = request_manager.post(helper_url + '/customRun', json=query_parameters, timeout=shared_config('timeout')) print(f'Response from external tool {internal_name}:', response.text) if response.status_code == 200: parsed_response = json.loads(response.text) return parsed_response #if parsed_response['status'] == 'success': # return parsed_response['body'] return 'Failed to call the tool: ' + response.text external_tool = Tool(internal_name, tool_schema['description'], parameters, run_external_tool, output_schema=tool_schema['output']) return external_tool def load_config(server_name): global ADDITIONAL_INFO with open('config.json') as f: config = json.load(f) with open('node_urls.json') as f: node_urls = json.load(f) for node_id, node_url in node_urls.items(): NODE_URLS[node_id] = node_url server_config = config['servers'][server_name] ADDITIONAL_INFO += 'This is the description of the service provided by the server:\n====\n' ADDITIONAL_INFO += server_config['description'] for action_description in server_config['actionDescriptions']: ADDITIONAL_INFO += f'\n- {action_description}' ADDITIONAL_INFO += '\n====\n\n' if server_config['internalDbSchema'] is not None: db_config = config['dbSchemas'][server_config['internalDbSchema']] if db_config['dbType'] == 'mongo': add_mongo_database(server_name, db_config) add_mongo_tools(server_name) elif db_config['dbType'] == 'sql': name_mappings = add_sql_database(db_config['tables'], server_name) add_sql_tools(name_mappings) else: raise ValueError('Unknown database type:', db_config['dbType']) for internal_name, schema_name in server_config.get('mockTools', {}).items(): tool_schema = config['toolSchemas'][schema_name] tool = prepare_mock_tool(tool_schema, internal_name, schema_name) TOOLS.append(tool) for internal_name, external_tool_config in server_config.get('externalTools', {}).items(): schema_name = external_tool_config['schema'] tool_schema = config['toolSchemas'][schema_name] tool_server = external_tool_config['server'] tool = prepare_external_tool(tool_schema, internal_name, tool_server) TOOLS.append(tool) print('Final additional info:', ADDITIONAL_INFO) print('Final tools:', TOOLS) ================================================ FILE: agents/server/main.py ================================================ import sys sys.path.append('.') import dotenv dotenv.load_dotenv() from pathlib import Path import os if os.environ.get('STORAGE_PATH') is None: os.environ['STORAGE_PATH'] = str(Path().parent / 'storage' / 'server') import json from threading import Lock import traceback from flask import Flask, request from agents.common.core import Suitability from agents.server.memory import PROTOCOL_INFOS, register_new_protocol, has_implementation, get_num_conversations, increment_num_conversations, has_implementation, add_routine, load_memory, save_memory from utils import load_protocol_document, execute_routine, download_and_verify_protocol, use_query_id from specialized_toolformers.responder import reply_to_query from specialized_toolformers.protocol_checker import check_protocol_for_tools from specialized_toolformers.programmer import write_routine_for_tools from specialized_toolformers.negotiator import handle_negotiation_for_tools from agents.server.config import TOOLS, get_additional_info, load_config app = Flask(__name__) NUM_CONVERSATIONS_FOR_ROUTINE = -1# 1e6 NUM_CONVERSATIONS_FOR_PROTOCOL = 5 #1e6 no_protocol_conversation_counter = 0 mutex = Lock() def call_implementation(protocol_hash, query): base_folder = Path(os.environ.get('STORAGE_PATH')) / 'routines' try: output = execute_routine(base_folder, protocol_hash, query, TOOLS) return { 'status': 'success', 'body': output } except Exception as e: print(traceback.print_exception(type(e), e, e.__traceback__)) print('Error executing routine:', e) print('Falling back to responder') return reply_to_query(query, protocol_hash, TOOLS, get_additional_info()) def handle_query_suitable(protocol_hash, query): increment_num_conversations(protocol_hash) if has_implementation(protocol_hash): return call_implementation(protocol_hash, query) elif get_num_conversations(protocol_hash) >= NUM_CONVERSATIONS_FOR_ROUTINE: # We've used this protocol enough times to justify writing a routine base_folder = Path(os.environ.get('STORAGE_PATH')) / 'protocol_documents' protocol_document = load_protocol_document(base_folder, protocol_hash) implementation = write_routine_for_tools(TOOLS, protocol_document, get_additional_info()) add_routine(protocol_hash, implementation) return call_implementation(protocol_hash, query) else: print('Calling with protocol_hash. Using tools:', TOOLS) return reply_to_query(query, protocol_hash, TOOLS, get_additional_info()) def handle_negotiation(raw_query): global no_protocol_conversation_counter no_protocol_conversation_counter = 0 raw_query = json.loads(raw_query) conversation_id = raw_query.get('conversationId', None) query = raw_query['body'] reply, conversation_id = handle_negotiation_for_tools(query, conversation_id, TOOLS, get_additional_info()) raw_reply = { 'conversationId': conversation_id, 'body': reply } return { 'status': 'success', 'body': json.dumps(raw_reply) } def handle_query(protocol_hash, protocol_sources, query): if protocol_hash is None: print('No protocol hash provided. Using TOOLS:', TOOLS) global no_protocol_conversation_counter no_protocol_conversation_counter += 1 return reply_to_query(query, None, TOOLS, get_additional_info()) if protocol_hash == 'negotiation': # Special protocol, default to human-written routine return handle_negotiation(query) if has_implementation(protocol_hash): return call_implementation(protocol_hash, query) if protocol_hash in PROTOCOL_INFOS: if PROTOCOL_INFOS[protocol_hash]['suitability'] == Suitability.UNKNOWN: # Determine if we can support this protocol base_folder = Path(os.environ.get('STORAGE_PATH')) / 'protocol_documents' protocol_document = load_protocol_document(base_folder, protocol_hash) if check_protocol_for_tools(protocol_document, TOOLS): PROTOCOL_INFOS[protocol_hash]['suitability'] = Suitability.ADEQUATE else: PROTOCOL_INFOS[protocol_hash]['suitability'] = Suitability.INADEQUATE save_memory() if PROTOCOL_INFOS[protocol_hash]['suitability'] == Suitability.ADEQUATE: return handle_query_suitable(protocol_hash, query) else: return { 'status': 'error', 'message': 'Protocol not suitable.' } else: print('Protocol sources:', protocol_sources) for protocol_source in protocol_sources: protocol_document = download_and_verify_protocol(protocol_hash, protocol_source) if protocol_document is not None: register_new_protocol(protocol_hash, protocol_source, protocol_document) is_suitable = check_protocol_for_tools(protocol_document, TOOLS) if is_suitable: PROTOCOL_INFOS[protocol_hash]['suitability'] = Suitability.ADEQUATE else: PROTOCOL_INFOS[protocol_hash]['suitability'] = Suitability.INADEQUATE save_memory() if is_suitable: return handle_query_suitable(protocol_hash, query) return { 'status': 'error', 'message': 'No valid protocol source provided.' } @app.route("/", methods=['POST']) def main(): data = request.get_json() protocol_hash = data.get('protocolHash', None) protocol_sources = data.get('protocolSources', []) query_id = data.get('queryId', None) if query_id is None: print('No query ID provided.') else: print('Query ID:', query_id) with mutex: with use_query_id(query_id): response = handle_query(protocol_hash, protocol_sources, data['body']) print('Final response:', response) return response @app.route("/wellknown", methods=['GET']) def wellknown(): return { 'status': 'success', 'protocols': { protocol_hash: [PROTOCOL_INFOS[protocol_hash]['source']] for protocol_hash in PROTOCOL_INFOS if PROTOCOL_INFOS[protocol_hash]['suitability'] == Suitability.ADEQUATE } } # Note: In a real-world case, the server would already register the protocol as part of the negotiation process @app.route("/registerNegotiatedProtocol", methods=['POST']) def register_negotiated_protocol(): data = request.get_json() protocol_hash = data['protocolHash'] protocol_sources = data['protocolSources'] protocol_document = None for protocol_source in protocol_sources: protocol_document = download_and_verify_protocol(protocol_hash, protocol_source) if protocol_document is not None: break if protocol_document is None: return { 'status': 'error', 'message': 'No valid protocol source provided.' } register_new_protocol(protocol_hash, protocol_source, protocol_document) PROTOCOL_INFOS[protocol_hash]['suitability'] = Suitability.ADEQUATE save_memory() return { 'status': 'success' } # Determines if the user should start a negotiation instead of a regular conversation # Note: in a real-world case, this would be determined independently by the server @app.route("/requiresNegotiation", methods=['GET']) def requires_negotiation(): return { 'status': 'success', 'requiresNegotiation': no_protocol_conversation_counter >= NUM_CONVERSATIONS_FOR_PROTOCOL } def init(): load_config(os.environ.get('AGENT_ID')) load_memory() init() ================================================ FILE: agents/server/memory.py ================================================ import json import os from pathlib import Path from agents.common.core import Suitability from utils import save_protocol_document, save_routine PROTOCOL_INFOS = {} def load_memory(): storage_path = Path(os.environ.get('STORAGE_PATH')) / 'memory.json' if not Path(storage_path).exists(): print('No memory found. Using default blank memory.') PROTOCOL_INFOS.clear() return with open(storage_path, 'r') as f: data = json.load(f) PROTOCOL_INFOS.clear() PROTOCOL_INFOS.update(data['protocol_infos']) print('Loaded memory:', PROTOCOL_INFOS.keys()) def save_memory(): storage_path = Path(os.environ.get('STORAGE_PATH')) / 'memory.json' storage_path.parent.mkdir(parents=True, exist_ok=True) with open(storage_path, 'w') as f: json.dump({ 'protocol_infos': PROTOCOL_INFOS }, f, indent=4) def register_new_protocol(protocol_hash, protocol_source, protocol_document): PROTOCOL_INFOS[protocol_hash] = { 'source': protocol_source, 'suitability': Suitability.UNKNOWN, 'has_implementation': False, 'num_conversations': 0 } base_folder = Path(os.environ.get('STORAGE_PATH')) / 'protocol_documents' save_protocol_document(base_folder, protocol_hash, protocol_document) save_memory() def has_implementation(protocol_hash): if protocol_hash not in PROTOCOL_INFOS: return False return PROTOCOL_INFOS[protocol_hash]['has_implementation'] def get_num_conversations(protocol_hash): if protocol_hash not in PROTOCOL_INFOS: return 0 return PROTOCOL_INFOS[protocol_hash]['num_conversations'] def increment_num_conversations(protocol_hash): PROTOCOL_INFOS[protocol_hash]['num_conversations'] += 1 save_memory() def add_routine(protocol_id, implementation): PROTOCOL_INFOS[protocol_id]['has_implementation'] = True base_folder = Path(os.environ.get('STORAGE_PATH')) / 'routines' save_routine(base_folder, protocol_id, implementation) save_memory() ================================================ FILE: agents/user/config.py ================================================ import json import random TASK_CONFIGS = [] TASK_SCHEMAS = {} NODE_URLS = {} # Workaround for the fact that once a constant is imported, it cannot be updated _PROTOCOL_DB_URL = None def get_protocol_db_url(): return _PROTOCOL_DB_URL def load_standard_config(user_name): print(f'Running in standalone mode as {user_name}.') with open('config.json') as f: config = json.load(f) for task_name, task_schema in config['taskSchemas'].items(): TASK_SCHEMAS[task_name] = task_schema user_config = config['users'][user_name] for task_config in user_config['tasks']: TASK_CONFIGS.append(task_config) with open('node_urls.json') as f: node_urls = json.load(f) for node_id, node_url in node_urls.items(): NODE_URLS[node_id] = node_url global _PROTOCOL_DB_URL _PROTOCOL_DB_URL = NODE_URLS[user_config['protocolDb']] def load_helper_config(master_server_name): # A helper user agent is a user agent that is used to access the external tools of a server print(f'Running in helper mode for {master_server_name}.') with open('config.json') as f: config = json.load(f) with open('node_urls.json') as f: node_urls = json.load(f) for node_id, node_url in node_urls.items(): NODE_URLS[node_id] = node_url server_config = config['servers'][master_server_name] for external_tool_name, external_tool_config in server_config['externalTools'].items(): schema_name = external_tool_config['schema'] task_schema = config['toolSchemas'][schema_name] TASK_SCHEMAS[external_tool_name] = task_schema global _PROTOCOL_DB_URL _PROTOCOL_DB_URL = NODE_URLS[server_config['protocolDb']] def load_config(user_name): if user_name.endswith('_helper'): load_helper_config(user_name[:-len('_helper')]) else: load_standard_config(user_name) def get_task(): # Pick a random task task_config = random.choice(TASK_CONFIGS) task_type = task_config['schema'] task_data = random.choice(task_config['choices']) target_server = random.choice(task_config['servers']) return task_type, task_data, target_server ================================================ FILE: agents/user/main.py ================================================ import sys sys.path.append('.') import dotenv dotenv.load_dotenv() import json import os from pathlib import Path import traceback from threading import Lock if os.environ.get('STORAGE_PATH') is None: os.environ['STORAGE_PATH'] = str(Path().parent / 'storage' / 'user') from agents.user.memory import load_memory, increment_num_conversations, get_num_protocol_uses, increment_num_protocol_uses, PROTOCOL_INFOS, save_memory, add_routine from specialized_toolformers.querier import send_query_with_protocol, send_query_without_protocol from specialized_toolformers.programmer import write_routine_for_task from toolformers.base import Tool, StringParameter from agents.user.protocol_management import decide_protocol, has_implementation from agents.user.config import get_task, load_config, TASK_SCHEMAS, NODE_URLS from utils import load_protocol_document, execute_routine, send_raw_query, use_query_id NUM_CONVERSATIONS_FOR_PROTOCOL = -1 NUM_CONVERSATIONS_FOR_ROUTINE = -1 from flask import Flask, request app = Flask(__name__) mutex = Lock() def call_using_implementation(task_type, task_schema, protocol_id, task_data, target_node): def send_to_server(query): print('Sending query:', query) response = send_raw_query(query, protocol_id, target_node, PROTOCOL_INFOS[protocol_id]['source']) if response.status_code == 200: parsed_response = json.loads(response.text) if parsed_response['status'] == 'success': body = parsed_response['body'] if isinstance(body, dict): # Sometimes this gets returned as a dict instead of a string return json.dumps(body) return body raise Exception('Error calling the server:', response.text) send_to_server_tool = Tool( 'send_to_server', 'Sends a query to the server and returns the response as described in the protocol document.', [ StringParameter('query', 'The query to send to the server.', True) ], send_to_server ) try: base_folder = Path(os.environ.get('STORAGE_PATH')) / 'routines' / task_type parsed_response = execute_routine(base_folder, protocol_id, task_data, [send_to_server_tool]) return parsed_response except Exception as e: print(traceback.print_exception(type(e), e, e.__traceback__)) print('Error executing routine:', e) print('Falling back to querier') response = send_query_with_protocol(task_schema, task_data, target_node, protocol_id, PROTOCOL_INFOS[protocol_id]['source']) return response @app.route("/", methods=['POST']) def main(): if os.environ.get('AGENT_ID').endswith('_helper'): return json.dumps({ 'status': 'error', 'message': 'This agent is a helper agent and cannot be used for direct communication.' }) data = request.get_json() query_id = data['queryId'] if query_id is None: print('No query ID provided.') else: print('Query ID:', query_id) with mutex: with use_query_id(query_id): response = run_random_task() print('Response:', response, flush=True) save_memory() return response def run_task(task_type, task_data, target_server): # Query the node to know its preferred protocols # If you already support a protocol, use it and call the corresponding routine # If you might accept one of the target's protocols but don't have an implementation: # - If the communication is sufficiently rare, use the querier with the protocol # - Otherwise, call the programmer to implement the protocol # If you haven't categorized the target's protocols yet, use the categorizer to classify them and go back # If you've classified all the target's protocols and none are suitable, check on the public protocol database and repeat # If you've checked all the public protocols and none are suitable: # - If the communication is sufficiently rare, use the querier without any protocol # - Otherwise, use the negotiator to reach an agreement with the target on a new protocol task_schema = TASK_SCHEMAS[task_type] target_node = NODE_URLS[target_server] protocol_id = decide_protocol(task_type, target_node, NUM_CONVERSATIONS_FOR_PROTOCOL, NUM_CONVERSATIONS_FOR_NEGOTIATED_PROTOCOL) if protocol_id is None: source = None else: source = PROTOCOL_INFOS[protocol_id]['source'] increment_num_conversations(task_type, target_node) if protocol_id is not None: print('Using protocol:', protocol_id) increment_num_protocol_uses(protocol_id) # Check if we have an implementation if has_implementation(task_type, protocol_id): return call_using_implementation(task_type, task_schema, protocol_id, task_data, target_node) # If we've talked enough times using a certain protocol, write an implementation elif get_num_protocol_uses(protocol_id) > NUM_CONVERSATIONS_FOR_ROUTINE: protocol_document = load_protocol_document(Path(os.environ.get('STORAGE_PATH')) / 'protocol_documents', protocol_id) routine = write_routine_for_task(task_schema, protocol_document) add_routine(task_type, protocol_id, routine) return call_using_implementation(task_type, task_schema, protocol_id, task_data, target_node) else: # Use the querier with the protocol response = send_query_with_protocol(task_schema, task_data, target_node, protocol_id, source) return response else: # Use the querier without any protocol print('Using the querier without any protocol') response = send_query_without_protocol(task_schema, task_data, target_node) return response def run_random_task(): # Generate task info (with structured data) and pick a target node task_type, task_data, target_server = get_task() return run_task(task_type, task_data, target_server) @app.route('/customRun', methods=['POST']) def custom_run(): data = request.get_json() task_type = data['type'] task_data = data['data'] target_server = data['targetServer'] query_id = data['queryId'] if query_id is None: print('No query ID provided.') else: print('Query ID:', query_id) print('Custom run:', task_type, task_data, target_server) with mutex: with use_query_id(query_id): return run_task(task_type, task_data, target_server) def init(): load_config(os.environ.get('AGENT_ID')) load_memory() print('Agent initialized') init() ================================================ FILE: agents/user/memory.py ================================================ import json import os from pathlib import Path from utils import save_routine # Stores for each protocol # - The id # - Whether it is suitable for each task # - The source of the protocol # - Whether there is an implementation available PROTOCOL_INFOS = {} # Indexed by task type and target node NUM_CONVERSATIONS = {} def load_memory(): storage_path = Path(os.environ.get('STORAGE_PATH')) / 'memory.json' if not Path(storage_path).exists(): print('No memory found. Using default blank memory.') PROTOCOL_INFOS.clear() NUM_CONVERSATIONS.clear() return with open(storage_path, 'r') as f: data = json.load(f) PROTOCOL_INFOS.clear() PROTOCOL_INFOS.update(data['protocol_infos']) NUM_CONVERSATIONS.clear() NUM_CONVERSATIONS.update(data['num_conversations']) print('Loaded memory:', PROTOCOL_INFOS.keys()) def save_memory(): storage_path = Path(os.environ.get('STORAGE_PATH')) / 'memory.json' storage_path.parent.mkdir(parents=True, exist_ok=True) with open(storage_path, 'w') as f: json.dump({ 'protocol_infos': PROTOCOL_INFOS, 'num_conversations': NUM_CONVERSATIONS }, f, indent=4) def get_num_protocol_uses(protocol_id): if protocol_id not in PROTOCOL_INFOS: return 0 return PROTOCOL_INFOS[protocol_id]['num_uses'] def increment_num_protocol_uses(protocol_id): PROTOCOL_INFOS[protocol_id]['num_uses'] += 1 save_memory() def get_num_conversations(task_type, target_node): if task_type not in NUM_CONVERSATIONS: return 0 if target_node not in NUM_CONVERSATIONS[task_type]: return 0 return NUM_CONVERSATIONS[task_type][target_node] def increment_num_conversations(task_type, target_node): if task_type not in NUM_CONVERSATIONS: NUM_CONVERSATIONS[task_type] = {} if target_node not in NUM_CONVERSATIONS[task_type]: NUM_CONVERSATIONS[task_type][target_node] = 0 NUM_CONVERSATIONS[task_type][target_node] += 1 def add_routine(task_type, protocol_id, implementation): PROTOCOL_INFOS[protocol_id]['has_implementation'][task_type] = True base_folder = Path(os.environ.get('STORAGE_PATH')) / 'routines' / task_type save_routine(base_folder, protocol_id, implementation) save_memory() ================================================ FILE: agents/user/protocol_management.py ================================================ import urllib import os from pathlib import Path import requests as request_manager from utils import load_protocol_document, save_protocol_document, compute_hash, shared_config from agents.user.config import TASK_SCHEMAS from agents.user.memory import get_num_conversations, PROTOCOL_INFOS, save_memory from specialized_toolformers.protocol_checker import check_protocol_for_task, filter_protocols_for_task from specialized_toolformers.negotiator import negotiate_protocol_for_task from agents.common.core import Suitability from agents.user.config import get_protocol_db_url def query_protocols(target_node): response = request_manager.get(f'{target_node}/wellknown', timeout=shared_config('timeout')) response = response.json() if response['status'] == 'success': return response['protocols'] else: return [] def has_implementation(task_type, protocol_id): if protocol_id not in PROTOCOL_INFOS: return False if task_type not in PROTOCOL_INFOS[protocol_id]['has_implementation']: return False return PROTOCOL_INFOS[protocol_id]['has_implementation'][task_type] def is_adequate(task_type, protocol_id): if protocol_id not in PROTOCOL_INFOS: return False if task_type not in PROTOCOL_INFOS[protocol_id]['suitability_info']: return False return PROTOCOL_INFOS[protocol_id]['suitability_info'][task_type] == Suitability.ADEQUATE def is_categorized(task_type, protocol_id): if protocol_id not in PROTOCOL_INFOS: return False if task_type not in PROTOCOL_INFOS[protocol_id]['suitability_info']: return False return PROTOCOL_INFOS[protocol_id]['suitability_info'][task_type] != Suitability.UNKNOWN def get_an_adequate_protocol(task_type, eligible_protocols): # Will ignore protocols that haven't been downloaded yet # First, try with protocols having an implementation protocols_with_implementations = [ protocol_id for protocol_id in eligible_protocols if is_adequate(task_type, protocol_id) and has_implementation(task_type, protocol_id) ] if len(protocols_with_implementations) > 0: print('Found protocol with implementation:', protocols_with_implementations[0]) return protocols_with_implementations[0] # If there is no matching implementation, try with protocols that have been categorized and have been deemed adequate adequate_protocols = [ protocol_id for protocol_id in eligible_protocols if is_adequate(task_type, protocol_id) ] if len(adequate_protocols) > 0: return adequate_protocols[0] # If there are still none, try with protocols that haven't been categorized yet (but are already in memory), categorize them and check again uncategorized_protocols = [protocol_id for protocol_id in eligible_protocols if protocol_id in PROTOCOL_INFOS and not is_categorized(task_type, protocol_id)] uncategorized_protocols = prefilter_protocols(uncategorized_protocols, task_type) for protocol_id in uncategorized_protocols: suitable = categorize_protocol(protocol_id, task_type) if suitable: return protocol_id # We're out of luck, return None return None def categorize_protocol(protocol_id, task_type): print('Categorizing protocol:', protocol_id) base_folder = Path(os.environ.get('STORAGE_PATH')) / 'protocol_documents' protocol_document = load_protocol_document(base_folder, protocol_id) suitable = check_protocol_for_task(protocol_document, TASK_SCHEMAS[task_type]) if suitable: PROTOCOL_INFOS[protocol_id]['suitability_info'][task_type] = Suitability.ADEQUATE else: PROTOCOL_INFOS[protocol_id]['suitability_info'][task_type] = Suitability.INADEQUATE save_memory() return suitable def prefilter_protocols(protocol_ids, task_type): print('Prefiltering protocols:', protocol_ids, 'for task type:', task_type) if len(protocol_ids) <= 1: # No point in prefiltering if there's only one protocol return protocol_ids protocol_metadatas = [] for protocol_id in protocol_ids: print('Checking suitability for protocol:', protocol_id) if task_type not in PROTOCOL_INFOS[protocol_id]['suitability_info'] or \ PROTOCOL_INFOS[protocol_id]['suitability_info'][task_type] == Suitability.UNKNOWN: protocol_metadatas.append({ 'id' : protocol_id, **PROTOCOL_INFOS[protocol_id]['metadata']}) filtered_protocols = filter_protocols_for_task(protocol_metadatas, TASK_SCHEMAS[task_type]) probably_inadequate = [protocol['id'] for protocol in protocol_metadatas if protocol not in filtered_protocols] for protocol_id in probably_inadequate: PROTOCOL_INFOS[protocol_id]['suitability_info'][task_type] = Suitability.PROBABLY_INADEQUATE save_memory() filtered_ids = [protocol['id'] for protocol in filtered_protocols] print('Filtered protocols:', filtered_ids) return filtered_ids def register_new_protocol(protocol_id, source, protocol_data): PROTOCOL_INFOS[protocol_id] = { 'suitability_info': {}, 'source': source, 'has_implementation': {}, 'num_uses' : 0, 'metadata' : { 'name' : protocol_data['name'], 'description' : protocol_data['description'] } } base_folder = Path(os.environ.get('STORAGE_PATH')) / 'protocol_documents' save_protocol_document(base_folder, protocol_id, protocol_data['protocol']) save_memory() def submit_protocol_to_public_db(protocol_id, protocol_data): response = request_manager.post(get_protocol_db_url(), json={ 'name': protocol_data['name'], 'description': protocol_data['description'], 'protocol': protocol_data['protocol'] }, timeout=shared_config('timeout')) source_url = f'{get_protocol_db_url()}/protocol?' + urllib.parse.urlencode({ 'id': protocol_id }) print('Submitted protocol to public database. URL:', source_url) return source_url if response.status_code == 200 else None def negotiate_protocol(task_type, target_node): task_schema = TASK_SCHEMAS[task_type] protocol_data = negotiate_protocol_for_task(task_schema, target_node) protocol_id = compute_hash(protocol_data['protocol']) source_url = submit_protocol_to_public_db(protocol_id, protocol_data) if source_url is not None: register_new_protocol(protocol_id, source_url, protocol_data) else: raise Exception('Failed to submit protocol to public database') # Share the protocol with the target response = request_manager.post(f'{target_node}/registerNegotiatedProtocol', json={ 'protocolHash': protocol_id, 'protocolSources': [source_url] }, timeout=shared_config('timeout')) if response.status_code != 200: raise Exception('Failed to share the protocol with the target:', response.text) return protocol_id def decide_protocol(task_type, target_node, num_conversations_for_protocol, num_conversations_for_negotiated_protocol): target_protocols = query_protocols(target_node) print('Target protocols:', target_protocols) protocol_id = get_an_adequate_protocol(task_type, list(target_protocols.keys())) if protocol_id is not None: print('Found adequate protocol from storage:', protocol_id) return protocol_id # If there are none, categorize the remaining target protocols, and try again for protocol_id, sources in target_protocols.items(): if protocol_id not in PROTOCOL_INFOS: for source in sources: response = request_manager.get(source, timeout=shared_config('timeout')) protocol_document = response.text metadata = request_manager.get(source.replace('protocol', 'metadata'), timeout=shared_config('timeout')).json() if metadata['status'] != 'success': print('Failed to retrieve metadata:', metadata) continue metadata = metadata['metadata'] protocol_data = { 'name': metadata['name'], 'description': metadata['description'], 'protocol': protocol_document } register_new_protocol(protocol_id, source, protocol_data) for protocol_id in prefilter_protocols(list(target_protocols.keys()), task_type): # Categorize the protocol suitable = categorize_protocol(protocol_id, task_type) if suitable: return protocol_id if get_num_conversations(task_type, target_node) < num_conversations_for_protocol: # No point in exploring potential protocols (outside of the explicitly supported ones) if we haven't talked enough times with the target return None # If there are still none, check if we have in our memory a suitable protocol protocol_id = get_an_adequate_protocol(task_type, PROTOCOL_INFOS.keys()) if protocol_id is not None: return protocol_id # If there are still none, check the public protocol database and categorize them # Note: in a real system, we wouldn't get all protocols from the database, but rather # only the ones likely to be suitable for the task public_protocols_response = request_manager.get(get_protocol_db_url(), timeout=shared_config('timeout')).json() if public_protocols_response['status'] == 'success': public_protocols = [x for x in public_protocols_response['protocols']] else: public_protocols = [] print('Stored protocols:', PROTOCOL_INFOS.keys()) print('Public protocols:', public_protocols) for protocol_metadata in public_protocols: protocol_id = protocol_metadata['id'] # Retrieve the protocol print('Protocol ID:', urllib.parse.quote_plus(protocol_id)) uri = f'{get_protocol_db_url()}/protocol?' + urllib.parse.urlencode({ 'id': protocol_id }) print('URI:', uri) protocol_document_response = request_manager.get(uri, timeout=shared_config('timeout')) if protocol_document_response.status_code == 200: protocol_document = protocol_document_response.text protocol_data = { 'name': protocol_metadata['name'], 'description': protocol_metadata['description'], 'protocol': protocol_document } register_new_protocol(protocol_id, uri, protocol_data) public_protocol_ids = prefilter_protocols([x['id'] for x in public_protocols], task_type) for protocol_id in public_protocol_ids: # Categorize the protocol suitable = categorize_protocol(protocol_id, task_type) if suitable: return protocol_id # If there are still none, check if we have talked enough times with the target to warrant a new protocol requires_negotiation_response = request_manager.get(f'{target_node}/requiresNegotiation', timeout=shared_config('timeout')) requires_negotiation = requires_negotiation_response.json()['requiresNegotiation'] if get_num_conversations(task_type, target_node) >= num_conversations_for_negotiated_protocol or requires_negotiation: protocol_id = negotiate_protocol(task_type, target_node) # Negotiated protocols are always suitable PROTOCOL_INFOS[protocol_id]['suitability_info'][task_type] = Suitability.ADEQUATE return protocol_id # If there are still none, use the querier without any protocol return None ================================================ FILE: compute_costs.py ================================================ from databases.mongo import query_database COSTS = { 'gpt-4o' : { 'prompt_tokens' : 5e-6, 'completion_tokens' : 15e-6 }, 'gpt-4o-mini' : { 'prompt_tokens' : 0.15e-6, 'completion_tokens' : 0.6e-6 } } def main(): # Retrieve the costs from the database costs = query_database('usageLogs', 'main', {}) total_cost = 0 for cost in costs: model = cost['model'] total_cost += cost['prompt_tokens'] * COSTS[model]['prompt_tokens'] total_cost += cost['completion_tokens'] * COSTS[model]['completion_tokens'] print(f'Total cost: {total_cost}') if __name__ == '__main__': main() ================================================ FILE: config.json ================================================ { "shared": { "timeout": 1000, "backoff_lower": 15, "backoff_upper": 30, "backoff_factor": 2, "backoff_retries": 4 }, "orchestration": { "startingPorts": { "user": 5000, "server": 6000, "protocolDb": 7000 } }, "protocolDbs": { "protocolDb1": { "peers": [ "protocolDb2" ] }, "protocolDb2": { "peers": [ "protocolDb1", "protocolDb3" ] }, "protocolDb3": { "peers": [ "protocolDb2" ] } }, "toolSchemas": { "temperature": { "description": "A tool to get the temperature forecast for a given day", "input": { "properties": { "location": { "type": "string", "description": "The location for which you want the temperature forecast. Fuzzy matching is allowed." }, "date": { "type": "string", "description": "The date for which you want the temperature forecast (format: YYYY-MM-DD)" } }, "required": [ "date", "location" ] }, "output": { "properties": { "temperature": { "type": "number", "description": "The predicted temperature for that day in \u00b0C" } }, "required": [ "temperature" ] } }, "todayWeather": { "description": "A tool to get the weather forecast for today", "input": { "properties": { "location": { "type": "string", "description": "The location for which you want the temperature forecast. Fuzzy matching is allowed." } }, "required": [ "location" ] }, "output": { "properties": { "weather": { "type": "string", "description": "The predicted weather for today", "enum": [ "sunny", "cloudy", "rainy", "snowy" ] } }, "required": [ "weather" ] } }, "requestDeliveryDriver": { "description": "A tool to request a food delivery at a certain location", "input": { "properties": { "restaurant": { "type": "string", "description": "The name of the restaurant from which the food is being delivered" }, "address": { "type": "string", "description": "The address to which the food should be delivered" } }, "required": [ "address" ] }, "output": { "properties": { "status": { "type": "string", "description": "The status of the delivery request.", "enum": [ "success", "failure" ] }, "error": { "type": "string", "description": "An error message. Required if status is \"failure\"." } }, "required": [ "status" ] } }, "isOpen": { "description": "A tool to determine if the linked restaurant is open.", "input": { "properties": { "date": { "type": "string", "description": "The date for which you want to know if the restaurant is open (format: YYYY-MM-DD)" } }, "required": [ "date" ] }, "output": { "properties": { "isOpen": { "type": "string", "description": "\"yes\" if the restaurant is open, \"no\" otherwise" } }, "required": [ "isOpen" ] } }, "weather": { "description": "A tool to get the weather forecast for a given day", "input": { "properties": { "location": { "type": "string", "description": "The location for which you want the temperature forecast. Fuzzy matching is allowed" }, "date": { "type": "string", "description": "The date for which you want the weather forecast (format: YYYY-MM-DD)" } }, "required": [ "date" ] }, "output": { "properties": { "temperature": { "type": "number", "description": "The predicted temperature for that day in \u00b0C" }, "precipitation": { "type": "number", "description": "The predicted precipitation for that day in mm" }, "weather": { "type": "string", "description": "The predicted weather for that day", "enum": [ "sunny", "cloudy", "rainy", "snowy" ] } }, "required": [ "temperature", "precipitation" ] } }, "currentDate": { "description": "A tool to get the current date", "input": { "properties": {}, "required": [] }, "output": { "properties": { "date": { "type": "string", "description": "The current date (format: YYYY-MM-DD)" } }, "required": [ "date" ] } }, "getMenu": { "description": "A tool to get the menu for a restaurant", "input": { "properties": { "cuisineType": { "type": "string", "description": "The type of cuisine of the restaurant", "enum": [ "italian", "chinese", "indian", "british" ] } }, "required": [ "cuisineType" ] }, "output": { "properties": { "menu": { "type": "array", "description": "The menu of the restaurant", "items": { "type": "object", "properties": { "name": { "type": "string", "description": "The name of the dish" }, "price": { "type": "number", "description": "The price of the dish" } } } } }, "required": [ "menu" ] } }, "dayOfTheWeek": { "description": "A tool to get the day of the week for a given date", "input": { "properties": { "date": { "type": "string", "description": "The date for which you want the day of the week (format: YYYY-MM-DD)" } }, "required": [ "date" ] }, "output": { "properties": { "type": "string", "day": { "enum": [ "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday" ], "description": "The day of the week for that date" } }, "required": [ "day" ] } }, "sendDriver": { "description": "A tool to send a driver to deliver food to a certain address", "input": { "properties": { "restaurant": { "type": "string", "description": "The name of the restaurant from which the food is being delivered" }, "address": { "type": "string", "description": "The address to which the food should be delivered" } }, "required": [ "address", "restaurant" ] }, "output": { "properties": { "status": { "type": "string", "description": "The status of the delivery request.", "enum": [ "success", "failure" ] }, "error": { "type": "string", "description": "An error message. Required if status is \"failure\"." } }, "required": [ "status" ] } }, "trafficInfo": { "description": "A tool to get the traffic information for a given location", "input": { "properties": { "date": { "type": "string", "description": "The date for which you want the traffic information (format: YYYY-MM-DD)" }, "location": { "type": "string", "description": "The location for which you want the traffic information. Fuzzy matching is allowed." } }, "required": [ "date", "location" ] }, "output": { "properties": { "traffic": { "type": "string", "enum": [ "clear", "light", "moderate", "heavy", "accident" ], "description": "The traffic information for that location. Fuzzy matching is allowed." } }, "required": [ "traffic" ] } }, "getAvailableTaxi": { "description": "A tool to determine if there is an available taxi", "input": { "properties": { "time": { "type": "string", "description": "The time for which you want to know if a taxi is available (format: HH:MM)" } }, "required": [ "time" ] }, "output": { "properties": { "taxiId": { "type": "string", "description": "The id of the available taxi. \"000\" if no taxi is available" } }, "required": [ "taxiId" ] } }, "assignTaxi": { "description": "A tool to assign a taxi to a certain address", "input": { "properties": { "taxiId": { "type": "string", "description": "The id of the taxi to assign" }, "time": { "type": "string", "description": "The time for which the taxi should be assigned (format: HH:MM)" }, "address": { "type": "string", "description": "The address to which the taxi should be assigned" } }, "required": [ "taxiId", "address" ] }, "output": { "properties": { "status": { "type": "string", "description": "The status of the taxi assignment.", "enum": [ "success", "failure" ] }, "error": { "type": "string", "description": "An error message. Required if status is \"failure\"." } }, "required": [ "status" ] } } }, "dbSchemas": { "hotel-mongo": { "dbType": "mongo", "description": "A MongoDB database schema for a hotel", "collections": { "rooms": { "description": "A collection of rooms in the hotel", "properties": { "roomNumber": { "type": "number", "description": "The number of the room" }, "bookings": { "type": "array", "description": "The bookings for this room", "items": { "type": "object", "properties": { "startDate": { "type": "string", "description": "The start date of the booking (format: YYYY-MM-DD)" }, "endDate": { "type": "string", "description": "The end date of the booking, inclusive (format: YYYY-MM-DD)" } } } } }, "initialValues": [ { "roomNumber": 101, "bookings": [] }, { "roomNumber": 102, "bookings": [] }, { "roomNumber": 103, "bookings": [] } ] } } }, "hotel-sql": { "dbType": "sql", "description": "A SQL database schema for a hotel", "tables": { "Bookings": { "description": "A table of bookings in the hotel", "columns": { "RoomNumber": "INT", "StartDate": "DATE", "EndDate": "DATE" }, "constraints": [ "PRIMARY KEY (RoomNumber, StartDate)" ] }, "Rooms": { "description": "A table of rooms in the hotel", "columns": { "RoomNumber": "INT PRIMARY KEY" }, "constraints": [], "initialValues": [ { "RoomNumber": 101 }, { "RoomNumber": 102 }, { "RoomNumber": 103 } ] } } }, "skiResort-mongo": { "dbType": "mongo", "description": "A SQL database schema for a ski resort", "collections": { "rooms": { "description": "A collection of rooms in the hotel", "properties": { "roomNumber": { "type": "number", "description": "The number of the room" }, "bookings": { "type": "array", "description": "The bookings for this room", "items": { "type": "object", "properties": { "startDate": { "type": "string", "description": "The start date of the booking (format: YYYY-MM-DD)" }, "endDate": { "type": "string", "description": "The end date of the booking, inclusive (format: YYYY-MM-DD)" } } } } }, "initialValues": [ { "roomNumber": 101, "bookings": [] }, { "roomNumber": 102, "bookings": [] }, { "roomNumber": 103, "bookings": [] } ] }, "skis": { "description": "Skis available for rental", "properties": { "skiId": { "type": "string", "description": "Id of the ski to rent" }, "skiType": { "type": "string", "description": "Type of ski", "enum": [ "racing", "carving", "backcountry" ] }, "bookings": { "type": "array", "description": "The bookings for this ski", "items": { "type": "object", "properties": { "startDate": { "type": "string", "description": "The start date of the booking (format: YYYY-MM-DD)" }, "endDate": { "type": "string", "description": "The end date of the booking, inclusive (format: YYYY-MM-DD)" } } } } }, "initialValues": [ { "skiId": "A234", "skiType": "racing", "bookings": [] }, { "skiId": "A453", "skiType": "racing", "bookings": [] }, { "skiId": "A321", "skiType": "carving", "bookings": [] }, { "skiId": "A331", "skiType": "carving", "bookings": [] }, { "skiId": "A212", "skiType": "backcountry", "bookings": [] }, { "skiId": "A989", "skiType": "backcountry", "bookings": [] } ] } } }, "skiResort-sql": { "dbType": "sql", "description": "A SQL database schema for a hotel", "tables": { "Bookings": { "description": "A table of bookings in the hotel", "columns": { "RoomNumber": "INT", "StartDate": "DATE", "EndDate": "DATE" }, "constraints": [ "PRIMARY KEY (RoomNumber, StartDate)" ] }, "Rooms": { "description": "A table of rooms in the hotel", "columns": { "RoomNumber": "INT PRIMARY KEY" }, "constraints": [], "initialValues": [ { "RoomNumber": 101 }, { "RoomNumber": 102 }, { "RoomNumber": 103 } ] }, "Skis": { "description": "A table of skis available for rental", "columns": { "SkiId": "VARCHAR(255) PRIMARY KEY", "SkiType": "VARCHAR(20)" }, "constraints": [], "initialValues": [ { "SkiId": "A234", "SkiType": "racing" }, { "SkiId": "A453", "SkiType": "racing" }, { "SkiId": "A321", "SkiType": "carving" }, { "SkiId": "A331", "SkiType": "carving" }, { "SkiId": "A212", "SkiType": "backcountry" }, { "SkiId": "A989", "SkiType": "backcountry" } ] }, "SkiRentals": { "description": "A table of ski rentals", "columns": { "SkiId": "VARCHAR(255)", "StartDate": "DATE", "EndDate": "DATE" }, "constraints": [ "PRIMARY KEY (SkiId, StartDate)" ] } } }, "restaurant-mongo": { "dbType": "mongo", "description": "A MongoDB database schema for a restaurant", "collections": { "bookings": { "properties": { "surname": { "type": "string", "description": "The surname linked to the booking" }, "numPeople": { "type": "number", "description": "The number of people for the booking" }, "date": { "type": "string", "description": "The date of the booking (format: YYYY-MM-DD)" }, "hour": { "type": "number", "description": "The hour of the booking (24-hour format, integer). Note that a booking always lasts an hour (e.g. 17 means from 5 pm to 6 pm)" } }, "initialValues": [] } } }, "restaurant-sql": { "dbType": "sql", "description": "A SQL database schema for a restaurant", "tables": { "Bookings": { "description": "A table of bookings in the restaurant", "columns": { "Surname": "VARCHAR(255)", "NumPeople": "INT", "Date": "DATE", "Hour": "INT" }, "constraints": [ "PRIMARY KEY (Surname, Date, Hour)" ] } } }, "cinema-mongo": { "dbType": "mongo", "description": "A MongoDB database schema for a cinema", "collections": { "screenings": { "description": "The screenings of movies", "properties": { "date": { "type": "string", "description": "The date of the screening" }, "hour": { "type": "number", "description": "The hour of the movie (24-hour format, integer)." }, "movie": { "type": "string", "description": "The title of the movie" }, "ticketsLeft": { "type": "number", "description": "The number of tickets left" } }, "initialValues": [ { "date": "2024-03-15", "movie": "Forrest Gump", "hour": 18, "ticketsLeft": 54 }, { "date": "2024-03-15", "movie": "Forrest Gump", "hour": 20, "ticketsLeft": 45 }, { "date": "2024-03-15", "movie": "Stand by Me", "hour": 18, "ticketsLeft": 32 }, { "date": "2024-03-15", "movie": "The Silence of the Lambs", "hour": 20, "ticketsLeft": 24 }, { "date": "2024-03-16", "movie": "Forrest Gump", "hour": 18, "ticketsLeft": 32 }, { "date": "2024-03-16", "movie": "Stand by Me", "hour": 18, "ticketsLeft": 18 }, { "date": "2024-03-16", "movie": "The Silence of the Lambs", "hour": 18, "ticketsLeft": 27 }, { "date": "2024-03-17", "movie": "Stand by Me", "hour": 18, "ticketsLeft": 35 }, { "date": "2024-03-17", "movie": "Stand by Me", "hour": 20, "ticketsLeft": 12 } ] } } }, "cinema-sql": { "dbType": "sql", "description": "A SQL database schema for a cinema", "tables": { "Screenings": { "description": "A table of screenings in the cinema", "columns": { "Date": "DATE", "Hour": "INT", "Movie": "VARCHAR(255)", "TicketsLeft": "INT" }, "constraints": [ "PRIMARY KEY (Date, Hour, Movie)" ], "initialValues": [ { "Date": "2024-03-15", "Movie": "Forrest Gump", "Hour": 18, "TicketsLeft": 54 }, { "Date": "2024-03-15", "Movie": "Forrest Gump", "Hour": 20, "TicketsLeft": 45 }, { "Date": "2024-03-15", "Movie": "Stand by Me", "Hour": 18, "TicketsLeft": 32 }, { "Date": "2024-03-15", "Movie": "The Silence of the Lambs", "Hour": 20, "TicketsLeft": 24 }, { "Date": "2024-03-16", "Movie": "Forrest Gump", "Hour": 18, "TicketsLeft": 32 }, { "Date": "2024-03-16", "Movie": "Stand by Me", "Hour": 18, "TicketsLeft": 18 }, { "Date": "2024-03-16", "Movie": "The Silence of the Lambs", "Hour": 18, "TicketsLeft": 27 }, { "Date": "2024-03-17", "Movie": "Stand by Me", "Hour": 18, "TicketsLeft": 35 }, { "Date": "2024-03-17", "Movie": "Stand by Me", "Hour": 20, "TicketsLeft": 12 } ] } } } }, "servers": { "weatherServer": { "modelType": "gpt-4o", "description": "You are the server for the weather service. Your task is to provide weather forecasts for a given day.", "actionDescriptions": [ "You can provide the weather forecast for a given day by interacting with the \"getWeather\" tool." ], "internalDbSchema": null, "externalTools": {}, "mockTools": { "getWeather": "weather", "currentDate": "currentDate" }, "idealTasks": [ "queryWeather" ], "protocolDb": "protocolDb2" }, "hotel1": { "modelType": "gpt-4o", "description": "You are the server for the Lodge Hotel, a small hotel near Paris. Your task is to handle bookings at your hotel.", "actionDescriptions": [ "You can check the availability of the rooms and book a new one by interacting with the database. Make sure that a room is not already booked" ], "internalDbSchema": "hotel-sql", "externalTools": {}, "protocolDb": "protocolDb3", "idealTasks": [ "bookRoom" ] }, "hotel2": { "modelType": "llama3-405b", "description": "You are the server for the Plaza Hotel, a small hotel in Barcelona. Your task is to handle bookings at your hotel.", "actionDescriptions": [ "You can check the availability of the rooms and book a new one by interacting with the database. Make sure that a room is not already booked" ], "internalDbSchema": "hotel-sql", "externalTools": {}, "protocolDb": "protocolDb3", "idealTasks": [ "bookRoom" ] }, "hotel3": { "modelType": "gpt-4o", "description": "You are the server for the Grand Hotel, a luxurious hotel in London. Your task is to handle bookings at your hotel.", "actionDescriptions": [ "You can check the availability of the rooms and book a new one by interacting with the database. Make sure that a room is not already booked", "If a customer asks for a restaurant recommendation, you can suggest the \"Il Forno\", an Italian restaurant. However, you need to make sure that the restaurant is open on the requested date (the hour doesn't matter) with the \"isOpen\" tool." ], "internalDbSchema": "hotel-mongo", "externalTools": { "isOpen": { "schema": "isOpen", "server": "restaurant1" } }, "protocolDb": "protocolDb1", "idealTasks": [ "bookRoom", "suggestRestaurant" ] }, "skiResort1": { "modelType": "gpt-4o", "description": "You are the server for the Alpine Resort, a luxurious ski resort near Zurich. Your task is to handle bookings at your resort", "actionDescriptions": [ "You can check the availability of the rooms and book a new one by interacting with the database. Make sure that a room is not already booked, and that the predicted temperature on that day is between -10 \u00b0C and +1 \u00b0C before you allow a user to book.", "Customers can rent skis from your resort. You can handle ski rentals by interacting with the database. Don't bother checking if the customers have a booking.", "You can get the weather forecast for a given day by interacting with the \"getWeather\" tool." ], "internalDbSchema": "skiResort-sql", "mockTools": {}, "externalTools": { "getTemperature": { "schema": "temperature", "server": "weatherServer" } }, "protocolDb": "protocolDb1", "idealTasks": [ "bookRoom", "rentSki" ] }, "skiResort2": { "modelType": "llama3-405b", "description": "You are the server for the Snowy Peaks Resort, a ski resort near Vienna. Your task is to handle bookings at your resort", "actionDescriptions": [ "You can check the availability of the rooms and book a new one by interacting with the database. Make sure that a room is not already booked, and that the predicted temperature on that day is between -10 \u00b0C and +1 \u00b0C before you allow a user to book.", "Customers can rent skis from your resort. You can handle ski rentals by interacting with the database. Don't bother checking if the customers have a booking.", "If a customer asks for a restaurant recommendation, you can suggest the \"Bengal Delight\", an Indian restaurant. However, you need to make sure that the restaurant is open on the requested date (the hour doesn't matter) with the \"isOpen\" tool." ], "internalDbSchema": "skiResort-mongo", "externalTools": { "getTemperature": { "schema": "temperature", "server": "weatherServer" }, "isOpen": { "schema": "isOpen", "server": "restaurant3" } }, "protocolDb": "protocolDb2", "idealTasks": [ "bookRoom", "rentSki", "suggestRestaurant" ] }, "restaurant1": { "modelType": "llama3-405b", "description": "You are the server for the Il Forno, an Italian restaurant in London. Your task is to handle bookings at your restaurant.", "actionDescriptions": [ "You can check the availability of the tables and book a new one by interacting with the database. Make sure that a table is not already booked. Bookings are univocally identified by surname + date + hour.", "You can get the menu of the restaurant by interacting with the getMenu tool.", "Your opening times are from 12:00 to 22:00 every day, except on Monday, when you're closed.", "You can request a food delivery driver by interacting with the \"requestDeliveryDriver\" tool. Don't worry about the payment or the actual cooking of the food, just make sure the food is delivered to the correct address. Also, the delivery service is extremely fast, so you can deliver far away, even in other countries." ], "mockTools": { "getMenu": "getMenu", "currentDate": "currentDate", "dayOfTheWeek": "dayOfTheWeek" }, "internalDbSchema": "restaurant-sql", "externalTools": { "requestDeliveryDriver": { "schema": "requestDeliveryDriver", "server": "deliveryService" } }, "protocolDb": "protocolDb1", "idealTasks": [ "bookTable", "openingTimes", "orderEverything" ] }, "restaurant2": { "modelType": "gpt-4o", "description": "You are the server for the Royal Oak, a British restaurant in Cairo. Your task is to handle bookings at your restaurant. Bookings are univocally identified by surname + date + hour.", "actionDescriptions": [ "You can check the availability of the tables and book a new one by interacting with the database. Make sure that a table is not already booked", "You can get the menu of the restaurant by interacting with the getMenu tool.", "Your opening times are from 17:00 to 23:00 everyday, except on Tuesday, when you're closed.", "You can request a food delivery driver by interacting with the \"requestDeliveryDriver\" tool. Don't worry about the payment or the actual cooking of the food, just make sure the food is delivered to the correct address. Also, the delivery service is extremely fast, so you can deliver far away, even in other countries." ], "mockTools": { "getMenu": "getMenu", "currentDate": "currentDate", "dayOfTheWeek": "dayOfTheWeek" }, "internalDbSchema": "restaurant-mongo", "externalTools": { "requestDeliveryDriver": { "schema": "requestDeliveryDriver", "server": "deliveryService" } }, "protocolDb": "protocolDb1", "idealTasks": [ "bookTable", "openingTimes", "orderEverything" ] }, "restaurant3": { "modelType": "gpt-4o", "description": "You are the server for the Bengal Delight, an Indian restaurant in Vienna. Your task is to handle bookings at your restaurant. Bookings are univocally identified by surname + date + hour.", "actionDescriptions": [ "You can check the availability of the tables and book a new one by interacting with the database. Make sure that a table is not already booked", "You can get the menu of the restaurant by interacting with the getMenu tool.", "Your opening times are from 17:00 to 23:00 everyday, except on Tuesday, when you're closed." ], "mockTools": { "getMenu": "getMenu", "currentDate": "currentDate", "dayOfTheWeek": "dayOfTheWeek" }, "internalDbSchema": "restaurant-sql", "externalTools": {}, "protocolDb": "protocolDb3", "idealTasks": [ "bookTable", "openingTimes" ] }, "deliveryService": { "modelType": "gpt-4o", "description": "You are the server for the Fast Delivery Service. Your task is to deliver food to the correct address as quickly as possible.", "actionDescriptions": [ "You can send a driver to deliver food to a certain address by interacting with the \"sendDriver\" tool. Don't worry about whether a driver is available or not, just assume that there is always a driver ready to go.", "You can get the current date by interacting with the \"currentDate\" tool.", "If the traffic is \"heavy\" or there is an \"accident\" at the location, you have to refuse the delivery request." ], "mockTools": { "sendDriver": "sendDriver", "currentDate": "currentDate" }, "internalDbSchema": null, "externalTools": { "trafficInfo": { "schema": "trafficInfo", "server": "trafficServer" } }, "protocolDb": "protocolDb1", "idealTasks": [] }, "cinema1": { "modelType": "gpt-4o", "description": "You are the server for the Silver Screen Cinema, a small cinema in Paris. Your task is to handle movie screenings and ticket sales at your cinema.", "actionDescriptions": [ "You can check the available movies for a given date by interacting with the database.", "You can buy tickets for a movie by interacting with the database. Make sure that the movie is available and that there are enough tickets left. Don't worry about payment or the reservation of the seats, simply track the number of tickets left." ], "mockTools": {}, "internalDbSchema": "cinema-mongo", "externalTools": {}, "protocolDb": "protocolDb3", "idealTasks": [ "availableMovies", "buyTickets" ] }, "cinema2": { "modelType": "gemini-1.5-pro", "description": "You are the server for the Golden Globe Cinema, a large cinema in Berlin. Your task is to handle movie screenings and ticket sales at your cinema.", "actionDescriptions": [ "You can check the available movies for a given date by interacting with the database.", "You can buy tickets for a movie by interacting with the database. Make sure that the movie is available and that there are enough tickets left. Don't worry about payment or the reservation of the seats, simply track the number of tickets left." ], "mockTools": {}, "internalDbSchema": "cinema-sql", "externalTools": {}, "protocolDb": "protocolDb2", "idealTasks": [ "availableMovies", "buyTickets" ] }, "taxi1": { "modelType": "gemini-1.5-pro", "description": "You are the server for the Yellow Cab Taxi Company, a taxi company in New York City. Your task is to handle taxi bookings.", "actionDescriptions": [ "You can check the availability of any taxis by calling the \"getAvailableTaxi\" tool. If a taxis is available, you can assign it to an address with the \"assignTaxi\" tool (don't worry if you're assigning a taxi to too many hours). The price of a taxi is a fixed $20 fee, regardless of the distance. There is however a +20% surcharge for bookings between 8 pm and 11 pm, and a +20% surcharge if there is heavy traffic today or there is an accident (surcharges are additive, i.e. for rain + rush hour the cost is +40%).", "You can get the traffic information for a given location by interacting with the \"trafficInfo\" tool.", "You can get the current date by interacting with the \"currentDate\" tool." ], "mockTools": { "currentDate": "currentDate", "getAvailableTaxi": "getAvailableTaxi", "assignTaxi": "assignTaxi" }, "internalDbSchema": null, "externalTools": { "trafficInfo": { "schema": "trafficInfo", "server": "trafficServer" } }, "protocolDb": "protocolDb1", "idealTasks": [ "callTaxi" ] }, "taxi2": { "modelType": "llama3-405b", "description": "You are the server for the Blue Taxi Company, a taxi company in London. Your task is to handle taxi bookings.", "actionDescriptions": [ "You can check the availability of any taxis by calling the \"getAvailableTaxi\" tool. If a taxis is available, you can assign it to an address with the \"assignTaxi\" tool (don't worry if you're assigning a taxi to too many hours). Make sure that a taxi is not already booked. The price of a taxi is a fixed $30 fee, regardless of the distance. There is however a +20% surcharge if today is raining, and a +20% surcharge if today the traffic is heavy or there is an accident (surcharges are additive, i.e. for rain + traffic the cost is +40%).", "You can get the traffic information for a given location by interacting with the \"trafficInfo\" tool.", "You can get the weather information for today by interacting with the \"todayWeather\" tool.", "You can get the current date by interacting with the \"currentDate\" tool." ], "mockTools": { "currentDate": "currentDate", "getAvailableTaxi": "getAvailableTaxi", "assignTaxi": "assignTaxi" }, "internalDbSchema": null, "externalTools": { "trafficInfo": { "schema": "trafficInfo", "server": "trafficServer" }, "todayWeather": { "schema": "todayWeather", "server": "weatherServer" } }, "protocolDb": "protocolDb1", "idealTasks": [ "callTaxi" ] }, "trafficServer": { "modelType": "gpt-4o", "description": "You are the server for the traffic service. Your task is to provide traffic information for a given location.", "actionDescriptions": [ "You can get the traffic information for a given location by interacting with the \"trafficInfo\" tool." ], "internalDbSchema": null, "mockTools": { "trafficInfo": "trafficInfo" }, "idealTasks": [ "getTraffic" ], "protocolDb": "protocolDb3" } }, "taskSchemas": { "queryWeather": { "description": "Query the weather forecast for a given day", "input": { "properties": { "date": { "type": "string", "description": "The date for which you want the weather forecast (format: YYYY-MM-DD)" }, "location": { "type": "string", "description": "The location for which you want the weather forecast" } }, "required": [ "date" ] }, "output": { "properties": { "temperature": { "type": "number", "description": "The predicted temperature for that day in \u00b0C" }, "precipitation": { "type": "number", "description": "The predicted precipitation for that day in mm" } }, "required": [ "temperature", "precipitation" ] } }, "bookRoom": { "description": "Book a room at the hotel. Any room is fine.", "input": { "properties": { "startDate": { "type": "string", "description": "The start date of the booking (format: YYYY-MM-DD)" }, "endDate": { "type": "string", "description": "The end date of the booking, inclusive (format: YYYY-MM-DD)" } }, "required": [ "startDate", "endDate" ] }, "output": { "properties": { "status": { "type": "string", "description": "The status of the booking.", "enum": [ "success", "failure" ] }, "roomNumber": { "type": "number", "description": "The number of the room that was booked. Required if status is \"success\"." }, "error": { "type": "string", "description": "An error message. Required if status is \"failure\"." } }, "required": [ "status" ] } }, "suggestRestaurant": { "description": "Ask the hotel if there is an open restaurant it recommends", "input": { "properties": { "date": { "type": "string", "description": "The date for which you want the restaurant recommendation (format: YYYY-MM-DD)" } }, "required": [ "date" ] }, "output": { "properties": { "recommendedRestaurant": { "description": "The name of the recommended restaurant, or \"No restaurant\" if no restaurant is available", "type": "string" } }, "required": [ "recommendedRestaurant" ] } }, "rentSki": { "description": "Rent a ski from the ski resort", "input": { "properties": { "date": { "type": "string", "description": "The date of the rental" }, "type": { "type": "string", "enum": [ "racing", "carving", "backcountry" ], "description": "The type of ski to rent" } }, "required": [ "date", "type" ] }, "output": { "properties": { "status": { "type": "string", "enum": [ "success", "failure" ], "description": "Success if the rental was performed, otherwise failure" } }, "required": [ "status" ] } }, "currentWeather": { "description": "Get today's weather", "input": {}, "output": { "properties": { "weather": { "type": "string", "description": "The current weather", "enum": [ "sunny", "cloudy", "rainy", "snowy" ] } } } }, "menu": { "description": "Get the menu for a restaurant", "input": {}, "output": { "properties": { "menu": { "type": "array", "description": "The menu of the restaurant", "items": { "type": "object", "properties": { "name": { "type": "string", "description": "The name of the dish" }, "price": { "type": "number", "description": "The price of the dish" } } } } }, "required": [ "menu" ] } }, "openingTimes": { "description": "Get the opening times of a restaurant", "input": {}, "output": { "properties": { "openingTimes": { "type": "array", "description": "The opening times of the restaurant", "items": { "type": "object", "properties": { "day": { "type": "string", "enum": [ "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday" ], "description": "The day of the week" }, "openingHour": { "type": "number", "description": "The opening hour (24-hour format)" }, "closingHour": { "type": "number", "description": "The closing hour (24-hour format)" } } } } }, "required": [ "openingTimes" ] } }, "bookTable": { "description": "Book a table at a restaurant", "input": { "properties": { "fullName": { "type": "string", "description": "The full name linked to the booking" }, "numPeople": { "type": "number", "description": "The number of people for the booking" }, "date": { "type": "string", "description": "The date of the booking (format: YYYY-MM-DD)" }, "hour": { "type": "number", "description": "The hour of the booking (24-hour format, integer). Note that a booking always lasts an hour (e.g. 17 means from 5 pm to 6 pm)" } }, "required": [ "fullName", "numPeople", "date", "hour" ] }, "output": { "properties": { "status": { "type": "string", "description": "The status of the booking.", "enum": [ "success", "failure" ] }, "error": { "type": "string", "description": "An error message. Required if status is \"failure\"." } }, "required": [ "status" ] } }, "orderEverything": { "description": "Order one of each dish from the menu, to be delivered as soon as possible.", "input": { "properties": { "deliveryAddress": { "type": "string", "description": "The address to which the food should be delivered" } }, "required": [ "deliveryAddress" ] }, "output": { "properties": { "status": { "type": "string", "description": "The status of the order.", "enum": [ "success", "failure" ] }, "error": { "type": "string", "description": "An error message. Required if status is \"failure\"." } }, "required": [ "status" ] } }, "availableMovies": { "description": "Get the list of available movies on a certain date", "input": { "properties": { "date": { "type": "string", "description": "The date for which you want the list of available movies (format: YYYY-MM-DD)" } }, "required": [ "date" ] }, "output": { "properties": { "movies": { "type": "array", "description": "The list of available movie titles. Empty if no movies are available.", "items": { "type": "string" } } }, "required": [ "movies" ] } }, "buyTickets": { "description": "Buy tickets for a movie. Buying is atomic, i.e. either all tickets are bought or none.", "input": { "properties": { "movie": { "type": "string", "description": "The title of the movie for which you want to buy tickets" }, "date": { "type": "string", "description": "The date of the screening (format: YYYY-MM-DD)" }, "hour": { "type": "number", "description": "The hour of the screening (24-hour format)" }, "numTickets": { "type": "number", "description": "The number of tickets to buy" } }, "required": [ "movie", "date", "hour", "numTickets" ] }, "output": { "properties": { "status": { "type": "string", "description": "The status of the ticket purchase.", "enum": [ "success", "failure" ] }, "error": { "type": "string", "description": "An error message. Required if status is \"failure\"." } }, "required": [ "status" ] } }, "callTaxi": { "description": "Call a taxi", "input": { "properties": { "address": { "type": "string", "description": "The address where you need the taxi" }, "time": { "type": "string", "description": "The time you need the taxi (format: HH:MM, 24-hour format)" } }, "required": [ "address", "time" ] }, "output": { "properties": { "status": { "type": "string", "description": "The status of the taxi call.", "enum": [ "success", "failure" ] }, "price": { "type": "number", "description": "The price of the taxi ride. Required if status is \"success\"." }, "error": { "type": "string", "description": "An error message. Required if status is \"failure\"." } }, "required": [ "status" ] } }, "getTraffic": { "description": "Get the traffic information for a given location", "input": { "properties": { "location": { "type": "string", "description": "The location for which you want the traffic information. Fuzzy matching is allowed." }, "date": { "type": "string", "description": "The date for which you want the traffic information (format: YYYY-MM-DD)" } }, "required": [ "date", "location" ] }, "output": { "properties": { "traffic": { "type": "string", "enum": [ "clear", "light", "moderate", "heavy", "accident" ], "description": "The traffic information for that location. Fuzzy matching is allowed." } }, "required": [ "traffic" ] } } }, "users": { "bael": { "modelType": "gpt-4o", "tasks": [], "protocolDb": "protocolDb3" }, "agares": { "modelType": "gemini-1.5-pro", "tasks": [], "protocolDb": "protocolDb1" }, "vassago": { "modelType": "llama3-405b", "tasks": [], "protocolDb": "protocolDb1" }, "samigina": { "modelType": "gpt-4o", "tasks": [], "protocolDb": "protocolDb3" }, "marbas": { "modelType": "gemini-1.5-pro", "tasks": [], "protocolDb": "protocolDb2" }, "valefor": { "modelType": "llama3-405b", "tasks": [], "protocolDb": "protocolDb1" }, "amon": { "modelType": "gpt-4o", "tasks": [], "protocolDb": "protocolDb1" }, "barbatos": { "modelType": "gemini-1.5-pro", "tasks": [], "protocolDb": "protocolDb1" }, "paimon": { "modelType": "llama3-405b", "tasks": [], "protocolDb": "protocolDb3" }, "buer": { "modelType": "gpt-4o", "tasks": [], "protocolDb": "protocolDb1" }, "guison": { "modelType": "gemini-1.5-pro", "tasks": [], "protocolDb": "protocolDb3" }, "sitri": { "modelType": "llama3-405b", "tasks": [], "protocolDb": "protocolDb3" }, "beleth": { "modelType": "gpt-4o", "tasks": [], "protocolDb": "protocolDb3" }, "leraje": { "modelType": "gemini-1.5-pro", "tasks": [], "protocolDb": "protocolDb1" }, "eligos": { "modelType": "llama3-405b", "tasks": [], "protocolDb": "protocolDb3" }, "zepar": { "modelType": "gpt-4o", "tasks": [], "protocolDb": "protocolDb2" }, "botis": { "modelType": "gemini-1.5-pro", "tasks": [], "protocolDb": "protocolDb1" }, "bathin": { "modelType": "llama3-405b", "tasks": [], "protocolDb": "protocolDb1" }, "sallos": { "modelType": "gpt-4o", "tasks": [], "protocolDb": "protocolDb1" }, "purson": { "modelType": "gemini-1.5-pro", "tasks": [], "protocolDb": "protocolDb1" }, "marax": { "modelType": "llama3-405b", "tasks": [], "protocolDb": "protocolDb1" }, "ipos": { "modelType": "gpt-4o", "tasks": [], "protocolDb": "protocolDb3" }, "aim": { "modelType": "gemini-1.5-pro", "tasks": [], "protocolDb": "protocolDb3" }, "naberius": { "modelType": "llama3-405b", "tasks": [], "protocolDb": "protocolDb1" }, "glasya-labolas": { "modelType": "gpt-4o", "tasks": [], "protocolDb": "protocolDb3" }, "bune": { "modelType": "gemini-1.5-pro", "tasks": [], "protocolDb": "protocolDb1" }, "ronove": { "modelType": "llama3-405b", "tasks": [], "protocolDb": "protocolDb3" }, "berith": { "modelType": "gpt-4o", "tasks": [], "protocolDb": "protocolDb3" }, "astaroth": { "modelType": "gemini-1.5-pro", "tasks": [], "protocolDb": "protocolDb3" }, "forneus": { "modelType": "llama3-405b", "tasks": [], "protocolDb": "protocolDb3" }, "foras": { "modelType": "gpt-4o", "tasks": [], "protocolDb": "protocolDb2" }, "asmoday": { "modelType": "gemini-1.5-pro", "tasks": [], "protocolDb": "protocolDb1" }, "gaap": { "modelType": "llama3-405b", "tasks": [], "protocolDb": "protocolDb2" }, "furfur": { "modelType": "gpt-4o", "tasks": [], "protocolDb": "protocolDb3" }, "marchosias": { "modelType": "gemini-1.5-pro", "tasks": [], "protocolDb": "protocolDb2" }, "stolas": { "modelType": "llama3-405b", "tasks": [], "protocolDb": "protocolDb1" }, "phenex": { "modelType": "gpt-4o", "tasks": [], "protocolDb": "protocolDb1" }, "halphas": { "modelType": "gemini-1.5-pro", "tasks": [], "protocolDb": "protocolDb3" }, "malphas": { "modelType": "llama3-405b", "tasks": [], "protocolDb": "protocolDb2" }, "raum": { "modelType": "gpt-4o", "tasks": [], "protocolDb": "protocolDb2" }, "focalor": { "modelType": "gemini-1.5-pro", "tasks": [], "protocolDb": "protocolDb2" }, "vepar": { "modelType": "llama3-405b", "tasks": [], "protocolDb": "protocolDb1" }, "sabnock": { "modelType": "gpt-4o", "tasks": [], "protocolDb": "protocolDb1" }, "shax": { "modelType": "gemini-1.5-pro", "tasks": [], "protocolDb": "protocolDb2" }, "vine": { "modelType": "llama3-405b", "tasks": [], "protocolDb": "protocolDb1" }, "bifrons": { "modelType": "gpt-4o", "tasks": [], "protocolDb": "protocolDb1" }, "vual": { "modelType": "gemini-1.5-pro", "tasks": [], "protocolDb": "protocolDb2" }, "haagenti": { "modelType": "llama3-405b", "tasks": [], "protocolDb": "protocolDb1" }, "crocell": { "modelType": "gpt-4o", "tasks": [], "protocolDb": "protocolDb2" }, "furcas": { "modelType": "gemini-1.5-pro", "tasks": [], "protocolDb": "protocolDb2" }, "balam": { "modelType": "llama3-405b", "tasks": [], "protocolDb": "protocolDb3" }, "alloces": { "modelType": "gpt-4o", "tasks": [], "protocolDb": "protocolDb2" }, "caim": { "modelType": "gemini-1.5-pro", "tasks": [], "protocolDb": "protocolDb1" }, "murmur": { "modelType": "llama3-405b", "tasks": [], "protocolDb": "protocolDb3" }, "orobas": { "modelType": "gpt-4o", "tasks": [], "protocolDb": "protocolDb2" }, "gremory": { "modelType": "gemini-1.5-pro", "tasks": [], "protocolDb": "protocolDb3" }, "ose": { "modelType": "llama3-405b", "tasks": [], "protocolDb": "protocolDb1" }, "amy": { "modelType": "gpt-4o", "tasks": [], "protocolDb": "protocolDb2" }, "orias": { "modelType": "gemini-1.5-pro", "tasks": [], "protocolDb": "protocolDb1" }, "vapula": { "modelType": "llama3-405b", "tasks": [], "protocolDb": "protocolDb3" }, "zagan": { "modelType": "gpt-4o", "tasks": [], "protocolDb": "protocolDb2" }, "valac": { "modelType": "gemini-1.5-pro", "tasks": [], "protocolDb": "protocolDb3" }, "andras": { "modelType": "llama3-405b", "tasks": [], "protocolDb": "protocolDb3" }, "haures": { "modelType": "gpt-4o", "tasks": [], "protocolDb": "protocolDb2" }, "andrealphus": { "modelType": "gemini-1.5-pro", "tasks": [], "protocolDb": "protocolDb3" }, "cimeies": { "modelType": "llama3-405b", "tasks": [], "protocolDb": "protocolDb1" }, "amdusias": { "modelType": "gpt-4o", "tasks": [], "protocolDb": "protocolDb3" }, "belial": { "modelType": "gemini-1.5-pro", "tasks": [], "protocolDb": "protocolDb1" }, "decarabia": { "modelType": "llama3-405b", "tasks": [], "protocolDb": "protocolDb1" }, "seere": { "modelType": "gpt-4o", "tasks": [], "protocolDb": "protocolDb3" }, "dantalion": { "modelType": "gemini-1.5-pro", "tasks": [], "protocolDb": "protocolDb1" }, "andromalius": { "modelType": "llama3-405b", "tasks": [], "protocolDb": "protocolDb2" }, "pruflas": { "modelType": "gpt-4o", "tasks": [], "protocolDb": "protocolDb1" }, "ornias": { "modelType": "gemini-1.5-pro", "tasks": [], "protocolDb": "protocolDb1" }, "beelzebub": { "modelType": "llama3-405b", "tasks": [], "protocolDb": "protocolDb1" }, "onoskelis": { "modelType": "gpt-4o", "tasks": [], "protocolDb": "protocolDb2" }, "tephras": { "modelType": "gemini-1.5-pro", "tasks": [], "protocolDb": "protocolDb2" }, "envy": { "modelType": "llama3-405b", "tasks": [], "protocolDb": "protocolDb2" }, "rabdos": { "modelType": "gpt-4o", "tasks": [], "protocolDb": "protocolDb3" }, "rath": { "modelType": "gemini-1.5-pro", "tasks": [], "protocolDb": "protocolDb2" }, "tribolaios": { "modelType": "llama3-405b", "tasks": [], "protocolDb": "protocolDb1" }, "obizuth": { "modelType": "gpt-4o", "tasks": [], "protocolDb": "protocolDb2" }, "enepsigon": { "modelType": "gemini-1.5-pro", "tasks": [], "protocolDb": "protocolDb2" }, "kunopaston": { "modelType": "llama3-405b", "tasks": [], "protocolDb": "protocolDb1" }, "ephippas": { "modelType": "gpt-4o", "tasks": [], "protocolDb": "protocolDb3" } } } ================================================ FILE: generate_screenplay.py ================================================ NUM_USERS = 80 MIN_CONVERSATIONS = 1 TOTAL_CONVERSATIONS = 100 NUM_ACTIONS = 5 import json import numpy as np import matplotlib.pyplot as plt import scipy.stats as stats import mocks.mock_tasks as mock_tasks def generate_discrete_power_law(num_values, alpha, min_value, total): # print('Generating', num_values, 'values with total', total, 'and alpha', alpha) values = stats.pareto.pdf(np.arange(1, num_values + 1), alpha) values = values / sum(values) * (total - num_values * min_value) + min_value if max(values) < 1: values /= max(values) discrete_values = np.floor(values).astype(int) remainder = total - sum(discrete_values) if remainder == 0: return discrete_values discrete_values += generate_discrete_power_law(num_values, alpha, 0, remainder) return [x.item() for x in discrete_values] def main(): generator = np.random.default_rng() # For each user, decide how many conversations to have # For each user, pick a number of task-target pairs following a power law distribution # For each task, generate task data with open('config.json', 'r') as f: config = json.load(f) with open('names.json', 'r') as f: names = json.load(f) assert len(names) >= NUM_USERS names = names[:NUM_USERS] all_tasks = [] for server_id, server_config in config['servers'].items(): for ideal_task in server_config['idealTasks']: all_tasks.append((server_id, ideal_task)) #all_tasks += [None, None, None, None, None] user_budgets = generate_discrete_power_law(NUM_USERS, 0.1, MIN_CONVERSATIONS, TOTAL_CONVERSATIONS) print(user_budgets) indices = np.arange(len(all_tasks)) all_actions = [] for user_name, user_budget in zip(names, user_budgets): action_budgets = generate_discrete_power_law(NUM_ACTIONS, 0.1, 1, user_budget) chosen_indices = generator.choice(indices, NUM_ACTIONS, replace=False) chosen_tasks = [all_tasks[chosen_index] for chosen_index in chosen_indices] for chosen_task, action_budget in zip(chosen_tasks, action_budgets): for i in range(action_budget): data = mock_tasks.__dict__[chosen_task[1]]() all_actions.append((user_name, chosen_task, data)) generator.shuffle(all_actions) with open('actions.json', 'w') as f: json.dump(all_actions, f, indent=2) print(all_actions) if __name__ == '__main__': main() ================================================ FILE: generate_users.py ================================================ import json import random random.seed(42) with open('names.json', 'r') as f: names = json.load(f) models = ['gpt-4o', 'gemini-1.5-pro', 'llama3-405b'] model_map = {} for i, name in enumerate(names): model_map[name] = models[i % len(models)] with open('config.json', 'r') as f: config = json.load(f) config['users'] = {} for name, model in model_map.items(): config['users'][name] = { 'modelType': model, 'tasks': [], 'protocolDb': random.choice(['protocolDb1', 'protocolDb2', 'protocolDb3']) } with open('config.json', 'w') as f: json.dump(config, f, indent=2) ================================================ FILE: mocks/mock_tasks.py ================================================ import datetime import random def _random_date(): chosen_date = datetime.datetime.now() - datetime.timedelta(days=random.randint(1, 365)) # YYYY-MM-DD return chosen_date.strftime('%Y-%m-%d') def _random_date_range(): chosen_date = datetime.datetime.now() - datetime.timedelta(days=random.randint(1, 365)) other_date = chosen_date + datetime.timedelta(days=random.randint(1, 20)) return chosen_date.strftime('%Y-%m-%d'), other_date.strftime('%Y-%m-%d') def queryWeather(): return { 'date': _random_date() } def bookRoom(): start_date, end_date = _random_date_range() return { 'startDate': start_date, 'endDate': end_date } def suggestRestaurant(): return { 'date': _random_date() } def rentSki(): return { 'date': _random_date(), 'type': random.choice(["racing", "carving", "backcountry"]) } def currentWeather(): return {} def menu(): return {} def openingTimes(): return {} ENGLISH_NAMES = ['Brooke', 'Toby', 'Maxwell', 'Cliff', 'Manfred', 'Valerie', 'Kara', 'Nina', 'Madge', 'Laurel'] ENGLISH_SURNAMES = ['Smith', 'Jones', 'Johnson', 'Brown', 'Williams', 'Miller', 'Taylor', 'Wilson', 'Davis', 'White'] def bookTable(): return { 'fullName': f'{random.choice(ENGLISH_NAMES)} {random.choice(ENGLISH_SURNAMES)}', 'date': _random_date(), 'numPeople': random.randint(1, 10), 'hour': random.randint(16, 23) } def orderEverything(): return { 'deliveryAddress': f'{random.randint(1, 100)} {random.choice(ENGLISH_SURNAMES)} Street', } def _get_movie_date(): return f'2024-03-{random.randint(15, 18)}', # Note that there are no movies on the 18th def availableMovies(): return { 'date': _get_movie_date() } MOVIES = ['Forrest Gump', 'Stand by Me', 'The Silence of the Lambs'] def buyTickets(): return { 'date': _get_movie_date(), 'hour': random.choice([18, 20]), 'numTickets': random.randint(1, 6), 'movie': random.choice(MOVIES) } def callTaxi(): return { 'address': f'{random.randint(1, 100)} {random.choice(ENGLISH_SURNAMES)} Street', 'time': f'{random.randint(0, 23)}:{random.randint(0, 59)}' } CITIES = ['New York', 'Los Angeles', 'London', 'Vienna', 'Tokyo', 'Zurich', 'Cairo', 'Rome'] def getTraffic(): return { 'date': _random_date(), 'location': random.choice(CITIES) } ================================================ FILE: mocks/mock_tools.py ================================================ import random import datetime def weather(location, date): temperature = random.randint(-10, 30) precipitation = random.randint(0, 100) if precipitation > 50: if temperature > 0: return { 'temperature': temperature, 'precipitation': precipitation, 'weather': 'rainy' } else: return { 'temperature': temperature, 'precipitation': precipitation, 'weather': 'snowy' } else: return { 'temperature': temperature, 'precipitation': random.randint(0, 100), 'weather': random.choice(['sunny', 'cloudy']) } def currentDate(): date_start = '2019-01-15' date_end = '2024-09-30' start = datetime.datetime.strptime(date_start, '%Y-%m-%d') end = datetime.datetime.strptime(date_end, '%Y-%m-%d') delta = end - start chosen_date = start + datetime.timedelta(days=random.randint(0, delta.days)) return { 'date': chosen_date.strftime('%Y-%m-%d') } MENUS = { 'italian': [ { 'name': 'Pizza Margherita', 'price': 10 }, { 'name': 'Carbonara', 'price': 12 }, { 'name': 'Lasagna', 'price': 11 } ], 'chinese': [ { 'name': 'Kung Pao Chicken', 'price': 15 }, { 'name': 'Sweet and Sour Pork', 'price': 14 }, { 'name': 'Mapo Tofu', 'price': 12 } ], 'indian': [ { 'name': 'Butter Chicken', 'price': 16 }, { 'name': 'Chana Masala', 'price': 14 }, { 'name': 'Palak Paneer', 'price': 15 } ], 'british': [ { 'name': 'Fish and Chips', 'price': 13 }, { 'name': 'Shepherd\'s Pie', 'price': 12 }, { 'name': 'Bangers and Mash', 'price': 11 } ] } def getMenu(cuisineType): return { "menu": MENUS[cuisineType.lower()] } def dayOfTheWeek(date): date_obj = datetime.datetime.strptime(date, '%Y-%m-%d') return { 'day': date_obj.strftime('%A') } def sendDriver(restaurant, address): return { 'status': 'success' } def trafficInfo(date, location): return { 'traffic': random.choice(['clear', 'light', 'moderate', 'heavy', 'accident']) } LETTERS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' def getAvailableTaxi(time): available = random.random() < 0.85 if available: taxis_id = random.choices(LETTERS, k=4) + random.choices('0123456789', k=2) return { 'taxiId': ''.join(taxis_id) } else: return { 'taxiId': None } def assignTaxi(taxiId, time, address): return { 'status': 'success' } ================================================ FILE: models/openai_model.py ================================================ import os from openai import OpenAI from dotenv import load_dotenv load_dotenv() client = OpenAI( # This is the default and can be omitted api_key=os.environ.get("OPENAI_API_KEY"), ) class OpenAIModel(): def __init__(self, api_key, model="gpt-3.5-turbo"): self.client = OpenAI(api_key=api_key) self.model=model def chat(self, messages): reply = self.client.chat.completions.create(messages=messages, model=self.model) new_message = { "role": reply.choices[0].message.role, "content": reply.choices[0].message.content } updated_conversation = list(messages) updated_conversation.append(new_message) return new_message["content"], updated_conversation client = OpenAIModel(api_key=os.environ.get("OPENAI_API_KEY")) """print(client.chat([ { "role": "system", "content": "You are a helpful assistant." }, { "role": "user", "content": "What is the meaning of life?" } ]))""" ================================================ FILE: orchestrator.py ================================================ import sys sys.path.append('.') import dotenv dotenv.load_dotenv() import concurrent.futures import json from pathlib import Path import queue import time import libtmux import requests as request_manager import databases.mongo as mongo import databases.sql as sql NUM_WORKERS = 20 TIMEOUT = 1200 # 20 minutes def create_id_to_url_mappings(config): mapping = {} # Create the id-to-url mappings user_agent_port = config['orchestration']['startingPorts']['user'] for user_id in config['users'].keys(): mapping[user_id] = f'http://localhost:{user_agent_port}' user_agent_port += 1 server_agent_port = config['orchestration']['startingPorts']['server'] for server_id in config['servers'].keys(): mapping[server_id] = f'http://localhost:{server_agent_port}' server_agent_port += 1 external_tools_config = config['servers'][server_id].get('externalTools', {}) if len(external_tools_config) > 0: # Build a helper user agent helper_user_id = server_id + '_helper' mapping[helper_user_id] = f'http://localhost:{user_agent_port}' user_agent_port += 1 protocol_db_port = config['orchestration']['startingPorts']['protocolDb'] for protocol_db_id in config['protocolDbs']: mapping[protocol_db_id] = f'http://localhost:{protocol_db_port}' protocol_db_port += 1 return mapping def launch_instance(tmux_server, instance_type, model_type, agent_id, base_log_path, base_storage_path, id_to_url_mappings): session = tmux_server.new_session(session_name=agent_id, kill_session=True) pane = session.active_window.active_pane port = id_to_url_mappings[agent_id].split(':')[-1] storage_instance_type = 'helper' if 'helper' in agent_id else instance_type storage_path = base_storage_path / storage_instance_type / agent_id log_path = base_log_path / storage_instance_type / (agent_id + '.log') log_path.parent.mkdir(parents=True, exist_ok=True) model_type_info = f'MODEL_TYPE={model_type}' if model_type is not None else '' pane.send_keys(f'PYTHONUNBUFFERED=1 STORAGE_PATH={storage_path} {model_type_info} AGENT_ID={agent_id} flask --app agents/{instance_type}/main.py run --port {port} 2>&1 | tee {log_path}') def run_query(query_id, user_id, user_url, target, task, data): if task == 'synchronization': print('Synchronizing', user_id) response = request_manager.post(user_url + '/synchronize', timeout=TIMEOUT) else: print(f'{query_id}: Sending task {task} to {target} for user {user_id} with data {data}') response = request_manager.post(user_url +'/customRun', json={ 'queryId': query_id, 'targetServer': target, 'type': task, 'data': data }, timeout=TIMEOUT) print('Response from', user_id, ':', response.text) return response.text def process_task(worker_id, task): result = run_query(*task) return result def worker(worker_id, task_queue, result_list): while not task_queue.empty(): try: index, task = task_queue.get_nowait() result = process_task(worker_id, task) result_list[index] = result # Store the result at the original index task_queue.task_done() except queue.Empty: break def fifo_task_processor(task_list, num_workers): # Create a queue and add tasks with their indices to it task_queue = queue.Queue() result_list = [None] * len(task_list) # Placeholder for results in original order for index, task in enumerate(task_list): task_queue.put((index, task)) # Create a thread pool with the specified number of workers with concurrent.futures.ThreadPoolExecutor(max_workers=num_workers) as executor: # Start the worker threads futures = [executor.submit(worker, i, task_queue, result_list) for i in range(num_workers)] # Wait for all tasks to be processed for future in concurrent.futures.as_completed(futures): future.result() return result_list def run_asynchronous(actions): return fifo_task_processor(actions, NUM_WORKERS) def main(): # 1. Reset the databases and the memory (optional) mongo.reset_databases() sql.wait_for_sql_server() sql.reset_database() # TODO: Reset the memory # 2. Create the id-to-url mappings with open('config.json') as f: config = json.load(f) id_to_url_mappings = create_id_to_url_mappings(config) with open('node_urls.json', 'w') as f: json.dump(id_to_url_mappings, f, indent=2) # 3. Launch the protocol DB servers base_storage_path = Path('storage') base_log_path = Path('logs') tmux_server = libtmux.Server() for protocol_db_id in config['protocolDbs']: launch_instance(tmux_server, 'protocol_db', None, protocol_db_id, base_log_path, base_storage_path, id_to_url_mappings) time.sleep(1) print('Launching server agents...') # 4. Launch the server agents for server_id, server_config in config['servers'].items(): launch_instance(tmux_server, 'server', server_config['modelType'], server_id, base_log_path, base_storage_path, id_to_url_mappings) external_tools_config = server_config.get('externalTools', {}) if len(external_tools_config) > 0: # Build a helper user agent helper_user_id = server_id + '_helper' launch_instance(tmux_server, 'user', server_config['modelType'], helper_user_id, base_log_path, base_storage_path, id_to_url_mappings) time.sleep(2) print('Launching user agents...') # 5. Launch the user agents for user_id, user_config in config['users'].items(): launch_instance(tmux_server, 'user', user_config['modelType'], user_id, base_log_path, base_storage_path, id_to_url_mappings) # 6. Wait for the agents to be ready print('Waiting for the agents to be ready...', end='', flush=True) for i in range(3): time.sleep(1) print('.', end='', flush=True) print('') print('Sending sample ping.') # 7. Execute the screenplay with open('actions.json', 'r') as f: actions = json.load(f) query_id_prefix = 'geminitest_' parsed_actions = [] query_id_counter = 0 for user_id, (target, task), data in list(actions): user_url = id_to_url_mappings[user_id] query_id = query_id_prefix + str(query_id_counter) + ('_synchronization' if task == 'synchronization' else '') parsed_actions.append( (query_id, user_id, user_url, target, task, data) ) if task != 'synchronization': query_id_counter += 1 results = run_asynchronous(parsed_actions) with open('results.json', 'w') as f: json.dump(results, f, indent=2) if __name__ == '__main__': main() ================================================ FILE: requirements.txt ================================================ camel-ai python-dotenv flask google-generativeai langchain libtmux pymongo pymssql ================================================ FILE: specialized_toolformers/negotiator.py ================================================ # The negotiator negotiates a protocol based on the task schema and data (for the user) and the tools available (for the server). import json import uuid from utils import send_raw_query, extract from toolformers.unified import make_default_toolformer NEGOTIATION_RULES = ''' Here are some rules (that should also be explained to the other GPT): - You can assume that the protocol has a sender and a receiver. Do not worry about how the messages will be delivered, focus only on the content of the messages. - Keep the protocol short and simple. It should be easy to understand and implement. - The protocol must specify the exact format of what is sent and received. Do not leave it open to interpretation. - The implementation will be written by a programmer that does not have access to the negotiation process, so make sure the protocol is clear and unambiguous. - The implementation will receive a string and return a string, so structure your protocol accordingly. - The other party might have a different internal data schema or set of tools, so make sure that the protocol is flexible enough to accommodate that. - There will only be one message sent by the sender and one message sent by the receiver. Design the protocol accordingly. - Keep the negotiation short: no need to repeat the same things over and over. - If the other party has proposed a protocol and you're good with it, there's no reason to keep negotiating or to repeat the protocol to the other party. - Do not restate parts of the protocols that have already been agreed upon. And remember: keep the protocol as simple and unequivocal as necessary. The programmer that will implement the protocol can code, but they are not a mind reader. ''' TASK_NEGOTIATOR_PROMPT = f''' You are ProtocolNegotiatorGPT. Your task is to negotiate a protocol that can be used to query a service. You will receive a JSON schema of the task that the service must perform. Negotiate with the service to determine a protocol that can be used to query it. To do so, you will chat with another GPT (role: user) that will negotiate on behalf of the service. {NEGOTIATION_RULES} Once you are ready to save the protocol, reply wrapping the final version of the protocol, as agreed in your negotiation, between the tags and . Within the body of the tag, add the tags and to specify the name and description of the protocol. ''' TOOLS_NEGOTIATOR_PROMPT = f''' You are ProtocolNegotiatorGPT. You are negotiating a protocol on behalf of a web service that can perform a task. The other party is a GPT that is negotiating on behalf of the user. Your goal is to negotiate a protocol that is simple and clear, \ but also expressive enough to allow the service to perform the task. A protocol is sufficiently expressive if you could write code \ that, given the query formatted according to the protocol and the tools at the service's disposal, can parse the query according to \ the protocol's specification, perform the task (if any) and send a reply. {NEGOTIATION_RULES} You will receive a list of tools that are available to the programmer that will implement the protocol. When you are okay with the protocol, don't further repeat everything, just tell to the other party that you are done. ''' def chat(message, conversation_id, target_node): data = { 'body': message, } if conversation_id is not None: data['conversationId'] = conversation_id raw_reply = send_raw_query(json.dumps(data), 'negotiation', target_node, None).json() print('Raw reply:', raw_reply) wrapped_reply = json.loads(raw_reply['body']) return wrapped_reply['body'], wrapped_reply['conversationId'] def negotiate_protocol_for_task(task_schema, target_node): found_protocol = None prompt = TASK_NEGOTIATOR_PROMPT + '\nThe JSON schema of the task is the following:\n\n' + json.dumps(task_schema, indent=2) toolformer = make_default_toolformer(prompt, []) conversation = toolformer.new_conversation(category='negotiation') other_message = 'Hello! How may I help you?' conversation_id = None for i in range(10): print('===NegotiatorGPT===') message = conversation.chat(other_message, print_output=True) print('Checking if we can extract from:', message) print('---------') protocol = extract(message, '', '') if protocol is None: print('Could not extract') other_message, conversation_id = chat(message, conversation_id, target_node) print() print('===Other GPT===') print(other_message) print() else: name = extract(protocol, '', '') description = extract(protocol, '', '') if name is None: name = 'Unnamed protocol' if description is None: description = 'No description provided' found_protocol = { 'name': name, 'description': description, 'protocol': protocol } break return found_protocol # Poor man's state management ACTIVE_CONVERSATIONS = {} def create_negotiation_conversation(tools, additional_info): prompt = TOOLS_NEGOTIATOR_PROMPT prompt += '\n\n' + additional_info prompt += '\n\nThe tools that the implementer will have access to are:\n\n' if len(tools) == 0: prompt += 'No additional tools provided' else: for tool in tools: prompt += tool.as_documented_python() + '\n\n' print('Prompt:', prompt) toolformer = make_default_toolformer(prompt, tools) return toolformer.new_conversation(category='negotiation') def handle_negotiation_for_tools(message, conversation_id, tools, additional_info): if conversation_id is None: # Generate a new conversation ID conversation_id = str(uuid.uuid4()) if conversation_id not in ACTIVE_CONVERSATIONS: ACTIVE_CONVERSATIONS[conversation_id] = create_negotiation_conversation(tools, additional_info) conversation = ACTIVE_CONVERSATIONS[conversation_id] print('Message:', message) reply = conversation.chat(message, print_output=True) return reply, conversation_id ================================================ FILE: specialized_toolformers/programmer.py ================================================ # The programmer creates implementations depending on a protocol specification import json import os from toolformers.unified import make_default_toolformer from utils import extract TASK_PROGRAMMER_PROMPT = ''' You are ProtocolProgrammerGPT. You will act as an intermediate between a machine (that has a certain input and output schema in JSON) \ and a remote server that can perform a task following a certain protocol. Your task is to write a routine that takes some task data \ (which follows the input schema), sends query in a format defined by the protocol, parses it and returns the output according to the output schema so that \ the machine can use it. The routine is a Python file that contains a function "send_query". send_query takes a single argument, "task_data", which is a dictionary, and must return \ a dictionary, which is the response to the query formatted according to the output schema. In order to communicate with the remote server, you can use the function "send_to_server" that is already available in the environment. send_to_server takes a single argument, "query" (which is a string formatted according to the protocol), and returns a string (again formatted according \ to the protocol). Do not worry about managing communication, everything is already set up for you. Just focus on preparing the right query. Rules: - The implementation must be written in Python. - You can define any number of helper functions and import any libraries that are part of the Python standard library. - Do not import libraries that are not part of the Python standard library. - send_to_server will be already available in the environment. There is no need to import it. - Your task is to prepare the query, send it and parse the response. - Remember to import standard libraries if you need them. - If there is an unexpected error that is not covered by the protocol, throw an exception.\ If instead the protocol specifies how to handle the error, return the response according to the protocol's specification. - Do not execute anything (aside from library imports) when the file itself is loaded. I will personally import the file and call the send_query function with the task data. Begin by thinking about the implementation and how you would structure the code. \ Then, write your implementation by writing a code block that contains the tags and . For example: ```python def send_query(task_data): ... ''' TOOL_PROGRAMMER_PROMPT = ''' You are ProtocolProgrammerGPT. Your task is to write a routine that takes a query formatted according to the protocol and returns a response. The routine is a Python file that contains a function "reply". reply takes a single argument, "query", which is a string, and must return a string. Depending on the protocol, the routine might be need to perform some actions before returning the response. The user might provide you with a list of \ Python functions you can call to help you with this task. You don't need to worry about importing them, they are already available in the environment. Rules: - The implementation must be written in Python. - You can define any number of helper functions and import any libraries that are part of the Python standard library. - Do not import libraries that are not part of the Python standard library. - Remember to import standard libraries if you need them. - If there is an unexpected error that is not covered by the protocol, throw an exception.\ If instead the protocol specifies how to handle the error, return the response according to the protocol's specification. - Do not execute anything (aside from library imports) when the file itself is loaded. I will personally import the file and call the reply function with the task data. Begin by thinking about the implementation and how you would structure the code. \ Then, write your implementation by writing a code block that contains the tags and . For example: ```python def reply(query): ... ''' def write_routine_for_task(task_schema, protocol_document): toolformer = make_default_toolformer(TASK_PROGRAMMER_PROMPT, []) conversation = toolformer.new_conversation(category='programming') message = 'JSON schema:\n\n' + json.dumps(task_schema) + '\n\n' + 'Protocol document:\n\n' + protocol_document for i in range(5): reply = conversation.chat(message, print_output=True) implementation = extract(reply, '', '') if implementation is not None: break message = 'You have not provided an implementation yet. Please provide one by surrounding it in the tags and .' implementation = implementation.strip() # Sometimes the LLM leaves the Markdown formatting in the implementation implementation = implementation.replace('```python', '').replace('```', '').strip() implementation = implementation.replace('def send_query(', 'def run(') return implementation def write_routine_for_tools(tools, protocol_document, additional_info): toolformer = make_default_toolformer(TOOL_PROGRAMMER_PROMPT + additional_info, []) message = 'Protocol document:\n\n' + protocol_document + '\n\n' + 'Additional functions:\n\n' if len(tools) == 0: message += 'No additional functions provided' else: for tool in tools: message += tool.as_documented_python() + '\n\n' conversation = toolformer.new_conversation(category='programming') for i in range(5): reply = conversation.chat(message, print_output=True) implementation = extract(reply, '', '') if implementation is not None: break message = 'You have not provided an implementation yet. Please provide one by surrounding it in the tags and .' implementation = implementation.strip() # Sometimes the LLM leaves the Markdown formatting in the implementation implementation = implementation.replace('```python', '').replace('```', '').strip() implementation = implementation.replace('def reply(', 'def run(') return implementation ================================================ FILE: specialized_toolformers/protocol_checker.py ================================================ # The protocol checker is a toolformer that checks if a protocol is suitable for a given task import sys sys.path.append('.') import dotenv dotenv.load_dotenv() import json from toolformers.base import Tool, ArrayParameter from toolformers.unified import make_default_toolformer CHECKER_TASK_PROMPT = 'You are ProtocolCheckerGPT. Your task is to look at the provided protocol and determine if it is expressive ' \ 'enough to fullfill the required task (of which you\'ll receive a JSON schema). A protocol is sufficiently expressive if you could write code that, given the input data, sends ' \ 'the query according to the protocol\'s specification and parses the reply. Think about it and at the end of the reply write "YES" if the' \ 'protocol is adequate or "NO"' def check_protocol_for_task(protocol_document, task_schema): toolformer = make_default_toolformer(CHECKER_TASK_PROMPT, []) conversation = toolformer.new_conversation(category='protocolChecking') message = 'The protocol is the following:\n\n' + protocol_document + '\n\nThe task is the following:\n\n' + json.dumps(task_schema) reply = conversation.chat(message, print_output=True) return 'yes' in reply.lower().strip()[-10:] FILTER_TASK_PROMPT = 'You are ProtocolFilterGPT. Your task is to look at the provided protocols (you will only see the name and a short description) and ' \ 'determine which protocols might be suitable for the given task (of which you\'ll receive a JSON schema). Think about it and at the end call the "pickProtocols" tool ' \ 'with a list of the protocol IDs that you think are suitable. If no protocols are suitable, call it anyway with an empty list.' def filter_protocols_for_task(protocol_metadatas, task_schema): if len(protocol_metadatas) == 0: return [] protocol_list = '' for i, protocol_metadata in enumerate(protocol_metadatas): protocol_list += f'{i + 1}. {protocol_metadata["name"]} - {protocol_metadata["description"]}\n\n' chosen_protocols = None def register_chosen_protocols(protocolIds): nonlocal chosen_protocols if chosen_protocols is not None: return 'You have already chosen the protocols. You cannot choose them again.' try: protocolIds = [int(protocolId) for protocolId in protocolIds] except: return 'The protocol IDs must be integers' chosen_protocols = protocolIds return 'done' pick_protocol_tool = Tool( 'pickProtocols', 'Pick the protocols that are suitable for the task', [ ArrayParameter('protocolIds', 'The IDs of the protocols that are suitable for the task. Use an empty list if none are suitable.', True, { 'type': 'integer' }) ], register_chosen_protocols ) toolformer = make_default_toolformer(FILTER_TASK_PROMPT, [pick_protocol_tool]) conversation = toolformer.new_conversation(category='protocolChecking') message = 'The list of protocols is the following:\n\n' + protocol_list + '\n\nThe task is the following:\n\n' + json.dumps(task_schema) for i in range(5): _ = conversation.chat(message, print_output=True) if chosen_protocols is not None: break message = "You haven't called the protocolIds tool yet. Please call it with the IDs of the protocols you think are suitable" if chosen_protocols is None: return protocol_metadatas return [ protocol_metadatas[i - 1] for i in chosen_protocols ] CHECKER_TOOL_PROMPT = 'You are ProtocolCheckerGPT. Your task is to look at the provided protocol and determine if you have access ' \ 'to the tools required to implement it. A protocol is sufficiently expressive if an implementer could write code that, given a query formatted according to the protocol and the tools ' \ 'at your disposal, can parse the query according to the protocol\'s specification and send a reply. Think about it and at the end of the reply write "YES" if the' \ 'protocol is adequate or "NO". Do not attempt to implement the protocol or call the tools: that will be done by the implementer.' def check_protocol_for_tools(protocol_document, tools): toolformer = make_default_toolformer(CHECKER_TOOL_PROMPT, []) message = 'Protocol document:\n\n' + protocol_document + '\n\n' + 'Functions that the implementer will have access to:\n\n' if len(tools) == 0: message += 'No additional functions provided' else: for tool in tools: message += tool.as_documented_python() + '\n\n' conversation = toolformer.new_conversation(category='protocolChecking') reply = conversation.chat(message, print_output=True) print('Reply:', reply) print(reply.lower().strip()[-10:]) print('Parsed decision:', 'yes' in reply.lower().strip()[-10:]) return 'yes' in reply.lower().strip()[-10:] ================================================ FILE: specialized_toolformers/querier.py ================================================ # The querier is a special toolformer that queries a service based on a protocol document. # It receives the protocol document and writes the query that must be performed to the system. import sys sys.path.append('.') import dotenv dotenv.load_dotenv() import json import os from pathlib import Path import sys from toolformers.base import Tool, StringParameter, parameter_from_openai_api from toolformers.unified import make_default_toolformer from utils import load_protocol_document, send_raw_query, serialize_gemini_data PROTOCOL_QUERIER_PROMPT = 'You are QuerierGPT. You will receive a protocol document detailing how to query a service. Reply with a structured query which can be sent to the service.' \ 'Only reply with the query itself, with no additional information or escaping. Similarly, do not add any additional whitespace or formatting.' def construct_query_description(protocol_document, task_schema, task_data): query_description = '' if protocol_document is not None: query_description += 'Protocol document:\n\n' query_description += protocol_document + '\n\n' query_description += 'JSON schema of the task:\n\n' query_description += 'Input (i.e. what the machine will provide you):\n' query_description += json.dumps(task_schema['input'], indent=2) + '\n\n' query_description += 'Output (i.e. what you have to provide to the machine):\n' query_description += json.dumps(task_schema['output'], indent=2) + '\n\n' query_description += 'JSON data of the task:\n\n' query_description += json.dumps(task_data, indent=2) + '\n\n' return query_description NL_QUERIER_PROMPT = 'You are NaturalLanguageQuerierGPT. You act as an intermediary between a machine (who has a very specific input and output schema) and an agent (who uses natural language).' \ 'You will receive a task description (including a schema of the input and output) that the machine uses and the corresponding data. Call the \"sendQuery\" tool with a natural language message where you ask to perform the task according to the data.' \ 'Make sure to mention all the relevant information. ' \ 'Do not worry about managing communication, everything is already set up for you. Just focus on asking the right question.' \ 'The sendQuery tool will return the reply of the service.\n' \ 'Once you receive the reply, call the \"deliverStructuredOutput\" tool with parameters according to the task\'s output schema. \n' \ 'Note: you cannot call sendQuery multiple times, so make sure to ask the right question the first time. Similarly, you cannot call deliverStructuredOutput multiple times, so make sure to deliver the right output the first time.' \ 'If the query fails, do not attempt to send another query.' def parse_and_handle_query(query, target_node, protocol_id, source): response = send_raw_query(query, protocol_id, target_node, source) if response.status_code == 200: parsed_response = json.loads(response.text) if parsed_response['status'] == 'success': return parsed_response['body'] return 'Error calling the tool: ' + response.text + ' (status code: ' + str(response.status_code) + ')' def get_output_parameters(task_schema): output_schema = task_schema['output'] required_parameters = output_schema['required'] parameters = [] for parameter_name, parameter_schema in output_schema['properties'].items(): parameter = parameter_from_openai_api(parameter_name, parameter_schema, parameter_name in required_parameters) parameters.append(parameter) return parameters def handle_conversation(prompt, message, target_node, protocol_id, source, output_parameters): sent_query_counter = 0 def send_query_internal(query): print('Sending query:', query) nonlocal sent_query_counter if sent_query_counter > 50: # All hope is lost, crash sys.exit(-2) elif sent_query_counter > 10: # LLM is not listening, throw an exception raise Exception('Too many attempts to send queries. Exiting.') elif sent_query_counter > 5: # LLM is not listening, issue a warning return 'You have attempted to send too many queries. Finish the message and allow the user to speak, or the system will crash.' elif sent_query_counter > 0: return 'You have already sent a query. You cannot send another one.' sent_query_counter += 1 return parse_and_handle_query(query, target_node, protocol_id, source) send_query_tool = Tool('sendQuery', 'Send a query to the other service based on a protocol document.', [ StringParameter('query', 'The query to send to the service', True) ], send_query_internal) found_output = None registered_output_counter = 0 def register_output(**kwargs): print('Registering output:', kwargs) nonlocal found_output nonlocal registered_output_counter if registered_output_counter > 50: # All hope is lost, crash sys.exit(-2) elif registered_output_counter > 10: # LLM is not listening, raise an exception raise Exception('Too many attempts to register outputs. Exiting.') elif registered_output_counter > 5: # LLM is not listening, issue a warning return 'You have attempted to register too many outputs. Finish the message and allow the user to speak, or the system will crash.' elif registered_output_counter > 0: return 'You have already registered an output. You cannot register another one.' output = serialize_gemini_data(kwargs) output = json.dumps(kwargs) found_output = output registered_output_counter += 1 return 'Done' register_output_tool = Tool('deliverStructuredOutput', 'Deliver the structured output to the machine.', output_parameters , register_output) toolformer = make_default_toolformer(prompt, [send_query_tool, register_output_tool]) conversation = toolformer.new_conversation(category='conversation') for i in range(5): conversation.chat(message, print_output=True) if found_output is not None: break # If we haven't sent a query yet, we can't proceed if sent_query_counter == 0: message = 'You must send a query before delivering the structured output.' elif found_output is None: message = 'You must deliver the structured output.' return found_output def send_query_with_protocol(task_schema, task_data, target_node, protocol_id, source): base_folder = Path(os.environ.get('STORAGE_PATH')) / 'protocol_documents' protocol_document = load_protocol_document(base_folder, protocol_id) query_description = construct_query_description(protocol_document, task_schema, task_data) output_parameters = get_output_parameters(task_schema) return handle_conversation(PROTOCOL_QUERIER_PROMPT, query_description, target_node, protocol_id, source, output_parameters) def send_query_without_protocol(task_schema, task_data, target_node): query_description = construct_query_description(None, task_schema, task_data) output_parameters = get_output_parameters(task_schema) return handle_conversation(NL_QUERIER_PROMPT, query_description, target_node, None, None, output_parameters) ================================================ FILE: specialized_toolformers/responder.py ================================================ # The responder is a special toolformer that replies to a service based on a protocol document. # It receives the protocol document and writes the response that must be sent to the system. import sys sys.path.append('.') import dotenv dotenv.load_dotenv() import json import os from pathlib import Path from toolformers.unified import make_default_toolformer from utils import load_protocol_document # TODO: A tool to declare an error? PROTOCOL_RESPONDER_PROMPT = 'You are ResponderGPT. You will receive a protocol document detailing how to respond to a query. '\ 'Use the provided functions to execute what is requested and provide the response according to the protocol\'s specification. ' \ 'Only reply with the response itself, with no additional information or escaping. Similarly, do not add any additional whitespace or formatting.'# \ # 'If you do not have enough information to reply, or if you cannot execute the request, reply with "ERROR" (without quotes).' def reply_with_protocol_document(query, protocol_document, tools, additional_info): print('===NL RESPONDER (WITH PROTOCOL)===') toolformer = make_default_toolformer(PROTOCOL_RESPONDER_PROMPT + additional_info, tools) conversation = toolformer.new_conversation(category='conversation') prompt = 'The protocol is the following:\n\n' + protocol_document + '\n\nThe query is the following:' + query reply = conversation.chat(prompt, print_output=True) print('======') if 'error' in reply.lower().strip()[-10:]: return json.dumps({ 'status': 'error', }) return json.dumps({ 'status': 'success', 'body': reply }) NL_RESPONDER_PROMPT = 'You are NaturalLanguageResponderGPT. You will receive a query from a user. ' \ 'Use the provided functions to execute what is requested and reply with a response (in natural language). ' \ 'Important: the user does not have the capacity to respond to follow-up questions, so if you think you have enough information to reply/execute the actions, do so.' #'If you do not have enough information to reply, if you cannot execute the request, or if the request is invalid, reply with "ERROR" (without quotes).' \ def reply_to_nl_query(query, tools, additional_info): print('===NL RESPONDER (NO PROTOCOL)===') print(NL_RESPONDER_PROMPT + additional_info) toolformer = make_default_toolformer(NL_RESPONDER_PROMPT + additional_info, tools) conversation = toolformer.new_conversation(category='conversation') reply = conversation.chat(query, print_output=True) print('======') if 'error' in reply.lower().strip()[-10:]: return json.dumps({ 'status': 'error', }) return json.dumps({ 'status': 'success', 'body': reply }) def reply_to_query(query, protocol_id, tools, additional_info): print('Additional info:', additional_info) if protocol_id is None: return reply_to_nl_query(query, tools, additional_info) else: base_folder = Path(os.environ.get('STORAGE_PATH')) / 'protocol_documents' protocol_document = load_protocol_document(base_folder, protocol_id) return reply_with_protocol_document(query, protocol_document, tools, additional_info) ================================================ FILE: toolformers/__init__.py ================================================ ================================================ FILE: toolformers/base.py ================================================ from abc import ABC, abstractmethod import json from google.generativeai.types import CallableFunctionDeclaration import google.generativeai.types.content_types as content_types from databases.mongo import insert_one from utils import get_query_id class Parameter: def __init__(self, name, description, required): self.name = name self.description = description self.required = required def as_openai_info(self): pass def as_standard_api(self): pass class StringParameter(Parameter): def __init__(self, name, description, required): super().__init__(name, description, required) def as_openai_info(self): return { "type": "string", "name": self.name, "description": self.description } def as_standard_api(self): return { "type": "string", "name": self.name, "description": self.description, "required": self.required } def as_natural_language(self): return f'{self.name} (string{", required" if self.required else ""}): {self.description}.' def as_documented_python(self): return f'{self.name} (str{", required" if self.required else ""}): {self.description}.' def as_gemini_tool(self): return { 'type': 'string', 'description': self.description } @staticmethod def from_standard_api(api_info): return StringParameter(api_info["name"], api_info["description"], api_info["required"]) class EnumParameter(Parameter): def __init__(self, name, description, values, required): super().__init__(name, description, required) self.values = values def as_openai_info(self): return { "type": "string", "description": self.description, "values": self.values } def as_standard_api(self): return { "type": "enum", "name": self.name, "description": self.description, "values": self.values, "required": self.required } def as_natural_language(self): return f'{self.name} (enum{", required" if self.required else ""}): {self.description}. Possible values: {", ".join(self.values)}' def as_documented_python(self): return f'{self.name} (str{", required" if self.required else ""}): {self.description}. Possible values: {", ".join(self.values)}' def as_gemini_tool(self): return { 'description': self.description, 'type': 'string', 'enum': self.values } @staticmethod def from_standard_api(api_info): return EnumParameter(api_info["name"], api_info["description"], api_info["values"], api_info["required"]) class NumberParameter(Parameter): def __init__(self, name, description, required): super().__init__(name, description, required) def as_openai_info(self): return { "type": "number", "description": self.description } def as_standard_api(self): return { "type": "number", "name": self.name, "description": self.description, "required": self.required } def as_natural_language(self): return f'{self.name} (number): {self.description}' def as_documented_python(self): return f'{self.name} (number): {self.description}' def as_gemini_tool(self): return { 'description': self.description, 'type': 'number' } class ArrayParameter(Parameter): def __init__(self, name, description, required, item_schema): super().__init__(name, description, required) self.item_schema = item_schema def as_openai_info(self): return { "type": "array", "description": self.description, "items": self.item_schema } def as_standard_api(self): return { "type": "array", "name": self.name, "description": self.description, "required": self.required, "item_schema": self.item_schema } def as_natural_language(self): return f'{self.name} (array): {self.description}. Each item should follow the JSON schema: {json.dumps(self.item_schema)}' def as_documented_python(self): return f'{self.name} (list): {self.description}. Each item should follow the JSON schema: {json.dumps(self.item_schema)}' def as_gemini_tool(self): return { 'description': self.description, 'type': 'array', 'items': self.item_schema } def parameter_from_openai_api(parameter_name, schema, required): if 'enum' in schema: return EnumParameter(parameter_name, schema['description'], schema['enum'], required) elif schema['type'] == 'string': return StringParameter(parameter_name, schema['description'], required) elif schema['type'] == 'number': return NumberParameter(parameter_name, schema['description'], required) elif schema['type'] == 'array': return ArrayParameter(parameter_name, schema['description'], required, schema['items']) else: raise ValueError(f'Unknown parameter type: {schema["type"]}') class Tool: def __init__(self, name, description, parameters, function, output_schema=None): self.name = name self.description = description self.parameters = parameters self.function = function self.output_schema = output_schema def call_tool_for_toolformer(self, *args, **kwargs): print(f'Toolformer called tool {self.name} with args {args} and kwargs {kwargs}') # Unlike a call from a routine, this call catches exceptions and returns them as strings try: tool_reply = self.function(*args, **kwargs) print(f'Tool {self.name} returned: {tool_reply}') return tool_reply except Exception as e: print(f'Tool {self.name} failed with exception: {e}') return 'Tool call failed: ' + str(e) def as_openai_info(self): return { "type": "function", "function": { "name": self.name, "description": self.description, "parameters": { "type" : "object", "properties": {parameter.name : parameter.as_openai_info() for parameter in self.parameters}, "required": [parameter.name for parameter in self.parameters if parameter.required] } } } def as_gemini_tool(self) -> CallableFunctionDeclaration: if len(self.parameters) == 0: parameters = None else: parameters = { 'type': 'object', 'properties': {parameter.name: parameter.as_gemini_tool() for parameter in self.parameters}, 'required': [parameter.name for parameter in self.parameters if parameter.required] } return content_types.Tool([CallableFunctionDeclaration( name=self.name, description=self.description, parameters=parameters, function=self.call_tool_for_toolformer )]) def as_llama_schema(self): schema = { 'name': self.name, 'description': self.description, 'parameters': {parameter.name : parameter.as_openai_info() for parameter in self.parameters}, 'required': [parameter.name for parameter in self.parameters if parameter.required] } if self.output_schema is not None: schema['output_schema'] = self.output_schema return schema def as_natural_language(self): print('Converting to natural language') print('Number of parameters:', len(self.parameters)) nl = f'Function {self.name}: {self.description}. Parameters:\n' if len(self.parameters) == 0: nl += 'No parameters.' else: for parameter in self.parameters: nl += '\t' + parameter.as_natural_language() + '\n' if self.output_schema is not None: nl += f'\Returns a dictionary with schema: {json.dumps(self.output_schema, indent=2)}' return nl def as_standard_api(self): return { "name": self.name, "description": self.description, "parameters": [parameter.as_standard_api() for parameter in self.parameters] } def as_documented_python(self): documented_python = f'Tool {self.name}:\n\n{self.description}\nParameters:\n' if len(self.parameters) == 0: documented_python += 'No parameters.' else: for parameter in self.parameters: documented_python += '\t' + parameter.as_documented_python() + '\n' if self.output_schema is not None: documented_python += f'\Returns a dictionary with schema: {json.dumps(self.output_schema, indent=2)}' return documented_python def as_executable_function(self): # Create an actual function that can be called def f(*args, **kwargs): print('Routine called tool', self.name, 'with args', args, 'and kwargs', kwargs) response = self.function(*args, **kwargs) print('Tool', self.name, 'returned:', response) return response return f @staticmethod def from_standard_api(api_info): # TODO: Drop? parameters = [] for parameter in api_info["parameters"]: if parameter["type"] == "string": parameters.append(StringParameter.from_standard_api(parameter)) elif parameter["type"] == "enum": parameters.append(EnumParameter.from_standard_api(parameter)) else: raise ValueError(f"Unknown parameter type: {parameter['type']}") return Tool(api_info["name"], api_info["description"], parameters, None) def send_usage_to_db(usage, time_start, time_end, agent, category, model): usage = { 'timeStart': { '$date': time_start.isoformat() }, 'timeEnd': { '$date': time_end.isoformat() }, 'prompt_tokens': usage['prompt_tokens'], 'completion_tokens': usage['completion_tokens'], 'agent': agent, 'category': category, 'model': model, 'queryId': get_query_id() } insert_one('usageLogs', 'main', usage) class Conversation(ABC): @abstractmethod def chat(self, message, role='user', print_output=True): pass class Toolformer(ABC): @abstractmethod def new_conversation(self, category=None) -> Conversation: pass ================================================ FILE: toolformers/camel.py ================================================ import datetime import os from typing import List import warnings from toolformers.base import Conversation, Toolformer, Tool, send_usage_to_db from camel.messages import BaseMessage from camel.models import ModelFactory from camel.types import ModelPlatformType, ModelType from camel.messages import BaseMessage as bm from camel.agents import ChatAgent from camel.toolkits.openai_function import OpenAIFunction from camel.configs.openai_config import ChatGPTConfig class CamelConversation(Conversation): def __init__(self, toolformer, agent, category=None): self.toolformer = toolformer self.agent = agent self.category = category def chat(self, message, role='user', print_output=True): agent_id = os.environ.get('AGENT_ID', None) start_time = datetime.datetime.now() if role == 'user': formatted_message = BaseMessage.make_user_message('user', message) elif role == 'assistant': formatted_message = BaseMessage.make_assistant_message('assistant', message) else: raise ValueError('Role must be either "user" or "assistant".') response = self.agent.step(formatted_message) #print(response.info) if response.info.get('usage', None) is not None: send_usage_to_db(response.info.get('usage', None), start_time, datetime.datetime.now(), agent_id, self.category, self.toolformer.name) else: warnings.warn('No usage information found in response.') reply = response.msg.content if print_output: print(reply) return reply class CamelToolformer(Toolformer): def __init__(self, model_platform, model_type, model_config_dict, system_prompt, tools, name=None): self.model_platform = model_platform self.model_type = model_type self.model_config_dict = model_config_dict self.system_prompt = system_prompt self.tools = tools self._name = name @property def name(self): if self._name is None: return f'{self.model_platform.value}_{self.model_type.value}' else: return self._name def new_conversation(self, category=None) -> Conversation: model = ModelFactory.create( model_platform=self.model_platform, model_type=self.model_type, model_config_dict=self.model_config_dict ) agent = ChatAgent( model=model, system_message=bm.make_assistant_message('system', self.system_prompt), tools=self.tools ) return CamelConversation(self, agent, category) def make_openai_toolformer(model_type_internal, system_prompt, tools : List[Tool]): if model_type_internal == 'gpt-4o': model_type = ModelType.GPT_4O elif model_type_internal == 'gpt-4o-mini': model_type = ModelType.GPT_4O_MINI else: raise ValueError('Model type must be either "gpt-4o" or "gpt-4o-mini".') formatted_tools = [OpenAIFunction(tool.call_tool_for_toolformer, tool.as_openai_info()) for tool in tools] return CamelToolformer( model_platform=ModelPlatformType.OPENAI, model_type=model_type, model_config_dict=ChatGPTConfig(temperature=0.2, tools=formatted_tools).as_dict(), system_prompt=system_prompt, tools=formatted_tools, name=model_type_internal ) ================================================ FILE: toolformers/gemini.py ================================================ import datetime import os from random import random import time import traceback from typing import List from toolformers.base import Conversation, Tool, Toolformer, send_usage_to_db import google.generativeai as genai from google.generativeai.generative_models import ChatSession genai.configure(api_key=os.environ['GOOGLE_API_KEY']) class GeminiConversation(Conversation): def __init__(self, model_name, chat_agent : ChatSession, category=None): self.model_name = model_name self.chat_agent = chat_agent self.category = category def chat(self, message, role='user', print_output=True): agent_id = os.environ.get('AGENT_ID', None) time_start = datetime.datetime.now() exponential_backoff_lower = 30 exponential_backoff_higher = 60 for i in range(5): try: response = self.chat_agent.send_message({ 'role': role, 'parts': [ message ] }) break except Exception as e: print(e) if '429' in str(e): print('Rate limit exceeded. Waiting with random exponential backoff.') if i < 4: time.sleep(random() * (exponential_backoff_higher - exponential_backoff_lower) + exponential_backoff_lower) exponential_backoff_lower *= 2 exponential_backoff_higher *= 2 elif 'candidates[0]' in traceback.format_exc(): # When Gemini has nothing to say, it raises an error with this message print('No response') return 'No response' elif '500' in str(e): # Sometimes Gemini just decides to return a 500 error for absolutely no reason. Retry. print('500 error') time.sleep(5) traceback.print_exc() else: raise e time_end = datetime.datetime.now() usage_info = { 'prompt_tokens': response.usage_metadata.prompt_token_count, 'completion_tokens': response.usage_metadata.candidates_token_count } send_usage_to_db( usage_info, time_start, time_end, agent_id, self.category, self.model_name ) reply = response.text if print_output: print(reply) return reply class GeminiToolformer(Toolformer): def __init__(self, model_name, system_prompt, tools): self.model_name = model_name self.system_prompt = system_prompt self.tools = tools def new_conversation(self, category=None) -> Conversation: print('Tools:') print('\n'.join([str(tool.as_openai_info()) for tool in self.tools])) model = genai.GenerativeModel( model_name=self.model_name, system_instruction=self.system_prompt, tools=[tool.as_gemini_tool() for tool in self.tools] ) chat = model.start_chat(enable_automatic_function_calling=True) return GeminiConversation(self.model_name, chat, category) def make_gemini_toolformer(model_name, system_prompt, tools : List[Tool]): if model_name not in ['gemini-1.5-flash', 'gemini-1.5-pro']: raise ValueError(f"Unknown model name: {model_name}") return GeminiToolformer(model_name, system_prompt, tools) ================================================ FILE: toolformers/llama/__init__.py ================================================ from .llama import make_llama_toolformer ================================================ FILE: toolformers/llama/api_gateway.py ================================================ import logging import os import sys from typing import Optional from langchain_community.llms.sambanova import SambaStudio from langchain_core.language_models.llms import LLM current_dir = os.path.dirname(os.path.abspath(__file__)) utils_dir = os.path.abspath(os.path.join(current_dir, '..')) repo_dir = os.path.abspath(os.path.join(utils_dir, '..')) sys.path.append(utils_dir) sys.path.append(repo_dir) from toolformers.llama.sambanova_langchain import SambaNovaCloud EMBEDDING_MODEL = 'intfloat/e5-large-v2' NORMALIZE_EMBEDDINGS = True # Configure the logger logging.basicConfig( level=logging.INFO, format='%(asctime)s [%(levelname)s] - %(message)s', handlers=[ logging.StreamHandler(), ], ) logger = logging.getLogger(__name__) class APIGateway: @staticmethod def load_llm( type: str, streaming: bool = False, coe: bool = False, do_sample: Optional[bool] = None, max_tokens_to_generate: Optional[int] = None, temperature: Optional[float] = None, select_expert: Optional[str] = None, top_p: Optional[float] = None, top_k: Optional[int] = None, repetition_penalty: Optional[float] = None, stop_sequences: Optional[str] = None, process_prompt: Optional[bool] = False, sambastudio_base_url: Optional[str] = None, sambastudio_base_uri: Optional[str] = None, sambastudio_project_id: Optional[str] = None, sambastudio_endpoint_id: Optional[str] = None, sambastudio_api_key: Optional[str] = None, sambanova_url: Optional[str] = None, sambanova_api_key: Optional[str] = None, ) -> LLM: """Loads a langchain Sambanova llm model given a type and parameters Args: type (str): wether to use sambastudio, or SambaNova Cloud model "sncloud" streaming (bool): wether to use streaming method. Defaults to False. coe (bool): whether to use coe model. Defaults to False. do_sample (bool) : Optional wether to do sample. max_tokens_to_generate (int) : Optional max number of tokens to generate. temperature (float) : Optional model temperature. select_expert (str) : Optional expert to use when using CoE models. top_p (float) : Optional model top_p. top_k (int) : Optional model top_k. repetition_penalty (float) : Optional model repetition penalty. stop_sequences (str) : Optional model stop sequences. process_prompt (bool) : Optional default to false. sambastudio_base_url (str): Optional SambaStudio environment URL". sambastudio_base_uri (str): Optional SambaStudio-base-URI". sambastudio_project_id (str): Optional SambaStudio project ID. sambastudio_endpoint_id (str): Optional SambaStudio endpoint ID. sambastudio_api_token (str): Optional SambaStudio endpoint API key. sambanova_url (str): Optional SambaNova Cloud URL", sambanova_api_key (str): Optional SambaNovaCloud API key. Returns: langchain llm model """ if type == 'sambastudio': envs = { 'sambastudio_base_url': sambastudio_base_url, 'sambastudio_base_uri': sambastudio_base_uri, 'sambastudio_project_id': sambastudio_project_id, 'sambastudio_endpoint_id': sambastudio_endpoint_id, 'sambastudio_api_key': sambastudio_api_key, } envs = {k: v for k, v in envs.items() if v is not None} if coe: model_kwargs = { 'do_sample': do_sample, 'max_tokens_to_generate': max_tokens_to_generate, 'temperature': temperature, 'select_expert': select_expert, 'top_p': top_p, 'top_k': top_k, 'repetition_penalty': repetition_penalty, 'stop_sequences': stop_sequences, 'process_prompt': process_prompt, } model_kwargs = {k: v for k, v in model_kwargs.items() if v is not None} llm = SambaStudio( **envs, streaming=streaming, model_kwargs=model_kwargs, ) else: model_kwargs = { 'do_sample': do_sample, 'max_tokens_to_generate': max_tokens_to_generate, 'temperature': temperature, 'top_p': top_p, 'top_k': top_k, 'repetition_penalty': repetition_penalty, 'stop_sequences': stop_sequences, } model_kwargs = {k: v for k, v in model_kwargs.items() if v is not None} llm = SambaStudio( **envs, streaming=streaming, model_kwargs=model_kwargs, ) elif type == 'sncloud': envs = { 'sambanova_url': sambanova_url, 'sambanova_api_key': sambanova_api_key, } envs = {k: v for k, v in envs.items() if v is not None} llm = SambaNovaCloud( **envs, max_tokens=max_tokens_to_generate, model=select_expert, temperature=temperature, top_k=top_k, top_p=top_p, ) else: raise ValueError(f"Invalid LLM API: {type}, only 'sncloud' and 'sambastudio' are supported.") return llm ================================================ FILE: toolformers/llama/function_calling.py ================================================ import json import os import re from random import random from pprint import pprint import time from typing import List, Optional, Union from langchain_core.messages.ai import AIMessage from langchain_core.messages.human import HumanMessage from langchain_core.messages.tool import ToolMessage from langchain_core.prompts import ChatPromptTemplate from langchain_core.runnables import RunnableLambda from toolformers.base import Tool, StringParameter from toolformers.llama.api_gateway import APIGateway from toolformers.llama.utils import get_total_usage, usage_tracker FUNCTION_CALLING_SYSTEM_PROMPT = """You have access to the following tools: {tools} You can call one or more tools by adding a section to your message. For example: ```json [{{ "tool": , "tool_input": }}] ``` Note that you can select multiple tools at once by adding more objects to the list. Do not add \ multiple sections to the same message. You will see the invocation of the tools in the response. Think step by step Do not call a tool if the input depends on another tool output that you do not have yet. Do not try to answer until you get all the tools output, if you do not have an answer yet, you can continue calling tools until you do. Your answer should be in the same language as the initial query. """ # noqa E501 conversational_response = Tool( name='ConversationalResponse', description='Respond conversationally only if no other tools should be called for a given query, or if you have a final answer. Response must be in the same language as the user query.', parameters=[StringParameter(name='response', description='Conversational response to the user. Must be in the same language as the user query.', required=True)], function=None ) class FunctionCallingLlm: """ function calling llm class """ def __init__( self, tools: Optional[Union[Tool, List[Tool]]] = None, default_tool: Optional[Tool] = None, system_prompt: Optional[str] = None, prod_mode: bool = False, api: str = 'sncloud', coe: bool = False, do_sample: bool = False, max_tokens_to_generate: Optional[int] = None, temperature: float = 0.2, select_expert: Optional[str] = None, ) -> None: """ Args: tools (Optional[Union[Tool, List[Tool]]]): The tools to use. default_tool (Optional[Tool]): The default tool to use. defaults to ConversationalResponse system_prompt (Optional[str]): The system prompt to use. defaults to FUNCTION_CALLING_SYSTEM_PROMPT prod_mode (bool): Whether to use production mode. Defaults to False. api (str): The api to use. Defaults to 'sncloud'. coe (bool): Whether to use coe. Defaults to False. do_sample (bool): Whether to do sample. Defaults to False. max_tokens_to_generate (Optional[int]): The max tokens to generate. If None, the model will attempt to use the maximum available tokens. temperature (float): The model temperature. Defaults to 0.2. select_expert (Optional[str]): The expert to use. Defaults to None. """ self.prod_mode = prod_mode sambanova_api_key = os.environ.get('SAMBANOVA_API_KEY') self.api = api self.llm = APIGateway.load_llm( type=api, streaming=True, coe=coe, do_sample=do_sample, max_tokens_to_generate=max_tokens_to_generate, temperature=temperature, select_expert=select_expert, process_prompt=False, sambanova_api_key=sambanova_api_key, ) if isinstance(tools, Tool): tools = [tools] self.tools = tools if system_prompt is None: system_prompt = '' system_prompt = system_prompt.replace('{','{{').replace('}', '}}') if len(self.tools) > 0: system_prompt += '\n\n' system_prompt += FUNCTION_CALLING_SYSTEM_PROMPT self.system_prompt = system_prompt if default_tool is None: default_tool = conversational_response def execute(self, invoked_tools: List[dict]) -> tuple[bool, List[str]]: """ Given a list of tool executions the llm return as required execute them given the name with the mane in tools_map and the input arguments if there is only one tool call and it is default conversational one, the response is marked as final response Args: invoked_tools (List[dict]): The list of tool executions generated by the LLM. """ if self.tools is not None: tools_map = {tool.name.lower(): tool for tool in self.tools} else: tools_map = {} tool_msg = "Tool '{name}' response: {response}" tools_msgs = [] if len(invoked_tools) == 1 and invoked_tools[0]['tool'].lower() == 'conversationalresponse': final_answer = True return final_answer, [invoked_tools[0]['tool_input']['response']] final_answer = False for tool in invoked_tools: if tool['tool'].lower() == 'invocationerror': tools_msgs.append(f'Tool invocation error: {tool["tool_input"]}') elif tool['tool'].lower() != 'conversationalresponse': print(f"\n\n---\nTool {tool['tool'].lower()} invoked with input {tool['tool_input']}\n") if tool['tool'].lower() not in tools_map: tools_msgs.append(f'Tool {tool["tool"]} not found') else: response = tools_map[tool['tool'].lower()].call_tool_for_toolformer(**tool['tool_input']) # print(f'Tool response: {str(response)}\n---\n\n') tools_msgs.append(tool_msg.format(name=tool['tool'], response=str(response))) return final_answer, tools_msgs def json_finder(self, input_string: str) -> Optional[str]: """ find json structures in an LLM string response, if bad formatted using LLM to correct it Args: input_string (str): The string to find the json structure in. """ # 1. Ideal pattern: correctly surrounded by tags json_pattern_1 = re.compile(r'(.*)', re.DOTALL + re.IGNORECASE) # 2. Sometimes the closing tag is missing json_pattern_2 = re.compile(r'(.*)', re.DOTALL + re.IGNORECASE) # 3. Sometimes it accidentally uses instead of json_pattern_3 = re.compile(r'(.*)', re.DOTALL + re.IGNORECASE) # 4. Sometimes it accidentally uses instead of and the closing tag is missing json_pattern_4 = re.compile(r'(.*)', re.DOTALL + re.IGNORECASE) # Find the first JSON structure in the string json_match = json_pattern_1.search(input_string) or json_pattern_2.search(input_string) or json_pattern_3.search(input_string) or json_pattern_4.search(input_string) if json_match: json_str = json_match.group(1) # 1. Outermost list of JSON object call_pattern_1 = re.compile(r'\[.*\]', re.DOTALL) # 2. Outermost JSON object call_pattern_2 = re.compile(r'\{.*\}', re.DOTALL) call_match_1 = call_pattern_1.search(json_str) call_match_2 = call_pattern_2.search(json_str) if call_match_1: json_str = call_match_1.group(0) try: return json.loads(json_str) except Exception as e: return [{'tool': 'InvocationError', 'tool_input' : str(e)}] elif call_match_2: json_str = call_match_2.group(0) try: return [json.loads(json_str)] except Exception as e: return [{'tool': 'InvocationError', 'tool_input' : str(e)}] else: return [{'tool': 'InvocationError', 'tool_input' : 'Could not find JSON object in the section'}] else: dummy_json_response = [{'tool': 'ConversationalResponse', 'tool_input': {'response': input_string}}] json_str = dummy_json_response return json_str def msgs_to_llama3_str(self, msgs: list) -> str: """ convert a list of langchain messages with roles to expected LLmana 3 input Args: msgs (list): The list of langchain messages. """ formatted_msgs = [] for msg in msgs: if msg.type == 'system': sys_placeholder = ( '<|begin_of_text|><|start_header_id|>system<|end_header_id|>system<|end_header_id|> {msg}' ) formatted_msgs.append(sys_placeholder.format(msg=msg.content)) elif msg.type == 'human': human_placeholder = '<|eot_id|><|start_header_id|>user<|end_header_id|>\nUser: {msg} <|eot_id|><|start_header_id|>assistant<|end_header_id|>\nAssistant:' # noqa E501 formatted_msgs.append(human_placeholder.format(msg=msg.content)) elif msg.type == 'ai': assistant_placeholder = '<|eot_id|><|start_header_id|>assistant<|end_header_id|>\nAssistant: {msg}' formatted_msgs.append(assistant_placeholder.format(msg=msg.content)) elif msg.type == 'tool': tool_placeholder = '<|eot_id|><|start_header_id|>tools<|end_header_id|>\n{msg} <|eot_id|><|start_header_id|>assistant<|end_header_id|>\nAssistant:' # noqa E501 formatted_msgs.append(tool_placeholder.format(msg=msg.content)) else: raise ValueError(f'Invalid message type: {msg.type}') return '\n'.join(formatted_msgs) def msgs_to_sncloud(self, msgs: list) -> list: """ convert a list of langchain messages with roles to expected FastCoE input Args: msgs (list): The list of langchain messages. """ formatted_msgs = [] for msg in msgs: if msg.type == 'system': formatted_msgs.append({'role': 'system', 'content': msg.content}) elif msg.type == 'human': formatted_msgs.append({'role': 'user', 'content': msg.content}) elif msg.type == 'ai': formatted_msgs.append({'role': 'assistant', 'content': msg.content}) elif msg.type == 'tool': formatted_msgs.append({'role': 'tools', 'content': msg.content}) else: raise ValueError(f'Invalid message type: {msg.type}') return json.dumps(formatted_msgs) def function_call_llm(self, query: str, max_it: int = 5, debug: bool = False) -> str: """ invocation method for function calling workflow Args: query (str): The query to execute. max_it (int, optional): The maximum number of iterations. Defaults to 5. debug (bool, optional): Whether to print debug information. Defaults to False. """ function_calling_chat_template = ChatPromptTemplate.from_messages([('system', self.system_prompt)]) tools_schemas = [tool.as_llama_schema() for tool in self.tools] history = function_calling_chat_template.format_prompt(tools=tools_schemas).to_messages() history.append(HumanMessage(query)) tool_call_id = 0 # identification for each tool calling required to create ToolMessages with usage_tracker(): for i in range(max_it): json_parsing_chain = RunnableLambda(self.json_finder) if self.api == 'sncloud': prompt = self.msgs_to_sncloud(history) else: prompt = self.msgs_to_llama3_str(history) # print(f'\n\n---\nCalling function calling LLM with prompt: \n{prompt}\n') exponential_backoff_lower = 30 exponential_backoff_higher = 60 llm_response = None for _ in range(5): try: llm_response = self.llm.invoke(prompt, stream_options={'include_usage': True}) break except Exception as e: if '429' in str(e): print('Rate limit exceeded. Waiting with random exponential backoff.') time.sleep(random() * (exponential_backoff_higher - exponential_backoff_lower) + exponential_backoff_lower) exponential_backoff_lower *= 2 exponential_backoff_higher *= 2 else: raise e print('LLM response:', llm_response) # print(f'\nFunction calling LLM response: \n{llm_response}\n---\n') parsed_tools_llm_response = json_parsing_chain.invoke(llm_response) history.append(AIMessage(llm_response)) final_answer, tools_msgs = self.execute(parsed_tools_llm_response) if final_answer: # if response was marked as final response in execution final_response = tools_msgs[0] if debug: print('\n\n---\nFinal function calling LLM history: \n') pprint(f'{history}') return final_response, get_total_usage() else: history.append(ToolMessage('\n'.join(tools_msgs), tool_call_id=tool_call_id)) tool_call_id += 1 raise Exception('Not a final response yet', history) ================================================ FILE: toolformers/llama/llama.py ================================================ import datetime import os from typing import List from toolformers.base import Conversation, Toolformer, Tool, send_usage_to_db from toolformers.llama.function_calling import FunctionCallingLlm class LlamaConversation(Conversation): def __init__(self, model_name, function_calling_llm : FunctionCallingLlm, category=None): self.model_name = model_name self.function_calling_llm = function_calling_llm self.category = category def chat(self, message, role='user', print_output=True): if role != 'user': raise ValueError('Role must be "user"') agent_id = os.environ.get('AGENT_ID', None) start_time = datetime.datetime.now() response, usage_data = self.function_calling_llm.function_call_llm(message) end_time = datetime.datetime.now() print('Usage data:', usage_data) if print_output: print(response) send_usage_to_db(usage_data, start_time, end_time, agent_id, self.category, self.model_name) return response class LlamaToolformer(Toolformer): def __init__(self, model_name: str, system_prompt: str, tools: List[Tool]): self.function_calling_llm = FunctionCallingLlm(system_prompt=system_prompt, tools=tools, select_expert=model_name) self.model_name = model_name def new_conversation(self, category=None) -> LlamaConversation: return LlamaConversation(self.model_name, self.function_calling_llm, category) def make_llama_toolformer(model_name, system_prompt: str, tools: List[Tool]): if model_name not in ['llama3-8b', 'llama3-70b', 'llama3-405b']: raise ValueError(f"Unknown model name: {model_name}") return LlamaToolformer(model_name, system_prompt, tools) ================================================ FILE: toolformers/llama/sambanova_langchain.py ================================================ """Langchain Wrapper around Sambanova LLM APIs.""" import json from typing import Any, Dict, Generator, Iterator, List, Optional, Union import requests from langchain_core.callbacks.manager import CallbackManagerForLLMRun from langchain_core.language_models.llms import LLM from langchain_core.outputs import GenerationChunk from langchain_core.pydantic_v1 import Extra from langchain_core.utils import get_from_dict_or_env, pre_init from langchain_core.runnables import RunnableConfig, ensure_config from langchain_core.language_models.base import ( LanguageModelInput, ) from toolformers.llama.utils import append_to_usage_tracker class SSEndpointHandler: """ SambaNova Systems Interface for SambaStudio model endpoints. :param str host_url: Base URL of the DaaS API service """ def __init__(self, host_url: str, api_base_uri: str): """ Initialize the SSEndpointHandler. :param str host_url: Base URL of the DaaS API service :param str api_base_uri: Base URI of the DaaS API service """ self.host_url = host_url self.api_base_uri = api_base_uri self.http_session = requests.Session() def _process_response(self, response: requests.Response) -> Dict: """ Processes the API response and returns the resulting dict. All resulting dicts, regardless of success or failure, will contain the `status_code` key with the API response status code. If the API returned an error, the resulting dict will contain the key `detail` with the error message. If the API call was successful, the resulting dict will contain the key `data` with the response data. :param requests.Response response: the response object to process :return: the response dict :type: dict """ result: Dict[str, Any] = {} try: result = response.json() except Exception as e: result['detail'] = str(e) if 'status_code' not in result: result['status_code'] = response.status_code return result def _process_streaming_response( self, response: requests.Response, ) -> Generator[Dict, None, None]: """Process the streaming response""" if 'api/predict/nlp' in self.api_base_uri: try: import sseclient except ImportError: raise ImportError( 'could not import sseclient library' 'Please install it with `pip install sseclient-py`.' ) client = sseclient.SSEClient(response) close_conn = False for event in client.events(): if event.event == 'error_event': close_conn = True chunk = { 'event': event.event, 'data': event.data, 'status_code': response.status_code, } yield chunk if close_conn: client.close() elif 'api/v2/predict/generic' in self.api_base_uri or 'api/predict/generic' in self.api_base_uri: try: for line in response.iter_lines(): chunk = json.loads(line) if 'status_code' not in chunk: chunk['status_code'] = response.status_code yield chunk except Exception as e: raise RuntimeError(f'Error processing streaming response: {e}') else: raise ValueError(f'handling of endpoint uri: {self.api_base_uri} not implemented') def _get_full_url(self, path: str) -> str: """ Return the full API URL for a given path. :param str path: the sub-path :returns: the full API URL for the sub-path :type: str """ return f'{self.host_url}/{self.api_base_uri}/{path}' def nlp_predict( self, project: str, endpoint: str, key: str, input: Union[List[str], str], params: Optional[str] = '', stream: bool = False, ) -> Dict: """ NLP predict using inline input string. :param str project: Project ID in which the endpoint exists :param str endpoint: Endpoint ID :param str key: API Key :param str input_str: Input string :param str params: Input params string :returns: Prediction results :type: dict """ if isinstance(input, str): input = [input] if 'api/predict/nlp' in self.api_base_uri: if params: data = {'inputs': input, 'params': json.loads(params)} else: data = {'inputs': input} elif 'api/v2/predict/generic' in self.api_base_uri: items = [{'id': f'item{i}', 'value': item} for i, item in enumerate(input)] if params: data = {'items': items, 'params': json.loads(params)} else: data = {'items': items} elif 'api/predict/generic' in self.api_base_uri: if params: data = {'instances': input, 'params': json.loads(params)} else: data = {'instances': input} else: raise ValueError(f'handling of endpoint uri: {self.api_base_uri} not implemented') response = self.http_session.post( self._get_full_url(f'{project}/{endpoint}'), headers={'key': key}, json=data, ) return self._process_response(response) def nlp_predict_stream( self, project: str, endpoint: str, key: str, input: Union[List[str], str], params: Optional[str] = '', ) -> Iterator[Dict]: """ NLP predict using inline input string. :param str project: Project ID in which the endpoint exists :param str endpoint: Endpoint ID :param str key: API Key :param str input_str: Input string :param str params: Input params string :returns: Prediction results :type: dict """ if 'api/predict/nlp' in self.api_base_uri: if isinstance(input, str): input = [input] if params: data = {'inputs': input, 'params': json.loads(params)} else: data = {'inputs': input} elif 'api/v2/predict/generic' in self.api_base_uri: if isinstance(input, str): input = [input] items = [{'id': f'item{i}', 'value': item} for i, item in enumerate(input)] if params: data = {'items': items, 'params': json.loads(params)} else: data = {'items': items} elif 'api/predict/generic' in self.api_base_uri: if isinstance(input, list): input = input[0] if params: data = {'instance': input, 'params': json.loads(params)} else: data = {'instance': input} else: raise ValueError(f'handling of endpoint uri: {self.api_base_uri} not implemented') # Streaming output response = self.http_session.post( self._get_full_url(f'stream/{project}/{endpoint}'), headers={'key': key}, json=data, stream=True, ) for chunk in self._process_streaming_response(response): yield chunk class SambaStudio(LLM): """ SambaStudio large language models. To use, you should have the environment variables ``SAMBASTUDIO_BASE_URL`` set with your SambaStudio environment URL. ``SAMBASTUDIO_BASE_URI`` set with your SambaStudio api base URI. ``SAMBASTUDIO_PROJECT_ID`` set with your SambaStudio project ID. ``SAMBASTUDIO_ENDPOINT_ID`` set with your SambaStudio endpoint ID. ``SAMBASTUDIO_API_KEY`` set with your SambaStudio endpoint API key. https://sambanova.ai/products/enterprise-ai-platform-sambanova-suite read extra documentation in https://docs.sambanova.ai/sambastudio/latest/index.html Example: .. code-block:: python from langchain_community.llms.sambanova import SambaStudio SambaStudio( sambastudio_base_url="your-SambaStudio-environment-URL", sambastudio_base_uri="your-SambaStudio-base-URI", sambastudio_project_id="your-SambaStudio-project-ID", sambastudio_endpoint_id="your-SambaStudio-endpoint-ID", sambastudio_api_key="your-SambaStudio-endpoint-API-key, streaming=False model_kwargs={ "do_sample": False, "max_tokens_to_generate": 1000, "temperature": 0.7, "top_p": 1.0, "repetition_penalty": 1, "top_k": 50, #"process_prompt": False, #"select_expert": "Meta-Llama-3-8B-Instruct" }, ) """ sambastudio_base_url: str = '' """Base url to use""" sambastudio_base_uri: str = '' """endpoint base uri""" sambastudio_project_id: str = '' """Project id on sambastudio for model""" sambastudio_endpoint_id: str = '' """endpoint id on sambastudio for model""" sambastudio_api_key: str = '' """sambastudio api key""" model_kwargs: Optional[dict] = None """Key word arguments to pass to the model.""" streaming: Optional[bool] = False """Streaming flag to get streamed response.""" class Config: """Configuration for this pydantic object.""" extra = Extra.forbid @classmethod def is_lc_serializable(cls) -> bool: return True @property def _identifying_params(self) -> Dict[str, Any]: """Get the identifying parameters.""" return {**{'model_kwargs': self.model_kwargs}} @property def _llm_type(self) -> str: """Return type of llm.""" return 'Sambastudio LLM' @pre_init def validate_environment(cls, values: Dict) -> Dict: """Validate that api key and python package exists in environment.""" values['sambastudio_base_url'] = get_from_dict_or_env(values, 'sambastudio_base_url', 'SAMBASTUDIO_BASE_URL') values['sambastudio_base_uri'] = get_from_dict_or_env( values, 'sambastudio_base_uri', 'SAMBASTUDIO_BASE_URI', default='api/predict/generic', ) values['sambastudio_project_id'] = get_from_dict_or_env( values, 'sambastudio_project_id', 'SAMBASTUDIO_PROJECT_ID' ) values['sambastudio_endpoint_id'] = get_from_dict_or_env( values, 'sambastudio_endpoint_id', 'SAMBASTUDIO_ENDPOINT_ID' ) values['sambastudio_api_key'] = get_from_dict_or_env(values, 'sambastudio_api_key', 'SAMBASTUDIO_API_KEY') return values def _get_tuning_params(self, stop: Optional[List[str]]) -> str: """ Get the tuning parameters to use when calling the LLM. Args: stop: Stop words to use when generating. Model output is cut off at the first occurrence of any of the stop substrings. Returns: The tuning parameters as a JSON string. """ _model_kwargs = self.model_kwargs or {} _kwarg_stop_sequences = _model_kwargs.get('stop_sequences', []) _stop_sequences = stop or _kwarg_stop_sequences # if not _kwarg_stop_sequences: # _model_kwargs["stop_sequences"] = ",".join( # f'"{x}"' for x in _stop_sequences # ) if 'api/v2/predict/generic' in self.sambastudio_base_uri: tuning_params_dict = _model_kwargs else: tuning_params_dict = {k: {'type': type(v).__name__, 'value': str(v)} for k, v in (_model_kwargs.items())} # _model_kwargs["stop_sequences"] = _kwarg_stop_sequences tuning_params = json.dumps(tuning_params_dict) return tuning_params def _handle_nlp_predict(self, sdk: SSEndpointHandler, prompt: Union[List[str], str], tuning_params: str) -> str: """ Perform an NLP prediction using the SambaStudio endpoint handler. Args: sdk: The SSEndpointHandler to use for the prediction. prompt: The prompt to use for the prediction. tuning_params: The tuning parameters to use for the prediction. Returns: The prediction result. Raises: ValueError: If the prediction fails. """ response = sdk.nlp_predict( self.sambastudio_project_id, self.sambastudio_endpoint_id, self.sambastudio_api_key, prompt, tuning_params, ) if response['status_code'] != 200: optional_detail = response.get('detail') if optional_detail: raise RuntimeError( f"Sambanova /complete call failed with status code " f"{response['status_code']}.\n Details: {optional_detail}" ) else: raise RuntimeError( f"Sambanova /complete call failed with status code " f"{response['status_code']}.\n response {response}" ) if 'api/predict/nlp' in self.sambastudio_base_uri: return response['data'][0]['completion'] elif 'api/v2/predict/generic' in self.sambastudio_base_uri: return response['items'][0]['value']['completion'] elif 'api/predict/generic' in self.sambastudio_base_uri: return response['predictions'][0]['completion'] else: raise ValueError(f'handling of endpoint uri: {self.sambastudio_base_uri} not implemented') def _handle_completion_requests(self, prompt: Union[List[str], str], stop: Optional[List[str]]) -> str: """ Perform a prediction using the SambaStudio endpoint handler. Args: prompt: The prompt to use for the prediction. stop: stop sequences. Returns: The prediction result. Raises: ValueError: If the prediction fails. """ ss_endpoint = SSEndpointHandler(self.sambastudio_base_url, self.sambastudio_base_uri) tuning_params = self._get_tuning_params(stop) return self._handle_nlp_predict(ss_endpoint, prompt, tuning_params) def _handle_nlp_predict_stream( self, sdk: SSEndpointHandler, prompt: Union[List[str], str], tuning_params: str ) -> Iterator[GenerationChunk]: """ Perform a streaming request to the LLM. Args: sdk: The SVEndpointHandler to use for the prediction. prompt: The prompt to use for the prediction. tuning_params: The tuning parameters to use for the prediction. Returns: An iterator of GenerationChunks. """ for chunk in sdk.nlp_predict_stream( self.sambastudio_project_id, self.sambastudio_endpoint_id, self.sambastudio_api_key, prompt, tuning_params, ): if chunk['status_code'] != 200: error = chunk.get('error') if error: optional_code = error.get('code') optional_details = error.get('details') optional_message = error.get('message') raise ValueError( f"Sambanova /complete call failed with status code " f"{chunk['status_code']}.\n" f"Message: {optional_message}\n" f"Details: {optional_details}\n" f"Code: {optional_code}\n" ) else: raise RuntimeError( f"Sambanova /complete call failed with status code " f"{chunk['status_code']}." f"{chunk}." ) if 'api/predict/nlp' in self.sambastudio_base_uri: text = json.loads(chunk['data'])['stream_token'] elif 'api/v2/predict/generic' in self.sambastudio_base_uri: text = chunk['result']['items'][0]['value']['stream_token'] elif 'api/predict/generic' in self.sambastudio_base_uri: if len(chunk['result']['responses']) > 0: text = chunk['result']['responses'][0]['stream_token'] else: text = '' else: raise ValueError(f'handling of endpoint uri: {self.sambastudio_base_uri}' f'not implemented') generated_chunk = GenerationChunk(text=text) yield generated_chunk def _stream( self, prompt: Union[List[str], str], stop: Optional[List[str]] = None, run_manager: Optional[CallbackManagerForLLMRun] = None, **kwargs: Any, ) -> Iterator[GenerationChunk]: """Call out to Sambanova's complete endpoint. Args: prompt: The prompt to pass into the model. stop: Optional list of stop words to use when generating. Returns: The string generated by the model. """ ss_endpoint = SSEndpointHandler(self.sambastudio_base_url, self.sambastudio_base_uri) tuning_params = self._get_tuning_params(stop) try: if self.streaming: for chunk in self._handle_nlp_predict_stream(ss_endpoint, prompt, tuning_params): if run_manager: run_manager.on_llm_new_token(chunk.text) yield chunk else: return except Exception as e: # Handle any errors raised by the inference endpoint raise ValueError(f'Error raised by the inference endpoint: {e}') from e def _handle_stream_request( self, prompt: Union[List[str], str], stop: Optional[List[str]], run_manager: Optional[CallbackManagerForLLMRun], kwargs: Dict[str, Any], ) -> str: """ Perform a streaming request to the LLM. Args: prompt: The prompt to generate from. stop: Stop words to use when generating. Model output is cut off at the first occurrence of any of the stop substrings. run_manager: Callback manager for the run. **kwargs: Additional keyword arguments. directly passed to the sambastudio model in API call. Returns: The model output as a string. """ completion = '' for chunk in self._stream(prompt=prompt, stop=stop, run_manager=run_manager, **kwargs): completion += chunk.text return completion def _call( self, prompt: Union[List[str], str], stop: Optional[List[str]] = None, run_manager: Optional[CallbackManagerForLLMRun] = None, **kwargs: Any, ) -> str: """Call out to Sambanova's complete endpoint. Args: prompt: The prompt to pass into the model. stop: Optional list of stop words to use when generating. Returns: The string generated by the model. """ if stop is not None: raise Exception('stop not implemented') try: if self.streaming: return self._handle_stream_request(prompt, stop, run_manager, kwargs) return self._handle_completion_requests(prompt, stop) except Exception as e: # Handle any errors raised by the inference endpoint raise ValueError(f'Error raised by the inference endpoint: {e}') from e class SambaNovaCloud(LLM): """ SambaNova Cloud large language models. To use, you should have the environment variables ``SAMBANOVA_URL`` set with your SambaNova Cloud URL. ``SAMBANOVA_API_KEY`` set with your SambaNova Cloud API Key. http://cloud.sambanova.ai/ Example: .. code-block:: python SambaNovaCloud( sambanova_url = SambaNova cloud endpoint URL, sambanova_api_key = set with your SambaNova cloud API key, max_tokens = mas number of tokens to generate stop_tokens = list of stop tokens model = model name ) """ sambanova_url: str = '' """SambaNova Cloud Url""" sambanova_api_key: str = '' """SambaNova Cloud api key""" max_tokens: int = None """max tokens to generate""" stop_tokens: list = ['<|eot_id|>'] """Stop tokens""" model: str = 'llama3-8b' """LLM model expert to use""" temperature: float = 0.0 """model temperature""" top_p: float = 0.0 """model top p""" top_k: int = 1 """model top k""" stream_api: bool = True """use stream api""" stream_options: dict = {'include_usage': True} """stream options, include usage to get generation metrics""" class Config: """Configuration for this pydantic object.""" extra = Extra.forbid @classmethod def is_lc_serializable(cls) -> bool: return True @property def _identifying_params(self) -> Dict[str, Any]: """Get the identifying parameters.""" return { 'model': self.model, 'max_tokens': self.max_tokens, 'stop': self.stop_tokens, 'temperature': self.temperature, 'top_p': self.top_p, 'top_k': self.top_k, } def invoke( self, input: LanguageModelInput, config: Optional[RunnableConfig] = None, *, stop: Optional[List[str]] = None, **kwargs: Any, ) -> str: config = ensure_config(config) response = self.generate_prompt( [self._convert_input(input)], stop=stop, callbacks=config.get("callbacks"), tags=config.get("tags"), metadata=config.get("metadata"), run_name=config.get("run_name"), run_id=config.pop("run_id", None), **kwargs, ) run_infos = response.run if len(run_infos) > 1: raise NotImplementedError('Multiple runs not supported') run_id = run_infos[0].run_id #print('Raw response:', response.run) #print('Run ID:', run_id) #print(USAGE_TRACKER) #if run_id in USAGE_TRACKER: # print('Usage:', USAGE_TRACKER[run_id]) #return response return ( response .generations[0][0] .text ) @property def _llm_type(self) -> str: """Return type of llm.""" return 'SambaNova Cloud' @pre_init def validate_environment(cls, values: Dict) -> Dict: """Validate that api key and python package exists in environment.""" values['sambanova_url'] = get_from_dict_or_env( values, 'sambanova_url', 'SAMBANOVA_URL', default='https://api.sambanova.ai/v1/chat/completions' ) values['sambanova_api_key'] = get_from_dict_or_env(values, 'sambanova_api_key', 'SAMBANOVA_API_KEY') return values def _handle_nlp_predict_stream( self, prompt: Union[List[str], str], stop: List[str], ) -> Iterator[GenerationChunk]: """ Perform a streaming request to the LLM. Args: prompt: The prompt to use for the prediction. stop: list of stop tokens Returns: An iterator of GenerationChunks. """ try: import sseclient except ImportError: raise ImportError('could not import sseclient library' 'Please install it with `pip install sseclient-py`.') try: formatted_prompt = json.loads(prompt) except: formatted_prompt = [{'role': 'user', 'content': prompt}] http_session = requests.Session() if not stop: stop = self.stop_tokens data = { 'messages': formatted_prompt, 'max_tokens': self.max_tokens, 'stop': stop, 'model': self.model, 'temperature': self.temperature, 'top_p': self.top_p, 'top_k': self.top_k, 'stream': self.stream_api, 'stream_options': self.stream_options, } # Streaming output response = http_session.post( self.sambanova_url, headers={'Authorization': f'Bearer {self.sambanova_api_key}', 'Content-Type': 'application/json'}, json=data, stream=True, ) client = sseclient.SSEClient(response) close_conn = False if response.status_code != 200: raise RuntimeError( f'Sambanova /complete call failed with status code ' f'{response.status_code}.' f'{response.text}.' ) for event in client.events(): if event.event == 'error_event': close_conn = True #print('Event:', event.data) chunk = { 'event': event.event, 'data': event.data, 'status_code': response.status_code, } if chunk.get('error'): raise RuntimeError( f"Sambanova /complete call failed with status code " f"{chunk['status_code']}." f"{chunk}." ) try: # check if the response is a final event in that case event data response is '[DONE]' #if 'usage' in chunk['data']: # usage = json.loads(chunk['data']) # print('Usage:', usage) if chunk['data'] != '[DONE]': data = json.loads(chunk['data']) if data.get('error'): raise RuntimeError( f"Sambanova /complete call failed with status code " f"{chunk['status_code']}." f"{chunk}." ) # check if the response is a final response with usage stats (not includes content) if data.get('usage') is None: # check is not "end of text" response if data['choices'][0]['finish_reason'] is None: text = data['choices'][0]['delta']['content'] generated_chunk = GenerationChunk(text=text) yield generated_chunk else: #if data['id'] not in USAGE_TRACKER: # USAGE_TRACKER[data['id']] = [] #USAGE_TRACKER[data['id']].append(data['usage']) append_to_usage_tracker(data['usage']) #print(f'Usage for id {data["id"]}:', data['usage']) except Exception as e: raise Exception(f'Error getting content chunk raw streamed response: {chunk}') def _stream( self, prompt: Union[List[str], str], stop: Optional[List[str]] = None, run_manager: Optional[CallbackManagerForLLMRun] = None, **kwargs: Any, ) -> Iterator[GenerationChunk]: """Call out to Sambanova's complete endpoint. Args: prompt: The prompt to pass into the model. stop: Optional list of stop words to use when generating. Returns: The string generated by the model. """ try: for chunk in self._handle_nlp_predict_stream(prompt, stop): if run_manager: run_manager.on_llm_new_token(chunk.text) yield chunk except Exception as e: # Handle any errors raised by the inference endpoint raise ValueError(f'Error raised by the inference endpoint: {e}') from e def _handle_stream_request( self, prompt: Union[List[str], str], stop: Optional[List[str]], run_manager: Optional[CallbackManagerForLLMRun], kwargs: Dict[str, Any], ) -> str: """ Perform a streaming request to the LLM. Args: prompt: The prompt to generate from. stop: Stop words to use when generating. Model output is cut off at the first occurrence of any of the stop substrings. run_manager: Callback manager for the run. **kwargs: Additional keyword arguments. directly passed to the Sambanova Cloud model in API call. Returns: The model output as a string. """ completion = '' for chunk in self._stream(prompt=prompt, stop=stop, run_manager=run_manager, **kwargs): completion += chunk.text return completion def _call( self, prompt: Union[List[str], str], stop: Optional[List[str]] = None, run_manager: Optional[CallbackManagerForLLMRun] = None, **kwargs: Any, ) -> str: """Call out to Sambanova's complete endpoint. Args: prompt: The prompt to pass into the model. stop: Optional list of stop words to use when generating. Returns: The string generated by the model. """ try: return self._handle_stream_request(prompt, stop, run_manager, kwargs) except Exception as e: # Handle any errors raised by the inference endpoint raise ValueError(f'Error raised by the inference endpoint: {e}') from e ================================================ FILE: toolformers/llama/utils.py ================================================ from contextlib import contextmanager USAGE_TRACKER = None @contextmanager def usage_tracker(): global USAGE_TRACKER assert USAGE_TRACKER is None USAGE_TRACKER = [] try: yield finally: USAGE_TRACKER = None def get_total_usage(): global USAGE_TRACKER prompt_tokens = 0 completion_tokens = 0 for usage in USAGE_TRACKER: prompt_tokens += usage['prompt_tokens'] completion_tokens += usage['completion_tokens'] return { 'prompt_tokens': prompt_tokens, 'completion_tokens': completion_tokens } def append_to_usage_tracker(usage): global USAGE_TRACKER USAGE_TRACKER.append(usage) ================================================ FILE: toolformers/unified.py ================================================ import os from toolformers.camel import make_openai_toolformer from toolformers.gemini import make_gemini_toolformer from toolformers.llama import make_llama_toolformer def make_toolformer(model_type_internal, system_prompt, tools): if model_type_internal in ['gpt-4o', 'gpt-4o-mini']: return make_openai_toolformer(model_type_internal, system_prompt, tools) elif model_type_internal in ['gemini-1.5-flash', 'gemini-1.5-pro']: return make_gemini_toolformer(model_type_internal, system_prompt, tools) elif model_type_internal in ['llama3-8b', 'llama3-70b', 'llama3-405b']: return make_llama_toolformer(model_type_internal, system_prompt, tools) else: raise ValueError(f'Unsupported model type: {model_type_internal}') def make_default_toolformer(system_prompt, tools): model_type_internal = os.environ.get('MODEL_TYPE', 'gpt-4o-mini') return make_toolformer(model_type_internal, system_prompt, tools) ================================================ FILE: utils.py ================================================ import base64 from contextlib import contextmanager import hashlib import importlib import json from pathlib import Path import urllib import requests as request_manager from proto.marshal.collections.repeated import RepeatedComposite from proto.marshal.collections.maps import MapComposite def compute_hash(s): # Hash a string using SHA-1 and return the base64 encoded result m = hashlib.sha1() m.update(s.encode()) b = m.digest() return base64.b64encode(b).decode('ascii') def save_protocol_document(base_folder, protocol_id, protocol_document): if isinstance(base_folder, str): base_folder = Path(base_folder) protocol_id = urllib.parse.quote_plus(protocol_id) path = base_folder / (protocol_id + '.txt') path.parent.mkdir(parents=True, exist_ok=True) with open(str(path), 'w') as f: f.write(protocol_document) def load_protocol_document(base_folder, protocol_id): if isinstance(base_folder, str): base_folder = Path(base_folder) protocol_id = urllib.parse.quote_plus(protocol_id) path = base_folder / (protocol_id + '.txt') with open(str(path), 'r') as f: return f.read() def execute_routine(base_folder, protocol_id, task_data, tools): if isinstance(base_folder, str): base_folder = Path(base_folder) protocol_id = urllib.parse.quote_plus(protocol_id) protocol_id = protocol_id.replace('%', '_') path = base_folder / f'{protocol_id}.py' print('Loading module from:', path) # TODO: This should be done in a safe, containerized environment spec = importlib.util.spec_from_file_location(protocol_id, path) loaded_module = importlib.util.module_from_spec(spec) spec.loader.exec_module(loaded_module) for tool in tools: loaded_module.__dict__[tool.name] = tool.as_executable_function() return loaded_module.run(task_data) def save_routine(base_folder, protocol_id, routine): if isinstance(base_folder, str): base_folder = Path(base_folder) protocol_id = urllib.parse.quote_plus(protocol_id) protocol_id = protocol_id.replace('%', '_') path = base_folder / f'{protocol_id}.py' path.parent.mkdir(parents=True, exist_ok=True) with open(str(path), 'w') as f: f.write(routine) def extract(text, start_tag, end_tag): start_position = text.lower().find(start_tag.lower()) end_position = text.lower().find(end_tag.lower()) if start_position == -1 or end_position == -1: return None return text[start_position + len(start_tag):end_position].strip() def download_and_verify_protocol(protocol_hash, protocol_source): response = request_manager.get(protocol_source, timeout=shared_config('timeout')) # It's just a simple txt file if response.status_code == 200: protocol = response.text print('Protocol:', protocol) print('Found hash:', compute_hash(protocol)) print('Target hash:', protocol_hash) # Check if the hash matches if compute_hash(protocol) == protocol_hash: print('Hashes match!') # Save the protocol in the known protocols # PROTOCOL_INFOS[protocol_hash] = { # 'protocol': protocol, # 'source': protocol_source, # 'suitability': Suitability.UNKNOWN # } # # Store it in protocol_documents for future reference # base_folder = Path(os.environ.get('STORAGE_PATH')) / 'protocol_documents' # save_protocol_document(base_folder, protocol_hash, protocol) return protocol print('Failed to download protocol from', protocol_source) return None def send_raw_query(text, protocol_id, target_node, source): return request_manager.post(target_node, json={ 'protocolHash': protocol_id, 'body': text, 'protocolSources' : [source], 'queryId': get_query_id() }, timeout=shared_config('timeout')) _QUERY_ID = None @contextmanager def use_query_id(query_id): global _QUERY_ID assert _QUERY_ID is None, 'Cannot nest query IDs' _QUERY_ID = query_id try: yield finally: _QUERY_ID = None def get_query_id(): return _QUERY_ID SHARED_CONFIG = None def shared_config(key, fallback='no_fallback'): global SHARED_CONFIG if SHARED_CONFIG is None: with open('config.json') as f: SHARED_CONFIG = json.load(f)['shared'] if fallback == 'no_fallback': return SHARED_CONFIG[key] else: return SHARED_CONFIG.get(key, fallback) def serialize_gemini_data(output): # Some Gemini objects are not JSON-serializable, so we need to serialize them manually if isinstance(output, RepeatedComposite): print('RepeatedComposite:', output) parsed = [] for i in range(len(output)): parsed.append(serialize_gemini_data(output[i])) return parsed elif isinstance(output, MapComposite): print('MapComposite:', output) parsed = {} for key in output: parsed[key] = serialize_gemini_data(output[key]) return parsed elif isinstance(output, list): for i, item in enumerate(output): output[i] = serialize_gemini_data(item) elif isinstance(output, dict): for key, value in output.items(): output[key] = serialize_gemini_data(value) return output