mirror of
https://github.com/nomic-ai/gpt4all.git
synced 2025-09-07 19:40:21 +00:00
chat: generate follow-up questions after response (#2634)
* user can configure the prompt and when they appear * also make the name generation prompt configurable Signed-off-by: Adam Treat <treat.adam@gmail.com> Signed-off-by: Jared Van Bortel <jared@nomic.ai> Co-authored-by: Jared Van Bortel <jared@nomic.ai>
This commit is contained in:
@@ -227,16 +227,40 @@ MySettingsTab {
|
||||
MySettings.userDefaultModel = comboBox.currentText
|
||||
}
|
||||
}
|
||||
MySettingsLabel {
|
||||
id: suggestionModeLabel
|
||||
text: qsTr("Suggestion Mode")
|
||||
helpText: qsTr("Generate suggested follow-up questions at the end of responses.")
|
||||
Layout.row: 6
|
||||
Layout.column: 0
|
||||
}
|
||||
MyComboBox {
|
||||
id: suggestionModeBox
|
||||
Layout.row: 6
|
||||
Layout.column: 2
|
||||
Layout.minimumWidth: 400
|
||||
Layout.maximumWidth: 400
|
||||
Layout.alignment: Qt.AlignRight
|
||||
model: [ qsTr("When chatting with LocalDocs"), qsTr("Whenever possible"), qsTr("Never") ]
|
||||
Accessible.name: suggestionModeLabel.text
|
||||
Accessible.description: suggestionModeLabel.helpText
|
||||
onActivated: {
|
||||
MySettings.suggestionMode = suggestionModeBox.currentIndex;
|
||||
}
|
||||
Component.onCompleted: {
|
||||
suggestionModeBox.currentIndex = MySettings.suggestionMode;
|
||||
}
|
||||
}
|
||||
MySettingsLabel {
|
||||
id: modelPathLabel
|
||||
text: qsTr("Download Path")
|
||||
helpText: qsTr("Where to store local models and the LocalDocs database.")
|
||||
Layout.row: 6
|
||||
Layout.row: 7
|
||||
Layout.column: 0
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
Layout.row: 6
|
||||
Layout.row: 7
|
||||
Layout.column: 2
|
||||
Layout.alignment: Qt.AlignRight
|
||||
Layout.minimumWidth: 400
|
||||
@@ -273,12 +297,12 @@ MySettingsTab {
|
||||
id: dataLakeLabel
|
||||
text: qsTr("Enable Datalake")
|
||||
helpText: qsTr("Send chats and feedback to the GPT4All Open-Source Datalake.")
|
||||
Layout.row: 7
|
||||
Layout.row: 8
|
||||
Layout.column: 0
|
||||
}
|
||||
MyCheckBox {
|
||||
id: dataLakeBox
|
||||
Layout.row: 7
|
||||
Layout.row: 8
|
||||
Layout.column: 2
|
||||
Layout.alignment: Qt.AlignRight
|
||||
Component.onCompleted: { dataLakeBox.checked = MySettings.networkIsActive; }
|
||||
@@ -296,7 +320,7 @@ MySettingsTab {
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
Layout.row: 8
|
||||
Layout.row: 9
|
||||
Layout.column: 0
|
||||
Layout.columnSpan: 3
|
||||
Layout.fillWidth: true
|
||||
@@ -319,7 +343,7 @@ MySettingsTab {
|
||||
id: nThreadsLabel
|
||||
text: qsTr("CPU Threads")
|
||||
helpText: qsTr("The number of CPU threads used for inference and embedding.")
|
||||
Layout.row: 9
|
||||
Layout.row: 10
|
||||
Layout.column: 0
|
||||
}
|
||||
MyTextField {
|
||||
@@ -327,7 +351,7 @@ MySettingsTab {
|
||||
color: theme.textColor
|
||||
font.pixelSize: theme.fontSizeLarge
|
||||
Layout.alignment: Qt.AlignRight
|
||||
Layout.row: 9
|
||||
Layout.row: 10
|
||||
Layout.column: 2
|
||||
Layout.minimumWidth: 200
|
||||
Layout.maximumWidth: 200
|
||||
@@ -351,12 +375,12 @@ MySettingsTab {
|
||||
id: saveChatsContextLabel
|
||||
text: qsTr("Save Chat Context")
|
||||
helpText: qsTr("Save the chat model's state to disk for faster loading. WARNING: Uses ~2GB per chat.")
|
||||
Layout.row: 10
|
||||
Layout.row: 11
|
||||
Layout.column: 0
|
||||
}
|
||||
MyCheckBox {
|
||||
id: saveChatsContextBox
|
||||
Layout.row: 10
|
||||
Layout.row: 11
|
||||
Layout.column: 2
|
||||
Layout.alignment: Qt.AlignRight
|
||||
checked: MySettings.saveChatsContext
|
||||
@@ -368,12 +392,12 @@ MySettingsTab {
|
||||
id: serverChatLabel
|
||||
text: qsTr("Enable Local Server")
|
||||
helpText: qsTr("Expose an OpenAI-Compatible server to localhost. WARNING: Results in increased resource usage.")
|
||||
Layout.row: 11
|
||||
Layout.row: 12
|
||||
Layout.column: 0
|
||||
}
|
||||
MyCheckBox {
|
||||
id: serverChatBox
|
||||
Layout.row: 11
|
||||
Layout.row: 12
|
||||
Layout.column: 2
|
||||
Layout.alignment: Qt.AlignRight
|
||||
checked: MySettings.serverChat
|
||||
@@ -385,7 +409,7 @@ MySettingsTab {
|
||||
id: serverPortLabel
|
||||
text: qsTr("API Server Port")
|
||||
helpText: qsTr("The port to use for the local server. Requires restart.")
|
||||
Layout.row: 12
|
||||
Layout.row: 13
|
||||
Layout.column: 0
|
||||
}
|
||||
MyTextField {
|
||||
@@ -393,7 +417,7 @@ MySettingsTab {
|
||||
text: MySettings.networkPort
|
||||
color: theme.textColor
|
||||
font.pixelSize: theme.fontSizeLarge
|
||||
Layout.row: 12
|
||||
Layout.row: 13
|
||||
Layout.column: 2
|
||||
Layout.minimumWidth: 200
|
||||
Layout.maximumWidth: 200
|
||||
|
@@ -797,7 +797,7 @@ Rectangle {
|
||||
|
||||
delegate: GridLayout {
|
||||
width: listView.contentItem.width - 15
|
||||
rows: 3
|
||||
rows: 5
|
||||
columns: 2
|
||||
|
||||
Item {
|
||||
@@ -850,6 +850,8 @@ Rectangle {
|
||||
font.pixelSize: theme.fontSizeLarger
|
||||
font.bold: true
|
||||
color: theme.conversationHeader
|
||||
enabled: false
|
||||
focus: false
|
||||
readOnly: true
|
||||
}
|
||||
Text {
|
||||
@@ -872,6 +874,7 @@ Rectangle {
|
||||
case Chat.LocalDocsProcessing: return qsTr("searching localdocs: ") + currentChat.collectionList.join(", ") + " ...";
|
||||
case Chat.PromptProcessing: return qsTr("processing ...")
|
||||
case Chat.ResponseGeneration: return qsTr("generating response ...");
|
||||
case Chat.GeneratingQuestions: return qsTr("generating questions ...");
|
||||
default: return ""; // handle unexpected values
|
||||
}
|
||||
}
|
||||
@@ -1094,7 +1097,16 @@ Rectangle {
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
Layout.preferredWidth: childrenRect.width
|
||||
Layout.preferredHeight: childrenRect.height
|
||||
visible: consolidatedSources.length !== 0 && MySettings.localDocsShowReferences && (!currentResponse || !currentChat.responseInProgress)
|
||||
visible: {
|
||||
if (consolidatedSources.length === 0)
|
||||
return false
|
||||
if (!MySettings.localDocsShowReferences)
|
||||
return false
|
||||
if (currentResponse && currentChat.responseInProgress
|
||||
&& currentChat.responseState !== Chat.GeneratingQuestions )
|
||||
return false
|
||||
return true
|
||||
}
|
||||
|
||||
MyButton {
|
||||
backgroundColor: theme.sourcesBackground
|
||||
@@ -1171,7 +1183,16 @@ Rectangle {
|
||||
Layout.row: 3
|
||||
Layout.column: 1
|
||||
Layout.topMargin: 5
|
||||
visible: consolidatedSources.length !== 0 && MySettings.localDocsShowReferences && (!currentResponse || !currentChat.responseInProgress)
|
||||
visible: {
|
||||
if (consolidatedSources.length === 0)
|
||||
return false
|
||||
if (!MySettings.localDocsShowReferences)
|
||||
return false
|
||||
if (currentResponse && currentChat.responseInProgress
|
||||
&& currentChat.responseState !== Chat.GeneratingQuestions )
|
||||
return false
|
||||
return true
|
||||
}
|
||||
clip: true
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: 0
|
||||
@@ -1310,45 +1331,250 @@ Rectangle {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
property bool shouldAutoScroll: true
|
||||
property bool isAutoScrolling: false
|
||||
function shouldShowSuggestions() {
|
||||
if (!currentResponse)
|
||||
return false;
|
||||
if (MySettings.suggestionMode === 2) // Off
|
||||
return false;
|
||||
if (MySettings.suggestionMode === 0 && consolidatedSources.length === 0) // LocalDocs only
|
||||
return false;
|
||||
return currentChat.responseState === Chat.GeneratingQuestions || currentChat.generatedQuestions.length !== 0;
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: currentChat
|
||||
function onResponseChanged() {
|
||||
listView.scrollToEnd()
|
||||
Item {
|
||||
visible: shouldShowSuggestions()
|
||||
Layout.row: 4
|
||||
Layout.column: 0
|
||||
Layout.topMargin: 20
|
||||
Layout.alignment: Qt.AlignVCenter | Qt.AlignRight
|
||||
Layout.preferredWidth: 28
|
||||
Layout.preferredHeight: 28
|
||||
Image {
|
||||
id: stack
|
||||
sourceSize: Qt.size(28, 28)
|
||||
fillMode: Image.PreserveAspectFit
|
||||
mipmap: true
|
||||
visible: false
|
||||
source: "qrc:/gpt4all/icons/stack.svg"
|
||||
}
|
||||
|
||||
ColorOverlay {
|
||||
anchors.fill: stack
|
||||
source: stack
|
||||
color: theme.conversationHeader
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
visible: shouldShowSuggestions()
|
||||
Layout.row: 4
|
||||
Layout.column: 1
|
||||
Layout.topMargin: 20
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: 38
|
||||
RowLayout {
|
||||
spacing: 5
|
||||
anchors.left: parent.left
|
||||
anchors.top: parent.top
|
||||
anchors.bottom: parent.bottom
|
||||
|
||||
TextArea {
|
||||
text: qsTr("Suggested follow-ups")
|
||||
padding: 0
|
||||
font.pixelSize: theme.fontSizeLarger
|
||||
font.bold: true
|
||||
color: theme.conversationHeader
|
||||
enabled: false
|
||||
focus: false
|
||||
readOnly: true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
visible: shouldShowSuggestions()
|
||||
Layout.row: 5
|
||||
Layout.column: 1
|
||||
Layout.fillWidth: true
|
||||
Layout.minimumHeight: 1
|
||||
spacing: 10
|
||||
Repeater {
|
||||
model: currentChat.generatedQuestions
|
||||
TextArea {
|
||||
id: followUpText
|
||||
Layout.fillWidth: true
|
||||
Layout.alignment: Qt.AlignLeft
|
||||
rightPadding: 40
|
||||
topPadding: 10
|
||||
leftPadding: 20
|
||||
bottomPadding: 10
|
||||
text: modelData
|
||||
focus: false
|
||||
readOnly: true
|
||||
wrapMode: Text.WordWrap
|
||||
hoverEnabled: !currentChat.responseInProgress
|
||||
color: theme.textColor
|
||||
font.pixelSize: theme.fontSizeLarge
|
||||
background: Rectangle {
|
||||
color: hovered ? theme.sourcesBackgroundHovered : theme.sourcesBackground
|
||||
radius: 10
|
||||
}
|
||||
MouseArea {
|
||||
id: maFollowUp
|
||||
anchors.fill: parent
|
||||
enabled: !currentChat.responseInProgress
|
||||
onClicked: function() {
|
||||
var chat = window.currentChat
|
||||
var followup = modelData
|
||||
chat.stopGenerating()
|
||||
chat.newPromptResponsePair(followup);
|
||||
chat.prompt(followup,
|
||||
MySettings.promptTemplate,
|
||||
MySettings.maxLength,
|
||||
MySettings.topK,
|
||||
MySettings.topP,
|
||||
MySettings.minP,
|
||||
MySettings.temperature,
|
||||
MySettings.promptBatchSize,
|
||||
MySettings.repeatPenalty,
|
||||
MySettings.repeatPenaltyTokens)
|
||||
}
|
||||
}
|
||||
Item {
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
width: 40
|
||||
height: 40
|
||||
visible: !currentChat.responseInProgress
|
||||
Image {
|
||||
id: plusImage
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
sourceSize.width: 20
|
||||
sourceSize.height: 20
|
||||
mipmap: true
|
||||
visible: false
|
||||
source: "qrc:/gpt4all/icons/plus.svg"
|
||||
}
|
||||
|
||||
ColorOverlay {
|
||||
anchors.fill: plusImage
|
||||
source: plusImage
|
||||
color: theme.styledTextColor
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
Layout.fillWidth: true
|
||||
color: "transparent"
|
||||
radius: 10
|
||||
Layout.preferredHeight: currentChat.responseInProgress ? 40 : 0
|
||||
clip: true
|
||||
ColumnLayout {
|
||||
id: followUpLayout
|
||||
anchors.fill: parent
|
||||
Rectangle {
|
||||
id: myRect1
|
||||
Layout.preferredWidth: 0
|
||||
Layout.minimumWidth: 0
|
||||
Layout.maximumWidth: parent.width
|
||||
height: 12
|
||||
color: theme.sourcesBackgroundHovered
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: myRect2
|
||||
Layout.preferredWidth: 0
|
||||
Layout.minimumWidth: 0
|
||||
Layout.maximumWidth: parent.width
|
||||
height: 12
|
||||
color: theme.sourcesBackgroundHovered
|
||||
}
|
||||
|
||||
SequentialAnimation {
|
||||
id: followUpProgressAnimation
|
||||
ParallelAnimation {
|
||||
PropertyAnimation {
|
||||
target: myRect1
|
||||
property: "Layout.preferredWidth"
|
||||
from: 0
|
||||
to: followUpLayout.width
|
||||
duration: 1000
|
||||
}
|
||||
PropertyAnimation {
|
||||
target: myRect2
|
||||
property: "Layout.preferredWidth"
|
||||
from: 0
|
||||
to: followUpLayout.width / 2
|
||||
duration: 1000
|
||||
}
|
||||
}
|
||||
SequentialAnimation {
|
||||
loops: Animation.Infinite
|
||||
ParallelAnimation {
|
||||
PropertyAnimation {
|
||||
target: myRect1
|
||||
property: "opacity"
|
||||
from: 1
|
||||
to: 0.2
|
||||
duration: 1500
|
||||
}
|
||||
PropertyAnimation {
|
||||
target: myRect2
|
||||
property: "opacity"
|
||||
from: 1
|
||||
to: 0.2
|
||||
duration: 1500
|
||||
}
|
||||
}
|
||||
ParallelAnimation {
|
||||
PropertyAnimation {
|
||||
target: myRect1
|
||||
property: "opacity"
|
||||
from: 0.2
|
||||
to: 1
|
||||
duration: 1500
|
||||
}
|
||||
PropertyAnimation {
|
||||
target: myRect2
|
||||
property: "opacity"
|
||||
from: 0.2
|
||||
to: 1
|
||||
duration: 1500
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onVisibleChanged: {
|
||||
if (visible)
|
||||
followUpProgressAnimation.start();
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on Layout.preferredHeight {
|
||||
NumberAnimation {
|
||||
duration: 300
|
||||
easing.type: Easing.InOutQuad
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function scrollToEnd() {
|
||||
if (listView.shouldAutoScroll) {
|
||||
listView.isAutoScrolling = true
|
||||
listView.positionViewAtEnd()
|
||||
listView.isAutoScrolling = false
|
||||
}
|
||||
listView.positionViewAtEnd()
|
||||
}
|
||||
|
||||
onContentYChanged: {
|
||||
if (!isAutoScrolling)
|
||||
shouldAutoScroll = atYEnd
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
shouldAutoScroll = true
|
||||
positionViewAtEnd()
|
||||
}
|
||||
|
||||
footer: Item {
|
||||
id: bottomPadding
|
||||
width: parent.width
|
||||
height: 0
|
||||
onContentHeightChanged: {
|
||||
if (atYEnd)
|
||||
scrollToEnd()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
|
@@ -250,45 +250,64 @@ MySettingsTab {
|
||||
}
|
||||
}
|
||||
|
||||
MySettingsLabel {
|
||||
id: chatNamePromptLabel
|
||||
text: qsTr("Chat Name Prompt")
|
||||
helpText: qsTr("Prompt used to automatically generate chat names.")
|
||||
Layout.row: 11
|
||||
Layout.column: 0
|
||||
Layout.topMargin: 15
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: optionalImageRect
|
||||
visible: false // FIXME: for later
|
||||
Layout.row: 2
|
||||
Layout.column: 1
|
||||
Layout.rowSpan: 5
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
Layout.fillHeight: true
|
||||
Layout.maximumWidth: height
|
||||
Layout.topMargin: 35
|
||||
Layout.bottomMargin: 35
|
||||
Layout.leftMargin: 35
|
||||
width: 3000
|
||||
radius: 10
|
||||
id: chatNamePrompt
|
||||
Layout.row: 12
|
||||
Layout.column: 0
|
||||
Layout.columnSpan: 2
|
||||
Layout.fillWidth: true
|
||||
Layout.minimumHeight: Math.max(100, chatNamePromptTextArea.contentHeight + 20)
|
||||
color: "transparent"
|
||||
Item {
|
||||
anchors.centerIn: parent
|
||||
height: childrenRect.height
|
||||
Image {
|
||||
id: img
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
width: 100
|
||||
height: 100
|
||||
source: "qrc:/gpt4all/icons/image.svg"
|
||||
}
|
||||
Text {
|
||||
text: qsTr("Add\noptional image")
|
||||
font.pixelSize: theme.fontSizeLarge
|
||||
anchors.top: img.bottom
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
wrapMode: TextArea.Wrap
|
||||
horizontalAlignment: Qt.AlignHCenter
|
||||
color: theme.mutedTextColor
|
||||
}
|
||||
clip: true
|
||||
MyTextArea {
|
||||
id: chatNamePromptTextArea
|
||||
anchors.fill: parent
|
||||
text: root.currentModelInfo.chatNamePrompt
|
||||
Accessible.role: Accessible.EditableText
|
||||
Accessible.name: chatNamePromptLabel.text
|
||||
Accessible.description: chatNamePromptLabel.text
|
||||
}
|
||||
}
|
||||
|
||||
MySettingsLabel {
|
||||
id: suggestedFollowUpPromptLabel
|
||||
text: qsTr("Suggested FollowUp Prompt")
|
||||
helpText: qsTr("Prompt used to generate suggested follow-up questions.")
|
||||
Layout.row: 13
|
||||
Layout.column: 0
|
||||
Layout.topMargin: 15
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: suggestedFollowUpPrompt
|
||||
Layout.row: 14
|
||||
Layout.column: 0
|
||||
Layout.columnSpan: 2
|
||||
Layout.fillWidth: true
|
||||
Layout.minimumHeight: Math.max(100, suggestedFollowUpPromptTextArea.contentHeight + 20)
|
||||
color: "transparent"
|
||||
clip: true
|
||||
MyTextArea {
|
||||
id: suggestedFollowUpPromptTextArea
|
||||
anchors.fill: parent
|
||||
text: root.currentModelInfo.suggestedFollowUpPrompt
|
||||
Accessible.role: Accessible.EditableText
|
||||
Accessible.name: suggestedFollowUpPromptLabel.text
|
||||
Accessible.description: suggestedFollowUpPromptLabel.text
|
||||
}
|
||||
}
|
||||
|
||||
GridLayout {
|
||||
Layout.row: 11
|
||||
Layout.row: 15
|
||||
Layout.column: 0
|
||||
Layout.columnSpan: 2
|
||||
Layout.topMargin: 15
|
||||
@@ -784,7 +803,7 @@ MySettingsTab {
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
Layout.row: 12
|
||||
Layout.row: 16
|
||||
Layout.column: 0
|
||||
Layout.columnSpan: 2
|
||||
Layout.topMargin: 15
|
||||
|
Reference in New Issue
Block a user