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
- Create a
create_harness_agent(...) with stream=True.
- Register a tool that requires approval.
- Pass
options={"response_format": SomePydanticModel} to agent.run.
- Inspect
response.value — it is None.
Same "per-run concept lost by a re-wrapping layer" family as #7402 and #7236.
Code Sample
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
Package Versions
agent-framework-core: 1.12.1
Python Version
No response
Additional Context
No response
Description
What happened?
ToolApprovalMiddleware._process_streamfinalizes the outer stream withAgentResponse.from_updateswithout passingoutput_format_type:return ResponseStream(_stream(), finalizer=AgentResponse.from_updates)
Because
from_updatesreceives the text updates but noresponse_format, the finalAgentResponse.valueisNonefor streaming runs that combine tool approval with a structured-output schema.The non-streaming path is unaffected because it returns the inner
AgentResponsedirectly.What did you expect to happen?
response.valueshould be parsed into the Pydantic model / schema supplied viaresponse_format, matching the non-harness streaming path in_agents.py:1229.Steps to reproduce the issue
create_harness_agent(...)withstream=True.options={"response_format": SomePydanticModel}toagent.run.response.value— it isNone.Same "per-run concept lost by a re-wrapping layer" family as #7402 and #7236.
Code Sample
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
Package Versions
agent-framework-core: 1.12.1
Python Version
No response
Additional Context
No response