From a53c84d0d15c77228a8c4697f71072eb67c1446e Mon Sep 17 00:00:00 2001 From: hkfires <10558748+hkfires@users.noreply.github.com> Date: Fri, 31 Oct 2025 08:20:47 +0800 Subject: [PATCH] feat(gemini): apply default safety settings across request translators --- .../claude/gemini-cli_claude_request.go | 6 ++- .../gemini/gemini-cli_gemini_request.go | 3 +- .../gemini-cli_openai_request.go | 3 +- .../gemini/claude/gemini_claude_request.go | 6 ++- internal/translator/gemini/common/safety.go | 47 +++++++++++++++++++ .../gemini-cli/gemini_gemini-cli_request.go | 3 +- .../gemini/gemini/gemini_gemini_request.go | 7 ++- .../chat-completions/gemini_openai_request.go | 3 ++ .../gemini_openai-responses_request.go | 5 +- 9 files changed, 75 insertions(+), 8 deletions(-) create mode 100644 internal/translator/gemini/common/safety.go diff --git a/internal/translator/gemini-cli/claude/gemini-cli_claude_request.go b/internal/translator/gemini-cli/claude/gemini-cli_claude_request.go index 1699f018..a4c737f0 100644 --- a/internal/translator/gemini-cli/claude/gemini-cli_claude_request.go +++ b/internal/translator/gemini-cli/claude/gemini-cli_claude_request.go @@ -11,6 +11,7 @@ import ( "strings" client "github.com/router-for-me/CLIProxyAPI/v6/internal/interfaces" + "github.com/router-for-me/CLIProxyAPI/v6/internal/translator/gemini/common" "github.com/router-for-me/CLIProxyAPI/v6/internal/util" "github.com/tidwall/gjson" "github.com/tidwall/sjson" @@ -173,5 +174,8 @@ func ConvertClaudeRequestToCLI(modelName string, inputRawJSON []byte, _ bool) [] out, _ = sjson.Set(out, "request.generationConfig.topK", v.Num) } - return []byte(out) + outBytes := []byte(out) + outBytes = common.AttachDefaultSafetySettings(outBytes, "request.safetySettings") + + return outBytes } diff --git a/internal/translator/gemini-cli/gemini/gemini-cli_gemini_request.go b/internal/translator/gemini-cli/gemini/gemini-cli_gemini_request.go index a406a8aa..e252a765 100644 --- a/internal/translator/gemini-cli/gemini/gemini-cli_gemini_request.go +++ b/internal/translator/gemini-cli/gemini/gemini-cli_gemini_request.go @@ -10,6 +10,7 @@ import ( "encoding/json" "fmt" + "github.com/router-for-me/CLIProxyAPI/v6/internal/translator/gemini/common" "github.com/router-for-me/CLIProxyAPI/v6/internal/util" log "github.com/sirupsen/logrus" "github.com/tidwall/gjson" @@ -97,7 +98,7 @@ func ConvertGeminiRequestToGeminiCLI(_ string, inputRawJSON []byte, _ bool) []by } } - return rawJSON + return common.AttachDefaultSafetySettings(rawJSON, "request.safetySettings") } // FunctionCallGroup represents a group of function calls and their responses diff --git a/internal/translator/gemini-cli/openai/chat-completions/gemini-cli_openai_request.go b/internal/translator/gemini-cli/openai/chat-completions/gemini-cli_openai_request.go index fec15a07..164d46ae 100644 --- a/internal/translator/gemini-cli/openai/chat-completions/gemini-cli_openai_request.go +++ b/internal/translator/gemini-cli/openai/chat-completions/gemini-cli_openai_request.go @@ -8,6 +8,7 @@ import ( "strings" "github.com/router-for-me/CLIProxyAPI/v6/internal/misc" + "github.com/router-for-me/CLIProxyAPI/v6/internal/translator/gemini/common" "github.com/router-for-me/CLIProxyAPI/v6/internal/util" log "github.com/sirupsen/logrus" "github.com/tidwall/gjson" @@ -308,7 +309,7 @@ func ConvertOpenAIRequestToGeminiCLI(modelName string, inputRawJSON []byte, _ bo } } - return out + return common.AttachDefaultSafetySettings(out, "request.safetySettings") } // itoa converts int to string without strconv import for few usages. diff --git a/internal/translator/gemini/claude/gemini_claude_request.go b/internal/translator/gemini/claude/gemini_claude_request.go index 9b5a1671..4ae6cbea 100644 --- a/internal/translator/gemini/claude/gemini_claude_request.go +++ b/internal/translator/gemini/claude/gemini_claude_request.go @@ -11,6 +11,7 @@ import ( "strings" client "github.com/router-for-me/CLIProxyAPI/v6/internal/interfaces" + "github.com/router-for-me/CLIProxyAPI/v6/internal/translator/gemini/common" "github.com/router-for-me/CLIProxyAPI/v6/internal/util" "github.com/tidwall/gjson" "github.com/tidwall/sjson" @@ -166,5 +167,8 @@ func ConvertClaudeRequestToGemini(modelName string, inputRawJSON []byte, _ bool) out, _ = sjson.Set(out, "generationConfig.topK", v.Num) } - return []byte(out) + result := []byte(out) + result = common.AttachDefaultSafetySettings(result, "safetySettings") + + return result } diff --git a/internal/translator/gemini/common/safety.go b/internal/translator/gemini/common/safety.go new file mode 100644 index 00000000..e4b14293 --- /dev/null +++ b/internal/translator/gemini/common/safety.go @@ -0,0 +1,47 @@ +package common + +import ( + "github.com/tidwall/gjson" + "github.com/tidwall/sjson" +) + +// DefaultSafetySettings returns the default Gemini safety configuration we attach to requests. +func DefaultSafetySettings() []map[string]string { + return []map[string]string{ + { + "category": "HARM_CATEGORY_HARASSMENT", + "threshold": "OFF", + }, + { + "category": "HARM_CATEGORY_HATE_SPEECH", + "threshold": "OFF", + }, + { + "category": "HARM_CATEGORY_SEXUALLY_EXPLICIT", + "threshold": "OFF", + }, + { + "category": "HARM_CATEGORY_DANGEROUS_CONTENT", + "threshold": "OFF", + }, + { + "category": "HARM_CATEGORY_CIVIC_INTEGRITY", + "threshold": "BLOCK_NONE", + }, + } +} + +// AttachDefaultSafetySettings ensures the default safety settings are present when absent. +// The caller must provide the target JSON path (e.g. "safetySettings" or "request.safetySettings"). +func AttachDefaultSafetySettings(rawJSON []byte, path string) []byte { + if gjson.GetBytes(rawJSON, path).Exists() { + return rawJSON + } + + out, err := sjson.SetBytes(rawJSON, path, DefaultSafetySettings()) + if err != nil { + return rawJSON + } + + return out +} diff --git a/internal/translator/gemini/gemini-cli/gemini_gemini-cli_request.go b/internal/translator/gemini/gemini-cli/gemini_gemini-cli_request.go index 9dc4d39a..d5a15281 100644 --- a/internal/translator/gemini/gemini-cli/gemini_gemini-cli_request.go +++ b/internal/translator/gemini/gemini-cli/gemini_gemini-cli_request.go @@ -9,6 +9,7 @@ import ( "bytes" "fmt" + "github.com/router-for-me/CLIProxyAPI/v6/internal/translator/gemini/common" "github.com/router-for-me/CLIProxyAPI/v6/internal/util" "github.com/tidwall/gjson" "github.com/tidwall/sjson" @@ -45,5 +46,5 @@ func ConvertGeminiCLIRequestToGemini(_ string, inputRawJSON []byte, _ bool) []by } } - return rawJSON + return common.AttachDefaultSafetySettings(rawJSON, "safetySettings") } diff --git a/internal/translator/gemini/gemini/gemini_gemini_request.go b/internal/translator/gemini/gemini/gemini_gemini_request.go index cc23edb3..685cc66f 100644 --- a/internal/translator/gemini/gemini/gemini_gemini_request.go +++ b/internal/translator/gemini/gemini/gemini_gemini_request.go @@ -7,6 +7,7 @@ import ( "bytes" "fmt" + "github.com/router-for-me/CLIProxyAPI/v6/internal/translator/gemini/common" "github.com/router-for-me/CLIProxyAPI/v6/internal/util" "github.com/tidwall/gjson" "github.com/tidwall/sjson" @@ -19,10 +20,10 @@ import ( // It keeps the payload otherwise unchanged. func ConvertGeminiRequestToGemini(_ string, inputRawJSON []byte, _ bool) []byte { rawJSON := bytes.Clone(inputRawJSON) - // Fast path: if no contents field, return as-is + // Fast path: if no contents field, only attach safety settings contents := gjson.GetBytes(rawJSON, "contents") if !contents.Exists() { - return rawJSON + return common.AttachDefaultSafetySettings(rawJSON, "safetySettings") } toolsResult := gjson.GetBytes(rawJSON, "tools") @@ -71,5 +72,7 @@ func ConvertGeminiRequestToGemini(_ string, inputRawJSON []byte, _ bool) []byte return true }) + out = common.AttachDefaultSafetySettings(out, "safetySettings") + return out } diff --git a/internal/translator/gemini/openai/chat-completions/gemini_openai_request.go b/internal/translator/gemini/openai/chat-completions/gemini_openai_request.go index 60e3042a..f98dbbf5 100644 --- a/internal/translator/gemini/openai/chat-completions/gemini_openai_request.go +++ b/internal/translator/gemini/openai/chat-completions/gemini_openai_request.go @@ -8,6 +8,7 @@ import ( "strings" "github.com/router-for-me/CLIProxyAPI/v6/internal/misc" + "github.com/router-for-me/CLIProxyAPI/v6/internal/translator/gemini/common" "github.com/router-for-me/CLIProxyAPI/v6/internal/util" log "github.com/sirupsen/logrus" "github.com/tidwall/gjson" @@ -308,6 +309,8 @@ func ConvertOpenAIRequestToGemini(modelName string, inputRawJSON []byte, _ bool) } } + out = common.AttachDefaultSafetySettings(out, "safetySettings") + return out } diff --git a/internal/translator/gemini/openai/responses/gemini_openai-responses_request.go b/internal/translator/gemini/openai/responses/gemini_openai-responses_request.go index 051eb2d5..287beb43 100644 --- a/internal/translator/gemini/openai/responses/gemini_openai-responses_request.go +++ b/internal/translator/gemini/openai/responses/gemini_openai-responses_request.go @@ -4,6 +4,7 @@ import ( "bytes" "strings" + "github.com/router-for-me/CLIProxyAPI/v6/internal/translator/gemini/common" "github.com/router-for-me/CLIProxyAPI/v6/internal/util" "github.com/tidwall/gjson" "github.com/tidwall/sjson" @@ -291,5 +292,7 @@ func ConvertOpenAIResponsesRequestToGemini(modelName string, inputRawJSON []byte } } } - return []byte(out) + result := []byte(out) + result = common.AttachDefaultSafetySettings(result, "safetySettings") + return result }