187 lines
5.7 KiB
JavaScript
187 lines
5.7 KiB
JavaScript
|
|
/**
|
|||
|
|
* Query Service
|
|||
|
|
*
|
|||
|
|
* Сервис отправки запросов к RAG backend.
|
|||
|
|
*/
|
|||
|
|
|
|||
|
|
import api from './api-client.js'
|
|||
|
|
import appState from '../state/appState.js'
|
|||
|
|
import { validateJSON } from '../utils/validation.utils.js'
|
|||
|
|
import { generateUUID } from '../utils/format.utils.js'
|
|||
|
|
import { loadFileAsJSON, loadFileAsText } from '../utils/file.utils.js'
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Построить тело запроса из UI
|
|||
|
|
* @param {string} mode - Режим ('questions' или 'raw-json')
|
|||
|
|
* @param {string} questionsText - Текст вопросов (для режима questions)
|
|||
|
|
* @param {string} jsonText - JSON текст (для режима raw-json)
|
|||
|
|
* @returns {Array<{body: string, with_docs: boolean}>} Массив вопросов
|
|||
|
|
*/
|
|||
|
|
export function buildRequestBody(mode, questionsText, jsonText) {
|
|||
|
|
if (mode === 'questions') {
|
|||
|
|
const questions = questionsText
|
|||
|
|
.split('\n')
|
|||
|
|
.map(line => line.trim())
|
|||
|
|
.filter(line => line.length > 0)
|
|||
|
|
|
|||
|
|
if (questions.length === 0) {
|
|||
|
|
throw new Error('Введите хотя бы один вопрос')
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const settings = appState.settings || {}
|
|||
|
|
const defaultWithDocs = settings.defaultWithDocs !== undefined
|
|||
|
|
? settings.defaultWithDocs
|
|||
|
|
: true
|
|||
|
|
|
|||
|
|
return questions.map(q => ({
|
|||
|
|
body: q,
|
|||
|
|
with_docs: defaultWithDocs
|
|||
|
|
}))
|
|||
|
|
} else if (mode === 'raw-json') {
|
|||
|
|
const validation = validateJSON(jsonText)
|
|||
|
|
|
|||
|
|
if (!validation.valid) {
|
|||
|
|
throw new Error(validation.error)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return validation.data
|
|||
|
|
} else {
|
|||
|
|
throw new Error(`Неизвестный режим: ${mode}`)
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Отправить запрос к RAG backend
|
|||
|
|
* @param {string} environment - Окружение (ift/psi/prod)
|
|||
|
|
* @param {string} apiMode - Режим API ('bench' или 'backend')
|
|||
|
|
* @param {Array} questions - Массив вопросов
|
|||
|
|
* @param {boolean} resetSession - Сбрасывать ли сессию (только для backend mode)
|
|||
|
|
* @returns {Promise<object>} API response
|
|||
|
|
*/
|
|||
|
|
export async function sendQuery(environment, apiMode, questions, resetSession = true) {
|
|||
|
|
let apiResponse
|
|||
|
|
|
|||
|
|
if (apiMode === 'bench') {
|
|||
|
|
apiResponse = await api.benchQuery(environment, questions)
|
|||
|
|
} else if (apiMode === 'backend') {
|
|||
|
|
apiResponse = await api.backendQuery(environment, questions, resetSession)
|
|||
|
|
} else {
|
|||
|
|
throw new Error(`Неизвестный режим API: ${apiMode}`)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Validate response format
|
|||
|
|
if (!apiResponse.response ||
|
|||
|
|
!apiResponse.response.answers ||
|
|||
|
|
!Array.isArray(apiResponse.response.answers)) {
|
|||
|
|
throw new Error('Некорректный формат ответа: отсутствует поле "answers"')
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return apiResponse
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Обработать результат запроса и обновить AppState
|
|||
|
|
* @param {string} environment - Окружение
|
|||
|
|
* @param {Array} requestBody - Тело запроса
|
|||
|
|
* @param {object} apiResponse - Ответ от API
|
|||
|
|
*/
|
|||
|
|
export function processQueryResponse(environment, requestBody, apiResponse) {
|
|||
|
|
const env = appState.getEnvironment(environment)
|
|||
|
|
|
|||
|
|
// Update environment state
|
|||
|
|
env.currentRequest = requestBody
|
|||
|
|
env.currentResponse = apiResponse.response
|
|||
|
|
env.requestId = apiResponse.request_id
|
|||
|
|
env.requestTimestamp = apiResponse.timestamp
|
|||
|
|
env.currentAnswerIndex = 0
|
|||
|
|
env.annotations = {}
|
|||
|
|
|
|||
|
|
// Save to localStorage
|
|||
|
|
appState.saveEnvironmentToStorage(environment)
|
|||
|
|
|
|||
|
|
return env
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Загрузить запрос из файла
|
|||
|
|
* @param {File} file - JSON файл с запросом
|
|||
|
|
* @returns {Promise<Array>} Массив вопросов
|
|||
|
|
*/
|
|||
|
|
export async function loadRequestFromFile(file) {
|
|||
|
|
try {
|
|||
|
|
const data = await loadFileAsJSON(file)
|
|||
|
|
|
|||
|
|
// Validate it's an array
|
|||
|
|
if (!Array.isArray(data)) {
|
|||
|
|
throw new Error('Файл должен содержать JSON массив')
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return data
|
|||
|
|
} catch (error) {
|
|||
|
|
console.error('Error loading request from file:', error)
|
|||
|
|
throw new Error(`Ошибка загрузки запроса: ${error.message}`)
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Загрузить ответ из файла
|
|||
|
|
* @param {File} file - JSON файл с ответом
|
|||
|
|
* @param {string} environment - Текущее окружение
|
|||
|
|
* @returns {Promise<object>} Загруженный ответ
|
|||
|
|
*/
|
|||
|
|
export async function loadResponseFromFile(file, environment) {
|
|||
|
|
try {
|
|||
|
|
const data = await loadFileAsJSON(file)
|
|||
|
|
|
|||
|
|
// Validate response format
|
|||
|
|
if (!data.answers || !Array.isArray(data.answers)) {
|
|||
|
|
throw new Error('Файл должен содержать объект с полем "answers" (массив)')
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const env = appState.getEnvironment(environment)
|
|||
|
|
|
|||
|
|
// Set response
|
|||
|
|
env.currentResponse = data
|
|||
|
|
env.currentAnswerIndex = 0
|
|||
|
|
env.requestTimestamp = new Date().toISOString()
|
|||
|
|
env.requestId = 'loaded-' + generateUUID()
|
|||
|
|
env.annotations = {}
|
|||
|
|
|
|||
|
|
// Try to reconstruct request from questions in response
|
|||
|
|
env.currentRequest = data.answers.map(answer => ({
|
|||
|
|
body: answer.question,
|
|||
|
|
with_docs: true
|
|||
|
|
}))
|
|||
|
|
|
|||
|
|
// Save to localStorage
|
|||
|
|
appState.saveEnvironmentToStorage(environment)
|
|||
|
|
|
|||
|
|
return data
|
|||
|
|
} catch (error) {
|
|||
|
|
console.error('Error loading response from file:', error)
|
|||
|
|
throw new Error(`Ошибка загрузки ответа: ${error.message}`)
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Извлечь вопросы из textarea
|
|||
|
|
* @param {string} text - Текст из textarea
|
|||
|
|
* @returns {Array<string>} Массив вопросов
|
|||
|
|
*/
|
|||
|
|
export function extractQuestions(text) {
|
|||
|
|
return text
|
|||
|
|
.split('\n')
|
|||
|
|
.map(line => line.trim())
|
|||
|
|
.filter(line => line.length > 0)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Export as default object
|
|||
|
|
export default {
|
|||
|
|
buildRequestBody,
|
|||
|
|
sendQuery,
|
|||
|
|
processQueryResponse,
|
|||
|
|
loadRequestFromFile,
|
|||
|
|
loadResponseFromFile,
|
|||
|
|
extractQuestions
|
|||
|
|
}
|