Metadata-Version: 2.4
Name: modis-client
Version: 0.1.0
Summary: Python client for MoDIS Service Nodes
Requires-Python: >=3.11
Description-Content-Type: text/markdown
Requires-Dist: httpx>=0.27.0
Requires-Dist: pydantic>=2.7.0
Provides-Extra: dev
Requires-Dist: build>=1.2.0; extra == "dev"
Requires-Dist: mypy>=1.10.0; extra == "dev"
Requires-Dist: pytest>=8.0.0; extra == "dev"
Requires-Dist: pytest-asyncio>=0.23.0; extra == "dev"
Requires-Dist: ruff>=0.8.0; extra == "dev"

# MoDIS Python Client

`modis-client` is the Python client package for MoDIS Service Nodes (MSNs).
It exposes MoDIS-native request and response objects for direct Python callers.

The PyPI distribution name is `modis-client`. `pymodis` and `modis` are already
used by unrelated packages, while `modis-client` currently appears available.

## Install

From a checkout:

```bash
python -m pip install -e .
```

The import package is `modis_client`:

```python
from modis_client import SingleNodeClient

with SingleNodeClient(base_url="http://localhost:8000") as client:
    response = client.chat_completion({
        "model": "qwen3.6-27b",
        "messages": [{"role": "user", "content": "Hello MoDIS."}],
    })

print(response.content.content)
```

The package supports sync and async single-node clients, routing clients, batch
requests, explicit streaming methods, MoDIS tool-call fields, and Pydantic v2
models with MoDIS wire aliases.

## Async Client

```python
from modis_client import AsyncSingleNodeClient

async with AsyncSingleNodeClient(base_url="http://localhost:8000") as client:
    response = await client.responses({
        "model": "qwen3.6-27b",
        "input": "Summarize MoDIS in one sentence.",
    })
```

## Batch Requests

```python
responses = client.chat_completion([
    {"model": "qwen3.6-27b", "messages": [{"role": "user", "content": "one"}]},
    {"model": "qwen3.6-27b", "messages": [{"role": "user", "content": "two"}]},
])
```

## Streaming

```python
for event in client.chat_completion_stream({
    "model": "qwen3.6-27b",
    "messages": [{"role": "user", "content": "Count to three."}],
}):
    if event.event == "delta":
        print(event.payload)
```

## Tool Calls

```python
response = client.chat_completion({
    "model": "qwen3.6-27b",
    "messages": [{"role": "user", "content": "What time is it?"}],
    "tools": [
        {
            "type": "function",
            "function": {
                "name": "get_time",
                "description": "Return the current local time.",
                "parameters": {"type": "object", "properties": {}},
            },
        }
    ],
    "toolChoice": "auto",
    "parallelToolCalls": True,
})
```

## Routing

```python
from modis_client import RoutingClient

client = RoutingClient(
    nodes=[
        "http://msn-a:8000",
        {"base_url": "http://msn-b:8000", "priority": 5, "node_id": "msn-b"},
    ],
    strategy="balanced",
)
```

## Pydantic Models

Responses are returned as Pydantic models by default. Use `to_wire()` or
`model_dump(by_alias=True, exclude_none=True)` when you need the MoDIS JSON
shape:

```python
wire_payload = response.to_wire()
```

`ModisClientError` is raised for transport failures, typed MoDIS errors, and
stream protocol errors.

## Development

```bash
python -m pip install -e ".[dev]"
pytest
ruff check .
ruff format --check .
mypy src
python -m build
```

## Live Service Tests

Live tests are opt-in and require a running `modis-service` or compatible
MoDIS endpoint. They are skipped unless `MODIS_LIVE_SERVICE_URL` is set.

```bash
MODIS_LIVE_SERVICE_URL=http://localhost:8000 \
MODIS_LIVE_TEXT_MODEL=qwen3.6-27b \
MODIS_LIVE_IMAGE_MODEL=qwen-image \
.venv/bin/python -m pytest -m live
```

Optional variables:

| Variable | Default | Purpose |
| --- | --- | --- |
| `MODIS_LIVE_TIMEOUT` | `900` | Per-request timeout in seconds. |
| `MODIS_LIVE_CONCURRENCY` | `4` | Concurrent async text requests. |
| `MODIS_LIVE_IMAGE_WIDTH` | `1024` | Image test width. |
| `MODIS_LIVE_IMAGE_HEIGHT` | `1024` | Image test height. |
| `MODIS_LIVE_IMAGE_STEPS` | `20` | Image inference steps. |
