Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion ollama/_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -1320,8 +1320,9 @@ def _copy_images(images: Optional[Sequence[Union[Image, Any]]]) -> Iterator[Imag

def _copy_messages(messages: Optional[Sequence[Union[Mapping[str, Any], Message]]]) -> Iterator[Message]:
for message in messages or []:
# Keep empty strings (e.g. tool results with content='') — only drop None.
yield Message.model_validate(
{k: list(_copy_images(v)) if k == 'images' else v for k, v in dict(message).items() if v},
{k: list(_copy_images(v)) if k == 'images' else v for k, v in dict(message).items() if v is not None},
)


Expand Down
16 changes: 16 additions & 0 deletions tests/test_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -1379,6 +1379,22 @@ async def test_arbitrary_roles_accepted_in_message_request_async(monkeypatch: py
await client.chat(model='llama3.1', messages=[{'role': 'somerandomrole', 'content': "I'm ok with you adding any role message now!"}, {'role': 'user', 'content': 'Hello world!'}])


def test_copy_messages_preserves_empty_string_content():
from ollama._client import _copy_messages

msgs = list(
_copy_messages(
[
{'role': 'assistant', 'content': ''},
{'role': 'tool', 'content': '', 'tool_name': 'web_search'},
]
)
)
assert msgs[0].content == ''
assert msgs[1].content == ''
assert msgs[1].tool_name == 'web_search'


def test_client_web_search_requires_bearer_auth_header(monkeypatch: pytest.MonkeyPatch):
monkeypatch.delenv('OLLAMA_API_KEY', raising=False)

Expand Down