Skip to content

Commit

Permalink
WIP: embeddings using PHI benchmarks
Browse files Browse the repository at this point in the history
  • Loading branch information
dvorka committed Apr 6, 2024
1 parent fdb6381 commit cc7453c
Show file tree
Hide file tree
Showing 29 changed files with 4,167 additions and 2,732 deletions.
13 changes: 10 additions & 3 deletions Changelog
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
2024-03-?? Martin Dvorak <[email protected]>

* Released v2.1.0 - ...
2024-04-?? Martin Dvorak <[email protected]>

* Released v2.1.0 - Local LLMs support, Notes semantic search
and orphan library documents management.
* WIP: Feature: ollama integration brings privacy safe Wingman
via ollama hosted LLMs.
* WIP: Feature: embeddings calculation by ollama hosted LLM models
* WIP: Enhancement: improved LLM providers configuration: OpenAI and ollama
(environment vs. configuration; LLM model selection; provider choice)
* WIP: Enhancement: library orphans

2024-02-16 Martin Dvorak <[email protected]>

Expand Down
Binary file modified app/resources/qt/translations/mindforger_cs.qm
Binary file not shown.
1,250 changes: 715 additions & 535 deletions app/resources/qt/translations/mindforger_cs.ts

Large diffs are not rendered by default.

Binary file modified app/resources/qt/translations/mindforger_en.qm
Binary file not shown.
1,252 changes: 716 additions & 536 deletions app/resources/qt/translations/mindforger_en.ts

Large diffs are not rendered by default.

Binary file modified app/resources/qt/translations/mindforger_nerd_cs.qm
Binary file not shown.
1,244 changes: 712 additions & 532 deletions app/resources/qt/translations/mindforger_nerd_cs.ts

Large diffs are not rendered by default.

Binary file modified app/resources/qt/translations/mindforger_nerd_en.qm
Binary file not shown.
1,244 changes: 712 additions & 532 deletions app/resources/qt/translations/mindforger_nerd_en.ts

Large diffs are not rendered by default.

Binary file modified app/resources/qt/translations/mindforger_zh_cn.qm
Binary file not shown.
1,250 changes: 715 additions & 535 deletions app/resources/qt/translations/mindforger_zh_cn.ts

Large diffs are not rendered by default.

85 changes: 62 additions & 23 deletions app/src/qt/dialogs/configuration_dialog.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -685,48 +685,30 @@ ConfigurationDialog::WingmanOpenAiTab::WingmanOpenAiTab(QWidget* parent, QComboB
parentLlmProvidersCombo{parentLlmProvidersCombo},
config(Configuration::getInstance())
{
helpLabel = new QLabel(
tr(
"<html><a href='https://openai.com'>OpenAI</a> LLM provider configuration:\n"
"<ul>"
"<li>Generate new OpenAI API key at <a href='https://platform.openai.com/api-keys'>openai.com</a>.</li>"
"<li>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>"
"</ul>"
).arg(ENV_VAR_OPENAI_API_KEY));
BUG: if key in config > it overrides the env var > must be visible
hidden ONLY if env key is set & config key is empty
helpLabel->setVisible(!config.canWingmanOpenAiFromEnv());
apiKeyLabel = new QLabel(tr("<br>API key:"));
apiKeyLabel->setVisible(helpLabel->isVisible());
apiKeyEdit = new QLineEdit(this);
apiKeyEdit->setVisible(helpLabel->isVisible());
refreshLlmModelsButton = new QPushButton(tr("Refresh LLM models"), this);
refreshLlmModelsButton->setVisible(false);
// 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());
// LLM model can be choose at any time when a valid configuration is available
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);
configuredLabel->setVisible(!helpLabel->isVisible());
// help text is created using initialized dialog fields
helpLabel = new QLabel(getHelpLabelText(), this);

QVBoxLayout* llmProvidersLayout = new QVBoxLayout();
llmProvidersLayout->addWidget(helpLabel);
llmProvidersLayout->addWidget(apiKeyLabel);
llmProvidersLayout->addWidget(apiKeyEdit);
llmProvidersLayout->addWidget(configuredLabel);
llmProvidersLayout->addWidget(llmModelsLabel);
llmProvidersLayout->addWidget(llmModelsCombo);
QHBoxLayout* buttonsLayout = new QHBoxLayout{};
Expand All @@ -751,14 +733,56 @@ ConfigurationDialog::WingmanOpenAiTab::WingmanOpenAiTab(QWidget* parent, QComboB
ConfigurationDialog::WingmanOpenAiTab::~WingmanOpenAiTab()
{
delete helpLabel;
delete configuredLabel;
delete apiKeyLabel;
delete apiKeyEdit;
delete refreshLlmModelsButton;
delete clearApiKeyButton;
delete llmModelsLabel;
delete llmModelsCombo;
}

QString ConfigurationDialog::WingmanOpenAiTab::getHelpLabelText() const
{
if(apiKeyEdit->text().size()==0) {
if(config.canWingmanOpenAiFromEnv()) {
// key: NOT in config & IN env > ENV used
return tr(
"The OpenAI API key is configured using the environment<br/>"
"variable <b>%1</b>.<br/>"
"If you want to override it, then set the key below.<br/>"
"It will be saved <font color='#ff0000'>unencrypted</font> to "
"<b>.mindforger.md</b> configuration file in your home directory."
).arg(ENV_VAR_OPENAI_API_KEY);
} else {
// key: NOT in config & NOT in env > NO key
return tr(
"<html><a href='https://openai.com'>OpenAI</a> LLM provider configuration:\n"
"<ul>"
"<li>Generate new OpenAI API key at <a href='https://platform.openai.com/api-keys'>openai.com</a>.</li>"
"<li>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>"
"</ul>"
).arg(ENV_VAR_OPENAI_API_KEY);
}
} else {
if(config.canWingmanOpenAiFromEnv()) {
// key: IN config & IN env > CONFIG used
return tr(
"The OpenAI API key is configured both using the environment<br/>"
"variable <b>%1</b> and configuration.<br/>"
"Configuration overrides environment - key from below is used."
).arg(ENV_VAR_OPENAI_API_KEY);
} else {
// key: IN config & NOT env > CONFIG used
return tr(
"The OpenAI API key is configured using the key below."
).arg(ENV_VAR_OPENAI_API_KEY);
}
}
}

void refreshWingmanLlmProvidersComboBox(
QComboBox* parentLlmProvidersCombo,
Configuration& config
Expand Down Expand Up @@ -834,6 +858,8 @@ void ConfigurationDialog::WingmanOpenAiTab::refresh()
apiKeyEdit->setText(QString::fromStdString(config.getWingmanOpenAiApiKey()));
llmModelsCombo->setCurrentText(
QString::fromStdString(config.getWingmanOpenAiLlm()));

helpLabel->setText(getHelpLabelText());
}

void ConfigurationDialog::WingmanOpenAiTab::save()
Expand Down Expand Up @@ -868,6 +894,7 @@ ConfigurationDialog::WingmanOllamaTab::WingmanOllamaTab(QWidget* parent, QComboB
setOllamaButton->setToolTip(
tr("Add ollama to the dropdown with LLM providers (if not there) and set it for use with Wingman")
);
refreshLlmModelsButton = new QPushButton(tr("Refresh LLM models"), this);
clearUrlButton = new QPushButton(tr("Clear URL"), this);
llmModelsLabel = new QLabel(tr("LLM model:"));
llmModelsCombo = new QComboBox();
Expand All @@ -880,6 +907,7 @@ ConfigurationDialog::WingmanOllamaTab::WingmanOllamaTab(QWidget* parent, QComboB
llmProvidersLayout->addWidget(llmModelsLabel);
llmProvidersLayout->addWidget(llmModelsCombo);
QHBoxLayout* buttonsLayout = new QHBoxLayout{};
buttonsLayout->addWidget(refreshLlmModelsButton);
buttonsLayout->addWidget(setOllamaButton);
buttonsLayout->addWidget(clearUrlButton);
llmProvidersLayout->addLayout(buttonsLayout);
Expand All @@ -891,6 +919,9 @@ ConfigurationDialog::WingmanOllamaTab::WingmanOllamaTab(QWidget* parent, QComboB
layout->addStretch();
setLayout(layout);

QObject::connect(
refreshLlmModelsButton, SIGNAL(clicked()),
this, SLOT(refreshLlmModelsSlot()));
QObject::connect(
setOllamaButton, SIGNAL(clicked()),
this, SLOT(setOllamaSlot()));
Expand All @@ -904,11 +935,19 @@ ConfigurationDialog::WingmanOllamaTab::~WingmanOllamaTab()
delete helpLabel;
delete urlLabel;
delete urlEdit;
delete refreshLlmModelsButton;
delete clearUrlButton;
delete llmModelsLabel;
delete llmModelsCombo;
}

void ConfigurationDialog::WingmanOllamaTab::refreshLlmModelsSlot()
{
MF_DEBUG("Signal SLOT: refresh LLM models" << endl);

// TODO: refresh LLM models - how to call mind cleanly?
}

void ConfigurationDialog::WingmanOllamaTab::setOllamaSlot()
{
MF_DEBUG("Signal SLOT: set ollama" << endl);
Expand Down
8 changes: 7 additions & 1 deletion app/src/qt/dialogs/configuration_dialog.h
Original file line number Diff line number Diff line change
Expand Up @@ -90,9 +90,9 @@ class ConfigurationDialog::WingmanOpenAiTab : public QWidget
Configuration& config;

QLabel* helpLabel;
QLabel* configuredLabel;
QLabel* apiKeyLabel;
QLineEdit* apiKeyEdit;
QPushButton* refreshLlmModelsButton;
QPushButton* setOpenAiButton;
QPushButton* clearApiKeyButton;
QLabel* llmModelsLabel;
Expand All @@ -105,6 +105,10 @@ class ConfigurationDialog::WingmanOpenAiTab : public QWidget
void refresh();
void save();

private:

QString getHelpLabelText() const;

private slots:
void setOpenAiSlot();
void clearApiKeySlot();
Expand All @@ -125,6 +129,7 @@ class ConfigurationDialog::WingmanOllamaTab : public QWidget
QLabel* helpLabel;
QLabel* urlLabel;
QLineEdit* urlEdit;
QPushButton* refreshLlmModelsButton;
QPushButton* setOllamaButton;
QPushButton* clearUrlButton;
QLabel* llmModelsLabel;
Expand All @@ -138,6 +143,7 @@ class ConfigurationDialog::WingmanOllamaTab : public QWidget
void save();

private slots:
void refreshLlmModelsSlot();
void setOllamaSlot();
void clearUrlSlot();
};
Expand Down
3 changes: 3 additions & 0 deletions app/src/qt/main_menu_presenter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,9 @@ MainMenuPresenter::MainMenuPresenter(MainWindowPresenter* mwp)
QObject::connect(
view->actionMindAutolink, SIGNAL(triggered()),
mwp, SLOT(doActionMindToggleAutolink()));
QObject::connect(
view->actionMindSemanticSearch, SIGNAL(triggered()),
mwp, SLOT(doActionMindToggleSemanticSearch()));
QObject::connect(
view->actionMindLearnDirectory, SIGNAL(triggered()),
mwp, SLOT(doActionMindLearnRepository()));
Expand Down
7 changes: 7 additions & 0 deletions app/src/qt/main_menu_view.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,12 @@ MainMenuView::MainMenuView(MainWindowView& mainWindowView)
actionMindAutolink->setVisible(false);
#endif

// if Wingman is not configured and/or capable, then dialog with error message is shown (unable to detect it here)
actionMindSemanticSearch = new QAction(QIcon(":/menu-icons/find.svg"), tr("&Semantic Search"), mainWindow);
actionMindSemanticSearch->setCheckable(true);
actionMindSemanticSearch->setStatusTip(tr("Use Wingman LLM to search for similar Notes (associations) using text embeddings..."));
actionMindSemanticSearch->setShortcut(QKeySequence(Qt::CTRL+Qt::SHIFT+Qt::Key_S));

actionMindWingman = new QAction(QIcon(":/menu-icons/wingman-green.svg"), tr("&Wingman LLM"), mainWindow);
actionMindWingman->setShortcut(QKeySequence(Qt::CTRL+Qt::Key_Slash));
actionMindWingman->setStatusTip(tr("Open Wingman dialog..."));
Expand Down Expand Up @@ -205,6 +211,7 @@ MainMenuView::MainMenuView(MainWindowView& mainWindowView)
menuMind->addSeparator();
menuMind->addAction(actionMindThink);
menuMind->addAction(actionMindAutolink);
menuMind->addAction(actionMindSemanticSearch);
menuMind->addAction(actionMindWingman);
menuMind->addAction(actionMindTool);
menuMind->addAction(actionMindScope);
Expand Down
3 changes: 3 additions & 0 deletions app/src/qt/main_menu_view.h
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,9 @@ class MainMenuView : public QObject
QAction* actionMindRemember;
QAction* actionMindThink;
QAction* actionMindAutolink;
#ifdef DO_MF_DEBUG
QAction* actionMindSemanticSearch;
#endif
QAction* actionMindWingman;
QAction* actionMindTool;
QAction* actionMindScope;
Expand Down
32 changes: 32 additions & 0 deletions app/src/qt/main_window_presenter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -666,6 +666,38 @@ void MainWindowPresenter::doActionMindToggleAutolink()
}
}

void MainWindowPresenter::doActionMindToggleSemanticSearch()
{
// TODO: to be implemented in analogous way as autolinking
if(config.isSemanticSearch()) {
config.setSemanticSearch(false);
statusBar->showInfo(tr("Semantic search disabled"));
} else {
// check whether possible
if(config.canWingmanOllama()) {
config.setSemanticSearch(true);
statusBar->showInfo(tr("Semantic search activated"));
} else {
config.setSemanticSearch(false);
statusBar->showError(tr("Semantic search cannot be activated - missing dependencies"));
QMessageBox::critical(
&view,
tr("Semantic Search"),
tr("Semantic search cannot be activated - ollama Wingman must be configured.")
);
return;
}
}
mdConfigRepresentation->save(config);

// activate semantic search
if(config.isSemanticSearch()) {
statusBar->showInfo(tr("Refresh semantic search index ~ text embeddings of all (modified) Notes..."));
mind->refreshEmbeddings();
}
}


void MainWindowPresenter::doActionNameDescFocusSwap()
{
if(orloj->isFacetActive(OrlojPresenterFacets::FACET_EDIT_OUTLINE_HEADER)) {
Expand Down
1 change: 1 addition & 0 deletions app/src/qt/main_window_presenter.h
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,7 @@ public slots:
void doActionMindSleep();
void doActionMindToggleThink();
void doActionMindToggleAutolink();
void doActionMindToggleSemanticSearch();
void doActionMindLearnRepository();
void doActionMindLearnFile();
void doActionMindRelearn(QString path);
Expand Down
11 changes: 9 additions & 2 deletions build/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -147,9 +147,9 @@ build: clean-app ../app/mindforger ## build production MindForger application bi


.PHONY: build-dev
build-dev: clean-app ## build development MindForger application binary
build-dev: clean-app ## build development MindForger application binary w/ debug info
@echo "Building DEV MindForger executable..."
cd .. && qmake -r mindforger.pro DEFINES+=DO_MF_DEBUG && make -j $(CPU_CORES) ; cd ..
cd .. && qmake -r mindforger.pro CONFIG+=debug DEFINES+=DO_MF_DEBUG && make -j $(CPU_CORES) ; cd ..
ifeq ($(UNAME), Darwin)
@echo "\nIf build succeeded, then MindForger executable can be found in:\n app/mindforger.app/Contents/MacOS/mindforger"
ls -al ../app/mindforger.app/Contents/MacOS/mindforger
Expand Down Expand Up @@ -204,6 +204,13 @@ else
# cd ../app && pwd && ./mindforger /home/dvorka/mf-devel/bug-copy-image
endif

run-gdb: build-dev ## run MindForger using gdb
ifeq ($(UNAME), Darwin)
cd ../app/mindforger.app/Contents/MacOS && ./mindforger /Users/dvorka/mf-devel/mf-copy
else
cd ../app && pwd && gdb --args ./mindforger /home/dvorka/mf-devel/library-trainer
endif

#
# install
#
Expand Down
4 changes: 3 additions & 1 deletion lib/src/config/configuration.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ const string Configuration::DEFAULT_UI_HTML_CSS_THEME = string{UI_DEFAULT_HTML_C
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 = string{LLM_MODEL_GPT35_TURBO};
const string Configuration::DEFAULT_WINGMAN_LLM_MODEL_OLLAMA = string{LLM_MODEL_LLAMA2};
const string Configuration::DEFAULT_WINGMAN_LLM_MODEL_OLLAMA = string{LLM_MODEL_PHI};

Configuration::Configuration()
: asyncMindThreshold{},
Expand All @@ -51,6 +51,7 @@ Configuration::Configuration()
autolinking{DEFAULT_AUTOLINKING},
autolinkingColonSplit{},
autolinkingCaseInsensitive{},
semanticSearch{DEFAULT_SEMANTIC_SEARCH},
wingmanProvider{DEFAULT_WINGMAN_LLM_PROVIDER},
wingmanOpenAiApiKey{},
wingmanOpenAiLlm{DEFAULT_WINGMAN_LLM_MODEL_OPENAI},
Expand Down Expand Up @@ -151,6 +152,7 @@ void Configuration::clear()
autolinking = DEFAULT_AUTOLINKING;
autolinkingColonSplit = DEFAULT_AUTOLINKING_COLON_SPLIT;
autolinkingCaseInsensitive = DEFAULT_AUTOLINKING_CASE_INSENSITIVE;
semanticSearch = DEFAULT_SEMANTIC_SEARCH;
wingmanProvider = DEFAULT_WINGMAN_LLM_PROVIDER;
wingmanOpenAiApiKey.clear();
wingmanOpenAiLlm = DEFAULT_WINGMAN_LLM_MODEL_OPENAI;
Expand Down
7 changes: 7 additions & 0 deletions lib/src/config/configuration.h
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ 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";
constexpr const auto LLM_MODEL_PHI = "phi";

// const in constexpr makes value const
constexpr const auto ENV_VAR_HOME = "HOME";
Expand Down Expand Up @@ -275,6 +276,8 @@ class Configuration {
static constexpr const bool DEFAULT_AUTOLINKING_CASE_INSENSITIVE = true;
static constexpr const bool DEFAULT_SAVE_READS_METADATA = true;

static constexpr const bool DEFAULT_SEMANTIC_SEARCH = false;

static constexpr const bool UI_DEFAULT_NERD_TARGET_AUDIENCE = true;
static const std::string DEFAULT_STARTUP_VIEW_NAME;
static const std::string DEFAULT_UI_THEME_NAME;
Expand Down Expand Up @@ -343,6 +346,8 @@ class Configuration {
bool autolinkingColonSplit;
bool autolinkingCaseInsensitive;

bool semanticSearch;

/*
Wingman configuration, initialization and use:
Expand Down Expand Up @@ -544,6 +549,8 @@ class Configuration {
void setAutolinkingColonSplit(bool autolinkingColonSplit) { this->autolinkingColonSplit=autolinkingColonSplit; }
bool isAutolinkingCaseInsensitive() const { return autolinkingCaseInsensitive; }
void setAutolinkingCaseInsensitive(bool autolinkingCaseInsensitive) { this->autolinkingCaseInsensitive=autolinkingCaseInsensitive; }
bool isSemanticSearch() const { return semanticSearch; }
void setSemanticSearch(bool semanticSearch) { this->semanticSearch=semanticSearch; }
unsigned int getMd2HtmlOptions() const { return md2HtmlOptions; }
AssociationAssessmentAlgorithm getAaAlgorithm() const { return aaAlgorithm; }
void setAaAlgorithm(AssociationAssessmentAlgorithm aaa) { aaAlgorithm = aaa; }
Expand Down
Loading

0 comments on commit cc7453c

Please sign in to comment.