| @@ -141,6 +141,7 @@ func (e *GeminiCLIExecutor) Execute(ctx context.Context, auth *cliproxyauth.Auth · 140 unmodified lines |
| 141 | requestedModel := helps.PayloadRequestedModel(opts, req.Model) | 141 | requestedModel := helps.PayloadRequestedModel(opts, req.Model) |
| 142 | requestPath := helps.PayloadRequestPath(opts) | 142 | requestPath := helps.PayloadRequestPath(opts) |
| 143 | basePayload = helps.ApplyPayloadConfigWithRequest(e.cfg, baseModel, "gemini", from.String(), "request", basePayload, originalTranslated, requestedModel, requestPath, opts.Headers) | 143 | basePayload = helps.ApplyPayloadConfigWithRequest(e.cfg, baseModel, "gemini", from.String(), "request", basePayload, originalTranslated, requestedModel, requestPath, opts.Headers) |
| | 144 | basePayload = cleanGeminiCLIRequestSchemas(basePayload) |
| 144 | | 145 | |
| 145 | action := "generateContent" | 146 | action := "generateContent" |
| 146 | if req.Metadata != nil { | 147 | if req.Metadata != nil { |
| @@ -297,6 +298,7 @@ func (e *GeminiCLIExecutor) ExecuteStream(ctx context.Context, auth *cliproxyaut · 150 unmodified lines |
| 297 | requestedModel := helps.PayloadRequestedModel(opts, req.Model) | 298 | requestedModel := helps.PayloadRequestedModel(opts, req.Model) |
| 298 | requestPath := helps.PayloadRequestPath(opts) | 299 | requestPath := helps.PayloadRequestPath(opts) |
| 299 | basePayload = helps.ApplyPayloadConfigWithRequest(e.cfg, baseModel, "gemini", from.String(), "request", basePayload, originalTranslated, requestedModel, requestPath, opts.Headers) | 300 | basePayload = helps.ApplyPayloadConfigWithRequest(e.cfg, baseModel, "gemini", from.String(), "request", basePayload, originalTranslated, requestedModel, requestPath, opts.Headers) |
| | 301 | basePayload = cleanGeminiCLIRequestSchemas(basePayload) |
| 300 | | 302 | |
| 301 | projectID := resolveGeminiProjectID(auth) | 303 | projectID := resolveGeminiProjectID(auth) |
| 302 | | 304 | |
| @@ -530,6 +532,7 @@ func (e *GeminiCLIExecutor) CountTokens(ctx context.Context, auth *cliproxyauth. · 227 unmodified lines |
| 530 | payload = deleteJSONField(payload, "model") | 532 | payload = deleteJSONField(payload, "model") |
| 531 | payload = deleteJSONField(payload, "request.safetySettings") | 533 | payload = deleteJSONField(payload, "request.safetySettings") |
| 532 | payload = fixGeminiCLIImageAspectRatio(baseModel, payload) | 534 | payload = fixGeminiCLIImageAspectRatio(baseModel, payload) |
| | 535 | payload = cleanGeminiCLIRequestSchemas(payload) |
| 533 | | 536 | |
| 534 | tok, errTok := tokenSource.Token() | 537 | tok, errTok := tokenSource.Token() |
| 535 | if errTok != nil { | 538 | if errTok != nil { |
| @@ -859,6 +862,65 @@ func deleteJSONField(body []byte, key string) []byte { · 323 unmodified lines |
| 859 | return updated | 862 | return updated |
| 860 | } | 863 | } |
| 861 | | 864 | |
| | 865 | func cleanGeminiCLIRequestSchemas(body []byte) []byte { |
| | 866 | if len(body) == 0 { |
| | 867 | return body |
| | 868 | } |
| | 869 | hasTools := gjson.GetBytes(body, "request.tools.0").Exists() |
| | 870 | hasResponseSchema := gjson.GetBytes(body, "request.generationConfig.responseSchema").Exists() |
| | 871 | hasResponseJSONSchema := gjson.GetBytes(body, "request.generationConfig.responseJsonSchema").Exists() |
| | 872 | if !hasTools && !hasResponseSchema && !hasResponseJSONSchema { |
| | 873 | return body |
| | 874 | } |
| | 875 | |
| | 876 | tools := gjson.GetBytes(body, "request.tools") |
| | 877 | if tools.IsArray() { |
| | 878 | for i, tool := range tools.Array() { |
| | 879 | for _, declarationsKey := range []string{"function_declarations", "functionDeclarations"} { |
| | 880 | funcDecls := tool.Get(declarationsKey) |
| | 881 | if !funcDecls.IsArray() { |
| | 882 | continue |
| | 883 | } |
| | 884 | for j, decl := range funcDecls.Array() { |
| | 885 | for _, schemaKey := range []string{"parameters", "parametersJsonSchema"} { |
| | 886 | params := decl.Get(schemaKey) |
| | 887 | if !params.Exists() || !params.IsObject() { |
| | 888 | continue |
| | 889 | } |
| | 890 | cleaned := util.CleanJSONSchemaForGemini(params.Raw) |
| | 891 | path := fmt.Sprintf("request.tools.%d.%s.%d.%s", i, declarationsKey, j, schemaKey) |
| | 892 | updated, errSet := sjson.SetRawBytes(body, path, []byte(cleaned)) |
| | 893 | if errSet != nil { |
| | 894 | log.Errorf("gemini cli executor: failed to set cleaned schema at %s: %v", path, errSet) |
| | 895 | continue |
| | 896 | } |
| | 897 | body = updated |
| | 898 | } |
| | 899 | } |
| | 900 | } |
| | 901 | } |
| | 902 | } |
| | 903 | |
| | 904 | for _, schemaPath := range []string{ |
| | 905 | "request.generationConfig.responseSchema", |
| | 906 | "request.generationConfig.responseJsonSchema", |
| | 907 | } { |
| | 908 | responseSchema := gjson.GetBytes(body, schemaPath) |
| | 909 | if !responseSchema.IsObject() { |
| | 910 | continue |
| | 911 | } |
| | 912 | cleaned := util.CleanJSONSchemaForGemini(responseSchema.Raw) |
| | 913 | updated, errSet := sjson.SetRawBytes(body, schemaPath, []byte(cleaned)) |
| | 914 | if errSet != nil { |
| | 915 | log.Errorf("gemini cli executor: failed to set cleaned response schema at %s: %v", schemaPath, errSet) |
| | 916 | continue |
| | 917 | } |
| | 918 | body = updated |
| | 919 | } |
| | 920 | |
| | 921 | return body |
| | 922 | } |
| | 923 | |
| 862 | func fixGeminiCLIImageAspectRatio(modelName string, rawJSON []byte) []byte { | 924 | func fixGeminiCLIImageAspectRatio(modelName string, rawJSON []byte) []byte { |
| 863 | if modelName == "gemini-2.5-flash-image-preview" { | 925 | if modelName == "gemini-2.5-flash-image-preview" { |
| 864 | aspectRatioResult := gjson.GetBytes(rawJSON, "request.generationConfig.imageConfig.aspectRatio") | 926 | aspectRatioResult := gjson.GetBytes(rawJSON, "request.generationConfig.imageConfig.aspectRatio") |