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