Repository navigation
feat(go-sdk): add token usage to RUN_FINISHED and RUN_ERROR - #2685
Merged
Merged
Conversation
Ports the TokenUsage type and the usage field that TypeScript, Python and .NET already ship. Usage is a list, one entry per (provider, model), so a run that invokes several models keeps them separate; consumers that only need totals sum across entries. It lives on the terminal event rather than per-event, and RUN_ERROR carries the partial usage of a run that failed after one or more model calls completed. Counts are pointers because zero is meaningful data: a call that produced no output tokens is not the same as a call that did not report the figure. int64 matches the protobuf int64 and .NET long? bindings. The type deliberately carries no content-bearing or identifying fields, only provider and model labels and numeric counts, because usage feeds anonymous telemetry. Validate rejects a negative count. The peer SDKs constrain every count to a non-negative integer, and the protobuf transport writes them through an int64 writer, so catching it at the source beats a rejected event or a mid-stream crash at the receiver.
Contributor
Python Preview PackagesVersion
Install with uvAdd the TestPyPI index to your [[tool.uv.index]]
name = "testpypi"
url = "https://test.pypi.org/simple/"
explicit = trueThen install the packages you need: # Core SDK
uv add 'ag-ui-protocol==0.0.0.dev1788888863' --index testpypi
# Integrations (each already depends on the matching ag-ui-protocol preview)
uv add 'ag-ui-langgraph==0.0.0.dev1788888863' --index testpypi
uv add 'ag-ui-crewai==0.0.0.dev1788888863' --index testpypi
# NOTE: ag-ui-agent-spec depends on pyagentspec (git-only, not on PyPI).
# You will need to install pyagentspec separately from its git repo.
uv add 'ag-ui-agent-spec==0.0.0.dev1788888863' --index testpypi
uv add 'ag_ui_adk==0.0.0.dev1788888863' --index testpypi
uv add 'ag_ui_strands==0.0.0.dev1788888863' --index testpypiInstall with pippip install \
--index-url https://test.pypi.org/simple/ \
--extra-index-url https://pypi.org/simple/ \
ag-ui-protocol==0.0.0.dev1788888863
Commit: 2d87c6b |
@ag-ui/a2a-middleware
@ag-ui/a2ui-middleware
@ag-ui/event-throttle-middleware
@ag-ui/mcp-apps-middleware
@ag-ui/mcp-middleware
@ag-ui/a2a
@ag-ui/adk
@ag-ui/ag2
@ag-ui/agno
@ag-ui/aws-strands
@ag-ui/claude-agent-sdk
@ag-ui/claude-managed-agents
@ag-ui/crewai
@ag-ui/langchain
@ag-ui/langgraph
@ag-ui/llamaindex
@ag-ui/mastra
@ag-ui/pydantic-ai
@ag-ui/vercel-ai-sdk
@ag-ui/watsonx
@ag-ui/a2ui-toolkit
create-ag-ui-app
@ag-ui/client
@ag-ui/core
@ag-ui/encoder
@ag-ui/proto
commit: |
1 task done
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #2684
Ports the
TokenUsagetype and theusagefield that TypeScript, Python and .NET already ship. The Go SDK had zero occurrences of either, so a Go agent could not report what a run cost and usage a peer SDK sent was dropped on decode.Third in the same series, after #2678 (metadata and subagent events) and #2683 (RunFinished outcome guards). Rebased onto current main, so it applies cleanly on top of both.
What lands
TokenUsage—provider,model,inputTokens,outputTokens,totalTokens,reasoningTokens,cachedInputTokens. Numeric counts and labels only, with no content-bearing or identifying fields, matching the peer SDKs' stated reason: usage feeds anonymous telemetry.usage []TokenUsageonRunFinishedEventandRunErrorEvent, withWithUsageandWithErrorUsageoptions. A list, so a run invoking several models keeps one entry per (provider, model);RUN_ERRORcarries the partial usage of a run that failed after one or more model calls completed.Validate()rejects a negative count, naming the offending entry and field.Decisions worth a reviewer's eye
*int64, not plain integers. Zero is meaningful data: a call that produced no output tokens is not the same as a call that did not report the figure, andomitemptyon a plain int erases that distinction.int64matches the protobufint64and .NETlong?bindings. There is aTokenCount(int64) *int64helper so populating one does not need a temporary variable at every call site.Validate()rather than clamped or ignored. TypeScript declares every countz.number().int().nonnegative(), and the protobuf transport writes them through an int64 writer that throws on a bad value — so catching it at the source beats a rejected event, or a mid-stream crash, at the receiver. This follows the same reasoning as fix(go-sdk): stop RunFinished emitting an outcome the peer SDKs reject #2683's interrupt-outcome check.validateUsageis shared between the two events rather than duplicated, and names the event in the error so messages match the rest of the package (RunFinishedEvent validation failed: usage[1]: ...).token_usage.pyof those; this PR ports only the protocol surface. Mapping helpers can follow if there is appetite for them.Testing
go test ./...passes, andgofmt/go vetare clean on every touched file. (events.goandreasoning_events.gocarry pre-existinggofmtdrift in a const block, untouched here rather than mixing an unrelated reformat into the diff.)Beyond the unit tests, wire compatibility was verified against the real peer parsers rather than by inspection — the TypeScript zod schemas in
sdks/typescript/packages/core/srcand the Python pydantic models inag_ui.core, run over the exact bytes Go emits:RUN_FINISHEDwith two full usage entriesRUN_ERRORwith partial usageRUN_FINISHEDwith no usageusagekeyusagekey"Nothing stripped" is the check that matters here:
TokenUsageSchemadrops unrecognized keys silently, so a near-miss on field names would lose the counts with no error. Comparing what zod returns against what Go sent confirms every field survives.No breaking changes:
usageis optional and omitted when absent, so every existing event serializes exactly as before.