Skip to content

Commit ed920be

Browse files
committed
use hatch instead of poetry
1 parent 65f94b4 commit ed920be

13 files changed

+425
-838
lines changed

.github/workflows/publish.yaml

+3-6
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,11 @@ jobs:
1414
contents: write
1515
steps:
1616
- uses: actions/checkout@v4
17-
- run: pipx install poetry
1817
- uses: actions/setup-python@v5
18+
- uses: astral-sh/setup-uv@v5
1919
with:
20-
python-version: '3.x'
21-
cache: poetry
22-
- run: |
23-
poetry version ${GITHUB_REF_NAME#v}
24-
poetry build
20+
enable-cache: true
21+
- run: uv build
2522
- uses: pypa/gh-action-pypi-publish@release/v1
2623
- run: gh release upload $GITHUB_REF_NAME dist/*
2724
env:

.github/workflows/test.yaml

+12-22
Original file line numberDiff line numberDiff line change
@@ -8,39 +8,29 @@ on:
88

99
jobs:
1010
test:
11-
strategy:
12-
matrix:
13-
python-version: ['3.8', '3.9', '3.10', '3.11', '3.12', '3.13']
1411
runs-on: ubuntu-latest
1512
steps:
1613
- uses: actions/checkout@v4
17-
- run: pipx install poetry
18-
- uses: actions/setup-python@v5
19-
with:
20-
python-version: ${{ matrix.python-version }}
21-
cache: poetry
22-
- run: poetry install --with=dev
23-
- run: poetry run pytest . --junitxml=junit/test-results-${{ matrix.python-version }}.xml --cov=ollama --cov-report=xml --cov-report=html
24-
- uses: actions/upload-artifact@v4
14+
- uses: astral-sh/setup-uv@v5
2515
with:
26-
name: pytest-results-${{ matrix.python-version }}
27-
path: junit/test-results-${{ matrix.python-version }}.xml
16+
enable-cache: true
17+
- run: uvx hatch test -acp
2818
if: ${{ always() }}
2919
lint:
3020
runs-on: ubuntu-latest
3121
steps:
3222
- uses: actions/checkout@v4
33-
- run: pipx install poetry
3423
- uses: actions/setup-python@v5
24+
- uses: astral-sh/setup-uv@v5
3525
with:
36-
python-version: "3.13"
37-
cache: poetry
38-
- run: poetry install --with=dev
39-
- run: poetry run ruff check --output-format=github .
40-
- run: poetry run ruff format --check .
41-
- name: check poetry.lock is up-to-date
42-
run: poetry check --lock
26+
enable-cache: true
27+
- name: check formatting
28+
run: uvx hatch fmt --check -f
29+
- name: check linting
30+
run: uvx hatch fmt --check -l --output-format=github
31+
- name: check uv.lock is up-to-date
32+
run: uv lock --check
4333
- name: check requirements.txt is up-to-date
4434
run: |
45-
poetry export >requirements.txt
35+
uv export >requirements.txt
4636
git diff --exit-code requirements.txt

examples/chat-with-history.py

+1-4
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,7 @@
2323
user_input = input('Chat with history: ')
2424
response = chat(
2525
'llama3.2',
26-
messages=messages
27-
+ [
28-
{'role': 'user', 'content': user_input},
29-
],
26+
messages=[*messages, {'role': 'user', 'content': user_input}],
3027
)
3128

3229
# Add the response to the messages to maintain the history

examples/multimodal-generate.py

+1-4
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,7 @@
88
latest = httpx.get('https://xkcd.com/info.0.json')
99
latest.raise_for_status()
1010

11-
if len(sys.argv) > 1:
12-
num = int(sys.argv[1])
13-
else:
14-
num = random.randint(1, latest.json().get('num'))
11+
num = int(sys.argv[1]) if len(sys.argv) > 1 else random.randint(1, latest.json().get('num'))
1512

1613
comic = httpx.get(f'https://xkcd.com/{num}/info.0.json')
1714
comic.raise_for_status()

ollama/_client.py

+16-14
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
overload,
2323
)
2424

25+
import anyio
2526
from pydantic.json_schema import JsonSchemaValue
2627

2728
from ollama._utils import convert_function_to_tool
@@ -75,6 +76,7 @@ def __init__(
7576
self,
7677
client,
7778
host: Optional[str] = None,
79+
*,
7880
follow_redirects: bool = True,
7981
timeout: Any = None,
8082
headers: Optional[Mapping[str, str]] = None,
@@ -253,7 +255,7 @@ def generate(
253255
stream=stream,
254256
raw=raw,
255257
format=format,
256-
images=[image for image in _copy_images(images)] if images else None,
258+
images=list(_copy_images(images)) if images else None,
257259
options=options,
258260
keep_alive=keep_alive,
259261
).model_dump(exclude_none=True),
@@ -336,8 +338,8 @@ def add_two_numbers(a: int, b: int) -> int:
336338
'/api/chat',
337339
json=ChatRequest(
338340
model=model,
339-
messages=[message for message in _copy_messages(messages)],
340-
tools=[tool for tool in _copy_tools(tools)],
341+
messages=list(_copy_messages(messages)),
342+
tools=list(_copy_tools(tools)),
341343
stream=stream,
342344
format=format,
343345
options=options,
@@ -756,7 +758,7 @@ async def generate(
756758
stream=stream,
757759
raw=raw,
758760
format=format,
759-
images=[image for image in _copy_images(images)] if images else None,
761+
images=list(_copy_images(images)) if images else None,
760762
options=options,
761763
keep_alive=keep_alive,
762764
).model_dump(exclude_none=True),
@@ -840,8 +842,8 @@ def add_two_numbers(a: int, b: int) -> int:
840842
'/api/chat',
841843
json=ChatRequest(
842844
model=model,
843-
messages=[message for message in _copy_messages(messages)],
844-
tools=[tool for tool in _copy_tools(tools)],
845+
messages=list(_copy_messages(messages)),
846+
tools=list(_copy_tools(tools)),
845847
stream=stream,
846848
format=format,
847849
options=options,
@@ -991,7 +993,7 @@ async def create(
991993
parameters: Optional[Union[Mapping[str, Any], Options]] = None,
992994
messages: Optional[Sequence[Union[Mapping[str, Any], Message]]] = None,
993995
*,
994-
stream: Literal[True] = True,
996+
stream: Literal[False] = False,
995997
) -> ProgressResponse: ...
996998

997999
@overload
@@ -1054,19 +1056,19 @@ async def create(
10541056

10551057
async def create_blob(self, path: Union[str, Path]) -> str:
10561058
sha256sum = sha256()
1057-
with open(path, 'rb') as r:
1059+
async with await anyio.open_file(path, 'rb') as r:
10581060
while True:
1059-
chunk = r.read(32 * 1024)
1061+
chunk = await r.read(32 * 1024)
10601062
if not chunk:
10611063
break
10621064
sha256sum.update(chunk)
10631065

10641066
digest = f'sha256:{sha256sum.hexdigest()}'
10651067

10661068
async def upload_bytes():
1067-
with open(path, 'rb') as r:
1069+
async with await anyio.open_file(path, 'rb') as r:
10681070
while True:
1069-
chunk = r.read(32 * 1024)
1071+
chunk = await r.read(32 * 1024)
10701072
if not chunk:
10711073
break
10721074
yield chunk
@@ -1133,7 +1135,7 @@ def _copy_images(images: Optional[Sequence[Union[Image, Any]]]) -> Iterator[Imag
11331135
def _copy_messages(messages: Optional[Sequence[Union[Mapping[str, Any], Message]]]) -> Iterator[Message]:
11341136
for message in messages or []:
11351137
yield Message.model_validate(
1136-
{k: [image for image in _copy_images(v)] if k == 'images' else v for k, v in dict(message).items() if v},
1138+
{k: list(_copy_images(v)) if k == 'images' else v for k, v in dict(message).items() if v},
11371139
)
11381140

11391141

@@ -1143,7 +1145,7 @@ def _copy_tools(tools: Optional[Sequence[Union[Mapping[str, Any], Tool, Callable
11431145

11441146

11451147
def _as_path(s: Optional[Union[str, PathLike]]) -> Union[Path, None]:
1146-
if isinstance(s, str) or isinstance(s, Path):
1148+
if isinstance(s, (str, Path)):
11471149
try:
11481150
if (p := Path(s)).exists():
11491151
return p
@@ -1225,7 +1227,7 @@ def _parse_host(host: Optional[str]) -> str:
12251227
elif scheme == 'https':
12261228
port = 443
12271229

1228-
split = urllib.parse.urlsplit('://'.join([scheme, hostport]))
1230+
split = urllib.parse.urlsplit(f'{scheme}://{hostport}')
12291231
host = split.hostname or '127.0.0.1'
12301232
port = split.port or port
12311233

ollama/_types.py

+8-9
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import contextlib
12
import json
23
from base64 import b64decode, b64encode
34
from datetime import datetime
@@ -78,8 +79,8 @@ def __contains__(self, key: str) -> bool:
7879
if key in self.model_fields_set:
7980
return True
8081

81-
if key in self.model_fields:
82-
return self.model_fields[key].default is not None
82+
if value := self.model_fields.get(key):
83+
return value.default is not None
8384

8485
return False
8586

@@ -97,7 +98,7 @@ def get(self, key: str, default: Any = None) -> Any:
9798
>>> msg.get('tool_calls')[0]['function']['name']
9899
'foo'
99100
"""
100-
return self[key] if key in self else default
101+
return getattr(self, key) if hasattr(self, key) else default
101102

102103

103104
class Options(SubscriptableBaseModel):
@@ -332,7 +333,7 @@ class ChatRequest(BaseGenerateRequest):
332333
@model_serializer(mode='wrap')
333334
def serialize_model(self, nxt):
334335
output = nxt(self)
335-
if 'tools' in output and output['tools']:
336+
if output.get('tools'):
336337
for tool in output['tools']:
337338
if 'function' in tool and 'parameters' in tool['function'] and 'defs' in tool['function']['parameters']:
338339
tool['function']['parameters']['$defs'] = tool['function']['parameters'].pop('defs')
@@ -536,12 +537,10 @@ class ResponseError(Exception):
536537
"""
537538

538539
def __init__(self, error: str, status_code: int = -1):
539-
try:
540-
# try to parse content as JSON and extract 'error'
541-
# fallback to raw content if JSON parsing fails
540+
# try to parse content as JSON and extract 'error'
541+
# fallback to raw content if JSON parsing fails
542+
with contextlib.suppress(json.JSONDecodeError):
542543
error = json.loads(error).get('error', error)
543-
except json.JSONDecodeError:
544-
...
545544

546545
super().__init__(error)
547546
self.error = error

ollama/_utils.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,12 @@ def _parse_docstring(doc_string: Union[str, None]) -> dict[str, str]:
1515
if not doc_string:
1616
return parsed_docstring
1717

18-
key = hash(doc_string)
18+
key = str(hash(doc_string))
1919
for line in doc_string.splitlines():
2020
lowered_line = line.lower().strip()
2121
if lowered_line.startswith('args:'):
2222
key = 'args'
23-
elif lowered_line.startswith('returns:') or lowered_line.startswith('yields:') or lowered_line.startswith('raises:'):
23+
elif lowered_line.startswith(('returns:', 'yields:', 'raises:')):
2424
key = '_'
2525

2626
else:
@@ -54,7 +54,7 @@ def _parse_docstring(doc_string: Union[str, None]) -> dict[str, str]:
5454

5555

5656
def convert_function_to_tool(func: Callable) -> Tool:
57-
doc_string_hash = hash(inspect.getdoc(func))
57+
doc_string_hash = str(hash(inspect.getdoc(func)))
5858
parsed_docstring = _parse_docstring(inspect.getdoc(func))
5959
schema = type(
6060
func.__name__,

0 commit comments

Comments
 (0)