# Upload API

## Обзор

API для загрузки аудиозаписей звонков и автоматического запуска их транскрибации и анализа с помощью ИИ.

После загрузки файл автоматически:

1. ✅ Загружается в облачное хранилище
2. ✅ Отправляется на транскрибацию
3. ✅ Анализируется с помощью LLM
4. ✅ Классифицируется по намерениям и сущностям

***

## Endpoint

```
POST /api/v1/speech-analytics/calls/upload/
```

***

## Аутентификация

Требуется токен авторизации в заголовке:

```http
Authorization: Bearer <your_token>
```

***

## Параметры запроса

### Content-Type

```
multipart/form-data
```

### Обязательные поля

| Параметр     | Тип     | Описание                                      |
| ------------ | ------- | --------------------------------------------- |
| `audio_file` | File    | Аудиофайл звонка                              |
| `project`    | Integer | ID проекта (для определения схемы метаданных) |
| `metadata`   | JSON    | Метаданные звонка в формате JSON              |

***

## Форматы аудио

Поддерживаемые форматы:

* ✅ MP3
* ✅ WAV
* ✅ OGG
* ✅ OPUS
* ✅ M4A
* ✅ FLAC

**Ограничения:**

* Максимальный размер файла: **20 МБ**
* Рекомендуемое качество: 16 kHz, 16-bit
* Для стерео: канал 0 = оператор (agent), канал 1 = клиент

***

## Структура метаданных

<details>

<summary>Базовая структура</summary>

```json
{
  "interaction_id": "call_123456",
  "channel": "voice",
  "timestamp": "2024-11-25T10:30:00Z",
  "agent": {
    "name": "Иван Петров",
    "id": "emp_001",
    "department": "Продажи"
  },
  "client": {
    "name": "ООО Рога и Копыта",
    "phone": "+79991234567",
    "email": "client@example.com"
  },
  "voice": {
    "recording_mode": "stereo",
    "provider": "zadarma",
    "stereo_mapping": {
      "0": "agent",
      "1": "client"
    }
  }
}
```

</details>

### Обязательные поля

* `interaction_id` (string) - Уникальный идентификатор звонка
* `channel` (string) - Канал коммуникации: `"voice"`, `"chat"`, `"email"` и т.д.
* `timestamp` (string ISO 8601) - Время звонка

<details>

<summary>Поля для голосовых звонков</summary>

Если `channel` = `"voice"`, обязательно добавьте секцию `voice`:

```json
{
  "voice": {
    "recording_mode": "stereo",
    "provider": "zadarma",
    "stereo_mapping": {
      "0": "agent",
      "1": "client"
    }
  }
}
```

</details>

<details>

<summary>Участники звонка</summary>

```json
{
  "agent": {
    "name": "Имя менеджера",
    "id": "emp_001",
    "department": "Продажи"
  },
  "client": {
    "name": "Имя клиента",
    "phone": "+79991234567",
    "email": "client@test.com"
  }
}
```

</details>

***

## Примеры запросов

<details>

<summary>cURL — Mono запись</summary>

```bash
curl -X POST "https://api.example.com/api/v1/speech-analytics/calls/upload/" \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -F "audio_file=@/path/to/call.mp3" \
  -F "project=42" \
  -F 'metadata={
    "interaction_id": "call_20241125_001",
    "channel": "voice",
    "timestamp": "2024-11-25T10:30:00Z",
    "agent": {
      "name": "Иван Петров",
      "id": "emp_123"
    },
    "client": {
      "name": "Мария Иванова",
      "phone": "+79991234567"
    },
    "voice": {
      "recording_mode": "mono",
      "provider": "mango"
    }
  }'
```

</details>

<details>

<summary>cURL — Stereo запись</summary>

```bash
curl -X POST "https://api.example.com/api/v1/speech-analytics/calls/upload/" \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -F "audio_file=@/path/to/call_stereo.wav" \
  -F "project=42" \
  -F 'metadata={
    "interaction_id": "call_20241125_002",
    "channel": "voice",
    "timestamp": "2024-11-25T11:00:00Z",
    "agent": {
      "name": "Петр Сидоров"
    },
    "client": {
      "name": "ООО Компания",
      "phone": "+74951234567"
    },
    "voice": {
      "recording_mode": "stereo",
      "provider": "zadarma",
      "stereo_mapping": {
        "0": "agent",
        "1": "client"
      }
    }
  }'
```

</details>

<details>

<summary>Python (requests)</summary>

```python
import requests
import json

url = "https://api.example.com/api/v1/speech-analytics/calls/upload/"
headers = {
    "Authorization": "Bearer YOUR_TOKEN"
}

metadata = {
    "interaction_id": "call_001",
    "channel": "voice",
    "timestamp": "2024-11-25T10:30:00Z",
    "agent": {
        "name": "Иван Петров",
        "id": "emp_123"
    },
    "client": {
        "name": "Клиент",
        "phone": "+79991234567"
    },
    "voice": {
        "recording_mode": "stereo",
        "provider": "zadarma",
        "stereo_mapping": {
            "0": "agent",
            "1": "client"
        }
    }
}

files = {
    'audio_file': open('/path/to/call.mp3', 'rb')
}

data = {
    'project': 42,
    'metadata': json.dumps(metadata)
}

response = requests.post(url, headers=headers, files=files, data=data)
print(response.json())
```

</details>

<details>

<summary>JavaScript (fetch)</summary>

```javascript
const formData = new FormData();
formData.append('audio_file', audioFile); // File object
formData.append('project', '42');
formData.append('metadata', JSON.stringify({
  interaction_id: 'call_001',
  channel: 'voice',
  timestamp: new Date().toISOString(),
  manager: {
    name: 'Иван Петров',
    id: 'emp_123'
  },
  client: {
    name: 'Клиент',
    phone: '+79991234567'
  },
  voice: {
    recording_mode: 'mono',
    provider: 'mango'
  }
}));

const response = await fetch('/api/v1/speech-analytics/calls/upload/', {
  method: 'POST',
  headers: {
    'Authorization': 'Bearer YOUR_TOKEN'
  },
  body: formData
});

const data = await response.json();
console.log(data);
```

</details>

***

## Ответ сервера

<details>

<summary>Успешная загрузка (200 OK)</summary>

```json
{
  "id": 12345,
  "audio_file_uuid": "550e8400-e29b-41d4-a716-446655440000",
  "audio_file_name": "call.mp3",
  "status": "uploaded",
  "metadata": {
    "interaction_id": "call_001",
    "channel": "voice",
    "timestamp": "2024-11-25T10:30:00Z",
    "agent": {
      "name": "Иван Петров"
    },
    "client": {
      "name": "Клиент",
      "phone": "+79991234567"
    },
    "voice": {
      "recording_mode": "stereo"
    }
  },
  "created_at": "2024-11-25T10:32:15.123Z",
  "message": "Call uploaded successfully. Transcription started."
}
```

</details>

### Поля ответа

| Поле              | Тип      | Описание                       |
| ----------------- | -------- | ------------------------------ |
| `id`              | Integer  | ID созданного звонка           |
| `audio_file_uuid` | UUID     | Уникальный идентификатор файла |
| `audio_file_name` | String   | Имя загруженного файла         |
| `status`          | String   | Статус обработки (см. ниже)    |
| `metadata`        | Object   | Сохраненные метаданные         |
| `created_at`      | DateTime | Время создания записи          |
| `message`         | String   | Информационное сообщение       |

***

## Статусы обработки

После загрузки звонок проходит через следующие статусы:

| Статус         | Описание                        | Время     |
| -------------- | ------------------------------- | --------- |
| `uploaded`     | ✅ Файл загружен в S3            | 1-5 сек   |
| `transcribing` | 🔄 Идет транскрибация аудио     | 1-10 мин  |
| `transcribed`  | ✅ Транскрибация завершена       | -         |
| `analyzing`    | 🔄 Идет анализ с помощью LLM    | 10-60 сек |
| `completed`    | ✅ Обработка завершена полностью | -         |
| `failed`       | ❌ Ошибка обработки              | -         |

**Опрос статуса:**

```
GET /api/v1/speech-analytics/calls/{id}/
```

***

## Ошибки

<details>

<summary>400 Bad Request — Неверный формат файла</summary>

```json
{
  "audio_file": [
    "Unsupported file format. Allowed: mp3, wav, ogg, opus, m4a, flac"
  ]
}
```

</details>

<details>

<summary>400 Bad Request — Файл слишком большой</summary>

```json
{
  "audio_file": [
    "File size exceeds maximum allowed size of 100 MB"
  ]
}
```

</details>

<details>

<summary>400 Bad Request — Неверные метаданные</summary>

```json
{
  "metadata": [
    "Missing required field: interaction_id"
  ]
}
```

</details>

<details>

<summary>400 Bad Request — Валидация по схеме</summary>

```json
{
  "metadata": [
    "Metadata validation failed: 'voice' is a required property"
  ]
}
```

</details>

<details>

<summary>400 Bad Request — Нет активной схемы</summary>

```json
{
  "metadata": [
    "No active metadata schema found for project 42"
  ]
}
```

</details>

<details>

<summary>403 Forbidden — Нет прав</summary>

```json
{
  "detail": "You do not have permission to perform this action."
}
```

</details>

<details>

<summary>404 Not Found — Проект не найден</summary>

```json
{
  "project": [
    "Project not found"
  ]
}
```

</details>

***

## Лучшие практики

<details>

<summary>1. Качество аудио</summary>

* ✅ Используйте минимум 8 kHz sampling rate (рекомендуется 16 kHz)
* ✅ 16-bit PCM для лучшего качества транскрибации
* ✅ Избегайте высокого сжатия MP3 (используйте bitrate ≥ 128 kbps)

</details>

<details>

<summary>2. Стерео vs Mono</summary>

* **Стерео** (2 канала): более точное разделение спикеров
  * Канал 0 = менеджер, Канал 1 = клиент
  * Обязательно укажите `stereo_mapping`
* **Mono** (1 канал): разделение по алгоритму speaker diarization
  * Менее точное, но работает без специальной настройки

</details>

<details>

<summary>3. Метаданные</summary>

* ✅ Заполняйте `interaction_id` уникальным значением для каждого звонка
* ✅ Указывайте имена участников для лучшей читаемости отчетов
* ✅ Добавляйте данные для связывания с CRM
* ✅ Используйте ISO 8601 формат для `timestamp`

</details>

<details>

<summary>4. Обработка ошибок</summary>

* ✅ Проверяйте размер файла до загрузки (≤ 20 МБ)
* ✅ Валидируйте формат файла на клиенте
* ✅ Обрабатывайте таймауты (загрузка может занять 10-30 сек)
* ✅ Сохраняйте `id` звонка для последующего опроса статуса

</details>

<details>

<summary>5. Производительность</summary>

* 🚀 Используйте асинхронную загрузку когда грузите много файлов
* 🚀 Сжимайте аудио перед загрузкой (MP3 128 kbps оптимально)
* 🚀 Не ждите завершения обработки — используйте webhooks или polling

</details>

***

## Получение результатов

### Опрос статуса

```bash
curl -X GET "https://api.example.com/api/v1/speech-analytics/calls/12345/" \
  -H "Authorization: Bearer YOUR_TOKEN"
```

<details>

<summary>Ответ со всеми данными</summary>

```json
{
  "id": 12345,
  "status": "completed",
  "audio_file_name": "call.mp3",
  "audio_duration_seconds": 180,
  "metadata": { ... },
  "transcript": {
    "full_text": "Менеджер: Здравствуйте...",
    "language": "ru-RU",
    "utterances": [
      {
        "speaker_role": "manager",
        "text": "Здравствуйте",
        "start_time_ms": 0,
        "end_time_ms": 500,
        "intent": "greeting",
        "confidence": 0.95
      }
    ]
  },
  "analysis": {
    "overall_quality": "good",
    "call_outcome": "sale_completed",
    "brief_summary": "Успешная продажа продукта X...",
    "communication_score": 8.5,
    "product_knowledge_score": 9.0,
    "objection_handling_score": 7.5
  },
  "created_at": "2024-11-25T10:32:15Z"
}
```

</details>
