solver-sdk
Version:
SDK для интеграции с Code Solver Backend API
291 lines • 13.4 kB
JavaScript
import { WebSocketNamespace } from '../utils/code-solver-websocket-client.js';
/**
* API для работы с рассуждениями
*/
export class ReasoningApi {
/**
* Создает новый экземпляр API для работы с рассуждениями
* @param {HttpClient} httpClient HTTP клиент
* @param {ProjectsApi} projectsApi API для работы с проектами
*/
constructor(httpClient, projectsApi) {
/** WebSocket клиент */
this.wsClient = null;
/** Родительский SDK */
this.parentSdk = null;
this.httpClient = httpClient;
this.projectsApi = projectsApi;
}
/**
* Получает список рассуждений
* @param {GetReasoningsParams} [params] Параметры для получения списка
* @returns {Promise<Reasoning[]>} Список рассуждений
*/
async getReasonings(params) {
return this.httpClient.get('/api/reasonings', params);
}
/**
* Получает рассуждение по ID
* @param {string} reasoningId ID рассуждения
* @returns {Promise<Reasoning>} Рассуждение
*/
async getReasoning(reasoningId) {
return this.httpClient.get(`/api/v1/reasoning/${reasoningId}`);
}
/**
* Создает новое рассуждение
* @param {ReasoningOptions} options Опции для рассуждения
* @returns {Promise<Reasoning>} Созданное рассуждение
*/
async createReasoning(options) {
return this.httpClient.post('/api/v1/reasoning/create', options);
}
/**
* Создает новое рассуждение с автоматическим переключением между регионами при ошибке перегрузки
* @param {ReasoningOptions} options Опции для рассуждения
* @returns {Promise<Reasoning>} Созданное рассуждение
*/
async createReasoningWithRegionFailover(options) {
// Список всех доступных регионов
const allRegions = ['us-east-1', 'eu-west-1', 'ap-southeast-2'];
// Начинаем с региона из параметров, или первого в списке
let startRegionIndex = 0;
if (options.options?.region) {
const regionIndex = allRegions.indexOf(options.options.region);
if (regionIndex !== -1) {
startRegionIndex = regionIndex;
}
}
// Реорганизуем массив, чтобы начать с указанного региона
const regions = [
...allRegions.slice(startRegionIndex),
...allRegions.slice(0, startRegionIndex)
];
// Последняя ошибка, будет возвращена если все регионы недоступны
let lastError = null;
// Пробуем каждый регион по очереди
for (let i = 0; i < regions.length; i++) {
const region = regions[i];
try {
console.log(`Попытка запроса рассуждения к Anthropic API в регионе ${region}`);
// Копируем опции и устанавливаем текущий регион
const regionOptions = {
...options,
options: {
...(options.options || {}),
region
}
};
// Отправляем запрос с конкретным регионом
return await this.createReasoning(regionOptions);
}
catch (error) {
lastError = error;
// Проверяем, является ли ошибка ошибкой перегрузки (код 529)
const isOverloadError = error.status === 529 ||
error.code === 529 ||
(error.response?.status === 529) ||
(error.message && error.message.includes('overloaded')) ||
(error.error?.type === 'overloaded_error');
if (isOverloadError) {
console.warn(`Регион ${region} перегружен, пробуем следующий регион для рассуждения`);
// Продолжаем цикл и пробуем следующий регион
continue;
}
else {
// Если ошибка не связана с перегрузкой, прекращаем попытки
console.error(`Ошибка в регионе ${region}, не связанная с перегрузкой:`, error);
throw error;
}
}
}
// Если мы здесь, значит все регионы перегружены
throw lastError || new Error('Все регионы Anthropic API перегружены, попробуйте создать рассуждение позже');
}
/**
* Запускает рассуждение
* @param {string} reasoningId ID рассуждения
* @returns {Promise<Reasoning>} Обновленное рассуждение
*/
async startReasoning(reasoningId) {
return this.httpClient.post(`/api/v1/reasoning/start/${reasoningId}`);
}
/**
* Останавливает рассуждение
* @param {string} reasoningId ID рассуждения
* @returns {Promise<Reasoning>} Обновленное рассуждение
*/
async stopReasoning(reasoningId) {
return this.httpClient.post(`/api/v1/reasoning/cancel/${reasoningId}`);
}
/**
* Удаляет рассуждение
* @param {string} reasoningId ID рассуждения
* @returns {Promise<void>}
*/
async deleteReasoning(reasoningId) {
return this.httpClient.delete(`/api/v1/reasoning/${reasoningId}`);
}
/**
* Получает ход мыслей рассуждения
* @param {string} reasoningId ID рассуждения
* @returns {Promise<ThinkingStep[]>} Ход мыслей
*/
async getThinking(reasoningId) {
return this.httpClient.get(`/api/v1/reasoning/${reasoningId}/thinking`);
}
/**
* Получает все рассуждения проекта
* @param {string} projectId ID проекта
* @returns {Promise<Reasoning[]>} Список рассуждений
*/
async getAllReasonings(projectId) {
return this.httpClient.get(`/api/v1/reasoning/project/${projectId}`);
}
/**
* Получает список доступных моделей для рассуждений
* @deprecated Используйте sdk.models.getAllModels() вместо этого метода
* @returns {Promise<AllModelsResponse>} Список доступных моделей
*/
async getModels() {
return this.httpClient.get('/api/v1/models');
}
/**
* Подключается к WebSocket для событий рассуждений
* @returns {Promise<boolean>} Результат подключения
*/
async connectWebSocket() {
try {
if (!this.parentSdk || typeof this.parentSdk.getWebSocketClient !== 'function') {
throw new Error('Родительский SDK не настроен или не поддерживает WebSocket');
}
this.wsClient = this.parentSdk.getWebSocketClient();
await this.wsClient.connect(WebSocketNamespace.REASONING);
return this.wsClient.isConnected(WebSocketNamespace.REASONING);
}
catch (error) {
console.error('[ReasoningApi] Ошибка при подключении к WebSocket:', error.message);
return false;
}
}
/**
* Отключается от WebSocket для событий рассуждений
* @returns {Promise<void>}
*/
async disconnectWebSocket() {
if (this.wsClient) {
await this.wsClient.disconnect(WebSocketNamespace.REASONING);
}
}
/**
* Проверяет, подключен ли WebSocket
* @returns {boolean} Состояние подключения
*/
isWebSocketConnected() {
return this.wsClient ? this.wsClient.isConnected(WebSocketNamespace.REASONING) : false;
}
/**
* Устанавливает родительский SDK
* @param sdk Родительский SDK
*/
setParent(sdk) {
this.parentSdk = sdk;
}
/**
* Подписывается на событие через WebSocket
* @param event Название события
* @param callback Функция обратного вызова
*/
on(event, callback) {
if (!this.wsClient) {
console.warn('[ReasoningApi] WebSocket не подключен');
return;
}
this.wsClient.on(event, callback);
}
/**
* Отправляет событие через WebSocket
* @param event Название события
* @param data Данные для отправки
*/
emitSocketEvent(event, data) {
if (!this.wsClient) {
console.warn('[ReasoningApi] WebSocket не подключен');
return;
}
this.wsClient.send(WebSocketNamespace.REASONING, event, data);
}
/**
* Отправляет событие через WebSocket с ожиданием ответа
* @param event Имя события
* @param data Данные для отправки
* @param timeout Таймаут ожидания ответа
* @returns {Promise<any>} Ответ от сервера
*/
async sendSocketEventWithResponse(event, data, timeout = 5000) {
if (!this.wsClient) {
throw new Error('[ReasoningApi] WebSocket не подключен');
}
// Реализуем собственный механизм emitWithAck, если wsClient не поддерживает его
return new Promise((resolve, reject) => {
try {
// Создаем уникальный ID для запроса
const requestId = `req_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;
// Имя события для ответа
const responseEvent = `${event}_response`;
// Обработчик для получения ответа
const responseHandler = (response) => {
if (response && response.requestId === requestId) {
// Очищаем таймер и удаляем обработчик
clearTimeout(timeoutId);
if (this.wsClient) {
this.wsClient.off(responseEvent, responseHandler);
}
// Разрешаем промис с ответом
resolve(response.data || response);
}
};
// Устанавливаем таймаут
const timeoutId = setTimeout(() => {
if (this.wsClient) {
this.wsClient.off(responseEvent, responseHandler);
}
reject(new Error(`Таймаут ожидания ответа на событие ${event}`));
}, timeout);
// Регистрируем обработчик
this.wsClient.on(responseEvent, responseHandler);
// Отправляем событие
this.wsClient.send(WebSocketNamespace.REASONING, event, {
...data,
requestId
});
}
catch (error) {
reject(error);
}
});
}
/**
* Присоединяется к конкретному рассуждению через WebSocket
* @param reasoningId ID рассуждения
* @returns {Promise<any>} Результат операции
*/
async joinReasoning(reasoningId) {
if (!this.wsClient || !this.isWebSocketConnected()) {
await this.connectWebSocket();
}
return this.sendSocketEventWithResponse('JOIN_REASONING', { reasoningId }, 5000);
}
/**
* Покидает конкретное рассуждение через WebSocket
* @param reasoningId ID рассуждения
*/
leaveReasoning(reasoningId) {
if (!this.wsClient || !this.isWebSocketConnected()) {
console.warn('[ReasoningApi] WebSocket не подключен');
return;
}
this.emitSocketEvent('LEAVE_REASONING', { reasoningId });
}
}
//# sourceMappingURL=reasoning-api.js.map