diff --git a/CMakeLists.txt b/CMakeLists.txt index a6d530c..f1956b8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -118,6 +118,7 @@ add_qtc_plugin(QodeAssist providers/OpenAIResponsesProvider.hpp providers/OpenAIResponsesProvider.cpp providers/QwenProvider.hpp providers/QwenProvider.cpp providers/QwenResponsesProvider.hpp providers/QwenResponsesProvider.cpp + providers/DeepSeekProvider.hpp providers/DeepSeekProvider.cpp QodeAssist.qrc LSPCompletion.hpp LLMSuggestion.hpp LLMSuggestion.cpp diff --git a/README.md b/README.md index 17dac7e..6bdaace 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ [![License: GPL v3](https://img.shields.io/badge/License-GPLv3-blue.svg)](https://www.gnu.org/licenses/gpl-3.0) [![Discord](https://dcbadge.limes.pink/api/server/BGMkUsXUgf?style=flat)](https://discord.gg/BGMkUsXUgf) -![qodeassist-icon](https://github.com/user-attachments/assets/dc336712-83cb-440d-8761-8d0a31de898d) **QodeAssist** brings a full AI coding workflow to Qt Creator for C++ and QML — smart code completion, multi-panel chat, inline quick refactoring, and project-aware tool calling. It works with local runtimes (Ollama, llama.cpp, LM Studio) and cloud providers (Claude, OpenAI, Google AI, Mistral, Qwen), can run as an **MCP server** so other clients reuse its project context, and can also act as an **MCP client** to consume tools from external MCP servers (authenticated MCP servers are not supported yet). +![qodeassist-icon](https://github.com/user-attachments/assets/dc336712-83cb-440d-8761-8d0a31de898d) **QodeAssist** brings a full AI coding workflow to Qt Creator for C++ and QML — smart code completion, multi-panel chat, inline quick refactoring, and project-aware tool calling. It works with local runtimes (Ollama, llama.cpp, LM Studio) and cloud providers (Claude, OpenAI, Google AI, Mistral, Qwen, DeepSeek), can run as an **MCP server** so other clients reuse its project context, and can also act as an **MCP client** to consume tools from external MCP servers (authenticated MCP servers are not supported yet). ⚠️ **Important Notice About Paid Providers** > When using paid providers like Claude, OpenRouter or OpenAI-compatible services: @@ -39,7 +39,8 @@ QodeAssist enhances Qt Creator with AI-powered coding assistance: - **MCP Server** — expose QodeAssist's project-aware tools to external MCP clients (Claude Code, VS Code, Claude Desktop via bridge) - **MCP Client Hub** — connect QodeAssist to external MCP servers and use their tools in Chat and Quick Refactor (authenticated MCP servers are not supported yet) - **File Context** — attach, link, or auto-sync open editor files for richer prompts -- **Many Providers** — Ollama, llama.cpp, LM Studio (Chat + Responses), Claude, OpenAI (Chat + Responses), Google AI, Mistral, Codestral, OpenRouter, Qwen (OpenAI + Responses), any OpenAI-compatible endpoint +- **Many Providers** — Ollama, llama.cpp, LM Studio (Chat + Responses), Claude, OpenAI (Chat + Responses), Google AI, Mistral, Codestral, OpenRouter, Qwen (OpenAI + Responses), DeepSeek, any OpenAI-compatible endpoint +- **Reasoning / Thinking** — streamed chain-of-thought is shown for reasoning models across Claude, Google, OpenAI Responses, and any OpenAI-compatible endpoint that returns `reasoning_content` (DeepSeek, Qwen QwQ/Qwen3-Thinking, LM Studio, OpenRouter, …) - **Customizable** — per-project rules (`.qodeassist/rules/`), agent roles, reusable refactor templates, full prompt-template control **Join our [Discord Community](https://discord.gg/BGMkUsXUgf)** to get support and connect with other users! @@ -170,6 +171,7 @@ The Quick Setup feature provides one-click configuration for popular cloud AI mo - **Mistral AI** (Codestral 2501) - **Google AI** (Gemini 2.5 Flash) - **Qwen** (Qwen3.6 Plus, Qwen3.7 Max) + - **DeepSeek** (DeepSeek V4 Flash, DeepSeek V4 Pro) 3. **Configure API Key** - Click "Configure API Key" button and enter your API key in Provider Settings All settings (provider, model, template, URL) are configured automatically. Just add your API key and you're ready to go! @@ -191,6 +193,7 @@ For advanced users or local models, choose your preferred provider and follow th - **[Mistral AI](docs/mistral-configuration.md)** / **Codestral** - **[Google AI](docs/google-ai-configuration.md)** — Gemini - **Qwen (Alibaba)** — DashScope OpenAI-compatible Chat and Responses endpoints +- **DeepSeek** — `deepseek-chat` and `deepseek-reasoner` (reasoning shown as thinking) - **OpenAI-compatible** — OpenRouter and any custom endpoint ### Recommended Models for Best Experience @@ -255,7 +258,7 @@ Configure in: `Tools → Options → QodeAssist → Code Completion → General - **[Chat Summarization](docs/chat-summarization.md)** - Compress long conversations into AI-generated summaries - **[File Context](docs/file-context.md)** - Attach or link files for better context - Automatic syncing with open editor files (optional) -- Extended thinking mode (Claude, other providers in plan) - Enable deeper reasoning for complex tasks +- Extended thinking / reasoning mode - shows streamed chain-of-thought for reasoning models (Claude, Google, OpenAI Responses, and OpenAI-compatible endpoints returning `reasoning_content` such as DeepSeek, Qwen, LM Studio, OpenRouter) ### Quick Refactoring - Inline code refactoring directly in the editor with AI assistance diff --git a/pluginllmcore/ProviderID.hpp b/pluginllmcore/ProviderID.hpp index 53c9f9e..fb2d2d9 100644 --- a/pluginllmcore/ProviderID.hpp +++ b/pluginllmcore/ProviderID.hpp @@ -15,6 +15,7 @@ enum class ProviderID { OpenRouter, GoogleAI, LlamaCpp, - Qwen + Qwen, + DeepSeek }; } diff --git a/providers/DeepSeekProvider.cpp b/providers/DeepSeekProvider.cpp new file mode 100644 index 0000000..ca12e60 --- /dev/null +++ b/providers/DeepSeekProvider.cpp @@ -0,0 +1,110 @@ +// Copyright (C) 2024-2026 Petr Mironychev +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "DeepSeekProvider.hpp" + +#include +#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 +#include +#include + +namespace QodeAssist::Providers { + +DeepSeekProvider::DeepSeekProvider(QObject *parent) + : PluginLLMCore::Provider(parent) + , m_client(new ::LLMQore::OpenAIClient(QString(), QString(), QString(), this)) +{ + Tools::registerQodeAssistTools(m_client->tools()); +} + +QString DeepSeekProvider::name() const +{ + return "DeepSeek"; +} + +QString DeepSeekProvider::apiKey() const +{ + return Settings::providerSettings().deepSeekApiKey(); +} + +QString DeepSeekProvider::url() const +{ + return "https://api.deepseek.com"; +} + +QFuture> DeepSeekProvider::getInstalledModels(const QString &url) +{ + m_client->setUrl(url); + m_client->setApiKey(apiKey()); + return m_client->listModels(); +} + +PluginLLMCore::ProviderID DeepSeekProvider::providerID() const +{ + return PluginLLMCore::ProviderID::DeepSeek; +} + +PluginLLMCore::ProviderCapabilities DeepSeekProvider::capabilities() const +{ + return PluginLLMCore::ProviderCapability::Tools + | PluginLLMCore::ProviderCapability::Thinking + | PluginLLMCore::ProviderCapability::ModelListing; +} + +void DeepSeekProvider::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.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 DeepSeek request").arg(toolsDefinitions.size())); + } + } +} + +::LLMQore::BaseClient *DeepSeekProvider::client() const +{ + return m_client; +} + +} // namespace QodeAssist::Providers diff --git a/providers/DeepSeekProvider.hpp b/providers/DeepSeekProvider.hpp new file mode 100644 index 0000000..d156ca4 --- /dev/null +++ b/providers/DeepSeekProvider.hpp @@ -0,0 +1,37 @@ +// Copyright (C) 2024-2026 Petr Mironychev +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include +#include + +namespace QodeAssist::Providers { + +class DeepSeekProvider : public PluginLLMCore::Provider +{ + Q_OBJECT +public: + explicit DeepSeekProvider(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> getInstalledModels(const QString &url) override; + PluginLLMCore::ProviderID providerID() const override; + PluginLLMCore::ProviderCapabilities capabilities() const override; + + ::LLMQore::BaseClient *client() const override; + QString apiKey() const override; + +private: + ::LLMQore::OpenAIClient *m_client; +}; + +} // namespace QodeAssist::Providers diff --git a/providers/LMStudioProvider.cpp b/providers/LMStudioProvider.cpp index ee2402b..d83f71e 100644 --- a/providers/LMStudioProvider.cpp +++ b/providers/LMStudioProvider.cpp @@ -57,7 +57,8 @@ PluginLLMCore::ProviderID LMStudioProvider::providerID() const PluginLLMCore::ProviderCapabilities LMStudioProvider::capabilities() const { return PluginLLMCore::ProviderCapability::Tools | PluginLLMCore::ProviderCapability::Image - | PluginLLMCore::ProviderCapability::ModelListing; + | PluginLLMCore::ProviderCapability::ModelListing + | PluginLLMCore::ProviderCapability::Thinking; } void LMStudioProvider::prepareRequest( diff --git a/providers/MistralAIProvider.cpp b/providers/MistralAIProvider.cpp index 99e1896..9f706a9 100644 --- a/providers/MistralAIProvider.cpp +++ b/providers/MistralAIProvider.cpp @@ -55,7 +55,8 @@ PluginLLMCore::ProviderID MistralAIProvider::providerID() const PluginLLMCore::ProviderCapabilities MistralAIProvider::capabilities() const { return PluginLLMCore::ProviderCapability::Tools | PluginLLMCore::ProviderCapability::Image - | PluginLLMCore::ProviderCapability::ModelListing; + | PluginLLMCore::ProviderCapability::ModelListing + | PluginLLMCore::ProviderCapability::Thinking; } void MistralAIProvider::prepareRequest( diff --git a/providers/OllamaCompatProvider.cpp b/providers/OllamaCompatProvider.cpp index 1b7f77f..566c4f6 100644 --- a/providers/OllamaCompatProvider.cpp +++ b/providers/OllamaCompatProvider.cpp @@ -113,7 +113,8 @@ PluginLLMCore::ProviderID OllamaCompatProvider::providerID() const PluginLLMCore::ProviderCapabilities OllamaCompatProvider::capabilities() const { return PluginLLMCore::ProviderCapability::Tools | PluginLLMCore::ProviderCapability::Image - | PluginLLMCore::ProviderCapability::ModelListing; + | PluginLLMCore::ProviderCapability::ModelListing + | PluginLLMCore::ProviderCapability::Thinking; } ::LLMQore::BaseClient *OllamaCompatProvider::client() const diff --git a/providers/OpenAICompatProvider.cpp b/providers/OpenAICompatProvider.cpp index 8c966f2..af04ca2 100644 --- a/providers/OpenAICompatProvider.cpp +++ b/providers/OpenAICompatProvider.cpp @@ -98,7 +98,8 @@ PluginLLMCore::ProviderID OpenAICompatProvider::providerID() const PluginLLMCore::ProviderCapabilities OpenAICompatProvider::capabilities() const { - return PluginLLMCore::ProviderCapability::Tools | PluginLLMCore::ProviderCapability::Image; + return PluginLLMCore::ProviderCapability::Tools | PluginLLMCore::ProviderCapability::Image + | PluginLLMCore::ProviderCapability::Thinking; } ::LLMQore::BaseClient *OpenAICompatProvider::client() const diff --git a/providers/OpenAIProvider.cpp b/providers/OpenAIProvider.cpp index cea5432..05ade77 100644 --- a/providers/OpenAIProvider.cpp +++ b/providers/OpenAIProvider.cpp @@ -129,7 +129,8 @@ PluginLLMCore::ProviderID OpenAIProvider::providerID() const PluginLLMCore::ProviderCapabilities OpenAIProvider::capabilities() const { return PluginLLMCore::ProviderCapability::Tools | PluginLLMCore::ProviderCapability::Image - | PluginLLMCore::ProviderCapability::ModelListing; + | PluginLLMCore::ProviderCapability::ModelListing + | PluginLLMCore::ProviderCapability::Thinking; } ::LLMQore::BaseClient *OpenAIProvider::client() const diff --git a/providers/Providers.hpp b/providers/Providers.hpp index 762e1ba..a80a457 100644 --- a/providers/Providers.hpp +++ b/providers/Providers.hpp @@ -5,6 +5,7 @@ #include "pluginllmcore/ProvidersManager.hpp" #include "providers/ClaudeProvider.hpp" +#include "providers/DeepSeekProvider.hpp" #include "providers/CodestralProvider.hpp" #include "providers/GoogleAIProvider.hpp" #include "providers/LMStudioProvider.hpp" @@ -40,6 +41,7 @@ inline void registerProviders() providerManager.registerProvider(); providerManager.registerProvider(); providerManager.registerProvider(); + providerManager.registerProvider(); } } // namespace QodeAssist::Providers diff --git a/settings/ConfigurationManager.cpp b/settings/ConfigurationManager.cpp index bbaeed7..81002bb 100644 --- a/settings/ConfigurationManager.cpp +++ b/settings/ConfigurationManager.cpp @@ -121,6 +121,28 @@ QVector ConfigurationManager::getPredefinedConfigurations( qwenMax.type = type; qwenMax.isPredefined = true; + AIConfiguration deepSeekFlash; + deepSeekFlash.id = "preset_deepseek_flash"; + deepSeekFlash.name = "DeepSeek V4 Flash"; + deepSeekFlash.provider = "DeepSeek"; + deepSeekFlash.model = "deepseek-v4-flash"; + deepSeekFlash.url = "https://api.deepseek.com"; + deepSeekFlash.customEndpoint = ""; + deepSeekFlash.templateName = "OpenAI Compatible"; + deepSeekFlash.type = type; + deepSeekFlash.isPredefined = true; + + AIConfiguration deepSeekPro; + deepSeekPro.id = "preset_deepseek_pro"; + deepSeekPro.name = "DeepSeek V4 Pro"; + deepSeekPro.provider = "DeepSeek"; + deepSeekPro.model = "deepseek-v4-pro"; + deepSeekPro.url = "https://api.deepseek.com"; + deepSeekPro.customEndpoint = ""; + deepSeekPro.templateName = "OpenAI Compatible"; + deepSeekPro.type = type; + deepSeekPro.isPredefined = true; + AIConfiguration gpt; gpt.id = "preset_gpt"; gpt.name = "gpt-5.5"; @@ -141,6 +163,8 @@ QVector ConfigurationManager::getPredefinedConfigurations( presets.append(geminiFlash); presets.append(qwenPlus); presets.append(qwenMax); + presets.append(deepSeekFlash); + presets.append(deepSeekPro); return presets; } diff --git a/settings/ProviderSettings.cpp b/settings/ProviderSettings.cpp index 8053bf2..9596768 100644 --- a/settings/ProviderSettings.cpp +++ b/settings/ProviderSettings.cpp @@ -132,6 +132,15 @@ ProviderSettings::ProviderSettings() qwenApiKey.setDefaultValue(""); qwenApiKey.setAutoApply(true); + // DeepSeek Settings + deepSeekApiKey.setSettingsKey(Constants::DEEPSEEK_API_KEY); + deepSeekApiKey.setLabelText(Tr::tr("DeepSeek API Key:")); + deepSeekApiKey.setDisplayStyle(Utils::StringAspect::LineEditDisplay); + deepSeekApiKey.setPlaceHolderText(Tr::tr("Enter your API key here")); + deepSeekApiKey.setHistoryCompleter(Constants::DEEPSEEK_API_KEY_HISTORY); + deepSeekApiKey.setDefaultValue(""); + deepSeekApiKey.setAutoApply(true); + resetToDefaults.m_buttonText = Tr::tr("Reset Page to Defaults"); readSettings(); @@ -163,6 +172,8 @@ ProviderSettings::ProviderSettings() Group{title(Tr::tr("llama.cpp Settings")), Column{llamaCppApiKey}}, Space{8}, Group{title(Tr::tr("Qwen (Alibaba) Settings")), Column{qwenApiKey}}, + Space{8}, + Group{title(Tr::tr("DeepSeek Settings")), Column{deepSeekApiKey}}, Stretch{1}}; }); } @@ -201,6 +212,9 @@ void ProviderSettings::setupConnections() llamaCppApiKey.writeSettings(); }); connect(&qwenApiKey, &ButtonAspect::changed, this, [this]() { qwenApiKey.writeSettings(); }); + connect(&deepSeekApiKey, &ButtonAspect::changed, this, [this]() { + deepSeekApiKey.writeSettings(); + }); } void ProviderSettings::resetSettingsToDefaults() @@ -224,6 +238,7 @@ void ProviderSettings::resetSettingsToDefaults() resetAspect(ollamaBasicAuthApiKey); resetAspect(llamaCppApiKey); resetAspect(qwenApiKey); + resetAspect(deepSeekApiKey); writeSettings(); } } diff --git a/settings/ProviderSettings.hpp b/settings/ProviderSettings.hpp index 87742ed..d8d50a7 100644 --- a/settings/ProviderSettings.hpp +++ b/settings/ProviderSettings.hpp @@ -29,6 +29,7 @@ public: Utils::StringAspect ollamaBasicAuthApiKey{this}; Utils::StringAspect llamaCppApiKey{this}; Utils::StringAspect qwenApiKey{this}; + Utils::StringAspect deepSeekApiKey{this}; private: void setupConnections(); diff --git a/settings/SettingsConstants.hpp b/settings/SettingsConstants.hpp index 9104904..d5f830d 100644 --- a/settings/SettingsConstants.hpp +++ b/settings/SettingsConstants.hpp @@ -165,6 +165,8 @@ const char LLAMA_CPP_API_KEY[] = "QodeAssist.llamaCppApiKey"; const char LLAMA_CPP_API_KEY_HISTORY[] = "QodeAssist.llamaCppApiKeyHistory"; const char QWEN_API_KEY[] = "QodeAssist.qwenApiKey"; const char QWEN_API_KEY_HISTORY[] = "QodeAssist.qwenApiKeyHistory"; +const char DEEPSEEK_API_KEY[] = "QodeAssist.deepSeekApiKey"; +const char DEEPSEEK_API_KEY_HISTORY[] = "QodeAssist.deepSeekApiKeyHistory"; const char CLAUDE_ENABLE_PROMPT_CACHING[] = "QodeAssist.claudeEnablePromptCaching"; const char CLAUDE_USE_EXTENDED_CACHE_TTL[] = "QodeAssist.claudeUseExtendedCacheTTL"; diff --git a/sources/external/llmqore b/sources/external/llmqore index 95a7b3a..68ecec3 160000 --- a/sources/external/llmqore +++ b/sources/external/llmqore @@ -1 +1 @@ -Subproject commit 95a7b3ac51b8f47bc55deffc6b3181d47c8a8f24 +Subproject commit 68ecec3dc9fe2600eab20c53dad8327e8696dc60 diff --git a/templates/OpenAICompatible.hpp b/templates/OpenAICompatible.hpp index 7822949..2180c2c 100644 --- a/templates/OpenAICompatible.hpp +++ b/templates/OpenAICompatible.hpp @@ -82,6 +82,7 @@ public: case PluginLLMCore::ProviderID::LMStudio: case PluginLLMCore::ProviderID::LlamaCpp: case PluginLLMCore::ProviderID::Qwen: + case PluginLLMCore::ProviderID::DeepSeek: return true; default: return false;