Skip to content

Commit 7cbff80

Browse files
committed
Serialize the source excerpts from and to pure json
Signed-off-by: Adam Treat <treat.adam@gmail.com>
1 parent 1a0b483 commit 7cbff80

File tree

6 files changed

+149
-185
lines changed

6 files changed

+149
-185
lines changed

gpt4all-chat/chatllm.cpp

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -798,10 +798,8 @@ bool ChatLLM::promptInternal(const QList<QString> &collectionList, const QString
798798
QString docsContext;
799799
if (!localDocsExcerpts.isEmpty()) {
800800
// FIXME(adam): we should be using the new tool template if available otherwise this I guess
801-
QStringList results;
802-
for (const SourceExcerpt &info : localDocsExcerpts)
803-
results << u"Collection: %1\nPath: %2\nExcerpt: %3"_s.arg(info.collection, info.path, info.text);
804-
docsContext = u"### Context:\n%1\n\n"_s.arg(results.join("\n\n"));
801+
QString json = SourceExcerpt::toJson(localDocsExcerpts);
802+
docsContext = u"### Context:\n%1\n\n"_s.arg(json);
805803
}
806804

807805
int n_threads = MySettings::globalInstance()->threadCount();
@@ -910,9 +908,6 @@ bool ChatLLM::promptInternal(const QList<QString> &collectionList, const QString
910908
emit sourceExcerptsChanged(sourceExcerpts);
911909
}
912910

913-
// Erase the context of the tool call
914-
m_ctx.n_past = std::max(0, m_ctx.n_past);
915-
m_ctx.tokens.erase(m_ctx.tokens.end() - m_promptResponseTokens, m_ctx.tokens.end());
916911
m_promptResponseTokens = 0;
917912
m_promptTokens = 0;
918913
m_response = std::string();

gpt4all-chat/chatmodel.h

Lines changed: 39 additions & 110 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@ struct ChatItem
2929
Q_PROPERTY(bool thumbsUpState MEMBER thumbsUpState)
3030
Q_PROPERTY(bool thumbsDownState MEMBER thumbsDownState)
3131
Q_PROPERTY(QList<SourceExcerpt> sources MEMBER sources)
32-
Q_PROPERTY(QList<SourceExcerpt> consolidatedSources MEMBER consolidatedSources)
3332

3433
public:
3534
// TODO: Maybe we should include the model name here as well as timestamp?
@@ -39,7 +38,6 @@ struct ChatItem
3938
QString prompt;
4039
QString newResponse;
4140
QList<SourceExcerpt> sources;
42-
QList<SourceExcerpt> consolidatedSources;
4341
bool currentResponse = false;
4442
bool stopped = false;
4543
bool thumbsUpState = false;
@@ -65,8 +63,7 @@ class ChatModel : public QAbstractListModel
6563
StoppedRole,
6664
ThumbsUpStateRole,
6765
ThumbsDownStateRole,
68-
SourcesRole,
69-
ConsolidatedSourcesRole
66+
SourcesRole
7067
};
7168

7269
int rowCount(const QModelIndex &parent = QModelIndex()) const override
@@ -102,8 +99,6 @@ class ChatModel : public QAbstractListModel
10299
return item.thumbsDownState;
103100
case SourcesRole:
104101
return QVariant::fromValue(item.sources);
105-
case ConsolidatedSourcesRole:
106-
return QVariant::fromValue(item.consolidatedSources);
107102
}
108103

109104
return QVariant();
@@ -122,7 +117,6 @@ class ChatModel : public QAbstractListModel
122117
roles[ThumbsUpStateRole] = "thumbsUpState";
123118
roles[ThumbsDownStateRole] = "thumbsDownState";
124119
roles[SourcesRole] = "sources";
125-
roles[ConsolidatedSourcesRole] = "consolidatedSources";
126120
return roles;
127121
}
128122

@@ -200,34 +194,17 @@ class ChatModel : public QAbstractListModel
200194
}
201195
}
202196

203-
QList<SourceExcerpt> consolidateSources(const QList<SourceExcerpt> &sources) {
204-
QMap<QString, SourceExcerpt> groupedData;
205-
for (const SourceExcerpt &info : sources) {
206-
QString key = !info.file.isEmpty() ? info.file : info.url;
207-
if (groupedData.contains(key)) {
208-
groupedData[key].text += "\n---\n" + info.text;
209-
} else {
210-
groupedData[key] = info;
211-
}
212-
}
213-
QList<SourceExcerpt> consolidatedSources = groupedData.values();
214-
return consolidatedSources;
215-
}
216-
217197
Q_INVOKABLE void updateSources(int index, const QList<SourceExcerpt> &sources)
218198
{
219199
if (index < 0 || index >= m_chatItems.size()) return;
220200

221201
ChatItem &item = m_chatItems[index];
222202
if (sources.isEmpty()) {
223203
item.sources.clear();
224-
item.consolidatedSources.clear();
225204
} else {
226205
item.sources << sources;
227-
item.consolidatedSources << consolidateSources(sources);
228206
}
229207
emit dataChanged(createIndex(index, 0), createIndex(index, 0), {SourcesRole});
230-
emit dataChanged(createIndex(index, 0), createIndex(index, 0), {ConsolidatedSourcesRole});
231208
}
232209

233210
Q_INVOKABLE void updateThumbsUpState(int index, bool b)
@@ -278,61 +255,7 @@ class ChatModel : public QAbstractListModel
278255
stream << c.stopped;
279256
stream << c.thumbsUpState;
280257
stream << c.thumbsDownState;
281-
if (version > 7) {
282-
stream << c.sources.size();
283-
for (const SourceExcerpt &info : c.sources) {
284-
Q_ASSERT(!info.file.isEmpty());
285-
stream << info.collection;
286-
stream << info.path;
287-
stream << info.file;
288-
stream << info.title;
289-
stream << info.author;
290-
stream << info.date;
291-
stream << info.text;
292-
stream << info.page;
293-
stream << info.from;
294-
stream << info.to;
295-
if (version > 9) {
296-
stream << info.url;
297-
stream << info.favicon;
298-
}
299-
}
300-
} else if (version > 2) {
301-
QList<QString> references;
302-
QList<QString> referencesContext;
303-
int validReferenceNumber = 1;
304-
for (const SourceExcerpt &info : c.sources) {
305-
if (info.file.isEmpty())
306-
continue;
307-
308-
QString reference;
309-
{
310-
QTextStream stream(&reference);
311-
stream << (validReferenceNumber++) << ". ";
312-
if (!info.title.isEmpty())
313-
stream << "\"" << info.title << "\". ";
314-
if (!info.author.isEmpty())
315-
stream << "By " << info.author << ". ";
316-
if (!info.date.isEmpty())
317-
stream << "Date: " << info.date << ". ";
318-
stream << "In " << info.file << ". ";
319-
if (info.page != -1)
320-
stream << "Page " << info.page << ". ";
321-
if (info.from != -1) {
322-
stream << "Lines " << info.from;
323-
if (info.to != -1)
324-
stream << "-" << info.to;
325-
stream << ". ";
326-
}
327-
stream << "[Context](context://" << validReferenceNumber - 1 << ")";
328-
}
329-
references.append(reference);
330-
referencesContext.append(info.text);
331-
}
332-
333-
stream << references.join("\n");
334-
stream << referencesContext;
335-
}
258+
stream << SourceExcerpt::toJson(c.sources);
336259
}
337260
return stream.status() == QDataStream::Ok;
338261
}
@@ -352,31 +275,36 @@ class ChatModel : public QAbstractListModel
352275
stream >> c.stopped;
353276
stream >> c.thumbsUpState;
354277
stream >> c.thumbsDownState;
355-
if (version > 7) {
278+
if (version > 9) {
279+
QList<SourceExcerpt> sources;
280+
QString json;
281+
stream >> json;
282+
QString errorString;
283+
sources = SourceExcerpt::fromJson(json, errorString);
284+
Q_ASSERT(errorString.isEmpty());
285+
c.sources = sources;
286+
} else if (version > 7) {
356287
qsizetype count;
357288
stream >> count;
358289
QList<SourceExcerpt> sources;
359290
for (int i = 0; i < count; ++i) {
360-
SourceExcerpt info;
361-
stream >> info.collection;
362-
stream >> info.path;
363-
stream >> info.file;
364-
stream >> info.title;
365-
stream >> info.author;
366-
stream >> info.date;
367-
stream >> info.text;
368-
stream >> info.page;
369-
stream >> info.from;
370-
stream >> info.to;
371-
if (version > 9) {
372-
stream >> info.url;
373-
stream >> info.favicon;
374-
}
375-
sources.append(info);
291+
SourceExcerpt source;
292+
stream >> source.collection;
293+
stream >> source.path;
294+
stream >> source.file;
295+
stream >> source.title;
296+
stream >> source.author;
297+
stream >> source.date;
298+
Excerpt excerpt;
299+
stream >> excerpt.text;
300+
stream >> excerpt.page;
301+
stream >> excerpt.from;
302+
stream >> excerpt.to;
303+
source.excerpts = QList{ excerpt };
304+
sources.append(source);
376305
}
377306
c.sources = sources;
378-
c.consolidatedSources = consolidateSources(sources);
379-
}else if (version > 2) {
307+
} else if (version > 2) {
380308
QString references;
381309
QList<QString> referencesContext;
382310
stream >> references;
@@ -398,7 +326,8 @@ class ChatModel : public QAbstractListModel
398326
for (int j = 0; j < referenceList.size(); ++j) {
399327
QString reference = referenceList[j];
400328
QString context = referencesContext[j];
401-
SourceExcerpt info;
329+
SourceExcerpt source;
330+
Excerpt excerpt;
402331
QTextStream refStream(&reference);
403332
QString dummy;
404333
int validReferenceNumber;
@@ -407,36 +336,36 @@ class ChatModel : public QAbstractListModel
407336
if (reference.contains("\"")) {
408337
int startIndex = reference.indexOf('"') + 1;
409338
int endIndex = reference.indexOf('"', startIndex);
410-
info.title = reference.mid(startIndex, endIndex - startIndex);
339+
source.title = reference.mid(startIndex, endIndex - startIndex);
411340
}
412341

413342
// Extract author (after "By " and before the next period)
414343
if (reference.contains("By ")) {
415344
int startIndex = reference.indexOf("By ") + 3;
416345
int endIndex = reference.indexOf('.', startIndex);
417-
info.author = reference.mid(startIndex, endIndex - startIndex).trimmed();
346+
source.author = reference.mid(startIndex, endIndex - startIndex).trimmed();
418347
}
419348

420349
// Extract date (after "Date: " and before the next period)
421350
if (reference.contains("Date: ")) {
422351
int startIndex = reference.indexOf("Date: ") + 6;
423352
int endIndex = reference.indexOf('.', startIndex);
424-
info.date = reference.mid(startIndex, endIndex - startIndex).trimmed();
353+
source.date = reference.mid(startIndex, endIndex - startIndex).trimmed();
425354
}
426355

427356
// Extract file name (after "In " and before the "[Context]")
428357
if (reference.contains("In ") && reference.contains(". [Context]")) {
429358
int startIndex = reference.indexOf("In ") + 3;
430359
int endIndex = reference.indexOf(". [Context]", startIndex);
431-
info.file = reference.mid(startIndex, endIndex - startIndex).trimmed();
360+
source.file = reference.mid(startIndex, endIndex - startIndex).trimmed();
432361
}
433362

434363
// Extract page number (after "Page " and before the next space)
435364
if (reference.contains("Page ")) {
436365
int startIndex = reference.indexOf("Page ") + 5;
437366
int endIndex = reference.indexOf(' ', startIndex);
438367
if (endIndex == -1) endIndex = reference.length();
439-
info.page = reference.mid(startIndex, endIndex - startIndex).toInt();
368+
excerpt.page = reference.mid(startIndex, endIndex - startIndex).toInt();
440369
}
441370

442371
// Extract lines (after "Lines " and before the next space or hyphen)
@@ -446,18 +375,18 @@ class ChatModel : public QAbstractListModel
446375
if (endIndex == -1) endIndex = reference.length();
447376
int hyphenIndex = reference.indexOf('-', startIndex);
448377
if (hyphenIndex != -1 && hyphenIndex < endIndex) {
449-
info.from = reference.mid(startIndex, hyphenIndex - startIndex).toInt();
450-
info.to = reference.mid(hyphenIndex + 1, endIndex - hyphenIndex - 1).toInt();
378+
excerpt.from = reference.mid(startIndex, hyphenIndex - startIndex).toInt();
379+
excerpt.to = reference.mid(hyphenIndex + 1, endIndex - hyphenIndex - 1).toInt();
451380
} else {
452-
info.from = reference.mid(startIndex, endIndex - startIndex).toInt();
381+
excerpt.from = reference.mid(startIndex, endIndex - startIndex).toInt();
453382
}
454383
}
455-
info.text = context;
456-
sources.append(info);
384+
excerpt.text = context;
385+
source.excerpts = QList{ excerpt };
386+
sources.append(source);
457387
}
458388

459389
c.sources = sources;
460-
c.consolidatedSources = consolidateSources(sources);
461390
}
462391
}
463392
beginInsertRows(QModelIndex(), m_chatItems.size(), m_chatItems.size());

gpt4all-chat/qml/ChatView.qml

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1106,7 +1106,7 @@ Rectangle {
11061106
Layout.preferredWidth: childrenRect.width
11071107
Layout.preferredHeight: childrenRect.height
11081108
visible: {
1109-
if (consolidatedSources.length === 0)
1109+
if (sources.length === 0)
11101110
return false
11111111
if (!MySettings.localDocsShowReferences)
11121112
return false
@@ -1134,9 +1134,9 @@ Rectangle {
11341134
sourceSize.height: 24
11351135
mipmap: true
11361136
source: {
1137-
if (typeof consolidatedSources === 'undefined'
1138-
|| typeof consolidatedSources[0] === 'undefined'
1139-
|| consolidatedSources[0].url === "")
1137+
if (typeof sources === 'undefined'
1138+
|| typeof sources[0] === 'undefined'
1139+
|| sources[0].url === "")
11401140
return "qrc:/gpt4all/icons/db.svg";
11411141
else
11421142
return "qrc:/gpt4all/icons/globe.svg";
@@ -1151,7 +1151,7 @@ Rectangle {
11511151
}
11521152

11531153
Text {
1154-
text: qsTr("%1 Sources").arg(consolidatedSources.length)
1154+
text: qsTr("%1 Sources").arg(sources.length)
11551155
padding: 0
11561156
font.pixelSize: theme.fontSizeLarge
11571157
font.bold: true
@@ -1199,7 +1199,7 @@ Rectangle {
11991199
Layout.column: 1
12001200
Layout.topMargin: 5
12011201
visible: {
1202-
if (consolidatedSources.length === 0)
1202+
if (sources.length === 0)
12031203
return false
12041204
if (!MySettings.localDocsShowReferences)
12051205
return false
@@ -1240,9 +1240,9 @@ Rectangle {
12401240
id: flow
12411241
Layout.fillWidth: true
12421242
spacing: 10
1243-
visible: consolidatedSources.length !== 0
1243+
visible: sources.length !== 0
12441244
Repeater {
1245-
model: consolidatedSources
1245+
model: sources
12461246

12471247
delegate: Rectangle {
12481248
radius: 10
@@ -1361,7 +1361,7 @@ Rectangle {
13611361
return false;
13621362
if (MySettings.suggestionMode === 2) // Off
13631363
return false;
1364-
if (MySettings.suggestionMode === 0 && consolidatedSources.length === 0) // LocalDocs only
1364+
if (MySettings.suggestionMode === 0 && sources.length === 0) // LocalDocs only
13651365
return false;
13661366
return currentChat.responseState === Chat.GeneratingQuestions || currentChat.generatedQuestions.length !== 0;
13671367
}

gpt4all-chat/server.cpp

Lines changed: 4 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -408,12 +408,8 @@ QHttpServerResponse Server::handleCompletionRequest(const QHttpServerRequest &re
408408
message.insert("role", "assistant");
409409
message.insert("content", result);
410410
choice.insert("message", message);
411-
if (MySettings::globalInstance()->localDocsShowReferences()) {
412-
QJsonArray references;
413-
for (const auto &ref : infos)
414-
references.append(ref.toJson());
415-
choice.insert("references", references);
416-
}
411+
if (MySettings::globalInstance()->localDocsShowReferences())
412+
choice.insert("references", SourceExcerpt::toJson(infos));
417413
choices.append(choice);
418414
}
419415
} else {
@@ -426,12 +422,8 @@ QHttpServerResponse Server::handleCompletionRequest(const QHttpServerRequest &re
426422
choice.insert("index", index++);
427423
choice.insert("logprobs", QJsonValue::Null); // We don't support
428424
choice.insert("finish_reason", responseTokens == max_tokens ? "length" : "stop");
429-
if (MySettings::globalInstance()->localDocsShowReferences()) {
430-
QJsonArray references;
431-
for (const auto &ref : infos)
432-
references.append(ref.toJson());
433-
choice.insert("references", references);
434-
}
425+
if (MySettings::globalInstance()->localDocsShowReferences())
426+
choice.insert("references", SourceExcerpt::toJson(infos));
435427
choices.append(choice);
436428
}
437429
}

0 commit comments

Comments
 (0)