# WebSocket протокол

## Канал

`ws://HOST:PORT/ws` (по умолчанию `ws://0.0.0.0:8765/ws`).

Сообщения — JSON в текстовых WebSocket-фреймах.

## Сообщения от клиента (Python AI Agent) к серверу

### `init` — инициализация сессии

```json
{
  "type": "init",
  "session_id": "uuid-or-name",
  "voice_ref": "voices/default_neutral.wav",
  "voice_refs": {
    "neutral": "voices/default_neutral.wav",
    "happy": "voices/default_happy.wav",
    "sad": "voices/default_sad.wav"
  },
  "language": "ru",
  "speed": 1.0,
  "emotion": "neutral",
  "seq": 1
}
```

- `voice_ref` — основной референс голоса.
- `voice_refs` — словарь референсов для каждой эмоции (опционально).
- `language` — код языка (`ru`, `en`, `ua`, `es`, `de`, `fr`, ...).
- `speed` — скорость речи, 0.5–2.0.
- `emotion` — текущая эмоция.

### `text` — чанк текста от LLM

```json
{
  "type": "text",
  "payload": "Привет, ",
  "emotion": "happy",
  "seq": 2
}
```

- `payload` — очередная часть сгенерированного текста.
- `emotion` — переопределить эмоцию для этого и последующих сегментов (опционально).

### `flush` — озвучить всё, что в буфере

```json
{
  "type": "flush",
  "seq": 3
}
```

Используется, когда LLM закончил генерацию и остался незавершённый текст.

### `stop` — немедленно прервать вещание

```json
{
  "type": "stop",
  "reason": "interrupt",
  "seq": 4
}
```

Очищает буфер, аудио-очередь и отменяет текущую генерацию.

### `emotion` — сменить эмоцию

```json
{
  "type": "emotion",
  "emotion": "sad",
  "seq": 5
}
```

Сервер подберёт референс из `voice_refs[emotion]` или основной `voice_ref`.

### `config` — изменить параметры

```json
{
  "type": "config",
  "speed": 1.1,
  "language": "en",
  "seq": 6
}
```

## Сообщения от сервера к клиенту

### `status` — события жизненного цикла

```json
{
  "type": "status",
  "event": "session_ready",
  "seq": 1
}
```

Возможные `event`:

- `session_ready` — сессия инициализирована.
- `segment_started` — начался синтез сегмента.
- `segment_finished` — сегмент синтезирован и аудио-чанк отправлен клиенту.
- `finished` — все сегменты из буфера обработаны.
- `stopped` — вещание прервано командой `stop`.
- `error` — произошла ошибка.

### `audio` — аудио-чанк

```json
{
  "type": "audio",
  "format": "pcm_s16le",
  "sample_rate": 24000,
  "channels": 1,
  "data": "<base64 PCM16>",
  "seq": 7
}
```

### `error` — ошибка

```json
{
  "type": "error",
  "message": "Invalid message: ...",
  "seq": null
}
```

## Порядок сообщений в типичном сценарии

```
Agent -> Server: init
Server -> Agent: status: session_ready

Agent -> Server: text "Привет"
Agent -> Server: text ", как "
Agent -> Server: text "дела?"
Server -> Agent: status: segment_started
Server -> Agent: audio <chunk 1>
Server -> Agent: status: segment_finished

Agent -> Server: stop
Server -> Agent: status: stopped

Agent -> Server: text "Новая фраза"
...
```

> Важно: `segment_finished` отправляется **после** `audio`, чтобы клиент, ожидающий
> завершения сегмента, уже получил аудио-данные и мог продолжить воспроизведение.

## Примечания

- `seq` — клиентский счётчик, который сервер копирует в `status` и использует как
  идентификатор сегмента для `audio` (один `seq` на весь сегмент, не на каждый
  фрейм). Это упрощает сопоставление ответов с запросами.
