diff --git a/app/service/chat/gemini_chat_service.py b/app/service/chat/gemini_chat_service.py index 0845b57..c8276ea 100644 --- a/app/service/chat/gemini_chat_service.py +++ b/app/service/chat/gemini_chat_service.py @@ -119,6 +119,14 @@ def _build_tools(model: str, payload: Dict[str, Any]) -> List[Dict[str, Any]]: record[k] = v return record + def _is_structured_output_request(payload: Dict[str, Any]) -> bool: + """检查请求是否要求结构化JSON输出""" + try: + generation_config = payload.get("generationConfig", {}) + return generation_config.get("responseMimeType") == "application/json" + except (AttributeError, TypeError): + return False + tool = dict() if payload and isinstance(payload, dict) and "tools" in payload: if payload.get("tools") and isinstance(payload.get("tools"), dict): @@ -127,19 +135,25 @@ def _build_tools(model: str, payload: Dict[str, Any]) -> List[Dict[str, Any]]: if items and isinstance(items, list): tool.update(_merge_tools(items)) - if ( - settings.TOOLS_CODE_EXECUTION_ENABLED - and not (model.endswith("-search") or "-thinking" in model) - and not _has_image_parts(payload.get("contents", [])) - ): - tool["codeExecution"] = {} - if model.endswith("-search"): - tool["googleSearch"] = {} - - real_model = _get_real_model(model) - if real_model in settings.URL_CONTEXT_MODELS and settings.URL_CONTEXT_ENABLED: - tool["urlContext"] = {} - + # "Tool use with a response mime type: 'application/json' is unsupported" + # Gemini API限制:不支持同时使用tools和结构化输出(response_mime_type='application/json') + # 当请求指定了JSON响应格式时,跳过所有工具的添加以避免API错误 + has_structured_output = _is_structured_output_request(payload) + if not has_structured_output: + if ( + settings.TOOLS_CODE_EXECUTION_ENABLED + and not (model.endswith("-search") or "-thinking" in model) + and not _has_image_parts(payload.get("contents", [])) + ): + tool["codeExecution"] = {} + + if model.endswith("-search"): + tool["googleSearch"] = {} + + real_model = _get_real_model(model) + if real_model in settings.URL_CONTEXT_MODELS and settings.URL_CONTEXT_ENABLED: + tool["urlContext"] = {} + # 解决 "Tool use with function calling is unsupported" 问题 if tool.get("functionDeclarations") or _has_function_call(payload.get("contents", [])): tool.pop("googleSearch", None) diff --git a/app/service/chat/vertex_express_chat_service.py b/app/service/chat/vertex_express_chat_service.py index 6885ac0..e8371a6 100644 --- a/app/service/chat/vertex_express_chat_service.py +++ b/app/service/chat/vertex_express_chat_service.py @@ -97,6 +97,14 @@ def _build_tools(model: str, payload: Dict[str, Any]) -> List[Dict[str, Any]]: record[k] = v return record + def _is_structured_output_request(payload: Dict[str, Any]) -> bool: + """检查请求是否要求结构化JSON输出""" + try: + generation_config = payload.get("generationConfig", {}) + return generation_config.get("responseMimeType") == "application/json" + except (AttributeError, TypeError): + return False + tool = dict() if payload and isinstance(payload, dict) and "tools" in payload: if payload.get("tools") and isinstance(payload.get("tools"), dict): @@ -105,18 +113,24 @@ def _build_tools(model: str, payload: Dict[str, Any]) -> List[Dict[str, Any]]: if items and isinstance(items, list): tool.update(_merge_tools(items)) - if ( - settings.TOOLS_CODE_EXECUTION_ENABLED - and not (model.endswith("-search") or "-thinking" in model) - and not _has_image_parts(payload.get("contents", [])) - ): - tool["codeExecution"] = {} - if model.endswith("-search"): - tool["googleSearch"] = {} - - real_model = _get_real_model(model) - if real_model in settings.URL_CONTEXT_MODELS and settings.URL_CONTEXT_ENABLED: - tool["urlContext"] = {} + # "Tool use with a response mime type: 'application/json' is unsupported" + # Gemini API限制:不支持同时使用tools和结构化输出(response_mime_type='application/json') + # 当请求指定了JSON响应格式时,跳过所有工具的添加以避免API错误 + has_structured_output = _is_structured_output_request(payload) + if not has_structured_output: + if ( + settings.TOOLS_CODE_EXECUTION_ENABLED + and not (model.endswith("-search") or "-thinking" in model) + and not _has_image_parts(payload.get("contents", [])) + ): + tool["codeExecution"] = {} + + if model.endswith("-search"): + tool["googleSearch"] = {} + + real_model = _get_real_model(model) + if real_model in settings.URL_CONTEXT_MODELS and settings.URL_CONTEXT_ENABLED: + tool["urlContext"] = {} # 解决 "Tool use with function calling is unsupported" 问题 if tool.get("functionDeclarations") or _has_function_call(payload.get("contents", [])):