Skip to content
Merged
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
19 changes: 14 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -274,16 +274,25 @@ response = ollama.systemone(
print(response.answers['team'])
```

System One uses `POST /v1/systemone` and requires Ollama v0.35.0 or later with a
compatible local model such as `nimble`. It returns one JSON response; streaming
and cloud models are not supported.
System One uses `POST /v1/systemone` and returns one JSON response; streaming is
not supported. It runs locally with decision models such as `nimble` (Ollama
v0.35.0 or later) or `clef` (v0.35.1 or later), and in the cloud with `clef:cloud`
on a paid plan (through Ollama v0.40.5 or later, or [directly against
ollama.com](#cloud-api-ollamacom)).

Clef also reads images (v0.40.0 or later locally): pass `images` as file paths,
bytes, or base64 strings, shared by all questions. Cloud requests accept up to 4
PNG, JPEG, or WebP images (each at most 4 MiB and 16 megapixels, 8 MiB total).

`state` and question `instructions` accept text, JSON objects, or arrays. Questions
are evaluated in their supplied order:

- `choice`: 2–26 option keys mapped to descriptions; `None` uses the key as its description.
- `choice`: 2–26 option keys mapped to descriptions (up to 255 in the cloud); `None` uses the key as its description.
- `noul`: probability of true, with optional `{"false": "No", "true": "Yes"}` descriptions.
- `score`: 2–26 descriptions ordered lowest to highest; returns a potentially fractional, zero-based score.
- `score`: 2–10 descriptions ordered lowest to highest; returns a potentially fractional, zero-based score.

In the cloud, question ids may only contain ASCII letters, digits, `_`, `.`, and
`-`, up to 100 characters, and a request may ask up to 64 questions.

Responses contain `model`, `answers`, and `usage.input_tokens` / `usage.output_tokens`.
Confidence measures probability concentration, not calibrated correctness.
Expand Down
12 changes: 10 additions & 2 deletions ollama/_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -413,8 +413,11 @@ def systemone(
state: SystemOneContent,
questions: Mapping[str, Union[SystemOneQuestion, Mapping[str, Any]]],
keep_alive: Optional[Union[float, str]] = None,
images: Optional[Sequence[Union[str, bytes, Image]]] = None,
) -> SystemOneResponse:
"""Score choice, noul, and score questions with a local System One model.
"""Score choice, noul, and score questions with a System One decision model.

`images` are shared by all questions, in order; only models that accept images (such as Clef) support them.

Returns one JSON response. Streaming is not supported.
"""
Expand All @@ -425,6 +428,7 @@ def systemone(
json=SystemOneRequest(
model=model,
state=state,
images=list(_copy_images(images)) if images else None,
questions=questions,
keep_alive=keep_alive,
).model_dump(exclude_none=True),
Expand Down Expand Up @@ -1069,8 +1073,11 @@ async def systemone(
state: SystemOneContent,
questions: Mapping[str, Union[SystemOneQuestion, Mapping[str, Any]]],
keep_alive: Optional[Union[float, str]] = None,
images: Optional[Sequence[Union[str, bytes, Image]]] = None,
) -> SystemOneResponse:
"""Score choice, noul, and score questions with a local System One model.
"""Score choice, noul, and score questions with a System One decision model.

`images` are shared by all questions, in order; only models that accept images (such as Clef) support them.

Returns one JSON response. Streaming is not supported.
"""
Expand All @@ -1081,6 +1088,7 @@ async def systemone(
json=SystemOneRequest(
model=model,
state=state,
images=list(_copy_images(images)) if images else None,
questions=questions,
keep_alive=keep_alive,
).model_dump(exclude_none=True),
Expand Down
2 changes: 2 additions & 0 deletions ollama/_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -648,6 +648,8 @@ class SystemOneScoreQuestion(SubscriptableBaseModel):
class SystemOneRequest(SubscriptableBaseModel):
model: str
state: SystemOneContent
images: Optional[Sequence[Image]] = None
'Images shared by all questions; base64 PNG, JPEG, or WebP.'
questions: Dict[str, SystemOneQuestion]
keep_alive: Optional[Union[float, str]] = None

Expand Down
22 changes: 22 additions & 0 deletions tests/test_systemone.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import base64
import inspect
import json

Expand Down Expand Up @@ -86,3 +87,24 @@ def handle(request):
await response
assert error.value.status_code == status
assert error.value.error == message


@pytest.mark.anyio
@pytest.mark.parametrize('client_type', [ollama.Client, ollama.AsyncClient])
async def test_systemone_images(client_type, tmp_path):
png = b'\x89PNG\r\n\x1a\nsynthetic'
path = tmp_path / 'ticket.png'
path.write_bytes(png)
encoded = base64.b64encode(png).decode()

def handle(request):
body = json.loads(request.content)
assert body['images'] == [encoded] * 4
assert list(body) == ['model', 'state', 'images', 'questions']
return httpx.Response(200, json={'model': 'clef', 'answers': {'refund': {'type': 'noul', 'noul': 0.5}}, 'usage': {'input_tokens': 1, 'output_tokens': 0}})

client = client_type(transport=httpx.MockTransport(handle))
response = client.systemone('clef', 'See screenshot', {'refund': {'type': 'noul', 'instructions': 'Refund?'}}, images=[png, encoded, str(path), ollama.Image(value=path)])
if inspect.isawaitable(response):
response = await response
assert response.answers['refund'].noul == 0.5
Loading