Sub2API v1.0 - AI API 网关(二开初始版本,基于上游 Wei-Shaw/sub2api)
Release / update-version (push) Has been cancelled
Release / build-frontend (push) Has been cancelled
Release / release (push) Has been cancelled
Release / sync-version-file (push) Has been cancelled
CI / shell (push) Canceled after 0s
CI / test (push) Canceled after 0s
CI / frontend (push) Canceled after 0s
CI / golangci-lint (push) Canceled after 0s
Security Scan / backend-security (push) Canceled after 0s
Security Scan / frontend-security (push) Canceled after 0s
Release / update-version (push) Has been cancelled
Release / build-frontend (push) Has been cancelled
Release / release (push) Has been cancelled
Release / sync-version-file (push) Has been cancelled
CI / shell (push) Canceled after 0s
CI / test (push) Canceled after 0s
CI / frontend (push) Canceled after 0s
CI / golangci-lint (push) Canceled after 0s
Security Scan / backend-security (push) Canceled after 0s
Security Scan / frontend-security (push) Canceled after 0s
This commit is contained in:
@@ -0,0 +1,138 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/Wei-Shaw/sub2api/internal/pkg/apicompat"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func mustRawJSON(t *testing.T, s string) json.RawMessage {
|
||||
t.Helper()
|
||||
return json.RawMessage(s)
|
||||
}
|
||||
|
||||
func TestShouldAutoInjectPromptCacheKeyForCompat(t *testing.T) {
|
||||
require.True(t, shouldAutoInjectPromptCacheKeyForCompat("gpt-5.5"))
|
||||
require.True(t, shouldAutoInjectPromptCacheKeyForCompat("gpt-5.5-pro"))
|
||||
require.True(t, shouldAutoInjectPromptCacheKeyForCompat("gpt-5.4"))
|
||||
require.True(t, shouldAutoInjectPromptCacheKeyForCompat("gpt-5.4-mini"))
|
||||
require.True(t, shouldAutoInjectPromptCacheKeyForCompat("gpt-5.2"))
|
||||
require.True(t, shouldAutoInjectPromptCacheKeyForCompat("gpt-5.3"))
|
||||
require.True(t, shouldAutoInjectPromptCacheKeyForCompat("gpt-5.3-codex"))
|
||||
require.True(t, shouldAutoInjectPromptCacheKeyForCompat("gpt-5.3-codex-spark"))
|
||||
require.False(t, shouldAutoInjectPromptCacheKeyForCompat("gpt-4o"))
|
||||
}
|
||||
|
||||
func TestDeriveCompatPromptCacheKey_StableAcrossLaterTurns(t *testing.T) {
|
||||
base := &apicompat.ChatCompletionsRequest{
|
||||
Model: "gpt-5.4",
|
||||
Messages: []apicompat.ChatMessage{
|
||||
{Role: "system", Content: mustRawJSON(t, `"You are helpful."`)},
|
||||
{Role: "user", Content: mustRawJSON(t, `"Hello"`)},
|
||||
},
|
||||
}
|
||||
extended := &apicompat.ChatCompletionsRequest{
|
||||
Model: "gpt-5.4",
|
||||
Messages: []apicompat.ChatMessage{
|
||||
{Role: "system", Content: mustRawJSON(t, `"You are helpful."`)},
|
||||
{Role: "user", Content: mustRawJSON(t, `"Hello"`)},
|
||||
{Role: "assistant", Content: mustRawJSON(t, `"Hi there!"`)},
|
||||
{Role: "user", Content: mustRawJSON(t, `"How are you?"`)},
|
||||
},
|
||||
}
|
||||
|
||||
k1 := deriveCompatPromptCacheKey(base, "gpt-5.4")
|
||||
k2 := deriveCompatPromptCacheKey(extended, "gpt-5.4")
|
||||
require.Equal(t, k1, k2, "cache key should be stable across later turns")
|
||||
require.NotEmpty(t, k1)
|
||||
}
|
||||
|
||||
func TestDeriveCompatPromptCacheKey_DiffersAcrossSessions(t *testing.T) {
|
||||
req1 := &apicompat.ChatCompletionsRequest{
|
||||
Model: "gpt-5.4",
|
||||
Messages: []apicompat.ChatMessage{
|
||||
{Role: "user", Content: mustRawJSON(t, `"Question A"`)},
|
||||
},
|
||||
}
|
||||
req2 := &apicompat.ChatCompletionsRequest{
|
||||
Model: "gpt-5.4",
|
||||
Messages: []apicompat.ChatMessage{
|
||||
{Role: "user", Content: mustRawJSON(t, `"Question B"`)},
|
||||
},
|
||||
}
|
||||
|
||||
k1 := deriveCompatPromptCacheKey(req1, "gpt-5.4")
|
||||
k2 := deriveCompatPromptCacheKey(req2, "gpt-5.4")
|
||||
require.NotEqual(t, k1, k2, "different first user messages should yield different keys")
|
||||
}
|
||||
|
||||
func TestDeriveCompatPromptCacheKey_UsesResolvedSparkFamily(t *testing.T) {
|
||||
req := &apicompat.ChatCompletionsRequest{
|
||||
Model: "gpt-5.3-codex-spark",
|
||||
Messages: []apicompat.ChatMessage{
|
||||
{Role: "user", Content: mustRawJSON(t, `"Question A"`)},
|
||||
},
|
||||
}
|
||||
|
||||
k1 := deriveCompatPromptCacheKey(req, "gpt-5.3-codex-spark")
|
||||
k2 := deriveCompatPromptCacheKey(req, " openai/gpt-5.3-codex-spark ")
|
||||
require.NotEmpty(t, k1)
|
||||
require.Equal(t, k1, k2, "resolved spark family should derive a stable compat cache key")
|
||||
}
|
||||
|
||||
func TestDeriveAnthropicCompatPromptCacheKey_StableAcrossLaterTurns(t *testing.T) {
|
||||
base := &apicompat.AnthropicRequest{
|
||||
Model: "claude-sonnet-4-5",
|
||||
System: mustRawJSON(t, `"You are helpful."`),
|
||||
Messages: []apicompat.AnthropicMessage{
|
||||
{Role: "user", Content: mustRawJSON(t, `"Open repo"`)},
|
||||
},
|
||||
}
|
||||
extended := &apicompat.AnthropicRequest{
|
||||
Model: "claude-sonnet-4-5",
|
||||
System: mustRawJSON(t, `"You are helpful."`),
|
||||
Messages: []apicompat.AnthropicMessage{
|
||||
{Role: "user", Content: mustRawJSON(t, `"Open repo"`)},
|
||||
{Role: "assistant", Content: mustRawJSON(t, `"Opened."`)},
|
||||
{Role: "user", Content: mustRawJSON(t, `"Run tests"`)},
|
||||
},
|
||||
}
|
||||
|
||||
k1 := deriveAnthropicCompatPromptCacheKey(base, "gpt-5.3-codex")
|
||||
k2 := deriveAnthropicCompatPromptCacheKey(extended, "gpt-5.3-codex")
|
||||
require.NotEmpty(t, k1)
|
||||
require.Equal(t, k1, k2, "cache key should stay stable as later Claude Code turns append history")
|
||||
}
|
||||
|
||||
func TestDeriveAnthropicCompatPromptCacheKey_UsesCacheControlAnchors(t *testing.T) {
|
||||
base := &apicompat.AnthropicRequest{
|
||||
Model: "claude-sonnet-4-5",
|
||||
System: mustRawJSON(t, `[
|
||||
{"type":"text","text":"project instructions","cache_control":{"type":"ephemeral"}}
|
||||
]`),
|
||||
Messages: []apicompat.AnthropicMessage{
|
||||
{Role: "user", Content: mustRawJSON(t, `[
|
||||
{"type":"text","text":"repo anchor","cache_control":{"type":"ephemeral"}}
|
||||
]`)},
|
||||
},
|
||||
}
|
||||
extended := &apicompat.AnthropicRequest{
|
||||
Model: base.Model,
|
||||
System: base.System,
|
||||
Messages: []apicompat.AnthropicMessage{
|
||||
base.Messages[0],
|
||||
{Role: "assistant", Content: mustRawJSON(t, `[{"type":"text","text":"Opened."}]`)},
|
||||
{Role: "user", Content: mustRawJSON(t, `[{"type":"text","text":"Run tests"}]`)},
|
||||
},
|
||||
}
|
||||
|
||||
k1 := deriveAnthropicCompatPromptCacheKey(base, "gpt-5.4")
|
||||
k2 := deriveAnthropicCompatPromptCacheKey(extended, "gpt-5.4")
|
||||
require.NotEmpty(t, k1)
|
||||
require.Equal(t, k1, k2)
|
||||
require.True(t, strings.HasPrefix(k1, "anthropic-cache-"))
|
||||
require.False(t, strings.HasPrefix(k1, compatPromptCacheKeyPrefix))
|
||||
}
|
||||
Reference in New Issue
Block a user