solver-sdk
Version:
SDK для интеграции с Code Solver Backend API (совместимо с браузером и Node.js), с поддержкой функциональности мышления (Thinking Mode)
503 lines • 19.8 kB
JavaScript
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 { CodeSolverWebSocketClient, WebSocketNamespace } from './utils/code-solver-websocket-client.js';
import { AgentsApi } from './api/agents-api.js';
import { ChatApi } from './api/chat-api.js';
import { ModelsApi } from './api/models-api.js';
import { DependenciesApi } from './api/dependencies-api.js';
// Версия SDK
const SDK_VERSION = '1.5.0';
/**
* Определение типа среды выполнения
* @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
* Поддерживает работу как в браузере, так и в Node.js
*/
export class CodeSolverSDK {
/**
* Создает новый экземпляр SDK
* @param {CodeSolverSDKOptions} options Опции SDK
*/
constructor(options) {
/** WebSocket клиент для работы с реалтайм API */
this.wsClient = 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);
// Устанавливаем ссылку на 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 клиент
* @returns {CodeSolverWebSocketClient} WebSocket клиент
*/
getWebSocketClient() {
if (!this.wsClient) {
const wsURL = this.wsURL;
this.wsClient = new CodeSolverWebSocketClient(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.wsClient;
}
/**
* 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;
}
/**
* Получает опции SDK
*/
get options() {
return this._options;
}
/**
* Получает конфигурацию WebSocket
*/
get wsConfig() {
return this._options.websocket || {};
}
/**
* Получает URL для WebSocket соединений
*/
get wsURL() {
return this._options.wsURL || this._options.baseURL.replace(/^http/, 'ws');
}
/**
* Получает HTTP-клиент
*/
get client() {
return this.httpClient;
}
/**
* Получает текущую среду выполнения
* @returns {string} Среда выполнения ('browser', 'node', 'unknown')
*/
getEnvironment() {
return this.environment;
}
/**
* Закрывает все соединения и освобождает ресурсы
*/
dispose() {
if (this.wsClient) {
this.wsClient.disconnectAll();
this.wsClient = null;
}
}
/**
* Устанавливает глобальный обработчик ошибок SDK
* @param {(error: Error) => void} handler Функция-обработчик ошибок
*/
static setErrorHandler(handler) {
CodeSolverSDK.errorHandler = handler;
}
/**
* Обрабатывает ошибку через глобальный обработчик, если он установлен
* @param {Error} error Ошибка для обработки
*/
static handleError(error) {
if (CodeSolverSDK.errorHandler) {
CodeSolverSDK.errorHandler(error);
}
else {
console.error('[CodeSolverSDK]', error);
}
}
/**
* Возвращает текущую версию SDK
* @returns {string} Версия SDK
*/
static getVersion() {
return SDK_VERSION;
}
/**
* Возвращает текущую версию SDK
* @returns {string} Версия SDK
*/
getVersion() {
return SDK_VERSION;
}
/**
* Устанавливает новый API ключ для SDK
* @param {string} apiKey Новый API ключ
*/
setApiKey(apiKey) {
if (!apiKey) {
throw new Error('API ключ не может быть пустым');
}
// Обновляем опции
this._options.apiKey = apiKey;
// Пересоздаем HTTP клиент с новым API ключом
const newHttpClient = new HttpClient(this._options.baseURL, {
headers: {
...this._options.headers,
'Authorization': `Bearer ${apiKey}`
},
timeout: this._options.timeout,
httpsAgent: this.environment === 'node' ? this._options.httpsAgent : undefined
});
// Обновляем ссылки на клиент и API
this.httpClient = newHttpClient;
this._agents = new AgentsApi(newHttpClient);
this._context = new ContextApi(newHttpClient, this._projects);
this._projects = new ProjectsApi(newHttpClient);
this._search = new SearchApi(newHttpClient);
this._reasoning = new ReasoningApi(newHttpClient, this._projects);
this._codeModification = new CodeModificationApi(newHttpClient);
this._chat = new ChatApi(newHttpClient);
this._models = new ModelsApi(newHttpClient);
this._dependencies = new DependenciesApi(newHttpClient);
// Если есть WebSocket клиент, пересоздаем его
if (this.wsClient) {
const isConnected = this.wsClient.isConnectedToReasoning() || this.wsClient.isConnectedToIndexing();
this.wsClient.disconnectAll();
this.wsClient = new CodeSolverWebSocketClient(this._options.baseURL, {
apiKey,
headers: this._options.headers
});
// Если был подключен, восстанавливаем соединение
if (isConnected) {
this.connect().catch(() => { });
}
}
}
/**
* Подключается к WebSocket серверу
* @returns {Promise<boolean>} Promise с результатом подключения
*/
async connect() {
try {
const wsClient = this.getWebSocketClient();
// Проверяем доступность сервера перед подключением
const serverAvailable = await this.checkHealth().catch(() => false);
if (!serverAvailable) {
this.logger.warn('Сервер недоступен, WebSocket подключение может быть нестабильным');
}
// Подключаемся к обоим пространствам имен
await wsClient.connectToReasoning();
await wsClient.connectToIndexing();
return true;
}
catch (error) {
CodeSolverSDK.handleError(error);
return false;
}
}
/**
* Отключается от WebSocket сервера
* @returns {Promise<void>} Promise без результата
*/
async disconnect() {
if (this.wsClient) {
await this.wsClient.disconnectAll();
}
}
/**
* Проверяет, подключен ли SDK к WebSocket серверу
* @returns {boolean} Статус подключения
*/
isConnected() {
if (!this.wsClient)
return false;
return this.wsClient.isConnectedToReasoning() || this.wsClient.isConnectedToIndexing();
}
/**
* Проверяет доступность API (алиас для checkHealth)
* @returns {Promise<boolean>} Promise с результатом проверки
*/
async isHealthy() {
return this.checkHealth();
}
/**
* Проверяет доступность всех сервисов API
* @returns {Promise<{[key: string]: boolean}>} Статус каждого сервиса
*/
async checkServices() {
try {
const results = {
api: false,
websocket: false,
database: false,
search: false
};
try {
// Проверка API
results.api = await this.checkHealth();
// Проверка других компонентов через запрос статуса
const response = await this.httpClient.get('/api/v1/status');
if (response && typeof response === 'object') {
results.database = Boolean(response.database?.connected);
results.search = Boolean(response.search?.connected);
// Проверка WebSocket соединения
if (this.wsClient) {
results.websocket = this.wsClient.isConnectedToReasoning() ||
this.wsClient.isConnectedToIndexing();
}
else {
try {
const wsClient = this.getWebSocketClient();
await wsClient.connectToReasoning();
results.websocket = true;
await wsClient.disconnect(WebSocketNamespace.REASONING);
}
catch (e) {
results.websocket = false;
}
}
}
}
catch (error) {
// Игнорируем ошибки при проверке
}
return results;
}
catch (error) {
return {
api: false,
websocket: false,
database: false,
search: false
};
}
}
/**
* Закрывает соединение с сервером (алиас для dispose)
*/
close() {
this.dispose();
return Promise.resolve();
}
/**
* Выполняет запрос к API для генерации рассуждения с использованием Anthropic API
* с поддержкой потоковой передачи данных через WebSocket
*
* @param {ReasoningOptions} options Опции для рассуждения
* @param {AnthropicStreamCallbacks} callbacks Коллбэки для обработки событий
* @returns {Promise<void>} Promise без результата
*/
async executeReasoning(options, callbacks) {
try {
// Подключаемся к WebSocket серверу если еще не подключены
if (!this.isConnected()) {
await this.connect();
}
// Получаем WebSocket клиент
const wsClient = this.getWebSocketClient();
// Подписываемся на события
this.setupAnthropicEventHandlers(wsClient, callbacks);
// Отправляем запрос на выполнение рассуждения
wsClient.sendToReasoning('execute_reasoning', {
projectId: options.projectId,
prompt: options.query,
level: options.level || 'STANDARD',
type: options.type || 'THINKING',
currentFile: options.currentFilePath,
selection: options.selection,
options: {
modelProvider: options.options?.modelProvider || 'anthropic',
modelName: options.options?.modelName || 'claude-3-sonnet-20240229',
streamResponse: true,
...(options.options || {})
}
});
this.logger.debug(`Запрос на выполнение рассуждения отправлен: ${JSON.stringify({
projectId: options.projectId,
prompt: options.query.substring(0, 50) + '...',
level: options.level
})}`);
}
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;
}
}
/**
* Настраивает обработчики событий Anthropic API
* @param {CodeSolverWebSocketClient} wsClient WebSocket клиент
* @param {AnthropicStreamCallbacks} callbacks Коллбэки для обработки событий
* @private
*/
setupAnthropicEventHandlers(wsClient, callbacks) {
// Регистрируем обработчики для всех типов событий
wsClient.on('message_start', (data) => {
callbacks.onMessageStart?.(data);
callbacks.onAny?.('message_start', data);
}, WebSocketNamespace.REASONING);
wsClient.on('thinking_block_start', (data) => {
callbacks.onThinkingStart?.(data);
callbacks.onAny?.('thinking_block_start', data);
}, WebSocketNamespace.REASONING);
wsClient.on('thinking_block_delta', (data) => {
callbacks.onThinkingDelta?.(data);
callbacks.onAny?.('thinking_block_delta', data);
}, WebSocketNamespace.REASONING);
wsClient.on('thinking_block_stop', (data) => {
callbacks.onThinkingStop?.(data);
callbacks.onAny?.('thinking_block_stop', data);
}, WebSocketNamespace.REASONING);
wsClient.on('content_block_start', (data) => {
callbacks.onContentStart?.(data);
callbacks.onAny?.('content_block_start', data);
}, WebSocketNamespace.REASONING);
wsClient.on('content_block_delta', (data) => {
callbacks.onContentDelta?.(data);
callbacks.onAny?.('content_block_delta', data);
}, WebSocketNamespace.REASONING);
wsClient.on('content_block_stop', (data) => {
callbacks.onContentStop?.(data);
callbacks.onAny?.('content_block_stop', data);
}, WebSocketNamespace.REASONING);
wsClient.on('message_stop', (data) => {
callbacks.onMessageStop?.(data);
callbacks.onAny?.('message_stop', data);
}, WebSocketNamespace.REASONING);
wsClient.on('error', (data) => {
callbacks.onError?.(data);
callbacks.onAny?.('error', data);
}, WebSocketNamespace.REASONING);
}
}
/** Глобальный обработчик ошибок */
CodeSolverSDK.errorHandler = null;
//# sourceMappingURL=code-solver-sdk.js.map