Skip to content

Report the system model's usage from Foundation Models 27 - #353

Open
akbashev wants to merge 3 commits into
huggingface:mainfrom
akbashev:fix/system-model-usage
Open

akbashev wants to merge 3 commits into
huggingface:mainfrom
akbashev:fix/system-model-usage

Conversation

@akbashev

Copy link
Copy Markdown
Contributor

Builds on #336. Only the last commit, "Report the system model's usage from Foundation Models 27", belongs to this PR.

Why

The system model reports no usage: its responses and snapshots are always zero. From OS 27, Foundation Models reports usage, and with #336 ALM reports it the same way: a response's usage is its final request, and session.usage counts every request.

What

  • From OS 27, a response's and each snapshot's usage is Foundation Models' usage for the final request.
  • The earlier requests come from the Foundation Models session's usage. The system model makes a session for each request, so its usage is that response's total. ALM's session.usage counts every request, as Foundation Models' does.
  • When the delegate stops at a tool call, the response has no Foundation Models usage of its own. The session's usage when each call started tells the stopping round from earlier ones: calls of the same round all see the same usage.
  • Before OS 27, on compilers before Swift 6.4, and on tvOS, where Foundation Models has no usage, the counts stay zero.

Measured on macOS 27 with a tool round: the response reported 186 input and 29 output tokens, and session.usage 320 and 49, the first request's 134 input tokens plus the final request's 186.

Tests

  • stopSplitsTheStoppingRoundFromEarlierOnes, with no model: a stop in the second round, after two calls in the first, and a stop in the first round.
  • On the device, when the system model is available:
    • responseReportsTheFinalRequest.
    • streamedResponseReportsTheFinalRequest: the running total never goes down.
    • stoppedResponseCountsItsRequests.

@akbashev
akbashev force-pushed the fix/system-model-usage branch from baa0302 to 9b273f8 Compare October 22, 2026 12:04
@akbashev

Copy link
Copy Markdown
Contributor Author

Rebased onto main now that #336 is merged.

@akbashev
akbashev force-pushed the fix/system-model-usage branch from 9b273f8 to b64760b Compare October 22, 2026 13:35
@akbashev

akbashev commented Oct 22, 2026 •

Copy link
Copy Markdown
Contributor Author

@mattt
Rebased onto main after #357, and the system model now counts its requests the way #357 does: each round's usage is reported as the next tool call starts, so a response that throws after a round (a failing tool, say) keeps that round's usage. Test: failedResponseCountsTheRoundsItCompleted, which fails on the rebase alone (the session counts 0) and passes with the change.

@mattt mattt left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, @akbashev. Counting each round as the next tool call starts looks right to me.

One case still loses usage, though: the final round is only counted when the response returns. If anything throws after Foundation Models returns — a model that wraps this one, say, or structured content that fails to convert — that round's usage is dropped, and for a response with no tool calls, that's all of it. The Anthropic and Ollama models report each request as it completes, which is what RequestUsageChannel expects. Could you report the remaining session usage as soon as Foundation Models returns, before the content is converted, and add a test for a response that throws after that?

Non-blocking: failedResponseCountsTheRoundsItCompleted passes without checking anything when the model answers without calling the tool, and its catch accepts any error.

The system model reported no usage. From OS 27, Foundation Models reports
the final request's usage on each response and snapshot, and every request
in its session's usage, so the system model now maps both: the final
request from the response, and the earlier ones from the session, which it
makes for each request. When the delegate stops at a tool call, the session's
usage when each call started tells the stopping round from earlier ones.
Since huggingface#357, a response that isn't streamed counts each request as it
completes, so one that throws keeps what its requests used. The system
model now does too: when a tool call starts, the round that made it has
completed, and its usage is reported then. A response that throws after a
round, such as when a tool fails, keeps that round's usage.
A response's last round was counted only when the response returned, so anything that threw after Foundation Models returned, such as a model wrapping this one, dropped it: for a response without tool calls, that was all its usage. Each round's usage is now reported as Foundation Models returns, before the content is made.
@akbashev
akbashev force-pushed the fix/system-model-usage branch from c246105 to a9d1c79 Compare October 29, 2026 14:28
@akbashev

Copy link
Copy Markdown
Contributor Author

Thanks, @mattt. Done: once Foundation Models returns, the rounds' remaining usage is reported before the content is made, so a response that throws after that — a wrapping model, or content that fails to convert — keeps it. responseThatThrowsAfterFoundationModelsReturnsKeepsItsUsage wraps the system model in one that throws once it returns, for text and structured content; both counted nothing before the change.

failedResponseCountsTheRoundsItCompleted now expects the tool's own error, so it fails if the model answers without calling the tool. Rebased onto main.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants