# API

Базовый URL: `http://<host>/api/v1`

Авторизация: `Authorization: Bearer <api_key>`

> Получить API-ключ можно при регистрации через UI (отображается в настройках профиля).

---

## 1. Поиск по базе знаний

`POST /api/v1/search`

Поиск релевантных тикетов в коллекции с опциональной генерацией ответа.

### Request body

```json
{
  "collection_id": "83f64913-9cb6-495e-984a-cbae31eacf54",
  "query": "не могу начать смену в приложении",
  "generate_answer": true
}
```

| Поле             | Тип     | По умолчанию | Описание                          |
|------------------|---------|--------------|------------------------------------|
| `collection_id`  | string  | —            | ID коллекции                       |
| `query`          | string  | —            | Поисковый запрос                   |
| `generate_answer`| boolean | `true`       | Сгенерировать ответ LLM или нет    |

### Response

```json
{
  "answer": "Проверьте подключение к интернету...",
  "sources": [
    {
      "ticket_id": "12345",
      "score": 0.5885,
      "category": "техподдержка",
      "full_text": "Категория: техподдержка\nПроблема: ...\nРешение: ..."
    }
  ]
}
```

| Поле     | Тип                        | Описание                              |
|----------|----------------------------|----------------------------------------|
| `answer` | string или null            | Сгенерированный ответ LLM или null     |
| `sources`| array of SourceItem        | Найденные тикеты (от 0 до 5)          |

### SourceItem

| Поле       | Тип     | Описание                    |
|------------|---------|-----------------------------|
| `ticket_id`| string  | ID тикета                   |
| `score`    | float   | Релевантность (0..1)        |
| `category` | string  | Категория тикета            |
| `full_text`| string  | Полный текст решения        |

### Пример (curl)

```bash
curl -s -X POST http://sovet.itoservice.ru/api/v1/search \
  -H "Authorization: Bearer <api-key>" \
  -H "Content-Type: application/json" \
  -d '{
    "collection_id": "83f64913-9cb6-495e-984a-cbae31eacf54",
    "query": "не могу начать смену в приложении",
    "generate_answer": true
  }'
```

### Пример (Python)

```python
import requests

resp = requests.post(
    "http://sovet.itoservice.ru/api/v1/search",
    headers={"Authorization": "Bearer <api-key>"},
    json={
        "collection_id": "83f64913-9cb6-495e-984a-cbae31eacf54",
        "query": "не могу начать смену в приложении",
        "generate_answer": True,
    },
)
data = resp.json()
print(data["answer"])
```

---

## 2. Загрузка тикетов

`POST /api/v1/tickets?collection_id=<uuid>`

Загрузка массива тикетов в коллекцию. Дубликаты по `ticket_id` автоматически пропускаются.

### Request body

```json
{
  "tickets": [
    {
      "ticket_id": 12345,
      "category": "техподдержка",
      "description": "Не работает принтер",
      "client": "Иван",
      "messages": [
        {"role": "user", "text": "У меня не печатает принтер"},
        {"role": "assistant", "text": "Проверьте подключение кабеля USB к компьютеру..."}
      ]
    }
  ]
}
```

| Поле          | Тип                  | По умолчанию | Описание                    |
|---------------|----------------------|--------------|-----------------------------|
| `ticket_id`   | int или string       | —            | Уникальный ID тикета         |
| `category`    | string               | `""`         | Категория проблемы          |
| `description` | string               | `""`         | Краткое описание проблемы   |
| `client`      | string               | `""`         | Имя клиента                 |
| `messages`    | array of Message     | `[]`         | Переписка (user + assistant)|

### Message

```json
{"role": "user|assistant", "text": "текст сообщения"}
```

- `role` — `"user"` или `"assistant"`. Сообщения `"assistant"` используются как решение.
- `text` — текст сообщения. Решение должно быть длиннее 100 символов и не заканчиваться вопросительным знаком.

### Response

```json
{
  "processed": 1,
  "skipped": 0,
  "collection_ticket_count": 100
}
```

| Поле                   | Тип   | Описание                          |
|------------------------|-------|-----------------------------------|
| `processed`            | int   | Количество обработанных тикетов   |
| `skipped`              | int   | Пропущено (дубликат или нет решения)|
| `collection_ticket_count`| int | Всего тикетов в коллекции         |

### Почему тикет может быть пропущен (skipped)

1. **Дубликат** — `ticket_id` уже существует в коллекции
2. **Нет решения** — в `messages` нет сообщения от `assistant` длиннее 100 символов
3. **Решение — вопрос** — последнее сообщение `assistant` заканчивается на `?` и короче 150 символов
4. **Решение — заглушка** — текст содержит фразы вроде «уточните», «приложите скрин», «позвоните нам» и т.п.

### Пример (curl)

```bash
curl -s -X POST 'http://sovet.itoservice.ru/api/v1/tickets?collection_id=<uuid>' \
  -H "Authorization: Bearer <api-key>" \
  -H "Content-Type: application/json" \
  -d '{"tickets": [{"ticket_id": 1, "description": "Проблема", "messages": [{"role": "user", "text": "вопрос"}, {"role": "assistant", "text": "Проверьте соединение и перезагрузите устройство, затем повторите попытку. Если не помогает – обратитесь к администратору системы."}]}]}'
```

---

## 3. Список коллекций

`GET /api/v1/collections`

### Response

```json
[
  {
    "id": "83f64913-9cb6-495e-984a-cbae31eacf54",
    "user_id": "30939310-e3a3-4993-b1ac-10c81b751843",
    "name": "OFRS",
    "ticket_count": 2898,
    "created_at": "2026-06-29T11:45:27.866413Z"
  }
]
```

---

## 4. Профиль пользователя

`GET /api/v1/me`

### Response

```json
{
  "id": "30939310-e3a3-4993-b1ac-10c81b751843",
  "email": "user@example.com",
  "api_key": "abc123...",
  "plan": "pro",
  "created_at": "2026-06-29T11:45:27.866413Z"
}
```

---

## 5. Сброс API-ключа

`PATCH /api/v1/me/api-key`

### Response

```json
{
  "api_key": "новый-ключ-32-символа"
}
```

---

## 6. JWT-эндпоинты (для UI)

Эти эндпоинты требуют JWT-токен (получается через `/api/login`), а не API-ключ.

| Метод | Путь                          | Описание                     |
|-------|-------------------------------|------------------------------|
| POST  | `/api/register`               | Регистрация                  |
| POST  | `/api/login`                  | Вход (возвращает JWT)        |
| GET   | `/api/me`                     | Профиль                      |
| POST  | `/api/collections`            | Создать коллекцию            |
| GET   | `/api/collections`            | Список коллекций             |
| GET   | `/api/collections/{id}`       | Информация о коллекции       |
| DELETE| `/api/collections/{id}`       | Удалить коллекцию            |
| POST  | `/api/collections/{id}/tickets`       | Загрузить тикеты (JSON)|
| POST  | `/api/collections/{id}/tickets/upload`| Загрузить тикеты (файл)|
| POST  | `/api/search`                 | Поиск                        |

### Регистрация

```json
POST /api/register
{
  "email": "user@example.com",
  "password": "secret123",
  "plan": "free"
}
→ 201 {"access_token": "jwt...", "token_type": "bearer"}
```

### Логин

```json
POST /api/login
{
  "email": "user@example.com",
  "password": "secret123"
}
→ {"access_token": "jwt...", "token_type": "bearer"}
```

---

## 7. Ошибки

Все ошибки возвращают стандартный формат FastAPI:

```json
{
  "detail": "Collection not found"
}
```

HTTP-статусы:
- `200` — успех
- `201` — создано
- `400` — неверный запрос
- `401` — неавторизован
- `404` — не найдено
- `429` — превышен лимит запросов (30 req/min)
- `500` — внутренняя ошибка сервера

---

## 8. Rate limiting

30 запросов в минуту на один IP-адрес. При превышении — `429 Too Many Requests`.

Лимит действует на все эндпоинты, кроме `/api/health`.

---

## 9. Health check

`GET /api/health`

```json
{"status": "ok"}
```

Не требует авторизации.
