solver-sdk
Version:
SDK для интеграции с Code Solver Backend API
326 lines • 17.2 kB
JavaScript
import { WebSocketNamespace } from '../constants/websocket-namespaces.constants.js';
import { WebSocketEvents as WsEvents } from '../constants/websocket-events.constants.js';
import { BaseWebSocketClient } from './base-ws-client.js';
import { createReasoningConnectionParams } from '../utils/reasoning-auth-helper.js';
/**
* WebSocket клиент для пространства имен рассуждений
*/
export class ReasoningWebSocketClient extends BaseWebSocketClient {
/**
* Создает новый WebSocket клиент для рассуждений
* @param {string} baseURL Базовый URL API
* @param {ReasoningWebSocketClientOptions} options Опции клиента
*/
constructor(baseURL, options = {}) {
super(WebSocketNamespace.REASONING, baseURL, options);
/** ID активного рассуждения */
this.activeReasoningId = null;
/** Обработчики событий мышления */
this.thinkingEventHandlers = new Map();
// Инициализируем ID рассуждения, если указан
this.activeReasoningId = options.reasoningId || null;
// Инициализируем обработчики событий
this.callbacks = options.callbacks || {};
}
/**
* Подключается к серверу WebSocket
* @param {boolean} autoJoin Автоматически присоединиться к рассуждению
* @returns {Promise<boolean>} Успешность подключения
*/
async connectToReasoning(autoJoin = true) {
try {
// Получаем параметры авторизации для пространства имен рассуждений
const authParams = createReasoningConnectionParams(this.options.apiKey, this.activeReasoningId, this.sessionManager.getSessionToken(this.namespace));
// Подключаемся к серверу
const connected = await this.connect(authParams);
if (!connected) {
this.logger.error('Не удалось подключиться к серверу рассуждений');
return false;
}
// Аутентифицируемся с увеличенным таймаутом
try {
const authResult = await this.emitWithAck(WsEvents.AUTHENTICATE, {
token: this.options.apiKey,
reasoningId: this.activeReasoningId
}, 10000);
this.logger.debug('Результат аутентификации в namespace рассуждений', authResult);
// Если сервер вернул токен сессии, сохраняем его
if (authResult.sessionToken) {
this.sessionManager.saveSessionToken(this.namespace, authResult.sessionToken);
}
}
catch (error) {
this.logger.error('Ошибка аутентификации в namespace рассуждений', error);
return false;
}
// Если у нас есть ID рассуждения и включено автоматическое присоединение
if (this.activeReasoningId && autoJoin) {
return this.joinReasoning(this.activeReasoningId);
}
return true;
}
catch (error) {
this.logger.error('Ошибка подключения к пространству имен рассуждений', error);
return false;
}
}
/**
* Присоединяется к сессии рассуждения
* @param {string} reasoningId ID сессии рассуждения
* @returns {Promise<boolean>} Успешность операции
*/
async joinReasoning(reasoningId) {
if (!reasoningId) {
this.logger.error('Попытка присоединиться к пустому reasoningId');
return false;
}
// Сохраняем ID рассуждения
this.activeReasoningId = reasoningId;
try {
// Отправляем запрос на присоединение к сессии рассуждения
const joinResult = await this.emitWithAck(WsEvents.JOIN_REASONING, {
reasoningId,
token: this.options.apiKey
}, 10000);
this.logger.debug(`Результат присоединения к рассуждению ${reasoningId}`, joinResult);
if (joinResult.success !== true) {
this.logger.error(`Ошибка при присоединении к рассуждению: ${joinResult.error || 'Неизвестная ошибка'}`);
return false;
}
return true;
}
catch (error) {
this.logger.error(`Ошибка при присоединении к рассуждению ${reasoningId}`, error);
return false;
}
}
/**
* Запускает рассуждение
* @param {string} reasoningId ID сессии рассуждения
* @returns {Promise<boolean>} Успешность операции
*/
async startReasoning(reasoningId) {
const targetId = reasoningId || this.activeReasoningId;
if (!targetId) {
this.logger.error('Попытка запустить рассуждение без указания ID');
return false;
}
try {
// Отправляем запрос на запуск рассуждения
const startResult = await this.emitWithAck(WsEvents.START_REASONING, {
reasoningId: targetId,
token: this.options.apiKey
}, 10000);
this.logger.debug(`Результат запуска рассуждения ${targetId}`, startResult);
if (startResult.success !== true) {
this.logger.error(`Ошибка при запуске рассуждения: ${startResult.error || 'Неизвестная ошибка'}`);
return false;
}
return true;
}
catch (error) {
this.logger.error(`Ошибка при запуске рассуждения ${targetId}`, error);
return false;
}
}
/**
* Создает новое рассуждение на сервере
* @returns {Promise<string>} ID нового рассуждения
*/
async createNewReasoning() {
if (!this.isConnected()) {
this.logger.debug('Подключение к пространству имен рассуждений для создания нового рассуждения');
const connected = await this.connectToReasoning(false);
if (!connected) {
throw new Error('Не удалось подключиться к пространству имен рассуждений');
}
}
this.logger.debug('Отправка запроса на создание нового рассуждения');
const result = await this.emitWithAck(WsEvents.CREATE_REASONING, {
token: this.options.apiKey
}, 10000);
if (!result.reasoningId) {
throw new Error(`Сервер не вернул ID рассуждения: ${JSON.stringify(result)}`);
}
// Сохраняем ID нового рассуждения
this.activeReasoningId = result.reasoningId;
return result.reasoningId;
}
/**
* Подключается к сессии thinking с расширенными возможностями
* @param {string} [reasoningId="system"] Идентификатор рассуждения
* @param {(data: any) => void} [thinkingHandler] Обработчик событий мышления
* @returns {Promise<string>} Идентификатор сессии рассуждения
*/
async connectToThinkingSession(reasoningId = "system", thinkingHandler) {
try {
// Подключаемся к пространству имен
const connected = await this.connectToReasoning(false);
if (!connected) {
throw new Error('Не удалось подключиться к пространству имен рассуждений');
}
// Если reasoningId == "system", сервер заменит его на новый
// с префиксом "system-". Для получения нового ID нужно подписаться
// на событие создания рассуждения.
if (reasoningId === "system") {
// Будем ждать ответа о создании рассуждения
const newReasoningId = await this.createNewReasoning();
this.activeReasoningId = newReasoningId;
// Отправляем запрос на присоединение к сессии рассуждения
const joinSuccess = await this.joinReasoning(newReasoningId);
if (!joinSuccess) {
throw new Error(`Ошибка при присоединении к сессии рассуждения: ${newReasoningId}`);
}
// Отправляем запрос на запуск рассуждения
const startSuccess = await this.startReasoning(newReasoningId);
if (!startSuccess) {
throw new Error(`Ошибка при запуске рассуждения: ${newReasoningId}`);
}
// Если передан обработчик событий мышления, подписываемся
if (thinkingHandler) {
this.subscribeToThinking(newReasoningId, thinkingHandler);
}
return newReasoningId;
}
else {
this.activeReasoningId = reasoningId;
// Отправляем запрос на присоединение к сессии рассуждения
const joinSuccess = await this.joinReasoning(reasoningId);
if (!joinSuccess) {
throw new Error(`Ошибка при присоединении к сессии рассуждения: ${reasoningId}`);
}
// Отправляем запрос на запуск рассуждения
const startSuccess = await this.startReasoning(reasoningId);
if (!startSuccess) {
throw new Error(`Ошибка при запуске рассуждения: ${reasoningId}`);
}
// Если передан обработчик событий мышления, подписываемся
if (thinkingHandler) {
this.subscribeToThinking(reasoningId, thinkingHandler);
}
return reasoningId;
}
}
catch (error) {
throw new Error(`Ошибка при подключении к сессии thinking: ${error instanceof Error ? error.message : String(error)}`);
}
}
/**
* Настраивает обработчики событий для стрима от Anthropic
*/
setupAnthropicStreamHandlers() {
if (!this.isConnected() || !this.client) {
this.logger.warn('Попытка настроить обработчики событий Anthropic Stream, но клиент не подключен');
return;
}
// Устанавливаем обработчики для различных событий
// Обработчик для события начала сообщения
if (this.callbacks.onMessageStart) {
this.client.on('message_start', this.callbacks.onMessageStart);
}
// Обработчик для события начала блока контента
this.client.on('content_block_start', (data) => {
if (data.content_type === 'thinking' && this.callbacks.onThinkingStart) {
this.callbacks.onThinkingStart(data);
}
else if (this.callbacks.onContentStart) {
this.callbacks.onContentStart(data);
}
// Для отладки
if (this.callbacks.onAny) {
this.callbacks.onAny('content_block_start', data);
}
});
// Обработчик для события дельты блока контента
this.client.on('content_block_delta', (data) => {
if (data.delta.type === 'thinking_delta' && this.callbacks.onThinkingDelta && data.delta.thinking) {
this.callbacks.onThinkingDelta({
index: data.index,
delta: data.delta.thinking
});
}
else if (this.callbacks.onContentDelta) {
this.callbacks.onContentDelta(data);
}
// Для отладки
if (this.callbacks.onAny) {
this.callbacks.onAny('content_block_delta', data);
}
});
// Обработчик для события остановки блока контента
this.client.on('content_block_stop', (data) => {
if (data.content_type === 'thinking' && this.callbacks.onThinkingStop) {
this.callbacks.onThinkingStop(data);
}
else if (this.callbacks.onContentStop) {
this.callbacks.onContentStop(data);
}
// Для отладки
if (this.callbacks.onAny) {
this.callbacks.onAny('content_block_stop', data);
}
});
// Обработчик для события окончания сообщения
if (this.callbacks.onMessageStop) {
this.client.on('message_stop', this.callbacks.onMessageStop);
}
// Обработчик для события ошибки
if (this.callbacks.onError) {
this.client.on('error', this.callbacks.onError);
}
}
/**
* Подписывается на события мышления
* @param {string} reasoningId Идентификатор рассуждения
* @param {Function} handler Обработчик событий мышления
* @returns {void}
*/
subscribeToThinking(reasoningId, handler) {
// Сохраняем обработчик
this.thinkingEventHandlers.set(reasoningId, handler);
// Подписываемся на события мышления
this.on(`thinking:${reasoningId}`, handler);
// Дублируем подписку для полной совместимости
this.on(`reasoning:thinking:${reasoningId}`, handler);
this.logger.debug(`Подписан на события мышления для ${reasoningId}`);
}
/**
* Отписывается от событий мышления
* @param {string} reasoningId Идентификатор рассуждения
* @returns {void}
*/
unsubscribeFromThinking(reasoningId) {
// Получаем обработчик
const handler = this.thinkingEventHandlers.get(reasoningId);
if (handler) {
// Отписываемся от событий
this.off(`thinking:${reasoningId}`, handler);
this.off(`reasoning:thinking:${reasoningId}`, handler);
// Удаляем обработчик
this.thinkingEventHandlers.delete(reasoningId);
this.logger.debug(`Отписан от событий мышления для ${reasoningId}`);
}
}
/**
* Получает текущий ID активного рассуждения
* @returns {string | null} ID активного рассуждения или null
*/
getActiveReasoningId() {
return this.activeReasoningId;
}
/**
* Устанавливает ID активного рассуждения
* @param {string} reasoningId ID рассуждения
* @returns {boolean} Успешность установки
*/
setActiveReasoningId(reasoningId) {
if (!reasoningId) {
this.logger.error('Попытка установить пустой reasoningId');
return false;
}
this.activeReasoningId = reasoningId;
this.logger.debug(`Установлен активный reasoningId: ${reasoningId}`);
return true;
}
}
//# sourceMappingURL=reasoning-ws-client.js.map