first commit
This commit is contained in:
@@ -0,0 +1,639 @@
|
||||
// neuralnetworkmanager.cpp
|
||||
#include "neuralnetworkmanager.h"
|
||||
#include <QNetworkRequest>
|
||||
#include <QJsonDocument>
|
||||
#include <QUrl>
|
||||
#include <QDebug>
|
||||
#include <QDateTime>
|
||||
#include <QTimer>
|
||||
#include <QSslCipher>
|
||||
#include <QSslSocket>
|
||||
#include <QFile>
|
||||
#include <QElapsedTimer>
|
||||
|
||||
NeuralNetworkManager::NeuralNetworkManager(QObject *parent)
|
||||
: QObject(parent)
|
||||
, networkManager(new QNetworkAccessManager(this))
|
||||
{
|
||||
|
||||
// Инициализируем SSL конфигурацию
|
||||
setupSSL();
|
||||
|
||||
// Инициализируем конфигурации сетей
|
||||
setupDefaultConfigs();
|
||||
|
||||
// Инициализируем GigaChat токен как невалидный
|
||||
gigaChatAuth.tokenExpiry = 0;
|
||||
|
||||
// Подключаем обработчик сетевых ответов
|
||||
connect(networkManager, &QNetworkAccessManager::finished,
|
||||
this, &NeuralNetworkManager::handleNetworkReply);
|
||||
|
||||
}
|
||||
|
||||
NeuralNetworkManager::~NeuralNetworkManager()
|
||||
{
|
||||
// Очищаем очередь сообщений
|
||||
pendingMessages.clear();
|
||||
}
|
||||
|
||||
void NeuralNetworkManager::setupSSL()
|
||||
{
|
||||
// Настройка стандартной SSL конфигурации
|
||||
QSslConfiguration sslConfig = QSslConfiguration::defaultConfiguration();
|
||||
|
||||
// Устанавливаем протокол TLS 1.2 или новее
|
||||
sslConfig.setProtocol(QSsl::TlsV1_2OrLater);
|
||||
sslConfig.setPeerVerifyMode(QSslSocket::VerifyPeer);
|
||||
|
||||
// Загружаем российские корневые сертификаты если доступны
|
||||
QStringList certPaths = {
|
||||
"/etc/ssl/certs/russian_trusted_root_ca.pem",
|
||||
":/certs/russian_ca.pem",
|
||||
"./certs/russian_ca.pem"
|
||||
};
|
||||
|
||||
QList<QSslCertificate> russianCerts;
|
||||
for (const QString &path : certPaths) {
|
||||
if (QFile::exists(path)) {
|
||||
QFile certFile(path);
|
||||
if (certFile.open(QIODevice::ReadOnly)) {
|
||||
QByteArray certData = certFile.readAll();
|
||||
russianCerts += QSslCertificate::fromData(certData);
|
||||
certFile.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!russianCerts.isEmpty()) {
|
||||
QList<QSslCertificate> defaultCerts = sslConfig.caCertificates();
|
||||
defaultCerts += russianCerts;
|
||||
sslConfig.setCaCertificates(defaultCerts);
|
||||
}
|
||||
|
||||
// Устанавливаем как конфигурацию по умолчанию
|
||||
QSslConfiguration::setDefaultConfiguration(sslConfig);
|
||||
|
||||
}
|
||||
|
||||
void NeuralNetworkManager::setupSSLForGigaChat()
|
||||
{
|
||||
QSslConfiguration config = QSslConfiguration::defaultConfiguration();
|
||||
|
||||
// Критически важные настройки для GigaChat
|
||||
config.setProtocol(QSsl::TlsV1_2OrLater);
|
||||
config.setPeerVerifyMode(QSslSocket::VerifyPeer);
|
||||
|
||||
// Дополнительные настройки для совместимости
|
||||
config.setSslOption(QSsl::SslOptionDisableSessionTickets, false);
|
||||
config.setSslOption(QSsl::SslOptionDisableCompression, false);
|
||||
config.setSslOption(QSsl::SslOptionDisableServerNameIndication, false);
|
||||
config.setPeerVerifyDepth(2);
|
||||
|
||||
QSslConfiguration::setDefaultConfiguration(config);
|
||||
}
|
||||
|
||||
void NeuralNetworkManager::setupDefaultConfigs()
|
||||
{
|
||||
// DeepSeek
|
||||
NetworkConfig deepseekConfig;
|
||||
deepseekConfig.baseUrl = "https://api.deepseek.com";
|
||||
deepseekConfig.endpoint = "/chat/completions";
|
||||
deepseekConfig.model = "deepseek-chat";
|
||||
networkConfigs[DeepSeek] = deepseekConfig;
|
||||
|
||||
// GigaChat
|
||||
NetworkConfig gigachatConfig;
|
||||
gigachatConfig.baseUrl = "https://gigachat.devices.sberbank.ru";
|
||||
gigachatConfig.endpoint = "/api/v1/chat/completions";
|
||||
gigachatConfig.model = "GigaChat";
|
||||
networkConfigs[GigaChat] = gigachatConfig;
|
||||
|
||||
// ChatGPT
|
||||
NetworkConfig chatgptConfig;
|
||||
chatgptConfig.baseUrl = "https://api.openai.com";
|
||||
chatgptConfig.endpoint = "/v1/chat/completions";
|
||||
chatgptConfig.model = "gpt-3.5-turbo";
|
||||
networkConfigs[ChatGPT] = chatgptConfig;
|
||||
|
||||
// Ollama
|
||||
NetworkConfig ollamaConfig;
|
||||
ollamaConfig.baseUrl = "http://localhost:11434";
|
||||
ollamaConfig.endpoint = "/api/chat";
|
||||
ollamaConfig.model = "zephyr:7b";
|
||||
networkConfigs[Ollama] = ollamaConfig;
|
||||
}
|
||||
|
||||
void NeuralNetworkManager::sendMessage(const QString &message, NetworkType networkType)
|
||||
{
|
||||
QMutexLocker locker(&mutex);
|
||||
|
||||
if (!networkConfigs.contains(networkType)) {
|
||||
emit errorOccurred("Неизвестный тип нейросети");
|
||||
return;
|
||||
}
|
||||
|
||||
// Для GigaChat проверяем токен
|
||||
if (networkType == GigaChat) {
|
||||
qint64 currentTime = QDateTime::currentSecsSinceEpoch();
|
||||
|
||||
if (!isGigaChatTokenValid()) {
|
||||
|
||||
|
||||
// Добавляем сообщение в очередь
|
||||
PendingMessage pendingMsg;
|
||||
pendingMsg.message = message;
|
||||
pendingMsg.networkType = networkType;
|
||||
pendingMessages.append(pendingMsg);
|
||||
|
||||
// Запрашиваем новый токен если еще не запрашиваем
|
||||
if (!isProcessingGigaChatToken) {
|
||||
requestGigaChatToken();
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Если токен валиден, отправляем сообщение сразу
|
||||
sendMessageInternal(message, networkType);
|
||||
}
|
||||
|
||||
void NeuralNetworkManager::sendMessageInternal(const QString &message, NetworkType networkType)
|
||||
{
|
||||
QElapsedTimer timer;
|
||||
timer.start();
|
||||
|
||||
QString fullMessage = currentPrefix.isEmpty() ? message : currentPrefix + " " + message;
|
||||
|
||||
QNetworkRequest request = prepareRequest(networkType);
|
||||
QByteArray requestData = prepareRequestBody(networkType, fullMessage);
|
||||
|
||||
|
||||
|
||||
// Устанавливаем таймаут
|
||||
// request.setTransferTimeout(30000);
|
||||
|
||||
QNetworkReply *reply = networkManager->post(request, requestData);
|
||||
reply->setProperty("networkType", networkType);
|
||||
|
||||
// Для GigaChat обрабатываем SSL ошибки
|
||||
if (networkType == GigaChat) {
|
||||
connect(reply, &QNetworkReply::sslErrors,
|
||||
this, [reply](const QList<QSslError> &errors) {
|
||||
for (const QSslError &error : errors) {
|
||||
}
|
||||
reply->ignoreSslErrors();
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void NeuralNetworkManager::setPrefix(const QString &prefix)
|
||||
{
|
||||
QMutexLocker locker(&mutex);
|
||||
currentPrefix = prefix;
|
||||
}
|
||||
|
||||
void NeuralNetworkManager::setApiKey(NetworkType networkType, const QString &apiKey)
|
||||
{
|
||||
QMutexLocker locker(&mutex);
|
||||
if (networkConfigs.contains(networkType)) {
|
||||
networkConfigs[networkType].apiKey = apiKey;
|
||||
}
|
||||
}
|
||||
|
||||
void NeuralNetworkManager::setModel(NetworkType networkType, const QString &model)
|
||||
{
|
||||
QMutexLocker locker(&mutex);
|
||||
if (networkConfigs.contains(networkType)) {
|
||||
networkConfigs[networkType].model = model;
|
||||
}
|
||||
}
|
||||
|
||||
void NeuralNetworkManager::setBaseUrl(NetworkType networkType, const QString &url)
|
||||
{
|
||||
QMutexLocker locker(&mutex);
|
||||
if (networkConfigs.contains(networkType)) {
|
||||
networkConfigs[networkType].baseUrl = url;
|
||||
}
|
||||
}
|
||||
|
||||
void NeuralNetworkManager::setOllamaUrl(const QString &url)
|
||||
{
|
||||
setBaseUrl(Ollama, url);
|
||||
}
|
||||
|
||||
void NeuralNetworkManager::setGigaChatCredentials(const QString &clientId,
|
||||
const QString &authorizationCode)
|
||||
{
|
||||
QMutexLocker locker(&mutex);
|
||||
gigaChatAuth.clientId = clientId;
|
||||
gigaChatAuth.authorizationCode = authorizationCode;
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
void NeuralNetworkManager::refreshGigaChatToken()
|
||||
{
|
||||
QMutexLocker locker(&mutex);
|
||||
if (!gigaChatAuth.clientId.isEmpty() && !gigaChatAuth.authorizationCode.isEmpty()) {
|
||||
requestGigaChatToken();
|
||||
}
|
||||
}
|
||||
|
||||
QNetworkRequest NeuralNetworkManager::prepareRequest(NetworkType type)
|
||||
{
|
||||
if (!networkConfigs.contains(type)) {
|
||||
qWarning() << "Попытка подготовить запрос для неизвестного типа:" << type;
|
||||
return QNetworkRequest();
|
||||
}
|
||||
|
||||
NetworkConfig config = networkConfigs[type];
|
||||
|
||||
// Проверяем и корректируем URL
|
||||
QString baseUrl = config.baseUrl;
|
||||
QString endpoint = config.endpoint;
|
||||
|
||||
if (baseUrl.isEmpty()) {
|
||||
qWarning() << "Base URL пуст для типа" << type;
|
||||
return QNetworkRequest();
|
||||
}
|
||||
|
||||
// Убедимся, что baseUrl имеет схему
|
||||
if (!baseUrl.startsWith("http://") && !baseUrl.startsWith("https://")) {
|
||||
baseUrl = "https://" + baseUrl;
|
||||
}
|
||||
|
||||
QString urlStr = baseUrl + endpoint;
|
||||
QUrl url(urlStr);
|
||||
|
||||
if (!url.isValid()) {
|
||||
qWarning() << "Некорректный URL:" << urlStr;
|
||||
return QNetworkRequest();
|
||||
}
|
||||
|
||||
QNetworkRequest request(url);
|
||||
|
||||
// ОСОБАЯ обработка для GigaChat
|
||||
if (type == GigaChat) {
|
||||
|
||||
|
||||
// SSL конфигурация для GigaChat
|
||||
setupSSLForGigaChat();
|
||||
QSslConfiguration sslConfig = QSslConfiguration::defaultConfiguration();
|
||||
request.setSslConfiguration(sslConfig);
|
||||
|
||||
// Заголовки для GigaChat
|
||||
request.setRawHeader("Accept-Encoding", "gzip, deflate, br");
|
||||
request.setRawHeader("Accept", "application/json");
|
||||
request.setRawHeader("Content-Type", "application/json");
|
||||
|
||||
if (!gigaChatAuth.accessToken.isEmpty()) {
|
||||
QString authHeader = QString("Bearer %1").arg(gigaChatAuth.accessToken);
|
||||
request.setRawHeader("Authorization", authHeader.toUtf8());
|
||||
} else {
|
||||
qWarning() << "Токен GigaChat отсутствует!";
|
||||
}
|
||||
|
||||
|
||||
|
||||
} else {
|
||||
// Общая настройка для других сервисов
|
||||
QSslConfiguration sslConfig = QSslConfiguration::defaultConfiguration();
|
||||
sslConfig.setPeerVerifyMode(QSslSocket::VerifyPeer);
|
||||
request.setSslConfiguration(sslConfig);
|
||||
|
||||
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
|
||||
|
||||
if (type == DeepSeek || type == ChatGPT) {
|
||||
if (!config.apiKey.isEmpty()) {
|
||||
request.setRawHeader("Authorization",
|
||||
QString("Bearer %1").arg(config.apiKey).toUtf8());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Общие заголовки
|
||||
request.setRawHeader("User-Agent", "TwitchBot/1.0 (Qt Network)");
|
||||
request.setAttribute(QNetworkRequest::HTTP2AllowedAttribute, true);
|
||||
request.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true);
|
||||
|
||||
return request;
|
||||
}
|
||||
|
||||
QByteArray NeuralNetworkManager::prepareRequestBody(NetworkType type, const QString &message)
|
||||
{
|
||||
QJsonObject requestBody;
|
||||
QJsonArray messages;
|
||||
|
||||
QJsonObject messageObj;
|
||||
messageObj["role"] = "user";
|
||||
messageObj["content"] = message;
|
||||
messages.append(messageObj);
|
||||
|
||||
if (type == Ollama) {
|
||||
// Формат запроса для Ollama
|
||||
requestBody["model"] = networkConfigs[type].model;
|
||||
requestBody["messages"] = messages;
|
||||
requestBody["stream"] = false;
|
||||
|
||||
QJsonObject options;
|
||||
options["temperature"] = 0.7;
|
||||
options["num_predict"] = 200;
|
||||
requestBody["options"] = options;
|
||||
} else {
|
||||
// Формат запроса для остальных API (OpenAI-совместимый)
|
||||
requestBody["model"] = networkConfigs[type].model;
|
||||
requestBody["messages"] = messages;
|
||||
requestBody["max_tokens"] = 300;
|
||||
requestBody["temperature"] = 0.7;
|
||||
requestBody["stream"] = false;
|
||||
}
|
||||
|
||||
return QJsonDocument(requestBody).toJson();
|
||||
}
|
||||
|
||||
void NeuralNetworkManager::handleNetworkReply(QNetworkReply *reply)
|
||||
{
|
||||
QElapsedTimer timer;
|
||||
timer.start();
|
||||
|
||||
// Определяем тип запроса
|
||||
bool isTokenRequest = reply->property("isTokenRequest").toBool();
|
||||
QString url = reply->url().toString();
|
||||
|
||||
|
||||
// Если это запрос токена GigaChat
|
||||
if (isTokenRequest || url.contains("oauth")) {
|
||||
handleGigaChatTokenReply(reply);
|
||||
reply->deleteLater();
|
||||
return;
|
||||
}
|
||||
|
||||
// Обработка обычных ответов от нейросетей
|
||||
NetworkType type = static_cast<NetworkType>(reply->property("networkType").toInt());
|
||||
|
||||
if (reply->error() != QNetworkReply::NoError) {
|
||||
QString errorMsg = QString("Ошибка сети (%1): %2")
|
||||
.arg(type)
|
||||
.arg(reply->errorString());
|
||||
|
||||
|
||||
// Для GigaChat проверяем, не истек ли токен
|
||||
if (type == GigaChat &&
|
||||
(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt() == 401 ||
|
||||
reply->error() == QNetworkReply::AuthenticationRequiredError)) {
|
||||
|
||||
gigaChatAuth.tokenExpiry = 0; // Помечаем токен как устаревший
|
||||
|
||||
// Запрашиваем новый токен
|
||||
if (!isProcessingGigaChatToken) {
|
||||
requestGigaChatToken();
|
||||
}
|
||||
} else {
|
||||
emit errorOccurred(errorMsg);
|
||||
}
|
||||
|
||||
reply->deleteLater();
|
||||
return;
|
||||
}
|
||||
|
||||
QByteArray data = reply->readAll();
|
||||
QString response = parseResponse(type, data);
|
||||
|
||||
if (!response.isEmpty()) {
|
||||
QString truncatedResponse = truncateResponse(response);
|
||||
emit responseReceived(truncatedResponse);
|
||||
} else {
|
||||
emit errorOccurred("Не удалось получить ответ от нейросети");
|
||||
}
|
||||
|
||||
reply->deleteLater();
|
||||
}
|
||||
|
||||
void NeuralNetworkManager::handleGigaChatTokenReply(QNetworkReply *reply)
|
||||
{
|
||||
QMutexLocker locker(&mutex);
|
||||
|
||||
QByteArray data = reply->readAll();
|
||||
|
||||
isProcessingGigaChatToken = false;
|
||||
|
||||
if (reply->error() != QNetworkReply::NoError) {
|
||||
QString errorMsg = QString("Ошибка получения токена GigaChat: %1").arg(reply->errorString());
|
||||
|
||||
// Очищаем очередь сообщений если не удалось получить токен
|
||||
pendingMessages.clear();
|
||||
emit errorOccurred(errorMsg);
|
||||
|
||||
reply->deleteLater();
|
||||
return;
|
||||
}
|
||||
|
||||
QJsonDocument doc = QJsonDocument::fromJson(data);
|
||||
if (doc.isNull()) {
|
||||
pendingMessages.clear();
|
||||
emit errorOccurred("Неверный формат ответа от GigaChat");
|
||||
|
||||
reply->deleteLater();
|
||||
return;
|
||||
}
|
||||
|
||||
QJsonObject json = doc.object();
|
||||
|
||||
if (json.contains("access_token")) {
|
||||
gigaChatAuth.accessToken = json["access_token"].toString();
|
||||
|
||||
int expiresIn = json.contains("expires_in")
|
||||
? json["expires_in"].toInt(1800)
|
||||
: 1800;
|
||||
|
||||
// Устанавливаем время истечения (текущее время + expiresIn секунд)
|
||||
qint64 currentTime = QDateTime::currentSecsSinceEpoch();
|
||||
gigaChatAuth.tokenExpiry = currentTime + expiresIn - 300; // Вычитаем 5 минут для запаса
|
||||
|
||||
emit gigaChatTokenRefreshed();
|
||||
|
||||
// Обрабатываем сообщения из очереди
|
||||
processPendingMessages();
|
||||
|
||||
} else if (json.contains("error")) {
|
||||
QString errorMsg = json["error"].toString();
|
||||
|
||||
// Очищаем очередь сообщений
|
||||
pendingMessages.clear();
|
||||
emit errorOccurred("Ошибка GigaChat: " + errorMsg);
|
||||
} else {
|
||||
pendingMessages.clear();
|
||||
emit errorOccurred("Неизвестный формат ответа от GigaChat");
|
||||
}
|
||||
|
||||
reply->deleteLater();
|
||||
}
|
||||
|
||||
bool NeuralNetworkManager::isGigaChatTokenValid() const
|
||||
{
|
||||
qint64 currentTime = QDateTime::currentSecsSinceEpoch();
|
||||
|
||||
// Проверяем есть ли токен и не истек ли он
|
||||
bool isValid = !gigaChatAuth.accessToken.isEmpty() &&
|
||||
gigaChatAuth.tokenExpiry > currentTime;
|
||||
|
||||
qDebug() << "Проверка токена GigaChat:"
|
||||
<< "Токен:" << (!gigaChatAuth.accessToken.isEmpty() ? "есть" : "нет")
|
||||
<< "Текущее время:" << currentTime
|
||||
<< "Истекает:" << gigaChatAuth.tokenExpiry
|
||||
<< "Валиден:" << (isValid ? "да" : "нет");
|
||||
|
||||
return isValid;
|
||||
}
|
||||
|
||||
bool NeuralNetworkManager::shouldRefreshGigaChatToken() const
|
||||
{
|
||||
qint64 currentTime = QDateTime::currentSecsSinceEpoch();
|
||||
|
||||
// Проверяем, нужно ли обновить токен (осталось меньше 5 минут)
|
||||
bool shouldRefresh = gigaChatAuth.tokenExpiry > 0 &&
|
||||
(gigaChatAuth.tokenExpiry - currentTime) < 300;
|
||||
|
||||
qDebug() << "Нужно ли обновить токен:"
|
||||
<< "Текущее время:" << currentTime
|
||||
<< "Истекает:" << gigaChatAuth.tokenExpiry
|
||||
<< "Осталось секунд:" << (gigaChatAuth.tokenExpiry - currentTime)
|
||||
<< "Обновить:" << (shouldRefresh ? "да" : "нет");
|
||||
|
||||
return shouldRefresh;
|
||||
}
|
||||
|
||||
void NeuralNetworkManager::requestGigaChatToken()
|
||||
{
|
||||
if (isProcessingGigaChatToken) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (gigaChatAuth.clientId.isEmpty() || gigaChatAuth.authorizationCode.isEmpty()) {
|
||||
emit errorOccurred("Не установлены учетные данные GigaChat");
|
||||
|
||||
// Очищаем очередь сообщений
|
||||
pendingMessages.clear();
|
||||
return;
|
||||
}
|
||||
|
||||
isProcessingGigaChatToken = true;
|
||||
|
||||
QString tokenUrl = "https://ngw.devices.sberbank.ru:9443/api/v2/oauth";
|
||||
QNetworkRequest request((QUrl(tokenUrl)));
|
||||
|
||||
// Настройка SSL для GigaChat
|
||||
setupSSLForGigaChat();
|
||||
QSslConfiguration sslConfig = QSslConfiguration::defaultConfiguration();
|
||||
request.setSslConfiguration(sslConfig);
|
||||
|
||||
// Заголовки
|
||||
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded");
|
||||
request.setRawHeader("Accept", "application/json");
|
||||
request.setRawHeader("User-Agent",
|
||||
"Mozilla/5.0 (Windows NT 10.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36");
|
||||
request.setRawHeader("RqUID", gigaChatAuth.clientId.toUtf8());
|
||||
request.setRawHeader("Authorization",
|
||||
QString("Basic %1").arg(gigaChatAuth.authorizationCode).toUtf8());
|
||||
|
||||
// Тело запроса
|
||||
QByteArray postData = "scope=" + gigaChatScope.toUtf8();
|
||||
|
||||
QNetworkReply *reply = networkManager->post(request, postData);
|
||||
reply->setProperty("isTokenRequest", true);
|
||||
|
||||
// Обработка SSL ошибок
|
||||
connect(reply, &QNetworkReply::sslErrors,
|
||||
this, [reply](const QList<QSslError> &errors) {
|
||||
reply->ignoreSslErrors();
|
||||
});
|
||||
}
|
||||
|
||||
void NeuralNetworkManager::processPendingMessages()
|
||||
{
|
||||
|
||||
while (!pendingMessages.isEmpty()) {
|
||||
PendingMessage pendingMsg = pendingMessages.takeFirst();
|
||||
|
||||
// Проверяем, что токен все еще валиден
|
||||
if (isGigaChatTokenValid()) {
|
||||
sendMessageInternal(pendingMsg.message, pendingMsg.networkType);
|
||||
} else {
|
||||
pendingMessages.prepend(pendingMsg); // Возвращаем сообщение в начало очереди
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QString NeuralNetworkManager::parseResponse(NetworkType type, const QByteArray &data)
|
||||
{
|
||||
QJsonDocument doc = QJsonDocument::fromJson(data);
|
||||
if (doc.isNull()) {
|
||||
return QString();
|
||||
}
|
||||
|
||||
QJsonObject json = doc.object();
|
||||
|
||||
if (type == Ollama) {
|
||||
// Парсинг ответа Ollama
|
||||
if (json.contains("message")) {
|
||||
QJsonObject message = json["message"].toObject();
|
||||
if (message.contains("content")) {
|
||||
return message["content"].toString().trimmed();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Парсинг ответа OpenAI-совместимых API
|
||||
if (json.contains("choices") && json["choices"].isArray()) {
|
||||
QJsonArray choices = json["choices"].toArray();
|
||||
if (!choices.isEmpty()) {
|
||||
QJsonObject choice = choices[0].toObject();
|
||||
if (choice.contains("message")) {
|
||||
QJsonObject message = choice["message"].toObject();
|
||||
if (message.contains("content")) {
|
||||
return message["content"].toString().trimmed();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Проверка на ошибки API
|
||||
if (json.contains("error")) {
|
||||
QJsonObject error = json["error"].toObject();
|
||||
QString errorMsg = error.contains("message")
|
||||
? error["message"].toString()
|
||||
: "Неизвестная ошибка API";
|
||||
return QString();
|
||||
}
|
||||
}
|
||||
|
||||
return QString();
|
||||
}
|
||||
|
||||
QString NeuralNetworkManager::truncateResponse(const QString &response)
|
||||
{
|
||||
// Ограничиваем длину ответа 400 символами
|
||||
if (response.length() > 400) {
|
||||
QString truncated = response.left(400);
|
||||
|
||||
// Пытаемся обрезать по последнему предложению
|
||||
int lastDot = truncated.lastIndexOf('.');
|
||||
int lastExcl = truncated.lastIndexOf('!');
|
||||
int lastQuest = truncated.lastIndexOf('?');
|
||||
int lastBreak = qMax(lastDot, qMax(lastExcl, lastQuest));
|
||||
|
||||
if (lastBreak > 300) {
|
||||
truncated = truncated.left(lastBreak + 1);
|
||||
} else {
|
||||
truncated += "...";
|
||||
}
|
||||
|
||||
return truncated;
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
Reference in New Issue
Block a user