From db6284a71b3d6a7ce66d0f4293f4907fa8989faa Mon Sep 17 00:00:00 2001 From: Dmytro Struk <13853051+dmytrostruk@users.noreply.github.com> Date: Fri, 16 Jan 2026 21:36:32 -0800 Subject: [PATCH 1/4] Add kwargs to create_agent method --- .../_project_provider.py | 5 ++ .../getting_started/agents/azure_ai/README.md | 1 + .../azure_ai_with_content_filtering.py | 66 +++++++++++++++++++ 3 files changed, 72 insertions(+) create mode 100644 python/samples/getting_started/agents/azure_ai/azure_ai_with_content_filtering.py diff --git a/python/packages/azure-ai/agent_framework_azure_ai/_project_provider.py b/python/packages/azure-ai/agent_framework_azure_ai/_project_provider.py index edad03f5b4..94e0ead679 100644 --- a/python/packages/azure-ai/agent_framework_azure_ai/_project_provider.py +++ b/python/packages/azure-ai/agent_framework_azure_ai/_project_provider.py @@ -164,6 +164,7 @@ async def create_agent( default_options: TOptions_co | None = None, middleware: Sequence[Middleware] | None = None, context_provider: ContextProvider | None = None, + **kwargs: Any, ) -> "ChatAgent[TOptions_co]": """Create a new agent on the Azure AI service and return a local ChatAgent wrapper. @@ -178,6 +179,7 @@ async def create_agent( These options are applied to every run unless overridden. middleware: List of middleware to intercept agent and function invocations. context_provider: Context provider to include during agent invocation. + **kwargs: Additional parameters passed to PromptAgentDefinition. Returns: ChatAgent: A ChatAgent instance configured with the created agent. @@ -211,6 +213,9 @@ async def create_agent( if normalized_tools: args["tools"] = to_azure_ai_tools(normalized_tools) + # Merge any additional kwargs into args + args.update(kwargs) + created_agent = await self._project_client.agents.create_version( agent_name=name, definition=PromptAgentDefinition(**args), diff --git a/python/samples/getting_started/agents/azure_ai/README.md b/python/samples/getting_started/agents/azure_ai/README.md index f60b64cf18..719f233b36 100644 --- a/python/samples/getting_started/agents/azure_ai/README.md +++ b/python/samples/getting_started/agents/azure_ai/README.md @@ -17,6 +17,7 @@ This folder contains examples demonstrating different ways to create and use age | [`azure_ai_with_code_interpreter.py`](azure_ai_with_code_interpreter.py) | Shows how to use the `HostedCodeInterpreterTool` with Azure AI agents to write and execute Python code for mathematical problem solving and data analysis. | | [`azure_ai_with_code_interpreter_file_generation.py`](azure_ai_with_code_interpreter_file_generation.py) | Shows how to retrieve file IDs from code interpreter generated files using both streaming and non-streaming approaches. | | [`azure_ai_with_code_interpreter_file_download.py`](azure_ai_with_code_interpreter_file_download.py) | Shows how to download files generated by code interpreter using the OpenAI containers API. | +| [`azure_ai_with_content_filtering.py`](azure_ai_with_content_filtering.py) | Shows how to enable content filtering (RAI policy) on Azure AI agents using `RaiConfig`. Requires creating an RAI policy in Azure AI Foundry portal first. | | [`azure_ai_with_existing_agent.py`](azure_ai_with_existing_agent.py) | Shows how to work with a pre-existing agent by providing the agent name and version to the Azure AI client. Demonstrates agent reuse patterns for production scenarios. | | [`azure_ai_with_existing_conversation.py`](azure_ai_with_existing_conversation.py) | Demonstrates how to use an existing conversation created on the service side with Azure AI agents. Shows two approaches: specifying conversation ID at the client level and using AgentThread with an existing conversation ID. | | [`azure_ai_with_application_endpoint.py`](azure_ai_with_application_endpoint.py) | Demonstrates calling the Azure AI application-scoped endpoint. | diff --git a/python/samples/getting_started/agents/azure_ai/azure_ai_with_content_filtering.py b/python/samples/getting_started/agents/azure_ai/azure_ai_with_content_filtering.py new file mode 100644 index 0000000000..23b5f416a1 --- /dev/null +++ b/python/samples/getting_started/agents/azure_ai/azure_ai_with_content_filtering.py @@ -0,0 +1,66 @@ +# Copyright (c) Microsoft. All rights reserved. + +import asyncio + +from agent_framework.azure import AzureAIProjectAgentProvider +from azure.ai.projects.models import RaiConfig +from azure.identity.aio import AzureCliCredential + +""" +Azure AI Agent with Content Filtering (RAI Policy) Example + +This sample demonstrates how to enable content filtering on Azure AI agents using RaiConfig. + +Prerequisites: +1. Create an RAI Policy in Azure AI Foundry portal: + - Go to Azure AI Foundry > Your Project > Guardrails + Controls > Content Filters + - Create a new content filter or use an existing one + - Note the policy name + +2. Set environment variables: + - AZURE_AI_PROJECT_ENDPOINT: Your Azure AI Foundry project endpoint + - AZURE_AI_MODEL_DEPLOYMENT_NAME: Your model deployment name + +3. Run `az login` to authenticate +""" + + +async def main() -> None: + print("=== Azure AI Agent with Content Filtering ===\n") + + # Replace with your RAI policy from Azure AI Foundry portal + rai_policy_name = ( + "/subscriptions/{subscriptionId}/resourceGroups/{resourceGroup}/providers/" + "Microsoft.CognitiveServices/accounts/{accountName}/raiPolicies/{policyName}" + ) + + async with ( + AzureCliCredential() as credential, + AzureAIProjectAgentProvider(credential=credential) as provider, + ): + # Create agent with content filtering enabled via rai_config + agent = await provider.create_agent( + name="ContentFilteredAgent", + instructions="You are a helpful assistant.", + rai_config=RaiConfig(rai_policy_name=rai_policy_name), + ) + + # Test with a normal query + query = "What is the capital of France?" + print(f"User: {query}") + result = await agent.run(query) + print(f"Agent: {result}\n") + + # Test with a query that might trigger content filtering + # (depending on your RAI policy configuration) + query2 = "Tell me something inappropriate." + print(f"User: {query2}") + try: + result2 = await agent.run(query2) + print(f"Agent: {result2}\n") + except Exception as e: + print(f"Content filter triggered: {e}\n") + + +if __name__ == "__main__": + asyncio.run(main()) From 7ca6bb3219932edf2db1e2e8ea5eb1f83c707e83 Mon Sep 17 00:00:00 2001 From: Dmytro Struk <13853051+dmytrostruk@users.noreply.github.com> Date: Fri, 16 Jan 2026 21:55:22 -0800 Subject: [PATCH 2/4] Added test for kwargs --- .../packages/azure-ai/tests/test_provider.py | 43 +++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/python/packages/azure-ai/tests/test_provider.py b/python/packages/azure-ai/tests/test_provider.py index e3dfa0995a..6bc0a402f8 100644 --- a/python/packages/azure-ai/tests/test_provider.py +++ b/python/packages/azure-ai/tests/test_provider.py @@ -207,6 +207,49 @@ async def test_provider_create_agent_missing_model(mock_project_client: MagicMoc await provider.create_agent(name="test-agent") +async def test_provider_create_agent_with_kwargs( + mock_project_client: MagicMock, + azure_ai_unit_test_env: dict[str, str], +) -> None: + """Test AzureAIProjectAgentProvider.create_agent passes kwargs to PromptAgentDefinition.""" + with patch("agent_framework_azure_ai._project_provider.AzureAISettings") as mock_settings: + mock_settings.return_value.project_endpoint = azure_ai_unit_test_env["AZURE_AI_PROJECT_ENDPOINT"] + mock_settings.return_value.model_deployment_name = azure_ai_unit_test_env["AZURE_AI_MODEL_DEPLOYMENT_NAME"] + + provider = AzureAIProjectAgentProvider(project_client=mock_project_client) + + # Mock agent creation response + mock_agent_version = MagicMock(spec=AgentVersionDetails) + mock_agent_version.id = "agent-id" + mock_agent_version.name = "test-agent" + mock_agent_version.version = "1.0" + mock_agent_version.description = None + mock_agent_version.definition = MagicMock(spec=PromptAgentDefinition) + mock_agent_version.definition.model = "gpt-4" + mock_agent_version.definition.instructions = None + mock_agent_version.definition.temperature = None + mock_agent_version.definition.top_p = None + mock_agent_version.definition.tools = [] + + mock_project_client.agents.create_version = AsyncMock(return_value=mock_agent_version) + + # Create a mock RaiConfig-like object + mock_rai_config = MagicMock() + mock_rai_config.rai_policy_name = "policy-name" + + # Call create_agent with additional kwargs + await provider.create_agent( + name="test-agent", + model="gpt-4", + rai_config=mock_rai_config, + ) + + # Verify kwargs were passed to PromptAgentDefinition + call_args = mock_project_client.agents.create_version.call_args + definition = call_args[1]["definition"] + assert definition.rai_config is mock_rai_config + + async def test_provider_get_agent_with_name(mock_project_client: MagicMock) -> None: """Test AzureAIProjectAgentProvider.get_agent with name parameter.""" provider = AzureAIProjectAgentProvider(project_client=mock_project_client) From 6c067af519a3624433a66e60464b5fa94ec24b74 Mon Sep 17 00:00:00 2001 From: Dmytro Struk <13853051+dmytrostruk@users.noreply.github.com> Date: Sun, 18 Jan 2026 19:57:30 -0800 Subject: [PATCH 3/4] Addressed comment --- .../agent_framework_azure_ai/__init__.py | 3 ++- .../agent_framework_azure_ai/_client.py | 17 ++++++++++++----- .../_project_provider.py | 19 +++++++------------ .../packages/azure-ai/tests/test_provider.py | 10 +++++----- .../core/agent_framework/azure/__init__.py | 1 + .../core/agent_framework/azure/__init__.pyi | 2 ++ .../azure_ai_with_content_filtering.py | 4 ++-- 7 files changed, 31 insertions(+), 25 deletions(-) diff --git a/python/packages/azure-ai/agent_framework_azure_ai/__init__.py b/python/packages/azure-ai/agent_framework_azure_ai/__init__.py index c0cd4d249c..e90f3e6337 100644 --- a/python/packages/azure-ai/agent_framework_azure_ai/__init__.py +++ b/python/packages/azure-ai/agent_framework_azure_ai/__init__.py @@ -4,7 +4,7 @@ from ._agent_provider import AzureAIAgentsProvider from ._chat_client import AzureAIAgentClient, AzureAIAgentOptions -from ._client import AzureAIClient +from ._client import AzureAIClient, AzureAIProjectAgentOptions from ._project_provider import AzureAIProjectAgentProvider from ._shared import AzureAISettings @@ -18,6 +18,7 @@ "AzureAIAgentOptions", "AzureAIAgentsProvider", "AzureAIClient", + "AzureAIProjectAgentOptions", "AzureAIProjectAgentProvider", "AzureAISettings", "__version__", diff --git a/python/packages/azure-ai/agent_framework_azure_ai/_client.py b/python/packages/azure-ai/agent_framework_azure_ai/_client.py index 8bc5318b99..031b6aed21 100644 --- a/python/packages/azure-ai/agent_framework_azure_ai/_client.py +++ b/python/packages/azure-ai/agent_framework_azure_ai/_client.py @@ -2,7 +2,7 @@ import sys from collections.abc import Callable, Mapping, MutableMapping, MutableSequence, Sequence -from typing import TYPE_CHECKING, Any, ClassVar, Generic, TypedDict, TypeVar, cast +from typing import Any, ClassVar, Generic, TypedDict, TypeVar, cast from agent_framework import ( AGENT_FRAMEWORK_USER_AGENT, @@ -20,12 +20,14 @@ ) from agent_framework.exceptions import ServiceInitializationError from agent_framework.observability import use_instrumentation +from agent_framework.openai import OpenAIResponsesOptions from agent_framework.openai._responses_client import OpenAIBaseResponsesClient from azure.ai.projects.aio import AIProjectClient from azure.ai.projects.models import ( MCPTool, PromptAgentDefinition, PromptAgentDefinitionText, + RaiConfig, ) from azure.core.credentials_async import AsyncTokenCredential from azure.core.exceptions import ResourceNotFoundError @@ -33,9 +35,6 @@ from ._shared import AzureAISettings, create_text_format_config -if TYPE_CHECKING: - from agent_framework.openai import OpenAIResponsesOptions - if sys.version_info >= (3, 13): from typing import TypeVar # type: ignore # pragma: no cover else: @@ -52,10 +51,17 @@ logger = get_logger("agent_framework.azure") + +class AzureAIProjectAgentOptions(OpenAIResponsesOptions): + """Azure AI Project Agent options.""" + + rai_config: RaiConfig + + TAzureAIClientOptions = TypeVar( "TAzureAIClientOptions", bound=TypedDict, # type: ignore[valid-type] - default="OpenAIResponsesOptions", + default="AzureAIProjectAgentOptions", covariant=True, ) @@ -397,6 +403,7 @@ async def _prepare_options( "model", "tools", "response_format", + "rai_config", "temperature", "top_p", "text", diff --git a/python/packages/azure-ai/agent_framework_azure_ai/_project_provider.py b/python/packages/azure-ai/agent_framework_azure_ai/_project_provider.py index 94e0ead679..fb1db84824 100644 --- a/python/packages/azure-ai/agent_framework_azure_ai/_project_provider.py +++ b/python/packages/azure-ai/agent_framework_azure_ai/_project_provider.py @@ -2,7 +2,7 @@ import sys from collections.abc import Callable, MutableMapping, Sequence -from typing import TYPE_CHECKING, Any, Generic, TypedDict +from typing import Any, Generic, TypedDict from agent_framework import ( AGENT_FRAMEWORK_USER_AGENT, @@ -26,12 +26,9 @@ from azure.core.credentials_async import AsyncTokenCredential from pydantic import ValidationError -from ._client import AzureAIClient +from ._client import AzureAIClient, AzureAIProjectAgentOptions from ._shared import AzureAISettings, create_text_format_config, from_azure_ai_tools, to_azure_ai_tools -if TYPE_CHECKING: - from agent_framework.openai import OpenAIResponsesOptions - if sys.version_info >= (3, 13): from typing import Self, TypeVar # pragma: no cover else: @@ -46,7 +43,7 @@ TOptions_co = TypeVar( "TOptions_co", bound=TypedDict, # type: ignore[valid-type] - default="OpenAIResponsesOptions", + default="AzureAIProjectAgentOptions", covariant=True, ) @@ -164,7 +161,6 @@ async def create_agent( default_options: TOptions_co | None = None, middleware: Sequence[Middleware] | None = None, context_provider: ContextProvider | None = None, - **kwargs: Any, ) -> "ChatAgent[TOptions_co]": """Create a new agent on the Azure AI service and return a local ChatAgent wrapper. @@ -179,7 +175,6 @@ async def create_agent( These options are applied to every run unless overridden. middleware: List of middleware to intercept agent and function invocations. context_provider: Context provider to include during agent invocation. - **kwargs: Additional parameters passed to PromptAgentDefinition. Returns: ChatAgent: A ChatAgent instance configured with the created agent. @@ -195,9 +190,10 @@ async def create_agent( "or set 'AZURE_AI_MODEL_DEPLOYMENT_NAME' environment variable." ) - # Extract response_format from default_options if present + # Extract options from default_options if present opts = dict(default_options) if default_options else {} response_format = opts.get("response_format") + rai_config = opts.get("rai_config") args: dict[str, Any] = {"model": resolved_model} @@ -207,15 +203,14 @@ async def create_agent( args["text"] = PromptAgentDefinitionText( format=create_text_format_config(response_format) # type: ignore[arg-type] ) + if rai_config: + args["rai_config"] = rai_config # Normalize tools once and reuse for both Azure AI API and ChatAgent normalized_tools = normalize_tools(tools) if normalized_tools: args["tools"] = to_azure_ai_tools(normalized_tools) - # Merge any additional kwargs into args - args.update(kwargs) - created_agent = await self._project_client.agents.create_version( agent_name=name, definition=PromptAgentDefinition(**args), diff --git a/python/packages/azure-ai/tests/test_provider.py b/python/packages/azure-ai/tests/test_provider.py index 6bc0a402f8..627f5a96ea 100644 --- a/python/packages/azure-ai/tests/test_provider.py +++ b/python/packages/azure-ai/tests/test_provider.py @@ -207,11 +207,11 @@ async def test_provider_create_agent_missing_model(mock_project_client: MagicMoc await provider.create_agent(name="test-agent") -async def test_provider_create_agent_with_kwargs( +async def test_provider_create_agent_with_rai_config( mock_project_client: MagicMock, azure_ai_unit_test_env: dict[str, str], ) -> None: - """Test AzureAIProjectAgentProvider.create_agent passes kwargs to PromptAgentDefinition.""" + """Test AzureAIProjectAgentProvider.create_agent passes rai_config from default_options.""" with patch("agent_framework_azure_ai._project_provider.AzureAISettings") as mock_settings: mock_settings.return_value.project_endpoint = azure_ai_unit_test_env["AZURE_AI_PROJECT_ENDPOINT"] mock_settings.return_value.model_deployment_name = azure_ai_unit_test_env["AZURE_AI_MODEL_DEPLOYMENT_NAME"] @@ -237,14 +237,14 @@ async def test_provider_create_agent_with_kwargs( mock_rai_config = MagicMock() mock_rai_config.rai_policy_name = "policy-name" - # Call create_agent with additional kwargs + # Call create_agent with rai_config in default_options await provider.create_agent( name="test-agent", model="gpt-4", - rai_config=mock_rai_config, + default_options={"rai_config": mock_rai_config}, ) - # Verify kwargs were passed to PromptAgentDefinition + # Verify rai_config was passed to PromptAgentDefinition call_args = mock_project_client.agents.create_version.call_args definition = call_args[1]["definition"] assert definition.rai_config is mock_rai_config diff --git a/python/packages/core/agent_framework/azure/__init__.py b/python/packages/core/agent_framework/azure/__init__.py index ea94d83f0e..21ca71d85b 100644 --- a/python/packages/core/agent_framework/azure/__init__.py +++ b/python/packages/core/agent_framework/azure/__init__.py @@ -9,6 +9,7 @@ "AgentResponseCallbackProtocol": ("agent_framework_azurefunctions", "agent-framework-azurefunctions"), "AzureAIAgentClient": ("agent_framework_azure_ai", "agent-framework-azure-ai"), "AzureAIAgentOptions": ("agent_framework_azure_ai", "agent-framework-azure-ai"), + "AzureAIProjectAgentOptions": ("agent_framework_azure_ai", "agent-framework-azure-ai"), "AzureAIClient": ("agent_framework_azure_ai", "agent-framework-azure-ai"), "AzureAIProjectAgentProvider": ("agent_framework_azure_ai", "agent-framework-azure-ai"), "AzureAISearchContextProvider": ("agent_framework_azure_ai_search", "agent-framework-azure-ai-search"), diff --git a/python/packages/core/agent_framework/azure/__init__.pyi b/python/packages/core/agent_framework/azure/__init__.pyi index 155ad5067f..a7e311f315 100644 --- a/python/packages/core/agent_framework/azure/__init__.pyi +++ b/python/packages/core/agent_framework/azure/__init__.pyi @@ -4,6 +4,7 @@ from agent_framework_azure_ai import ( AzureAIAgentClient, AzureAIAgentsProvider, AzureAIClient, + AzureAIProjectAgentOptions, AzureAIProjectAgentProvider, AzureAISettings, ) @@ -28,6 +29,7 @@ __all__ = [ "AzureAIAgentClient", "AzureAIAgentsProvider", "AzureAIClient", + "AzureAIProjectAgentOptions", "AzureAIProjectAgentProvider", "AzureAISearchContextProvider", "AzureAISearchSettings", diff --git a/python/samples/getting_started/agents/azure_ai/azure_ai_with_content_filtering.py b/python/samples/getting_started/agents/azure_ai/azure_ai_with_content_filtering.py index 23b5f416a1..72597b1cd8 100644 --- a/python/samples/getting_started/agents/azure_ai/azure_ai_with_content_filtering.py +++ b/python/samples/getting_started/agents/azure_ai/azure_ai_with_content_filtering.py @@ -38,11 +38,11 @@ async def main() -> None: AzureCliCredential() as credential, AzureAIProjectAgentProvider(credential=credential) as provider, ): - # Create agent with content filtering enabled via rai_config + # Create agent with content filtering enabled via default_options agent = await provider.create_agent( name="ContentFilteredAgent", instructions="You are a helpful assistant.", - rai_config=RaiConfig(rai_policy_name=rai_policy_name), + default_options={"rai_config": RaiConfig(rai_policy_name=rai_policy_name)}, ) # Test with a normal query From f3bf4c7a7c4c4f2b1384b3e0dd5b92c9121f344d Mon Sep 17 00:00:00 2001 From: Dmytro Struk <13853051+dmytrostruk@users.noreply.github.com> Date: Sun, 18 Jan 2026 20:00:49 -0800 Subject: [PATCH 4/4] Added doc string --- python/packages/azure-ai/agent_framework_azure_ai/_client.py | 1 + 1 file changed, 1 insertion(+) diff --git a/python/packages/azure-ai/agent_framework_azure_ai/_client.py b/python/packages/azure-ai/agent_framework_azure_ai/_client.py index 031b6aed21..cad5e32e02 100644 --- a/python/packages/azure-ai/agent_framework_azure_ai/_client.py +++ b/python/packages/azure-ai/agent_framework_azure_ai/_client.py @@ -56,6 +56,7 @@ class AzureAIProjectAgentOptions(OpenAIResponsesOptions): """Azure AI Project Agent options.""" rai_config: RaiConfig + """Configuration for Responsible AI (RAI) content filtering and safety features.""" TAzureAIClientOptions = TypeVar(