Metadata-Version: 2.5
Name: unishift-client-py
Version: 0.1.6
Summary: Python client for the Unishift platform API
Project-URL: Homepage, https://unishift.ru
Project-URL: Repository, https://gitlab.com/unishift-platform/modules/unishift-client-py
Project-URL: Documentation, https://unishift.ru
Author: Unishift Platform
License-Expression: MIT
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Typing :: Typed
Requires-Python: >=3.10
Requires-Dist: httpx>=0.27.0
Requires-Dist: pyyaml>=6.0
Description-Content-Type: text/markdown

# unishift-client-py

Python-клиент для взаимодействия со всеми сервисами платформы [Unishift](https://unishift.ru). HTTP-запросы выполняются через [httpx](https://www.python-httpx.org/).

## Установка

```bash
pip install unishift-client-py
```

Или из GitLab:

```bash
pip install git+ssh://git@gitlab.com/unishift-platform/modules/unishift-client-py.git
```

## Подключение к хосту

По умолчанию клиент ходит через **gateway** на `http://localhost:8090`. URL сервиса собирается как:

```text
{gateway_url}/proxy/{service}/api/v1/...
```

Если `gateway_url` пустой — запросы идут **напрямую** к сервисам:

```text
{service_url}/api/v1/...
```

### Через gateway (по умолчанию)

```python
from unishift import Client, default_config

cfg = default_config()  # gateway_url = http://localhost:8090
# или другой хост:
cfg.use_gateway("https://api.example.com")

client = Client(cfg)
```

### Напрямую к сервисам

```python
from unishift import Client, default_config

cfg = default_config()
cfg.use_direct_services()  # очищает gateway_url
# при необходимости поправьте URL отдельных сервисов:
# cfg.auth_url = "http://auth:8091"
# cfg.spaces_url = "http://spaces:8092"

client = Client(cfg)
```

### Кастомный Config

```python
from unishift import Client, Config

cfg = Config(
    gateway_url="https://api.example.com",
    timeout=60.0,
    retries=5,
    token="eyJ...",          # API-токен (PAT) или session JWT → Bearer
    # service_token="zope",  # только для межсервисных вызовов → Service
)
client = Client(cfg)
```

## Параметры конфигурации

| Параметр | Тип | По умолчанию (`default_config`) | Описание |
|----------|-----|----------------------------------|----------|
| `gateway_url` | `str` | `http://localhost:8090` | Базовый URL gateway. Если задан — все сервисы идут через `/proxy/{service}`. Пустая строка — direct mode |
| `auth_url` | `str` | `http://localhost:8091` | Auth (только direct) |
| `spaces_url` | `str` | `http://localhost:8092` | Spaces (только direct) |
| `documents_url` | `str` | `http://localhost:8093` | Documents (только direct) |
| `command_manager_url` | `str` | `http://localhost:8094` | Command Manager (только direct) |
| `scheduler_url` | `str` | `http://localhost:8095` | Scheduler (только direct) |
| `storage_url` | `str` | `http://localhost:8096` | Storage (только direct) |
| `env_manager_url` | `str` | `http://localhost:8097` | Env Manager / props (только direct) |
| `spark_grid_url` | `str` | `http://localhost:8087` | Spark Grid (только direct) |
| `flow_manager_url` | `str` | `http://localhost:8099` | Flow Manager (только direct) |
| `ai_router_url` | `str` | `http://localhost:8101` | AI Router (только direct) |
| `notifications_url` | `str` | `http://localhost:8103` | Notifications (только direct) |
| `task_tracker_url` | `str` | `http://localhost:8102` | Task Tracker (только direct) |
| `timeout` | `float` | `30.0` | Таймаут HTTP-запроса (секунды) |
| `retries` | `int` | `3` | Число повторных попыток при сетевых ошибках и 5xx |
| `token` | `str` | `""` | Bearer JWT: session после логина или personal API token (PAT). Имеет приоритет над `service_token` |
| `service_token` | `str` | `""` | Токен для межсервисных вызовов (`Authorization: Service …`) |

Методы `Config`:

| Метод | Описание |
|-------|----------|
| `use_gateway(gateway_url)` | Включить proxy через gateway |
| `use_direct_services()` | Отключить gateway, звонить по `*_url` |

Хелперы на уровне модуля: `use_gateway(config, url)`, `use_direct_services(config)`.

## Авторизация к инстансу

Подключение к удалённому (или локальному) инстансу через gateway и логин пользователя:

```python
from unishift import Client, LoginRequest, default_config

cfg = default_config()
cfg.use_gateway("https://your-instance.example.com")  # URL gateway инстанса
# локально: cfg.use_gateway("http://localhost:8090")

client = Client(cfg)

auth = client.auth.login(
    LoginRequest(login="user@example.com", password="secret")
)
client.set_token(auth.token)  # Bearer JWT на все последующие запросы

profile = client.auth.get_profile()
print(profile.id, profile.login)
```

После `set_token` можно вызывать любые сервисы от имени пользователя:

```python
spaces = client.spaces.get_spaces()
scopes = client.ai_router.list_scopes()
```

Обновление сессии:

```python
auth = client.auth.refresh_session()
client.set_token(auth.token)
```

Для межсервисных вызовов без пользовательского логина используйте `service_token` (см. ниже). Для скриптов от имени пользователя — API-токен (PAT).

## API-токен (Personal Access Token)

API-токен — долгоживущий Bearer JWT пользователя (Settings → API keys или `POST /auth/tokens`). Это **не** service token: передаётся как `Authorization: Bearer …`, идентичность берётся из claims (без `X-User-ID`).

```python
from unishift import Client, default_config

cfg = default_config()
cfg.use_gateway("https://your-instance.example.com")
cfg.token = "eyJ..."  # секрет из Settings → API keys (показывается один раз при создании)
client = Client(cfg)

# Или после создания клиента:
# client.set_token("eyJ...")

spaces = client.spaces.get_spaces()
```

Создание токена через API (нужна уже авторизованная сессия):

```python
from unishift.auth import CreateAccessTokenRequest

created = client.auth.create_access_token(
    CreateAccessTokenRequest(name="ci", note="pipelines", ttl_seconds=86400 * 30)
)
print(created.token)  # сохранить сразу — больше не вернётся
client.set_token(created.token)
```

## Использование

```python
from unishift import (
    Client,
    CreateJobRequest,
    GetCommandsQuery,
    GetPropsQuery,
    JobVariable,
    LoginRequest,
    default_config,
)

cfg = default_config()
client = Client(cfg)

auth = client.auth.login(LoginRequest(login="user", password="password"))
client.set_token(auth.token)

spaces = client.spaces.get_spaces()
props = client.props.get_props(GetPropsQuery())
commands = client.commands.get_commands(GetCommandsQuery())
job = client.commands.create_job(
    CreateJobRequest(
        command_id="...",
        variables=[JobVariable(name="x", value="1")],
    )
)
```

## Сервисы

| Клиент | Описание |
|--------|----------|
| `client.auth` | Регистрация, логин, профиль |
| `client.spaces` | Пространства |
| `client.documents` | Документы (требует space_id) |
| `client.task_tracker` | Задачи, workflow, настраиваемые поля (требует space_id) |
| `client.commands` | Команды, коллекции, jobs |
| `client.scheduler` | Календари и события |
| `client.storage` | Файлы |
| `client.props` | Пропсы (зашифрованное хранилище) |
| `client.gateway` | Health, список сервисов |
| `client.flow_manager` | Пайплайны и инстансы |
| `client.spark_grid` | Медиафайлы и теги |
| `client.ai_router` | AI-провайдеры, чаты, RAG, agent |
| `client.notifications` | Отправка уведомлений по шаблону |

## Сервисный токен (межсервисное взаимодействие)

Только для вызовов сервис→сервис (`Authorization: Service …`). Для пользовательских скриптов и CI используйте API-токен (раздел выше), а не `service_token`.

```python
from unishift import Client, GetPropsQuery, default_config

cfg = default_config()
cfg.service_token = "zope"
client = Client(cfg)

# Или после создания
client.set_service_token("zope")

prop = client.props.get_prop_by_id("...")
props = client.props.get_props_for_user("user-uuid", GetPropsQuery(limit=100))
```

## Acting user (X-User-ID)

```python
client.set_acting_user("user-uuid")
as_user_client = client.as_user("user-uuid")
```

## RAG (ingest)

```python
# kwargs
client.ai_router.ingest_document(
    scope="docs",
    title="FAQ",
    content="...",
    content_type="markdown",
    metadata={"category": "support"},
)

# или через IngestDocumentRequest
from unishift import IngestDocumentRequest

client.ai_router.ingest_document(
    IngestDocumentRequest(scope="docs", title="FAQ", content="...")
)
```

## Сборка пакета

Версия берётся из переменной окружения `PACKAGE_VERSION` (fallback `0.1.1`):

```bash
PACKAGE_VERSION=0.2.0 python -m build
```
