# План развития Brief Bench FastAPI **Дата создания:** 17 декабря 2025 **Статус:** Backend готов, требуется frontend интеграция и тестирование --- ## Текущее состояние ### Готово (Backend) - Структура FastAPI приложения - JWT авторизация (8-значный логин) - TgBackendInterface (полная реализация с httpx) - DBApiClient (интеграция с DB API) - RagService (интеграция с RAG backends, mTLS) - API endpoints: - `POST /api/v1/auth/login` - авторизация - `GET/PUT /api/v1/settings` - настройки пользователя - `POST /api/v1/query/bench` - batch запросы - `POST /api/v1/query/backend` - последовательные запросы - `POST/GET/DELETE /api/v1/analysis/sessions` - сессии анализа - Docker setup (Dockerfile, docker-compose.yml) - Документация (README.md, DB_API_CONTRACT.md) ### Требуется доделать - Frontend файлы (перенос из rag-bench-old-version) - API client для frontend - Интеграция frontend с новым API - Тестирование - Production deployment конфигурация --- ## Этап 1: Подготовка Frontend ### 1.1 Перенос статических файлов из rag-bench-old-version **Старая архитектура:** - Статический SPA напрямую делал fetch запросы к RAG backends - Настройки хранились в localStorage (отдельно для каждого окружения) - Встроенная поддержка 3 окружений (IFT, PSI, PROD) - Два режима: Bench (batch) и Backend (sequential) - Аннотации, экспорт/импорт, детальный UI для анализа ответов **Файлы для переноса:** ```bash # Скопировать из rag-bench-old-version/ в static/ static/ ├── index.html # Основная страница (24KB, 495 строк) ├── styles.css # Material Design стили (23KB) ├── app.js # Основная логика (61KB, ~1500+ строк) ├── settings.js # Дефолтные настройки (3KB) └── api-client.js # НОВЫЙ ФАЙЛ - будет создан ``` **Ключевые изменения в архитектуре:** 1. **Было**: `Browser → RAG Backend` (прямой fetch) 2. **Стало**: `Browser → FastAPI → RAG Backend` 3. **Было**: Настройки в localStorage 4. **Стало**: Настройки в DB API (персональные для каждого пользователя) 5. **Добавлено**: JWT авторизация с 8-значным логином ### 1.2 Создать API client для frontend **Файл:** `static/api-client.js` **Полная реализация:** ```javascript /** * Brief Bench API Client * Взаимодействие с FastAPI backend */ class BriefBenchAPI { constructor() { this.baseURL = '/api/v1' } // ============================================ // Internal Helpers // ============================================ _getToken() { return localStorage.getItem('access_token') } _setToken(token) { localStorage.setItem('access_token', token) } _clearToken() { localStorage.removeItem('access_token') } _getHeaders(includeAuth = true) { const headers = { 'Content-Type': 'application/json' } if (includeAuth) { const token = this._getToken() if (token) { headers['Authorization'] = `Bearer ${token}` } } return headers } async _handleResponse(response) { // Handle 401 Unauthorized if (response.status === 401) { this._clearToken() throw new Error('Сессия истекла. Пожалуйста, войдите снова.') } // Handle 502 Bad Gateway (RAG backend error) if (response.status === 502) { throw new Error('RAG backend недоступен или вернул ошибку') } // Handle other errors if (!response.ok) { const errorData = await response.json().catch(() => ({})) throw new Error(errorData.detail || `HTTP ${response.status}: ${response.statusText}`) } // Handle 204 No Content if (response.status === 204) { return null } return await response.json() } async _request(endpoint, options = {}) { const url = `${this.baseURL}${endpoint}` try { const response = await fetch(url, options) return await this._handleResponse(response) } catch (error) { console.error(`API request failed: ${endpoint}`, error) throw error } } // ============================================ // Auth API // ============================================ /** * Авторизация с 8-значным логином * @param {string} login - 8-значный логин * @returns {Promise<{access_token: string, user: object}>} */ async login(login) { const response = await this._request(`/auth/login?login=${login}`, { method: 'POST', headers: this._getHeaders(false) }) // Сохранить токен this._setToken(response.access_token) return response } /** * Выход (очистка токена) */ logout() { this._clearToken() window.location.reload() } /** * Проверка авторизации * @returns {boolean} */ isAuthenticated() { return !!this._getToken() } // ============================================ // Settings API // ============================================ /** * Получить настройки пользователя для всех окружений * @returns {Promise<{user_id: string, settings: object, updated_at: string}>} */ async getSettings() { return await this._request('/settings', { method: 'GET', headers: this._getHeaders() }) } /** * Обновить настройки пользователя * @param {object} settings - Объект с настройками для окружений * @returns {Promise<{user_id: string, settings: object, updated_at: string}>} */ async updateSettings(settings) { return await this._request('/settings', { method: 'PUT', headers: this._getHeaders(), body: JSON.stringify({ settings }) }) } // ============================================ // Query API // ============================================ /** * Отправить batch запрос (Bench mode) * @param {string} environment - Окружение (ift/psi/prod) * @param {Array<{body: string, with_docs: boolean}>} questions - Массив вопросов * @returns {Promise<{request_id: string, timestamp: string, environment: string, response: object}>} */ async benchQuery(environment, questions) { return await this._request('/query/bench', { method: 'POST', headers: this._getHeaders(), body: JSON.stringify({ environment, questions }) }) } /** * Отправить последовательные запросы (Backend mode) * @param {string} environment - Окружение (ift/psi/prod) * @param {Array<{body: string, with_docs: boolean}>} questions - Массив вопросов * @param {boolean} resetSession - Сбрасывать ли сессию после каждого вопроса * @returns {Promise<{request_id: string, timestamp: string, environment: string, response: object}>} */ async backendQuery(environment, questions, resetSession = true) { return await this._request('/query/backend', { method: 'POST', headers: this._getHeaders(), body: JSON.stringify({ environment, questions, reset_session: resetSession }) }) } // ============================================ // Analysis Sessions API // ============================================ /** * Сохранить сессию анализа * @param {object} sessionData - Данные сессии * @returns {Promise} */ async saveSession(sessionData) { return await this._request('/analysis/sessions', { method: 'POST', headers: this._getHeaders(), body: JSON.stringify(sessionData) }) } /** * Получить список сессий * @param {string|null} environment - Фильтр по окружению (опционально) * @param {number} limit - Лимит результатов * @param {number} offset - Смещение для пагинации * @returns {Promise<{sessions: Array, total: number}>} */ async getSessions(environment = null, limit = 50, offset = 0) { const params = new URLSearchParams({ limit, offset }) if (environment) { params.append('environment', environment) } return await this._request(`/analysis/sessions?${params}`, { method: 'GET', headers: this._getHeaders() }) } /** * Получить конкретную сессию * @param {string} sessionId - ID сессии * @returns {Promise} */ async getSession(sessionId) { return await this._request(`/analysis/sessions/${sessionId}`, { method: 'GET', headers: this._getHeaders() }) } /** * Удалить сессию * @param {string} sessionId - ID сессии * @returns {Promise} */ async deleteSession(sessionId) { return await this._request(`/analysis/sessions/${sessionId}`, { method: 'DELETE', headers: this._getHeaders() }) } } // Export API client instance const api = new BriefBenchAPI() ``` **Особенности реализации:** - Автоматическое добавление `Authorization: Bearer {token}` из localStorage - Обработка всех HTTP ошибок (401 → logout + reload, 502 → RAG error) - Singleton instance (`const api = new BriefBenchAPI()`) - Методы соответствуют FastAPI endpoints один-к-одному --- ## Этап 2: Адаптация Frontend под новую архитектуру ### 2.1 Добавить Login Screen **Изменения в `index.html`:** ```html

Brief Bench

``` **Логика в `app.js`:** - При загрузке проверить наличие токена в localStorage - Если нет → показать login screen - Если есть → валидировать токен (попробовать загрузить настройки) - Если токен невалидный → показать login screen ### 2.2 Переписать вызовы API **Старый код (прямые fetch):** ```javascript // Было const response = await fetch('https://rag-backend/api/bench', { method: 'POST', headers: { ... }, body: JSON.stringify(questions) }) ``` **Новый код (через API client):** ```javascript // Стало const api = new BriefBenchAPI() const response = await api.benchQuery('ift', questions) ``` **Что нужно переписать:** - Загрузка настроек пользователя (при старте приложения) - Отправка bench/backend запросов - Сохранение сессий анализа - Загрузка истории сессий - Экспорт данных ### 2.3 Управление Settings через UI **Новая логика:** 1. При загрузке приложения: `GET /api/v1/settings` → загрузить настройки для всех 3 окружений 2. Отобразить в UI (вкладки для IFT/PSI/PROD) 3. При изменении: `PUT /api/v1/settings` → сохранить в DB API **Поля настроек (для каждого окружения):** - API Mode: bench / backend (radio buttons) - Bearer Token (input, password type) - System Platform (input) - System Platform User (input) - Platform User ID (input) - Platform ID (input) - With Classify (checkbox, только для backend mode) - Reset Session Mode (checkbox, только для backend mode) ### 2.4 Интеграция History (сессии анализа) **Функционал:** 1. После выполнения анализа → кнопка "Сохранить сессию" 2. При сохранении: `POST /api/v1/analysis/sessions` - Отправить environment, api_mode, request, response, annotations 3. Вкладка "История": - Загрузить список: `GET /api/v1/analysis/sessions?environment=ift` - Отобразить в виде таблицы (дата, окружение, режим, кол-во вопросов) - При клике → загрузить полную сессию: `GET /api/v1/analysis/sessions/{id}` - Кнопка удаления: `DELETE /api/v1/analysis/sessions/{id}` --- ## Этап 3: Environment Selector ### 3.1 UI для выбора окружения **Добавить в `index.html`:** ```html
``` **Логика:** - При выборе окружения → загрузить настройки для этого окружения - Отобразить текущий apiMode (bench/backend) - При отправке запроса → использовать выбранное окружение ### 3.2 Валидация перед отправкой **Проверки:** 1. Выбрано окружение 2. Настройки для окружения существуют 3. apiMode соответствует выбранному режиму (bench/backend) 4. Если требуется bearerToken → он заполнен **Показывать ошибки:** - "Настройки для окружения ИФТ не найдены. Перейдите в Settings." - "Окружение ПСИ настроено на Backend mode, но вы выбрали Bench режим." --- ## Этап 4: Тестирование ### 4.1 Ручное тестирование **Сценарии:** 1. **Авторизация:** - Успешный вход (8 цифр) - Ошибка (невалидный логин) - Истечение токена (через 30 дней или вручную испортить токен) 2. **Настройки:** - Загрузка настроек для всех окружений - Изменение настроек для одного окружения - Сохранение → перезагрузка страницы → настройки сохранились 3. **Bench Query:** - Выбрать IFT, bench mode - Отправить несколько вопросов - Проверить что ответ отображается корректно - Проверить headers в Network tab (Request-Id, System-Id, Bearer token) 4. **Backend Query:** - Выбрать PSI, backend mode - Отправить вопросы последовательно - Проверить что между вопросами происходит reset session 5. **Сессии анализа:** - Сохранить сессию после анализа - Открыть историю → найти сессию - Загрузить сессию → проверить что данные корректны - Удалить сессию 6. **Ошибки:** - DB API недоступен → показать ошибку - RAG backend недоступен → показать ошибку 502 - Невалидный токен → редирект на login ### 4.2 Автоматическое тестирование (опционально) **Backend tests (pytest):** ```bash tests/ ├── test_auth.py # Тесты авторизации ├── test_settings.py # Тесты settings endpoints ├── test_query.py # Тесты query endpoints ├── test_analysis.py # Тесты analysis endpoints └── conftest.py # Fixtures (mock DB API, mock RAG) ``` **Установка:** ```bash pip install pytest pytest-asyncio httpx ``` **Запуск:** ```bash pytest tests/ -v ``` --- ## Этап 5: Production Deployment ### 5.1 Настройка .env для production **Критичные изменения:** ```bash # Application DEBUG=false # JWT (сгенерировать новый секретный ключ!) JWT_SECRET_KEY=<сгенерированный-секрет-256-бит> # DB API (production URL) DB_API_URL=https://db-api.production.com/api/v1 # RAG Backends (production hosts) IFT_RAG_HOST=ift-rag.production.com PSI_RAG_HOST=psi-rag.production.com PROD_RAG_HOST=prod-rag.production.com # mTLS сертификаты (положить в certs/) IFT_RAG_CERT_CA=/app/certs/ift/ca.crt IFT_RAG_CERT_KEY=/app/certs/ift/client.key IFT_RAG_CERT_CERT=/app/certs/ift/client.crt # (аналогично для PSI и PROD) ``` ### 5.2 CORS configuration **Изменить `app/main.py`:** ```python # CORS middleware app.add_middleware( CORSMiddleware, allow_origins=[ "https://brief-bench.production.com", # Production domain "http://localhost:8000" # Для локальной разработки ], allow_credentials=True, allow_methods=["GET", "POST", "PUT", "DELETE"], allow_headers=["Authorization", "Content-Type"], ) ``` ### 5.3 Размещение mTLS сертификатов ```bash certs/ ├── ift/ │ ├── ca.crt │ ├── client.key │ ├── client.crt ├── psi/ │ ├── ca.crt │ ├── client.key │ ├── client.crt └── prod/ ├── ca.crt ├── client.key └── client.crt ``` **Важно:** Убедиться что права доступа `600` или `400` (только чтение владельцем) ### 5.4 Docker deployment **Запуск:** ```bash # Создать .env cp .env.example .env nano .env # Заполнить production значения # Разместить сертификаты mkdir -p certs/ift certs/psi certs/prod # Скопировать сертификаты в соответствующие папки # Запустить docker-compose up -d # Проверить логи docker-compose logs -f fastapi # Проверить здоровье curl http://localhost:8000/health ``` ### 5.5 Logging и Monitoring **Добавить middleware для логирования (опционально):** ```python # app/middleware/logging_middleware.py import logging import time from fastapi import Request logger = logging.getLogger(__name__) async def log_requests(request: Request, call_next): start_time = time.time() logger.info(f"Request: {request.method} {request.url}") response = await call_next(request) process_time = time.time() - start_time logger.info( f"Response: {response.status_code} " f"({process_time:.2f}s)" ) return response ``` **Зарегистрировать в `app/main.py`:** ```python from app.middleware.logging_middleware import log_requests app.middleware("http")(log_requests) ``` --- ## Этап 6: Дополнительные улучшения (опционально) ### 6.1 Rate Limiting - Ограничить количество запросов от одного пользователя - Использовать `slowapi` или redis-based rate limiter ### 6.2 WebSocket для real-time updates - Показывать прогресс для backend mode (вопрос N из M) - Использовать FastAPI WebSocket ### 6.3 Export функционал - CSV экспорт сессий анализа - JSON экспорт для интеграции с другими системами ### 6.4 Advanced Analytics - Dashboard с метриками (кол-во запросов, успешность, время ответа) - Графики по окружениям --- ## Приоритезация задач ### Критично (сделать в первую очередь) 1. Перенос статических файлов из rag-bench-old-version → `static/` 2. Создание `api-client.js` 3. Добавление login screen в `index.html` 4. Переписывание вызовов API в `app.js` 5. Тестирование auth flow ### Важно (сделать после критичного) 6. Интеграция Settings UI 7. Environment selector 8. Сохранение и загрузка сессий анализа 9. Ручное тестирование всех сценариев ### Желательно (если есть время) 10. Автоматические тесты (pytest) 11. Production deployment настройка 12. Logging middleware 13. Rate limiting 14. Export функционал --- ## Следующие шаги **Рекомендуемый порядок:** 1. Посмотреть структуру старого проекта `rag-bench-old-version` 2. Скопировать статические файлы в `static/` 3. Создать `api-client.js` с методами для всех endpoints 4. Модифицировать `index.html` (добавить login screen) 5. Переписать `app.js` для работы с новым API 6. Протестировать локально с `uvicorn app.main:app --reload` 7. Исправить найденные проблемы 8. Подготовить production deployment **Готовы начать с первого этапа?**