Address comments and fix pipeline

This commit is contained in:
Tao Chen
2026-06-02 15:02:24 -07:00
Unverified
parent 4f942a3b3f
commit 072c5ad39d
2 changed files with 66 additions and 4 deletions
@@ -1648,6 +1648,7 @@ class _ToolApprovalMockAgent(SupportsAgentRun):
session: AgentSession | None = ...,
**kwargs: Any,
) -> Awaitable[AgentResponse[Any]]: ...
@overload
def run(
self,
@@ -1910,6 +1911,61 @@ class TestWorkflowAgentToolApproval:
assert "done" in final_text
assert approval_id in final_text
async def test_tool_approval_response_rejected_resumes_agent(self) -> None:
"""Rejection path: ``approved=False`` is forwarded to the inner agent and clears the pending request.
The WorkflowAgent must route a rejection response back to the paused
``AgentExecutor`` exactly the same way as an approval — only the
``approved`` flag differs. The inner agent decides what to do with it.
"""
approval_id = "approval-reject-1"
mock_agent = _ToolApprovalMockAgent(
name="approval-reject-agent",
tool_name="delete_file",
tool_arguments={"path": "/tmp/x"},
approval_request_ids=[approval_id],
)
@executor
async def start(messages: list[Message], ctx: WorkflowContext[AgentExecutorRequest]) -> None:
await ctx.send_message(AgentExecutorRequest(messages=messages, should_respond=True))
workflow = WorkflowBuilder(start_executor=start).add_edge(start, mock_agent).build()
agent = WorkflowAgent(workflow=workflow, name="Approval Reject Agent")
first_result = await agent.run("delete it")
approval = self._find_approval_request(
[c for m in first_result.messages for c in m.contents],
tool_name="delete_file",
)
assert approval is not None
assert mock_agent.run_count == 1
# Reject the tool invocation.
approval_response = approval.to_function_approval_response(approved=False) # type: ignore[attr-defined]
response_message = Message(role="user", contents=[approval_response])
final_result = await agent.run(response_message)
assert isinstance(final_result, AgentResponse)
# The inner agent must have been resumed and seen ``approved=False``.
assert mock_agent.run_count == 2
approvals_seen = [
c for m in mock_agent.last_run_messages for c in m.contents if c.type == "function_approval_response"
]
assert len(approvals_seen) == 1
assert approvals_seen[0].id == approval_id # type: ignore[attr-defined]
assert approvals_seen[0].approved is False # type: ignore[attr-defined]
# Pending approval cleared regardless of approve/reject.
pending = await workflow._runner_context.get_pending_request_info_events()
assert approval_id not in pending
# The final assistant message reflects the rejection.
final_text = " ".join(m.text or "" for m in final_result.messages)
assert "approved=False" in final_text
assert approval_id in final_text
async def test_tool_approval_request_id_matches_pending_request(self) -> None:
"""The approval request id surfaced by WorkflowAgent matches the workflow's pending request id.
@@ -28,6 +28,7 @@ import zipfile
from pathlib import Path
from azure.ai.projects.aio import AIProjectClient
from azure.ai.projects.models import CreateSkillVersionFromFilesBody
from azure.core.exceptions import ResourceNotFoundError
from azure.identity.aio import DefaultAzureCredential
from dotenv import load_dotenv
@@ -68,8 +69,13 @@ async def main() -> None:
name = skill_md.parent.name
print(f"Provisioning skill '{name}' from {skill_md.relative_to(SKILLS_DIR.parent)}...")
await _delete_skill_if_exists(project, name)
imported = await project.beta.skills.create_from_package(_zip_skill_md(skill_md))
print(f" Imported skill '{imported.name}' (id={imported.skill_id}, has_blob={imported.has_blob}).")
imported = await project.beta.skills.create_from_files(
name,
content=CreateSkillVersionFromFilesBody(
files=[(f"{name}.zip", _zip_skill_md(skill_md), "application/zip")]
),
)
print(f" Imported skill '{imported.name}' (id={imported.skill_id}, version={imported.version}).")
print("Verifying skills via project.beta.skills.list()...")
listed = {skill.name: skill async for skill in project.beta.skills.list()}
@@ -79,8 +85,8 @@ async def main() -> None:
if skill is None:
raise RuntimeError(f"Skill '{name}' was imported but is not present in the project listing.")
print(
f" OK '{skill.name}': id={skill.skill_id}, "
f"description={skill.description!r}, has_blob={skill.has_blob}"
f" OK '{skill.name}': id={skill.id}, "
f"description={skill.description!r}, default_version={skill.default_version}"
)
print("Done.")