From 7521489aba3b48716b2b0eef331432d1c10bfefe Mon Sep 17 00:00:00 2001 From: FloatingDream528 <82099714+FloatingDream528@users.noreply.github.com> Date: Mon, 28 Sep 2026 20:52:59 +0800 Subject: [PATCH] =?UTF-8?q?fix(converter):=20=E6=B8=85=E6=B4=97=20schema?= =?UTF-8?q?=20=E4=B8=AD=E7=9A=84=20enum=20=E4=BB=A5=E9=80=82=E9=85=8D=20Ge?= =?UTF-8?q?mini=20OpenAPI=20TYPE=5FSTRING=20=E6=A0=A1=E9=AA=8C=E9=99=90?= =?UTF-8?q?=E5=88=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/converter/openai2gemini.py | 20 +++++++++++++++- tests/test_openai2gemini_tool_calls.py | 32 ++++++++++++++++++++++++++ 2 files changed, 51 insertions(+), 1 deletion(-) diff --git a/src/converter/openai2gemini.py b/src/converter/openai2gemini.py index 6c983d4d8..99acabc7f 100644 --- a/src/converter/openai2gemini.py +++ b/src/converter/openai2gemini.py @@ -582,7 +582,16 @@ def _clean_schema_for_gemini(schema: Any, root_schema: Optional[Dict[str, Any]] # 10. 去重 required 数组 if "required" in result and isinstance(result["required"], list): result["required"] = list(dict.fromkeys(result["required"])) # 保持顺序去重 - + + # Gemini OpenAPI 规范要求:enum 元素必须为 TYPE_STRING + if "enum" in result: + enum_val = result.get("enum") + if isinstance(enum_val, list): + if result.get("type") == "BOOLEAN" or any(isinstance(x, bool) for x in enum_val): + result.pop("enum", None) + else: + result["enum"] = [str(x) for x in enum_val] + return result @@ -769,6 +778,15 @@ def _clean_schema_for_parameters_json_schema( else: result.pop("required", None) + # Gemini OpenAPI 规范要求:enum 元素必须为 TYPE_STRING + if "enum" in result: + enum_val = result.get("enum") + if isinstance(enum_val, list): + if result.get("type") == "boolean" or any(isinstance(x, bool) for x in enum_val): + result.pop("enum", None) + else: + result["enum"] = [str(x) for x in enum_val] + return result diff --git a/tests/test_openai2gemini_tool_calls.py b/tests/test_openai2gemini_tool_calls.py index 763115558..bedc19a7f 100644 --- a/tests/test_openai2gemini_tool_calls.py +++ b/tests/test_openai2gemini_tool_calls.py @@ -181,3 +181,35 @@ def test_convert_openai_request_merges_adjacent_same_role_contents(): assert "text" in part_types + + +def test_clean_schema_removes_boolean_enum_and_stringifies_enum(): + """测试 Gemini schema 清洗:移除 boolean 类型的 enum,并将非字符串 enum 转换为字符串""" + from src.converter.openai2gemini import ( + _clean_schema_for_gemini, + _clean_schema_for_parameters_json_schema, + ) + + schema_params = { + "type": "object", + "properties": { + "is_active": {"type": "boolean", "enum": [True, False]}, + "status_code": {"type": "integer", "enum": [200, 404, 500]}, + "tag": {"type": "string", "enum": ["a", "b"]}, + }, + } + cleaned_params = _clean_schema_for_parameters_json_schema(schema_params) + assert "enum" not in cleaned_params["properties"]["is_active"] + assert cleaned_params["properties"]["status_code"]["enum"] == ["200", "404", "500"] + assert cleaned_params["properties"]["tag"]["enum"] == ["a", "b"] + + schema_gemini = { + "type": "object", + "properties": { + "flag": {"type": "boolean", "enum": [True, False]}, + "code": {"type": "integer", "enum": [1, 2]}, + }, + } + cleaned_gemini = _clean_schema_for_gemini(schema_gemini) + assert "enum" not in cleaned_gemini["properties"]["flag"] + assert cleaned_gemini["properties"]["code"]["enum"] == ["1", "2"]