refactor: Inject settings into LLMClientInterface (#114)

This reduces reliance on global state and makes it more possible to test
the code.
This commit is contained in:
Povilas Kanapickas 2025-03-08 16:08:15 +02:00 committed by GitHub
parent 3a494d5254
commit 6c323642e4
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 40 additions and 31 deletions

View File

@ -45,8 +45,12 @@ QString extractFilePathFromRequest(const QJsonObject &request)
return QUrl(uri).toLocalFile(); return QUrl(uri).toLocalFile();
} }
LLMClientInterface::LLMClientInterface() LLMClientInterface::LLMClientInterface(
const Settings::GeneralSettings &generalSettings,
const Settings::CodeCompletionSettings &completeSettings)
: m_requestHandler(this) : m_requestHandler(this)
, m_generalSettings(generalSettings)
, m_completeSettings(completeSettings)
{ {
connect( connect(
&m_requestHandler, &m_requestHandler,
@ -158,16 +162,15 @@ void LLMClientInterface::handleExit(const QJsonObject &request)
void LLMClientInterface::handleCompletion(const QJsonObject &request) void LLMClientInterface::handleCompletion(const QJsonObject &request)
{ {
auto updatedContext = prepareContext(request); auto updatedContext = prepareContext(request);
auto &completeSettings = Settings::codeCompletionSettings();
auto &generalSettings = Settings::generalSettings();
bool isPreset1Active = Context::ContextManager::isSpecifyCompletion(request, generalSettings); bool isPreset1Active = Context::ContextManager::isSpecifyCompletion(request, m_generalSettings);
const auto providerName = !isPreset1Active ? generalSettings.ccProvider() const auto providerName = !isPreset1Active ? m_generalSettings.ccProvider()
: generalSettings.ccPreset1Provider(); : m_generalSettings.ccPreset1Provider();
const auto modelName = !isPreset1Active ? generalSettings.ccModel() const auto modelName = !isPreset1Active ? m_generalSettings.ccModel()
: generalSettings.ccPreset1Model(); : m_generalSettings.ccPreset1Model();
const auto url = !isPreset1Active ? generalSettings.ccUrl() : generalSettings.ccPreset1Url(); const auto url = !isPreset1Active ? m_generalSettings.ccUrl()
: m_generalSettings.ccPreset1Url();
const auto provider = LLMCore::ProvidersManager::instance().getProviderByName(providerName); const auto provider = LLMCore::ProvidersManager::instance().getProviderByName(providerName);
@ -176,8 +179,8 @@ void LLMClientInterface::handleCompletion(const QJsonObject &request)
return; return;
} }
auto templateName = !isPreset1Active ? generalSettings.ccTemplate() auto templateName = !isPreset1Active ? m_generalSettings.ccTemplate()
: generalSettings.ccPreset1Template(); : m_generalSettings.ccPreset1Template();
auto promptTemplate = LLMCore::PromptTemplateManager::instance().getFimTemplateByName( auto promptTemplate = LLMCore::PromptTemplateManager::instance().getFimTemplateByName(
templateName); templateName);
@ -194,30 +197,30 @@ 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 = completeSettings.stream() ? QString{"streamGenerateContent?alt=sse"} QString stream = m_completeSettings.stream() ? QString{"streamGenerateContent?alt=sse"}
: QString{"generateContent?"}; : 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(QString("%1%2").arg( config.url = QUrl(QString("%1%2").arg(
url, url,
promptTemplate->type() == LLMCore::TemplateType::FIM ? provider->completionEndpoint() promptTemplate->type() == LLMCore::TemplateType::FIM ? provider->completionEndpoint()
: provider->chatEndpoint())); : provider->chatEndpoint()));
config.providerRequest = {{"model", modelName}, {"stream", completeSettings.stream()}}; config.providerRequest = {{"model", modelName}, {"stream", m_completeSettings.stream()}};
} }
config.apiKey = provider->apiKey(); config.apiKey = provider->apiKey();
config.multiLineCompletion = completeSettings.multiLineCompletion(); config.multiLineCompletion = m_completeSettings.multiLineCompletion();
const auto stopWords = QJsonArray::fromStringList(config.promptTemplate->stopWords()); const auto stopWords = QJsonArray::fromStringList(config.promptTemplate->stopWords());
if (!stopWords.isEmpty()) if (!stopWords.isEmpty())
config.providerRequest["stop"] = stopWords; config.providerRequest["stop"] = stopWords;
QString systemPrompt; QString systemPrompt;
if (completeSettings.useSystemPrompt()) if (m_completeSettings.useSystemPrompt())
systemPrompt.append( systemPrompt.append(
completeSettings.useUserMessageTemplateForCC() m_completeSettings.useUserMessageTemplateForCC()
&& promptTemplate->type() == LLMCore::TemplateType::Chat && promptTemplate->type() == LLMCore::TemplateType::Chat
? completeSettings.systemPromptForNonFimModels() ? m_completeSettings.systemPromptForNonFimModels()
: completeSettings.systemPrompt()); : m_completeSettings.systemPrompt());
if (updatedContext.fileContext.has_value()) if (updatedContext.fileContext.has_value())
systemPrompt.append(updatedContext.fileContext.value()); systemPrompt.append(updatedContext.fileContext.value());
@ -225,8 +228,8 @@ void LLMClientInterface::handleCompletion(const QJsonObject &request)
if (promptTemplate->type() == LLMCore::TemplateType::Chat) { if (promptTemplate->type() == LLMCore::TemplateType::Chat) {
QString userMessage; QString userMessage;
if (completeSettings.useUserMessageTemplateForCC()) { if (m_completeSettings.useUserMessageTemplateForCC()) {
userMessage = completeSettings.processMessageToFIM( userMessage = m_completeSettings.processMessageToFIM(
updatedContext.prefix.value_or(""), updatedContext.suffix.value_or("")); updatedContext.prefix.value_or(""), updatedContext.suffix.value_or(""));
} else { } else {
userMessage = updatedContext.prefix.value_or("") + updatedContext.suffix.value_or(""); userMessage = updatedContext.prefix.value_or("") + updatedContext.suffix.value_or("");
@ -274,17 +277,16 @@ LLMCore::ContextData LLMClientInterface::prepareContext(
Context::DocumentContextReader reader( Context::DocumentContextReader reader(
textDocument->document(), textDocument->mimeType(), textDocument->filePath().toString()); textDocument->document(), textDocument->mimeType(), textDocument->filePath().toString());
return reader.prepareContext(lineNumber, cursorPosition, Settings::codeCompletionSettings()); return reader.prepareContext(lineNumber, cursorPosition, m_completeSettings);
} }
void LLMClientInterface::sendCompletionToClient( void LLMClientInterface::sendCompletionToClient(
const QString &completion, const QJsonObject &request, bool isComplete) const QString &completion, const QJsonObject &request, bool isComplete)
{ {
auto &generalSettings = Settings::generalSettings(); bool isPreset1Active = Context::ContextManager::isSpecifyCompletion(request, m_generalSettings);
bool isPreset1Active = Context::ContextManager::isSpecifyCompletion(request, generalSettings);
auto templateName = !isPreset1Active ? generalSettings.ccTemplate() auto templateName = !isPreset1Active ? m_generalSettings.ccTemplate()
: generalSettings.ccPreset1Template(); : m_generalSettings.ccPreset1Template();
auto promptTemplate = LLMCore::PromptTemplateManager::instance().getFimTemplateByName( auto promptTemplate = LLMCore::PromptTemplateManager::instance().getFimTemplateByName(
templateName); templateName);
@ -303,7 +305,7 @@ void LLMClientInterface::sendCompletionToClient(
QString processedCompletion QString processedCompletion
= promptTemplate->type() == LLMCore::TemplateType::Chat = promptTemplate->type() == LLMCore::TemplateType::Chat
&& Settings::codeCompletionSettings().smartProcessInstuctText() && m_completeSettings.smartProcessInstuctText()
? CodeHandler::processText(completion, extractFilePathFromRequest(request)) ? CodeHandler::processText(completion, extractFilePathFromRequest(request))
: completion; : completion;

View File

@ -25,6 +25,8 @@
#include <context/ProgrammingLanguage.hpp> #include <context/ProgrammingLanguage.hpp>
#include <llmcore/ContextData.hpp> #include <llmcore/ContextData.hpp>
#include <llmcore/RequestHandler.hpp> #include <llmcore/RequestHandler.hpp>
#include <settings/CodeCompletionSettings.hpp>
#include <settings/GeneralSettings.hpp>
class QNetworkReply; class QNetworkReply;
class QNetworkAccessManager; class QNetworkAccessManager;
@ -36,7 +38,9 @@ class LLMClientInterface : public LanguageClient::BaseClientInterface
Q_OBJECT Q_OBJECT
public: public:
LLMClientInterface(); LLMClientInterface(
const Settings::GeneralSettings &generalSettings,
const Settings::CodeCompletionSettings &completeSettings);
Utils::FilePath serverDeviceTemplate() const override; Utils::FilePath serverDeviceTemplate() const override;
@ -61,6 +65,8 @@ private:
LLMCore::ContextData prepareContext( LLMCore::ContextData prepareContext(
const QJsonObject &request, const QStringView &accumulatedCompletion = QString{}); const QJsonObject &request, const QStringView &accumulatedCompletion = QString{});
const Settings::CodeCompletionSettings &m_completeSettings;
const Settings::GeneralSettings &m_generalSettings;
LLMCore::RequestHandler m_requestHandler; LLMCore::RequestHandler m_requestHandler;
QElapsedTimer m_completionTimer; QElapsedTimer m_completionTimer;
QMap<QString, qint64> m_requestStartTimes; QMap<QString, qint64> m_requestStartTimes;

View File

@ -45,7 +45,8 @@ using namespace Core;
namespace QodeAssist { namespace QodeAssist {
QodeAssistClient::QodeAssistClient() QodeAssistClient::QodeAssistClient()
: LanguageClient::Client(new LLMClientInterface()) : LanguageClient::Client(
new LLMClientInterface(Settings::generalSettings(), Settings::codeCompletionSettings()))
, m_recentCharCount(0) , m_recentCharCount(0)
{ {
setName("Qode Assist"); setName("Qode Assist");

View File

@ -358,7 +358,7 @@ void CodeCompletionSettings::resetSettingsToDefaults()
} }
} }
QString CodeCompletionSettings::processMessageToFIM(const QString &prefix, const QString &suffix) QString CodeCompletionSettings::processMessageToFIM(const QString &prefix, const QString &suffix) const
{ {
QString result = userMessageTemplateForCC(); QString result = userMessageTemplateForCC();
result.replace("${prefix}", prefix); result.replace("${prefix}", prefix);

View File

@ -79,7 +79,7 @@ public:
// API Configuration Settings // API Configuration Settings
Utils::StringAspect apiKey{this}; Utils::StringAspect apiKey{this};
QString processMessageToFIM(const QString &prefix, const QString &suffix); QString processMessageToFIM(const QString &prefix, const QString &suffix) const;
private: private:
void setupConnections(); void setupConnections();