mirror of
https://github.com/router-for-me/CLIProxyAPI.git
synced 2026-02-18 04:10:51 +08:00
refactor(translator): remove session ID logic from signature caching and associated tests
This commit is contained in:
@@ -7,8 +7,6 @@ package claude
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"crypto/sha256"
|
|
||||||
"encoding/hex"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/router-for-me/CLIProxyAPI/v6/internal/cache"
|
"github.com/router-for-me/CLIProxyAPI/v6/internal/cache"
|
||||||
@@ -19,33 +17,6 @@ import (
|
|||||||
"github.com/tidwall/sjson"
|
"github.com/tidwall/sjson"
|
||||||
)
|
)
|
||||||
|
|
||||||
// deriveSessionID generates a stable session ID from the request.
|
|
||||||
// Uses the hash of the first user message to identify the conversation.
|
|
||||||
func deriveSessionID(rawJSON []byte) string {
|
|
||||||
userIDResult := gjson.GetBytes(rawJSON, "metadata.user_id")
|
|
||||||
if userIDResult.Exists() {
|
|
||||||
userID := userIDResult.String()
|
|
||||||
idx := strings.Index(userID, "session_")
|
|
||||||
if idx != -1 {
|
|
||||||
return userID[idx+8:]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
messages := gjson.GetBytes(rawJSON, "messages")
|
|
||||||
if !messages.IsArray() {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
for _, msg := range messages.Array() {
|
|
||||||
if msg.Get("role").String() == "user" {
|
|
||||||
content := msg.Get("content").Raw
|
|
||||||
if content != "" {
|
|
||||||
h := sha256.Sum256([]byte(content))
|
|
||||||
return hex.EncodeToString(h[:16])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
// ConvertClaudeRequestToAntigravity parses and transforms a Claude Code API request into Gemini CLI API format.
|
// ConvertClaudeRequestToAntigravity parses and transforms a Claude Code API request into Gemini CLI API format.
|
||||||
// It extracts the model name, system instruction, message contents, and tool declarations
|
// It extracts the model name, system instruction, message contents, and tool declarations
|
||||||
// from the raw JSON request and returns them in the format expected by the Gemini CLI API.
|
// from the raw JSON request and returns them in the format expected by the Gemini CLI API.
|
||||||
@@ -68,9 +39,6 @@ func ConvertClaudeRequestToAntigravity(modelName string, inputRawJSON []byte, _
|
|||||||
enableThoughtTranslate := true
|
enableThoughtTranslate := true
|
||||||
rawJSON := bytes.Clone(inputRawJSON)
|
rawJSON := bytes.Clone(inputRawJSON)
|
||||||
|
|
||||||
// Derive session ID for signature caching
|
|
||||||
sessionID := deriveSessionID(rawJSON)
|
|
||||||
|
|
||||||
// system instruction
|
// system instruction
|
||||||
systemInstructionJSON := ""
|
systemInstructionJSON := ""
|
||||||
hasSystemInstruction := false
|
hasSystemInstruction := false
|
||||||
@@ -133,7 +101,7 @@ func ConvertClaudeRequestToAntigravity(modelName string, inputRawJSON []byte, _
|
|||||||
// Always try cached signature first (more reliable than client-provided)
|
// Always try cached signature first (more reliable than client-provided)
|
||||||
// Client may send stale or invalid signatures from different sessions
|
// Client may send stale or invalid signatures from different sessions
|
||||||
signature := ""
|
signature := ""
|
||||||
if sessionID != "" && thinkingText != "" {
|
if thinkingText != "" {
|
||||||
if cachedSig := cache.GetCachedSignature(modelName, thinkingText); cachedSig != "" {
|
if cachedSig := cache.GetCachedSignature(modelName, thinkingText); cachedSig != "" {
|
||||||
signature = cachedSig
|
signature = cachedSig
|
||||||
// log.Debugf("Using cached signature for thinking block")
|
// log.Debugf("Using cached signature for thinking block")
|
||||||
|
|||||||
@@ -98,10 +98,7 @@ func TestConvertClaudeRequestToAntigravity_ThinkingBlocks(t *testing.T) {
|
|||||||
]
|
]
|
||||||
}`)
|
}`)
|
||||||
|
|
||||||
// Derive session ID and cache the signature
|
cache.CacheSignature("claude-sonnet-4-5-thinking", thinkingText, validSignature)
|
||||||
sessionID := deriveSessionID(inputJSON)
|
|
||||||
cache.CacheSignature("claude-sonnet-4-5-thinking", sessionID, thinkingText, validSignature)
|
|
||||||
defer cache.ClearSignatureCache(sessionID)
|
|
||||||
|
|
||||||
output := ConvertClaudeRequestToAntigravity("claude-sonnet-4-5-thinking", inputJSON, false)
|
output := ConvertClaudeRequestToAntigravity("claude-sonnet-4-5-thinking", inputJSON, false)
|
||||||
outputStr := string(output)
|
outputStr := string(output)
|
||||||
@@ -266,10 +263,7 @@ func TestConvertClaudeRequestToAntigravity_ToolUse_WithSignature(t *testing.T) {
|
|||||||
]
|
]
|
||||||
}`)
|
}`)
|
||||||
|
|
||||||
// Derive session ID and cache the signature
|
cache.CacheSignature("claude-sonnet-4-5-thinking", thinkingText, validSignature)
|
||||||
sessionID := deriveSessionID(inputJSON)
|
|
||||||
cache.CacheSignature("claude-sonnet-4-5-thinking", sessionID, thinkingText, validSignature)
|
|
||||||
defer cache.ClearSignatureCache(sessionID)
|
|
||||||
|
|
||||||
output := ConvertClaudeRequestToAntigravity("claude-sonnet-4-5-thinking", inputJSON, false)
|
output := ConvertClaudeRequestToAntigravity("claude-sonnet-4-5-thinking", inputJSON, false)
|
||||||
outputStr := string(output)
|
outputStr := string(output)
|
||||||
@@ -306,10 +300,7 @@ func TestConvertClaudeRequestToAntigravity_ReorderThinking(t *testing.T) {
|
|||||||
]
|
]
|
||||||
}`)
|
}`)
|
||||||
|
|
||||||
// Derive session ID and cache the signature
|
cache.CacheSignature("claude-sonnet-4-5-thinking", thinkingText, validSignature)
|
||||||
sessionID := deriveSessionID(inputJSON)
|
|
||||||
cache.CacheSignature("claude-sonnet-4-5-thinking", sessionID, thinkingText, validSignature)
|
|
||||||
defer cache.ClearSignatureCache(sessionID)
|
|
||||||
|
|
||||||
output := ConvertClaudeRequestToAntigravity("claude-sonnet-4-5-thinking", inputJSON, false)
|
output := ConvertClaudeRequestToAntigravity("claude-sonnet-4-5-thinking", inputJSON, false)
|
||||||
outputStr := string(output)
|
outputStr := string(output)
|
||||||
@@ -517,10 +508,7 @@ func TestConvertClaudeRequestToAntigravity_TrailingSignedThinking_Kept(t *testin
|
|||||||
]
|
]
|
||||||
}`)
|
}`)
|
||||||
|
|
||||||
// Derive session ID and cache the signature
|
cache.CacheSignature("claude-sonnet-4-5-thinking", thinkingText, validSignature)
|
||||||
sessionID := deriveSessionID(inputJSON)
|
|
||||||
cache.CacheSignature("claude-sonnet-4-5-thinking", sessionID, thinkingText, validSignature)
|
|
||||||
defer cache.ClearSignatureCache(sessionID)
|
|
||||||
|
|
||||||
output := ConvertClaudeRequestToAntigravity("claude-sonnet-4-5-thinking", inputJSON, false)
|
output := ConvertClaudeRequestToAntigravity("claude-sonnet-4-5-thinking", inputJSON, false)
|
||||||
outputStr := string(output)
|
outputStr := string(output)
|
||||||
|
|||||||
@@ -70,7 +70,6 @@ func ConvertAntigravityResponseToClaude(_ context.Context, _ string, originalReq
|
|||||||
HasFirstResponse: false,
|
HasFirstResponse: false,
|
||||||
ResponseType: 0,
|
ResponseType: 0,
|
||||||
ResponseIndex: 0,
|
ResponseIndex: 0,
|
||||||
SessionID: deriveSessionID(originalRequestRawJSON),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
modelName := gjson.GetBytes(requestRawJSON, "model").String()
|
modelName := gjson.GetBytes(requestRawJSON, "model").String()
|
||||||
|
|||||||
@@ -144,7 +144,7 @@ func TestConvertAntigravityResponseToClaude_SignatureCached(t *testing.T) {
|
|||||||
ConvertAntigravityResponseToClaude(ctx, "claude-sonnet-4-5-thinking", requestJSON, requestJSON, signatureChunk, ¶m)
|
ConvertAntigravityResponseToClaude(ctx, "claude-sonnet-4-5-thinking", requestJSON, requestJSON, signatureChunk, ¶m)
|
||||||
|
|
||||||
// Verify signature was cached
|
// Verify signature was cached
|
||||||
cachedSig := cache.GetCachedSignature("claude-sonnet-4-5-thinking", sessionID, thinkingText)
|
cachedSig := cache.GetCachedSignature("claude-sonnet-4-5-thinking", thinkingText)
|
||||||
if cachedSig != validSignature {
|
if cachedSig != validSignature {
|
||||||
t.Errorf("Expected cached signature '%s', got '%s'", validSignature, cachedSig)
|
t.Errorf("Expected cached signature '%s', got '%s'", validSignature, cachedSig)
|
||||||
}
|
}
|
||||||
@@ -223,13 +223,12 @@ func TestConvertAntigravityResponseToClaude_MultipleThinkingBlocks(t *testing.T)
|
|||||||
// Process first thinking block
|
// Process first thinking block
|
||||||
ConvertAntigravityResponseToClaude(ctx, "claude-sonnet-4-5-thinking", requestJSON, requestJSON, block1Thinking, ¶m)
|
ConvertAntigravityResponseToClaude(ctx, "claude-sonnet-4-5-thinking", requestJSON, requestJSON, block1Thinking, ¶m)
|
||||||
params := param.(*Params)
|
params := param.(*Params)
|
||||||
sessionID := params.SessionID
|
|
||||||
firstThinkingText := params.CurrentThinkingText.String()
|
firstThinkingText := params.CurrentThinkingText.String()
|
||||||
|
|
||||||
ConvertAntigravityResponseToClaude(ctx, "claude-sonnet-4-5-thinking", requestJSON, requestJSON, block1Sig, ¶m)
|
ConvertAntigravityResponseToClaude(ctx, "claude-sonnet-4-5-thinking", requestJSON, requestJSON, block1Sig, ¶m)
|
||||||
|
|
||||||
// Verify first signature cached
|
// Verify first signature cached
|
||||||
if cache.GetCachedSignature("claude-sonnet-4-5-thinking", sessionID, firstThinkingText) != validSig1 {
|
if cache.GetCachedSignature("claude-sonnet-4-5-thinking", firstThinkingText) != validSig1 {
|
||||||
t.Error("First thinking block signature should be cached")
|
t.Error("First thinking block signature should be cached")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -243,76 +242,7 @@ func TestConvertAntigravityResponseToClaude_MultipleThinkingBlocks(t *testing.T)
|
|||||||
ConvertAntigravityResponseToClaude(ctx, "claude-sonnet-4-5-thinking", requestJSON, requestJSON, block2Sig, ¶m)
|
ConvertAntigravityResponseToClaude(ctx, "claude-sonnet-4-5-thinking", requestJSON, requestJSON, block2Sig, ¶m)
|
||||||
|
|
||||||
// Verify second signature cached
|
// Verify second signature cached
|
||||||
if cache.GetCachedSignature("claude-sonnet-4-5-thinking", sessionID, secondThinkingText) != validSig2 {
|
if cache.GetCachedSignature("claude-sonnet-4-5-thinking", secondThinkingText) != validSig2 {
|
||||||
t.Error("Second thinking block signature should be cached")
|
t.Error("Second thinking block signature should be cached")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDeriveSessionIDFromRequest(t *testing.T) {
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
input []byte
|
|
||||||
wantEmpty bool
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "valid user message",
|
|
||||||
input: []byte(`{"messages": [{"role": "user", "content": "Hello"}]}`),
|
|
||||||
wantEmpty: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "user message with content array",
|
|
||||||
input: []byte(`{"messages": [{"role": "user", "content": [{"type": "text", "text": "Hello"}]}]}`),
|
|
||||||
wantEmpty: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "no user message",
|
|
||||||
input: []byte(`{"messages": [{"role": "assistant", "content": "Hi"}]}`),
|
|
||||||
wantEmpty: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "empty messages",
|
|
||||||
input: []byte(`{"messages": []}`),
|
|
||||||
wantEmpty: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "no messages field",
|
|
||||||
input: []byte(`{}`),
|
|
||||||
wantEmpty: true,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, tt := range tests {
|
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
|
||||||
result := deriveSessionID(tt.input)
|
|
||||||
if tt.wantEmpty && result != "" {
|
|
||||||
t.Errorf("Expected empty session ID, got '%s'", result)
|
|
||||||
}
|
|
||||||
if !tt.wantEmpty && result == "" {
|
|
||||||
t.Error("Expected non-empty session ID")
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestDeriveSessionIDFromRequest_Deterministic(t *testing.T) {
|
|
||||||
input := []byte(`{"messages": [{"role": "user", "content": "Same message"}]}`)
|
|
||||||
|
|
||||||
id1 := deriveSessionID(input)
|
|
||||||
id2 := deriveSessionID(input)
|
|
||||||
|
|
||||||
if id1 != id2 {
|
|
||||||
t.Errorf("Session ID should be deterministic: '%s' != '%s'", id1, id2)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestDeriveSessionIDFromRequest_DifferentMessages(t *testing.T) {
|
|
||||||
input1 := []byte(`{"messages": [{"role": "user", "content": "Message A"}]}`)
|
|
||||||
input2 := []byte(`{"messages": [{"role": "user", "content": "Message B"}]}`)
|
|
||||||
|
|
||||||
id1 := deriveSessionID(input1)
|
|
||||||
id2 := deriveSessionID(input2)
|
|
||||||
|
|
||||||
if id1 == id2 {
|
|
||||||
t.Error("Different messages should produce different session IDs")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
Reference in New Issue
Block a user