2025-12-17 15:37:32 +01:00
|
|
|
|
# План развития Brief Bench FastAPI
|
|
|
|
|
|
|
|
|
|
|
|
**Дата создания:** 17 декабря 2025
|
|
|
|
|
|
**Статус:** Backend готов, требуется frontend интеграция и тестирование
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## Текущее состояние
|
|
|
|
|
|
|
2025-12-18 09:52:02 +01:00
|
|
|
|
### Готово (Backend)
|
2025-12-17 15:37:32 +01:00
|
|
|
|
- Структура 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)
|
2025-12-18 09:52:02 +01:00
|
|
|
|
- Документация (README.md, DB_API_CONTRACT.md)
|
2025-12-17 15:37:32 +01:00
|
|
|
|
|
2025-12-18 09:52:02 +01:00
|
|
|
|
### Требуется доделать
|
2025-12-17 15:37:32 +01:00
|
|
|
|
- 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<object>}
|
|
|
|
|
|
*/
|
|
|
|
|
|
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<object>}
|
|
|
|
|
|
*/
|
|
|
|
|
|
async getSession(sessionId) {
|
|
|
|
|
|
return await this._request(`/analysis/sessions/${sessionId}`, {
|
|
|
|
|
|
method: 'GET',
|
|
|
|
|
|
headers: this._getHeaders()
|
|
|
|
|
|
})
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* Удалить сессию
|
|
|
|
|
|
* @param {string} sessionId - ID сессии
|
|
|
|
|
|
* @returns {Promise<null>}
|
|
|
|
|
|
*/
|
|
|
|
|
|
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
|
|
|
|
|
|
<!-- Login screen (показывается если нет токена) -->
|
|
|
|
|
|
<div id="login-screen">
|
|
|
|
|
|
<h1>Brief Bench</h1>
|
|
|
|
|
|
<input type="text" id="login-input" placeholder="8-значный логин" maxlength="8">
|
|
|
|
|
|
<button id="login-btn">Войти</button>
|
|
|
|
|
|
<div id="login-error"></div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- Main app (показывается после авторизации) -->
|
|
|
|
|
|
<div id="app" style="display: none;">
|
|
|
|
|
|
<!-- Существующий интерфейс -->
|
|
|
|
|
|
</div>
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
**Логика в `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
|
|
|
|
|
|
<div id="environment-selector">
|
|
|
|
|
|
<label>Окружение:</label>
|
|
|
|
|
|
<select id="env-select">
|
|
|
|
|
|
<option value="ift">ИФТ</option>
|
|
|
|
|
|
<option value="psi">ПСИ</option>
|
|
|
|
|
|
<option value="prod">ПРОД</option>
|
|
|
|
|
|
</select>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
**Логика:**
|
|
|
|
|
|
- При выборе окружения → загрузить настройки для этого окружения
|
|
|
|
|
|
- Отобразить текущий 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 с метриками (кол-во запросов, успешность, время ответа)
|
|
|
|
|
|
- Графики по окружениям
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## Приоритезация задач
|
|
|
|
|
|
|
2025-12-18 09:52:02 +01:00
|
|
|
|
### Критично (сделать в первую очередь)
|
2025-12-17 15:37:32 +01:00
|
|
|
|
1. Перенос статических файлов из rag-bench-old-version → `static/`
|
|
|
|
|
|
2. Создание `api-client.js`
|
|
|
|
|
|
3. Добавление login screen в `index.html`
|
|
|
|
|
|
4. Переписывание вызовов API в `app.js`
|
|
|
|
|
|
5. Тестирование auth flow
|
|
|
|
|
|
|
2025-12-18 09:52:02 +01:00
|
|
|
|
### Важно (сделать после критичного)
|
2025-12-17 15:37:32 +01:00
|
|
|
|
6. Интеграция Settings UI
|
|
|
|
|
|
7. Environment selector
|
|
|
|
|
|
8. Сохранение и загрузка сессий анализа
|
|
|
|
|
|
9. Ручное тестирование всех сценариев
|
|
|
|
|
|
|
2025-12-18 09:52:02 +01:00
|
|
|
|
### Желательно (если есть время)
|
2025-12-17 15:37:32 +01:00
|
|
|
|
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
|
|
|
|
|
|
|
|
|
|
|
|
**Готовы начать с первого этапа?**
|