| @@ -141,6 +141,7 @@ func (e *GeminiCLIExecutor) Execute(ctx context.Context, auth *cliproxyauth.Auth · 140 unmodified lines |
| 141 | 141 | requestedModel := helps.PayloadRequestedModel(opts, req.Model) |
| 142 | 142 | requestPath := helps.PayloadRequestPath(opts) |
| 143 | 143 | basePayload = helps.ApplyPayloadConfigWithRequest(e.cfg, baseModel, "gemini", from.String(), "request", basePayload, originalTranslated, requestedModel, requestPath, opts.Headers) |
| 144 | + basePayload = cleanGeminiCLIRequestSchemas(basePayload) |
| 144 | 145 | |
| 145 | 146 | action := "generateContent" |
| 146 | 147 | if req.Metadata != nil { |
| @@ -297,6 +298,7 @@ func (e *GeminiCLIExecutor) ExecuteStream(ctx context.Context, auth *cliproxyaut · 150 unmodified lines |
| 297 | 298 | requestedModel := helps.PayloadRequestedModel(opts, req.Model) |
| 298 | 299 | requestPath := helps.PayloadRequestPath(opts) |
| 299 | 300 | basePayload = helps.ApplyPayloadConfigWithRequest(e.cfg, baseModel, "gemini", from.String(), "request", basePayload, originalTranslated, requestedModel, requestPath, opts.Headers) |
| 301 | + basePayload = cleanGeminiCLIRequestSchemas(basePayload) |
| 300 | 302 | |
| 301 | 303 | projectID := resolveGeminiProjectID(auth) |
| 302 | 304 | |
| @@ -530,6 +532,7 @@ func (e *GeminiCLIExecutor) CountTokens(ctx context.Context, auth *cliproxyauth. · 227 unmodified lines |
| 530 | 532 | payload = deleteJSONField(payload, "model") |
| 531 | 533 | payload = deleteJSONField(payload, "request.safetySettings") |
| 532 | 534 | payload = fixGeminiCLIImageAspectRatio(baseModel, payload) |
| 535 | + payload = cleanGeminiCLIRequestSchemas(payload) |
| 533 | 536 | |
| 534 | 537 | tok, errTok := tokenSource.Token() |
| 535 | 538 | if errTok != nil { |
| @@ -859,6 +862,65 @@ func deleteJSONField(body []byte, key string) []byte { · 323 unmodified lines |
| 859 | 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 | 924 | func fixGeminiCLIImageAspectRatio(modelName string, rawJSON []byte) []byte { |
| 863 | 925 | if modelName == "gemini-2.5-flash-image-preview" { |
| 864 | 926 | aspectRatioResult := gjson.GetBytes(rawJSON, "request.generationConfig.imageConfig.aspectRatio") |