Skip to content

Commit 6ec414a

Browse files
authored
Add test and delta class for fabric tool. (#42597)
1 parent 0cf4523 commit 6ec414a

File tree

11 files changed

+162
-33
lines changed

11 files changed

+162
-33
lines changed

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,7 @@
149149
"azure.ai.agents.models.RunStepDeltaMcpToolCall": "Azure.AI.Agents.RunStepDeltaMcpToolCall",
150150
"azure.ai.agents.models.RunStepDeltaMessageCreation": "Azure.AI.Agents.RunStepDeltaMessageCreation",
151151
"azure.ai.agents.models.RunStepDeltaMessageCreationObject": "Azure.AI.Agents.RunStepDeltaMessageCreationObject",
152+
"azure.ai.agents.models.RunStepDeltaMicrosoftFabricToolCall": "Azure.AI.Agents.RunStepDeltaMicrosoftFabricToolCall",
152153
"azure.ai.agents.models.RunStepDeltaOpenAPIObject": "Azure.AI.Agents.RunStepDeltaOpenAPIObject",
153154
"azure.ai.agents.models.RunStepDeltaOpenAPIToolCall": "Azure.AI.Agents.RunStepDeltaOpenAPIToolCall",
154155
"azure.ai.agents.models.RunStepDeltaToolCallObject": "Azure.AI.Agents.RunStepDeltaToolCallObject",

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_0082be7355"
5+
"Tag": "python/ai/azure-ai-agents_56f374c86c"
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
@@ -158,6 +158,7 @@
158158
RunStepDeltaMcpToolCall,
159159
RunStepDeltaMessageCreation,
160160
RunStepDeltaMessageCreationObject,
161+
RunStepDeltaMicrosoftFabricToolCall,
161162
RunStepDeltaOpenAPIObject,
162163
RunStepDeltaOpenAPIToolCall,
163164
RunStepDeltaToolCall,
@@ -399,6 +400,7 @@
399400
"RunStepDeltaMcpToolCall",
400401
"RunStepDeltaMessageCreation",
401402
"RunStepDeltaMessageCreationObject",
403+
"RunStepDeltaMicrosoftFabricToolCall",
402404
"RunStepDeltaOpenAPIObject",
403405
"RunStepDeltaOpenAPIToolCall",
404406
"RunStepDeltaToolCall",

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

Lines changed: 47 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5070,8 +5070,9 @@ class RunStepDeltaToolCall(_Model):
50705070
RunStepDeltaAzureAISearchToolCall, RunStepDeltaAzureFunctionToolCall,
50715071
RunStepDeltaCustomBingGroundingToolCall, RunStepDeltaBingGroundingToolCall,
50725072
RunStepDeltaCodeInterpreterToolCall, RunStepDeltaConnectedAgentToolCall,
5073-
RunStepDeltaDeepResearchToolCall, RunStepDeltaFileSearchToolCall, RunStepDeltaFunctionToolCall,
5074-
RunStepDeltaMcpToolCall, RunStepDeltaOpenAPIToolCall
5073+
RunStepDeltaDeepResearchToolCall, RunStepDeltaMicrosoftFabricToolCall,
5074+
RunStepDeltaFileSearchToolCall, RunStepDeltaFunctionToolCall, RunStepDeltaMcpToolCall,
5075+
RunStepDeltaOpenAPIToolCall
50755076
50765077
:ivar index: The index of the tool call detail in the run step's tool_calls array. Required.
50775078
:vartype index: int
@@ -5916,6 +5917,48 @@ def __init__(self, *args: Any, **kwargs: Any) -> None:
59165917
super().__init__(*args, **kwargs)
59175918

59185919

5920+
class RunStepDeltaMicrosoftFabricToolCall(RunStepDeltaToolCall, discriminator="fabric_dataagent"):
5921+
"""Represents the Microsoft Fabric tool call in a streaming run step.
5922+
5923+
:ivar index: The index of the tool call detail in the run step's tool_calls array. Required.
5924+
:vartype index: int
5925+
:ivar id: The ID of the tool call, used when submitting outputs to the run. Required.
5926+
:vartype id: str
5927+
:ivar type: The object type, which is always 'fabric_dataagent'. Required. Default value is
5928+
"fabric_dataagent".
5929+
:vartype type: str
5930+
:ivar microsoft_fabric: Fabric input and output. Required.
5931+
:vartype microsoft_fabric: dict[str, str]
5932+
"""
5933+
5934+
type: Literal["fabric_dataagent"] = rest_discriminator(name="type", visibility=["read", "create", "update", "delete", "query"]) # type: ignore
5935+
"""The object type, which is always 'fabric_dataagent'. Required. Default value is
5936+
\"fabric_dataagent\"."""
5937+
microsoft_fabric: Dict[str, str] = rest_field(
5938+
name="fabric_dataagent", visibility=["read", "create", "update", "delete", "query"]
5939+
)
5940+
"""Fabric input and output. Required."""
5941+
5942+
@overload
5943+
def __init__(
5944+
self,
5945+
*,
5946+
index: int,
5947+
id: str, # pylint: disable=redefined-builtin
5948+
microsoft_fabric: Dict[str, str],
5949+
) -> None: ...
5950+
5951+
@overload
5952+
def __init__(self, mapping: Mapping[str, Any]) -> None:
5953+
"""
5954+
:param mapping: raw JSON to initialize the model.
5955+
:type mapping: Mapping[str, Any]
5956+
"""
5957+
5958+
def __init__(self, *args: Any, **kwargs: Any) -> None:
5959+
super().__init__(*args, type="fabric_dataagent", **kwargs)
5960+
5961+
59195962
class RunStepDeltaOpenAPIObject(RunStepDeltaDetail, discriminator="openapi"):
59205963
"""Represents an invocation of openapi as part of a streaming run step.
59215964
@@ -6438,7 +6481,7 @@ class RunStepMicrosoftFabricToolCall(RunStepToolCall, discriminator="fabric_data
64386481
:ivar type: The object type, which is always 'fabric_dataagent'. Required. Default value is
64396482
"fabric_dataagent".
64406483
:vartype type: str
6441-
:ivar microsoft_fabric: Reserved for future use. Required.
6484+
:ivar microsoft_fabric: Fabric input and output. Required.
64426485
:vartype microsoft_fabric: dict[str, str]
64436486
"""
64446487

@@ -6448,7 +6491,7 @@ class RunStepMicrosoftFabricToolCall(RunStepToolCall, discriminator="fabric_data
64486491
microsoft_fabric: Dict[str, str] = rest_field(
64496492
name="fabric_dataagent", visibility=["read", "create", "update", "delete", "query"]
64506493
)
6451-
"""Reserved for future use. Required."""
6494+
"""Fabric input and output. Required."""
64526495

64536496
@overload
64546497
def __init__(

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,4 +19,5 @@ AZURE_AI_AGENTS_TESTS_BING_CONNECTION_ID=
1919
AZURE_AI_AGENTS_TESTS_PLAYWRIGHT_CONNECTION_ID=
2020
AZURE_AI_AGENTS_TESTS_DEEP_RESEARCH_MODEL=
2121
AZURE_AI_AGENTS_TESTS_BING_CUSTOM_CONNECTION_ID=
22-
AZURE_AI_AGENTS_TESTS_BING_CONFIGURATION_NAME=
22+
AZURE_AI_AGENTS_TESTS_BING_CONFIGURATION_NAME=
23+
AZURE_AI_AGENTS_TESTS_FABRIC_CONNECTION_ID=

sdk/ai/azure-ai-agents/samples/agents_async/sample_agents_mcp_async.py

Lines changed: 25 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -44,12 +44,12 @@ async def main() -> None:
4444
# Get MCP server configuration from environment variables
4545
mcp_server_url = os.environ.get("MCP_SERVER_URL", "https://gitmcp.io/Azure/azure-rest-api-specs")
4646
mcp_server_label = os.environ.get("MCP_SERVER_LABEL", "github")
47-
47+
4848
project_client = AIProjectClient(
4949
endpoint=os.environ["PROJECT_ENDPOINT"],
5050
credential=DefaultAzureCredential(),
5151
)
52-
52+
5353
# Initialize agent MCP tool
5454
mcp_tool = McpTool(
5555
server_label=mcp_server_label,
@@ -60,11 +60,11 @@ async def main() -> None:
6060
search_api_code = "search_azure_rest_api_code"
6161
mcp_tool.allow_tool(search_api_code)
6262
print(f"Allowed tools: {mcp_tool.allowed_tools}")
63-
63+
6464
# Create agent with MCP tool and process agent run
6565
async with project_client:
6666
agents_client = project_client.agents
67-
67+
6868
# Create a new agent.
6969
# NOTE: To reuse existing agent, fetch it with get_agent(agent_id)
7070
agent = await agents_client.create_agent(
@@ -73,39 +73,39 @@ async def main() -> None:
7373
instructions="You are a helpful agent that can use MCP tools to assist users. Use the available MCP tools to answer questions and perform tasks.",
7474
tools=mcp_tool.definitions,
7575
)
76-
76+
7777
print(f"Created agent, ID: {agent.id}")
7878
print(f"MCP Server: {mcp_tool.server_label} at {mcp_tool.server_url}")
79-
79+
8080
# Create thread for communication
8181
thread = await agents_client.threads.create()
8282
print(f"Created thread, ID: {thread.id}")
83-
83+
8484
# Create message to thread
8585
message = await agents_client.messages.create(
8686
thread_id=thread.id,
8787
role="user",
8888
content="Please summarize the Azure REST API specifications Readme",
8989
)
9090
print(f"Created message, ID: {message.id}")
91-
91+
9292
# Create and process agent run in thread with MCP tools
9393
mcp_tool.update_headers("SuperSecret", "123456")
9494
# mcp_tool.set_approval_mode("never") # Uncomment to disable approval requirement
9595
run = await agents_client.runs.create(thread_id=thread.id, agent_id=agent.id, tool_resources=mcp_tool.resources)
9696
print(f"Created run, ID: {run.id}")
97-
97+
9898
while run.status in ["queued", "in_progress", "requires_action"]:
9999
await asyncio.sleep(1)
100100
run = await agents_client.runs.get(thread_id=thread.id, run_id=run.id)
101-
101+
102102
if run.status == "requires_action" and isinstance(run.required_action, SubmitToolApprovalAction):
103103
tool_calls = run.required_action.submit_tool_approval.tool_calls
104104
if not tool_calls:
105105
print("No tool calls provided - cancelling run")
106106
await agents_client.runs.cancel(thread_id=thread.id, run_id=run.id)
107107
break
108-
108+
109109
tool_approvals = []
110110
for tool_call in tool_calls:
111111
if isinstance(tool_call, RequiredMcpToolCall):
@@ -120,36 +120,36 @@ async def main() -> None:
120120
)
121121
except Exception as e:
122122
print(f"Error approving tool_call {tool_call.id}: {e}")
123-
123+
124124
print(f"tool_approvals: {tool_approvals}")
125125
if tool_approvals:
126126
await agents_client.runs.submit_tool_outputs(
127127
thread_id=thread.id, run_id=run.id, tool_approvals=tool_approvals
128128
)
129-
129+
130130
print(f"Current run status: {run.status}")
131-
131+
132132
print(f"Run completed with status: {run.status}")
133133
if run.status == "failed":
134134
print(f"Run failed: {run.last_error}")
135-
135+
136136
# Display run steps and tool calls
137137
run_steps = agents_client.run_steps.list(thread_id=thread.id, run_id=run.id)
138-
138+
139139
# Loop through each step
140140
async for step in run_steps:
141141
print(f"Step {step['id']} status: {step['status']}")
142-
142+
143143
# Check if there are tool calls in the step details
144144
step_details = step.get("step_details", {})
145145
tool_calls = step_details.get("tool_calls", [])
146-
146+
147147
if tool_calls:
148148
print(" MCP Tool calls:")
149149
for call in tool_calls:
150150
print(f" Tool Call ID: {call.get('id')}")
151151
print(f" Type: {call.get('type')}")
152-
152+
153153
if isinstance(step_details, RunStepActivityDetails):
154154
for activity in step_details.activities:
155155
for function_name, function_definition in activity.tools.items():
@@ -164,9 +164,9 @@ async def main() -> None:
164164
print(f" Description: {func_argument.description}")
165165
else:
166166
print("This function has no parameters")
167-
167+
168168
print() # add an extra newline between steps
169-
169+
170170
# Fetch and log all messages
171171
messages = agents_client.messages.list(thread_id=thread.id, order=ListSortOrder.ASCENDING)
172172
print("\nConversation:")
@@ -176,22 +176,23 @@ async def main() -> None:
176176
last_text = msg.text_messages[-1]
177177
print(f"{msg.role.upper()}: {last_text.text.value}")
178178
print("-" * 50)
179-
179+
180180
# Example of dynamic tool management
181181
print(f"\nDemonstrating dynamic tool management:")
182182
print(f"Current allowed tools: {mcp_tool.allowed_tools}")
183-
183+
184184
# Remove a tool
185185
try:
186186
mcp_tool.disallow_tool(search_api_code)
187187
print(f"After removing {search_api_code}: {mcp_tool.allowed_tools}")
188188
except ValueError as e:
189189
print(f"Error removing tool: {e}")
190-
190+
191191
# Clean-up and delete the agent once the run is finished.
192192
# NOTE: Comment out this line if you plan to reuse the agent later.
193193
await agents_client.delete_agent(agent.id)
194194
print("Deleted agent")
195195

196+
196197
if __name__ == "__main__":
197198
asyncio.run(main())

sdk/ai/azure-ai-agents/tests/conftest.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,12 @@ def azure_workspace_triad_sanitizer():
171171
value="/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/00000/providers/Microsoft.CognitiveServices/accounts/00000/projects/00000/connections/00000",
172172
)
173173

174+
# Sanitize Fabric data agent connection
175+
add_body_key_sanitizer(
176+
json_path="tools[*].fabric_dataagent.connections[*].connection_id",
177+
value="/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/00000/providers/Microsoft.CognitiveServices/accounts/00000/projects/00000/connections/00000",
178+
)
179+
174180
# Sanitize API key from service response (/tests/connections)
175181
add_body_key_sanitizer(json_path="properties.credentials.key", value="Sanitized")
176182

sdk/ai/azure-ai-agents/tests/test_agents_client.py

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
CodeInterpreterToolResource,
3636
ConnectedAgentTool,
3737
DeepResearchTool,
38+
FabricTool,
3839
FilePurpose,
3940
FileSearchTool,
4041
FileSearchToolCallContent,
@@ -66,11 +67,13 @@
6667
RunStepDeltaCustomBingGroundingToolCall,
6768
RunStepDeltaBingGroundingToolCall,
6869
RunStepDeltaFileSearchToolCall,
70+
RunStepDeltaMicrosoftFabricToolCall,
6971
RunStepDeltaOpenAPIToolCall,
7072
RunStepDeltaToolCallObject,
7173
RunStepFileSearchToolCall,
7274
RunStepFileSearchToolCallResult,
7375
RunStepFileSearchToolCallResults,
76+
RunStepMicrosoftFabricToolCall,
7477
RunStepOpenAPIToolCall,
7578
RunStepToolCallDetails,
7679
RunStatus,
@@ -3518,6 +3521,40 @@ def test_custom_bing_grounding_tool_streaming(self, **kwargs):
35183521
),
35193522
)
35203523

3524+
@agentClientPreparer()
3525+
@recorded_by_proxy
3526+
def test_microsoft_fabric_tool(self, **kwargs):
3527+
"""Test Microsoft Fabric tool call in non-streaming Scenario."""
3528+
with self.create_client(by_endpoint=True, **kwargs) as client:
3529+
model_name = "gpt-4o"
3530+
fabric_tool = FabricTool(connection_id=kwargs.get("azure_ai_agents_tests_fabric_connection_id"))
3531+
3532+
self._do_test_tool(
3533+
client=client,
3534+
model_name=model_name,
3535+
tool_to_test=fabric_tool,
3536+
instructions="You are helpful agent",
3537+
prompt="What are top 3 weather events with largest revenue loss?",
3538+
expected_class=RunStepMicrosoftFabricToolCall,
3539+
)
3540+
3541+
@agentClientPreparer()
3542+
@recorded_by_proxy
3543+
def test_microsoft_fabric_tool_streaming(self, **kwargs):
3544+
"""Test Microsoft Fabric tool call in streaming Scenario."""
3545+
with self.create_client(by_endpoint=True, **kwargs) as client:
3546+
model_name = "gpt-4o"
3547+
fabric_tool = FabricTool(connection_id=kwargs.get("azure_ai_agents_tests_fabric_connection_id"))
3548+
3549+
self._do_test_tool_streaming(
3550+
client=client,
3551+
model_name=model_name,
3552+
tool_to_test=fabric_tool,
3553+
instructions="You are helpful agent",
3554+
prompt="What are top 3 weather events with largest revenue loss?",
3555+
expected_delta_class=RunStepDeltaMicrosoftFabricToolCall,
3556+
)
3557+
35213558
def _do_test_tool(
35223559
self,
35233560
client,

0 commit comments

Comments
 (0)