Skip to content

Commit

Permalink
WIP ollama preferences
Browse files Browse the repository at this point in the history
  • Loading branch information
dvorka committed Mar 9, 2024
1 parent 57d6277 commit 3adc4dc
Show file tree
Hide file tree
Showing 6 changed files with 239 additions and 49 deletions.
10 changes: 10 additions & 0 deletions KNOWN_ISSUES.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,16 @@
MindForger known issues - see [GitHub issues](https://github.com/dvorka/mindforger/issues?q=is%3Aopen+is%3Aissue+label%3A%22bug+%3Alady_beetle%3A%22)
for the complete list of bugs.

# 2.1.0

* Autolinking can crash the application on notebook or note delete (undefined link target).
* Autolinking can break MathJax code blocks/text integrity in Markdown text.
* Windows Server R2 2012: empty MF documentation repository in wrong location when MF is launched
at the end of installation.
* Notebook HTML export doesn't export local images: links to filesystem are kept intact, images
are not copied.
* Frontend memleaks.

# 2.0.0

* Autolinking can crash the application on notebook or note delete (undefined link target).
Expand Down
4 changes: 2 additions & 2 deletions PAD.xml
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,8 @@
</Company_Info>
<Program_Info>
<Program_Name>MindForger</Program_Name>
<Program_Version>2.0.0</Program_Version>
<Program_Release_Month>02</Program_Release_Month>
<Program_Version>2.1.0</Program_Version>
<Program_Release_Month>03</Program_Release_Month>
<Program_Release_Day>16</Program_Release_Day>
<Program_Release_Year>2024</Program_Release_Year>
<Program_Cost_Dollars>0</Program_Cost_Dollars>
Expand Down
205 changes: 167 additions & 38 deletions app/src/qt/dialogs/configuration_dialog.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -675,75 +675,201 @@ void ConfigurationDialog::MindTab::save()
}

/*
* Wingman tab
* Wingman Open AI tab
*/

ConfigurationDialog::WingmanTab::WingmanTab(QWidget* parent)
ConfigurationDialog::WingmanOpenAiTab::WingmanOpenAiTab(QWidget* parent)
: QWidget(parent),
openAiComboLabel{"OpenAI"},
config(Configuration::getInstance())
{
llmProvidersLabel = new QLabel(tr("LLM provider:"), this);
llmProvidersCombo = new QComboBox{this};
helpLabel = new QLabel(
tr(
"<html><a href='https://openai.com'>OpenAI</a> LLM provider configuration:\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 directory.</li>"
"<li><font color='#ff0000'>Restart</font> MindForger to apply the change.</li>"
"</ul>"
).arg(ENV_VAR_OPENAI_API_KEY));
helpLabel->setVisible(!config.canWingmanOpenAi());
apiKeyEdit = new QLineEdit(this);
apiKeyEdit->setVisible(!config.canWingmanOpenAi());
clearApiKeyButton = new QPushButton(tr("Clear OpenAI API Key"), this);

QVBoxLayout* llmProvidersLayout = new QVBoxLayout();
llmProvidersLayout->addWidget(helpLabel);
llmProvidersLayout->addWidget(apiKeyEdit);
llmProvidersLayout->addWidget(clearApiKeyButton);
llmProvidersLayout->addStretch();

QVBoxLayout* layout = new QVBoxLayout();
layout->addLayout(llmProvidersLayout);
layout->addStretch();
setLayout(layout);

QObject::connect(
llmProvidersCombo, SIGNAL(currentIndexChanged(int)),
this, SLOT(handleComboBoxChanged(int))
clearApiKeyButton, SIGNAL(clicked()),
this, SLOT(clearApiKeySlot()));
}

ConfigurationDialog::WingmanOpenAiTab::~WingmanOpenAiTab()
{
delete helpLabel;
delete apiKeyEdit;
delete clearApiKeyButton;
}

void ConfigurationDialog::WingmanOpenAiTab::clearApiKeySlot()
{
apiKeyEdit->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::WingmanOpenAiTab::refresh()
{
apiKeyEdit->setText(QString::fromStdString(config.getWingmanOpenAiApiKey()));

if(apiKeyEdit->text().size() == 0) {
clearApiKeyButton->setVisible(false);
} else {
clearApiKeyButton->setVisible(true);
}
}

void ConfigurationDialog::WingmanOpenAiTab::save()
{
config.setWingmanOpenAiApiKey(apiKeyEdit->text().toStdString());
}


llmHelpLabel = new QLabel(
/*
* Wingman ollama
*/

ConfigurationDialog::WingmanOllamaTab::WingmanOllamaTab(QWidget* parent)
: QWidget(parent),
config(Configuration::getInstance())
{
helpLabel = new QLabel(
tr(
"<html>Configure <a href='https://openai.com'>OpenAI</a> LLM provider:\n"
"<html><a href='https://openai.com'>OpenAI</a> LLM provider configuration:\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>"
"<b>.mindforger.md</b> file in your home directory.</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) {
helpLabel->setVisible(!config.canWingmanOllama());
urlEdit = new QLineEdit(this);
urlEdit->setVisible(!config.canWingmanOllama());
clearUrlButton = new QPushButton(tr("Clear ollama URL"), this);

QVBoxLayout* llmProvidersLayout = new QVBoxLayout();
llmProvidersLayout->addWidget(helpLabel);
llmProvidersLayout->addWidget(urlEdit);
llmProvidersLayout->addWidget(clearUrlButton);
llmProvidersLayout->addStretch();

QVBoxLayout* layout = new QVBoxLayout();
layout->addLayout(llmProvidersLayout);
layout->addStretch();
setLayout(layout);

QObject::connect(
clearUrlButton, SIGNAL(clicked()),
this, SLOT(clearUrlSlot()));
}

ConfigurationDialog::WingmanOllamaTab::~WingmanOllamaTab()
{
delete helpLabel;
delete urlEdit;
delete clearUrlButton;
}

void ConfigurationDialog::WingmanOllamaTab::clearUrlSlot()
{
urlEdit->clear();
QMessageBox::information(
this,
tr("URL Cleared"),
xxx
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::WingmanOpenAiTab::refresh()
{
openAiApiKeyEdit->setText(QString::fromStdString(config.getWingmanOpenAiApiKey()));

if(openAiApiKeyEdit->text().size() == 0) {
clearOpenAiApiKeyButton->setVisible(false);
} else {
clearOpenAiApiKeyButton->setVisible(true);
}
}

void ConfigurationDialog::WingmanOpenAiTab::save()
{
config.setWingmanOpenAiApiKey(openAiApiKeyEdit->text().toStdString());
}

/*
* Wingman tab
*/

ConfigurationDialog::WingmanTab::WingmanTab(QWidget* parent)
: QWidget(parent),
openAiComboLabel{"OpenAI"},
config(Configuration::getInstance())
{
llmProvidersLabel = new QLabel(tr("LLM provider:"), this);
llmProvidersCombo = new QComboBox{this};
QObject::connect(
llmProvidersCombo, SIGNAL(currentIndexChanged(int)),
this, SLOT(handleComboBoxChanged(int))
);

wingmanTabWidget = new QTabWidget;
wingmanOpenAiTab = new WingmanOpenAiTab{this};
wingmanOllamaTab = new WingmanOllamaTab{this};
wingmanTabWidget->addTab(wingmanOpenAiTab, tr("OpenAI"));
wingmanTabWidget->addTab(wingmanOllamaTab, tr("ollama"));

// assembly
QVBoxLayout* nLayout = new QVBoxLayout{this};
nLayout->addWidget(llmProvidersLabel);
nLayout->addWidget(llmProvidersCombo);
nLayout->addWidget(llmHelpLabel);
nLayout->addWidget(openAiApiKeyEdit);
nLayout->addWidget(clearOpenAiApiKeyButton);

nLayout->addWidget(wingmanTabWidget);

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) {
string comboItemLabel{llmProvidersCombo->itemText(index).toStdString()};
Expand All @@ -763,7 +889,6 @@ ConfigurationDialog::WingmanTab::~WingmanTab()
{
delete llmProvidersLabel;
delete llmProvidersCombo;
delete llmHelpLabel;
}

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

// OpenAI tab
wingmanOpenAiTab->save();
// ollama tab
// TODO ... xxx ...
}

/*
Expand Down
59 changes: 55 additions & 4 deletions app/src/qt/dialogs/configuration_dialog.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ class ConfigurationDialog : public QDialog
class NavigatorTab;
class MindTab;
class WingmanTab;
class WingmanOpenAiTab;
class WingmanOllamaTab;

private:
QTabWidget* tabWidget;
Expand Down Expand Up @@ -71,6 +73,56 @@ private slots:
void saveConfigSignal();
};

/**
* @brief Wingman tab's OpenAI tab.
*/
class ConfigurationDialog::WingmanOpenAiTab : public QWidget
{
Q_OBJECT

private:
Configuration& config;

QLabel* helpLabel;
QLineEdit* apiKeyEdit;
QPushButton* clearApiKeyButton;

public:
explicit WingmanOpenAiTab(QWidget* parent);
~WingmanOpenAiTab();

void refresh();
void save();

private slots:
void clearApiKeySlot();
};

/**
* @brief Wingman tab's ollama tab.
*/
class ConfigurationDialog::WingmanOllamaTab : public QWidget
{
Q_OBJECT

private:
Configuration& config;

QLabel* helpLabel;
QLineEdit* urlEdit;
QPushButton* clearUrlButton;

public:
explicit WingmanOllamaTab(QWidget* parent);
~WingmanOllamaTab();

void refresh();
void save();

private slots:
void clearUrlSlot();
};

/**
* @brief Wingman tab.
*/
Expand All @@ -86,9 +138,9 @@ class ConfigurationDialog::WingmanTab : public QWidget
QLabel* llmProvidersLabel;
QComboBox* llmProvidersCombo;

QLabel* llmHelpLabel;
QLineEdit* openAiApiKeyEdit;
QPushButton* clearOpenAiApiKeyButton;
QTabWidget* wingmanTabWidget;
WingmanOpenAiTab* wingmanOpenAiTab;
WingmanOllamaTab* wingmanOllamaTab;

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

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

/**
Expand Down
2 changes: 1 addition & 1 deletion appveyor.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.

version: 2.0.{build}
version: 2.1.{build}

skip_commits:
files:
Expand Down
Loading

0 comments on commit 3adc4dc

Please sign in to comment.