Skip to content

Commit 81eeb94

Browse files
committed
Adding chat dialog for wingman #1514
1 parent 2c06d2a commit 81eeb94

File tree

7 files changed

+291
-1
lines changed

7 files changed

+291
-1
lines changed

app/app.pro

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -276,6 +276,7 @@ HEADERS += \
276276
src/qt/dialogs/rm_library_dialog.h \
277277
src/qt/dialogs/run_tool_dialog.h \
278278
src/qt/dialogs/wingman_dialog.h \
279+
src/qt/dialogs/chat_dialog.h \
279280
src/qt/dialogs/sync_library_dialog.h \
280281
src/qt/dialogs/terminal_dialog.h \
281282
src/qt/kanban_column_model.h \
@@ -398,6 +399,7 @@ SOURCES += \
398399
src/qt/dialogs/rm_library_dialog.cpp \
399400
src/qt/dialogs/run_tool_dialog.cpp \
400401
src/qt/dialogs/wingman_dialog.cpp \
402+
src/qt/dialogs/chat_dialog.cpp \
401403
src/qt/dialogs/sync_library_dialog.cpp \
402404
src/qt/dialogs/terminal_dialog.cpp \
403405
src/qt/kanban_column_model.cpp \

app/src/qt/dialogs/chat_dialog.cpp

Lines changed: 191 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,191 @@
1+
/*
2+
chat_dialog.cpp MindForger thinking notebook
3+
4+
Copyright (C) 2016-2024 Martin Dvorak <[email protected]>
5+
6+
This program is free software; you can redistribute it and/or
7+
modify it under the terms of the GNU General Public License
8+
as published by the Free Software Foundation; either version 2
9+
of the License, or (at your option) any later version.
10+
11+
This program is distributed in the hope that it will be useful,
12+
but WITHOUT ANY WARRANTY; without even the implied warranty of
13+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+
GNU General Public License for more details.
15+
16+
You should have received a copy of the GNU General Public License
17+
along with this program. If not, see <http://www.gnu.org/licenses/>.
18+
*/
19+
#include "chat_dialog.h"
20+
21+
namespace m8r {
22+
23+
using namespace std;
24+
25+
ChatDialog::ChatDialog(QWidget* parent)
26+
: QDialog(parent)
27+
{
28+
setWindowTitle(tr("Wingman Chat"));
29+
30+
cmdEdit = new MyLineEdit(this, this);
31+
32+
cmdCompleter = new QCompleter(new QStandardItemModel(cmdEdit), this);
33+
cmdCompleter->setCompletionMode(QCompleter::CompletionMode::InlineCompletion);
34+
cmdCompleter->setCaseSensitivity(Qt::CaseInsensitive);
35+
QStandardItemModel* cmdCompleterModel =
36+
dynamic_cast<QStandardItemModel*>(cmdCompleter->model());
37+
if(cmdCompleterModel) {
38+
// prompts
39+
completerCommands.clear();
40+
completerCommands << QString::fromStdString("Explain like I'm five: ");
41+
completerCommands << QString::fromStdString("Create plan in bullet list for ");
42+
43+
completerCommands << QString::fromStdString("exit");
44+
completerCommands << QString::fromStdString("quit");
45+
completerCommands << QString::fromStdString("bye");
46+
47+
for(auto c:completerCommands) {
48+
cmdCompleterModel->appendRow(new QStandardItem(c));
49+
}
50+
}
51+
52+
cmdEdit->setCompleter(cmdCompleter);
53+
54+
chatWindow = new QTextEdit(this);
55+
chatWindow->setReadOnly(true);
56+
chatWindow->clear();
57+
chatWindow->insertHtml(QString::fromStdString(getPrompt()));
58+
59+
QVBoxLayout* layout = new QVBoxLayout{this};
60+
layout->addWidget(chatWindow);
61+
layout->addWidget(cmdEdit);
62+
setLayout(layout);
63+
64+
resize(fontMetrics().averageCharWidth()*100, fontMetrics().height()*35);
65+
}
66+
67+
ChatDialog::~ChatDialog()
68+
{
69+
}
70+
71+
void ChatDialog::show()
72+
{
73+
// > Summarize. [green]
74+
// 🤖 Lorem ipsum dolor sit amet, [gray]
75+
// consectetur adipiscing elit.
76+
//
77+
// Explain like I'm 5: NLP.
78+
// 🤖 Lorem ipsum dolor sit amet,
79+
//
80+
// >
81+
// ^ cursor
82+
83+
cmdEdit->clear();
84+
cmdEdit->setFocus();
85+
86+
setModal(true);
87+
QDialog::show();
88+
}
89+
90+
string ChatDialog::getPrompt(bool error)
91+
{
92+
string prompt{"<font color='#0000ff'>" + getCwd() + "</font>"};
93+
prompt.append("<br/><font color='");
94+
if(error) {
95+
prompt.append("#ff0000");
96+
} else {
97+
prompt.append("#00ff00");
98+
}
99+
prompt.append("'>&gt;</font> ");
100+
return prompt;
101+
}
102+
103+
void ChatDialog::insertPrompt(const std::string& prompt)
104+
{
105+
chatWindow->insertHtml(
106+
QString::fromStdString(
107+
"<font color='#00bb00'>"
108+
"&gt; " + prompt +
109+
"</font>"
110+
));
111+
chatWindow->insertHtml(QString::fromStdString("<br/><br/>"));
112+
}
113+
114+
void ChatDialog::insertOutput(const std::string& output)
115+
{
116+
chatWindow->insertHtml(QString::fromStdString("🤖 " + output));
117+
chatWindow->insertHtml(QString::fromStdString("<br/><br/>"));
118+
}
119+
120+
void ChatDialog::runCommand()
121+
{
122+
if(cmdEdit->text().size()) {
123+
// TODO help
124+
if(cmdEdit->text() == QString::fromStdString("clear")
125+
|| cmdEdit->text() == QString::fromStdString("cls")
126+
) {
127+
chatWindow->clear();
128+
chatWindow->insertHtml(QString::fromStdString(getPrompt()));
129+
} else if(cmdEdit->text() == QString::fromStdString("exit")
130+
|| cmdEdit->text() == QString::fromStdString("quit")
131+
|| cmdEdit->text() == QString::fromStdString("bye")
132+
) {
133+
QDialog::close();
134+
} else {
135+
chatWindow->insertHtml(
136+
cmdEdit->text() + QString::fromStdString("<br/><br/>")
137+
);
138+
139+
string cmd{cmdEdit->text().toStdString()};
140+
141+
// add command to completer
142+
QStandardItemModel* completerModel
143+
= dynamic_cast<QStandardItemModel*>(cmdCompleter->model());
144+
if(!completerModel) {
145+
completerModel = new QStandardItemModel();
146+
}
147+
completerModel->insertRow(
148+
0, new QStandardItem(cmdEdit->text())
149+
);
150+
151+
// run prompt
152+
MF_DEBUG("Running prompt: '" << cmd << "'" << endl);
153+
int statusCode{0};
154+
string cmdStdOut{};
155+
156+
// TODO run prompt
157+
// TODO run prompt
158+
// TODO run prompt
159+
160+
MF_DEBUG("Chat command finished with status: " << statusCode << endl);
161+
chatWindow->insertHtml(QString::fromStdString("<br/>"));
162+
163+
if(cmdStdOut.size()) {
164+
replaceAll("\n", "<br/>", cmdStdOut);
165+
chatWindow->insertHtml(
166+
QString::fromStdString("🤖 " + cmdStdOut + "<br/>")
167+
);
168+
}
169+
170+
if(statusCode) {
171+
cerr << "Chat command failed with status: " << statusCode << endl;
172+
chatWindow->insertHtml(
173+
QString::fromStdString(getPrompt(true))
174+
);
175+
} else {
176+
chatWindow->insertHtml(
177+
QString::fromStdString(getPrompt())
178+
);
179+
}
180+
181+
// scroll down by moving cursor to the end AND ensuring it's visible
182+
chatWindow->moveCursor(QTextCursor::End);
183+
chatWindow->ensureCursorVisible();
184+
}
185+
}
186+
187+
cmdEdit->clear();
188+
MF_DEBUG("Chat prompt cleared: " << cmdEdit->text().toStdString() << endl);
189+
}
190+
191+
} // m8r namespace

app/src/qt/dialogs/chat_dialog.h

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
/*
2+
chat_dialog.h MindForger thinking notebook
3+
4+
Copyright (C) 2016-2024 Martin Dvorak <[email protected]>
5+
6+
This program is free software; you can redistribute it and/or
7+
modify it under the terms of the GNU General Public License
8+
as published by the Free Software Foundation; either version 2
9+
of the License, or (at your option) any later version.
10+
11+
This program is distributed in the hope that it will be useful,
12+
but WITHOUT ANY WARRANTY; without even the implied warranty of
13+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+
GNU General Public License for more details.
15+
16+
You should have received a copy of the GNU General Public License
17+
along with this program. If not, see <http://www.gnu.org/licenses/>.
18+
*/
19+
#ifndef M8RUI_CHAT_DIALOG_H
20+
#define M8RUI_CHAT_DIALOG_H
21+
22+
23+
#include <iostream>
24+
#include <stdexcept>
25+
#include <stdio.h>
26+
#include <string>
27+
28+
#include "../../lib/src/debug.h"
29+
#include "../../lib/src/gear/file_utils.h"
30+
#include "../../lib/src/gear/string_utils.h"
31+
32+
#include <QtWidgets>
33+
34+
namespace m8r {
35+
36+
class ChatDialog : public QDialog
37+
{
38+
Q_OBJECT
39+
40+
class MyLineEdit : public QLineEdit
41+
{
42+
private:
43+
ChatDialog* chatDialog;
44+
public:
45+
explicit MyLineEdit(ChatDialog* chatDialog, QWidget* parent)
46+
: QLineEdit(parent),
47+
chatDialog(chatDialog)
48+
{}
49+
virtual void keyPressEvent(QKeyEvent* event) override {
50+
switch(event->key()) {
51+
case Qt::Key_Return: // Qt::Key_Enter is keypad Enter
52+
chatDialog->runCommand();
53+
setFocus();
54+
return;
55+
}
56+
57+
QLineEdit::keyPressEvent(event);; // continue event dispatch (completer needs to get the event)
58+
}
59+
};
60+
61+
private:
62+
MyLineEdit* cmdEdit;
63+
QCompleter* cmdCompleter;
64+
QStringList completerCommands;
65+
66+
QTextEdit* chatWindow;
67+
68+
public:
69+
explicit ChatDialog(QWidget* parent);
70+
ChatDialog(const ChatDialog&) = delete;
71+
ChatDialog(const ChatDialog&&) = delete;
72+
ChatDialog& operator =(const ChatDialog&) = delete;
73+
ChatDialog& operator =(const ChatDialog&&) = delete;
74+
~ChatDialog();
75+
76+
void insertPrompt(const std::string& prompt);
77+
void insertOutput(const std::string& output);
78+
79+
void show();
80+
81+
private:
82+
void runCommand();
83+
std::string getPrompt(bool error=false);
84+
};
85+
86+
}
87+
#endif // M8RUI_TERMINAL_DIALOG_H

app/src/qt/dialogs/terminal_dialog.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,7 @@ void TerminalDialog::runCommand()
164164
// TODO _popen WINDOWS
165165
#ifdef _WIN32
166166
FILE* pipe = _popen(cmd.c_str(), "r");
167-
# else
167+
#else
168168
FILE* pipe = popen(cmd.c_str(), "r");
169169
#endif
170170
if(!pipe) {

app/src/qt/main_window_presenter.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ MainWindowPresenter::MainWindowPresenter(MainWindowView& view)
5858
rmLibraryDialog = new RemoveLibraryDialog(&view);
5959
runToolDialog = new RunToolDialog{&view};
6060
wingmanDialog = new WingmanDialog{&view};
61+
chatDialog = new ChatDialog{&view};
6162
scopeDialog = new ScopeDialog{mind->getOntology(), &view};
6263
newOrganizerDialog = new OrganizerNewDialog{mind->getOntology(), &view};
6364
newOutlineDialog = new OutlineNewDialog{
@@ -2105,6 +2106,7 @@ void MainWindowPresenter::doActionWingman()
21052106
void MainWindowPresenter::handleActionWingman()
21062107
{
21072108
MF_DEBUG("SIGNAL handled: WINGMAN dialog..." << endl);
2109+
this->wingmanDialog->hide();
21082110

21092111
string wingmanAnswer{};
21102112

@@ -2116,6 +2118,11 @@ void MainWindowPresenter::handleActionWingman()
21162118
wingmanAnswer
21172119
);
21182120

2121+
// show result
2122+
this->chatDialog->insertPrompt("Summarize.");
2123+
this->chatDialog->insertOutput(wingmanAnswer);
2124+
this->chatDialog->show();
2125+
21192126
// TODO TODO TODO continue here
21202127
}
21212128

app/src/qt/main_window_presenter.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
#include "dialogs/rm_library_dialog.h"
4040
#include "dialogs/run_tool_dialog.h"
4141
#include "dialogs/wingman_dialog.h"
42+
#include "dialogs/chat_dialog.h"
4243
#include "dialogs/organizer_new_dialog.h"
4344
#include "dialogs/outline_new_dialog.h"
4445
#include "dialogs/note_new_dialog.h"
@@ -120,6 +121,7 @@ class MainWindowPresenter : public QObject
120121
RemoveLibraryDialog* rmLibraryDialog;
121122
RunToolDialog* runToolDialog;
122123
WingmanDialog* wingmanDialog;
124+
ChatDialog* chatDialog;
123125
ScopeDialog* scopeDialog;
124126
OrganizerNewDialog* newOrganizerDialog;
125127
OutlineNewDialog* newOutlineDialog;

lib/src/mind/ai/llm/wingman.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ class Wingman
4646
Wingman& operator =(const Wingman&&) = delete;
4747
virtual ~Wingman();
4848

49+
// dialog || menu Notebook/Wingman/Summarize || menu Note/Wingman/Summarize
4950
virtual void summarize(const std::string& text, std::string& summary) = 0;
5051
};
5152

0 commit comments

Comments
 (0)