Metadata-Version: 2.4
Name: synth-client
Version: 0.1.0
Summary: Python client SDK for the Synth API
Project-URL: Homepage, https://gitlab.actcognitive.org/Mawlle/synth
Project-URL: Repository, https://gitlab.actcognitive.org/Mawlle/synth
Author: Mawlle
License: MIT
Keywords: api-client,augmentation,computer-vision,sdk,synth
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
Classifier: Topic :: Scientific/Engineering :: Image Processing
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Requires-Python: <3.14,>=3.12
Requires-Dist: httpx>=0.28.1
Requires-Dist: pillow>=10.0
Requires-Dist: pydantic>=2.0
Description-Content-Type: text/markdown

# synth-client

Простой Python SDK для Synth API.

## Что это

`synth-client` нужен, чтобы работать с Synth из Python без ручных HTTP-запросов.

Через него можно:
- загружать и искать изображения
- получать описания и аннотации
- запускать аугментации
- ждать завершения асинхронных задач
- собирать простой pipeline для батчевой обработки

## Установка

Если вы находитесь в корне этого репозитория:

```bash
uv add synth-client
```

Если пакет уже есть в workspace, `uv` подключит локальный workspace member.

Для локального запуска скриптов из этого репозитория также подойдут:

```bash
pip install -e ./synth-client
```

или

```bash
PYTHONPATH=./synth-client python script.py
```

## Быстрый старт

```python
from synth_client import SynthClient

client = SynthClient(
    base_url="http://localhost:8000",
    collection="images",
)

images = client.images.list(limit=10)
print(images[0].id if images else "no images")

client.close()
```

Можно и через контекстный менеджер:

```python
from synth_client import SynthClient

with SynthClient(base_url="http://localhost:8000") as client:
    stats = client.get_stats()
    print(stats.images)
```

## Основные объекты

### `SynthClient`

Корневой клиент.

Основные поля:
- `client.images` — операции с изображениями
- `client.augmentations` — подсказки и асинхронные задачи аугментации

Основные методы:
- `list_collections()`
- `create_collection(name)`
- `delete_collection(name)`
- `get_stats()`
- `pipeline()`

### `ImageDTO`

Объект изображения, который возвращается из API.

Часто используемые поля:
- `id`
- `name`
- `description`
- `s3_url`
- `parent_id`
- `augmentation_prompt`
- `augmentation_category`

### `AugmentationSuggestion`

Одна подсказка для аугментации:

```python
AugmentationSuggestion(
    prompt="add snow on the road",
    category="weather",
)
```

### `AugmentationTask`

Статус фоновой задачи:
- `task_id`
- `status`
- `progress`
- `result`
- `error`

### `AugmentationResult`

Нормализованный результат после `wait()`:
- `task_id`
- `status`
- `image_ids`
- `total`

## Работа с изображениями

### Получить список изображений

```python
from synth_client import SynthClient

with SynthClient() as client:
    images = client.images.list(limit=20, offset=0)
    for image in images:
        print(image.id, image.name)
```

### Получить одно изображение

```python
with SynthClient() as client:
    image = client.images.get("image-id")
    print(image.description)
```

### Поиск

По тексту:

```python
with SynthClient() as client:
    images = client.images.search(query="red car in city", limit=5)
```

По похожему изображению:

```python
with SynthClient() as client:
    similar = client.images.search(image_id="image-id", limit=5)
```

### Загрузка файла

```python
with SynthClient() as client:
    image = client.images.upload("./examples/car.jpg")
    print(image.id)
```

### Описание изображения

```python
with SynthClient() as client:
    description = client.images.describe("image-id")
    print(description)
```

### Детекция объектов

```python
with SynthClient() as client:
    boxes = client.images.detect_objects(
        image_id="image-id",
        prompt="car, pedestrian, traffic light",
    )
    for box in boxes:
        print(box.label, box.x_min, box.y_min, box.x_max, box.y_max)
```

По прямой ссылке на изображение:

```python
with SynthClient() as client:
    boxes = client.images.detect_objects(
        image_url="https://example.com/image.jpg",
        prompt="person",
    )
    for box in boxes:
        print(box.model_dump())
```

Нужно передать ровно один из аргументов:
- `image_id`
- `image_url`

### Сегментация объектов

```python
with SynthClient() as client:
    boxes = client.images.segment_objects(
        image_url="https://example.com/image.jpg",
        prompt="person",
    )
    for box in boxes:
        print(box.label, box.polygon)
```

Для сегментации `BoundingBox` может содержать:
- `polygon` — список точек контура `[[x, y], ...]`

### Обновление аннотаций вручную

```python
from synth_client import BoundingBox, SynthClient

with SynthClient() as client:
    updated = client.images.update_annotations(
        "image-id",
        [
            BoundingBox(
                label="car",
                x_min=10,
                y_min=20,
                x_max=200,
                y_max=180,
            )
        ],
    )
    print(updated.annotations)
```

## Аугментации

### Получить подсказки

```python
with SynthClient() as client:
    suggestions = client.augmentations.suggest("image-id")
    for item in suggestions:
        print(item.category, item.prompt)
```

### Запустить аугментацию из подсказок

```python
from synth_client import AugmentationSuggestion, SynthClient

with SynthClient() as client:
    task = client.augmentations.start(
        image_id="image-id",
        suggestions=[
            AugmentationSuggestion(
                prompt="heavy rain at night",
                category="weather",
            )
        ],
    )
    print(task.task_id)
```

### Запустить аугментацию из списка prompt'ов

```python
with SynthClient() as client:
    task = client.augmentations.start_from_prompts(
        image_id="image-id",
        prompts=[
            "heavy rain at night",
            "dense fog on the road",
        ],
        categories=[
            "weather",
            "weather",
        ],
    )
```

### Дождаться завершения задачи

```python
from synth_client import SynthClient, TaskFailedError

with SynthClient() as client:
    task = client.augmentations.start_from_prompts(
        image_id="image-id",
        prompts=["heavy rain at night"],
    )

    try:
        result = client.augmentations.wait(task.task_id, timeout=300)
        print(result.image_ids)
    except TaskFailedError as exc:
        print(exc)
```

### Проверить статус вручную

```python
with SynthClient() as client:
    status = client.augmentations.status("task-id")
    print(status.status, status.progress)
```

### Отменить задачу

```python
with SynthClient() as client:
    response = client.augmentations.cancel("task-id")
    print(response["status"])
```

### Запустить tree augmentation

```python
from synth_client import GenerationParams, SynthClient

with SynthClient() as client:
    task = client.augmentations.start_tree(
        root_image_id="image-id",
        epochs=2,
        suggestions_per_node=3,
        generation_params=GenerationParams(
            num_inference_steps=30,
            true_cfg_scale=4.5,
            guidance_scale=1.2,
        ),
    )
    print(task.task_id)
```

## Pipeline

Если нужно обработать несколько изображений подряд, можно использовать `pipeline()`.

```python
from synth_client import SynthClient

with SynthClient(collection="images") as client:
    pipeline = client.pipeline()
    dataset = pipeline.dataset_from_query("city street", limit=3)
    results = pipeline.run(
        dataset,
        prompts=["heavy rain", "dense fog"],
        categories=["weather", "weather"],
        timeout=300,
    )

    for result in results:
        print(result.task_id, result.image_ids)
```

## Обработка ошибок

Базовое исключение SDK:

```python
from synth_client import SynthClient, SynthClientError

try:
    with SynthClient(base_url="http://localhost:8000") as client:
        client.images.get("missing-id")
except SynthClientError as exc:
    print(exc)
```

Если сервер вернул ошибку HTTP, в `SynthClientError` доступны:
- `status_code`
- `response_body`

Для упавших async-задач используется `TaskFailedError`.

## Публичный импорт

```python
from synth_client import SynthClient
```

Также доступны:

```python
from synth_client import (
    AugmentationPipeline,
    AugmentationResult,
    AugmentationSuggestion,
    AugmentationTask,
    BoundingBox,
    GenerationParams,
    ImageDTO,
    SynthDataset,
    SynthClientError,
    TaskFailedError,
)
```
