diff --git a/dotnet/src/Types.cs b/dotnet/src/Types.cs
index 7a2ad2951..b408f7216 100644
--- a/dotnet/src/Types.cs
+++ b/dotnet/src/Types.cs
@@ -3308,6 +3308,12 @@ public sealed class ModelBilling
///
[JsonPropertyName("multiplier")]
public double? Multiplier { get; set; }
+
+ ///
+ /// Token-level pricing information for this model.
+ ///
+ [JsonPropertyName("tokenPrices")]
+ public ModelBillingTokenPrices? TokenPrices { get; set; }
}
///
@@ -3509,6 +3515,8 @@ public sealed class SystemMessageTransformRpcResponse
[JsonSerializable(typeof(McpServerConfig))]
[JsonSerializable(typeof(MessageOptions))]
[JsonSerializable(typeof(ModelBilling))]
+[JsonSerializable(typeof(GitHub.Copilot.Rpc.ModelBillingTokenPrices))]
+[JsonSerializable(typeof(GitHub.Copilot.Rpc.ModelBillingTokenPricesLongContext))]
[JsonSerializable(typeof(ModelCapabilities))]
[JsonSerializable(typeof(ModelCapabilitiesOverride))]
[JsonSerializable(typeof(ModelInfo))]
diff --git a/dotnet/test/Unit/SerializationTests.cs b/dotnet/test/Unit/SerializationTests.cs
index b0797d34b..65d92c7fd 100644
--- a/dotnet/test/Unit/SerializationTests.cs
+++ b/dotnet/test/Unit/SerializationTests.cs
@@ -50,6 +50,56 @@ public void ProviderConfig_CanSerializeHeaders_WithSdkOptions()
Assert.Equal(4096, deserialized.MaxOutputTokens);
}
+ [Fact]
+ public void ModelBilling_CanSerializeTokenPrices_WithSdkOptions()
+ {
+ var options = GetSerializerOptions();
+ var original = new ModelBilling
+ {
+ Multiplier = 1.5,
+ TokenPrices = new GitHub.Copilot.Rpc.ModelBillingTokenPrices
+ {
+ InputPrice = 2.0,
+ OutputPrice = 8.0,
+ CachePrice = 0.5,
+ BatchSize = 1_000_000L,
+ ContextMax = 128_000L,
+ LongContext = new GitHub.Copilot.Rpc.ModelBillingTokenPricesLongContext
+ {
+ InputPrice = 4.0,
+ OutputPrice = 16.0,
+ CachePrice = 1.0,
+ ContextMax = 1_000_000L
+ }
+ }
+ };
+
+ var json = JsonSerializer.Serialize(original, options);
+ using var document = JsonDocument.Parse(json);
+ var root = document.RootElement;
+ Assert.Equal(1.5, root.GetProperty("multiplier").GetDouble());
+ var tokenPrices = root.GetProperty("tokenPrices");
+ Assert.Equal(2.0, tokenPrices.GetProperty("inputPrice").GetDouble());
+ Assert.Equal(8.0, tokenPrices.GetProperty("outputPrice").GetDouble());
+ Assert.Equal(0.5, tokenPrices.GetProperty("cachePrice").GetDouble());
+ Assert.Equal(1_000_000L, tokenPrices.GetProperty("batchSize").GetInt64());
+ Assert.Equal(128_000L, tokenPrices.GetProperty("contextMax").GetInt64());
+ var longContext = tokenPrices.GetProperty("longContext");
+ Assert.Equal(4.0, longContext.GetProperty("inputPrice").GetDouble());
+ Assert.Equal(1_000_000L, longContext.GetProperty("contextMax").GetInt64());
+
+ var deserialized = JsonSerializer.Deserialize(json, options);
+ Assert.NotNull(deserialized);
+ Assert.Equal(1.5, deserialized.Multiplier);
+ Assert.NotNull(deserialized.TokenPrices);
+ Assert.Equal(2.0, deserialized.TokenPrices.InputPrice);
+ Assert.Equal(1_000_000L, deserialized.TokenPrices.BatchSize);
+ Assert.Equal(128_000L, deserialized.TokenPrices.ContextMax);
+ Assert.NotNull(deserialized.TokenPrices.LongContext);
+ Assert.Equal(16.0, deserialized.TokenPrices.LongContext.OutputPrice);
+ Assert.Equal(1_000_000L, deserialized.TokenPrices.LongContext.ContextMax);
+ }
+
[Fact]
public void MessageOptions_CanSerializeRequestHeaders_WithSdkOptions()
{
diff --git a/go/client_test.go b/go/client_test.go
index d5ba47da8..cb6fbd9df 100644
--- a/go/client_test.go
+++ b/go/client_test.go
@@ -892,6 +892,78 @@ func TestListModelsWithCustomHandler(t *testing.T) {
}
}
+func TestModelBillingTokenPricesJSON(t *testing.T) {
+ int64Ptr := func(v int64) *int64 {
+ return &v
+ }
+
+ wire := `{
+ "multiplier": 1.5,
+ "tokenPrices": {
+ "inputPrice": 2.0,
+ "outputPrice": 8.0,
+ "cachePrice": 0.5,
+ "batchSize": 1000000,
+ "contextMax": 128000,
+ "longContext": {
+ "inputPrice": 4.0,
+ "outputPrice": 16.0,
+ "cachePrice": 1.0,
+ "contextMax": 1000000
+ }
+ }
+ }`
+ expected := rpc.ModelBillingTokenPrices{
+ InputPrice: Float64(2.0),
+ OutputPrice: Float64(8.0),
+ CachePrice: Float64(0.5),
+ BatchSize: int64Ptr(1000000),
+ ContextMax: int64Ptr(128000),
+ LongContext: &rpc.ModelBillingTokenPricesLongContext{
+ InputPrice: Float64(4.0),
+ OutputPrice: Float64(16.0),
+ CachePrice: Float64(1.0),
+ ContextMax: int64Ptr(1000000),
+ },
+ }
+
+ var billing ModelBilling
+ if err := json.Unmarshal([]byte(wire), &billing); err != nil {
+ t.Fatalf("unmarshal failed: %v", err)
+ }
+
+ if billing.TokenPrices == nil {
+ t.Fatal("expected TokenPrices to be set")
+ }
+ tp := billing.TokenPrices
+ if !reflect.DeepEqual(*tp, expected) {
+ t.Errorf("unexpected TokenPrices: %+v", tp)
+ }
+ if tp.LongContext == nil {
+ t.Fatal("expected LongContext to be set")
+ }
+ lc := tp.LongContext
+ if lc.InputPrice == nil || *lc.InputPrice != 4.0 {
+ t.Errorf("unexpected LongContext.InputPrice: %v", lc.InputPrice)
+ }
+ if lc.ContextMax == nil || *lc.ContextMax != 1000000 {
+ t.Errorf("unexpected LongContext.ContextMax: %v", lc.ContextMax)
+ }
+
+ // Round-trip back to JSON and ensure the nested structure survives.
+ out, err := json.Marshal(billing)
+ if err != nil {
+ t.Fatalf("marshal failed: %v", err)
+ }
+ var reparsed ModelBilling
+ if err := json.Unmarshal(out, &reparsed); err != nil {
+ t.Fatalf("re-unmarshal failed: %v", err)
+ }
+ if reparsed.TokenPrices == nil || !reflect.DeepEqual(*reparsed.TokenPrices, expected) {
+ t.Errorf("round-trip lost token price data: %s", out)
+ }
+}
+
func TestListModelsHandlerCachesResults(t *testing.T) {
customModels := []ModelInfo{
{
diff --git a/go/types.go b/go/types.go
index 7ffd454a3..32a8809b8 100644
--- a/go/types.go
+++ b/go/types.go
@@ -1599,7 +1599,8 @@ type ModelPolicy struct {
// ModelBilling contains model billing information
type ModelBilling struct {
- Multiplier *float64 `json:"multiplier,omitempty"`
+ Multiplier *float64 `json:"multiplier,omitempty"`
+ TokenPrices *rpc.ModelBillingTokenPrices `json:"tokenPrices,omitempty"`
}
// ModelInfo contains information about an available model
diff --git a/java/src/main/java/com/github/copilot/rpc/ModelBilling.java b/java/src/main/java/com/github/copilot/rpc/ModelBilling.java
index c7bfc72b5..f495e8747 100644
--- a/java/src/main/java/com/github/copilot/rpc/ModelBilling.java
+++ b/java/src/main/java/com/github/copilot/rpc/ModelBilling.java
@@ -4,8 +4,12 @@
package com.github.copilot.rpc;
+import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
+import com.github.copilot.generated.rpc.ModelBillingTokenPrices;
+import java.util.OptionalDouble;
/**
* Model billing information.
@@ -16,14 +20,41 @@
public class ModelBilling {
@JsonProperty("multiplier")
- private double multiplier;
+ @JsonInclude(JsonInclude.Include.NON_NULL)
+ private Double multiplier;
+ @JsonProperty("tokenPrices")
+ private ModelBillingTokenPrices tokenPrices;
+
+ @JsonIgnore
public double getMultiplier() {
- return multiplier;
+ return multiplier != null ? multiplier : 0.0;
}
public ModelBilling setMultiplier(double multiplier) {
this.multiplier = multiplier;
return this;
}
+
+ /**
+ * Returns the billing multiplier as an {@link java.util.OptionalDouble},
+ * allowing callers to distinguish "absent" from "zero".
+ *
+ * @return an {@link java.util.OptionalDouble} containing the multiplier, or
+ * {@link java.util.OptionalDouble#empty()} if not set
+ * @since 1.0.2
+ */
+ @JsonIgnore
+ public OptionalDouble getMultiplierOpt() {
+ return multiplier == null ? OptionalDouble.empty() : OptionalDouble.of(multiplier);
+ }
+
+ public ModelBillingTokenPrices getTokenPrices() {
+ return tokenPrices;
+ }
+
+ public ModelBilling setTokenPrices(ModelBillingTokenPrices tokenPrices) {
+ this.tokenPrices = tokenPrices;
+ return this;
+ }
}
diff --git a/java/src/test/java/com/github/copilot/MetadataApiTest.java b/java/src/test/java/com/github/copilot/MetadataApiTest.java
index b2c775eb1..ec3b9ea70 100644
--- a/java/src/test/java/com/github/copilot/MetadataApiTest.java
+++ b/java/src/test/java/com/github/copilot/MetadataApiTest.java
@@ -7,11 +7,14 @@
import com.fasterxml.jackson.databind.ObjectMapper;
import com.github.copilot.generated.SessionEvent;
import com.github.copilot.generated.ToolExecutionProgressEvent;
+import com.github.copilot.generated.rpc.ModelBillingTokenPrices;
+import com.github.copilot.generated.rpc.ModelBillingTokenPricesLongContext;
import com.github.copilot.rpc.*;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import java.util.List;
+import java.util.OptionalDouble;
import static org.junit.jupiter.api.Assertions.*;
@@ -143,7 +146,20 @@ void testModelInfoDeserialization() throws Exception {
"terms": "https://example.com/terms"
},
"billing": {
- "multiplier": 1.5
+ "multiplier": 1.5,
+ "tokenPrices": {
+ "inputPrice": 2.0,
+ "outputPrice": 8.0,
+ "cachePrice": 0.5,
+ "batchSize": 1000000,
+ "contextMax": 128000,
+ "longContext": {
+ "inputPrice": 4.0,
+ "outputPrice": 16.0,
+ "cachePrice": 1.0,
+ "contextMax": 1000000
+ }
+ }
}
}
""";
@@ -174,6 +190,49 @@ void testModelInfoDeserialization() throws Exception {
// Billing
assertNotNull(model.getBilling());
assertEquals(1.5, model.getBilling().getMultiplier());
+ assertEquals(OptionalDouble.of(1.5), model.getBilling().getMultiplierOpt());
+
+ // Token prices
+ ModelBillingTokenPrices tokenPrices = model.getBilling().getTokenPrices();
+ assertNotNull(tokenPrices);
+ assertEquals(2.0, tokenPrices.inputPrice());
+ assertEquals(8.0, tokenPrices.outputPrice());
+ assertEquals(0.5, tokenPrices.cachePrice());
+ assertEquals(Long.valueOf(1000000), tokenPrices.batchSize());
+ assertEquals(Long.valueOf(128000), tokenPrices.contextMax());
+
+ // Long context tier
+ ModelBillingTokenPricesLongContext longContext = tokenPrices.longContext();
+ assertNotNull(longContext);
+ assertEquals(4.0, longContext.inputPrice());
+ assertEquals(16.0, longContext.outputPrice());
+ assertEquals(1.0, longContext.cachePrice());
+ assertEquals(Long.valueOf(1000000), longContext.contextMax());
+ }
+
+ @Test
+ void testModelBillingSerializationOmitsNullMultiplier() throws Exception {
+ var billing = new ModelBilling();
+
+ String json = MAPPER.writeValueAsString(billing);
+
+ assertFalse(json.contains("multiplier"));
+ }
+
+ @Test
+ void testModelBillingMultiplierOptPresent() throws Exception {
+ ModelBilling billing = MAPPER.readValue("{\"multiplier\": 1.5}", ModelBilling.class);
+
+ assertEquals(OptionalDouble.of(1.5), billing.getMultiplierOpt());
+ assertEquals(1.5, billing.getMultiplier());
+ }
+
+ @Test
+ void testModelBillingMultiplierOptAbsent() throws Exception {
+ ModelBilling billing = MAPPER.readValue("{}", ModelBilling.class);
+
+ assertEquals(OptionalDouble.empty(), billing.getMultiplierOpt());
+ assertEquals(0.0, billing.getMultiplier());
}
@Test
diff --git a/nodejs/src/index.ts b/nodejs/src/index.ts
index c044f2b94..df2a3cf64 100644
--- a/nodejs/src/index.ts
+++ b/nodejs/src/index.ts
@@ -81,6 +81,8 @@ export type {
DefaultAgentConfig,
MessageOptions,
ModelBilling,
+ ModelBillingTokenPrices,
+ ModelBillingTokenPricesLongContext,
ModelCapabilities,
ModelCapabilitiesOverride,
ModelInfo,
diff --git a/nodejs/src/types.ts b/nodejs/src/types.ts
index 75aa5159f..f6636e2df 100644
--- a/nodejs/src/types.ts
+++ b/nodejs/src/types.ts
@@ -14,10 +14,17 @@ import type {
SessionEvent as GeneratedSessionEvent,
} from "./generated/session-events.js";
import type { CopilotSession } from "./session.js";
-import type { RemoteSessionMode } from "./generated/rpc.js";
-import type { OpenCanvasInstance } from "./generated/rpc.js";
+import type {
+ ModelBillingTokenPrices,
+ OpenCanvasInstance,
+ RemoteSessionMode,
+} from "./generated/rpc.js";
import type { ToolSet } from "./toolSet.js";
export type { RemoteSessionMode } from "./generated/rpc.js";
+export type {
+ ModelBillingTokenPrices,
+ ModelBillingTokenPricesLongContext,
+} from "./generated/rpc.js";
export type SessionEvent = GeneratedSessionEvent;
export type { ReasoningSummary } from "./generated/session-events.js";
export type { SessionFsProvider } from "./sessionFsProvider.js";
@@ -2376,7 +2383,10 @@ export interface ModelPolicy {
* Model billing information
*/
export interface ModelBilling {
+ /** Billing cost multiplier relative to the base rate */
multiplier?: number;
+ /** Token-level pricing information for this model */
+ tokenPrices?: ModelBillingTokenPrices;
}
/**
diff --git a/nodejs/test/client.test.ts b/nodejs/test/client.test.ts
index 9352eb627..0d552652b 100644
--- a/nodejs/test/client.test.ts
+++ b/nodejs/test/client.test.ts
@@ -1440,6 +1440,22 @@ describe("CopilotClient", () => {
supports: { vision: false, reasoningEffort: false },
limits: { max_context_window_tokens: 128000 },
},
+ billing: {
+ multiplier: 1.5,
+ tokenPrices: {
+ inputPrice: 2.0,
+ outputPrice: 8.0,
+ cachePrice: 0.5,
+ batchSize: 1000000,
+ contextMax: 128000,
+ longContext: {
+ inputPrice: 4.0,
+ outputPrice: 16.0,
+ cachePrice: 1.0,
+ contextMax: 1000000,
+ },
+ },
+ },
},
];
@@ -1451,6 +1467,7 @@ describe("CopilotClient", () => {
const models = await client.listModels();
expect(handler).toHaveBeenCalledTimes(1);
expect(models).toEqual(customModels);
+ expect(models[0].billing?.tokenPrices?.longContext?.contextMax).toBe(1000000);
});
it("caches onListModels results on subsequent calls", async () => {
diff --git a/python/copilot/__init__.py b/python/copilot/__init__.py
index 3f1a84d25..24deeabd1 100644
--- a/python/copilot/__init__.py
+++ b/python/copilot/__init__.py
@@ -68,6 +68,10 @@
TelemetryConfig,
UriRuntimeConnection,
)
+from .generated.rpc import (
+ ModelBillingTokenPrices,
+ ModelBillingTokenPricesLongContext,
+)
from .generated.session_events import (
PermissionRequest,
SessionEvent,
@@ -202,6 +206,8 @@
"MCPServerConfig",
"MCPStdioServerConfig",
"ModelBilling",
+ "ModelBillingTokenPrices",
+ "ModelBillingTokenPricesLongContext",
"ModelCapabilities",
"ModelCapabilitiesOverride",
"ModelInfo",
diff --git a/python/copilot/client.py b/python/copilot/client.py
index 7dcec6e8f..cfa85c0fa 100644
--- a/python/copilot/client.py
+++ b/python/copilot/client.py
@@ -62,6 +62,8 @@
)
from .generated.rpc import (
ClientSessionApiHandlers,
+ ModelBillingTokenPrices,
+ ModelBillingTokenPricesLongContext, # noqa: F401
OpenCanvasInstance,
RemoteSessionMode,
ServerRpc,
@@ -672,19 +674,25 @@ class ModelBilling:
"""Model billing information"""
multiplier: float | None = None
+ token_prices: ModelBillingTokenPrices | None = None
@staticmethod
def from_dict(obj: Any) -> ModelBilling:
assert isinstance(obj, dict)
multiplier = obj.get("multiplier")
- if multiplier is None:
- return ModelBilling()
- return ModelBilling(multiplier=float(multiplier))
+ tp = obj.get("tokenPrices")
+ token_prices = ModelBillingTokenPrices.from_dict(tp) if tp is not None else None
+ return ModelBilling(
+ multiplier=float(multiplier) if multiplier is not None else None,
+ token_prices=token_prices,
+ )
def to_dict(self) -> dict:
result: dict = {}
if self.multiplier is not None:
result["multiplier"] = self.multiplier
+ if self.token_prices is not None:
+ result["tokenPrices"] = self.token_prices.to_dict()
return result
diff --git a/python/test_client.py b/python/test_client.py
index 502d410ab..367485b26 100644
--- a/python/test_client.py
+++ b/python/test_client.py
@@ -11,6 +11,8 @@
from copilot import (
CopilotClient,
+ ModelBillingTokenPrices,
+ ModelBillingTokenPricesLongContext,
RuntimeConnection,
StdioRuntimeConnection,
define_tool,
@@ -18,6 +20,7 @@
from copilot.client import (
CloudSessionOptions,
CloudSessionRepository,
+ ModelBilling,
ModelCapabilities,
ModelInfo,
ModelLimits,
@@ -504,6 +507,80 @@ async def mock_request(method, params, **kwargs):
await client.force_stop()
+class TestModelBilling:
+ def test_token_prices_round_trip(self):
+ """ModelBilling.from_dict/to_dict round-trips tokenPrices and longContext."""
+ wire = {
+ "multiplier": 1.5,
+ "tokenPrices": {
+ "inputPrice": 2.0,
+ "outputPrice": 8.0,
+ "cachePrice": 0.5,
+ "batchSize": 1000000,
+ "contextMax": 128000,
+ "longContext": {
+ "inputPrice": 4.0,
+ "outputPrice": 16.0,
+ "cachePrice": 1.0,
+ "contextMax": 1000000,
+ },
+ },
+ }
+
+ billing = ModelBilling.from_dict(wire)
+
+ assert billing.multiplier == 1.5
+ assert isinstance(billing.token_prices, ModelBillingTokenPrices)
+ prices = billing.token_prices
+ assert prices.input_price == 2.0
+ assert prices.output_price == 8.0
+ assert prices.cache_price == 0.5
+ assert prices.batch_size == 1000000
+ assert prices.context_max == 128000
+ assert isinstance(prices.long_context, ModelBillingTokenPricesLongContext)
+ long_context = prices.long_context
+ assert long_context.input_price == 4.0
+ assert long_context.output_price == 16.0
+ assert long_context.cache_price == 1.0
+ assert long_context.context_max == 1000000
+
+ assert billing.to_dict() == wire
+
+ def test_token_prices_absent(self):
+ """ModelBilling without tokenPrices leaves token_prices unset."""
+ billing = ModelBilling.from_dict({"multiplier": 1.0})
+ assert billing.token_prices is None
+ assert billing.to_dict() == {"multiplier": 1.0}
+
+ def test_token_prices_empty_object_round_trip(self):
+ """ModelBilling preserves present but empty tokenPrices."""
+ billing = ModelBilling.from_dict({"tokenPrices": {}})
+
+ assert isinstance(billing.token_prices, ModelBillingTokenPrices)
+ prices = billing.token_prices
+ assert prices.input_price is None
+ assert prices.output_price is None
+ assert prices.cache_price is None
+ assert prices.batch_size is None
+ assert prices.context_max is None
+ assert prices.long_context is None
+ assert billing.to_dict() == {"tokenPrices": {}}
+
+ def test_long_context_empty_object_round_trip(self):
+ """ModelBilling preserves present but empty longContext."""
+ billing = ModelBilling.from_dict({"tokenPrices": {"longContext": {}}})
+
+ assert isinstance(billing.token_prices, ModelBillingTokenPrices)
+ prices = billing.token_prices
+ assert isinstance(prices.long_context, ModelBillingTokenPricesLongContext)
+ long_context = prices.long_context
+ assert long_context.input_price is None
+ assert long_context.output_price is None
+ assert long_context.cache_price is None
+ assert long_context.context_max is None
+ assert billing.to_dict() == {"tokenPrices": {"longContext": {}}}
+
+
class TestOnListModels:
@pytest.mark.asyncio
async def test_list_models_with_custom_handler(self):
diff --git a/rust/src/types.rs b/rust/src/types.rs
index 8b9b5960a..505afef49 100644
--- a/rust/src/types.rs
+++ b/rust/src/types.rs
@@ -4170,7 +4170,8 @@ impl InputFormat {
/// [`crate::rpc`]; they live here so the crate-root
/// `pub use types::*` surfaces them alongside hand-written SDK types.
pub use crate::generated::api_types::{
- Model, ModelBilling, ModelCapabilities, ModelCapabilitiesLimits, ModelCapabilitiesLimitsVision,
+ Model, ModelBilling, ModelBillingTokenPrices, ModelBillingTokenPricesLongContext,
+ ModelCapabilities, ModelCapabilitiesLimits, ModelCapabilitiesLimitsVision,
ModelCapabilitiesSupports, ModelList, ModelPolicy, PermissionDecision,
PermissionDecisionApproveOnce, PermissionDecisionReject, PermissionDecisionUserNotAvailable,
};
diff --git a/rust/tests/session_test.rs b/rust/tests/session_test.rs
index 244885697..6677b3c98 100644
--- a/rust/tests/session_test.rs
+++ b/rust/tests/session_test.rs
@@ -1202,7 +1202,27 @@ async fn list_models_returns_typed_model_info() {
"id": id,
"result": {
"models": [
- { "id": "gpt-4", "name": "GPT-4", "capabilities": {} },
+ {
+ "id": "gpt-4",
+ "name": "GPT-4",
+ "capabilities": {},
+ "billing": {
+ "multiplier": 1.5,
+ "tokenPrices": {
+ "inputPrice": 2.0,
+ "outputPrice": 8.0,
+ "cachePrice": 0.5,
+ "batchSize": 1000000,
+ "contextMax": 128000,
+ "longContext": {
+ "inputPrice": 4.0,
+ "outputPrice": 16.0,
+ "cachePrice": 1.0,
+ "contextMax": 1000000
+ }
+ }
+ }
+ },
{ "id": "claude-sonnet-4", "name": "Claude Sonnet", "capabilities": {} },
]
},
@@ -1213,6 +1233,22 @@ async fn list_models_returns_typed_model_info() {
assert_eq!(models.len(), 2);
assert_eq!(models[0].id, "gpt-4");
assert_eq!(models[1].name, "Claude Sonnet");
+
+ // Token prices are surfaced through the re-exported public types.
+ let token_prices: &github_copilot_sdk::types::ModelBillingTokenPrices = models[0]
+ .billing
+ .as_ref()
+ .expect("billing")
+ .token_prices
+ .as_ref()
+ .expect("token prices");
+ assert_eq!(token_prices.input_price, Some(2.0));
+ assert_eq!(token_prices.batch_size, Some(1000000));
+ assert_eq!(token_prices.context_max, Some(128000));
+ let long_context: &github_copilot_sdk::types::ModelBillingTokenPricesLongContext =
+ token_prices.long_context.as_ref().expect("long context");
+ assert_eq!(long_context.output_price, Some(16.0));
+ assert_eq!(long_context.context_max, Some(1000000));
}
#[tokio::test]