Files
challenge-admin-pl/docs/CHALLENGE_API_README.md
2025-12-09 12:25:29 +03:00

16 KiB
Raw Blame History

Challenge Service API Documentation

Сервис для проверки заданий в реальном времени через LLM (GigaChat).

Описание

Система позволяет:

  • Создавать задания с описанием в Markdown
  • Объединять задания в цепочки
  • Пользователям проходить задания и отправлять результаты
  • Автоматически проверять результаты через GigaChat с ограничением потоков
  • Отслеживать статистику по пользователям и системе

Конфигурация

В .env файле добавьте:

CHALLENGE_LLM_THREADS=2  # Количество одновременных проверок через LLM

API Endpoints

Все endpoints начинаются с префикса /api/challenge

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

POST /auth

Регистрация/авторизация пользователя по nickname.

Request:

{
  "nickname": "user123"
}

Валидация:

  • nickname: обязательное поле, от 3 до 50 символов

Response:

{
  "error": null,
  "data": {
    "ok": true,
    "userId": "507f1f77bcf86cd799439011"
  }
}

Управление заданиями

Важно: Все операции создания/обновления/удаления заданий требуют авторизации через Keycloak с ролью teacher или challenge-author.

POST /task

Создание нового задания (требует роль teacher или challenge-author).

Headers:

Authorization: Bearer <keycloak_token>

Request:

{
  "title": "Написать функцию сортировки",
  "description": "# Задание\n\nНапишите функцию сортировки массива чисел...",
  "hiddenInstructions": "Проверь сложность алгоритма. Должна быть O(n log n), не принимай bubble sort"
}

Валидация:

  • title: обязательное поле, максимум 255 символов
  • description: обязательное поле
  • hiddenInstructions: опциональное поле

Response:

{
  "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:

{
  "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:

{
  "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:

{
  "name": "Новое название цепочки",
  "taskIds": ["507f1f77bcf86cd799439011", "507f1f77bcf86cd799439012"]
}

Валидация:

  • name: опциональное поле, максимум 255 символов
  • taskIds: опциональное поле, массив строк

DELETE /chain/:chainId

Удаление цепочки (требует роль teacher или challenge-author).

Headers:

Authorization: Bearer <keycloak_token>

Отправка и проверка заданий

POST /submit

Отправка результата выполнения задания на проверку.

Request:

{
  "userId": "507f1f77bcf86cd799439011",
  "taskId": "507f1f77bcf86cd799439012",
  "result": "function sort(arr) { return arr.sort((a, b) => a - b); }"
}

Response:

{
  "error": null,
  "data": {
    "queueId": "550e8400-e29b-41d4-a716-446655440000",
    "submissionId": "507f1f77bcf86cd799439013"
  }
}

Валидация:

  • userId: обязательное поле
  • taskId: обязательное поле
  • result: обязательное поле

GET /check-status/:queueId

Polling endpoint для проверки статуса проверки.

Response (в процессе):

{
  "error": null,
  "data": {
    "status": "waiting",
    "position": 3
  }
}

Response (завершено):

{
  "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:

{
  "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:

{
  "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:

{
  "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:

{
  "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 должен быть уникальным в системе