Add projects

This commit is contained in:
Alois 2026-08-24 00:10:41 +02:00
commit 8b607dd700
Signed by: alois
SSH key fingerprint: SHA256:GBzT2DXvAuGV9XIV5W3WrzVpjU54FThmxHXdbz95J24
1802 changed files with 503346 additions and 2 deletions

View file

@ -0,0 +1,19 @@
package responses
import (
. "github.com/router-for-me/CLIProxyAPI/v7/internal/constant"
"github.com/router-for-me/CLIProxyAPI/v7/internal/interfaces"
"github.com/router-for-me/CLIProxyAPI/v7/internal/translator/translator"
)
func init() {
translator.Register(
OpenaiResponse,
Gemini,
ConvertOpenAIResponsesRequestToGemini,
interfaces.TranslateResponse{
Stream: ConvertGeminiResponseToOpenAIResponses,
NonStream: ConvertGeminiResponseToOpenAIResponsesNonStream,
},
)
}

View file

@ -0,0 +1,32 @@
package responses
import (
"testing"
"github.com/tidwall/gjson"
)
func TestConvertOpenAIResponsesRequestToGeminiBuildsGenerationConfigWithoutIntermediateObject(t *testing.T) {
input := []byte(`{"input":"hello","temperature":0.5,"top_p":0.9,"stop_sequences":["done"],"text":{"format":{"type":"json_schema","schema":{"type":"object"}}}}`)
output := ConvertOpenAIResponsesRequestToGemini("gemini-test", input, false)
if got := gjson.GetBytes(output, "generationConfig.temperature").Float(); got != 0.5 {
t.Fatalf("temperature = %v, want 0.5", got)
}
if got := gjson.GetBytes(output, "generationConfig.topP").Float(); got != 0.9 {
t.Fatalf("topP = %v, want 0.9", got)
}
if got := gjson.GetBytes(output, "generationConfig.stopSequences.0").String(); got != "done" {
t.Fatalf("stop sequence = %q, want done", got)
}
if got := gjson.GetBytes(output, "generationConfig.responseMimeType").String(); got != "application/json" {
t.Fatalf("responseMimeType = %q, want application/json", got)
}
if !gjson.GetBytes(output, "generationConfig.responseJsonSchema").Exists() {
t.Fatal("responseJsonSchema should be present")
}
if gjson.GetBytes(output, "generationConfig.responseSchema").Exists() {
t.Fatal("responseSchema should not be present")
}
}

View file

@ -0,0 +1,199 @@
package responses
import (
"encoding/base64"
"encoding/json"
"strings"
sigcompat "github.com/router-for-me/CLIProxyAPI/v7/internal/signature"
"github.com/tidwall/gjson"
"github.com/tidwall/sjson"
)
const (
geminiResponsesCarrierPrefix = "cpa-gemini-responses-carrier-v1:"
geminiResponsesCarrierNext = "next"
geminiResponsesCarrierPrevious = "previous"
geminiResponsesCarrierStandalone = "standalone"
geminiResponsesCarrierText = "text"
geminiResponsesCarrierFunction = "function"
geminiResponsesCarrierAny = "any"
geminiResponsesCarrierDirectionField = "_cpa_reasoning_direction"
geminiResponsesCarrierTargetField = "_cpa_reasoning_target"
geminiResponsesCarrierSignatureField = "_cpa_reasoning_signature"
geminiResponsesCarrierSummaryField = "_cpa_reasoning_summary"
)
func encodeGeminiResponsesCarrier(rawSignature, direction, targetKind string) string {
rawSignature = strings.TrimSpace(rawSignature)
if rawSignature == "" {
return ""
}
return geminiResponsesCarrierPrefix + direction + ":" + targetKind + ":" + base64.RawStdEncoding.EncodeToString([]byte(rawSignature))
}
func decodeGeminiResponsesCarrier(rawSignature string) (signatureValue, direction, targetKind string, marked, ok bool) {
rawSignature = strings.TrimSpace(rawSignature)
if !strings.HasPrefix(rawSignature, geminiResponsesCarrierPrefix) {
return rawSignature, "", "", false, true
}
marked = true
if len(rawSignature) > (sigcompat.MaxGeminiThoughtSignatureLen*4/3)+1024 {
return "", "", "", true, false
}
fields := strings.SplitN(strings.TrimPrefix(rawSignature, geminiResponsesCarrierPrefix), ":", 3)
if len(fields) != 3 {
return "", "", "", true, false
}
direction, targetKind = fields[0], fields[1]
switch direction {
case geminiResponsesCarrierNext, geminiResponsesCarrierPrevious, geminiResponsesCarrierStandalone:
default:
return "", "", "", true, false
}
switch targetKind {
case geminiResponsesCarrierText, geminiResponsesCarrierFunction, geminiResponsesCarrierAny:
default:
return "", "", "", true, false
}
decoded, errDecode := base64.RawStdEncoding.DecodeString(fields[2])
if errDecode != nil || len(decoded) == 0 || strings.HasPrefix(string(decoded), geminiResponsesCarrierPrefix) {
return "", "", "", true, false
}
return string(decoded), direction, targetKind, true, true
}
func compatibleGeminiResponsesCarrierSignature(rawSignature, targetKind string) (string, bool) {
blockKind := sigcompat.SignatureBlockKindGeminiModelPart
if targetKind == geminiResponsesCarrierFunction {
blockKind = sigcompat.SignatureBlockKindGeminiFunctionCall
}
normalized, compatible := sigcompat.CompatibleSignatureForProviderBlock(sigcompat.SignatureProviderGemini, rawSignature, blockKind)
if !compatible || sigcompat.IsGeminiThoughtSignatureBypass(sigcompat.SignaturePayloadWithoutProviderPrefix(normalized)) {
return "", false
}
return normalized, true
}
func geminiResponsesCarrierSemanticTarget(item gjson.Result) string {
switch item.Get("type").String() {
case "function_call", "custom_tool_call":
return geminiResponsesCarrierFunction
case "reasoning":
if strings.TrimSpace(item.Get("summary.0.text").String()) != "" {
return geminiResponsesCarrierText
}
}
if _, ok := openAIResponsesAssistantVisibleText(item); ok {
return geminiResponsesCarrierText
}
return ""
}
func geminiResponsesCarrierMatchesAdjacent(items []gjson.Result, index int, direction, targetKind string) bool {
step := 1
if direction == geminiResponsesCarrierPrevious {
step = -1
}
for adjacent := index + step; adjacent >= 0 && adjacent < len(items); adjacent += step {
if kind := geminiResponsesCarrierSemanticTarget(items[adjacent]); kind != "" {
return targetKind == geminiResponsesCarrierAny || targetKind == kind
}
if !isOpenAIResponsesDetachedCarrier(items[adjacent]) {
return false
}
}
return false
}
func hasInternalCarrierFields(item gjson.Result) bool {
return item.Get(geminiResponsesCarrierDirectionField).Exists() ||
item.Get(geminiResponsesCarrierTargetField).Exists() ||
item.Get(geminiResponsesCarrierSignatureField).Exists() ||
item.Get(geminiResponsesCarrierSummaryField).Exists()
}
func stripGeminiResponsesCarrierMetadata(rawJSON string) ([]byte, bool) {
var fields map[string]json.RawMessage
if err := json.Unmarshal([]byte(rawJSON), &fields); err != nil {
return []byte(rawJSON), false
}
delete(fields, geminiResponsesCarrierDirectionField)
delete(fields, geminiResponsesCarrierTargetField)
delete(fields, geminiResponsesCarrierSignatureField)
delete(fields, geminiResponsesCarrierSummaryField)
stripped, errMarshal := json.Marshal(fields)
if errMarshal != nil {
return []byte(rawJSON), false
}
return stripped, true
}
func normalizeGeminiResponsesCarriers(items []gjson.Result) ([]gjson.Result, bool) {
normalized := make([]gjson.Result, 0, len(items))
hasValidCarrier := false
for itemIndex, originalItem := range items {
item := originalItem
var itemJSON []byte
if hasInternalCarrierFields(originalItem) {
stripped, ok := stripGeminiResponsesCarrierMetadata(originalItem.Raw)
if ok {
itemJSON = stripped
item = gjson.ParseBytes(itemJSON)
}
}
if item.Get("type").String() != "reasoning" {
normalized = append(normalized, item)
continue
}
if len(itemJSON) == 0 {
itemJSON = []byte(item.Raw)
}
rawSignature := strings.TrimSpace(item.Get("encrypted_content").String())
signature, direction, targetKind, marked, ok := decodeGeminiResponsesCarrier(rawSignature)
if !marked {
if rawSignature != "" {
_, hasCompatibleRawCarrier := compatibleGeminiResponsesCarrierSignature(rawSignature, geminiResponsesCarrierAny)
hasValidCarrier = hasValidCarrier || hasCompatibleRawCarrier
}
normalized = append(normalized, item)
continue
}
if ok {
signature, ok = compatibleGeminiResponsesCarrierSignature(signature, targetKind)
}
if ok && direction != geminiResponsesCarrierStandalone {
ok = geminiResponsesCarrierMatchesAdjacent(items, itemIndex, direction, targetKind)
}
isDetached := isOpenAIResponsesDetachedCarrier(item)
hasSummary := strings.TrimSpace(item.Get("summary.0.text").String()) != ""
validSummaryCarrier := hasSummary && ((direction == geminiResponsesCarrierStandalone && (targetKind == geminiResponsesCarrierText || targetKind == geminiResponsesCarrierAny)) || direction == geminiResponsesCarrierNext)
if !ok || (!isDetached && !validSummaryCarrier) {
if strings.TrimSpace(item.Get("summary.0.text").String()) == "" {
continue
}
itemJSON, _ = sjson.DeleteBytes(itemJSON, "encrypted_content")
normalized = append(normalized, gjson.ParseBytes(itemJSON))
continue
}
hasValidCarrier = true
itemJSON, _ = sjson.SetBytes(itemJSON, "encrypted_content", signature)
itemJSON, _ = sjson.SetBytes(itemJSON, geminiResponsesCarrierDirectionField, direction)
itemJSON, _ = sjson.SetBytes(itemJSON, geminiResponsesCarrierTargetField, targetKind)
normalized = append(normalized, gjson.ParseBytes(itemJSON))
}
return normalized, hasValidCarrier
}
func geminiResponsesCarrierDirection(item gjson.Result) string {
return item.Get(geminiResponsesCarrierDirectionField).String()
}
func geminiResponsesCarrierTarget(item gjson.Result) string {
return item.Get(geminiResponsesCarrierTargetField).String()
}
func isOpenAIResponsesDetachedCarrier(item gjson.Result) bool {
return item.Get("type").String() == "reasoning" && strings.TrimSpace(item.Get("encrypted_content").String()) != "" && strings.TrimSpace(item.Get("summary.0.text").String()) == ""
}

View file

@ -0,0 +1,167 @@
package responses
import (
"context"
"encoding/base64"
"strconv"
"strings"
"testing"
"github.com/tidwall/gjson"
"google.golang.org/protobuf/encoding/protowire"
)
func TestGeminiResponsesCarrierRoundTrip(t *testing.T) {
for _, testCase := range []struct {
direction string
targetKind string
}{
{geminiResponsesCarrierNext, geminiResponsesCarrierText},
{geminiResponsesCarrierPrevious, geminiResponsesCarrierFunction},
{geminiResponsesCarrierStandalone, geminiResponsesCarrierAny},
} {
encoded := encodeGeminiResponsesCarrier(testResponsesGeminiThoughtSignature, testCase.direction, testCase.targetKind)
signature, direction, targetKind, marked, ok := decodeGeminiResponsesCarrier(encoded)
if !marked || !ok || signature != testResponsesGeminiThoughtSignature || direction != testCase.direction || targetKind != testCase.targetKind {
t.Fatalf("carrier round-trip = %q/%q/%q marked=%v ok=%v", signature, direction, targetKind, marked, ok)
}
}
}
func TestNormalizeGeminiResponsesCarriersDropsMalformedEnvelope(t *testing.T) {
items := gjson.Parse(`[{"type":"reasoning","encrypted_content":"` + geminiResponsesCarrierPrefix + `previous:text:not-base64!","summary":[]},{"type":"message","role":"assistant","content":[{"type":"output_text","text":"safe"}]}]`).Array()
normalized, hasCarrier := normalizeGeminiResponsesCarriers(items)
if hasCarrier || len(normalized) != 1 || normalized[0].Get("type").String() != "message" || strings.Contains(normalized[0].Raw, geminiResponsesCarrierPrefix) {
t.Fatalf("malformed carrier was preserved: %v", normalized)
}
}
func TestConvertOpenAIResponsesRequestToGemini_DecodesCarrierForAliasModel(t *testing.T) {
carrier := encodeGeminiResponsesCarrier(testResponsesGeminiThoughtSignature, geminiResponsesCarrierNext, geminiResponsesCarrierText)
request := []byte(`{"model":"alias-without-provider-name","input":[{"type":"reasoning","encrypted_content":"` + carrier + `","summary":[]},{"type":"message","role":"assistant","content":[{"type":"output_text","text":"answer"}]}]}`)
translated := ConvertOpenAIResponsesRequestToGemini("alias-without-provider-name", request, false)
part := gjson.GetBytes(translated, "contents.0.parts.0")
if part.Get("text").String() != "answer" || part.Get("thoughtSignature").String() != testResponsesGeminiThoughtSignature || strings.Contains(string(translated), geminiResponsesCarrierPrefix) {
t.Fatalf("alias model did not decode carrier: %s", translated)
}
}
func TestGeminiResponsesWrappedUUIDFunctionSignatureRoundTrip(t *testing.T) {
const providerUUID = "e24830a7-5cd6-42fe-998b-ee539e72b9c3"
inner := protowire.AppendTag(nil, 1, protowire.BytesType)
inner = protowire.AppendBytes(inner, []byte(providerUUID))
outer := protowire.AppendTag(nil, 2, protowire.BytesType)
outer = protowire.AppendBytes(outer, inner)
signature := base64.StdEncoding.EncodeToString(outer)
providerResponse := `data: {"response":{"candidates":[{"content":{"role":"model","parts":[{"thoughtSignature":"` + signature + `","functionCall":{"id":"native-call","name":"run","args":{"command":"true"}}}]},"finishReason":"STOP"}],"modelVersion":"gemini-3.6-flash","responseId":"wrapped-uuid"}}`
var state any
chunks := ConvertGeminiResponseToOpenAIResponses(context.Background(), "gemini-3.6-flash", []byte(`{"model":"alias-without-provider-name"}`), nil, []byte(providerResponse), &state)
clientItems := make([]string, 0, 2)
callID := ""
for _, chunk := range chunks {
event, data := parseSSEEvent(t, chunk)
if event != "response.output_item.done" {
continue
}
item := data.Get("item")
switch item.Get("type").String() {
case "reasoning":
decoded, direction, targetKind, marked, ok := decodeGeminiResponsesCarrier(item.Get("encrypted_content").String())
if !marked || !ok || decoded != signature || direction != geminiResponsesCarrierNext || targetKind != geminiResponsesCarrierFunction {
t.Fatalf("provider signature carrier = marked:%v ok:%v direction:%q target:%q", marked, ok, direction, targetKind)
}
clientItems = append(clientItems, item.Raw)
case "function_call":
callID = item.Get("call_id").String()
clientItems = append(clientItems, item.Raw)
}
}
if len(clientItems) != 2 || callID == "" {
t.Fatalf("Responses client items = %v, call ID present=%v", clientItems, callID != "")
}
clientItems = append(clientItems, `{"type":"function_call_output","call_id":`+strconv.Quote(callID)+`,"output":"ok"}`)
request := []byte(`{"model":"alias-without-provider-name","input":[` + strings.Join(clientItems, ",") + `]}`)
translated := ConvertOpenAIResponsesRequestToGemini("alias-without-provider-name", request, false)
var functionPart gjson.Result
gjson.GetBytes(translated, "contents").ForEach(func(_, content gjson.Result) bool {
content.Get("parts").ForEach(func(_, part gjson.Result) bool {
if part.Get("functionCall").Exists() {
functionPart = part
return false
}
return true
})
return !functionPart.Exists()
})
if !functionPart.Exists() || functionPart.Get("functionCall.name").String() != "run" || functionPart.Get("functionCall.args.command").String() != "true" {
t.Fatalf("function carrier did not bind to the native call: %s", translated)
}
if got := functionPart.Get("thoughtSignature").String(); got != signature || got == geminiResponsesThoughtSignature {
t.Fatalf("function signature = %q, want provider-native wrapped UUID signature", got)
}
if strings.Contains(string(translated), geminiResponsesCarrierPrefix) {
t.Fatalf("carrier envelope reached Gemini: %s", translated)
}
}
func TestConvertOpenAIResponsesRequestToGemini_DecodesLegacyRawCarrierForAliasModel(t *testing.T) {
request := []byte(`{"model":"alias-without-provider-name","input":[{"type":"reasoning","encrypted_content":"` + testResponsesGeminiThoughtSignature + `","summary":[]},{"type":"function_call","call_id":"call-1","name":"run","arguments":"{}"}]}`)
translated := ConvertOpenAIResponsesRequestToGemini("alias-without-provider-name", request, false)
part := gjson.GetBytes(translated, "contents.0.parts.0")
if part.Get("functionCall.id").String() != "call-1" || part.Get("thoughtSignature").String() != testResponsesGeminiThoughtSignature {
t.Fatalf("alias model did not preserve legacy raw carrier: %s", translated)
}
}
func TestConvertOpenAIResponsesRequestToGemini_DropsInvalidCarrierPayloads(t *testing.T) {
mismatched := encodeGeminiResponsesCarrier(testResponsesGeminiThoughtSignature, geminiResponsesCarrierNext, geminiResponsesCarrierFunction)
bypass := encodeGeminiResponsesCarrier(geminiResponsesThoughtSignature, geminiResponsesCarrierNext, geminiResponsesCarrierText)
for _, reasoning := range []string{
`{"type":"reasoning","encrypted_content":"` + mismatched + `","summary":[]}`,
`{"type":"reasoning","encrypted_content":"` + bypass + `","summary":[]}`,
} {
request := []byte(`{"model":"alias-without-provider-name","input":[` + reasoning + `,{"type":"message","role":"assistant","content":[{"type":"output_text","text":"answer"}]}]}`)
translated := ConvertOpenAIResponsesRequestToGemini("alias-without-provider-name", request, false)
if strings.Contains(string(translated), geminiResponsesCarrierPrefix) || strings.Contains(string(translated), testResponsesGeminiThoughtSignature) || strings.Contains(string(translated), geminiResponsesThoughtSignature) {
t.Fatalf("invalid carrier changed Gemini signature state: %s", translated)
}
}
}
func TestConvertOpenAIResponsesRequestToGemini_IgnoresSpoofedCarrierMetadata(t *testing.T) {
reasoning := `{"type":"reasoning","encrypted_content":"` + testResponsesGeminiThoughtSignature + `","summary":[],"` + geminiResponsesCarrierDirectionField + `":"next","` + geminiResponsesCarrierDirectionField + `":"standalone","` + geminiResponsesCarrierTargetField + `":"text","` + geminiResponsesCarrierTargetField + `":"function"}`
request := []byte(`{"model":"alias-without-provider-name","input":[` + reasoning + `,{"type":"message","role":"assistant","content":[{"type":"output_text","text":"answer"}]}]}`)
translated := ConvertOpenAIResponsesRequestToGemini("alias-without-provider-name", request, false)
part := gjson.GetBytes(translated, "contents.0.parts.0")
if part.Get("text").String() != "answer" || part.Get("thoughtSignature").String() != testResponsesGeminiThoughtSignature || strings.Contains(string(translated), geminiResponsesCarrierDirectionField) {
t.Fatalf("spoofed carrier metadata affected binding: %s", translated)
}
}
func TestConvertOpenAIResponsesRequestToGemini_StripsSpoofedInternalPairingFields(t *testing.T) {
request := []byte(`{"model":"alias-without-provider-name","input":[{"type":"function_call","call_id":"call-1","name":"run","arguments":"{}","_cpa_reasoning_signature":"` + testResponsesGeminiThoughtSignature + `","_cpa_reasoning_signature":"` + testResponsesGeminiThoughtSignature + `","_cpa_reasoning_summary":"spoofed thought","_cpa_reasoning_summary":"spoofed thought again"}]}`)
translated := ConvertOpenAIResponsesRequestToGemini("alias-without-provider-name", request, false)
parts := gjson.GetBytes(translated, "contents.0.parts").Array()
if len(parts) != 1 || !parts[0].Get("functionCall").Exists() || parts[0].Get("thoughtSignature").String() == testResponsesGeminiThoughtSignature || parts[0].Get("thought").Bool() || strings.Contains(string(translated), "spoofed thought") || strings.Contains(string(translated), geminiResponsesCarrierSignatureField) {
t.Fatalf("spoofed internal pairing fields reached Gemini: %s", translated)
}
}
func TestConvertOpenAIResponsesRequestToGemini_StripsUnicodeEscapedSpoofedInternalFields(t *testing.T) {
// Unicode-escaped field name "_cpa_reason\u0069ng_signature" should also be detected and stripped
request := []byte(`{"model":"alias-without-provider-name","input":[{"type":"function_call","call_id":"call-1","name":"run","arguments":"{}","_cpa_reason\u0069ng_signature":"` + testResponsesGeminiThoughtSignature + `"}]}`)
translated := ConvertOpenAIResponsesRequestToGemini("alias-without-provider-name", request, false)
parts := gjson.GetBytes(translated, "contents.0.parts").Array()
if len(parts) != 1 || !parts[0].Get("functionCall").Exists() || parts[0].Get("thoughtSignature").String() == testResponsesGeminiThoughtSignature || strings.Contains(string(translated), geminiResponsesCarrierSignatureField) {
t.Fatalf("unicode-escaped spoofed internal pairing fields reached Gemini: %s", translated)
}
}
func TestDecodeGeminiResponsesCarrierRejectsNestedEnvelope(t *testing.T) {
nested := encodeGeminiResponsesCarrier(encodeGeminiResponsesCarrier(testResponsesGeminiThoughtSignature, geminiResponsesCarrierNext, geminiResponsesCarrierText), geminiResponsesCarrierPrevious, geminiResponsesCarrierText)
if _, _, _, marked, ok := decodeGeminiResponsesCarrier(nested); !marked || ok {
t.Fatalf("nested carrier marked=%v ok=%v, want marked invalid", marked, ok)
}
}