mirror of
https://github.com/Palm1r/QodeAssist.git
synced 2026-05-30 02:49:12 -04:00
feat: LM Studio response API and Ollama OpenAI API
This commit is contained in:
@@ -28,7 +28,7 @@ LMStudioProvider::LMStudioProvider(QObject *parent)
|
||||
|
||||
QString LMStudioProvider::name() const
|
||||
{
|
||||
return "LM Studio";
|
||||
return "LM Studio (Chat Completions)";
|
||||
}
|
||||
|
||||
QString LMStudioProvider::apiKey() const
|
||||
|
||||
148
providers/LMStudioResponsesProvider.cpp
Normal file
148
providers/LMStudioResponsesProvider.cpp
Normal file
@@ -0,0 +1,148 @@
|
||||
// Copyright (C) 2024-2026 Petr Mironychev
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#include "LMStudioResponsesProvider.hpp"
|
||||
|
||||
#include <LLMQore/ToolsManager.hpp>
|
||||
|
||||
#include "logger/Logger.hpp"
|
||||
#include "settings/ChatAssistantSettings.hpp"
|
||||
#include "settings/CodeCompletionSettings.hpp"
|
||||
#include "settings/GeneralSettings.hpp"
|
||||
#include "settings/ProviderSettings.hpp"
|
||||
#include "settings/QuickRefactorSettings.hpp"
|
||||
#include "tools/ToolsRegistration.hpp"
|
||||
|
||||
#include <QJsonArray>
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonObject>
|
||||
|
||||
namespace QodeAssist::Providers {
|
||||
|
||||
LMStudioResponsesProvider::LMStudioResponsesProvider(QObject *parent)
|
||||
: PluginLLMCore::Provider(parent)
|
||||
, m_client(new ::LLMQore::OpenAIResponsesClient(QString(), QString(), QString(), this))
|
||||
{
|
||||
Tools::registerQodeAssistTools(m_client->tools());
|
||||
}
|
||||
|
||||
QString LMStudioResponsesProvider::name() const
|
||||
{
|
||||
return "LM Studio (Responses API)";
|
||||
}
|
||||
|
||||
QString LMStudioResponsesProvider::apiKey() const
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
QString LMStudioResponsesProvider::url() const
|
||||
{
|
||||
return "http://localhost:1234";
|
||||
}
|
||||
|
||||
void LMStudioResponsesProvider::prepareRequest(
|
||||
QJsonObject &request,
|
||||
PluginLLMCore::PromptTemplate *prompt,
|
||||
PluginLLMCore::ContextData context,
|
||||
PluginLLMCore::RequestType type,
|
||||
bool isToolsEnabled,
|
||||
bool isThinkingEnabled)
|
||||
{
|
||||
if (!prompt->isSupportProvider(providerID())) {
|
||||
LOG_MESSAGE(QString("Template %1 doesn't support %2 provider").arg(name(), prompt->name()));
|
||||
}
|
||||
|
||||
prompt->prepareRequest(request, context);
|
||||
|
||||
auto applyModelParams = [&request](const auto &settings) {
|
||||
request["max_output_tokens"] = settings.maxTokens();
|
||||
|
||||
if (settings.useTopP()) {
|
||||
request["top_p"] = settings.topP();
|
||||
}
|
||||
};
|
||||
|
||||
auto applyThinkingMode = [&request](const auto &settings) {
|
||||
QString effortStr = settings.openAIResponsesReasoningEffort.stringValue().toLower();
|
||||
if (effortStr.isEmpty()) {
|
||||
effortStr = "medium";
|
||||
}
|
||||
|
||||
QJsonObject reasoning;
|
||||
reasoning["effort"] = effortStr;
|
||||
request["reasoning"] = reasoning;
|
||||
request["max_output_tokens"] = settings.thinkingMaxTokens();
|
||||
request["store"] = true;
|
||||
|
||||
QJsonArray include;
|
||||
include.append("reasoning.encrypted_content");
|
||||
request["include"] = include;
|
||||
};
|
||||
|
||||
if (type == PluginLLMCore::RequestType::CodeCompletion) {
|
||||
applyModelParams(Settings::codeCompletionSettings());
|
||||
} else if (type == PluginLLMCore::RequestType::QuickRefactoring) {
|
||||
const auto &qrSettings = Settings::quickRefactorSettings();
|
||||
applyModelParams(qrSettings);
|
||||
|
||||
if (isThinkingEnabled) {
|
||||
applyThinkingMode(qrSettings);
|
||||
}
|
||||
} else {
|
||||
const auto &chatSettings = Settings::chatAssistantSettings();
|
||||
applyModelParams(chatSettings);
|
||||
|
||||
if (isThinkingEnabled) {
|
||||
applyThinkingMode(chatSettings);
|
||||
}
|
||||
}
|
||||
|
||||
if (isToolsEnabled) {
|
||||
const auto toolsDefinitions = m_client->tools()->getToolsDefinitions();
|
||||
if (!toolsDefinitions.isEmpty()) {
|
||||
request["tools"] = toolsDefinitions;
|
||||
LOG_MESSAGE(QString("Added %1 tools to LM Studio Responses request")
|
||||
.arg(toolsDefinitions.size()));
|
||||
}
|
||||
}
|
||||
|
||||
request["stream"] = true;
|
||||
}
|
||||
|
||||
QFuture<QList<QString>> LMStudioResponsesProvider::getInstalledModels(const QString &baseUrl)
|
||||
{
|
||||
QString url = baseUrl;
|
||||
if (!url.endsWith(QStringLiteral("/v1")))
|
||||
url += QStringLiteral("/v1");
|
||||
m_client->setUrl(url);
|
||||
m_client->setApiKey(apiKey());
|
||||
return m_client->listModels();
|
||||
}
|
||||
|
||||
PluginLLMCore::RequestID LMStudioResponsesProvider::sendRequest(
|
||||
const QUrl &url, const QJsonObject &payload, const QString &endpoint)
|
||||
{
|
||||
const QString effectiveEndpoint
|
||||
= endpoint.isEmpty() ? QStringLiteral("/v1/responses") : endpoint;
|
||||
return PluginLLMCore::Provider::sendRequest(url, payload, effectiveEndpoint);
|
||||
}
|
||||
|
||||
PluginLLMCore::ProviderID LMStudioResponsesProvider::providerID() const
|
||||
{
|
||||
return PluginLLMCore::ProviderID::OpenAIResponses;
|
||||
}
|
||||
|
||||
PluginLLMCore::ProviderCapabilities LMStudioResponsesProvider::capabilities() const
|
||||
{
|
||||
return PluginLLMCore::ProviderCapability::Tools | PluginLLMCore::ProviderCapability::Thinking
|
||||
| PluginLLMCore::ProviderCapability::Image
|
||||
| PluginLLMCore::ProviderCapability::ModelListing;
|
||||
}
|
||||
|
||||
::LLMQore::BaseClient *LMStudioResponsesProvider::client() const
|
||||
{
|
||||
return m_client;
|
||||
}
|
||||
|
||||
} // namespace QodeAssist::Providers
|
||||
40
providers/LMStudioResponsesProvider.hpp
Normal file
40
providers/LMStudioResponsesProvider.hpp
Normal file
@@ -0,0 +1,40 @@
|
||||
// Copyright (C) 2024-2026 Petr Mironychev
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <LLMQore/OpenAIResponsesClient.hpp>
|
||||
#include <pluginllmcore/Provider.hpp>
|
||||
|
||||
namespace QodeAssist::Providers {
|
||||
|
||||
class LMStudioResponsesProvider : public PluginLLMCore::Provider
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit LMStudioResponsesProvider(QObject *parent = nullptr);
|
||||
|
||||
QString name() const override;
|
||||
QString url() const override;
|
||||
void prepareRequest(
|
||||
QJsonObject &request,
|
||||
PluginLLMCore::PromptTemplate *prompt,
|
||||
PluginLLMCore::ContextData context,
|
||||
PluginLLMCore::RequestType type,
|
||||
bool isToolsEnabled,
|
||||
bool isThinkingEnabled) override;
|
||||
QFuture<QList<QString>> getInstalledModels(const QString &url) override;
|
||||
PluginLLMCore::ProviderID providerID() const override;
|
||||
PluginLLMCore::ProviderCapabilities capabilities() const override;
|
||||
|
||||
::LLMQore::BaseClient *client() const override;
|
||||
QString apiKey() const override;
|
||||
|
||||
PluginLLMCore::RequestID sendRequest(
|
||||
const QUrl &url, const QJsonObject &payload, const QString &endpoint) override;
|
||||
|
||||
private:
|
||||
::LLMQore::OpenAIResponsesClient *m_client;
|
||||
};
|
||||
|
||||
} // namespace QodeAssist::Providers
|
||||
124
providers/OllamaCompatProvider.cpp
Normal file
124
providers/OllamaCompatProvider.cpp
Normal file
@@ -0,0 +1,124 @@
|
||||
// Copyright (C) 2024-2026 Petr Mironychev
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#include "OllamaCompatProvider.hpp"
|
||||
|
||||
#include <LLMQore/ToolsManager.hpp>
|
||||
|
||||
#include "logger/Logger.hpp"
|
||||
#include "settings/ChatAssistantSettings.hpp"
|
||||
#include "settings/CodeCompletionSettings.hpp"
|
||||
#include "settings/GeneralSettings.hpp"
|
||||
#include "settings/ProviderSettings.hpp"
|
||||
#include "settings/QuickRefactorSettings.hpp"
|
||||
#include "tools/ToolsRegistration.hpp"
|
||||
|
||||
#include <QJsonArray>
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonObject>
|
||||
|
||||
namespace QodeAssist::Providers {
|
||||
|
||||
OllamaCompatProvider::OllamaCompatProvider(QObject *parent)
|
||||
: PluginLLMCore::Provider(parent)
|
||||
, m_client(new ::LLMQore::OpenAIClient(QString(), QString(), QString(), this))
|
||||
{
|
||||
Tools::registerQodeAssistTools(m_client->tools());
|
||||
}
|
||||
|
||||
QString OllamaCompatProvider::name() const
|
||||
{
|
||||
return "Ollama (OpenAI-compatible)";
|
||||
}
|
||||
|
||||
QString OllamaCompatProvider::apiKey() const
|
||||
{
|
||||
return Settings::providerSettings().ollamaBasicAuthApiKey();
|
||||
}
|
||||
|
||||
QString OllamaCompatProvider::url() const
|
||||
{
|
||||
return "http://localhost:11434";
|
||||
}
|
||||
|
||||
void OllamaCompatProvider::prepareRequest(
|
||||
QJsonObject &request,
|
||||
PluginLLMCore::PromptTemplate *prompt,
|
||||
PluginLLMCore::ContextData context,
|
||||
PluginLLMCore::RequestType type,
|
||||
bool isToolsEnabled,
|
||||
bool isThinkingEnabled)
|
||||
{
|
||||
if (!prompt->isSupportProvider(providerID())) {
|
||||
LOG_MESSAGE(QString("Template %1 doesn't support %2 provider").arg(name(), prompt->name()));
|
||||
}
|
||||
|
||||
prompt->prepareRequest(request, context);
|
||||
|
||||
auto applyModelParams = [&request](const auto &settings) {
|
||||
request["max_tokens"] = settings.maxTokens();
|
||||
request["temperature"] = settings.temperature();
|
||||
|
||||
if (settings.useTopP())
|
||||
request["top_p"] = settings.topP();
|
||||
if (settings.useTopK())
|
||||
request["top_k"] = settings.topK();
|
||||
if (settings.useFrequencyPenalty())
|
||||
request["frequency_penalty"] = settings.frequencyPenalty();
|
||||
if (settings.usePresencePenalty())
|
||||
request["presence_penalty"] = settings.presencePenalty();
|
||||
};
|
||||
|
||||
if (type == PluginLLMCore::RequestType::CodeCompletion) {
|
||||
applyModelParams(Settings::codeCompletionSettings());
|
||||
} else if (type == PluginLLMCore::RequestType::QuickRefactoring) {
|
||||
applyModelParams(Settings::quickRefactorSettings());
|
||||
} else {
|
||||
applyModelParams(Settings::chatAssistantSettings());
|
||||
}
|
||||
|
||||
if (isToolsEnabled) {
|
||||
auto toolsDefinitions = m_client->tools()->getToolsDefinitions();
|
||||
if (!toolsDefinitions.isEmpty()) {
|
||||
request["tools"] = toolsDefinitions;
|
||||
LOG_MESSAGE(
|
||||
QString("Added %1 tools to OllamaCompat request").arg(toolsDefinitions.size()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QFuture<QList<QString>> OllamaCompatProvider::getInstalledModels(const QString &baseUrl)
|
||||
{
|
||||
QString url = baseUrl;
|
||||
if (!url.endsWith(QStringLiteral("/v1")))
|
||||
url += QStringLiteral("/v1");
|
||||
m_client->setUrl(url);
|
||||
m_client->setApiKey(apiKey());
|
||||
return m_client->listModels();
|
||||
}
|
||||
|
||||
PluginLLMCore::RequestID OllamaCompatProvider::sendRequest(
|
||||
const QUrl &url, const QJsonObject &payload, const QString &endpoint)
|
||||
{
|
||||
const QString effectiveEndpoint
|
||||
= endpoint.isEmpty() ? QStringLiteral("/v1/chat/completions") : endpoint;
|
||||
return PluginLLMCore::Provider::sendRequest(url, payload, effectiveEndpoint);
|
||||
}
|
||||
|
||||
PluginLLMCore::ProviderID OllamaCompatProvider::providerID() const
|
||||
{
|
||||
return PluginLLMCore::ProviderID::OpenAICompatible;
|
||||
}
|
||||
|
||||
PluginLLMCore::ProviderCapabilities OllamaCompatProvider::capabilities() const
|
||||
{
|
||||
return PluginLLMCore::ProviderCapability::Tools | PluginLLMCore::ProviderCapability::Image
|
||||
| PluginLLMCore::ProviderCapability::ModelListing;
|
||||
}
|
||||
|
||||
::LLMQore::BaseClient *OllamaCompatProvider::client() const
|
||||
{
|
||||
return m_client;
|
||||
}
|
||||
|
||||
} // namespace QodeAssist::Providers
|
||||
40
providers/OllamaCompatProvider.hpp
Normal file
40
providers/OllamaCompatProvider.hpp
Normal file
@@ -0,0 +1,40 @@
|
||||
// Copyright (C) 2024-2026 Petr Mironychev
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <LLMQore/OpenAIClient.hpp>
|
||||
#include <pluginllmcore/Provider.hpp>
|
||||
|
||||
namespace QodeAssist::Providers {
|
||||
|
||||
class OllamaCompatProvider : public PluginLLMCore::Provider
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit OllamaCompatProvider(QObject *parent = nullptr);
|
||||
|
||||
QString name() const override;
|
||||
QString url() const override;
|
||||
void prepareRequest(
|
||||
QJsonObject &request,
|
||||
PluginLLMCore::PromptTemplate *prompt,
|
||||
PluginLLMCore::ContextData context,
|
||||
PluginLLMCore::RequestType type,
|
||||
bool isToolsEnabled,
|
||||
bool isThinkingEnabled) override;
|
||||
QFuture<QList<QString>> getInstalledModels(const QString &url) override;
|
||||
PluginLLMCore::ProviderID providerID() const override;
|
||||
PluginLLMCore::ProviderCapabilities capabilities() const override;
|
||||
|
||||
::LLMQore::BaseClient *client() const override;
|
||||
QString apiKey() const override;
|
||||
|
||||
PluginLLMCore::RequestID sendRequest(
|
||||
const QUrl &url, const QJsonObject &payload, const QString &endpoint) override;
|
||||
|
||||
private:
|
||||
::LLMQore::OpenAIClient *m_client;
|
||||
};
|
||||
|
||||
} // namespace QodeAssist::Providers
|
||||
@@ -28,7 +28,7 @@ OllamaProvider::OllamaProvider(QObject *parent)
|
||||
|
||||
QString OllamaProvider::name() const
|
||||
{
|
||||
return "Ollama";
|
||||
return "Ollama (Native)";
|
||||
}
|
||||
|
||||
QString OllamaProvider::apiKey() const
|
||||
|
||||
@@ -27,7 +27,7 @@ OpenAIProvider::OpenAIProvider(QObject *parent)
|
||||
|
||||
QString OpenAIProvider::name() const
|
||||
{
|
||||
return "OpenAI";
|
||||
return "OpenAI (Chat Completions)";
|
||||
}
|
||||
|
||||
QString OpenAIProvider::apiKey() const
|
||||
|
||||
@@ -27,7 +27,7 @@ OpenAIResponsesProvider::OpenAIResponsesProvider(QObject *parent)
|
||||
|
||||
QString OpenAIResponsesProvider::name() const
|
||||
{
|
||||
return "OpenAI Responses";
|
||||
return "OpenAI (Responses API)";
|
||||
}
|
||||
|
||||
QString OpenAIResponsesProvider::apiKey() const
|
||||
@@ -98,24 +98,11 @@ void OpenAIResponsesProvider::prepareRequest(
|
||||
}
|
||||
|
||||
if (isToolsEnabled) {
|
||||
const auto toolsDefinitions
|
||||
= m_client->tools()->getToolsDefinitions();
|
||||
const auto toolsDefinitions = m_client->tools()->getToolsDefinitions();
|
||||
if (!toolsDefinitions.isEmpty()) {
|
||||
QJsonArray responsesTools;
|
||||
|
||||
for (const QJsonValue &toolValue : toolsDefinitions) {
|
||||
const QJsonObject tool = toolValue.toObject();
|
||||
if (tool.contains("function")) {
|
||||
const QJsonObject functionObj = tool["function"].toObject();
|
||||
QJsonObject responsesTool;
|
||||
responsesTool["type"] = "function";
|
||||
responsesTool["name"] = functionObj["name"];
|
||||
responsesTool["description"] = functionObj["description"];
|
||||
responsesTool["parameters"] = functionObj["parameters"];
|
||||
responsesTools.append(responsesTool);
|
||||
}
|
||||
}
|
||||
request["tools"] = responsesTools;
|
||||
request["tools"] = toolsDefinitions;
|
||||
LOG_MESSAGE(QString("Added %1 tools to OpenAI Responses request")
|
||||
.arg(toolsDefinitions.size()));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -8,8 +8,10 @@
|
||||
#include "providers/CodestralProvider.hpp"
|
||||
#include "providers/GoogleAIProvider.hpp"
|
||||
#include "providers/LMStudioProvider.hpp"
|
||||
#include "providers/LMStudioResponsesProvider.hpp"
|
||||
#include "providers/LlamaCppProvider.hpp"
|
||||
#include "providers/MistralAIProvider.hpp"
|
||||
#include "providers/OllamaCompatProvider.hpp"
|
||||
#include "providers/OllamaProvider.hpp"
|
||||
#include "providers/OpenAICompatProvider.hpp"
|
||||
#include "providers/OpenAIProvider.hpp"
|
||||
@@ -22,11 +24,13 @@ inline void registerProviders()
|
||||
{
|
||||
auto &providerManager = PluginLLMCore::ProvidersManager::instance();
|
||||
providerManager.registerProvider<OllamaProvider>();
|
||||
providerManager.registerProvider<OllamaCompatProvider>();
|
||||
providerManager.registerProvider<ClaudeProvider>();
|
||||
providerManager.registerProvider<OpenAIProvider>();
|
||||
providerManager.registerProvider<OpenAIResponsesProvider>();
|
||||
providerManager.registerProvider<OpenAICompatProvider>();
|
||||
providerManager.registerProvider<LMStudioProvider>();
|
||||
providerManager.registerProvider<LMStudioResponsesProvider>();
|
||||
providerManager.registerProvider<OpenRouterProvider>();
|
||||
providerManager.registerProvider<MistralAIProvider>();
|
||||
providerManager.registerProvider<GoogleAIProvider>();
|
||||
|
||||
Reference in New Issue
Block a user