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
28 changes: 28 additions & 0 deletions ollama/_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -664,6 +664,20 @@ def show(self, model: str) -> ShowResponse:
).model_dump(exclude_none=True),
)

def exists(self, model: str) -> bool:
"""
Returns True if the model exists, False if it does not.

Raises `ResponseError` for errors other than 404.
"""
try:
self.show(model)
return True
except ResponseError as e:
if e.status_code == 404:
return False
raise

def ps(self) -> ProcessResponse:
return self._request(
ProcessResponse,
Expand Down Expand Up @@ -1305,6 +1319,20 @@ async def show(self, model: str) -> ShowResponse:
).model_dump(exclude_none=True),
)

async def exists(self, model: str) -> bool:
"""
Returns True if the model exists, False if it does not.

Raises `ResponseError` for errors other than 404.
"""
try:
await self.show(model)
return True
except ResponseError as e:
if e.status_code == 404:
return False
raise

async def ps(self) -> ProcessResponse:
return await self._request(
ProcessResponse,
Expand Down
102 changes: 101 additions & 1 deletion tests/test_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
from werkzeug.wrappers import Request, Response

from ollama._client import CONNECTION_ERROR_MESSAGE, AsyncClient, Client, _copy_tools
from ollama._types import Image, Message
from ollama._types import Image, Message, ResponseError

PNG_BASE64 = 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAADElEQVR4nGNgYGAAAAAEAAH2FzhVAAAAAElFTkSuQmCC'
PNG_BYTES = base64.b64decode(PNG_BASE64)
Expand Down Expand Up @@ -879,6 +879,55 @@ def test_client_copy(httpserver: HTTPServer):
assert response['status'] == 'success'


def test_client_exists_true(httpserver: HTTPServer):
httpserver.expect_ordered_request(
'/api/show',
method='POST',
json={'model': 'llama3.2'},
).respond_with_json(
{
'modelfile': '# Modelfile',
'template': '{{ .Prompt }}',
}
)

client = Client(httpserver.url_for('/'))
assert client.exists('llama3.2') is True


def test_client_exists_false(httpserver: HTTPServer):
httpserver.expect_ordered_request(
'/api/show',
method='POST',
json={'model': 'missing-model'},
).respond_with_json({'error': 'model not found'}, status=404)

client = Client(httpserver.url_for('/'))
assert client.exists('missing-model') is False


def test_client_exists_raises_on_non_404_error(httpserver: HTTPServer):
httpserver.expect_ordered_request(
'/api/show',
method='POST',
json={'model': 'broken-model'},
).respond_with_json({'error': 'internal server error'}, status=500)

client = Client(httpserver.url_for('/'))
with pytest.raises(ResponseError) as exc_info:
client.exists('broken-model')

assert exc_info.value.status_code == 500
assert exc_info.value.error == 'internal server error'


def test_client_exists_raises_on_connection_error():
client = Client('http://localhost:1234')

with pytest.raises(ConnectionError, match=CONNECTION_ERROR_MESSAGE):
client.exists('model')


async def test_async_client_chat(httpserver: HTTPServer):
httpserver.expect_ordered_request(
'/api/chat',
Expand Down Expand Up @@ -1256,6 +1305,57 @@ async def test_async_client_copy(httpserver: HTTPServer):
assert response['status'] == 'success'


async def test_async_client_exists_true(httpserver: HTTPServer):
httpserver.expect_ordered_request(
'/api/show',
method='POST',
json={'model': 'llama3.2'},
).respond_with_json(
{
'modelfile': '# Modelfile',
'template': '{{ .Prompt }}',
}
)

client = AsyncClient(httpserver.url_for('/'))
assert await client.exists('llama3.2') is True


async def test_async_client_exists_false(httpserver: HTTPServer):
httpserver.expect_ordered_request(
'/api/show',
method='POST',
json={'model': 'missing-model'},
).respond_with_json({'error': 'model not found'}, status=404)

client = AsyncClient(httpserver.url_for('/'))
assert await client.exists('missing-model') is False


async def test_async_client_exists_raises_on_non_404_error(httpserver: HTTPServer):
httpserver.expect_ordered_request(
'/api/show',
method='POST',
json={'model': 'broken-model'},
).respond_with_json({'error': 'internal server error'}, status=500)

client = AsyncClient(httpserver.url_for('/'))
with pytest.raises(ResponseError) as exc_info:
await client.exists('broken-model')

assert exc_info.value.status_code == 500
assert exc_info.value.error == 'internal server error'


async def test_async_client_exists_raises_on_connection_error():
client = AsyncClient('http://localhost:1234')

with pytest.raises(ConnectionError) as exc_info:
await client.exists('model')

assert str(exc_info.value) == CONNECTION_ERROR_MESSAGE


def test_headers():
client = Client()
assert client._client.headers['content-type'] == 'application/json'
Expand Down