diff --git a/gpt4all-chat/bravesearch.h b/gpt4all-chat/bravesearch.h index 0cf1118b..ef7c14e1 100644 --- a/gpt4all-chat/bravesearch.h +++ b/gpt4all-chat/bravesearch.h @@ -51,6 +51,7 @@ public: QString name() const override { return tr("Web Search"); } QString description() const override { return tr("Search the web"); } QString function() const override { return "web_search"; } + ToolEnums::PrivacyScope privacyScope() const override { return ToolEnums::PrivacyScope::None; } QJsonObject paramSchema() const override; QJsonObject exampleParams() const override; bool isBuiltin() const override { return true; } diff --git a/gpt4all-chat/localdocssearch.h b/gpt4all-chat/localdocssearch.h index b4ea3e94..be9ed04f 100644 --- a/gpt4all-chat/localdocssearch.h +++ b/gpt4all-chat/localdocssearch.h @@ -37,6 +37,7 @@ public: QString name() const override { return tr("LocalDocs Search"); } QString description() const override { return tr("Search the local docs"); } QString function() const override { return "localdocs_search"; } + ToolEnums::PrivacyScope privacyScope() const override { return ToolEnums::PrivacyScope::Local; } QJsonObject paramSchema() const override; bool isBuiltin() const override { return true; } ToolEnums::UsageMode usageMode() const override { return ToolEnums::UsageMode::ForceUsage; } diff --git a/gpt4all-chat/main.cpp b/gpt4all-chat/main.cpp index 4546a95b..b3cef897 100644 --- a/gpt4all-chat/main.cpp +++ b/gpt4all-chat/main.cpp @@ -7,6 +7,7 @@ #include "modellist.h" #include "mysettings.h" #include "network.h" +#include "toolmodel.h" #include "../gpt4all-backend/llmodel.h" @@ -67,6 +68,8 @@ int main(int argc, char *argv[]) qmlRegisterSingletonInstance("download", 1, 0, "Download", Download::globalInstance()); qmlRegisterSingletonInstance("network", 1, 0, "Network", Network::globalInstance()); qmlRegisterSingletonInstance("localdocs", 1, 0, "LocalDocs", LocalDocs::globalInstance()); + qmlRegisterSingletonInstance("toollist", 1, 0, "ToolList", ToolModel::globalInstance()); + qmlRegisterUncreatableMetaObject(ToolEnums::staticMetaObject, "toolenums", 1, 0, "ToolEnums", "Error: only enums"); qmlRegisterUncreatableMetaObject(MySettingsEnums::staticMetaObject, "mysettingsenums", 1, 0, "MySettingsEnums", "Error: only enums"); const QUrl url(u"qrc:/gpt4all/main.qml"_qs); diff --git a/gpt4all-chat/main.qml b/gpt4all-chat/main.qml index 16b5b65c..ad1f0295 100644 --- a/gpt4all-chat/main.qml +++ b/gpt4all-chat/main.qml @@ -12,6 +12,8 @@ import network import gpt4all import localdocs import mysettings +import toollist +import toolenums Window { id: window @@ -413,7 +415,11 @@ Window { ColorOverlay { id: antennaColored - visible: ModelList.selectableModels.count !== 0 && (currentChat.isServer || currentChat.modelInfo.isOnline || MySettings.networkIsActive) + visible: ModelList.selectableModels.count !== 0 + && (MySettings.networkIsActive + || currentChat.modelInfo.isOnline + || currentChat.isServer + || ToolList.privacyScope === ToolEnums.PrivacyScope.None) anchors.fill: antennaImage source: antennaImage color: theme.styledTextColor @@ -422,8 +428,10 @@ Window { return qsTr("The datalake is enabled") else if (currentChat.modelInfo.isOnline) return qsTr("Using a network model") - else if (currentChat.modelInfo.isOnline) + else if (currentChat.isServer) return qsTr("Server mode is enabled") + else if (ToolList.privacyScope === ToolEnums.PrivacyScope.None) + return qsTr("One or more enabled tools is not private") return "" } ToolTip.visible: maAntenna.containsMouse diff --git a/gpt4all-chat/tool.h b/gpt4all-chat/tool.h index 86f95904..88a3dbc9 100644 --- a/gpt4all-chat/tool.h +++ b/gpt4all-chat/tool.h @@ -22,6 +22,14 @@ namespace ToolEnums { ForceUsage, // Attempt to force usage of the tool rather than let the LLM decide. NOTE: Not always possible. }; Q_ENUM_NS(UsageMode) + + // Ordered in increasing levels of privacy + enum class PrivacyScope { + None = 0, // Tool call data does not have any privacy scope + LocalOrg = 1, // Tool call data does not leave the local organization + Local = 2 // Tool call data does not leave the machine + }; + Q_ENUM_NS(PrivacyScope) } class Tool : public QObject { @@ -29,6 +37,7 @@ class Tool : public QObject { Q_PROPERTY(QString name READ name CONSTANT) Q_PROPERTY(QString description READ description CONSTANT) Q_PROPERTY(QString function READ function CONSTANT) + Q_PROPERTY(ToolEnums::PrivacyScope privacyScope READ privacyScope CONSTANT) Q_PROPERTY(QJsonObject paramSchema READ paramSchema CONSTANT) Q_PROPERTY(QJsonObject exampleParams READ exampleParams CONSTANT) Q_PROPERTY(QUrl url READ url CONSTANT) @@ -54,6 +63,9 @@ public: // [Required] Must be unique. Name of the function to invoke. Must be a-z, A-Z, 0-9, or contain underscores and dashes, with a maximum length of 64. virtual QString function() const = 0; + // [Required] The privacy scope + virtual ToolEnums::PrivacyScope privacyScope() const = 0; + // [Optional] Json schema describing the tool's parameters. An empty object specifies no parameters. // https://json-schema.org/understanding-json-schema/ // https://platform.openai.com/docs/api-reference/runs/createRun#runs-createrun-tools diff --git a/gpt4all-chat/toolmodel.cpp b/gpt4all-chat/toolmodel.cpp index 9f8547ea..a2fdbdc0 100644 --- a/gpt4all-chat/toolmodel.cpp +++ b/gpt4all-chat/toolmodel.cpp @@ -22,10 +22,12 @@ ToolModel::ToolModel() Tool* localDocsSearch = new LocalDocsSearch; m_tools.append(localDocsSearch); m_toolMap.insert(localDocsSearch->function(), localDocsSearch); + connect(localDocsSearch, &Tool::usageModeChanged, this, &ToolModel::privacyScopeChanged); Tool *braveSearch = new BraveSearch; m_tools.append(braveSearch); m_toolMap.insert(braveSearch->function(), braveSearch); + connect(braveSearch, &Tool::usageModeChanged, this, &ToolModel::privacyScopeChanged); } bool ToolModel::eventFilter(QObject *obj, QEvent *ev) @@ -34,3 +36,12 @@ bool ToolModel::eventFilter(QObject *obj, QEvent *ev) emit dataChanged(index(0, 0), index(m_tools.size() - 1, 0)); return false; } + +ToolEnums::PrivacyScope ToolModel::privacyScope() const +{ + ToolEnums::PrivacyScope scope = ToolEnums::PrivacyScope::Local; // highest scope + for (const Tool *t : m_tools) + if (t->usageMode() != ToolEnums::UsageMode::Disabled) + scope = std::min(scope, t->privacyScope()); + return scope; +} diff --git a/gpt4all-chat/toolmodel.h b/gpt4all-chat/toolmodel.h index 315b7360..4135771e 100644 --- a/gpt4all-chat/toolmodel.h +++ b/gpt4all-chat/toolmodel.h @@ -9,6 +9,7 @@ class ToolModel : public QAbstractListModel { Q_OBJECT Q_PROPERTY(int count READ count NOTIFY countChanged) + Q_PROPERTY(ToolEnums::PrivacyScope privacyScope READ privacyScope NOTIFY privacyScopeChanged) public: static ToolModel *globalInstance(); @@ -17,6 +18,7 @@ public: NameRole = Qt::UserRole + 1, DescriptionRole, FunctionRole, + PrivacyScopeRole, ParametersRole, UrlRole, ApiKeyRole, @@ -46,6 +48,8 @@ public: return item->description(); case FunctionRole: return item->function(); + case PrivacyScopeRole: + return QVariant::fromValue(item->privacyScope()); case ParametersRole: return item->paramSchema(); case UrlRole: @@ -69,6 +73,7 @@ public: roles[NameRole] = "name"; roles[DescriptionRole] = "description"; roles[FunctionRole] = "function"; + roles[PrivacyScopeRole] = "privacyScope"; roles[ParametersRole] = "parameters"; roles[UrlRole] = "url"; roles[ApiKeyRole] = "apiKey"; @@ -94,8 +99,12 @@ public: int count() const { return m_tools.size(); } + // Returns the least private scope of all enabled tools + ToolEnums::PrivacyScope privacyScope() const; + Q_SIGNALS: void countChanged(); + void privacyScopeChanged(); void valueChanged(int index, const QString &value); protected: