| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157 |
- package service
- import (
- "testing"
- "github.com/stretchr/testify/assert"
- "github.com/tidwall/gjson"
- )
- func TestExtractOpenAIUsageFromJSONBytes_MergesHostedImageGenToolUsage(t *testing.T) {
- // SSE response.completed event with response.usage + response.tool_usage.image_gen
- body := []byte(`{
- "type": "response.completed",
- "response": {
- "usage": {
- "input_tokens": 43792,
- "output_tokens": 1005,
- "total_tokens": 44797
- },
- "tool_usage": {
- "image_gen": {
- "input_tokens": 7918,
- "input_tokens_details": {"image_tokens": 7620, "text_tokens": 298},
- "output_tokens": 186,
- "output_tokens_details": {"image_tokens": 186, "text_tokens": 0},
- "total_tokens": 8104
- }
- }
- }
- }`)
- usage, ok := extractOpenAIUsageFromJSONBytes(body)
- assert.True(t, ok)
- assert.Equal(t, 43792, usage.InputTokens, "input_tokens from response.usage")
- assert.Equal(t, 1005, usage.OutputTokens, "output_tokens from response.usage")
- assert.Equal(t, 186, usage.ImageOutputTokens, "image output tokens merged from tool_usage.image_gen")
- assert.Equal(t, 7620, usage.ImageInputTokens, "image input tokens merged from tool_usage.image_gen")
- }
- func TestExtractOpenAIUsageFromJSONBytes_NonStreamingMergesImageGen(t *testing.T) {
- // Non-streaming response with top-level usage + tool_usage
- body := []byte(`{
- "id": "resp_abc123",
- "object": "response",
- "usage": {
- "input_tokens": 5000,
- "output_tokens": 200
- },
- "tool_usage": {
- "image_gen": {
- "input_tokens": 3000,
- "input_tokens_details": {"image_tokens": 2800, "text_tokens": 200},
- "output_tokens": 150,
- "output_tokens_details": {"image_tokens": 150, "text_tokens": 0},
- "total_tokens": 3150
- }
- }
- }`)
- usage, ok := extractOpenAIUsageFromJSONBytes(body)
- assert.True(t, ok)
- assert.Equal(t, 5000, usage.InputTokens)
- assert.Equal(t, 200, usage.OutputTokens)
- assert.Equal(t, 150, usage.ImageOutputTokens, "image output tokens from tool_usage.image_gen")
- assert.Equal(t, 2800, usage.ImageInputTokens, "image input tokens from tool_usage.image_gen")
- }
- func TestExtractOpenAIUsageFromJSONBytes_NoToolUsageUnchanged(t *testing.T) {
- // Standard response without tool_usage — behavior should be unchanged
- body := []byte(`{
- "usage": {
- "input_tokens": 100,
- "output_tokens": 50
- }
- }`)
- usage, ok := extractOpenAIUsageFromJSONBytes(body)
- assert.True(t, ok)
- assert.Equal(t, 100, usage.InputTokens)
- assert.Equal(t, 50, usage.OutputTokens)
- assert.Equal(t, 0, usage.ImageOutputTokens, "no image tokens without tool_usage")
- assert.Equal(t, 0, usage.ImageInputTokens, "no image tokens without tool_usage")
- }
- func TestExtractOpenAIUsageFromJSONBytes_BaseUsageHasImageTokensNoOverride(t *testing.T) {
- // If base usage already has image tokens (e.g. from output_tokens_details),
- // tool_usage should NOT override them.
- body := []byte(`{
- "usage": {
- "input_tokens": 100,
- "output_tokens": 50,
- "output_tokens_details": {"image_tokens": 30}
- },
- "tool_usage": {
- "image_gen": {
- "input_tokens": 200,
- "output_tokens": 100,
- "output_tokens_details": {"image_tokens": 100, "text_tokens": 0},
- "total_tokens": 300
- }
- }
- }`)
- usage, ok := extractOpenAIUsageFromJSONBytes(body)
- assert.True(t, ok)
- assert.Equal(t, 30, usage.ImageOutputTokens, "base usage image tokens preserved, not overridden")
- }
- func TestMergeHostedImageGenToolUsage_EmptyImageGen(t *testing.T) {
- tests := []struct {
- name string
- json string
- }{
- {"missing", `{}`},
- {"null", `{"image_gen": null}`},
- {"not object", `{"image_gen": 42}`},
- }
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- usage := OpenAIUsage{InputTokens: 100, OutputTokens: 50}
- original := usage
- body := []byte(tt.json)
- mergeHostedImageGenToolUsage(gjson.GetBytes(body, "image_gen"), &usage)
- assert.Equal(t, original, usage, "usage should be unchanged")
- })
- }
- }
- func TestParseSSEUsageBytes_ResponseCompletedWithImageGen(t *testing.T) {
- svc := &OpenAIGatewayService{}
- data := []byte(`{
- "type": "response.completed",
- "response": {
- "usage": {
- "input_tokens": 10000,
- "output_tokens": 500
- },
- "tool_usage": {
- "image_gen": {
- "input_tokens": 4000,
- "input_tokens_details": {"image_tokens": 3800, "text_tokens": 200},
- "output_tokens": 186,
- "output_tokens_details": {"image_tokens": 186, "text_tokens": 0},
- "total_tokens": 4186
- }
- }
- }
- }`)
- usage := &OpenAIUsage{}
- svc.parseSSEUsageBytes(data, usage)
- assert.Equal(t, 10000, usage.InputTokens)
- assert.Equal(t, 500, usage.OutputTokens)
- assert.Equal(t, 186, usage.ImageOutputTokens, "image output tokens from SSE tool_usage")
- assert.Equal(t, 3800, usage.ImageInputTokens, "image input tokens from SSE tool_usage")
- }
|