microsoft / microsoft/agent-framework

.NET: Python: [Bug]: Foundry Hosting omits usage from /responses terminal events

Open
#7,416 1 comment 0 reactions 1 assignee View on GitHub

@TaoChenOSU is already working on this.

Since Jul 30, 2026.

.NET hosting python
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

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.