Skip to content

Commit

Permalink
Adding ability to configure OpenAI API key from UI and store it to MF…
Browse files Browse the repository at this point in the history
… config #1514
  • Loading branch information
dvorka committed Feb 12, 2024
1 parent e3faa18 commit 78f45b9
Show file tree
Hide file tree
Showing 5 changed files with 79 additions and 17 deletions.
44 changes: 39 additions & 5 deletions app/src/qt/dialogs/configuration_dialog.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -692,25 +692,57 @@ ConfigurationDialog::WingmanTab::WingmanTab(QWidget* parent)

llmHelpLabel = new QLabel(
tr(
"<html>To configure <a href='https://openai.com'>OpenAI</a> LLM provider, "
"<a href='https://platform.openai.com/api-keys'>generate OpenAI API key</a> "
"and set"
"<br/>"
"%1 shell environment variable with the key."
"<html>Configure <a href='https://openai.com'>OpenAI</a> LLM provider:\n"
"<ul>"
"<li><a href='https://platform.openai.com/api-keys'>Generate</a> an OpenAI API key.</li>"
"<li>Set the API key:"
"<br>a) either set the <b>%1</b> environment variable<br/>"
"with the API key<br/>"
"b) or paste the API key below to save it <font color='#ff0000'>unencrypted</font> to<br/>"
"<b>.mindforger.md</b> file in your home dir.</li>"
"<li><font color='#ff0000'>Restart</font> MindForger to apply the change.</li>"
"</ul>"
).arg(ENV_VAR_OPENAI_API_KEY));
llmHelpLabel->setVisible(!config.canWingmanOpenAi());
openAiApiKeyEdit = new QLineEdit(this);
openAiApiKeyEdit->setVisible(!config.canWingmanOpenAi());
clearOpenAiApiKeyButton = new QPushButton(tr("Clear OpenAI API Key"), this);
if(config.getWingmanOpenAiApiKey().size() == 0) {
clearOpenAiApiKeyButton->setVisible(false);
}

// assembly
QVBoxLayout* nLayout = new QVBoxLayout{this};
nLayout->addWidget(llmProvidersLabel);
nLayout->addWidget(llmProvidersCombo);
nLayout->addWidget(llmHelpLabel);
nLayout->addWidget(openAiApiKeyEdit);
nLayout->addWidget(clearOpenAiApiKeyButton);
QGroupBox* nGroup = new QGroupBox{tr("Large language model (LLM) providers"), this};
nGroup->setLayout(nLayout);

QVBoxLayout* boxesLayout = new QVBoxLayout{this};
boxesLayout->addWidget(nGroup);
boxesLayout->addStretch();
setLayout(boxesLayout);

QObject::connect(
clearOpenAiApiKeyButton, SIGNAL(clicked()),
this, SLOT(clearOpenAiApiKeySlot()));

}

void ConfigurationDialog::WingmanTab::clearOpenAiApiKeySlot()
{
openAiApiKeyEdit->clear();
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 "
"and restart MindForger to apply this change.")
);
}

void ConfigurationDialog::WingmanTab::handleComboBoxChanged(int index) {
Expand Down Expand Up @@ -749,6 +781,7 @@ void ConfigurationDialog::WingmanTab::refresh()
llmProvidersCombo->addItem(
QString::fromStdString(openAiComboLabel), WingmanLlmProviders::WINGMAN_PROVIDER_OPENAI);
}
openAiApiKeyEdit->setText(QString::fromStdString(config.getWingmanOpenAiApiKey()));
// set the last selected provider
llmProvidersCombo->setCurrentIndex(
llmProvidersCombo->findData(config.getWingmanLlmProvider()));
Expand All @@ -761,6 +794,7 @@ void ConfigurationDialog::WingmanTab::save()
WingmanLlmProviders llmProvider = static_cast<WingmanLlmProviders>(
llmProvidersCombo->itemData(llmProvidersCombo->currentIndex()).toInt());
config.setWingmanLlmProvider(llmProvider);
config.setWingmanOpenAiApiKey(openAiApiKeyEdit->text().toStdString());
}

/*
Expand Down
3 changes: 3 additions & 0 deletions app/src/qt/dialogs/configuration_dialog.h
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,8 @@ class ConfigurationDialog::WingmanTab : public QWidget
QComboBox* llmProvidersCombo;

QLabel* llmHelpLabel;
QLineEdit* openAiApiKeyEdit;
QPushButton* clearOpenAiApiKeyButton;

public:
explicit WingmanTab(QWidget* parent);
Expand All @@ -98,6 +100,7 @@ class ConfigurationDialog::WingmanTab : public QWidget

private slots:
void handleComboBoxChanged(int index);
void clearOpenAiApiKeySlot();
};

/**
Expand Down
27 changes: 20 additions & 7 deletions lib/src/config/configuration.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ Configuration::Configuration()
autolinkingCaseInsensitive{},
wingmanProvider{DEFAULT_WINGMAN_LLM_PROVIDER},
wingmanApiKey{},
wingmanOpenAiApiKey{},
wingmanLlmModel{DEFAULT_WINGMAN_LLM_MODEL_OPENAI},
md2HtmlOptions{},
distributorSleepInterval{DEFAULT_DISTRIBUTOR_SLEEP_INTERVAL},
Expand Down Expand Up @@ -150,6 +151,7 @@ void Configuration::clear()
autolinkingCaseInsensitive = DEFAULT_AUTOLINKING_CASE_INSENSITIVE;
wingmanProvider = DEFAULT_WINGMAN_LLM_PROVIDER;
wingmanApiKey.clear();
wingmanOpenAiApiKey.clear();
wingmanLlmModel.clear();
timeScopeAsString.assign(DEFAULT_TIME_SCOPE);
tagsScope.clear();
Expand Down Expand Up @@ -391,7 +393,14 @@ const char* Configuration::getEditorFromEnv()

bool Configuration::canWingmanOpenAi()
{
return std::getenv(ENV_VAR_OPENAI_API_KEY) != nullptr?true:false;
if (
this->wingmanOpenAiApiKey.size() > 0
|| std::getenv(ENV_VAR_OPENAI_API_KEY) != nullptr
) {
return true;
}

return false;
}

void Configuration::setWingmanLlmProvider(WingmanLlmProviders provider)
Expand Down Expand Up @@ -429,18 +438,22 @@ bool Configuration::initWingmanOpenAi() {
if(canWingmanOpenAi()) {
MF_DEBUG(
" Wingman OpenAI API key found in the shell environment variable "
"MINDFORGER_OPENAI_API_KEY" << endl);
const char* apiKeyEnv = std::getenv(ENV_VAR_OPENAI_API_KEY);
MF_DEBUG(" Wingman API key loaded from the env: " << apiKeyEnv << endl);
wingmanApiKey = apiKeyEnv;
"MINDFORGER_OPENAI_API_KEY or set in MF config" << endl);
if(wingmanOpenAiApiKey.size() > 0) {
wingmanApiKey = wingmanOpenAiApiKey;
} else {
const char* apiKeyEnv = std::getenv(ENV_VAR_OPENAI_API_KEY);
MF_DEBUG(" Wingman API key loaded from the env: " << apiKeyEnv << endl);
wingmanApiKey = apiKeyEnv;
}
wingmanLlmModel = DEFAULT_WINGMAN_LLM_MODEL_OPENAI;
wingmanProvider = WingmanLlmProviders::WINGMAN_PROVIDER_OPENAI;
return true;
}

MF_DEBUG(
" Wingman OpenAI API key NOT found in the environment variable "
"MINDFORGER_OPENAI_API_KEY" << endl);
" Wingman OpenAI API key NEITHER found in the environment variable "
"MINDFORGER_OPENAI_API_KEY, NOR set in MF configuration" << endl);
wingmanApiKey.clear();
wingmanLlmModel.clear();
wingmanProvider = WingmanLlmProviders::WINGMAN_PROVIDER_NONE;
Expand Down
16 changes: 11 additions & 5 deletions lib/src/config/configuration.h
Original file line number Diff line number Diff line change
Expand Up @@ -371,9 +371,10 @@ class Configuration {
- appWindow.llmProvider used to detect configuration change
- on change: re-init Wingman DIALOG (refresh pre-defined prompts)
*/
WingmanLlmProviders wingmanProvider; // "OpenAI", "Google", "Mock"
std::string wingmanApiKey; // OpenAI/Bard/... API key loaded from the shell environment
std::string wingmanLlmModel; // gpt-3.5-turbo
WingmanLlmProviders wingmanProvider; // "none", "Mock", "OpenAI", ...
std::string wingmanApiKey; // API key of the currently configured Wingman LLM provider
std::string wingmanOpenAiApiKey; // OpenAI API specified by user in the config, env or UI
std::string wingmanLlmModel; // preferred LLM model the currently configured provider, like "gpt-3.5-turbo"

TimeScope timeScope;
std::string timeScopeAsString;
Expand Down Expand Up @@ -568,13 +569,18 @@ class Configuration {
*/
bool initWingman();
public:
std::string getWingmanOpenAiApiKey() const { return wingmanOpenAiApiKey; }
void setWingmanOpenAiApiKey(std::string apiKey) { wingmanOpenAiApiKey = apiKey; }
/**
* @brief Get API key of the currently configured Wingman LLM provider.
*/
std::string getWingmanApiKey() const { return wingmanApiKey; }
/**
* @brief Get preferred Wingman's LLM provider model name.
* @brief Get preferred Wingman LLM provider model name.
*/
std::string getWingmanLlmModel() const { return wingmanLlmModel; }
/**
* @brief Check whether Wingman's LLM provider is ready from
* @brief Check whether a Wingman LLM provider is ready from
* the configuration perspective.
*/
bool isWingman();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ constexpr const auto CONFIG_SETTING_MIND_TAGS_SCOPE_LABEL = "* Tags scope: ";
constexpr const auto CONFIG_SETTING_MIND_DISTRIBUTOR_INTERVAL = "* Async refresh interval (ms): ";
constexpr const auto CONFIG_SETTING_MIND_AUTOLINKING = "* Autolinking: ";
constexpr const auto CONFIG_SETTING_MIND_WINGMAN_PROVIDER = "* Wingman LLM provider: ";
constexpr const auto CONFIG_SETTING_MIND_OPENAI_KEY = "* Wingman's OpenAI API key: ";

// application
constexpr const auto CONFIG_SETTING_STARTUP_VIEW_LABEL = "* Startup view: ";
Expand Down Expand Up @@ -410,6 +411,9 @@ void MarkdownConfigurationRepresentation::configurationSection(
} else {
c.setWingmanLlmProvider(WingmanLlmProviders::WINGMAN_PROVIDER_NONE);
}
} else if(line->find(CONFIG_SETTING_MIND_OPENAI_KEY) != std::string::npos) {
string k = line->substr(strlen(CONFIG_SETTING_MIND_OPENAI_KEY));
c.setWingmanOpenAiApiKey(k);
}
}
}
Expand Down Expand Up @@ -503,6 +507,8 @@ string& MarkdownConfigurationRepresentation::to(Configuration* c, string& md)
" * Examples: yes, no" << endl <<
CONFIG_SETTING_MIND_WINGMAN_PROVIDER << Configuration::getWingmanLlmProviderAsString(c?c->getWingmanLlmProvider():Configuration::DEFAULT_WINGMAN_LLM_PROVIDER) << endl <<
" * Examples: none, openai" << endl <<
CONFIG_SETTING_MIND_OPENAI_KEY << (c?c->getWingmanOpenAiApiKey():"") << endl <<
" * OpenAI API key generated at https://platform.openai.com/api-keys to be used by Wingman as LLM provider" << endl <<
endl <<

"# " << CONFIG_SECTION_APP << endl <<
Expand Down

0 comments on commit 78f45b9

Please sign in to comment.