Python: AzureAI Bing Connection Name Support (#1364)

* bing connection name support

* error handling + unit tests

* connection id and name handling
This commit is contained in:
Giles Odigwe
2025-10-09 14:13:07 -07:00
committed by GitHub
Unverified
parent 4cd81fe8e7
commit 7238cde5af
4 changed files with 112 additions and 25 deletions
@@ -862,8 +862,9 @@ class AzureAIAgentClient(BaseChatClient):
config_args["market"] = market
if set_lang := additional_props.get("set_lang"):
config_args["set_lang"] = set_lang
# Bing Grounding
# Bing Grounding (support both connection_id and connection_name)
connection_id = additional_props.get("connection_id") or os.getenv("BING_CONNECTION_ID")
connection_name = additional_props.get("connection_name") or os.getenv("BING_CONNECTION_NAME")
# Custom Bing Search
custom_connection_name = additional_props.get("custom_connection_name") or os.getenv(
"BING_CUSTOM_CONNECTION_NAME"
@@ -872,8 +873,26 @@ class AzureAIAgentClient(BaseChatClient):
"BING_CUSTOM_INSTANCE_NAME"
)
bing_search: BingGroundingTool | BingCustomSearchTool | None = None
if connection_id and not custom_connection_name and not custom_configuration_name:
bing_search = BingGroundingTool(connection_id=connection_id, **config_args)
if (
(connection_id or connection_name)
and not custom_connection_name
and not custom_configuration_name
):
if connection_id:
conn_id = connection_id
elif connection_name:
try:
bing_connection = await self.project_client.connections.get(name=connection_name)
except HttpResponseError as err:
raise ServiceInitializationError(
f"Bing connection '{connection_name}' not found in the Azure AI Project.",
err,
) from err
else:
conn_id = bing_connection.id
else:
raise ServiceInitializationError("Neither connection_id nor connection_name provided.")
bing_search = BingGroundingTool(connection_id=conn_id, **config_args)
if custom_connection_name and custom_configuration_name:
try:
bing_custom_connection = await self.project_client.connections.get(
@@ -892,10 +911,11 @@ class AzureAIAgentClient(BaseChatClient):
)
if not bing_search:
raise ServiceInitializationError(
"Bing search tool requires either a 'connection_id' for Bing Grounding "
"Bing search tool requires either 'connection_id' or 'connection_name' for Bing Grounding "
"or both 'custom_connection_name' and 'custom_instance_name' for Custom Bing Search. "
"These can be provided via the tool's additional_properties or environment variables: "
"'BING_CONNECTION_ID', 'BING_CUSTOM_CONNECTION_NAME', 'BING_CUSTOM_INSTANCE_NAME'"
"These can be provided via additional_properties or environment variables: "
"'BING_CONNECTION_ID', 'BING_CONNECTION_NAME', 'BING_CUSTOM_CONNECTION_NAME', "
"'BING_CUSTOM_INSTANCE_NAME'"
)
tool_definitions.extend(bing_search.definitions)
case HostedCodeInterpreterTool():
@@ -46,7 +46,7 @@ from azure.ai.agents.models import (
)
from azure.ai.projects.models import ConnectionType
from azure.core.credentials_async import AsyncTokenCredential
from azure.core.exceptions import HttpResponseError
from azure.core.exceptions import HttpResponseError, ResourceNotFoundError
from azure.identity.aio import AzureCliCredential
from pydantic import BaseModel, Field, ValidationError
from pytest import MonkeyPatch
@@ -834,7 +834,7 @@ async def test_azure_ai_chat_client_prep_tools_web_search_bing_grounding(mock_ai
web_search_tool = HostedWebSearchTool(
additional_properties={
"connection_id": "test-connection-id",
"connection_name": "test-connection-name",
"count": 5,
"freshness": "Day",
"market": "en-US",
@@ -842,6 +842,11 @@ async def test_azure_ai_chat_client_prep_tools_web_search_bing_grounding(mock_ai
}
)
# Mock connection get
mock_connection = MagicMock()
mock_connection.id = "test-connection-id"
mock_ai_project_client.connections.get = AsyncMock(return_value=mock_connection)
# Mock BingGroundingTool
with patch("agent_framework_azure_ai._chat_client.BingGroundingTool") as mock_bing_grounding:
mock_bing_tool = MagicMock()
@@ -857,6 +862,35 @@ async def test_azure_ai_chat_client_prep_tools_web_search_bing_grounding(mock_ai
)
async def test_azure_ai_chat_client_prep_tools_web_search_bing_grounding_with_connection_id(
mock_ai_project_client: MagicMock,
) -> None:
"""Test _prep_tools with HostedWebSearchTool using Bing Grounding with connection_id (no HTTP call)."""
chat_client = create_test_azure_ai_chat_client(mock_ai_project_client, agent_id="test-agent")
web_search_tool = HostedWebSearchTool(
additional_properties={
"connection_id": "direct-connection-id",
"count": 3,
}
)
# Mock BingGroundingTool
with patch("agent_framework_azure_ai._chat_client.BingGroundingTool") as mock_bing_grounding:
mock_bing_tool = MagicMock()
mock_bing_tool.definitions = [{"type": "bing_grounding"}]
mock_bing_grounding.return_value = mock_bing_tool
result = await chat_client._prep_tools([web_search_tool]) # type: ignore
assert len(result) == 1
assert result[0] == {"type": "bing_grounding"}
# Verify that connection_id was used directly (no HTTP call to connections.get)
mock_ai_project_client.connections.get.assert_not_called()
mock_bing_grounding.assert_called_once_with(connection_id="direct-connection-id", count=3)
async def test_azure_ai_chat_client_prep_tools_web_search_custom_bing(mock_ai_project_client: MagicMock) -> None:
"""Test _prep_tools with HostedWebSearchTool using Custom Bing Search."""
@@ -912,15 +946,23 @@ async def test_azure_ai_chat_client_prep_tools_web_search_custom_bing_connection
await chat_client._prep_tools([web_search_tool]) # type: ignore
async def test_azure_ai_chat_client_prep_tools_web_search_missing_config(mock_ai_project_client: MagicMock) -> None:
"""Test _prep_tools with HostedWebSearchTool missing required configuration."""
async def test_azure_ai_chat_client_prep_tools_web_search_bing_grounding_connection_error(
mock_ai_project_client: MagicMock,
) -> None:
"""Test _prep_tools with HostedWebSearchTool when Bing Grounding connection is not found."""
chat_client = create_test_azure_ai_chat_client(mock_ai_project_client, agent_id="test-agent")
# Web search tool with no connection configuration
web_search_tool = HostedWebSearchTool()
web_search_tool = HostedWebSearchTool(
additional_properties={
"connection_name": "nonexistent-bing-connection",
}
)
with pytest.raises(ServiceInitializationError, match="Bing search tool requires either a 'connection_id'"):
# Mock connection get to raise HttpResponseError
mock_ai_project_client.connections.get = AsyncMock(side_effect=HttpResponseError("Connection not found"))
with pytest.raises(ServiceInitializationError, match="Bing connection 'nonexistent-bing-connection' not found"):
await chat_client._prep_tools([web_search_tool]) # type: ignore
@@ -1397,6 +1439,28 @@ async def test_azure_ai_chat_client_create_agent_stream_submit_tool_outputs(
assert final_thread_id == "test-thread"
async def test_azure_ai_chat_client_setup_azure_ai_observability_resource_not_found(
mock_ai_project_client: MagicMock,
) -> None:
"""Test setup_azure_ai_observability when Application Insights connection string is not found."""
chat_client = create_test_azure_ai_chat_client(mock_ai_project_client, agent_id="test-agent")
# Mock telemetry.get_application_insights_connection_string to raise ResourceNotFoundError
mock_ai_project_client.telemetry.get_application_insights_connection_string = AsyncMock(
side_effect=ResourceNotFoundError("No Application Insights found")
)
# Mock logger.warning to capture the warning message
with patch("agent_framework_azure_ai._chat_client.logger") as mock_logger:
await chat_client.setup_azure_ai_observability()
# Verify warning was logged
mock_logger.warning.assert_called_once_with(
"No Application Insights connection string found for the Azure AI Project, "
"please call setup_observability() manually."
)
def get_weather(
location: Annotated[str, Field(description="The location to get the weather for.")],
) -> str:
@@ -38,16 +38,18 @@ Before running the examples, you need to set up your environment variables. You
AZURE_AI_MODEL_DEPLOYMENT_NAME="your-model-deployment-name"
```
3. For samples using Bing Grounding search (like `azure_ai_with_bing_grounding.py` and `azure_ai_with_multiple_tools.py`), you'll also need:
3. For samples using Bing Grounding search (like `azure_ai_with_bing_grounding.py` and `azure_ai_with_multiple_tools.py`), you'll also need either:
```
BING_CONNECTION_ID="/subscriptions/{subscription-id}/resourceGroups/{resource-group}/providers/Microsoft.CognitiveServices/accounts/{ai-service-name}/projects/{project-name}/connections/{connection-name}"
BING_CONNECTION_NAME="bing-grounding-connection"
# OR
BING_CONNECTION_ID="your-bing-connection-id"
```
To get your Bing connection ID:
To get your Bing connection details:
- Go to [Azure AI Foundry portal](https://ai.azure.com)
- Navigate to your project's "Connected resources" section
- Add a new connection for "Grounding with Bing Search"
- Copy the connection ID
- Copy either the connection name or ID
### Option 2: Using environment variables directly
@@ -56,7 +58,9 @@ Set the environment variables in your shell:
```bash
export AZURE_AI_PROJECT_ENDPOINT="your-project-endpoint"
export AZURE_AI_MODEL_DEPLOYMENT_NAME="your-model-deployment-name"
export BING_CONNECTION_ID="your-bing-connection-id" # Optional, only needed for web search samples
export BING_CONNECTION_NAME="your-bing-connection-name" # Optional, only needed for web search samples
# OR
export BING_CONNECTION_ID="your-bing-connection-id" # Alternative to BING_CONNECTION_NAME
```
### Required Variables
@@ -66,4 +70,4 @@ export BING_CONNECTION_ID="your-bing-connection-id" # Optional, only needed for
### Optional Variables
- `BING_CONNECTION_ID`: Your Bing connection ID (required for `azure_ai_with_bing_grounding.py` and `azure_ai_with_multiple_tools.py`)
- `BING_CONNECTION_NAME` or `BING_CONNECTION_ID`: Your Bing connection name or ID (required for `azure_ai_with_bing_grounding.py` and `azure_ai_with_multiple_tools.py`)
@@ -12,23 +12,22 @@ uses Bing Grounding search to find real-time information from the web.
Prerequisites:
1. A connected Grounding with Bing Search resource in your Azure AI project
2. Set the BING_CONNECTION_ID environment variable to your Bing connection ID
Example: BING_CONNECTION_ID="/subscriptions/{subscription-id}/resourceGroups/{resource-group}/
providers/Microsoft.CognitiveServices/accounts/{ai-service-name}/projects/{project-name}/
connections/{connection-name}"
2. Set either BING_CONNECTION_NAME or BING_CONNECTION_ID environment variable
Example: BING_CONNECTION_NAME="bing-grounding-connection"
Example: BING_CONNECTION_ID="your-bing-connection-id"
To set up Bing Grounding:
1. Go to Azure AI Foundry portal (https://ai.azure.com)
2. Navigate to your project's "Connected resources" section
3. Add a new connection for "Grounding with Bing Search"
4. Copy the connection ID and set it as the BING_CONNECTION_ID environment variable
4. Copy either the connection name or ID and set the appropriate environment variable
"""
async def main() -> None:
"""Main function demonstrating Azure AI agent with Bing Grounding search."""
# 1. Create Bing Grounding search tool using HostedWebSearchTool
# The connection_id will be automatically picked up from BING_CONNECTION_ID environment variable
# The connection_name or ID will be automatically picked up from environment variable
bing_search_tool = HostedWebSearchTool(
name="Bing Grounding Search",
description="Search the web for current information using Bing",