mirror of
https://github.com/router-for-me/CLIProxyAPI.git
synced 2026-02-02 20:40:52 +08:00
Merge pull request #1298 from sususu98/fix/restore-usageMetadata-in-gemini-translator
fix(translator): restore usageMetadata in Gemini responses from Antigravity
This commit is contained in:
@@ -41,6 +41,7 @@ func ConvertAntigravityResponseToGemini(ctx context.Context, _ string, originalR
|
|||||||
responseResult := gjson.GetBytes(rawJSON, "response")
|
responseResult := gjson.GetBytes(rawJSON, "response")
|
||||||
if responseResult.Exists() {
|
if responseResult.Exists() {
|
||||||
chunk = []byte(responseResult.Raw)
|
chunk = []byte(responseResult.Raw)
|
||||||
|
chunk = restoreUsageMetadata(chunk)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
chunkTemplate := "[]"
|
chunkTemplate := "[]"
|
||||||
@@ -76,7 +77,8 @@ func ConvertAntigravityResponseToGemini(ctx context.Context, _ string, originalR
|
|||||||
func ConvertAntigravityResponseToGeminiNonStream(_ context.Context, _ string, originalRequestRawJSON, requestRawJSON, rawJSON []byte, _ *any) string {
|
func ConvertAntigravityResponseToGeminiNonStream(_ context.Context, _ string, originalRequestRawJSON, requestRawJSON, rawJSON []byte, _ *any) string {
|
||||||
responseResult := gjson.GetBytes(rawJSON, "response")
|
responseResult := gjson.GetBytes(rawJSON, "response")
|
||||||
if responseResult.Exists() {
|
if responseResult.Exists() {
|
||||||
return responseResult.Raw
|
chunk := restoreUsageMetadata([]byte(responseResult.Raw))
|
||||||
|
return string(chunk)
|
||||||
}
|
}
|
||||||
return string(rawJSON)
|
return string(rawJSON)
|
||||||
}
|
}
|
||||||
@@ -84,3 +86,15 @@ func ConvertAntigravityResponseToGeminiNonStream(_ context.Context, _ string, or
|
|||||||
func GeminiTokenCount(ctx context.Context, count int64) string {
|
func GeminiTokenCount(ctx context.Context, count int64) string {
|
||||||
return fmt.Sprintf(`{"totalTokens":%d,"promptTokensDetails":[{"modality":"TEXT","tokenCount":%d}]}`, count, count)
|
return fmt.Sprintf(`{"totalTokens":%d,"promptTokensDetails":[{"modality":"TEXT","tokenCount":%d}]}`, count, count)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// restoreUsageMetadata renames cpaUsageMetadata back to usageMetadata.
|
||||||
|
// The executor renames usageMetadata to cpaUsageMetadata in non-terminal chunks
|
||||||
|
// to preserve usage data while hiding it from clients that don't expect it.
|
||||||
|
// When returning standard Gemini API format, we must restore the original name.
|
||||||
|
func restoreUsageMetadata(chunk []byte) []byte {
|
||||||
|
if cpaUsage := gjson.GetBytes(chunk, "cpaUsageMetadata"); cpaUsage.Exists() {
|
||||||
|
chunk, _ = sjson.SetRawBytes(chunk, "usageMetadata", []byte(cpaUsage.Raw))
|
||||||
|
chunk, _ = sjson.DeleteBytes(chunk, "cpaUsageMetadata")
|
||||||
|
}
|
||||||
|
return chunk
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,95 @@
|
|||||||
|
package gemini
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestRestoreUsageMetadata(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
input []byte
|
||||||
|
expected string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "cpaUsageMetadata renamed to usageMetadata",
|
||||||
|
input: []byte(`{"modelVersion":"gemini-3-pro","cpaUsageMetadata":{"promptTokenCount":100,"candidatesTokenCount":200}}`),
|
||||||
|
expected: `{"modelVersion":"gemini-3-pro","usageMetadata":{"promptTokenCount":100,"candidatesTokenCount":200}}`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "no cpaUsageMetadata unchanged",
|
||||||
|
input: []byte(`{"modelVersion":"gemini-3-pro","usageMetadata":{"promptTokenCount":100}}`),
|
||||||
|
expected: `{"modelVersion":"gemini-3-pro","usageMetadata":{"promptTokenCount":100}}`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "empty input",
|
||||||
|
input: []byte(`{}`),
|
||||||
|
expected: `{}`,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
result := restoreUsageMetadata(tt.input)
|
||||||
|
if string(result) != tt.expected {
|
||||||
|
t.Errorf("restoreUsageMetadata() = %s, want %s", string(result), tt.expected)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestConvertAntigravityResponseToGeminiNonStream(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
input []byte
|
||||||
|
expected string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "cpaUsageMetadata restored in response",
|
||||||
|
input: []byte(`{"response":{"modelVersion":"gemini-3-pro","cpaUsageMetadata":{"promptTokenCount":100}}}`),
|
||||||
|
expected: `{"modelVersion":"gemini-3-pro","usageMetadata":{"promptTokenCount":100}}`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "usageMetadata preserved",
|
||||||
|
input: []byte(`{"response":{"modelVersion":"gemini-3-pro","usageMetadata":{"promptTokenCount":100}}}`),
|
||||||
|
expected: `{"modelVersion":"gemini-3-pro","usageMetadata":{"promptTokenCount":100}}`,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
result := ConvertAntigravityResponseToGeminiNonStream(context.Background(), "", nil, nil, tt.input, nil)
|
||||||
|
if result != tt.expected {
|
||||||
|
t.Errorf("ConvertAntigravityResponseToGeminiNonStream() = %s, want %s", result, tt.expected)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestConvertAntigravityResponseToGeminiStream(t *testing.T) {
|
||||||
|
ctx := context.WithValue(context.Background(), "alt", "")
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
input []byte
|
||||||
|
expected string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "cpaUsageMetadata restored in streaming response",
|
||||||
|
input: []byte(`data: {"response":{"modelVersion":"gemini-3-pro","cpaUsageMetadata":{"promptTokenCount":100}}}`),
|
||||||
|
expected: `{"modelVersion":"gemini-3-pro","usageMetadata":{"promptTokenCount":100}}`,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
results := ConvertAntigravityResponseToGemini(ctx, "", nil, nil, tt.input, nil)
|
||||||
|
if len(results) != 1 {
|
||||||
|
t.Fatalf("expected 1 result, got %d", len(results))
|
||||||
|
}
|
||||||
|
if results[0] != tt.expected {
|
||||||
|
t.Errorf("ConvertAntigravityResponseToGemini() = %s, want %s", results[0], tt.expected)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user