diff --git a/app/src/qt/dialogs/configuration_dialog.cpp b/app/src/qt/dialogs/configuration_dialog.cpp
index b0818dfc..173cb477 100644
--- a/app/src/qt/dialogs/configuration_dialog.cpp
+++ b/app/src/qt/dialogs/configuration_dialog.cpp
@@ -680,8 +680,9 @@ void ConfigurationDialog::MindTab::save()
* Wingman Open AI tab
*/
-ConfigurationDialog::WingmanOpenAiTab::WingmanOpenAiTab(QWidget* parent)
+ConfigurationDialog::WingmanOpenAiTab::WingmanOpenAiTab(QWidget* parent, QComboBox* parentLlmProvidersCombo)
: QWidget(parent),
+ parentLlmProvidersCombo{parentLlmProvidersCombo},
config(Configuration::getInstance())
{
helpLabel = new QLabel(
@@ -689,21 +690,30 @@ ConfigurationDialog::WingmanOpenAiTab::WingmanOpenAiTab(QWidget* parent)
"OpenAI LLM provider configuration:\n"
"
"
"- Generate new OpenAI API key at openai.com.
"
- "- Set the API key:"
- "
a) either set the %1 environment variable
"
+ " - a) either set the %1 environment variable
"
"with the API key
"
"b) or paste the API key below to save it unencrypted to
"
".mindforger.md file in your home directory. "
"
"
).arg(ENV_VAR_OPENAI_API_KEY));
- helpLabel->setVisible(!config.canWingmanOpenAi());
+ helpLabel->setVisible(!config.canWingmanOpenAiFromEnv());
apiKeyLabel = new QLabel(tr("
API key:"));
apiKeyLabel->setVisible(helpLabel->isVisible());
apiKeyEdit = new QLineEdit(this);
apiKeyEdit->setVisible(helpLabel->isVisible());
- setOllamaButton = new QPushButton(tr("Set ollama"), this); // enabled on valid config > add ollama to drop down > choose it in drop down
+ // enabled on valid config > add ollama to drop down > choose it in drop down
+ setOpenAiButton = new QPushButton(tr("Set OpenAI as LLM Provider"), this);
+ setOpenAiButton->setToolTip(
+ tr("Add OpenAI to the dropdown with LLM providers (if not there) and set it for use with Wingman")
+ );
+ setOpenAiButton->setVisible(helpLabel->isVisible());
clearApiKeyButton = new QPushButton(tr("Clear API Key"), this);
clearApiKeyButton->setVisible(helpLabel->isVisible());
+ llmModelsLabel = new QLabel(tr("LLM model:"));
+ llmModelsCombo = new QComboBox();
+ llmModelsCombo->addItem(LLM_MODEL_NONE);
+ llmModelsCombo->addItem(LLM_MODEL_GPT35_TURBO);
+ llmModelsCombo->addItem(LLM_MODEL_GPT4);
configuredLabel = new QLabel(
tr("The OpenAI API key is configured using the environment variable."), this);
@@ -713,8 +723,13 @@ ConfigurationDialog::WingmanOpenAiTab::WingmanOpenAiTab(QWidget* parent)
llmProvidersLayout->addWidget(helpLabel);
llmProvidersLayout->addWidget(apiKeyLabel);
llmProvidersLayout->addWidget(apiKeyEdit);
- llmProvidersLayout->addWidget(clearApiKeyButton);
llmProvidersLayout->addWidget(configuredLabel);
+ llmProvidersLayout->addWidget(llmModelsLabel);
+ llmProvidersLayout->addWidget(llmModelsCombo);
+ QHBoxLayout* buttonsLayout = new QHBoxLayout{};
+ buttonsLayout->addWidget(setOpenAiButton);
+ buttonsLayout->addWidget(clearApiKeyButton);
+ llmProvidersLayout->addLayout(buttonsLayout);
llmProvidersLayout->addStretch();
QVBoxLayout* layout = new QVBoxLayout();
@@ -722,6 +737,9 @@ ConfigurationDialog::WingmanOpenAiTab::WingmanOpenAiTab(QWidget* parent)
layout->addStretch();
setLayout(layout);
+ QObject::connect(
+ setOpenAiButton, SIGNAL(clicked()),
+ this, SLOT(setOpenAiSlot()));
QObject::connect(
clearApiKeyButton, SIGNAL(clicked()),
this, SLOT(clearApiKeySlot()));
@@ -734,29 +752,92 @@ ConfigurationDialog::WingmanOpenAiTab::~WingmanOpenAiTab()
delete apiKeyLabel;
delete apiKeyEdit;
delete clearApiKeyButton;
+ delete llmModelsLabel;
+ delete llmModelsCombo;
+}
+
+void refreshWingmanLlmProvidersComboBox(
+ QComboBox* parentLlmProvidersCombo,
+ Configuration& config
+)
+{
+ parentLlmProvidersCombo->clear();
+ parentLlmProvidersCombo->addItem(WINGMAN_NONE_COMBO_LABEL); // NO LLM provider - NO Wingman
+#ifdef MF_WIP
+ if(config.canWingmanMock()) {
+ parentLlmProvidersCombo->addItem(
+ QString::fromStdString(WINGMAN_MOCK_COMBO_LABEL),
+ WingmanLlmProviders::WINGMAN_PROVIDER_MOCK);
+ }
+#endif
+ if(config.canWingmanOpenAi()) {
+ parentLlmProvidersCombo->addItem(
+ QString::fromStdString(WINGMAN_OPENAI_COMBO_LABEL),
+ WingmanLlmProviders::WINGMAN_PROVIDER_OPENAI);
+ }
+ if(config.canWingmanOllama()) {
+ parentLlmProvidersCombo->addItem(
+ QString::fromStdString(WINGMAN_OLLAMA_COMBO_LABEL),
+ WingmanLlmProviders::WINGMAN_PROVIDER_OLLAMA);
+ }
+
+ // select configure provider in the combo box
+ parentLlmProvidersCombo->setCurrentIndex(
+ parentLlmProvidersCombo->findData(config.getWingmanLlmProvider()));
+
+}
+
+void ConfigurationDialog::WingmanOpenAiTab::setOpenAiSlot()
+{
+ MF_DEBUG("Signal SLOT: set OpenAI" << endl);
+
+ if(apiKeyEdit->text().size()==0 && !config.canWingmanOpenAiFromEnv()) {
+ QMessageBox::critical(
+ this,
+ tr("LLM Provider Config Error"),
+ tr(
+ "Unable to set OpenAI as LLM provider as neither API key is set in the configuration, "
+ "nor it is defined and environment variable")
+ );
+ config.setWingmanLlmProvider(WingmanLlmProviders::WINGMAN_PROVIDER_NONE);
+ refreshWingmanLlmProvidersComboBox(parentLlmProvidersCombo, config);
+ }
+
+ save();
+ config.setWingmanLlmProvider(WingmanLlmProviders::WINGMAN_PROVIDER_OPENAI);
+ refreshWingmanLlmProvidersComboBox(parentLlmProvidersCombo, config);
}
void ConfigurationDialog::WingmanOpenAiTab::clearApiKeySlot()
{
apiKeyEdit->clear();
+
+ save();
+ if(config.getWingmanLlmProvider() == WingmanLlmProviders::WINGMAN_PROVIDER_OPENAI) {
+ config.setWingmanLlmProvider(WingmanLlmProviders::WINGMAN_PROVIDER_NONE);
+ }
+ refreshWingmanLlmProvidersComboBox(parentLlmProvidersCombo, config);
QMessageBox::information(
this,
tr("OpenAI API Key Cleared"),
tr(
- "API key has been cleared from the configuration. "
- "Please close the configuration dialog with the OK button to finish "
- "the reconfiguration")
+ "API key has been cleared from the configuration and "
+ "OpenAI is no longer the LLM provider.")
);
}
void ConfigurationDialog::WingmanOpenAiTab::refresh()
{
apiKeyEdit->setText(QString::fromStdString(config.getWingmanOpenAiApiKey()));
+ llmModelsCombo->setCurrentText(
+ QString::fromStdString(config.getWingmanOpenAiLlm()));
}
void ConfigurationDialog::WingmanOpenAiTab::save()
{
config.setWingmanOpenAiApiKey(apiKeyEdit->text().toStdString());
+ config.setWingmanOpenAiLlm(
+ llmModelsCombo->itemText(llmModelsCombo->currentIndex()).toStdString());
}
@@ -764,8 +845,9 @@ void ConfigurationDialog::WingmanOpenAiTab::save()
* Wingman ollama
*/
-ConfigurationDialog::WingmanOllamaTab::WingmanOllamaTab(QWidget* parent)
+ConfigurationDialog::WingmanOllamaTab::WingmanOllamaTab(QWidget* parent, QComboBox* parentLlmProvidersCombo)
: QWidget(parent),
+ parentLlmProvidersCombo{parentLlmProvidersCombo},
config(Configuration::getInstance())
{
helpLabel = new QLabel(
@@ -778,13 +860,24 @@ ConfigurationDialog::WingmanOllamaTab::WingmanOllamaTab(QWidget* parent)
helpLabel->setVisible(!config.canWingmanOllama());
urlLabel = new QLabel(tr("
ollama server URL:"));
urlEdit = new QLineEdit(this);
+ // enabled on valid config > add ollama to drop down > choose it in drop down
+ setOllamaButton = new QPushButton(tr("Set ollama as LLM Provider"), this);
+ setOllamaButton->setToolTip(
+ tr("Add ollama to the dropdown with LLM providers (if not there) and set it for use with Wingman")
+ );
clearUrlButton = new QPushButton(tr("Clear URL"), this);
+ llmModelsLabel = new QLabel(tr("LLM model:"));
+ llmModelsCombo = new QComboBox();
+ llmModelsCombo->addItem(LLM_MODEL_NONE);
QVBoxLayout* llmProvidersLayout = new QVBoxLayout();
llmProvidersLayout->addWidget(helpLabel);
llmProvidersLayout->addWidget(urlLabel);
llmProvidersLayout->addWidget(urlEdit);
+ llmProvidersLayout->addWidget(setOllamaButton);
llmProvidersLayout->addWidget(clearUrlButton);
+ llmProvidersLayout->addWidget(llmModelsLabel);
+ llmProvidersLayout->addWidget(llmModelsCombo);
llmProvidersLayout->addStretch();
QVBoxLayout* layout = new QVBoxLayout();
@@ -792,6 +885,9 @@ ConfigurationDialog::WingmanOllamaTab::WingmanOllamaTab(QWidget* parent)
layout->addStretch();
setLayout(layout);
+ QObject::connect(
+ setOllamaButton, SIGNAL(clicked()),
+ this, SLOT(setOllamaSlot()));
QObject::connect(
clearUrlButton, SIGNAL(clicked()),
this, SLOT(clearUrlSlot()));
@@ -803,19 +899,46 @@ ConfigurationDialog::WingmanOllamaTab::~WingmanOllamaTab()
delete urlLabel;
delete urlEdit;
delete clearUrlButton;
+ delete llmModelsLabel;
+ delete llmModelsCombo;
+}
+
+void ConfigurationDialog::WingmanOllamaTab::setOllamaSlot()
+{
+ MF_DEBUG("Signal SLOT: set ollama" << endl);
+
+ if(urlEdit->text().size()==0) {
+ QMessageBox::critical(
+ this,
+ tr("LLM Provider Config Error"),
+ tr(
+ "Unable to set ollama as LLM provider as neither API key is set in the configuration, "
+ "nor it is defined and environment variable")
+ );
+ config.setWingmanLlmProvider(WingmanLlmProviders::WINGMAN_PROVIDER_NONE);
+ refreshWingmanLlmProvidersComboBox(parentLlmProvidersCombo, config);
+ }
+
+ save();
+ config.setWingmanLlmProvider(WingmanLlmProviders::WINGMAN_PROVIDER_OLLAMA);
+ refreshWingmanLlmProvidersComboBox(parentLlmProvidersCombo, config);
}
void ConfigurationDialog::WingmanOllamaTab::clearUrlSlot()
{
urlEdit->clear();
+
+ save();
+ if(config.getWingmanLlmProvider() == WingmanLlmProviders::WINGMAN_PROVIDER_OLLAMA) {
+ config.setWingmanLlmProvider(WingmanLlmProviders::WINGMAN_PROVIDER_NONE);
+ }
+ refreshWingmanLlmProvidersComboBox(parentLlmProvidersCombo, config);
QMessageBox::information(
this,
- tr("ollama URL Cleared"),
+ tr("ollama Server URL Cleared"),
tr(
- "ollama URL has been cleared from the configuration. "
- "Please close the configuration dialog with the OK button "
- "to finish the reconfiguration.")
- );
+ "ollama server URL has been cleared from the configuration and "
+ "ollama is no longer the LLM provider."));
}
void ConfigurationDialog::WingmanOllamaTab::refresh()
@@ -838,8 +961,6 @@ void ConfigurationDialog::WingmanOllamaTab::save()
ConfigurationDialog::WingmanTab::WingmanTab(QWidget* parent)
: QWidget(parent),
- openAiComboLabel{"OpenAI"},
- ollamaComboLabel{"ollama"},
config(Configuration::getInstance())
{
llmProvidersLabel = new QLabel(tr("LLM provider:"), this);
@@ -850,8 +971,8 @@ ConfigurationDialog::WingmanTab::WingmanTab(QWidget* parent)
);
wingmanTabWidget = new QTabWidget;
- wingmanOpenAiTab = new WingmanOpenAiTab{this};
- wingmanOllamaTab = new WingmanOllamaTab{this};
+ wingmanOpenAiTab = new WingmanOpenAiTab{this, llmProvidersCombo};
+ wingmanOllamaTab = new WingmanOllamaTab{this, llmProvidersCombo};
wingmanTabWidget->addTab(wingmanOpenAiTab, tr("OpenAI"));
wingmanTabWidget->addTab(wingmanOllamaTab, tr("ollama"));
@@ -875,7 +996,7 @@ ConfigurationDialog::WingmanTab::WingmanTab(QWidget* parent)
void ConfigurationDialog::WingmanTab::handleComboBoxChanged(int index) {
string comboItemLabel{llmProvidersCombo->itemText(index).toStdString()};
MF_DEBUG("WingmanTab::handleComboBoxChange: '" << comboItemLabel << "'" << endl);
- if(this->isVisible() && comboItemLabel == openAiComboLabel) {
+ if(this->isVisible() && comboItemLabel == WINGMAN_OPENAI_COMBO_LABEL) {
QMessageBox::warning(
this,
tr("Data Privacy Warning"),
@@ -895,27 +1016,10 @@ ConfigurationDialog::WingmanTab::~WingmanTab()
void ConfigurationDialog::WingmanTab::refresh()
{
// refresh LLM providers combo
- llmProvidersCombo->clear();
- llmProvidersCombo->addItem(""); // disable Wingman
-#ifdef MF_WIP
- if(config.canWingmanMock()) {
- llmProvidersCombo->addItem(
- QString{"Mock"}, WingmanLlmProviders::WINGMAN_PROVIDER_MOCK);
- }
-#endif
- if(config.canWingmanOpenAi()) {
- llmProvidersCombo->addItem(
- QString::fromStdString(openAiComboLabel), WingmanLlmProviders::WINGMAN_PROVIDER_OPENAI);
- }
- if(config.canWingmanOllama()) {
- llmProvidersCombo->addItem(
- QString::fromStdString(ollamaComboLabel), WingmanLlmProviders::WINGMAN_PROVIDER_OLLAMA);
- }
+ refreshWingmanLlmProvidersComboBox(llmProvidersCombo, config);
+
wingmanOpenAiTab->refresh();
wingmanOllamaTab->refresh();
- // set the last selected provider
- llmProvidersCombo->setCurrentIndex(
- llmProvidersCombo->findData(config.getWingmanLlmProvider()));
}
void ConfigurationDialog::WingmanTab::save()
diff --git a/app/src/qt/dialogs/configuration_dialog.h b/app/src/qt/dialogs/configuration_dialog.h
index 5cabcc5c..140375e9 100644
--- a/app/src/qt/dialogs/configuration_dialog.h
+++ b/app/src/qt/dialogs/configuration_dialog.h
@@ -73,6 +73,11 @@ private slots:
void saveConfigSignal();
};
+constexpr const auto WINGMAN_NONE_COMBO_LABEL = "";
+constexpr const auto WINGMAN_OPENAI_COMBO_LABEL = "OpenAI";
+constexpr const auto WINGMAN_OLLAMA_COMBO_LABEL = "ollama";
+constexpr const auto WINGMAN_MOCK_COMBO_LABEL = "mock";
+
/**
* @brief Wingman tab's OpenAI tab.
*/
@@ -81,22 +86,27 @@ class ConfigurationDialog::WingmanOpenAiTab : public QWidget
Q_OBJECT
private:
+ QComboBox* parentLlmProvidersCombo;
Configuration& config;
QLabel* helpLabel;
QLabel* configuredLabel;
QLabel* apiKeyLabel;
QLineEdit* apiKeyEdit;
+ QPushButton* setOpenAiButton;
QPushButton* clearApiKeyButton;
+ QLabel* llmModelsLabel;
+ QComboBox* llmModelsCombo;
public:
- explicit WingmanOpenAiTab(QWidget* parent);
+ explicit WingmanOpenAiTab(QWidget* parent, QComboBox* parentLlmProvidersCombo);
~WingmanOpenAiTab();
void refresh();
void save();
private slots:
+ void setOpenAiSlot();
void clearApiKeySlot();
};
@@ -108,21 +118,27 @@ class ConfigurationDialog::WingmanOllamaTab : public QWidget
Q_OBJECT
private:
+ QComboBox* parentLlmProvidersCombo;
+
Configuration& config;
QLabel* helpLabel;
QLabel* urlLabel;
QLineEdit* urlEdit;
+ QPushButton* setOllamaButton;
QPushButton* clearUrlButton;
+ QLabel* llmModelsLabel;
+ QComboBox* llmModelsCombo;
public:
- explicit WingmanOllamaTab(QWidget* parent);
+ explicit WingmanOllamaTab(QWidget* parent, QComboBox* parentLlmProvidersCombo);
~WingmanOllamaTab();
void refresh();
void save();
private slots:
+ void setOllamaSlot();
void clearUrlSlot();
};
@@ -134,9 +150,6 @@ class ConfigurationDialog::WingmanTab : public QWidget
Q_OBJECT
private:
- const std::string openAiComboLabel;
- const std::string ollamaComboLabel;
-
Configuration& config;
QLabel* llmProvidersLabel;
diff --git a/lib/src/config/configuration.cpp b/lib/src/config/configuration.cpp
index dfbbbaa7..17307b9b 100644
--- a/lib/src/config/configuration.cpp
+++ b/lib/src/config/configuration.cpp
@@ -29,10 +29,6 @@ using namespace m8r::filesystem;
namespace m8r {
-const string LLM_MODEL_GPT35 = string{"gpt-3.5"};
-// TODO ollama does NOT have to host llama2 > it should NOT be offered as default model
-const string LLM_MODEL_LLAMA2 = string{"llama2"};
-
const string KnowledgeTool::TOOL_PHRASE = string{"<>"};
// non-primitive constants initializations
@@ -42,8 +38,8 @@ const string Configuration::DEFAULT_UI_THEME_NAME = string{UI_DEFAULT_THEME};
const string Configuration::DEFAULT_UI_HTML_CSS_THEME = string{UI_DEFAULT_HTML_CSS_THEME};
const string Configuration::DEFAULT_EDITOR_FONT= string{UI_DEFAULT_EDITOR_FONT};
const string Configuration::DEFAULT_TIME_SCOPE = string{"0y0m0d0h0m"};
-const string Configuration::DEFAULT_WINGMAN_LLM_MODEL_OPENAI = LLM_MODEL_GPT35;
-const string Configuration::DEFAULT_WINGMAN_LLM_MODEL_OLLAMA = LLM_MODEL_LLAMA2;
+const string Configuration::DEFAULT_WINGMAN_LLM_MODEL_OPENAI = string{LLM_MODEL_GPT35_TURBO};
+const string Configuration::DEFAULT_WINGMAN_LLM_MODEL_OLLAMA = string{LLM_MODEL_LLAMA2};
Configuration::Configuration()
: asyncMindThreshold{},
@@ -398,12 +394,18 @@ const char* Configuration::getEditorFromEnv()
return editor;
}
+bool Configuration::canWingmanOpenAiFromEnv()
+{
+ if(std::getenv(ENV_VAR_OPENAI_API_KEY) != nullptr) {
+ return true;
+ }
+
+ return false;
+}
+
bool Configuration::canWingmanOpenAi()
{
- if (
- this->wingmanOpenAiApiKey.size() > 0
- || std::getenv(ENV_VAR_OPENAI_API_KEY) != nullptr
- ) {
+ if(this->wingmanOpenAiApiKey.size() > 0 || canWingmanOpenAiFromEnv()) {
return true;
}
diff --git a/lib/src/config/configuration.h b/lib/src/config/configuration.h
index 347279cb..2797b2fb 100644
--- a/lib/src/config/configuration.h
+++ b/lib/src/config/configuration.h
@@ -52,6 +52,12 @@ enum WingmanLlmProviders {
WINGMAN_PROVIDER_OLLAMA
};
+constexpr const auto LLM_MODEL_NONE = "";
+constexpr const auto LLM_MODEL_GPT35_TURBO = "gpt-3.5-turbo";
+constexpr const auto LLM_MODEL_GPT4 = "gpt-4";
+// TODO ollama does NOT have to host llama2 > it should NOT be offered as default model
+constexpr const auto LLM_MODEL_LLAMA2 = "llama2";
+
// const in constexpr makes value const
constexpr const auto ENV_VAR_HOME = "HOME";
constexpr const auto ENV_VAR_DISPLAY = "DISPLAY";
@@ -569,6 +575,7 @@ class Configuration {
#else
bool canWingmanMock() { return false; }
#endif
+ bool canWingmanOpenAiFromEnv();
bool canWingmanOpenAi();
bool canWingmanOllama();
private:
diff --git a/lib/src/mind/ai/llm/openai_wingman.cpp b/lib/src/mind/ai/llm/openai_wingman.cpp
index 0fc16b62..f01bf64a 100644
--- a/lib/src/mind/ai/llm/openai_wingman.cpp
+++ b/lib/src/mind/ai/llm/openai_wingman.cpp
@@ -134,7 +134,7 @@ void OpenAiWingman::curlGet(CommandWingmanChat& command) {
<< "<<<"
<< endl);
- // TODO this must be refactored to parent class so that all wingmans can use it
+ // TODO this must be refactored to parent class so that all Wingmans can use it
#if defined(_WIN32) || defined(__APPLE__)
/* Qt Networking examples:
*
diff --git a/lib/src/mind/mind.cpp b/lib/src/mind/mind.cpp
index 2b58b9c9..02784063 100644
--- a/lib/src/mind/mind.cpp
+++ b/lib/src/mind/mind.cpp
@@ -1464,10 +1464,8 @@ void Mind::initWingman()
delete wingman;
wingman = nullptr;
}
- wingman = (Wingman*)new OpenAiWingman{
- config.getWingmanOpenAiApiKey()
- // TODO config.getWingmanOpenAiLlm()
- };
+ wingman = (Wingman*)new OpenAiWingman{config.getWingmanOpenAiApiKey()};
+ wingman->setLlmModel(config.getWingmanOpenAiLlm());
wingmanLlmProvider = config.getWingmanLlmProvider();
return;
case WingmanLlmProviders::WINGMAN_PROVIDER_OLLAMA:
@@ -1476,10 +1474,8 @@ void Mind::initWingman()
delete wingman;
wingman = nullptr;
}
- wingman = (Wingman*)new OllamaWingman{
- config.getWingmanOllamaUrl(),
- // TODO config.getWingmanOllamaLlm()
- };
+ wingman = (Wingman*)new OllamaWingman{config.getWingmanOllamaUrl()};
+ wingman->setLlmModel(config.getWingmanOllamaLlm());
wingmanLlmProvider = config.getWingmanLlmProvider();
return;
case WingmanLlmProviders::WINGMAN_PROVIDER_MOCK:
@@ -1491,6 +1487,7 @@ void Mind::initWingman()
return;
case WingmanLlmProviders::WINGMAN_PROVIDER_NONE:
MF_DEBUG(" MIND Wingman init: set to NONE > deinitialize > NO Wingman" << endl);
+ break;
default:
MF_DEBUG(" MIND Wingman init: UNKNOWN > NO Wingman" << endl);
break;