UNPKG

solver-sdk

Version:

SDK для интеграции с Code Solver Backend API

326 lines 17.2 kB
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