feat: Enhance response validation and parsing in tool.py (#23456)

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
This commit is contained in:
Guangdong Liu
2025-08-07 09:04:51 +08:00
committed by GitHub
parent ad61b42494
commit 3ff52f1809

View File

@@ -1,7 +1,8 @@
import json import json
from collections.abc import Generator from collections.abc import Generator
from dataclasses import dataclass
from os import getenv from os import getenv
from typing import Any, Optional from typing import Any, Optional, Union
from urllib.parse import urlencode from urllib.parse import urlencode
import httpx import httpx
@@ -20,6 +21,20 @@ API_TOOL_DEFAULT_TIMEOUT = (
) )
@dataclass
class ParsedResponse:
"""Represents a parsed HTTP response with type information"""
content: Union[str, dict]
is_json: bool
def to_string(self) -> str:
"""Convert response to string format for credential validation"""
if isinstance(self.content, dict):
return json.dumps(self.content, ensure_ascii=False)
return str(self.content)
class ApiTool(Tool): class ApiTool(Tool):
""" """
Api tool Api tool
@@ -58,7 +73,9 @@ class ApiTool(Tool):
response = self.do_http_request(self.api_bundle.server_url, self.api_bundle.method, headers, parameters) response = self.do_http_request(self.api_bundle.server_url, self.api_bundle.method, headers, parameters)
# validate response # validate response
return self.validate_and_parse_response(response) parsed_response = self.validate_and_parse_response(response)
# For credential validation, always return as string
return parsed_response.to_string()
def tool_provider_type(self) -> ToolProviderType: def tool_provider_type(self) -> ToolProviderType:
return ToolProviderType.API return ToolProviderType.API
@@ -112,23 +129,36 @@ class ApiTool(Tool):
return headers return headers
def validate_and_parse_response(self, response: httpx.Response) -> str: def validate_and_parse_response(self, response: httpx.Response) -> ParsedResponse:
""" """
validate the response validate the response and return parsed content with type information
:return: ParsedResponse with content and is_json flag
""" """
if isinstance(response, httpx.Response): if isinstance(response, httpx.Response):
if response.status_code >= 400: if response.status_code >= 400:
raise ToolInvokeError(f"Request failed with status code {response.status_code} and {response.text}") raise ToolInvokeError(f"Request failed with status code {response.status_code} and {response.text}")
if not response.content: if not response.content:
return "Empty response from the tool, please check your parameters and try again." return ParsedResponse(
"Empty response from the tool, please check your parameters and try again.", False
)
# Check content type
content_type = response.headers.get("content-type", "").lower()
is_json_content_type = "application/json" in content_type
# Try to parse as JSON
try: try:
response = response.json() json_response = response.json()
try: # If content-type indicates JSON, return as JSON object
return json.dumps(response, ensure_ascii=False) if is_json_content_type:
return ParsedResponse(json_response, True)
else:
# If content-type doesn't indicate JSON, treat as text regardless of content
return ParsedResponse(response.text, False)
except Exception: except Exception:
return json.dumps(response) # Not valid JSON, return as text
except Exception: return ParsedResponse(response.text, False)
return response.text
else: else:
raise ValueError(f"Invalid response type {type(response)}") raise ValueError(f"Invalid response type {type(response)}")
@@ -369,7 +399,14 @@ class ApiTool(Tool):
response = self.do_http_request(self.api_bundle.server_url, self.api_bundle.method, headers, tool_parameters) response = self.do_http_request(self.api_bundle.server_url, self.api_bundle.method, headers, tool_parameters)
# validate response # validate response
response = self.validate_and_parse_response(response) parsed_response = self.validate_and_parse_response(response)
# assemble invoke message # assemble invoke message based on response type
yield self.create_text_message(response) if parsed_response.is_json and isinstance(parsed_response.content, dict):
yield self.create_json_message(parsed_response.content)
else:
# Convert to string if needed and create text message
text_response = (
parsed_response.content if isinstance(parsed_response.content, str) else str(parsed_response.content)
)
yield self.create_text_message(text_response)