Repository: agentscope-ai/CoPaw (python)

Issue:
[Bug]: AGENT_UNKNOWN_ERROR - 'NoneType' object has no attribute 'arguments'

## Description

Installed copaw via pipx, init and add custom LLMs provider (OpenAI-compatible), said hi to console and it happened.

## Environment

- **CoPaw version:** [e.g. 0.x.x or git commit]
- **OS:** [e.g. macOS 14, Ubuntu 22.04, Windows 11]
- **Install method:** [pip / one-line install / Docker / from source]
- **Python version (if applicable):** [e.g. 3.10]

## Logs / Screenshots

The console:

<img width="3807" height="1901" alt="Image" src="https://github.com/user-attachments/assets/630f5111-9503-4197-95fd-0ece922b9252" />

The log from running copaw:

```
INFO:     127.0.0.1:49814 - "POST /api/agent/process HTTP/1.1" 200 OK
INFO .local/share/pipx/venvs/copaw/lib/python3.12/site-packages/copaw/app/runner/runner.py:68 | 2026-03-03 10:19:04 | Handle agent query:
{
  "session_id": "1772531267498",
  "user_id": "default",
  "channel": "console",
  "msgs_len": 1,
  "msgs_str": "[Msg(id='msg_d44c6af8-e2fb-44e8-b053-714cddf09d72', name='user', content=[{'type': 'text', 'text': 'hi'}], role='user', metadata=None, timestamp='2026-03-03 10:19:04.367', invocation_id='None')]..."
}
INFO .local/share/pipx/venvs/copaw/lib/python3.12/site-packages/copaw/agents/utils/token_counting.py:41 | 2026-03-03 10:19:04 | Using local Qwen tokenizer from /root/.local/share/pipx/venvs/copaw/lib/python3.12/site-packages/copaw/tokenizer
PyTorch was not found. Models won't be available and only tokenizers, configuration and file/data utilities can be used.
/root/.local/share/pipx/venvs/copaw/lib/python3.12/site-packages/copaw/app/runner/query_error_dump.py:83: DeprecationWarning: datetime.datetime.utcnow() is deprecated and scheduled for removal in a future version. Use timezone-aware objects to represent datetimes in UTC: datetime.datetime.now(datetime.UTC).
  "ts_utc": datetime.utcnow().isoformat() + "Z",
ERROR .local/share/pipx/venvs/copaw/lib/python3.12/site-packages/copaw/app/runner/runner.py:159 | 2026-03-03 10:19:13 | Error in query handler: 'NoneType' object has no attribute 'arguments'
(Details:  /tmp/copaw_query_error_d1n92v_p.json)
Traceback (most recent call last):
  File "/root/.local/share/pipx/venvs/copaw/lib/python3.12/site-packages/copaw/app/runner/runner.py", line 140, in query_handler
    async for msg, last in stream_printing_messages(
  File "/root/.local/share/pipx/venvs/copaw/lib/python3.12/site-packages/agentscope/pipeline/_functional.py", line 192, in stream_printing_messages
    raise exception from None
  File "/root/.local/share/pipx/venvs/copaw/lib/python3.12/site-packages/agentscope/agent/_agent_base.py", line 455, in __call__
    reply_msg = await self.reply(*args, **kwargs)
                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/root/.local/share/pipx/venvs/copaw/lib/python3.12/site-packages/agentscope/agent/_agent_meta.py", line 120, in async_wrapper
    current_output = await original_func(
                     ^^^^^^^^^^^^^^^^^^^^
  File "/root/.local/share/pipx/venvs/copaw/lib/python3.12/site-packages/copaw/agents/react_agent.py", line 320, in reply
    return await super().reply(msg=msg, structured_model=structured_model)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/root/.local/share/pipx/venvs/copaw/lib/python3.12/site-packages/agentscope/agent/_agent_meta.py", line 120, in async_wrapper
    current_output = await original_func(
                     ^^^^^^^^^^^^^^^^^^^^
  File "/root/.local/share/pipx/venvs/copaw/lib/python3.12/site-packages/agentscope/tracing/_trace.py", line 392, in wrapper
    return await func(self, *args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/root/.local/share/pipx/venvs/copaw/lib/python3.12/site-packages/agentscope/agent/_react_agent.py", line 437, in reply
    msg_reasoning = await self._reasoning(tool_choice)
                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/root/.local/share/pipx/venvs/copaw/lib/python3.12/site-packages/agentscope/agent/_agent_meta.py", line 120, in async_wrapper
    current_output = await original_func(
                     ^^^^^^^^^^^^^^^^^^^^
  File "/root/.local/share/pipx/venvs/copaw/lib/python3.12/site-packages/copaw/agents/react_agent.py", line 287, in _reasoning
    return await super()._reasoning(tool_choice=tool_choice)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/root/.local/share/pipx/venvs/copaw/lib/python3.12/site-packages/agentscope/agent/_agent_meta.py", line 120, in async_wrapper
    current_output = await original_func(
                     ^^^^^^^^^^^^^^^^^^^^
  File "/root/.local/share/pipx/venvs/copaw/lib/python3.12/site-packages/agentscope/agent/_react_agent.py", line 581, in _reasoning
    async for content_chunk in res:
  File "/root/.local/share/pipx/venvs/copaw/lib/python3.12/site-packages/agentscope/model/_openai_model.py", line 397, in _parse_openai_stream_response
    if tool_call.function.arguments is not None:
       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
AttributeError: 'NoneType' object has no attribute 'arguments'
/root/.local/share/pipx/venvs/copaw/lib/python3.12/site-packages/copaw/app/runner/manager.py:140: DeprecationWarning: datetime.datetime.utcnow() is deprecated and scheduled for removal in a future version. Use timezone-aware objects to represent datetimes in UTC: datetime.datetime.now(datetime.UTC).
  spec.updated_at = datetime.utcnow()
{'code': 'AGENT_UNKNOWN_ERROR', 'message': "Unknown agent error: AttributeError: 'NoneType' object has no attribute 'arguments'\n(Details:  /tmp/copaw_query_error_d1n92v_p.json)"}: Traceback (most recent call last):
  File "/root/.local/share/pipx/venvs/copaw/lib/python3.12/site-packages/agentscope_runtime/engine/runner.py", line 324, in stream_query
    async for event in stream_adapter(
  File "/root/.local/share/pipx/venvs/copaw/lib/python3.12/site-packages/agentscope_runtime/adapters/agentscope/stream.py", line 58, in adapt_agentscope_message_stream
    async for msg, last in source_stream:
  File "/root/.local/share/pipx/venvs/copaw/lib/python3.12/site-packages/agentscope_runtime/engine/runner.py", line 179, in _call_handler_streaming
    async for item in result:
  File "/root/.local/share/pipx/venvs/copaw/lib/python3.12/site-packages/copaw/app/runner/runner.py", line 140, in query_handler
    async for msg, last in stream_printing_messages(
  File "/root/.local/share/pipx/venvs/copaw/lib/python3.12/site-packages/agentscope/pipeline/_functional.py", line 192, in stream_printing_messages
    raise exception from None
  File "/root/.local/share/pipx/venvs/copaw/lib/python3.12/site-packages/agentscope/agent/_agent_base.py", line 455, in __call__
    reply_msg = await self.reply(*args, **kwargs)
                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/root/.local/share/pipx/venvs/copaw/lib/python3.12/site-packages/agentscope/agent/_agent_meta.py", line 120, in async_wrapper
    current_output = await original_func(
                     ^^^^^^^^^^^^^^^^^^^^
  File "/root/.local/share/pipx/venvs/copaw/lib/python3.12/site-packages/copaw/agents/react_agent.py", line 320, in reply
    return await super().reply(msg=msg, structured_model=structured_model)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/root/.local/share/pipx/venvs/copaw/lib/python3.12/site-packages/agentscope/agent/_agent_meta.py", line 120, in async_wrapper
    current_output = await original_func(
                     ^^^^^^^^^^^^^^^^^^^^
  File "/root/.local/share/pipx/venvs/copaw/lib/python3.12/site-packages/agentscope/tracing/_trace.py", line 392, in wrapper
    return await func(self, *args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/root/.local/share/pipx/venvs/copaw/lib/python3.12/site-packages/agentscope/agent/_react_agent.py", line 437, in reply
    msg_reasoning = await self._reasoning(tool_choice)
                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/root/.local/share/pipx/venvs/copaw/lib/python3.12/site-packages/agentscope/agent/_agent_meta.py", line 120, in async_wrapper
    current_output = await original_func(
                     ^^^^^^^^^^^^^^^^^^^^
  File "/root/.local/share/pipx/venvs/copaw/lib/python3.12/site-packages/copaw/agents/react_agent.py", line 287, in _reasoning
    return await super()._reasoning(tool_choice=tool_choice)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/root/.local/share/pipx/venvs/copaw/lib/python3.12/site-packages/agentscope/agent/_agent_meta.py", line 120, in async_wrapper
    current_output = await original_func(
                     ^^^^^^^^^^^^^^^^^^^^
  File "/root/.local/share/pipx/venvs/copaw/lib/python3.12

[copaw_query_error_d1n92v_p.json](https://github.com/user-attachments/files/25712463/copaw_query_error_d1n92v_p.json)

/site-packages/agentscope/agent/_react_agent.py", line 581, in _reasoning
    async for content_chunk in res:
  File "/root/.local/share/pipx/venvs/copaw/lib/python3.12/site-packages/agentscope/model/_openai_model.py", line 397, in _parse_openai_stream_response
    if tool_call.function.arguments is not None:
       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
AttributeError: 'NoneType' object has no attribute 'arguments'
```

/tmp/copaw_query_error_d1n92v_p.json logs:

[copaw_query_error_d1n92v_p.json](https://github.com/user-attachments/files/25712468/copaw_query_error_d1n92v_p.json)



Patch:
diff --git a/src/copaw/agents/model_factory.py b/src/copaw/agents/model_factory.py
index dad973a98..f68ed59eb 100644
--- a/src/copaw/agents/model_factory.py
+++ b/src/copaw/agents/model_factory.py
@@ -241,9 +241,9 @@ def _get_chat_model_class_from_provider() -> Type[ChatModelBase]:
     """Get the chat model class from provider configuration.
 
     Returns:
-        Chat model class, defaults to OpenAIChatModel if not found
+        Chat model class, defaults to OpenAI-compatible chat model if not found
     """
-    chat_model_class = OpenAIChatModel  # default
+    chat_model_class = get_chat_model_class("OpenAIChatModel")
     try:
         providers_data = load_providers_json()
         provider_id = providers_data.active_llm.provider_id
@@ -256,7 +256,7 @@ def _get_chat_model_class_from_provider() -> Type[ChatModelBase]:
     except Exception as e:
         logger.debug(
             "Failed to determine chat model from provider: %s, "
-            "using OpenAIChatModel",
+            "using OpenAI-compatible default chat model",
             e,
         )
     return chat_model_class
diff --git a/src/copaw/providers/openai_chat_model_compat.py b/src/copaw/providers/openai_chat_model_compat.py
new file mode 100644
index 000000000..a1396f3fa
--- /dev/null
+++ b/src/copaw/providers/openai_chat_model_compat.py
@@ -0,0 +1,177 @@
+# -*- coding: utf-8 -*-
+"""OpenAI chat model compatibility wrappers."""
+
+from __future__ import annotations
+
+import json
+from datetime import datetime
+from types import SimpleNamespace
+from typing import Any, AsyncGenerator, Type
+
+from agentscope.model import OpenAIChatModel
+from agentscope.model._model_response import ChatResponse
+from pydantic import BaseModel
+
+
+def _clone_with_overrides(obj: Any, **overrides: Any) -> Any:
+    """Clone a stream object into a mutable namespace with overrides."""
+    data = dict(getattr(obj, "__dict__", {}))
+    data.update(overrides)
+    return SimpleNamespace(**data)
+
+
+def _sanitize_tool_call(tool_call: Any) -> Any | None:
+    """Normalize a tool call for parser safety, or drop it if unusable."""
+    if not hasattr(tool_call, "index"):
+        return None
+
+    function = getattr(tool_call, "function", None)
+    if function is None:
+        return None
+
+    has_name = hasattr(function, "name")
+    has_arguments = hasattr(function, "arguments")
+
+    raw_name = getattr(function, "name", "")
+    if isinstance(raw_name, str):
+        safe_name = raw_name
+    elif raw_name is None:
+        safe_name = ""
+    else:
+        safe_name = str(raw_name)
+
+    raw_arguments = getattr(function, "arguments", "")
+    if isinstance(raw_arguments, str):
+        safe_arguments = raw_arguments
+    elif raw_arguments is None:
+        safe_arguments = ""
+    else:
+        try:
+            safe_arguments = json.dumps(raw_arguments, ensure_ascii=False)
+        except (TypeError, ValueError):
+            safe_arguments = str(raw_arguments)
+
+    if (
+        has_name
+        and has_arguments
+        and isinstance(raw_name, str)
+        and isinstance(
+            raw_arguments,
+            str,
+        )
+    ):
+        return tool_call
+
+    safe_function = SimpleNamespace(
+        name=safe_name,
+        arguments=safe_arguments,
+    )
+    return _clone_with_overrides(tool_call, function=safe_function)
+
+
+def _sanitize_chunk(chunk: Any) -> Any:
+    """Drop/normalize malformed tool-calls in a streaming chunk."""
+    choices = getattr(chunk, "choices", None)
+    if not choices:
+        return chunk
+
+    sanitized_choices: list[Any] = []
+    changed = False
+
+    for choice in choices:
+        delta = getattr(choice, "delta", None)
+        if delta is None:
+            sanitized_choices.append(choice)
+            continue
+
+        raw_tool_calls = getattr(delta, "tool_calls", None)
+        if not raw_tool_calls:
+            sanitized_choices.append(choice)
+            continue
+
+        choice_changed = False
+        sanitized_tool_calls: list[Any] = []
+        for tool_call in raw_tool_calls:
+            sanitized = _sanitize_tool_call(tool_call)
+            if sanitized is not tool_call:
+                choice_changed = True
+            if sanitized is not None:
+                sanitized_tool_calls.append(sanitized)
+
+        if choice_changed:
+            changed = True
+            sanitized_delta = _clone_with_overrides(
+                delta,
+                tool_calls=sanitized_tool_calls,
+            )
+            sanitized_choice = _clone_with_overrides(
+                choice,
+                delta=sanitized_delta,
+            )
+            sanitized_choices.append(sanitized_choice)
+            continue
+
+        sanitized_choices.append(choice)
+
+    if not changed:
+        return chunk
+    return _clone_with_overrides(chunk, choices=sanitized_choices)
+
+
+def _sanitize_stream_item(item: Any) -> Any:
+    """Sanitize either plain stream chunks or structured stream items."""
+    if hasattr(item, "chunk"):
+        chunk = item.chunk
+        sanitized_chunk = _sanitize_chunk(chunk)
+        if sanitized_chunk is chunk:
+            return item
+        return _clone_with_overrides(item, chunk=sanitized_chunk)
+
+    return _sanitize_chunk(item)
+
+
+class _SanitizedStream:
+    """Proxy OpenAI async stream that sanitizes each emitted item."""
+
+    def __init__(self, stream: Any):
+        self._stream = stream
+        self._ctx_stream: Any | None = None
+
+    async def __aenter__(self) -> "_SanitizedStream":
+        self._ctx_stream = await self._stream.__aenter__()
+        return self
+
+    async def __aexit__(
+        self,
+        exc_type: Any,
+        exc: Any,
+        tb: Any,
+    ) -> bool | None:
+        return await self._stream.__aexit__(exc_type, exc, tb)
+
+    def __aiter__(self) -> "_SanitizedStream":
+        return self
+
+    async def __anext__(self) -> Any:
+        if self._ctx_stream is None:
+            raise StopAsyncIteration
+        item = await self._ctx_stream.__anext__()
+        return _sanitize_stream_item(item)
+
+
+class OpenAIChatModelCompat(OpenAIChatModel):
+    """OpenAIChatModel with robust parsing for malformed tool-call chunks."""
+
+    async def _parse_openai_stream_response(
+        self,
+        start_datetime: datetime,
+        response: Any,
+        structured_model: Type[BaseModel] | None = None,
+    ) -> AsyncGenerator[ChatResponse, None]:
+        sanitized_response = _SanitizedStream(response)
+        async for parsed in super()._parse_openai_stream_response(
+            start_datetime=start_datetime,
+            response=sanitized_response,
+            structured_model=structured_model,
+        ):
+            yield parsed
diff --git a/src/copaw/providers/registry.py b/src/copaw/providers/registry.py
index f1fdf35ef..64032a936 100644
--- a/src/copaw/providers/registry.py
+++ b/src/copaw/providers/registry.py
@@ -6,9 +6,10 @@
 import re
 from typing import TYPE_CHECKING, List, Optional, Type
 
-from agentscope.model import ChatModelBase, OpenAIChatModel
+from agentscope.model import ChatModelBase
 
 from .models import CustomProviderData, ModelInfo, ProviderDefinition
+from .openai_chat_model_compat import OpenAIChatModelCompat
 
 if TYPE_CHECKING:
     from .models import ProvidersData
@@ -305,7 +306,7 @@ def sync_ollama_models() -> None:
 
 
 _CHAT_MODEL_MAP: dict[str, Type[ChatModelBase]] = {
-    "OpenAIChatModel": OpenAIChatModel,
+    "OpenAIChatModel": OpenAIChatModelCompat,
 }
 
 
@@ -316,6 +317,6 @@ def get_chat_model_class(chat_model_name: str) -> Type[ChatModelBase]:
         chat_model_name: Name of the chat model class (e.g., "OpenAIChatModel")
 
     Returns:
-        Chat model class, defaults to OpenAIChatModel if not found.
+        Chat model class, defaults to OpenAIChatModel-compatible parser.
     """
-    return _CHAT_MODEL_MAP.get(chat_model_name, OpenAIChatModel)
+    return _CHAT_MODEL_MAP.get(chat_model_name, OpenAIChatModelCompat)


Test patch:
diff --git a/tests/test_openai_stream_toolcall_compat.py b/tests/test_openai_stream_toolcall_compat.py
new file mode 100644
index 000000000..1c2c40e3c
--- /dev/null
+++ b/tests/test_openai_stream_toolcall_compat.py
@@ -0,0 +1,176 @@
+# -*- coding: utf-8 -*-
+from __future__ import annotations
+
+import json
+from datetime import datetime
+from types import SimpleNamespace
+from typing import Any
+
+from copaw.providers.openai_chat_model_compat import (
+    OpenAIChatModelCompat,
+    _sanitize_tool_call,
+)
+from copaw.providers.registry import get_chat_model_class
+
+
+class CompatHarnessOpenAIChatModel(OpenAIChatModelCompat):
+    async def parse_stream_for_test(
+        self,
+        start_datetime: datetime,
+        stream: Any,
+    ) -> list[Any]:
+        responses = []
+        async for response in self._parse_openai_stream_response(
+            start_datetime,
+            stream,
+        ):
+            responses.append(response)
+        return responses
+
+
+class FakeAsyncStream:
+    def __init__(self, items: list[Any]):
+        self._items = items
+        self._iter = None
+
+    async def __aenter__(self) -> "FakeAsyncStream":
+        self._iter = iter(self._items)
+        return self
+
+    async def __aexit__(self, exc_type, exc, tb) -> bool:
+        return False
+
+    def __aiter__(self) -> "FakeAsyncStream":
+        return self
+
+    async def __anext__(self) -> Any:
+        assert self._iter is not None
+        try:
+            return next(self._iter)
+        except StopIteration as exc:
+            raise StopAsyncIteration from exc
+
+
+def _make_chunk(tool_calls: list[Any]) -> Any:
+    delta = SimpleNamespace(
+        reasoning_content=None,
+        content=None,
+        tool_calls=tool_calls,
+    )
+    choice = SimpleNamespace(delta=delta)
+    return SimpleNamespace(usage=None, choices=[choice])
+
+
+async def test_registry_maps_openai_model_to_compat() -> None:
+    assert get_chat_model_class("OpenAIChatModel") is OpenAIChatModelCompat
+
+
+async def test_stream_parser_skips_tool_call_without_function() -> None:
+    model = CompatHarnessOpenAIChatModel(
+        "dummy",
+        api_key="sk-test",
+        stream=True,
+    )
+
+    malformed_tool_call = SimpleNamespace(
+        index=0,
+        id="call_bad",
+        function=None,
+    )
+    none_arguments_tool_call = SimpleNamespace(
+        index=1,
+        id="call_partial",
+        function=SimpleNamespace(name="ping", arguments=None),
+    )
+    valid_tool_call = SimpleNamespace(
+        index=0,
+        id="call_ok",
+        function=SimpleNamespace(name="ping", arguments='{"x":1}'),
+    )
+
+    stream = FakeAsyncStream(
+        [
+            _make_chunk([malformed_tool_call]),
+            _make_chunk([none_arguments_tool_call]),
+            _make_chunk([valid_tool_call]),
+        ],
+    )
+
+    responses = await model.parse_stream_for_test(
+        datetime.now(),
+        stream,
+    )
+
+    assert responses
+    tool_blocks = [
+        block
+        for response in responses
+        for block in response.content
+        if block.get("type") == "tool_use"
+    ]
+    assert tool_blocks
+    assert tool_blocks[-1]["name"] == "ping"
+    assert tool_blocks[-1]["input"] == {"x": 1}
+
+
+def test_sanitize_tool_call_normalizes_non_string_arguments() -> None:
+    none_arguments_tool_call = SimpleNamespace(
+        index=0,
+        id="call_partial",
+        function=SimpleNamespace(name="ping", arguments=None),
+    )
+    non_string_arguments_tool_call = SimpleNamespace(
+        index=1,
+        id="call_dict",
+        function=SimpleNamespace(name="ping", arguments={"x": 2}),
+    )
+    missing_arguments_tool_call = SimpleNamespace(
+        index=2,
+        id="call_missing_args",
+        function=SimpleNamespace(name="ping"),
+    )
+    missing_name_tool_call = SimpleNamespace(
+        index=3,
+        id="call_missing_name",
+        function=SimpleNamespace(arguments={"x": 3}),
+    )
+    missing_name_and_arguments_tool_call = SimpleNamespace(
+        index=4,
+        id="call_missing_both",
+        function=SimpleNamespace(),
+    )
+
+    sanitized_none_arguments = _sanitize_tool_call(none_arguments_tool_call)
+    assert sanitized_none_arguments is not None
+    assert sanitized_none_arguments.function.name == "ping"
+    assert sanitized_none_arguments.function.arguments == ""
+
+    sanitized_non_string_arguments = _sanitize_tool_call(
+        non_string_arguments_tool_call,
+    )
+    assert sanitized_non_string_arguments is not None
+    assert sanitized_non_string_arguments.function.name == "ping"
+    assert isinstance(sanitized_non_string_arguments.function.arguments, str)
+    assert json.loads(sanitized_non_string_arguments.function.arguments) == {
+        "x": 2,
+    }
+
+    sanitized_missing_arguments = _sanitize_tool_call(
+        missing_arguments_tool_call,
+    )
+    assert sanitized_missing_arguments is not None
+    assert sanitized_missing_arguments.function.name == "ping"
+    assert sanitized_missing_arguments.function.arguments == ""
+
+    sanitized_missing_name = _sanitize_tool_call(missing_name_tool_call)
+    assert sanitized_missing_name is not None
+    assert sanitized_missing_name.function.name == ""
+    assert isinstance(sanitized_missing_name.function.arguments, str)
+    assert json.loads(sanitized_missing_name.function.arguments) == {"x": 3}
+
+    sanitized_missing_name_and_arguments = _sanitize_tool_call(
+        missing_name_and_arguments_tool_call,
+    )
+    assert sanitized_missing_name_and_arguments is not None
+    assert sanitized_missing_name_and_arguments.function.name == ""
+    assert sanitized_missing_name_and_arguments.function.arguments == ""