{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# ReAct OpenAPI MS Graph Agent" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Install Required Libraries\n", "Before starting, ensure the required libraries are installed:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "!pip install dapr-agents python-dotenv " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Import Environment Variables" ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [], "source": [ "from dapr_agents import OpenAPIReActAgent\n", "from dapr_agents.tool.utils import OpenAPISpecParser\n", "from dotenv import load_dotenv\n", "import logging" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Enable Logging" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [], "source": [ "logging.basicConfig(level=logging.INFO)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Load Environment Variables" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "True" ] }, "execution_count": 3, "metadata": {}, "output_type": "execute_result" } ], "source": [ "load_dotenv() # take environment variables from .env." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Define MS Graph HTTP Header with Access Token" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [], "source": [ "import requests\n", "import os\n", "\n", "def construct_auth_headers():\n", " CLIENT_ID = os.getenv(\"AAD_APP_ID\")\n", " CLIENT_SECRET = os.getenv(\"AAD_APP_CLIENT_SECRET\")\n", " TENANT_ID = os.getenv(\"TENANT_ID\")\n", "\n", " AUTH_URL = f\"https://login.microsoftonline.com/{TENANT_ID}/oauth2/v2.0/token\"\n", "\n", " # POST\n", " auth_response = requests.post(AUTH_URL, {\n", " 'grant_type': 'client_credentials',\n", " 'client_id': CLIENT_ID,\n", " 'client_secret': CLIENT_SECRET,\n", " 'scope': 'https://graph.microsoft.com/.default'\n", " })\n", "\n", " # convert the response to JSON\n", " auth_response_data = auth_response.json()\n", "\n", " # save the access token\n", " access_token = auth_response_data['access_token']\n", "\n", " return {\"Authorization\": f\"Bearer {access_token}\"}\n", "\n", "\n", "# Get API credentials.\n", "headers = construct_auth_headers()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Process MS Graph Users OpenAPI Spec Remotely" ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [], "source": [ "openapi_spec_url = \"https://raw.githubusercontent.com/microsoftgraph/msgraph-sdk-powershell/dev/openApiDocs/v1.0/Users.yml\"\n", "spec_parser = OpenAPISpecParser.from_url(openapi_spec_url)" ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "('GET /users',\n", " 'List properties and relationships of the user objects.',\n", " Operation(tags=['users.user'], summary='List users', description='List properties and relationships of the user objects.', externalDocs=ExternalDocumentation(description='Find more info here', url='https://learn.microsoft.com/graph/api/intune-mam-user-list?view=graph-rest-1.0'), operationId='user_ListUser', parameters=[Parameter(description='Indicates the requested consistency level. Documentation URL: https://docs.microsoft.com/graph/aad-advanced-queries', required=False, deprecated=False, style='simple', explode=None, param_schema=Schema(title=None, multipleOf=None, maximum=None, exclusiveMaximum=None, minimum=None, exclusiveMinimum=None, maxLength=None, minLength=None, pattern=None, maxItems=None, minItems=None, uniqueItems=None, maxProperties=None, minProperties=None, required=None, enum=None, type=, allOf=None, oneOf=None, anyOf=None, schema_not=None, items=None, properties=None, additionalProperties=None, description=None, schema_format=None, default=None, nullable=None, discriminator=None, readOnly=None, writeOnly=None, xml=None, externalDocs=None, example=None, deprecated=None), example=None, examples={'example-1': Example(summary=None, description=\"$search and $count queries require the client to set the ConsistencyLevel HTTP header to 'eventual'.\", value='eventual', externalValue=None)}, content=None, name='ConsistencyLevel', param_in=, allowEmptyValue=False, allowReserved=False), Reference(ref='#/components/parameters/top'), Reference(ref='#/components/parameters/search'), Reference(ref='#/components/parameters/filter'), Reference(ref='#/components/parameters/count'), Parameter(description='Order items by property values', required=False, deprecated=False, style='form', explode=False, param_schema=Schema(title=None, multipleOf=None, maximum=None, exclusiveMaximum=None, minimum=None, exclusiveMinimum=None, maxLength=None, minLength=None, pattern=None, maxItems=None, minItems=None, uniqueItems=True, maxProperties=None, minProperties=None, required=None, enum=None, type=, allOf=None, oneOf=None, anyOf=None, schema_not=None, items=Schema(title=None, multipleOf=None, maximum=None, exclusiveMaximum=None, minimum=None, exclusiveMinimum=None, maxLength=None, minLength=None, pattern=None, maxItems=None, minItems=None, uniqueItems=None, maxProperties=None, minProperties=None, required=None, enum=None, type=, allOf=None, oneOf=None, anyOf=None, schema_not=None, items=None, properties=None, additionalProperties=None, description=None, schema_format=None, default=None, nullable=None, discriminator=None, readOnly=None, writeOnly=None, xml=None, externalDocs=None, example=None, deprecated=None), properties=None, additionalProperties=None, description=None, schema_format=None, default=None, nullable=None, discriminator=None, readOnly=None, writeOnly=None, xml=None, externalDocs=None, example=None, deprecated=None), example=None, examples=None, content=None, name='$orderby', param_in=, allowEmptyValue=False, allowReserved=False), Parameter(description='Select properties to be returned', required=False, deprecated=False, style='form', explode=False, param_schema=Schema(title=None, multipleOf=None, maximum=None, exclusiveMaximum=None, minimum=None, exclusiveMinimum=None, maxLength=None, minLength=None, pattern=None, maxItems=None, minItems=None, uniqueItems=True, maxProperties=None, minProperties=None, required=None, enum=None, type=, allOf=None, oneOf=None, anyOf=None, schema_not=None, items=Schema(title=None, multipleOf=None, maximum=None, exclusiveMaximum=None, minimum=None, exclusiveMinimum=None, maxLength=None, minLength=None, pattern=None, maxItems=None, minItems=None, uniqueItems=None, maxProperties=None, minProperties=None, required=None, enum=None, type=, allOf=None, oneOf=None, anyOf=None, schema_not=None, items=None, properties=None, additionalProperties=None, description=None, schema_format=None, default=None, nullable=None, discriminator=None, readOnly=None, writeOnly=None, xml=None, externalDocs=None, example=None, deprecated=None), properties=None, additionalProperties=None, description=None, schema_format=None, default=None, nullable=None, discriminator=None, readOnly=None, writeOnly=None, xml=None, externalDocs=None, example=None, deprecated=None), example=None, examples=None, content=None, name='$select', param_in=, allowEmptyValue=False, allowReserved=False), Parameter(description='Expand related entities', required=False, deprecated=False, style='form', explode=False, param_schema=Schema(title=None, multipleOf=None, maximum=None, exclusiveMaximum=None, minimum=None, exclusiveMinimum=None, maxLength=None, minLength=None, pattern=None, maxItems=None, minItems=None, uniqueItems=True, maxProperties=None, minProperties=None, required=None, enum=None, type=, allOf=None, oneOf=None, anyOf=None, schema_not=None, items=Schema(title=None, multipleOf=None, maximum=None, exclusiveMaximum=None, minimum=None, exclusiveMinimum=None, maxLength=None, minLength=None, pattern=None, maxItems=None, minItems=None, uniqueItems=None, maxProperties=None, minProperties=None, required=None, enum=None, type=, allOf=None, oneOf=None, anyOf=None, schema_not=None, items=None, properties=None, additionalProperties=None, description=None, schema_format=None, default=None, nullable=None, discriminator=None, readOnly=None, writeOnly=None, xml=None, externalDocs=None, example=None, deprecated=None), properties=None, additionalProperties=None, description=None, schema_format=None, default=None, nullable=None, discriminator=None, readOnly=None, writeOnly=None, xml=None, externalDocs=None, example=None, deprecated=None), example=None, examples=None, content=None, name='$expand', param_in=, allowEmptyValue=False, allowReserved=False)], requestBody=None, responses={'2XX': Reference(ref='#/components/responses/microsoft.graph.userCollectionResponse'), 'default': Reference(ref='#/components/responses/error')}, callbacks=None, deprecated=False, security=None, servers=None, x-ms-pageable={'nextLinkName': '@odata.nextLink', 'operationName': 'listMore'}, x-ms-docs-operation-type='operation'))" ] }, "execution_count": 6, "metadata": {}, "output_type": "execute_result" } ], "source": [ "spec_parser.endpoints[0]" ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "236" ] }, "execution_count": 7, "metadata": {}, "output_type": "execute_result" } ], "source": [ "len(spec_parser.endpoints)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## (Optional) OpenAPI Specification to OpenAI Function Call" ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [], "source": [ "from dapr_agents.tool.utils.openapi import openapi_spec_to_openai_fn\n", "\n", "functions = openapi_spec_to_openai_fn(spec_parser)" ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "{'definition': {'type': 'function',\n", " 'function': {'name': 'user_ListUser',\n", " 'description': 'List properties and relationships of the user objects.',\n", " 'parameters': {'type': 'object',\n", " 'properties': {'params': {'type': 'object',\n", " 'properties': {'$top': {'minimum': 0.0,\n", " 'type': 'integer',\n", " 'description': 'Show only the first n items'},\n", " '$search': {'type': 'string',\n", " 'description': 'Search items by search phrases'},\n", " '$filter': {'type': 'string',\n", " 'description': 'Filter items by property values'},\n", " '$count': {'type': 'boolean', 'description': 'Include count of items'},\n", " '$orderby': {'uniqueItems': True,\n", " 'type': 'array',\n", " 'items': {'type': 'string'},\n", " 'description': 'Order items by property values'},\n", " '$select': {'uniqueItems': True,\n", " 'type': 'array',\n", " 'items': {'type': 'string'},\n", " 'description': 'Select properties to be returned'},\n", " '$expand': {'uniqueItems': True,\n", " 'type': 'array',\n", " 'items': {'type': 'string'},\n", " 'description': 'Expand related entities'}},\n", " 'required': []},\n", " 'headers': {'type': 'object',\n", " 'properties': {'ConsistencyLevel': {'type': 'string',\n", " 'description': 'Indicates the requested consistency level. Documentation URL: https://docs.microsoft.com/graph/aad-advanced-queries'}},\n", " 'required': []}}}}},\n", " 'metadata': {'method': 'get',\n", " 'url': 'https://graph.microsoft.com/v1.0//users'}}" ] }, "execution_count": 9, "metadata": {}, "output_type": "execute_result" } ], "source": [ "functions[0]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Initialize Chroma Vectorstore" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Define Embedding Function" ] }, { "cell_type": "code", "execution_count": 12, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "INFO:dapr_agents.document.embedder.sentence:Downloading SentenceTransformer model: all-MiniLM-L6-v2\n", "INFO:sentence_transformers.SentenceTransformer:Load pretrained SentenceTransformer: all-MiniLM-L6-v2\n", "INFO:dapr_agents.document.embedder.sentence:Model loaded successfully.\n" ] } ], "source": [ "from dapr_agents.document.embedder import SentenceTransformerEmbedder\n", "\n", "embedding_function = SentenceTransformerEmbedder(\n", " model=\"all-MiniLM-L6-v2\"\n", ")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Initialize Vectorstore" ] }, { "cell_type": "code", "execution_count": 14, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "INFO:dapr_agents.storage.vectorstores.chroma:ChromaVectorStore initialized with collection: api_toolbox\n" ] } ], "source": [ "from dapr_agents.storage import ChromaVectorStore\n", "\n", "api_vector_store = ChromaVectorStore(\n", " name=\"api_toolbox\",\n", " embedding_function=embedding_function,\n", ")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Define OpenAPI ReAct Agent" ] }, { "cell_type": "code", "execution_count": 15, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "INFO:dapr_agents.llm.openai.client.base:Initializing OpenAI client...\n", "INFO:dapr_agents.agent.patterns.openapi.react:Setting up VectorToolStore for OpenAPIReActAgent...\n", "INFO:dapr_agents.tool.storage.vectorstore:Adding tools to Vector Tool Store.\n", "INFO:dapr_agents.document.embedder.sentence:Generating embeddings for 236 input(s).\n" ] }, { "data": { "application/vnd.jupyter.widget-view+json": { "model_id": "b206f22263d343a8b3976225c67e74b3", "version_major": 2, "version_minor": 0 }, "text/plain": [ "Batches: 0%| | 0/8 [00:00 Available Tools \n", "┏━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓\n", "┃ Name Description Signature ┃\n", "┡━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┩\n", "│ GetOpenapiDefinition │ │ GetOpenapiDefinition(user_input: str) │\n", "│ │ Get potential APIs for the user to │ │\n", "│ │ use to accompish task. │ │\n", "│ │ You have to choose the right one │ │\n", "│ │ after getting a response. │ │\n", "│ │ This tool MUST be used before │ │\n", "│ │ calling any APIs. │ │\n", "│ │ │ │\n", "│ OpenApiCallExecutor │ │ OpenApiCallExecutor(path_template: str, │\n", "│ │ Execute an API call based on │ method: str, path_params: Dict, data: Dict, │\n", "│ │ provided parameters and configuration. │ headers: Optional = None, params: Optional │\n", "│ │ It MUST be used after the │ = None, kwargs: Any) │\n", "│ │ get_openapi_definition to call APIs. │ │\n", "│ │ Make sure to include the right │ │\n", "│ │ header values to authenticate to the API │ │\n", "│ │ if needed. │ │\n", "│ │ │ │\n", "└──────────────────────┴────────────────────────────────────────────┴─────────────────────────────────────────────┘\n", "\n" ], "text/plain": [ "\u001b[3m Available Tools \u001b[0m\n", "┏━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓\n", "┃\u001b[1m \u001b[0m\u001b[1mName \u001b[0m\u001b[1m \u001b[0m┃\u001b[1m \u001b[0m\u001b[1mDescription \u001b[0m\u001b[1m \u001b[0m┃\u001b[1m \u001b[0m\u001b[1mSignature \u001b[0m\u001b[1m \u001b[0m┃\n", "┡━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┩\n", "│\u001b[1;36m \u001b[0m\u001b[1;36mGetOpenapiDefinition\u001b[0m\u001b[1;36m \u001b[0m│ │ GetOpenapiDefinition(user_input: str) │\n", "│\u001b[1;36m \u001b[0m│ Get potential APIs for the user to │ │\n", "│\u001b[1;36m \u001b[0m│ use to accompish task. │ │\n", "│\u001b[1;36m \u001b[0m│ You have to choose the right one │ │\n", "│\u001b[1;36m \u001b[0m│ after getting a response. │ │\n", "│\u001b[1;36m \u001b[0m│ This tool MUST be used before │ │\n", "│\u001b[1;36m \u001b[0m│ calling any APIs. │ │\n", "│\u001b[1;36m \u001b[0m│ │ │\n", "│\u001b[1;36m \u001b[0m\u001b[1;36mOpenApiCallExecutor \u001b[0m\u001b[1;36m \u001b[0m│ │ OpenApiCallExecutor(path_template: str, │\n", "│\u001b[1;36m \u001b[0m│ Execute an API call based on │ method: str, path_params: Dict, data: Dict, │\n", "│\u001b[1;36m \u001b[0m│ provided parameters and configuration. │ headers: Optional = None, params: Optional │\n", "│\u001b[1;36m \u001b[0m│ It MUST be used after the │ = None, kwargs: Any) │\n", "│\u001b[1;36m \u001b[0m│ get_openapi_definition to call APIs. │ │\n", "│\u001b[1;36m \u001b[0m│ Make sure to include the right │ │\n", "│\u001b[1;36m \u001b[0m│ header values to authenticate to the API │ │\n", "│\u001b[1;36m \u001b[0m│ if needed. │ │\n", "│\u001b[1;36m \u001b[0m│ │ │\n", "└──────────────────────┴────────────────────────────────────────────┴─────────────────────────────────────────────┘\n" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "AIAgent.tool_executor.help" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Search for APIs" ] }, { "cell_type": "code", "execution_count": 17, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "INFO:dapr_agents.tool.storage.vectorstore:Searching for tools similar to query: Get information about a user with ID da48bd32-94bd-4263-b23a-5b9820a67fab\n", "INFO:dapr_agents.document.embedder.sentence:Generating embeddings for 1 input(s).\n" ] }, { "data": { "application/vnd.jupyter.widget-view+json": { "model_id": "da61a88331eb4d61bd6103a3435b3461", "version_major": 2, "version_minor": 0 }, "text/plain": [ "Batches: 0%| | 0/1 [00:00,\n", " ,\n", " ]}" ] }, "execution_count": 17, "metadata": {}, "output_type": "execute_result" } ], "source": [ "prompt = \"Get information about a user with ID da48bd32-94bd-4263-b23a-5b9820a67fab\"\n", "AIAgent.tool_vector_store.get_similar_tools(query_texts=prompt)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Run OpenAPI Agent" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "INFO:dapr_agents.agent.base:Pre-filled prompt template with variables: dict_keys(['chat_history'])\n", "INFO:dapr_agents.agent.patterns.react.base:Iteration 1/10 started.\n", "INFO:dapr_agents.llm.openai.chat:Invoking ChatCompletion API.\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "\u001b[38;2;242;182;128muser:\u001b[0m\n", "\u001b[38;2;242;182;128m\u001b[0m\u001b[38;2;242;182;128mGet information about a user with ID da48bd32-94bd-4263-b23a-5b9820a67fab\u001b[0m\u001b[0m\n", "\u001b[0m\u001b[0m\n", "\u001b[0m--------------------------------------------------------------------------------\u001b[0m\n", "\u001b[0m\u001b[0m\u001b[0m\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "INFO:httpx:HTTP Request: POST https://api.openai.com/v1/chat/completions \"HTTP/1.1 200 OK\"\n", "INFO:dapr_agents.llm.openai.chat:Chat completion retrieved successfully.\n", "INFO:dapr_agents.agent.patterns.react.base:Executing GetOpenapiDefinition with arguments {'user_input': 'Get user information by user ID'}\n", "INFO:dapr_agents.tool.executor:Attempting to execute tool: GetOpenapiDefinition\n", "INFO:dapr_agents.tool.storage.vectorstore:Searching for tools similar to query: ['Get user information by user ID']\n", "INFO:dapr_agents.document.embedder.sentence:Generating embeddings for 1 input(s).\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "\u001b[38;2;217;95;118mThought: To get information about a user using the provided ID, we need to interact with an API that handles user data. I will first locate the appropriate OpenAPI definition to identify the correct API endpoint for retrieving user information. \u001b[0m\n", "\u001b[38;2;217;95;118m\u001b[0m\n", "\u001b[38;2;217;95;118mLet's start by using the available tools to find the suitable API.\u001b[0m\u001b[0m\n", "\u001b[38;2;191;69;126mAction: {\"name\": \"GetOpenapiDefinition\", \"arguments\": {\"user_input\": \"Get user information by user ID\"}}\u001b[0m\u001b[0m\n" ] }, { "data": { "application/vnd.jupyter.widget-view+json": { "model_id": "c55dabbc4e9e472cb3fe2e0104e20742", "version_major": 2, "version_minor": 0 }, "text/plain": [ "Batches: 0%| | 0/1 [00:00