mirror of
https://github.com/CJackHwang/ds2api.git
synced 2026-05-04 00:15:28 +08:00
117 lines
6.8 KiB
Go
117 lines
6.8 KiB
Go
package translatorcliproxy
|
|
|
|
import (
|
|
"strings"
|
|
"testing"
|
|
|
|
sdktranslator "github.com/router-for-me/CLIProxyAPI/v6/sdk/translator"
|
|
)
|
|
|
|
func TestToOpenAIClaude(t *testing.T) {
|
|
raw := []byte(`{"model":"claude-sonnet-4-5","messages":[{"role":"user","content":"hi"}],"stream":false}`)
|
|
got := ToOpenAI(sdktranslator.FormatClaude, "claude-sonnet-4-5", raw, false)
|
|
s := string(got)
|
|
if !strings.Contains(s, `"messages"`) || !strings.Contains(s, `"model"`) {
|
|
t.Fatalf("unexpected translated request: %s", s)
|
|
}
|
|
}
|
|
|
|
func TestToOpenAIGeminiThinkingBudgetZeroDisablesReasoning(t *testing.T) {
|
|
raw := []byte(`{"contents":[{"role":"user","parts":[{"text":"hi"}]}],"generationConfig":{"thinkingConfig":{"thinkingBudget":0}}}`)
|
|
got := string(ToOpenAI(sdktranslator.FormatGemini, "gemini-2.5-flash", raw, false))
|
|
if !strings.Contains(got, `"reasoning_effort":"none"`) {
|
|
t.Fatalf("expected Gemini thinkingBudget=0 to translate to reasoning_effort none, got: %s", got)
|
|
}
|
|
}
|
|
|
|
func TestFromOpenAINonStreamClaude(t *testing.T) {
|
|
original := []byte(`{"model":"claude-sonnet-4-5","messages":[{"role":"user","content":"hi"}],"stream":false}`)
|
|
translatedReq := []byte(`{"model":"claude-sonnet-4-5","messages":[{"role":"user","content":"hi"}],"stream":false}`)
|
|
openaibody := []byte(`{"id":"chatcmpl_1","object":"chat.completion","created":1,"model":"claude-sonnet-4-5","choices":[{"index":0,"message":{"role":"assistant","content":"hello"},"finish_reason":"stop"}],"usage":{"prompt_tokens":1,"completion_tokens":1,"total_tokens":2}}`)
|
|
got := FromOpenAINonStream(sdktranslator.FormatClaude, "claude-sonnet-4-5", original, translatedReq, openaibody)
|
|
if !strings.Contains(string(got), `"type":"message"`) {
|
|
t.Fatalf("expected claude response format, got: %s", string(got))
|
|
}
|
|
}
|
|
|
|
func TestFromOpenAINonStreamClaudePreservesUsageFromOpenAI(t *testing.T) {
|
|
original := []byte(`{"model":"claude-sonnet-4-5","messages":[{"role":"user","content":"hi"}],"stream":false}`)
|
|
translatedReq := []byte(`{"model":"claude-sonnet-4-5","messages":[{"role":"user","content":"hi"}],"stream":false}`)
|
|
openaibody := []byte(`{"id":"chatcmpl_1","object":"chat.completion","created":1,"model":"claude-sonnet-4-5","choices":[{"index":0,"message":{"role":"assistant","content":"hello"},"finish_reason":"stop"}],"usage":{"prompt_tokens":11,"completion_tokens":29,"total_tokens":40}}`)
|
|
got := string(FromOpenAINonStream(sdktranslator.FormatClaude, "claude-sonnet-4-5", original, translatedReq, openaibody))
|
|
if !strings.Contains(got, `"input_tokens":11`) || !strings.Contains(got, `"output_tokens":29`) {
|
|
t.Fatalf("expected claude usage to preserve prompt/completion tokens, got: %s", got)
|
|
}
|
|
}
|
|
|
|
func TestFromOpenAINonStreamGeminiPreservesUsageFromOpenAI(t *testing.T) {
|
|
original := []byte(`{"contents":[{"role":"user","parts":[{"text":"hi"}]}]}`)
|
|
translatedReq := []byte(`{"model":"gemini-2.5-pro","messages":[{"role":"user","content":"hi"}],"stream":false}`)
|
|
openaibody := []byte(`{"id":"chatcmpl_1","object":"chat.completion","created":1,"model":"gemini-2.5-pro","choices":[{"index":0,"message":{"role":"assistant","content":"hello"},"finish_reason":"stop"}],"usage":{"prompt_tokens":11,"completion_tokens":29,"total_tokens":40}}`)
|
|
got := string(FromOpenAINonStream(sdktranslator.FormatGemini, "gemini-2.5-pro", original, translatedReq, openaibody))
|
|
if !strings.Contains(got, `"promptTokenCount":11`) || !strings.Contains(got, `"candidatesTokenCount":29`) || !strings.Contains(got, `"totalTokenCount":40`) {
|
|
t.Fatalf("expected gemini usageMetadata to preserve prompt/completion tokens, got: %s", got)
|
|
}
|
|
}
|
|
|
|
func TestFromOpenAINonStreamPreservesResponsesUsageShape(t *testing.T) {
|
|
original := []byte(`{"contents":[{"role":"user","parts":[{"text":"hi"}]}]}`)
|
|
translatedReq := []byte(`{"model":"gemini-2.5-pro","messages":[{"role":"user","content":"hi"}],"stream":false}`)
|
|
openaibody := []byte(`{"id":"resp_1","object":"response","model":"gemini-2.5-pro","usage":{"input_tokens":"11","output_tokens":"29","total_tokens":"40"}}`)
|
|
gotGemini := string(FromOpenAINonStream(sdktranslator.FormatGemini, "gemini-2.5-pro", original, translatedReq, openaibody))
|
|
if !strings.Contains(gotGemini, `"promptTokenCount":11`) || !strings.Contains(gotGemini, `"candidatesTokenCount":29`) || !strings.Contains(gotGemini, `"totalTokenCount":40`) {
|
|
t.Fatalf("expected gemini usageMetadata from input/output usage fields, got: %s", gotGemini)
|
|
}
|
|
|
|
origClaude := []byte(`{"model":"claude-sonnet-4-5","messages":[{"role":"user","content":"hi"}],"stream":false}`)
|
|
gotClaude := string(FromOpenAINonStream(sdktranslator.FormatClaude, "claude-sonnet-4-5", origClaude, origClaude, openaibody))
|
|
if !strings.Contains(gotClaude, `"input_tokens":11`) || !strings.Contains(gotClaude, `"output_tokens":29`) {
|
|
t.Fatalf("expected claude usage from input/output usage fields, got: %s", gotClaude)
|
|
}
|
|
}
|
|
|
|
func TestParseFormatAliases(t *testing.T) {
|
|
cases := map[string]sdktranslator.Format{
|
|
"responses": sdktranslator.FormatOpenAIResponse,
|
|
"anthropic": sdktranslator.FormatClaude,
|
|
"geminicli": sdktranslator.FormatGeminiCLI,
|
|
"openai-codex": sdktranslator.FormatCodex,
|
|
"antigravity": sdktranslator.FormatAntigravity,
|
|
"chat-completions": sdktranslator.FormatOpenAI,
|
|
}
|
|
for in, want := range cases {
|
|
if got := ParseFormat(in); got != want {
|
|
t.Fatalf("ParseFormat(%q)=%q want %q", in, got, want)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestToOpenAIByNameAllSupportedFormats(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
format string
|
|
model string
|
|
body string
|
|
}{
|
|
{name: "openai", format: "openai", model: "gpt-4.1", body: `{"model":"gpt-4.1","messages":[{"role":"user","content":"hi"}],"stream":false}`},
|
|
{name: "responses", format: "responses", model: "gpt-4.1", body: `{"model":"gpt-4.1","input":"hello","stream":false}`},
|
|
{name: "claude", format: "claude", model: "claude-sonnet-4-5", body: `{"model":"claude-sonnet-4-5","messages":[{"role":"user","content":"hello"}],"stream":false}`},
|
|
{name: "gemini", format: "gemini", model: "gemini-2.5-pro", body: `{"contents":[{"role":"user","parts":[{"text":"hello"}]}]}`},
|
|
{name: "gemini-cli", format: "gemini-cli", model: "gemini-2.5-pro", body: `{"model":"gemini-2.5-pro","messages":[{"role":"user","content":"hello"}],"stream":false}`},
|
|
{name: "codex", format: "codex", model: "gpt-5-codex", body: `{"model":"gpt-5-codex","messages":[{"role":"user","content":"hello"}],"stream":false}`},
|
|
{name: "antigravity", format: "antigravity", model: "gpt-4.1", body: `{"model":"gpt-4.1","messages":[{"role":"user","content":"hello"}],"stream":false}`},
|
|
}
|
|
|
|
for _, tc := range tests {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
got := ToOpenAIByName(tc.format, tc.model, []byte(tc.body), false)
|
|
if len(got) == 0 {
|
|
t.Fatalf("expected non-empty conversion result for format=%s", tc.format)
|
|
}
|
|
if !strings.Contains(string(got), `"model"`) {
|
|
t.Fatalf("expected model field in converted payload, got=%s", string(got))
|
|
}
|
|
})
|
|
}
|
|
}
|