This commit is contained in:
SengokuCola
2026-05-05 17:57:23 +08:00
4 changed files with 58 additions and 0 deletions

View File

@@ -0,0 +1,11 @@
from src.config.model_configs import ModelInfo
def test_model_identifier_strips_surrounding_whitespace() -> None:
model_info = ModelInfo(
api_provider="test-provider",
model_identifier=" glm-5.1 ",
name="test-model",
)
assert model_info.model_identifier == "glm-5.1"

View File

@@ -1,16 +1,59 @@
from types import SimpleNamespace
import pytest
from src.config.model_configs import APIProvider, ReasoningParseMode, ToolArgumentParseMode
from src.llm_models.model_client.openai_client import (
_OpenAIStreamAccumulator,
_build_reasoning_key,
_default_normal_response_parser,
_parse_tool_arguments,
_sanitize_messages_for_toolless_request,
)
from src.llm_models.payload_content.message import Message, RoleType, TextMessagePart
from src.llm_models.payload_content.tool_option import ToolCall
@pytest.mark.parametrize("parse_mode", list(ToolArgumentParseMode))
def test_parse_tool_arguments_treats_blank_arguments_as_empty_dict(parse_mode: ToolArgumentParseMode) -> None:
assert _parse_tool_arguments("", parse_mode, None) == {}
assert _parse_tool_arguments(" ", parse_mode, None) == {}
def test_normal_response_parser_accepts_empty_string_arguments_for_parameterless_tool() -> None:
response = SimpleNamespace(
choices=[
SimpleNamespace(
finish_reason="tool_calls",
message=SimpleNamespace(
content=None,
tool_calls=[
SimpleNamespace(
id="finish-call",
type="function",
function=SimpleNamespace(name="finish", arguments=""),
)
],
),
)
],
usage=None,
model="glm-5.1",
)
api_response, usage_record = _default_normal_response_parser(
response,
reasoning_parse_mode=ReasoningParseMode.AUTO,
tool_argument_parse_mode=ToolArgumentParseMode.AUTO,
reasoning_key=None,
)
assert len(api_response.tool_calls) == 1
assert api_response.tool_calls[0].func_name == "finish"
assert api_response.tool_calls[0].args == {}
assert usage_record is None
def test_sanitize_messages_for_toolless_request_drops_assistant_tool_call_without_parts() -> None:
messages = [
Message(

View File

@@ -351,6 +351,7 @@ class ModelInfo(ConfigBase):
Gemini 客户端会按自身支持的字段筛选并映射到 GenerateContentConfig、EmbedContentConfig 或音频请求配置中。"""
def model_post_init(self, context: Any = None):
self.model_identifier = self.model_identifier.strip()
if not self.model_identifier:
raise ValueError(t("config.model_identifier_empty_generic"))
if not self.name:

View File

@@ -585,6 +585,9 @@ def _parse_tool_arguments(
Raises:
RespParseException: 当参数无法解析为字典时抛出。
"""
if not raw_arguments.strip():
return {}
try:
if parse_mode == ToolArgumentParseMode.STRICT:
arguments: Any = json.loads(raw_arguments)