gigachat-node
Version:
The unoffical JavaScript/TypesSript library for the GigaChat API
380 lines (379 loc) • 17.4 kB
JavaScript
"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;