microsoft / microsoft/agent-framework

Python: [Bug]: Harness Agent drops structured value on the streaming path with tool approval

Open
#7,418 3 comments 0 reactions 1 assignee Claimed by @westey-m View on GitHub
agents harness likely-fixed python
Dominant language
Python
Stars
13.6k
Forks
2.3k
Avg merge
2d 45m
Merged PRs (30d)
358

Description

### Description

**What happened?**

`ToolApprovalMiddleware._process_stream` finalizes the outer stream with `AgentResponse.from_updates` without passing `output_format_type`:

return ResponseStream(_stream(), finalizer=AgentResponse.from_updates)

Because `from_updates` receives the text updates but no `response_format`, the final `AgentResponse.value` is `None` for streaming runs that combine tool approval with a structured-output schema.

The non-streaming path is unaffected because it returns the inner `AgentResponse` directly.

**What did you expect to happen?**

`response.value` should be parsed into the Pydantic model / schema supplied via `response_format`, matching the non-harness streaming path in `_agents.py:1229`.

**Steps to reproduce the issue**

1. Create a `create_harness_agent(...)` with `stream=True`.
2. Register a tool that requires approval.
3. Pass `options={"response_format": SomePydanticModel}` to `agent.run`.
4. Inspect `response.value` — it is `None`.

Same "per-run concept lost by a re-wrapping layer" family as #7402 and #7236.

### Code Sample

```markdown
from typing import Any

import asyncio
from pydantic import BaseModel

from agent_framework import (
AgentSession,
BaseChatClient,
ChatResponse,
Message,
create_harness_agent,
)

class Answer(BaseModel):
answer: str

class EchoTool:
@tool(approval_mode="always_require")
async def echo(self, text: str) -> str:
return text

class FakeStreamingClient(BaseChatClient):
async def _inner_get_response(self, *, messages, stream, options, **kwargs):
return ChatResponse(
messages=[Message("assistant", ['{"answer": "42"}'])]
)

async def main():
agent = create_harness_agent(
client=FakeStreamingClient(),
tools=[EchoTool().echo],
stream=True,
)
session = AgentSession()

response = await agent.run(
"Call echo and return an Answer.",
session=session,
options={"response_format": Answer},
)

print(type(response.value)) # Currently: NoneType
assert isinstance(response.value, Answer) # Fails today

asyncio.run(main())
```

### Error Messages / Stack Traces

```markdown

```

### Package Versions

agent-framework-core: 1.12.1

### Python Version

_No response_

### Additional Context

_No response_

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.