From efbe7aa9d1c53dcf8254190367ecea3b15b7f298 Mon Sep 17 00:00:00 2001 From: ParthSareen Date: Sun, 29 Dec 2024 14:50:32 -0800 Subject: [PATCH 1/5] Improve error messaging on connection failure --- ollama/_client.py | 12 ++++++++---- ollama/_types.py | 3 +++ 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/ollama/_client.py b/ollama/_client.py index c0fccd47..d496fc69 100644 --- a/ollama/_client.py +++ b/ollama/_client.py @@ -111,12 +111,14 @@ def __init__(self, host: Optional[str] = None, **kwargs) -> None: super().__init__(httpx.Client, host, **kwargs) def _request_raw(self, *args, **kwargs): - r = self._client.request(*args, **kwargs) try: + r = self._client.request(*args, **kwargs) r.raise_for_status() + return r except httpx.HTTPStatusError as e: raise ResponseError(e.response.text, e.response.status_code) from None - return r + except httpx.ConnectError: + raise ResponseError('Failed to connect to Ollama. Please check that Ollama is downloaded, running and accessible. https://ollama.com/download', 503) from None @overload def _request( @@ -613,12 +615,14 @@ def __init__(self, host: Optional[str] = None, **kwargs) -> None: super().__init__(httpx.AsyncClient, host, **kwargs) async def _request_raw(self, *args, **kwargs): - r = await self._client.request(*args, **kwargs) try: + r = await self._client.request(*args, **kwargs) r.raise_for_status() + return r except httpx.HTTPStatusError as e: raise ResponseError(e.response.text, e.response.status_code) from None - return r + except httpx.ConnectError: + raise ResponseError('Failed to connect to Ollama. Please check that Ollama is downloaded, running and accessible. https://ollama.com/download', 503) from None @overload async def _request( diff --git a/ollama/_types.py b/ollama/_types.py index d70a4ac1..710c536e 100644 --- a/ollama/_types.py +++ b/ollama/_types.py @@ -535,3 +535,6 @@ def __init__(self, error: str, status_code: int = -1): self.status_code = status_code 'HTTP status code of the response.' + + def __str__(self) -> str: + return f'{self.error} (status code: {self.status_code})' From eb551ceeee33e1b36eb0fc7651ed6c54fdb45fd1 Mon Sep 17 00:00:00 2001 From: ParthSareen Date: Sun, 29 Dec 2024 15:14:13 -0800 Subject: [PATCH 2/5] Throw connection error instead --- ollama/_client.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ollama/_client.py b/ollama/_client.py index d496fc69..2ba7148e 100644 --- a/ollama/_client.py +++ b/ollama/_client.py @@ -118,7 +118,7 @@ def _request_raw(self, *args, **kwargs): except httpx.HTTPStatusError as e: raise ResponseError(e.response.text, e.response.status_code) from None except httpx.ConnectError: - raise ResponseError('Failed to connect to Ollama. Please check that Ollama is downloaded, running and accessible. https://ollama.com/download', 503) from None + raise ConnectionError('Failed to connect to Ollama. Please check that Ollama is downloaded, running and accessible. https://ollama.com/download') from None @overload def _request( @@ -622,7 +622,7 @@ async def _request_raw(self, *args, **kwargs): except httpx.HTTPStatusError as e: raise ResponseError(e.response.text, e.response.status_code) from None except httpx.ConnectError: - raise ResponseError('Failed to connect to Ollama. Please check that Ollama is downloaded, running and accessible. https://ollama.com/download', 503) from None + raise ConnectionError('Failed to connect to Ollama. Please check that Ollama is downloaded, running and accessible. https://ollama.com/download') from None @overload async def _request( From d5847be61de2232345b9f1586f248c0c71f52a16 Mon Sep 17 00:00:00 2001 From: ParthSareen Date: Mon, 6 Jan 2025 11:33:51 -0800 Subject: [PATCH 3/5] Add tests --- tests/test_client.py | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/tests/test_client.py b/tests/test_client.py index 67e87b94..bea5ce32 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -1112,3 +1112,30 @@ def test_tool_validation(): with pytest.raises(ValidationError): invalid_tool = {'type': 'invalid_type', 'function': {'name': 'test'}} list(_copy_tools([invalid_tool])) + + +def test_client_connection_error(): + client = Client('http://localhost:1234') + with pytest.raises(ConnectionError) as exc_info: + client.chat('model', messages=[{'role': 'user', 'content': 'prompt'}]) + assert str(exc_info.value) == 'Failed to connect to Ollama. Please check that Ollama is downloaded, running and accessible. https://ollama.com/download' + with pytest.raises(ConnectionError) as exc_info: + client.generate('model', 'prompt') + assert str(exc_info.value) == 'Failed to connect to Ollama. Please check that Ollama is downloaded, running and accessible. https://ollama.com/download' + with pytest.raises(ConnectionError) as exc_info: + client.show('model') + assert str(exc_info.value) == 'Failed to connect to Ollama. Please check that Ollama is downloaded, running and accessible. https://ollama.com/download' + + +@pytest.mark.asyncio +async def test_async_client_connection_error(): + client = AsyncClient('http://localhost:1234') + with pytest.raises(ConnectionError) as exc_info: + await client.chat('model', messages=[{'role': 'user', 'content': 'prompt'}]) + assert str(exc_info.value) == 'Failed to connect to Ollama. Please check that Ollama is downloaded, running and accessible. https://ollama.com/download' + with pytest.raises(ConnectionError) as exc_info: + await client.generate('model', 'prompt') + assert str(exc_info.value) == 'Failed to connect to Ollama. Please check that Ollama is downloaded, running and accessible. https://ollama.com/download' + with pytest.raises(ConnectionError) as exc_info: + await client.show('model') + assert str(exc_info.value) == 'Failed to connect to Ollama. Please check that Ollama is downloaded, running and accessible. https://ollama.com/download' From 0c343dd14a26a0647fb9d1ba0c559975ed9fa9a7 Mon Sep 17 00:00:00 2001 From: ParthSareen Date: Thu, 16 Jan 2025 13:38:36 -0800 Subject: [PATCH 4/5] Address comments --- ollama/_client.py | 7 +++++-- tests/test_client.py | 14 +++++++------- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/ollama/_client.py b/ollama/_client.py index 2ba7148e..cbe43c94 100644 --- a/ollama/_client.py +++ b/ollama/_client.py @@ -106,6 +106,9 @@ def __init__( ) +CONNECTION_ERROR_MESSAGE = 'Failed to connect to Ollama. Please check that Ollama is downloaded, running and accessible. https://ollama.com/download' + + class Client(BaseClient): def __init__(self, host: Optional[str] = None, **kwargs) -> None: super().__init__(httpx.Client, host, **kwargs) @@ -118,7 +121,7 @@ def _request_raw(self, *args, **kwargs): except httpx.HTTPStatusError as e: raise ResponseError(e.response.text, e.response.status_code) from None except httpx.ConnectError: - raise ConnectionError('Failed to connect to Ollama. Please check that Ollama is downloaded, running and accessible. https://ollama.com/download') from None + raise ConnectionError(CONNECTION_ERROR_MESSAGE) from None @overload def _request( @@ -622,7 +625,7 @@ async def _request_raw(self, *args, **kwargs): except httpx.HTTPStatusError as e: raise ResponseError(e.response.text, e.response.status_code) from None except httpx.ConnectError: - raise ConnectionError('Failed to connect to Ollama. Please check that Ollama is downloaded, running and accessible. https://ollama.com/download') from None + raise ConnectionError(CONNECTION_ERROR_MESSAGE) from None @overload async def _request( diff --git a/tests/test_client.py b/tests/test_client.py index bea5ce32..e74c936d 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -9,7 +9,7 @@ from pytest_httpserver import HTTPServer, URIPattern from werkzeug.wrappers import Request, Response -from ollama._client import AsyncClient, Client, _copy_tools +from ollama._client import CONNECTION_ERROR_MESSAGE, AsyncClient, Client, _copy_tools PNG_BASE64 = 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAADElEQVR4nGNgYGAAAAAEAAH2FzhVAAAAAElFTkSuQmCC' PNG_BYTES = base64.b64decode(PNG_BASE64) @@ -1116,15 +1116,15 @@ def test_tool_validation(): def test_client_connection_error(): client = Client('http://localhost:1234') - with pytest.raises(ConnectionError) as exc_info: + + with pytest.raises(ConnectionError, match=CONNECTION_ERROR_MESSAGE): client.chat('model', messages=[{'role': 'user', 'content': 'prompt'}]) - assert str(exc_info.value) == 'Failed to connect to Ollama. Please check that Ollama is downloaded, running and accessible. https://ollama.com/download' - with pytest.raises(ConnectionError) as exc_info: + with pytest.raises(ConnectionError, match=CONNECTION_ERROR_MESSAGE): + client.chat('model', messages=[{'role': 'user', 'content': 'prompt'}]) + with pytest.raises(ConnectionError, match=CONNECTION_ERROR_MESSAGE): client.generate('model', 'prompt') - assert str(exc_info.value) == 'Failed to connect to Ollama. Please check that Ollama is downloaded, running and accessible. https://ollama.com/download' - with pytest.raises(ConnectionError) as exc_info: + with pytest.raises(ConnectionError, match=CONNECTION_ERROR_MESSAGE): client.show('model') - assert str(exc_info.value) == 'Failed to connect to Ollama. Please check that Ollama is downloaded, running and accessible. https://ollama.com/download' @pytest.mark.asyncio From b03c3f762b90f7a86c723d1de3a9e8bd2de005f0 Mon Sep 17 00:00:00 2001 From: ParthSareen Date: Thu, 16 Jan 2025 13:53:52 -0800 Subject: [PATCH 5/5] Format create example --- examples/create.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/create.py b/examples/create.py index dfc9094f..14967a98 100755 --- a/examples/create.py +++ b/examples/create.py @@ -2,9 +2,9 @@ client = Client() response = client.create( - model='my-assistant', - from_='llama3.2', - system="You are mario from Super Mario Bros.", - stream=False + model='my-assistant', + from_='llama3.2', + system='You are mario from Super Mario Bros.', + stream=False, ) print(response.status)