Modify for 1.1
This commit is contained in:
parent
876b72ef6a
commit
f5f20dd0e9
8 changed files with 258 additions and 79 deletions
2
pom.xml
2
pom.xml
|
@ -36,7 +36,7 @@
|
|||
<dependency>
|
||||
<groupId>eu.m724</groupId>
|
||||
<artifactId>chatapi</artifactId>
|
||||
<version>1.0</version>
|
||||
<version>1.1.1</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
|
|
|
@ -1,14 +0,0 @@
|
|||
package eu.m724.chaqt;
|
||||
|
||||
import eu.m724.chatapi.chat.Chat;
|
||||
|
||||
public class ChatWindowViewModel {
|
||||
private ChatWindowWidget widget;
|
||||
|
||||
private Chat chat;
|
||||
|
||||
public ChatWindowViewModel(ChatWindowWidget widget, Chat chat) {
|
||||
this.widget = widget;
|
||||
this.chat = chat;
|
||||
}
|
||||
}
|
|
@ -1,62 +0,0 @@
|
|||
package eu.m724.chaqt;
|
||||
|
||||
import eu.m724.chatapi.chat.Chat;
|
||||
import io.qt.core.Qt;
|
||||
import io.qt.widgets.*;
|
||||
|
||||
public class ChatWindowWidget extends QWidget {
|
||||
private ChatWindowViewModel viewModel;
|
||||
|
||||
private QVBoxLayout layout;
|
||||
private QTextEdit textEdit;
|
||||
|
||||
public ChatWindowWidget(Chat chat) {
|
||||
this.viewModel = new ChatWindowViewModel(this, chat);
|
||||
|
||||
//
|
||||
|
||||
this.layout = new QVBoxLayout();
|
||||
layout.setAlignment(Qt.AlignmentFlag.AlignBottom); // or top?
|
||||
|
||||
QWidget widget = new QWidget();
|
||||
widget.setLayout(layout);
|
||||
|
||||
QScrollArea scrollArea = new QScrollArea();
|
||||
scrollArea.setWidgetResizable(true);
|
||||
scrollArea.setWidget(widget);
|
||||
|
||||
//
|
||||
|
||||
this.textEdit = new QTextEdit();
|
||||
textEdit.setPlaceholderText("Write a message...");
|
||||
textEdit.textChanged.connect(this, "handleComposerType()");
|
||||
|
||||
QPushButton sendButton = new QPushButton("Send");
|
||||
|
||||
QHBoxLayout composerLayout = new QHBoxLayout();
|
||||
composerLayout.addWidget(textEdit);
|
||||
composerLayout.addWidget(sendButton, 0, Qt.AlignmentFlag.AlignBottom);
|
||||
|
||||
QWidget composerWidget = new QWidget();
|
||||
composerWidget.setLayout(composerLayout);
|
||||
|
||||
handleComposerType(); // TODO make it resizable and fix it
|
||||
|
||||
//
|
||||
|
||||
QVBoxLayout mainLayout = new QVBoxLayout();
|
||||
mainLayout.addWidget(scrollArea, 1);
|
||||
mainLayout.addWidget(composerWidget);
|
||||
setLayout(mainLayout);
|
||||
|
||||
for (int i=0; i<10; i++) {
|
||||
QPushButton pushButton = new QPushButton("hello");
|
||||
layout.addWidget(pushButton);
|
||||
}
|
||||
}
|
||||
|
||||
public void handleComposerType() {
|
||||
int height = (int) textEdit.document().size().height();
|
||||
textEdit.setFixedHeight(height + textEdit.contentsMargins().top() + textEdit.contentsMargins().bottom());
|
||||
}
|
||||
}
|
|
@ -1,8 +1,8 @@
|
|||
package eu.m724.chaqt;
|
||||
|
||||
import eu.m724.chaqt.chatwindow.ChatWindowWidget;
|
||||
import eu.m724.chatapi.chat.Chat;
|
||||
import io.qt.widgets.QApplication;
|
||||
import io.qt.widgets.QMessageBox;
|
||||
|
||||
public class Main {
|
||||
|
||||
|
@ -11,9 +11,10 @@ public class Main {
|
|||
|
||||
Chat chat = new Chat();
|
||||
|
||||
ChatWindowWidget widget = new ChatWindowWidget(chat);
|
||||
ChatWindowWidget widget = new ChatWindowWidget();
|
||||
widget.show();
|
||||
|
||||
QApplication.exec();
|
||||
QApplication.shutdown();
|
||||
}
|
||||
}
|
22
src/main/java/eu/m724/chaqt/ObservableChat.java
Normal file
22
src/main/java/eu/m724/chaqt/ObservableChat.java
Normal file
|
@ -0,0 +1,22 @@
|
|||
package eu.m724.chaqt;
|
||||
|
||||
import eu.m724.chatapi.chat.Chat;
|
||||
import eu.m724.chatapi.chat.ChatMessage;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public class ObservableChat extends Chat {
|
||||
private Set<Consumer<ChatMessage>> watchers = new HashSet<>();
|
||||
|
||||
@Override
|
||||
public void addMessage(ChatMessage message) {
|
||||
super.addMessage(message);
|
||||
watchers.forEach(c -> c.accept(message));
|
||||
}
|
||||
|
||||
public void watch(Consumer<ChatMessage> callable) {
|
||||
this.watchers.add(callable);
|
||||
}
|
||||
}
|
30
src/main/java/eu/m724/chaqt/QMessage.java
Normal file
30
src/main/java/eu/m724/chaqt/QMessage.java
Normal file
|
@ -0,0 +1,30 @@
|
|||
package eu.m724.chaqt;
|
||||
|
||||
import io.qt.core.QMargins;
|
||||
import io.qt.gui.QPalette;
|
||||
import io.qt.widgets.QLabel;
|
||||
import io.qt.widgets.QVBoxLayout;
|
||||
import io.qt.widgets.QWidget;
|
||||
|
||||
// TODO I think we could put Qtextdocument here
|
||||
public class QMessage extends QWidget {
|
||||
private final QLabel label;
|
||||
|
||||
public QMessage(boolean assistant, String text) {
|
||||
this.label = new QLabel(text);
|
||||
label.setWordWrap(true);
|
||||
|
||||
QVBoxLayout layout = new QVBoxLayout();
|
||||
layout.addWidget(label);
|
||||
|
||||
if (assistant) {
|
||||
layout.setContentsMargins(5,0,0, 0);
|
||||
}
|
||||
|
||||
setLayout(layout);
|
||||
}
|
||||
|
||||
public void addText(String text) {
|
||||
label.setText(label.text() + text);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,69 @@
|
|||
package eu.m724.chaqt.chatwindow;
|
||||
|
||||
import eu.m724.chatapi.chat.Chat;
|
||||
import eu.m724.chatapi.chat.ChatEvent;
|
||||
import eu.m724.chatapi.chat.ChatMessage;
|
||||
import eu.m724.chatapi.example.ExampleSource;
|
||||
import eu.m724.chatapi.source.ChatResponse;
|
||||
import eu.m724.chatapi.source.ChatSource;
|
||||
import io.qt.core.QObject.Signal1;
|
||||
import io.qt.core.QThread;
|
||||
|
||||
public class ChatWindowViewModel {
|
||||
private final ChatWindowWidget widget;
|
||||
private final Signal1<ChatMessage> chatMessageSignal;
|
||||
private final Signal1<ChatEvent> chatEventSignal;
|
||||
|
||||
private Chat chat = new Chat();
|
||||
private ChatSource source = new ExampleSource(); // TODO
|
||||
|
||||
|
||||
public ChatWindowViewModel(ChatWindowWidget widget, Signal1<ChatEvent> chatEventSignal, Signal1<ChatMessage> chatMessageSignal) {
|
||||
this.widget = widget;
|
||||
this.chatEventSignal = chatEventSignal;
|
||||
this.chatMessageSignal = chatMessageSignal;
|
||||
}
|
||||
|
||||
|
||||
void send(String text) {
|
||||
ChatMessage promptMessage = new ChatMessage(false, text);
|
||||
chatMessageSignal.emit(promptMessage);
|
||||
chat.addMessage(new ChatMessage(false, text));
|
||||
|
||||
ChatResponse response = source.ask(chat);
|
||||
ChatMessage message = response.message();
|
||||
|
||||
chatMessageSignal.emit(message);
|
||||
message.addEventConsumer(chatEventSignal::emit, true);
|
||||
|
||||
/*QThread thread = new QThread() {
|
||||
@Override
|
||||
protected void run() {
|
||||
ChatEvent token;
|
||||
do {
|
||||
try {
|
||||
token = response.eventQueue().take();
|
||||
} catch (InterruptedException e) {
|
||||
throw new RuntimeException(e); // TODO
|
||||
}
|
||||
|
||||
chatEventSignal.emit(token);
|
||||
|
||||
if (token.finishReason() != null) {
|
||||
chatMessageSignal.emit(response.message().join()); // TODO ???
|
||||
}
|
||||
/*if (!"error".equals(token.finishReason())) {
|
||||
if (token.text() != null) {
|
||||
chatEventSignal.emit(token.text());
|
||||
}
|
||||
} else {
|
||||
System.out.print("Error: " + token.error().toString());
|
||||
}/
|
||||
} while (token.finishReason() == null);
|
||||
// QApplication.invokeLater(() -> resultField.setText(result));
|
||||
}
|
||||
};
|
||||
thread.start();*/
|
||||
//
|
||||
}
|
||||
}
|
133
src/main/java/eu/m724/chaqt/chatwindow/ChatWindowWidget.java
Normal file
133
src/main/java/eu/m724/chaqt/chatwindow/ChatWindowWidget.java
Normal file
|
@ -0,0 +1,133 @@
|
|||
package eu.m724.chaqt.chatwindow;
|
||||
|
||||
import eu.m724.chaqt.QMessage;
|
||||
import eu.m724.chatapi.chat.ChatEvent;
|
||||
import eu.m724.chatapi.chat.ChatMessage;
|
||||
import io.qt.core.Qt;
|
||||
import io.qt.widgets.*;
|
||||
|
||||
public class ChatWindowWidget extends QWidget {
|
||||
private ChatWindowViewModel viewModel;
|
||||
|
||||
private QVBoxLayout layout;
|
||||
private QTextEdit textEdit;
|
||||
|
||||
// TODO are both really needed
|
||||
private final Signal1<ChatMessage> chatMessageSignal = new Signal1<>();
|
||||
private final Signal1<ChatEvent> chatEventSignal = new Signal1<>();
|
||||
|
||||
private QMessage latestMessage;
|
||||
|
||||
public ChatWindowWidget() {
|
||||
this.viewModel = new ChatWindowViewModel(this, chatEventSignal, chatMessageSignal);
|
||||
|
||||
//
|
||||
|
||||
chatEventSignal.connect(this::handleChatEvent);
|
||||
chatMessageSignal.connect(this::handleChatMessage);
|
||||
|
||||
//
|
||||
|
||||
this.layout = new QVBoxLayout();
|
||||
layout.setAlignment(Qt.AlignmentFlag.AlignBottom); // or top?
|
||||
|
||||
QWidget widget = new QWidget();
|
||||
widget.setLayout(layout);
|
||||
|
||||
QScrollArea scrollArea = new QScrollArea();
|
||||
scrollArea.setHorizontalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOff);
|
||||
scrollArea.setWidgetResizable(true);
|
||||
scrollArea.setWidget(widget);
|
||||
|
||||
//
|
||||
|
||||
this.textEdit = new QTextEdit();
|
||||
textEdit.setPlaceholderText("Write a message...");
|
||||
textEdit.textChanged.connect(this, "handleComposerType()");
|
||||
|
||||
QPushButton sendButton = new QPushButton("Send");
|
||||
sendButton.clicked.connect(this::onSend);
|
||||
|
||||
QHBoxLayout composerLayout = new QHBoxLayout();
|
||||
composerLayout.addWidget(textEdit);
|
||||
composerLayout.addWidget(sendButton, 0, Qt.AlignmentFlag.AlignBottom);
|
||||
|
||||
QWidget composerWidget = new QWidget();
|
||||
composerWidget.setLayout(composerLayout);
|
||||
|
||||
handleComposerType(); // TODO make it resizable and fix it
|
||||
|
||||
//
|
||||
|
||||
QVBoxLayout mainLayout = new QVBoxLayout();
|
||||
mainLayout.addWidget(scrollArea, 1);
|
||||
mainLayout.addWidget(composerWidget);
|
||||
setLayout(mainLayout);
|
||||
|
||||
/*for (int i=0; i<10; i++) {
|
||||
QPushButton pushButton = new QPushButton("hello");
|
||||
layout.addWidget(pushButton);
|
||||
}*/
|
||||
|
||||
textEdit.setFocus();
|
||||
}
|
||||
|
||||
public void handleComposerType() {
|
||||
int height = (int) textEdit.document().size().height();
|
||||
textEdit.setFixedHeight(height + textEdit.contentsMargins().top() + textEdit.contentsMargins().bottom());
|
||||
}
|
||||
|
||||
public void onSend() {
|
||||
String prompt = textEdit.getPlainText();
|
||||
if (!prompt.isBlank()) {
|
||||
textEdit.setText("");
|
||||
viewModel.send(prompt);
|
||||
|
||||
textEdit.setEnabled(false);
|
||||
textEdit.setPlaceholderText("Asking...");
|
||||
}
|
||||
}
|
||||
|
||||
private void handleChatMessage(ChatMessage message) {
|
||||
System.out.printf("Widget received a message. Is response: %b\n", message.response());
|
||||
|
||||
QMessage qMessage = new QMessage(false, message.content());
|
||||
layout.addWidget(qMessage);
|
||||
|
||||
// if the message is by assistant, disable composer.
|
||||
// I don't know if this is good, but it works
|
||||
if (message.response()) {
|
||||
textEdit.setEnabled(false);
|
||||
textEdit.setPlaceholderText("Thinking...");
|
||||
}
|
||||
}
|
||||
|
||||
private void handleChatEvent(ChatEvent chatEvent) {
|
||||
String token = chatEvent.text();
|
||||
|
||||
// usually when it's the first token
|
||||
if (latestMessage == null) {
|
||||
latestMessage = new QMessage(true, "");
|
||||
layout.addWidget(latestMessage);
|
||||
|
||||
textEdit.setPlaceholderText("Responding...");
|
||||
}
|
||||
|
||||
if (token != null) {
|
||||
latestMessage.addText(token);
|
||||
}
|
||||
|
||||
// when finished
|
||||
if (chatEvent.finishReason() != null) {
|
||||
// to make sure we won't be streaming to the same message
|
||||
latestMessage = null;
|
||||
|
||||
// enable composer
|
||||
textEdit.setEnabled(true);
|
||||
textEdit.setPlaceholderText("Write a message...");
|
||||
textEdit.setFocus();
|
||||
}
|
||||
|
||||
System.out.printf("Event: %s %s\n", chatEvent.finishReason(), chatEvent.text());
|
||||
} // TODO that's messy so do something about that
|
||||
}
|
Loading…
Reference in a new issue