mirror of
https://github.com/Wei-Shaw/sub2api.git
synced 2026-09-24 16:05:44 +08:00
fix(openai): harden GPT-5.6 billing and usage
This commit is contained in:
@@ -53,6 +53,26 @@ func TestUsageConversionsPreserveCacheWriteTokens(t *testing.T) {
|
||||
require.Equal(t, 200, roundTrip.InputTokensDetails.CacheWriteTokens)
|
||||
}
|
||||
|
||||
func TestResponsesUsageNestedCacheWritePresenceOverridesTopLevelAlias(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
nestedJSON string
|
||||
want int
|
||||
}{
|
||||
{name: "explicit zero", nestedJSON: `{"cache_write_tokens":0}`, want: 0},
|
||||
{name: "nonzero", nestedJSON: `{"cache_write_tokens":7}`, want: 7},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
var usage ResponsesUsage
|
||||
payload := []byte(`{"input_tokens":20,"output_tokens":2,"cache_creation_input_tokens":19,"input_tokens_details":` + tt.nestedJSON + `}`)
|
||||
require.NoError(t, json.Unmarshal(payload, &usage))
|
||||
require.Equal(t, tt.want, usage.CacheCreationInputTokens)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestChatCompletionsToResponses_SystemMessage(t *testing.T) {
|
||||
req := &ChatCompletionsRequest{
|
||||
Model: "gpt-4o",
|
||||
|
||||
@@ -332,6 +332,10 @@ type ResponsesUsage struct {
|
||||
|
||||
func (u *ResponsesUsage) UnmarshalJSON(data []byte) error {
|
||||
type responsesUsageAlias ResponsesUsage
|
||||
type cacheTokenPresence struct {
|
||||
CacheCreationTokens *int `json:"cache_creation_tokens"`
|
||||
CacheWriteTokens *int `json:"cache_write_tokens"`
|
||||
}
|
||||
var aux struct {
|
||||
responsesUsageAlias
|
||||
PromptTokens int `json:"prompt_tokens"`
|
||||
@@ -345,6 +349,13 @@ func (u *ResponsesUsage) UnmarshalJSON(data []byte) error {
|
||||
if err := json.Unmarshal(data, &aux); err != nil {
|
||||
return err
|
||||
}
|
||||
var nestedPresence struct {
|
||||
InputTokensDetails *cacheTokenPresence `json:"input_tokens_details"`
|
||||
PromptTokensDetails *cacheTokenPresence `json:"prompt_tokens_details"`
|
||||
}
|
||||
if err := json.Unmarshal(data, &nestedPresence); err != nil {
|
||||
return err
|
||||
}
|
||||
*u = ResponsesUsage(aux.responsesUsageAlias)
|
||||
if u.InputTokens == 0 && aux.PromptTokens != 0 {
|
||||
u.InputTokens = aux.PromptTokens
|
||||
@@ -368,6 +379,20 @@ func (u *ResponsesUsage) UnmarshalJSON(data []byte) error {
|
||||
if u.OutputTokensDetails == nil && aux.CompletionTokensDetails != nil {
|
||||
u.OutputTokensDetails = aux.CompletionTokensDetails
|
||||
}
|
||||
var canonicalCacheCreationTokens *int
|
||||
switch {
|
||||
case nestedPresence.InputTokensDetails != nil && nestedPresence.InputTokensDetails.CacheWriteTokens != nil:
|
||||
canonicalCacheCreationTokens = nestedPresence.InputTokensDetails.CacheWriteTokens
|
||||
case nestedPresence.PromptTokensDetails != nil && nestedPresence.PromptTokensDetails.CacheWriteTokens != nil:
|
||||
canonicalCacheCreationTokens = nestedPresence.PromptTokensDetails.CacheWriteTokens
|
||||
case nestedPresence.InputTokensDetails != nil && nestedPresence.InputTokensDetails.CacheCreationTokens != nil:
|
||||
canonicalCacheCreationTokens = nestedPresence.InputTokensDetails.CacheCreationTokens
|
||||
case nestedPresence.PromptTokensDetails != nil && nestedPresence.PromptTokensDetails.CacheCreationTokens != nil:
|
||||
canonicalCacheCreationTokens = nestedPresence.PromptTokensDetails.CacheCreationTokens
|
||||
}
|
||||
if canonicalCacheCreationTokens != nil {
|
||||
u.CacheCreationInputTokens = max(*canonicalCacheCreationTokens, 0)
|
||||
}
|
||||
if u.TotalTokens == 0 && (u.InputTokens != 0 || u.OutputTokens != 0) {
|
||||
u.TotalTokens = u.InputTokens + u.OutputTokens
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ type Model struct {
|
||||
|
||||
// DefaultModels OpenAI models list
|
||||
var DefaultModels = []Model{
|
||||
{ID: "gpt-5.6", Object: "model", Created: 1780876800, OwnedBy: "openai", Type: "model", DisplayName: "GPT-5.6 (Sol)"},
|
||||
{ID: "gpt-5.6-sol", Object: "model", Created: 1780876800, OwnedBy: "openai", Type: "model", DisplayName: "GPT-5.6 Sol"},
|
||||
{ID: "gpt-5.6-terra", Object: "model", Created: 1780876800, OwnedBy: "openai", Type: "model", DisplayName: "GPT-5.6 Terra"},
|
||||
{ID: "gpt-5.6-luna", Object: "model", Created: 1780876800, OwnedBy: "openai", Type: "model", DisplayName: "GPT-5.6 Luna"},
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
package openai
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestDefaultModelsIncludeBareGPT56Alias(t *testing.T) {
|
||||
require.Contains(t, DefaultModelIDs(), "gpt-5.6")
|
||||
}
|
||||
Reference in New Issue
Block a user