Skip to content

Surface ModelBilling.tokenPrices on public SDK types#1633

Open
MackinnonBuck wants to merge 2 commits into
mainfrom
mackinnonbuck/expose-modelbilling-tokenprices
Open

Surface ModelBilling.tokenPrices on public SDK types#1633
MackinnonBuck wants to merge 2 commits into
mainfrom
mackinnonbuck/expose-modelbilling-tokenprices

Conversation

@MackinnonBuck

Copy link
Copy Markdown
Collaborator

Summary

ModelBilling.tokenPrices is present on the wire and in every SDK's generated protocol types, but the hand-curated public ModelBilling types exposed only multiplier. Consumers had to narrow through local augmentations to read tier pricing (e.g. to detect a distinct long-context tier and label tiers like 200K / 1M in a model picker).

This adds tokenPrices to the curated public ModelBilling across all SDKs, mirroring the shape already published in the generated schema.

Shape

  • ModelBillingTokenPrices: inputPrice, outputPrice, cachePrice (numbers), batchSize, contextMax (ints), longContext
  • ModelBillingTokenPricesLongContext: inputPrice, outputPrice, cachePrice (numbers), contextMax (int)

All fields optional; JSDoc/descriptions copied from the runtime schema.

Per-SDK changes

SDK Change
Node types.ts: curated interfaces + tokenPrices?; exported from index.ts
Python client.py: dataclasses with from_dict/to_dict, wired into ModelBilling; exported in __init__.py
Go types.go: structs + TokenPrices pointer field
.NET Types.cs: sealed classes + property + [JsonSerializable] registrations
Java new ModelBillingTokenPrices.java / ...LongContext.java + field on ModelBilling.java
Rust types.rs: re-export the two nested generated types (generated ModelBilling already carries token_prices)

No generated files were modified — they already carry the field. No runtime changes required.

Tests

Added coverage exercising the new field in every SDK (round-trip (de)serialization for Python/Go/.NET, deserialization assertions for Java, type + passthrough for Node, and re-export verification for Rust). All passing.

The generated wire types already carry `ModelBilling.tokenPrices`, but the
hand-curated public `ModelBilling` types exposed only `multiplier`, forcing
consumers to augment locally to read tier pricing.

Add `tokenPrices` (with `inputPrice`, `outputPrice`, `cachePrice`, `batchSize`,
`contextMax`, and a `longContext` tier) to the curated public `ModelBilling`
across all SDKs:

- Node, Python, Go, .NET, Java: declare curated `ModelBillingTokenPrices` and
  `ModelBillingTokenPricesLongContext` types matching each repo's conventions,
  plus the required public exports/registrations.
- Rust: re-export the two nested generated types alongside `ModelBilling`.

Add tests exercising the new field in every SDK.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@MackinnonBuck MackinnonBuck requested a review from a team as a code owner June 11, 2026 18:03
Copilot AI review requested due to automatic review settings June 11, 2026 18:03

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This PR surfaces ModelBilling.tokenPrices on the curated, public-facing ModelBilling types across the SDKs (Node, Python, Go, .NET, Java, Rust), aligning the hand-authored public API with an already-present wire/generated schema field so consumers can read tiered token pricing (including long-context tier details).

Changes:

  • Added public ModelBillingTokenPrices / ...LongContext types (or re-exports) and exposed tokenPrices on ModelBilling across SDKs.
  • Updated per-language serialization/deserialization paths (where applicable) to carry tokenPrices.
  • Added/expanded tests in each SDK to validate the new field is preserved through (de)serialization and public exports.
Show a summary per file
File Description
rust/tests/session_test.rs Extends model-list test payload and asserts access to re-exported token price types.
rust/src/types.rs Re-exports generated ModelBillingTokenPrices* types via the public types module.
python/test_client.py Adds round-trip tests for ModelBilling.tokenPrices and the nested long-context tier.
python/copilot/client.py Introduces dataclasses + (de)serialization for tokenPrices and wires into ModelBilling.
python/copilot/init.py Exports the new Python billing token price types at package level.
nodejs/test/client.test.ts Adds billing.tokenPrices to test model payload and asserts deep access.
nodejs/src/types.ts Adds curated public TS interfaces for ModelBillingTokenPrices* and ModelBilling.tokenPrices.
nodejs/src/index.ts Exports the new TS public types from the package entrypoint.
java/src/test/java/com/github/copilot/MetadataApiTest.java Extends deserialization test to assert token price fields and long-context tier parsing.
java/src/main/java/com/github/copilot/rpc/ModelBillingTokenPricesLongContext.java Adds public RPC type for long-context tier pricing.
java/src/main/java/com/github/copilot/rpc/ModelBillingTokenPrices.java Adds public RPC type for token-level pricing, including long-context tier.
java/src/main/java/com/github/copilot/rpc/ModelBilling.java Adds tokenPrices field to ModelBilling.
go/types.go Adds TokenPrices pointer field to ModelBilling and new pricing structs.
go/client_test.go Adds JSON (un)marshal + round-trip test for ModelBilling.tokenPrices.
dotnet/test/Unit/SerializationTests.cs Adds serializer round-trip test for ModelBilling.TokenPrices using SDK options.
dotnet/src/Types.cs Adds new .NET types for token prices + source-gen registrations and adds TokenPrices to ModelBilling.

Copilot's findings

  • Files reviewed: 16/16 changed files
  • Comments generated: 3

Comment thread python/copilot/client.py
Comment thread python/copilot/client.py
Comment thread java/src/main/java/com/github/copilot/rpc/ModelBilling.java
@github-actions

This comment has been minimized.

Honor the "all billing fields optional" contract surfaced in review:

- Python: deserialize present-but-empty `tokenPrices`/`longContext` objects
  (`{}`) into instances with all fields None rather than collapsing them to
  None, using presence (`is not None`) checks instead of truthiness.
- Java: change `ModelBilling.multiplier` from primitive `double` to boxed
  `Double` so "not present on the wire" is distinguishable from an explicit
  0.0; the global NON_NULL ObjectMapper omits it when null.

Add tests covering both behaviors.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copilot's findings

  • Files reviewed: 16/16 changed files
  • Comments generated: 0 new

@github-actions

Copy link
Copy Markdown
Contributor

Cross-SDK Consistency Review ✅

This PR adds ModelBillingTokenPrices and ModelBillingTokenPricesLongContext to the public ModelBilling type and demonstrates excellent cross-SDK consistency. All six SDKs are updated in lock step.

Coverage

SDK New Types Exported Tests
Node.js ModelBillingTokenPrices, ModelBillingTokenPricesLongContext interfaces index.ts
Python ModelBillingTokenPrices, ModelBillingTokenPricesLongContext dataclasses __init__.py
Go ModelBillingTokenPrices, ModelBillingTokenPricesLongContext structs ✅ public package
.NET ModelBillingTokenPrices, ModelBillingTokenPricesLongContext sealed classes + [JsonSerializable]
Java ModelBillingTokenPrices.java, ModelBillingTokenPricesLongContext.java ✅ public package
Rust Re-exported generated ModelBillingTokenPrices, ModelBillingTokenPricesLongContext types.rs

API Shape Consistency

All fields are correctly mapped to language-idiomatic types and names:

Wire field Node.js Python Go .NET Java Rust
inputPrice number? float|None *float64 double? Double Option<f64>
outputPrice number? float|None *float64 double? Double Option<f64>
cachePrice number? float|None *float64 double? Double Option<f64>
batchSize number? int|None *int int? Integer Option<i64>
contextMax number? int|None *int int? Integer Option<i64>
longContext ...|undefined ...|None *ModelBilling... ...? ModelBilling... Option<...>

Noteworthy: Java multiplier type change

The PR changes Java ModelBilling.multiplier from double (primitive, non-nullable) to Double (boxed, nullable), aligning it with all other SDKs where multiplier is optional. This is a potentially breaking API change for Java consumers who stored the return value as a primitive double without null checks (auto-unboxing a null Double throws NullPointerException).

The change is correct and improves consistency — multiplier was the only SDK where this field was non-nullable — and the PR adds a specific test (testModelBillingSerializationOmitsNullMultiplier) verifying the new behavior. This is worth calling out in release notes.


Overall, this is a thorough, well-structured update. No cross-SDK gaps found.

Generated by SDK Consistency Review Agent for issue #1633 · sonnet46 1.1M ·

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copilot's findings

  • Files reviewed: 16/16 changed files
  • Comments generated: 0 new

@SteveSandersonMS SteveSandersonMS left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Looks great.

@edburns Can you give an opinion on the Java breaking change (double -> Double) here? Will this be acceptable in a patch release or do we need to find an alternative?

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.

3 participants