diff --git a/CMakeLists.txt b/CMakeLists.txt index 0dce79c..a6d530c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -116,6 +116,8 @@ add_qtc_plugin(QodeAssist providers/LlamaCppProvider.hpp providers/LlamaCppProvider.cpp providers/CodestralProvider.hpp providers/CodestralProvider.cpp providers/OpenAIResponsesProvider.hpp providers/OpenAIResponsesProvider.cpp + providers/QwenProvider.hpp providers/QwenProvider.cpp + providers/QwenResponsesProvider.hpp providers/QwenResponsesProvider.cpp QodeAssist.qrc LSPCompletion.hpp LLMSuggestion.hpp LLMSuggestion.cpp diff --git a/README.md b/README.md index cf437bf..17dac7e 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), 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), 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,7 @@ 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, 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), any OpenAI-compatible endpoint - **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! @@ -169,6 +169,7 @@ The Quick Setup feature provides one-click configuration for popular cloud AI mo - **OpenAI** (gpt-5.2-codex) - **Mistral AI** (Codestral 2501) - **Google AI** (Gemini 2.5 Flash) + - **Qwen** (Qwen3.6 Plus, Qwen3.7 Max) 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! @@ -189,6 +190,7 @@ For advanced users or local models, choose your preferred provider and follow th - **[OpenAI](docs/openai-configuration.md)** — Chat Completions and Responses API - **[Mistral AI](docs/mistral-configuration.md)** / **Codestral** - **[Google AI](docs/google-ai-configuration.md)** — Gemini +- **Qwen (Alibaba)** — DashScope OpenAI-compatible Chat and Responses endpoints - **OpenAI-compatible** — OpenRouter and any custom endpoint ### Recommended Models for Best Experience diff --git a/pluginllmcore/ProviderID.hpp b/pluginllmcore/ProviderID.hpp index 6682058..53c9f9e 100644 --- a/pluginllmcore/ProviderID.hpp +++ b/pluginllmcore/ProviderID.hpp @@ -14,6 +14,7 @@ enum class ProviderID { MistralAI, OpenRouter, GoogleAI, - LlamaCpp + LlamaCpp, + Qwen }; } diff --git a/providers/Providers.hpp b/providers/Providers.hpp index a8832a6..762e1ba 100644 --- a/providers/Providers.hpp +++ b/providers/Providers.hpp @@ -17,6 +17,8 @@ #include "providers/OpenAIProvider.hpp" #include "providers/OpenAIResponsesProvider.hpp" #include "providers/OpenRouterAIProvider.hpp" +#include "providers/QwenProvider.hpp" +#include "providers/QwenResponsesProvider.hpp" namespace QodeAssist::Providers { @@ -36,6 +38,8 @@ inline void registerProviders() providerManager.registerProvider(); providerManager.registerProvider(); providerManager.registerProvider(); + providerManager.registerProvider(); + providerManager.registerProvider(); } } // namespace QodeAssist::Providers diff --git a/providers/QwenProvider.cpp b/providers/QwenProvider.cpp new file mode 100644 index 0000000..ff48b45 --- /dev/null +++ b/providers/QwenProvider.cpp @@ -0,0 +1,34 @@ +// Copyright (C) 2024-2026 Petr Mironychev +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "QwenProvider.hpp" + +#include "settings/ProviderSettings.hpp" + +namespace QodeAssist::Providers { + +QwenProvider::QwenProvider(QObject *parent) + : OpenAICompatProvider(parent) +{} + +QString QwenProvider::name() const +{ + return "Qwen (OpenAI)"; +} + +QString QwenProvider::apiKey() const +{ + return Settings::providerSettings().qwenApiKey(); +} + +QString QwenProvider::url() const +{ + return "https://dashscope-intl.aliyuncs.com/compatible-mode/v1"; +} + +PluginLLMCore::ProviderID QwenProvider::providerID() const +{ + return PluginLLMCore::ProviderID::Qwen; +} + +} // namespace QodeAssist::Providers diff --git a/providers/QwenProvider.hpp b/providers/QwenProvider.hpp new file mode 100644 index 0000000..8cacedb --- /dev/null +++ b/providers/QwenProvider.hpp @@ -0,0 +1,21 @@ +// Copyright (C) 2024-2026 Petr Mironychev +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include "providers/OpenAICompatProvider.hpp" + +namespace QodeAssist::Providers { + +class QwenProvider : public OpenAICompatProvider +{ +public: + explicit QwenProvider(QObject *parent = nullptr); + + QString name() const override; + QString url() const override; + QString apiKey() const override; + PluginLLMCore::ProviderID providerID() const override; +}; + +} // namespace QodeAssist::Providers diff --git a/providers/QwenResponsesProvider.cpp b/providers/QwenResponsesProvider.cpp new file mode 100644 index 0000000..8bcc9b5 --- /dev/null +++ b/providers/QwenResponsesProvider.cpp @@ -0,0 +1,40 @@ +// Copyright (C) 2024-2026 Petr Mironychev +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "QwenResponsesProvider.hpp" + +#include "settings/ProviderSettings.hpp" + +namespace QodeAssist::Providers { + +QwenResponsesProvider::QwenResponsesProvider(QObject *parent) + : OpenAIResponsesProvider(parent) +{} + +QString QwenResponsesProvider::name() const +{ + return "Qwen (OpenAI Response)"; +} + +QString QwenResponsesProvider::apiKey() const +{ + return Settings::providerSettings().qwenApiKey(); +} + +QString QwenResponsesProvider::url() const +{ + return "https://dashscope-intl.aliyuncs.com/compatible-mode/v1"; +} + +QFuture> QwenResponsesProvider::getInstalledModels(const QString &) +{ + return QtFuture::makeReadyFuture(QList{}); +} + +PluginLLMCore::ProviderCapabilities QwenResponsesProvider::capabilities() const +{ + return PluginLLMCore::ProviderCapability::Tools | PluginLLMCore::ProviderCapability::Thinking + | PluginLLMCore::ProviderCapability::Image; +} + +} // namespace QodeAssist::Providers diff --git a/providers/QwenResponsesProvider.hpp b/providers/QwenResponsesProvider.hpp new file mode 100644 index 0000000..a685670 --- /dev/null +++ b/providers/QwenResponsesProvider.hpp @@ -0,0 +1,23 @@ +// Copyright (C) 2024-2026 Petr Mironychev +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include "providers/OpenAIResponsesProvider.hpp" + +namespace QodeAssist::Providers { + +class QwenResponsesProvider : public OpenAIResponsesProvider +{ + Q_OBJECT +public: + explicit QwenResponsesProvider(QObject *parent = nullptr); + + QString name() const override; + QString url() const override; + QString apiKey() const override; + QFuture> getInstalledModels(const QString &url) override; + PluginLLMCore::ProviderCapabilities capabilities() const override; +}; + +} // namespace QodeAssist::Providers diff --git a/settings/ConfigurationManager.cpp b/settings/ConfigurationManager.cpp index b090c63..bbaeed7 100644 --- a/settings/ConfigurationManager.cpp +++ b/settings/ConfigurationManager.cpp @@ -99,6 +99,28 @@ QVector ConfigurationManager::getPredefinedConfigurations( geminiFlash.type = type; geminiFlash.isPredefined = true; + AIConfiguration qwenPlus; + qwenPlus.id = "preset_qwen_plus"; + qwenPlus.name = "Qwen3.6 Plus"; + qwenPlus.provider = "Qwen (OpenAI Response)"; + qwenPlus.model = "qwen3.6-plus"; + qwenPlus.url = "https://dashscope-intl.aliyuncs.com/compatible-mode/v1"; + qwenPlus.customEndpoint = ""; + qwenPlus.templateName = "OpenAI Responses"; + qwenPlus.type = type; + qwenPlus.isPredefined = true; + + AIConfiguration qwenMax; + qwenMax.id = "preset_qwen_max"; + qwenMax.name = "Qwen3.7 Max"; + qwenMax.provider = "Qwen (OpenAI Response)"; + qwenMax.model = "qwen3.7-max"; + qwenMax.url = "https://dashscope-intl.aliyuncs.com/compatible-mode/v1"; + qwenMax.customEndpoint = ""; + qwenMax.templateName = "OpenAI Responses"; + qwenMax.type = type; + qwenMax.isPredefined = true; + AIConfiguration gpt; gpt.id = "preset_gpt"; gpt.name = "gpt-5.5"; @@ -117,6 +139,8 @@ QVector ConfigurationManager::getPredefinedConfigurations( presets.append(codestral); presets.append(mistral); presets.append(geminiFlash); + presets.append(qwenPlus); + presets.append(qwenMax); return presets; } diff --git a/settings/ProviderSettings.cpp b/settings/ProviderSettings.cpp index c0df2f9..8053bf2 100644 --- a/settings/ProviderSettings.cpp +++ b/settings/ProviderSettings.cpp @@ -123,6 +123,15 @@ ProviderSettings::ProviderSettings() llamaCppApiKey.setDefaultValue(""); llamaCppApiKey.setAutoApply(true); + // Qwen (Alibaba) Settings + qwenApiKey.setSettingsKey(Constants::QWEN_API_KEY); + qwenApiKey.setLabelText(Tr::tr("Qwen API Key:")); + qwenApiKey.setDisplayStyle(Utils::StringAspect::LineEditDisplay); + qwenApiKey.setPlaceHolderText(Tr::tr("Enter your API key here")); + qwenApiKey.setHistoryCompleter(Constants::QWEN_API_KEY_HISTORY); + qwenApiKey.setDefaultValue(""); + qwenApiKey.setAutoApply(true); + resetToDefaults.m_buttonText = Tr::tr("Reset Page to Defaults"); readSettings(); @@ -152,6 +161,8 @@ ProviderSettings::ProviderSettings() Group{title(Tr::tr("Ollama Settings")), Column{ollamaBasicAuthApiKey}}, Space{8}, Group{title(Tr::tr("llama.cpp Settings")), Column{llamaCppApiKey}}, + Space{8}, + Group{title(Tr::tr("Qwen (Alibaba) Settings")), Column{qwenApiKey}}, Stretch{1}}; }); } @@ -189,6 +200,7 @@ void ProviderSettings::setupConnections() connect(&llamaCppApiKey, &ButtonAspect::changed, this, [this]() { llamaCppApiKey.writeSettings(); }); + connect(&qwenApiKey, &ButtonAspect::changed, this, [this]() { qwenApiKey.writeSettings(); }); } void ProviderSettings::resetSettingsToDefaults() @@ -211,6 +223,7 @@ void ProviderSettings::resetSettingsToDefaults() resetAspect(googleAiApiKey); resetAspect(ollamaBasicAuthApiKey); resetAspect(llamaCppApiKey); + resetAspect(qwenApiKey); writeSettings(); } } diff --git a/settings/ProviderSettings.hpp b/settings/ProviderSettings.hpp index d2ed259..87742ed 100644 --- a/settings/ProviderSettings.hpp +++ b/settings/ProviderSettings.hpp @@ -28,6 +28,7 @@ public: Utils::StringAspect googleAiApiKey{this}; Utils::StringAspect ollamaBasicAuthApiKey{this}; Utils::StringAspect llamaCppApiKey{this}; + Utils::StringAspect qwenApiKey{this}; private: void setupConnections(); diff --git a/settings/SettingsConstants.hpp b/settings/SettingsConstants.hpp index 363fba8..9104904 100644 --- a/settings/SettingsConstants.hpp +++ b/settings/SettingsConstants.hpp @@ -163,6 +163,8 @@ const char OLLAMA_BASIC_AUTH_API_KEY[] = "QodeAssist.ollamaBasicAuthApiKey"; const char OLLAMA_BASIC_AUTH_API_KEY_HISTORY[] = "QodeAssist.ollamaBasicAuthApiKeyHistory"; 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 CLAUDE_ENABLE_PROMPT_CACHING[] = "QodeAssist.claudeEnablePromptCaching"; const char CLAUDE_USE_EXTENDED_CACHE_TTL[] = "QodeAssist.claudeUseExtendedCacheTTL"; diff --git a/templates/OpenAICompatible.hpp b/templates/OpenAICompatible.hpp index ee33563..7822949 100644 --- a/templates/OpenAICompatible.hpp +++ b/templates/OpenAICompatible.hpp @@ -81,6 +81,7 @@ public: case PluginLLMCore::ProviderID::OpenRouter: case PluginLLMCore::ProviderID::LMStudio: case PluginLLMCore::ProviderID::LlamaCpp: + case PluginLLMCore::ProviderID::Qwen: return true; default: return false;