Add projects
This commit is contained in:
parent
2d3a9ad623
commit
8b607dd700
1802 changed files with 503346 additions and 2 deletions
|
|
@ -0,0 +1,228 @@
|
|||
// Claude thinking signature validation wrappers for Antigravity bypass mode.
|
||||
package claude
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"strings"
|
||||
|
||||
"github.com/router-for-me/CLIProxyAPI/v7/internal/cache"
|
||||
"github.com/router-for-me/CLIProxyAPI/v7/internal/signature"
|
||||
translatorcommon "github.com/router-for-me/CLIProxyAPI/v7/internal/translator/common"
|
||||
"github.com/tidwall/gjson"
|
||||
"github.com/tidwall/sjson"
|
||||
)
|
||||
|
||||
const (
|
||||
maxBypassSignatureLen = signature.MaxClaudeThinkingSignatureLen
|
||||
|
||||
// Gemini carrier envelopes exist only on the Claude-facing wire. The request
|
||||
// translator validates and unwraps them before writing native Gemini parts.
|
||||
geminiClaudeCarrierPrefix = "cpa-gemini-carrier-v1:"
|
||||
geminiClaudeCarrierNext = "next"
|
||||
geminiClaudeCarrierPrevious = "previous"
|
||||
geminiClaudeCarrierStandalone = "standalone"
|
||||
geminiClaudeCarrierText = "text"
|
||||
geminiClaudeCarrierFunction = "function"
|
||||
geminiClaudeCarrierAny = "any"
|
||||
)
|
||||
|
||||
type claudeSignatureTree = signature.ClaudeSignatureTree
|
||||
|
||||
func encodeGeminiClaudeCarrierSignature(rawSignature, direction, targetKind string) string {
|
||||
rawSignature = strings.TrimSpace(rawSignature)
|
||||
if rawSignature == "" {
|
||||
return ""
|
||||
}
|
||||
return geminiClaudeCarrierPrefix + direction + ":" + targetKind + ":" + base64.RawStdEncoding.EncodeToString([]byte(rawSignature))
|
||||
}
|
||||
|
||||
func decodeGeminiClaudeCarrierSignature(rawSignature string) (signatureValue, direction, targetKind string, marked, ok bool) {
|
||||
rawSignature = strings.TrimSpace(rawSignature)
|
||||
if !strings.HasPrefix(rawSignature, geminiClaudeCarrierPrefix) {
|
||||
return rawSignature, "", "", false, true
|
||||
}
|
||||
marked = true
|
||||
if len(rawSignature) > (signature.MaxGeminiThoughtSignatureLen*4/3)+1024 {
|
||||
return "", "", "", true, false
|
||||
}
|
||||
fields := strings.SplitN(strings.TrimPrefix(rawSignature, geminiClaudeCarrierPrefix), ":", 3)
|
||||
if len(fields) != 3 {
|
||||
return "", "", "", true, false
|
||||
}
|
||||
direction, targetKind = fields[0], fields[1]
|
||||
switch direction {
|
||||
case geminiClaudeCarrierNext, geminiClaudeCarrierPrevious, geminiClaudeCarrierStandalone:
|
||||
default:
|
||||
return "", "", "", true, false
|
||||
}
|
||||
switch targetKind {
|
||||
case geminiClaudeCarrierText, geminiClaudeCarrierFunction, geminiClaudeCarrierAny:
|
||||
default:
|
||||
return "", "", "", true, false
|
||||
}
|
||||
decoded, errDecode := base64.RawStdEncoding.DecodeString(fields[2])
|
||||
if errDecode != nil || len(decoded) == 0 || strings.HasPrefix(string(decoded), geminiClaudeCarrierPrefix) {
|
||||
return "", "", "", true, false
|
||||
}
|
||||
blockKind := signature.SignatureBlockKindGeminiModelPart
|
||||
if targetKind == geminiClaudeCarrierFunction {
|
||||
blockKind = signature.SignatureBlockKindGeminiFunctionCall
|
||||
}
|
||||
normalized, compatible := signature.CompatibleSignatureForProviderBlock(signature.SignatureProviderGemini, string(decoded), blockKind)
|
||||
if !compatible || signature.IsGeminiThoughtSignatureBypass(signature.SignaturePayloadWithoutProviderPrefix(normalized)) {
|
||||
return "", "", "", true, false
|
||||
}
|
||||
return normalized, direction, targetKind, true, true
|
||||
}
|
||||
|
||||
func geminiClaudeSemanticTargetKind(block gjson.Result) string {
|
||||
switch block.Get("type").String() {
|
||||
case "text":
|
||||
return geminiClaudeCarrierText
|
||||
case "tool_use":
|
||||
return geminiClaudeCarrierFunction
|
||||
case "thinking":
|
||||
if strings.TrimSpace(block.Get("thinking").String()) != "" {
|
||||
return geminiClaudeCarrierText
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func geminiClaudeCarrierMatchesAdjacent(blocks []gjson.Result, index int, direction, targetKind string) bool {
|
||||
step := 1
|
||||
if direction == geminiClaudeCarrierPrevious {
|
||||
step = -1
|
||||
}
|
||||
for adjacent := index + step; adjacent >= 0 && adjacent < len(blocks); adjacent += step {
|
||||
if kind := geminiClaudeSemanticTargetKind(blocks[adjacent]); kind != "" {
|
||||
return targetKind == geminiClaudeCarrierAny || targetKind == kind
|
||||
}
|
||||
if blocks[adjacent].Get("type").String() != "thinking" || strings.TrimSpace(blocks[adjacent].Get("thinking").String()) != "" {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// StripEmptySignatureThinkingBlocks removes thinking blocks whose signatures
|
||||
// are empty or not valid Claude thinking signatures. These usually come from
|
||||
// proxy-generated responses where no real Claude signature exists.
|
||||
func StripEmptySignatureThinkingBlocks(payload []byte) []byte {
|
||||
return signature.StripInvalidClaudeThinkingBlocks(payload, signature.ClaudeSignatureValidationOptions{PrefixOnly: true})
|
||||
}
|
||||
|
||||
// StripInvalidGeminiSignatureThinkingBlocks preserves only thinking carriers
|
||||
// whose signatures can be replayed to Gemini. Claude Code uses these carriers
|
||||
// to return provider-native signatures from prior translated responses.
|
||||
func StripInvalidGeminiSignatureThinkingBlocks(payload []byte) []byte {
|
||||
messages := gjson.GetBytes(payload, "messages")
|
||||
if !messages.IsArray() {
|
||||
return payload
|
||||
}
|
||||
changed := false
|
||||
messageItems := make([][]byte, 0, len(messages.Array()))
|
||||
for _, message := range messages.Array() {
|
||||
messageJSON := []byte(message.Raw)
|
||||
content := message.Get("content")
|
||||
if !content.IsArray() {
|
||||
messageItems = append(messageItems, messageJSON)
|
||||
continue
|
||||
}
|
||||
contentChanged := false
|
||||
assistantMessage := strings.EqualFold(message.Get("role").String(), "assistant")
|
||||
contentBlocks := content.Array()
|
||||
contentItems := make([][]byte, 0, len(contentBlocks))
|
||||
pendingCarrierTargetKind := ""
|
||||
for blockIndex, block := range contentBlocks {
|
||||
if block.Get("type").String() == "thinking" {
|
||||
rawSignature := strings.TrimSpace(block.Get("signature").String())
|
||||
thinkingText := strings.TrimSpace(block.Get("thinking").String())
|
||||
if rawSignature == "" && thinkingText != "" && (pendingCarrierTargetKind == geminiClaudeCarrierAny || pendingCarrierTargetKind == geminiClaudeCarrierText) {
|
||||
pendingCarrierTargetKind = ""
|
||||
contentItems = append(contentItems, []byte(block.Raw))
|
||||
continue
|
||||
}
|
||||
innerSignature, direction, targetKind, marked, okCarrier := decodeGeminiClaudeCarrierSignature(rawSignature)
|
||||
blockKind := signature.SignatureBlockKindGeminiModelPart
|
||||
if marked && targetKind == geminiClaudeCarrierFunction {
|
||||
blockKind = signature.SignatureBlockKindGeminiFunctionCall
|
||||
}
|
||||
invalidMarkedPlacement := false
|
||||
if marked {
|
||||
switch direction {
|
||||
case geminiClaudeCarrierNext, geminiClaudeCarrierPrevious:
|
||||
invalidMarkedPlacement = !geminiClaudeCarrierMatchesAdjacent(contentBlocks, blockIndex, direction, targetKind)
|
||||
case geminiClaudeCarrierStandalone:
|
||||
invalidMarkedPlacement = thinkingText != "" && targetKind == geminiClaudeCarrierFunction
|
||||
}
|
||||
if thinkingText != "" && direction == geminiClaudeCarrierPrevious {
|
||||
invalidMarkedPlacement = true
|
||||
}
|
||||
}
|
||||
if !okCarrier || !assistantMessage || invalidMarkedPlacement {
|
||||
pendingCarrierTargetKind = ""
|
||||
contentChanged = true
|
||||
continue
|
||||
}
|
||||
if !marked {
|
||||
innerSignature = rawSignature
|
||||
}
|
||||
if _, ok := signature.CompatibleSignatureForProviderBlock(signature.SignatureProviderGemini, innerSignature, blockKind); !ok {
|
||||
pendingCarrierTargetKind = ""
|
||||
contentChanged = true
|
||||
continue
|
||||
}
|
||||
if marked && direction == geminiClaudeCarrierNext {
|
||||
pendingCarrierTargetKind = targetKind
|
||||
} else {
|
||||
pendingCarrierTargetKind = ""
|
||||
}
|
||||
} else {
|
||||
pendingCarrierTargetKind = ""
|
||||
}
|
||||
contentItems = append(contentItems, []byte(block.Raw))
|
||||
}
|
||||
if contentChanged {
|
||||
messageJSON, _ = sjson.SetRawBytes(messageJSON, "content", translatorcommon.JoinRawArray(contentItems))
|
||||
changed = true
|
||||
}
|
||||
messageItems = append(messageItems, messageJSON)
|
||||
}
|
||||
if !changed {
|
||||
return payload
|
||||
}
|
||||
updated, errSet := sjson.SetRawBytes(payload, "messages", translatorcommon.JoinRawArray(messageItems))
|
||||
if errSet != nil {
|
||||
return payload
|
||||
}
|
||||
return updated
|
||||
}
|
||||
|
||||
func StripInvalidBypassSignatureThinkingBlocks(payload []byte) []byte {
|
||||
return signature.StripInvalidClaudeThinkingBlocks(payload, claudeBypassSignatureValidationOptions())
|
||||
}
|
||||
|
||||
func ValidateClaudeBypassSignatures(inputRawJSON []byte) error {
|
||||
return signature.ValidateClaudeThinkingSignatures(inputRawJSON, claudeBypassSignatureValidationOptions())
|
||||
}
|
||||
|
||||
func normalizeClaudeBypassSignature(rawSignature string) (string, error) {
|
||||
return signature.NormalizeClaudeThinkingSignature(rawSignature, claudeBypassSignatureValidationOptions())
|
||||
}
|
||||
|
||||
func inspectDoubleLayerSignature(sig string) (*claudeSignatureTree, error) {
|
||||
return signature.InspectClaudeDoubleLayerSignature(sig)
|
||||
}
|
||||
|
||||
func inspectSingleLayerSignature(sig string) (*claudeSignatureTree, error) {
|
||||
return signature.InspectClaudeSingleLayerSignature(sig)
|
||||
}
|
||||
|
||||
func inspectClaudeSignaturePayload(payload []byte, encodingLayers int) (*claudeSignatureTree, error) {
|
||||
return signature.InspectClaudeSignaturePayload(payload, encodingLayers)
|
||||
}
|
||||
|
||||
func claudeBypassSignatureValidationOptions() signature.ClaudeSignatureValidationOptions {
|
||||
return signature.ClaudeSignatureValidationOptions{Strict: cache.SignatureBypassStrictMode()}
|
||||
}
|
||||
Loading…
Reference in a new issue