diff --git a/demos/a2a_llama_stack/__init__.py b/demos/a2a_llama_stack/agents/a2a_composer/__init__.py similarity index 100% rename from demos/a2a_llama_stack/__init__.py rename to demos/a2a_llama_stack/agents/a2a_composer/__init__.py diff --git a/demos/a2a_llama_stack/agents/a2a_composer/__main__.py b/demos/a2a_llama_stack/agents/a2a_composer/__main__.py new file mode 100644 index 00000000..296bdd30 --- /dev/null +++ b/demos/a2a_llama_stack/agents/a2a_composer/__main__.py @@ -0,0 +1,72 @@ +import os +import logging + +from llama_stack_client import LlamaStackClient, Agent + +from common.server import A2AServer +from common.types import AgentCard, AgentCapabilities, AgentSkill + +from .task_manager import AgentTaskManager, SUPPORTED_CONTENT_TYPES + +logging.basicConfig(level=logging.INFO) + + +def build_server(host: str = "0.0.0.0", port: int = 10012): + # 1) instantiate your agent with the required parameters + agent = Agent( + client=LlamaStackClient(base_url=os.getenv("REMOTE_BASE_URL", "http://localhost:8321")), + model=os.getenv("INFERENCE_MODEL_ID", "llama3.2:3b-instruct-fp16"), + instructions=("You are skilled at writing human-friendly text based on the query and associated skills."), + max_infer_iters=3, + sampling_params = { + "strategy": {"type": "greedy"}, + "max_tokens": 4096, + }, + ) + + # 2) wrap it in the A2A TaskManager + task_manager = AgentTaskManager(agent=agent, internal_session_id=True) + + # 3) advertise your tools as AgentSkills + card = AgentCard( + name="Writing Agent", + description="Generate human-friendly text based on the query and associated skills", + url=f"http://{host}:{port}/", + version="0.1.0", + defaultInputModes=["text/plain"], + defaultOutputModes=SUPPORTED_CONTENT_TYPES, + capabilities=AgentCapabilities( + streaming=False, + pushNotifications=False, + stateTransitionHistory=False, + ), + skills = [ + AgentSkill( + id="writing_agent", + name="Writing Agent", + description="Write human-friendly text based on the query and associated skills", + tags=["writing"], + examples=["Write human-friendly text based on the query and associated skills"], + inputModes=["text/plain"], + outputModes=["application/json"], + ) + ] + ) + + return A2AServer( + agent_card=card, + task_manager=task_manager, + host=host, + port=port, + ) + +if __name__ == "__main__": + import click + + @click.command() + @click.option("--host", default="0.0.0.0") + @click.option("--port", default=10010, type=int) + def main(host, port): + build_server(host, port).start() + + main() diff --git a/demos/a2a_llama_stack/agents/a2a_composer/agent.py b/demos/a2a_llama_stack/agents/a2a_composer/agent.py new file mode 100644 index 00000000..e69de29b diff --git a/demos/a2a_llama_stack/agents/a2a_composer/task_manager.py b/demos/a2a_llama_stack/agents/a2a_composer/task_manager.py new file mode 100644 index 00000000..9d710038 --- /dev/null +++ b/demos/a2a_llama_stack/agents/a2a_composer/task_manager.py @@ -0,0 +1,134 @@ +import logging +from typing import AsyncIterable, Union, AsyncIterator + +from llama_stack_client import Agent, AgentEventLogger + +import common.server.utils as utils +from common.server.task_manager import InMemoryTaskManager +from common.types import ( + SendTaskRequest, SendTaskResponse, + SendTaskStreamingRequest, SendTaskStreamingResponse, + TaskStatus, Artifact, + Message, TaskState, + TaskStatusUpdateEvent, TaskArtifactUpdateEvent, + JSONRPCResponse, +) + +logger = logging.getLogger(__name__) + +SUPPORTED_CONTENT_TYPES = ["text", "text/plain", "application/json"] + + +class AgentTaskManager(InMemoryTaskManager): + def __init__(self, agent: Agent, internal_session_id=False): + super().__init__() + self.agent = agent + if internal_session_id: + self.session_id = self.agent.create_session("custom-agent-session") + else: + self.session_id = None + + def _validate_request( + self, request: Union[SendTaskRequest, SendTaskStreamingRequest] + ) -> JSONRPCResponse | None: + params = request.params + if not utils.are_modalities_compatible( + params.acceptedOutputModes, + SUPPORTED_CONTENT_TYPES + ): + logger.warning("Unsupported output modes: %s", params.acceptedOutputModes) + return utils.new_incompatible_types_error(request.id) + return None + + async def on_send_task(self, request: SendTaskRequest) -> SendTaskResponse: + err = self._validate_request(request) + if err: + return err + + await self.upsert_task(request.params) + result = self._invoke( + request.params.message.parts[0].text, + request.params.sessionId + ) + parts = [{"type": "text", "text": result}] + status = TaskStatus(state=TaskState.COMPLETED, message=Message(role="agent", parts=parts)) + task = await self._update_store(request.params.id, status, [Artifact(parts=parts)]) + return SendTaskResponse(id=request.id, result=task) + + async def on_send_task_subscribe( + self, request: SendTaskStreamingRequest + ) -> AsyncIterable[SendTaskStreamingResponse] | JSONRPCResponse: + err = self._validate_request(request) + if err: + return err + + await self.upsert_task(request.params) + return self._stream_generator(request) + + async def _stream_generator( + self, request: SendTaskStreamingRequest + ) -> AsyncIterable[SendTaskStreamingResponse]: + params = request.params + query = params.message.parts[0].text + + async for update in self._stream(query, params.sessionId): + done = update["is_task_complete"] + content = update["content"] + delta = update["updates"] + + state = TaskState.COMPLETED if done else TaskState.WORKING + text = content if done else delta + parts = [{"type": "text", "text": text}] + artifacts = [Artifact(parts=parts)] if done else None + + status = TaskStatus(state=state, message=Message(role="agent", parts=parts)) + await self._update_store(request.params.id, status, artifacts or []) + + yield SendTaskStreamingResponse( + id=request.id, + result=TaskStatusUpdateEvent(id=params.id, status=status, final=done) + ) + if artifacts: + yield SendTaskStreamingResponse( + id=request.id, + result=TaskArtifactUpdateEvent(id=params.id, artifact=artifacts[0]) + ) + + async def _update_store(self, task_id: str, status: TaskStatus, artifacts): + async with self.lock: + task = self.tasks[task_id] + task.status = status + if artifacts: + task.artifacts = (task.artifacts or []) + artifacts + return task + + def _invoke(self, query: str, session_id: str) -> str: + """ + Route the user query through the Agent, executing tools as needed. + """ + # Determine which session to use + if self.session_id is not None: + sid = self.session_id + else: + sid = self.agent.create_session(session_id) + + # Send the user query to the Agent + turn_resp = self.agent.create_turn( + messages=[{"role": "user", "content": query}], + session_id=sid, + ) + + # Extract tool and LLM outputs from events + logs = AgentEventLogger().log(turn_resp) + output = "" + for event in logs: + if hasattr(event, "content") and event.content: + output += event.content + return output + + async def _stream(self, query: str, session_id: str) -> AsyncIterator[dict]: + """ + Simplest streaming stub: synchronously invoke and emit once. + """ + result = self._invoke(query, session_id) + yield {"updates": result, "is_task_complete": True, "content": result} diff --git a/demos/a2a_llama_stack/agents/a2a_custom_tools/__init__.py b/demos/a2a_llama_stack/agents/a2a_custom_tools/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/demos/a2a_llama_stack/agents/a2a_custom_tools/__main__.py b/demos/a2a_llama_stack/agents/a2a_custom_tools/__main__.py new file mode 100644 index 00000000..0c96410f --- /dev/null +++ b/demos/a2a_llama_stack/agents/a2a_custom_tools/__main__.py @@ -0,0 +1,85 @@ +import os +import logging + +from llama_stack_client import LlamaStackClient, Agent + +from common.server import A2AServer +from common.types import AgentCard, AgentCapabilities, AgentSkill + +from .agent import random_number_tool, date_tool +from .task_manager import AgentTaskManager, SUPPORTED_CONTENT_TYPES + +logging.basicConfig(level=logging.INFO) + + +def build_server(host: str = "0.0.0.0", port: int = 10010): + # 1) instantiate your agent with the required parameters + agent = Agent( + client=LlamaStackClient(base_url=os.getenv("REMOTE_BASE_URL", "http://localhost:8321")), + model=os.getenv("INFERENCE_MODEL_ID", "llama3.1:8b-instruct-fp16"), + instructions=( + "You have access to two tools:\n" + "- random_number_tool: generates one random integer between 1 and 100\n" + "- date_tool: returns today's date in YYYY-MM-DD format\n" + "Always use the appropriate tool to answer user queries." + ), + tools=[random_number_tool, date_tool], + max_infer_iters=3, + ) + + # 2) wrap it in the A2A TaskManager + task_manager = AgentTaskManager(agent=agent, internal_session_id=True) + + # 3) advertise your tools as AgentSkills + card = AgentCard( + name="Custom Agent", + description="Generates random numbers or retrieve today's dates", + url=f"http://{host}:{port}/", + version="0.1.0", + defaultInputModes=["text/plain"], + defaultOutputModes=SUPPORTED_CONTENT_TYPES, + capabilities=AgentCapabilities( + streaming=False, + pushNotifications=False, + stateTransitionHistory=False, + ), + skills=[ + AgentSkill( + id="random_number_tool", + name="Random Number Generator", + description="Generates a random number between 1 and 100", + tags=["random"], + examples=["Give me a random number between 1 and 100"], + inputModes=["text/plain"], + outputModes=["text/plain"], + ), + + AgentSkill( + id="date_tool", + name="Date Provider", + description="Returns today's date in YYYY-MM-DD format", + tags=["date"], + examples=["What's the date today?"], + inputModes=["text/plain"], + outputModes=["text/plain"], + ), + ], + ) + + return A2AServer( + agent_card=card, + task_manager=task_manager, + host=host, + port=port, + ) + +if __name__ == "__main__": + import click + + @click.command() + @click.option("--host", default="0.0.0.0") + @click.option("--port", default=10010, type=int) + def main(host, port): + build_server(host, port).start() + + main() diff --git a/demos/a2a_llama_stack/agent.py b/demos/a2a_llama_stack/agents/a2a_custom_tools/agent.py similarity index 100% rename from demos/a2a_llama_stack/agent.py rename to demos/a2a_llama_stack/agents/a2a_custom_tools/agent.py diff --git a/demos/a2a_llama_stack/agents/a2a_custom_tools/task_manager.py b/demos/a2a_llama_stack/agents/a2a_custom_tools/task_manager.py new file mode 100644 index 00000000..9d710038 --- /dev/null +++ b/demos/a2a_llama_stack/agents/a2a_custom_tools/task_manager.py @@ -0,0 +1,134 @@ +import logging +from typing import AsyncIterable, Union, AsyncIterator + +from llama_stack_client import Agent, AgentEventLogger + +import common.server.utils as utils +from common.server.task_manager import InMemoryTaskManager +from common.types import ( + SendTaskRequest, SendTaskResponse, + SendTaskStreamingRequest, SendTaskStreamingResponse, + TaskStatus, Artifact, + Message, TaskState, + TaskStatusUpdateEvent, TaskArtifactUpdateEvent, + JSONRPCResponse, +) + +logger = logging.getLogger(__name__) + +SUPPORTED_CONTENT_TYPES = ["text", "text/plain", "application/json"] + + +class AgentTaskManager(InMemoryTaskManager): + def __init__(self, agent: Agent, internal_session_id=False): + super().__init__() + self.agent = agent + if internal_session_id: + self.session_id = self.agent.create_session("custom-agent-session") + else: + self.session_id = None + + def _validate_request( + self, request: Union[SendTaskRequest, SendTaskStreamingRequest] + ) -> JSONRPCResponse | None: + params = request.params + if not utils.are_modalities_compatible( + params.acceptedOutputModes, + SUPPORTED_CONTENT_TYPES + ): + logger.warning("Unsupported output modes: %s", params.acceptedOutputModes) + return utils.new_incompatible_types_error(request.id) + return None + + async def on_send_task(self, request: SendTaskRequest) -> SendTaskResponse: + err = self._validate_request(request) + if err: + return err + + await self.upsert_task(request.params) + result = self._invoke( + request.params.message.parts[0].text, + request.params.sessionId + ) + parts = [{"type": "text", "text": result}] + status = TaskStatus(state=TaskState.COMPLETED, message=Message(role="agent", parts=parts)) + task = await self._update_store(request.params.id, status, [Artifact(parts=parts)]) + return SendTaskResponse(id=request.id, result=task) + + async def on_send_task_subscribe( + self, request: SendTaskStreamingRequest + ) -> AsyncIterable[SendTaskStreamingResponse] | JSONRPCResponse: + err = self._validate_request(request) + if err: + return err + + await self.upsert_task(request.params) + return self._stream_generator(request) + + async def _stream_generator( + self, request: SendTaskStreamingRequest + ) -> AsyncIterable[SendTaskStreamingResponse]: + params = request.params + query = params.message.parts[0].text + + async for update in self._stream(query, params.sessionId): + done = update["is_task_complete"] + content = update["content"] + delta = update["updates"] + + state = TaskState.COMPLETED if done else TaskState.WORKING + text = content if done else delta + parts = [{"type": "text", "text": text}] + artifacts = [Artifact(parts=parts)] if done else None + + status = TaskStatus(state=state, message=Message(role="agent", parts=parts)) + await self._update_store(request.params.id, status, artifacts or []) + + yield SendTaskStreamingResponse( + id=request.id, + result=TaskStatusUpdateEvent(id=params.id, status=status, final=done) + ) + if artifacts: + yield SendTaskStreamingResponse( + id=request.id, + result=TaskArtifactUpdateEvent(id=params.id, artifact=artifacts[0]) + ) + + async def _update_store(self, task_id: str, status: TaskStatus, artifacts): + async with self.lock: + task = self.tasks[task_id] + task.status = status + if artifacts: + task.artifacts = (task.artifacts or []) + artifacts + return task + + def _invoke(self, query: str, session_id: str) -> str: + """ + Route the user query through the Agent, executing tools as needed. + """ + # Determine which session to use + if self.session_id is not None: + sid = self.session_id + else: + sid = self.agent.create_session(session_id) + + # Send the user query to the Agent + turn_resp = self.agent.create_turn( + messages=[{"role": "user", "content": query}], + session_id=sid, + ) + + # Extract tool and LLM outputs from events + logs = AgentEventLogger().log(turn_resp) + output = "" + for event in logs: + if hasattr(event, "content") and event.content: + output += event.content + return output + + async def _stream(self, query: str, session_id: str) -> AsyncIterator[dict]: + """ + Simplest streaming stub: synchronously invoke and emit once. + """ + result = self._invoke(query, session_id) + yield {"updates": result, "is_task_complete": True, "content": result} diff --git a/demos/a2a_llama_stack/agents/a2a_planner/__init__.py b/demos/a2a_llama_stack/agents/a2a_planner/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/demos/a2a_llama_stack/__main__.py b/demos/a2a_llama_stack/agents/a2a_planner/__main__.py similarity index 52% rename from demos/a2a_llama_stack/__main__.py rename to demos/a2a_llama_stack/agents/a2a_planner/__main__.py index a645fcf4..f091dab2 100644 --- a/demos/a2a_llama_stack/__main__.py +++ b/demos/a2a_llama_stack/agents/a2a_planner/__main__.py @@ -6,7 +6,6 @@ from common.server import A2AServer from common.types import AgentCard, AgentCapabilities, AgentSkill -from .agent import random_number_tool, date_tool from .task_manager import AgentTaskManager, SUPPORTED_CONTENT_TYPES logging.basicConfig(level=logging.INFO) @@ -15,16 +14,14 @@ def build_server(host: str = "0.0.0.0", port: int = 10010): # 1) instantiate your agent with the required parameters agent = Agent( - client=LlamaStackClient(base_url=os.getenv("LLAMA_STACK_URL", "http://localhost:8321")), - model=os.getenv("MODEL_ID", "llama3.2:3b-instruct-fp16"), - instructions=( - "You have access to two tools:\n" - "- random_number_tool: generates a random integer between 1 and 100\n" - "- date_tool: returns today's date in YYYY-MM-DD format\n" - "Use the appropriate tool to answer user queries." - ), - tools=[random_number_tool, date_tool], - max_infer_iters=3, + client=LlamaStackClient(base_url=os.getenv("REMOTE_BASE_URL", "http://localhost:8321")), + model=os.getenv("INFERENCE_MODEL_ID", "llama3.1:8b-instruct-fp16"), + instructions="You are an orchestration assistant. Ensure you count correctly the number of skills needed.", + max_infer_iters=10, + sampling_params = { + "strategy": {"type": "greedy"}, + "max_tokens": 4096, + }, ) # 2) wrap it in the A2A TaskManager @@ -32,17 +29,28 @@ def build_server(host: str = "0.0.0.0", port: int = 10010): # 3) advertise your tools as AgentSkills card = AgentCard( - name="Custom Agent", - description="Generates random numbers or dates", + name="Orchestration Agent", + description="Plans which tool to call for each user question", url=f"http://{host}:{port}/", version="0.1.0", defaultInputModes=["text/plain"], defaultOutputModes=SUPPORTED_CONTENT_TYPES, - capabilities=AgentCapabilities(streaming=True), - skills=[ - AgentSkill(id="random_number", name="Random Number Generator"), - AgentSkill(id="get_date", name="Date Provider"), - ], + capabilities=AgentCapabilities( + streaming=False, + pushNotifications=False, + stateTransitionHistory=False, + ), + skills = [ + AgentSkill( + id="orchestrate", + name="Orchestration Planner", + description="Plan user questions into JSON steps of {skill_id}", + tags=["orchestration"], + examples=["Plan: What's today's date and a random number?"], + inputModes=["text/plain"], + outputModes=["application/json"], + ) + ] ) return A2AServer( diff --git a/demos/a2a_llama_stack/agents/a2a_planner/agent.py b/demos/a2a_llama_stack/agents/a2a_planner/agent.py new file mode 100644 index 00000000..e69de29b diff --git a/demos/a2a_llama_stack/agents/a2a_planner/task_manager.py b/demos/a2a_llama_stack/agents/a2a_planner/task_manager.py new file mode 100644 index 00000000..cc41ecce --- /dev/null +++ b/demos/a2a_llama_stack/agents/a2a_planner/task_manager.py @@ -0,0 +1,158 @@ +import logging +from typing import AsyncIterable, Union, AsyncIterator + +from llama_stack_client import Agent, AgentEventLogger + +import common.server.utils as utils +from common.server.task_manager import InMemoryTaskManager +from common.types import ( + SendTaskRequest, SendTaskResponse, + SendTaskStreamingRequest, SendTaskStreamingResponse, + TaskStatus, Artifact, + Message, TaskState, + TaskStatusUpdateEvent, TaskArtifactUpdateEvent, + JSONRPCResponse, +) + +logger = logging.getLogger(__name__) + +SUPPORTED_CONTENT_TYPES = ["text", "text/plain", "application/json"] + + +class AgentTaskManager(InMemoryTaskManager): + def __init__(self, agent: Agent, internal_session_id=False): + super().__init__() + self.agent = agent + if internal_session_id: + self.session_id = self.agent.create_session("custom-agent-session") + else: + self.session_id = None + + def _validate_request( + self, request: Union[SendTaskRequest, SendTaskStreamingRequest] + ) -> JSONRPCResponse | None: + params = request.params + if not utils.are_modalities_compatible( + params.acceptedOutputModes, + SUPPORTED_CONTENT_TYPES + ): + logger.warning("Unsupported output modes: %s", params.acceptedOutputModes) + return utils.new_incompatible_types_error(request.id) + return None + + async def on_send_task(self, request: SendTaskRequest) -> SendTaskResponse: + err = self._validate_request(request) + if err: + return err + + await self.upsert_task(request.params) + result = self._invoke( + request.params.message.parts[0].text, + request.params.sessionId + ) + parts = [{"type": "text", "text": result}] + status = TaskStatus(state=TaskState.COMPLETED, message=Message(role="agent", parts=parts)) + task = await self._update_store(request.params.id, status, [Artifact(parts=parts)]) + return SendTaskResponse(id=request.id, result=task) + + async def on_send_task_subscribe( + self, request: SendTaskStreamingRequest + ) -> AsyncIterable[SendTaskStreamingResponse] | JSONRPCResponse: + err = self._validate_request(request) + if err: + return err + + await self.upsert_task(request.params) + return self._stream_generator(request) + + async def _stream_generator( + self, request: SendTaskStreamingRequest + ) -> AsyncIterable[SendTaskStreamingResponse]: + params = request.params + query = params.message.parts[0].text + + async for update in self._stream(query, params.sessionId): + done = update["is_task_complete"] + content = update["content"] + delta = update["updates"] + + state = TaskState.COMPLETED if done else TaskState.WORKING + text = content if done else delta + parts = [{"type": "text", "text": text}] + artifacts = [Artifact(parts=parts)] if done else None + + status = TaskStatus(state=state, message=Message(role="agent", parts=parts)) + await self._update_store(request.params.id, status, artifacts or []) + + yield SendTaskStreamingResponse( + id=request.id, + result=TaskStatusUpdateEvent(id=params.id, status=status, final=done) + ) + if artifacts: + yield SendTaskStreamingResponse( + id=request.id, + result=TaskArtifactUpdateEvent(id=params.id, artifact=artifacts[0]) + ) + + async def _update_store(self, task_id: str, status: TaskStatus, artifacts): + async with self.lock: + task = self.tasks[task_id] + task.status = status + if artifacts: + task.artifacts = (task.artifacts or []) + artifacts + return task + + def _invoke(self, query: str, session_id: str) -> str: + """ + Route the user query through the Agent, executing tools as needed. + """ + # Determine which session to use + if self.session_id is not None: + sid = self.session_id + else: + sid = self.agent.create_session(session_id) + + import json + + # parse payload JSON + data = json.loads(query) + skills_meta, question = data["skills"], data["question"] + print(f"\nskills_meta: {skills_meta}\n") + + # rebuild instructions on the fly + plan_instructions = ( + "You are an orchestration assistant.\n" + "Available skills (id & name):\n" + f"{json.dumps(skills_meta, indent=2)}\n\n" + "When given a user question, respond _only_ with a JSON array of objects, " + "each with key `skill_id`, without any surrounding object. You may be asked to write single or multiple skills.\n" + "For example for multiple tools:\n" + "[" + "{\"skill_id\": \"tool_1\"}, " + "{\"skill_id\": \"tool_2\"}" + "]" + ) + + # send it all as one user message (no 'system' role!) + combined = plan_instructions + "\n\nUser question: " + question + + # Send the plan instructions to the Agent + turn_resp = self.agent.create_turn( + messages=[{"role": "user", "content": combined}], + session_id=sid, + ) + + # Extract tool and LLM outputs from events + logs = AgentEventLogger().log(turn_resp) + output = "" + for event in logs: + if hasattr(event, "content") and event.content: + output += event.content + return output + + async def _stream(self, query: str, session_id: str) -> AsyncIterator[dict]: + """ + Simplest streaming stub: synchronously invoke and emit once. + """ + result = self._invoke(query, session_id) + yield {"updates": result, "is_task_complete": True, "content": result} diff --git a/demos/a2a_llama_stack/notebooks/A2A_Agentic_RAG.ipynb b/demos/a2a_llama_stack/notebooks/A2A_Agentic_RAG.ipynb index 784c5725..bf404766 100644 --- a/demos/a2a_llama_stack/notebooks/A2A_Agentic_RAG.ipynb +++ b/demos/a2a_llama_stack/notebooks/A2A_Agentic_RAG.ipynb @@ -23,7 +23,10 @@ "## Prerequisites\n", "\n", "Before starting, ensure you have the following:\n", + "- `python_requires >= 3.13`\n", + "\n", "- Followed the instructions in the [Setup Guide](../../rag_agentic/notebooks/Level0_getting_started_with_Llama_Stack.ipynb) notebook. \n", + "\n", "- Llama Stack server should be using milvus as its vector DB provider.\n", "\n", "## Additional environment variables\n", @@ -42,26 +45,96 @@ }, { "cell_type": "code", - "execution_count": 1, - "id": "46cc193c-03da-4e4d-8b75-e59df3e4569e", + "execution_count": 6, + "id": "5e50535c", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "Cloning into 'A2A'...\n", - "remote: Enumerating objects: 1750, done.\u001b[K\n", - "remote: Counting objects: 100% (61/61), done.\u001b[K\n", - "remote: Compressing objects: 100% (39/39), done.\u001b[K\n", - "remote: Total 1750 (delta 40), reused 22 (delta 22), pack-reused 1689 (from 2)\u001b[K\n", - "Receiving objects: 100% (1750/1750), 4.14 MiB | 10.28 MiB/s, done.\n", - "Resolving deltas: 100% (905/905), done.\n" + "Collecting git+https://github.com/google/A2A.git#subdirectory=samples/python\n", + " Cloning https://github.com/google/A2A.git to /private/var/folders/p4/635191ns4599kwjkqt12kwd80000gn/T/pip-req-build-aswa9mq1\n", + " Running command git clone --filter=blob:none --quiet https://github.com/google/A2A.git /private/var/folders/p4/635191ns4599kwjkqt12kwd80000gn/T/pip-req-build-aswa9mq1\n", + " Resolved https://github.com/google/A2A.git to commit 081fa20bdfede24922c49e8e56fcdfbee0db0c28\n", + " Installing build dependencies ... \u001b[?25ldone\n", + "\u001b[?25h Getting requirements to build wheel ... \u001b[?25ldone\n", + "\u001b[?25h Preparing metadata (pyproject.toml) ... \u001b[?25ldone\n", + "\u001b[?25hRequirement already satisfied: a2a-sdk>=0.2.1 in /Users/kcogan/Documents/llama-stack-on-ocp/venv7/lib/python3.13/site-packages (from a2a-samples==0.1.0) (0.2.1)\n", + "Requirement already satisfied: httpx-sse>=0.4.0 in /Users/kcogan/Documents/llama-stack-on-ocp/venv7/lib/python3.13/site-packages (from a2a-samples==0.1.0) (0.4.0)\n", + "Requirement already satisfied: httpx>=0.28.1 in /Users/kcogan/Documents/llama-stack-on-ocp/venv7/lib/python3.13/site-packages (from a2a-samples==0.1.0) (0.28.1)\n", + "Requirement already satisfied: jwcrypto>=1.5.6 in /Users/kcogan/Documents/llama-stack-on-ocp/venv7/lib/python3.13/site-packages (from a2a-samples==0.1.0) (1.5.6)\n", + "Requirement already satisfied: pydantic>=2.10.6 in /Users/kcogan/Documents/llama-stack-on-ocp/venv7/lib/python3.13/site-packages (from a2a-samples==0.1.0) (2.11.4)\n", + "Requirement already satisfied: pyjwt>=2.10.1 in /Users/kcogan/Documents/llama-stack-on-ocp/venv7/lib/python3.13/site-packages (from a2a-samples==0.1.0) (2.10.1)\n", + "Requirement already satisfied: sse-starlette>=2.2.1 in /Users/kcogan/Documents/llama-stack-on-ocp/venv7/lib/python3.13/site-packages (from a2a-samples==0.1.0) (2.3.5)\n", + "Requirement already satisfied: starlette>=0.46.1 in /Users/kcogan/Documents/llama-stack-on-ocp/venv7/lib/python3.13/site-packages (from a2a-samples==0.1.0) (0.46.2)\n", + "Requirement already satisfied: typing-extensions>=4.12.2 in /Users/kcogan/Documents/llama-stack-on-ocp/venv7/lib/python3.13/site-packages (from a2a-samples==0.1.0) (4.13.2)\n", + "Requirement already satisfied: uvicorn>=0.34.0 in /Users/kcogan/Documents/llama-stack-on-ocp/venv7/lib/python3.13/site-packages (from a2a-samples==0.1.0) (0.34.2)\n", + "Requirement already satisfied: opentelemetry-api>=1.33.0 in /Users/kcogan/Documents/llama-stack-on-ocp/venv7/lib/python3.13/site-packages (from a2a-sdk>=0.2.1->a2a-samples==0.1.0) (1.33.1)\n", + "Requirement already satisfied: opentelemetry-sdk>=1.33.0 in /Users/kcogan/Documents/llama-stack-on-ocp/venv7/lib/python3.13/site-packages (from a2a-sdk>=0.2.1->a2a-samples==0.1.0) (1.33.1)\n", + "Requirement already satisfied: anyio in /Users/kcogan/Documents/llama-stack-on-ocp/venv7/lib/python3.13/site-packages (from httpx>=0.28.1->a2a-samples==0.1.0) (4.9.0)\n", + "Requirement already satisfied: certifi in /Users/kcogan/Documents/llama-stack-on-ocp/venv7/lib/python3.13/site-packages (from httpx>=0.28.1->a2a-samples==0.1.0) (2025.4.26)\n", + "Requirement already satisfied: httpcore==1.* in /Users/kcogan/Documents/llama-stack-on-ocp/venv7/lib/python3.13/site-packages (from httpx>=0.28.1->a2a-samples==0.1.0) (1.0.9)\n", + "Requirement already satisfied: idna in /Users/kcogan/Documents/llama-stack-on-ocp/venv7/lib/python3.13/site-packages (from httpx>=0.28.1->a2a-samples==0.1.0) (3.10)\n", + "Requirement already satisfied: h11>=0.16 in /Users/kcogan/Documents/llama-stack-on-ocp/venv7/lib/python3.13/site-packages (from httpcore==1.*->httpx>=0.28.1->a2a-samples==0.1.0) (0.16.0)\n", + "Requirement already satisfied: cryptography>=3.4 in /Users/kcogan/Documents/llama-stack-on-ocp/venv7/lib/python3.13/site-packages (from jwcrypto>=1.5.6->a2a-samples==0.1.0) (45.0.2)\n", + "Requirement already satisfied: annotated-types>=0.6.0 in /Users/kcogan/Documents/llama-stack-on-ocp/venv7/lib/python3.13/site-packages (from pydantic>=2.10.6->a2a-samples==0.1.0) (0.7.0)\n", + "Requirement already satisfied: pydantic-core==2.33.2 in /Users/kcogan/Documents/llama-stack-on-ocp/venv7/lib/python3.13/site-packages (from pydantic>=2.10.6->a2a-samples==0.1.0) (2.33.2)\n", + "Requirement already satisfied: typing-inspection>=0.4.0 in /Users/kcogan/Documents/llama-stack-on-ocp/venv7/lib/python3.13/site-packages (from pydantic>=2.10.6->a2a-samples==0.1.0) (0.4.0)\n", + "Requirement already satisfied: click>=7.0 in /Users/kcogan/Documents/llama-stack-on-ocp/venv7/lib/python3.13/site-packages (from uvicorn>=0.34.0->a2a-samples==0.1.0) (8.2.0)\n", + "Requirement already satisfied: sniffio>=1.1 in /Users/kcogan/Documents/llama-stack-on-ocp/venv7/lib/python3.13/site-packages (from anyio->httpx>=0.28.1->a2a-samples==0.1.0) (1.3.1)\n", + "Requirement already satisfied: cffi>=1.14 in /Users/kcogan/Documents/llama-stack-on-ocp/venv7/lib/python3.13/site-packages (from cryptography>=3.4->jwcrypto>=1.5.6->a2a-samples==0.1.0) (1.17.1)\n", + "Requirement already satisfied: deprecated>=1.2.6 in /Users/kcogan/Documents/llama-stack-on-ocp/venv7/lib/python3.13/site-packages (from opentelemetry-api>=1.33.0->a2a-sdk>=0.2.1->a2a-samples==0.1.0) (1.2.18)\n", + "Requirement already satisfied: importlib-metadata<8.7.0,>=6.0 in /Users/kcogan/Documents/llama-stack-on-ocp/venv7/lib/python3.13/site-packages (from opentelemetry-api>=1.33.0->a2a-sdk>=0.2.1->a2a-samples==0.1.0) (8.6.1)\n", + "Requirement already satisfied: opentelemetry-semantic-conventions==0.54b1 in /Users/kcogan/Documents/llama-stack-on-ocp/venv7/lib/python3.13/site-packages (from opentelemetry-sdk>=1.33.0->a2a-sdk>=0.2.1->a2a-samples==0.1.0) (0.54b1)\n", + "Requirement already satisfied: pycparser in /Users/kcogan/Documents/llama-stack-on-ocp/venv7/lib/python3.13/site-packages (from cffi>=1.14->cryptography>=3.4->jwcrypto>=1.5.6->a2a-samples==0.1.0) (2.22)\n", + "Requirement already satisfied: wrapt<2,>=1.10 in /Users/kcogan/Documents/llama-stack-on-ocp/venv7/lib/python3.13/site-packages (from deprecated>=1.2.6->opentelemetry-api>=1.33.0->a2a-sdk>=0.2.1->a2a-samples==0.1.0) (1.17.2)\n", + "Requirement already satisfied: zipp>=3.20 in /Users/kcogan/Documents/llama-stack-on-ocp/venv7/lib/python3.13/site-packages (from importlib-metadata<8.7.0,>=6.0->opentelemetry-api>=1.33.0->a2a-sdk>=0.2.1->a2a-samples==0.1.0) (3.21.0)\n", + "\n", + "\u001b[1m[\u001b[0m\u001b[34;49mnotice\u001b[0m\u001b[1;39;49m]\u001b[0m\u001b[39;49m A new release of pip is available: \u001b[0m\u001b[31;49m25.0.1\u001b[0m\u001b[39;49m -> \u001b[0m\u001b[32;49m25.1.1\u001b[0m\n", + "\u001b[1m[\u001b[0m\u001b[34;49mnotice\u001b[0m\u001b[1;39;49m]\u001b[0m\u001b[39;49m To update, run: \u001b[0m\u001b[32;49mpip install --upgrade pip\u001b[0m\n", + "Requirement already satisfied: llama_stack_client in /Users/kcogan/Documents/llama-stack-on-ocp/venv7/lib/python3.13/site-packages (0.2.7)\n", + "Requirement already satisfied: dotenv in /Users/kcogan/Documents/llama-stack-on-ocp/venv7/lib/python3.13/site-packages (0.9.9)\n", + "Requirement already satisfied: anyio<5,>=3.5.0 in /Users/kcogan/Documents/llama-stack-on-ocp/venv7/lib/python3.13/site-packages (from llama_stack_client) (4.9.0)\n", + "Requirement already satisfied: click in /Users/kcogan/Documents/llama-stack-on-ocp/venv7/lib/python3.13/site-packages (from llama_stack_client) (8.2.0)\n", + "Requirement already satisfied: distro<2,>=1.7.0 in /Users/kcogan/Documents/llama-stack-on-ocp/venv7/lib/python3.13/site-packages (from llama_stack_client) (1.9.0)\n", + "Requirement already satisfied: httpx<1,>=0.23.0 in /Users/kcogan/Documents/llama-stack-on-ocp/venv7/lib/python3.13/site-packages (from llama_stack_client) (0.28.1)\n", + "Requirement already satisfied: pandas in /Users/kcogan/Documents/llama-stack-on-ocp/venv7/lib/python3.13/site-packages (from llama_stack_client) (2.2.3)\n", + "Requirement already satisfied: prompt-toolkit in /Users/kcogan/Documents/llama-stack-on-ocp/venv7/lib/python3.13/site-packages (from llama_stack_client) (3.0.51)\n", + "Requirement already satisfied: pyaml in /Users/kcogan/Documents/llama-stack-on-ocp/venv7/lib/python3.13/site-packages (from llama_stack_client) (25.1.0)\n", + "Requirement already satisfied: pydantic<3,>=1.9.0 in /Users/kcogan/Documents/llama-stack-on-ocp/venv7/lib/python3.13/site-packages (from llama_stack_client) (2.11.4)\n", + "Requirement already satisfied: rich in /Users/kcogan/Documents/llama-stack-on-ocp/venv7/lib/python3.13/site-packages (from llama_stack_client) (14.0.0)\n", + "Requirement already satisfied: sniffio in /Users/kcogan/Documents/llama-stack-on-ocp/venv7/lib/python3.13/site-packages (from llama_stack_client) (1.3.1)\n", + "Requirement already satisfied: termcolor in /Users/kcogan/Documents/llama-stack-on-ocp/venv7/lib/python3.13/site-packages (from llama_stack_client) (3.1.0)\n", + "Requirement already satisfied: tqdm in /Users/kcogan/Documents/llama-stack-on-ocp/venv7/lib/python3.13/site-packages (from llama_stack_client) (4.67.1)\n", + "Requirement already satisfied: typing-extensions<5,>=4.7 in /Users/kcogan/Documents/llama-stack-on-ocp/venv7/lib/python3.13/site-packages (from llama_stack_client) (4.13.2)\n", + "Requirement already satisfied: python-dotenv in /Users/kcogan/Documents/llama-stack-on-ocp/venv7/lib/python3.13/site-packages (from dotenv) (1.1.0)\n", + "Requirement already satisfied: idna>=2.8 in /Users/kcogan/Documents/llama-stack-on-ocp/venv7/lib/python3.13/site-packages (from anyio<5,>=3.5.0->llama_stack_client) (3.10)\n", + "Requirement already satisfied: certifi in /Users/kcogan/Documents/llama-stack-on-ocp/venv7/lib/python3.13/site-packages (from httpx<1,>=0.23.0->llama_stack_client) (2025.4.26)\n", + "Requirement already satisfied: httpcore==1.* in /Users/kcogan/Documents/llama-stack-on-ocp/venv7/lib/python3.13/site-packages (from httpx<1,>=0.23.0->llama_stack_client) (1.0.9)\n", + "Requirement already satisfied: h11>=0.16 in /Users/kcogan/Documents/llama-stack-on-ocp/venv7/lib/python3.13/site-packages (from httpcore==1.*->httpx<1,>=0.23.0->llama_stack_client) (0.16.0)\n", + "Requirement already satisfied: annotated-types>=0.6.0 in /Users/kcogan/Documents/llama-stack-on-ocp/venv7/lib/python3.13/site-packages (from pydantic<3,>=1.9.0->llama_stack_client) (0.7.0)\n", + "Requirement already satisfied: pydantic-core==2.33.2 in /Users/kcogan/Documents/llama-stack-on-ocp/venv7/lib/python3.13/site-packages (from pydantic<3,>=1.9.0->llama_stack_client) (2.33.2)\n", + "Requirement already satisfied: typing-inspection>=0.4.0 in /Users/kcogan/Documents/llama-stack-on-ocp/venv7/lib/python3.13/site-packages (from pydantic<3,>=1.9.0->llama_stack_client) (0.4.0)\n", + "Requirement already satisfied: numpy>=1.26.0 in /Users/kcogan/Documents/llama-stack-on-ocp/venv7/lib/python3.13/site-packages (from pandas->llama_stack_client) (2.2.6)\n", + "Requirement already satisfied: python-dateutil>=2.8.2 in /Users/kcogan/Documents/llama-stack-on-ocp/venv7/lib/python3.13/site-packages (from pandas->llama_stack_client) (2.9.0.post0)\n", + "Requirement already satisfied: pytz>=2020.1 in /Users/kcogan/Documents/llama-stack-on-ocp/venv7/lib/python3.13/site-packages (from pandas->llama_stack_client) (2025.2)\n", + "Requirement already satisfied: tzdata>=2022.7 in /Users/kcogan/Documents/llama-stack-on-ocp/venv7/lib/python3.13/site-packages (from pandas->llama_stack_client) (2025.2)\n", + "Requirement already satisfied: wcwidth in /Users/kcogan/Documents/llama-stack-on-ocp/venv7/lib/python3.13/site-packages (from prompt-toolkit->llama_stack_client) (0.2.13)\n", + "Requirement already satisfied: PyYAML in /Users/kcogan/Documents/llama-stack-on-ocp/venv7/lib/python3.13/site-packages (from pyaml->llama_stack_client) (6.0.2)\n", + "Requirement already satisfied: markdown-it-py>=2.2.0 in /Users/kcogan/Documents/llama-stack-on-ocp/venv7/lib/python3.13/site-packages (from rich->llama_stack_client) (3.0.0)\n", + "Requirement already satisfied: pygments<3.0.0,>=2.13.0 in /Users/kcogan/Documents/llama-stack-on-ocp/venv7/lib/python3.13/site-packages (from rich->llama_stack_client) (2.19.1)\n", + "Requirement already satisfied: mdurl~=0.1 in /Users/kcogan/Documents/llama-stack-on-ocp/venv7/lib/python3.13/site-packages (from markdown-it-py>=2.2.0->rich->llama_stack_client) (0.1.2)\n", + "Requirement already satisfied: six>=1.5 in /Users/kcogan/Documents/llama-stack-on-ocp/venv7/lib/python3.13/site-packages (from python-dateutil>=2.8.2->pandas->llama_stack_client) (1.17.0)\n", + "\n", + "\u001b[1m[\u001b[0m\u001b[34;49mnotice\u001b[0m\u001b[1;39;49m]\u001b[0m\u001b[39;49m A new release of pip is available: \u001b[0m\u001b[31;49m25.0.1\u001b[0m\u001b[39;49m -> \u001b[0m\u001b[32;49m25.1.1\u001b[0m\n", + "\u001b[1m[\u001b[0m\u001b[34;49mnotice\u001b[0m\u001b[1;39;49m]\u001b[0m\u001b[39;49m To update, run: \u001b[0m\u001b[32;49mpip install --upgrade pip\u001b[0m\n" ] } ], "source": [ - "! git clone https://github.com/google/A2A.git" + "! pip install \"git+https://github.com/google/A2A.git#subdirectory=samples/python\"\n", + "! pip install llama_stack_client dotenv" ] }, { @@ -74,7 +147,7 @@ }, { "cell_type": "code", - "execution_count": 1, + "execution_count": 7, "id": "92301f97-17f4-4f48-a3da-a5288b8b02dd", "metadata": {}, "outputs": [], @@ -96,7 +169,7 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": 8, "id": "839cf607-0301-41dc-ab13-d57b9ac20bd8", "metadata": {}, "outputs": [], @@ -120,7 +193,7 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 9, "id": "9b87b139-bd18-47b2-889a-1b8ed3018655", "metadata": {}, "outputs": [ @@ -130,8 +203,8 @@ "text": [ "Connected to Llama Stack server\n", "Inference Parameters:\n", - "\tModel: granite32-8b\n", - "\tSampling Parameters: {'strategy': {'type': 'greedy'}, 'max_tokens': 4096}\n", + "\tModel: llama32-3b\n", + "\tSampling Parameters: {'strategy': {'type': 'greedy'}, 'max_tokens': 512}\n", "\tstream: False\n" ] } @@ -216,17 +289,17 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 10, "id": "3b8ee65a-5925-44ed-a81b-f0df2c52465e", "metadata": {}, "outputs": [ { "data": { "text/plain": [ - "VectorDBRegisterResponse(embedding_dimension=384, embedding_model='all-MiniLM-L6-v2', identifier='test_vector_db_56361858-f5f2-4b61-9fea-28efb200c36c', provider_id='milvus', provider_resource_id='test_vector_db_56361858-f5f2-4b61-9fea-28efb200c36c', type='vector_db', access_attributes=None)" + "VectorDBRegisterResponse(embedding_dimension=384, embedding_model='all-MiniLM-L6-v2', identifier='test_vector_db_0efe2f59-9ec0-48bc-b673-4d4559a7e882', provider_id='milvus', provider_resource_id='test_vector_db_0efe2f59-9ec0-48bc-b673-4d4559a7e882', type='vector_db', access_attributes=None)" ] }, - "execution_count": 4, + "execution_count": 10, "metadata": {}, "output_type": "execute_result" } @@ -252,7 +325,7 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 11, "id": "93e86b69-7b8e-4418-976c-2a694a99a55b", "metadata": {}, "outputs": [], @@ -286,7 +359,7 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 12, "id": "2dd4664a-ff7f-4474-b6af-3a4ad3f73052", "metadata": { "tags": [] @@ -300,7 +373,7 @@ " sampling_params=sampling_params,\n", " tools=[\n", " dict(\n", - " name=\"builtin::rag\",\n", + " name=\"builtin::rag/knowledge_search\",\n", " args={\n", " \"vector_db_ids\": [vector_db_id],\n", " },\n", @@ -322,7 +395,7 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": null, "id": "2f11c6a2-2568-4181-88e8-0e5eb514ed6c", "metadata": {}, "outputs": [ @@ -330,26 +403,23 @@ "name": "stderr", "output_type": "stream", "text": [ - "INFO: Started server process [82835]\n", + "INFO: Started server process [14257]\n", "INFO: Waiting for application startup.\n", "INFO: Application startup complete.\n", - "INFO: Uvicorn running on http://localhost:8080 (Press CTRL+C to quit)\n" + "INFO: Uvicorn running on http://localhost:10020 (Press CTRL+C to quit)\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ - "INFO: ::1:56243 - \"GET /.well-known/agent.json HTTP/1.1\" 200 OK\n", - "INFO: ::1:56247 - \"POST / HTTP/1.1\" 200 OK\n", - "INFO: ::1:56252 - \"POST / HTTP/1.1\" 200 OK\n", - "INFO: ::1:56260 - \"POST / HTTP/1.1\" 200 OK\n", - "INFO: ::1:56267 - \"POST / HTTP/1.1\" 200 OK\n" + "INFO: ::1:58564 - \"GET /.well-known/agent.json HTTP/1.1\" 200 OK\n", + "INFO: ::1:58565 - \"POST / HTTP/1.1\" 200 OK\n" ] } ], "source": [ - "rag_agent_local_port = int(os.getenv(\"RAG_AGENT_LOCAL_PORT\"))\n", + "rag_agent_local_port = int(os.getenv(\"RAG_AGENT_LOCAL_PORT\", \"10010\"))\n", "rag_agent_url = f\"http://localhost:{rag_agent_local_port}\"\n", "\n", "agent_card = AgentCard(\n", @@ -388,7 +458,7 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": 14, "id": "7414837e-b04e-432d-82c3-6719a8f62132", "metadata": {}, "outputs": [], @@ -398,6 +468,7 @@ " client,\n", " model=model_id,\n", " instructions=\"You are a helpful assistant. When a tool is used, only print its output without adding more content.\",\n", + " sampling_params=sampling_params,\n", " tools=[rag_agent_tool],\n", ")" ] @@ -412,7 +483,7 @@ }, { "cell_type": "code", - "execution_count": 12, + "execution_count": 15, "id": "95b9baa2-4739-426a-b79a-2ff90f44c023", "metadata": { "tags": [] @@ -427,7 +498,7 @@ "\n", "---------- 📍 Step 1: InferenceStep ----------\n", "🛠️ Tool call Generated:\n", - "\u001b[35mTool call: OpenShift Knowledge Source Agent, Arguments: {'query': 'How to install OpenShift'}\u001b[0m\n", + "\u001b[35mTool call: OpenShift Knowledge Source Agent, Arguments: {'query': 'install OpenShift'}\u001b[0m\n", "\n", "---------- 📍 Step 2: ToolExecutionStep ----------\n", "🔧 Executing tool...\n" @@ -436,11 +507,11 @@ { "data": { "text/html": [ - "
'<tool_call>Tool:knowledge_search Args:{\\'query\\': \\'How to install OpenShift\\'}Tool:knowledge_search Response:[TextContentItem(text=\\'knowledge_search tool found 5 chunks:\\\\nBEGIN of knowledge_search tool results.\\\\n\\', type=\\'text\\'), TextContentItem(text=\\'Result 1:\\\\nDocument_id:num-0\\\\nContent:  \"Import from Git\" entry. Click on it, and paste the URL of a project, for example,\\\\ngitlab.com/akosma/simple-deno-api.git.\\\\nAs soon as you paste the URL, OpenShift will immediately analyze the structure and programming\\\\nlanguage of the project and automatically recommend options for its build process. In our case, it’s\\\\na small application built with the Go programming language, and as such, it will advise the options\\\\nshown on the screen.\\\\nFigure 5. Deploying a project directly from its Git repository\\\\n25\\\\nThis particular example doesn’t require more configurations than the ones shown on the screen;\\\\nclick the [\\\\u2009Create\\\\u2009] button.\\\\nAfter a few seconds, you will see your application running on the \"Topology\" screen. OpenShift will\\\\ndownload the source code and trigger your project’s build. Click on the Topology screen icon to see\\\\nthe \"Build\" section, indicating that a build is running. The compilation and deployment of your\\\\napplication can take some time, depending on the complexity of the source code and the\\\\nprogramming language used.\\\\nOnce the build has finished, on the same pane, you will see a route available under the \"Routes\"\\\\nsection. Click on it, and you will see your application in action.\\\\n10.2. Container Registry\\\\nOpenShift has built your application source code, and the product of this build process is a\\\\ncontainer. You can see the container that OpenShift made for you on the \"Administrator\"\\\\nperspective, selecting the \"Builds\" menu and then the \"ImageStreams\" menu entry.\\\\nOpenShift includes a container registry; developers can use it as any other registry from outside the\\\\ncluster. Let us use \"podman\" to access the container registry and run the container locally on your\\\\nworkstation.\\\\nUsers must have the \"registry-editor\" and the \"system:image-builder\" roles to access the container\\\\nregistry. Since we’re connected to the Web Console using the \"kubeadmin\" user, we can provide\\\\nthose roles directly from the user interface without using the command line.\\\\nNavigate to the \"User Management\" section and select \"RoleBindings.\" Click on the [\\\\u2009Create\\\\nbinding\\\\u2009] button, and fill the form using the following values:\\\\n• Name: developer-sourcecode-registry-editor\\\\n• Namespace: sourcecode\\\\n• Role name: registry-editor\\\\n• Subject: User\\\\n• Subject name: developer\\\\nDo the same for the \"system:image-builder\" role, using a different \"Name\" field\\\\n\\', type=\\'text\\'), TextContentItem(text=\\'Result 2:\\\\nDocument_id:num-0\\\\nContent: 23\\\\ninstall OpenShift Local, 16\\\\nJ\\\\nJAR file, 23\\\\nJava, 14, 24, 44\\\\nJavaScript, 24, 44\\\\nJenkins, 28\\\\nK\\\\nKiali, 36\\\\nKibana, 40, 40\\\\nKnative, 34\\\\nKubernetes, 7\\\\nL\\\\nlogs, 40\\\\nM\\\\nMicroservices, 36\\\\nmonitor, 40\\\\nN\\\\nNode.js, 14\\\\nnon-root accounts, 20\\\\nO\\\\nOpenShift 4.12, 33\\\\nOpenShift Kubernetes Distribution, 8\\\\nOpenShift Service Mesh, 36\\\\noperator, 28, 36\\\\nOperatorHub, 33, 36\\\\nOperators, 33\\\\n48\\\\nP\\\\nperspectives, 22\\\\nPHP, 14, 24\\\\nPlatform-as-a-Service, 8\\\\nprivilege escalation, 19\\\\nprivileged ports, 20\\\\nProject, 9\\\\nPrometheus, 40, 44\\\\nPromQL, 45\\\\nPython, 14, 24, 44\\\\nQ\\\\nQuarkus, 14, 44\\\\nR\\\\nRed Hat developer account, 13\\\\nRed Hat OpenShift, 7\\\\nRed Hat OpenShift Dev Spaces, 14\\\\nRed Hat OpenShift Local, 8, 15\\\\nRed Hat OpenShift Pipelines, 28\\\\nRed Hat Quay, 20\\\\nRed Hat Universal Base Images, 19\\\\nrole, 19\\\\nRoute, 9\\\\nRust, 14\\\\nS\\\\nScala, 14\\\\nScaling, 42\\\\nsecure by default, 19\\\\nSecurity Context Constraints, 19\\\\nServerless, 34\\\\nservice mesh, 36\\\\nsource code project, 22\\\\nstateful applications, 33\\\\nT\\\\nTekton, 28\\\\ntemplates, 32\\\\nTopology, 27\\\\nTwelve-Factor App, 21, 40\\\\nTypeScript, 24\\\\nU\\\\nUBI, 19\\\\nV\\\\nVertical scaling, 42\\\\nVisual Studio Code, 14\\\\nW\\\\nWeb Console, 22, 40\\\\n49\\\\n\\', type=\\'text\\'), TextContentItem(text=\\'Result 3:\\\\nDocument_id:num-0\\\\nContent: .\\\\nThese characteristics set OpenShift apart as an excellent Kubernetes platform for enterprise users.\\\\nThe latest version of OpenShift available at the time of this writing is 4.12.\\\\n3.2. Is Red Hat OpenShift Open Source?\\\\nRed Hat OpenShift is a commercial product based on an open-source project called OKD. This\\\\nacronym means \" OpenShift Kubernetes Distribution\" and is publicly available for everyone to\\\\ninspect and contribute. Like the upstream Kubernetes project, OKD developers use the Go\\\\nprogramming language.\\\\n3.3. How can I run OpenShift?\\\\nToday, Red Hat OpenShift is available through various mechanisms and formats:\\\\n• DevOps teams can install it in their data centers \"on-premise.\"\\\\n• Major hyperscalers such as AWS, Azure, Google Cloud Platform, and IBM Cloud offer managed\\\\nRed Hat OpenShift installations.\\\\n• Developers can either run OpenShift locally on their workstations using Red Hat OpenShift\\\\nLocal, also known as CRC or \"Code-Ready Containers\"\\\\n• They can also request a 30-day trial OpenShift cluster, offered by Red Hat, at no charge, for\\\\ntesting and evaluation purposes.\\\\nRed Hat OpenShift is an integrated Platform-as-a-Service for enterprise users based on Kubernetes.\\\\nIt is tightly integrated with advanced security settings, developer tooling, and monitoring\\\\nmechanisms, allowing DevOps teams to be more productive.\\\\n8\\\\nChapter 4. OpenShift-only Custom Resource\\\\nDefinitions\\\\nRed Hat OpenShift is a complete DevOps platform extending Kubernetes in various ways. It bundles\\\\na constellation of Custom Resource Definitions (CRDs) to make the life of developers and cluster\\\\nadministrators easier.\\\\nLet us talk first about the CRDs only available on OpenShift.\\\\n4.1. Project\\\\nAn OpenShift Project is similar to a Kubernetes namespace, but more tightly integrated into the\\\\nsecurity system of OpenShift through additional annotations.\\\\napiVersion: project.openshift.io/v1\\\\nkind: Project\\\\nmetadata:\\\\n\\\\xa0 name: linkedin-learning-project\\\\n\\\\xa0 annotations:\\\\n\\\\xa0   openshift.io/description: \"Project description\"\\\\n\\\\xa0   openshift.io/display-name: \"Display name\"\\\\n4.2. Route\\\\nThe OpenShift Route object was one of the primary inspirations during the development of the\\\\nIngress object. In OpenShift, Ingress and Route objects work together to ensure your applications\\\\nare available outside the cluster.\\\\napiVersion: route.openshift.io/v1\\\\nkind: Route\\\\nmetadata:\\\\n\\\\xa0 name: my-route\\\\nspec:\\\\n\\\\xa0 host:\\\\n\\', type=\\'text\\'), TextContentItem(text=\\'Result 4:\\\\nDocument_id:num-0\\\\nContent:  We\\\\nrecommend you to check the official Red Hat OpenShift Local documentation for an updated list of\\\\nrequirements at the official documentation website.\\\\n\\\\uf05a\\\\nRegarding Linux, even if Red Hat does not officially support them, OpenShift Local\\\\ncan run on other distributions, such as Ubuntu or Debian, with minor caveats.\\\\nRunning OpenShift Local on any Linux distribution requires a few additional\\\\nsoftware packages to be installed through your default package manager. The\\\\n15\\\\ndocumentation at crc.dev/crc has more information about this subject.\\\\n7.2. Hardware Requirements\\\\nIn terms of hardware, OpenShift Local has some strict requirements. Your system must use a recent\\\\nIntel CPU (except for Macs, where Apple Silicon machines are supported) with at least four physical\\\\ncores and have at least 16 GB of RAM. Be aware that the base installation of OpenShift Local\\\\nrequires at least 9 GB free to start. Of course, to run other applications on OpenShift Local, you will\\\\nneed more RAM, so using a computer with at least 32 GB of RAM is strongly recommended.\\\\nOpenShift Local also requires at least 35 GB of free disk space for its installation. The memory\\\\nrequirements are likely to increase in the future, so please check the documentation at crc.dev for\\\\nmore up-to-date information.\\\\n7.3. Installation\\\\nTo install OpenShift Local, open your web browser and navigate to console.redhat.com/openshift/\\\\ncreate/local . Download the latest release of OpenShift Local and the \"pull secret\" file. The latter is a\\\\nfile containing a key identifying your copy of OpenShift Local to your Red Hat Developer account.\\\\nUnzip the file containing the OpenShift Local executable, and using your terminal, run the\\\\ncommand crc setup . This command will prepare your copy of OpenShift Local, verifying\\\\nrequirements and setting the required configuration values.\\\\nOnce the crc setup command is ready, launch crc start. Running crc start can take a long time,\\\\naround 20 minutes, on a recent PC.\\\\nOnce started, access the OpenShift Web Console with the crc console command, which will open\\\\nyour default browser. OpenShift Local uses the developer username and password to log in as a\\\\nlow-privilege user, while the kubeadmin user uses a random-generated password. Use the crc\\\\nconsole --credentials command to find the credentials required to log in as the kubeadmin user.\\\\nOpenShift Local allows developers to perform various everyday tasks as if it were a standard\\\\nOpenShift cluster, like deploying applications\\\\n\\', type=\\'text\\'), TextContentItem(text=\\'Result 5:\\\\nDocument_id:num-0\\\\nContent: _02 branch of the GitHub\\\\nrepository for this course.\\\\nThe whole point of OpenShift is to be able to deploy, run, and monitor containerized applications.\\\\nDevOps engineers can deploy containers in OpenShift clusters through various means, for example:\\\\n• Using a YAML manifest.\\\\n• Deploying a single container using the web console.\\\\n• Building a project stored in a Git repository anywhere on the Internet and deploying the\\\\nresulting container.\\\\n• Using the integrated CI/CD pipelines.\\\\n• Using the odo tool together with \"Devfiles.\"\\\\nEach approach has pros and cons; in this chapter, we will review how to use the web console and\\\\nhow to use the odo tool.\\\\n9.1. Deploying Applications with the Web Console\\\\nDevOps engineers can deploy applications immediately using the Web Console. Launch your CRC\\\\ninstance and open the web console in your preferred web browser. The URL of the OpenShift web\\\\nconsole is \"https://console-openshift-console.apps-crc.testing.\"\\\\nThe OpenShift Web Console offers two major perspectives:\\\\n• The \"Administrator\" perspective.\\\\n• And the \"Developer\" perspective.\\\\nFor this explanation, select the \"Developer\" perspective.\\\\nThe first time you open the Developer perspective, a popup invites you to follow a user interface\\\\ntour.\\\\nOn the left-hand side, the perspective menu shows an entry titled \"Add,\" which, as the name\\\\nimplies, provides various mechanisms to deploy applications on an OpenShift cluster.\\\\nThe \"Add\" screen shows the various ways DevOps engineers can deploy applications on a cluster:\\\\n• Using the Developer Catalog, browsing and choosing among a long list of available databases,\\\\nmessage queues, and other valuable components to build applications, or entering your\\\\npreferred Helm chart repository to extend the catalog.\\\\n• Specifying the URL to a specific container on any standard container registry.\\\\n• Specifying the URL of a source code project stored on a Git repository, for example, but not\\\\n22\\\\nlimited to GitHub, GitLab, Gitea, or other locations.\\\\n• Importing YAML directly or even a JAR file with a Java application.\\\\nLet us select the \"Container Image\" option, where we can specify the URL of a ready-to-use\\\\ncontainer.\\\\nEnter the URL of the container on the field, and click on the [\\\\u2009Create\\\\u2009] button at the bottom of the\\\\npage. You do not need to change any other value on the form.\\\\nA few seconds later, depending on the size of the container and the speed of your Internet\\\\nconnection,\\\\n\\', type=\\'text\\'), TextContentItem(text=\\'END of knowledge_search tool results.\\\\n\\', type=\\'text\\'), TextContentItem(text=\\'The above results were retrieved to help answer the user\\\\\\'s query: \"How to install OpenShift\". Use them as supporting information only in answering this query.\\\\n\\', type=\\'text\\')]To install OpenShift, you can follow these steps:\\n\\n1. **Install on your data center (on-premise):** You can install OpenShift in your own data center. This involves setting up a Kubernetes cluster and then deploying OpenShift on top of it. Detailed instructions can be found in the official Red Hat OpenShift documentation.\\n\\n2. **Use managed services from hyperscalers:** Major cloud providers like AWS, Azure, Google Cloud Platform, and IBM Cloud offer managed OpenShift services. You can provision an OpenShift cluster through their respective web interfaces.\\n\\n3. **Run OpenShift Locally on your workstation:** Red Hat OpenShift Local, also known as CodeReady Containers (CRC), allows you to run OpenShift on your local machine. Here\\'s how:\\n   - Download the latest release of OpenShift Local and the \"pull secret\" file from the official Red Hat website.\\n   - Unzip the downloaded file and run the command `crc setup` in your terminal to prepare your OpenShift Local instance.\\n   - After the setup is complete, start OpenShift Local with the command `crc start`. This process can take around 20 minutes.\\n   - Access the OpenShift Web Console with the command `crc console`. The default login credentials are the developer username and password for a low-privilege user, and the kubeadmin user uses a random-generated password. You can find the kubeadmin credentials with the command `crc console --credentials`.\\n\\nRemember, running OpenShift Locally requires a recent Intel CPU (or Apple Silicon for Macs), at least 4 physical cores, 16 GB of RAM, and 35 GB of free disk space.\\n\\nFor more detailed and up-to-date information, always refer to the official Red Hat OpenShift documentation.'\n",
+       "
'Tool:knowledge_search Args:{\\'query\\': \\'OpenShift installation\\'}Tool:knowledge_search Response:[TextContentItem(text=\\'knowledge_search tool found 5 chunks:\\\\nBEGIN of knowledge_search tool results.\\\\n\\', type=\\'text\\'), TextContentItem(text=\\'Result 1:\\\\nDocument_id:num-0\\\\nContent: 23\\\\ninstall OpenShift Local, 16\\\\nJ\\\\nJAR file, 23\\\\nJava, 14, 24, 44\\\\nJavaScript, 24, 44\\\\nJenkins, 28\\\\nK\\\\nKiali, 36\\\\nKibana, 40, 40\\\\nKnative, 34\\\\nKubernetes, 7\\\\nL\\\\nlogs, 40\\\\nM\\\\nMicroservices, 36\\\\nmonitor, 40\\\\nN\\\\nNode.js, 14\\\\nnon-root accounts, 20\\\\nO\\\\nOpenShift 4.12, 33\\\\nOpenShift Kubernetes Distribution, 8\\\\nOpenShift Service Mesh, 36\\\\noperator, 28, 36\\\\nOperatorHub, 33, 36\\\\nOperators, 33\\\\n48\\\\nP\\\\nperspectives, 22\\\\nPHP, 14, 24\\\\nPlatform-as-a-Service, 8\\\\nprivilege escalation, 19\\\\nprivileged ports, 20\\\\nProject, 9\\\\nPrometheus, 40, 44\\\\nPromQL, 45\\\\nPython, 14, 24, 44\\\\nQ\\\\nQuarkus, 14, 44\\\\nR\\\\nRed Hat developer account, 13\\\\nRed Hat OpenShift, 7\\\\nRed Hat OpenShift Dev Spaces, 14\\\\nRed Hat OpenShift Local, 8, 15\\\\nRed Hat OpenShift Pipelines, 28\\\\nRed Hat Quay, 20\\\\nRed Hat Universal Base Images, 19\\\\nrole, 19\\\\nRoute, 9\\\\nRust, 14\\\\nS\\\\nScala, 14\\\\nScaling, 42\\\\nsecure by default, 19\\\\nSecurity Context Constraints, 19\\\\nServerless, 34\\\\nservice mesh, 36\\\\nsource code project, 22\\\\nstateful applications, 33\\\\nT\\\\nTekton, 28\\\\ntemplates, 32\\\\nTopology, 27\\\\nTwelve-Factor App, 21, 40\\\\nTypeScript, 24\\\\nU\\\\nUBI, 19\\\\nV\\\\nVertical scaling, 42\\\\nVisual Studio Code, 14\\\\nW\\\\nWeb Console, 22, 40\\\\n49\\\\n\\', type=\\'text\\'), TextContentItem(text=\\'Result 2:\\\\nDocument_id:num-0\\\\nContent:  We\\\\nrecommend you to check the official Red Hat OpenShift Local documentation for an updated list of\\\\nrequirements at the official documentation website.\\\\n\\\\uf05a\\\\nRegarding Linux, even if Red Hat does not officially support them, OpenShift Local\\\\ncan run on other distributions, such as Ubuntu or Debian, with minor caveats.\\\\nRunning OpenShift Local on any Linux distribution requires a few additional\\\\nsoftware packages to be installed through your default package manager. The\\\\n15\\\\ndocumentation at crc.dev/crc has more information about this subject.\\\\n7.2. Hardware Requirements\\\\nIn terms of hardware, OpenShift Local has some strict requirements. Your system must use a recent\\\\nIntel CPU (except for Macs, where Apple Silicon machines are supported) with at least four physical\\\\ncores and have at least 16 GB of RAM. Be aware that the base installation of OpenShift Local\\\\nrequires at least 9 GB free to start. Of course, to run other applications on OpenShift Local, you will\\\\nneed more RAM, so using a computer with at least 32 GB of RAM is strongly recommended.\\\\nOpenShift Local also requires at least 35 GB of free disk space for its installation. The memory\\\\nrequirements are likely to increase in the future, so please check the documentation at crc.dev for\\\\nmore up-to-date information.\\\\n7.3. Installation\\\\nTo install OpenShift Local, open your web browser and navigate to console.redhat.com/openshift/\\\\ncreate/local . Download the latest release of OpenShift Local and the \"pull secret\" file. The latter is a\\\\nfile containing a key identifying your copy of OpenShift Local to your Red Hat Developer account.\\\\nUnzip the file containing the OpenShift Local executable, and using your terminal, run the\\\\ncommand crc setup . This command will prepare your copy of OpenShift Local, verifying\\\\nrequirements and setting the required configuration values.\\\\nOnce the crc setup command is ready, launch crc start. Running crc start can take a long time,\\\\naround 20 minutes, on a recent PC.\\\\nOnce started, access the OpenShift Web Console with the crc console command, which will open\\\\nyour default browser. OpenShift Local uses the developer username and password to log in as a\\\\nlow-privilege user, while the kubeadmin user uses a random-generated password. Use the crc\\\\nconsole --credentials command to find the credentials required to log in as the kubeadmin user.\\\\nOpenShift Local allows developers to perform various everyday tasks as if it were a standard\\\\nOpenShift cluster, like deploying applications\\\\n\\', type=\\'text\\'), TextContentItem(text=\\'Result 3:\\\\nDocument_id:num-0\\\\nContent:  \"Import from Git\" entry. Click on it, and paste the URL of a project, for example,\\\\ngitlab.com/akosma/simple-deno-api.git.\\\\nAs soon as you paste the URL, OpenShift will immediately analyze the structure and programming\\\\nlanguage of the project and automatically recommend options for its build process. In our case, it’s\\\\na small application built with the Go programming language, and as such, it will advise the options\\\\nshown on the screen.\\\\nFigure 5. Deploying a project directly from its Git repository\\\\n25\\\\nThis particular example doesn’t require more configurations than the ones shown on the screen;\\\\nclick the [\\\\u2009Create\\\\u2009] button.\\\\nAfter a few seconds, you will see your application running on the \"Topology\" screen. OpenShift will\\\\ndownload the source code and trigger your project’s build. Click on the Topology screen icon to see\\\\nthe \"Build\" section, indicating that a build is running. The compilation and deployment of your\\\\napplication can take some time, depending on the complexity of the source code and the\\\\nprogramming language used.\\\\nOnce the build has finished, on the same pane, you will see a route available under the \"Routes\"\\\\nsection. Click on it, and you will see your application in action.\\\\n10.2. Container Registry\\\\nOpenShift has built your application source code, and the product of this build process is a\\\\ncontainer. You can see the container that OpenShift made for you on the \"Administrator\"\\\\nperspective, selecting the \"Builds\" menu and then the \"ImageStreams\" menu entry.\\\\nOpenShift includes a container registry; developers can use it as any other registry from outside the\\\\ncluster. Let us use \"podman\" to access the container registry and run the container locally on your\\\\nworkstation.\\\\nUsers must have the \"registry-editor\" and the \"system:image-builder\" roles to access the container\\\\nregistry. Since we’re connected to the Web Console using the \"kubeadmin\" user, we can provide\\\\nthose roles directly from the user interface without using the command line.\\\\nNavigate to the \"User Management\" section and select \"RoleBindings.\" Click on the [\\\\u2009Create\\\\nbinding\\\\u2009] button, and fill the form using the following values:\\\\n• Name: developer-sourcecode-registry-editor\\\\n• Namespace: sourcecode\\\\n• Role name: registry-editor\\\\n• Subject: User\\\\n• Subject name: developer\\\\nDo the same for the \"system:image-builder\" role, using a different \"Name\" field\\\\n\\', type=\\'text\\'), TextContentItem(text=\\'Result 4:\\\\nDocument_id:num-0\\\\nContent: .\\\\nThese characteristics set OpenShift apart as an excellent Kubernetes platform for enterprise users.\\\\nThe latest version of OpenShift available at the time of this writing is 4.12.\\\\n3.2. Is Red Hat OpenShift Open Source?\\\\nRed Hat OpenShift is a commercial product based on an open-source project called OKD. This\\\\nacronym means \" OpenShift Kubernetes Distribution\" and is publicly available for everyone to\\\\ninspect and contribute. Like the upstream Kubernetes project, OKD developers use the Go\\\\nprogramming language.\\\\n3.3. How can I run OpenShift?\\\\nToday, Red Hat OpenShift is available through various mechanisms and formats:\\\\n• DevOps teams can install it in their data centers \"on-premise.\"\\\\n• Major hyperscalers such as AWS, Azure, Google Cloud Platform, and IBM Cloud offer managed\\\\nRed Hat OpenShift installations.\\\\n• Developers can either run OpenShift locally on their workstations using Red Hat OpenShift\\\\nLocal, also known as CRC or \"Code-Ready Containers\"\\\\n• They can also request a 30-day trial OpenShift cluster, offered by Red Hat, at no charge, for\\\\ntesting and evaluation purposes.\\\\nRed Hat OpenShift is an integrated Platform-as-a-Service for enterprise users based on Kubernetes.\\\\nIt is tightly integrated with advanced security settings, developer tooling, and monitoring\\\\nmechanisms, allowing DevOps teams to be more productive.\\\\n8\\\\nChapter 4. OpenShift-only Custom Resource\\\\nDefinitions\\\\nRed Hat OpenShift is a complete DevOps platform extending Kubernetes in various ways. It bundles\\\\na constellation of Custom Resource Definitions (CRDs) to make the life of developers and cluster\\\\nadministrators easier.\\\\nLet us talk first about the CRDs only available on OpenShift.\\\\n4.1. Project\\\\nAn OpenShift Project is similar to a Kubernetes namespace, but more tightly integrated into the\\\\nsecurity system of OpenShift through additional annotations.\\\\napiVersion: project.openshift.io/v1\\\\nkind: Project\\\\nmetadata:\\\\n\\\\xa0 name: linkedin-learning-project\\\\n\\\\xa0 annotations:\\\\n\\\\xa0   openshift.io/description: \"Project description\"\\\\n\\\\xa0   openshift.io/display-name: \"Display name\"\\\\n4.2. Route\\\\nThe OpenShift Route object was one of the primary inspirations during the development of the\\\\nIngress object. In OpenShift, Ingress and Route objects work together to ensure your applications\\\\nare available outside the cluster.\\\\napiVersion: route.openshift.io/v1\\\\nkind: Route\\\\nmetadata:\\\\n\\\\xa0 name: my-route\\\\nspec:\\\\n\\\\xa0 host:\\\\n\\', type=\\'text\\'), TextContentItem(text=\\'Result 5:\\\\nDocument_id:num-0\\\\nContent: _02 branch of the GitHub\\\\nrepository for this course.\\\\nThe whole point of OpenShift is to be able to deploy, run, and monitor containerized applications.\\\\nDevOps engineers can deploy containers in OpenShift clusters through various means, for example:\\\\n• Using a YAML manifest.\\\\n• Deploying a single container using the web console.\\\\n• Building a project stored in a Git repository anywhere on the Internet and deploying the\\\\nresulting container.\\\\n• Using the integrated CI/CD pipelines.\\\\n• Using the odo tool together with \"Devfiles.\"\\\\nEach approach has pros and cons; in this chapter, we will review how to use the web console and\\\\nhow to use the odo tool.\\\\n9.1. Deploying Applications with the Web Console\\\\nDevOps engineers can deploy applications immediately using the Web Console. Launch your CRC\\\\ninstance and open the web console in your preferred web browser. The URL of the OpenShift web\\\\nconsole is \"https://console-openshift-console.apps-crc.testing.\"\\\\nThe OpenShift Web Console offers two major perspectives:\\\\n• The \"Administrator\" perspective.\\\\n• And the \"Developer\" perspective.\\\\nFor this explanation, select the \"Developer\" perspective.\\\\nThe first time you open the Developer perspective, a popup invites you to follow a user interface\\\\ntour.\\\\nOn the left-hand side, the perspective menu shows an entry titled \"Add,\" which, as the name\\\\nimplies, provides various mechanisms to deploy applications on an OpenShift cluster.\\\\nThe \"Add\" screen shows the various ways DevOps engineers can deploy applications on a cluster:\\\\n• Using the Developer Catalog, browsing and choosing among a long list of available databases,\\\\nmessage queues, and other valuable components to build applications, or entering your\\\\npreferred Helm chart repository to extend the catalog.\\\\n• Specifying the URL to a specific container on any standard container registry.\\\\n• Specifying the URL of a source code project stored on a Git repository, for example, but not\\\\n22\\\\nlimited to GitHub, GitLab, Gitea, or other locations.\\\\n• Importing YAML directly or even a JAR file with a Java application.\\\\nLet us select the \"Container Image\" option, where we can specify the URL of a ready-to-use\\\\ncontainer.\\\\nEnter the URL of the container on the field, and click on the [\\\\u2009Create\\\\u2009] button at the bottom of the\\\\npage. You do not need to change any other value on the form.\\\\nA few seconds later, depending on the size of the container and the speed of your Internet\\\\nconnection,\\\\n\\', type=\\'text\\'), TextContentItem(text=\\'END of knowledge_search tool results.\\\\n\\', type=\\'text\\'), TextContentItem(text=\\'The above results were retrieved to help answer the user\\\\\\'s query: \"OpenShift installation\". Use them as supporting information only in answering this query.\\\\n\\', type=\\'text\\')]To install OpenShift, you can follow these steps:\\n\\n1. Check the system requirements for OpenShift Local, which is a version of OpenShift that can be installed on a local machine. The requirements include a recent Intel CPU with at least four physical cores, 16 GB of RAM, and 35 GB of free disk space.\\n2. Download the latest version of OpenShift Local from the official Red Hat website.\\n3. Unzip the downloaded file and run the `crc setup` command to prepare the installation.\\n4. Launch the `crc start` command to start the installation process, which may take around 20 minutes.\\n5. Once the installation is complete, access the OpenShift Web Console using the `crc console` command, which will open your default browser.\\n6. Log in to the Web Console using the developer username and password, or use the `crc console --credentials` command to find the credentials required to log in as the kubeadmin user.\\n7. You can then deploy applications using the web console or using the odo tool.\\n\\nNote that OpenShift is also available through various mechanisms and formats, including installation in data centers, managed installations by hyperscalers, and trial clusters offered by Red Hat.'\n",
        "
\n" ], "text/plain": [ - "\u001b[32m'\u001b[0m\u001b[32m<\u001b[0m\u001b[32mtool_call\u001b[0m\u001b[32m>\u001b[0m\u001b[32mTool:knowledge_search Args:\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\'query\\': \\'How to install OpenShift\\'\u001b[0m\u001b[32m}\u001b[0m\u001b[32mTool:knowledge_search Response:\u001b[0m\u001b[32m[\u001b[0m\u001b[32mTextContentItem\u001b[0m\u001b[32m(\u001b[0m\u001b[32mtext\u001b[0m\u001b[32m=\\'knowledge_search tool found 5 chunks:\\\\nBEGIN of knowledge_search tool results.\\\\n\\', \u001b[0m\u001b[32mtype\u001b[0m\u001b[32m=\\'text\\'\u001b[0m\u001b[32m)\u001b[0m\u001b[32m, TextContentItem\u001b[0m\u001b[32m(\u001b[0m\u001b[32mtext\u001b[0m\u001b[32m=\\'Result 1:\\\\nDocument_id:num-0\\\\nContent: \"Import from Git\" entry. Click on it, and paste the URL of a project, for example,\\\\ngitlab.com/akosma/simple-deno-api.git.\\\\nAs soon as you paste the URL, OpenShift will immediately analyze the structure and programming\\\\nlanguage of the project and automatically recommend options for its build process. In our case, it’s\\\\na small application built with the Go programming language, and as such, it will advise the options\\\\nshown on the screen.\\\\nFigure 5. Deploying a project directly from its Git repository\\\\n25\\\\nThis particular example doesn’t require more configurations than the ones shown on the screen;\\\\nclick the \u001b[0m\u001b[32m[\u001b[0m\u001b[32m\\\\u2009Create\\\\u2009\u001b[0m\u001b[32m]\u001b[0m\u001b[32m button.\\\\nAfter a few seconds, you will see your application running on the \"Topology\" screen. OpenShift will\\\\ndownload the source code and trigger your project’s build. Click on the Topology screen icon to see\\\\nthe \"Build\" section, indicating that a build is running. The compilation and deployment of your\\\\napplication can take some time, depending on the complexity of the source code and the\\\\nprogramming language used.\\\\nOnce the build has finished, on the same pane, you will see a route available under the \"Routes\"\\\\nsection. Click on it, and you will see your application in action.\\\\n10.2. Container Registry\\\\nOpenShift has built your application source code, and the product of this build process is a\\\\ncontainer. You can see the container that OpenShift made for you on the \"Administrator\"\\\\nperspective, selecting the \"Builds\" menu and then the \"ImageStreams\" menu entry.\\\\nOpenShift includes a container registry; developers can use it as any other registry from outside the\\\\ncluster. Let us use \"podman\" to access the container registry and run the container locally on your\\\\nworkstation.\\\\nUsers must have the \"registry-editor\" and the \"system:image-builder\" roles to access the container\\\\nregistry. Since we’re connected to the Web Console using the \"kubeadmin\" user, we can provide\\\\nthose roles directly from the user interface without using the command line.\\\\nNavigate to the \"User Management\" section and select \"RoleBindings.\" Click on the \u001b[0m\u001b[32m[\u001b[0m\u001b[32m\\\\u2009Create\\\\nbinding\\\\u2009\u001b[0m\u001b[32m]\u001b[0m\u001b[32m button, and fill the form using the following values:\\\\n• Name: developer-sourcecode-registry-editor\\\\n• Namespace: sourcecode\\\\n• Role name: registry-editor\\\\n• Subject: User\\\\n• Subject name: developer\\\\nDo the same for the \"system:image-builder\" role, using a different \"Name\" field\\\\n\\', \u001b[0m\u001b[32mtype\u001b[0m\u001b[32m=\\'text\\'\u001b[0m\u001b[32m)\u001b[0m\u001b[32m, TextContentItem\u001b[0m\u001b[32m(\u001b[0m\u001b[32mtext\u001b[0m\u001b[32m=\\'Result 2:\\\\nDocument_id:num-0\\\\nContent: 23\\\\ninstall OpenShift Local, 16\\\\nJ\\\\nJAR file, 23\\\\nJava, 14, 24, 44\\\\nJavaScript, 24, 44\\\\nJenkins, 28\\\\nK\\\\nKiali, 36\\\\nKibana, 40, 40\\\\nKnative, 34\\\\nKubernetes, 7\\\\nL\\\\nlogs, 40\\\\nM\\\\nMicroservices, 36\\\\nmonitor, 40\\\\nN\\\\nNode.js, 14\\\\nnon-root accounts, 20\\\\nO\\\\nOpenShift 4.12, 33\\\\nOpenShift Kubernetes Distribution, 8\\\\nOpenShift Service Mesh, 36\\\\noperator, 28, 36\\\\nOperatorHub, 33, 36\\\\nOperators, 33\\\\n48\\\\nP\\\\nperspectives, 22\\\\nPHP, 14, 24\\\\nPlatform-as-a-Service, 8\\\\nprivilege escalation, 19\\\\nprivileged ports, 20\\\\nProject, 9\\\\nPrometheus, 40, 44\\\\nPromQL, 45\\\\nPython, 14, 24, 44\\\\nQ\\\\nQuarkus, 14, 44\\\\nR\\\\nRed Hat developer account, 13\\\\nRed Hat OpenShift, 7\\\\nRed Hat OpenShift Dev Spaces, 14\\\\nRed Hat OpenShift Local, 8, 15\\\\nRed Hat OpenShift Pipelines, 28\\\\nRed Hat Quay, 20\\\\nRed Hat Universal Base Images, 19\\\\nrole, 19\\\\nRoute, 9\\\\nRust, 14\\\\nS\\\\nScala, 14\\\\nScaling, 42\\\\nsecure by default, 19\\\\nSecurity Context Constraints, 19\\\\nServerless, 34\\\\nservice mesh, 36\\\\nsource code project, 22\\\\nstateful applications, 33\\\\nT\\\\nTekton, 28\\\\ntemplates, 32\\\\nTopology, 27\\\\nTwelve-Factor App, 21, 40\\\\nTypeScript, 24\\\\nU\\\\nUBI, 19\\\\nV\\\\nVertical scaling, 42\\\\nVisual Studio Code, 14\\\\nW\\\\nWeb Console, 22, 40\\\\n49\\\\n\\', \u001b[0m\u001b[32mtype\u001b[0m\u001b[32m=\\'text\\'\u001b[0m\u001b[32m)\u001b[0m\u001b[32m, TextContentItem\u001b[0m\u001b[32m(\u001b[0m\u001b[32mtext\u001b[0m\u001b[32m=\\'Result 3:\\\\nDocument_id:num-0\\\\nContent: .\\\\nThese characteristics set OpenShift apart as an excellent Kubernetes platform for enterprise users.\\\\nThe latest version of OpenShift available at the time of this writing is 4.12.\\\\n3.2. Is Red Hat OpenShift Open Source?\\\\nRed Hat OpenShift is a commercial product based on an open-source project called OKD. This\\\\nacronym means \" OpenShift Kubernetes Distribution\" and is publicly available for everyone to\\\\ninspect and contribute. Like the upstream Kubernetes project, OKD developers use the Go\\\\nprogramming language.\\\\n3.3. How can I run OpenShift?\\\\nToday, Red Hat OpenShift is available through various mechanisms and formats:\\\\n• DevOps teams can install it in their data centers \"on-premise.\"\\\\n• Major hyperscalers such as AWS, Azure, Google Cloud Platform, and IBM Cloud offer managed\\\\nRed Hat OpenShift installations.\\\\n• Developers can either run OpenShift locally on their workstations using Red Hat OpenShift\\\\nLocal, also known as CRC or \"Code-Ready Containers\"\\\\n• They can also request a 30-day trial OpenShift cluster, offered by Red Hat, at no charge, for\\\\ntesting and evaluation purposes.\\\\nRed Hat OpenShift is an integrated Platform-as-a-Service for enterprise users based on Kubernetes.\\\\nIt is tightly integrated with advanced security settings, developer tooling, and monitoring\\\\nmechanisms, allowing DevOps teams to be more productive.\\\\n8\\\\nChapter 4. OpenShift-only Custom Resource\\\\nDefinitions\\\\nRed Hat OpenShift is a complete DevOps platform extending Kubernetes in various ways. It bundles\\\\na constellation of Custom Resource Definitions \u001b[0m\u001b[32m(\u001b[0m\u001b[32mCRDs\u001b[0m\u001b[32m)\u001b[0m\u001b[32m to make the life of developers and cluster\\\\nadministrators easier.\\\\nLet us talk first about the CRDs only available on OpenShift.\\\\n4.1. Project\\\\nAn OpenShift Project is similar to a Kubernetes namespace, but more tightly integrated into the\\\\nsecurity system of OpenShift through additional annotations.\\\\napiVersion: project.openshift.io/v1\\\\nkind: Project\\\\nmetadata:\\\\n\\\\xa0 name: linkedin-learning-project\\\\n\\\\xa0 annotations:\\\\n\\\\xa0 openshift.io/description: \"Project description\"\\\\n\\\\xa0 openshift.io/display-name: \"Display name\"\\\\n4.2. Route\\\\nThe OpenShift Route object was one of the primary inspirations during the development of the\\\\nIngress object. In OpenShift, Ingress and Route objects work together to ensure your applications\\\\nare available outside the cluster.\\\\napiVersion: route.openshift.io/v1\\\\nkind: Route\\\\nmetadata:\\\\n\\\\xa0 name: my-route\\\\nspec:\\\\n\\\\xa0 host:\\\\n\\', \u001b[0m\u001b[32mtype\u001b[0m\u001b[32m=\\'text\\'\u001b[0m\u001b[32m)\u001b[0m\u001b[32m, TextContentItem\u001b[0m\u001b[32m(\u001b[0m\u001b[32mtext\u001b[0m\u001b[32m=\\'Result 4:\\\\nDocument_id:num-0\\\\nContent: We\\\\nrecommend you to check the official Red Hat OpenShift Local documentation for an updated list of\\\\nrequirements at the official documentation website.\\\\n\\\\uf05a\\\\nRegarding Linux, even if Red Hat does not officially support them, OpenShift Local\\\\ncan run on other distributions, such as Ubuntu or Debian, with minor caveats.\\\\nRunning OpenShift Local on any Linux distribution requires a few additional\\\\nsoftware packages to be installed through your default package manager. The\\\\n15\\\\ndocumentation at crc.dev/crc has more information about this subject.\\\\n7.2. Hardware Requirements\\\\nIn terms of hardware, OpenShift Local has some strict requirements. Your system must use a recent\\\\nIntel CPU \u001b[0m\u001b[32m(\u001b[0m\u001b[32mexcept for Macs, where Apple Silicon machines are supported\u001b[0m\u001b[32m)\u001b[0m\u001b[32m with at least four physical\\\\ncores and have at least 16 GB of RAM. Be aware that the base installation of OpenShift Local\\\\nrequires at least 9 GB free to start. Of course, to run other applications on OpenShift Local, you will\\\\nneed more RAM, so using a computer with at least 32 GB of RAM is strongly recommended.\\\\nOpenShift Local also requires at least 35 GB of free disk space for its installation. The memory\\\\nrequirements are likely to increase in the future, so please check the documentation at crc.dev for\\\\nmore up-to-date information.\\\\n7.3. Installation\\\\nTo install OpenShift Local, open your web browser and navigate to console.redhat.com/openshift/\\\\ncreate/local . Download the latest release of OpenShift Local and the \"pull secret\" file. The latter is a\\\\nfile containing a key identifying your copy of OpenShift Local to your Red Hat Developer account.\\\\nUnzip the file containing the OpenShift Local executable, and using your terminal, run the\\\\ncommand crc setup . This command will prepare your copy of OpenShift Local, verifying\\\\nrequirements and setting the required configuration values.\\\\nOnce the crc setup command is ready, launch crc start. Running crc start can take a long time,\\\\naround 20 minutes, on a recent PC.\\\\nOnce started, access the OpenShift Web Console with the crc console command, which will open\\\\nyour default browser. OpenShift Local uses the developer username and password to log in as a\\\\nlow-privilege user, while the kubeadmin user uses a random-generated password. Use the crc\\\\nconsole --credentials command to find the credentials required to log in as the kubeadmin user.\\\\nOpenShift Local allows developers to perform various everyday tasks as if it were a standard\\\\nOpenShift cluster, like deploying applications\\\\n\\', \u001b[0m\u001b[32mtype\u001b[0m\u001b[32m=\\'text\\'\u001b[0m\u001b[32m)\u001b[0m\u001b[32m, TextContentItem\u001b[0m\u001b[32m(\u001b[0m\u001b[32mtext\u001b[0m\u001b[32m=\\'Result 5:\\\\nDocument_id:num-0\\\\nContent: _02 branch of the GitHub\\\\nrepository for this course.\\\\nThe whole point of OpenShift is to be able to deploy, run, and monitor containerized applications.\\\\nDevOps engineers can deploy containers in OpenShift clusters through various means, for example:\\\\n• Using a YAML manifest.\\\\n• Deploying a single container using the web console.\\\\n• Building a project stored in a Git repository anywhere on the Internet and deploying the\\\\nresulting container.\\\\n• Using the integrated CI/CD pipelines.\\\\n• Using the odo tool together with \"Devfiles.\"\\\\nEach approach has pros and cons; in this chapter, we will review how to use the web console and\\\\nhow to use the odo tool.\\\\n9.1. Deploying Applications with the Web Console\\\\nDevOps engineers can deploy applications immediately using the Web Console. Launch your CRC\\\\ninstance and open the web console in your preferred web browser. The URL of the OpenShift web\\\\nconsole is \"https://console-openshift-console.apps-crc.testing.\"\\\\nThe OpenShift Web Console offers two major perspectives:\\\\n• The \"Administrator\" perspective.\\\\n• And the \"Developer\" perspective.\\\\nFor this explanation, select the \"Developer\" perspective.\\\\nThe first time you open the Developer perspective, a popup invites you to follow a user interface\\\\ntour.\\\\nOn the left-hand side, the perspective menu shows an entry titled \"Add,\" which, as the name\\\\nimplies, provides various mechanisms to deploy applications on an OpenShift cluster.\\\\nThe \"Add\" screen shows the various ways DevOps engineers can deploy applications on a cluster:\\\\n• Using the Developer Catalog, browsing and choosing among a long list of available databases,\\\\nmessage queues, and other valuable components to build applications, or entering your\\\\npreferred Helm chart repository to extend the catalog.\\\\n• Specifying the URL to a specific container on any standard container registry.\\\\n• Specifying the URL of a source code project stored on a Git repository, for example, but not\\\\n22\\\\nlimited to GitHub, GitLab, Gitea, or other locations.\\\\n• Importing YAML directly or even a JAR file with a Java application.\\\\nLet us select the \"Container Image\" option, where we can specify the URL of a ready-to-use\\\\ncontainer.\\\\nEnter the URL of the container on the field, and click on the \u001b[0m\u001b[32m[\u001b[0m\u001b[32m\\\\u2009Create\\\\u2009\u001b[0m\u001b[32m]\u001b[0m\u001b[32m button at the bottom of the\\\\npage. You do not need to change any other value on the form.\\\\nA few seconds later, depending on the size of the container and the speed of your Internet\\\\nconnection,\\\\n\\', \u001b[0m\u001b[32mtype\u001b[0m\u001b[32m=\\'text\\'\u001b[0m\u001b[32m)\u001b[0m\u001b[32m, TextContentItem\u001b[0m\u001b[32m(\u001b[0m\u001b[32mtext\u001b[0m\u001b[32m=\\'END of knowledge_search tool results.\\\\n\\', \u001b[0m\u001b[32mtype\u001b[0m\u001b[32m=\\'text\\'\u001b[0m\u001b[32m)\u001b[0m\u001b[32m, TextContentItem\u001b[0m\u001b[32m(\u001b[0m\u001b[32mtext\u001b[0m\u001b[32m=\\'The above results were retrieved to help answer the user\\\\\\'s query: \"How to install OpenShift\". Use them as supporting information only in answering this query.\\\\n\\', \u001b[0m\u001b[32mtype\u001b[0m\u001b[32m=\\'text\\'\u001b[0m\u001b[32m)\u001b[0m\u001b[32m]\u001b[0m\u001b[32mTo install OpenShift, you can follow these steps:\\n\\n1. **Install on your data center \u001b[0m\u001b[32m(\u001b[0m\u001b[32mon-premise\u001b[0m\u001b[32m)\u001b[0m\u001b[32m:** You can install OpenShift in your own data center. This involves setting up a Kubernetes cluster and then deploying OpenShift on top of it. Detailed instructions can be found in the official Red Hat OpenShift documentation.\\n\\n2. **Use managed services from hyperscalers:** Major cloud providers like AWS, Azure, Google Cloud Platform, and IBM Cloud offer managed OpenShift services. You can provision an OpenShift cluster through their respective web interfaces.\\n\\n3. **Run OpenShift Locally on your workstation:** Red Hat OpenShift Local, also known as CodeReady Containers \u001b[0m\u001b[32m(\u001b[0m\u001b[32mCRC\u001b[0m\u001b[32m)\u001b[0m\u001b[32m, allows you to run OpenShift on your local machine. Here\\'s how:\\n - Download the latest release of OpenShift Local and the \"pull secret\" file from the official Red Hat website.\\n - Unzip the downloaded file and run the command `crc setup` in your terminal to prepare your OpenShift Local instance.\\n - After the setup is complete, start OpenShift Local with the command `crc start`. This process can take around 20 minutes.\\n - Access the OpenShift Web Console with the command `crc console`. The default login credentials are the developer username and password for a low-privilege user, and the kubeadmin user uses a random-generated password. You can find the kubeadmin credentials with the command `crc console --credentials`.\\n\\nRemember, running OpenShift Locally requires a recent Intel CPU \u001b[0m\u001b[32m(\u001b[0m\u001b[32mor Apple Silicon for Macs\u001b[0m\u001b[32m)\u001b[0m\u001b[32m, at least 4 physical cores, 16 GB of RAM, and 35 GB of free disk space.\\n\\nFor more detailed and up-to-date information, always refer to the official Red Hat OpenShift documentation.'\u001b[0m\n" + "\u001b[32m'Tool:knowledge_search Args:\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\'query\\': \\'OpenShift installation\\'\u001b[0m\u001b[32m}\u001b[0m\u001b[32mTool:knowledge_search Response:\u001b[0m\u001b[32m[\u001b[0m\u001b[32mTextContentItem\u001b[0m\u001b[32m(\u001b[0m\u001b[32mtext\u001b[0m\u001b[32m=\\'knowledge_search tool found 5 chunks:\\\\nBEGIN of knowledge_search tool results.\\\\n\\', \u001b[0m\u001b[32mtype\u001b[0m\u001b[32m=\\'text\\'\u001b[0m\u001b[32m)\u001b[0m\u001b[32m, TextContentItem\u001b[0m\u001b[32m(\u001b[0m\u001b[32mtext\u001b[0m\u001b[32m=\\'Result 1:\\\\nDocument_id:num-0\\\\nContent: 23\\\\ninstall OpenShift Local, 16\\\\nJ\\\\nJAR file, 23\\\\nJava, 14, 24, 44\\\\nJavaScript, 24, 44\\\\nJenkins, 28\\\\nK\\\\nKiali, 36\\\\nKibana, 40, 40\\\\nKnative, 34\\\\nKubernetes, 7\\\\nL\\\\nlogs, 40\\\\nM\\\\nMicroservices, 36\\\\nmonitor, 40\\\\nN\\\\nNode.js, 14\\\\nnon-root accounts, 20\\\\nO\\\\nOpenShift 4.12, 33\\\\nOpenShift Kubernetes Distribution, 8\\\\nOpenShift Service Mesh, 36\\\\noperator, 28, 36\\\\nOperatorHub, 33, 36\\\\nOperators, 33\\\\n48\\\\nP\\\\nperspectives, 22\\\\nPHP, 14, 24\\\\nPlatform-as-a-Service, 8\\\\nprivilege escalation, 19\\\\nprivileged ports, 20\\\\nProject, 9\\\\nPrometheus, 40, 44\\\\nPromQL, 45\\\\nPython, 14, 24, 44\\\\nQ\\\\nQuarkus, 14, 44\\\\nR\\\\nRed Hat developer account, 13\\\\nRed Hat OpenShift, 7\\\\nRed Hat OpenShift Dev Spaces, 14\\\\nRed Hat OpenShift Local, 8, 15\\\\nRed Hat OpenShift Pipelines, 28\\\\nRed Hat Quay, 20\\\\nRed Hat Universal Base Images, 19\\\\nrole, 19\\\\nRoute, 9\\\\nRust, 14\\\\nS\\\\nScala, 14\\\\nScaling, 42\\\\nsecure by default, 19\\\\nSecurity Context Constraints, 19\\\\nServerless, 34\\\\nservice mesh, 36\\\\nsource code project, 22\\\\nstateful applications, 33\\\\nT\\\\nTekton, 28\\\\ntemplates, 32\\\\nTopology, 27\\\\nTwelve-Factor App, 21, 40\\\\nTypeScript, 24\\\\nU\\\\nUBI, 19\\\\nV\\\\nVertical scaling, 42\\\\nVisual Studio Code, 14\\\\nW\\\\nWeb Console, 22, 40\\\\n49\\\\n\\', \u001b[0m\u001b[32mtype\u001b[0m\u001b[32m=\\'text\\'\u001b[0m\u001b[32m)\u001b[0m\u001b[32m, TextContentItem\u001b[0m\u001b[32m(\u001b[0m\u001b[32mtext\u001b[0m\u001b[32m=\\'Result 2:\\\\nDocument_id:num-0\\\\nContent: We\\\\nrecommend you to check the official Red Hat OpenShift Local documentation for an updated list of\\\\nrequirements at the official documentation website.\\\\n\\\\uf05a\\\\nRegarding Linux, even if Red Hat does not officially support them, OpenShift Local\\\\ncan run on other distributions, such as Ubuntu or Debian, with minor caveats.\\\\nRunning OpenShift Local on any Linux distribution requires a few additional\\\\nsoftware packages to be installed through your default package manager. The\\\\n15\\\\ndocumentation at crc.dev/crc has more information about this subject.\\\\n7.2. Hardware Requirements\\\\nIn terms of hardware, OpenShift Local has some strict requirements. Your system must use a recent\\\\nIntel CPU \u001b[0m\u001b[32m(\u001b[0m\u001b[32mexcept for Macs, where Apple Silicon machines are supported\u001b[0m\u001b[32m)\u001b[0m\u001b[32m with at least four physical\\\\ncores and have at least 16 GB of RAM. Be aware that the base installation of OpenShift Local\\\\nrequires at least 9 GB free to start. Of course, to run other applications on OpenShift Local, you will\\\\nneed more RAM, so using a computer with at least 32 GB of RAM is strongly recommended.\\\\nOpenShift Local also requires at least 35 GB of free disk space for its installation. The memory\\\\nrequirements are likely to increase in the future, so please check the documentation at crc.dev for\\\\nmore up-to-date information.\\\\n7.3. Installation\\\\nTo install OpenShift Local, open your web browser and navigate to console.redhat.com/openshift/\\\\ncreate/local . Download the latest release of OpenShift Local and the \"pull secret\" file. The latter is a\\\\nfile containing a key identifying your copy of OpenShift Local to your Red Hat Developer account.\\\\nUnzip the file containing the OpenShift Local executable, and using your terminal, run the\\\\ncommand crc setup . This command will prepare your copy of OpenShift Local, verifying\\\\nrequirements and setting the required configuration values.\\\\nOnce the crc setup command is ready, launch crc start. Running crc start can take a long time,\\\\naround 20 minutes, on a recent PC.\\\\nOnce started, access the OpenShift Web Console with the crc console command, which will open\\\\nyour default browser. OpenShift Local uses the developer username and password to log in as a\\\\nlow-privilege user, while the kubeadmin user uses a random-generated password. Use the crc\\\\nconsole --credentials command to find the credentials required to log in as the kubeadmin user.\\\\nOpenShift Local allows developers to perform various everyday tasks as if it were a standard\\\\nOpenShift cluster, like deploying applications\\\\n\\', \u001b[0m\u001b[32mtype\u001b[0m\u001b[32m=\\'text\\'\u001b[0m\u001b[32m)\u001b[0m\u001b[32m, TextContentItem\u001b[0m\u001b[32m(\u001b[0m\u001b[32mtext\u001b[0m\u001b[32m=\\'Result 3:\\\\nDocument_id:num-0\\\\nContent: \"Import from Git\" entry. Click on it, and paste the URL of a project, for example,\\\\ngitlab.com/akosma/simple-deno-api.git.\\\\nAs soon as you paste the URL, OpenShift will immediately analyze the structure and programming\\\\nlanguage of the project and automatically recommend options for its build process. In our case, it’s\\\\na small application built with the Go programming language, and as such, it will advise the options\\\\nshown on the screen.\\\\nFigure 5. Deploying a project directly from its Git repository\\\\n25\\\\nThis particular example doesn’t require more configurations than the ones shown on the screen;\\\\nclick the \u001b[0m\u001b[32m[\u001b[0m\u001b[32m\\\\u2009Create\\\\u2009\u001b[0m\u001b[32m]\u001b[0m\u001b[32m button.\\\\nAfter a few seconds, you will see your application running on the \"Topology\" screen. OpenShift will\\\\ndownload the source code and trigger your project’s build. Click on the Topology screen icon to see\\\\nthe \"Build\" section, indicating that a build is running. The compilation and deployment of your\\\\napplication can take some time, depending on the complexity of the source code and the\\\\nprogramming language used.\\\\nOnce the build has finished, on the same pane, you will see a route available under the \"Routes\"\\\\nsection. Click on it, and you will see your application in action.\\\\n10.2. Container Registry\\\\nOpenShift has built your application source code, and the product of this build process is a\\\\ncontainer. You can see the container that OpenShift made for you on the \"Administrator\"\\\\nperspective, selecting the \"Builds\" menu and then the \"ImageStreams\" menu entry.\\\\nOpenShift includes a container registry; developers can use it as any other registry from outside the\\\\ncluster. Let us use \"podman\" to access the container registry and run the container locally on your\\\\nworkstation.\\\\nUsers must have the \"registry-editor\" and the \"system:image-builder\" roles to access the container\\\\nregistry. Since we’re connected to the Web Console using the \"kubeadmin\" user, we can provide\\\\nthose roles directly from the user interface without using the command line.\\\\nNavigate to the \"User Management\" section and select \"RoleBindings.\" Click on the \u001b[0m\u001b[32m[\u001b[0m\u001b[32m\\\\u2009Create\\\\nbinding\\\\u2009\u001b[0m\u001b[32m]\u001b[0m\u001b[32m button, and fill the form using the following values:\\\\n• Name: developer-sourcecode-registry-editor\\\\n• Namespace: sourcecode\\\\n• Role name: registry-editor\\\\n• Subject: User\\\\n• Subject name: developer\\\\nDo the same for the \"system:image-builder\" role, using a different \"Name\" field\\\\n\\', \u001b[0m\u001b[32mtype\u001b[0m\u001b[32m=\\'text\\'\u001b[0m\u001b[32m)\u001b[0m\u001b[32m, TextContentItem\u001b[0m\u001b[32m(\u001b[0m\u001b[32mtext\u001b[0m\u001b[32m=\\'Result 4:\\\\nDocument_id:num-0\\\\nContent: .\\\\nThese characteristics set OpenShift apart as an excellent Kubernetes platform for enterprise users.\\\\nThe latest version of OpenShift available at the time of this writing is 4.12.\\\\n3.2. Is Red Hat OpenShift Open Source?\\\\nRed Hat OpenShift is a commercial product based on an open-source project called OKD. This\\\\nacronym means \" OpenShift Kubernetes Distribution\" and is publicly available for everyone to\\\\ninspect and contribute. Like the upstream Kubernetes project, OKD developers use the Go\\\\nprogramming language.\\\\n3.3. How can I run OpenShift?\\\\nToday, Red Hat OpenShift is available through various mechanisms and formats:\\\\n• DevOps teams can install it in their data centers \"on-premise.\"\\\\n• Major hyperscalers such as AWS, Azure, Google Cloud Platform, and IBM Cloud offer managed\\\\nRed Hat OpenShift installations.\\\\n• Developers can either run OpenShift locally on their workstations using Red Hat OpenShift\\\\nLocal, also known as CRC or \"Code-Ready Containers\"\\\\n• They can also request a 30-day trial OpenShift cluster, offered by Red Hat, at no charge, for\\\\ntesting and evaluation purposes.\\\\nRed Hat OpenShift is an integrated Platform-as-a-Service for enterprise users based on Kubernetes.\\\\nIt is tightly integrated with advanced security settings, developer tooling, and monitoring\\\\nmechanisms, allowing DevOps teams to be more productive.\\\\n8\\\\nChapter 4. OpenShift-only Custom Resource\\\\nDefinitions\\\\nRed Hat OpenShift is a complete DevOps platform extending Kubernetes in various ways. It bundles\\\\na constellation of Custom Resource Definitions \u001b[0m\u001b[32m(\u001b[0m\u001b[32mCRDs\u001b[0m\u001b[32m)\u001b[0m\u001b[32m to make the life of developers and cluster\\\\nadministrators easier.\\\\nLet us talk first about the CRDs only available on OpenShift.\\\\n4.1. Project\\\\nAn OpenShift Project is similar to a Kubernetes namespace, but more tightly integrated into the\\\\nsecurity system of OpenShift through additional annotations.\\\\napiVersion: project.openshift.io/v1\\\\nkind: Project\\\\nmetadata:\\\\n\\\\xa0 name: linkedin-learning-project\\\\n\\\\xa0 annotations:\\\\n\\\\xa0 openshift.io/description: \"Project description\"\\\\n\\\\xa0 openshift.io/display-name: \"Display name\"\\\\n4.2. Route\\\\nThe OpenShift Route object was one of the primary inspirations during the development of the\\\\nIngress object. In OpenShift, Ingress and Route objects work together to ensure your applications\\\\nare available outside the cluster.\\\\napiVersion: route.openshift.io/v1\\\\nkind: Route\\\\nmetadata:\\\\n\\\\xa0 name: my-route\\\\nspec:\\\\n\\\\xa0 host:\\\\n\\', \u001b[0m\u001b[32mtype\u001b[0m\u001b[32m=\\'text\\'\u001b[0m\u001b[32m)\u001b[0m\u001b[32m, TextContentItem\u001b[0m\u001b[32m(\u001b[0m\u001b[32mtext\u001b[0m\u001b[32m=\\'Result 5:\\\\nDocument_id:num-0\\\\nContent: _02 branch of the GitHub\\\\nrepository for this course.\\\\nThe whole point of OpenShift is to be able to deploy, run, and monitor containerized applications.\\\\nDevOps engineers can deploy containers in OpenShift clusters through various means, for example:\\\\n• Using a YAML manifest.\\\\n• Deploying a single container using the web console.\\\\n• Building a project stored in a Git repository anywhere on the Internet and deploying the\\\\nresulting container.\\\\n• Using the integrated CI/CD pipelines.\\\\n• Using the odo tool together with \"Devfiles.\"\\\\nEach approach has pros and cons; in this chapter, we will review how to use the web console and\\\\nhow to use the odo tool.\\\\n9.1. Deploying Applications with the Web Console\\\\nDevOps engineers can deploy applications immediately using the Web Console. Launch your CRC\\\\ninstance and open the web console in your preferred web browser. The URL of the OpenShift web\\\\nconsole is \"https://console-openshift-console.apps-crc.testing.\"\\\\nThe OpenShift Web Console offers two major perspectives:\\\\n• The \"Administrator\" perspective.\\\\n• And the \"Developer\" perspective.\\\\nFor this explanation, select the \"Developer\" perspective.\\\\nThe first time you open the Developer perspective, a popup invites you to follow a user interface\\\\ntour.\\\\nOn the left-hand side, the perspective menu shows an entry titled \"Add,\" which, as the name\\\\nimplies, provides various mechanisms to deploy applications on an OpenShift cluster.\\\\nThe \"Add\" screen shows the various ways DevOps engineers can deploy applications on a cluster:\\\\n• Using the Developer Catalog, browsing and choosing among a long list of available databases,\\\\nmessage queues, and other valuable components to build applications, or entering your\\\\npreferred Helm chart repository to extend the catalog.\\\\n• Specifying the URL to a specific container on any standard container registry.\\\\n• Specifying the URL of a source code project stored on a Git repository, for example, but not\\\\n22\\\\nlimited to GitHub, GitLab, Gitea, or other locations.\\\\n• Importing YAML directly or even a JAR file with a Java application.\\\\nLet us select the \"Container Image\" option, where we can specify the URL of a ready-to-use\\\\ncontainer.\\\\nEnter the URL of the container on the field, and click on the \u001b[0m\u001b[32m[\u001b[0m\u001b[32m\\\\u2009Create\\\\u2009\u001b[0m\u001b[32m]\u001b[0m\u001b[32m button at the bottom of the\\\\npage. You do not need to change any other value on the form.\\\\nA few seconds later, depending on the size of the container and the speed of your Internet\\\\nconnection,\\\\n\\', \u001b[0m\u001b[32mtype\u001b[0m\u001b[32m=\\'text\\'\u001b[0m\u001b[32m)\u001b[0m\u001b[32m, TextContentItem\u001b[0m\u001b[32m(\u001b[0m\u001b[32mtext\u001b[0m\u001b[32m=\\'END of knowledge_search tool results.\\\\n\\', \u001b[0m\u001b[32mtype\u001b[0m\u001b[32m=\\'text\\'\u001b[0m\u001b[32m)\u001b[0m\u001b[32m, TextContentItem\u001b[0m\u001b[32m(\u001b[0m\u001b[32mtext\u001b[0m\u001b[32m=\\'The above results were retrieved to help answer the user\\\\\\'s query: \"OpenShift installation\". Use them as supporting information only in answering this query.\\\\n\\', \u001b[0m\u001b[32mtype\u001b[0m\u001b[32m=\\'text\\'\u001b[0m\u001b[32m)\u001b[0m\u001b[32m]\u001b[0m\u001b[32mTo install OpenShift, you can follow these steps:\\n\\n1. Check the system requirements for OpenShift Local, which is a version of OpenShift that can be installed on a local machine. The requirements include a recent Intel CPU with at least four physical cores, 16 GB of RAM, and 35 GB of free disk space.\\n2. Download the latest version of OpenShift Local from the official Red Hat website.\\n3. Unzip the downloaded file and run the `crc setup` command to prepare the installation.\\n4. Launch the `crc start` command to start the installation process, which may take around 20 minutes.\\n5. Once the installation is complete, access the OpenShift Web Console using the `crc console` command, which will open your default browser.\\n6. Log in to the Web Console using the developer username and password, or use the `crc console --credentials` command to find the credentials required to log in as the kubeadmin user.\\n7. You can then deploy applications using the web console or using the odo tool.\\n\\nNote that OpenShift is also available through various mechanisms and formats, including installation in data centers, managed installations by hyperscalers, and trial clusters offered by Red Hat.'\u001b[0m\n" ] }, "metadata": {}, @@ -455,19 +526,15 @@ "🤖 Model Response:\n", "\u001b[35mTo install OpenShift, you can follow these steps:\n", "\n", - "1. **Install on your data center (on-premise):** You can install OpenShift in your own data center. This involves setting up a Kubernetes cluster and then deploying OpenShift on top of it. Detailed instructions can be found in the official Red Hat OpenShift documentation.\n", - "\n", - "2. **Use managed services from hyperscalers:** Major cloud providers like AWS, Azure, Google Cloud Platform, and IBM Cloud offer managed OpenShift services. You can provision an OpenShift cluster through their respective web interfaces.\n", - "\n", - "3. **Run OpenShift Locally on your workstation:** Red Hat OpenShift Local, also known as CodeReady Containers (CRC), allows you to run OpenShift on your local machine. Here's how:\n", - " - Download the latest release of OpenShift Local and the \"pull secret\" file from the official Red Hat website.\n", - " - Unzip the downloaded file and run the command `crc setup` in your terminal to prepare your OpenShift Local instance.\n", - " - After the setup is complete, start OpenShift Local with the command `crc start`. This process can take around 20 minutes.\n", - " - Access the OpenShift Web Console with the command `crc console`. The default login credentials are the developer username and password for a low-privilege user, and the kubeadmin user uses a random-generated password. You can find the kubeadmin credentials with the command `crc console --credentials`.\n", - "\n", - "Remember, running OpenShift Locally requires a recent Intel CPU (or Apple Silicon for Macs), at least 4 physical cores, 16 GB of RAM, and 35 GB of free disk space.\n", + "1. Check the system requirements for OpenShift Local, which is a version of OpenShift that can be installed on a local machine. The requirements include a recent Intel CPU with at least four physical cores, 16 GB of RAM, and 35 GB of free disk space.\n", + "2. Download the latest version of OpenShift Local from the official Red Hat website.\n", + "3. Unzip the downloaded file and run the `crc setup` command to prepare the installation.\n", + "4. Launch the `crc start` command to start the installation process, which may take around 20 minutes.\n", + "5. Once the installation is complete, access the OpenShift Web Console using the `crc console` command, which will open your default browser.\n", + "6. Log in to the Web Console using the developer username and password, or use the `crc console --credentials` command to find the credentials required to log in as the kubeadmin user.\n", + "7. You can then deploy applications using the web console or using the odo tool.\n", "\n", - "For more detailed and up-to-date information, always refer to the official Red Hat OpenShift documentation.\n", + "Note that OpenShift is also available through various mechanisms and formats, including installation in data centers, managed installations by hyperscalers, and trial clusters offered by Red Hat.\n", "\u001b[0m\n", "========== Query processing completed ========== \n", "\n" @@ -516,7 +583,7 @@ ], "metadata": { "kernelspec": { - "display_name": "Python 3 (ipykernel)", + "display_name": "venv7", "language": "python", "name": "python3" }, @@ -530,7 +597,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.10.16" + "version": "3.13.3" } }, "nbformat": 4, diff --git a/demos/a2a_llama_stack/notebooks/A2A_Multi_Agent.ipynb b/demos/a2a_llama_stack/notebooks/A2A_Multi_Agent.ipynb new file mode 100644 index 00000000..b8bee553 --- /dev/null +++ b/demos/a2a_llama_stack/notebooks/A2A_Multi_Agent.ipynb @@ -0,0 +1,1047 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "c55ab371", + "metadata": {}, + "source": [ + "# A2A Multi Agent - Quick-Start Notebook\n", + "\n", + "Welcome to the A2A Multi-Agent Quick-Start notebook! This guide will show you how to set up and interact with a multi-agent system using the Agent-to-Agent (A2A) protocol within the Llama Stack environment.\n", + "\n", + "## Overview\n", + "\n", + "This notebook guides you through the following key steps to set up and interact with a multi-agent system:\n", + "\n", + "1. **Setting up the Environment**: Preparing your Python environment by installing necessary libraries and configuring asynchronous operations.\n", + "\n", + "2. **Agent Management**: Understanding how to connect to and manage the different agents within the system.\n", + "\n", + "3. **Defining Agent Task Flow**: Exploring the multi-phase process (planning, execution, and composition) by which agents collaboratively solve a query.\n", + "\n", + "4. **Launching Agent Servers**: Starting the Agent-to-Agent (A2A) orchestrator and skill agent servers.\n", + "\n", + "5. **Interacting with Agents**: Sending questions to the agent team and observing their orchestrated responses.\n", + "\n", + "## Prerequisites\n", + "\n", + "Before you begin, ensure that you have:\n", + "\n", + "* `python_requires >= 3.13`.\n", + "\n", + "* Completed the initial setup as outlined in the [Setup Guide](../../rag_agentic/notebooks/Level0_getting_started_with_Llama_Stack.ipynb) notebook.\n", + "\n", + "## Environment Variables\n", + "\n", + "This notebook is designed to be flexible, allowing you to connect to either a local or a remote Llama Stack instance, and to specify the inference models used by the agents. You can configure these aspects using the following environment variables:\n", + "\n", + "* `REMOTE_BASE_URL`: Set this variable if you intend to connect to a **remote Llama Stack instance**. If this variable is not set, the notebook will default to running with a local instance.\n", + "\n", + "* `INFERENCE_MODEL_ID`: Define this variable to specify the default Large Language Model (LLM) that agents should use for inference. We recommend using `llama3.1:8b-instruct-fp16` for optimal performance.\n", + "\n", + "**Note on Agent-Specific Models:**\n", + "If you require different inference models for individual agents, you can achieve this by directly opening and modifying the `__main__.py` file within each respective agent's folder (e.g. `demos/a2a_llama_stack/agents/a2a_custom_tools/__main__.py`)." + ] + }, + { + "cell_type": "markdown", + "id": "b853d0ba", + "metadata": {}, + "source": [ + "## 1. Setting Up the Notebook Environment\n", + "\n", + "We'll start by setting up the necessary environment and installing the required packages to enable A2A communication." + ] + }, + { + "cell_type": "markdown", + "id": "2d1a1075", + "metadata": {}, + "source": [ + "We'll install the official [A2A sample implementation by Google](https://github.com/google/A2A/tree/main/samples/python), the Llama Stack client, and other essential packages for asynchronous operations in Jupyter. Run the following command:" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "eb5bce08", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Collecting git+https://github.com/google/A2A.git#subdirectory=samples/python\n", + " Cloning https://github.com/google/A2A.git to /private/var/folders/p4/635191ns4599kwjkqt12kwd80000gn/T/pip-req-build-11e356th\n", + " Running command git clone --filter=blob:none --quiet https://github.com/google/A2A.git /private/var/folders/p4/635191ns4599kwjkqt12kwd80000gn/T/pip-req-build-11e356th\n", + " Resolved https://github.com/google/A2A.git to commit 081fa20bdfede24922c49e8e56fcdfbee0db0c28\n", + " Installing build dependencies ... \u001b[?25ldone\n", + "\u001b[?25h Getting requirements to build wheel ... \u001b[?25ldone\n", + "\u001b[?25h Preparing metadata (pyproject.toml) ... \u001b[?25ldone\n", + "\u001b[?25hRequirement already satisfied: a2a-sdk>=0.2.1 in /Users/kcogan/Documents/llama-stack-on-ocp/venv8/lib/python3.13/site-packages (from a2a-samples==0.1.0) (0.2.1)\n", + "Requirement already satisfied: httpx-sse>=0.4.0 in /Users/kcogan/Documents/llama-stack-on-ocp/venv8/lib/python3.13/site-packages (from a2a-samples==0.1.0) (0.4.0)\n", + "Requirement already satisfied: httpx>=0.28.1 in /Users/kcogan/Documents/llama-stack-on-ocp/venv8/lib/python3.13/site-packages (from a2a-samples==0.1.0) (0.28.1)\n", + "Requirement already satisfied: jwcrypto>=1.5.6 in /Users/kcogan/Documents/llama-stack-on-ocp/venv8/lib/python3.13/site-packages (from a2a-samples==0.1.0) (1.5.6)\n", + "Requirement already satisfied: pydantic>=2.10.6 in /Users/kcogan/Documents/llama-stack-on-ocp/venv8/lib/python3.13/site-packages (from a2a-samples==0.1.0) (2.11.4)\n", + "Requirement already satisfied: pyjwt>=2.10.1 in /Users/kcogan/Documents/llama-stack-on-ocp/venv8/lib/python3.13/site-packages (from a2a-samples==0.1.0) (2.10.1)\n", + "Requirement already satisfied: sse-starlette>=2.2.1 in /Users/kcogan/Documents/llama-stack-on-ocp/venv8/lib/python3.13/site-packages (from a2a-samples==0.1.0) (2.3.5)\n", + "Requirement already satisfied: starlette>=0.46.1 in /Users/kcogan/Documents/llama-stack-on-ocp/venv8/lib/python3.13/site-packages (from a2a-samples==0.1.0) (0.46.2)\n", + "Requirement already satisfied: typing-extensions>=4.12.2 in /Users/kcogan/Documents/llama-stack-on-ocp/venv8/lib/python3.13/site-packages (from a2a-samples==0.1.0) (4.13.2)\n", + "Requirement already satisfied: uvicorn>=0.34.0 in /Users/kcogan/Documents/llama-stack-on-ocp/venv8/lib/python3.13/site-packages (from a2a-samples==0.1.0) (0.34.2)\n", + "Requirement already satisfied: opentelemetry-api>=1.33.0 in /Users/kcogan/Documents/llama-stack-on-ocp/venv8/lib/python3.13/site-packages (from a2a-sdk>=0.2.1->a2a-samples==0.1.0) (1.33.1)\n", + "Requirement already satisfied: opentelemetry-sdk>=1.33.0 in /Users/kcogan/Documents/llama-stack-on-ocp/venv8/lib/python3.13/site-packages (from a2a-sdk>=0.2.1->a2a-samples==0.1.0) (1.33.1)\n", + "Requirement already satisfied: anyio in /Users/kcogan/Documents/llama-stack-on-ocp/venv8/lib/python3.13/site-packages (from httpx>=0.28.1->a2a-samples==0.1.0) (4.9.0)\n", + "Requirement already satisfied: certifi in /Users/kcogan/Documents/llama-stack-on-ocp/venv8/lib/python3.13/site-packages (from httpx>=0.28.1->a2a-samples==0.1.0) (2025.4.26)\n", + "Requirement already satisfied: httpcore==1.* in /Users/kcogan/Documents/llama-stack-on-ocp/venv8/lib/python3.13/site-packages (from httpx>=0.28.1->a2a-samples==0.1.0) (1.0.9)\n", + "Requirement already satisfied: idna in /Users/kcogan/Documents/llama-stack-on-ocp/venv8/lib/python3.13/site-packages (from httpx>=0.28.1->a2a-samples==0.1.0) (3.10)\n", + "Requirement already satisfied: h11>=0.16 in /Users/kcogan/Documents/llama-stack-on-ocp/venv8/lib/python3.13/site-packages (from httpcore==1.*->httpx>=0.28.1->a2a-samples==0.1.0) (0.16.0)\n", + "Requirement already satisfied: cryptography>=3.4 in /Users/kcogan/Documents/llama-stack-on-ocp/venv8/lib/python3.13/site-packages (from jwcrypto>=1.5.6->a2a-samples==0.1.0) (45.0.2)\n", + "Requirement already satisfied: annotated-types>=0.6.0 in /Users/kcogan/Documents/llama-stack-on-ocp/venv8/lib/python3.13/site-packages (from pydantic>=2.10.6->a2a-samples==0.1.0) (0.7.0)\n", + "Requirement already satisfied: pydantic-core==2.33.2 in /Users/kcogan/Documents/llama-stack-on-ocp/venv8/lib/python3.13/site-packages (from pydantic>=2.10.6->a2a-samples==0.1.0) (2.33.2)\n", + "Requirement already satisfied: typing-inspection>=0.4.0 in /Users/kcogan/Documents/llama-stack-on-ocp/venv8/lib/python3.13/site-packages (from pydantic>=2.10.6->a2a-samples==0.1.0) (0.4.0)\n", + "Requirement already satisfied: click>=7.0 in /Users/kcogan/Documents/llama-stack-on-ocp/venv8/lib/python3.13/site-packages (from uvicorn>=0.34.0->a2a-samples==0.1.0) (8.2.0)\n", + "Requirement already satisfied: sniffio>=1.1 in /Users/kcogan/Documents/llama-stack-on-ocp/venv8/lib/python3.13/site-packages (from anyio->httpx>=0.28.1->a2a-samples==0.1.0) (1.3.1)\n", + "Requirement already satisfied: cffi>=1.14 in /Users/kcogan/Documents/llama-stack-on-ocp/venv8/lib/python3.13/site-packages (from cryptography>=3.4->jwcrypto>=1.5.6->a2a-samples==0.1.0) (1.17.1)\n", + "Requirement already satisfied: deprecated>=1.2.6 in /Users/kcogan/Documents/llama-stack-on-ocp/venv8/lib/python3.13/site-packages (from opentelemetry-api>=1.33.0->a2a-sdk>=0.2.1->a2a-samples==0.1.0) (1.2.18)\n", + "Requirement already satisfied: importlib-metadata<8.7.0,>=6.0 in /Users/kcogan/Documents/llama-stack-on-ocp/venv8/lib/python3.13/site-packages (from opentelemetry-api>=1.33.0->a2a-sdk>=0.2.1->a2a-samples==0.1.0) (8.6.1)\n", + "Requirement already satisfied: opentelemetry-semantic-conventions==0.54b1 in /Users/kcogan/Documents/llama-stack-on-ocp/venv8/lib/python3.13/site-packages (from opentelemetry-sdk>=1.33.0->a2a-sdk>=0.2.1->a2a-samples==0.1.0) (0.54b1)\n", + "Requirement already satisfied: pycparser in /Users/kcogan/Documents/llama-stack-on-ocp/venv8/lib/python3.13/site-packages (from cffi>=1.14->cryptography>=3.4->jwcrypto>=1.5.6->a2a-samples==0.1.0) (2.22)\n", + "Requirement already satisfied: wrapt<2,>=1.10 in /Users/kcogan/Documents/llama-stack-on-ocp/venv8/lib/python3.13/site-packages (from deprecated>=1.2.6->opentelemetry-api>=1.33.0->a2a-sdk>=0.2.1->a2a-samples==0.1.0) (1.17.2)\n", + "Requirement already satisfied: zipp>=3.20 in /Users/kcogan/Documents/llama-stack-on-ocp/venv8/lib/python3.13/site-packages (from importlib-metadata<8.7.0,>=6.0->opentelemetry-api>=1.33.0->a2a-sdk>=0.2.1->a2a-samples==0.1.0) (3.21.0)\n", + "\n", + "\u001b[1m[\u001b[0m\u001b[34;49mnotice\u001b[0m\u001b[1;39;49m]\u001b[0m\u001b[39;49m A new release of pip is available: \u001b[0m\u001b[31;49m25.0.1\u001b[0m\u001b[39;49m -> \u001b[0m\u001b[32;49m25.1.1\u001b[0m\n", + "\u001b[1m[\u001b[0m\u001b[34;49mnotice\u001b[0m\u001b[1;39;49m]\u001b[0m\u001b[39;49m To update, run: \u001b[0m\u001b[32;49mpip install --upgrade pip\u001b[0m\n", + "Requirement already satisfied: llama_stack_client in /Users/kcogan/Documents/llama-stack-on-ocp/venv8/lib/python3.13/site-packages (0.2.7)\n", + "Requirement already satisfied: asyncclick in /Users/kcogan/Documents/llama-stack-on-ocp/venv8/lib/python3.13/site-packages (8.1.8.0)\n", + "Requirement already satisfied: nest_asyncio in /Users/kcogan/Documents/llama-stack-on-ocp/venv8/lib/python3.13/site-packages (1.6.0)\n", + "Requirement already satisfied: ipywidgets in /Users/kcogan/Documents/llama-stack-on-ocp/venv8/lib/python3.13/site-packages (8.1.7)\n", + "Requirement already satisfied: dotenv in /Users/kcogan/Documents/llama-stack-on-ocp/venv8/lib/python3.13/site-packages (0.9.9)\n", + "Requirement already satisfied: anyio<5,>=3.5.0 in /Users/kcogan/Documents/llama-stack-on-ocp/venv8/lib/python3.13/site-packages (from llama_stack_client) (4.9.0)\n", + "Requirement already satisfied: click in /Users/kcogan/Documents/llama-stack-on-ocp/venv8/lib/python3.13/site-packages (from llama_stack_client) (8.2.0)\n", + "Requirement already satisfied: distro<2,>=1.7.0 in /Users/kcogan/Documents/llama-stack-on-ocp/venv8/lib/python3.13/site-packages (from llama_stack_client) (1.9.0)\n", + "Requirement already satisfied: httpx<1,>=0.23.0 in /Users/kcogan/Documents/llama-stack-on-ocp/venv8/lib/python3.13/site-packages (from llama_stack_client) (0.28.1)\n", + "Requirement already satisfied: pandas in /Users/kcogan/Documents/llama-stack-on-ocp/venv8/lib/python3.13/site-packages (from llama_stack_client) (2.2.3)\n", + "Requirement already satisfied: prompt-toolkit in /Users/kcogan/Documents/llama-stack-on-ocp/venv8/lib/python3.13/site-packages (from llama_stack_client) (3.0.51)\n", + "Requirement already satisfied: pyaml in /Users/kcogan/Documents/llama-stack-on-ocp/venv8/lib/python3.13/site-packages (from llama_stack_client) (25.1.0)\n", + "Requirement already satisfied: pydantic<3,>=1.9.0 in /Users/kcogan/Documents/llama-stack-on-ocp/venv8/lib/python3.13/site-packages (from llama_stack_client) (2.11.4)\n", + "Requirement already satisfied: rich in /Users/kcogan/Documents/llama-stack-on-ocp/venv8/lib/python3.13/site-packages (from llama_stack_client) (14.0.0)\n", + "Requirement already satisfied: sniffio in /Users/kcogan/Documents/llama-stack-on-ocp/venv8/lib/python3.13/site-packages (from llama_stack_client) (1.3.1)\n", + "Requirement already satisfied: termcolor in /Users/kcogan/Documents/llama-stack-on-ocp/venv8/lib/python3.13/site-packages (from llama_stack_client) (3.1.0)\n", + "Requirement already satisfied: tqdm in /Users/kcogan/Documents/llama-stack-on-ocp/venv8/lib/python3.13/site-packages (from llama_stack_client) (4.67.1)\n", + "Requirement already satisfied: typing-extensions<5,>=4.7 in /Users/kcogan/Documents/llama-stack-on-ocp/venv8/lib/python3.13/site-packages (from llama_stack_client) (4.13.2)\n", + "Requirement already satisfied: comm>=0.1.3 in /Users/kcogan/Documents/llama-stack-on-ocp/venv8/lib/python3.13/site-packages (from ipywidgets) (0.2.2)\n", + "Requirement already satisfied: ipython>=6.1.0 in /Users/kcogan/Documents/llama-stack-on-ocp/venv8/lib/python3.13/site-packages (from ipywidgets) (9.2.0)\n", + "Requirement already satisfied: traitlets>=4.3.1 in /Users/kcogan/Documents/llama-stack-on-ocp/venv8/lib/python3.13/site-packages (from ipywidgets) (5.14.3)\n", + "Requirement already satisfied: widgetsnbextension~=4.0.14 in /Users/kcogan/Documents/llama-stack-on-ocp/venv8/lib/python3.13/site-packages (from ipywidgets) (4.0.14)\n", + "Requirement already satisfied: jupyterlab_widgets~=3.0.15 in /Users/kcogan/Documents/llama-stack-on-ocp/venv8/lib/python3.13/site-packages (from ipywidgets) (3.0.15)\n", + "Requirement already satisfied: python-dotenv in /Users/kcogan/Documents/llama-stack-on-ocp/venv8/lib/python3.13/site-packages (from dotenv) (1.1.0)\n", + "Requirement already satisfied: idna>=2.8 in /Users/kcogan/Documents/llama-stack-on-ocp/venv8/lib/python3.13/site-packages (from anyio<5,>=3.5.0->llama_stack_client) (3.10)\n", + "Requirement already satisfied: certifi in /Users/kcogan/Documents/llama-stack-on-ocp/venv8/lib/python3.13/site-packages (from httpx<1,>=0.23.0->llama_stack_client) (2025.4.26)\n", + "Requirement already satisfied: httpcore==1.* in /Users/kcogan/Documents/llama-stack-on-ocp/venv8/lib/python3.13/site-packages (from httpx<1,>=0.23.0->llama_stack_client) (1.0.9)\n", + "Requirement already satisfied: h11>=0.16 in /Users/kcogan/Documents/llama-stack-on-ocp/venv8/lib/python3.13/site-packages (from httpcore==1.*->httpx<1,>=0.23.0->llama_stack_client) (0.16.0)\n", + "Requirement already satisfied: decorator in /Users/kcogan/Documents/llama-stack-on-ocp/venv8/lib/python3.13/site-packages (from ipython>=6.1.0->ipywidgets) (5.2.1)\n", + "Requirement already satisfied: ipython-pygments-lexers in /Users/kcogan/Documents/llama-stack-on-ocp/venv8/lib/python3.13/site-packages (from ipython>=6.1.0->ipywidgets) (1.1.1)\n", + "Requirement already satisfied: jedi>=0.16 in /Users/kcogan/Documents/llama-stack-on-ocp/venv8/lib/python3.13/site-packages (from ipython>=6.1.0->ipywidgets) (0.19.2)\n", + "Requirement already satisfied: matplotlib-inline in /Users/kcogan/Documents/llama-stack-on-ocp/venv8/lib/python3.13/site-packages (from ipython>=6.1.0->ipywidgets) (0.1.7)\n", + "Requirement already satisfied: pexpect>4.3 in /Users/kcogan/Documents/llama-stack-on-ocp/venv8/lib/python3.13/site-packages (from ipython>=6.1.0->ipywidgets) (4.9.0)\n", + "Requirement already satisfied: pygments>=2.4.0 in /Users/kcogan/Documents/llama-stack-on-ocp/venv8/lib/python3.13/site-packages (from ipython>=6.1.0->ipywidgets) (2.19.1)\n", + "Requirement already satisfied: stack_data in /Users/kcogan/Documents/llama-stack-on-ocp/venv8/lib/python3.13/site-packages (from ipython>=6.1.0->ipywidgets) (0.6.3)\n", + "Requirement already satisfied: wcwidth in /Users/kcogan/Documents/llama-stack-on-ocp/venv8/lib/python3.13/site-packages (from prompt-toolkit->llama_stack_client) (0.2.13)\n", + "Requirement already satisfied: annotated-types>=0.6.0 in /Users/kcogan/Documents/llama-stack-on-ocp/venv8/lib/python3.13/site-packages (from pydantic<3,>=1.9.0->llama_stack_client) (0.7.0)\n", + "Requirement already satisfied: pydantic-core==2.33.2 in /Users/kcogan/Documents/llama-stack-on-ocp/venv8/lib/python3.13/site-packages (from pydantic<3,>=1.9.0->llama_stack_client) (2.33.2)\n", + "Requirement already satisfied: typing-inspection>=0.4.0 in /Users/kcogan/Documents/llama-stack-on-ocp/venv8/lib/python3.13/site-packages (from pydantic<3,>=1.9.0->llama_stack_client) (0.4.0)\n", + "Requirement already satisfied: numpy>=1.26.0 in /Users/kcogan/Documents/llama-stack-on-ocp/venv8/lib/python3.13/site-packages (from pandas->llama_stack_client) (2.2.6)\n", + "Requirement already satisfied: python-dateutil>=2.8.2 in /Users/kcogan/Documents/llama-stack-on-ocp/venv8/lib/python3.13/site-packages (from pandas->llama_stack_client) (2.9.0.post0)\n", + "Requirement already satisfied: pytz>=2020.1 in /Users/kcogan/Documents/llama-stack-on-ocp/venv8/lib/python3.13/site-packages (from pandas->llama_stack_client) (2025.2)\n", + "Requirement already satisfied: tzdata>=2022.7 in /Users/kcogan/Documents/llama-stack-on-ocp/venv8/lib/python3.13/site-packages (from pandas->llama_stack_client) (2025.2)\n", + "Requirement already satisfied: PyYAML in /Users/kcogan/Documents/llama-stack-on-ocp/venv8/lib/python3.13/site-packages (from pyaml->llama_stack_client) (6.0.2)\n", + "Requirement already satisfied: markdown-it-py>=2.2.0 in /Users/kcogan/Documents/llama-stack-on-ocp/venv8/lib/python3.13/site-packages (from rich->llama_stack_client) (3.0.0)\n", + "Requirement already satisfied: parso<0.9.0,>=0.8.4 in /Users/kcogan/Documents/llama-stack-on-ocp/venv8/lib/python3.13/site-packages (from jedi>=0.16->ipython>=6.1.0->ipywidgets) (0.8.4)\n", + "Requirement already satisfied: mdurl~=0.1 in /Users/kcogan/Documents/llama-stack-on-ocp/venv8/lib/python3.13/site-packages (from markdown-it-py>=2.2.0->rich->llama_stack_client) (0.1.2)\n", + "Requirement already satisfied: ptyprocess>=0.5 in /Users/kcogan/Documents/llama-stack-on-ocp/venv8/lib/python3.13/site-packages (from pexpect>4.3->ipython>=6.1.0->ipywidgets) (0.7.0)\n", + "Requirement already satisfied: six>=1.5 in /Users/kcogan/Documents/llama-stack-on-ocp/venv8/lib/python3.13/site-packages (from python-dateutil>=2.8.2->pandas->llama_stack_client) (1.17.0)\n", + "Requirement already satisfied: executing>=1.2.0 in /Users/kcogan/Documents/llama-stack-on-ocp/venv8/lib/python3.13/site-packages (from stack_data->ipython>=6.1.0->ipywidgets) (2.2.0)\n", + "Requirement already satisfied: asttokens>=2.1.0 in /Users/kcogan/Documents/llama-stack-on-ocp/venv8/lib/python3.13/site-packages (from stack_data->ipython>=6.1.0->ipywidgets) (3.0.0)\n", + "Requirement already satisfied: pure-eval in /Users/kcogan/Documents/llama-stack-on-ocp/venv8/lib/python3.13/site-packages (from stack_data->ipython>=6.1.0->ipywidgets) (0.2.3)\n", + "\n", + "\u001b[1m[\u001b[0m\u001b[34;49mnotice\u001b[0m\u001b[1;39;49m]\u001b[0m\u001b[39;49m A new release of pip is available: \u001b[0m\u001b[31;49m25.0.1\u001b[0m\u001b[39;49m -> \u001b[0m\u001b[32;49m25.1.1\u001b[0m\n", + "\u001b[1m[\u001b[0m\u001b[34;49mnotice\u001b[0m\u001b[1;39;49m]\u001b[0m\u001b[39;49m To update, run: \u001b[0m\u001b[32;49mpip install --upgrade pip\u001b[0m\n" + ] + } + ], + "source": [ + "! pip install \"git+https://github.com/google/A2A.git#subdirectory=samples/python\"\n", + "! pip install llama_stack_client asyncclick nest_asyncio ipywidgets dotenv" + ] + }, + { + "cell_type": "markdown", + "id": "3f49992c", + "metadata": {}, + "source": [ + "Next, we'll add the necessary paths to `sys.path` to ensure we can import the required libraries later in the notebook." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "c49b3fcf", + "metadata": {}, + "outputs": [], + "source": [ + "import sys\n", + "# the path of the A2A library\n", + "sys.path.append('./A2A/samples/python')\n", + "# the path to our own utils\n", + "sys.path.append('../..')" + ] + }, + { + "cell_type": "markdown", + "id": "36e1216b", + "metadata": {}, + "source": [ + "We will now proceed with importing all the necessary Python libraries and modules for the notebook." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "95a70138", + "metadata": {}, + "outputs": [], + "source": [ + "from common.server import A2AServer\n", + "from common.types import AgentCard, AgentSkill, AgentCapabilities\n", + "from a2a_llama_stack.A2ATool import A2ATool\n", + "from a2a_llama_stack.task_manager import AgentTaskManager\n", + "\n", + "# for asynchronously serving the A2A agent\n", + "import os\n", + "import time\n", + "import json\n", + "import logging\n", + "from dotenv import load_dotenv\n", + "import urllib.parse\n", + "from uuid import uuid4\n", + "from typing import Any, Dict, List, Tuple\n", + "import subprocess\n", + "import socket\n", + "import asyncio\n", + "import nest_asyncio\n", + "import threading\n", + "import concurrent.futures as cf\n", + "\n", + "\n", + "\n", + "# Importing custom modules from the common package\n", + "from common.client import A2AClient, A2ACardResolver\n", + "from common.utils.push_notification_auth import PushNotificationReceiverAuth\n", + "from hosts.cli.push_notification_listener import PushNotificationListener" + ] + }, + { + "cell_type": "markdown", + "id": "4943937a", + "metadata": {}, + "source": [ + "Next, we will initialize our environment as described in detail in our [\"Getting Started\" notebook](../../rag_agentic/notebooks/Level0_getting_started_with_Llama_Stack.ipynb). Please refer to it for additional explanations." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "c5253d5c", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Connected to Llama Stack server\n", + "Inference Parameters:\n", + "\tModel: llama3.1:8b-instruct-fp16\n", + "\tSampling Parameters: {'strategy': {'type': 'greedy'}, 'max_tokens': 4096}\n", + "\tstream: False\n" + ] + } + ], + "source": [ + "# for accessing the environment variables\n", + "import os\n", + "from dotenv import load_dotenv\n", + "load_dotenv()\n", + "\n", + "\n", + "base_url = os.getenv(\"REMOTE_BASE_URL\", \"http://localhost:8321\")\n", + " \n", + "print(f\"Connected to Llama Stack server\")\n", + "\n", + "# the model you wish to use that is configured with the Llama Stack server\n", + "model_id = os.getenv(\"INFERENCE_MODEL_ID\", \"llama3.1:8b-instruct-fp16\")\n", + "\n", + "temperature = float(os.getenv(\"TEMPERATURE\", 0.0))\n", + "if temperature > 0.0:\n", + " top_p = float(os.getenv(\"TOP_P\", 0.95))\n", + " strategy = {\"type\": \"top_p\", \"temperature\": temperature, \"top_p\": top_p}\n", + "else:\n", + " strategy = {\"type\": \"greedy\"}\n", + "\n", + "max_tokens = int(os.getenv(\"MAX_TOKENS\", 4096))\n", + "\n", + "# sampling_params will later be used to pass the parameters to Llama Stack Agents/Inference APIs\n", + "sampling_params = {\n", + " \"strategy\": strategy,\n", + " \"max_tokens\": max_tokens,\n", + "}\n", + "\n", + "stream_env = os.getenv(\"STREAM\", \"False\")\n", + "# the Boolean 'stream' parameter will later be passed to Llama Stack Agents/Inference APIs\n", + "# any value non equal to 'False' will be considered as 'True'\n", + "stream = (stream_env != \"False\")\n", + "\n", + "print(f\"Inference Parameters:\\n\\tModel: {model_id}\\n\\tSampling Parameters: {sampling_params}\\n\\tstream: {stream}\")" + ] + }, + { + "cell_type": "markdown", + "id": "5fb3a22f", + "metadata": {}, + "source": [ + "We'll configure basic logging to provide informative output from the agents as they run, which can be very helpful for debugging and understanding the flow." + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "86bde581", + "metadata": {}, + "outputs": [], + "source": [ + "# Configuring basic logging for the application\n", + "logging.basicConfig(level=logging.INFO,\n", + " format=\"%(asctime)s %(levelname)s %(name)s: %(message)s\")\n", + "logger = logging.getLogger(__name__)" + ] + }, + { + "cell_type": "markdown", + "id": "91f8fbb5", + "metadata": {}, + "source": [ + "We need to use `nest_asyncio` to make sure things run smoothly when your Python code tries to do multiple tasks at the same time in Jupyter." + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "62b7e70c", + "metadata": {}, + "outputs": [], + "source": [ + "nest_asyncio.apply()" + ] + }, + { + "cell_type": "markdown", + "id": "1b8ac79e", + "metadata": {}, + "source": [ + "## 2. Understanding the `AgentManager`\n", + "\n", + "The `AgentManager` class is key to connecting with and managing the different agents in our system.\n", + "\n", + "It handles:\n", + "\n", + "* Connecting to your orchestrator agent.\n", + "\n", + "* Connecting to individual skill agents (the ones that perform specific tasks).\n", + "\n", + "* Managing unique session IDs for each connection.\n", + "\n", + "* Using a helper function (`_send_payload`) to send tasks to the agents." + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "e7cea371", + "metadata": {}, + "outputs": [], + "source": [ + "AgentInfo = Tuple[str, Any, A2AClient, str]\n", + "\n", + "class AgentManager:\n", + " def __init__(self, urls: List[str]):\n", + " # first URL is your orchestrator…\n", + " self.orchestrator: AgentInfo = self._make_agent_info(urls[0])\n", + " # …the rest are skill agents, each keyed by skill.id\n", + " self.skills: Dict[str, AgentInfo] = {\n", + " skill.id: info\n", + " for url in urls[1:]\n", + " for info in (self._make_agent_info(url),)\n", + " for skill in info[1].skills\n", + " }\n", + "\n", + " @staticmethod\n", + " def _make_agent_info(url: str) -> AgentInfo:\n", + " card = A2ACardResolver(url).get_agent_card()\n", + " client = A2AClient(agent_card=card)\n", + " session = uuid4().hex\n", + " return url, card, client, session\n", + "\n", + "\n", + "async def _send_payload(client, card, session, payload, streaming: bool) -> str:\n", + " if not streaming:\n", + " res = await client.send_task(payload)\n", + " return res.result.status.message.parts[0].text.strip()\n", + "\n", + " text = \"\"\n", + " async for ev in client.send_task_streaming(payload):\n", + " part = ev.result.status.message.parts[0].text or \"\"\n", + " print(part, end=\"\", flush=True)\n", + " text = part\n", + " print()\n", + " return text" + ] + }, + { + "cell_type": "markdown", + "id": "4ee4d979", + "metadata": {}, + "source": [ + "## 3. Preparing and Sending Tasks to Agents\n", + "\n", + "This section defines functions that format the user's question into a structured message (a JSON payload) that the agents can understand and process. It then uses the `_send_payload` helper from the `AgentManager` to send this task to the appropriate agent." + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "a2f9f439", + "metadata": {}, + "outputs": [], + "source": [ + "def _build_skill_meta(mgr):\n", + " \"\"\"Gather metadata for every skill in all executor cards.\"\"\"\n", + " return [\n", + " {\n", + " \"skill_id\": s.id, \"name\": s.name,\n", + " \"description\": getattr(s, \"description\", None),\n", + " \"inputSchema\": getattr(s, \"inputSchema\", None),\n", + " \"outputSchema\": getattr(s, \"outputSchema\", None),\n", + " }\n", + " for _, card, _, _ in mgr.skills.values()\n", + " for s in card.skills\n", + " ]\n", + "\n", + "async def _send_task_to_agent(mgr, client, card, session, question, push=False, host=None, port=None) -> str:\n", + " \"\"\"Build a card-driven payload (with optional push) and dispatch it.\"\"\"\n", + " # Skill metadata + input parts\n", + " skills = _build_skill_meta(mgr)\n", + " content = {\"skills\": skills, \"question\": question}\n", + " modes = getattr(card, \"acceptedInputModes\", [\"text\"])\n", + " parts = ([{\"type\": \"json\", \"json\": content}]\n", + " if \"json\" in modes\n", + " else [{\"type\": \"text\", \"text\": json.dumps(content)}])\n", + "\n", + " # Optional push URL & auth\n", + " can_push = push and getattr(card.capabilities, \"pushNotifications\", False)\n", + " push_url = (urllib.parse.urljoin(f\"http://{host}:{port}\", \"/notify\")\n", + " if can_push and host and port else None)\n", + " schemes = getattr(card.authentication, \"supportedSchemes\", [\"bearer\"])\n", + "\n", + " # Assemble payload\n", + " payload = {\n", + " \"id\": uuid4().hex,\n", + " \"sessionId\": session,\n", + " \"acceptedOutputModes\": card.defaultOutputModes,\n", + " \"message\": {\"role\": \"user\", \"parts\": parts},\n", + " **({\"pushNotification\": {\"url\": push_url,\n", + " \"authentication\": {\"schemes\": schemes}}}\n", + " if push_url else {})\n", + " }\n", + "\n", + " # Dispatch, letting the card decide streaming vs one-shot\n", + " stream = getattr(card.capabilities, \"streaming\", False)\n", + " return await _send_payload(client, card, session, payload, stream)\n" + ] + }, + { + "cell_type": "markdown", + "id": "a5862103", + "metadata": {}, + "source": [ + "## 4. Defining the Agent Task Flow: Planning, Execution, and Composition\n", + "This section contains the core logic for how the agents work together to answer a question. It's broken down into three distinct phases:\n", + "\n", + "* **Planning:** The orchestrator agent figures out the steps needed to answer the question and which skill agents to use.\n", + "\n", + "* **Execution:** The notebook code calls the necessary skill agents based on the plan and collects their results.\n", + "\n", + "* **Composition:** A final agent combines the results from the skill agents into a human-friendly answer." + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "b86bd386", + "metadata": {}, + "outputs": [], + "source": [ + "async def _planning_phase(agent_manager, question, push, host, port):\n", + " \"\"\"Ask orchestrator for a plan, parse/fix JSON if necessary.\"\"\"\n", + " _, card, client, sess = agent_manager.orchestrator\n", + "\n", + " raw = await _send_task_to_agent(agent_manager, client, card, sess, question, push=push, host=host, port=port)\n", + " print(f\"Raw plan ➡️ {raw}\")\n", + "\n", + " try:\n", + " return json.loads(raw[: raw.rfind(\"]\") + 1])\n", + " except ValueError:\n", + " print(\"\\033[31mPlan parse failed, fixing invalid JSON...\\033[0m\")\n", + " fixer = \"Fix this json to be valid: \" + raw\n", + " fixed = await _send_task_to_agent(agent_manager, client, card, sess, fixer, push=push, host=host, port=port)\n", + " return json.loads(fixed)\n", + "\n", + "\n", + "async def _execution_phase(agent_manager, plan, push, host, port):\n", + " \"\"\"Run each step in the plan via its skill and collect outputs.\"\"\"\n", + " results = []\n", + " for i, step in enumerate(plan, 1):\n", + " sid, inp = step[\"skill_id\"], json.dumps(step.get(\"input\", {}))\n", + " print(f\"➡️ Step {i}: {sid}({inp})\")\n", + "\n", + " info = agent_manager.skills.get(sid)\n", + " if not info:\n", + " print(f\"\\033[31mNo executor for '{sid}', skipping.\\033[0m\")\n", + " results.append({\"skill_id\": sid, \"output\": None})\n", + " continue\n", + "\n", + " _, skill_card, skill_client, skill_sess = info\n", + " out = await _send_task_to_agent(agent_manager, skill_client, skill_card, skill_sess, f\"{sid}({inp})\", push=push, host=host, port=port)\n", + " print(f\" ✅ → {out}\")\n", + " results.append({\"skill_id\": sid, \"output\": out})\n", + "\n", + " return results\n", + "\n", + "def _compose_prompt(parts, question):\n", + " \"\"\"Create the final composition prompt for the orchestrator.\"\"\"\n", + " return (\n", + " f\"Using the following information: {json.dumps(parts)}, \"\n", + " f\"write a clear and human-friendly response to the question: '{question}'. \"\n", + " \"Keep it concise and easy to understand and respond like a human with character. \"\n", + " \"Only use the information provided. If you cannot answer the question, say 'I don't know'. \"\n", + " \"Never show any code or JSON, just the answer.\\n\\n\"\n", + " )\n" + ] + }, + { + "cell_type": "markdown", + "id": "6c6d3fe2", + "metadata": {}, + "source": [ + "## 5. Orchestrating the Agent Interaction\n", + "This is the main function that ties together the planning, execution, and composition phases to answer a user's question using the agent team." + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "86d7612a", + "metadata": {}, + "outputs": [], + "source": [ + "async def ask_question(\n", + " agent_manager: AgentManager,\n", + " question: str,\n", + " push: bool = False,\n", + " push_receiver: str = \"http://localhost:5000\",\n", + ") -> str:\n", + " # Unpack orchestrator info\n", + " orch_url, orch_card, orch_client, orch_session = agent_manager.orchestrator\n", + "\n", + " # Optionally start push listener\n", + " host = port = None\n", + " if push:\n", + " parsed = urllib.parse.urlparse(push_receiver)\n", + " host, port = parsed.hostname, parsed.port\n", + " auth = PushNotificationReceiverAuth()\n", + " await auth.load_jwks(f\"{orch_url}/.well-known/jwks.json\")\n", + " PushNotificationListener(\n", + " host=host,\n", + " port=port,\n", + " notification_receiver_auth=auth\n", + " ).start()\n", + "\n", + " # --- Planning Phase ---\n", + " print(\"\\n\\033[1;33m=========== 🧠 Planning Phase ===========\\033[0m\")\n", + " plan = await _planning_phase(agent_manager, question, push=push, host=host, port=port)\n", + " print(f\"\\n\\033[1;32mFinal plan ➡️ {plan}\\033[0m\")\n", + "\n", + " # --- Execution Phase ---\n", + " print(\"\\n\\033[1;33m=========== ⚡️ Execution Phase ===========\\033[0m\")\n", + " parts = await _execution_phase(agent_manager, plan, push=push, host=host, port=port)\n", + "\n", + " # --- Composing Answer ---\n", + " print(\"\\n\\033[1;33m=========== 🛠️ Composing Answer ===========\\033[0m\")\n", + " comp_prompt = _compose_prompt(parts, question)\n", + " WRITING_AGENT_ID = \"writing_agent\"\n", + "\n", + " _, skill_card, skill_client, skill_sess = agent_manager.skills.get(WRITING_AGENT_ID)\n", + " final = await _send_task_to_agent(agent_manager, skill_client, skill_card, skill_sess, comp_prompt, push=push, host=host, port=port)\n", + "\n", + " print(\"\\n\\033[1;36m🎉 FINAL ANSWER\\033[0m\")\n", + " print(final)\n", + " print(\"\\033[1;36m====================================\\033[0m\")\n", + " return final\n" + ] + }, + { + "cell_type": "markdown", + "id": "4d580d4f", + "metadata": {}, + "source": [ + "## 6. Launching the A2A Agent Servers\n", + "Before we can interact with the agents, we need to start their servers. This bootstrap script handles bringing the complete A2A stack online.\n", + "\n", + "It performs three key actions in sequence:\n", + "\n", + "1. Defines connection details (ports and modules).\n", + "\n", + "2. Starts each agent in parallel and waits for them to be ready.\n", + "\n", + "3. Connects to the running agents and summarizes their status." + ] + }, + { + "cell_type": "markdown", + "id": "60bd2d20", + "metadata": {}, + "source": [ + "Below, we set up the network addresses (`URLS`) for our orchestrator and skill agents, and specify the Python modules that implement their functionality. These definitions are crucial for starting and connecting to the agents in the next steps." + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "ceb43397", + "metadata": {}, + "outputs": [], + "source": [ + "ORCHESTRATOR_URL = \"http://localhost:10010\"\n", + "EXECUTOR_URLS = [\"http://localhost:10011\", \"http://localhost:10012\"]\n", + "URLS = [ORCHESTRATOR_URL, *EXECUTOR_URLS]\n", + "MODULES = [\n", + " \"agents.a2a_planner\",\n", + " \"agents.a2a_custom_tools\",\n", + " \"agents.a2a_composer\",\n", + "]" + ] + }, + { + "cell_type": "markdown", + "id": "9580373e", + "metadata": {}, + "source": [ + "This launches the agent processes and wait until each server is ready and listening on its assigned port." + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "3083dd64", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "✅ agents.a2a_planner ready on port 10010\n", + "✅ agents.a2a_custom_tools ready on port 10011\n", + "✅ agents.a2a_composer ready on port 10012\n" + ] + } + ], + "source": [ + "os.chdir('..') # change to the directory where the script is located\n", + "\n", + "def _launch(mod, url):\n", + " port = int(url.split(\":\")[-1])\n", + " subprocess.Popen([sys.executable, \"-m\", mod, \"--port\", str(port)],\n", + " stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)\n", + " while socket.socket().connect_ex((\"127.0.0.1\", port)): time.sleep(.1)\n", + " return f\"✅ {mod} ready on port {port}\"\n", + "\n", + "with cf.ThreadPoolExecutor() as pool:\n", + " print(*pool.map(_launch, MODULES, URLS), sep=\"\\n\")" + ] + }, + { + "cell_type": "markdown", + "id": "96c1a74a", + "metadata": {}, + "source": [ + "Now that the agents should be running, we'll use our `AgentManager` to connect to them, confirm they are online, and see what skills they offer." + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "id": "0ea51ded", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "2025-05-20 16:02:19,356 INFO httpx: HTTP Request: GET http://localhost:10010/.well-known/agent.json \"HTTP/1.1 200 OK\"\n", + "2025-05-20 16:02:19,366 INFO httpx: HTTP Request: GET http://localhost:10011/.well-known/agent.json \"HTTP/1.1 200 OK\"\n", + "2025-05-20 16:02:19,376 INFO httpx: HTTP Request: GET http://localhost:10012/.well-known/agent.json \"HTTP/1.1 200 OK\"\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "\u001b[1;36m===================== 🛰️ Connected Agents =====================\u001b[0m\n", + "Orchestrator: http://localhost:10010 (Orchestration Agent)\n", + "Executors:\n", + " • random_number_tool -> http://localhost:10011 (Custom Agent)\n", + " • date_tool -> http://localhost:10011 (Custom Agent)\n", + " • writing_agent -> http://localhost:10012 (Writing Agent)\n", + "\u001b[1;36m===============================================================\u001b[0m\n" + ] + } + ], + "source": [ + "_agent_manager = AgentManager(URLS)\n", + "orch_url, orch_card, *_ = _agent_manager.orchestrator\n", + "\n", + "print(\"\\n\\033[1;36m===================== 🛰️ Connected Agents =====================\\033[0m\")\n", + "print(f\"Orchestrator: {orch_url} ({orch_card.name})\")\n", + "print(\"Executors:\")\n", + "for sid, (u, card, *_) in _agent_manager.skills.items():\n", + " print(f\" • {sid} -> {u} ({card.name})\")\n", + "print(\"\\033[1;36m===============================================================\\033[0m\")" + ] + }, + { + "cell_type": "markdown", + "id": "f32892ab", + "metadata": {}, + "source": [ + "## 7. Asking the Agent Team a Question!\n", + "\n", + "Finally, it's time to put our agent team to work! We'll use the `ask_question` function we defined earlier to send our queries and see the multi-agent system in action." + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "id": "6476a816", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "\u001b[1;33m=========== 🧠 Planning Phase ===========\u001b[0m\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "2025-05-20 16:02:31,947 INFO httpx: HTTP Request: POST http://0.0.0.0:10010/ \"HTTP/1.1 200 OK\"\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Raw plan ➡️ [\n", + " {\"skill_id\": \"date_tool\"},\n", + " {\"skill_id\": \"random_number_tool\"},\n", + " {\"skill_id\": \"random_number_tool\"},\n", + " {\"skill_id\": \"random_number_tool\"},\n", + " {\"skill_id\": \"random_number_tool\"},\n", + " {\"skill_id\": \"random_number_tool\"}\n", + "]\n", + "\n", + "\u001b[1;32mFinal plan ➡️ [{'skill_id': 'date_tool'}, {'skill_id': 'random_number_tool'}, {'skill_id': 'random_number_tool'}, {'skill_id': 'random_number_tool'}, {'skill_id': 'random_number_tool'}, {'skill_id': 'random_number_tool'}]\u001b[0m\n", + "\n", + "\u001b[1;33m=========== ⚡️ Execution Phase ===========\u001b[0m\n", + "➡️ Step 1: date_tool({})\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "2025-05-20 16:02:37,550 INFO httpx: HTTP Request: POST http://0.0.0.0:10011/ \"HTTP/1.1 200 OK\"\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + " ✅ → {\"type\": \"function\", \"name\": \"date_tool\", \"parameters\": {}}Tool:date_tool Args:{}Tool:date_tool Response:\"2025-05-20\"The date today is 2023-05-20.\n", + "➡️ Step 2: random_number_tool({})\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "2025-05-20 16:02:41,982 INFO httpx: HTTP Request: POST http://0.0.0.0:10011/ \"HTTP/1.1 200 OK\"\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + " ✅ → {\"type\": \"function\", \"name\": \"random_number_tool\", \"parameters\": {}}Tool:random_number_tool Args:{}Tool:random_number_tool Response:1The random number generated is 1.\n", + "➡️ Step 3: random_number_tool({})\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "2025-05-20 16:02:46,690 INFO httpx: HTTP Request: POST http://0.0.0.0:10011/ \"HTTP/1.1 200 OK\"\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + " ✅ → {\"type\": \"function\", \"name\": \"random_number_tool\", \"parameters\": {}}Tool:random_number_tool Args:{}Tool:random_number_tool Response:86The random number generated is 86.\n", + "➡️ Step 4: random_number_tool({})\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "2025-05-20 16:02:51,154 INFO httpx: HTTP Request: POST http://0.0.0.0:10011/ \"HTTP/1.1 200 OK\"\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + " ✅ → {\"type\": \"function\", \"name\": \"random_number_tool\", \"parameters\": {}}Tool:random_number_tool Args:{}Tool:random_number_tool Response:20The random number generated is 20.\n", + "➡️ Step 5: random_number_tool({})\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "2025-05-20 16:02:55,637 INFO httpx: HTTP Request: POST http://0.0.0.0:10011/ \"HTTP/1.1 200 OK\"\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + " ✅ → {\"type\": \"function\", \"name\": \"random_number_tool\", \"parameters\": {}}Tool:random_number_tool Args:{}Tool:random_number_tool Response:5The random number generated is 5.\n", + "➡️ Step 6: random_number_tool({})\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "2025-05-20 16:03:00,145 INFO httpx: HTTP Request: POST http://0.0.0.0:10011/ \"HTTP/1.1 200 OK\"\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + " ✅ → {\"type\": \"function\", \"name\": \"random_number_tool\", \"parameters\": {}}Tool:random_number_tool Args:{}Tool:random_number_tool Response:44The random number generated is 44.\n", + "\n", + "\u001b[1;33m=========== 🛠️ Composing Answer ===========\u001b[0m\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "2025-05-20 16:03:05,302 INFO httpx: HTTP Request: POST http://0.0.0.0:10012/ \"HTTP/1.1 200 OK\"\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "\u001b[1;36m🎉 FINAL ANSWER\u001b[0m\n", + "Here's your response:\n", + "\n", + "\"Today's date is May 20th, 2023. Here are five random numbers: 1, 86, 20, 5, and 44.\"\n", + "\u001b[1;36m====================================\u001b[0m\n", + "\n", + "\u001b[1;33m=========== 🧠 Planning Phase ===========\u001b[0m\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "2025-05-20 16:03:11,257 INFO httpx: HTTP Request: POST http://0.0.0.0:10010/ \"HTTP/1.1 200 OK\"\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Raw plan ➡️ [\n", + " {\"skill_id\": \"date_tool\"}\n", + "]\n", + "\n", + "\u001b[1;32mFinal plan ➡️ [{'skill_id': 'date_tool'}]\u001b[0m\n", + "\n", + "\u001b[1;33m=========== ⚡️ Execution Phase ===========\u001b[0m\n", + "➡️ Step 1: date_tool({})\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "2025-05-20 16:03:28,192 INFO httpx: HTTP Request: POST http://0.0.0.0:10011/ \"HTTP/1.1 200 OK\"\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + " ✅ → {\"type\": \"function\", \"name\": \"date_tool\", \"parameters\": {}}Tool:date_tool Args:{}Tool:date_tool Response:\"2025-05-20\"The current date is May 20, 2025.\n", + "\n", + "\u001b[1;33m=========== 🛠️ Composing Answer ===========\u001b[0m\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "2025-05-20 16:03:30,939 INFO httpx: HTTP Request: POST http://0.0.0.0:10012/ \"HTTP/1.1 200 OK\"\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "\u001b[1;36m🎉 FINAL ANSWER\u001b[0m\n", + "\"Today's date is May 20, 2025.\"\n", + "\u001b[1;36m====================================\u001b[0m\n", + "\n", + "\u001b[1;33m=========== 🧠 Planning Phase ===========\u001b[0m\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "2025-05-20 16:03:37,648 INFO httpx: HTTP Request: POST http://0.0.0.0:10010/ \"HTTP/1.1 200 OK\"\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Raw plan ➡️ [\n", + " {\"skill_id\": \"random_number_tool\"}\n", + "]\n", + "\n", + "\u001b[1;32mFinal plan ➡️ [{'skill_id': 'random_number_tool'}]\u001b[0m\n", + "\n", + "\u001b[1;33m=========== ⚡️ Execution Phase ===========\u001b[0m\n", + "➡️ Step 1: random_number_tool({})\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "2025-05-20 16:03:54,412 INFO httpx: HTTP Request: POST http://0.0.0.0:10011/ \"HTTP/1.1 200 OK\"\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + " ✅ → {\"type\": \"function\", \"name\": \"random_number_tool\", \"parameters\": {}}Tool:random_number_tool Args:{}Tool:random_number_tool Response:21The random number generated is 21.\n", + "\n", + "\u001b[1;33m=========== 🛠️ Composing Answer ===========\u001b[0m\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "2025-05-20 16:03:57,080 INFO httpx: HTTP Request: POST http://0.0.0.0:10012/ \"HTTP/1.1 200 OK\"\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "\u001b[1;36m🎉 FINAL ANSWER\u001b[0m\n", + "\"Here's a random number: 21.\"\n", + "\u001b[1;36m====================================\u001b[0m\n" + ] + } + ], + "source": [ + "questions = [ \n", + " \"Get todays date then generate five random numbers\",\n", + " \"Get todays date?\",\n", + " \"generate a random number\",\n", + " ]\n", + "\n", + "for question in questions:\n", + " await ask_question(_agent_manager, question)" + ] + }, + { + "cell_type": "markdown", + "id": "3d0c00af", + "metadata": {}, + "source": [ + "## Key Takeaways\n", + "Congratulations! You've successfully set up and interacted with a multi-agent system using the A2A protocol. You saw how an orchestrator agent planned the task, how skill agents executed the steps, and how a composition agent put it all together for you.\n", + "\n", + "Future demos will cover more advanced aspects of agent-to-agent communication." + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "venv8", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.13.3" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +}