# 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 ``` **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 ``` **Request:** ```json { "title": "Новый заголовок", "description": "Новое описание", "hiddenInstructions": "Обновленные инструкции для LLM" } ``` **Валидация:** - `title`: опциональное поле, максимум 255 символов - `description`: опциональное поле - `hiddenInstructions`: опциональное поле #### DELETE `/task/:taskId` Удаление задания (требует роль `teacher` или `challenge-author`). Также удаляется из всех цепочек. **Headers:** ``` Authorization: Bearer ``` ### Управление цепочками заданий **Важно:** Все операции создания/обновления/удаления цепочек требуют авторизации через 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 ``` **Request:** ```json { "name": "Новое название цепочки", "taskIds": ["507f1f77bcf86cd799439011", "507f1f77bcf86cd799439012"] } ``` **Валидация:** - `name`: опциональное поле, максимум 255 символов - `taskIds`: опциональное поле, массив строк #### DELETE `/chain/:chainId` Удаление цепочки (требует роль `teacher` или `challenge-author`). **Headers:** ``` Authorization: Bearer ``` ### Отправка и проверка заданий #### 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 должен быть уникальным в системе