From 4d6ca5be352cb20decf0bb5a6831cda4c7673a43 Mon Sep 17 00:00:00 2001 From: jsimonweb Date: Tue, 11 Nov 2025 16:04:12 -0800 Subject: [PATCH 1/8] add Agent Builder A2A with Agent Framework example app --- .../Dockerfile | 10 ++ .../README.md | 139 ++++++++++++++++++ .../docker-compose.yml | 12 ++ .../elastic_agent_builder_a2a.py | 37 +++++ .../env.example | 2 + .../test_elastic_agent_builder_a2a.py | 47 ++++++ 6 files changed, 247 insertions(+) create mode 100644 example-apps/agent-builder-a2a-agent-framework/Dockerfile create mode 100644 example-apps/agent-builder-a2a-agent-framework/README.md create mode 100644 example-apps/agent-builder-a2a-agent-framework/docker-compose.yml create mode 100644 example-apps/agent-builder-a2a-agent-framework/elastic_agent_builder_a2a.py create mode 100644 example-apps/agent-builder-a2a-agent-framework/env.example create mode 100644 example-apps/agent-builder-a2a-agent-framework/test_elastic_agent_builder_a2a.py diff --git a/example-apps/agent-builder-a2a-agent-framework/Dockerfile b/example-apps/agent-builder-a2a-agent-framework/Dockerfile new file mode 100644 index 00000000..c32c1947 --- /dev/null +++ b/example-apps/agent-builder-a2a-agent-framework/Dockerfile @@ -0,0 +1,10 @@ +FROM python:3.11-slim + +WORKDIR /app + +COPY elastic_agent_builder_a2a.py test_elastic_agent_builder_a2a.py ./ + +RUN pip install agent-framework +RUN pip install python-dotenv + +CMD ["python", "-m", "unittest", "test_elastic_agent_builder_a2a.py"] diff --git a/example-apps/agent-builder-a2a-agent-framework/README.md b/example-apps/agent-builder-a2a-agent-framework/README.md new file mode 100644 index 00000000..36eaa8fc --- /dev/null +++ b/example-apps/agent-builder-a2a-agent-framework/README.md @@ -0,0 +1,139 @@ +# Elastic Agent Builder A2A App + +**Getting started with Agent Builder and A2A using Microsoft Agent Framework** + +This is a example Python console app that demonstrates how to connect and utilize an [Elastic Agent Builder](https://www.elastic.co/elasticsearch/agent-builder) agent via the Agent2Agent (A2A) Protocol orchestrated with the [Microsoft Agent Framework](https://learn.microsoft.com/en-us/agent-framework/overview/agent-framework-overview). + +## Prerequisites + +1. An Elasticsearch deployment running in [Elastic Cloud](https://cloud.elastic.co/registration). + * Requires Elasticsearch Serverless (or for hosted deployments at least Elasticsearch version 9.2.0). +2. An integrated development environment (IDE) like [Visual Studio Code](https://code.visualstudio.com/download) running on your local computer. +3. [Python version 3.10 or greater](https://www.python.org/downloads/) installed on your local computer. + +## Setup your Elasticsearch deployment + +1. Create an index named `my-docs` in your Elasticsearch deployment by running the following command in Elastic Developer Tools: + + PUT /my-docs + { + "mappings": { + "properties": { + "title": { "type": "text" }, + "content": { + "type": "semantic_text" + }, + "filename": { "type": "keyword" }, + "last_modified": { "type": "date" } + } + } + } +2. Insert a document into your index named `greetings.md` by running the following command in Elastic Developer Tools: + + PUT /my-docs/_doc/greetings-md + { + "title": "Greetings", + "content": " + # Greetings + ## Basic Greeting + Hello! + + ## Helloworld Greeting + Hello World! 🌎 + + ## Not Greeting + I'm only a greeting agent. 🤷 + + ", + "filename": "greetings.md", + "last_modified": "2025-11-04T12:00:00Z" + } + +3. In Elastic Agent Builder, create a **tool** with the following values: +* **Type**: `ES|QL` +* **Tool ID**: `example.get_greetings` +* **Description**: `Get greetings doc from Elasticsearch my\docs index.` +* **ES|QL**: + + FROM my-docs | WHERE filename == "greetings.md" + +4. In Elastic Agent Builder, create an **agent** with the following values: +* **Agent ID**: `helloworld_agent` +* **Custom Instructions**: + + If the prompt contains greeting text like "Hi" or "Hello" then respond with only the Basic Hello text from your documents. + + If the prompt contains the text “Hello World” then respond with only the Hello World text from your documents. + + In all other cases where the prompt does not contain greeting words, then respond with only the Not Greeting text from your documents. + +* **Display Name**: `HelloWorld Agent` +* **Display Description**: `An agent that responds to greetings.` + + + +## Running the example app + +1. Open Visual Studio Code and open a new terminal within the Visual Studio Code editor. +2. In the open terminal, clone the Search Labs source code repository which contains the Elastic Agent Builder A2A App example. + + git clone https://github.com/elastic/elasticsearch-labs + +3. `cd` to change directory to the example code located in the `example-apps/agent-builder-a2a-agent-framework` subdirectory. + + cd elasticsearch-labs/example-apps/agent-builder-a2a-agent-framework + +4. Replace placeholder values in `elastic_agent_builder_a2a.py` with values copied from your Elastic deployment. + 1. Open the file `elastic_agent_builder_a2a.py` in the Visual Studio editor. + 2. Replace + 1. In your Elastic deployment, go to the Elastic Agent Builder - Tools page. Click the **MCP Server** dropdown at the top of the Tools page. Select **Copy MCP Server URL.** + 2. In Visual Studio add the **MCP Server URL** value to the `elastic-agent-builder-a2a.py` file. + * Find where the placeholder text “**\**” appears and paste in the copied **MCP Server URL** to replace the placeholder text. Now edit the pasted **MCP Server URL**. Delete the text “mcp” at the end of the URL and replace it with the text “a2a”. The edited URL should look something like this + + `https://example-project-a123.kb.westus2.azure.elastic.cloud/api/agent_builder/a2a` + + 3. Replace \ + 1. In your Elastic deployment, click **Elasticsearch** in the navigation menu to go to your deployment’s home page. + 2. Click **Create API key** to create a new API key. + 3. After the API key is created, copy the API Key value. + 4. In Visual Studio add the API Key value to the `elastic-agent-builder-a2a.pys` file. + * Find where the placeholder text “**\**” appears and paste in the copied API Key value to replace the placeholder text. + + 4. Confirm the **relative_card_path** is set correctly in the `elastic-agent-builder-a2a.py` file by finding the code line that starts with the text “agent_card”. Confirm that the **relative_card_path** matches the Agent ID you specified when you created the agent in Elastic Agent Builder. If your Agent ID is “helloworld_agent” then the **relative_card_path** should be set to `/helloworld_agent.json` + 5. Save the `elastic_agent_builder_a2a.py` file in the Visual Studio editor. + +5. Create a Python virtual environment by running the following code in the Visual Studio Code terminal. + + python \-m venv .venv + +6. Activate the Python virtual environment. + * If you’re running MacOS, the command to activate the virtual environment is: + + source .venv/bin/activate + + * If you’re on Windows, the command to activate the virtual environment is: + + .venv\Scripts\activate + +7. Install the Microsoft Agent Framework with the following `pip` command: + + pip install agent-framework + +8. Run the example code by entering the following command into the terminal: + + python elastic-agent-builder-a2a.py + +## Running the example test + +1. Setup the environment variables. + 1. Make a copy of the file `env.example` and name the new file `.env ` + 2. Edit the `.env` file to replace the placeholder text with actual values from your Elastic deployment. See instructions on where to get these values in the [Running the example app](#running-the-example-app) section of this `README.md` file. + * Replace **YOUR-ELASTIC-AGENT-BUILDER-URL** + * Replace **YOUR-ELASTIC-API-KEY** +2. Run the test directly with Python. + + python test_elastic_agent_builder_a2a.py + +3. Run the test with Docker. + + docker compose up diff --git a/example-apps/agent-builder-a2a-agent-framework/docker-compose.yml b/example-apps/agent-builder-a2a-agent-framework/docker-compose.yml new file mode 100644 index 00000000..bf7c9834 --- /dev/null +++ b/example-apps/agent-builder-a2a-agent-framework/docker-compose.yml @@ -0,0 +1,12 @@ +version: '3.8' + +services: + elastic-agent-builder-a2a-test: + build: + context: . + dockerfile: Dockerfile + container_name: elastic-agent-builder-a2a-test + environment: + - ES_AGENT_URL=${ES_AGENT_URL} + - ES_API_KEY=${ES_API_KEY} + \ No newline at end of file diff --git a/example-apps/agent-builder-a2a-agent-framework/elastic_agent_builder_a2a.py b/example-apps/agent-builder-a2a-agent-framework/elastic_agent_builder_a2a.py new file mode 100644 index 00000000..39685240 --- /dev/null +++ b/example-apps/agent-builder-a2a-agent-framework/elastic_agent_builder_a2a.py @@ -0,0 +1,37 @@ +import asyncio +import httpx +from a2a.client import A2ACardResolver +from agent_framework.a2a import A2AAgent + +async def main(): + a2a_agent_host = "" + + print(f"Connection to Elastic A2A agent at: {a2a_agent_host}") + + custom_headers = { + "Authorization": "ApiKey " + } + + async with httpx.AsyncClient(timeout=60.0, headers=custom_headers) as http_client: + # Resolve the A2A Agent Card + resolver = A2ACardResolver(httpx_client=http_client, base_url=a2a_agent_host) + agent_card = await resolver.get_agent_card(relative_card_path="/helloworld_agent.json") + print(f"Found Agent: {agent_card.name} - {agent_card.description}") + + # Use the Agent + agent = A2AAgent( + name=agent_card.name, + description=agent_card.description, + agent_card=agent_card, + url=a2a_agent_host, + http_client=http_client + ) + prompt = input("Enter Greeting >>> ") + print("\nSending message to Elastic A2A agent...") + response = await agent.run(prompt) + print("\nAgent Response:") + for message in response.messages: + print(message.text) + +if __name__ == "__main__": + asyncio.run(main()) diff --git a/example-apps/agent-builder-a2a-agent-framework/env.example b/example-apps/agent-builder-a2a-agent-framework/env.example new file mode 100644 index 00000000..02bc9cbf --- /dev/null +++ b/example-apps/agent-builder-a2a-agent-framework/env.example @@ -0,0 +1,2 @@ +ES_AGENT_URL=YOUR-ELASTIC-AGENT-BUILDER-URL +ES_API_KEY=YOUR-ELASTIC-API-KEY diff --git a/example-apps/agent-builder-a2a-agent-framework/test_elastic_agent_builder_a2a.py b/example-apps/agent-builder-a2a-agent-framework/test_elastic_agent_builder_a2a.py new file mode 100644 index 00000000..3d7c71f8 --- /dev/null +++ b/example-apps/agent-builder-a2a-agent-framework/test_elastic_agent_builder_a2a.py @@ -0,0 +1,47 @@ +import unittest +from unittest.mock import patch +from dotenv import load_dotenv +import os +import asyncio +from pathlib import Path +import importlib.util + +class test_main_function(unittest.TestCase): + def setUp(self): + # Replace placeholder values with .env values + load_dotenv() + + self.test_file = Path("elastic_agent_builder_a2a.py") + self.backup_file = Path("elastic_agent_builder_a2a.py.backup") + + if self.test_file.exists(): + self.original_content = self.test_file.read_text() + self.backup_file.write_text(self.original_content) + + self.es_agent_url = os.getenv("ES_AGENT_URL") + self.es_api_key = os.getenv("ES_API_KEY") + content = self.test_file.read_text() + modified_content = content.replace("", self.es_agent_url) + modified_content = modified_content.replace("", self.es_api_key) + self.test_file.write_text(modified_content) + + # Import the modified module + spec = importlib.util.spec_from_file_location("elastic_agent_builder_a2a", self.test_file) + self.user_input_module = importlib.util.module_from_spec(spec) + spec.loader.exec_module(self.user_input_module) + + def tearDown(self): + if self.backup_file.exists(): + self.test_file.write_text(self.backup_file.read_text()) + self.backup_file.unlink() + + + @patch('builtins.input', return_value='hello world') + @patch('builtins.print') + def test_main_input(self, mock_print, mock_input): + # Run test + asyncio.run(self.user_input_module.main()) + mock_print.assert_called_with("Hello World! 🌎") + +if __name__ == "__main__": + unittest.main() \ No newline at end of file From 3c5e56c0488d025a2933c5bdb1288911a9eac067 Mon Sep 17 00:00:00 2001 From: jsimonweb Date: Tue, 11 Nov 2025 16:51:00 -0800 Subject: [PATCH 2/8] pre-commit formatting --- .../elastic_agent_builder_a2a.py | 12 +++++---- .../test_elastic_agent_builder_a2a.py | 25 ++++++++++++------- 2 files changed, 23 insertions(+), 14 deletions(-) diff --git a/example-apps/agent-builder-a2a-agent-framework/elastic_agent_builder_a2a.py b/example-apps/agent-builder-a2a-agent-framework/elastic_agent_builder_a2a.py index 39685240..f62ccee8 100644 --- a/example-apps/agent-builder-a2a-agent-framework/elastic_agent_builder_a2a.py +++ b/example-apps/agent-builder-a2a-agent-framework/elastic_agent_builder_a2a.py @@ -3,19 +3,20 @@ from a2a.client import A2ACardResolver from agent_framework.a2a import A2AAgent + async def main(): a2a_agent_host = "" print(f"Connection to Elastic A2A agent at: {a2a_agent_host}") - custom_headers = { - "Authorization": "ApiKey " - } + custom_headers = {"Authorization": "ApiKey "} async with httpx.AsyncClient(timeout=60.0, headers=custom_headers) as http_client: # Resolve the A2A Agent Card resolver = A2ACardResolver(httpx_client=http_client, base_url=a2a_agent_host) - agent_card = await resolver.get_agent_card(relative_card_path="/helloworld_agent.json") + agent_card = await resolver.get_agent_card( + relative_card_path="/helloworld_agent.json" + ) print(f"Found Agent: {agent_card.name} - {agent_card.description}") # Use the Agent @@ -24,7 +25,7 @@ async def main(): description=agent_card.description, agent_card=agent_card, url=a2a_agent_host, - http_client=http_client + http_client=http_client, ) prompt = input("Enter Greeting >>> ") print("\nSending message to Elastic A2A agent...") @@ -33,5 +34,6 @@ async def main(): for message in response.messages: print(message.text) + if __name__ == "__main__": asyncio.run(main()) diff --git a/example-apps/agent-builder-a2a-agent-framework/test_elastic_agent_builder_a2a.py b/example-apps/agent-builder-a2a-agent-framework/test_elastic_agent_builder_a2a.py index 3d7c71f8..30512809 100644 --- a/example-apps/agent-builder-a2a-agent-framework/test_elastic_agent_builder_a2a.py +++ b/example-apps/agent-builder-a2a-agent-framework/test_elastic_agent_builder_a2a.py @@ -3,9 +3,10 @@ from dotenv import load_dotenv import os import asyncio -from pathlib import Path +from pathlib import Path import importlib.util + class test_main_function(unittest.TestCase): def setUp(self): # Replace placeholder values with .env values @@ -21,12 +22,18 @@ def setUp(self): self.es_agent_url = os.getenv("ES_AGENT_URL") self.es_api_key = os.getenv("ES_API_KEY") content = self.test_file.read_text() - modified_content = content.replace("", self.es_agent_url) - modified_content = modified_content.replace("", self.es_api_key) + modified_content = content.replace( + "", self.es_agent_url + ) + modified_content = modified_content.replace( + "", self.es_api_key + ) self.test_file.write_text(modified_content) # Import the modified module - spec = importlib.util.spec_from_file_location("elastic_agent_builder_a2a", self.test_file) + spec = importlib.util.spec_from_file_location( + "elastic_agent_builder_a2a", self.test_file + ) self.user_input_module = importlib.util.module_from_spec(spec) spec.loader.exec_module(self.user_input_module) @@ -35,13 +42,13 @@ def tearDown(self): self.test_file.write_text(self.backup_file.read_text()) self.backup_file.unlink() - - @patch('builtins.input', return_value='hello world') - @patch('builtins.print') + @patch("builtins.input", return_value="hello world") + @patch("builtins.print") def test_main_input(self, mock_print, mock_input): # Run test asyncio.run(self.user_input_module.main()) mock_print.assert_called_with("Hello World! 🌎") - + + if __name__ == "__main__": - unittest.main() \ No newline at end of file + unittest.main() From a120ed3dd6456c9735216fe751824065c27ebf69 Mon Sep 17 00:00:00 2001 From: jsimonweb Date: Tue, 11 Nov 2025 17:35:44 -0800 Subject: [PATCH 3/8] update cloud registration url --- example-apps/agent-builder-a2a-agent-framework/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/example-apps/agent-builder-a2a-agent-framework/README.md b/example-apps/agent-builder-a2a-agent-framework/README.md index 36eaa8fc..93ae091e 100644 --- a/example-apps/agent-builder-a2a-agent-framework/README.md +++ b/example-apps/agent-builder-a2a-agent-framework/README.md @@ -6,7 +6,7 @@ This is a example Python console app that demonstrates how to connect and utiliz ## Prerequisites -1. An Elasticsearch deployment running in [Elastic Cloud](https://cloud.elastic.co/registration). +1. An Elasticsearch deployment running in [Elastic Cloud](https://cloud.elastic.co/registration?utm_source=github&utm_content=elasticsearch-labs-example-apps). * Requires Elasticsearch Serverless (or for hosted deployments at least Elasticsearch version 9.2.0). 2. An integrated development environment (IDE) like [Visual Studio Code](https://code.visualstudio.com/download) running on your local computer. 3. [Python version 3.10 or greater](https://www.python.org/downloads/) installed on your local computer. From 08ae56671da65b01b3a075e91e655c3fafd25669 Mon Sep 17 00:00:00 2001 From: jsimonweb Date: Wed, 12 Nov 2025 09:09:48 -0800 Subject: [PATCH 4/8] update to address pre-commit requirement --- example-apps/agent-builder-a2a-agent-framework/README.md | 6 +++--- example-apps/agent-builder-a2a-agent-framework/env.example | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/example-apps/agent-builder-a2a-agent-framework/README.md b/example-apps/agent-builder-a2a-agent-framework/README.md index 93ae091e..281ec30d 100644 --- a/example-apps/agent-builder-a2a-agent-framework/README.md +++ b/example-apps/agent-builder-a2a-agent-framework/README.md @@ -127,9 +127,9 @@ This is a example Python console app that demonstrates how to connect and utiliz 1. Setup the environment variables. 1. Make a copy of the file `env.example` and name the new file `.env ` - 2. Edit the `.env` file to replace the placeholder text with actual values from your Elastic deployment. See instructions on where to get these values in the [Running the example app](#running-the-example-app) section of this `README.md` file. - * Replace **YOUR-ELASTIC-AGENT-BUILDER-URL** - * Replace **YOUR-ELASTIC-API-KEY** + 2. Edit the `.env` file to set the values of the environment variables to use the values copied from your Elastic deployment. See instructions on where to get these values in the [Running the example app](#running-the-example-app) section of this `README.md` file. + * Set the value of **ES_AGENT_URL** to be the value of **YOUR-ELASTIC-AGENT-BUILDER-URL** + * Set the value of **ES_API_KEY** to be value of **YOUR-ELASTIC-API-KEY** 2. Run the test directly with Python. python test_elastic_agent_builder_a2a.py diff --git a/example-apps/agent-builder-a2a-agent-framework/env.example b/example-apps/agent-builder-a2a-agent-framework/env.example index 02bc9cbf..d1f1dfb7 100644 --- a/example-apps/agent-builder-a2a-agent-framework/env.example +++ b/example-apps/agent-builder-a2a-agent-framework/env.example @@ -1,2 +1,2 @@ -ES_AGENT_URL=YOUR-ELASTIC-AGENT-BUILDER-URL -ES_API_KEY=YOUR-ELASTIC-API-KEY +ES_AGENT_URL= +ES_API_KEY= From 5e2a42a312c4d97d6dae8145a978c02032cc5cef Mon Sep 17 00:00:00 2001 From: jsimonweb Date: Fri, 14 Nov 2025 13:53:41 -0800 Subject: [PATCH 5/8] fix typos --- .../agent-builder-a2a-agent-framework/README.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/example-apps/agent-builder-a2a-agent-framework/README.md b/example-apps/agent-builder-a2a-agent-framework/README.md index 281ec30d..b1556fe4 100644 --- a/example-apps/agent-builder-a2a-agent-framework/README.md +++ b/example-apps/agent-builder-a2a-agent-framework/README.md @@ -2,7 +2,7 @@ **Getting started with Agent Builder and A2A using Microsoft Agent Framework** -This is a example Python console app that demonstrates how to connect and utilize an [Elastic Agent Builder](https://www.elastic.co/elasticsearch/agent-builder) agent via the Agent2Agent (A2A) Protocol orchestrated with the [Microsoft Agent Framework](https://learn.microsoft.com/en-us/agent-framework/overview/agent-framework-overview). +This is an example Python console app that demonstrates how to connect and utilize an [Elastic Agent Builder](https://www.elastic.co/elasticsearch/agent-builder) agent via the Agent2Agent (A2A) Protocol orchestrated with the [Microsoft Agent Framework](https://learn.microsoft.com/en-us/agent-framework/overview/agent-framework-overview). ## Prerequisites @@ -52,7 +52,7 @@ This is a example Python console app that demonstrates how to connect and utiliz 3. In Elastic Agent Builder, create a **tool** with the following values: * **Type**: `ES|QL` * **Tool ID**: `example.get_greetings` -* **Description**: `Get greetings doc from Elasticsearch my\docs index.` +* **Description**: `Get greetings doc from Elasticsearch my-docs index.` * **ES|QL**: FROM my-docs | WHERE filename == "greetings.md" @@ -88,23 +88,23 @@ This is a example Python console app that demonstrates how to connect and utiliz 2. Replace 1. In your Elastic deployment, go to the Elastic Agent Builder - Tools page. Click the **MCP Server** dropdown at the top of the Tools page. Select **Copy MCP Server URL.** 2. In Visual Studio add the **MCP Server URL** value to the `elastic-agent-builder-a2a.py` file. - * Find where the placeholder text “**\**” appears and paste in the copied **MCP Server URL** to replace the placeholder text. Now edit the pasted **MCP Server URL**. Delete the text “mcp” at the end of the URL and replace it with the text “a2a”. The edited URL should look something like this + * Find where the placeholder text “****” appears and paste in the copied **MCP Server URL** to replace the placeholder text. Now edit the pasted **MCP Server URL**. Delete the text “mcp” at the end of the URL and replace it with the text “a2a”. The edited URL should look something like this `https://example-project-a123.kb.westus2.azure.elastic.cloud/api/agent_builder/a2a` - 3. Replace \ + 3. Replace 1. In your Elastic deployment, click **Elasticsearch** in the navigation menu to go to your deployment’s home page. 2. Click **Create API key** to create a new API key. 3. After the API key is created, copy the API Key value. - 4. In Visual Studio add the API Key value to the `elastic-agent-builder-a2a.pys` file. - * Find where the placeholder text “**\**” appears and paste in the copied API Key value to replace the placeholder text. + 4. In Visual Studio add the API Key value to the `elastic-agent-builder-a2a.py` file. + * Find where the placeholder text “****” appears and paste in the copied API Key value to replace the placeholder text. 4. Confirm the **relative_card_path** is set correctly in the `elastic-agent-builder-a2a.py` file by finding the code line that starts with the text “agent_card”. Confirm that the **relative_card_path** matches the Agent ID you specified when you created the agent in Elastic Agent Builder. If your Agent ID is “helloworld_agent” then the **relative_card_path** should be set to `/helloworld_agent.json` 5. Save the `elastic_agent_builder_a2a.py` file in the Visual Studio editor. 5. Create a Python virtual environment by running the following code in the Visual Studio Code terminal. - python \-m venv .venv + python -m venv .venv 6. Activate the Python virtual environment. * If you’re running MacOS, the command to activate the virtual environment is: From 6c5aa71fd20e164c2df0f8a4271cd476adf26e7b Mon Sep 17 00:00:00 2001 From: jsimonweb Date: Fri, 14 Nov 2025 14:27:15 -0800 Subject: [PATCH 6/8] fix typo --- example-apps/agent-builder-a2a-agent-framework/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/example-apps/agent-builder-a2a-agent-framework/README.md b/example-apps/agent-builder-a2a-agent-framework/README.md index b1556fe4..d9b3cd16 100644 --- a/example-apps/agent-builder-a2a-agent-framework/README.md +++ b/example-apps/agent-builder-a2a-agent-framework/README.md @@ -121,7 +121,7 @@ This is an example Python console app that demonstrates how to connect and utili 8. Run the example code by entering the following command into the terminal: - python elastic-agent-builder-a2a.py + python elastic_agent_builder_a2a.py ## Running the example test From dae782705d68f58d6c78ead4eb780763574579ed Mon Sep 17 00:00:00 2001 From: jsimonweb Date: Wed, 19 Nov 2025 03:25:39 -0800 Subject: [PATCH 7/8] address review comments --- .../Dockerfile | 10 -- .../docker-compose.yml | 12 -- .../env.example | 2 - .../test_elastic_agent_builder_a2a.py | 54 -------- .../Dockerfile | 15 ++ .../README.md | 56 ++++---- .../docker-compose.yml | 9 ++ .../elastic_agent_builder_a2a.py | 8 +- .../env.example | 2 + .../requirements.txt | 131 ++++++++++++++++++ 10 files changed, 188 insertions(+), 111 deletions(-) delete mode 100644 example-apps/agent-builder-a2a-agent-framework/Dockerfile delete mode 100644 example-apps/agent-builder-a2a-agent-framework/docker-compose.yml delete mode 100644 example-apps/agent-builder-a2a-agent-framework/env.example delete mode 100644 example-apps/agent-builder-a2a-agent-framework/test_elastic_agent_builder_a2a.py create mode 100644 supporting-blog-content/agent-builder-a2a-agent-framework/Dockerfile rename {example-apps => supporting-blog-content}/agent-builder-a2a-agent-framework/README.md (65%) create mode 100644 supporting-blog-content/agent-builder-a2a-agent-framework/docker-compose.yml rename {example-apps => supporting-blog-content}/agent-builder-a2a-agent-framework/elastic_agent_builder_a2a.py (84%) create mode 100644 supporting-blog-content/agent-builder-a2a-agent-framework/env.example create mode 100644 supporting-blog-content/agent-builder-a2a-agent-framework/requirements.txt diff --git a/example-apps/agent-builder-a2a-agent-framework/Dockerfile b/example-apps/agent-builder-a2a-agent-framework/Dockerfile deleted file mode 100644 index c32c1947..00000000 --- a/example-apps/agent-builder-a2a-agent-framework/Dockerfile +++ /dev/null @@ -1,10 +0,0 @@ -FROM python:3.11-slim - -WORKDIR /app - -COPY elastic_agent_builder_a2a.py test_elastic_agent_builder_a2a.py ./ - -RUN pip install agent-framework -RUN pip install python-dotenv - -CMD ["python", "-m", "unittest", "test_elastic_agent_builder_a2a.py"] diff --git a/example-apps/agent-builder-a2a-agent-framework/docker-compose.yml b/example-apps/agent-builder-a2a-agent-framework/docker-compose.yml deleted file mode 100644 index bf7c9834..00000000 --- a/example-apps/agent-builder-a2a-agent-framework/docker-compose.yml +++ /dev/null @@ -1,12 +0,0 @@ -version: '3.8' - -services: - elastic-agent-builder-a2a-test: - build: - context: . - dockerfile: Dockerfile - container_name: elastic-agent-builder-a2a-test - environment: - - ES_AGENT_URL=${ES_AGENT_URL} - - ES_API_KEY=${ES_API_KEY} - \ No newline at end of file diff --git a/example-apps/agent-builder-a2a-agent-framework/env.example b/example-apps/agent-builder-a2a-agent-framework/env.example deleted file mode 100644 index d1f1dfb7..00000000 --- a/example-apps/agent-builder-a2a-agent-framework/env.example +++ /dev/null @@ -1,2 +0,0 @@ -ES_AGENT_URL= -ES_API_KEY= diff --git a/example-apps/agent-builder-a2a-agent-framework/test_elastic_agent_builder_a2a.py b/example-apps/agent-builder-a2a-agent-framework/test_elastic_agent_builder_a2a.py deleted file mode 100644 index 30512809..00000000 --- a/example-apps/agent-builder-a2a-agent-framework/test_elastic_agent_builder_a2a.py +++ /dev/null @@ -1,54 +0,0 @@ -import unittest -from unittest.mock import patch -from dotenv import load_dotenv -import os -import asyncio -from pathlib import Path -import importlib.util - - -class test_main_function(unittest.TestCase): - def setUp(self): - # Replace placeholder values with .env values - load_dotenv() - - self.test_file = Path("elastic_agent_builder_a2a.py") - self.backup_file = Path("elastic_agent_builder_a2a.py.backup") - - if self.test_file.exists(): - self.original_content = self.test_file.read_text() - self.backup_file.write_text(self.original_content) - - self.es_agent_url = os.getenv("ES_AGENT_URL") - self.es_api_key = os.getenv("ES_API_KEY") - content = self.test_file.read_text() - modified_content = content.replace( - "", self.es_agent_url - ) - modified_content = modified_content.replace( - "", self.es_api_key - ) - self.test_file.write_text(modified_content) - - # Import the modified module - spec = importlib.util.spec_from_file_location( - "elastic_agent_builder_a2a", self.test_file - ) - self.user_input_module = importlib.util.module_from_spec(spec) - spec.loader.exec_module(self.user_input_module) - - def tearDown(self): - if self.backup_file.exists(): - self.test_file.write_text(self.backup_file.read_text()) - self.backup_file.unlink() - - @patch("builtins.input", return_value="hello world") - @patch("builtins.print") - def test_main_input(self, mock_print, mock_input): - # Run test - asyncio.run(self.user_input_module.main()) - mock_print.assert_called_with("Hello World! 🌎") - - -if __name__ == "__main__": - unittest.main() diff --git a/supporting-blog-content/agent-builder-a2a-agent-framework/Dockerfile b/supporting-blog-content/agent-builder-a2a-agent-framework/Dockerfile new file mode 100644 index 00000000..b8f80f9a --- /dev/null +++ b/supporting-blog-content/agent-builder-a2a-agent-framework/Dockerfile @@ -0,0 +1,15 @@ +FROM python:3.11-slim + +WORKDIR /app + +RUN python -m venv /opt/venv + +ENV PATH="/opt/venv/bin:$PATH" + +COPY requirements.txt . + +RUN pip install -r requirements.txt + +COPY elastic_agent_builder_a2a.py . + +CMD ["python", "elastic_agent_builder_a2a.py"] \ No newline at end of file diff --git a/example-apps/agent-builder-a2a-agent-framework/README.md b/supporting-blog-content/agent-builder-a2a-agent-framework/README.md similarity index 65% rename from example-apps/agent-builder-a2a-agent-framework/README.md rename to supporting-blog-content/agent-builder-a2a-agent-framework/README.md index d9b3cd16..54d1173a 100644 --- a/example-apps/agent-builder-a2a-agent-framework/README.md +++ b/supporting-blog-content/agent-builder-a2a-agent-framework/README.md @@ -8,7 +8,7 @@ This is an example Python console app that demonstrates how to connect and utili 1. An Elasticsearch deployment running in [Elastic Cloud](https://cloud.elastic.co/registration?utm_source=github&utm_content=elasticsearch-labs-example-apps). * Requires Elasticsearch Serverless (or for hosted deployments at least Elasticsearch version 9.2.0). -2. An integrated development environment (IDE) like [Visual Studio Code](https://code.visualstudio.com/download) running on your local computer. +2. A text editor or an integrated development environment (IDE) like [Visual Studio Code](https://code.visualstudio.com/download) running on your local computer. 3. [Python version 3.10 or greater](https://www.python.org/downloads/) installed on your local computer. ## Setup your Elasticsearch deployment @@ -72,41 +72,44 @@ This is an example Python console app that demonstrates how to connect and utili -## Running the example app +## Clone the example app -1. Open Visual Studio Code and open a new terminal within the Visual Studio Code editor. -2. In the open terminal, clone the Search Labs source code repository which contains the Elastic Agent Builder A2A App example. +1. Open a terminal and clone the Search Labs source code repository which contains the Elastic Agent Builder A2A App example. Run the following command to clone the example app: git clone https://github.com/elastic/elasticsearch-labs -3. `cd` to change directory to the example code located in the `example-apps/agent-builder-a2a-agent-framework` subdirectory. +3. `cd` to change directory to the example code located in the `supporting-blog-content/agent-builder-a2a-agent-framework` subdirectory. - cd elasticsearch-labs/example-apps/agent-builder-a2a-agent-framework + cd elasticsearch-labs/supporting-blog-content/agent-builder-a2a-agent-framework -4. Replace placeholder values in `elastic_agent_builder_a2a.py` with values copied from your Elastic deployment. - 1. Open the file `elastic_agent_builder_a2a.py` in the Visual Studio editor. - 2. Replace +## Setup up the environment variables + +1. Setup the environment variables with values copied from your Elastic deployment. + 1. Make a copy of the file `env.example` and name the new file `.env ` + 2. Edit the `.env` file to set the values of the environment variables to use the values copied from your Elastic deployment. + * Replace 1. In your Elastic deployment, go to the Elastic Agent Builder - Tools page. Click the **MCP Server** dropdown at the top of the Tools page. Select **Copy MCP Server URL.** - 2. In Visual Studio add the **MCP Server URL** value to the `elastic-agent-builder-a2a.py` file. + 2. Add the **MCP Server URL** value to the `.env` file. * Find where the placeholder text “****” appears and paste in the copied **MCP Server URL** to replace the placeholder text. Now edit the pasted **MCP Server URL**. Delete the text “mcp” at the end of the URL and replace it with the text “a2a”. The edited URL should look something like this `https://example-project-a123.kb.westus2.azure.elastic.cloud/api/agent_builder/a2a` - 3. Replace + * Replace 1. In your Elastic deployment, click **Elasticsearch** in the navigation menu to go to your deployment’s home page. 2. Click **Create API key** to create a new API key. 3. After the API key is created, copy the API Key value. - 4. In Visual Studio add the API Key value to the `elastic-agent-builder-a2a.py` file. + 4. Add the API Key value to the `.env` file. * Find where the placeholder text “****” appears and paste in the copied API Key value to replace the placeholder text. + + 3. Save the changes to the `.env` file. - 4. Confirm the **relative_card_path** is set correctly in the `elastic-agent-builder-a2a.py` file by finding the code line that starts with the text “agent_card”. Confirm that the **relative_card_path** matches the Agent ID you specified when you created the agent in Elastic Agent Builder. If your Agent ID is “helloworld_agent” then the **relative_card_path** should be set to `/helloworld_agent.json` - 5. Save the `elastic_agent_builder_a2a.py` file in the Visual Studio editor. +## Running the example app with Python -5. Create a Python virtual environment by running the following code in the Visual Studio Code terminal. +1. Create a Python virtual environment by running the following code in the terminal. python -m venv .venv -6. Activate the Python virtual environment. +2. Activate the Python virtual environment. * If you’re running MacOS, the command to activate the virtual environment is: source .venv/bin/activate @@ -115,25 +118,16 @@ This is an example Python console app that demonstrates how to connect and utili .venv\Scripts\activate -7. Install the Microsoft Agent Framework with the following `pip` command: +3. Install the Microsoft Agent Framework along with its necessary Python packages by running the following `pip` command: - pip install agent-framework + pip install -r requirements.txt -8. Run the example code by entering the following command into the terminal: +4. Run the example app by entering the following command into the terminal: python elastic_agent_builder_a2a.py -## Running the example test - -1. Setup the environment variables. - 1. Make a copy of the file `env.example` and name the new file `.env ` - 2. Edit the `.env` file to set the values of the environment variables to use the values copied from your Elastic deployment. See instructions on where to get these values in the [Running the example app](#running-the-example-app) section of this `README.md` file. - * Set the value of **ES_AGENT_URL** to be the value of **YOUR-ELASTIC-AGENT-BUILDER-URL** - * Set the value of **ES_API_KEY** to be value of **YOUR-ELASTIC-API-KEY** -2. Run the test directly with Python. - - python test_elastic_agent_builder_a2a.py +## Running the example app with Docker -3. Run the test with Docker. +1. Run the example app with Docker by entering the following command into the terminal: - docker compose up + docker compose run elastic-agent-builder-a2a diff --git a/supporting-blog-content/agent-builder-a2a-agent-framework/docker-compose.yml b/supporting-blog-content/agent-builder-a2a-agent-framework/docker-compose.yml new file mode 100644 index 00000000..8a2834df --- /dev/null +++ b/supporting-blog-content/agent-builder-a2a-agent-framework/docker-compose.yml @@ -0,0 +1,9 @@ +services: + elastic-agent-builder-a2a: + build: . + container_name: elastic-agent-builder-a2a + stdin_open: true + tty: true + environment: + - ES_AGENT_URL=${ES_AGENT_URL} + - ES_API_KEY=${ES_API_KEY} \ No newline at end of file diff --git a/example-apps/agent-builder-a2a-agent-framework/elastic_agent_builder_a2a.py b/supporting-blog-content/agent-builder-a2a-agent-framework/elastic_agent_builder_a2a.py similarity index 84% rename from example-apps/agent-builder-a2a-agent-framework/elastic_agent_builder_a2a.py rename to supporting-blog-content/agent-builder-a2a-agent-framework/elastic_agent_builder_a2a.py index f62ccee8..c6132bee 100644 --- a/example-apps/agent-builder-a2a-agent-framework/elastic_agent_builder_a2a.py +++ b/supporting-blog-content/agent-builder-a2a-agent-framework/elastic_agent_builder_a2a.py @@ -1,15 +1,19 @@ import asyncio +from dotenv import load_dotenv import httpx +import os from a2a.client import A2ACardResolver from agent_framework.a2a import A2AAgent async def main(): - a2a_agent_host = "" + load_dotenv() + a2a_agent_host = os.getenv("ES_AGENT_URL") + a2a_agent_key = os.getenv("ES_API_KEY") print(f"Connection to Elastic A2A agent at: {a2a_agent_host}") - custom_headers = {"Authorization": "ApiKey "} + custom_headers = {"Authorization": f"ApiKey {a2a_agent_key}"} async with httpx.AsyncClient(timeout=60.0, headers=custom_headers) as http_client: # Resolve the A2A Agent Card diff --git a/supporting-blog-content/agent-builder-a2a-agent-framework/env.example b/supporting-blog-content/agent-builder-a2a-agent-framework/env.example new file mode 100644 index 00000000..a857c0fd --- /dev/null +++ b/supporting-blog-content/agent-builder-a2a-agent-framework/env.example @@ -0,0 +1,2 @@ +ES_AGENT_URL= +ES_API_KEY= \ No newline at end of file diff --git a/supporting-blog-content/agent-builder-a2a-agent-framework/requirements.txt b/supporting-blog-content/agent-builder-a2a-agent-framework/requirements.txt new file mode 100644 index 00000000..f54e6fd9 --- /dev/null +++ b/supporting-blog-content/agent-builder-a2a-agent-framework/requirements.txt @@ -0,0 +1,131 @@ +a2a-sdk~=0.3.14 +ag-ui-protocol~=0.1.10 +agent-framework~=1.0.0b251114 +agent-framework-a2a~=1.0.0b251114 +agent-framework-ag-ui~=1.0.0b251117 +agent-framework-anthropic~=1.0.0b251114 +agent-framework-azure-ai~=1.0.0b251114 +agent-framework-azurefunctions~=1.0.0b251114 +agent-framework-chatkit~=1.0.0b251114 +agent-framework-copilotstudio~=1.0.0b251114 +agent-framework-core~=1.0.0b251114 +agent-framework-devui~=1.0.0b251114 +agent-framework-lab~=1.0.0b251024 +agent-framework-mem0~=1.0.0b251114 +agent-framework-purview~=1.0.0b251114 +agent-framework-redis~=1.0.0b251114 +aiohappyeyeballs~=2.6.1 +aiohttp~=3.13.2 +aiosignal~=1.4.0 +annotated-doc~=0.0.4 +annotated-types~=0.7.0 +anthropic~=0.74.0 +anyio~=4.11.0 +attrs~=25.4.0 +azure-ai-agents~=1.2.0b5 +azure-ai-projects~=2.0.0b2 +azure-core~=1.36.0 +azure-functions~=1.24.0 +azure-functions-durable~=1.4.0 +azure-identity~=1.25.1 +azure-storage-blob~=12.27.1 +backoff~=2.2.1 +cachetools~=6.2.2 +certifi~=2025.11.12 +cffi~=2.0.0 +charset-normalizer~=3.4.4 +click~=8.3.1 +colorama~=0.4.6 +cryptography~=46.0.3 +distro~=1.9.0 +docstring_parser~=0.17.0 +fastapi~=0.121.2 +frozenlist~=1.8.0 +furl~=2.1.4 +google-api-core~=2.28.1 +google-auth~=2.43.0 +googleapis-common-protos~=1.72.0 +griffe~=1.15.0 +grpcio~=1.76.0 +h11~=0.16.0 +h2~=4.3.0 +hpack~=4.1.0 +httpcore~=1.0.9 +httptools~=0.7.1 +httpx~=0.28.1 +httpx-sse~=0.4.3 +hyperframe~=6.1.0 +idna~=3.11 +importlib_metadata~=8.7.0 +isodate~=0.7.2 +jiter~=0.12.0 +jsonpath-ng~=1.7.0 +jsonschema~=4.25.1 +jsonschema-specifications~=2025.9.1 +MarkupSafe~=3.0.3 +mcp~=1.21.2 +mem0ai~=1.0.1 +microsoft-agents-activity~=0.6.0 +microsoft-agents-copilotstudio-client~=0.6.0 +microsoft-agents-hosting-core~=0.6.0 +ml_dtypes~=0.5.4 +msal~=1.34.0 +msal-extensions~=1.3.1 +multidict~=6.7.0 +numpy~=2.3.5 +openai~=2.8.1 +openai-agents~=0.6.0 +openai-chatkit~=1.3.0 +opentelemetry-api~=1.38.0 +opentelemetry-exporter-otlp-proto-common~=1.38.0 +opentelemetry-exporter-otlp-proto-grpc~=1.38.0 +opentelemetry-proto~=1.38.0 +opentelemetry-sdk~=1.38.0 +opentelemetry-semantic-conventions~=0.59b0 +opentelemetry-semantic-conventions-ai~=0.4.13 +orderedmultidict~=1.0.2 +packaging~=25.0 +ply~=3.11 +portalocker~=3.2.0 +posthog~=7.0.1 +propcache~=0.4.1 +proto-plus~=1.26.1 +protobuf~=5.29.5 +pyasn1~=0.6.1 +pyasn1_modules~=0.4.2 +pycparser~=2.23 +pydantic~=2.12.4 +pydantic-settings~=2.12.0 +pydantic_core~=2.41.5 +PyJWT~=2.10.1 +python-dateutil~=2.9.0.post0 +python-dotenv~=1.2.1 +python-multipart~=0.0.20 +python-ulid~=3.1.0 +pytz~=2025.2 +PyYAML~=6.0.3 +qdrant-client~=1.16.0 +redis~=6.4.0 +redisvl~=0.11.0 +referencing~=0.37.0 +requests~=2.32.5 +rpds-py~=0.29.0 +rsa~=4.9.1 +six~=1.17.0 +sniffio~=1.3.1 +SQLAlchemy~=2.0.44 +sse-starlette~=3.0.3 +starlette~=0.49.3 +tenacity~=9.1.2 +tqdm~=4.67.1 +types-requests~=2.32.4.20250913 +typing-inspection~=0.4.2 +typing_extensions~=4.15.0 +urllib3~=2.5.0 +uvicorn~=0.38.0 +uvloop~=0.22.1 +watchfiles~=1.1.1 +websockets~=15.0.1 +Werkzeug~=3.1.3 +yarl~=1.22.0 +zipp~=3.23.0 \ No newline at end of file From 49bbd91f4eb49e32e5828a686614e30f6c0e264a Mon Sep 17 00:00:00 2001 From: jsimonweb Date: Wed, 19 Nov 2025 21:35:23 -0800 Subject: [PATCH 8/8] update README --- .../README.md | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/supporting-blog-content/agent-builder-a2a-agent-framework/README.md b/supporting-blog-content/agent-builder-a2a-agent-framework/README.md index 54d1173a..c2515564 100644 --- a/supporting-blog-content/agent-builder-a2a-agent-framework/README.md +++ b/supporting-blog-content/agent-builder-a2a-agent-framework/README.md @@ -6,14 +6,14 @@ This is an example Python console app that demonstrates how to connect and utili ## Prerequisites -1. An Elasticsearch deployment running in [Elastic Cloud](https://cloud.elastic.co/registration?utm_source=github&utm_content=elasticsearch-labs-example-apps). - * Requires Elasticsearch Serverless (or for hosted deployments at least Elasticsearch version 9.2.0). +1. An Elasticsearch project/deployment running in [Elastic Cloud](https://cloud.elastic.co/registration?utm_source=github&utm_content=elasticsearch-labs-example-apps). + * Requires Elasticsearch serverless project (or for hosted deployments at least Elasticsearch version 9.2.0). 2. A text editor or an integrated development environment (IDE) like [Visual Studio Code](https://code.visualstudio.com/download) running on your local computer. 3. [Python version 3.10 or greater](https://www.python.org/downloads/) installed on your local computer. -## Setup your Elasticsearch deployment +## Set up your Elasticsearch project -1. Create an index named `my-docs` in your Elasticsearch deployment by running the following command in Elastic Developer Tools: +1. Create an index named `my-docs` in your Elasticsearch project by running the following command in Elastic Developer Tools: PUT /my-docs { @@ -82,20 +82,20 @@ This is an example Python console app that demonstrates how to connect and utili cd elasticsearch-labs/supporting-blog-content/agent-builder-a2a-agent-framework -## Setup up the environment variables +## Set up the environment variables -1. Setup the environment variables with values copied from your Elastic deployment. +1. Set up the environment variables with values copied from your Elastic project. 1. Make a copy of the file `env.example` and name the new file `.env ` - 2. Edit the `.env` file to set the values of the environment variables to use the values copied from your Elastic deployment. + 2. Edit the `.env` file to set the values of the environment variables to use the values copied from your Elastic project. * Replace - 1. In your Elastic deployment, go to the Elastic Agent Builder - Tools page. Click the **MCP Server** dropdown at the top of the Tools page. Select **Copy MCP Server URL.** + 1. In your Elastic project, go to the Elastic Agent Builder - Tools page. Click the **MCP Server** dropdown at the top of the Tools page. Select **Copy MCP Server URL.** 2. Add the **MCP Server URL** value to the `.env` file. * Find where the placeholder text “****” appears and paste in the copied **MCP Server URL** to replace the placeholder text. Now edit the pasted **MCP Server URL**. Delete the text “mcp” at the end of the URL and replace it with the text “a2a”. The edited URL should look something like this `https://example-project-a123.kb.westus2.azure.elastic.cloud/api/agent_builder/a2a` * Replace - 1. In your Elastic deployment, click **Elasticsearch** in the navigation menu to go to your deployment’s home page. + 1. In your Elastic project, click **Elasticsearch** in the navigation menu to go to your project’s home page. 2. Click **Create API key** to create a new API key. 3. After the API key is created, copy the API Key value. 4. Add the API Key value to the `.env` file.