mirror of
https://github.com/nomic-ai/gpt4all.git
synced 2025-06-28 00:07:04 +00:00
Move the jinja processing to mysettings and validation.
Signed-off-by: Adam Treat <treat.adam@gmail.com>
This commit is contained in:
parent
227dbfd18b
commit
48117cda46
@ -31,7 +31,6 @@
|
||||
#include <cmath>
|
||||
#include <cstddef>
|
||||
#include <functional>
|
||||
#include <jinja2cpp/template.h>
|
||||
#include <limits>
|
||||
#include <optional>
|
||||
#include <string_view>
|
||||
@ -1337,34 +1336,14 @@ void ChatLLM::processSystemPrompt()
|
||||
if (!isModelLoaded() || m_processedSystemPrompt || m_restoreStateFromText || m_isServer)
|
||||
return;
|
||||
|
||||
const std::string systemPromptTemplate = MySettings::globalInstance()->modelSystemPromptTemplate(m_modelInfo).toStdString();
|
||||
|
||||
// FIXME: This needs to be moved to settings probably and the same code used for validation
|
||||
jinja2::ValuesMap params;
|
||||
params.insert({"currentDate", QDate::currentDate().toString().toStdString()});
|
||||
|
||||
jinja2::ValuesList toolList;
|
||||
int c = ToolModel::globalInstance()->count();
|
||||
for (int i = 0; i < c; ++i) {
|
||||
Tool *t = ToolModel::globalInstance()->get(i);
|
||||
if (t->usageMode() == ToolEnums::UsageMode::Enabled)
|
||||
toolList.push_back(t->jinjaValue());
|
||||
}
|
||||
params.insert({"toolList", toolList});
|
||||
|
||||
std::string systemPrompt;
|
||||
|
||||
jinja2::Template t;
|
||||
t.Load(systemPromptTemplate);
|
||||
const auto renderResult = t.RenderAsString(params);
|
||||
QString error;
|
||||
const std::string systemPrompt = MySettings::globalInstance()->modelSystemPrompt(m_modelInfo, error).toStdString();
|
||||
|
||||
// The GUI should not allow setting an improper template, but it is always possible someone hand
|
||||
// edits the settings file to produce an improper one.
|
||||
Q_ASSERT(renderResult);
|
||||
if (renderResult)
|
||||
systemPrompt = renderResult.value();
|
||||
else
|
||||
qWarning() << "ERROR: Could not parse system prompt template:" << renderResult.error().ToString();
|
||||
Q_ASSERT(error.isEmpty());
|
||||
if (!error.isEmpty())
|
||||
qWarning() << "ERROR: Could not parse system prompt template:" << error;
|
||||
|
||||
if (QString::fromStdString(systemPrompt).trimmed().isEmpty()) {
|
||||
m_processedSystemPrompt = true;
|
||||
|
@ -1,6 +1,8 @@
|
||||
#include "mysettings.h"
|
||||
|
||||
#include "../gpt4all-backend/llmodel.h"
|
||||
#include "tool.h"
|
||||
#include "toolmodel.h"
|
||||
|
||||
#include <QDebug>
|
||||
#include <QDir>
|
||||
@ -18,6 +20,7 @@
|
||||
#include <QtLogging>
|
||||
|
||||
#include <algorithm>
|
||||
#include <jinja2cpp/template.h>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
@ -676,3 +679,45 @@ void MySettings::setLanguageAndLocale(const QString &bcp47Name)
|
||||
QLocale::setDefault(locale);
|
||||
emit languageAndLocaleChanged();
|
||||
}
|
||||
|
||||
QString MySettings::validateModelSystemPromptTemplate(const QString &proposedTemplate)
|
||||
{
|
||||
QString error;
|
||||
systemPromptInternal(proposedTemplate, error);
|
||||
return error;
|
||||
}
|
||||
|
||||
QString MySettings::modelSystemPrompt(const ModelInfo &info, QString &error)
|
||||
{
|
||||
return systemPromptInternal(modelSystemPromptTemplate(info), error);
|
||||
}
|
||||
|
||||
QString MySettings::systemPromptInternal(const QString &proposedTemplate, QString &error)
|
||||
{
|
||||
jinja2::ValuesMap params;
|
||||
params.insert({"currentDate", QDate::currentDate().toString().toStdString()});
|
||||
|
||||
jinja2::ValuesList toolList;
|
||||
int c = ToolModel::globalInstance()->count();
|
||||
for (int i = 0; i < c; ++i) {
|
||||
Tool *t = ToolModel::globalInstance()->get(i);
|
||||
if (t->usageMode() == ToolEnums::UsageMode::Enabled)
|
||||
toolList.push_back(t->jinjaValue());
|
||||
}
|
||||
params.insert({"toolList", toolList});
|
||||
|
||||
QString systemPrompt;
|
||||
jinja2::Template t;
|
||||
const auto loadResult = t.Load(proposedTemplate.toStdString(), "systemPromptTemplate" /*Used in error messages*/);
|
||||
if (!loadResult) {
|
||||
error = QString::fromStdString(loadResult.error().ToString());
|
||||
return systemPrompt;
|
||||
}
|
||||
|
||||
const auto renderResult = t.RenderAsString(params);
|
||||
if (renderResult)
|
||||
systemPrompt = QString::fromStdString(renderResult.value());
|
||||
else
|
||||
error = QString::fromStdString(renderResult.error().ToString());
|
||||
return systemPrompt;
|
||||
}
|
||||
|
@ -204,6 +204,10 @@ public:
|
||||
int networkPort() const;
|
||||
void setNetworkPort(int value);
|
||||
|
||||
// Jinja aware methods for validating and parsing/rendering the system prompt
|
||||
Q_INVOKABLE QString validateModelSystemPromptTemplate(const QString &proposedTemplate);
|
||||
QString modelSystemPrompt(const ModelInfo &info, QString &error);
|
||||
|
||||
Q_SIGNALS:
|
||||
void nameChanged(const ModelInfo &info);
|
||||
void filenameChanged(const ModelInfo &info);
|
||||
@ -269,6 +273,8 @@ private:
|
||||
void setModelSetting(const QString &name, const ModelInfo &info, const QVariant &value, bool force,
|
||||
bool signal = false);
|
||||
QString filePathForLocale(const QLocale &locale);
|
||||
QString systemPromptInternal(const QString &proposedTemplate, QString &error);
|
||||
|
||||
};
|
||||
|
||||
#endif // MYSETTINGS_H
|
||||
|
@ -153,13 +153,30 @@ MySettingsTab {
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
MySettingsLabel {
|
||||
visible: !root.currentModelInfo.isOnline
|
||||
text: qsTr("System Prompt")
|
||||
helpText: qsTr("Prefixed at the beginning of every conversation. Must contain the appropriate framing tokens.")
|
||||
RowLayout {
|
||||
Layout.row: 7
|
||||
Layout.column: 0
|
||||
Layout.columnSpan: 2
|
||||
Layout.topMargin: 15
|
||||
spacing: 10
|
||||
MySettingsLabel {
|
||||
text: qsTr("System Prompt Template")
|
||||
helpText: qsTr("Prefixed at the beginning of every conversation. Must contain the appropriate framing tokens.")
|
||||
}
|
||||
MySettingsLabel {
|
||||
id: systemPromptTemplateError
|
||||
color: theme.textErrorColor
|
||||
wrapMode: TextArea.Wrap
|
||||
Timer {
|
||||
id: errorTimer
|
||||
interval: 500 // 500 ms delay
|
||||
repeat: false
|
||||
property string text: ""
|
||||
onTriggered: {
|
||||
systemPromptTemplateError.text = errorTimer.text;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
@ -188,7 +205,15 @@ MySettingsTab {
|
||||
}
|
||||
}
|
||||
onTextChanged: {
|
||||
MySettings.setModelSystemPromptTemplate(root.currentModelInfo, text)
|
||||
var errorString = MySettings.validateModelSystemPromptTemplate(text);
|
||||
if (errorString === "") {
|
||||
errorTimer.stop();
|
||||
systemPromptTemplateError.text = ""; // Clear any previous error
|
||||
MySettings.setModelSystemPromptTemplate(root.currentModelInfo, text);
|
||||
} else {
|
||||
errorTimer.text = errorString;
|
||||
errorTimer.restart();
|
||||
}
|
||||
}
|
||||
Accessible.role: Accessible.EditableText
|
||||
}
|
||||
|
@ -1,95 +0,0 @@
|
||||
#ifndef SOURCEEXCERT_H
|
||||
#define SOURCEEXCERT_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QJsonObject>
|
||||
#include <QFileInfo>
|
||||
#include <QUrl>
|
||||
|
||||
using namespace Qt::Literals::StringLiterals;
|
||||
|
||||
struct SourceExcerpt {
|
||||
Q_GADGET
|
||||
Q_PROPERTY(QString date MEMBER date)
|
||||
Q_PROPERTY(QString text MEMBER text)
|
||||
Q_PROPERTY(QString collection MEMBER collection)
|
||||
Q_PROPERTY(QString path MEMBER path)
|
||||
Q_PROPERTY(QString file MEMBER file)
|
||||
Q_PROPERTY(QString url MEMBER url)
|
||||
Q_PROPERTY(QString favicon MEMBER favicon)
|
||||
Q_PROPERTY(QString title MEMBER title)
|
||||
Q_PROPERTY(QString author MEMBER author)
|
||||
Q_PROPERTY(int page MEMBER page)
|
||||
Q_PROPERTY(int from MEMBER from)
|
||||
Q_PROPERTY(int to MEMBER to)
|
||||
Q_PROPERTY(QString fileUri READ fileUri STORED false)
|
||||
|
||||
public:
|
||||
QString date; // [Required] The creation or the last modification date whichever is latest
|
||||
QString text; // [Required] The text actually used in the augmented context
|
||||
QString collection; // [Optional] The name of the collection
|
||||
QString path; // [Optional] The full path
|
||||
QString file; // [Optional] The name of the file, but not the full path
|
||||
QString url; // [Optional] The name of the remote url
|
||||
QString favicon; // [Optional] The favicon
|
||||
QString title; // [Optional] The title of the document
|
||||
QString author; // [Optional] The author of the document
|
||||
int page = -1; // [Optional] The page where the text was found
|
||||
int from = -1; // [Optional] The line number where the text begins
|
||||
int to = -1; // [Optional] The line number where the text ends
|
||||
|
||||
QString fileUri() const {
|
||||
// QUrl reserved chars that are not UNSAFE_PATH according to glib/gconvert.c
|
||||
static const QByteArray s_exclude = "!$&'()*+,/:=@~"_ba;
|
||||
|
||||
Q_ASSERT(!QFileInfo(path).isRelative());
|
||||
#ifdef Q_OS_WINDOWS
|
||||
Q_ASSERT(!path.contains('\\')); // Qt normally uses forward slash as path separator
|
||||
#endif
|
||||
|
||||
auto escaped = QString::fromUtf8(QUrl::toPercentEncoding(path, s_exclude));
|
||||
if (escaped.front() != '/')
|
||||
escaped = '/' + escaped;
|
||||
return u"file://"_s + escaped;
|
||||
}
|
||||
|
||||
QJsonObject toJson() const
|
||||
{
|
||||
QJsonObject result;
|
||||
result.insert("date", date);
|
||||
result.insert("text", text);
|
||||
result.insert("collection", collection);
|
||||
result.insert("path", path);
|
||||
result.insert("file", file);
|
||||
result.insert("url", url);
|
||||
result.insert("favicon", favicon);
|
||||
result.insert("title", title);
|
||||
result.insert("author", author);
|
||||
result.insert("page", page);
|
||||
result.insert("from", from);
|
||||
result.insert("to", to);
|
||||
return result;
|
||||
}
|
||||
|
||||
bool operator==(const SourceExcerpt &other) const {
|
||||
return date == other.date &&
|
||||
text == other.text &&
|
||||
collection == other.collection &&
|
||||
path == other.path &&
|
||||
file == other.file &&
|
||||
url == other.url &&
|
||||
favicon == other.favicon &&
|
||||
title == other.title &&
|
||||
author == other.author &&
|
||||
page == other.page &&
|
||||
from == other.from &&
|
||||
to == other.to;
|
||||
}
|
||||
bool operator!=(const SourceExcerpt &other) const {
|
||||
return !(*this == other);
|
||||
}
|
||||
};
|
||||
|
||||
Q_DECLARE_METATYPE(SourceExcerpt)
|
||||
|
||||
#endif // SOURCEEXCERT_H
|
Loading…
Reference in New Issue
Block a user