From e688f45198a09dfa363fc93e2aeba0125ecae993 Mon Sep 17 00:00:00 2001 From: Martin Dvorak Date: Sat, 13 Jan 2024 20:54:46 +0100 Subject: [PATCH] Wingman MOCK done. --- app/src/qt/dialogs/chat_dialog.cpp | 91 ++++++++++++---------- app/src/qt/dialogs/chat_dialog.h | 4 +- app/src/qt/dialogs/wingman_dialog.cpp | 104 +++++++++++-------------- app/src/qt/dialogs/wingman_dialog.h | 49 ++++++++---- app/src/qt/main_menu_view.cpp | 6 +- app/src/qt/main_menu_view.h | 2 +- app/src/qt/main_window_presenter.cpp | 92 +++++++++++++++------- app/src/qt/note_editor_view.cpp | 2 + lib/src/config/configuration.h | 2 +- lib/src/gear/string_utils.h | 4 +- lib/src/mind/ai/llm/bard_wingman.cpp | 31 ++++++++ lib/src/mind/ai/llm/bard_wingman.h | 100 ++++++++++++++++++++++++ lib/src/mind/ai/llm/mock_wingman.cpp | 2 +- lib/src/mind/ai/llm/openai_wingman.cpp | 2 +- lib/src/mind/ai/llm/wingman.h | 79 +++++++++++++++++++ lib/src/mind/mind.h | 1 + 16 files changed, 420 insertions(+), 151 deletions(-) create mode 100644 lib/src/mind/ai/llm/bard_wingman.cpp create mode 100644 lib/src/mind/ai/llm/bard_wingman.h diff --git a/app/src/qt/dialogs/chat_dialog.cpp b/app/src/qt/dialogs/chat_dialog.cpp index e89eac03..d861d034 100644 --- a/app/src/qt/dialogs/chat_dialog.cpp +++ b/app/src/qt/dialogs/chat_dialog.cpp @@ -22,6 +22,9 @@ namespace m8r { using namespace std; +const string COLOR_PROMPT_GREEN{"#00bb00"}; +const string COLOR_PROMPT_BLUE{"#00aaaa"}; + ChatDialog::ChatDialog(QWidget* parent) : QDialog(parent) { @@ -54,7 +57,7 @@ ChatDialog::ChatDialog(QWidget* parent) chatWindow = new QTextEdit(this); chatWindow->setReadOnly(true); chatWindow->clear(); - chatWindow->insertHtml(QString::fromStdString(getPrompt())); + chatWindow->insertHtml(QString::fromStdString(getTerminalPrompt())); QVBoxLayout* layout = new QVBoxLayout{this}; layout->addWidget(chatWindow); @@ -70,15 +73,20 @@ ChatDialog::~ChatDialog() void ChatDialog::show() { - // > Summarize. [green] - // 🤖 Lorem ipsum dolor sit amet, [gray] + // Notebook/ OpenAI chatGPT3.5 [blue, yellow] + // $ Summarize. [green] + // + // Lorem ipsum dolor sit amet, [gray] // consectetur adipiscing elit. // - // Explain like I'm 5: NLP. - // 🤖 Lorem ipsum dolor sit amet, + // ~/notebook/ OpenAI chatGPT3.5 + // $ Explain 'NLP' like I'm 5. + // + // Lorem ipsum dolor sit amet, // - // > - // ^ cursor + // ~/notebook/ OpenAI chatGPT3.5 + // $ + // ^ cursor cmdEdit->clear(); cmdEdit->setFocus(); @@ -87,14 +95,27 @@ void ChatDialog::show() QDialog::show(); } -string ChatDialog::getPrompt(bool error) +string ChatDialog::getTerminalPrompt(bool error) { - string prompt{"" + getCwd() + ""}; - prompt.append("
@" + thing + " " + + "" + thingName + "" + "        " + "" + wingmanModel + "" + "
" + }; + + prompt.append("> "); return prompt; @@ -104,17 +125,26 @@ void ChatDialog::insertPrompt(const std::string& prompt) { chatWindow->insertHtml( QString::fromStdString( - "" - "> " + prompt + + "" + + prompt + "" + "

" )); - chatWindow->insertHtml(QString::fromStdString("

")); + chatWindow->moveCursor(QTextCursor::End); + chatWindow->ensureCursorVisible(); } -void ChatDialog::insertOutput(const std::string& output) +void ChatDialog::insertOutput(const std::string& output, bool error) { - chatWindow->insertHtml(QString::fromStdString("🤖 " + output)); - chatWindow->insertHtml(QString::fromStdString("

")); + chatWindow->insertHtml( + QString::fromStdString( + "
" + + output + + "
" + + getTerminalPrompt(error) + )); + chatWindow->moveCursor(QTextCursor::End); + chatWindow->ensureCursorVisible(); } void ChatDialog::runCommand() @@ -125,7 +155,7 @@ void ChatDialog::runCommand() || cmdEdit->text() == QString::fromStdString("cls") ) { chatWindow->clear(); - chatWindow->insertHtml(QString::fromStdString(getPrompt())); + chatWindow->insertHtml(QString::fromStdString(getTerminalPrompt())); } else if(cmdEdit->text() == QString::fromStdString("exit") || cmdEdit->text() == QString::fromStdString("quit") || cmdEdit->text() == QString::fromStdString("bye") @@ -151,36 +181,17 @@ void ChatDialog::runCommand() // run prompt MF_DEBUG("Running prompt: '" << cmd << "'" << endl); int statusCode{0}; - string cmdStdOut{}; + string cmdStdOut{"Foo result Lorem ipsum dolor sit amet, consectetur adipiscing elit."}; // TODO run prompt // TODO run prompt // TODO run prompt MF_DEBUG("Chat command finished with status: " << statusCode << endl); - chatWindow->insertHtml(QString::fromStdString("
")); - if(cmdStdOut.size()) { - replaceAll("\n", "
", cmdStdOut); - chatWindow->insertHtml( - QString::fromStdString("🤖 " + cmdStdOut + "
") - ); + // replaceAll("\n", "
", cmdStdOut); + this->insertOutput(cmdStdOut, statusCode!=0?true:false); } - - if(statusCode) { - cerr << "Chat command failed with status: " << statusCode << endl; - chatWindow->insertHtml( - QString::fromStdString(getPrompt(true)) - ); - } else { - chatWindow->insertHtml( - QString::fromStdString(getPrompt()) - ); - } - - // scroll down by moving cursor to the end AND ensuring it's visible - chatWindow->moveCursor(QTextCursor::End); - chatWindow->ensureCursorVisible(); } } diff --git a/app/src/qt/dialogs/chat_dialog.h b/app/src/qt/dialogs/chat_dialog.h index d72256d9..5e2b9e05 100644 --- a/app/src/qt/dialogs/chat_dialog.h +++ b/app/src/qt/dialogs/chat_dialog.h @@ -74,13 +74,13 @@ class ChatDialog : public QDialog ~ChatDialog(); void insertPrompt(const std::string& prompt); - void insertOutput(const std::string& output); + void insertOutput(const std::string& output, bool error=false); void show(); private: void runCommand(); - std::string getPrompt(bool error=false); + std::string getTerminalPrompt(bool error=false); }; } diff --git a/app/src/qt/dialogs/wingman_dialog.cpp b/app/src/qt/dialogs/wingman_dialog.cpp index 80de6716..01c0399b 100644 --- a/app/src/qt/dialogs/wingman_dialog.cpp +++ b/app/src/qt/dialogs/wingman_dialog.cpp @@ -22,59 +22,33 @@ namespace m8r { using namespace std; -const vector WingmanDialog::outlinePrompts( - { - QString{"Summarize."}, - QString{"Generate tags from the text."}, - QString{"Find persons."}, - QString{"Find locations."}, - QString{"Find organizations."}, - QString{"Chat with the content."}, +WingmanDialog::WingmanDialog( + const vector& predefinedOPrompts, + const vector& predefinedNPrompts, + const vector& predefinedTPrompts, + QWidget* parent +): + context{}, + QDialog(parent) +{ + for(string prompt:predefinedOPrompts) { + outlinePrompts.push_back(QString::fromStdString(prompt)); } -); -const vector WingmanDialog::notePrompts( - { - QString{"Summarize."}, - QString{"Shorten."}, - QString{"Explain #NAME like I'm 5."}, - QString{"Generate tags."}, - QString{"Fix grammar."}, - QString{"Rewrite formally."}, - QString{"Rewrite informally."}, - QString{"Rewrite to be funny."}, - QString{"Chat with the content."}, - // other UCs: - // - NER UCs - // - simplify - // - beautify - // - translate - // - fix spelling - // - fix style - // - create plan ... + for(string prompt:predefinedNPrompts) { + notePrompts.push_back(QString::fromStdString(prompt)); } -); -const vector WingmanDialog::textPrompts( - { - QString{"Complete the text."}, - QString{"Complete the last text line."}, - QString{"Explain like I'm 5."}, - QString{"Fix grammar."}, - QString{"Generate tags."}, - QString{"Rewrite formally."}, - QString{"Rewrite informally."}, - QString{"Rewrite to Kafka style."}, + for(string prompt:predefinedTPrompts) { + textPrompts.push_back(QString::fromStdString(prompt)); } -); -WingmanDialog::WingmanDialog(QWidget* parent) - : QDialog(parent) -{ // UI setWindowTitle(tr("Wingman")); preludeLabel = new QLabel{ tr( - "Wingman can run a predefined or custom prompt." + "Wingman can run a predefined or custom prompt" + " " + "with the selected context." "
" ), parent @@ -90,8 +64,10 @@ WingmanDialog::WingmanDialog(QWidget* parent) predefinedPromptsCombo->addItem(toolName); } - promptLabel = new QLabel{tr("Your:"), parent}; - promptEdit = new QLineEdit{parent}; + promptLabel = new QLabel{ + tr("Your (overrides Predefined, use #NAME or #TEXT to include context):"), + parent}; + promptEdit = new QTextEdit{parent}; promptEdit->setToolTip( tr("Type in your prompt like: 'Translate the following text to Spanish: #CONTENT.")); @@ -117,17 +93,12 @@ WingmanDialog::WingmanDialog(QWidget* parent) contextEdit = new QLineEdit{parent}; contextEdit->setReadOnly(true); - postmortemLabel = new QLabel{ - tr("Use #NAME or #TEXT to include it to your prompt."), - parent}; - - contentLayout->addWidget(contextTypeLabel); - contentLayout->addWidget(contextTypeEdit); contentLayout->addWidget(contextNameLabel); contentLayout->addWidget(contextNameEdit); contentLayout->addWidget(contextLabel); contentLayout->addWidget(contextEdit); - contentLayout->addWidget(postmortemLabel); + contentLayout->addWidget(contextTypeLabel); + contentLayout->addWidget(contextTypeEdit); contentGroup->setLayout(contentLayout); // IMPROVE disable/enable find button if text/path is valid: freedom vs validation @@ -176,12 +147,19 @@ WingmanDialog::~WingmanDialog() delete contextLabel; delete contextEdit; - delete postmortemLabel; - delete runButton; delete closeButton; } +void WingmanDialog::clear() +{ + this->context.clear(); + + this->promptEdit->clear(); + this->contextNameEdit->clear(); + this->contextEdit->clear(); +} + void WingmanDialog::initForMode(WingmanDialogModes mode) { this->mode=mode; @@ -189,18 +167,28 @@ void WingmanDialog::initForMode(WingmanDialogModes mode) switch(mode) { case WingmanDialogModes::WINGMAN_DIALOG_MODE_OUTLINE: contextTypeEdit->setText(tr("outline")); - contextEdit->setText(tr("")); + contextEdit->setText(tr("")); break; case WingmanDialogModes::WINGMAN_DIALOG_MODE_NOTE: contextTypeEdit->setText(tr("note")); - contextEdit->setText(tr("")); + contextEdit->setText(tr("")); break; case WingmanDialogModes::WINGMAN_DIALOG_MODE_TEXT: - contextEdit->setText(tr("")); + contextNameEdit->clear(); + contextEdit->clear(); break; } } +void WingmanDialog::setContextText(QString context) { + this->context=context; + this->contextEdit->setText(context.mid(0, 50).append("...")); +} + +QString WingmanDialog::getContextText() const { + return context; +} + void WingmanDialog::show() { QDialog::show(); diff --git a/app/src/qt/dialogs/wingman_dialog.h b/app/src/qt/dialogs/wingman_dialog.h index 808ee86f..d9c8db6d 100644 --- a/app/src/qt/dialogs/wingman_dialog.h +++ b/app/src/qt/dialogs/wingman_dialog.h @@ -19,6 +19,9 @@ #ifndef M8RUI_WINGMAN_DIALOG_H #define M8RUI_WINGMAN_DIALOG_H +#include +#include + #include #include "../../lib/src/config/configuration.h" @@ -36,20 +39,21 @@ class WingmanDialog : public QDialog Q_OBJECT private: - - static const std::vector outlinePrompts; - static const std::vector notePrompts; - static const std::vector textPrompts; + std::vector outlinePrompts; + std::vector notePrompts; + std::vector textPrompts; WingmanDialogModes mode; + QString context; + QLabel* preludeLabel; QLabel* predefinedPromptsLabel; QComboBox* predefinedPromptsCombo; QLabel* promptLabel; - QLineEdit* promptEdit; + QTextEdit* promptEdit; QLabel* contextTypeLabel; QLineEdit* contextTypeEdit; @@ -58,34 +62,49 @@ class WingmanDialog : public QDialog QLabel* contextLabel; QLineEdit* contextEdit; - QLabel* postmortemLabel; - QPushButton* runButton; QPushButton* closeButton; public: - explicit WingmanDialog(QWidget* parent); + explicit WingmanDialog( + const std::vector& predefinedOPrompts, + const std::vector& predefinedNPrompts, + const std::vector& predefinedTPrompts, + QWidget* parent); WingmanDialog(const WingmanDialog&) = delete; WingmanDialog(const WingmanDialog&&) = delete; WingmanDialog& operator =(const WingmanDialog&) = delete; WingmanDialog& operator =(const WingmanDialog&&) = delete; ~WingmanDialog(); - void clear() { - this->promptEdit->clear(); - this->contextNameEdit->clear(); - this->contextEdit->clear(); - } + void clear(); void initForMode(WingmanDialogModes mode); + WingmanDialogModes getMode() const { return mode; } + void setPromptText(QString phrase) { this->promptEdit->setText(phrase); } + QString getPromptText() const { + if(this->promptEdit->toPlainText().isEmpty()) { + return predefinedPromptsCombo->currentText(); + } + return this->promptEdit->toPlainText(); + } + + void setContextType(WingmanDialogModes contextType) { + this->mode = contextType; + } + WingmanDialogModes getContextType() const { + return this->mode; + } void setContextNameText(QString contentName) { this->contextNameEdit->setText(contentName); } - void setContextText(QString content) { - this->contextEdit->setText(content); + QString getContextNameText() const { + return this->contextNameEdit->text(); } + void setContextText(QString context); + QString getContextText() const; void show(); diff --git a/app/src/qt/main_menu_view.cpp b/app/src/qt/main_menu_view.cpp index d3fe51b8..781bf4b4 100644 --- a/app/src/qt/main_menu_view.cpp +++ b/app/src/qt/main_menu_view.cpp @@ -741,8 +741,8 @@ MainMenuView::MainMenuView(MainWindowView& mainWindowView) actionEditExtract = new QAction(QIcon(":/menu-icons/cut.svg"), tr("E&xtract"), mainWindow); actionEditExtract->setStatusTip(tr("Create new Note from the text selected in the current Note...")); - actionEditRunTool = new QAction(QIcon(":/menu-icons/on.svg"), tr("Find Knowledge\tCtrl+/"), mainWindow); - actionEditRunTool->setStatusTip(tr("Run an external tool to find, explain, process text under the cursor")); + actionEditWingman = new QAction(QIcon(":/menu-icons/on.svg"), tr("&Wingman"), mainWindow); // \tCtrl+/ + actionEditWingman->setStatusTip(tr("Run an external tool to find, explain, process text under the cursor")); actionEditComplete = new QAction(QIcon(":/menu-icons/link.svg"), tr("Complete Link\tCtrl+L"), mainWindow); actionEditComplete->setStatusTip(tr("Complete word being written by finding link to Notebook or Note")); @@ -765,7 +765,7 @@ MainMenuView::MainMenuView(MainWindowView& mainWindowView) menuEdit->addAction(actionEditWordWrap); menuEdit->addAction(actionEditNameDescFocusSwap); menuEdit->addSeparator(); - menuEdit->addAction(actionEditRunTool); + menuEdit->addAction(actionEditWingman); menuEdit->addAction(actionEditComplete); menuEdit->addAction(actionEditExtract); menuEdit->addSeparator(); diff --git a/app/src/qt/main_menu_view.h b/app/src/qt/main_menu_view.h index ad29635d..b92bd41e 100644 --- a/app/src/qt/main_menu_view.h +++ b/app/src/qt/main_menu_view.h @@ -194,7 +194,7 @@ class MainMenuView : public QObject QAction* actionEditWordWrap; QAction* actionEditNameDescFocusSwap; QAction* actionEditExtract; - QAction* actionEditRunTool; + QAction* actionEditWingman; QAction* actionEditComplete; QAction* actionEditSpellCheck; diff --git a/app/src/qt/main_window_presenter.cpp b/app/src/qt/main_window_presenter.cpp index 5cff5e80..c21c0ca3 100644 --- a/app/src/qt/main_window_presenter.cpp +++ b/app/src/qt/main_window_presenter.cpp @@ -57,7 +57,11 @@ MainWindowPresenter::MainWindowPresenter(MainWindowView& view) syncLibraryDialog = new SyncLibraryDialog{&view}; rmLibraryDialog = new RemoveLibraryDialog(&view); runToolDialog = new RunToolDialog{&view}; - wingmanDialog = new WingmanDialog{&view}; + wingmanDialog = new WingmanDialog{ + mind->getWingman()->getPredefinedOPrompts(), + mind->getWingman()->getPredefinedNPrompts(), + mind->getWingman()->getPredefinedTPrompts(), + &view}; chatDialog = new ChatDialog{&view}; scopeDialog = new ScopeDialog{mind->getOntology(), &view}; newOrganizerDialog = new OrganizerNewDialog{mind->getOntology(), &view}; @@ -2042,24 +2046,37 @@ void MainWindowPresenter::doActionWingman() // - N tree: get N name // - O tree: get O name // - ... - QString phrase; + QString contextTextName{}; + QString contextText{}; + WingmanDialogModes contextType{WingmanDialogModes::WINGMAN_DIALOG_MODE_TEXT}; + if(orloj->isFacetActive(OrlojPresenterFacets::FACET_EDIT_NOTE)) { - phrase = orloj->getNoteEdit()->getView()->getNoteEditor()->getToolPhrase(); + contextText = orloj->getNoteEdit()->getView()->getNoteEditor()->getToolPhrase(); + contextType = WingmanDialogModes::WINGMAN_DIALOG_MODE_TEXT; } else if( orloj->isFacetActive(OrlojPresenterFacets::FACET_VIEW_OUTLINE) || orloj->isFacetActive(OrlojPresenterFacets::FACET_VIEW_OUTLINE_HEADER) ) { Outline* o = orloj->getOutlineView()->getCurrentOutline(); if(o) { - phrase = QString::fromStdString(o->getName()); + contextTextName = QString::fromStdString(o->getName()); + string contextTextStr{}; + mdRepresentation->to(o, &contextTextStr); + contextText = QString::fromStdString(contextTextStr); + contextType = WingmanDialogModes::WINGMAN_DIALOG_MODE_OUTLINE; } } else if(orloj->isFacetActive(OrlojPresenterFacets::FACET_VIEW_NOTE)) { Note* note = orloj->getOutlineView()->getOutlineTree()->getCurrentNote(); if(note) { - phrase = QString::fromStdString(note->getName()); + contextTextName = QString::fromStdString(note->getName()); + string contextTextStr{}; + mdRepresentation->to(note, &contextTextStr); + contextText = QString::fromStdString(contextTextStr); + contextType = WingmanDialogModes::WINGMAN_DIALOG_MODE_NOTE; } } else if(orloj->isFacetActive(OrlojPresenterFacets::FACET_EDIT_OUTLINE_HEADER)) { - phrase = orloj->getOutlineHeaderEdit()->getView()->getHeaderEditor()->getToolPhrase(); + contextText = orloj->getOutlineHeaderEdit()->getView()->getHeaderEditor()->getToolPhrase(); + contextType = WingmanDialogModes::WINGMAN_DIALOG_MODE_TEXT; } else if(orloj->isFacetActive(OrlojPresenterFacets::FACET_LIST_OUTLINES)) { int row = orloj->getOutlinesTable()->getCurrentRow(); if(row != OutlinesTablePresenter::NO_ROW) { @@ -2067,7 +2084,13 @@ void MainWindowPresenter::doActionWingman() = orloj->getOutlinesTable()->getModel()->item(row); if(item) { Outline* outline = item->data(Qt::UserRole + 1).value(); - phrase = QString::fromStdString(outline->getName()); + if(outline) { + contextTextName = QString::fromStdString(outline->getName()); + string contextTextStr{}; + mdRepresentation->to(outline, &contextTextStr); + contextText = QString::fromStdString(contextTextStr); + contextType = WingmanDialogModes::WINGMAN_DIALOG_MODE_OUTLINE; + } } } } else if(orloj->isFacetActive(OrlojPresenterFacets::FACET_MAP_OUTLINES)) { @@ -2077,29 +2100,37 @@ void MainWindowPresenter::doActionWingman() = orloj->getOutlinesMap()->getModel()->item(row); if(item) { Note* note = item->data(Qt::UserRole + 1).value(); - phrase = QString::fromStdString(note->getName()); + if(note) { + contextTextName = QString::fromStdString(note->getName()); + string contextTextStr{}; + mdRepresentation->to(note, &contextTextStr); + contextText = QString::fromStdString(contextTextStr); + contextType = WingmanDialogModes::WINGMAN_DIALOG_MODE_OUTLINE; + } } } } - if(phrase.length() == 0) { + if(contextTextName.length() == 0) { QMessageBox msgBox{ QMessageBox::Critical, - QObject::tr("Empty Phrase"), - QObject::tr("Phrase to search/explain/process is empty.") + QObject::tr("Empty Prompt"), + QObject::tr("Prompt to run/explain/process is empty.") }; msgBox.exec(); return; } - // TODO set type determined ^ - this->wingmanDialog->initForMode(WingmanDialogModes::WINGMAN_DIALOG_MODE_OUTLINE); - // TODO set context name e.g. N name - // TODO set context (actual text to be used in prompt) e.g. N description - + // context + this->wingmanDialog->initForMode(contextType); + if(contextType == WingmanDialogModes::WINGMAN_DIALOG_MODE_TEXT) { + this->wingmanDialog->setContextNameText(""); + this->wingmanDialog->setContextText(contextTextName); + } else { + this->wingmanDialog->setContextNameText(contextTextName); + this->wingmanDialog->setContextText(contextText); + } - // TODO rename content to context - this->wingmanDialog->setContextNameText(phrase); this->wingmanDialog->show(); } @@ -2110,20 +2141,27 @@ void MainWindowPresenter::handleActionWingman() string wingmanAnswer{}; - // TODO get and resolve prompt + // system prompt: prompt + context - // TODO this->wingmanDialog->getPrompt(); - mind->wingmanSummarize( - "FOO text", - wingmanAnswer - ); + // resolve prompt to system prompt + string systemPrompt{this->wingmanDialog->getPromptText().toStdString()}; + replaceAll( + CTX_INCLUDE_NAME, + this->wingmanDialog->getContextNameText().toStdString(), + systemPrompt); + replaceAll( + CTX_INCLUDE_TEXT, + this->wingmanDialog->getContextText().toStdString(), + systemPrompt); + + // RUN wingman + // TODO route action to wingman handler + mind->wingmanSummarize(systemPrompt, wingmanAnswer); // show result - this->chatDialog->insertPrompt("Summarize."); + this->chatDialog->insertPrompt(systemPrompt); // TODO trom huge prompts + suffix ... this->chatDialog->insertOutput(wingmanAnswer); this->chatDialog->show(); - - // TODO TODO TODO continue here } void MainWindowPresenter::doActionOutlineOrNoteNew() diff --git a/app/src/qt/note_editor_view.cpp b/app/src/qt/note_editor_view.cpp index a14ca196..456f81eb 100644 --- a/app/src/qt/note_editor_view.cpp +++ b/app/src/qt/note_editor_view.cpp @@ -101,10 +101,12 @@ NoteEditorView::NoteEditorView(QWidget* parent) this, SLOT(insertCompletion(QString)) ); // shortcut signals + /* new QShortcut( QKeySequence(QKeySequence(Qt::CTRL+Qt::Key_Slash)), this, SLOT(slotStartRunTool()) ); + */ new QShortcut( QKeySequence(QKeySequence(Qt::CTRL+Qt::Key_L)), this, SLOT(slotStartLinkCompletion()) diff --git a/lib/src/config/configuration.h b/lib/src/config/configuration.h index d54a3cce..236edc3a 100644 --- a/lib/src/config/configuration.h +++ b/lib/src/config/configuration.h @@ -70,7 +70,7 @@ constexpr const auto START_TO_OUTLINES_TREE = "outlines tree"; constexpr const auto START_TO_OUTLINES = "outlines"; constexpr const auto START_TO_TAGS = "tags"; constexpr const auto START_TO_RECENT = "recent"; -constexpr const auto START_TO_EISENHOWER_MATRIX = "Eisehower"; +constexpr const auto START_TO_EISENHOWER_MATRIX = "Eisenhower"; constexpr const auto START_TO_HOME_OUTLINE = "home"; constexpr const auto DEFAULT_STARTUP_VIEW = START_TO_OUTLINES; diff --git a/lib/src/gear/string_utils.h b/lib/src/gear/string_utils.h index 34c08ec9..4475c491 100644 --- a/lib/src/gear/string_utils.h +++ b/lib/src/gear/string_utils.h @@ -59,7 +59,7 @@ std::vector stringSplit(const std::string s, const std::string rege std::string normalizeToNcName(std::string name, char quoteChar); /** - * @brief Check wheter strings are identical while ignoring case. + * @brief Check whether the strings are identical while ignoring case. */ static inline bool stringistring(const std::string& a, const std::string& b) { @@ -88,7 +88,7 @@ static inline void stringToLower(const std::string& s, std::string& lowerS) * * New trimmed string is created and returned - caller is responsible for freeing it. */ -static inline char *stringTrim(const char *s) { +static inline char* stringTrim(const char* s) { if(s) { while(isspace(static_cast(*s))) { s++; diff --git a/lib/src/mind/ai/llm/bard_wingman.cpp b/lib/src/mind/ai/llm/bard_wingman.cpp new file mode 100644 index 00000000..73c08647 --- /dev/null +++ b/lib/src/mind/ai/llm/bard_wingman.cpp @@ -0,0 +1,31 @@ +/* + bard_wingman.cpp MindForger thinking notebook + + Copyright (C) 2016-2024 Martin Dvorak + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ +#include "bard_wingman.h" + +namespace m8r { + +BardWingman::BardWingman() +{ +} + +BardWingman::~BardWingman() +{ +} + +} // m8r namespace diff --git a/lib/src/mind/ai/llm/bard_wingman.h b/lib/src/mind/ai/llm/bard_wingman.h new file mode 100644 index 00000000..f950725b --- /dev/null +++ b/lib/src/mind/ai/llm/bard_wingman.h @@ -0,0 +1,100 @@ +/* + bard_wingman.h MindForger thinking notebook + + Copyright (C) 2016-2024 Martin Dvorak + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ +#ifndef M8R_BARD_WINGMAN_H +#define M8R_BARD_WINGMAN_H + +namespace m8r { + +/* + +PaLM2 prompt: Give C++ call to bard API to get inference + +ANSWER: + +To call the Bard API in C++ to get an inference, you can use the following code: + +#include +#include + +int main() { + // Initialize CURL + CURL *curl = curl_easy_init(); + if (!curl) { + std::cerr << "Failed to initialize CURL" << std::endl; + return 1; + } + + // Set the URL of the Bard API endpoint + curl_easy_setopt(curl, CURLOPT_URL, "https://bard.google.com/v2/query"); + + // Set the request body + std::string requestBody = "query=Hello, world!"; + curl_easy_setopt(curl, CURLOPT_POSTFIELDS, requestBody.c_str()); + + // Set the content type + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, + std::vector({ + {"Content-Type: application/json"}, + })); + + // Perform the request + CURLcode res = curl_easy_perform(curl); + if (res != CURLE_OK) { + std::cerr << "Failed to perform the request: " << curl_easy_strerror(res) + << std::endl; + return 1; + } + + // Get the response + std::string responseBody; + curl_easy_getinfo(curl, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &responseBody.size()); + curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &res); + if (res != CURLE_OK || responseBody.empty()) { + std::cerr << "Failed to get a valid response" << std::endl; + return 1; + } + + // Read the response body + std::vector responseBuffer(responseBody.size()); + curl_easy_read_buffer(curl, responseBuffer.data(), responseBuffer.size()); + responseBody.assign(responseBuffer.data(), responseBuffer.size()); + + // Close the connection + curl_easy_cleanup(curl); + + // Print the response + std::cout << responseBody << std::endl; + + return 0; +} + +*/ +class BardWingman +{ +public: + explicit BardWingman(); + BardWingman(const BardWingman&) = delete; + BardWingman(const BardWingman&&) = delete; + BardWingman& operator =(const BardWingman&) = delete; + BardWingman& operator =(const BardWingman&&) = delete; + ~BardWingman(); +}; + +} +#endif // M8R_BARD_WINGMAN_H diff --git a/lib/src/mind/ai/llm/mock_wingman.cpp b/lib/src/mind/ai/llm/mock_wingman.cpp index f977bf23..9a8d420f 100644 --- a/lib/src/mind/ai/llm/mock_wingman.cpp +++ b/lib/src/mind/ai/llm/mock_wingman.cpp @@ -35,7 +35,7 @@ void MockWingman::summarize(const string& text, string& summary) { MF_DEBUG("MockWingman::summarize() text:" << text << endl); - summary = "This is a MOCK summary of the text: '"+text+"'."; + summary = "SUMMARY(MOCK, '"+text+"')"; MF_DEBUG("MockWingman::summarize() summary:" << summary << endl); } diff --git a/lib/src/mind/ai/llm/openai_wingman.cpp b/lib/src/mind/ai/llm/openai_wingman.cpp index 496d8791..a1e43e14 100644 --- a/lib/src/mind/ai/llm/openai_wingman.cpp +++ b/lib/src/mind/ai/llm/openai_wingman.cpp @@ -71,7 +71,7 @@ void OpenAiWingman::summarize(const string& text, string& summary) { MF_DEBUG("OpenAiWingman::summarize() text:" << text << endl); - summary = "This is a OPENAI summary of the text: '"+text+"'."; + summary = "SUMMARY(OpenAI, '"+text+"')"; MF_DEBUG("OpenAiWingman::summarize() summary:" << summary << endl); } diff --git a/lib/src/mind/ai/llm/wingman.h b/lib/src/mind/ai/llm/wingman.h index 5e0f3c8b..40ebfdaa 100644 --- a/lib/src/mind/ai/llm/wingman.h +++ b/lib/src/mind/ai/llm/wingman.h @@ -20,14 +20,52 @@ #define M8R_WINGMAN_H #include +#include #include "../../../debug.h" namespace m8r { +/* + * Predefined LLM prompts. + */ + +constexpr const auto CTX_INCLUDE_NAME = "#NAME"; +constexpr const auto CTX_INCLUDE_TEXT = "#TEXT"; + +constexpr const auto PROMPT_SUMMARIZE = "Summarize: #NAME. #TEXT"; +constexpr const auto PROMPT_GENERATE_TAGS = "Generate tags for: #NAME. #TEXT"; +constexpr const auto PROMPT_FIND_PERSONS = "Find persons names in: #NAME. #TEXT"; +constexpr const auto PROMPT_FIND_LOCATIONS = "Find locations in: #NAME. #TEXT"; +constexpr const auto PROMPT_FIND_ORGS = "Find organizations in: #NAME. #TEXT"; +constexpr const auto PROMPT_CHAT = "Chat with the context."; + +constexpr const auto PROMPT_SHORTEN = "Shorten #TEXT"; +constexpr const auto PROMPT_EXPLAIN_LIKE_5 = "Explain #NAME like I'm 5."; +constexpr const auto PROMPT_FIX_GRAMMAR = "Fix grammar in: #TEXT"; +constexpr const auto PROMPT_REWRITE_FORMALLY = "Rewrite formally: #TEXT"; +constexpr const auto PROMPT_REWRITE_INFORMALLY = "Rewrite informally: #TEXT"; +constexpr const auto PROMPT_REWRITE_KAFKA = "Rewrite in Kafka style: #TEXT"; + +constexpr const auto PROMPT_COMPLETE_TEXT = "Complete the text: #TEXT"; + +// other UCs: +// - NER UCs +// - simplify +// - beautify +// - translate +// - fix spelling +// - fix style +// - create plan ... + +/* + * Wingman providers. + */ + enum WingmanLlmProviders { WINGMAN_PROVIDER_MOCK, WINGMAN_PROVIDER_OPENAI, + WINGMAN_PROVIDER_GOOGLE, }; /** @@ -38,6 +76,37 @@ class Wingman private: WingmanLlmProviders llmProvider; + std::vector outlinePrompts = { + PROMPT_SUMMARIZE, + PROMPT_GENERATE_TAGS, + PROMPT_FIND_PERSONS, + PROMPT_FIND_LOCATIONS, + PROMPT_FIND_ORGS, + // PROMPT_CHAT, + }; + + std::vector notePrompts = { + PROMPT_SUMMARIZE, + PROMPT_SHORTEN, + PROMPT_EXPLAIN_LIKE_5, + PROMPT_GENERATE_TAGS, + PROMPT_FIX_GRAMMAR, + PROMPT_REWRITE_FORMALLY, + PROMPT_REWRITE_INFORMALLY, + PROMPT_REWRITE_KAFKA, + // PROMPT_CHAT, + }; + + std::vector textPrompts = { + PROMPT_COMPLETE_TEXT, + PROMPT_EXPLAIN_LIKE_5, + PROMPT_FIX_GRAMMAR, + PROMPT_GENERATE_TAGS, + PROMPT_REWRITE_FORMALLY, + PROMPT_REWRITE_INFORMALLY, + PROMPT_REWRITE_KAFKA, + }; + public: explicit Wingman(WingmanLlmProviders llmProvider); Wingman(const Wingman&) = delete; @@ -46,6 +115,16 @@ class Wingman Wingman& operator =(const Wingman&&) = delete; virtual ~Wingman(); + virtual const std::vector& getPredefinedOPrompts() { + return outlinePrompts; + } + virtual const std::vector& getPredefinedNPrompts() { + return notePrompts; + } + virtual const std::vector& getPredefinedTPrompts() { + return textPrompts; + } + // dialog || menu Notebook/Wingman/Summarize || menu Note/Wingman/Summarize virtual void summarize(const std::string& text, std::string& summary) = 0; }; diff --git a/lib/src/mind/mind.h b/lib/src/mind/mind.h index 2174ea9a..7e2f6dc9 100644 --- a/lib/src/mind/mind.h +++ b/lib/src/mind/mind.h @@ -675,6 +675,7 @@ class Mind : public OntologyProvider /* * WINGMAN */ + Wingman* getWingman() const { return wingman; } void wingmanSummarize(const std::string& text, std::string& summary); /*