Skip to content

Commit 4bce352

Browse files
authored
Add run step delta for cusom bing search (#42510)
* Add run step delta for cusom bing search * Fixes
1 parent da404d2 commit 4bce352

18 files changed

+280
-72
lines changed

sdk/ai/azure-ai-agents/CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,14 @@
88
### Features Added
99

1010
- Add `RunStepDetailsActivity`, describing MCP function parameters.
11+
- Add `RunStepDeltaCustomBingGroundingToolCall`, describing `BingCustomSearchTool` updates in streaming scenario.
1112

1213
### Bugs Fixed
1314

1415
### Sample updates
1516

17+
- Bing Grounding and Bing Custom Search samples were fixed to correctly present references.
18+
1619
## 1.2.0b2 (2025-08-12)
1720

1821
### Features Added

sdk/ai/azure-ai-agents/README.md

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -369,7 +369,7 @@ Here is an example:
369369
<!-- SNIPPET:sample_agents_bing_grounding.create_agent_with_bing_grounding_tool -->
370370

371371
```python
372-
conn_id = os.environ["AZURE_BING_CONNECTION_ID"]
372+
conn_id = project_client.connections.get(os.environ["BING_CONNECTION_NAME"]).id
373373

374374
# Initialize agent bing tool and add the connection id
375375
bing = BingGroundingTool(connection_id=conn_id)
@@ -474,9 +474,7 @@ The tool approval flow looks like this:
474474
# Create and process agent run in thread with MCP tools
475475
mcp_tool.update_headers("SuperSecret", "123456")
476476
# mcp_tool.set_approval_mode("never") # Uncomment to disable approval requirement
477-
run = agents_client.runs.create(
478-
thread_id=thread.id, agent_id=agent.id, tool_resources=mcp_tool.resources
479-
)
477+
run = agents_client.runs.create(thread_id=thread.id, agent_id=agent.id, tool_resources=mcp_tool.resources)
480478
print(f"Created run, ID: {run.id}")
481479

482480
while run.status in ["queued", "in_progress", "requires_action"]:

sdk/ai/azure-ai-agents/apiview-properties.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,7 @@
136136
"azure.ai.agents.models.RunStepDeltaCodeInterpreterLogOutput": "Azure.AI.Agents.RunStepDeltaCodeInterpreterLogOutput",
137137
"azure.ai.agents.models.RunStepDeltaCodeInterpreterToolCall": "Azure.AI.Agents.RunStepDeltaCodeInterpreterToolCall",
138138
"azure.ai.agents.models.RunStepDeltaConnectedAgentToolCall": "Azure.AI.Agents.RunStepDeltaConnectedAgentToolCall",
139+
"azure.ai.agents.models.RunStepDeltaCustomBingGroundingToolCall": "Azure.AI.Agents.RunStepDeltaCustomBingGroundingToolCall",
139140
"azure.ai.agents.models.RunStepDeltaDeepResearchToolCall": "Azure.AI.Agents.RunStepDeltaDeepResearchToolCall",
140141
"azure.ai.agents.models.RunStepDeltaDetail": "Azure.AI.Agents.RunStepDeltaDetail",
141142
"azure.ai.agents.models.RunStepDeltaFileSearchToolCall": "Azure.AI.Agents.RunStepDeltaFileSearchToolCall",

sdk/ai/azure-ai-agents/assets.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,5 @@
22
"AssetsRepo": "Azure/azure-sdk-assets",
33
"AssetsRepoPrefixPath": "python",
44
"TagPrefix": "python/ai/azure-ai-agents",
5-
"Tag": "python/ai/azure-ai-agents_ad0998d8d9"
5+
"Tag": "python/ai/azure-ai-agents_6e57db9f8e"
66
}

sdk/ai/azure-ai-agents/azure/ai/agents/models/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,7 @@
145145
RunStepDeltaCodeInterpreterOutput,
146146
RunStepDeltaCodeInterpreterToolCall,
147147
RunStepDeltaConnectedAgentToolCall,
148+
RunStepDeltaCustomBingGroundingToolCall,
148149
RunStepDeltaDeepResearchToolCall,
149150
RunStepDeltaDetail,
150151
RunStepDeltaFileSearchToolCall,
@@ -382,6 +383,7 @@
382383
"RunStepDeltaCodeInterpreterOutput",
383384
"RunStepDeltaCodeInterpreterToolCall",
384385
"RunStepDeltaConnectedAgentToolCall",
386+
"RunStepDeltaCustomBingGroundingToolCall",
385387
"RunStepDeltaDeepResearchToolCall",
386388
"RunStepDeltaDetail",
387389
"RunStepDeltaFileSearchToolCall",

sdk/ai/azure-ai-agents/azure/ai/agents/models/_models.py

Lines changed: 55 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4425,15 +4425,16 @@ class RunStepBingCustomSearchToolCall(RunStepToolCall, discriminator="bing_custo
44254425
:ivar type: The object type, which is always 'bing_custom_search'. Required. Default value is
44264426
"bing_custom_search".
44274427
:vartype type: str
4428-
:ivar bing_custom_search: Reserved for future use. Required.
4428+
:ivar bing_custom_search: The dictionary with request and response from Custom Bing Grounding
4429+
search tool. Required.
44294430
:vartype bing_custom_search: dict[str, str]
44304431
"""
44314432

44324433
type: Literal["bing_custom_search"] = rest_discriminator(name="type", visibility=["read", "create", "update", "delete", "query"]) # type: ignore
44334434
"""The object type, which is always 'bing_custom_search'. Required. Default value is
44344435
\"bing_custom_search\"."""
44354436
bing_custom_search: Dict[str, str] = rest_field(visibility=["read", "create", "update", "delete", "query"])
4436-
"""Reserved for future use. Required."""
4437+
"""The dictionary with request and response from Custom Bing Grounding search tool. Required."""
44374438

44384439
@overload
44394440
def __init__(
@@ -4465,15 +4466,16 @@ class RunStepBingGroundingToolCall(RunStepToolCall, discriminator="bing_groundin
44654466
:ivar type: The object type, which is always 'bing_grounding'. Required. Default value is
44664467
"bing_grounding".
44674468
:vartype type: str
4468-
:ivar bing_grounding: Reserved for future use. Required.
4469+
:ivar bing_grounding: The dictionary with request and response from Bing Grounding search tool.
4470+
Required.
44694471
:vartype bing_grounding: dict[str, str]
44704472
"""
44714473

44724474
type: Literal["bing_grounding"] = rest_discriminator(name="type", visibility=["read", "create", "update", "delete", "query"]) # type: ignore
44734475
"""The object type, which is always 'bing_grounding'. Required. Default value is
44744476
\"bing_grounding\"."""
44754477
bing_grounding: Dict[str, str] = rest_field(visibility=["read", "create", "update", "delete", "query"])
4476-
"""Reserved for future use. Required."""
4478+
"""The dictionary with request and response from Bing Grounding search tool. Required."""
44774479

44784480
@overload
44794481
def __init__(
@@ -4985,10 +4987,11 @@ class RunStepDeltaToolCall(_Model):
49854987
call details.
49864988
49874989
You probably want to use the sub-classes and not this class directly. Known sub-classes are:
4988-
RunStepDeltaAzureAISearchToolCall, RunStepDeltaBingGroundingToolCall,
4989-
RunStepDeltaCodeInterpreterToolCall, RunStepDeltaConnectedAgentToolCall,
4990-
RunStepDeltaDeepResearchToolCall, RunStepDeltaFileSearchToolCall, RunStepDeltaFunctionToolCall,
4991-
RunStepDeltaMcpToolCall, RunStepDeltaOpenAPIToolCall
4990+
RunStepDeltaAzureAISearchToolCall, RunStepDeltaCustomBingGroundingToolCall,
4991+
RunStepDeltaBingGroundingToolCall, RunStepDeltaCodeInterpreterToolCall,
4992+
RunStepDeltaConnectedAgentToolCall, RunStepDeltaDeepResearchToolCall,
4993+
RunStepDeltaFileSearchToolCall, RunStepDeltaFunctionToolCall, RunStepDeltaMcpToolCall,
4994+
RunStepDeltaOpenAPIToolCall
49924995
49934996
:ivar index: The index of the tool call detail in the run step's tool_calls array. Required.
49944997
:vartype index: int
@@ -5078,15 +5081,16 @@ class RunStepDeltaBingGroundingToolCall(RunStepDeltaToolCall, discriminator="bin
50785081
:ivar type: The object type, which is always "bing_grounding". Required. Default value is
50795082
"bing_grounding".
50805083
:vartype type: str
5081-
:ivar bing_grounding: Reserved for future use. Required.
5084+
:ivar bing_grounding: The dictionary with request and response from Bing Grounding search tool.
5085+
Required.
50825086
:vartype bing_grounding: dict[str, str]
50835087
"""
50845088

50855089
type: Literal["bing_grounding"] = rest_discriminator(name="type", visibility=["read", "create", "update", "delete", "query"]) # type: ignore
50865090
"""The object type, which is always \"bing_grounding\". Required. Default value is
50875091
\"bing_grounding\"."""
50885092
bing_grounding: Dict[str, str] = rest_field(visibility=["read", "create", "update", "delete", "query"])
5089-
"""Reserved for future use. Required."""
5093+
"""The dictionary with request and response from Bing Grounding search tool. Required."""
50905094

50915095
@overload
50925096
def __init__(
@@ -5418,6 +5422,47 @@ def __init__(self, *args: Any, **kwargs: Any) -> None:
54185422
super().__init__(*args, type="connected_agent", **kwargs)
54195423

54205424

5425+
class RunStepDeltaCustomBingGroundingToolCall(RunStepDeltaToolCall, discriminator="bing_custom_search"):
5426+
"""Represents the Custom Bing Grounding tool call in a streaming run step.
5427+
5428+
:ivar index: The index of the tool call detail in the run step's tool_calls array. Required.
5429+
:vartype index: int
5430+
:ivar id: The ID of the tool call, used when submitting outputs to the run. Required.
5431+
:vartype id: str
5432+
:ivar type: The object type, which is always 'bing_custom_search'. Required. Default value is
5433+
"bing_custom_search".
5434+
:vartype type: str
5435+
:ivar bing_custom_search: The dictionary with request and response from Custom Bing Grounding
5436+
search tool. Required.
5437+
:vartype bing_custom_search: dict[str, str]
5438+
"""
5439+
5440+
type: Literal["bing_custom_search"] = rest_discriminator(name="type", visibility=["read", "create", "update", "delete", "query"]) # type: ignore
5441+
"""The object type, which is always 'bing_custom_search'. Required. Default value is
5442+
\"bing_custom_search\"."""
5443+
bing_custom_search: Dict[str, str] = rest_field(visibility=["read", "create", "update", "delete", "query"])
5444+
"""The dictionary with request and response from Custom Bing Grounding search tool. Required."""
5445+
5446+
@overload
5447+
def __init__(
5448+
self,
5449+
*,
5450+
index: int,
5451+
id: str, # pylint: disable=redefined-builtin
5452+
bing_custom_search: Dict[str, str],
5453+
) -> None: ...
5454+
5455+
@overload
5456+
def __init__(self, mapping: Mapping[str, Any]) -> None:
5457+
"""
5458+
:param mapping: raw JSON to initialize the model.
5459+
:type mapping: Mapping[str, Any]
5460+
"""
5461+
5462+
def __init__(self, *args: Any, **kwargs: Any) -> None:
5463+
super().__init__(*args, type="bing_custom_search", **kwargs)
5464+
5465+
54215466
class RunStepDeltaDeepResearchToolCall(RunStepDeltaToolCall, discriminator="deep_research"):
54225467
"""Represents the Deep research in a streaming run step.
54235468

sdk/ai/azure-ai-agents/azure_ai_agents_tests.env

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88
########################################################################################################################
99
# Agents tests
10-
#
10+
#
1111
AZURE_AI_AGENTS_TESTS_PROJECT_CONNECTION_STRING=
1212
AZURE_AI_AGENTS_TESTS_PROJECT_ENDPOINT=
1313
AZURE_AI_AGENTS_TESTS_DATA_PATH=
@@ -17,4 +17,6 @@ AZURE_AI_AGENTS_TESTS_SEARCH_CONNECTION_ID=
1717
AZURE_AI_AGENTS_TESTS_IS_TEST_RUN=True
1818
AZURE_AI_AGENTS_TESTS_BING_CONNECTION_ID=
1919
AZURE_AI_AGENTS_TESTS_PLAYWRIGHT_CONNECTION_ID=
20-
AZURE_AI_AGENTS_TESTS_DEEP_RESEARCH_MODEL=
20+
AZURE_AI_AGENTS_TESTS_DEEP_RESEARCH_MODEL=
21+
AZURE_AI_AGENTS_TESTS_BING_CUSTOM_CONNECTION_ID=
22+
AZURE_AI_AGENTS_TESTS_BING_CONFIGURATION_NAME=

sdk/ai/azure-ai-agents/samples/agents_streaming/sample_agents_stream_eventhandler_with_bing_grounding.py

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,12 @@
2121
page of your Azure AI Foundry portal.
2222
2) MODEL_DEPLOYMENT_NAME - The deployment name of the AI model, as found under the "Name" column in
2323
the "Models + endpoints" tab in your Azure AI Foundry project.
24-
3) AZURE_BING_CONNECTION_ID - The connection id of the Bing connection, as found in the "Connected resources" tab
25-
in your Azure AI Foundry project.
24+
3) BING_CONNECTION_NAME - The name of a connection to the Bing search resource as it is
25+
listed in Azure AI Foundry connected resources.
2626
"""
2727

2828
import os
29+
import re
2930
from typing import Any
3031
from azure.identity import DefaultAzureCredential
3132
from azure.ai.projects import AIProjectClient
@@ -45,7 +46,9 @@
4546
class MyEventHandler(AgentEventHandler):
4647

4748
def on_message_delta(self, delta: "MessageDeltaChunk") -> None:
48-
print(f"Text delta received: {delta.text}")
49+
# Do not print reference text as we will show actual citation instead.
50+
if re.match(r"\u3010(.+)\u3011", delta.text) is None:
51+
print(f"Text delta received: {delta.text}")
4952
if delta.delta.content and isinstance(delta.delta.content[0], MessageDeltaTextContent):
5053
delta_text_content = delta.delta.content[0]
5154
if delta_text_content.text and delta_text_content.text.annotations:
@@ -85,7 +88,7 @@ def on_unhandled_event(self, event_type: str, event_data: Any) -> None:
8588
with project_client:
8689
agents_client = project_client.agents
8790

88-
bing_connection_id = os.environ["AZURE_BING_CONNECTION_ID"]
91+
bing_connection_id = project_client.connections.get(os.environ["BING_CONNECTION_NAME"]).id
8992
print(f"Bing Connection ID: {bing_connection_id}")
9093

9194
# Initialize agent bing tool and add the connection id
@@ -117,7 +120,12 @@ def on_unhandled_event(self, event_type: str, event_data: Any) -> None:
117120

118121
response_message = agents_client.messages.get_last_message_by_role(thread_id=thread.id, role=MessageRole.AGENT)
119122
if response_message:
123+
responses = []
120124
for text_message in response_message.text_messages:
121-
print(f"Agent response: {text_message.text.value}")
125+
responses.append(text_message.text.value)
126+
message = " ".join(responses)
122127
for annotation in response_message.url_citation_annotations:
123-
print(f"URL Citation: [{annotation.url_citation.title}]({annotation.url_citation.url})")
128+
message = message.replace(
129+
annotation.text, f" [{annotation.url_citation.title}]({annotation.url_citation.url})"
130+
)
131+
print(f"Agent response: {message}")

sdk/ai/azure-ai-agents/samples/agents_streaming/sample_agents_stream_iteration_with_bing_grounding.py

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,12 @@
2121
page of your Azure AI Foundry portal.
2222
2) MODEL_DEPLOYMENT_NAME - The deployment name of the AI model, as found under the "Name" column in
2323
the "Models + endpoints" tab in your Azure AI Foundry project.
24-
3) AZURE_BING_CONNECTION_ID - The ID of the Bing connection, as found in the "Connected resources" tab
25-
in your Azure AI Foundry project.
24+
3) BING_CONNECTION_NAME - The name of a connection to the Bing search resource as it is
25+
listed in Azure AI Foundry connected resources.
2626
"""
2727

2828
import os
29+
import re
2930
from azure.ai.projects import AIProjectClient
3031
from azure.ai.agents.models import AgentStreamEvent, RunStepDeltaChunk
3132
from azure.ai.agents.models import (
@@ -48,7 +49,7 @@
4849
with project_client:
4950
agents_client = project_client.agents
5051

51-
bing_connection_id = os.environ["AZURE_BING_CONNECTION_ID"]
52+
bing_connection_id = project_client.connections.get(os.environ["BING_CONNECTION_NAME"]).id
5253
bing = BingGroundingTool(connection_id=bing_connection_id)
5354
print(f"Bing Connection ID: {bing_connection_id}")
5455

@@ -69,12 +70,15 @@
6970
print(f"Created message, message ID {message.id}")
7071

7172
# Process Agent run and stream events back to the client. It may take a few minutes for the agent to complete the run.
73+
reference_text = re.compile(r"\u3010(.+)\u3011")
7274
with agents_client.runs.stream(thread_id=thread.id, agent_id=agent.id) as stream:
7375

7476
for event_type, event_data, _ in stream:
7577

7678
if isinstance(event_data, MessageDeltaChunk):
77-
print(f"Text delta received: {event_data.text}")
79+
# Do not print reference text as we will show actual citation instead.
80+
if reference_text.match(event_data.text) is None:
81+
print(f"Text delta received: {event_data.text}")
7882
if event_data.delta.content and isinstance(event_data.delta.content[0], MessageDeltaTextContent):
7983
delta_text_content = event_data.delta.content[0]
8084
if delta_text_content.text and delta_text_content.text.annotations:
@@ -113,7 +117,12 @@
113117

114118
response_message = agents_client.messages.get_last_message_by_role(thread_id=thread.id, role=MessageRole.AGENT)
115119
if response_message:
120+
responses = []
116121
for text_message in response_message.text_messages:
117-
print(f"Agent response: {text_message.text.value}")
122+
responses.append(text_message.text.value)
123+
message = " ".join(responses)
118124
for annotation in response_message.url_citation_annotations:
119-
print(f"URL Citation: [{annotation.url_citation.title}]({annotation.url_citation.url})")
125+
message = message.replace(
126+
annotation.text, f" [{annotation.url_citation.title}]({annotation.url_citation.url})"
127+
)
128+
print(f"Agent response: {message}")

sdk/ai/azure-ai-agents/samples/agents_streaming/sample_agents_stream_iteration_with_mcp.py

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -93,9 +93,7 @@
9393
# Process Agent run and stream events back to the client. It may take a few minutes for the agent to complete the run.
9494
mcp_tool.update_headers("SuperSecret", "123456")
9595
# mcp_tool.set_approval_mode("never") # Uncomment to disable approval requirement
96-
with agents_client.runs.stream(
97-
thread_id=thread.id, agent_id=agent.id, tool_resources=mcp_tool.resources
98-
) as stream:
96+
with agents_client.runs.stream(thread_id=thread.id, agent_id=agent.id, tool_resources=mcp_tool.resources) as stream:
9997

10098
for event_type, event_data, _ in stream:
10199

@@ -201,9 +199,7 @@
201199
agents_client.delete_agent(agent.id)
202200
print("Deleted agent")
203201

204-
response_message = agents_client.messages.get_last_message_by_role(
205-
thread_id=thread.id, role=MessageRole.AGENT
206-
)
202+
response_message = agents_client.messages.get_last_message_by_role(thread_id=thread.id, role=MessageRole.AGENT)
207203
if response_message:
208204
for text_message in response_message.text_messages:
209205
print(f"Agent response: {text_message.text.value}")

0 commit comments

Comments
 (0)