mirror of
https://github.com/Palm1r/QodeAssist.git
synced 2025-11-14 14:02:47 -05:00
refactor: Remove non-streaming support (#229)
This commit is contained in:
@ -98,8 +98,7 @@ void ClientInterface::sendMessage(
|
|||||||
config.provider = provider;
|
config.provider = provider;
|
||||||
config.promptTemplate = promptTemplate;
|
config.promptTemplate = promptTemplate;
|
||||||
if (provider->providerID() == LLMCore::ProviderID::GoogleAI) {
|
if (provider->providerID() == LLMCore::ProviderID::GoogleAI) {
|
||||||
QString stream = chatAssistantSettings.stream() ? QString{"streamGenerateContent?alt=sse"}
|
QString stream = QString{"streamGenerateContent?alt=sse"};
|
||||||
: QString{"generateContent?"};
|
|
||||||
config.url = QUrl(QString("%1/models/%2:%3")
|
config.url = QUrl(QString("%1/models/%2:%3")
|
||||||
.arg(
|
.arg(
|
||||||
Settings::generalSettings().caUrl(),
|
Settings::generalSettings().caUrl(),
|
||||||
@ -109,8 +108,7 @@ void ClientInterface::sendMessage(
|
|||||||
config.url
|
config.url
|
||||||
= QString("%1%2").arg(Settings::generalSettings().caUrl(), provider->chatEndpoint());
|
= QString("%1%2").arg(Settings::generalSettings().caUrl(), provider->chatEndpoint());
|
||||||
config.providerRequest
|
config.providerRequest
|
||||||
= {{"model", Settings::generalSettings().caModel()},
|
= {{"model", Settings::generalSettings().caModel()}, {"stream", true}};
|
||||||
{"stream", chatAssistantSettings.stream()}};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
config.apiKey = provider->apiKey();
|
config.apiKey = provider->apiKey();
|
||||||
|
|||||||
@ -224,13 +224,12 @@ void LLMClientInterface::handleCompletion(const QJsonObject &request)
|
|||||||
config.promptTemplate = promptTemplate;
|
config.promptTemplate = promptTemplate;
|
||||||
// TODO refactor networking
|
// TODO refactor networking
|
||||||
if (provider->providerID() == LLMCore::ProviderID::GoogleAI) {
|
if (provider->providerID() == LLMCore::ProviderID::GoogleAI) {
|
||||||
QString stream = m_completeSettings.stream() ? QString{"streamGenerateContent?alt=sse"}
|
QString stream = QString{"streamGenerateContent?alt=sse"};
|
||||||
: QString{"generateContent?"};
|
|
||||||
config.url = QUrl(QString("%1/models/%2:%3").arg(url, modelName, stream));
|
config.url = QUrl(QString("%1/models/%2:%3").arg(url, modelName, stream));
|
||||||
} else {
|
} else {
|
||||||
config.url = QUrl(
|
config.url = QUrl(
|
||||||
QString("%1%2").arg(url, endpoint(provider, promptTemplate->type(), isPreset1Active)));
|
QString("%1%2").arg(url, endpoint(provider, promptTemplate->type(), isPreset1Active)));
|
||||||
config.providerRequest = {{"model", modelName}, {"stream", m_completeSettings.stream()}};
|
config.providerRequest = {{"model", modelName}, {"stream", true}};
|
||||||
}
|
}
|
||||||
config.apiKey = provider->apiKey();
|
config.apiKey = provider->apiKey();
|
||||||
config.multiLineCompletion = m_completeSettings.multiLineCompletion();
|
config.multiLineCompletion = m_completeSettings.multiLineCompletion();
|
||||||
|
|||||||
@ -138,8 +138,7 @@ void QuickRefactorHandler::prepareAndSendRequest(
|
|||||||
config.provider = provider;
|
config.provider = provider;
|
||||||
config.promptTemplate = promptTemplate;
|
config.promptTemplate = promptTemplate;
|
||||||
config.url = QString("%1%2").arg(settings.caUrl(), provider->chatEndpoint());
|
config.url = QString("%1%2").arg(settings.caUrl(), provider->chatEndpoint());
|
||||||
config.providerRequest
|
config.providerRequest = {{"model", settings.caModel()}, {"stream", true}};
|
||||||
= {{"model", settings.caModel()}, {"stream", Settings::chatAssistantSettings().stream()}};
|
|
||||||
config.apiKey = provider->apiKey();
|
config.apiKey = provider->apiKey();
|
||||||
|
|
||||||
LLMCore::ContextData context = prepareContext(editor, range, instructions);
|
LLMCore::ContextData context = prepareContext(editor, range, instructions);
|
||||||
|
|||||||
@ -15,6 +15,8 @@ add_library(LLMCore STATIC
|
|||||||
ValidationUtils.hpp ValidationUtils.cpp
|
ValidationUtils.hpp ValidationUtils.cpp
|
||||||
ProviderID.hpp
|
ProviderID.hpp
|
||||||
HttpClient.hpp HttpClient.cpp
|
HttpClient.hpp HttpClient.cpp
|
||||||
|
DataBuffers.hpp
|
||||||
|
SSEBuffer.hpp SSEBuffer.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
target_link_libraries(LLMCore
|
target_link_libraries(LLMCore
|
||||||
|
|||||||
39
llmcore/DataBuffers.hpp
Normal file
39
llmcore/DataBuffers.hpp
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2025 Petr Mironychev
|
||||||
|
*
|
||||||
|
* This file is part of QodeAssist.
|
||||||
|
*
|
||||||
|
* QodeAssist is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* QodeAssist is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with QodeAssist. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "SSEBuffer.hpp"
|
||||||
|
#include <QString>
|
||||||
|
|
||||||
|
namespace QodeAssist::LLMCore {
|
||||||
|
|
||||||
|
struct DataBuffers
|
||||||
|
{
|
||||||
|
SSEBuffer rawStreamBuffer;
|
||||||
|
QString responseContent;
|
||||||
|
|
||||||
|
void clear()
|
||||||
|
{
|
||||||
|
rawStreamBuffer.clear();
|
||||||
|
responseContent.clear();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace QodeAssist::LLMCore
|
||||||
@ -1,18 +1,31 @@
|
|||||||
#include "Provider.hpp"
|
#include "Provider.hpp"
|
||||||
|
|
||||||
|
#include <QJsonDocument>
|
||||||
|
|
||||||
namespace QodeAssist::LLMCore {
|
namespace QodeAssist::LLMCore {
|
||||||
|
|
||||||
Provider::Provider(QObject *parent)
|
Provider::Provider(QObject *parent)
|
||||||
: QObject(parent)
|
: QObject(parent)
|
||||||
, m_httpClient(std::make_unique<HttpClient>())
|
, m_httpClient(new HttpClient(this))
|
||||||
{
|
{
|
||||||
connect(m_httpClient.get(), &HttpClient::dataReceived, this, &Provider::onDataReceived);
|
connect(m_httpClient, &HttpClient::dataReceived, this, &Provider::onDataReceived);
|
||||||
connect(m_httpClient.get(), &HttpClient::requestFinished, this, &Provider::onRequestFinished);
|
connect(m_httpClient, &HttpClient::requestFinished, this, &Provider::onRequestFinished);
|
||||||
}
|
}
|
||||||
|
|
||||||
HttpClient *Provider::httpClient() const
|
HttpClient *Provider::httpClient() const
|
||||||
{
|
{
|
||||||
return m_httpClient.get();
|
return m_httpClient;
|
||||||
|
}
|
||||||
|
|
||||||
|
QJsonObject Provider::parseEventLine(const QString &line)
|
||||||
|
{
|
||||||
|
if (!line.startsWith("data: "))
|
||||||
|
return QJsonObject();
|
||||||
|
|
||||||
|
QString jsonStr = line.mid(6);
|
||||||
|
|
||||||
|
QJsonDocument doc = QJsonDocument::fromJson(jsonStr.toUtf8());
|
||||||
|
return doc.object();
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace QodeAssist::LLMCore
|
} // namespace QodeAssist::LLMCore
|
||||||
|
|||||||
@ -25,6 +25,7 @@
|
|||||||
#include <QString>
|
#include <QString>
|
||||||
|
|
||||||
#include "ContextData.hpp"
|
#include "ContextData.hpp"
|
||||||
|
#include "DataBuffers.hpp"
|
||||||
#include "HttpClient.hpp"
|
#include "HttpClient.hpp"
|
||||||
#include "PromptTemplate.hpp"
|
#include "PromptTemplate.hpp"
|
||||||
#include "RequestType.hpp"
|
#include "RequestType.hpp"
|
||||||
@ -73,8 +74,14 @@ signals:
|
|||||||
void fullResponseReceived(const QString &requestId, const QString &fullText);
|
void fullResponseReceived(const QString &requestId, const QString &fullText);
|
||||||
void requestFailed(const QString &requestId, const QString &error);
|
void requestFailed(const QString &requestId, const QString &error);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
QJsonObject parseEventLine(const QString &line);
|
||||||
|
|
||||||
|
QHash<RequestID, DataBuffers> m_dataBuffers;
|
||||||
|
QHash<RequestID, QUrl> m_requestUrls;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::unique_ptr<HttpClient> m_httpClient;
|
HttpClient *m_httpClient;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace QodeAssist::LLMCore
|
} // namespace QodeAssist::LLMCore
|
||||||
|
|||||||
@ -17,9 +17,13 @@
|
|||||||
* along with QodeAssist. If not, see <https://www.gnu.org/licenses/>.
|
* along with QodeAssist. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <QString>
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
namespace QodeAssist::LLMCore {
|
namespace QodeAssist::LLMCore {
|
||||||
|
|
||||||
enum RequestType { CodeCompletion, Chat, Embedding };
|
enum RequestType { CodeCompletion, Chat, Embedding };
|
||||||
|
|
||||||
|
using RequestID = QString;
|
||||||
}
|
}
|
||||||
|
|||||||
51
llmcore/SSEBuffer.cpp
Normal file
51
llmcore/SSEBuffer.cpp
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2025 Petr Mironychev
|
||||||
|
*
|
||||||
|
* This file is part of QodeAssist.
|
||||||
|
*
|
||||||
|
* QodeAssist is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* QodeAssist is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with QodeAssist. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "SSEBuffer.hpp"
|
||||||
|
|
||||||
|
namespace QodeAssist::LLMCore {
|
||||||
|
|
||||||
|
QStringList SSEBuffer::processData(const QByteArray &data)
|
||||||
|
{
|
||||||
|
m_buffer += QString::fromUtf8(data);
|
||||||
|
|
||||||
|
QStringList lines = m_buffer.split('\n');
|
||||||
|
m_buffer = lines.takeLast();
|
||||||
|
|
||||||
|
lines.removeAll(QString());
|
||||||
|
|
||||||
|
return lines;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SSEBuffer::clear()
|
||||||
|
{
|
||||||
|
m_buffer.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
QString SSEBuffer::currentBuffer() const
|
||||||
|
{
|
||||||
|
return m_buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SSEBuffer::hasIncompleteData() const
|
||||||
|
{
|
||||||
|
return !m_buffer.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace QodeAssist::LLMCore
|
||||||
42
llmcore/SSEBuffer.hpp
Normal file
42
llmcore/SSEBuffer.hpp
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2025 Petr Mironychev
|
||||||
|
*
|
||||||
|
* This file is part of QodeAssist.
|
||||||
|
*
|
||||||
|
* QodeAssist is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* QodeAssist is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with QodeAssist. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QString>
|
||||||
|
#include <QStringList>
|
||||||
|
|
||||||
|
namespace QodeAssist::LLMCore {
|
||||||
|
|
||||||
|
class SSEBuffer
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
SSEBuffer() = default;
|
||||||
|
|
||||||
|
QStringList processData(const QByteArray &data);
|
||||||
|
|
||||||
|
void clear();
|
||||||
|
QString currentBuffer() const;
|
||||||
|
bool hasIncompleteData() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
QString m_buffer;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace QodeAssist::LLMCore
|
||||||
@ -174,6 +174,9 @@ LLMCore::ProviderID ClaudeProvider::providerID() const
|
|||||||
void ClaudeProvider::sendRequest(
|
void ClaudeProvider::sendRequest(
|
||||||
const QString &requestId, const QUrl &url, const QJsonObject &payload)
|
const QString &requestId, const QUrl &url, const QJsonObject &payload)
|
||||||
{
|
{
|
||||||
|
m_dataBuffers[requestId].clear();
|
||||||
|
m_requestUrls[requestId] = url;
|
||||||
|
|
||||||
QNetworkRequest networkRequest(url);
|
QNetworkRequest networkRequest(url);
|
||||||
prepareNetworkRequest(networkRequest);
|
prepareNetworkRequest(networkRequest);
|
||||||
|
|
||||||
@ -187,50 +190,49 @@ void ClaudeProvider::sendRequest(
|
|||||||
|
|
||||||
void ClaudeProvider::onDataReceived(const QString &requestId, const QByteArray &data)
|
void ClaudeProvider::onDataReceived(const QString &requestId, const QByteArray &data)
|
||||||
{
|
{
|
||||||
QString &accumulatedResponse = m_accumulatedResponses[requestId];
|
LLMCore::DataBuffers &buffers = m_dataBuffers[requestId];
|
||||||
|
QStringList lines = buffers.rawStreamBuffer.processData(data);
|
||||||
|
|
||||||
QString tempResponse;
|
QString tempResponse;
|
||||||
bool isComplete = false;
|
bool isComplete = false;
|
||||||
|
|
||||||
QByteArrayList lines = data.split('\n');
|
for (const QString &line : lines) {
|
||||||
for (const QByteArray &line : lines) {
|
QJsonObject responseObj = parseEventLine(line);
|
||||||
QByteArray trimmedLine = line.trimmed();
|
if (responseObj.isEmpty())
|
||||||
if (trimmedLine.isEmpty())
|
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (!trimmedLine.startsWith("data:"))
|
|
||||||
continue;
|
|
||||||
trimmedLine = trimmedLine.mid(6);
|
|
||||||
|
|
||||||
QJsonDocument jsonResponse = QJsonDocument::fromJson(trimmedLine);
|
|
||||||
if (jsonResponse.isNull())
|
|
||||||
continue;
|
|
||||||
|
|
||||||
QJsonObject responseObj = jsonResponse.object();
|
|
||||||
QString eventType = responseObj["type"].toString();
|
QString eventType = responseObj["type"].toString();
|
||||||
|
|
||||||
if (eventType == "message_delta") {
|
if (eventType == "message_start") {
|
||||||
if (responseObj.contains("delta")) {
|
QString messageId = responseObj["message"].toObject()["id"].toString();
|
||||||
QJsonObject delta = responseObj["delta"].toObject();
|
LOG_MESSAGE(QString("Claude message started: %1").arg(messageId));
|
||||||
if (delta.contains("stop_reason")) {
|
|
||||||
isComplete = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (eventType == "content_block_delta") {
|
} else if (eventType == "content_block_delta") {
|
||||||
QJsonObject delta = responseObj["delta"].toObject();
|
QJsonObject delta = responseObj["delta"].toObject();
|
||||||
if (delta["type"].toString() == "text_delta") {
|
if (delta["type"].toString() == "text_delta") {
|
||||||
tempResponse += delta["text"].toString();
|
tempResponse += delta["text"].toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
} else if (eventType == "message_delta") {
|
||||||
|
QJsonObject delta = responseObj["delta"].toObject();
|
||||||
|
if (delta.contains("stop_reason")) {
|
||||||
|
isComplete = true;
|
||||||
|
QJsonObject usage = responseObj["usage"].toObject();
|
||||||
|
LOG_MESSAGE(QString("Tokens: input=%1, output=%2")
|
||||||
|
.arg(usage["input_tokens"].toInt())
|
||||||
|
.arg(usage["output_tokens"].toInt()));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!tempResponse.isEmpty()) {
|
if (!tempResponse.isEmpty()) {
|
||||||
accumulatedResponse += tempResponse;
|
buffers.responseContent += tempResponse;
|
||||||
emit partialResponseReceived(requestId, tempResponse);
|
emit partialResponseReceived(requestId, tempResponse);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isComplete) {
|
if (isComplete) {
|
||||||
emit fullResponseReceived(requestId, accumulatedResponse);
|
emit fullResponseReceived(requestId, buffers.responseContent);
|
||||||
m_accumulatedResponses.remove(requestId);
|
m_dataBuffers.remove(requestId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -240,15 +242,16 @@ void ClaudeProvider::onRequestFinished(const QString &requestId, bool success, c
|
|||||||
LOG_MESSAGE(QString("ClaudeProvider request %1 failed: %2").arg(requestId, error));
|
LOG_MESSAGE(QString("ClaudeProvider request %1 failed: %2").arg(requestId, error));
|
||||||
emit requestFailed(requestId, error);
|
emit requestFailed(requestId, error);
|
||||||
} else {
|
} else {
|
||||||
if (m_accumulatedResponses.contains(requestId)) {
|
if (m_dataBuffers.contains(requestId)) {
|
||||||
const QString fullResponse = m_accumulatedResponses[requestId];
|
const LLMCore::DataBuffers &buffers = m_dataBuffers[requestId];
|
||||||
if (!fullResponse.isEmpty()) {
|
if (!buffers.responseContent.isEmpty()) {
|
||||||
emit fullResponseReceived(requestId, fullResponse);
|
emit fullResponseReceived(requestId, buffers.responseContent);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
m_accumulatedResponses.remove(requestId);
|
m_dataBuffers.remove(requestId);
|
||||||
|
m_requestUrls.remove(requestId);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace QodeAssist::Providers
|
} // namespace QodeAssist::Providers
|
||||||
|
|||||||
@ -47,9 +47,6 @@ public:
|
|||||||
public slots:
|
public slots:
|
||||||
void onDataReceived(const QString &requestId, const QByteArray &data) override;
|
void onDataReceived(const QString &requestId, const QByteArray &data) override;
|
||||||
void onRequestFinished(const QString &requestId, bool success, const QString &error) override;
|
void onRequestFinished(const QString &requestId, bool success, const QString &error) override;
|
||||||
|
|
||||||
private:
|
|
||||||
QHash<QString, QString> m_accumulatedResponses;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace QodeAssist::Providers
|
} // namespace QodeAssist::Providers
|
||||||
|
|||||||
@ -172,6 +172,9 @@ LLMCore::ProviderID GoogleAIProvider::providerID() const
|
|||||||
void GoogleAIProvider::sendRequest(
|
void GoogleAIProvider::sendRequest(
|
||||||
const QString &requestId, const QUrl &url, const QJsonObject &payload)
|
const QString &requestId, const QUrl &url, const QJsonObject &payload)
|
||||||
{
|
{
|
||||||
|
m_dataBuffers[requestId].clear();
|
||||||
|
m_requestUrls[requestId] = url;
|
||||||
|
|
||||||
QNetworkRequest networkRequest(url);
|
QNetworkRequest networkRequest(url);
|
||||||
prepareNetworkRequest(networkRequest);
|
prepareNetworkRequest(networkRequest);
|
||||||
|
|
||||||
@ -186,8 +189,6 @@ void GoogleAIProvider::sendRequest(
|
|||||||
|
|
||||||
void GoogleAIProvider::onDataReceived(const QString &requestId, const QByteArray &data)
|
void GoogleAIProvider::onDataReceived(const QString &requestId, const QByteArray &data)
|
||||||
{
|
{
|
||||||
QString &accumulatedResponse = m_accumulatedResponses[requestId];
|
|
||||||
|
|
||||||
if (data.isEmpty()) {
|
if (data.isEmpty()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -205,204 +206,85 @@ void GoogleAIProvider::onDataReceived(const QString &requestId, const QByteArray
|
|||||||
|
|
||||||
LOG_MESSAGE(fullError);
|
LOG_MESSAGE(fullError);
|
||||||
emit requestFailed(requestId, fullError);
|
emit requestFailed(requestId, fullError);
|
||||||
m_accumulatedResponses.remove(requestId);
|
m_dataBuffers.remove(requestId);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isDone = false;
|
bool isDone = handleStreamResponse(requestId, data);
|
||||||
|
|
||||||
if (data.startsWith("data: ")) {
|
|
||||||
isDone = handleStreamResponse(requestId, data, accumulatedResponse);
|
|
||||||
} else {
|
|
||||||
isDone = handleRegularResponse(requestId, data, accumulatedResponse);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isDone) {
|
if (isDone) {
|
||||||
emit fullResponseReceived(requestId, accumulatedResponse);
|
LLMCore::DataBuffers &buffers = m_dataBuffers[requestId];
|
||||||
m_accumulatedResponses.remove(requestId);
|
emit fullResponseReceived(requestId, buffers.responseContent);
|
||||||
|
m_dataBuffers.remove(requestId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void GoogleAIProvider::onRequestFinished(const QString &requestId, bool success, const QString &error)
|
void GoogleAIProvider::onRequestFinished(const QString &requestId, bool success, const QString &error)
|
||||||
{
|
{
|
||||||
if (!success) {
|
if (!success) {
|
||||||
QString detailedError = error;
|
LOG_MESSAGE(QString("GoogleAIProvider request %1 failed: %2").arg(requestId, error));
|
||||||
|
emit requestFailed(requestId, error);
|
||||||
if (m_accumulatedResponses.contains(requestId)) {
|
|
||||||
const QString response = m_accumulatedResponses[requestId];
|
|
||||||
if (!response.isEmpty()) {
|
|
||||||
QJsonParseError parseError;
|
|
||||||
QJsonDocument doc = QJsonDocument::fromJson(response.toUtf8(), &parseError);
|
|
||||||
if (!doc.isNull() && doc.isObject()) {
|
|
||||||
QJsonObject obj = doc.object();
|
|
||||||
if (obj.contains("error")) {
|
|
||||||
QJsonObject errorObj = obj["error"].toObject();
|
|
||||||
QString apiError = errorObj["message"].toString();
|
|
||||||
int errorCode = errorObj["code"].toInt();
|
|
||||||
detailedError
|
|
||||||
= QString("Google AI API Error %1: %2").arg(errorCode).arg(apiError);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
LOG_MESSAGE(QString("GoogleAIProvider request %1 failed: %2").arg(requestId, detailedError));
|
|
||||||
emit requestFailed(requestId, detailedError);
|
|
||||||
} else {
|
} else {
|
||||||
if (m_accumulatedResponses.contains(requestId)) {
|
if (m_dataBuffers.contains(requestId)) {
|
||||||
const QString fullResponse = m_accumulatedResponses[requestId];
|
const LLMCore::DataBuffers &buffers = m_dataBuffers[requestId];
|
||||||
if (!fullResponse.isEmpty()) {
|
if (!buffers.responseContent.isEmpty()) {
|
||||||
emit fullResponseReceived(requestId, fullResponse);
|
emit fullResponseReceived(requestId, buffers.responseContent);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
m_accumulatedResponses.remove(requestId);
|
m_dataBuffers.remove(requestId);
|
||||||
|
m_requestUrls.remove(requestId);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GoogleAIProvider::handleStreamResponse(
|
bool GoogleAIProvider::handleStreamResponse(const QString &requestId, const QByteArray &data)
|
||||||
const QString &requestId, const QByteArray &data, QString &accumulatedResponse)
|
|
||||||
{
|
{
|
||||||
QByteArrayList lines = data.split('\n');
|
LLMCore::DataBuffers &buffers = m_dataBuffers[requestId];
|
||||||
|
QStringList lines = buffers.rawStreamBuffer.processData(data);
|
||||||
|
|
||||||
bool isDone = false;
|
bool isDone = false;
|
||||||
|
QString tempResponse;
|
||||||
|
|
||||||
for (const QByteArray &line : lines) {
|
for (const QString &line : lines) {
|
||||||
QByteArray trimmedLine = line.trimmed();
|
if (line.trimmed().isEmpty()) {
|
||||||
if (trimmedLine.isEmpty()) {
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (trimmedLine == "data: [DONE]") {
|
QJsonObject responseObj = parseEventLine(line);
|
||||||
isDone = true;
|
if (responseObj.isEmpty())
|
||||||
continue;
|
continue;
|
||||||
}
|
|
||||||
|
|
||||||
if (trimmedLine.startsWith("data: ")) {
|
if (responseObj.contains("candidates")) {
|
||||||
QByteArray jsonData = trimmedLine.mid(6);
|
QJsonArray candidates = responseObj["candidates"].toArray();
|
||||||
QJsonParseError parseError;
|
for (const QJsonValue &candidate : candidates) {
|
||||||
QJsonDocument doc = QJsonDocument::fromJson(jsonData, &parseError);
|
QJsonObject candidateObj = candidate.toObject();
|
||||||
if (doc.isNull() || !doc.isObject()) {
|
if (candidateObj.contains("content")) {
|
||||||
if (parseError.error != QJsonParseError::NoError) {
|
QJsonObject content = candidateObj["content"].toObject();
|
||||||
LOG_MESSAGE(QString("JSON parse error in GoogleAI stream: %1")
|
if (content.contains("parts")) {
|
||||||
.arg(parseError.errorString()));
|
QJsonArray parts = content["parts"].toArray();
|
||||||
}
|
for (const QJsonValue &part : parts) {
|
||||||
continue;
|
QJsonObject partObj = part.toObject();
|
||||||
}
|
if (partObj.contains("text")) {
|
||||||
|
tempResponse += partObj["text"].toString();
|
||||||
QJsonObject responseObj = doc.object();
|
|
||||||
|
|
||||||
if (responseObj.contains("error")) {
|
|
||||||
QJsonObject error = responseObj["error"].toObject();
|
|
||||||
QString errorMessage = error["message"].toString();
|
|
||||||
int errorCode = error["code"].toInt();
|
|
||||||
QString fullError
|
|
||||||
= QString("Google AI Stream Error %1: %2").arg(errorCode).arg(errorMessage);
|
|
||||||
|
|
||||||
LOG_MESSAGE(fullError);
|
|
||||||
emit requestFailed(requestId, fullError);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (responseObj.contains("candidates")) {
|
|
||||||
QJsonArray candidates = responseObj["candidates"].toArray();
|
|
||||||
if (!candidates.isEmpty()) {
|
|
||||||
QJsonObject candidate = candidates.first().toObject();
|
|
||||||
|
|
||||||
if (candidate.contains("finishReason")
|
|
||||||
&& !candidate["finishReason"].toString().isEmpty()) {
|
|
||||||
isDone = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (candidate.contains("content")) {
|
|
||||||
QJsonObject content = candidate["content"].toObject();
|
|
||||||
if (content.contains("parts")) {
|
|
||||||
QJsonArray parts = content["parts"].toArray();
|
|
||||||
QString partialContent;
|
|
||||||
for (const auto &part : parts) {
|
|
||||||
QJsonObject partObj = part.toObject();
|
|
||||||
if (partObj.contains("text")) {
|
|
||||||
partialContent += partObj["text"].toString();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!partialContent.isEmpty()) {
|
|
||||||
accumulatedResponse += partialContent;
|
|
||||||
emit partialResponseReceived(requestId, partialContent);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (candidateObj.contains("finishReason")) {
|
||||||
|
isDone = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!tempResponse.isEmpty()) {
|
||||||
|
buffers.responseContent += tempResponse;
|
||||||
|
emit partialResponseReceived(requestId, tempResponse);
|
||||||
|
}
|
||||||
|
|
||||||
return isDone;
|
return isDone;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GoogleAIProvider::handleRegularResponse(
|
|
||||||
const QString &requestId, const QByteArray &data, QString &accumulatedResponse)
|
|
||||||
{
|
|
||||||
QJsonParseError parseError;
|
|
||||||
QJsonDocument doc = QJsonDocument::fromJson(data, &parseError);
|
|
||||||
if (doc.isNull() || !doc.isObject()) {
|
|
||||||
QString error
|
|
||||||
= QString("Invalid JSON response from Google AI API: %1").arg(parseError.errorString());
|
|
||||||
LOG_MESSAGE(error);
|
|
||||||
emit requestFailed(requestId, error);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
QJsonObject response = doc.object();
|
|
||||||
|
|
||||||
if (response.contains("error")) {
|
|
||||||
QJsonObject error = response["error"].toObject();
|
|
||||||
QString errorMessage = error["message"].toString();
|
|
||||||
int errorCode = error["code"].toInt();
|
|
||||||
QString fullError = QString("Google AI API Error %1: %2").arg(errorCode).arg(errorMessage);
|
|
||||||
|
|
||||||
LOG_MESSAGE(fullError);
|
|
||||||
emit requestFailed(requestId, fullError);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!response.contains("candidates") || response["candidates"].toArray().isEmpty()) {
|
|
||||||
QString error = "No candidates in Google AI response";
|
|
||||||
LOG_MESSAGE(error);
|
|
||||||
emit requestFailed(requestId, error);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
QJsonObject candidate = response["candidates"].toArray().first().toObject();
|
|
||||||
if (!candidate.contains("content")) {
|
|
||||||
QString error = "No content in Google AI response candidate";
|
|
||||||
LOG_MESSAGE(error);
|
|
||||||
emit requestFailed(requestId, error);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
QJsonObject content = candidate["content"].toObject();
|
|
||||||
if (!content.contains("parts")) {
|
|
||||||
QString error = "No parts in Google AI response content";
|
|
||||||
LOG_MESSAGE(error);
|
|
||||||
emit requestFailed(requestId, error);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
QJsonArray parts = content["parts"].toArray();
|
|
||||||
QString responseContent;
|
|
||||||
for (const auto &part : parts) {
|
|
||||||
QJsonObject partObj = part.toObject();
|
|
||||||
if (partObj.contains("text")) {
|
|
||||||
responseContent += partObj["text"].toString();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!responseContent.isEmpty()) {
|
|
||||||
accumulatedResponse += responseContent;
|
|
||||||
emit partialResponseReceived(requestId, responseContent);
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace QodeAssist::Providers
|
} // namespace QodeAssist::Providers
|
||||||
|
|||||||
@ -49,11 +49,7 @@ public slots:
|
|||||||
void onRequestFinished(const QString &requestId, bool success, const QString &error) override;
|
void onRequestFinished(const QString &requestId, bool success, const QString &error) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QHash<QString, QString> m_accumulatedResponses;
|
bool handleStreamResponse(const QString &requestId, const QByteArray &data);
|
||||||
bool handleStreamResponse(
|
|
||||||
const QString &requestId, const QByteArray &data, QString &accumulatedResponse);
|
|
||||||
bool handleRegularResponse(
|
|
||||||
const QString &requestId, const QByteArray &data, QString &accumulatedResponse);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace QodeAssist::Providers
|
} // namespace QodeAssist::Providers
|
||||||
|
|||||||
@ -125,6 +125,9 @@ LLMCore::ProviderID LMStudioProvider::providerID() const
|
|||||||
void LMStudioProvider::sendRequest(
|
void LMStudioProvider::sendRequest(
|
||||||
const QString &requestId, const QUrl &url, const QJsonObject &payload)
|
const QString &requestId, const QUrl &url, const QJsonObject &payload)
|
||||||
{
|
{
|
||||||
|
m_dataBuffers[requestId].clear();
|
||||||
|
m_requestUrls[requestId] = url;
|
||||||
|
|
||||||
QNetworkRequest networkRequest(url);
|
QNetworkRequest networkRequest(url);
|
||||||
prepareNetworkRequest(networkRequest);
|
prepareNetworkRequest(networkRequest);
|
||||||
|
|
||||||
@ -139,16 +142,17 @@ void LMStudioProvider::sendRequest(
|
|||||||
|
|
||||||
void LMStudioProvider::onDataReceived(const QString &requestId, const QByteArray &data)
|
void LMStudioProvider::onDataReceived(const QString &requestId, const QByteArray &data)
|
||||||
{
|
{
|
||||||
QString &accumulatedResponse = m_accumulatedResponses[requestId];
|
LLMCore::DataBuffers &buffers = m_dataBuffers[requestId];
|
||||||
|
QStringList lines = buffers.rawStreamBuffer.processData(data);
|
||||||
|
|
||||||
if (data.isEmpty()) {
|
if (data.isEmpty()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isDone = false;
|
bool isDone = false;
|
||||||
QByteArrayList lines = data.split('\n');
|
QString tempResponse;
|
||||||
|
|
||||||
for (const QByteArray &line : lines) {
|
for (const QString &line : lines) {
|
||||||
if (line.trimmed().isEmpty()) {
|
if (line.trimmed().isEmpty()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -158,19 +162,11 @@ void LMStudioProvider::onDataReceived(const QString &requestId, const QByteArray
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
QByteArray jsonData = line;
|
QJsonObject responseObj = parseEventLine(line);
|
||||||
if (line.startsWith("data: ")) {
|
if (responseObj.isEmpty())
|
||||||
jsonData = line.mid(6);
|
|
||||||
}
|
|
||||||
|
|
||||||
QJsonParseError error;
|
|
||||||
QJsonDocument doc = QJsonDocument::fromJson(jsonData, &error);
|
|
||||||
|
|
||||||
if (doc.isNull()) {
|
|
||||||
continue;
|
continue;
|
||||||
}
|
|
||||||
|
|
||||||
auto message = LLMCore::OpenAIMessage::fromJson(doc.object());
|
auto message = LLMCore::OpenAIMessage::fromJson(responseObj);
|
||||||
if (message.hasError()) {
|
if (message.hasError()) {
|
||||||
LOG_MESSAGE("Error in LMStudio response: " + message.error);
|
LOG_MESSAGE("Error in LMStudio response: " + message.error);
|
||||||
continue;
|
continue;
|
||||||
@ -178,8 +174,7 @@ void LMStudioProvider::onDataReceived(const QString &requestId, const QByteArray
|
|||||||
|
|
||||||
QString content = message.getContent();
|
QString content = message.getContent();
|
||||||
if (!content.isEmpty()) {
|
if (!content.isEmpty()) {
|
||||||
accumulatedResponse += content;
|
tempResponse += content;
|
||||||
emit partialResponseReceived(requestId, content);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (message.isDone()) {
|
if (message.isDone()) {
|
||||||
@ -187,9 +182,14 @@ void LMStudioProvider::onDataReceived(const QString &requestId, const QByteArray
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!tempResponse.isEmpty()) {
|
||||||
|
buffers.responseContent += tempResponse;
|
||||||
|
emit partialResponseReceived(requestId, tempResponse);
|
||||||
|
}
|
||||||
|
|
||||||
if (isDone) {
|
if (isDone) {
|
||||||
emit fullResponseReceived(requestId, accumulatedResponse);
|
emit fullResponseReceived(requestId, buffers.responseContent);
|
||||||
m_accumulatedResponses.remove(requestId);
|
m_dataBuffers.remove(requestId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -199,15 +199,16 @@ void LMStudioProvider::onRequestFinished(const QString &requestId, bool success,
|
|||||||
LOG_MESSAGE(QString("LMStudioProvider request %1 failed: %2").arg(requestId, error));
|
LOG_MESSAGE(QString("LMStudioProvider request %1 failed: %2").arg(requestId, error));
|
||||||
emit requestFailed(requestId, error);
|
emit requestFailed(requestId, error);
|
||||||
} else {
|
} else {
|
||||||
if (m_accumulatedResponses.contains(requestId)) {
|
if (m_dataBuffers.contains(requestId)) {
|
||||||
const QString fullResponse = m_accumulatedResponses[requestId];
|
const LLMCore::DataBuffers &buffers = m_dataBuffers[requestId];
|
||||||
if (!fullResponse.isEmpty()) {
|
if (!buffers.responseContent.isEmpty()) {
|
||||||
emit fullResponseReceived(requestId, fullResponse);
|
emit fullResponseReceived(requestId, buffers.responseContent);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
m_accumulatedResponses.remove(requestId);
|
m_dataBuffers.remove(requestId);
|
||||||
|
m_requestUrls.remove(requestId);
|
||||||
}
|
}
|
||||||
|
|
||||||
void QodeAssist::Providers::LMStudioProvider::prepareRequest(
|
void QodeAssist::Providers::LMStudioProvider::prepareRequest(
|
||||||
|
|||||||
@ -47,9 +47,6 @@ public:
|
|||||||
public slots:
|
public slots:
|
||||||
void onDataReceived(const QString &requestId, const QByteArray &data) override;
|
void onDataReceived(const QString &requestId, const QByteArray &data) override;
|
||||||
void onRequestFinished(const QString &requestId, bool success, const QString &error) override;
|
void onRequestFinished(const QString &requestId, bool success, const QString &error) override;
|
||||||
|
|
||||||
private:
|
|
||||||
QHash<QString, QString> m_accumulatedResponses;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace QodeAssist::Providers
|
} // namespace QodeAssist::Providers
|
||||||
|
|||||||
@ -151,6 +151,9 @@ LLMCore::ProviderID LlamaCppProvider::providerID() const
|
|||||||
void LlamaCppProvider::sendRequest(
|
void LlamaCppProvider::sendRequest(
|
||||||
const QString &requestId, const QUrl &url, const QJsonObject &payload)
|
const QString &requestId, const QUrl &url, const QJsonObject &payload)
|
||||||
{
|
{
|
||||||
|
m_dataBuffers[requestId].clear();
|
||||||
|
m_requestUrls[requestId] = url;
|
||||||
|
|
||||||
QNetworkRequest networkRequest(url);
|
QNetworkRequest networkRequest(url);
|
||||||
prepareNetworkRequest(networkRequest);
|
prepareNetworkRequest(networkRequest);
|
||||||
|
|
||||||
@ -165,16 +168,17 @@ void LlamaCppProvider::sendRequest(
|
|||||||
|
|
||||||
void LlamaCppProvider::onDataReceived(const QString &requestId, const QByteArray &data)
|
void LlamaCppProvider::onDataReceived(const QString &requestId, const QByteArray &data)
|
||||||
{
|
{
|
||||||
QString &accumulatedResponse = m_accumulatedResponses[requestId];
|
LLMCore::DataBuffers &buffers = m_dataBuffers[requestId];
|
||||||
|
QStringList lines = buffers.rawStreamBuffer.processData(data);
|
||||||
|
|
||||||
if (data.isEmpty()) {
|
if (data.isEmpty()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isDone = data.contains("\"stop\":true") || data.contains("data: [DONE]");
|
bool isDone = data.contains("\"stop\":true") || data.contains("data: [DONE]");
|
||||||
|
QString tempResponse;
|
||||||
|
|
||||||
QByteArrayList lines = data.split('\n');
|
for (const QString &line : lines) {
|
||||||
for (const QByteArray &line : lines) {
|
|
||||||
if (line.trimmed().isEmpty()) {
|
if (line.trimmed().isEmpty()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -184,25 +188,15 @@ void LlamaCppProvider::onDataReceived(const QString &requestId, const QByteArray
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
QByteArray jsonData = line;
|
QJsonObject obj = parseEventLine(line);
|
||||||
if (line.startsWith("data: ")) {
|
if (obj.isEmpty())
|
||||||
jsonData = line.mid(6);
|
|
||||||
}
|
|
||||||
|
|
||||||
QJsonParseError error;
|
|
||||||
QJsonDocument doc = QJsonDocument::fromJson(jsonData, &error);
|
|
||||||
if (doc.isNull()) {
|
|
||||||
continue;
|
continue;
|
||||||
}
|
|
||||||
|
|
||||||
QJsonObject obj = doc.object();
|
|
||||||
QString content;
|
QString content;
|
||||||
|
|
||||||
if (obj.contains("content")) {
|
if (obj.contains("content")) {
|
||||||
content = obj["content"].toString();
|
content = obj["content"].toString();
|
||||||
if (!content.isEmpty()) {
|
if (!content.isEmpty()) {
|
||||||
accumulatedResponse += content;
|
tempResponse += content;
|
||||||
emit partialResponseReceived(requestId, content);
|
|
||||||
}
|
}
|
||||||
} else if (obj.contains("choices")) {
|
} else if (obj.contains("choices")) {
|
||||||
auto message = LLMCore::OpenAIMessage::fromJson(obj);
|
auto message = LLMCore::OpenAIMessage::fromJson(obj);
|
||||||
@ -213,8 +207,7 @@ void LlamaCppProvider::onDataReceived(const QString &requestId, const QByteArray
|
|||||||
|
|
||||||
content = message.getContent();
|
content = message.getContent();
|
||||||
if (!content.isEmpty()) {
|
if (!content.isEmpty()) {
|
||||||
accumulatedResponse += content;
|
tempResponse += content;
|
||||||
emit partialResponseReceived(requestId, content);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (message.isDone()) {
|
if (message.isDone()) {
|
||||||
@ -227,9 +220,14 @@ void LlamaCppProvider::onDataReceived(const QString &requestId, const QByteArray
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!tempResponse.isEmpty()) {
|
||||||
|
buffers.responseContent += tempResponse;
|
||||||
|
emit partialResponseReceived(requestId, tempResponse);
|
||||||
|
}
|
||||||
|
|
||||||
if (isDone) {
|
if (isDone) {
|
||||||
emit fullResponseReceived(requestId, accumulatedResponse);
|
emit fullResponseReceived(requestId, buffers.responseContent);
|
||||||
m_accumulatedResponses.remove(requestId);
|
m_dataBuffers.remove(requestId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -239,15 +237,16 @@ void LlamaCppProvider::onRequestFinished(const QString &requestId, bool success,
|
|||||||
LOG_MESSAGE(QString("LlamaCppProvider request %1 failed: %2").arg(requestId, error));
|
LOG_MESSAGE(QString("LlamaCppProvider request %1 failed: %2").arg(requestId, error));
|
||||||
emit requestFailed(requestId, error);
|
emit requestFailed(requestId, error);
|
||||||
} else {
|
} else {
|
||||||
if (m_accumulatedResponses.contains(requestId)) {
|
if (m_dataBuffers.contains(requestId)) {
|
||||||
const QString fullResponse = m_accumulatedResponses[requestId];
|
const LLMCore::DataBuffers &buffers = m_dataBuffers[requestId];
|
||||||
if (!fullResponse.isEmpty()) {
|
if (!buffers.responseContent.isEmpty()) {
|
||||||
emit fullResponseReceived(requestId, fullResponse);
|
emit fullResponseReceived(requestId, buffers.responseContent);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
m_accumulatedResponses.remove(requestId);
|
m_dataBuffers.remove(requestId);
|
||||||
|
m_requestUrls.remove(requestId);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace QodeAssist::Providers
|
} // namespace QodeAssist::Providers
|
||||||
|
|||||||
@ -47,9 +47,6 @@ public:
|
|||||||
public slots:
|
public slots:
|
||||||
void onDataReceived(const QString &requestId, const QByteArray &data) override;
|
void onDataReceived(const QString &requestId, const QByteArray &data) override;
|
||||||
void onRequestFinished(const QString &requestId, bool success, const QString &error) override;
|
void onRequestFinished(const QString &requestId, bool success, const QString &error) override;
|
||||||
|
|
||||||
private:
|
|
||||||
QHash<QString, QString> m_accumulatedResponses;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace QodeAssist::Providers
|
} // namespace QodeAssist::Providers
|
||||||
|
|||||||
@ -128,6 +128,9 @@ LLMCore::ProviderID MistralAIProvider::providerID() const
|
|||||||
void MistralAIProvider::sendRequest(
|
void MistralAIProvider::sendRequest(
|
||||||
const QString &requestId, const QUrl &url, const QJsonObject &payload)
|
const QString &requestId, const QUrl &url, const QJsonObject &payload)
|
||||||
{
|
{
|
||||||
|
m_dataBuffers[requestId].clear();
|
||||||
|
m_requestUrls[requestId] = url;
|
||||||
|
|
||||||
QNetworkRequest networkRequest(url);
|
QNetworkRequest networkRequest(url);
|
||||||
prepareNetworkRequest(networkRequest);
|
prepareNetworkRequest(networkRequest);
|
||||||
|
|
||||||
@ -142,16 +145,17 @@ void MistralAIProvider::sendRequest(
|
|||||||
|
|
||||||
void MistralAIProvider::onDataReceived(const QString &requestId, const QByteArray &data)
|
void MistralAIProvider::onDataReceived(const QString &requestId, const QByteArray &data)
|
||||||
{
|
{
|
||||||
QString &accumulatedResponse = m_accumulatedResponses[requestId];
|
LLMCore::DataBuffers &buffers = m_dataBuffers[requestId];
|
||||||
|
QStringList lines = buffers.rawStreamBuffer.processData(data);
|
||||||
|
|
||||||
if (data.isEmpty()) {
|
if (data.isEmpty()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isDone = false;
|
bool isDone = false;
|
||||||
QByteArrayList lines = data.split('\n');
|
QString tempResponse;
|
||||||
|
|
||||||
for (const QByteArray &line : lines) {
|
for (const QString &line : lines) {
|
||||||
if (line.trimmed().isEmpty()) {
|
if (line.trimmed().isEmpty()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -161,19 +165,11 @@ void MistralAIProvider::onDataReceived(const QString &requestId, const QByteArra
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
QByteArray jsonData = line;
|
QJsonObject responseObj = parseEventLine(line);
|
||||||
if (line.startsWith("data: ")) {
|
if (responseObj.isEmpty())
|
||||||
jsonData = line.mid(6);
|
|
||||||
}
|
|
||||||
|
|
||||||
QJsonParseError error;
|
|
||||||
QJsonDocument doc = QJsonDocument::fromJson(jsonData, &error);
|
|
||||||
|
|
||||||
if (doc.isNull()) {
|
|
||||||
continue;
|
continue;
|
||||||
}
|
|
||||||
|
|
||||||
auto message = LLMCore::OpenAIMessage::fromJson(doc.object());
|
auto message = LLMCore::OpenAIMessage::fromJson(responseObj);
|
||||||
if (message.hasError()) {
|
if (message.hasError()) {
|
||||||
LOG_MESSAGE("Error in MistralAI response: " + message.error);
|
LOG_MESSAGE("Error in MistralAI response: " + message.error);
|
||||||
continue;
|
continue;
|
||||||
@ -181,8 +177,7 @@ void MistralAIProvider::onDataReceived(const QString &requestId, const QByteArra
|
|||||||
|
|
||||||
QString content = message.getContent();
|
QString content = message.getContent();
|
||||||
if (!content.isEmpty()) {
|
if (!content.isEmpty()) {
|
||||||
accumulatedResponse += content;
|
tempResponse += content;
|
||||||
emit partialResponseReceived(requestId, content);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (message.isDone()) {
|
if (message.isDone()) {
|
||||||
@ -190,9 +185,14 @@ void MistralAIProvider::onDataReceived(const QString &requestId, const QByteArra
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!tempResponse.isEmpty()) {
|
||||||
|
buffers.responseContent += tempResponse;
|
||||||
|
emit partialResponseReceived(requestId, tempResponse);
|
||||||
|
}
|
||||||
|
|
||||||
if (isDone) {
|
if (isDone) {
|
||||||
emit fullResponseReceived(requestId, accumulatedResponse);
|
emit fullResponseReceived(requestId, buffers.responseContent);
|
||||||
m_accumulatedResponses.remove(requestId);
|
m_dataBuffers.remove(requestId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -203,15 +203,16 @@ void MistralAIProvider::onRequestFinished(
|
|||||||
LOG_MESSAGE(QString("MistralAIProvider request %1 failed: %2").arg(requestId, error));
|
LOG_MESSAGE(QString("MistralAIProvider request %1 failed: %2").arg(requestId, error));
|
||||||
emit requestFailed(requestId, error);
|
emit requestFailed(requestId, error);
|
||||||
} else {
|
} else {
|
||||||
if (m_accumulatedResponses.contains(requestId)) {
|
if (m_dataBuffers.contains(requestId)) {
|
||||||
const QString fullResponse = m_accumulatedResponses[requestId];
|
const LLMCore::DataBuffers &buffers = m_dataBuffers[requestId];
|
||||||
if (!fullResponse.isEmpty()) {
|
if (!buffers.responseContent.isEmpty()) {
|
||||||
emit fullResponseReceived(requestId, fullResponse);
|
emit fullResponseReceived(requestId, buffers.responseContent);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
m_accumulatedResponses.remove(requestId);
|
m_dataBuffers.remove(requestId);
|
||||||
|
m_requestUrls.remove(requestId);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MistralAIProvider::prepareRequest(
|
void MistralAIProvider::prepareRequest(
|
||||||
|
|||||||
@ -47,9 +47,6 @@ public:
|
|||||||
public slots:
|
public slots:
|
||||||
void onDataReceived(const QString &requestId, const QByteArray &data) override;
|
void onDataReceived(const QString &requestId, const QByteArray &data) override;
|
||||||
void onRequestFinished(const QString &requestId, bool success, const QString &error) override;
|
void onRequestFinished(const QString &requestId, bool success, const QString &error) override;
|
||||||
|
|
||||||
private:
|
|
||||||
QHash<QString, QString> m_accumulatedResponses;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace QodeAssist::Providers
|
} // namespace QodeAssist::Providers
|
||||||
|
|||||||
@ -188,6 +188,9 @@ LLMCore::ProviderID OllamaProvider::providerID() const
|
|||||||
void OllamaProvider::sendRequest(
|
void OllamaProvider::sendRequest(
|
||||||
const QString &requestId, const QUrl &url, const QJsonObject &payload)
|
const QString &requestId, const QUrl &url, const QJsonObject &payload)
|
||||||
{
|
{
|
||||||
|
m_dataBuffers[requestId].clear();
|
||||||
|
m_requestUrls[requestId] = url;
|
||||||
|
|
||||||
QNetworkRequest networkRequest(url);
|
QNetworkRequest networkRequest(url);
|
||||||
prepareNetworkRequest(networkRequest);
|
prepareNetworkRequest(networkRequest);
|
||||||
|
|
||||||
@ -201,22 +204,23 @@ void OllamaProvider::sendRequest(
|
|||||||
|
|
||||||
void OllamaProvider::onDataReceived(const QString &requestId, const QByteArray &data)
|
void OllamaProvider::onDataReceived(const QString &requestId, const QByteArray &data)
|
||||||
{
|
{
|
||||||
QString &accumulatedResponse = m_accumulatedResponses[requestId];
|
LLMCore::DataBuffers &buffers = m_dataBuffers[requestId];
|
||||||
|
QStringList lines = buffers.rawStreamBuffer.processData(data);
|
||||||
|
|
||||||
if (data.isEmpty()) {
|
if (data.isEmpty()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
QByteArrayList lines = data.split('\n');
|
|
||||||
bool isDone = false;
|
bool isDone = false;
|
||||||
|
QString tempResponse;
|
||||||
|
|
||||||
for (const QByteArray &line : lines) {
|
for (const QString &line : lines) {
|
||||||
if (line.trimmed().isEmpty()) {
|
if (line.trimmed().isEmpty()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
QJsonParseError error;
|
QJsonParseError error;
|
||||||
QJsonDocument doc = QJsonDocument::fromJson(line, &error);
|
QJsonDocument doc = QJsonDocument::fromJson(line.toUtf8(), &error);
|
||||||
if (doc.isNull()) {
|
if (doc.isNull()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -238,8 +242,7 @@ void OllamaProvider::onDataReceived(const QString &requestId, const QByteArray &
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!content.isEmpty()) {
|
if (!content.isEmpty()) {
|
||||||
accumulatedResponse += content;
|
tempResponse += content;
|
||||||
emit partialResponseReceived(requestId, content);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (obj["done"].toBool()) {
|
if (obj["done"].toBool()) {
|
||||||
@ -247,9 +250,14 @@ void OllamaProvider::onDataReceived(const QString &requestId, const QByteArray &
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!tempResponse.isEmpty()) {
|
||||||
|
buffers.responseContent += tempResponse;
|
||||||
|
emit partialResponseReceived(requestId, tempResponse);
|
||||||
|
}
|
||||||
|
|
||||||
if (isDone) {
|
if (isDone) {
|
||||||
emit fullResponseReceived(requestId, accumulatedResponse);
|
emit fullResponseReceived(requestId, buffers.responseContent);
|
||||||
m_accumulatedResponses.remove(requestId);
|
m_dataBuffers.remove(requestId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -259,15 +267,16 @@ void OllamaProvider::onRequestFinished(const QString &requestId, bool success, c
|
|||||||
LOG_MESSAGE(QString("OllamaProvider request %1 failed: %2").arg(requestId, error));
|
LOG_MESSAGE(QString("OllamaProvider request %1 failed: %2").arg(requestId, error));
|
||||||
emit requestFailed(requestId, error);
|
emit requestFailed(requestId, error);
|
||||||
} else {
|
} else {
|
||||||
if (m_accumulatedResponses.contains(requestId)) {
|
if (m_dataBuffers.contains(requestId)) {
|
||||||
const QString fullResponse = m_accumulatedResponses[requestId];
|
const LLMCore::DataBuffers &buffers = m_dataBuffers[requestId];
|
||||||
if (!fullResponse.isEmpty()) {
|
if (!buffers.responseContent.isEmpty()) {
|
||||||
emit fullResponseReceived(requestId, fullResponse);
|
emit fullResponseReceived(requestId, buffers.responseContent);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
m_accumulatedResponses.remove(requestId);
|
m_dataBuffers.remove(requestId);
|
||||||
|
m_requestUrls.remove(requestId);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace QodeAssist::Providers
|
} // namespace QodeAssist::Providers
|
||||||
|
|||||||
@ -47,9 +47,6 @@ public:
|
|||||||
public slots:
|
public slots:
|
||||||
void onDataReceived(const QString &requestId, const QByteArray &data) override;
|
void onDataReceived(const QString &requestId, const QByteArray &data) override;
|
||||||
void onRequestFinished(const QString &requestId, bool success, const QString &error) override;
|
void onRequestFinished(const QString &requestId, bool success, const QString &error) override;
|
||||||
|
|
||||||
private:
|
|
||||||
QHash<QString, QString> m_accumulatedResponses;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace QodeAssist::Providers
|
} // namespace QodeAssist::Providers
|
||||||
|
|||||||
@ -137,6 +137,9 @@ LLMCore::ProviderID OpenAICompatProvider::providerID() const
|
|||||||
void OpenAICompatProvider::sendRequest(
|
void OpenAICompatProvider::sendRequest(
|
||||||
const QString &requestId, const QUrl &url, const QJsonObject &payload)
|
const QString &requestId, const QUrl &url, const QJsonObject &payload)
|
||||||
{
|
{
|
||||||
|
m_dataBuffers[requestId].clear();
|
||||||
|
m_requestUrls[requestId] = url;
|
||||||
|
|
||||||
QNetworkRequest networkRequest(url);
|
QNetworkRequest networkRequest(url);
|
||||||
prepareNetworkRequest(networkRequest);
|
prepareNetworkRequest(networkRequest);
|
||||||
|
|
||||||
@ -151,16 +154,17 @@ void OpenAICompatProvider::sendRequest(
|
|||||||
|
|
||||||
void OpenAICompatProvider::onDataReceived(const QString &requestId, const QByteArray &data)
|
void OpenAICompatProvider::onDataReceived(const QString &requestId, const QByteArray &data)
|
||||||
{
|
{
|
||||||
QString &accumulatedResponse = m_accumulatedResponses[requestId];
|
LLMCore::DataBuffers &buffers = m_dataBuffers[requestId];
|
||||||
|
QStringList lines = buffers.rawStreamBuffer.processData(data);
|
||||||
|
|
||||||
if (data.isEmpty()) {
|
if (data.isEmpty()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isDone = false;
|
bool isDone = false;
|
||||||
QByteArrayList lines = data.split('\n');
|
QString tempResponse;
|
||||||
|
|
||||||
for (const QByteArray &line : lines) {
|
for (const QString &line : lines) {
|
||||||
if (line.trimmed().isEmpty()) {
|
if (line.trimmed().isEmpty()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -170,19 +174,11 @@ void OpenAICompatProvider::onDataReceived(const QString &requestId, const QByteA
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
QByteArray jsonData = line;
|
QJsonObject responseObj = parseEventLine(line);
|
||||||
if (line.startsWith("data: ")) {
|
if (responseObj.isEmpty())
|
||||||
jsonData = line.mid(6);
|
|
||||||
}
|
|
||||||
|
|
||||||
QJsonParseError error;
|
|
||||||
QJsonDocument doc = QJsonDocument::fromJson(jsonData, &error);
|
|
||||||
|
|
||||||
if (doc.isNull()) {
|
|
||||||
continue;
|
continue;
|
||||||
}
|
|
||||||
|
|
||||||
auto message = LLMCore::OpenAIMessage::fromJson(doc.object());
|
auto message = LLMCore::OpenAIMessage::fromJson(responseObj);
|
||||||
if (message.hasError()) {
|
if (message.hasError()) {
|
||||||
LOG_MESSAGE("Error in OpenAI response: " + message.error);
|
LOG_MESSAGE("Error in OpenAI response: " + message.error);
|
||||||
continue;
|
continue;
|
||||||
@ -190,8 +186,7 @@ void OpenAICompatProvider::onDataReceived(const QString &requestId, const QByteA
|
|||||||
|
|
||||||
QString content = message.getContent();
|
QString content = message.getContent();
|
||||||
if (!content.isEmpty()) {
|
if (!content.isEmpty()) {
|
||||||
accumulatedResponse += content;
|
tempResponse += content;
|
||||||
emit partialResponseReceived(requestId, content);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (message.isDone()) {
|
if (message.isDone()) {
|
||||||
@ -199,9 +194,14 @@ void OpenAICompatProvider::onDataReceived(const QString &requestId, const QByteA
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!tempResponse.isEmpty()) {
|
||||||
|
buffers.responseContent += tempResponse;
|
||||||
|
emit partialResponseReceived(requestId, tempResponse);
|
||||||
|
}
|
||||||
|
|
||||||
if (isDone) {
|
if (isDone) {
|
||||||
emit fullResponseReceived(requestId, accumulatedResponse);
|
emit fullResponseReceived(requestId, buffers.responseContent);
|
||||||
m_accumulatedResponses.remove(requestId);
|
m_dataBuffers.remove(requestId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -209,18 +209,19 @@ void OpenAICompatProvider::onRequestFinished(
|
|||||||
const QString &requestId, bool success, const QString &error)
|
const QString &requestId, bool success, const QString &error)
|
||||||
{
|
{
|
||||||
if (!success) {
|
if (!success) {
|
||||||
LOG_MESSAGE(QString("OpenAIProvider request %1 failed: %2").arg(requestId, error));
|
LOG_MESSAGE(QString("OpenAICompatProvider request %1 failed: %2").arg(requestId, error));
|
||||||
emit requestFailed(requestId, error);
|
emit requestFailed(requestId, error);
|
||||||
} else {
|
} else {
|
||||||
if (m_accumulatedResponses.contains(requestId)) {
|
if (m_dataBuffers.contains(requestId)) {
|
||||||
const QString fullResponse = m_accumulatedResponses[requestId];
|
const LLMCore::DataBuffers &buffers = m_dataBuffers[requestId];
|
||||||
if (!fullResponse.isEmpty()) {
|
if (!buffers.responseContent.isEmpty()) {
|
||||||
emit fullResponseReceived(requestId, fullResponse);
|
emit fullResponseReceived(requestId, buffers.responseContent);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
m_accumulatedResponses.remove(requestId);
|
m_dataBuffers.remove(requestId);
|
||||||
|
m_requestUrls.remove(requestId);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace QodeAssist::Providers
|
} // namespace QodeAssist::Providers
|
||||||
|
|||||||
@ -47,9 +47,6 @@ public:
|
|||||||
public slots:
|
public slots:
|
||||||
void onDataReceived(const QString &requestId, const QByteArray &data) override;
|
void onDataReceived(const QString &requestId, const QByteArray &data) override;
|
||||||
void onRequestFinished(const QString &requestId, bool success, const QString &error) override;
|
void onRequestFinished(const QString &requestId, bool success, const QString &error) override;
|
||||||
|
|
||||||
private:
|
|
||||||
QHash<QString, QString> m_accumulatedResponses;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace QodeAssist::Providers
|
} // namespace QodeAssist::Providers
|
||||||
|
|||||||
@ -175,6 +175,9 @@ LLMCore::ProviderID OpenAIProvider::providerID() const
|
|||||||
void OpenAIProvider::sendRequest(
|
void OpenAIProvider::sendRequest(
|
||||||
const QString &requestId, const QUrl &url, const QJsonObject &payload)
|
const QString &requestId, const QUrl &url, const QJsonObject &payload)
|
||||||
{
|
{
|
||||||
|
m_dataBuffers[requestId].clear();
|
||||||
|
m_requestUrls[requestId] = url;
|
||||||
|
|
||||||
QNetworkRequest networkRequest(url);
|
QNetworkRequest networkRequest(url);
|
||||||
prepareNetworkRequest(networkRequest);
|
prepareNetworkRequest(networkRequest);
|
||||||
|
|
||||||
@ -188,16 +191,17 @@ void OpenAIProvider::sendRequest(
|
|||||||
|
|
||||||
void OpenAIProvider::onDataReceived(const QString &requestId, const QByteArray &data)
|
void OpenAIProvider::onDataReceived(const QString &requestId, const QByteArray &data)
|
||||||
{
|
{
|
||||||
QString &accumulatedResponse = m_accumulatedResponses[requestId];
|
LLMCore::DataBuffers &buffers = m_dataBuffers[requestId];
|
||||||
|
QStringList lines = buffers.rawStreamBuffer.processData(data);
|
||||||
|
|
||||||
if (data.isEmpty()) {
|
if (data.isEmpty()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isDone = false;
|
bool isDone = false;
|
||||||
QByteArrayList lines = data.split('\n');
|
QString tempResponse;
|
||||||
|
|
||||||
for (const QByteArray &line : lines) {
|
for (const QString &line : lines) {
|
||||||
if (line.trimmed().isEmpty()) {
|
if (line.trimmed().isEmpty()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -207,19 +211,11 @@ void OpenAIProvider::onDataReceived(const QString &requestId, const QByteArray &
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
QByteArray jsonData = line;
|
QJsonObject responseObj = parseEventLine(line);
|
||||||
if (line.startsWith("data: ")) {
|
if (responseObj.isEmpty())
|
||||||
jsonData = line.mid(6);
|
|
||||||
}
|
|
||||||
|
|
||||||
QJsonParseError error;
|
|
||||||
QJsonDocument doc = QJsonDocument::fromJson(jsonData, &error);
|
|
||||||
|
|
||||||
if (doc.isNull()) {
|
|
||||||
continue;
|
continue;
|
||||||
}
|
|
||||||
|
|
||||||
auto message = LLMCore::OpenAIMessage::fromJson(doc.object());
|
auto message = LLMCore::OpenAIMessage::fromJson(responseObj);
|
||||||
if (message.hasError()) {
|
if (message.hasError()) {
|
||||||
LOG_MESSAGE("Error in OpenAI response: " + message.error);
|
LOG_MESSAGE("Error in OpenAI response: " + message.error);
|
||||||
continue;
|
continue;
|
||||||
@ -227,8 +223,7 @@ void OpenAIProvider::onDataReceived(const QString &requestId, const QByteArray &
|
|||||||
|
|
||||||
QString content = message.getContent();
|
QString content = message.getContent();
|
||||||
if (!content.isEmpty()) {
|
if (!content.isEmpty()) {
|
||||||
accumulatedResponse += content;
|
tempResponse += content;
|
||||||
emit partialResponseReceived(requestId, content);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (message.isDone()) {
|
if (message.isDone()) {
|
||||||
@ -236,9 +231,14 @@ void OpenAIProvider::onDataReceived(const QString &requestId, const QByteArray &
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!tempResponse.isEmpty()) {
|
||||||
|
buffers.responseContent += tempResponse;
|
||||||
|
emit partialResponseReceived(requestId, tempResponse);
|
||||||
|
}
|
||||||
|
|
||||||
if (isDone) {
|
if (isDone) {
|
||||||
emit fullResponseReceived(requestId, accumulatedResponse);
|
emit fullResponseReceived(requestId, buffers.responseContent);
|
||||||
m_accumulatedResponses.remove(requestId);
|
m_dataBuffers.remove(requestId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -248,15 +248,16 @@ void OpenAIProvider::onRequestFinished(const QString &requestId, bool success, c
|
|||||||
LOG_MESSAGE(QString("OpenAIProvider request %1 failed: %2").arg(requestId, error));
|
LOG_MESSAGE(QString("OpenAIProvider request %1 failed: %2").arg(requestId, error));
|
||||||
emit requestFailed(requestId, error);
|
emit requestFailed(requestId, error);
|
||||||
} else {
|
} else {
|
||||||
if (m_accumulatedResponses.contains(requestId)) {
|
if (m_dataBuffers.contains(requestId)) {
|
||||||
const QString fullResponse = m_accumulatedResponses[requestId];
|
const LLMCore::DataBuffers &buffers = m_dataBuffers[requestId];
|
||||||
if (!fullResponse.isEmpty()) {
|
if (!buffers.responseContent.isEmpty()) {
|
||||||
emit fullResponseReceived(requestId, fullResponse);
|
emit fullResponseReceived(requestId, buffers.responseContent);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
m_accumulatedResponses.remove(requestId);
|
m_dataBuffers.remove(requestId);
|
||||||
|
m_requestUrls.remove(requestId);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace QodeAssist::Providers
|
} // namespace QodeAssist::Providers
|
||||||
|
|||||||
@ -47,9 +47,6 @@ public:
|
|||||||
public slots:
|
public slots:
|
||||||
void onDataReceived(const QString &requestId, const QByteArray &data) override;
|
void onDataReceived(const QString &requestId, const QByteArray &data) override;
|
||||||
void onRequestFinished(const QString &requestId, bool success, const QString &error) override;
|
void onRequestFinished(const QString &requestId, bool success, const QString &error) override;
|
||||||
|
|
||||||
private:
|
|
||||||
QHash<QString, QString> m_accumulatedResponses;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace QodeAssist::Providers
|
} // namespace QodeAssist::Providers
|
||||||
|
|||||||
@ -53,16 +53,17 @@ LLMCore::ProviderID OpenRouterProvider::providerID() const
|
|||||||
|
|
||||||
void OpenRouterProvider::onDataReceived(const QString &requestId, const QByteArray &data)
|
void OpenRouterProvider::onDataReceived(const QString &requestId, const QByteArray &data)
|
||||||
{
|
{
|
||||||
QString &accumulatedResponse = m_accumulatedResponses[requestId];
|
LLMCore::DataBuffers &buffers = m_dataBuffers[requestId];
|
||||||
|
QStringList lines = buffers.rawStreamBuffer.processData(data);
|
||||||
|
|
||||||
if (data.isEmpty()) {
|
if (data.isEmpty()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isDone = false;
|
bool isDone = false;
|
||||||
QByteArrayList lines = data.split('\n');
|
QString tempResponse;
|
||||||
|
|
||||||
for (const QByteArray &line : lines) {
|
for (const QString &line : lines) {
|
||||||
if (line.trimmed().isEmpty() || line.contains("OPENROUTER PROCESSING")) {
|
if (line.trimmed().isEmpty() || line.contains("OPENROUTER PROCESSING")) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -72,28 +73,19 @@ void OpenRouterProvider::onDataReceived(const QString &requestId, const QByteArr
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
QByteArray jsonData = line;
|
QJsonObject responseObj = parseEventLine(line);
|
||||||
if (line.startsWith("data: ")) {
|
if (responseObj.isEmpty())
|
||||||
jsonData = line.mid(6);
|
|
||||||
}
|
|
||||||
|
|
||||||
QJsonParseError error;
|
|
||||||
QJsonDocument doc = QJsonDocument::fromJson(jsonData, &error);
|
|
||||||
|
|
||||||
if (doc.isNull()) {
|
|
||||||
continue;
|
continue;
|
||||||
}
|
|
||||||
|
|
||||||
auto message = LLMCore::OpenAIMessage::fromJson(doc.object());
|
auto message = LLMCore::OpenAIMessage::fromJson(responseObj);
|
||||||
if (message.hasError()) {
|
if (message.hasError()) {
|
||||||
LOG_MESSAGE("Error in OpenAI response: " + message.error);
|
LOG_MESSAGE("Error in OpenRouter response: " + message.error);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString content = message.getContent();
|
QString content = message.getContent();
|
||||||
if (!content.isEmpty()) {
|
if (!content.isEmpty()) {
|
||||||
accumulatedResponse += content;
|
tempResponse += content;
|
||||||
emit partialResponseReceived(requestId, content);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (message.isDone()) {
|
if (message.isDone()) {
|
||||||
@ -101,9 +93,14 @@ void OpenRouterProvider::onDataReceived(const QString &requestId, const QByteArr
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!tempResponse.isEmpty()) {
|
||||||
|
buffers.responseContent += tempResponse;
|
||||||
|
emit partialResponseReceived(requestId, tempResponse);
|
||||||
|
}
|
||||||
|
|
||||||
if (isDone) {
|
if (isDone) {
|
||||||
emit fullResponseReceived(requestId, accumulatedResponse);
|
emit fullResponseReceived(requestId, buffers.responseContent);
|
||||||
m_accumulatedResponses.remove(requestId);
|
m_dataBuffers.remove(requestId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -114,15 +111,16 @@ void OpenRouterProvider::onRequestFinished(
|
|||||||
LOG_MESSAGE(QString("OpenRouterProvider request %1 failed: %2").arg(requestId, error));
|
LOG_MESSAGE(QString("OpenRouterProvider request %1 failed: %2").arg(requestId, error));
|
||||||
emit requestFailed(requestId, error);
|
emit requestFailed(requestId, error);
|
||||||
} else {
|
} else {
|
||||||
if (m_accumulatedResponses.contains(requestId)) {
|
if (m_dataBuffers.contains(requestId)) {
|
||||||
const QString fullResponse = m_accumulatedResponses[requestId];
|
const LLMCore::DataBuffers &buffers = m_dataBuffers[requestId];
|
||||||
if (!fullResponse.isEmpty()) {
|
if (!buffers.responseContent.isEmpty()) {
|
||||||
emit fullResponseReceived(requestId, fullResponse);
|
emit fullResponseReceived(requestId, buffers.responseContent);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
m_accumulatedResponses.remove(requestId);
|
m_dataBuffers.remove(requestId);
|
||||||
|
m_requestUrls.remove(requestId);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace QodeAssist::Providers
|
} // namespace QodeAssist::Providers
|
||||||
|
|||||||
@ -19,7 +19,6 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "llmcore/Provider.hpp"
|
|
||||||
#include "providers/OpenAICompatProvider.hpp"
|
#include "providers/OpenAICompatProvider.hpp"
|
||||||
|
|
||||||
namespace QodeAssist::Providers {
|
namespace QodeAssist::Providers {
|
||||||
@ -35,9 +34,6 @@ public:
|
|||||||
public slots:
|
public slots:
|
||||||
void onDataReceived(const QString &requestId, const QByteArray &data) override;
|
void onDataReceived(const QString &requestId, const QByteArray &data) override;
|
||||||
void onRequestFinished(const QString &requestId, bool success, const QString &error) override;
|
void onRequestFinished(const QString &requestId, bool success, const QString &error) override;
|
||||||
|
|
||||||
private:
|
|
||||||
QHash<QString, QString> m_accumulatedResponses;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace QodeAssist::Providers
|
} // namespace QodeAssist::Providers
|
||||||
|
|||||||
@ -56,10 +56,6 @@ ChatAssistantSettings::ChatAssistantSettings()
|
|||||||
linkOpenFiles.setLabelText(Tr::tr("Sync open files with assistant by default"));
|
linkOpenFiles.setLabelText(Tr::tr("Sync open files with assistant by default"));
|
||||||
linkOpenFiles.setDefaultValue(false);
|
linkOpenFiles.setDefaultValue(false);
|
||||||
|
|
||||||
stream.setSettingsKey(Constants::CA_STREAM);
|
|
||||||
stream.setDefaultValue(true);
|
|
||||||
stream.setLabelText(Tr::tr("Enable stream option"));
|
|
||||||
|
|
||||||
autosave.setSettingsKey(Constants::CA_AUTOSAVE);
|
autosave.setSettingsKey(Constants::CA_AUTOSAVE);
|
||||||
autosave.setDefaultValue(true);
|
autosave.setDefaultValue(true);
|
||||||
autosave.setLabelText(Tr::tr("Enable autosave when message received"));
|
autosave.setLabelText(Tr::tr("Enable autosave when message received"));
|
||||||
@ -251,7 +247,6 @@ ChatAssistantSettings::ChatAssistantSettings()
|
|||||||
Group{title(Tr::tr("Chat Settings")),
|
Group{title(Tr::tr("Chat Settings")),
|
||||||
Column{Row{chatTokensThreshold, Stretch{1}},
|
Column{Row{chatTokensThreshold, Stretch{1}},
|
||||||
linkOpenFiles,
|
linkOpenFiles,
|
||||||
stream,
|
|
||||||
autosave,
|
autosave,
|
||||||
enableChatInBottomToolBar,
|
enableChatInBottomToolBar,
|
||||||
enableChatInNavigationPanel}},
|
enableChatInNavigationPanel}},
|
||||||
@ -294,7 +289,6 @@ void ChatAssistantSettings::resetSettingsToDefaults()
|
|||||||
QMessageBox::Yes | QMessageBox::No);
|
QMessageBox::Yes | QMessageBox::No);
|
||||||
|
|
||||||
if (reply == QMessageBox::Yes) {
|
if (reply == QMessageBox::Yes) {
|
||||||
resetAspect(stream);
|
|
||||||
resetAspect(chatTokensThreshold);
|
resetAspect(chatTokensThreshold);
|
||||||
resetAspect(temperature);
|
resetAspect(temperature);
|
||||||
resetAspect(maxTokens);
|
resetAspect(maxTokens);
|
||||||
|
|||||||
@ -35,7 +35,6 @@ public:
|
|||||||
// Chat settings
|
// Chat settings
|
||||||
Utils::IntegerAspect chatTokensThreshold{this};
|
Utils::IntegerAspect chatTokensThreshold{this};
|
||||||
Utils::BoolAspect linkOpenFiles{this};
|
Utils::BoolAspect linkOpenFiles{this};
|
||||||
Utils::BoolAspect stream{this};
|
|
||||||
Utils::BoolAspect autosave{this};
|
Utils::BoolAspect autosave{this};
|
||||||
Utils::BoolAspect enableChatInBottomToolBar{this};
|
Utils::BoolAspect enableChatInBottomToolBar{this};
|
||||||
Utils::BoolAspect enableChatInNavigationPanel{this};
|
Utils::BoolAspect enableChatInNavigationPanel{this};
|
||||||
|
|||||||
@ -51,10 +51,6 @@ CodeCompletionSettings::CodeCompletionSettings()
|
|||||||
multiLineCompletion.setDefaultValue(true);
|
multiLineCompletion.setDefaultValue(true);
|
||||||
multiLineCompletion.setLabelText(Tr::tr("Enable Multiline Completion"));
|
multiLineCompletion.setLabelText(Tr::tr("Enable Multiline Completion"));
|
||||||
|
|
||||||
stream.setSettingsKey(Constants::CC_STREAM);
|
|
||||||
stream.setDefaultValue(true);
|
|
||||||
stream.setLabelText(Tr::tr("Enable stream option"));
|
|
||||||
|
|
||||||
modelOutputHandler.setLabelText(Tr::tr("Text output proccessing mode:"));
|
modelOutputHandler.setLabelText(Tr::tr("Text output proccessing mode:"));
|
||||||
modelOutputHandler.setSettingsKey(Constants::CC_MODEL_OUTPUT_HANDLER);
|
modelOutputHandler.setSettingsKey(Constants::CC_MODEL_OUTPUT_HANDLER);
|
||||||
modelOutputHandler.setDisplayStyle(Utils::SelectionAspect::DisplayStyle::ComboBox);
|
modelOutputHandler.setDisplayStyle(Utils::SelectionAspect::DisplayStyle::ComboBox);
|
||||||
@ -303,7 +299,6 @@ CodeCompletionSettings::CodeCompletionSettings()
|
|||||||
Column{autoCompletion,
|
Column{autoCompletion,
|
||||||
Space{8},
|
Space{8},
|
||||||
multiLineCompletion,
|
multiLineCompletion,
|
||||||
stream,
|
|
||||||
Row{modelOutputHandler, Stretch{1}},
|
Row{modelOutputHandler, Stretch{1}},
|
||||||
Row{autoCompletionCharThreshold,
|
Row{autoCompletionCharThreshold,
|
||||||
autoCompletionTypingInterval,
|
autoCompletionTypingInterval,
|
||||||
@ -365,7 +360,6 @@ void CodeCompletionSettings::resetSettingsToDefaults()
|
|||||||
if (reply == QMessageBox::Yes) {
|
if (reply == QMessageBox::Yes) {
|
||||||
resetAspect(autoCompletion);
|
resetAspect(autoCompletion);
|
||||||
resetAspect(multiLineCompletion);
|
resetAspect(multiLineCompletion);
|
||||||
resetAspect(stream);
|
|
||||||
resetAspect(temperature);
|
resetAspect(temperature);
|
||||||
resetAspect(maxTokens);
|
resetAspect(maxTokens);
|
||||||
resetAspect(useTopP);
|
resetAspect(useTopP);
|
||||||
|
|||||||
@ -35,7 +35,6 @@ public:
|
|||||||
// Auto Completion Settings
|
// Auto Completion Settings
|
||||||
Utils::BoolAspect autoCompletion{this};
|
Utils::BoolAspect autoCompletion{this};
|
||||||
Utils::BoolAspect multiLineCompletion{this};
|
Utils::BoolAspect multiLineCompletion{this};
|
||||||
Utils::BoolAspect stream{this};
|
|
||||||
Utils::SelectionAspect modelOutputHandler{this};
|
Utils::SelectionAspect modelOutputHandler{this};
|
||||||
|
|
||||||
Utils::IntegerAspect startSuggestionTimer{this};
|
Utils::IntegerAspect startSuggestionTimer{this};
|
||||||
|
|||||||
@ -75,12 +75,10 @@ const char СС_AUTO_COMPLETION_CHAR_THRESHOLD[] = "QodeAssist.autoCompletionCha
|
|||||||
const char СС_AUTO_COMPLETION_TYPING_INTERVAL[] = "QodeAssist.autoCompletionTypingInterval";
|
const char СС_AUTO_COMPLETION_TYPING_INTERVAL[] = "QodeAssist.autoCompletionTypingInterval";
|
||||||
const char MAX_FILE_THRESHOLD[] = "QodeAssist.maxFileThreshold";
|
const char MAX_FILE_THRESHOLD[] = "QodeAssist.maxFileThreshold";
|
||||||
const char CC_MULTILINE_COMPLETION[] = "QodeAssist.ccMultilineCompletion";
|
const char CC_MULTILINE_COMPLETION[] = "QodeAssist.ccMultilineCompletion";
|
||||||
const char CC_STREAM[] = "QodeAssist.ccStream";
|
|
||||||
const char CC_MODEL_OUTPUT_HANDLER[] = "QodeAssist.ccModelOutputHandler";
|
const char CC_MODEL_OUTPUT_HANDLER[] = "QodeAssist.ccModelOutputHandler";
|
||||||
const char CUSTOM_JSON_TEMPLATE[] = "QodeAssist.customJsonTemplate";
|
const char CUSTOM_JSON_TEMPLATE[] = "QodeAssist.customJsonTemplate";
|
||||||
const char CA_TOKENS_THRESHOLD[] = "QodeAssist.caTokensThreshold";
|
const char CA_TOKENS_THRESHOLD[] = "QodeAssist.caTokensThreshold";
|
||||||
const char CA_LINK_OPEN_FILES[] = "QodeAssist.caLinkOpenFiles";
|
const char CA_LINK_OPEN_FILES[] = "QodeAssist.caLinkOpenFiles";
|
||||||
const char CA_STREAM[] = "QodeAssist.caStream";
|
|
||||||
const char CA_AUTOSAVE[] = "QodeAssist.caAutosave";
|
const char CA_AUTOSAVE[] = "QodeAssist.caAutosave";
|
||||||
const char CC_CUSTOM_LANGUAGES[] = "QodeAssist.ccCustomLanguages";
|
const char CC_CUSTOM_LANGUAGES[] = "QodeAssist.ccCustomLanguages";
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user