UNPKG

solver-sdk

Version:

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

493 lines 20.3 kB
import { HttpClient } from './utils/http-client.js'; import { ProjectsApi } from './api/projects-api.js'; import { SearchApi } from './api/search-api.js'; import { ContextApi } from './api/context-api.js'; import { ReasoningApi } from './api/reasoning-api.js'; import { CodeModificationApi } from './api/code-modification-api.js'; import { AgentsApi } from './api/agents-api.js'; import { ChatApi } from './api/chat-api/index.js'; import { ModelsApi } from './api/models-api.js'; import { DependenciesApi } from './api/dependencies-api.js'; import { FilesystemApi } from './api/filesystem-api.js'; // Импорт пространств имен WebSocket import { WebSocketNamespace } from './constants/websocket-namespaces.constants.js'; // Версия SDK const SDK_VERSION = '2.0.2'; // Импорт WebSocket клиентов import { IndexingWebSocketClient } from './ws/indexing-ws-client.js'; import { ReasoningWebSocketClient } from './ws/reasoning-ws-client.js'; import { FileSystemWsClient } from './ws/filesystem-ws-client.js'; import { DependenciesWsClient } from './ws/dependencies-ws-client.js'; /** * Определение типа среды выполнения * @returns 'browser' | 'node' | 'unknown' */ function getEnvironment() { if (typeof window !== 'undefined' && typeof window.document !== 'undefined') { return 'browser'; } else if (typeof process !== 'undefined' && process.versions && process.versions.node) { return 'node'; } return 'unknown'; } /** * Основной класс SDK для работы с Code Solver API * В версии 2.0 с современной архитектурой и без обратной совместимости */ export class CodeSolverSDK { /** * Создает новый экземпляр SDK * @param {CodeSolverSDKOptions} options Опции SDK */ constructor(options) { /** WebSocket клиенты для разных пространств имен */ this._indexingClient = null; this._reasoningClient = null; this._fileSystemClient = null; this._dependenciesClient = null; /** Активный ID проекта */ this._activeProjectId = null; /** Logger для внутреннего использования */ this.logger = { log: (message) => { if (this._options.debug) { console.log(`[CodeSolverSDK] ${message}`); } }, warn: (message) => { if (this._options.debug) { console.warn(`[CodeSolverSDK] ⚠️ ${message}`); } }, error: (message) => { console.error(`[CodeSolverSDK] 🔴 ${message}`); }, debug: (message) => { if (this._options.debug === 'verbose') { console.debug(`[CodeSolverSDK] 🔍 ${message}`); } } }; this._options = { ...options, mode: options.mode || 'auto' }; // Определяем среду выполнения this.environment = this._options.mode === 'auto' ? getEnvironment() : this._options.mode === 'browser' ? 'browser' : 'node'; // Инициализируем HTTP клиент this.httpClient = new HttpClient(this._options.baseURL, { headers: { ...(this._options.apiKey ? { 'Authorization': `Bearer ${this._options.apiKey}` } : {}), ...(this._options.headers || {}) }, timeout: this._options.timeout, httpsAgent: this.environment === 'node' ? this._options.httpsAgent : undefined }); // Инициализируем API клиенты this._projects = new ProjectsApi(this.httpClient); this._search = new SearchApi(this.httpClient); this._reasoning = new ReasoningApi(this.httpClient, this._projects); this._context = new ContextApi(this.httpClient, this._projects); this._codeModification = new CodeModificationApi(this.httpClient); this._agents = new AgentsApi(this.httpClient); this._chat = new ChatApi(this.httpClient); this._models = new ModelsApi(this.httpClient); this._dependencies = new DependenciesApi(this.httpClient); this._dependencies.setParent(this); this._filesystem = new FilesystemApi(this.httpClient); this._filesystem.setParent(this); // Устанавливаем ссылку на SDK для API классов, поддерживающих WebSocket if (typeof this._projects.setParent === 'function') { this._projects.setParent(this); } if (typeof this._reasoning.setParent === 'function') { this._reasoning.setParent(this); } if (typeof this._dependencies.setParent === 'function') { this._dependencies.setParent(this); } } /** * Проверяет доступность API * @returns {Promise<boolean>} Доступен ли API */ async checkHealth() { try { await this.httpClient.get('/health'); return true; } catch (error) { return false; } } /** * Получает WebSocket URL * @private */ get wsURL() { let url = this._options.baseURL || ''; // Заменяем протокол HTTP на WS if (url.startsWith('http://')) { url = url.replace('http://', 'ws://'); } else if (url.startsWith('https://')) { url = url.replace('https://', 'wss://'); } return url; } /** * Получает конфигурацию WebSocket * @private */ get wsConfig() { return this._options.websocket || {}; } /** * Получает клиент для индексации * @returns {IndexingWebSocketClient} Клиент для индексации */ getIndexingClient() { if (!this._indexingClient) { const wsURL = this.wsURL; this._indexingClient = new IndexingWebSocketClient(wsURL, { apiKey: this._options.apiKey, headers: this._options.headers, autoReconnect: this.wsConfig.reconnect !== false, maxRetries: this.wsConfig.reconnectAttempts || 5, retryDelay: this.wsConfig.reconnectDelay || 1000, rejectUnauthorized: this.wsConfig.rejectUnauthorized }); } return this._indexingClient; } /** * Получает клиент для рассуждений * @returns {ReasoningWebSocketClient} Клиент для рассуждений */ getReasoningClient() { if (!this._reasoningClient) { const wsURL = this.wsURL; this._reasoningClient = new ReasoningWebSocketClient(wsURL, { apiKey: this._options.apiKey, headers: this._options.headers, autoReconnect: this.wsConfig.reconnect !== false, maxRetries: this.wsConfig.reconnectAttempts || 5, retryDelay: this.wsConfig.reconnectDelay || 1000, rejectUnauthorized: this.wsConfig.rejectUnauthorized, callbacks: {} // Инициализируем пустой объект callbacks }); } return this._reasoningClient; } /** * Получает клиент для работы с файловой системой * @param {Partial<FileSystemWsClientOptions>} options Опции клиента файловой системы * @returns {FileSystemWsClient} Клиент для работы с файловой системой */ getFileSystemClient(options = {}) { if (!this._fileSystemClient) { const wsURL = this.wsURL; this._fileSystemClient = new FileSystemWsClient(wsURL, { onReadFile: options.onReadFile, onListFiles: options.onListFiles, onWatchStart: options.onWatchStart }); } return this._fileSystemClient; } /** * Получает клиент для работы с зависимостями * @returns {DependenciesWsClient} Клиент для работы с зависимостями */ getDependenciesClient() { if (!this._dependenciesClient) { const wsURL = this.wsURL; this._dependenciesClient = new DependenciesWsClient(WebSocketNamespace.DEPENDENCIES, wsURL, { apiKey: this._options.apiKey, headers: this._options.headers, autoReconnect: this.wsConfig.reconnect !== false, maxRetries: this.wsConfig.reconnectAttempts || 5, retryDelay: this.wsConfig.reconnectDelay || 1000, rejectUnauthorized: this.wsConfig.rejectUnauthorized }); } return this._dependenciesClient; } /** * Закрывает все WebSocket соединения */ disconnectAll() { if (this._indexingClient) { this._indexingClient.disconnect(); } if (this._reasoningClient) { this._reasoningClient.disconnect(); } if (this._fileSystemClient) { this._fileSystemClient.disconnect(); } if (this._dependenciesClient) { this._dependenciesClient.disconnect(); } } /** * API для работы с агентами */ get agents() { return this._agents; } /** * API для работы с контекстом */ get context() { return this._context; } /** * API для работы с проектами */ get projects() { return this._projects; } /** * API для поиска кода */ get search() { return this._search; } /** * API для работы с рассуждениями */ get reasoning() { return this._reasoning; } /** * API для модификации кода */ get codeModification() { return this._codeModification; } /** * Возвращает API для работы с чатом * @returns {ChatApi} API для работы с чатом */ get chat() { return this._chat; } /** * API для работы с моделями */ get models() { return this._models; } /** * API для работы с зависимостями */ get dependencies() { return this._dependencies; } /** * Получает API для работы с файловой системой * @returns {FilesystemApi} API для работы с файловой системой */ get filesystem() { return this._filesystem; } /** * Настраивает обработчики событий Anthropic API * @param {ReasoningWebSocketClient} reasoningClient WebSocket клиент для рассуждений * @param {AnthropicStreamCallbacks} callbacks Коллбэки для обработки событий * @private */ setupAnthropicEventHandlers(reasoningClient, callbacks) { // Устанавливаем все обработчики для клиента рассуждений reasoningClient.setupAnthropicStreamHandlers(); // Устанавливаем внешние обработчики if (callbacks) { reasoningClient.callbacks = callbacks; } } /** * Выполняет запрос к API для генерации рассуждения с использованием Anthropic API * с поддержкой потоковой передачи данных через WebSocket * * @param {ExtendedReasoningOptions} options Опции для рассуждения * @param {AnthropicStreamCallbacks} callbacks Коллбэки для обработки событий * @returns {Promise<void>} Promise без результата */ async executeReasoning(options, callbacks) { try { // Проверяем, что все необходимые параметры указаны if (!options.projectId) { throw new Error('Не указан projectId для выполнения рассуждения'); } // Подключаемся к WebSocket серверу если еще не подключены const reasoningClient = this.getReasoningClient(); if (!reasoningClient.isConnected()) { await reasoningClient.connectToReasoning(); } // Подписываемся на события this.setupAnthropicEventHandlers(reasoningClient, callbacks); // Отправляем запрос на выполнение рассуждения await reasoningClient.emitWithAck('create_reasoning', { projectId: options.projectId, request: { prompt: options.query, options: { streamMode: true, planningMode: Boolean(options.thinking), modelName: options.model || 'claude-3-sonnet' } } }); } catch (error) { this.logger.error(`Ошибка при отправке запроса на рассуждение: ${error instanceof Error ? error.message : String(error)}`); callbacks.onError?.({ code: 'SDK_ERROR', message: `Ошибка при отправке запроса: ${error instanceof Error ? error.message : String(error)}` }); throw error; } } /** * Подключается к WebSocket серверу * @param {string} projectId ID активного проекта (опционально) * @returns {Promise<boolean>} Promise с результатом подключения */ async connect(projectId) { try { // Если был передан projectId, сохраняем его как активный if (projectId) { this._activeProjectId = projectId; } // Подключаемся к различным пространствам имен await this.getIndexingClient().connect(); await this.getReasoningClient().connect(); // Подключаемся к файловой системе только если известен ID проекта if (this._activeProjectId) { await this.getFileSystemClient().connectToProject(this._activeProjectId); } else { this.logger.warn('Подключение к файловой системе пропущено: не указан projectId'); } await this.getDependenciesClient().connect(); return true; } catch (error) { CodeSolverSDK.handleError(error); return false; } } /** * Проверяет, подключен ли SDK к WebSocket серверу * @returns {boolean} Статус подключения */ isConnected() { if (!this._indexingClient && !this._reasoningClient && !this._fileSystemClient && !this._dependenciesClient) { return false; } const indexingConnected = this._indexingClient?.isConnected() || false; const reasoningConnected = this._reasoningClient?.isConnected() || false; const fileSystemConnected = this._fileSystemClient?.isConnected() || false; const dependenciesConnected = this._dependenciesClient?.isConnected() || false; return indexingConnected || reasoningConnected || fileSystemConnected || dependenciesConnected; } /** * Обрабатывает ошибку * @param {Error} error Объект ошибки */ static handleError(error) { if (CodeSolverSDK.errorHandler) { CodeSolverSDK.errorHandler(error); } else { console.error('[CodeSolverSDK] Необработанная ошибка:', error); } } /** * Устанавливает глобальный обработчик ошибок * @param {(error: Error) => void} handler Обработчик ошибок */ static setErrorHandler(handler) { CodeSolverSDK.errorHandler = handler; } /** * Очищает все ресурсы */ dispose() { this.disconnectAll(); } /** * Устанавливает новый API ключ * @param {string} apiKey Новый API ключ */ setApiKey(apiKey) { // Обновляем API ключ в опциях this._options.apiKey = apiKey; // Пересоздаем HTTP клиент с новым API ключом const newHttpClient = new HttpClient(this._options.baseURL, { headers: { ...(apiKey ? { 'Authorization': `Bearer ${apiKey}` } : {}), ...(this._options.headers || {}) }, timeout: this._options.timeout, httpsAgent: this.environment === 'node' ? this._options.httpsAgent : undefined }); // Обновляем API клиенты с новым HTTP клиентом this.httpClient = newHttpClient; this._projects = new ProjectsApi(newHttpClient); this._search = new SearchApi(newHttpClient); this._reasoning = new ReasoningApi(newHttpClient, this._projects); this._context = new ContextApi(newHttpClient, this._projects); this._codeModification = new CodeModificationApi(newHttpClient); this._agents = new AgentsApi(newHttpClient); this._chat = new ChatApi(newHttpClient); this._models = new ModelsApi(newHttpClient); this._dependencies = new DependenciesApi(newHttpClient); this._dependencies.setParent(this); this._filesystem = new FilesystemApi(newHttpClient); this._filesystem.setParent(this); // Если есть WebSocket клиенты, пересоздаем их if (this._indexingClient) { this._indexingClient.disconnect(); this._indexingClient = null; } if (this._reasoningClient) { this._reasoningClient.disconnect(); this._reasoningClient = null; } if (this._fileSystemClient) { this._fileSystemClient.disconnect(); this._fileSystemClient = null; } if (this._dependenciesClient) { this._dependenciesClient.disconnect(); this._dependenciesClient = null; } } /** * Устанавливает активный проект * @param {string} projectId ID проекта */ setActiveProject(projectId) { this._activeProjectId = projectId; // Если уже есть подключение к файловой системе, переподключаемся с новым projectId if (this._fileSystemClient && this._fileSystemClient.isConnected()) { this._fileSystemClient.disconnect(); this._fileSystemClient.connectToProject(projectId); } } /** * Возвращает ID активного проекта * @returns {string | null} ID активного проекта или null */ getActiveProject() { return this._activeProjectId; } } /** Глобальный обработчик ошибок */ CodeSolverSDK.errorHandler = null; //# sourceMappingURL=code-solver-sdk.js.map