diff --git a/settings/ProviderDetailPane.cpp b/settings/ProviderDetailPane.cpp index dec69bd..1fd0046 100644 --- a/settings/ProviderDetailPane.cpp +++ b/settings/ProviderDetailPane.cpp @@ -42,6 +42,11 @@ ProviderDetailPane::ProviderDetailPane(QWidget *parent) spp.setColor(QPalette::WindowText, Utils::creatorColor(Utils::Theme::PanelTextColorMid)); m_sourcePathLabel->setPalette(spp); + m_protocolLabel = new QLabel(this); + m_protocolLabel->setPalette(spp); + m_protocolLabel->setToolTip( + tr("The client API (protocol) this provider speaks. Fixed when the provider is created.")); + m_editBtn = new QPushButton(tr("Edit…"), this); m_editBtn->setDefault(true); m_openInEditorBtn = new QPushButton(tr("Open in editor"), this); @@ -89,6 +94,7 @@ ProviderDetailPane::ProviderDetailPane(QWidget *parent) headerLeft->setContentsMargins(0, 0, 0, 0); headerLeft->setSpacing(2); headerLeft->addLayout(titleRow); + headerLeft->addWidget(m_protocolLabel); headerLeft->addWidget(m_sourcePathLabel); auto *headerRow = new QHBoxLayout; @@ -107,8 +113,6 @@ ProviderDetailPane::ProviderDetailPane(QWidget *parent) auto *identitySection = new SectionBox(tr("Identity"), this); m_nameEdit = new QLineEdit(this); - m_typeEdit = new QLineEdit(this); - m_typeEdit->setReadOnly(true); m_descriptionEdit = new QPlainTextEdit(this); m_descriptionEdit->setMaximumHeight(60); m_descriptionEdit->setReadOnly(true); @@ -118,9 +122,6 @@ ProviderDetailPane::ProviderDetailPane(QWidget *parent) identityGrid->setVerticalSpacing(4); FormBuilder(identityGrid) .row(tr("Name:"), m_nameEdit) - .row(tr("Client API:"), m_typeEdit, - tr("The client API this provider speaks. " - "Cannot be changed after creation.")) .row(tr("Description:"), m_descriptionEdit); identitySection->bodyLayout()->addLayout(identityGrid); @@ -151,8 +152,10 @@ ProviderDetailPane::ProviderDetailPane(QWidget *parent) m_revealKeyBtn = new QToolButton(this); m_revealKeyBtn->setText(QStringLiteral("πŸ‘")); m_revealKeyBtn->setCheckable(true); - m_revealKeyBtn->setToolTip(tr("Show / hide API key")); + m_revealKeyBtn->setToolTip(tr("Show / hide the API key (loads the stored key)")); connect(m_revealKeyBtn, &QToolButton::toggled, this, [this](bool on) { + if (on && m_apiKeyEdit->text().isEmpty() && m_currentHasStoredKey) + emit apiKeyRevealRequested(); m_apiKeyEdit->setEchoMode(on ? QLineEdit::Normal : QLineEdit::Password); }); m_apiKeySaveBtn = new QPushButton(tr("Save key"), this); @@ -307,7 +310,7 @@ void ProviderDetailPane::populate(const Providers::ProviderInstance &inst, bool inst.description.isEmpty() ? tr("No description provided.") : inst.description); m_nameEdit->setText(inst.name); - m_typeEdit->setText(inst.clientApi); + m_protocolLabel->setText(tr("via %1").arg(inst.clientApi)); m_descriptionEdit->setPlainText(inst.description); m_urlEdit->setText(inst.url); @@ -363,7 +366,7 @@ void ProviderDetailPane::clear() m_sourcePathLabel->clear(); m_descriptionLabel->clear(); m_nameEdit->clear(); - m_typeEdit->clear(); + m_protocolLabel->clear(); m_descriptionEdit->clear(); m_urlEdit->clear(); m_apiKeyEdit->clear(); @@ -476,6 +479,16 @@ void ProviderDetailPane::changeEvent(QEvent *event) } } +void ProviderDetailPane::showRevealedKey(const QString &key) +{ + if (key.isEmpty()) + return; + m_apiKeyEdit->setText(key); + m_apiKeyEdit->setEchoMode(QLineEdit::Normal); + if (!m_revealKeyBtn->isChecked()) + m_revealKeyBtn->setChecked(true); +} + void ProviderDetailPane::setEditing(bool on) { m_editing = on; diff --git a/settings/ProviderDetailPane.hpp b/settings/ProviderDetailPane.hpp index 2526150..c27ae04 100644 --- a/settings/ProviderDetailPane.hpp +++ b/settings/ProviderDetailPane.hpp @@ -32,6 +32,7 @@ public: void populate(const Providers::ProviderInstance &inst, bool hasStoredKey); void clear(); void refreshKeyStatus(bool hasStoredKey); + void showRevealedKey(const QString &key); void setLaunchState(Providers::ProviderLauncher::State st, const QString &lastError); void resetLaunchTerminal(const QByteArray &scrollback); void appendLaunchBytes(const QByteArray &chunk); @@ -44,6 +45,7 @@ signals: void deleteRequested(); void apiKeySaveRequested(const QString &newKey); void apiKeyClearRequested(); + void apiKeyRevealRequested(); void launchStartRequested(const QString &providerName); void launchStopRequested(const QString &providerName); void launchRestartRequested(const QString &providerName); @@ -74,7 +76,7 @@ private: QLabel *m_descriptionLabel = nullptr; QLineEdit *m_nameEdit = nullptr; - QLineEdit *m_typeEdit = nullptr; + QLabel *m_protocolLabel = nullptr; QPlainTextEdit *m_descriptionEdit = nullptr; QLineEdit *m_urlEdit = nullptr; QLabel *m_samplePreview = nullptr; diff --git a/settings/ProvidersSettingsPage.cpp b/settings/ProvidersSettingsPage.cpp index c5950e9..c566008 100644 --- a/settings/ProvidersSettingsPage.cpp +++ b/settings/ProvidersSettingsPage.cpp @@ -125,6 +125,8 @@ public: this, &ProvidersPageWidget::onApiKeySave); connect(m_detailPane, &ProviderDetailPane::apiKeyClearRequested, this, &ProvidersPageWidget::onApiKeyClear); + connect(m_detailPane, &ProviderDetailPane::apiKeyRevealRequested, + this, &ProvidersPageWidget::onApiKeyReveal); connect(m_detailPane, &ProviderDetailPane::launchStartRequested, this, &ProvidersPageWidget::onLaunchStart); connect(m_detailPane, &ProviderDetailPane::launchStopRequested, @@ -447,6 +449,16 @@ private slots: m_detailPane->refreshKeyStatus(true); } + void onApiKeyReveal() + { + if (!m_factory || !m_secrets || m_currentName.isEmpty()) + return; + const auto *inst = m_factory->instanceByName(m_currentName); + if (!inst || inst->apiKeyRef.isEmpty()) + return; + m_detailPane->showRevealedKey(m_secrets->readKeySync(inst->apiKeyRef)); + } + void onApiKeyClear() { if (!m_factory || !m_secrets || m_currentName.isEmpty()) diff --git a/sources/providersConfig/claude.toml b/sources/providersConfig/claude.toml index fd6eac1..5004d83 100644 --- a/sources/providersConfig/claude.toml +++ b/sources/providersConfig/claude.toml @@ -2,7 +2,7 @@ schema_version = 1 name = "Claude" client_api = "Claude" -description = "Anthropic's hosted Claude API." +description = "Cloud (Anthropic). Claude Opus/Sonnet/Haiku via the Messages API." url = "https://api.anthropic.com" api_key_ref = "qodeassist/providers/Claude" diff --git a/sources/providersConfig/codestral.toml b/sources/providersConfig/codestral.toml index 2f4c75b..e526819 100644 --- a/sources/providersConfig/codestral.toml +++ b/sources/providersConfig/codestral.toml @@ -2,7 +2,7 @@ schema_version = 1 name = "Codestral" client_api = "Codestral" -description = "Mistral's Codestral FIM-capable code model API." +description = "Cloud (Mistral). Codestral β€” a code model with native fill-in-the-middle, great for completion." url = "https://codestral.mistral.ai" api_key_ref = "qodeassist/providers/Codestral" diff --git a/sources/providersConfig/googleai.toml b/sources/providersConfig/googleai.toml index 4ae0f06..7151fc9 100644 --- a/sources/providersConfig/googleai.toml +++ b/sources/providersConfig/googleai.toml @@ -2,7 +2,7 @@ schema_version = 1 name = "Google AI" client_api = "Google AI" -description = "Google AI Studio (Gemini) hosted API." +description = "Cloud (Google). Gemini models via Google AI Studio." url = "https://generativelanguage.googleapis.com/v1beta" api_key_ref = "qodeassist/providers/Google AI" diff --git a/sources/providersConfig/llamacpp.toml b/sources/providersConfig/llamacpp.toml index 3764ae4..4efb2ab 100644 --- a/sources/providersConfig/llamacpp.toml +++ b/sources/providersConfig/llamacpp.toml @@ -2,7 +2,7 @@ schema_version = 1 name = "llama.cpp" client_api = "llama.cpp" -description = "Local llama.cpp server (llama-server)." +description = "Local (llama.cpp). Your own llama-server running GGUF models. Point the URL at your server (default :8080)." url = "http://localhost:8080" api_key_ref = "qodeassist/providers/llama.cpp" diff --git a/sources/providersConfig/lmstudio_chat.toml b/sources/providersConfig/lmstudio_chat.toml index 672ed30..f54fa40 100644 --- a/sources/providersConfig/lmstudio_chat.toml +++ b/sources/providersConfig/lmstudio_chat.toml @@ -2,7 +2,7 @@ schema_version = 1 name = "LM Studio (Chat Completions)" client_api = "LM Studio (Chat Completions)" -description = "Local LM Studio server over the /v1/chat/completions endpoint." +description = "Local (LM Studio). The OpenAI Chat Completions API β€” the right pick for most LM Studio models." url = "http://localhost:1234" api_key_ref = "qodeassist/providers/LM Studio (Chat Completions)" diff --git a/sources/providersConfig/lmstudio_responses.toml b/sources/providersConfig/lmstudio_responses.toml index e2e46fb..bda0d35 100644 --- a/sources/providersConfig/lmstudio_responses.toml +++ b/sources/providersConfig/lmstudio_responses.toml @@ -2,7 +2,7 @@ schema_version = 1 name = "LM Studio (Responses API)" client_api = "LM Studio (Responses API)" -description = "Local LM Studio server over the /v1/responses endpoint." +description = "Local (LM Studio). The newer Responses API β€” use only if your model needs it, otherwise pick LM Studio (Chat Completions)." url = "http://localhost:1234" api_key_ref = "qodeassist/providers/LM Studio (Responses API)" diff --git a/sources/providersConfig/mistral.toml b/sources/providersConfig/mistral.toml index 4ca6f25..45f0a7b 100644 --- a/sources/providersConfig/mistral.toml +++ b/sources/providersConfig/mistral.toml @@ -2,7 +2,7 @@ schema_version = 1 name = "Mistral AI" client_api = "Mistral AI" -description = "Mistral's hosted chat / completions API." +description = "Cloud (Mistral). Mistral chat/instruct models, incl. the Magistral reasoning model. For Mistral's FIM code model, use Codestral." url = "https://api.mistral.ai" api_key_ref = "qodeassist/providers/Mistral AI" diff --git a/sources/providersConfig/ollama_compat.toml b/sources/providersConfig/ollama_compat.toml index 998053f..42dde5c 100644 --- a/sources/providersConfig/ollama_compat.toml +++ b/sources/providersConfig/ollama_compat.toml @@ -2,7 +2,7 @@ schema_version = 1 name = "Ollama (OpenAI-compatible)" client_api = "Ollama (OpenAI-compatible)" -description = "Local Ollama daemon spoken to via the OpenAI-compatible /v1 routes." +description = "Local (Ollama). The OpenAI-compatible /v1 routes β€” for OpenAI-style agents. For everyday use prefer Ollama (Native)." url = "http://localhost:11434" api_key_ref = "qodeassist/providers/Ollama (OpenAI-compatible)" diff --git a/sources/providersConfig/ollama_native.toml b/sources/providersConfig/ollama_native.toml index 4e900e7..b5a5626 100644 --- a/sources/providersConfig/ollama_native.toml +++ b/sources/providersConfig/ollama_native.toml @@ -2,7 +2,7 @@ schema_version = 1 name = "Ollama (Native)" client_api = "Ollama (Native)" -description = "Default local Ollama daemon over its native /api/* endpoints." +description = "Local (Ollama). The default β€” native /api endpoints with the best support for FIM, thinking and tools. Use this for the bundled Ollama agents." url = "http://localhost:11434" api_key_ref = "qodeassist/providers/Ollama (Native)" diff --git a/sources/providersConfig/openai_chat.toml b/sources/providersConfig/openai_chat.toml index da0be10..74f0bea 100644 --- a/sources/providersConfig/openai_chat.toml +++ b/sources/providersConfig/openai_chat.toml @@ -2,7 +2,7 @@ schema_version = 1 name = "OpenAI (Chat Completions)" client_api = "OpenAI (Chat Completions)" -description = "OpenAI's hosted /v1/chat/completions endpoint." +description = "Cloud (OpenAI). GPT models via the standard Chat Completions API. The default OpenAI choice." url = "https://api.openai.com/v1" api_key_ref = "qodeassist/providers/OpenAI (Chat Completions)" diff --git a/sources/providersConfig/openai_compat.toml b/sources/providersConfig/openai_compat.toml index b81665b..cd85ad1 100644 --- a/sources/providersConfig/openai_compat.toml +++ b/sources/providersConfig/openai_compat.toml @@ -2,7 +2,7 @@ schema_version = 1 name = "OpenAI Compatible" client_api = "OpenAI Compatible" -description = "Self-hosted OpenAI-compatible server (vLLM, TGI, ...). Edit the URL to match your deployment." +description = "Self-hosted, OpenAI-compatible server (vLLM, TGI, LiteLLM, …). Point the URL at your deployment." url = "http://localhost:1234/v1" api_key_ref = "qodeassist/providers/OpenAI Compatible" diff --git a/sources/providersConfig/openai_responses.toml b/sources/providersConfig/openai_responses.toml index 2fa3a3c..1fe1bf1 100644 --- a/sources/providersConfig/openai_responses.toml +++ b/sources/providersConfig/openai_responses.toml @@ -2,7 +2,7 @@ schema_version = 1 name = "OpenAI (Responses API)" client_api = "OpenAI (Responses API)" -description = "OpenAI's hosted /v1/responses endpoint." +description = "Cloud (OpenAI). GPT models via the newer Responses API (needed for some o-series reasoning models). If unsure, use OpenAI (Chat Completions)." url = "https://api.openai.com/v1" api_key_ref = "qodeassist/providers/OpenAI (Responses API)" diff --git a/sources/providersConfig/openrouter.toml b/sources/providersConfig/openrouter.toml index d7753f1..81ba95d 100644 --- a/sources/providersConfig/openrouter.toml +++ b/sources/providersConfig/openrouter.toml @@ -2,7 +2,7 @@ schema_version = 1 name = "OpenRouter" client_api = "OpenRouter" -description = "OpenRouter aggregator (https://openrouter.ai)." +description = "Cloud aggregator (openrouter.ai) β€” a single gateway to models from many providers (Anthropic, OpenAI, Google, Meta, …)." url = "https://openrouter.ai/api" api_key_ref = "qodeassist/providers/OpenRouter"