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
117 changes: 117 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
# Contributing to Ollama Python

Thank you for your interest in contributing to the official Ollama Python library! We welcome bug reports, bug fixes, feature proposals, documentation improvements, and example additions.

---

## Prerequisites

- **Python 3.8+** (Python 3.10+ recommended for development)
- **[uv](https://docs.astral.sh/uv/)** or **[hatch](https://hatch.pypa.io/)**
- A running instance of **[Ollama](https://ollama.com/download)** (optional for running unit/mock tests, required for real model execution)

---

## Development Setup

1. **Fork and clone the repository:**

```bash
git clone https://github.com/<your-username>/ollama-python.git
cd ollama-python
```

2. **Set up virtual environment and install dependencies:**

Using `uv`:
```bash
uv sync --all-extras --all-groups
```

Using `hatch`:
```bash
hatch env create
```

---

## Code Style and Quality

We use [Ruff](https://docs.astral.sh/ruff/) to format and lint our codebase.

- **Indentation**: 2 spaces (`indent-width = 2`, `indent-style = 'space'`)
- **Quotes**: Single quotes (`quote-style = 'single'`)
- **Line length**: 320 characters max

### Formatting & Linting Commands

Check formatting and linting:
```bash
uv run ruff format --check .
uv run ruff check .
```

Automatically fix format and lint issues:
```bash
uv run ruff format .
uv run ruff check --fix .
```

Or using `hatch`:
```bash
hatch fmt --check
```

---

## Running Tests

All unit tests use mock HTTP servers with `pytest-httpserver` and doctests across module docstrings.

Run the test suite with `uv`:
```bash
uv run pytest
```

Run tests across environments with `hatch`:
```bash
hatch test
```

Run a specific test file:
```bash
uv run pytest tests/test_client.py
uv run pytest tests/test_utils.py
uv run pytest tests/test_type_serialization.py
```

---

## Updating Lockfile and Requirements

If dependencies in `pyproject.toml` are modified, make sure the lockfile and exported requirements remain in sync:

```bash
uv lock
uv export > requirements.txt
```

Verify that requirements match:
```bash
git diff --exit-code requirements.txt
```

---

## Pull Request Guidelines

1. **Branch naming**: Use descriptive branch names (e.g. `fix/named-tempfile-windows`, `feat/client-api-key-arg`).
2. **Commit messages**: Follow concise conventional commit style:
- `fix: ...` for bug fixes
- `feat: ...` for new features
- `docs: ...` for documentation updates
- `test: ...` for test additions
- `refactor: ...` for code refactoring
3. **Tests**: Add unit tests for any new functionality or bug fixes in `tests/`.
4. **Documentation & Examples**: Update `README.md` and `examples/README.md` if changing public APIs or adding new features.
5. **No regressions**: Ensure formatting (`ruff format`), linting (`ruff check`), and all tests pass before submitting.
18 changes: 15 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,8 @@ curl https://ollama.com/api/tags
import os
from ollama import Client

client = Client(host='https://ollama.com', headers={'Authorization': 'Bearer ' + os.environ.get('OLLAMA_API_KEY')})
# You can pass api_key directly or rely on the OLLAMA_API_KEY environment variable
client = Client(host='https://ollama.com', api_key=os.environ.get('OLLAMA_API_KEY'))

messages = [
{
Expand All @@ -129,12 +130,16 @@ for part in client.chat('gpt-oss:120b', messages=messages, stream=True):
## Custom client
A custom client can be created by instantiating `Client` or `AsyncClient` from `ollama`.

All extra keyword arguments are passed into the [`httpx.Client`](https://www.python-httpx.org/api/#client).
Clients accept `host`, `api_key`, `headers`, `timeout`, and any keyword arguments supported by [`httpx.Client`](https://www.python-httpx.org/api/#client).

```python
from ollama import Client

client = Client(host='http://localhost:11434', headers={'x-some-header': 'some-value'})
client = Client(
host='http://localhost:11434',
api_key='your_api_key',
headers={'x-some-header': 'some-value'},
)
response = client.chat(
model='gemma4',
messages=[
Expand All @@ -146,6 +151,13 @@ response = client.chat(
)
```

Clients can also be used as context managers to ensure immediate resource and socket cleanup:

```python
with Client() as client:
response = client.chat(model='gemma4', messages=[{'role': 'user', 'content': 'Why is the sky blue?'}])
```

## Async client

The `AsyncClient` class is used to make asynchronous requests. It can be configured with the same fields as the `Client` class.
Expand Down
7 changes: 7 additions & 0 deletions examples/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,14 @@ See [ollama/docs/api.md](https://github.com/ollama/ollama/blob/main/docs/api.md)
- [async-chat.py](async-chat.py)
- [chat-stream.py](chat-stream.py) - Streamed outputs
- [chat-with-history.py](chat-with-history.py) - Chat with model and maintain history of the conversation
- [chat-logprobs.py](chat-logprobs.py) - Return log probabilities for chat completions

### Generate - Generate text with a model

- [generate.py](generate.py)
- [async-generate.py](async-generate.py)
- [generate-stream.py](generate-stream.py) - Streamed outputs
- [generate-logprobs.py](generate-logprobs.py) - Return log probabilities for generation
- [fill-in-middle.py](fill-in-middle.py) - Given a prefix and suffix, fill in the middle

### Tools/Function Calling - Call a function with a model
Expand All @@ -46,6 +48,7 @@ export OLLAMA_API_KEY="your_api_key_here"
```

- [web-search.py](web-search.py)
- [web-fetch.py](web-fetch.py) - Fetch and extract content from a web page
- [web-search-gpt-oss.py](web-search-gpt-oss.py) - Using browser research tools with gpt-oss

#### MCP server
Expand Down Expand Up @@ -131,3 +134,7 @@ Requirement: `pip install tqdm`
### System One - Score typed questions

- [systemone.py](systemone.py) - Choice, boolean probability, and rubric score questions

### Client Lifecycle & Context Managers - Manage connections and resources

- [context-manager.py](context-manager.py) - Use sync and async clients with context managers for automatic resource cleanup
44 changes: 44 additions & 0 deletions examples/context-manager.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# /// script
# requires-python = ">=3.8"
# dependencies = [
# "ollama",
# ]
# ///
"""
Example demonstrating how to use Client and AsyncClient as context managers
to ensure proper cleanup and release of underlying HTTP connections.
"""

import asyncio

from ollama import AsyncClient, Client


def sync_example():
print('=== Synchronous Client Context Manager ===')
# Entering the context manager initializes the client;
# exiting cleanly closes all httpx connection pools.
with Client() as client:
models = client.list()
print(f'Discovered {len(models.models)} installed models:')
for model in models.models[:3]:
print(f' - {model.model} (modified: {model.modified_at})')


async def async_example():
print('\n=== Asynchronous Client Context Manager ===')
async with AsyncClient() as client:
response = await client.generate(
model='gemma4',
prompt='Why is the sky blue in one sentence?',
)
print(f'Response: {response.response}')


if __name__ == '__main__':
sync_example()
# Run async example if an async event loop is available
try:
asyncio.run(async_example())
except Exception as e:
print(f'Async example skipped or encountered error: {e}')
37 changes: 37 additions & 0 deletions examples/web-fetch.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# /// script
# requires-python = ">=3.11"
# dependencies = [
# "rich",
# "ollama",
# ]
# ///
import os
import sys

from rich import print

from ollama import Client

api_key = os.getenv('OLLAMA_API_KEY')
if not api_key:
print('[bold red]Error:[/bold red] OLLAMA_API_KEY environment variable is required.')
print('Get your API key at https://ollama.com/settings/keys')
sys.exit(1)

# You can pass api_key directly or rely on OLLAMA_API_KEY
client = Client(api_key=api_key)

url = 'https://ollama.com'
print(f'Fetching content from: [bold cyan]{url}[/bold cyan]\n')

# Fetch using the client
response = client.web_fetch(url)

print(f'Title: [bold green]{response.title}[/bold green]')
if response.links:
print(f'Found {len(response.links)} links on the page:')
for link in response.links[:5]:
print(f' - {link}')

print('\nContent Preview:')
print(response.content[:500] if response.content else 'No content returned')
17 changes: 17 additions & 0 deletions ollama/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,22 @@
'Tool',
'WebFetchResponse',
'WebSearchResponse',
'chat',
'copy',
'create',
'create_blob',
'delete',
'embed',
'embeddings',
'generate',
'list',
'ps',
'pull',
'push',
'show',
'systemone',
'web_fetch',
'web_search',
]

_client = Client()
Expand All @@ -74,6 +90,7 @@
pull = _client.pull
push = _client.push
create = _client.create
create_blob = _client.create_blob
delete = _client.delete
list = _client.list
copy = _client.copy
Expand Down
Loading