microsoft / microsoft/agent-framework
.NET: Python: [Bug]: Foundry Hosting omits usage from /responses terminal events
@TaoChenOSU is already working on this.
Since Jul 30, 2026.
- Dominant language
- Python
- Stars
- 13.6k
- Forks
- 2.3k
- Avg merge
- 2d 45m
- Merged PRs (30d)
- 358
Description
### Description
When a Python MAF agent is served through `agent_framework_foundry_hosting.ResponsesHostServer`, the OpenAI-compatible `/responses` result omits `response.usage` from the terminal event. This affects both streaming and non-streaming requests, regular agents and workflow agents.
The underlying MAF response data already carries usage:
- Non-streaming `AgentResponse` exposes `usage_details`.
- Streaming providers emit `Content(type="usage", usage_details=...)`, which `ChatResponse.from_updates` normally aggregates.
- `azure-ai-agentserver-responses` supports `ResponseEventStream.emit_completed(usage=...)` and `emit_failed(..., usage=...)`.
However, Foundry Hosting never passes usage to those terminal event builders.
On current `main` at `ce5ee8a9c711288c59d411782e9c393d020d54f4`:
1. The non-streaming regular-agent path obtains the complete response, but converts only `response.messages`; `response.usage_details` is ignored. It then calls `emit_completed()` without usage:
https://github.com/microsoft/agent-framework/blob/ce5ee8a9c711288c59d411782e9c393d020d54f4/python/packages/foundry_hosting/agent_framework_foundry_hosting/_responses.py#L622-L652
2. The streaming regular-agent path processes every `update.contents` item but has no usage accumulator, then also calls `emit_completed()` without usage:
https://github.com/microsoft/agent-framework/blob/ce5ee8a9c711288c59d411782e9c393d020d54f4/python/packages/foundry_hosting/agent_framework_foundry_hosting/_responses.py#L632-L652
3. Workflow-agent paths have the same omission:
https://github.com/microsoft/agent-framework/blob/ce5ee8a9c711288c59d411782e9c393d020d54f4/python/packages/foundry_hosting/agent_framework_foundry_hosting/_responses.py#L772-L814
4. The failure path calls `emit_failed(message=...)` without any accumulated usage:
https://github.com/microsoft/agent-framework/blob/ce5ee8a9c711288c59d411782e9c393d020d54f4/python/packages/foundry_hosting/agent_framework_foundry_hosting/_responses.py#L834-L854
MAF's existing usage-content aggregation can be seen here:
https://github.com/microsoft/agent-framework/blob/ce5ee8a9c711288c59d411782e9c393d020d54f4/python/packages/core/agent_framework/_types.py#L1982-L1986
**Expected:** the terminal response includes aggregate usage:
```json
{
"type": "response.completed",
"response": {
"usage": {
"input_tokens": 100,
"input_tokens_details": {
"cache_write_tokens": 0,
"cached_tokens": 0
},
"output_tokens": 25,
"output_tokens_details": {
"reasoning_tokens": 0
},
"total_tokens": 125
}
}
}
```
**Actual:** the terminal `response.completed.response` has no `usage` key.
This prevents OpenAI-compatible clients of a Foundry-hosted MAF agent from performing token accounting, cost estimation, cache analysis, or reasoning-token analysis.
The .NET Foundry Hosting implementation already follows the expected pattern: it accumulates `UsageContent` and passes the result to `EmitCompleted(accumulatedUsage)`.
### Code Sample
```python
from openai import AsyncOpenAI
client = AsyncOpenAI(base_url=HOSTED_AGENT_ENDPOINT, api_key=API_KEY)
stream = await client.responses.create(
model="my-agent",
input="Hello",
stream=True,
)
async for event in stream:
if event.type == "response.completed":
print(event.response.usage) # None; the wire payload omits "usage"
```
### Error Messages / Stack Traces
No error is raised. The response succeeds but silently omits usage.
### Package Versions
`agent-framework-foundry-hosting`: current main at `ce5ee8a9c711288c59d411782e9c393d020d54f4`; `azure-ai-agentserver-responses`: `1.0.0b8`
### Python Version
Python 3.12
### Additional Context
A fix should:
- Convert MAF `UsageDetails` fields to the Responses `ResponseUsage` schema.
- Preserve `input_token_count`, `output_token_count`, `total_token_count`, `cache_creation_input_token_count`, `cache_read_input_token_count`, and `reasoning_output_token_count`.
- Accumulate usage across streaming updates, tool-loop iterations, and workflow executors.
- Pass accumulated usage to successful and failed terminal events when available.
- Add assertions for the serialized numeric usage payload, not only the terminal event type.
Related but not duplicate:
- #6823 covers incorrect cached/reasoning detail mapping in **.NET** Foundry Hosting, where usage is already emitted.
- #7051 covered usage loss while Python `FoundryChatClient` consumed an incomplete upstream response, not while Python Foundry Hosting produced `/responses` events.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Assessment
This issue has not been assessed yet.