fix api calls

This commit is contained in:
2025-12-09 12:25:29 +03:00
parent fd55d5a214
commit 2a08d9df35
10 changed files with 674 additions and 4196 deletions
+544
View File
@@ -0,0 +1,544 @@
# Challenge Service API Documentation
Сервис для проверки заданий в реальном времени через LLM (GigaChat).
## Описание
Система позволяет:
- Создавать задания с описанием в Markdown
- Объединять задания в цепочки
- Пользователям проходить задания и отправлять результаты
- Автоматически проверять результаты через GigaChat с ограничением потоков
- Отслеживать статистику по пользователям и системе
## Конфигурация
В `.env` файле добавьте:
```env
CHALLENGE_LLM_THREADS=2 # Количество одновременных проверок через LLM
```
## API Endpoints
Все endpoints начинаются с префикса `/api/challenge`
### Аутентификация
#### POST `/auth`
Регистрация/авторизация пользователя по nickname.
**Request:**
```json
{
"nickname": "user123"
}
```
**Валидация:**
- `nickname`: обязательное поле, от 3 до 50 символов
**Response:**
```json
{
"error": null,
"data": {
"ok": true,
"userId": "507f1f77bcf86cd799439011"
}
}
```
### Управление заданиями
**Важно:** Все операции создания/обновления/удаления заданий требуют авторизации через Keycloak с ролью `teacher` или `challenge-author`.
#### POST `/task`
Создание нового задания (требует роль `teacher` или `challenge-author`).
**Headers:**
```
Authorization: Bearer <keycloak_token>
```
**Request:**
```json
{
"title": "Написать функцию сортировки",
"description": "# Задание\n\nНапишите функцию сортировки массива чисел...",
"hiddenInstructions": "Проверь сложность алгоритма. Должна быть O(n log n), не принимай bubble sort"
}
```
**Валидация:**
- `title`: обязательное поле, максимум 255 символов
- `description`: обязательное поле
- `hiddenInstructions`: опциональное поле
**Response:**
```json
{
"error": null,
"data": {
"_id": "507f1f77bcf86cd799439011",
"title": "Написать функцию сортировки",
"description": "# Задание\n\n...",
"hiddenInstructions": "Проверь сложность алгоритма...",
"creator": { "sub": "...", "preferred_username": "teacher1" },
"createdAt": "2023-10-29T12:00:00.000Z",
"updatedAt": "2023-10-29T12:00:00.000Z"
}
}
```
**Примечание:** Поле `hiddenInstructions` опционально. Эти инструкции будут переданы в LLM при проверке, но скрыты от студентов.
#### GET `/task/:taskId`
Получение задания по ID.
**Важно:** Поля `hiddenInstructions` и `creator` возвращаются только для пользователей с ролью `teacher` или `challenge-author`. Обычные студенты их не увидят.
#### GET `/tasks`
Получение всех заданий.
**Примечание:** Для не-преподавателей поля `hiddenInstructions` и `creator` скрыты.
#### PUT `/task/:taskId`
Обновление задания (требует роль `teacher` или `challenge-author`).
**Headers:**
```
Authorization: Bearer <keycloak_token>
```
**Request:**
```json
{
"title": "Новый заголовок",
"description": "Новое описание",
"hiddenInstructions": "Обновленные инструкции для LLM"
}
```
**Валидация:**
- `title`: опциональное поле, максимум 255 символов
- `description`: опциональное поле
- `hiddenInstructions`: опциональное поле
#### DELETE `/task/:taskId`
Удаление задания (требует роль `teacher` или `challenge-author`). Также удаляется из всех цепочек.
**Headers:**
```
Authorization: Bearer <keycloak_token>
```
### Управление цепочками заданий
**Важно:** Все операции создания/обновления/удаления цепочек требуют авторизации через Keycloak с ролью `teacher` или `challenge-author`.
#### POST `/chain`
Создание цепочки заданий (требует роль `teacher` или `challenge-author`).
**Request:**
```json
{
"name": "Основы программирования",
"taskIds": ["507f1f77bcf86cd799439011", "507f1f77bcf86cd799439012"]
}
```
**Валидация:**
- `name`: обязательное поле, максимум 255 символов
- `taskIds`: обязательное поле, массив строк
#### GET `/chains`
Получение всех цепочек с заданиями.
**Примечание:** Для не-преподавателей поля `hiddenInstructions` и `creator` скрыты в заданиях внутри цепочек.
#### GET `/chain/:chainId`
Получение цепочки по ID.
**Примечание:** Для не-преподавателей поля `hiddenInstructions` и `creator` скрыты в заданиях внутри цепочки.
#### PUT `/chain/:chainId`
Обновление цепочки (требует роль `teacher` или `challenge-author`).
**Headers:**
```
Authorization: Bearer <keycloak_token>
```
**Request:**
```json
{
"name": "Новое название цепочки",
"taskIds": ["507f1f77bcf86cd799439011", "507f1f77bcf86cd799439012"]
}
```
**Валидация:**
- `name`: опциональное поле, максимум 255 символов
- `taskIds`: опциональное поле, массив строк
#### DELETE `/chain/:chainId`
Удаление цепочки (требует роль `teacher` или `challenge-author`).
**Headers:**
```
Authorization: Bearer <keycloak_token>
```
### Отправка и проверка заданий
#### POST `/submit`
Отправка результата выполнения задания на проверку.
**Request:**
```json
{
"userId": "507f1f77bcf86cd799439011",
"taskId": "507f1f77bcf86cd799439012",
"result": "function sort(arr) { return arr.sort((a, b) => a - b); }"
}
```
**Response:**
```json
{
"error": null,
"data": {
"queueId": "550e8400-e29b-41d4-a716-446655440000",
"submissionId": "507f1f77bcf86cd799439013"
}
}
```
**Валидация:**
- `userId`: обязательное поле
- `taskId`: обязательное поле
- `result`: обязательное поле
#### GET `/check-status/:queueId`
Polling endpoint для проверки статуса проверки.
**Response (в процессе):**
```json
{
"error": null,
"data": {
"status": "waiting",
"position": 3
}
}
```
**Response (завершено):**
```json
{
"error": null,
"data": {
"status": "completed",
"submission": {
"_id": "507f1f77bcf86cd799439013",
"user": {...},
"task": {...},
"result": "...",
"status": "accepted",
"feedback": "Отлично! Задание выполнено правильно.",
"attemptNumber": 1,
"submittedAt": "2023-10-29T12:00:00.000Z",
"checkedAt": "2023-10-29T12:00:05.000Z"
}
}
}
```
Статусы проверки:
- `waiting` - ожидает в очереди
- `in_progress` - проверяется
- `completed` - проверка завершена
- `error` - ошибка при проверке
- `not_found` - не найдено
Статусы submission:
- `pending` - ожидает проверки
- `in_progress` - проверяется
- `accepted` - принято
- `needs_revision` - требует доработки
### Просмотр попыток
#### GET `/user/:userId/submissions?taskId=...`
Получение всех попыток пользователя (опционально для конкретного задания).
**Response:**
```json
{
"error": null,
"data": [
{
"_id": "507f1f77bcf86cd799439013",
"task": {...},
"result": "...",
"status": "accepted",
"feedback": "...",
"attemptNumber": 2,
"submittedAt": "2023-10-29T12:00:00.000Z",
"checkedAt": "2023-10-29T12:00:05.000Z"
}
]
}
```
### Статистика
#### GET `/user/:userId/stats`
Детальная статистика пользователя.
**Response:**
```json
{
"error": null,
"data": {
"totalTasksAttempted": 5,
"completedTasks": 3,
"inProgressTasks": 1,
"needsRevisionTasks": 1,
"totalSubmissions": 8,
"averageCheckTimeMs": 5234,
"taskStats": [
{
"taskId": "507f1f77bcf86cd799439012",
"taskTitle": "Написать функцию сортировки",
"attempts": [...],
"totalAttempts": 2,
"status": "completed",
"lastAttemptAt": "2023-10-29T12:00:00.000Z"
}
],
"chainStats": [
{
"chainId": "507f1f77bcf86cd799439014",
"chainName": "Основы программирования",
"totalTasks": 5,
"completedTasks": 3,
"progress": 60
}
]
}
}
```
**Примечание:** Статусы заданий в `taskStats.status`:
- `not_attempted` - задание не начато
- `pending` - ожидает проверки
- `in_progress` - проверяется
- `needs_revision` - требует доработки
- `completed` - выполнено
#### GET `/stats`
Общая статистика системы.
**Response:**
```json
{
"error": null,
"data": {
"users": 150,
"tasks": 25,
"chains": 5,
"submissions": {
"total": 850,
"accepted": 420,
"rejected": 380,
"pending": 30,
"inProgress": 20
},
"averageCheckTimeMs": 5500,
"queue": {
"queueLength": 12,
"waiting": 10,
"inProgress": 2,
"maxConcurrency": 2,
"currentlyProcessing": 2
}
}
}
```
#### GET `/stats/v2`
Расширенная статистика системы с детальными данными для таблиц и прогресс-баров.
**Query параметры:**
- `chainId` (опционально): фильтрация статистики по конкретной цепочке
**Response:**
```json
{
"error": null,
"data": {
"users": 150,
"tasks": 25,
"chains": 5,
"submissions": {
"total": 850,
"accepted": 420,
"rejected": 380,
"pending": 30,
"inProgress": 20
},
"averageCheckTimeMs": 5500,
"queue": {
"queueLength": 12,
"waiting": 10,
"inProgress": 2,
"maxConcurrency": 2,
"currentlyProcessing": 2
},
"tasksTable": [
{
"taskId": "507f1f77bcf86cd799439012",
"title": "Написать функцию сортировки",
"totalAttempts": 45,
"uniqueUsers": 20,
"acceptedCount": 18,
"successRate": 90,
"averageAttemptsToSuccess": 1.5
}
],
"activeParticipants": [
{
"userId": "507f1f77bcf86cd799439011",
"nickname": "user123",
"totalSubmissions": 10,
"completedTasks": 5,
"chainProgress": [
{
"chainId": "507f1f77bcf86cd799439014",
"chainName": "Основы программирования",
"totalTasks": 10,
"completedTasks": 5,
"progressPercent": 50
}
]
}
],
"chainsDetailed": [
{
"chainId": "507f1f77bcf86cd799439014",
"name": "Основы программирования",
"totalTasks": 10,
"tasks": [
{
"taskId": "507f1f77bcf86cd799439012",
"title": "Написать функцию сортировки",
"description": "# Задание\n\n..."
}
],
"participantProgress": [
{
"userId": "507f1f77bcf86cd799439011",
"nickname": "user123",
"taskProgress": [
{
"taskId": "507f1f77bcf86cd799439012",
"taskTitle": "Написать функцию сортировки",
"status": "completed"
}
],
"completedCount": 5,
"progressPercent": 50
}
]
}
]
}
}
```
**Примечание:** Статусы задач в `taskProgress`:
- `not_started` - задание не начато
- `pending` - ожидает проверки
- `in_progress` - проверяется
- `needs_revision` - требует доработки
- `completed` - выполнено
## Архитектура
### Модели данных
- **ChallengeUser** - пользователи сервиса
- **ChallengeTask** - отдельные задания
- **ChallengeChain** - цепочки заданий
- **ChallengeSubmission** - результаты выполнения заданий
### Сервисы
- **challenge-checker.ts** - проверка заданий через GigaChat
- **ChallengeCheckQueue.ts** - управление очередью проверок
- **challengeQueueInstance.ts** - singleton экземпляр очереди
### Очередь проверки
Очередь работает in-memory с ограничением на количество одновременных проверок.
- Элементы добавляются в очередь при отправке задания
- Обработка происходит автоматически каждую секунду
- Количество параллельных проверок ограничено `CHALLENGE_LLM_THREADS`
- После завершения проверки результаты сохраняются в БД
- Записи в очереди удаляются через 5 минут после завершения
### Проверка через LLM
Для проверки используется GigaChat с промптом, который:
- Получает описание задания
- Получает результат пользователя
- Возвращает статус (ПРИНЯТО/ДОРАБОТКА) и feedback
Ответ парсится и сохраняется в submission.
## Примеры использования
### Типичный flow пользователя
1. Авторизация: `POST /api/challenge/auth`
2. Получение цепочек: `GET /api/challenge/chains`
3. Получение заданий цепочки: `GET /api/challenge/chain/:chainId`
4. Отправка результата: `POST /api/challenge/submit`
5. Polling проверки: `GET /api/challenge/check-status/:queueId` (каждые 2-3 секунды)
6. Просмотр статистики: `GET /api/challenge/user/:userId/stats`
### Типичный flow администратора
1. Создание заданий: `POST /api/challenge/task`
2. Создание цепочки: `POST /api/challenge/chain`
3. Просмотр общей статистики: `GET /api/challenge/stats`
4. Просмотр расширенной статистики: `GET /api/challenge/stats/v2` (опционально с `?chainId=...`)
## Ограничения и особенности
- Очередь работает in-memory, при перезапуске сервера незавершенные проверки вернутся в статус `pending`
- История всех попыток сохраняется полностью
- Markdown описания заданий хранятся как простой текст в БД
- Аутентификация простая, без паролей (только nickname)
- Nickname должен быть уникальным в системе