UNPKG

gigachat-node

Version:

The unoffical JavaScript/TypesSript library for the GigaChat API

380 lines (379 loc) 17.4 kB
"use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; Object.defineProperty(exports, "__esModule", { value: true }); exports.GigaChatError = exports.GigaChat = void 0; const crypto_1 = require("crypto"); const https_1 = require("https"); const GigaChatError_1 = require("./utils/GigaChatError"); Object.defineProperty(exports, "GigaChatError", { enumerable: true, get: function () { return GigaChatError_1.GigaChatError; } }); const HTTPClient_1 = require("./utils/HTTPClient"); /** * Класс для взаимодействия с API GigaChat. * Позволяет выполнять авторизацию, отправлять запросы к модели, загружать файлы и работать с потоками данных. */ class GigaChat { /** * Создает новый экземпляр GigaChat. * @param {GigaChatConfig} config Конфигурация клиента. */ constructor({ clientSecretKey, isIgnoreTSL = true, isPersonal = true, autoRefreshToken = true, imgOn = true, }) { /** * Основной URL API GigaChat. */ this.url = 'https://gigachat.devices.sberbank.ru/api/v1'; /** * URL для авторизации и получения токена. */ this.urlAuth = 'https://ngw.devices.sberbank.ru:9443/api/v2/oauth'; /** * Область действия (scope) API для личных пользователей. */ this.scopeForPersonal = 'GIGACHAT_API_PERS'; /** * Область действия (scope) API для корпоративных пользователей. */ this.scopeForCorporation = 'GIGACHAT_API_CORP'; this.clientSecretKey = clientSecretKey; this.isIgnoreTSL = isIgnoreTSL; this.isPersonal = isPersonal; this.autoRefreshToken = autoRefreshToken; this.imgOn = imgOn; this.httpClient = new HTTPClient_1.HTTPClient(this.url, undefined, this.isIgnoreTSL); } /** * Извлекает URL изображения из ответа модели. * @param {string} completionContent Содержимое ответа. * @returns {IExtractImage | null} Результат извлечения изображения. */ extractImageSource(completionContent) { const imgTagRegex = /<img[^>]+src\s*=\s*['"]([^'"]+)['"][^>]*>/; const match = completionContent.match(imgTagRegex); if (match) { return { imageId: match[1], text: completionContent.replace(imgTagRegex, ''), }; } else { return null; } } /** * Обработка ошибки * @param {unknown} error Ошибка. * @param {() => Promise<T>} currentFunction Функция, которую надо выполнить, если проблема была в токенах и она решилась. * @returns {Promise<T>} Результат выполнения currentFunction(). * @throws {GigaChatError} Специфичная ошибка API */ handlingError(error, currentFunction) { return __awaiter(this, void 0, void 0, function* () { if (error) if (error instanceof Error) { const err = error; // TLS/SSL if (err.code === 'UNABLE_TO_VERIFY_LEAF_SIGNATURE' || err.code === 'CERT_HAS_EXPIRED') { throw new GigaChatError_1.GigaChatError(`SSL/TLS error: ${err.message}`, 'SSL_ERROR'); } // Network if (err.code === 'ECONNRESET' || err.code === 'ETIMEDOUT' || err.code === 'EPROTO') { throw new GigaChatError_1.GigaChatError(`Network error: ${err.message}`, 'NETWORK_ERROR'); } // HTTP if (/^HTTP Error: \d{3} .+$/.test(err.message)) { const match = err.message.match(/^HTTP Error: (\d{3}) (.+)$/); if (match) { const errorData = { statusCode: Number(match[1]), statusMessage: match[2], }; if (errorData.statusCode === 401) { if (this.autoRefreshToken) { yield this.createToken(); return currentFunction(); } throw new GigaChatError_1.GigaChatError('Authorization token expired', 'AUTH_EXPIRED'); } if (errorData.statusCode === 400) { throw new GigaChatError_1.GigaChatError(`Validation error: ${errorData === null || errorData === void 0 ? void 0 : errorData.statusMessage}`, 'VALIDATION_ERROR'); } if (errorData.statusCode >= 500) { throw new GigaChatError_1.GigaChatError('Internal server error', 'SERVER_ERROR'); } } } } // Обработка неизвестных ошибок throw new GigaChatError_1.GigaChatError(`Unknown error: ${error}`, 'UNKNOWN_ERROR'); }); } /** * Создает новый токен доступа. * @returns {Promise<ITokenResponse>} Данные токена. */ createToken() { return __awaiter(this, void 0, void 0, function* () { try { const requestUID = (0, crypto_1.randomUUID)(); const data = new URLSearchParams(); if (this.isPersonal) { data.append('scope', this.scopeForPersonal); } else { data.append('scope', this.scopeForCorporation); } const url = new URL(this.urlAuth); const options = { hostname: url.hostname, path: url.pathname + url.search, port: url.port || 443, method: 'POST', headers: { Authorization: `Bearer ${this.clientSecretKey}`, RqUID: requestUID, 'Content-Type': 'application/x-www-form-urlencoded', 'Content-Length': Buffer.byteLength(data.toString()), }, agent: new https_1.Agent({ rejectUnauthorized: !this.isIgnoreTSL, }), }; const response = yield this.httpClient.makeRequest(options, data.toString()); this.authorization = response.access_token; this.httpClient.setAuthorization(response.access_token); return response; } catch (error) { const err = error; if (err.code === 'ECONNRESET' || err.code === 'ETIMEDOUT' || err.code === 'EPROTO') { throw new GigaChatError_1.GigaChatError(`HTTPS error (create token): ${err.message}`, 'HTTPS_ERROR'); } if (err.code === 'UNABLE_TO_VERIFY_LEAF_SIGNATURE' || err.code === 'CERT_HAS_EXPIRED') { throw new GigaChatError_1.GigaChatError(`SSL/TLS error (create token): ${err.message}`, 'SSL_ERROR'); } throw new GigaChatError_1.GigaChatError(`Unknown error (create token): ${err.message}`, 'UNKNOWN_ERROR'); } }); } /** * Отправляет запрос на завершение чата. * @param {ICompletionRequest} data - Данные запроса. * @returns {Promise<ICompletionResponse>} Ответ сервера. */ completion(data) { return __awaiter(this, void 0, void 0, function* () { const path = '/chat/completions'; try { const response = yield this.httpClient.post(path, data); for (let index = 0; index < response.choices.length; index++) { const completionContent = response.choices[index].message.content; if (this.imgOn) { const extractedResult = this.extractImageSource(completionContent); if (extractedResult) { response.choices[index].message.image = extractedResult.imageId; response.choices[index].message.content = extractedResult.text; } } } return response; } catch (error) { return yield this.handlingError(error, () => __awaiter(this, void 0, void 0, function* () { return yield this.httpClient.post(path, data); })); } }); } /** * Отправляет потоковый запрос на завершение чата. * @param {ICompletionRequest} data - Данные запроса. * @returns {Promise<Readable>} Потоковый ответ сервера. */ completionStream(data) { return __awaiter(this, void 0, void 0, function* () { const path = '/chat/completions'; const streamData = Object.assign(Object.assign({}, data), { stream: true }); try { const response = yield this.httpClient.post(path, streamData, true); return response; } catch (error) { return yield this.handlingError(error, () => __awaiter(this, void 0, void 0, function* () { return yield this.httpClient.post(path, streamData, true); })); } }); } /** * Получает список всех моделей. * @returns {Promise<IAllModelResponse>} Ответ сервера с моделями. */ allModels() { return __awaiter(this, void 0, void 0, function* () { const path = '/models'; try { const responce = yield this.httpClient.get(path); return responce.data; } catch (error) { return yield this.handlingError(error, () => __awaiter(this, void 0, void 0, function* () { return yield this.httpClient.get(path); })); } }); } /** * Выполняет embedding запроса. * @param {string[]} input - Входные данные. * @returns {Promise<IEmbeddingResponse>} Ответ сервера с embedding. */ embedding(input) { return __awaiter(this, void 0, void 0, function* () { const path = '/embeddings'; try { const responce = yield this.httpClient.post(path, { model: 'Embeddings', input: input }); return responce.data; } catch (error) { return yield this.handlingError(error, () => __awaiter(this, void 0, void 0, function* () { return yield this.httpClient.post(path, { input: input }); })); } }); } /** * Подсчитывает количество токенов в тексте. * @param {string} model - Модель. * @param {string[]} input - Входные данные. * @returns {Promise<ISummarizeResponse[]>} Ответ с количеством токенов. */ summarize(model, input) { return __awaiter(this, void 0, void 0, function* () { const path = '/tokens/count'; try { const responce = yield this.httpClient.post(path, { model, input }); return responce; } catch (error) { return yield this.handlingError(error, () => __awaiter(this, void 0, void 0, function* () { return yield this.httpClient.post(path, { model, input }); })); } }); } /** * Загружает файл в сервис. * @param {string} pathToFile - Путь к файлу. * @param {string} [purpose='general'] - Назначение файла. * @returns {Promise<IFile>} Ответ сервера с данными файла. */ uploadFile(pathToFile, purpose = 'general') { return __awaiter(this, void 0, void 0, function* () { try { const response = yield this.httpClient.postFiles(pathToFile, purpose); return response; } catch (error) { return yield this.handlingError(error, () => __awaiter(this, void 0, void 0, function* () { return yield this.httpClient.postFiles(pathToFile, purpose); })); } }); } /** * Получение списка доступных файлов. * @returns {Promise<IFile[]>} Массив объектов с информацией о доступных файлах. */ getAllFiles() { return __awaiter(this, void 0, void 0, function* () { const path = '/files'; try { const response = yield this.httpClient.get(path); return response; } catch (error) { return yield this.handlingError(error, () => __awaiter(this, void 0, void 0, function* () { return yield this.httpClient.get(path); })); } }); } /** * Получение информации о файле по идентификатору. * @param {string} fileId - Идентификатор файла. * @returns {Promise<IFile>} Объект с информацией о файле. */ getFileInfo(fileId) { return __awaiter(this, void 0, void 0, function* () { const path = `/files/${fileId}`; try { const response = yield this.httpClient.get(path); return response; } catch (error) { return yield this.handlingError(error, () => __awaiter(this, void 0, void 0, function* () { return yield this.httpClient.get(path); })); } }); } /** * Удаление файла по идентификатору. * @param {string} fileId - Идентификатор файла. * @returns {Promise<IFileDeleteResponse>} Ответ сервера. */ deleteFile(fileId) { return __awaiter(this, void 0, void 0, function* () { const path = `/files/${fileId}/delete`; try { const response = yield this.httpClient.post(path, {}); return response; } catch (error) { return yield this.handlingError(error, () => __awaiter(this, void 0, void 0, function* () { return yield this.httpClient.post(path, {}); })); } }); } /** * Получение баланса токенов по всем моделям. * @returns {Promise<IBalanceResponse>} Ответ сервера с информацией о балансе. */ getBalance() { return __awaiter(this, void 0, void 0, function* () { const path = '/balance'; try { const response = yield this.httpClient.get(path); return response; } catch (error) { return yield this.handlingError(error, () => __awaiter(this, void 0, void 0, function* () { return yield this.httpClient.get(path); })); } }); } downloadFile(fileId) { return __awaiter(this, void 0, void 0, function* () { const path = `/files/${fileId}/content`; try { const response = yield this.httpClient.get(path, true); return response; } catch (error) { return yield this.handlingError(error, () => __awaiter(this, void 0, void 0, function* () { return yield this.httpClient.get(path); })); } }); } } exports.GigaChat = GigaChat;