This commit is contained in:
parent
85dc167449
commit
cf86a9378c
13
app/main.py
13
app/main.py
|
|
@ -3,6 +3,7 @@
|
|||
from fastapi import FastAPI
|
||||
from fastapi.middleware.cors import CORSMiddleware
|
||||
from fastapi.staticfiles import StaticFiles
|
||||
from fastapi.responses import FileResponse
|
||||
|
||||
from app.api.v1 import auth, settings as settings_router, query, analysis
|
||||
from app.config import settings
|
||||
|
|
@ -30,13 +31,19 @@ app.include_router(query.router, prefix="/api/v1")
|
|||
app.include_router(analysis.router, prefix="/api/v1")
|
||||
|
||||
# Serve static files (frontend)
|
||||
# app.mount("/static", StaticFiles(directory="static"), name="static")
|
||||
app.mount("/static", StaticFiles(directory="static"), name="static")
|
||||
|
||||
|
||||
@app.get("/app")
|
||||
async def serve_frontend():
|
||||
"""Serve the main frontend application."""
|
||||
return FileResponse("static/index.html")
|
||||
|
||||
|
||||
@app.get("/")
|
||||
async def root():
|
||||
"""Root endpoint."""
|
||||
return {"message": "Brief Bench API", "version": "1.0.0"}
|
||||
"""Root endpoint - redirect to app."""
|
||||
return FileResponse("static/index.html")
|
||||
|
||||
|
||||
@app.get("/health")
|
||||
|
|
|
|||
|
|
@ -0,0 +1,246 @@
|
|||
/**
|
||||
* 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()
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,451 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="ru">
|
||||
<!-- LOCAL -->
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Brief Bench - RAG Testing Interface</title>
|
||||
|
||||
<!-- Google Fonts -->
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500;700&family=Roboto+Mono:wght@400;500&display=swap" rel="stylesheet">
|
||||
|
||||
<!-- Material Icons -->
|
||||
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
|
||||
|
||||
<!-- Styles -->
|
||||
<link rel="stylesheet" href="styles.css">
|
||||
</head>
|
||||
<body>
|
||||
<!-- Login Screen -->
|
||||
<div id="login-screen" class="login-container" style="display: none;">
|
||||
<div class="login-card">
|
||||
<h2 style="text-align: center; margin-bottom: var(--md-spacing-xl);">Brief Bench</h2>
|
||||
<div class="form-group">
|
||||
<label class="form-label" for="login-input">8-значный логин</label>
|
||||
<input
|
||||
type="text"
|
||||
class="form-input"
|
||||
id="login-input"
|
||||
placeholder="12345678"
|
||||
maxlength="8"
|
||||
pattern="[0-9]{8}"
|
||||
autocomplete="off"
|
||||
>
|
||||
<div class="form-helper-text" id="login-error" style="color: var(--md-error); display: none;"></div>
|
||||
</div>
|
||||
<button class="btn btn-filled" id="login-submit-btn" style="width: 100%;">
|
||||
Войти
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="app">
|
||||
<!-- App Bar -->
|
||||
<header class="app-bar">
|
||||
<div class="app-bar-title">
|
||||
<button class="btn-icon" id="menu-btn" title="Toggle menu">
|
||||
<span class="material-icons">menu</span>
|
||||
</button>
|
||||
<span>Brief Bench</span>
|
||||
</div>
|
||||
<div class="app-bar-actions">
|
||||
<button class="btn-icon" id="new-query-btn" title="New Query">
|
||||
<span class="material-icons">add_circle</span>
|
||||
</button>
|
||||
<button class="btn-icon" id="import-btn" title="Импорт анализа">
|
||||
<span class="material-icons">folder_open</span>
|
||||
</button>
|
||||
<button class="btn-icon" id="export-btn" title="Экспорт анализа">
|
||||
<span class="material-icons">save</span>
|
||||
</button>
|
||||
<button class="btn-icon" id="settings-btn" title="Settings">
|
||||
<span class="material-icons">settings</span>
|
||||
</button>
|
||||
<button class="btn-icon" id="logout-btn" title="Выход">
|
||||
<span class="material-icons">logout</span>
|
||||
</button>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<!-- Environment Tabs -->
|
||||
<div class="environment-tabs">
|
||||
<button class="env-tab active" data-env="ift">ИФТ</button>
|
||||
<button class="env-tab" data-env="psi">ПСИ</button>
|
||||
<button class="env-tab" data-env="prod">ПРОМ</button>
|
||||
</div>
|
||||
|
||||
<!-- Main Content Area -->
|
||||
<div class="app-content">
|
||||
<!-- Drawer / Sidebar -->
|
||||
<aside class="drawer" id="drawer">
|
||||
<div class="drawer-header">
|
||||
<div style="flex: 1;">
|
||||
<h6 style="margin-bottom: 4px;">Вопросы и ответы</h6>
|
||||
<div class="text-caption" id="questions-count">0 вопросов</div>
|
||||
</div>
|
||||
<button class="btn-icon" id="clear-all-btn" title="Очистить всё (обновить страницу)" style="align-self: flex-start;">
|
||||
<span class="material-icons">refresh</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="drawer-content" id="questions-list">
|
||||
<!-- Questions will be populated here -->
|
||||
<div class="empty-state">
|
||||
<div class="empty-state-icon">
|
||||
<span class="material-icons" style="font-size: inherit;">question_answer</span>
|
||||
</div>
|
||||
<div class="empty-state-text">Нет данных</div>
|
||||
<div class="empty-state-subtext">Отправьте запрос к RAG бэкенду</div>
|
||||
</div>
|
||||
</div>
|
||||
</aside>
|
||||
|
||||
<!-- Main Area -->
|
||||
<main class="main-area" id="main-area">
|
||||
<!-- Query Builder (shown when no data) -->
|
||||
<div id="query-builder" class="hidden">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h5 class="card-title">Создание запроса</h5>
|
||||
<div class="toggle-group">
|
||||
<button class="toggle-option active" data-mode="questions">Вопросы</button>
|
||||
<button class="toggle-option" data-mode="raw-json">Raw JSON</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-content">
|
||||
<!-- Questions Mode -->
|
||||
<div id="questions-mode" class="query-mode">
|
||||
<div class="form-group">
|
||||
<label class="form-label">Введите вопросы (каждый с новой строки)</label>
|
||||
<textarea
|
||||
id="questions-textarea"
|
||||
class="form-textarea"
|
||||
rows="10"
|
||||
placeholder="Какая текущая ВВП в России? Какие показатели у газпрома"></textarea>
|
||||
<div class="form-helper-text">Каждая строка будет отправлена как отдельный вопрос с флагом with_docs: true</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Raw JSON Mode -->
|
||||
<div id="raw-json-mode" class="query-mode hidden">
|
||||
<div class="form-group">
|
||||
<label class="form-label">JSON запрос</label>
|
||||
<textarea
|
||||
id="json-textarea"
|
||||
class="form-textarea"
|
||||
rows="10"
|
||||
placeholder='[ { "body": "Какая текущая ВВП в России?", "with_docs": true } ]'></textarea>
|
||||
<div class="form-helper-text" id="json-validation-message"></div>
|
||||
</div>
|
||||
<div class="flex gap-sm">
|
||||
<button class="btn btn-outlined" id="load-request-btn">
|
||||
<span class="material-icons">upload_file</span>
|
||||
Загрузить из файла
|
||||
</button>
|
||||
<button class="btn btn-text" id="validate-json-btn">
|
||||
<span class="material-icons">check_circle</span>
|
||||
Проверить JSON
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-actions">
|
||||
<button class="btn btn-outlined" id="load-response-btn">
|
||||
<span class="material-icons">folder_open</span>
|
||||
Загрузить ответ (Response)
|
||||
</button>
|
||||
<button class="btn btn-filled" id="send-query-btn">
|
||||
<span class="material-icons">send</span>
|
||||
Отправить запрос
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Answer Viewer (shown when data is available) -->
|
||||
<div id="answer-viewer" class="hidden">
|
||||
<!-- Question Header -->
|
||||
<div class="card mb-md">
|
||||
<div class="card-content">
|
||||
<div class="text-overline mb-sm">Вопрос #<span id="current-question-number">1</span></div>
|
||||
<h4 id="current-question-text"></h4>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Metadata Card -->
|
||||
<div class="card mb-md">
|
||||
<div class="card-header">
|
||||
<h6 class="card-title">Метаданные</h6>
|
||||
</div>
|
||||
<div class="card-content">
|
||||
<div class="metadata-grid">
|
||||
<div class="metadata-item">
|
||||
<div class="metadata-label">Время обработки</div>
|
||||
<div class="metadata-value" id="processing-time">-</div>
|
||||
</div>
|
||||
<div class="metadata-item">
|
||||
<div class="metadata-label">Request ID</div>
|
||||
<div class="metadata-value" id="request-id">-</div>
|
||||
</div>
|
||||
<div class="metadata-item">
|
||||
<div class="metadata-label">Timestamp</div>
|
||||
<div class="metadata-value" id="request-timestamp">-</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Answer Bodies -->
|
||||
<div class="card mb-md">
|
||||
<div class="card-header">
|
||||
<h6 class="card-title">Тексты ответов</h6>
|
||||
</div>
|
||||
<div class="tabs">
|
||||
<button class="tab active" data-tab="body-research">Research</button>
|
||||
<button class="tab" data-tab="body-analytical">Analytical Hub</button>
|
||||
</div>
|
||||
<div id="body-research" class="tab-content active">
|
||||
<div id="body-research-text"></div>
|
||||
<!-- Annotation Section -->
|
||||
<div class="annotation-section">
|
||||
<h6 class="mb-sm">Пометки</h6>
|
||||
<div class="annotation-issues mb-md">
|
||||
<label class="issue-checkbox">
|
||||
<input type="checkbox" class="checkbox" data-section="body_research" data-issue="factual_error">
|
||||
<span>Факт. ошибка</span>
|
||||
</label>
|
||||
<label class="issue-checkbox">
|
||||
<input type="checkbox" class="checkbox" data-section="body_research" data-issue="inaccurate_wording">
|
||||
<span>Неточность формулировки</span>
|
||||
</label>
|
||||
<label class="issue-checkbox">
|
||||
<input type="checkbox" class="checkbox" data-section="body_research" data-issue="insufficient_context">
|
||||
<span>Недостаточно контекста</span>
|
||||
</label>
|
||||
<label class="issue-checkbox">
|
||||
<input type="checkbox" class="checkbox" data-section="body_research" data-issue="offtopic">
|
||||
<span>Не по вопросу</span>
|
||||
</label>
|
||||
<label class="issue-checkbox">
|
||||
<input type="checkbox" class="checkbox" data-section="body_research" data-issue="technical_answer">
|
||||
<span>Технический ответ</span>
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label">Комментарий</label>
|
||||
<textarea class="form-textarea" rows="3" data-section="body_research" placeholder="Ваш комментарий..."></textarea>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="body-analytical" class="tab-content">
|
||||
<div id="body-analytical-text"></div>
|
||||
<!-- Annotation Section -->
|
||||
<div class="annotation-section">
|
||||
<h6 class="mb-sm">Пометки</h6>
|
||||
<div class="annotation-issues mb-md">
|
||||
<label class="issue-checkbox">
|
||||
<input type="checkbox" class="checkbox" data-section="body_analytical_hub" data-issue="factual_error">
|
||||
<span>Факт. ошибка</span>
|
||||
</label>
|
||||
<label class="issue-checkbox">
|
||||
<input type="checkbox" class="checkbox" data-section="body_analytical_hub" data-issue="inaccurate_wording">
|
||||
<span>Неточность формулировки</span>
|
||||
</label>
|
||||
<label class="issue-checkbox">
|
||||
<input type="checkbox" class="checkbox" data-section="body_analytical_hub" data-issue="insufficient_context">
|
||||
<span>Недостаточно контекста</span>
|
||||
</label>
|
||||
<label class="issue-checkbox">
|
||||
<input type="checkbox" class="checkbox" data-section="body_analytical_hub" data-issue="offtopic">
|
||||
<span>Не по вопросу</span>
|
||||
</label>
|
||||
<label class="issue-checkbox">
|
||||
<input type="checkbox" class="checkbox" data-section="body_analytical_hub" data-issue="technical_answer">
|
||||
<span>Технический ответ</span>
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label">Комментарий</label>
|
||||
<textarea class="form-textarea" rows="3" data-section="body_analytical_hub" placeholder="Ваш комментарий..."></textarea>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Overall Rating -->
|
||||
<div class="card mb-md">
|
||||
<div class="card-header">
|
||||
<h6 class="card-title">Общая оценка ответа</h6>
|
||||
</div>
|
||||
<div class="card-content">
|
||||
<div class="form-group">
|
||||
<label class="form-label" for="overall-rating">Оценка</label>
|
||||
<select class="form-select" id="overall-rating" aria-label="Общая оценка ответа">
|
||||
<option value="">Не оценено</option>
|
||||
<option value="correct">Корректно</option>
|
||||
<option value="partial">Частично корректно</option>
|
||||
<option value="incorrect">Некорректно</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label">Общий комментарий</label>
|
||||
<textarea class="form-textarea" rows="3" id="overall-comment" placeholder="Общий комментарий по ответу..."></textarea>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Documents Section -->
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h6 class="card-title">Документы</h6>
|
||||
</div>
|
||||
<div class="tabs">
|
||||
<button class="tab active" data-tab="docs-vectorstore">Docs from Vectorstore</button>
|
||||
<button class="tab" data-tab="docs-llm">Docs to LLM</button>
|
||||
</div>
|
||||
|
||||
<!-- Docs from Vectorstore -->
|
||||
<div id="docs-vectorstore" class="tab-content active">
|
||||
<div class="tabs">
|
||||
<button class="tab active" data-tab="vectorstore-research">Research</button>
|
||||
<button class="tab" data-tab="vectorstore-analytical">Analytical Hub</button>
|
||||
</div>
|
||||
<div id="vectorstore-research" class="tab-content active">
|
||||
<div id="vectorstore-research-docs"></div>
|
||||
</div>
|
||||
<div id="vectorstore-analytical" class="tab-content">
|
||||
<div id="vectorstore-analytical-docs"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Docs to LLM -->
|
||||
<div id="docs-llm" class="tab-content">
|
||||
<div class="tabs">
|
||||
<button class="tab active" data-tab="llm-research">Research</button>
|
||||
<button class="tab" data-tab="llm-analytical">Analytical Hub</button>
|
||||
</div>
|
||||
<div id="llm-research" class="tab-content active">
|
||||
<div id="llm-research-docs"></div>
|
||||
</div>
|
||||
<div id="llm-analytical" class="tab-content">
|
||||
<div id="llm-analytical-docs"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Settings Dialog -->
|
||||
<div class="dialog-overlay" id="settings-dialog">
|
||||
<div class="dialog">
|
||||
<div class="dialog-header">
|
||||
<h5 class="dialog-title">Настройки</h5>
|
||||
<button class="btn-icon" id="close-settings-btn">
|
||||
<span class="material-icons">close</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="dialog-content">
|
||||
<!-- Environment Selector -->
|
||||
<div class="form-group">
|
||||
<label class="form-label">Редактируемое окружение</label>
|
||||
<select class="form-select" id="settings-env-selector">
|
||||
<option value="ift">ИФТ</option>
|
||||
<option value="psi">ПСИ</option>
|
||||
<option value="prod">ПРОМ</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<hr style="margin: var(--md-spacing-lg) 0; border: none; border-top: 1px solid var(--md-divider);">
|
||||
|
||||
<!-- Environment-specific settings -->
|
||||
<div id="settings-env-fields">
|
||||
<div class="form-group">
|
||||
<label class="form-label">Режим API</label>
|
||||
<select class="form-select" id="setting-api-mode">
|
||||
<option value="bench">Bench (батч-тестирование)</option>
|
||||
<option value="backend">Backend (имитация бота)</option>
|
||||
</select>
|
||||
<div class="form-helper-text">Bench - отправка массива вопросов; Backend - вопросы по одному</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="form-label">Bearer Token (необязательно)</label>
|
||||
<input type="password" class="form-input" id="setting-bearer-token" placeholder="your-bearer-token">
|
||||
<div class="form-helper-text">Токен для авторизации запросов к RAG API</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="form-label">System-Platform (необязательно)</label>
|
||||
<input type="text" class="form-input" id="setting-system-platform" placeholder="platform-name">
|
||||
<div class="form-helper-text">Заголовок System-Platform для запросов</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="form-label">System-Platform-User (необязательно)</label>
|
||||
<input type="text" class="form-input" id="setting-system-platform-user" placeholder="user-name">
|
||||
<div class="form-helper-text">Заголовок System-Platform-User для запросов</div>
|
||||
</div>
|
||||
|
||||
<h6 class="mt-lg mb-md" id="backend-settings-header" style="display: none;">Настройки Backend Mode</h6>
|
||||
<div id="backend-settings" style="display: none;">
|
||||
<div class="form-group">
|
||||
<label class="form-label">Platform User ID</label>
|
||||
<input type="text" class="form-input" id="setting-platform-user-id" placeholder="user-123">
|
||||
<div class="form-helper-text">Идентификатор пользователя платформы</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="form-label">Platform ID</label>
|
||||
<input type="text" class="form-input" id="setting-platform-id" placeholder="platform-1">
|
||||
<div class="form-helper-text">Идентификатор платформы</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="form-label" style="display: flex; align-items: center; gap: 8px;">
|
||||
<input type="checkbox" id="setting-with-classify" class="checkbox">
|
||||
Включить классификацию вопросов
|
||||
</label>
|
||||
<div class="form-helper-text">Запрашивать question_type в ответе</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="form-label" style="display: flex; align-items: center; gap: 8px;">
|
||||
<input type="checkbox" id="setting-reset-session-mode" class="checkbox" checked>
|
||||
Сбрасывать сессию после каждого вопроса
|
||||
</label>
|
||||
<div class="form-helper-text">Если выключено, вопросы будут задаваться в рамках одной сессии</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="dialog-actions">
|
||||
<button class="btn btn-text" id="import-settings-btn">
|
||||
<span class="material-icons">upload</span>
|
||||
Импорт настроек
|
||||
</button>
|
||||
<button class="btn btn-text" id="export-settings-btn">
|
||||
<span class="material-icons">download</span>
|
||||
Экспорт настроек
|
||||
</button>
|
||||
<button class="btn btn-outlined" id="reset-settings-btn">Сброс</button>
|
||||
<button class="btn btn-filled" id="save-settings-btn">Сохранить</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Loading Overlay -->
|
||||
<div class="loading-overlay" id="loading-overlay">
|
||||
<div class="spinner"></div>
|
||||
<div class="loading-message" id="loading-message">Отправка запроса...</div>
|
||||
<div class="loading-submessage">Это может занять до 30 минут</div>
|
||||
</div>
|
||||
|
||||
<!-- Scripts -->
|
||||
<script src="settings.js"></script>
|
||||
<script src="api-client.js"></script>
|
||||
<script src="app.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,70 @@
|
|||
// Default settings for Brief Bench (FastAPI Version)
|
||||
// User-editable fields only - server configuration managed by FastAPI backend
|
||||
|
||||
const defaultSettings = {
|
||||
// Active environment
|
||||
activeEnvironment: 'ift', // 'ift', 'psi', or 'prod'
|
||||
|
||||
// Environment-specific settings
|
||||
environments: {
|
||||
ift: {
|
||||
name: 'ИФТ',
|
||||
apiMode: 'bench', // 'bench' or 'backend'
|
||||
|
||||
// Optional headers for RAG backend
|
||||
bearerToken: '', // Bearer token for authorization (optional)
|
||||
systemPlatform: '', // System-Platform header (optional)
|
||||
systemPlatformUser: '', // System-Platform-User header (optional)
|
||||
|
||||
// Backend mode settings
|
||||
platformUserId: '',
|
||||
platformId: '',
|
||||
withClassify: false,
|
||||
resetSessionMode: true // Reset session after each question
|
||||
},
|
||||
psi: {
|
||||
name: 'ПСИ',
|
||||
apiMode: 'bench', // 'bench' or 'backend'
|
||||
|
||||
// Optional headers for RAG backend
|
||||
bearerToken: '',
|
||||
systemPlatform: '',
|
||||
systemPlatformUser: '',
|
||||
|
||||
// Backend mode settings
|
||||
platformUserId: '',
|
||||
platformId: '',
|
||||
withClassify: false,
|
||||
resetSessionMode: true
|
||||
},
|
||||
prod: {
|
||||
name: 'ПРОМ',
|
||||
apiMode: 'bench', // 'bench' or 'backend'
|
||||
|
||||
// Optional headers for RAG backend
|
||||
bearerToken: '',
|
||||
systemPlatform: '',
|
||||
systemPlatformUser: '',
|
||||
|
||||
// Backend mode settings
|
||||
platformUserId: '',
|
||||
platformId: '',
|
||||
withClassify: false,
|
||||
resetSessionMode: true
|
||||
}
|
||||
},
|
||||
|
||||
// UI settings
|
||||
theme: 'light',
|
||||
autoSaveDrafts: true,
|
||||
requestTimeout: 1800000, // 30 minutes in milliseconds
|
||||
|
||||
// Query settings
|
||||
defaultWithDocs: true,
|
||||
defaultQueryMode: 'questions' // 'questions' or 'raw-json'
|
||||
};
|
||||
|
||||
// Export for use in app.js
|
||||
if (typeof module !== 'undefined' && module.exports) {
|
||||
module.exports = defaultSettings;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue