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
|
||
}
|