From b8815981661a18ee4f967066cfe132ce1a0eeeb8 Mon Sep 17 00:00:00 2001 From: Jared Van Bortel Date: Sun, 21 Jan 2024 19:53:55 -0500 Subject: [PATCH 01/27] py: improve README (#1860) Signed-off-by: Jared Van Bortel --- gpt4all-bindings/python/README.md | 37 +++++++++++++++++++++---------- 1 file changed, 25 insertions(+), 12 deletions(-) diff --git a/gpt4all-bindings/python/README.md b/gpt4all-bindings/python/README.md index 0c72008ecda4..c380d1698442 100644 --- a/gpt4all-bindings/python/README.md +++ b/gpt4all-bindings/python/README.md @@ -9,11 +9,17 @@ https://docs.gpt4all.io/gpt4all_python.html ## Installation +The easiest way to install the Python bindings for GPT4All is to use pip: + ``` pip install gpt4all ``` -## Local Build Instructions +This will download the latest version of the `gpt4all` package from PyPI. + +## Local Build + +As an alternative to downloading via pip, you may build the Python bindings from source. ### Prerequisites @@ -23,25 +29,32 @@ macOS users do not need Vulkan, as GPT4All will use Metal instead. ### Building the python bindings -**NOTE**: If you are doing this on a Windows machine, you must build the GPT4All backend using [MinGW64](https://www.mingw-w64.org/) compiler. +1. Clone GPT4All and change directory: +``` +git clone --recurse-submodules https://github.com/nomic-ai/gpt4all.git +cd gpt4all/gpt4all-backend +``` + +2. Build the backend. -1. Setup `llmodel` +If you are using Windows and have Visual Studio installed: +``` +cmake -B build +cmake --build build --parallel --config RelWithDebInfo +``` +For all other platforms: ``` -git clone --recurse-submodules https://github.com/nomic-ai/gpt4all.git -cd gpt4all/gpt4all-backend/ -mkdir build -cd build -cmake .. -cmake --build . --parallel # optionally append: --config Release +cmake -B build -DCMAKE_BUILD_TYPE=RelWithDebInfo +cmake --build build --parallel ``` -Confirm that `libllmodel.*` exists in `gpt4all-backend/build`. -2. Setup Python package +`RelWithDebInfo` is a good default, but you can also use `Release` or `Debug` depending on the situation. +2. Install the Python package: ``` cd ../../gpt4all-bindings/python -pip3 install -e . +pip install -e . ``` ## Usage From c7ea283f1ff9a8631ce1bf19c2746ef517f4ac7c Mon Sep 17 00:00:00 2001 From: Jared Van Bortel Date: Mon, 22 Jan 2024 10:01:31 -0500 Subject: [PATCH 02/27] chatllm: fix deserialization version mismatch (#1859) Signed-off-by: Jared Van Bortel --- gpt4all-chat/chatllm.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gpt4all-chat/chatllm.cpp b/gpt4all-chat/chatllm.cpp index 57cdc96d4519..ad13a81a89d8 100644 --- a/gpt4all-chat/chatllm.cpp +++ b/gpt4all-chat/chatllm.cpp @@ -794,7 +794,7 @@ bool ChatLLM::serialize(QDataStream &stream, int version, bool serializeKV) stream << responseLogits; } stream << m_ctx.n_past; - if (version >= 6) { + if (version >= 7) { stream << m_ctx.n_ctx; } stream << quint64(m_ctx.logits.size()); @@ -846,7 +846,7 @@ bool ChatLLM::deserialize(QDataStream &stream, int version, bool deserializeKV, stream >> n_past; if (!discardKV) m_ctx.n_past = n_past; - if (version >= 6) { + if (version >= 7) { uint32_t n_ctx; stream >> n_ctx; if (!discardKV) m_ctx.n_ctx = n_ctx; From 26acdebafacb303bc6785307fa9c1d4648c5c763 Mon Sep 17 00:00:00 2001 From: Jared Van Bortel Date: Mon, 22 Jan 2024 12:14:55 -0500 Subject: [PATCH 03/27] convert: replace GPTJConfig with AutoConfig (#1866) Signed-off-by: Jared Van Bortel --- .../scripts/convert_gptj_to_gguf.py | 4 +- .../scripts/convert_mpt_hf_to_gguf.py | 168 ------------------ .../scripts/convert_replit_v1_hf_to_gguf.py | 145 --------------- 3 files changed, 2 insertions(+), 315 deletions(-) delete mode 100755 gpt4all-backend/scripts/convert_mpt_hf_to_gguf.py delete mode 100755 gpt4all-backend/scripts/convert_replit_v1_hf_to_gguf.py diff --git a/gpt4all-backend/scripts/convert_gptj_to_gguf.py b/gpt4all-backend/scripts/convert_gptj_to_gguf.py index 9b8f3a627240..ed3ee5762363 100755 --- a/gpt4all-backend/scripts/convert_gptj_to_gguf.py +++ b/gpt4all-backend/scripts/convert_gptj_to_gguf.py @@ -27,7 +27,7 @@ import gguf import numpy as np -from transformers import AutoTokenizer, GPTJConfig, GPTJForCausalLM +from transformers import AutoConfig, AutoTokenizer, GPTJForCausalLM from transformers.models.gpt2 import tokenization_gpt2 @@ -63,7 +63,7 @@ print("gguf: get model metadata") -config = GPTJConfig(dir_model) +config = AutoConfig.from_pretrained(dir_model) block_count = config.n_layer gguf_writer.add_name("GPT-J") diff --git a/gpt4all-backend/scripts/convert_mpt_hf_to_gguf.py b/gpt4all-backend/scripts/convert_mpt_hf_to_gguf.py deleted file mode 100755 index b2688e56d0ae..000000000000 --- a/gpt4all-backend/scripts/convert_mpt_hf_to_gguf.py +++ /dev/null @@ -1,168 +0,0 @@ -#!/usr/bin/env python3 -# Convert Hugging Face fine-tuned bloom-like models to ggml format -# -# Usage: -# -# python3 models/convert-h5-to-ggml.py -# -# This script is similar to "convert-pt-to-ggml.py" -# - -from __future__ import annotations - -import json -import struct -import sys -from pathlib import Path - -import gguf -import numpy as np -import torch -from transformers import AutoTokenizer, AutoModelForCausalLM, AutoConfig, MptConfig -from transformers.models.gpt2 import tokenization_gpt2 - - -if not 3 <= len(sys.argv) < 5: - print("Usage: {} model-name dir-output [ftype]".format(Path(__file__).name)) - print(" model-name: name of the model to convert. Example: 'bigscience/bloomz-560m'") - print(" dir-output: directory where the output file will be written") - print(" ftype == 0 -> float32") - print(" ftype == 1 -> float16") - sys.exit(1) - -dir_model = Path(sys.argv[1]) -dir_out = Path(sys.argv[2]) - -# make sure the output directory exists -dir_out.mkdir(exist_ok=True) - -# possible data types -# ftype == 0 -> float32 -# ftype == 1 -> float16 -# -# map from ftype to string -ftype_str = ["f32", "f16"] - -ftype = 1 -if len(sys.argv) > 3: - ftype = int(sys.argv[3]) - if ftype < 0 or ftype > 1: - print("Invalid ftype: " + str(ftype)) - sys.exit(1) - -fname_out = dir_out / f"ggml-model-{dir_model.name}-{ftype_str[ftype]}.gguf" - - -ARCH = gguf.MODEL_ARCH.MPT -gguf_writer = gguf.GGUFWriter(fname_out, gguf.MODEL_ARCH_NAMES[ARCH]) - -print("gguf: get model metadata") - -config = AutoConfig.from_pretrained(dir_model, trust_remote_code=True) - -block_count = config.n_layers -gguf_writer.add_name("MPT") -gguf_writer.add_context_length(config.max_seq_len) -gguf_writer.add_embedding_length(config.d_model) -gguf_writer.add_block_count(block_count) -gguf_writer.add_feed_forward_length(4 * config.d_model) -gguf_writer.add_head_count(config.n_heads) -if kv_n_heads := config.attn_config.get('kv_n_heads'): - gguf_writer.add_head_count_kv(kv_n_heads) -gguf_writer.add_max_alibi_bias(config.attn_config['alibi_bias_max']) -gguf_writer.add_layer_norm_eps(MptConfig().layer_norm_epsilon) # use default from upstream transformers -gguf_writer.add_file_type(ftype) - -clip_qkv = config.attn_config['clip_qkv'] -if clip_qkv is not None: - gguf_writer.add_clamp_kqv(clip_qkv) - -print("gguf: get gpt2 tokenizer vocab") - -tokenizer = AutoTokenizer.from_pretrained(dir_model) - -special_ids = tokenizer.all_special_ids - -reverse_vocab = {id: encoded_tok for encoded_tok, id in tokenizer.vocab.items()} -added_tokens = tokenizer.get_added_vocab().values() -byte_encoder = tokenization_gpt2.bytes_to_unicode() -byte_decoder = {v: k for k, v in byte_encoder.items()} - -tokens: list[bytearray] = [] -toktypes: list[gguf.TokenType] = [] - -# The number of tokens in tokenizer.json can differ from the expected vocab size. -# This causes downstream issues with mismatched tensor sizes when running the inference -for i in range(config.vocab_size): - if i not in reverse_vocab: - print(f"Key {i} not in tokenizer vocabulary. Padding with an arbitrary token.") - pad_token = f"[PAD{i}]".encode("utf8") - text = bytearray(pad_token) - elif i in added_tokens: - # these tokens are not encoded, for some reason - text = bytearray(reverse_vocab[i].encode('utf-8')) - else: - text = bytearray([byte_decoder[c] for c in reverse_vocab[i]]) - - tokens.append(text) - - # TODO(cebtenzzre): is there a better way to do this? - toktypes.append(gguf.TokenType.CONTROL if i in special_ids else gguf.TokenType.NORMAL) - -gguf_writer.add_tokenizer_model("gpt2") -gguf_writer.add_token_list(tokens) -gguf_writer.add_token_types(toktypes) - -special_vocab = gguf.SpecialVocab(dir_model, load_merges=True) -special_vocab.add_to_gguf(gguf_writer) - -print("gguf: get tensor metadata") - -print("Loading model:", dir_model) -model = AutoModelForCausalLM.from_pretrained( - dir_model, config=config, torch_dtype=torch.float16 if ftype == 1 else torch.float32, - low_cpu_mem_usage=True, trust_remote_code=True, -) -print("Model loaded:", dir_model) - -tensor_map = gguf.get_tensor_name_map(ARCH, block_count) - -list_vars = model.state_dict() -for name in list_vars.keys(): - data = list_vars[name].squeeze().numpy() - print("Processing variable:", name, "with shape:", data.shape) - - n_dims = len(data.shape) - - # ftype == 0 -> float32, ftype == 1 -> float16 - ftype_cur = 0 - # Keep token embeddings in fp32 - if ftype == 1 and name[-7:] == ".weight" and n_dims == 2 and ".wte" not in name: - print(" Converting to float16") - data = data.astype(np.float16) - ftype_cur = 1 - elif ftype == 1 or data.dtype != np.float32: - print(" Converting to float32") - data = data.astype(np.float32) - ftype_cur = 0 - - # map tensor names - new_name = tensor_map.get_name(name, try_suffixes=(".weight", ".bias")) - if new_name is None: - print("Can not map tensor '" + name + "'") - sys.exit() - - gguf_writer.add_tensor(new_name, data) - - -print("gguf: write header") -gguf_writer.write_header_to_file() -print("gguf: write metadata") -gguf_writer.write_kv_data_to_file() -print("gguf: write tensors") -gguf_writer.write_tensors_to_file() - -gguf_writer.close() - -print(f"gguf: model successfully exported to '{fname_out}'") -print() diff --git a/gpt4all-backend/scripts/convert_replit_v1_hf_to_gguf.py b/gpt4all-backend/scripts/convert_replit_v1_hf_to_gguf.py deleted file mode 100755 index 43cb48714636..000000000000 --- a/gpt4all-backend/scripts/convert_replit_v1_hf_to_gguf.py +++ /dev/null @@ -1,145 +0,0 @@ -#!/usr/bin/env python3 -from __future__ import annotations - -import json -import struct -import sys -from pathlib import Path - -import gguf -import numpy as np -from sentencepiece import SentencePieceProcessor -from transformers import AutoConfig, AutoModelForCausalLM, AutoTokenizer - - -if not 2 <= len(sys.argv) < 4: - print("Usage: {} dir-model [ftype]\n".format(Path(__file__).name)) - print(" ftype == 0 -> float32") - print(" ftype == 1 -> float16") - sys.exit(1) - -# output in the same directory as the model -dir_model = Path(sys.argv[1]) - -# possible data types -# ftype == 0 -> float32 -# ftype == 1 -> float16 -# -# map from ftype to string -ftype_str = ["f32", "f16"] - -ftype = 1 -if len(sys.argv) > 2: - ftype = int(sys.argv[2]) - if ftype < 0 or ftype > 1: - print("Invalid ftype: " + str(ftype)) - sys.exit(1) - -fname_out = dir_model / ("ggml-replit-code-v1-3b-" + ftype_str[ftype] + ".gguf") - - -ARCH = gguf.MODEL_ARCH.MPT -gguf_writer = gguf.GGUFWriter(fname_out, gguf.MODEL_ARCH_NAMES[ARCH]) - -print("gguf: get model metadata") - -config = AutoConfig.from_pretrained(dir_model) - -block_count = config.n_layers -gguf_writer.add_name("Replit") -gguf_writer.add_context_length(config.max_seq_len) -gguf_writer.add_embedding_length(config.d_model) -gguf_writer.add_block_count(block_count) -gguf_writer.add_feed_forward_length(4 * config.d_model) -gguf_writer.add_head_count(config.n_heads) -gguf_writer.add_max_alibi_bias(config.attn_config.alibi_bias_max) -gguf_writer.add_layer_norm_eps(config.layer_norm_epsilon) -gguf_writer.add_file_type(ftype) - -clip_qkv = config.attn_config.clip_qkv -if clip_qkv is not None: - gguf_writer.add_clamp_kqv(clip_qkv) - -print("gguf: get sentencepiece tokenizer vocab") - -tokenizer = SentencePieceProcessor(str(dir_model / "spiece.model")) -#print(tokenizer.encode('I believe the meaning of life is')) - -tokens: list[bytearray] = [] -scores: list[float] = [] -toktypes: list[int] = [] - -for i in range(tokenizer.vocab_size()): - tokens.append(tokenizer.id_to_piece(i).encode('utf-8')) - scores.append(tokenizer.get_score(i)) - - toktype = gguf.TokenType.NORMAL - if tokenizer.is_unknown(i): - toktype = gguf.TokenType.UNKNOWN - elif tokenizer.is_control(i): - toktype = gguf.TokenType.CONTROL - elif tokenizer.is_unused(i): - toktype = gguf.TokenType.UNUSED - elif tokenizer.is_byte(i): - toktype = gguf.TokenType.BYTE - - toktypes.append(toktype) - -gguf_writer.add_tokenizer_model("llama") # sentencepiece -gguf_writer.add_token_list(tokens) -gguf_writer.add_token_scores(scores) -gguf_writer.add_token_types(toktypes) - -special_vocab = gguf.SpecialVocab(dir_model, load_merges=True) -special_vocab.add_to_gguf(gguf_writer) - -print("gguf: get tensor metadata") - -model = AutoModelForCausalLM.from_pretrained(dir_model, config=config, low_cpu_mem_usage=True) -#print(model) - -tensor_map = gguf.get_tensor_name_map(ARCH, block_count) - -list_vars = model.state_dict() -for name in list_vars.keys(): - print(name, list_vars[name].shape, list_vars[name].dtype) - -print(config) - -for name in list_vars.keys(): - data = list_vars[name].squeeze().numpy() - print("Processing variable:", name, "with shape:", data.shape) - - n_dims = len(data.shape) - - # ftype == 0 -> float32, ftype == 1 -> float16 - ftype_cur = 0 - if ftype == 1 and name[-7:] == ".weight" and n_dims == 2: - print(" Converting to float16") - data = data.astype(np.float16) - ftype_cur = 1 - elif ftype == 1 or data.dtype != np.float32: - print(" Converting to float32") - data = data.astype(np.float32) - ftype_cur = 0 - - # map tensor names - new_name = tensor_map.get_name(name, try_suffixes=(".weight", ".bias")) - if new_name is None: - print("Can not map tensor '" + name + "'") - sys.exit() - - gguf_writer.add_tensor(new_name, data) - - -print("gguf: write header") -gguf_writer.write_header_to_file() -print("gguf: write metadata") -gguf_writer.write_kv_data_to_file() -print("gguf: write tensors") -gguf_writer.write_tensors_to_file() - -gguf_writer.close() - -print(f"gguf: model successfully exported to '{fname_out}'") -print() From 27912f6e1a21daf68cf05b41841c2c06b8140571 Mon Sep 17 00:00:00 2001 From: Adam Treat Date: Mon, 22 Jan 2024 14:15:13 -0500 Subject: [PATCH 04/27] Fix bug with install of online models. --- gpt4all-chat/download.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/gpt4all-chat/download.cpp b/gpt4all-chat/download.cpp index 2a20fc28494c..e1a688a4032d 100644 --- a/gpt4all-chat/download.cpp +++ b/gpt4all-chat/download.cpp @@ -185,7 +185,10 @@ void Download::installModel(const QString &modelFile, const QString &apiKey) QTextStream stream(&file); stream << apiKey; file.close(); + ModelList::globalInstance()->updateModelsFromDirectory(); } + + ModelList::globalInstance()->updateDataByFilename(modelFile, ModelList::InstalledRole, true); } void Download::removeModel(const QString &modelFile) @@ -202,6 +205,7 @@ void Download::removeModel(const QString &modelFile) file.remove(); } + ModelList::globalInstance()->updateDataByFilename(modelFile, ModelList::InstalledRole, false); ModelList::globalInstance()->updateDataByFilename(modelFile, ModelList::BytesReceivedRole, 0); ModelList::globalInstance()->updateDataByFilename(modelFile, ModelList::BytesTotalRole, 0); ModelList::globalInstance()->updateDataByFilename(modelFile, ModelList::TimestampRole, 0); From 0a45dd384e29de4e8e282935a206e874b6ddd569 Mon Sep 17 00:00:00 2001 From: Karthik Nair Date: Thu, 25 Jan 2024 04:30:49 +0530 Subject: [PATCH 05/27] add fedora command for QT and related packages (#1871) Signed-off-by: Karthik Nair Co-authored-by: Jared Van Bortel --- gpt4all-chat/build_and_run.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/gpt4all-chat/build_and_run.md b/gpt4all-chat/build_and_run.md index be63bf36cfd8..25d28c7fa339 100644 --- a/gpt4all-chat/build_and_run.md +++ b/gpt4all-chat/build_and_run.md @@ -24,6 +24,11 @@ On Ubuntu 23.04, this looks like: sudo apt install build-essential qt6-base-dev qt6-declarative-dev qt6-wayland-dev qt6-svg-dev qt6-httpserver-dev qt6-webengine-dev libqt6core5compat6 qml6-module-qt5compat-graphicaleffects libqt6shadertools6 qtcreator cmake ninja-build ``` +On Fedora 39, this looks like: +``` +sudo dnf install make gcc gcc-c++ qt6-qtbase-devel qt6-qtdeclarative-devel qt6-qtwayland-devel qt6-qtsvg-devel qt6-qthttpserver-devel qt6-qtwebengine-devel qt6-qt5compat qt5-qtgraphicaleffects qt6-qtshadertools qt-creator cmake ninja-build +``` + ## Download Qt - Go to https://login.qt.io/register to create a free Qt account. From 697a5f5d2a2b7a481480cfa508cdc0fef3165c79 Mon Sep 17 00:00:00 2001 From: Adam Treat Date: Mon, 22 Jan 2024 14:41:47 -0500 Subject: [PATCH 06/27] New lightmode and darkmode themes with UI revamp. Signed-off-by: Adam Treat --- gpt4all-chat/CMakeLists.txt | 5 + gpt4all-chat/main.qml | 325 +++++++++++++----- gpt4all-chat/qml/AboutDialog.qml | 18 +- gpt4all-chat/qml/ApplicationSettings.qml | 66 ++-- gpt4all-chat/qml/ChatDrawer.qml | 21 +- gpt4all-chat/qml/CollectionsDialog.qml | 24 +- gpt4all-chat/qml/LocalDocsSettings.qml | 42 +-- gpt4all-chat/qml/ModelDownloaderDialog.qml | 68 +--- gpt4all-chat/qml/ModelSettings.qml | 132 +++---- gpt4all-chat/qml/MyBusyIndicator.qml | 2 +- gpt4all-chat/qml/MyButton.qml | 20 +- gpt4all-chat/qml/MyCheckBox.qml | 9 +- gpt4all-chat/qml/MyComboBox.qml | 11 +- gpt4all-chat/qml/MyDialog.qml | 4 +- gpt4all-chat/qml/MyDirectoryField.qml | 3 +- gpt4all-chat/qml/MySettingsButton.qml | 38 ++ .../qml/MySettingsDestructiveButton.qml | 38 ++ gpt4all-chat/qml/MySettingsLabel.qml | 10 + gpt4all-chat/qml/MySettingsStack.qml | 45 ++- gpt4all-chat/qml/MySettingsTab.qml | 16 +- gpt4all-chat/qml/MySlug.qml | 24 ++ gpt4all-chat/qml/MyTextArea.qml | 22 ++ gpt4all-chat/qml/MyTextField.qml | 10 +- gpt4all-chat/qml/MyToolButton.qml | 7 +- gpt4all-chat/qml/NetworkDialog.qml | 25 +- gpt4all-chat/qml/NewVersionDialog.qml | 7 +- gpt4all-chat/qml/PopupDialog.qml | 3 +- gpt4all-chat/qml/SettingsDialog.qml | 70 ++-- gpt4all-chat/qml/StartupDialog.qml | 20 +- gpt4all-chat/qml/Theme.qml | 162 +++++++-- gpt4all-chat/qml/ThumbsDownDialog.qml | 18 +- 31 files changed, 780 insertions(+), 485 deletions(-) create mode 100644 gpt4all-chat/qml/MySettingsButton.qml create mode 100644 gpt4all-chat/qml/MySettingsDestructiveButton.qml create mode 100644 gpt4all-chat/qml/MySettingsLabel.qml create mode 100644 gpt4all-chat/qml/MySlug.qml create mode 100644 gpt4all-chat/qml/MyTextArea.qml diff --git a/gpt4all-chat/CMakeLists.txt b/gpt4all-chat/CMakeLists.txt index d506f6e7c561..7eecf36cc844 100644 --- a/gpt4all-chat/CMakeLists.txt +++ b/gpt4all-chat/CMakeLists.txt @@ -111,10 +111,15 @@ qt_add_qml_module(chat qml/LocalDocsSettings.qml qml/MySettingsTab.qml qml/MySettingsStack.qml + qml/MySettingsDestructiveButton.qml + qml/MySettingsButton.qml + qml/MySettingsLabel.qml + qml/MySlug.qml qml/MyButton.qml qml/MyComboBox.qml qml/MyDialog.qml qml/MyDirectoryField.qml + qml/MyTextArea.qml qml/MyTextField.qml qml/MyCheckBox.qml qml/MyBusyIndicator.qml diff --git a/gpt4all-chat/main.qml b/gpt4all-chat/main.qml index 18b346a907d4..4d9ef6fabc99 100644 --- a/gpt4all-chat/main.qml +++ b/gpt4all-chat/main.qml @@ -14,10 +14,10 @@ import mysettings Window { id: window - width: 1280 - height: 720 - minimumWidth: 720 - minimumHeight: 480 + width: 1920 + height: 1080 + minimumWidth: 1280 + minimumHeight: 720 visible: true title: qsTr("GPT4All v") + Qt.application.version @@ -47,7 +47,7 @@ Window { } } - color: theme.backgroundDarkest + color: theme.black // Startup code Component.onCompleted: { @@ -197,13 +197,93 @@ Window { + "
  • Check out our discord channel for help") } + Rectangle { + id: yellowRibbon + anchors.left: parent.left + anchors.right: parent.right + anchors.top: header.bottom + height: 3 + color: theme.yellowAccent + } + + Rectangle { + id: titleBar + anchors.left: parent.left + anchors.right: parent.right + anchors.top: parent.top + anchors.topMargin: 5 + z: 200 + height: 25 + color: "transparent" + + RowLayout { + anchors.left: parent.left + anchors.leftMargin: 30 + + Text { + textFormat: Text.StyledText + text: "gpt4all.io |" + horizontalAlignment: Text.AlignLeft + font.pixelSize: theme.fontSizeFixedSmall + color: theme.gray300 + linkColor: theme.gray300 + onLinkActivated: { Qt.openUrlExternally("https://gpt4all.io") } + } + + Text { + textFormat: Text.StyledText + text: "github" + horizontalAlignment: Text.AlignLeft + font.pixelSize: theme.fontSizeFixedSmall + color: theme.gray300 + linkColor: theme.gray300 + onLinkActivated: { Qt.openUrlExternally("https://github.com/nomic-ai/gpt4all") } + } + } + + RowLayout { + anchors.right: parent.right + anchors.rightMargin: 30 + + Text { + textFormat: Text.StyledText + text: "nomic.ai |" + horizontalAlignment: Text.AlignRight + font.pixelSize: theme.fontSizeFixedSmall + color: theme.gray300 + linkColor: theme.gray300 + onLinkActivated: { Qt.openUrlExternally("https://nomic.ai") } + } + + Text { + textFormat: Text.StyledText + text: "twitter |" + horizontalAlignment: Text.AlignRight + font.pixelSize: theme.fontSizeFixedSmall + color: theme.gray300 + linkColor: theme.gray300 + onLinkActivated: { Qt.openUrlExternally("https://twitter.com/nomic_ai") } + } + + Text { + textFormat: Text.StyledText + text: "discord" + horizontalAlignment: Text.AlignRight + font.pixelSize: theme.fontSizeFixedSmall + color: theme.gray300 + linkColor: theme.gray300 + onLinkActivated: { Qt.openUrlExternally("https://discord.gg/4M2QFmTt2k") } + } + } + } + Rectangle { id: header anchors.left: parent.left anchors.right: parent.right anchors.top: parent.top height: 100 - color: theme.backgroundDarkest + color: theme.mainHeader Item { anchors.centerIn: parent height: childrenRect.height @@ -216,7 +296,7 @@ Window { font.pixelSize: theme.fontSizeLarger text: "" background: Rectangle { - color: theme.backgroundDarkest + color: theme.mainHeader } horizontalAlignment: TextInput.AlignRight } @@ -251,7 +331,7 @@ Window { } } background: Rectangle { - color: theme.backgroundDark + color: theme.mainComboBackground radius: 10 } contentItem: Text { @@ -261,8 +341,8 @@ Window { text: currentChat.modelLoadingError !== "" ? qsTr("Model loading error...") : comboBox.currentModelName - font: comboBox.font - color: theme.textColor + font.pixelSize: theme.fontSizeLarger + color: theme.white verticalAlignment: Text.AlignVCenter horizontalAlignment: Text.AlignHCenter elide: Text.ElideRight @@ -277,7 +357,9 @@ Window { verticalAlignment: Text.AlignVCenter } background: Rectangle { - color: highlighted ? theme.backgroundLight : theme.backgroundDark + color: (index % 2 === 0 ? theme.darkContrast : theme.lightContrast) + border.width: highlighted + border.color: theme.yellowAccent } highlighted: comboBox.highlightedIndex === index } @@ -314,7 +396,7 @@ Window { anchors.verticalCenter: parent.verticalCenter text: qsTr("Loading model...") font.pixelSize: theme.fontSizeLarge - color: theme.textAccent + color: theme.oppositeTextColor } } } @@ -335,7 +417,7 @@ Window { id: drawerButton anchors.left: parent.left anchors.top: parent.top - anchors.topMargin: 30 + anchors.topMargin: 42.5 anchors.leftMargin: 30 width: 40 height: 40 @@ -353,7 +435,7 @@ Window { Rectangle { id: bar1 - color: drawerButton.hovered ? theme.textColor : theme.backgroundLightest + color: drawerButton.hovered ? theme.iconBackgroundHovered : theme.iconBackgroundLight width: parent.width height: 6 radius: 2 @@ -363,7 +445,7 @@ Window { Rectangle { id: bar2 anchors.centerIn: parent - color: drawerButton.hovered ? theme.textColor : theme.backgroundLightest + color: drawerButton.hovered ? theme.iconBackgroundHovered : theme.iconBackgroundLight width: parent.width height: 6 radius: 2 @@ -373,7 +455,7 @@ Window { Rectangle { id: bar3 anchors.bottom: parent.bottom - color: drawerButton.hovered ? theme.textColor : theme.backgroundLightest + color: drawerButton.hovered ? theme.iconBackgroundHovered : theme.iconBackgroundLight width: parent.width height: 6 radius: 2 @@ -399,9 +481,10 @@ Window { MyToolButton { id: networkButton + backgroundColor: theme.iconBackgroundLight anchors.right: parent.right anchors.top: parent.top - anchors.topMargin: 30 + anchors.topMargin: 42.5 anchors.rightMargin: 30 width: 40 height: 40 @@ -439,9 +522,10 @@ Window { MyToolButton { id: collectionsButton + backgroundColor: theme.iconBackgroundLight anchors.right: networkButton.left anchors.top: parent.top - anchors.topMargin: 30 + anchors.topMargin: 42.5 anchors.rightMargin: 10 width: 40 height: 40 @@ -459,9 +543,10 @@ Window { MyToolButton { id: settingsButton + backgroundColor: theme.iconBackgroundLight anchors.right: collectionsButton.left anchors.top: parent.top - anchors.topMargin: 30 + anchors.topMargin: 42.5 anchors.rightMargin: 10 width: 40 height: 40 @@ -527,9 +612,10 @@ Window { MyToolButton { id: copyButton + backgroundColor: theme.iconBackgroundLight anchors.right: settingsButton.left anchors.top: parent.top - anchors.topMargin: 30 + anchors.topMargin: 42.5 anchors.rightMargin: 10 width: 40 height: 40 @@ -592,9 +678,10 @@ Window { MyToolButton { id: resetContextButton + backgroundColor: theme.iconBackgroundLight anchors.right: copyButton.left anchors.top: parent.top - anchors.topMargin: 30 + anchors.topMargin: 42.5 anchors.rightMargin: 10 width: 40 height: 40 @@ -616,7 +703,6 @@ Window { id: checkForUpdatesError anchors.centerIn: parent modal: false - opacity: 0.9 padding: 20 Text { horizontalAlignment: Text.AlignJustify @@ -627,7 +713,7 @@ Window { above where this application resides on your filesystem.

    If you can't start it manually, then I'm afraid you'll have to
    reinstall.") - color: theme.textColor + color: theme.textErrorColor font.pixelSize: theme.fontSizeLarge Accessible.role: Accessible.Dialog Accessible.name: text @@ -635,7 +721,7 @@ Window { } background: Rectangle { anchors.fill: parent - color: theme.backgroundDarkest + color: theme.containerBackground border.width: 1 border.color: theme.dialogBorder radius: 10 @@ -656,7 +742,7 @@ Window { ChatDrawer { id: drawer - y: header.height + y: header.height + yellowRibbon.height width: Math.min(600, 0.3 * window.width) height: window.height - y onDownloadClicked: { @@ -678,11 +764,11 @@ Window { Rectangle { id: conversation - color: theme.backgroundLight + color: theme.containerBackground anchors.left: parent.left anchors.right: parent.right anchors.bottom: parent.bottom - anchors.top: header.bottom + anchors.top: yellowRibbon.bottom ScrollView { id: scrollView @@ -695,58 +781,129 @@ Window { Rectangle { anchors.fill: parent - color: currentChat.isServer ? theme.backgroundDark : theme.backgroundLight + color: currentChat.isServer ? theme.black : theme.containerBackground - Text { - id: warningLabel - text: qsTr("You must install a model to continue. Models are available via the download dialog or you can install them manually by downloading from the GPT4All website (look for the Models Explorer) and placing them in the model folder. The model folder can be found in the settings dialog under the application tab.") - color: theme.textColor - font.pixelSize: theme.fontSizeLarge - width: 600 - linkColor: theme.linkColor - wrapMode: Text.WordWrap - anchors.centerIn: parent - visible: ModelList.installedModels.count === 0 - onLinkActivated: function(link) { - Qt.openUrlExternally(link) - } - } + Rectangle { + id: homePage + color: "transparent"//theme.green200 + anchors.fill: parent + visible: (ModelList.installedModels.count === 0 || chatModel.count === 0) && !currentChat.isServer + + ColumnLayout { + anchors.centerIn: parent + spacing: 0 + + Text { + Layout.alignment: Qt.AlignHCenter + text: qsTr("GPT4All") + color: theme.titleTextColor + font.pixelSize: theme.fontSizeLargest + 15 + font.bold: true + horizontalAlignment: Qt.AlignHCenter + wrapMode: Text.WordWrap + } - MyButton { - id: downloadButton - text: qsTr("Download models") - visible: ModelList.installedModels.count === 0 - anchors.top: warningLabel.bottom - anchors.topMargin: 20 - anchors.horizontalCenter: warningLabel.horizontalCenter - padding: 15 - leftPadding: 50 - Image { - anchors.verticalCenter: parent.verticalCenter - anchors.left: parent.left - anchors.leftMargin: 15 - width: 24 - height: 24 - mipmap: true - source: "qrc:/gpt4all/icons/download.svg" - } - background: Rectangle { - border.color: downloadButton.down ? theme.backgroundLightest : theme.buttonBorder - border.width: 2 - radius: 10 - color: downloadButton.hovered ? theme.backgroundLighter : theme.backgroundLight - } - onClicked: { - downloadNewModels.open(); + Text { + Layout.alignment: Qt.AlignHCenter + textFormat: Text.StyledText + text: qsTr( + "
      +
    • Run privacy-aware local chatbots. +
    • No internet required to use. +
    • CPU and GPU acceleration. +
    • Chat with your local data and documents. +
    • Built by Nomic AI and forever open-source. +
    + ") + color: theme.textColor + font.pixelSize: theme.fontSizeSmall + wrapMode: Text.WordWrap + } + + RowLayout { + spacing: 10 + Layout.alignment: Qt.AlignHCenter + Layout.topMargin: 30 + MySlug { + text: "MISTRAL" + color: theme.red600 + } + MySlug { + text: "FALCON" + color: theme.green600 + } + MySlug { + text: "LLAMA" + color: theme.purple500 + } + MySlug { + text: "LLAMA2" + color: theme.red400 + } + MySlug { + text: "MPT" + color: theme.green700 + } + MySlug { + text: "REPLIT" + color: theme.yellow700 + } + MySlug { + text: "STARCODER" + color: theme.purple400 + } + MySlug { + text: "SBERT" + color: theme.yellow600 + } + MySlug { + text: "GPT-J" + color: theme.gray600 + } + } + + MyButton { + id: downloadButton + Layout.alignment: Qt.AlignHCenter + Layout.topMargin: 40 + text: qsTr("Download models") + fontPixelSize: theme.fontSizeLargest + 10 + padding: 18 + leftPadding: 50 + Image { + id: image + anchors.verticalCenter: parent.verticalCenter + anchors.left: parent.left + anchors.leftMargin: 15 + width: 24 + height: 24 + mipmap: true + source: "qrc:/gpt4all/icons/download.svg" + } + ColorOverlay { + anchors.fill: image + source: image + color: theme.yellowAccent + } + onClicked: { + downloadNewModels.open(); + } + } } } ListView { id: listView - visible: ModelList.installedModels.count !== 0 + visible: ModelList.installedModels.count !== 0 && chatModel.count !== 0 anchors.fill: parent model: chatModel - ScrollBar.vertical: ScrollBar { policy: ScrollBar.AlwaysOn } + + ScrollBar.vertical: ScrollBar { + parent: listView.parent + anchors.top: listView.top + anchors.left: listView.right + anchors.bottom: listView.bottom + } Accessible.role: Accessible.List Accessible.name: qsTr("Conversation with the model") @@ -767,8 +924,8 @@ Window { background: Rectangle { opacity: 1.0 color: name === qsTr("Response: ") - ? (currentChat.isServer ? theme.backgroundDarkest : theme.backgroundLighter) - : (currentChat.isServer ? theme.backgroundDark : theme.backgroundLight) + ? (currentChat.isServer ? theme.black : theme.lightContrast) + : (currentChat.isServer ? theme.white : theme.darkContrast) } TapHandler { id: tapHandler @@ -793,7 +950,7 @@ Window { Component.onCompleted: { responseText.setLinkColor(theme.linkColor); - responseText.setHeaderColor(name === qsTr("Response: ") ? theme.backgroundLight : theme.backgroundLighter); + responseText.setHeaderColor(name === qsTr("Response: ") ? theme.darkContrast : theme.lightContrast); responseText.textDocument = textDocument } @@ -824,7 +981,7 @@ Window { } Label { anchors.verticalCenter: parent.verticalCenter - color: theme.textAccent + color: theme.mutedTextColor text: { switch (currentChat.responseState) { case Chat.ResponseStopped: return qsTr("response stopped ..."); @@ -981,6 +1138,7 @@ Window { MyButton { id: myButton visible: chatModel.count && !currentChat.isServer + textColor: theme.textColor Image { anchors.verticalCenter: parent.verticalCenter anchors.left: parent.left @@ -1010,10 +1168,10 @@ Window { } } background: Rectangle { - border.color: myButton.down ? theme.backgroundLightest : theme.buttonBorder + border.color: theme.conversationButtonBorder border.width: 2 radius: 10 - color: myButton.hovered ? theme.backgroundLighter : theme.backgroundLight + color: myButton.hovered ? theme.conversationButtonBackgroundHovered : theme.conversationButtonBackground } anchors.bottom: textInputView.top anchors.horizontalCenter: textInputView.horizontalCenter @@ -1037,12 +1195,13 @@ Window { RectangularGlow { id: effect + visible: !currentChat.isServer anchors.fill: textInputView glowRadius: 50 spread: 0 - color: theme.backgroundDark + color: theme.sendGlow cornerRadius: 10 - opacity: 0.2 + opacity: 0.1 } ScrollView { @@ -1053,7 +1212,7 @@ Window { anchors.margins: 30 height: Math.min(contentHeight, 200) visible: !currentChat.isServer - TextArea { + MyTextArea { id: textInput color: theme.textColor topPadding: 30 @@ -1061,14 +1220,8 @@ Window { leftPadding: 20 rightPadding: 40 enabled: currentChat.isModelLoaded && !currentChat.isServer - wrapMode: Text.WordWrap font.pixelSize: theme.fontSizeLarger placeholderText: qsTr("Send a message...") - placeholderTextColor: theme.mutedTextColor - background: Rectangle { - color: theme.backgroundAccent - radius: 10 - } Accessible.role: Accessible.EditableText Accessible.name: placeholderText Accessible.description: qsTr("Send messages/prompts to the model") @@ -1101,6 +1254,8 @@ Window { } MyToolButton { + backgroundColor: theme.sendButtonBackground + backgroundColorHovered: theme.sendButtonBackgroundHovered anchors.right: textInputView.right anchors.verticalCenter: textInputView.verticalCenter anchors.rightMargin: 15 diff --git a/gpt4all-chat/qml/AboutDialog.qml b/gpt4all-chat/qml/AboutDialog.qml index a3ce20da963c..ca63f9e672e2 100644 --- a/gpt4all-chat/qml/AboutDialog.qml +++ b/gpt4all-chat/qml/AboutDialog.qml @@ -11,7 +11,6 @@ MyDialog { id: abpoutDialog anchors.centerIn: parent modal: false - opacity: 0.9 padding: 20 width: 1024 height: column.height + 40 @@ -39,8 +38,9 @@ MyDialog { anchors.leftMargin: 30 anchors.verticalCenter: img.verticalCenter text: qsTr("About GPT4All") - font.pixelSize: theme.fontSizeLarger color: theme.textColor + font.pixelSize: theme.fontSizeLarge + font.bold: true } } @@ -51,31 +51,23 @@ MyDialog { ScrollBar.vertical.policy: ScrollBar.AlwaysOn ScrollBar.horizontal.policy: ScrollBar.AlwaysOff - TextArea { + MyTextArea { id: welcome - wrapMode: Text.Wrap width: 1024 - 40 - padding: 20 textFormat: TextEdit.MarkdownText text: qsTr("### Release notes\n") + Download.releaseInfo.notes + qsTr("### Contributors\n") + Download.releaseInfo.contributors - color: theme.textColor - font.pixelSize: theme.fontSizeLarge focus: false readOnly: true Accessible.role: Accessible.Paragraph Accessible.name: qsTr("Release notes") Accessible.description: qsTr("Release notes for this version") - background: Rectangle { - color: theme.backgroundLight - radius: 10 - } } } - Label { + MySettingsLabel { id: discordLink width: parent.width textFormat: Text.StyledText @@ -90,7 +82,7 @@ MyDialog { Accessible.name: qsTr("Discord link") } - Label { + MySettingsLabel { id: nomicProps width: parent.width textFormat: Text.StyledText diff --git a/gpt4all-chat/qml/ApplicationSettings.qml b/gpt4all-chat/qml/ApplicationSettings.qml index ed86fd5937c6..6d9251d374a6 100644 --- a/gpt4all-chat/qml/ApplicationSettings.qml +++ b/gpt4all-chat/qml/ApplicationSettings.qml @@ -18,11 +18,9 @@ MySettingsTab { columns: 3 rowSpacing: 10 columnSpacing: 10 - Label { + MySettingsLabel { id: themeLabel - text: qsTr("Theme:") - color: theme.textColor - font.pixelSize: theme.fontSizeLarge + text: qsTr("Theme") Layout.row: 1 Layout.column: 0 } @@ -53,11 +51,9 @@ MySettingsTab { MySettings.chatTheme = themeBox.currentText } } - Label { + MySettingsLabel { id: fontLabel - text: qsTr("Font Size:") - color: theme.textColor - font.pixelSize: theme.fontSizeLarge + text: qsTr("Font Size") Layout.row: 2 Layout.column: 0 } @@ -88,11 +84,9 @@ MySettingsTab { MySettings.fontSize = fontBox.currentText } } - Label { + MySettingsLabel { id: deviceLabel - text: qsTr("Device:") - color: theme.textColor - font.pixelSize: theme.fontSizeLarge + text: qsTr("Device") Layout.row: 3 Layout.column: 0 } @@ -126,11 +120,9 @@ MySettingsTab { MySettings.device = deviceBox.currentText } } - Label { + MySettingsLabel { id: defaultModelLabel - text: qsTr("Default model:") - color: theme.textColor - font.pixelSize: theme.fontSizeLarge + text: qsTr("Default model") Layout.row: 4 Layout.column: 0 } @@ -161,11 +153,9 @@ MySettingsTab { MySettings.userDefaultModel = comboBox.currentText } } - Label { + MySettingsLabel { id: modelPathLabel - text: qsTr("Download path:") - color: theme.textColor - font.pixelSize: theme.fontSizeLarge + text: qsTr("Download path") Layout.row: 5 Layout.column: 0 } @@ -190,7 +180,7 @@ MySettingsTab { } } } - MyButton { + MySettingsButton { Layout.row: 5 Layout.column: 2 text: qsTr("Browse") @@ -201,11 +191,9 @@ MySettingsTab { }) } } - Label { + MySettingsLabel { id: nThreadsLabel - text: qsTr("CPU Threads:") - color: theme.textColor - font.pixelSize: theme.fontSizeLarge + text: qsTr("CPU Threads") Layout.row: 6 Layout.column: 0 } @@ -233,11 +221,9 @@ MySettingsTab { Accessible.name: nThreadsLabel.text Accessible.description: ToolTip.text } - Label { + MySettingsLabel { id: saveChatsContextLabel - text: qsTr("Save chats context to disk:") - color: theme.textColor - font.pixelSize: theme.fontSizeLarge + text: qsTr("Save chats context to disk") Layout.row: 7 Layout.column: 0 } @@ -252,11 +238,9 @@ MySettingsTab { ToolTip.text: qsTr("WARNING: Saving chats to disk can be ~2GB per chat") ToolTip.visible: hovered } - Label { + MySettingsLabel { id: serverChatLabel - text: qsTr("Enable API server:") - color: theme.textColor - font.pixelSize: theme.fontSizeLarge + text: qsTr("Enable API server") Layout.row: 8 Layout.column: 0 } @@ -276,8 +260,8 @@ MySettingsTab { Layout.column: 0 Layout.columnSpan: 3 Layout.fillWidth: true - height: 1 - color: theme.tabBorder + height: 3 + color: theme.yellowAccent } } advancedSettings: GridLayout { @@ -289,14 +273,12 @@ MySettingsTab { Layout.column: 0 Layout.fillWidth: true Layout.columnSpan: 3 - height: 1 - color: theme.tabBorder + height: 3 + color: theme.yellowAccent } - Label { + MySettingsLabel { id: gpuOverrideLabel - text: qsTr("Force Metal (macOS+arm):") - color: theme.textColor - font.pixelSize: theme.fontSizeLarge + text: qsTr("Force Metal (macOS+arm)") Layout.row: 1 Layout.column: 0 } @@ -316,7 +298,7 @@ MySettingsTab { Layout.fillWidth: true Layout.alignment: Qt.AlignTop Layout.minimumHeight: warningLabel.height - Label { + MySettingsLabel { id: warningLabel width: parent.width color: theme.textErrorColor diff --git a/gpt4all-chat/qml/ChatDrawer.qml b/gpt4all-chat/qml/ChatDrawer.qml index 181a174f09ac..fbc604426903 100644 --- a/gpt4all-chat/qml/ChatDrawer.qml +++ b/gpt4all-chat/qml/ChatDrawer.qml @@ -12,7 +12,6 @@ import mysettings Drawer { id: chatDrawer modal: false - opacity: 0.9 Theme { id: theme @@ -23,7 +22,7 @@ Drawer { background: Rectangle { height: parent.height - color: theme.backgroundDarkest + color: theme.containerBackground } Item { @@ -43,12 +42,6 @@ Drawer { bottomPadding: 20 text: qsTr("\uFF0B New chat") Accessible.description: qsTr("Create a new chat") - background: Rectangle { - border.color: newChat.down ? theme.backgroundLightest : theme.buttonBorder - border.width: 2 - radius: 10 - color: newChat.hovered ? theme.backgroundDark : theme.backgroundDarkest - } onClicked: { ChatListModel.addChat(); Network.sendNewChat(ChatListModel.count) @@ -71,19 +64,23 @@ Drawer { anchors.fill: parent anchors.rightMargin: 10 model: ChatListModel - ScrollBar.vertical: ScrollBar { policy: ScrollBar.AlwaysOn } + ScrollBar.vertical: ScrollBar { + parent: conversationList.parent + anchors.top: conversationList.top + anchors.left: conversationList.right + anchors.bottom: conversationList.bottom + } delegate: Rectangle { id: chatRectangle width: conversationList.width height: chatName.height - opacity: 0.9 property bool isCurrent: ChatListModel.currentChat === ChatListModel.get(index) property bool isServer: ChatListModel.get(index) && ChatListModel.get(index).isServer property bool trashQuestionDisplayed: false visible: !isServer || MySettings.serverChat z: isCurrent ? 199 : 1 - color: isServer ? theme.backgroundDarkest : (index % 2 === 0 ? theme.backgroundLight : theme.backgroundLighter) + color: index % 2 === 0 ? theme.darkContrast : theme.lightContrast border.width: isCurrent border.color: chatName.readOnly ? theme.assistantColor : theme.userColor TextField { @@ -106,7 +103,7 @@ Drawer { font: chatName.font text: name elide: Text.ElideRight - elideWidth: chatName.width - 25 + elideWidth: chatName.width - 40 } background: Rectangle { color: "transparent" diff --git a/gpt4all-chat/qml/CollectionsDialog.qml b/gpt4all-chat/qml/CollectionsDialog.qml index b763e6880a5c..a23db45e83db 100644 --- a/gpt4all-chat/qml/CollectionsDialog.qml +++ b/gpt4all-chat/qml/CollectionsDialog.qml @@ -22,9 +22,10 @@ MyDialog { id: listLabel anchors.top: parent.top anchors.left: parent.left - text: qsTr("Local Documents:") + text: qsTr("Local Documents") + color: theme.titleTextColor font.pixelSize: theme.fontSizeLarge - color: theme.textColor + font.bold: true } ScrollView { @@ -37,21 +38,28 @@ MyDialog { anchors.right: parent.right clip: true contentHeight: 300 - ScrollBar.vertical.policy: ScrollBar.AlwaysOn + ScrollBar.vertical.policy: ScrollBar.AlwaysOff background: Rectangle { - color: theme.backgroundLighter + color: theme.controlBackground } ListView { id: listView model: LocalDocs.localDocsModel boundsBehavior: Flickable.StopAtBounds + ScrollBar.vertical: ScrollBar { + parent: listView.parent + anchors.top: listView.top + anchors.left: listView.right + anchors.bottom: listView.bottom + } + delegate: Rectangle { id: item width: listView.width height: collectionId.height + 40 - color: index % 2 === 0 ? theme.backgroundLight : theme.backgroundLighter + color: index % 2 === 0 ? theme.darkContrast : theme.lightContrast MyCheckBox { id: checkBox anchors.verticalCenter: parent.verticalCenter @@ -90,7 +98,7 @@ MyDialog { value: (model.totalBytesToIndex - model.currentBytesToIndex) / model.totalBytesToIndex background: Rectangle { implicitHeight: 45 - color: theme.backgroundDarkest + color: theme.progressBackground radius: 3 } contentItem: Item { @@ -100,7 +108,7 @@ MyDialog { width: itemProgressBar.visualPosition * parent.width height: parent.height radius: 2 - color: theme.assistantColor + color: theme.progressForeground } } Accessible.role: Accessible.ProgressBar @@ -123,7 +131,7 @@ MyDialog { } } - MyButton { + MySettingsButton { id: collectionSettings anchors.bottom: parent.bottom anchors.horizontalCenter: parent.horizontalCenter diff --git a/gpt4all-chat/qml/LocalDocsSettings.qml b/gpt4all-chat/qml/LocalDocsSettings.qml index 52deb9415e81..ecb6012ee32c 100644 --- a/gpt4all-chat/qml/LocalDocsSettings.qml +++ b/gpt4all-chat/qml/LocalDocsSettings.qml @@ -26,7 +26,7 @@ MySettingsTab { property alias collection: collection.text property alias folder_path: folderEdit.text - Label { + MySettingsLabel { id: downloadLabel Layout.fillWidth: true Layout.maximumWidth: parent.width @@ -34,11 +34,9 @@ MySettingsTab { visible: !hasEmbeddingModel Layout.alignment: Qt.AlignLeft text: qsTr("This feature requires the download of a text embedding model in order to index documents for later search. Please download the SBert text embedding model from the download dialog to proceed.") - font.pixelSize: theme.fontSizeLarger - color: theme.textColor } - MyButton { + MySettingsButton { visible: !hasEmbeddingModel Layout.topMargin: 20 Layout.alignment: Qt.AlignLeft @@ -97,7 +95,7 @@ MySettingsTab { } } - MyButton { + MySettingsButton { id: browseButton text: qsTr("Browse") onClicked: { @@ -107,7 +105,7 @@ MySettingsTab { } } - MyButton { + MySettingsButton { id: addButton text: qsTr("Add") Accessible.role: Accessible.Button @@ -143,7 +141,7 @@ MySettingsTab { id: item Layout.fillWidth: true height: buttons.height + 20 - color: index % 2 === 0 ? theme.backgroundDark : theme.backgroundDarker + color: index % 2 === 0 ? theme.darkContrast : theme.lightContrast property bool removing: false Text { @@ -177,7 +175,7 @@ MySettingsTab { anchors.margins: 20 width: removeButton.width height:removeButton.height - MyButton { + MySettingsButton { id: removeButton anchors.centerIn: parent text: qsTr("Remove") @@ -194,11 +192,9 @@ MySettingsTab { RowLayout { visible: hasEmbeddingModel - Label { + MySettingsLabel { id: showReferencesLabel - text: qsTr("Show references:") - color: theme.textColor - font.pixelSize: theme.fontSizeLarge + text: qsTr("Show references") } MyCheckBox { id: showReferencesBox @@ -214,8 +210,8 @@ MySettingsTab { Rectangle { visible: hasEmbeddingModel Layout.fillWidth: true - height: 1 - color: theme.tabBorder + height: 3 + color: theme.yellowAccent } } advancedSettings: GridLayout { @@ -230,17 +226,15 @@ MySettingsTab { Layout.column: 0 Layout.fillWidth: true Layout.columnSpan: 3 - height: 1 - color: theme.tabBorder + height: 3 + color: theme.yellowAccent } - Label { + MySettingsLabel { id: chunkLabel Layout.row: 1 Layout.column: 0 - color: theme.textColor - font.pixelSize: theme.fontSizeLarge - text: qsTr("Document snippet size (characters):") + text: qsTr("Document snippet size (characters)") } MyTextField { @@ -264,13 +258,11 @@ MySettingsTab { } } - Label { + MySettingsLabel { id: contextItemsPerPrompt Layout.row: 2 Layout.column: 0 - color: theme.textColor - font.pixelSize: theme.fontSizeLarge - text: qsTr("Max document snippets per prompt:") + text: qsTr("Max document snippets per prompt") } MyTextField { @@ -300,7 +292,7 @@ MySettingsTab { Layout.fillWidth: true Layout.alignment: Qt.AlignTop Layout.minimumHeight: warningLabel.height - Label { + MySettingsLabel { id: warningLabel width: parent.width color: theme.textErrorColor diff --git a/gpt4all-chat/qml/ModelDownloaderDialog.qml b/gpt4all-chat/qml/ModelDownloaderDialog.qml index 3475114bdbaa..f0b071d6bdbd 100644 --- a/gpt4all-chat/qml/ModelDownloaderDialog.qml +++ b/gpt4all-chat/qml/ModelDownloaderDialog.qml @@ -41,11 +41,12 @@ MyDialog { Label { id: listLabel - text: qsTr("Available Models:") - font.pixelSize: theme.fontSizeLarge + text: qsTr("Available Models") Layout.alignment: Qt.AlignLeft Layout.fillWidth: true - color: theme.textColor + color: theme.titleTextColor + font.pixelSize: theme.fontSizeLarge + font.bold: true } Label { @@ -84,7 +85,7 @@ MyDialog { id: delegateItem width: modelListView.width height: childrenRect.height - color: index % 2 === 0 ? theme.backgroundLight : theme.backgroundLighter + color: index % 2 === 0 ? theme.darkContrast : theme.lightContrast GridLayout { columns: 2 @@ -99,7 +100,7 @@ MyDialog { Layout.topMargin: 20 Layout.leftMargin: 20 Layout.columnSpan: 2 - color: theme.assistantColor + color: theme.titleTextColor Accessible.role: Accessible.Paragraph Accessible.name: qsTr("Model file") Accessible.description: qsTr("Model file to be downloaded") @@ -108,7 +109,8 @@ MyDialog { Rectangle { id: actionBox width: childrenRect.width + 20 - color: theme.backgroundDark + color: theme.containerBackground + border.color: theme.yellowAccent border.width: 1 radius: 10 Layout.row: 1 @@ -122,7 +124,7 @@ MyDialog { ColumnLayout { spacing: 0 - MyButton { + MySettingsButton { id: downloadButton text: isDownloading ? qsTr("Cancel") : isIncomplete ? qsTr("Resume") : qsTr("Download") font.pixelSize: theme.fontSizeLarge @@ -133,12 +135,6 @@ MyDialog { Layout.alignment: Qt.AlignTop | Qt.AlignHCenter visible: !isChatGPT && !installed && !calcHash && downloadError === "" Accessible.description: qsTr("Stop/restart/start the download") - background: Rectangle { - border.color: downloadButton.down ? theme.backgroundLightest : theme.buttonBorder - border.width: 2 - radius: 10 - color: downloadButton.hovered ? theme.backgroundDark : theme.backgroundDarkest - } onClicked: { if (!isDownloading) { Download.downloadModel(filename); @@ -148,10 +144,9 @@ MyDialog { } } - MyButton { + MySettingsDestructiveButton { id: removeButton text: qsTr("Remove") - font.pixelSize: theme.fontSizeLarge Layout.topMargin: 20 Layout.leftMargin: 20 Layout.minimumWidth: openaiKey.width @@ -159,18 +154,12 @@ MyDialog { Layout.alignment: Qt.AlignTop | Qt.AlignHCenter visible: installed || downloadError !== "" Accessible.description: qsTr("Remove model from filesystem") - background: Rectangle { - border.color: removeButton.down ? theme.backgroundLightest : theme.buttonBorder - border.width: 2 - radius: 10 - color: removeButton.hovered ? theme.backgroundDark : theme.backgroundDarkest - } onClicked: { Download.removeModel(filename); } } - MyButton { + MySettingsButton { id: installButton visible: !installed && isChatGPT Layout.topMargin: 20 @@ -180,12 +169,6 @@ MyDialog { Layout.alignment: Qt.AlignTop | Qt.AlignHCenter text: qsTr("Install") font.pixelSize: theme.fontSizeLarge - background: Rectangle { - border.color: installButton.down ? theme.backgroundLightest : theme.buttonBorder - border.width: 2 - radius: 10 - color: installButton.hovered ? theme.backgroundDark : theme.backgroundDarkest - } onClicked: { if (openaiKey.text === "") openaiKey.showError(); @@ -285,7 +268,7 @@ MyDialog { value: bytesReceived / bytesTotal background: Rectangle { implicitHeight: 45 - color: theme.backgroundDarkest + color: theme.progressBackground radius: 3 } contentItem: Item { @@ -295,7 +278,7 @@ MyDialog { width: itemProgressBar.visualPosition * parent.width height: parent.height radius: 2 - color: theme.assistantColor + color: theme.progressForeground } } Accessible.role: Accessible.ProgressBar @@ -350,21 +333,14 @@ MyDialog { Layout.minimumWidth: 150 Layout.maximumWidth: textMetrics.width + 25 Layout.alignment: Qt.AlignTop | Qt.AlignHCenter - color: theme.textColor - background: Rectangle { - color: theme.backgroundLighter - radius: 10 - } wrapMode: Text.WrapAnywhere function showError() { openaiKey.placeholderTextColor = theme.textErrorColor } onTextChanged: { - openaiKey.placeholderTextColor = theme.backgroundLightest + openaiKey.placeholderTextColor = theme.mutedTextColor } placeholderText: qsTr("enter $OPENAI_API_KEY") - font.pixelSize: theme.fontSizeLarge - placeholderTextColor: theme.backgroundLightest Accessible.role: Accessible.EditableText Accessible.name: placeholderText Accessible.description: qsTr("Whether the file hash is being calculated") @@ -402,18 +378,12 @@ MyDialog { Rectangle { width: modelListView.width height: expandButton.height + 80 - color: ModelList.downloadableModels.count % 2 === 0 ? theme.backgroundLight : theme.backgroundLighter - MyButton { + color: ModelList.downloadableModels.count % 2 === 0 ? theme.darkContrast : theme.lightContrast + MySettingsButton { id: expandButton anchors.centerIn: parent padding: 40 text: ModelList.downloadableModels.expanded ? qsTr("Show fewer models") : qsTr("Show more models") - background: Rectangle { - border.color: expandButton.down ? theme.backgroundLightest : theme.buttonBorder - border.width: 2 - radius: 10 - color: expandButton.hovered ? theme.backgroundDark : theme.backgroundDarkest - } onClicked: { ModelList.downloadableModels.expanded = !ModelList.downloadableModels.expanded; } @@ -435,9 +405,9 @@ MyDialog { MySettings.modelPath = selectedFolder } } - Label { + MySettingsLabel { id: modelPathLabel - text: qsTr("Download path:") + text: qsTr("Download path") font.pixelSize: theme.fontSizeLarge color: theme.textColor Layout.row: 1 @@ -461,7 +431,7 @@ MyDialog { } } } - MyButton { + MySettingsButton { text: qsTr("Browse") Accessible.description: qsTr("Choose where to save model files") onClicked: modelPathDialog.open() diff --git a/gpt4all-chat/qml/ModelSettings.qml b/gpt4all-chat/qml/ModelSettings.qml index c9f46735ab02..21257d0995ed 100644 --- a/gpt4all-chat/qml/ModelSettings.qml +++ b/gpt4all-chat/qml/ModelSettings.qml @@ -21,13 +21,11 @@ MySettingsTab { property var currentModelId: comboBox.currentValue property var currentModelInfo: ModelList.modelInfo(root.currentModelId) - Label { + MySettingsLabel { id: label Layout.row: 0 Layout.column: 0 - text: qsTr("Model/Character:") - color: theme.textColor - font.pixelSize: theme.fontSizeLarge + text: qsTr("Model/Character") } RowLayout { @@ -64,13 +62,13 @@ MySettingsTab { verticalAlignment: Text.AlignVCenter } background: Rectangle { - color: highlighted ? theme.backgroundLight : theme.backgroundDark + color: highlighted ? theme.lightContrast : theme.darkContrast } highlighted: comboBox.highlightedIndex === index } } - MyButton { + MySettingsButton { id: cloneButton text: qsTr("Clone") onClicked: { @@ -79,7 +77,7 @@ MySettingsTab { } } - MyButton { + MySettingsDestructiveButton { id: removeButton enabled: root.currentModelInfo.isClone text: qsTr("Remove") @@ -95,18 +93,15 @@ MySettingsTab { Layout.column: 0 Layout.topMargin: 15 spacing: 10 - Label { + MySettingsLabel { id: uniqueNameLabel - text: qsTr("Unique Name:") - color: theme.textColor - font.pixelSize: theme.fontSizeLarge + text: qsTr("Unique Name") } - Label { + MySettingsLabel { id: uniqueNameLabelHelp visible: false text: qsTr("Must contain a non-empty unique name that does not match any existing model/character.") color: theme.textErrorColor - font.pixelSize: theme.fontSizeLarge wrapMode: TextArea.Wrap } } @@ -116,7 +111,6 @@ MySettingsTab { text: root.currentModelName font.pixelSize: theme.fontSizeLarge enabled: root.currentModelInfo.isClone || root.currentModelInfo.description === "" - color: enabled ? theme.textColor : theme.mutedTextColor Layout.row: 3 Layout.column: 0 Layout.columnSpan: 2 @@ -142,10 +136,8 @@ MySettingsTab { } } - Label { - text: qsTr("Model File:") - color: theme.textColor - font.pixelSize: theme.fontSizeLarge + MySettingsLabel { + text: qsTr("Model File") Layout.row: 4 Layout.column: 0 Layout.topMargin: 15 @@ -155,18 +147,15 @@ MySettingsTab { text: root.currentModelInfo.filename font.pixelSize: theme.fontSizeLarge enabled: false - color: enabled ? theme.textColor : theme.mutedTextColor Layout.row: 5 Layout.column: 0 Layout.columnSpan: 2 Layout.fillWidth: true } - Label { + MySettingsLabel { visible: !root.currentModelInfo.isChatGPT - text: qsTr("System Prompt:") - color: theme.textColor - font.pixelSize: theme.fontSizeLarge + text: qsTr("System Prompt") Layout.row: 6 Layout.column: 0 Layout.topMargin: 15 @@ -181,18 +170,10 @@ MySettingsTab { Layout.fillWidth: true color: "transparent" Layout.minimumHeight: Math.max(100, systemPromptArea.contentHeight + 20) - TextArea { + MyTextArea { id: systemPromptArea anchors.fill: parent text: root.currentModelInfo.systemPrompt - color: theme.textColor - background: Rectangle { - implicitWidth: 150 - color: theme.backgroundDark - radius: 10 - } - padding: 10 - wrapMode: TextArea.Wrap Connections { target: MySettings function onSystemPromptChanged() { @@ -208,10 +189,10 @@ MySettingsTab { onTextChanged: { MySettings.setModelSystemPrompt(root.currentModelInfo, text) } - bottomPadding: 10 Accessible.role: Accessible.EditableText ToolTip.text: qsTr("The systemPrompt allows instructions to the model at the beginning of a chat.\nNOTE: A longer, detailed system prompt can lead to higher quality answers, but can also slow down generation.") ToolTip.visible: hovered + ToolTip.delay: Qt.styleHints.mousePressAndHoldInterval } } @@ -221,17 +202,14 @@ MySettingsTab { Layout.columnSpan: 2 Layout.topMargin: 15 spacing: 10 - Label { + MySettingsLabel { id: promptTemplateLabel - text: qsTr("Prompt Template:") - color: theme.textColor - font.pixelSize: theme.fontSizeLarge + text: qsTr("Prompt Template") } - Label { + MySettingsLabel { id: promptTemplateLabelHelp text: qsTr("Must contain the string \"%1\" to be replaced with the user's input.") color: theme.textErrorColor - font.pixelSize: theme.fontSizeLarge visible: templateTextArea.text.indexOf("%1") === -1 wrapMode: TextArea.Wrap } @@ -246,19 +224,10 @@ MySettingsTab { Layout.minimumHeight: Math.max(100, templateTextArea.contentHeight + 20) color: "transparent" clip: true - TextArea { + MyTextArea { id: templateTextArea anchors.fill: parent text: root.currentModelInfo.promptTemplate - color: theme.textColor - font.pixelSize: theme.fontSizeLarge - background: Rectangle { - implicitWidth: 150 - color: theme.backgroundDark - radius: 10 - } - padding: 10 - wrapMode: TextArea.Wrap Connections { target: MySettings function onPromptTemplateChanged() { @@ -276,12 +245,12 @@ MySettingsTab { MySettings.setModelPromptTemplate(root.currentModelInfo, text) } } - bottomPadding: 10 Accessible.role: Accessible.EditableText Accessible.name: promptTemplateLabel.text Accessible.description: promptTemplateLabelHelp.text ToolTip.text: qsTr("The prompt template partially determines how models will respond to prompts.\nNOTE: A longer, detailed template can lead to higher quality answers, but can also slow down generation.") ToolTip.visible: hovered + ToolTip.delay: Qt.styleHints.mousePressAndHoldInterval } } @@ -298,8 +267,6 @@ MySettingsTab { Layout.bottomMargin: 35 Layout.leftMargin: 35 width: 3000 - border.width: 1 - border.color: theme.tabBorder radius: 10 color: "transparent" Item { @@ -324,9 +291,8 @@ MySettingsTab { } } - Label { + MySettingsLabel { text: qsTr("Generation Settings") - color: theme.textColor Layout.row: 10 Layout.column: 0 Layout.columnSpan: 2 @@ -334,7 +300,7 @@ MySettingsTab { Layout.alignment: Qt.AlignHCenter Layout.minimumWidth: promptTemplate.width horizontalAlignment: Qt.AlignHCenter - font.pixelSize: theme.fontSizeLarger + font.pixelSize: theme.fontSizeLarge font.bold: true } @@ -349,12 +315,10 @@ MySettingsTab { rowSpacing: 10 columnSpacing: 10 - Label { + MySettingsLabel { id: contextLengthLabel visible: !root.currentModelInfo.isChatGPT - text: qsTr("Context Length:") - font.pixelSize: theme.fontSizeLarge - color: theme.textColor + text: qsTr("Context Length") Layout.row: 0 Layout.column: 0 } @@ -362,8 +326,8 @@ MySettingsTab { id: contextLengthField visible: !root.currentModelInfo.isChatGPT text: root.currentModelInfo.contextLength - color: theme.textColor font.pixelSize: theme.fontSizeLarge + color: theme.textColor ToolTip.text: qsTr("Maximum combined prompt/response tokens before information is lost.\nUsing more context than the model was trained on will yield poor results.\nNOTE: Does not take effect until you RESTART GPT4All or SWITCH MODELS.") ToolTip.visible: hovered Layout.row: 0 @@ -397,11 +361,9 @@ MySettingsTab { Accessible.description: ToolTip.text } - Label { + MySettingsLabel { id: tempLabel - text: qsTr("Temperature:") - color: theme.textColor - font.pixelSize: theme.fontSizeLarge + text: qsTr("Temperature") Layout.row: 1 Layout.column: 2 } @@ -409,8 +371,8 @@ MySettingsTab { MyTextField { id: temperatureField text: root.currentModelInfo.temperature - color: theme.textColor font.pixelSize: theme.fontSizeLarge + color: theme.textColor ToolTip.text: qsTr("Temperature increases the chances of choosing less likely tokens.\nNOTE: Higher temperature gives more creative but less predictable outputs.") ToolTip.visible: hovered Layout.row: 1 @@ -443,11 +405,9 @@ MySettingsTab { Accessible.name: tempLabel.text Accessible.description: ToolTip.text } - Label { + MySettingsLabel { id: topPLabel - text: qsTr("Top P:") - color: theme.textColor - font.pixelSize: theme.fontSizeLarge + text: qsTr("Top P") Layout.row: 2 Layout.column: 0 } @@ -488,12 +448,10 @@ MySettingsTab { Accessible.name: topPLabel.text Accessible.description: ToolTip.text } - Label { + MySettingsLabel { id: topKLabel visible: !root.currentModelInfo.isChatGPT - text: qsTr("Top K:") - color: theme.textColor - font.pixelSize: theme.fontSizeLarge + text: qsTr("Top K") Layout.row: 2 Layout.column: 2 } @@ -535,12 +493,10 @@ MySettingsTab { Accessible.name: topKLabel.text Accessible.description: ToolTip.text } - Label { + MySettingsLabel { id: maxLengthLabel visible: !root.currentModelInfo.isChatGPT - text: qsTr("Max Length:") - color: theme.textColor - font.pixelSize: theme.fontSizeLarge + text: qsTr("Max Length") Layout.row: 0 Layout.column: 2 } @@ -583,12 +539,10 @@ MySettingsTab { Accessible.description: ToolTip.text } - Label { + MySettingsLabel { id: batchSizeLabel visible: !root.currentModelInfo.isChatGPT - text: qsTr("Prompt Batch Size:") - font.pixelSize: theme.fontSizeLarge - color: theme.textColor + text: qsTr("Prompt Batch Size") Layout.row: 1 Layout.column: 0 } @@ -630,12 +584,10 @@ MySettingsTab { Accessible.name: batchSizeLabel.text Accessible.description: ToolTip.text } - Label { + MySettingsLabel { id: repeatPenaltyLabel visible: !root.currentModelInfo.isChatGPT - text: qsTr("Repeat Penalty:") - color: theme.textColor - font.pixelSize: theme.fontSizeLarge + text: qsTr("Repeat Penalty") Layout.row: 3 Layout.column: 0 } @@ -677,12 +629,10 @@ MySettingsTab { Accessible.name: repeatPenaltyLabel.text Accessible.description: ToolTip.text } - Label { + MySettingsLabel { id: repeatPenaltyTokensLabel visible: !root.currentModelInfo.isChatGPT - text: qsTr("Repeat Penalty Tokens:") - color: theme.textColor - font.pixelSize: theme.fontSizeLarge + text: qsTr("Repeat Penalty Tokens") Layout.row: 3 Layout.column: 2 } @@ -733,8 +683,8 @@ MySettingsTab { Layout.topMargin: 15 Layout.fillWidth: true Layout.minimumWidth: promptTemplate.width - height: 1 - color: theme.tabBorder + height: 3 + color: theme.yellowAccent } } } diff --git a/gpt4all-chat/qml/MyBusyIndicator.qml b/gpt4all-chat/qml/MyBusyIndicator.qml index d04f943e533f..d8ec05d452ec 100644 --- a/gpt4all-chat/qml/MyBusyIndicator.qml +++ b/gpt4all-chat/qml/MyBusyIndicator.qml @@ -43,7 +43,7 @@ BusyIndicator { implicitWidth: 10 implicitHeight: 10 radius: 5 - color: theme.textAccent + color: theme.yellowAccent required property int index diff --git a/gpt4all-chat/qml/MyButton.qml b/gpt4all-chat/qml/MyButton.qml index a33f85d1d867..37dae0392ec0 100644 --- a/gpt4all-chat/qml/MyButton.qml +++ b/gpt4all-chat/qml/MyButton.qml @@ -6,20 +6,30 @@ import QtQuick.Controls.Basic Button { id: myButton padding: 10 + rightPadding: 18 + leftPadding: 18 + property color textColor: theme.oppositeTextColor + property color mutedTextColor: theme.oppositeMutedTextColor + property color backgroundColor: theme.buttonBackground + property color backgroundColorHovered: theme.buttonBackgroundHovered + property real borderWidth: 0 + property color borderColor: "transparent" + property real fontPixelSize: theme.fontSizeLarge contentItem: Text { text: myButton.text horizontalAlignment: Text.AlignHCenter - color: myButton.enabled ? theme.textColor : theme.mutedTextColor - font.pixelSize: theme.fontSizeLarge + color: myButton.enabled ? textColor : mutedTextColor + font.pixelSize: fontPixelSize Accessible.role: Accessible.Button Accessible.name: text } background: Rectangle { - border.color: myButton.down ? theme.backgroundLightest : theme.buttonBorder - border.width: 2 radius: 10 - color: myButton.hovered ? theme.backgroundDark : theme.backgroundDarkest + border.width: borderWidth + border.color: borderColor + color: myButton.hovered ? backgroundColorHovered : backgroundColor } Accessible.role: Accessible.Button Accessible.name: text + ToolTip.delay: Qt.styleHints.mousePressAndHoldInterval } diff --git a/gpt4all-chat/qml/MyCheckBox.qml b/gpt4all-chat/qml/MyCheckBox.qml index 6bf2242dc3c5..5f119dc7c735 100644 --- a/gpt4all-chat/qml/MyCheckBox.qml +++ b/gpt4all-chat/qml/MyCheckBox.qml @@ -15,15 +15,17 @@ CheckBox { implicitHeight: 26 x: myCheckBox.leftPadding y: parent.height / 2 - height / 2 - border.color: theme.dialogBorder - color: "transparent" + border.color: theme.gray200 + color: theme.white + radius: 3 Rectangle { width: 14 height: 14 x: 6 y: 6 - color: theme.textColor + radius: 2 + color: theme.green600 visible: myCheckBox.checked } } @@ -36,4 +38,5 @@ CheckBox { verticalAlignment: Text.AlignVCenter leftPadding: myCheckBox.indicator.width + myCheckBox.spacing } + ToolTip.delay: Qt.styleHints.mousePressAndHoldInterval } \ No newline at end of file diff --git a/gpt4all-chat/qml/MyComboBox.qml b/gpt4all-chat/qml/MyComboBox.qml index e55a5b0413cf..d0c9919f9ff2 100644 --- a/gpt4all-chat/qml/MyComboBox.qml +++ b/gpt4all-chat/qml/MyComboBox.qml @@ -28,7 +28,7 @@ ComboBox { verticalAlignment: Text.AlignVCenter } background: Rectangle { - color: highlighted ? theme.backgroundLight : theme.backgroundDark + color: highlighted ? theme.lightContrast : theme.darkContrast } highlighted: comboBox.highlightedIndex === index } @@ -47,7 +47,7 @@ ComboBox { } background: Rectangle { - color: theme.backgroundDark + color: theme.black } } indicator: Canvas { @@ -73,13 +73,16 @@ ComboBox { context.moveTo(0, height / 2 + 2); context.lineTo(width / 2, height); context.lineTo(width, height / 2 + 2); - context.strokeStyle = comboBox.pressed ? theme.textAccent : theme.mutedTextColor; + context.strokeStyle = comboBox.pressed ? theme.gray400 : theme.gray300; context.stroke(); } } background: Rectangle { - color: theme.backgroundDark + color: theme.controlBackground + border.width: 1 + border.color: theme.controlBorder radius: 10 } + ToolTip.delay: Qt.styleHints.mousePressAndHoldInterval } diff --git a/gpt4all-chat/qml/MyDialog.qml b/gpt4all-chat/qml/MyDialog.qml index 480848fd1203..4174bad12a80 100644 --- a/gpt4all-chat/qml/MyDialog.qml +++ b/gpt4all-chat/qml/MyDialog.qml @@ -11,7 +11,7 @@ Dialog { background: Rectangle { width: parent.width height: parent.height - color: theme.backgroundDarkest + color: theme.containerBackground border.width: 1 border.color: theme.dialogBorder radius: 10 @@ -23,7 +23,7 @@ Dialog { anchors.centerIn: myCloseButton width: myCloseButton.width + 10 height: myCloseButton.height + 10 - color: theme.backgroundDarkest + color: theme.containerBackground } MyToolButton { diff --git a/gpt4all-chat/qml/MyDirectoryField.qml b/gpt4all-chat/qml/MyDirectoryField.qml index ed61ca56bc45..2c9eea7279bd 100644 --- a/gpt4all-chat/qml/MyDirectoryField.qml +++ b/gpt4all-chat/qml/MyDirectoryField.qml @@ -11,7 +11,8 @@ TextField { color: text === "" || isValid ? theme.textColor : theme.textErrorColor background: Rectangle { implicitWidth: 150 - color: theme.backgroundDark + color: theme.controlBackground radius: 10 } + ToolTip.delay: Qt.styleHints.mousePressAndHoldInterval } diff --git a/gpt4all-chat/qml/MySettingsButton.qml b/gpt4all-chat/qml/MySettingsButton.qml new file mode 100644 index 000000000000..8d282cac928a --- /dev/null +++ b/gpt4all-chat/qml/MySettingsButton.qml @@ -0,0 +1,38 @@ +import QtCore +import QtQuick +import QtQuick.Controls +import QtQuick.Controls.Basic +import mysettings + +Button { + id: myButton + padding: 10 + rightPadding: 18 + leftPadding: 18 + property color textColor: MySettings.chatTheme === "Dark" ? theme.green800 : theme.green600 + property color mutedTextColor: textColor + property color backgroundColor: MySettings.chatTheme === "Dark" ? theme.green400 : theme.green200 + property color backgroundColorHovered: theme.green300 + property real borderWidth: 0 + property color borderColor: "transparent" + property real fontPixelSize: theme.fontSizeLarge + + contentItem: Text { + text: myButton.text + horizontalAlignment: Text.AlignHCenter + color: myButton.enabled ? textColor : mutedTextColor + font.pixelSize: fontPixelSize + font.bold: true + Accessible.role: Accessible.Button + Accessible.name: text + } + background: Rectangle { + radius: 10 + border.width: borderWidth + border.color: borderColor + color: myButton.hovered ? backgroundColorHovered : backgroundColor + } + Accessible.role: Accessible.Button + Accessible.name: text + ToolTip.delay: Qt.styleHints.mousePressAndHoldInterval +} diff --git a/gpt4all-chat/qml/MySettingsDestructiveButton.qml b/gpt4all-chat/qml/MySettingsDestructiveButton.qml new file mode 100644 index 000000000000..041947d827c8 --- /dev/null +++ b/gpt4all-chat/qml/MySettingsDestructiveButton.qml @@ -0,0 +1,38 @@ +import QtCore +import QtQuick +import QtQuick.Controls +import QtQuick.Controls.Basic +import mysettings + +Button { + id: myButton + padding: 10 + rightPadding: 18 + leftPadding: 18 + font.pixelSize: theme.fontSizeLarge + property color textColor: MySettings.chatTheme === "Dark" ? theme.red800 : theme.red600 + property color mutedTextColor: MySettings.chatTheme === "Dark" ? theme.red400 : theme.red300 + property color backgroundColor: enabled ? (MySettings.chatTheme === "Dark" ? theme.red400 : theme.red200) : + (MySettings.chatTheme === "Dark" ? theme.red200 : theme.red100) + property color backgroundColorHovered: enabled ? (MySettings.chatTheme === "Dark" ? theme.red500 : theme.red300) : backgroundColor + property real borderWidth: 0 + property color borderColor: "transparent" + contentItem: Text { + text: myButton.text + horizontalAlignment: Text.AlignHCenter + color: myButton.enabled ? textColor : mutedTextColor + font.pixelSize: theme.fontSizeLarge + font.bold: true + Accessible.role: Accessible.Button + Accessible.name: text + } + background: Rectangle { + radius: 10 + border.width: borderWidth + border.color: borderColor + color: myButton.hovered ? backgroundColorHovered : backgroundColor + } + Accessible.role: Accessible.Button + Accessible.name: text + ToolTip.delay: Qt.styleHints.mousePressAndHoldInterval +} diff --git a/gpt4all-chat/qml/MySettingsLabel.qml b/gpt4all-chat/qml/MySettingsLabel.qml new file mode 100644 index 000000000000..e3aece07038b --- /dev/null +++ b/gpt4all-chat/qml/MySettingsLabel.qml @@ -0,0 +1,10 @@ +import QtCore +import QtQuick +import QtQuick.Controls +import QtQuick.Controls.Basic + +Label { + color: theme.titleTextColor + font.pixelSize: theme.fontSizeLarge - 4 + font.bold: true +} diff --git a/gpt4all-chat/qml/MySettingsStack.qml b/gpt4all-chat/qml/MySettingsStack.qml index cd3a559b50de..cb8e8f9d21c8 100644 --- a/gpt4all-chat/qml/MySettingsStack.qml +++ b/gpt4all-chat/qml/MySettingsStack.qml @@ -14,28 +14,38 @@ Item { id: theme } - property alias title: titleLabel.text + property alias title: titleLabelText.text property ListModel tabTitlesModel: ListModel { } property list tabs: [ ] - Label { + Rectangle { id: titleLabel anchors.top: parent.top - anchors.horizontalCenter: parent.horizontalCenter - color: theme.textColor - padding: 10 - font.bold: true - font.pixelSize: theme.fontSizeLarger + anchors.leftMargin: 20 + anchors.rightMargin: 15 + anchors.left: parent.left + anchors.right: parent.right + height: titleLabelText.height + color: "transparent" + Label { + id: titleLabelText + anchors.left: parent.left + color: theme.titleTextColor + topPadding: 10 + bottomPadding: 10 + font.pixelSize: theme.fontSizeLargest + font.bold: true + } } Rectangle { anchors.top: titleLabel.bottom - anchors.leftMargin: 15 + anchors.leftMargin: 20 anchors.rightMargin: 15 anchors.left: parent.left anchors.right: parent.right - height: 1 - color: theme.tabBorder + height: 3 + color: theme.yellowAccent } TabBar { @@ -62,9 +72,6 @@ Item { } background: Rectangle { color: "transparent" - border.width: 1 - border.color: tabButton.checked ? theme.tabBorder : "transparent" - radius: 10 } Accessible.role: Accessible.Button Accessible.name: model.title @@ -82,16 +89,8 @@ Item { anchors.rightMargin: 15 anchors.left: parent.left anchors.right: parent.right - height: 1 - color: theme.tabBorder - } - - Rectangle { - anchors.fill: parent - color: "transparent" - radius: 10 - border.width: 1 - border.color: theme.tabBorder + height: 3 + color: theme.yellowAccent } FolderDialog { diff --git a/gpt4all-chat/qml/MySettingsTab.qml b/gpt4all-chat/qml/MySettingsTab.qml index 7d47eb4f8e6a..32e153951262 100644 --- a/gpt4all-chat/qml/MySettingsTab.qml +++ b/gpt4all-chat/qml/MySettingsTab.qml @@ -32,13 +32,19 @@ Item { } ScrollView { + id: scrollView width: parent.width height: parent.height - padding: 15 - rightPadding: 20 + topPadding: 15 + leftPadding: 5 contentWidth: availableWidth contentHeight: innerColumn.height - ScrollBar.vertical.policy: ScrollBar.AlwaysOn + ScrollBar.vertical: ScrollBar { + parent: scrollView.parent + anchors.top: scrollView.top + anchors.left: scrollView.right + anchors.bottom: scrollView.bottom + } Theme { id: theme @@ -64,7 +70,7 @@ Item { Item { Layout.fillWidth: true height: restoreDefaultsButton.height - MyButton { + MySettingsButton { id: restoreDefaultsButton anchors.left: parent.left visible: showRestoreDefaultsButton @@ -78,7 +84,7 @@ Item { root.restoreDefaultsClicked(); } } - MyButton { + MySettingsButton { id: advancedSettingsButton anchors.right: parent.right visible: root.advancedSettings && showAdvancedSettingsButton diff --git a/gpt4all-chat/qml/MySlug.qml b/gpt4all-chat/qml/MySlug.qml new file mode 100644 index 000000000000..48cf0ac553b8 --- /dev/null +++ b/gpt4all-chat/qml/MySlug.qml @@ -0,0 +1,24 @@ +import QtCore +import QtQuick +import QtQuick.Controls +import QtQuick.Controls.Basic + +Label { + id: mySlug + padding: 3 + rightPadding: 9 + leftPadding: 9 + font.pixelSize: theme.fontSizeFixedSmall + background: Rectangle { + radius: 6 + border.width: 1 + border.color: mySlug.color + color: theme.slugBackground + } + ToolTip.visible: ma.containsMouse && ToolTip.text !== "" + MouseArea { + id: ma + anchors.fill: parent + hoverEnabled: true + } +} diff --git a/gpt4all-chat/qml/MyTextArea.qml b/gpt4all-chat/qml/MyTextArea.qml new file mode 100644 index 000000000000..e0894e9fbb0f --- /dev/null +++ b/gpt4all-chat/qml/MyTextArea.qml @@ -0,0 +1,22 @@ +import QtCore +import QtQuick +import QtQuick.Controls +import QtQuick.Controls.Basic + +TextArea { + id: myTextArea + color: enabled ? theme.textColor : theme.mutedTextColor + placeholderTextColor: theme.mutedTextColor + font.pixelSize: theme.fontSizeLarge + background: Rectangle { + implicitWidth: 150 + color: theme.controlBackground + border.width: 1 + border.color: theme.controlBorder + radius: 10 + } + padding: 10 + wrapMode: TextArea.Wrap + + ToolTip.delay: Qt.styleHints.mousePressAndHoldInterval +} \ No newline at end of file diff --git a/gpt4all-chat/qml/MyTextField.qml b/gpt4all-chat/qml/MyTextField.qml index 251aaf09aa73..730e33edc421 100644 --- a/gpt4all-chat/qml/MyTextField.qml +++ b/gpt4all-chat/qml/MyTextField.qml @@ -6,10 +6,14 @@ import QtQuick.Controls.Basic TextField { id: myTextField padding: 10 + placeholderTextColor: theme.mutedTextColor background: Rectangle { implicitWidth: 150 - color: theme.backgroundDark + color: myTextField.enabled ? theme.controlBackground : theme.disabledControlBackground + border.width: 1 + border.color: theme.controlBorder radius: 10 } - color: theme.textColor -} \ No newline at end of file + ToolTip.delay: Qt.styleHints.mousePressAndHoldInterval + color: enabled ? theme.textColor : theme.mutedTextColor +} diff --git a/gpt4all-chat/qml/MyToolButton.qml b/gpt4all-chat/qml/MyToolButton.qml index 7236b648cfa2..326582987dbc 100644 --- a/gpt4all-chat/qml/MyToolButton.qml +++ b/gpt4all-chat/qml/MyToolButton.qml @@ -7,6 +7,8 @@ import Qt5Compat.GraphicalEffects Button { id: myButton padding: 10 + property color backgroundColor: theme.iconBackgroundDark + property color backgroundColorHovered: theme.iconBackgroundHovered property bool toggled: false property alias source: image.source property alias fillMode: image.fillMode @@ -25,7 +27,7 @@ Button { anchors.fill: parent color: "transparent" visible: myButton.toggled - border.color: theme.backgroundLightest + border.color: theme.yellowAccent border.width: 1 radius: 10 } @@ -39,9 +41,10 @@ Button { ColorOverlay { anchors.fill: image source: image - color: myButton.hovered ? theme.textColor : "transparent" + color: myButton.hovered ? backgroundColorHovered : backgroundColor } } Accessible.role: Accessible.Button Accessible.name: text + ToolTip.delay: Qt.styleHints.mousePressAndHoldInterval } diff --git a/gpt4all-chat/qml/NetworkDialog.qml b/gpt4all-chat/qml/NetworkDialog.qml index e1fdc917e63b..c5755c8bd092 100644 --- a/gpt4all-chat/qml/NetworkDialog.qml +++ b/gpt4all-chat/qml/NetworkDialog.qml @@ -12,7 +12,6 @@ MyDialog { id: networkDialog anchors.centerIn: parent modal: true - opacity: 0.9 padding: 20 Theme { @@ -50,43 +49,27 @@ MyDialog { ScrollBar.vertical.policy: ScrollBar.AlwaysOn ScrollBar.horizontal.policy: ScrollBar.AlwaysOff - TextArea { + MyTextArea { id: textOptIn - wrapMode: Text.Wrap width: 1024 - 40 - padding: 20 text: qsTr("By enabling this feature, you will be able to participate in the democratic process of training a large language model by contributing data for future model improvements. When a GPT4All model responds to you and you have opted-in, your conversation will be sent to the GPT4All Open Source Datalake. Additionally, you can like/dislike its response. If you dislike a response, you can suggest an alternative response. This data will be collected and aggregated in the GPT4All Datalake. NOTE: By turning on this feature, you will be sending your data to the GPT4All Open Source Datalake. You should have no expectation of chat privacy when this feature is enabled. You should; however, have an expectation of an optional attribution if you wish. Your chat data will be openly available for anyone to download and will be used by Nomic AI to improve future GPT4All models. Nomic AI will retain all attribution information attached to your data and you will be credited as a contributor to any GPT4All model release that uses your data!") - color: theme.textColor - font.pixelSize: theme.fontSizeLarge focus: false readOnly: true Accessible.role: Accessible.Paragraph Accessible.name: qsTr("Terms for opt-in") Accessible.description: qsTr("Describes what will happen when you opt-in") - background: Rectangle { - color: theme.backgroundLight - radius: 10 - } } } - TextField { + MyTextField { id: attribution - color: theme.textColor - padding: 20 width: parent.width text: MySettings.networkAttribution - font.pixelSize: theme.fontSizeLarge placeholderText: qsTr("Please provide a name for attribution (optional)") - placeholderTextColor: theme.backgroundLightest - background: Rectangle { - color: theme.backgroundLighter - radius: 10 - } Accessible.role: Accessible.EditableText Accessible.name: qsTr("Attribution (optional)") Accessible.description: qsTr("Provide attribution") @@ -101,12 +84,12 @@ NOTE: By turning on this feature, you will be sending your data to the GPT4All O padding: 20 alignment: Qt.AlignRight spacing: 10 - MyButton { + MySettingsButton { text: qsTr("Enable") Accessible.description: qsTr("Enable opt-in") DialogButtonBox.buttonRole: DialogButtonBox.AcceptRole } - MyButton { + MySettingsButton { text: qsTr("Cancel") Accessible.description: qsTr("Cancel opt-in") DialogButtonBox.buttonRole: DialogButtonBox.RejectRole diff --git a/gpt4all-chat/qml/NewVersionDialog.qml b/gpt4all-chat/qml/NewVersionDialog.qml index 0d925e4c5dfa..7ade2ca978c6 100644 --- a/gpt4all-chat/qml/NewVersionDialog.qml +++ b/gpt4all-chat/qml/NewVersionDialog.qml @@ -31,12 +31,13 @@ MyDialog { anchors.left: parent.left topPadding: 20 bottomPadding: 20 - text: qsTr("New version is available:") - color: theme.textColor + text: qsTr("New version is available") + color: theme.titleTextColor font.pixelSize: theme.fontSizeLarge + font.bold: true } - MyButton { + MySettingsButton { id: button anchors.left: label.right anchors.leftMargin: 10 diff --git a/gpt4all-chat/qml/PopupDialog.qml b/gpt4all-chat/qml/PopupDialog.qml index 6c67bdb246d1..d542b04ade56 100644 --- a/gpt4all-chat/qml/PopupDialog.qml +++ b/gpt4all-chat/qml/PopupDialog.qml @@ -7,7 +7,6 @@ import QtQuick.Layouts Dialog { id: popupDialog anchors.centerIn: parent - opacity: 0.9 padding: 20 property alias text: textField.text property bool shouldTimeOut: true @@ -53,7 +52,7 @@ Dialog { background: Rectangle { anchors.fill: parent - color: theme.backgroundDarkest + color: theme.containerBackground border.width: 1 border.color: theme.dialogBorder radius: 10 diff --git a/gpt4all-chat/qml/SettingsDialog.qml b/gpt4all-chat/qml/SettingsDialog.qml index d8120753f1e2..440a1d37b4f2 100644 --- a/gpt4all-chat/qml/SettingsDialog.qml +++ b/gpt4all-chat/qml/SettingsDialog.qml @@ -41,46 +41,52 @@ MyDialog { } } - ScrollView { + Rectangle { id: stackList anchors.top: parent.top anchors.bottom: parent.bottom anchors.left: parent.left - width: 200 - ScrollBar.vertical.policy: ScrollBar.AsNeeded - clip: true + width: 220 + color: theme.controlBackground + radius: 10 - ListView { - id: listView - anchors.fill: parent - anchors.rightMargin: 10 - model: stacksModel + ScrollView { + anchors.top: parent.top + anchors.bottom: parent.bottom + anchors.left: parent.left + anchors.right: parent.right + anchors.topMargin: 10 + ScrollBar.vertical.policy: ScrollBar.AsNeeded + clip: true - delegate: Rectangle { - id: item - width: listView.width - height: titleLabel.height + 25 - color: "transparent" - border.color: theme.backgroundLighter - border.width: index == listView.currentIndex ? 1 : 0 - radius: 10 + ListView { + id: listView + anchors.fill: parent + model: stacksModel - Text { - id: titleLabel - anchors.verticalCenter: parent.verticalCenter - anchors.left: parent.left - anchors.margins: 20 - font.bold: index == listView.currentIndex - text: title - font.pixelSize: theme.fontSizeLarge - elide: Text.ElideRight - color: theme.textColor - width: 200 - } + delegate: Rectangle { + id: item + width: listView.width + height: titleLabel.height + 10 + color: "transparent" - TapHandler { - onTapped: { - listView.currentIndex = index + MyButton { + id: titleLabel + backgroundColor: index === listView.currentIndex ? theme.buttonBackground : theme.controlBackground + backgroundColorHovered: index === listView.currentIndex ? backgroundColor : theme.containerBackground + borderColor: index === listView.currentIndex ? theme.yellowAccent : "transparent" + borderWidth: index === listView.currentIndex ? 1 : 0 + textColor: index === listView.currentIndex ? theme.oppositeTextColor : theme.titleTextColor + anchors.verticalCenter: parent.verticalCenter + anchors.left: parent.left + anchors.right: parent.right + anchors.margins: 10 + font.bold: index === listView.currentIndex + text: title + font.pixelSize: theme.fontSizeLarge + onClicked: { + listView.currentIndex = index + } } } } diff --git a/gpt4all-chat/qml/StartupDialog.qml b/gpt4all-chat/qml/StartupDialog.qml index f0f2ebcb483d..115d37386803 100644 --- a/gpt4all-chat/qml/StartupDialog.qml +++ b/gpt4all-chat/qml/StartupDialog.qml @@ -52,27 +52,19 @@ MyDialog { ScrollBar.vertical.policy: ScrollBar.AlwaysOn ScrollBar.horizontal.policy: ScrollBar.AlwaysOff - TextArea { + MyTextArea { id: welcome - wrapMode: Text.Wrap width: 1024 - 40 - padding: 20 textFormat: TextEdit.MarkdownText text: qsTr("### Release notes\n") + Download.releaseInfo.notes + qsTr("### Contributors\n") + Download.releaseInfo.contributors - color: theme.textColor - font.pixelSize: theme.fontSizeLarge focus: false readOnly: true Accessible.role: Accessible.Paragraph Accessible.name: qsTr("Release notes") Accessible.description: qsTr("Release notes for this version") - background: Rectangle { - color: theme.backgroundLight - radius: 10 - } } } @@ -83,11 +75,9 @@ MyDialog { ScrollBar.vertical.policy: ScrollBar.AlwaysOn ScrollBar.horizontal.policy: ScrollBar.AlwaysOff - TextArea { + MyTextArea { id: optInTerms - wrapMode: Text.Wrap width: 1024 - 40 - padding: 20 textFormat: TextEdit.MarkdownText text: qsTr( "### Opt-ins for anonymous usage analytics and datalake @@ -105,17 +95,11 @@ to download and will be used by Nomic AI to improve future GPT4All models. Nomic attribution information attached to your data and you will be credited as a contributor to any GPT4All model release that uses your data!") - color: theme.textColor - font.pixelSize: theme.fontSizeLarge focus: false readOnly: true Accessible.role: Accessible.Paragraph Accessible.name: qsTr("Terms for opt-in") Accessible.description: qsTr("Describes what will happen when you opt-in") - background: Rectangle { - color: theme.backgroundLight - radius: 10 - } } } diff --git a/gpt4all-chat/qml/Theme.qml b/gpt4all-chat/qml/Theme.qml index d500f629d973..127a2a907149 100644 --- a/gpt4all-chat/qml/Theme.qml +++ b/gpt4all-chat/qml/Theme.qml @@ -4,24 +4,146 @@ import QtQuick.Controls.Basic import mysettings QtObject { - property color textColor: MySettings.chatTheme == "Dark" ? "#d1d5db" : "#2e2e34" - property color textAccent: MySettings.chatTheme == "Dark" ? "#8e8ea0" : "#71717f" - property color mutedTextColor: MySettings.chatTheme == "Dark" ? backgroundLightest : "#AFAFB5" - property color backgroundDarkest: MySettings.chatTheme == "Dark" ? "#1c1f21" : "#e3e3e5" - property color backgroundDarker: MySettings.chatTheme == "Dark" ? "#1e2123" : "#e0dedc" - property color backgroundDark: MySettings.chatTheme == "Dark" ? "#222527" : "#D2D1D5" - property color backgroundLight: MySettings.chatTheme == "Dark" ? "#343541" : "#FFFFFF" - property color backgroundLighter: MySettings.chatTheme == "Dark" ? "#444654" : "#F7F7F8" - property color backgroundLightest: MySettings.chatTheme == "Dark" ? "#7d7d8e" : "#82827a" - property color backgroundAccent: MySettings.chatTheme == "Dark" ? "#40414f" : "#E3E3E6" - property color buttonBorder: MySettings.chatTheme == "Dark" ? "#565869" : "#a9a9b0" - property color dialogBorder: MySettings.chatTheme == "Dark" ? "#d1d5db" : "#2e2e34" - property color userColor: MySettings.chatTheme == "Dark" ? "#ec86bf" : "#137382" - property color linkColor: MySettings.chatTheme == "Dark" ? "#55aaff" : "#aa5500" - property color tabBorder: MySettings.chatTheme == "Dark" ? backgroundLight : backgroundDark - property color assistantColor: "#10a37f" - property color textErrorColor: "red" - property real fontSizeLarge: MySettings.fontSize == "Small" ? Qt.application.font.pixelSize : MySettings.fontSize == "Medium" ? Qt.application.font.pixelSize + 5 : Qt.application.font.pixelSize + 10 - property real fontSizeLarger: MySettings.fontSize == "Small" ? Qt.application.font.pixelSize + 2 : MySettings.fontSize == "Medium" ? Qt.application.font.pixelSize + 7 : Qt.application.font.pixelSize + 12 -} + // black and white + property color black: Qt.hsla(231/360, 0.15, 0.19) + property color white: Qt.hsla(0, 0, 1) + + // dark mode black and white + property color darkwhite: Qt.hsla(0, 0, 0.85) + + // gray + property color gray0: white + property color gray50: Qt.hsla(25/360, 0.05, 0.97) + property color gray100: Qt.hsla(25/360,0.05, 0.95) + property color gray200: Qt.hsla(25/360, 0.05, 0.89) + property color gray300: Qt.hsla(25/360, 0.05, 0.82) + property color gray400: Qt.hsla(25/360, 0.05, 0.71) + property color gray500: Qt.hsla(25/360, 0.05, 0.60) + property color gray600: Qt.hsla(25/360, 0.05, 0.51) + property color gray700: Qt.hsla(25/360, 0.05, 0.42) + property color gray800: Qt.hsla(25/360, 0.05, 0.35) + property color gray900: Qt.hsla(25/360, 0.05, 0.31) + property color gray950: Qt.hsla(25/360, 0.05, 0.15) + + // darkmode + property color darkgray0: Qt.hsla(25/360, 0.05, 0.23) + property color darkgray50: Qt.hsla(25/360, 0.05, 0.21) + property color darkgray100: Qt.hsla(25/360, 0.05, 0.19) + property color darkgray200: Qt.hsla(25/360, 0.05, 0.17) + property color darkgray300: Qt.hsla(25/360, 0.05, 0.15) + property color darkgray400: Qt.hsla(25/360, 0.05, 0.13) + property color darkgray500: Qt.hsla(25/360, 0.05, 0.11) + property color darkgray600: Qt.hsla(25/360, 0.05, 0.09) + property color darkgray700: Qt.hsla(25/360, 0.05, 0.07) + property color darkgray800: Qt.hsla(25/360, 0.05, 0.05) + property color darkgray900: Qt.hsla(25/360, 0.05, 0.03) + property color darkgray950: Qt.hsla(25/360, 0.05, 0.01) + + // green + property color green50: Qt.hsla(120/360, 0.18, 0.97) + property color green100: Qt.hsla(120/360, 0.21, 0.93) + property color green200: Qt.hsla(124/360, 0.21, 0.85) + property color green300: Qt.hsla(122/360, 0.20, 0.73) + property color green400: Qt.hsla(122/360, 0.19, 0.58) + property color green500: Qt.hsla(121/360, 0.19, 0.45) + property color green600: Qt.hsla(122/360, 0.20, 0.33) + property color green700: Qt.hsla(122/360, 0.19, 0.29) + property color green800: Qt.hsla(123/360, 0.17, 0.24) + property color green900: Qt.hsla(124/360, 0.17, 0.20) + property color green950: Qt.hsla(125/360, 0.22, 0.10) + + // yellow + property color yellow50: Qt.hsla(47/360, 0.90, 0.96) + property color yellow100: Qt.hsla(46/360, 0.89, 0.89) + property color yellow200: Qt.hsla(45/360, 0.90, 0.77) + property color yellow300: Qt.hsla(44/360, 0.90, 0.66) + property color yellow400: Qt.hsla(41/360, 0.89, 0.56) + property color yellow500: Qt.hsla(36/360, 0.85, 0.50) + property color yellow600: Qt.hsla(30/360, 0.87, 0.44) + property color yellow700: Qt.hsla(24/360, 0.84, 0.37) + property color yellow800: Qt.hsla(21/360, 0.76, 0.31) + property color yellow900: Qt.hsla(20/360, 0.72, 0.26) + property color yellow950: Qt.hsla(19/360, 0.86, 0.14) + + // red + property color red50: Qt.hsla(0, 0.71, 0.97) + property color red100: Qt.hsla(0, 0.87, 0.94) + property color red200: Qt.hsla(0, 0.89, 0.89) + property color red300: Qt.hsla(0, 0.85, 0.77) + property color red400: Qt.hsla(0, 0.83, 0.71) + property color red500: Qt.hsla(0, 0.76, 0.60) + property color red600: Qt.hsla(0, 0.65, 0.51) + property color red700: Qt.hsla(0, 0.67, 0.42) + property color red800: Qt.hsla(0, 0.63, 0.35) + property color red900: Qt.hsla(0, 0.56, 0.31) + property color red950: Qt.hsla(0, 0.67, 0.15) + + // purple + property color purple400: Qt.hsla(279/360, 1.0, 0.73) + property color purple500: Qt.hsla(279/360, 1.0, 0.63) + property color purple600: Qt.hsla(279/360, 1.0, 0.53) + + property color yellowAccent: MySettings.chatTheme === "Dark" ? yellow300 : yellow300; + property color orangeAccent: MySettings.chatTheme === "Dark" ? yellow500 : yellow500; + + property color darkContrast: MySettings.chatTheme === "Dark" ? darkgray100 : gray100 + property color lightContrast: MySettings.chatTheme === "Dark" ? darkgray0 : gray0 + + property color controlBorder: MySettings.chatTheme === "Dark" ? darkgray0 : gray300 + property color controlBackground: MySettings.chatTheme === "Dark" ? darkgray100 : gray100 + property color disabledControlBackground: MySettings.chatTheme === "Dark" ? darkgray200 : gray200 + property color containerForeground: MySettings.chatTheme === "Dark" ? darkgray300 : gray300 + property color containerBackground: MySettings.chatTheme === "Dark" ? darkgray200 : gray200 + + property color progressForeground: yellowAccent + property color progressBackground: MySettings.chatTheme === "Dark" ? green600 : green600 + + property color buttonBackground: MySettings.chatTheme === "Dark" ? green700 : green700 + property color buttonBackgroundHovered: MySettings.chatTheme === "Dark" ? green500 : green500 + property color buttonBorder: MySettings.chatTheme === "Dark" ? yellow200 : yellow200 + + property color sendButtonBackground: yellowAccent + property color sendButtonBackgroundHovered: MySettings.chatTheme === "Dark" ? darkwhite : black + + property color conversationButtonBackground: MySettings.chatTheme === "Dark" ? darkgray100 : gray0 + property color conversationButtonBackgroundHovered: MySettings.chatTheme === "Dark" ? darkgray0 : gray100 + property color conversationButtonBorder: yellow200 + + property color iconBackgroundDark: MySettings.chatTheme === "Dark" ? green400 : green400 + property color iconBackgroundLight: MySettings.chatTheme === "Dark" ? darkwhite : white + property color iconBackgroundHovered: yellowAccent; + + property color slugBackground: MySettings.chatTheme === "Dark" ? darkgray300 : gray100 + + property color textColor: MySettings.chatTheme === "Dark" ? darkwhite : black + property color mutedTextColor: MySettings.chatTheme === "Dark" ? gray400 : gray600 + property color oppositeTextColor: MySettings.chatTheme === "Dark" ? darkwhite : white + property color oppositeMutedTextColor: MySettings.chatTheme === "Dark" ? darkwhite : white + property color textAccent: yellowAccent + property color textErrorColor: MySettings.chatTheme === "Dark" ? red400 : red400 + property color titleTextColor: MySettings.chatTheme === "Dark" ? green400 : green700 + + property color dialogBorder: MySettings.chatTheme === "Dark" ? darkgray0 : darkgray0 + property color linkColor: MySettings.chatTheme === "Dark" ? yellow600 : yellow600 + + property color mainHeader: MySettings.chatTheme === "Dark" ? green600 : green600 + property color mainComboBackground: MySettings.chatTheme === "Dark" ? green700 : green700 + property color sendGlow: MySettings.chatTheme === "Dark" ? green950 : green300 + + property color userColor: MySettings.chatTheme === "Dark" ? green700 : green700 + property color assistantColor: yellowAccent + + property real fontSizeFixedSmall: 16 + + property real fontSizeSmall: fontSizeLarge - 4 + property real fontSizeLarge: MySettings.fontSize === "Small" ? + Qt.application.font.pixelSize : MySettings.fontSize === "Medium" ? + Qt.application.font.pixelSize + 5 : Qt.application.font.pixelSize + 10 + property real fontSizeLarger: MySettings.fontSize === "Small" ? + Qt.application.font.pixelSize + 2 : MySettings.fontSize === "Medium" ? + Qt.application.font.pixelSize + 7 : Qt.application.font.pixelSize + 12 + property real fontSizeLargest: MySettings.fontSize === "Small" ? + Qt.application.font.pixelSize + 7 : MySettings.fontSize === "Medium" ? + Qt.application.font.pixelSize + 12 : Qt.application.font.pixelSize + 14 +} diff --git a/gpt4all-chat/qml/ThumbsDownDialog.qml b/gpt4all-chat/qml/ThumbsDownDialog.qml index cce91683ecc7..49f5831b7c6e 100644 --- a/gpt4all-chat/qml/ThumbsDownDialog.qml +++ b/gpt4all-chat/qml/ThumbsDownDialog.qml @@ -10,7 +10,6 @@ import llm MyDialog { id: thumbsDownDialog modal: true - opacity: 0.9 padding: 20 Theme { @@ -50,18 +49,9 @@ MyDialog { ScrollBar.vertical.policy: ScrollBar.AlwaysOn ScrollBar.horizontal.policy: ScrollBar.AlwaysOff - TextArea { + MyTextArea { id: thumbsDownNewResponse - color: theme.textColor - padding: 20 - wrapMode: Text.Wrap - font.pixelSize: theme.fontSizeLarge placeholderText: qsTr("Please provide a better response...") - placeholderTextColor: theme.backgroundLightest - background: Rectangle { - color: theme.backgroundLighter - radius: 10 - } } } } @@ -70,15 +60,13 @@ MyDialog { padding: 20 alignment: Qt.AlignRight spacing: 10 - MyButton { + MySettingsButton { text: qsTr("Submit") - font.pixelSize: theme.fontSizeLarge Accessible.description: qsTr("Submits the user's response") DialogButtonBox.buttonRole: DialogButtonBox.AcceptRole } - MyButton { + MySettingsButton { text: qsTr("Cancel") - font.pixelSize: theme.fontSizeLarge Accessible.description: qsTr("Closes the response dialog") DialogButtonBox.buttonRole: DialogButtonBox.RejectRole } From 6abeefb303e24343d691b4e00885bdbc1b35460b Mon Sep 17 00:00:00 2001 From: Adam Treat Date: Fri, 26 Jan 2024 17:07:48 -0500 Subject: [PATCH 07/27] Hover for links and increase font size a bit. Signed-off-by: Adam Treat --- gpt4all-chat/main.qml | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/gpt4all-chat/main.qml b/gpt4all-chat/main.qml index 4d9ef6fabc99..a686543db5d0 100644 --- a/gpt4all-chat/main.qml +++ b/gpt4all-chat/main.qml @@ -226,7 +226,8 @@ Window { horizontalAlignment: Text.AlignLeft font.pixelSize: theme.fontSizeFixedSmall color: theme.gray300 - linkColor: theme.gray300 + linkColor: hoverHandler1.hovered ? theme.yellowAccent : theme.gray300 + HoverHandler { id: hoverHandler1 } onLinkActivated: { Qt.openUrlExternally("https://gpt4all.io") } } @@ -236,7 +237,8 @@ Window { horizontalAlignment: Text.AlignLeft font.pixelSize: theme.fontSizeFixedSmall color: theme.gray300 - linkColor: theme.gray300 + linkColor: hoverHandler2.hovered ? theme.yellowAccent : theme.gray300 + HoverHandler { id: hoverHandler2 } onLinkActivated: { Qt.openUrlExternally("https://github.com/nomic-ai/gpt4all") } } } @@ -251,7 +253,8 @@ Window { horizontalAlignment: Text.AlignRight font.pixelSize: theme.fontSizeFixedSmall color: theme.gray300 - linkColor: theme.gray300 + linkColor: hoverHandler3.hovered ? theme.yellowAccent : theme.gray300 + HoverHandler { id: hoverHandler3 } onLinkActivated: { Qt.openUrlExternally("https://nomic.ai") } } @@ -261,7 +264,8 @@ Window { horizontalAlignment: Text.AlignRight font.pixelSize: theme.fontSizeFixedSmall color: theme.gray300 - linkColor: theme.gray300 + linkColor: hoverHandler4.hovered ? theme.yellowAccent : theme.gray300 + HoverHandler { id: hoverHandler4 } onLinkActivated: { Qt.openUrlExternally("https://twitter.com/nomic_ai") } } @@ -271,7 +275,8 @@ Window { horizontalAlignment: Text.AlignRight font.pixelSize: theme.fontSizeFixedSmall color: theme.gray300 - linkColor: theme.gray300 + linkColor: hoverHandler5.hovered ? theme.yellowAccent : theme.gray300 + HoverHandler { id: hoverHandler5 } onLinkActivated: { Qt.openUrlExternally("https://discord.gg/4M2QFmTt2k") } } } @@ -816,7 +821,7 @@ Window { ") color: theme.textColor - font.pixelSize: theme.fontSizeSmall + font.pixelSize: theme.fontSizeLarge wrapMode: Text.WordWrap } From 363f6659e4ded6e741cc61dcc4e85856186f848b Mon Sep 17 00:00:00 2001 From: Adam Treat Date: Fri, 26 Jan 2024 17:09:18 -0500 Subject: [PATCH 08/27] Fix the settings font size to be a tad bigger. Signed-off-by: Adam Treat --- gpt4all-chat/qml/Theme.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gpt4all-chat/qml/Theme.qml b/gpt4all-chat/qml/Theme.qml index 127a2a907149..dc5cca03f220 100644 --- a/gpt4all-chat/qml/Theme.qml +++ b/gpt4all-chat/qml/Theme.qml @@ -136,7 +136,7 @@ QtObject { property real fontSizeFixedSmall: 16 - property real fontSizeSmall: fontSizeLarge - 4 + property real fontSizeSmall: fontSizeLarge - 2 property real fontSizeLarge: MySettings.fontSize === "Small" ? Qt.application.font.pixelSize : MySettings.fontSize === "Medium" ? Qt.application.font.pixelSize + 5 : Qt.application.font.pixelSize + 10 From c1fce502f77105b52e53c142ebe1cb9f2b3dabf3 Mon Sep 17 00:00:00 2001 From: Adam Treat Date: Fri, 26 Jan 2024 17:13:39 -0500 Subject: [PATCH 09/27] Fix checkbox background in dark mode. Signed-off-by: Adam Treat --- gpt4all-chat/qml/MyCheckBox.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gpt4all-chat/qml/MyCheckBox.qml b/gpt4all-chat/qml/MyCheckBox.qml index 5f119dc7c735..90dd5d9e3d16 100644 --- a/gpt4all-chat/qml/MyCheckBox.qml +++ b/gpt4all-chat/qml/MyCheckBox.qml @@ -16,7 +16,7 @@ CheckBox { x: myCheckBox.leftPadding y: parent.height / 2 - height / 2 border.color: theme.gray200 - color: theme.white + color: theme.containerBackground radius: 3 Rectangle { From 34de19ebf69275e8ce19cf94d2baaa176efdfa74 Mon Sep 17 00:00:00 2001 From: Adam Treat Date: Mon, 29 Jan 2024 11:12:12 -0500 Subject: [PATCH 10/27] Add a legacy dark mode. Signed-off-by: Adam Treat --- gpt4all-chat/main.qml | 36 +- gpt4all-chat/qml/ApplicationSettings.qml | 8 +- gpt4all-chat/qml/LocalDocsSettings.qml | 4 +- gpt4all-chat/qml/ModelDownloaderDialog.qml | 7 +- gpt4all-chat/qml/ModelSettings.qml | 2 +- gpt4all-chat/qml/MyBusyIndicator.qml | 2 +- gpt4all-chat/qml/MyButton.qml | 9 +- gpt4all-chat/qml/MyCheckBox.qml | 6 +- gpt4all-chat/qml/MySettingsLabel.qml | 2 +- gpt4all-chat/qml/MySettingsStack.qml | 4 +- gpt4all-chat/qml/MyToolButton.qml | 2 +- gpt4all-chat/qml/SettingsDialog.qml | 2 +- gpt4all-chat/qml/Theme.qml | 485 +++++++++++++++++++-- 13 files changed, 491 insertions(+), 78 deletions(-) diff --git a/gpt4all-chat/main.qml b/gpt4all-chat/main.qml index a686543db5d0..817db9edc7e4 100644 --- a/gpt4all-chat/main.qml +++ b/gpt4all-chat/main.qml @@ -198,12 +198,12 @@ Window { } Rectangle { - id: yellowRibbon + id: accentRibbon anchors.left: parent.left anchors.right: parent.right anchors.top: header.bottom height: 3 - color: theme.yellowAccent + color: theme.accentColor } Rectangle { @@ -225,8 +225,8 @@ Window { text: "gpt4all.io |" horizontalAlignment: Text.AlignLeft font.pixelSize: theme.fontSizeFixedSmall - color: theme.gray300 - linkColor: hoverHandler1.hovered ? theme.yellowAccent : theme.gray300 + color: theme.iconBackgroundHovered + linkColor: hoverHandler1.hovered ? theme.accentColor : theme.iconBackgroundHovered HoverHandler { id: hoverHandler1 } onLinkActivated: { Qt.openUrlExternally("https://gpt4all.io") } } @@ -236,8 +236,8 @@ Window { text: "github" horizontalAlignment: Text.AlignLeft font.pixelSize: theme.fontSizeFixedSmall - color: theme.gray300 - linkColor: hoverHandler2.hovered ? theme.yellowAccent : theme.gray300 + color: theme.iconBackgroundHovered + linkColor: hoverHandler2.hovered ? theme.accentColor : theme.iconBackgroundHovered HoverHandler { id: hoverHandler2 } onLinkActivated: { Qt.openUrlExternally("https://github.com/nomic-ai/gpt4all") } } @@ -252,8 +252,8 @@ Window { text: "nomic.ai |" horizontalAlignment: Text.AlignRight font.pixelSize: theme.fontSizeFixedSmall - color: theme.gray300 - linkColor: hoverHandler3.hovered ? theme.yellowAccent : theme.gray300 + color: theme.iconBackgroundHovered + linkColor: hoverHandler3.hovered ? theme.accentColor : theme.iconBackgroundHovered HoverHandler { id: hoverHandler3 } onLinkActivated: { Qt.openUrlExternally("https://nomic.ai") } } @@ -263,8 +263,8 @@ Window { text: "twitter |" horizontalAlignment: Text.AlignRight font.pixelSize: theme.fontSizeFixedSmall - color: theme.gray300 - linkColor: hoverHandler4.hovered ? theme.yellowAccent : theme.gray300 + color: theme.iconBackgroundHovered + linkColor: hoverHandler4.hovered ? theme.accentColor : theme.iconBackgroundHovered HoverHandler { id: hoverHandler4 } onLinkActivated: { Qt.openUrlExternally("https://twitter.com/nomic_ai") } } @@ -274,8 +274,8 @@ Window { text: "discord" horizontalAlignment: Text.AlignRight font.pixelSize: theme.fontSizeFixedSmall - color: theme.gray300 - linkColor: hoverHandler5.hovered ? theme.yellowAccent : theme.gray300 + color: theme.iconBackgroundHovered + linkColor: hoverHandler5.hovered ? theme.accentColor : theme.iconBackgroundHovered HoverHandler { id: hoverHandler5 } onLinkActivated: { Qt.openUrlExternally("https://discord.gg/4M2QFmTt2k") } } @@ -364,7 +364,7 @@ Window { background: Rectangle { color: (index % 2 === 0 ? theme.darkContrast : theme.lightContrast) border.width: highlighted - border.color: theme.yellowAccent + border.color: theme.accentColor } highlighted: comboBox.highlightedIndex === index } @@ -747,7 +747,7 @@ Window { ChatDrawer { id: drawer - y: header.height + yellowRibbon.height + y: header.height + accentRibbon.height width: Math.min(600, 0.3 * window.width) height: window.height - y onDownloadClicked: { @@ -769,11 +769,11 @@ Window { Rectangle { id: conversation - color: theme.containerBackground + color: theme.conversationBackground anchors.left: parent.left anchors.right: parent.right anchors.bottom: parent.bottom - anchors.top: yellowRibbon.bottom + anchors.top: accentRibbon.bottom ScrollView { id: scrollView @@ -786,7 +786,7 @@ Window { Rectangle { anchors.fill: parent - color: currentChat.isServer ? theme.black : theme.containerBackground + color: currentChat.isServer ? theme.black : theme.conversationBackground Rectangle { id: homePage @@ -888,7 +888,7 @@ Window { ColorOverlay { anchors.fill: image source: image - color: theme.yellowAccent + color: theme.accentColor } onClicked: { downloadNewModels.open(); diff --git a/gpt4all-chat/qml/ApplicationSettings.qml b/gpt4all-chat/qml/ApplicationSettings.qml index 6d9251d374a6..5a0ef4434c8e 100644 --- a/gpt4all-chat/qml/ApplicationSettings.qml +++ b/gpt4all-chat/qml/ApplicationSettings.qml @@ -29,9 +29,9 @@ MySettingsTab { Layout.row: 1 Layout.column: 1 Layout.columnSpan: 1 - Layout.minimumWidth: 50 + Layout.minimumWidth: 200 Layout.fillWidth: false - model: ["Dark", "Light"] + model: ["Dark", "Light", "LegacyDark"] Accessible.role: Accessible.ComboBox Accessible.name: qsTr("Color theme") Accessible.description: qsTr("Color theme for the chat client to use") @@ -261,7 +261,7 @@ MySettingsTab { Layout.columnSpan: 3 Layout.fillWidth: true height: 3 - color: theme.yellowAccent + color: theme.accentColor } } advancedSettings: GridLayout { @@ -274,7 +274,7 @@ MySettingsTab { Layout.fillWidth: true Layout.columnSpan: 3 height: 3 - color: theme.yellowAccent + color: theme.accentColor } MySettingsLabel { id: gpuOverrideLabel diff --git a/gpt4all-chat/qml/LocalDocsSettings.qml b/gpt4all-chat/qml/LocalDocsSettings.qml index ecb6012ee32c..c9ac4abd09ba 100644 --- a/gpt4all-chat/qml/LocalDocsSettings.qml +++ b/gpt4all-chat/qml/LocalDocsSettings.qml @@ -211,7 +211,7 @@ MySettingsTab { visible: hasEmbeddingModel Layout.fillWidth: true height: 3 - color: theme.yellowAccent + color: theme.accentColor } } advancedSettings: GridLayout { @@ -227,7 +227,7 @@ MySettingsTab { Layout.fillWidth: true Layout.columnSpan: 3 height: 3 - color: theme.yellowAccent + color: theme.accentColor } MySettingsLabel { diff --git a/gpt4all-chat/qml/ModelDownloaderDialog.qml b/gpt4all-chat/qml/ModelDownloaderDialog.qml index f0b071d6bdbd..8a79a7cba12f 100644 --- a/gpt4all-chat/qml/ModelDownloaderDialog.qml +++ b/gpt4all-chat/qml/ModelDownloaderDialog.qml @@ -42,6 +42,7 @@ MyDialog { Label { id: listLabel text: qsTr("Available Models") + visible: false Layout.alignment: Qt.AlignLeft Layout.fillWidth: true color: theme.titleTextColor @@ -49,6 +50,10 @@ MyDialog { font.bold: true } + Item { + height: 0 // for visible space between close button and rest of dialog + } + Label { visible: !ModelList.downloadableModels.count && !ModelList.asyncModelRequestOngoing Layout.fillWidth: true @@ -110,7 +115,7 @@ MyDialog { id: actionBox width: childrenRect.width + 20 color: theme.containerBackground - border.color: theme.yellowAccent + border.color: theme.accentColor border.width: 1 radius: 10 Layout.row: 1 diff --git a/gpt4all-chat/qml/ModelSettings.qml b/gpt4all-chat/qml/ModelSettings.qml index 21257d0995ed..1c9b0249581b 100644 --- a/gpt4all-chat/qml/ModelSettings.qml +++ b/gpt4all-chat/qml/ModelSettings.qml @@ -684,7 +684,7 @@ MySettingsTab { Layout.fillWidth: true Layout.minimumWidth: promptTemplate.width height: 3 - color: theme.yellowAccent + color: theme.accentColor } } } diff --git a/gpt4all-chat/qml/MyBusyIndicator.qml b/gpt4all-chat/qml/MyBusyIndicator.qml index d8ec05d452ec..6369471d8dad 100644 --- a/gpt4all-chat/qml/MyBusyIndicator.qml +++ b/gpt4all-chat/qml/MyBusyIndicator.qml @@ -43,7 +43,7 @@ BusyIndicator { implicitWidth: 10 implicitHeight: 10 radius: 5 - color: theme.yellowAccent + color: theme.accentColor required property int index diff --git a/gpt4all-chat/qml/MyButton.qml b/gpt4all-chat/qml/MyButton.qml index 37dae0392ec0..d79c275b1e1b 100644 --- a/gpt4all-chat/qml/MyButton.qml +++ b/gpt4all-chat/qml/MyButton.qml @@ -2,6 +2,7 @@ import QtCore import QtQuick import QtQuick.Controls import QtQuick.Controls.Basic +import mysettings Button { id: myButton @@ -12,8 +13,8 @@ Button { property color mutedTextColor: theme.oppositeMutedTextColor property color backgroundColor: theme.buttonBackground property color backgroundColorHovered: theme.buttonBackgroundHovered - property real borderWidth: 0 - property color borderColor: "transparent" + property real borderWidth: MySettings.chatTheme === "LegacyDark" ? 1 : 0 + property color borderColor: theme.buttonBorder property real fontPixelSize: theme.fontSizeLarge contentItem: Text { text: myButton.text @@ -25,8 +26,8 @@ Button { } background: Rectangle { radius: 10 - border.width: borderWidth - border.color: borderColor + border.width: myButton.borderWidth + border.color: myButton.borderColor color: myButton.hovered ? backgroundColorHovered : backgroundColor } Accessible.role: Accessible.Button diff --git a/gpt4all-chat/qml/MyCheckBox.qml b/gpt4all-chat/qml/MyCheckBox.qml index 90dd5d9e3d16..9cf16bfb39c6 100644 --- a/gpt4all-chat/qml/MyCheckBox.qml +++ b/gpt4all-chat/qml/MyCheckBox.qml @@ -15,8 +15,8 @@ CheckBox { implicitHeight: 26 x: myCheckBox.leftPadding y: parent.height / 2 - height / 2 - border.color: theme.gray200 - color: theme.containerBackground + border.color: theme.checkboxBorder + color: "transparent" radius: 3 Rectangle { @@ -25,7 +25,7 @@ CheckBox { x: 6 y: 6 radius: 2 - color: theme.green600 + color: theme.checkboxForeground visible: myCheckBox.checked } } diff --git a/gpt4all-chat/qml/MySettingsLabel.qml b/gpt4all-chat/qml/MySettingsLabel.qml index e3aece07038b..64f0626f5618 100644 --- a/gpt4all-chat/qml/MySettingsLabel.qml +++ b/gpt4all-chat/qml/MySettingsLabel.qml @@ -4,7 +4,7 @@ import QtQuick.Controls import QtQuick.Controls.Basic Label { - color: theme.titleTextColor + color: theme.settingsTitleTextColor font.pixelSize: theme.fontSizeLarge - 4 font.bold: true } diff --git a/gpt4all-chat/qml/MySettingsStack.qml b/gpt4all-chat/qml/MySettingsStack.qml index cb8e8f9d21c8..1c25996e5f00 100644 --- a/gpt4all-chat/qml/MySettingsStack.qml +++ b/gpt4all-chat/qml/MySettingsStack.qml @@ -45,7 +45,7 @@ Item { anchors.left: parent.left anchors.right: parent.right height: 3 - color: theme.yellowAccent + color: theme.accentColor } TabBar { @@ -90,7 +90,7 @@ Item { anchors.left: parent.left anchors.right: parent.right height: 3 - color: theme.yellowAccent + color: theme.accentColor } FolderDialog { diff --git a/gpt4all-chat/qml/MyToolButton.qml b/gpt4all-chat/qml/MyToolButton.qml index 326582987dbc..2e79e37b8a6a 100644 --- a/gpt4all-chat/qml/MyToolButton.qml +++ b/gpt4all-chat/qml/MyToolButton.qml @@ -27,7 +27,7 @@ Button { anchors.fill: parent color: "transparent" visible: myButton.toggled - border.color: theme.yellowAccent + border.color: theme.accentColor border.width: 1 radius: 10 } diff --git a/gpt4all-chat/qml/SettingsDialog.qml b/gpt4all-chat/qml/SettingsDialog.qml index 440a1d37b4f2..ec8cdb4a2f28 100644 --- a/gpt4all-chat/qml/SettingsDialog.qml +++ b/gpt4all-chat/qml/SettingsDialog.qml @@ -74,7 +74,7 @@ MyDialog { id: titleLabel backgroundColor: index === listView.currentIndex ? theme.buttonBackground : theme.controlBackground backgroundColorHovered: index === listView.currentIndex ? backgroundColor : theme.containerBackground - borderColor: index === listView.currentIndex ? theme.yellowAccent : "transparent" + borderColor: index === listView.currentIndex ? theme.accentColor : "transparent" borderWidth: index === listView.currentIndex ? 1 : 0 textColor: index === listView.currentIndex ? theme.oppositeTextColor : theme.titleTextColor anchors.verticalCenter: parent.verticalCenter diff --git a/gpt4all-chat/qml/Theme.qml b/gpt4all-chat/qml/Theme.qml index dc5cca03f220..67cb4fc60cb9 100644 --- a/gpt4all-chat/qml/Theme.qml +++ b/gpt4all-chat/qml/Theme.qml @@ -12,7 +12,7 @@ QtObject { // dark mode black and white property color darkwhite: Qt.hsla(0, 0, 0.85) - // gray + // gray // FIXME: These are slightly less red than what atlas uses. should resolve diff property color gray0: white property color gray50: Qt.hsla(25/360, 0.05, 0.97) property color gray100: Qt.hsla(25/360,0.05, 0.95) @@ -79,60 +79,467 @@ QtObject { property color red900: Qt.hsla(0, 0.56, 0.31) property color red950: Qt.hsla(0, 0.67, 0.15) - // purple + // purple // FIXME: These are slightly more uniform than what atlas uses. should resolve diff + property color purple50: Qt.hsla(279/360, 1.0, 0.98) + property color purple100: Qt.hsla(279/360, 1.0, 0.95) + property color purple200: Qt.hsla(279/360, 1.0, 0.91) + property color purple300: Qt.hsla(279/360, 1.0, 0.84) property color purple400: Qt.hsla(279/360, 1.0, 0.73) property color purple500: Qt.hsla(279/360, 1.0, 0.63) property color purple600: Qt.hsla(279/360, 1.0, 0.53) + property color purple700: Qt.hsla(279/360, 1.0, 0.47) + property color purple800: Qt.hsla(279/360, 1.0, 0.39) + property color purple900: Qt.hsla(279/360, 1.0, 0.32) + property color purple950: Qt.hsla(279/360, 1.0, 0.22) - property color yellowAccent: MySettings.chatTheme === "Dark" ? yellow300 : yellow300; - property color orangeAccent: MySettings.chatTheme === "Dark" ? yellow500 : yellow500; + property color blue0: "#d0d5db" + property color blue100: "#8e8ea0" + property color blue200: "#7d7d8e" + property color blue400: "#444654" + property color blue500: "#343541" + property color blue600: "#2c2d37" + property color blue800: "#232628" + property color blue900: "#222527" + property color blue950: "#1c1f21" + property color blue1000: "#0e1011" - property color darkContrast: MySettings.chatTheme === "Dark" ? darkgray100 : gray100 - property color lightContrast: MySettings.chatTheme === "Dark" ? darkgray0 : gray0 + property color accentColor: { + switch (MySettings.chatTheme) { + case "LegacyDark": + return blue200; + case "Dark": + return yellow300; + default: + return yellow300; + } + } - property color controlBorder: MySettings.chatTheme === "Dark" ? darkgray0 : gray300 - property color controlBackground: MySettings.chatTheme === "Dark" ? darkgray100 : gray100 - property color disabledControlBackground: MySettings.chatTheme === "Dark" ? darkgray200 : gray200 - property color containerForeground: MySettings.chatTheme === "Dark" ? darkgray300 : gray300 - property color containerBackground: MySettings.chatTheme === "Dark" ? darkgray200 : gray200 + property color darkContrast: { + switch (MySettings.chatTheme) { + case "LegacyDark": + return blue500; + case "Dark": + return darkgray100; + default: + return gray100; + } + } - property color progressForeground: yellowAccent - property color progressBackground: MySettings.chatTheme === "Dark" ? green600 : green600 + property color lightContrast: { + switch (MySettings.chatTheme) { + case "LegacyDark": + return blue400; + case "Dark": + return darkgray0; + default: + return gray0; + } + } - property color buttonBackground: MySettings.chatTheme === "Dark" ? green700 : green700 - property color buttonBackgroundHovered: MySettings.chatTheme === "Dark" ? green500 : green500 - property color buttonBorder: MySettings.chatTheme === "Dark" ? yellow200 : yellow200 + property color controlBorder: { + switch (MySettings.chatTheme) { + case "LegacyDark": + return blue800; + case "Dark": + return darkgray0; + default: + return gray300; + } + } - property color sendButtonBackground: yellowAccent - property color sendButtonBackgroundHovered: MySettings.chatTheme === "Dark" ? darkwhite : black + property color controlBackground: { + switch (MySettings.chatTheme) { + case "LegacyDark": + return blue950; + case "Dark": + return darkgray100; + default: + return gray100; + } + } - property color conversationButtonBackground: MySettings.chatTheme === "Dark" ? darkgray100 : gray0 - property color conversationButtonBackgroundHovered: MySettings.chatTheme === "Dark" ? darkgray0 : gray100 - property color conversationButtonBorder: yellow200 + property color disabledControlBackground: { + switch (MySettings.chatTheme) { + case "LegacyDark": + return blue950; + case "Dark": + return darkgray200; + default: + return gray200; + } + } - property color iconBackgroundDark: MySettings.chatTheme === "Dark" ? green400 : green400 - property color iconBackgroundLight: MySettings.chatTheme === "Dark" ? darkwhite : white - property color iconBackgroundHovered: yellowAccent; + property color conversationBackground: { + switch (MySettings.chatTheme) { + case "LegacyDark": + return blue500; + default: + return containerBackground; + } + } - property color slugBackground: MySettings.chatTheme === "Dark" ? darkgray300 : gray100 + property color containerForeground: { + switch (MySettings.chatTheme) { + case "LegacyDark": + return blue950; + case "Dark": + return darkgray300; + default: + return gray300; + } + } - property color textColor: MySettings.chatTheme === "Dark" ? darkwhite : black - property color mutedTextColor: MySettings.chatTheme === "Dark" ? gray400 : gray600 - property color oppositeTextColor: MySettings.chatTheme === "Dark" ? darkwhite : white - property color oppositeMutedTextColor: MySettings.chatTheme === "Dark" ? darkwhite : white - property color textAccent: yellowAccent - property color textErrorColor: MySettings.chatTheme === "Dark" ? red400 : red400 - property color titleTextColor: MySettings.chatTheme === "Dark" ? green400 : green700 + property color containerBackground: { + switch (MySettings.chatTheme) { + case "LegacyDark": + return blue900; + case "Dark": + return darkgray200; + default: + return gray200; + } + } - property color dialogBorder: MySettings.chatTheme === "Dark" ? darkgray0 : darkgray0 - property color linkColor: MySettings.chatTheme === "Dark" ? yellow600 : yellow600 + property color progressForeground: { + switch (MySettings.chatTheme) { + case "LegacyDark": + return accentColor; + case "Dark": + return accentColor; + default: + return accentColor; + } + } - property color mainHeader: MySettings.chatTheme === "Dark" ? green600 : green600 - property color mainComboBackground: MySettings.chatTheme === "Dark" ? green700 : green700 - property color sendGlow: MySettings.chatTheme === "Dark" ? green950 : green300 + property color progressBackground: { + switch (MySettings.chatTheme) { + case "LegacyDark": + return blue900; + case "Dark": + return green600; + default: + return green600; + } + } - property color userColor: MySettings.chatTheme === "Dark" ? green700 : green700 - property color assistantColor: yellowAccent + property color checkboxBorder: { + switch (MySettings.chatTheme) { + case "LegacyDark": + return accentColor; + case "Dark": + return gray200; + default: + return gray600; + } + } + + property color checkboxForeground: { + switch (MySettings.chatTheme) { + case "LegacyDark": + return accentColor; + case "Dark": + return green600; + default: + return green600; + } + } + + property color buttonBackground: { + switch (MySettings.chatTheme) { + case "LegacyDark": + return blue950; + case "Dark": + return green700; + default: + return green700; + } + } + + property color buttonBackgroundHovered: { + switch (MySettings.chatTheme) { + case "LegacyDark": + return blue900; + case "Dark": + return green500; + default: + return green500; + } + } + + property color buttonBorder: { + switch (MySettings.chatTheme) { + case "LegacyDark": + return accentColor; + case "Dark": + return yellow200; + default: + return yellow200; + } + } + + property color sendButtonBackground: { + switch (MySettings.chatTheme) { + case "LegacyDark": + return accentColor; + case "Dark": + return accentColor; + default: + return accentColor; + } + } + + property color sendButtonBackgroundHovered: { + switch (MySettings.chatTheme) { + case "LegacyDark": + return blue0; + case "Dark": + return darkwhite; + default: + return black; + } + } + + property color conversationButtonBackground: { + switch (MySettings.chatTheme) { + case "LegacyDark": + return blue500; + case "Dark": + return darkgray100; + default: + return gray0; + } + } + + property color conversationButtonBackgroundHovered: { + switch (MySettings.chatTheme) { + case "LegacyDark": + return blue400; + case "Dark": + return darkgray0; + default: + return gray100; + } + } + + property color conversationButtonBorder: { + switch (MySettings.chatTheme) { + case "LegacyDark": + return accentColor; + case "Dark": + return yellow200; + default: + return yellow200; + } + } + + property color iconBackgroundDark: { + switch (MySettings.chatTheme) { + case "LegacyDark": + return blue200; + case "Dark": + return green400; + default: + return green400; + } + } + + property color iconBackgroundLight: { + switch (MySettings.chatTheme) { + case "LegacyDark": + return blue200; + case "Dark": + return darkwhite; + default: + return white; + } + } + + property color iconBackgroundHovered: { + switch (MySettings.chatTheme) { + case "LegacyDark": + return blue0; + case "Dark": + return accentColor; + default: + return accentColor; + } + } + + property color slugBackground: { + switch (MySettings.chatTheme) { + case "LegacyDark": + return blue600; + case "Dark": + return darkgray300; + default: + return gray100; + } + } + + property color textColor: { + switch (MySettings.chatTheme) { + case "LegacyDark": + return blue0; + case "Dark": + return darkwhite; + default: + return black; + } + } + + property color mutedTextColor: { + switch (MySettings.chatTheme) { + case "LegacyDark": + return blue200; + case "Dark": + return gray400; + default: + return gray600; + } + } + + property color oppositeTextColor: { + switch (MySettings.chatTheme) { + case "LegacyDark": + return white; + case "Dark": + return darkwhite; + default: + return white; + } + } + + property color oppositeMutedTextColor: { + switch (MySettings.chatTheme) { + case "LegacyDark": + return white; + case "Dark": + return darkwhite; + default: + return white; + } + } + + property color textAccent: { + switch (MySettings.chatTheme) { + case "LegacyDark": + return accentColor; + case "Dark": + return accentColor; + default: + return accentColor; + } + } + + property color textErrorColor: { + switch (MySettings.chatTheme) { + case "LegacyDark": + return red400; + case "Dark": + return red400; + default: + return red400; + } + } + + property color settingsTitleTextColor: { + switch (MySettings.chatTheme) { + case "LegacyDark": + return blue100; + case "Dark": + return green400; + default: + return green700; + } + } + + property color titleTextColor: { + switch (MySettings.chatTheme) { + case "LegacyDark": + return purple400; + case "Dark": + return green400; + default: + return green700; + } + } + + property color dialogBorder: { + switch (MySettings.chatTheme) { + case "LegacyDark": + return accentColor; + case "Dark": + return darkgray0; + default: + return darkgray0; + } + } + + property color linkColor: { + switch (MySettings.chatTheme) { + case "LegacyDark": + return yellow600; + case "Dark": + return yellow600; + default: + return yellow600; + } + } + + property color mainHeader: { + switch (MySettings.chatTheme) { + case "LegacyDark": + return blue900; + case "Dark": + return green600; + default: + return green600; + } + } + + property color mainComboBackground: { + switch (MySettings.chatTheme) { + case "LegacyDark": + return blue950; + case "Dark": + return green700; + default: + return green700; + } + } + + property color sendGlow: { + switch (MySettings.chatTheme) { + case "LegacyDark": + return blue1000; + case "Dark": + return green950; + default: + return green300; + } + } + + property color userColor: { + switch (MySettings.chatTheme) { + case "LegacyDark": + return blue700; + case "Dark": + return green700; + default: + return green700; + } + } + + property color assistantColor: { + switch (MySettings.chatTheme) { + case "LegacyDark": + return purple400; + case "Dark": + return accentColor; + default: + return accentColor; + } + } property real fontSizeFixedSmall: 16 From 3556f63a29ffcb86f4412ed7e99697c92d9a3723 Mon Sep 17 00:00:00 2001 From: Adam Treat Date: Mon, 29 Jan 2024 12:31:18 -0500 Subject: [PATCH 11/27] Make the setting labels font a bit bigger and fix hover. Signed-off-by: Adam Treat --- gpt4all-chat/main.qml | 22 +++++++++++----------- gpt4all-chat/qml/MySettingsLabel.qml | 2 +- gpt4all-chat/qml/Theme.qml | 13 +++++++------ 3 files changed, 19 insertions(+), 18 deletions(-) diff --git a/gpt4all-chat/main.qml b/gpt4all-chat/main.qml index 817db9edc7e4..6e317062b3fa 100644 --- a/gpt4all-chat/main.qml +++ b/gpt4all-chat/main.qml @@ -225,8 +225,8 @@ Window { text: "gpt4all.io |" horizontalAlignment: Text.AlignLeft font.pixelSize: theme.fontSizeFixedSmall - color: theme.iconBackgroundHovered - linkColor: hoverHandler1.hovered ? theme.accentColor : theme.iconBackgroundHovered + color: theme.iconBackgroundLight + linkColor: hoverHandler1.hovered ? theme.iconBackgroundHovered : theme.iconBackgroundLight HoverHandler { id: hoverHandler1 } onLinkActivated: { Qt.openUrlExternally("https://gpt4all.io") } } @@ -236,8 +236,8 @@ Window { text: "github" horizontalAlignment: Text.AlignLeft font.pixelSize: theme.fontSizeFixedSmall - color: theme.iconBackgroundHovered - linkColor: hoverHandler2.hovered ? theme.accentColor : theme.iconBackgroundHovered + color: theme.iconBackgroundLight + linkColor: hoverHandler2.hovered ? theme.iconBackgroundHovered : theme.iconBackgroundLight HoverHandler { id: hoverHandler2 } onLinkActivated: { Qt.openUrlExternally("https://github.com/nomic-ai/gpt4all") } } @@ -252,8 +252,8 @@ Window { text: "nomic.ai |" horizontalAlignment: Text.AlignRight font.pixelSize: theme.fontSizeFixedSmall - color: theme.iconBackgroundHovered - linkColor: hoverHandler3.hovered ? theme.accentColor : theme.iconBackgroundHovered + color: theme.iconBackgroundLight + linkColor: hoverHandler3.hovered ? theme.iconBackgroundHovered : theme.iconBackgroundLight HoverHandler { id: hoverHandler3 } onLinkActivated: { Qt.openUrlExternally("https://nomic.ai") } } @@ -263,8 +263,8 @@ Window { text: "twitter |" horizontalAlignment: Text.AlignRight font.pixelSize: theme.fontSizeFixedSmall - color: theme.iconBackgroundHovered - linkColor: hoverHandler4.hovered ? theme.accentColor : theme.iconBackgroundHovered + color: theme.iconBackgroundLight + linkColor: hoverHandler4.hovered ? theme.iconBackgroundHovered : theme.iconBackgroundLight HoverHandler { id: hoverHandler4 } onLinkActivated: { Qt.openUrlExternally("https://twitter.com/nomic_ai") } } @@ -274,8 +274,8 @@ Window { text: "discord" horizontalAlignment: Text.AlignRight font.pixelSize: theme.fontSizeFixedSmall - color: theme.iconBackgroundHovered - linkColor: hoverHandler5.hovered ? theme.accentColor : theme.iconBackgroundHovered + color: theme.iconBackgroundLight + linkColor: hoverHandler5.hovered ? theme.iconBackgroundHovered : theme.iconBackgroundLight HoverHandler { id: hoverHandler5 } onLinkActivated: { Qt.openUrlExternally("https://discord.gg/4M2QFmTt2k") } } @@ -533,7 +533,7 @@ Window { anchors.topMargin: 42.5 anchors.rightMargin: 10 width: 40 - height: 40 + height: 42.5 z: 200 padding: 15 toggled: currentChat.collectionList.length diff --git a/gpt4all-chat/qml/MySettingsLabel.qml b/gpt4all-chat/qml/MySettingsLabel.qml index 64f0626f5618..6dd3e643d61d 100644 --- a/gpt4all-chat/qml/MySettingsLabel.qml +++ b/gpt4all-chat/qml/MySettingsLabel.qml @@ -5,6 +5,6 @@ import QtQuick.Controls.Basic Label { color: theme.settingsTitleTextColor - font.pixelSize: theme.fontSizeLarge - 4 + font.pixelSize: theme.fontSizeSmall font.bold: true } diff --git a/gpt4all-chat/qml/Theme.qml b/gpt4all-chat/qml/Theme.qml index 67cb4fc60cb9..a2543558fcb1 100644 --- a/gpt4all-chat/qml/Theme.qml +++ b/gpt4all-chat/qml/Theme.qml @@ -542,15 +542,16 @@ QtObject { } property real fontSizeFixedSmall: 16 + property real fontSize: Qt.application.font.pixelSize property real fontSizeSmall: fontSizeLarge - 2 property real fontSizeLarge: MySettings.fontSize === "Small" ? - Qt.application.font.pixelSize : MySettings.fontSize === "Medium" ? - Qt.application.font.pixelSize + 5 : Qt.application.font.pixelSize + 10 + fontSize : MySettings.fontSize === "Medium" ? + fontSize + 5 : fontSize + 10 property real fontSizeLarger: MySettings.fontSize === "Small" ? - Qt.application.font.pixelSize + 2 : MySettings.fontSize === "Medium" ? - Qt.application.font.pixelSize + 7 : Qt.application.font.pixelSize + 12 + fontSize + 2 : MySettings.fontSize === "Medium" ? + fontSize + 7 : fontSize + 12 property real fontSizeLargest: MySettings.fontSize === "Small" ? - Qt.application.font.pixelSize + 7 : MySettings.fontSize === "Medium" ? - Qt.application.font.pixelSize + 12 : Qt.application.font.pixelSize + 14 + fontSize + 7 : MySettings.fontSize === "Medium" ? + fontSize + 12 : fontSize + 14 } From cfa22ab1c45f660adb29de3a96bf2aea43e21e7b Mon Sep 17 00:00:00 2001 From: Adam Treat Date: Mon, 29 Jan 2024 13:06:47 -0500 Subject: [PATCH 12/27] Change to a color that exists. Signed-off-by: Adam Treat --- gpt4all-chat/qml/Theme.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gpt4all-chat/qml/Theme.qml b/gpt4all-chat/qml/Theme.qml index a2543558fcb1..44b006b4503b 100644 --- a/gpt4all-chat/qml/Theme.qml +++ b/gpt4all-chat/qml/Theme.qml @@ -522,7 +522,7 @@ QtObject { property color userColor: { switch (MySettings.chatTheme) { case "LegacyDark": - return blue700; + return blue800; case "Dark": return green700; default: From 29d2c936d1aee1196e7a0fd08458f8b0d173c544 Mon Sep 17 00:00:00 2001 From: Jared Van Bortel Date: Mon, 29 Jan 2024 13:57:42 -0500 Subject: [PATCH 13/27] chat: don't show "retrieving localdocs" for zero collections (#1874) Signed-off-by: Jared Van Bortel --- gpt4all-chat/chat.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gpt4all-chat/chat.cpp b/gpt4all-chat/chat.cpp index 0d9b7bc4f009..0e66c5c20bfd 100644 --- a/gpt4all-chat/chat.cpp +++ b/gpt4all-chat/chat.cpp @@ -112,7 +112,7 @@ void Chat::resetResponseState() m_tokenSpeed = QString(); emit tokenSpeedChanged(); m_responseInProgress = true; - m_responseState = Chat::LocalDocsRetrieval; + m_responseState = m_collections.empty() ? Chat::PromptProcessing : Chat::LocalDocsRetrieval; emit responseInProgressChanged(); emit responseStateChanged(); } @@ -120,7 +120,7 @@ void Chat::resetResponseState() void Chat::prompt(const QString &prompt) { resetResponseState(); - emit promptRequested( m_collections, prompt); + emit promptRequested(m_collections, prompt); } void Chat::regenerateResponse() From 38c61493d25617641ca9f63333c82a63ddd1ab2c Mon Sep 17 00:00:00 2001 From: Jared Van Bortel Date: Thu, 25 Jan 2024 16:58:46 -0500 Subject: [PATCH 14/27] backend: update to latest commit of llama.cpp Vulkan PR Signed-off-by: Jared Van Bortel --- gpt4all-backend/bert.cpp | 11 +-- gpt4all-backend/gptj.cpp | 6 +- gpt4all-backend/llama.cpp-mainline | 2 +- gpt4all-backend/llama.cpp.cmake | 3 +- gpt4all-backend/llamamodel.cpp | 106 ++++++++++++++--------------- gpt4all-backend/llamamodel_impl.h | 2 +- gpt4all-backend/llmodel.h | 26 +++++-- gpt4all-backend/llmodel_c.cpp | 13 ++-- gpt4all-backend/llmodel_shared.h | 45 ------------ 9 files changed, 87 insertions(+), 127 deletions(-) diff --git a/gpt4all-backend/bert.cpp b/gpt4all-backend/bert.cpp index 2424d72c61eb..e2d21265159a 100644 --- a/gpt4all-backend/bert.cpp +++ b/gpt4all-backend/bert.cpp @@ -381,10 +381,9 @@ void bert_eval( struct ggml_tensor *KQ = ggml_mul_mat(ctx0, K, Q); // KQ = soft_max(KQ / sqrt(head width)) - KQ = ggml_soft_max(ctx0, - ggml_scale(ctx0, - KQ, - ggml_new_f32(ctx0, 1.0f / sqrt((float)d_head)))); + KQ = ggml_soft_max( + ctx0, ggml_scale(ctx0, KQ, 1.0f / sqrt((float)d_head)) + ); V = ggml_cont(ctx0, ggml_transpose(ctx0, V)); struct ggml_tensor *KQV = ggml_mul_mat(ctx0, V, KQ); @@ -490,10 +489,6 @@ struct bert_ctx * bert_load_from_file(const char *fname) #endif bert_ctx * new_bert = new bert_ctx; -#if defined(GGML_USE_KOMPUTE) - new_bert->buf_compute.force_cpu = true; - new_bert->work_buf.force_cpu = true; -#endif bert_model & model = new_bert->model; bert_vocab & vocab = new_bert->vocab; diff --git a/gpt4all-backend/gptj.cpp b/gpt4all-backend/gptj.cpp index 074ef5dcb1c8..6303ed849b1f 100644 --- a/gpt4all-backend/gptj.cpp +++ b/gpt4all-backend/gptj.cpp @@ -414,11 +414,7 @@ bool gptj_eval( struct ggml_tensor * KQ = ggml_mul_mat(ctx0, K, Q); // KQ_scaled = KQ / sqrt(n_embd/n_head) - struct ggml_tensor * KQ_scaled = - ggml_scale(ctx0, - KQ, - ggml_new_f32(ctx0, 1.0f/sqrt(float(n_embd)/n_head)) - ); + struct ggml_tensor * KQ_scaled = ggml_scale(ctx0, KQ, 1.0f/sqrt(float(n_embd)/n_head)); // KQ_masked = mask_past(KQ_scaled) struct ggml_tensor * KQ_masked = ggml_diag_mask_inf(ctx0, KQ_scaled, n_past); diff --git a/gpt4all-backend/llama.cpp-mainline b/gpt4all-backend/llama.cpp-mainline index 01307d86bbe9..15da9c89f14a 160000 --- a/gpt4all-backend/llama.cpp-mainline +++ b/gpt4all-backend/llama.cpp-mainline @@ -1 +1 @@ -Subproject commit 01307d86bbe980128308c36b64c494fb9dbaa5bf +Subproject commit 15da9c89f14a6cd44a4b45d65bf1f02d5762fe90 diff --git a/gpt4all-backend/llama.cpp.cmake b/gpt4all-backend/llama.cpp.cmake index 8a2ce5ee717b..f8aa532f1760 100644 --- a/gpt4all-backend/llama.cpp.cmake +++ b/gpt4all-backend/llama.cpp.cmake @@ -175,6 +175,7 @@ if (LLAMA_KOMPUTE) DEPENDS ${LLAMA_DIR}/${source} ${LLAMA_DIR}/kompute-shaders/common.comp ${LLAMA_DIR}/kompute-shaders/op_getrows.comp + ${LLAMA_DIR}/kompute-shaders/op_mul_mv_q_n_pre.comp ${LLAMA_DIR}/kompute-shaders/op_mul_mv_q_n.comp COMMAND ${glslc_executable} --target-env=vulkan1.2 -o ${spv_file} ${LLAMA_DIR}/${source} COMMENT "Compiling ${source} to ${source}.spv" @@ -231,7 +232,6 @@ if (LLAMA_KOMPUTE) kompute-shaders/op_add.comp kompute-shaders/op_addrow.comp kompute-shaders/op_mul.comp - kompute-shaders/op_mulrow.comp kompute-shaders/op_silu.comp kompute-shaders/op_relu.comp kompute-shaders/op_gelu.comp @@ -264,7 +264,6 @@ if (LLAMA_KOMPUTE) shaderop_add.h shaderop_addrow.h shaderop_mul.h - shaderop_mulrow.h shaderop_silu.h shaderop_relu.h shaderop_gelu.h diff --git a/gpt4all-backend/llamamodel.cpp b/gpt4all-backend/llamamodel.cpp index d19d7b5404b7..4152dd1955e3 100644 --- a/gpt4all-backend/llamamodel.cpp +++ b/gpt4all-backend/llamamodel.cpp @@ -96,6 +96,7 @@ static int llama_sample_top_p_top_k( struct LLamaPrivate { const std::string modelPath; bool modelLoaded; + int device = -1; llama_model *model = nullptr; llama_context *ctx = nullptr; llama_model_params model_params; @@ -167,24 +168,17 @@ bool LLamaModel::loadModel(const std::string &modelPath, int n_ctx) if (llama_verbose()) { std::cerr << "llama.cpp: using Metal" << std::endl; } - // metal always runs the whole model if n_gpu_layers is not 0, at least - // currently - d_ptr->model_params.n_gpu_layers = 1; -#endif -#ifdef GGML_USE_KOMPUTE - if (ggml_vk_has_device()) { - // vulkan always runs the whole model if n_gpu_layers is not 0, at least - // currently - d_ptr->model_params.n_gpu_layers = 1; + d_ptr->model_params.n_gpu_layers = 100; +#elif defined(GGML_USE_KOMPUTE) + if (d_ptr->device != -1) { + d_ptr->model_params.main_gpu = d_ptr->device; + d_ptr->model_params.n_gpu_layers = 100; } #endif d_ptr->model = llama_load_model_from_file_gpt4all(modelPath.c_str(), &d_ptr->model_params); if (!d_ptr->model) { -#ifdef GGML_USE_KOMPUTE - // Explicitly free the device so next load it doesn't use it - ggml_vk_free_device(); -#endif + d_ptr->device = -1; std::cerr << "LLAMA ERROR: failed to load model from " << modelPath << std::endl; return false; } @@ -214,10 +208,7 @@ bool LLamaModel::loadModel(const std::string &modelPath, int n_ctx) d_ptr->ctx = llama_new_context_with_model(d_ptr->model, d_ptr->ctx_params); if (!d_ptr->ctx) { -#ifdef GGML_USE_KOMPUTE - // Explicitly free the device so next load it doesn't use it - ggml_vk_free_device(); -#endif + d_ptr->device = -1; std::cerr << "LLAMA ERROR: failed to init context for model " << modelPath << std::endl; return false; } @@ -225,7 +216,7 @@ bool LLamaModel::loadModel(const std::string &modelPath, int n_ctx) d_ptr->end_tokens = {llama_token_eos(d_ptr->model)}; #ifdef GGML_USE_KOMPUTE - if (ggml_vk_has_device()) { + if (usingGPUDevice() && ggml_vk_has_device()) { std::cerr << "llama.cpp: using Vulkan on " << ggml_vk_current_device().name << std::endl; } #endif @@ -339,62 +330,70 @@ const std::vector &LLamaModel::endTokens() const std::vector LLamaModel::availableGPUDevices(size_t memoryRequired) { #if defined(GGML_USE_KOMPUTE) - std::vector vkDevices = ggml_vk_available_devices(memoryRequired); - - std::vector devices; - for(const auto& vkDevice : vkDevices) { - LLModel::GPUDevice device; - device.index = vkDevice.index; - device.type = vkDevice.type; - device.heapSize = vkDevice.heapSize; - device.name = vkDevice.name; - device.vendor = vkDevice.vendor; - - devices.push_back(device); - } + size_t count = 0; + auto * vkDevices = ggml_vk_available_devices(memoryRequired, &count); + + if (vkDevices) { + std::vector devices; + devices.reserve(count); + + for (size_t i = 0; i < count; ++i) { + auto & dev = vkDevices[i]; + devices.emplace_back( + /* index = */ dev.index, + /* type = */ dev.type, + /* heapSize = */ dev.heapSize, + /* name = */ dev.name, + /* vendor = */ dev.vendor + ); + } - return devices; -#else - return std::vector(); + free(vkDevices); + return devices; + } #endif + + return {}; } -bool LLamaModel::initializeGPUDevice(size_t memoryRequired, const std::string& device) +bool LLamaModel::initializeGPUDevice(size_t memoryRequired, const std::string &name) { #if defined(GGML_USE_KOMPUTE) - return ggml_vk_init_device(memoryRequired, device); + ggml_vk_device device; + bool ok = ggml_vk_get_device(&device, memoryRequired, name.c_str()); + if (ok) { + d_ptr->device = device.index; + return true; + } #else - return false; + (void)memoryRequired; + (void)name; #endif + return false; } bool LLamaModel::initializeGPUDevice(const LLModel::GPUDevice &device, std::string *unavail_reason) { - bool result = false; #if defined(GGML_USE_KOMPUTE) - ggml_vk_device vkDevice; - vkDevice.index = device.index; - vkDevice.type = device.type; - vkDevice.heapSize = device.heapSize; - vkDevice.name = device.name; - vkDevice.vendor = device.vendor; - result = ggml_vk_init_device(vkDevice); - if (!result && unavail_reason) { - *unavail_reason = "failed to init GPU"; - } + (void)unavail_reason; + d_ptr->device = device.index; + return true; #else + (void)device; if (unavail_reason) { *unavail_reason = "built without Kompute"; } + return false; #endif - return result; } bool LLamaModel::initializeGPUDevice(int device) { #if defined(GGML_USE_KOMPUTE) - return ggml_vk_init_device(device); + d_ptr->device = device; + return true; #else + (void)device; return false; #endif } @@ -402,7 +401,7 @@ bool LLamaModel::initializeGPUDevice(int device) bool LLamaModel::hasGPUDevice() { #if defined(GGML_USE_KOMPUTE) - return ggml_vk_has_device(); + return d_ptr->device != -1; #else return false; #endif @@ -411,11 +410,12 @@ bool LLamaModel::hasGPUDevice() bool LLamaModel::usingGPUDevice() { #if defined(GGML_USE_KOMPUTE) - return ggml_vk_using_vulkan(); + return hasGPUDevice() && d_ptr->model_params.n_gpu_layers > 0; #elif defined(GGML_USE_METAL) return true; -#endif +#else return false; +#endif } std::string get_arch_name(gguf_context *ctx_gguf) { diff --git a/gpt4all-backend/llamamodel_impl.h b/gpt4all-backend/llamamodel_impl.h index c32b2413c90a..7c097637c955 100644 --- a/gpt4all-backend/llamamodel_impl.h +++ b/gpt4all-backend/llamamodel_impl.h @@ -26,7 +26,7 @@ class LLamaModel : public LLModel { void setThreadCount(int32_t n_threads) override; int32_t threadCount() const override; std::vector availableGPUDevices(size_t memoryRequired) override; - bool initializeGPUDevice(size_t memoryRequired, const std::string& device) override; + bool initializeGPUDevice(size_t memoryRequired, const std::string& name) override; bool initializeGPUDevice(const GPUDevice &device, std::string *unavail_reason) override; bool initializeGPUDevice(int device) override; bool hasGPUDevice() override; diff --git a/gpt4all-backend/llmodel.h b/gpt4all-backend/llmodel.h index c0b8bca965cb..a5ae2d54fc00 100644 --- a/gpt4all-backend/llmodel.h +++ b/gpt4all-backend/llmodel.h @@ -17,11 +17,14 @@ class LLModel { using Token = int32_t; struct GPUDevice { - int index = 0; - int type = 0; - size_t heapSize = 0; + int index; + int type; + size_t heapSize; std::string name; std::string vendor; + + GPUDevice(int index, int type, size_t heapSize, std::string name, std::string vendor): + index(index), type(type), heapSize(heapSize), name(std::move(name)), vendor(std::move(vendor)) {} }; class Implementation { @@ -98,14 +101,25 @@ class LLModel { return *m_implementation; } - virtual std::vector availableGPUDevices(size_t /*memoryRequired*/) { return std::vector(); } - virtual bool initializeGPUDevice(size_t /*memoryRequired*/, const std::string& /*device*/) { return false; } - virtual bool initializeGPUDevice(const GPUDevice &/*device*/, std::string *unavail_reason = nullptr) { + virtual std::vector availableGPUDevices(size_t memoryRequired) { + (void)memoryRequired; + return {}; + } + + virtual bool initializeGPUDevice(size_t memoryRequired, const std::string& name) { + (void)memoryRequired; + (void)name; + return false; + } + + virtual bool initializeGPUDevice(const GPUDevice & device, std::string *unavail_reason = nullptr) { + (void)device; if (unavail_reason) { *unavail_reason = "model has no GPU support"; } return false; } + virtual bool initializeGPUDevice(int /*device*/) { return false; } virtual bool hasGPUDevice() { return false; } virtual bool usingGPUDevice() { return false; } diff --git a/gpt4all-backend/llmodel_c.cpp b/gpt4all-backend/llmodel_c.cpp index c8af2ca3d49f..bfeca69c61d7 100644 --- a/gpt4all-backend/llmodel_c.cpp +++ b/gpt4all-backend/llmodel_c.cpp @@ -230,12 +230,13 @@ bool llmodel_gpu_init_gpu_device_by_string(llmodel_model model, size_t memoryReq bool llmodel_gpu_init_gpu_device_by_struct(llmodel_model model, const llmodel_gpu_device *device) { - LLModel::GPUDevice d; - d.index = device->index; - d.type = device->type; - d.heapSize = device->heapSize; - d.name = device->name; - d.vendor = device->vendor; + LLModel::GPUDevice d( + /* index = */ device->index, + /* type = */ device->type, + /* heapSize = */ device->heapSize, + /* name = */ device->name, + /* vendor = */ device->vendor + ); LLModelWrapper *wrapper = reinterpret_cast(model); return wrapper->llModel->initializeGPUDevice(d); } diff --git a/gpt4all-backend/llmodel_shared.h b/gpt4all-backend/llmodel_shared.h index b7b1a8376300..aa132849dbe5 100644 --- a/gpt4all-backend/llmodel_shared.h +++ b/gpt4all-backend/llmodel_shared.h @@ -4,50 +4,6 @@ #include #include -#if defined(GGML_USE_KOMPUTE) -#include "ggml-kompute.h" -struct llm_buffer { - uint8_t * addr = NULL; - size_t size = 0; - ggml_vk_memory memory; - bool force_cpu = false; - - llm_buffer() = default; - - void resize(size_t size) { - free(); - - if (!ggml_vk_has_device() || force_cpu) { - this->addr = new uint8_t[size]; - this->size = size; - } else { - this->memory = ggml_vk_allocate(size); - this->addr = (uint8_t*)memory.data; - this->size = size; - } - } - - void free() { - if (!memory.primaryMemory) { - delete[] addr; - } else if (memory.data) { - ggml_vk_free_memory(memory); - } - this->addr = NULL; - this->size = 0; - } - - ~llm_buffer() { - free(); - } - - // disable copy and move - llm_buffer(const llm_buffer&) = delete; - llm_buffer(llm_buffer&&) = delete; - llm_buffer& operator=(const llm_buffer&) = delete; - llm_buffer& operator=(llm_buffer&&) = delete; -}; -#else struct llm_buffer { uint8_t * addr = NULL; size_t size = 0; @@ -62,7 +18,6 @@ struct llm_buffer { delete[] addr; } }; -#endif struct llm_kv_cache { struct ggml_tensor * k; From f549d5a70adda7a75011014aab0de4337c2f027e Mon Sep 17 00:00:00 2001 From: Jared Van Bortel Date: Mon, 29 Jan 2024 17:15:35 -0500 Subject: [PATCH 15/27] backend : quick llama.cpp update to fix fallback to CPU Signed-off-by: Jared Van Bortel --- gpt4all-backend/llama.cpp-mainline | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gpt4all-backend/llama.cpp-mainline b/gpt4all-backend/llama.cpp-mainline index 15da9c89f14a..28921b84e454 160000 --- a/gpt4all-backend/llama.cpp-mainline +++ b/gpt4all-backend/llama.cpp-mainline @@ -1 +1 @@ -Subproject commit 15da9c89f14a6cd44a4b45d65bf1f02d5762fe90 +Subproject commit 28921b84e4547c42fd7d23615d92c9d894a6cc2d From 061d1969f8f199c09ac94a92cd74f59ab827be13 Mon Sep 17 00:00:00 2001 From: Jared Van Bortel Date: Wed, 31 Jan 2024 14:17:44 -0500 Subject: [PATCH 16/27] expose n_gpu_layers parameter of llama.cpp (#1890) Also dynamically limit the GPU layers and context length fields to the maximum supported by the model. Signed-off-by: Jared Van Bortel --- gpt4all-backend/bert.cpp | 6 +- gpt4all-backend/bert_impl.h | 4 +- gpt4all-backend/gptj.cpp | 6 +- gpt4all-backend/gptj_impl.h | 4 +- gpt4all-backend/llama.cpp-mainline | 2 +- gpt4all-backend/llamamodel.cpp | 134 +++++++++++------- gpt4all-backend/llamamodel_impl.h | 19 +-- gpt4all-backend/llmodel.cpp | 55 ++++--- gpt4all-backend/llmodel.h | 25 +++- gpt4all-backend/llmodel_c.cpp | 17 +-- gpt4all-backend/llmodel_c.h | 6 +- .../csharp/Gpt4All/Bindings/LLModel.cs | 2 +- .../csharp/Gpt4All/Bindings/NativeMethods.cs | 3 +- .../Gpt4All/Model/Gpt4AllModelFactory.cs | 2 +- gpt4all-bindings/golang/binding.cpp | 2 +- .../java/com/hexadevlabs/gpt4all/LLModel.java | 2 +- .../hexadevlabs/gpt4all/LLModelLibrary.java | 2 +- gpt4all-bindings/python/gpt4all/gpt4all.py | 6 +- gpt4all-bindings/python/gpt4all/pyllmodel.py | 24 ++-- gpt4all-bindings/typescript/index.cc | 4 +- gpt4all-chat/chatgpt.cpp | 6 +- gpt4all-chat/chatgpt.h | 4 +- gpt4all-chat/chatlistmodel.h | 7 + gpt4all-chat/chatllm.cpp | 11 +- gpt4all-chat/embllm.cpp | 2 +- gpt4all-chat/main.cpp | 4 + gpt4all-chat/modellist.cpp | 49 +++++++ gpt4all-chat/modellist.h | 32 +++-- gpt4all-chat/mysettings.cpp | 23 +++ gpt4all-chat/mysettings.h | 5 + gpt4all-chat/qml/ModelSettings.qml | 70 ++++++++- 31 files changed, 381 insertions(+), 157 deletions(-) diff --git a/gpt4all-backend/bert.cpp b/gpt4all-backend/bert.cpp index e2d21265159a..01b348d0f5cd 100644 --- a/gpt4all-backend/bert.cpp +++ b/gpt4all-backend/bert.cpp @@ -709,9 +709,10 @@ Bert::~Bert() { bert_free(d_ptr->ctx); } -bool Bert::loadModel(const std::string &modelPath, int n_ctx) +bool Bert::loadModel(const std::string &modelPath, int n_ctx, int ngl) { (void)n_ctx; + (void)ngl; d_ptr->ctx = bert_load_from_file(modelPath.c_str()); d_ptr->n_threads = std::min(4, (int32_t) std::thread::hardware_concurrency()); d_ptr->modelLoaded = d_ptr->ctx != nullptr; @@ -724,10 +725,11 @@ bool Bert::isModelLoaded() const return d_ptr->modelLoaded; } -size_t Bert::requiredMem(const std::string &modelPath, int n_ctx) +size_t Bert::requiredMem(const std::string &modelPath, int n_ctx, int ngl) { (void)modelPath; (void)n_ctx; + (void)ngl; return 0; } diff --git a/gpt4all-backend/bert_impl.h b/gpt4all-backend/bert_impl.h index b39e77e58d9f..072e9783217f 100644 --- a/gpt4all-backend/bert_impl.h +++ b/gpt4all-backend/bert_impl.h @@ -18,9 +18,9 @@ class Bert : public LLModel { bool supportsEmbedding() const override { return true; } bool supportsCompletion() const override { return true; } - bool loadModel(const std::string &modelPath, int n_ctx) override; + bool loadModel(const std::string &modelPath, int n_ctx, int ngl) override; bool isModelLoaded() const override; - size_t requiredMem(const std::string &modelPath, int n_ctx) override; + size_t requiredMem(const std::string &modelPath, int n_ctx, int ngl) override; size_t stateSize() const override; size_t saveState(uint8_t *dest) const override; size_t restoreState(const uint8_t *src) override; diff --git a/gpt4all-backend/gptj.cpp b/gpt4all-backend/gptj.cpp index 6303ed849b1f..40db378a4c6b 100644 --- a/gpt4all-backend/gptj.cpp +++ b/gpt4all-backend/gptj.cpp @@ -672,8 +672,9 @@ GPTJ::GPTJ() d_ptr->modelLoaded = false; } -size_t GPTJ::requiredMem(const std::string &modelPath, int n_ctx) { +size_t GPTJ::requiredMem(const std::string &modelPath, int n_ctx, int ngl) { (void)n_ctx; + (void)ngl; gptj_model dummy_model; gpt_vocab dummy_vocab; size_t mem_req; @@ -681,8 +682,9 @@ size_t GPTJ::requiredMem(const std::string &modelPath, int n_ctx) { return mem_req; } -bool GPTJ::loadModel(const std::string &modelPath, int n_ctx) { +bool GPTJ::loadModel(const std::string &modelPath, int n_ctx, int ngl) { (void)n_ctx; + (void)ngl; std::mt19937 rng(time(NULL)); d_ptr->rng = rng; diff --git a/gpt4all-backend/gptj_impl.h b/gpt4all-backend/gptj_impl.h index c2100b24c476..01d5698f79f0 100644 --- a/gpt4all-backend/gptj_impl.h +++ b/gpt4all-backend/gptj_impl.h @@ -17,9 +17,9 @@ class GPTJ : public LLModel { bool supportsEmbedding() const override { return false; } bool supportsCompletion() const override { return true; } - bool loadModel(const std::string &modelPath, int n_ctx) override; + bool loadModel(const std::string &modelPath, int n_ctx, int ngl) override; bool isModelLoaded() const override; - size_t requiredMem(const std::string &modelPath, int n_ctx) override; + size_t requiredMem(const std::string &modelPath, int n_ctx, int ngl) override; size_t stateSize() const override; size_t saveState(uint8_t *dest) const override; size_t restoreState(const uint8_t *src) override; diff --git a/gpt4all-backend/llama.cpp-mainline b/gpt4all-backend/llama.cpp-mainline index 28921b84e454..997a733992d3 160000 --- a/gpt4all-backend/llama.cpp-mainline +++ b/gpt4all-backend/llama.cpp-mainline @@ -1 +1 @@ -Subproject commit 28921b84e4547c42fd7d23615d92c9d894a6cc2d +Subproject commit 997a733992d38fe5851f68f4e69cf24fa983e8f1 diff --git a/gpt4all-backend/llamamodel.cpp b/gpt4all-backend/llamamodel.cpp index 4152dd1955e3..1fab51381ad5 100644 --- a/gpt4all-backend/llamamodel.cpp +++ b/gpt4all-backend/llamamodel.cpp @@ -32,6 +32,9 @@ #include "ggml-kompute.h" #endif +// Maximum supported GGUF version +static constexpr int GGUF_VER_MAX = 3; + namespace { const char *modelType_ = "LLaMA"; } @@ -121,8 +124,9 @@ struct llama_file_hparams { enum llama_ftype ftype = LLAMA_FTYPE_MOSTLY_F16; }; -size_t LLamaModel::requiredMem(const std::string &modelPath, int n_ctx) { +size_t LLamaModel::requiredMem(const std::string &modelPath, int n_ctx, int ngl) { // TODO(cebtenzzre): update to GGUF + (void)ngl; // FIXME(cetenzzre): use this value auto fin = std::ifstream(modelPath, std::ios::binary); fin.seekg(0, std::ios_base::end); size_t filesize = fin.tellg(); @@ -144,7 +148,7 @@ size_t LLamaModel::requiredMem(const std::string &modelPath, int n_ctx) { return filesize + est_kvcache_size; } -bool LLamaModel::loadModel(const std::string &modelPath, int n_ctx) +bool LLamaModel::loadModel(const std::string &modelPath, int n_ctx, int ngl) { gpt_params params; @@ -168,11 +172,14 @@ bool LLamaModel::loadModel(const std::string &modelPath, int n_ctx) if (llama_verbose()) { std::cerr << "llama.cpp: using Metal" << std::endl; } + + // always fully offload on Metal + // TODO(cebtenzzre): use this parameter to allow using more than 53% of system RAM to load a model d_ptr->model_params.n_gpu_layers = 100; #elif defined(GGML_USE_KOMPUTE) if (d_ptr->device != -1) { d_ptr->model_params.main_gpu = d_ptr->device; - d_ptr->model_params.n_gpu_layers = 100; + d_ptr->model_params.n_gpu_layers = ngl; } #endif @@ -323,13 +330,70 @@ const std::vector &LLamaModel::endTokens() const return d_ptr->end_tokens; } -#if defined(GGML_USE_KOMPUTE) -#include "ggml-kompute.h" -#endif +std::string get_arch_name(gguf_context *ctx_gguf) { + std::string arch_name; + const int kid = gguf_find_key(ctx_gguf, "general.architecture"); + enum gguf_type ktype = gguf_get_kv_type(ctx_gguf, kid); + if (ktype != (GGUF_TYPE_STRING)) { + throw std::runtime_error("ERROR: Can't get general architecture from gguf file."); + } + return gguf_get_val_str(ctx_gguf, kid); +} + +static gguf_context *load_gguf(const char *fname, std::string &arch) { + struct gguf_init_params params = { + /*.no_alloc = */ true, + /*.ctx = */ nullptr, + }; + gguf_context *ctx = gguf_init_from_file(fname, params); + if (!ctx) { + std::cerr << __func__ << ": gguf_init_from_file failed\n"; + return nullptr; + } + + int gguf_ver = gguf_get_version(ctx); + if (gguf_ver > GGUF_VER_MAX) { + std::cerr << __func__ << ": unsupported gguf version: " << gguf_ver << "\n"; + gguf_free(ctx); + return nullptr; + } + + arch = get_arch_name(ctx); + return ctx; +} + +static int32_t get_arch_key_u32(std::string const &modelPath, std::string const &archKey) { + std::string arch; + auto * ctx = load_gguf(modelPath.c_str(), arch); + + int32_t value = -1; + if (ctx) { + auto key = arch + "." + archKey; + int keyidx = gguf_find_key(ctx, key.c_str()); + if (keyidx != -1) { + value = gguf_get_val_u32(ctx, keyidx); + } else { + std::cerr << __func__ << ": " << key << "not found in " << modelPath << "\n"; + } + } -std::vector LLamaModel::availableGPUDevices(size_t memoryRequired) + gguf_free(ctx); + return value; +} + +int32_t LLamaModel::maxContextLength(std::string const &modelPath) const { -#if defined(GGML_USE_KOMPUTE) + return get_arch_key_u32(modelPath, "context_length"); +} + +int32_t LLamaModel::layerCount(std::string const &modelPath) const +{ + return get_arch_key_u32(modelPath, "block_count"); +} + +std::vector LLamaModel::availableGPUDevices(size_t memoryRequired) const +{ +#ifdef GGML_USE_KOMPUTE size_t count = 0; auto * vkDevices = ggml_vk_available_devices(memoryRequired, &count); @@ -346,6 +410,7 @@ std::vector LLamaModel::availableGPUDevices(size_t memoryReq /* name = */ dev.name, /* vendor = */ dev.vendor ); + ggml_vk_device_destroy(&dev); } free(vkDevices); @@ -356,7 +421,7 @@ std::vector LLamaModel::availableGPUDevices(size_t memoryReq return {}; } -bool LLamaModel::initializeGPUDevice(size_t memoryRequired, const std::string &name) +bool LLamaModel::initializeGPUDevice(size_t memoryRequired, const std::string &name) const { #if defined(GGML_USE_KOMPUTE) ggml_vk_device device; @@ -372,11 +437,11 @@ bool LLamaModel::initializeGPUDevice(size_t memoryRequired, const std::string &n return false; } -bool LLamaModel::initializeGPUDevice(const LLModel::GPUDevice &device, std::string *unavail_reason) +bool LLamaModel::initializeGPUDevice(int device, std::string *unavail_reason) const { #if defined(GGML_USE_KOMPUTE) (void)unavail_reason; - d_ptr->device = device.index; + d_ptr->device = device; return true; #else (void)device; @@ -387,17 +452,6 @@ bool LLamaModel::initializeGPUDevice(const LLModel::GPUDevice &device, std::stri #endif } -bool LLamaModel::initializeGPUDevice(int device) -{ -#if defined(GGML_USE_KOMPUTE) - d_ptr->device = device; - return true; -#else - (void)device; - return false; -#endif -} - bool LLamaModel::hasGPUDevice() { #if defined(GGML_USE_KOMPUTE) @@ -418,16 +472,6 @@ bool LLamaModel::usingGPUDevice() #endif } -std::string get_arch_name(gguf_context *ctx_gguf) { - std::string arch_name; - const int kid = gguf_find_key(ctx_gguf, "general.architecture"); - enum gguf_type ktype = gguf_get_kv_type(ctx_gguf, kid); - if (ktype != (GGUF_TYPE_STRING)) { - throw std::runtime_error("ERROR: Can't get general architecture from gguf file."); - } - return gguf_get_val_str(ctx_gguf, kid); -} - #if defined(_WIN32) #define DLL_EXPORT __declspec(dllexport) #else @@ -447,35 +491,19 @@ DLL_EXPORT const char *get_build_variant() { return GGML_BUILD_VARIANT; } -DLL_EXPORT bool magic_match(const char * fname) { - struct ggml_context * ctx_meta = NULL; - struct gguf_init_params params = { - /*.no_alloc = */ true, - /*.ctx = */ &ctx_meta, - }; - gguf_context *ctx_gguf = gguf_init_from_file(fname, params); - if (!ctx_gguf) { - std::cerr << __func__ << ": gguf_init_from_file failed\n"; - return false; - } +DLL_EXPORT bool magic_match(const char *fname) { + std::string arch; + auto * ctx = load_gguf(fname, arch); bool valid = true; - - int gguf_ver = gguf_get_version(ctx_gguf); - if (valid && gguf_ver > 3) { - std::cerr << __func__ << ": unsupported gguf version: " << gguf_ver << "\n"; - valid = false; - } - - auto arch = get_arch_name(ctx_gguf); - if (valid && !(arch == "llama" || arch == "starcoder" || arch == "falcon" || arch == "mpt")) { + if (!(arch == "llama" || arch == "starcoder" || arch == "falcon" || arch == "mpt")) { if (!(arch == "gptj" || arch == "bert")) { // we support these via other modules std::cerr << __func__ << ": unsupported model architecture: " << arch << "\n"; } valid = false; } - gguf_free(ctx_gguf); + gguf_free(ctx); return valid; } diff --git a/gpt4all-backend/llamamodel_impl.h b/gpt4all-backend/llamamodel_impl.h index 7c097637c955..27eb580b07cf 100644 --- a/gpt4all-backend/llamamodel_impl.h +++ b/gpt4all-backend/llamamodel_impl.h @@ -4,8 +4,9 @@ #ifndef LLAMAMODEL_H #define LLAMAMODEL_H -#include #include +#include +#include #include #include "llmodel.h" @@ -17,23 +18,22 @@ class LLamaModel : public LLModel { bool supportsEmbedding() const override { return false; } bool supportsCompletion() const override { return true; } - bool loadModel(const std::string &modelPath, int n_ctx) override; + bool loadModel(const std::string &modelPath, int n_ctx, int ngl) override; bool isModelLoaded() const override; - size_t requiredMem(const std::string &modelPath, int n_ctx) override; + size_t requiredMem(const std::string &modelPath, int n_ctx, int ngl) override; size_t stateSize() const override; size_t saveState(uint8_t *dest) const override; size_t restoreState(const uint8_t *src) override; void setThreadCount(int32_t n_threads) override; int32_t threadCount() const override; - std::vector availableGPUDevices(size_t memoryRequired) override; - bool initializeGPUDevice(size_t memoryRequired, const std::string& name) override; - bool initializeGPUDevice(const GPUDevice &device, std::string *unavail_reason) override; - bool initializeGPUDevice(int device) override; + std::vector availableGPUDevices(size_t memoryRequired) const override; + bool initializeGPUDevice(size_t memoryRequired, const std::string& name) const override; + bool initializeGPUDevice(int device, std::string *unavail_reason) const override; bool hasGPUDevice() override; bool usingGPUDevice() override; private: - LLamaPrivate *d_ptr; + std::unique_ptr d_ptr; protected: std::vector tokenize(PromptContext &, const std::string&) const override; @@ -42,6 +42,9 @@ class LLamaModel : public LLModel { bool evalTokens(PromptContext& ctx, const std::vector &tokens) const override; int32_t contextLength() const override; const std::vector& endTokens() const override; + + int32_t maxContextLength(std::string const &modelPath) const override; + int32_t layerCount(std::string const &modelPath) const override; }; #endif // LLAMAMODEL_H diff --git a/gpt4all-backend/llmodel.cpp b/gpt4all-backend/llmodel.cpp index bc9176f0a44c..506b2c06acc8 100644 --- a/gpt4all-backend/llmodel.cpp +++ b/gpt4all-backend/llmodel.cpp @@ -2,15 +2,17 @@ #include "dlhandle.h" #include "sysinfo.h" -#include -#include -#include -#include -#include #include #include -#include +#include +#include +#include +#include #include +#include +#include +#include + #ifdef _MSC_VER #include #endif @@ -158,7 +160,7 @@ LLModel *LLModel::Implementation::construct(const std::string &modelPath, std::s * load time, not construct time. right now n_ctx is incorrectly hardcoded 2048 in * most (all?) places where this is called, causing underestimation of required * memory. */ - size_t req_mem = metalimpl->requiredMem(modelPath, n_ctx); + size_t req_mem = metalimpl->requiredMem(modelPath, n_ctx, 100); float req_to_total = (float) req_mem / (float) total_mem; // on a 16GB M2 Mac a 13B q4_0 (0.52) works for me but a 13B q4_K_M (0.55) does not if (req_to_total >= 0.53) { @@ -193,26 +195,39 @@ LLModel *LLModel::Implementation::construct(const std::string &modelPath, std::s } LLModel *LLModel::Implementation::constructDefaultLlama() { - const LLModel::Implementation *impl = nullptr; - for (const auto &i : implementationList()) { - if (i.m_buildVariant == "metal" || i.m_modelType != "LLaMA") continue; - impl = &i; - } - if (!impl) { - std::cerr << "LLModel ERROR: Could not find CPU LLaMA implementation\n"; - return nullptr; - } - auto fres = impl->m_construct(); - fres->m_implementation = impl; - return fres; + static std::unique_ptr llama([]() -> LLModel * { + const LLModel::Implementation *impl = nullptr; + for (const auto &i : implementationList()) { + if (i.m_buildVariant == "metal" || i.m_modelType != "LLaMA") continue; + impl = &i; + } + if (!impl) { + std::cerr << "LLModel ERROR: Could not find CPU LLaMA implementation\n"; + return nullptr; + } + auto fres = impl->m_construct(); + fres->m_implementation = impl; + return fres; + }()); + return llama.get(); } std::vector LLModel::Implementation::availableGPUDevices() { - static LLModel *llama = LLModel::Implementation::constructDefaultLlama(); // (memory leak) + auto * llama = constructDefaultLlama(); if (llama) { return llama->availableGPUDevices(0); } return {}; } +int32_t LLModel::Implementation::maxContextLength(const std::string &modelPath) { + auto * llama = constructDefaultLlama(); + return llama ? llama->maxContextLength(modelPath) : -1; +} + +int32_t LLModel::Implementation::layerCount(const std::string &modelPath) { + auto * llama = constructDefaultLlama(); + return llama ? llama->layerCount(modelPath) : -1; +} + void LLModel::Implementation::setImplementationsSearchPath(const std::string& path) { s_implementations_search_path = path; } diff --git a/gpt4all-backend/llmodel.h b/gpt4all-backend/llmodel.h index a5ae2d54fc00..7fc5e71dc902 100644 --- a/gpt4all-backend/llmodel.h +++ b/gpt4all-backend/llmodel.h @@ -42,6 +42,8 @@ class LLModel { static const Implementation *implementation(const char *fname, const std::string& buildVariant); static LLModel *construct(const std::string &modelPath, std::string buildVariant = "auto", int n_ctx = 2048); static std::vector availableGPUDevices(); + static int32_t maxContextLength(const std::string &modelPath); + static int32_t layerCount(const std::string &modelPath); static void setImplementationsSearchPath(const std::string& path); static const std::string& implementationsSearchPath(); @@ -77,9 +79,9 @@ class LLModel { virtual bool supportsEmbedding() const = 0; virtual bool supportsCompletion() const = 0; - virtual bool loadModel(const std::string &modelPath, int n_ctx) = 0; + virtual bool loadModel(const std::string &modelPath, int n_ctx, int ngl) = 0; virtual bool isModelLoaded() const = 0; - virtual size_t requiredMem(const std::string &modelPath, int n_ctx) = 0; + virtual size_t requiredMem(const std::string &modelPath, int n_ctx, int ngl) = 0; virtual size_t stateSize() const { return 0; } virtual size_t saveState(uint8_t */*dest*/) const { return 0; } virtual size_t restoreState(const uint8_t */*src*/) { return 0; } @@ -101,18 +103,18 @@ class LLModel { return *m_implementation; } - virtual std::vector availableGPUDevices(size_t memoryRequired) { + virtual std::vector availableGPUDevices(size_t memoryRequired) const { (void)memoryRequired; return {}; } - virtual bool initializeGPUDevice(size_t memoryRequired, const std::string& name) { + virtual bool initializeGPUDevice(size_t memoryRequired, const std::string& name) const { (void)memoryRequired; (void)name; return false; } - virtual bool initializeGPUDevice(const GPUDevice & device, std::string *unavail_reason = nullptr) { + virtual bool initializeGPUDevice(int device, std::string *unavail_reason = nullptr) const { (void)device; if (unavail_reason) { *unavail_reason = "model has no GPU support"; @@ -120,7 +122,6 @@ class LLModel { return false; } - virtual bool initializeGPUDevice(int /*device*/) { return false; } virtual bool hasGPUDevice() { return false; } virtual bool usingGPUDevice() { return false; } @@ -134,6 +135,18 @@ class LLModel { virtual int32_t contextLength() const = 0; virtual const std::vector& endTokens() const = 0; + virtual int32_t maxContextLength(std::string const &modelPath) const + { + (void)modelPath; + return -1; + } + + virtual int32_t layerCount(std::string const &modelPath) const + { + (void)modelPath; + return -1; + } + // This is a helper function called from the default implementation of 'prompt' but it can be // shared by all base classes so it isn't virtual void recalculateContext(PromptContext &promptCtx, std::function recalculate); diff --git a/gpt4all-backend/llmodel_c.cpp b/gpt4all-backend/llmodel_c.cpp index bfeca69c61d7..8ba59b2b7a39 100644 --- a/gpt4all-backend/llmodel_c.cpp +++ b/gpt4all-backend/llmodel_c.cpp @@ -47,16 +47,16 @@ void llmodel_model_destroy(llmodel_model model) { delete reinterpret_cast(model); } -size_t llmodel_required_mem(llmodel_model model, const char *model_path, int n_ctx) +size_t llmodel_required_mem(llmodel_model model, const char *model_path, int n_ctx, int ngl) { LLModelWrapper *wrapper = reinterpret_cast(model); - return wrapper->llModel->requiredMem(model_path, n_ctx); + return wrapper->llModel->requiredMem(model_path, n_ctx, ngl); } -bool llmodel_loadModel(llmodel_model model, const char *model_path, int n_ctx) +bool llmodel_loadModel(llmodel_model model, const char *model_path, int n_ctx, int ngl) { LLModelWrapper *wrapper = reinterpret_cast(model); - return wrapper->llModel->loadModel(model_path, n_ctx); + return wrapper->llModel->loadModel(model_path, n_ctx, ngl); } bool llmodel_isModelLoaded(llmodel_model model) @@ -230,15 +230,8 @@ bool llmodel_gpu_init_gpu_device_by_string(llmodel_model model, size_t memoryReq bool llmodel_gpu_init_gpu_device_by_struct(llmodel_model model, const llmodel_gpu_device *device) { - LLModel::GPUDevice d( - /* index = */ device->index, - /* type = */ device->type, - /* heapSize = */ device->heapSize, - /* name = */ device->name, - /* vendor = */ device->vendor - ); LLModelWrapper *wrapper = reinterpret_cast(model); - return wrapper->llModel->initializeGPUDevice(d); + return wrapper->llModel->initializeGPUDevice(device->index); } bool llmodel_gpu_init_gpu_device_by_int(llmodel_model model, int device) diff --git a/gpt4all-backend/llmodel_c.h b/gpt4all-backend/llmodel_c.h index dcd53f2ec788..50d35edac6dd 100644 --- a/gpt4all-backend/llmodel_c.h +++ b/gpt4all-backend/llmodel_c.h @@ -111,18 +111,20 @@ void llmodel_model_destroy(llmodel_model model); * @param model A pointer to the llmodel_model instance. * @param model_path A string representing the path to the model file. * @param n_ctx Maximum size of context window + * @param ngl Number of GPU layers to use (Vulkan) * @return size greater than 0 if the model was parsed successfully, 0 if file could not be parsed. */ -size_t llmodel_required_mem(llmodel_model model, const char *model_path, int n_ctx); +size_t llmodel_required_mem(llmodel_model model, const char *model_path, int n_ctx, int ngl); /** * Load a model from a file. * @param model A pointer to the llmodel_model instance. * @param model_path A string representing the path to the model file. * @param n_ctx Maximum size of context window + * @param ngl Number of GPU layers to use (Vulkan) * @return true if the model was loaded successfully, false otherwise. */ -bool llmodel_loadModel(llmodel_model model, const char *model_path, int n_ctx); +bool llmodel_loadModel(llmodel_model model, const char *model_path, int n_ctx, int ngl); /** * Check if a model is loaded. diff --git a/gpt4all-bindings/csharp/Gpt4All/Bindings/LLModel.cs b/gpt4all-bindings/csharp/Gpt4All/Bindings/LLModel.cs index 04fbbc5ba8d9..a56b38a57681 100644 --- a/gpt4all-bindings/csharp/Gpt4All/Bindings/LLModel.cs +++ b/gpt4all-bindings/csharp/Gpt4All/Bindings/LLModel.cs @@ -183,7 +183,7 @@ public bool IsLoaded() /// true if the model was loaded successfully, false otherwise. public bool Load(string modelPath) { - return NativeMethods.llmodel_loadModel(_handle, modelPath, 2048); + return NativeMethods.llmodel_loadModel(_handle, modelPath, 2048, 100); } protected void Destroy() diff --git a/gpt4all-bindings/csharp/Gpt4All/Bindings/NativeMethods.cs b/gpt4all-bindings/csharp/Gpt4All/Bindings/NativeMethods.cs index c6ea9e111920..7ac955c5166b 100644 --- a/gpt4all-bindings/csharp/Gpt4All/Bindings/NativeMethods.cs +++ b/gpt4all-bindings/csharp/Gpt4All/Bindings/NativeMethods.cs @@ -71,7 +71,8 @@ public static extern IntPtr llmodel_model_create2( public static extern bool llmodel_loadModel( [NativeTypeName("llmodel_model")] IntPtr model, [NativeTypeName("const char *")][MarshalAs(UnmanagedType.LPUTF8Str)] string model_path, - [NativeTypeName("int32_t")] int n_ctx); + [NativeTypeName("int32_t")] int n_ctx, + [NativeTypeName("int32_t")] int ngl); [DllImport("libllmodel", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] diff --git a/gpt4all-bindings/csharp/Gpt4All/Model/Gpt4AllModelFactory.cs b/gpt4all-bindings/csharp/Gpt4All/Model/Gpt4AllModelFactory.cs index 5e668dc29508..8350a66ac1e2 100644 --- a/gpt4all-bindings/csharp/Gpt4All/Model/Gpt4AllModelFactory.cs +++ b/gpt4all-bindings/csharp/Gpt4All/Model/Gpt4AllModelFactory.cs @@ -43,7 +43,7 @@ private IGpt4AllModel CreateModel(string modelPath) } _logger.LogDebug("Model created handle=0x{ModelHandle:X8}", handle); _logger.LogInformation("Model loading started"); - var loadedSuccessfully = NativeMethods.llmodel_loadModel(handle, modelPath, 2048); + var loadedSuccessfully = NativeMethods.llmodel_loadModel(handle, modelPath, 2048, 100); _logger.LogInformation("Model loading completed success={ModelLoadSuccess}", loadedSuccessfully); if (!loadedSuccessfully) { diff --git a/gpt4all-bindings/golang/binding.cpp b/gpt4all-bindings/golang/binding.cpp index 1ccb9e61f134..0026d8658572 100644 --- a/gpt4all-bindings/golang/binding.cpp +++ b/gpt4all-bindings/golang/binding.cpp @@ -23,7 +23,7 @@ void* load_model(const char *fname, int n_threads) { fprintf(stderr, "%s: error '%s'\n", __func__, new_error); return nullptr; } - if (!llmodel_loadModel(model, fname, 2048)) { + if (!llmodel_loadModel(model, fname, 2048, 100)) { llmodel_model_destroy(model); return nullptr; } diff --git a/gpt4all-bindings/java/src/main/java/com/hexadevlabs/gpt4all/LLModel.java b/gpt4all-bindings/java/src/main/java/com/hexadevlabs/gpt4all/LLModel.java index 6c0d053ee0e2..769de02a42b4 100644 --- a/gpt4all-bindings/java/src/main/java/com/hexadevlabs/gpt4all/LLModel.java +++ b/gpt4all-bindings/java/src/main/java/com/hexadevlabs/gpt4all/LLModel.java @@ -195,7 +195,7 @@ public LLModel(Path modelPath) { if(model == null) { throw new IllegalStateException("Could not load, gpt4all backend returned error: " + error.getValue().getString(0)); } - library.llmodel_loadModel(model, modelPathAbs, 2048); + library.llmodel_loadModel(model, modelPathAbs, 2048, 100); if(!library.llmodel_isModelLoaded(model)){ throw new IllegalStateException("The model " + modelName + " could not be loaded"); diff --git a/gpt4all-bindings/java/src/main/java/com/hexadevlabs/gpt4all/LLModelLibrary.java b/gpt4all-bindings/java/src/main/java/com/hexadevlabs/gpt4all/LLModelLibrary.java index b2d48e34d297..356b6149b82f 100644 --- a/gpt4all-bindings/java/src/main/java/com/hexadevlabs/gpt4all/LLModelLibrary.java +++ b/gpt4all-bindings/java/src/main/java/com/hexadevlabs/gpt4all/LLModelLibrary.java @@ -61,7 +61,7 @@ public LLModelPromptContext(jnr.ffi.Runtime runtime) { Pointer llmodel_model_create2(String model_path, String build_variant, PointerByReference error); void llmodel_model_destroy(Pointer model); - boolean llmodel_loadModel(Pointer model, String model_path, int n_ctx); + boolean llmodel_loadModel(Pointer model, String model_path, int n_ctx, int ngl); boolean llmodel_isModelLoaded(Pointer model); @u_int64_t long llmodel_get_state_size(Pointer model); @u_int64_t long llmodel_save_state_data(Pointer model, Pointer dest); diff --git a/gpt4all-bindings/python/gpt4all/gpt4all.py b/gpt4all-bindings/python/gpt4all/gpt4all.py index 50c3c88e4c5d..fcd0a91d0bab 100644 --- a/gpt4all-bindings/python/gpt4all/gpt4all.py +++ b/gpt4all-bindings/python/gpt4all/gpt4all.py @@ -70,6 +70,7 @@ def __init__( n_threads: Optional[int] = None, device: Optional[str] = "cpu", n_ctx: int = 2048, + ngl: int = 100, verbose: bool = False, ): """ @@ -92,6 +93,7 @@ def __init__( Note: If a selected GPU device does not have sufficient RAM to accommodate the model, an error will be thrown, and the GPT4All instance will be rendered invalid. It's advised to ensure the device has enough memory before initiating the model. n_ctx: Maximum size of context window + ngl: Number of GPU layers to use (Vulkan) verbose: If True, print debug messages. """ self.model_type = model_type @@ -99,8 +101,8 @@ def __init__( # Retrieve model and download if allowed self.config: ConfigType = self.retrieve_model(model_name, model_path=model_path, allow_download=allow_download, verbose=verbose) if device is not None and device != "cpu": - self.model.init_gpu(model_path=self.config["path"], device=device, n_ctx=n_ctx) - self.model.load_model(self.config["path"], n_ctx) + self.model.init_gpu(model_path=self.config["path"], device=device, n_ctx=n_ctx, ngl=ngl) + self.model.load_model(self.config["path"], n_ctx, ngl) # Set n_threads if n_threads is not None: self.model.set_thread_count(n_threads) diff --git a/gpt4all-bindings/python/gpt4all/pyllmodel.py b/gpt4all-bindings/python/gpt4all/pyllmodel.py index f3a1ee8e9153..f313e3054c5f 100644 --- a/gpt4all-bindings/python/gpt4all/pyllmodel.py +++ b/gpt4all-bindings/python/gpt4all/pyllmodel.py @@ -182,16 +182,16 @@ def __del__(self): if self.model is not None: self.llmodel_lib.llmodel_model_destroy(self.model) - def memory_needed(self, model_path: str, n_ctx: int) -> int: + def memory_needed(self, model_path: str, n_ctx: int, ngl: int) -> int: self.model = None - return self._memory_needed(model_path, n_ctx) + return self._memory_needed(model_path, n_ctx, ngl) - def _memory_needed(self, model_path: str, n_ctx: int) -> int: + def _memory_needed(self, model_path: str, n_ctx: int, ngl: int) -> int: if self.model is None: self.model = _create_model(model_path.encode()) - return llmodel.llmodel_required_mem(self.model, model_path.encode(), n_ctx) + return llmodel.llmodel_required_mem(self.model, model_path.encode(), n_ctx, ngl) - def list_gpu(self, model_path: str, n_ctx: int) -> list[LLModelGPUDevice]: + def list_gpu(self, model_path: str, n_ctx: int, ngl: int) -> list[LLModelGPUDevice]: """ Lists available GPU devices that satisfy the model's memory requirements. @@ -201,13 +201,15 @@ def list_gpu(self, model_path: str, n_ctx: int) -> list[LLModelGPUDevice]: Path to the model. n_ctx : int Maximum size of context window + ngl : int + Number of GPU layers to use (Vulkan) Returns ------- list A list of LLModelGPUDevice structures representing available GPU devices. """ - mem_required = self._memory_needed(model_path, n_ctx) + mem_required = self._memory_needed(model_path, n_ctx, ngl) return self._list_gpu(mem_required) def _list_gpu(self, mem_required: int) -> list[LLModelGPUDevice]: @@ -217,8 +219,8 @@ def _list_gpu(self, mem_required: int) -> list[LLModelGPUDevice]: raise ValueError("Unable to retrieve available GPU devices") return devices_ptr[:num_devices.value] - def init_gpu(self, model_path: str, device: str, n_ctx: int): - mem_required = self._memory_needed(model_path, n_ctx) + def init_gpu(self, model_path: str, device: str, n_ctx: int, ngl: int): + mem_required = self._memory_needed(model_path, n_ctx, ngl) success = self.llmodel_lib.llmodel_gpu_init_gpu_device_by_string(self.model, mem_required, device.encode()) if not success: @@ -241,7 +243,7 @@ def init_gpu(self, model_path: str, device: str, n_ctx: int): error_msg += "\nUnavailable GPUs due to insufficient memory or features: {}.".format(unavailable_gpus) raise ValueError(error_msg) - def load_model(self, model_path: str, n_ctx: int) -> bool: + def load_model(self, model_path: str, n_ctx: int, ngl: int) -> bool: """ Load model from a file. @@ -251,6 +253,8 @@ def load_model(self, model_path: str, n_ctx: int) -> bool: Model filepath n_ctx : int Maximum size of context window + ngl : int + Number of GPU layers to use (Vulkan) Returns ------- @@ -258,7 +262,7 @@ def load_model(self, model_path: str, n_ctx: int) -> bool: """ self.model = _create_model(model_path.encode()) - llmodel.llmodel_loadModel(self.model, model_path.encode(), n_ctx) + llmodel.llmodel_loadModel(self.model, model_path.encode(), n_ctx, ngl) filename = os.path.basename(model_path) self.model_name = os.path.splitext(filename)[0] diff --git a/gpt4all-bindings/typescript/index.cc b/gpt4all-bindings/typescript/index.cc index 6a69ad4970c5..31a3e3c4fb69 100644 --- a/gpt4all-bindings/typescript/index.cc +++ b/gpt4all-bindings/typescript/index.cc @@ -28,7 +28,7 @@ Napi::Function NodeModelWrapper::GetClass(Napi::Env env) { Napi::Value NodeModelWrapper::GetRequiredMemory(const Napi::CallbackInfo& info) { auto env = info.Env(); - return Napi::Number::New(env, static_cast( llmodel_required_mem(GetInference(), full_model_path.c_str(), 2048) )); + return Napi::Number::New(env, static_cast( llmodel_required_mem(GetInference(), full_model_path.c_str(), 2048, 100) )); } Napi::Value NodeModelWrapper::GetGpuDevices(const Napi::CallbackInfo& info) @@ -161,7 +161,7 @@ Napi::Value NodeModelWrapper::GetRequiredMemory(const Napi::CallbackInfo& info) } } - auto success = llmodel_loadModel(GetInference(), full_weight_path.c_str(), 2048); + auto success = llmodel_loadModel(GetInference(), full_weight_path.c_str(), 2048, 100); if(!success) { Napi::Error::New(env, "Failed to load model at given path").ThrowAsJavaScriptException(); return; diff --git a/gpt4all-chat/chatgpt.cpp b/gpt4all-chat/chatgpt.cpp index 98d241dda89c..5f3da91d180a 100644 --- a/gpt4all-chat/chatgpt.cpp +++ b/gpt4all-chat/chatgpt.cpp @@ -20,17 +20,19 @@ ChatGPT::ChatGPT() { } -size_t ChatGPT::requiredMem(const std::string &modelPath, int n_ctx) +size_t ChatGPT::requiredMem(const std::string &modelPath, int n_ctx, int ngl) { Q_UNUSED(modelPath); Q_UNUSED(n_ctx); + Q_UNUSED(ngl); return 0; } -bool ChatGPT::loadModel(const std::string &modelPath, int n_ctx) +bool ChatGPT::loadModel(const std::string &modelPath, int n_ctx, int ngl) { Q_UNUSED(modelPath); Q_UNUSED(n_ctx); + Q_UNUSED(ngl); return true; } diff --git a/gpt4all-chat/chatgpt.h b/gpt4all-chat/chatgpt.h index 7bb3912f55af..11d84606bccf 100644 --- a/gpt4all-chat/chatgpt.h +++ b/gpt4all-chat/chatgpt.h @@ -48,9 +48,9 @@ class ChatGPT : public QObject, public LLModel { bool supportsEmbedding() const override { return false; } bool supportsCompletion() const override { return true; } - bool loadModel(const std::string &modelPath, int n_ctx) override; + bool loadModel(const std::string &modelPath, int n_ctx, int ngl) override; bool isModelLoaded() const override; - size_t requiredMem(const std::string &modelPath, int n_ctx) override; + size_t requiredMem(const std::string &modelPath, int n_ctx, int ngl) override; size_t stateSize() const override; size_t saveState(uint8_t *dest) const override; size_t restoreState(const uint8_t *src) override; diff --git a/gpt4all-chat/chatlistmodel.h b/gpt4all-chat/chatlistmodel.h index 20729204f064..24a95c3dc62a 100644 --- a/gpt4all-chat/chatlistmodel.h +++ b/gpt4all-chat/chatlistmodel.h @@ -192,6 +192,13 @@ class ChatListModel : public QAbstractListModel int count() const { return m_chats.size(); } + void clearChats() { + m_newChat = nullptr; + m_serverChat = nullptr; + m_currentChat = nullptr; + m_chats.clear(); + } + void removeChatFile(Chat *chat) const; Q_INVOKABLE void saveChats(); void restoreChat(Chat *chat); diff --git a/gpt4all-chat/chatllm.cpp b/gpt4all-chat/chatllm.cpp index ad13a81a89d8..eac7b0c23e32 100644 --- a/gpt4all-chat/chatllm.cpp +++ b/gpt4all-chat/chatllm.cpp @@ -247,10 +247,9 @@ bool ChatLLM::loadModel(const ModelInfo &modelInfo) model->setAPIKey(apiKey); m_llModelInfo.model = model; } else { - - // TODO: make configurable in UI auto n_ctx = MySettings::globalInstance()->modelContextLength(modelInfo); m_ctx.n_ctx = n_ctx; + auto ngl = MySettings::globalInstance()->modelGpuLayers(modelInfo); std::string buildVariant = "auto"; #if defined(Q_OS_MAC) && defined(__arm__) @@ -269,7 +268,7 @@ bool ChatLLM::loadModel(const ModelInfo &modelInfo) if (requestedDevice == "CPU") { emit reportFallbackReason(""); // fallback not applicable } else { - const size_t requiredMemory = m_llModelInfo.model->requiredMem(filePath.toStdString(), n_ctx); + const size_t requiredMemory = m_llModelInfo.model->requiredMem(filePath.toStdString(), n_ctx, ngl); std::vector availableDevices = m_llModelInfo.model->availableGPUDevices(requiredMemory); LLModel::GPUDevice *device = nullptr; @@ -288,7 +287,7 @@ bool ChatLLM::loadModel(const ModelInfo &modelInfo) std::string unavail_reason; if (!device) { // GPU not available - } else if (!m_llModelInfo.model->initializeGPUDevice(*device, &unavail_reason)) { + } else if (!m_llModelInfo.model->initializeGPUDevice(device->index, &unavail_reason)) { emit reportFallbackReason(QString::fromStdString("
    " + unavail_reason)); } else { actualDevice = QString::fromStdString(device->name); @@ -298,14 +297,14 @@ bool ChatLLM::loadModel(const ModelInfo &modelInfo) // Report which device we're actually using emit reportDevice(actualDevice); - bool success = m_llModelInfo.model->loadModel(filePath.toStdString(), n_ctx); + bool success = m_llModelInfo.model->loadModel(filePath.toStdString(), n_ctx, ngl); if (actualDevice == "CPU") { // we asked llama.cpp to use the CPU } else if (!success) { // llama_init_from_file returned nullptr emit reportDevice("CPU"); emit reportFallbackReason("
    GPU loading failed (out of VRAM?)"); - success = m_llModelInfo.model->loadModel(filePath.toStdString(), n_ctx); + success = m_llModelInfo.model->loadModel(filePath.toStdString(), n_ctx, 0); } else if (!m_llModelInfo.model->usingGPUDevice()) { // ggml_vk_init was not called in llama.cpp // We might have had to fallback to CPU after load if the model is not possible to accelerate diff --git a/gpt4all-chat/embllm.cpp b/gpt4all-chat/embllm.cpp index 7be2d3480a5c..bc7b2ef9089c 100644 --- a/gpt4all-chat/embllm.cpp +++ b/gpt4all-chat/embllm.cpp @@ -30,7 +30,7 @@ bool EmbeddingLLM::loadModel() } m_model = LLModel::Implementation::construct(filePath.toStdString()); - bool success = m_model->loadModel(filePath.toStdString(), 2048); + bool success = m_model->loadModel(filePath.toStdString(), 2048, 0); if (!success) { qWarning() << "WARNING: Could not load sbert"; delete m_model; diff --git a/gpt4all-chat/main.cpp b/gpt4all-chat/main.cpp index 1b8cb3538da2..d015581aba1d 100644 --- a/gpt4all-chat/main.cpp +++ b/gpt4all-chat/main.cpp @@ -63,5 +63,9 @@ int main(int argc, char *argv[]) } #endif + // Make sure ChatLLM threads are joined before global destructors run. + // Otherwise, we can get a heap-use-after-free inside of llama.cpp. + ChatListModel::globalInstance()->clearChats(); + return app.exec(); } diff --git a/gpt4all-chat/modellist.cpp b/gpt4all-chat/modellist.cpp index bb68836130e5..97403594c2e3 100644 --- a/gpt4all-chat/modellist.cpp +++ b/gpt4all-chat/modellist.cpp @@ -1,6 +1,7 @@ #include "modellist.h" #include "mysettings.h" #include "network.h" +#include "../gpt4all-backend/llmodel.h" #include #include @@ -108,6 +109,41 @@ void ModelInfo::setContextLength(int l) m_contextLength = l; } +int ModelInfo::maxContextLength() const +{ + if (m_maxContextLength != -1) return m_maxContextLength; + auto path = (dirpath + filename()).toStdString(); + int layers = LLModel::Implementation::maxContextLength(path); + if (layers < 0) { + layers = 4096; // fallback value + } + m_maxContextLength = layers; + return m_maxContextLength; +} + +int ModelInfo::gpuLayers() const +{ + return MySettings::globalInstance()->modelGpuLayers(*this); +} + +void ModelInfo::setGpuLayers(int l) +{ + if (isClone) MySettings::globalInstance()->setModelGpuLayers(*this, l, isClone /*force*/); + m_gpuLayers = l; +} + +int ModelInfo::maxGpuLayers() const +{ + if (m_maxGpuLayers != -1) return m_maxGpuLayers; + auto path = (dirpath + filename()).toStdString(); + int layers = LLModel::Implementation::layerCount(path); + if (layers < 0) { + layers = 100; // fallback value + } + m_maxGpuLayers = layers; + return m_maxGpuLayers; +} + double ModelInfo::repeatPenalty() const { return MySettings::globalInstance()->modelRepeatPenalty(*this); @@ -286,6 +322,7 @@ ModelList::ModelList() connect(MySettings::globalInstance(), &MySettings::maxLengthChanged, this, &ModelList::updateDataForSettings); connect(MySettings::globalInstance(), &MySettings::promptBatchSizeChanged, this, &ModelList::updateDataForSettings); connect(MySettings::globalInstance(), &MySettings::contextLengthChanged, this, &ModelList::updateDataForSettings); + connect(MySettings::globalInstance(), &MySettings::gpuLayersChanged, this, &ModelList::updateDataForSettings); connect(MySettings::globalInstance(), &MySettings::repeatPenaltyChanged, this, &ModelList::updateDataForSettings); connect(MySettings::globalInstance(), &MySettings::repeatPenaltyTokensChanged, this, &ModelList::updateDataForSettings);; connect(MySettings::globalInstance(), &MySettings::promptTemplateChanged, this, &ModelList::updateDataForSettings); @@ -539,6 +576,8 @@ QVariant ModelList::dataInternal(const ModelInfo *info, int role) const return info->promptBatchSize(); case ContextLengthRole: return info->contextLength(); + case GpuLayersRole: + return info->gpuLayers(); case RepeatPenaltyRole: return info->repeatPenalty(); case RepeatPenaltyTokensRole: @@ -664,6 +703,10 @@ void ModelList::updateData(const QString &id, int role, const QVariant &value) info->setMaxLength(value.toInt()); break; case PromptBatchSizeRole: info->setPromptBatchSize(value.toInt()); break; + case ContextLengthRole: + info->setContextLength(value.toInt()); break; + case GpuLayersRole: + info->setGpuLayers(value.toInt()); break; case RepeatPenaltyRole: info->setRepeatPenalty(value.toDouble()); break; case RepeatPenaltyTokensRole: @@ -755,6 +798,7 @@ QString ModelList::clone(const ModelInfo &model) updateData(id, ModelList::MaxLengthRole, model.maxLength()); updateData(id, ModelList::PromptBatchSizeRole, model.promptBatchSize()); updateData(id, ModelList::ContextLengthRole, model.contextLength()); + updateData(id, ModelList::GpuLayersRole, model.contextLength()); updateData(id, ModelList::RepeatPenaltyRole, model.repeatPenalty()); updateData(id, ModelList::RepeatPenaltyTokensRole, model.repeatPenaltyTokens()); updateData(id, ModelList::PromptTemplateRole, model.promptTemplate()); @@ -1123,6 +1167,8 @@ void ModelList::parseModelsJsonFile(const QByteArray &jsonData, bool save) updateData(id, ModelList::PromptBatchSizeRole, obj["promptBatchSize"].toInt()); if (obj.contains("contextLength")) updateData(id, ModelList::ContextLengthRole, obj["contextLength"].toInt()); + if (obj.contains("gpuLayers")) + updateData(id, ModelList::GpuLayersRole, obj["gpuLayers"].toInt()); if (obj.contains("repeatPenalty")) updateData(id, ModelList::RepeatPenaltyRole, obj["repeatPenalty"].toDouble()); if (obj.contains("repeatPenaltyTokens")) @@ -1217,6 +1263,8 @@ void ModelList::updateModelsFromSettings() const int promptBatchSize = settings.value(g + "/promptBatchSize").toInt(); Q_ASSERT(settings.contains(g + "/contextLength")); const int contextLength = settings.value(g + "/contextLength").toInt(); + Q_ASSERT(settings.contains(g + "/gpuLayers")); + const int gpuLayers = settings.value(g + "/gpuLayers").toInt(); Q_ASSERT(settings.contains(g + "/repeatPenalty")); const double repeatPenalty = settings.value(g + "/repeatPenalty").toDouble(); Q_ASSERT(settings.contains(g + "/repeatPenaltyTokens")); @@ -1236,6 +1284,7 @@ void ModelList::updateModelsFromSettings() updateData(id, ModelList::MaxLengthRole, maxLength); updateData(id, ModelList::PromptBatchSizeRole, promptBatchSize); updateData(id, ModelList::ContextLengthRole, contextLength); + updateData(id, ModelList::GpuLayersRole, gpuLayers); updateData(id, ModelList::RepeatPenaltyRole, repeatPenalty); updateData(id, ModelList::RepeatPenaltyTokensRole, repeatPenaltyTokens); updateData(id, ModelList::PromptTemplateRole, promptTemplate); diff --git a/gpt4all-chat/modellist.h b/gpt4all-chat/modellist.h index c314540744ad..475d6a40bb10 100644 --- a/gpt4all-chat/modellist.h +++ b/gpt4all-chat/modellist.h @@ -40,6 +40,9 @@ struct ModelInfo { Q_PROPERTY(int maxLength READ maxLength WRITE setMaxLength) Q_PROPERTY(int promptBatchSize READ promptBatchSize WRITE setPromptBatchSize) Q_PROPERTY(int contextLength READ contextLength WRITE setContextLength) + Q_PROPERTY(int maxContextLength READ maxContextLength) + Q_PROPERTY(int gpuLayers READ gpuLayers WRITE setGpuLayers) + Q_PROPERTY(int maxGpuLayers READ maxGpuLayers) Q_PROPERTY(double repeatPenalty READ repeatPenalty WRITE setRepeatPenalty) Q_PROPERTY(int repeatPenaltyTokens READ repeatPenaltyTokens WRITE setRepeatPenaltyTokens) Q_PROPERTY(QString promptTemplate READ promptTemplate WRITE setPromptTemplate) @@ -97,6 +100,10 @@ struct ModelInfo { void setPromptBatchSize(int s); int contextLength() const; void setContextLength(int l); + int maxContextLength() const; + int gpuLayers() const; + void setGpuLayers(int l); + int maxGpuLayers() const; double repeatPenalty() const; void setRepeatPenalty(double p); int repeatPenaltyTokens() const; @@ -110,16 +117,19 @@ struct ModelInfo { QString m_id; QString m_name; QString m_filename; - double m_temperature = 0.7; - double m_topP = 0.4; - int m_topK = 40; - int m_maxLength = 4096; - int m_promptBatchSize = 128; - int m_contextLength = 2048; - double m_repeatPenalty = 1.18; - int m_repeatPenaltyTokens = 64; - QString m_promptTemplate = "### Human:\n%1\n### Assistant:\n"; - QString m_systemPrompt = "### System:\nYou are an AI assistant who gives a quality response to whatever humans ask of you.\n"; + double m_temperature = 0.7; + double m_topP = 0.4; + int m_topK = 40; + int m_maxLength = 4096; + int m_promptBatchSize = 128; + int m_contextLength = 2048; + mutable int m_maxContextLength = -1; + int m_gpuLayers = 100; + mutable int m_maxGpuLayers = -1; + double m_repeatPenalty = 1.18; + int m_repeatPenaltyTokens = 64; + QString m_promptTemplate = "### Human:\n%1\n### Assistant:\n"; + QString m_systemPrompt = "### System:\nYou are an AI assistant who gives a quality response to whatever humans ask of you.\n"; friend class MySettings; }; Q_DECLARE_METATYPE(ModelInfo) @@ -232,6 +242,7 @@ class ModelList : public QAbstractListModel MaxLengthRole, PromptBatchSizeRole, ContextLengthRole, + GpuLayersRole, RepeatPenaltyRole, RepeatPenaltyTokensRole, PromptTemplateRole, @@ -275,6 +286,7 @@ class ModelList : public QAbstractListModel roles[MaxLengthRole] = "maxLength"; roles[PromptBatchSizeRole] = "promptBatchSize"; roles[ContextLengthRole] = "contextLength"; + roles[GpuLayersRole] = "gpuLayers"; roles[RepeatPenaltyRole] = "repeatPenalty"; roles[RepeatPenaltyTokensRole] = "repeatPenaltyTokens"; roles[PromptTemplateRole] = "promptTemplate"; diff --git a/gpt4all-chat/mysettings.cpp b/gpt4all-chat/mysettings.cpp index 5f5c7b801b62..f9774bde2f57 100644 --- a/gpt4all-chat/mysettings.cpp +++ b/gpt4all-chat/mysettings.cpp @@ -91,6 +91,7 @@ void MySettings::restoreModelDefaults(const ModelInfo &model) setModelMaxLength(model, model.m_maxLength); setModelPromptBatchSize(model, model.m_promptBatchSize); setModelContextLength(model, model.m_contextLength); + setModelGpuLayers(model, model.m_gpuLayers); setModelRepeatPenalty(model, model.m_repeatPenalty); setModelRepeatPenaltyTokens(model, model.m_repeatPenaltyTokens); setModelPromptTemplate(model, model.m_promptTemplate); @@ -303,6 +304,28 @@ void MySettings::setModelContextLength(const ModelInfo &m, int l, bool force) emit contextLengthChanged(m); } +int MySettings::modelGpuLayers(const ModelInfo &m) const +{ + QSettings setting; + setting.sync(); + return setting.value(QString("model-%1").arg(m.id()) + "/gpuLayers", m.m_gpuLayers).toInt(); +} + +void MySettings::setModelGpuLayers(const ModelInfo &m, int l, bool force) +{ + if (modelGpuLayers(m) == l && !force) + return; + + QSettings setting; + if (m.m_gpuLayers == l && !m.isClone) + setting.remove(QString("model-%1").arg(m.id()) + "/gpuLayers"); + else + setting.setValue(QString("model-%1").arg(m.id()) + "/gpuLayers", l); + setting.sync(); + if (!force) + emit gpuLayersChanged(m); +} + double MySettings::modelRepeatPenalty(const ModelInfo &m) const { QSettings setting; diff --git a/gpt4all-chat/mysettings.h b/gpt4all-chat/mysettings.h index 3287f413a562..4bfbef6b6390 100644 --- a/gpt4all-chat/mysettings.h +++ b/gpt4all-chat/mysettings.h @@ -63,6 +63,8 @@ class MySettings : public QObject Q_INVOKABLE void setModelSystemPrompt(const ModelInfo &m, const QString &p, bool force = false); int modelContextLength(const ModelInfo &m) const; Q_INVOKABLE void setModelContextLength(const ModelInfo &m, int s, bool force = false); + int modelGpuLayers(const ModelInfo &m) const; + Q_INVOKABLE void setModelGpuLayers(const ModelInfo &m, int s, bool force = false); // Application settings int threadCount() const; @@ -85,6 +87,8 @@ class MySettings : public QObject void setDevice(const QString &u); int32_t contextLength() const; void setContextLength(int32_t value); + int32_t gpuLayers() const; + void setGpuLayers(int32_t value); // Release/Download settings QString lastVersionStarted() const; @@ -121,6 +125,7 @@ class MySettings : public QObject void maxLengthChanged(const ModelInfo &model); void promptBatchSizeChanged(const ModelInfo &model); void contextLengthChanged(const ModelInfo &model); + void gpuLayersChanged(const ModelInfo &model); void repeatPenaltyChanged(const ModelInfo &model); void repeatPenaltyTokensChanged(const ModelInfo &model); void promptTemplateChanged(const ModelInfo &model); diff --git a/gpt4all-chat/qml/ModelSettings.qml b/gpt4all-chat/qml/ModelSettings.qml index 1c9b0249581b..ce2f51570643 100644 --- a/gpt4all-chat/qml/ModelSettings.qml +++ b/gpt4all-chat/qml/ModelSettings.qml @@ -332,9 +332,6 @@ MySettingsTab { ToolTip.visible: hovered Layout.row: 0 Layout.column: 1 - validator: IntValidator { - bottom: 1 - } Connections { target: MySettings function onContextLengthChanged() { @@ -349,11 +346,18 @@ MySettingsTab { } onEditingFinished: { var val = parseInt(text) - if (!isNaN(val)) { + if (isNaN(val)) { + text = root.currentModelInfo.contextLength + } else { + if (val < 8) { + val = 8 + contextLengthField.text = val + } else if (val > root.currentModelInfo.maxContextLength) { + val = root.currentModelInfo.maxContextLength + contextLengthField.text = val + } MySettings.setModelContextLength(root.currentModelInfo, val) focus = false - } else { - text = root.currentModelInfo.contextLength } } Accessible.role: Accessible.EditableText @@ -674,6 +678,60 @@ MySettingsTab { Accessible.name: repeatPenaltyTokensLabel.text Accessible.description: ToolTip.text } + + MySettingsLabel { + id: gpuLayersLabel + visible: !root.currentModelInfo.isChatGPT + text: qsTr("GPU Layers") + Layout.row: 4 + Layout.column: 0 + } + MyTextField { + id: gpuLayersField + visible: !root.currentModelInfo.isChatGPT + text: root.currentModelInfo.gpuLayers + font.pixelSize: theme.fontSizeLarge + color: theme.textColor + ToolTip.text: qsTr("How many GPU layers to load into VRAM. Decrease this if GPT4All runs out of VRAM while loading this model.\nLower values increase CPU load and RAM usage, and make inference slower.\nNOTE: Does not take effect until you RESTART GPT4All or SWITCH MODELS.") + ToolTip.visible: hovered + Layout.row: 4 + Layout.column: 1 + Connections { + target: MySettings + function onGpuLayersChanged() { + gpuLayersField.text = root.currentModelInfo.gpuLayers + } + } + Connections { + target: root + function onCurrentModelInfoChanged() { + if (root.currentModelInfo.gpuLayers == 100) { + gpuLayersField.text = root.currentModelInfo.maxGpuLayers + } else { + gpuLayersField.text = root.currentModelInfo.gpuLayers + } + } + } + onEditingFinished: { + var val = parseInt(text) + if (isNaN(val)) { + gpuLayersField.text = root.currentModelInfo.gpuLayers + } else { + if (val < 1) { + val = 1 + gpuLayersField.text = val + } else if (val > root.currentModelInfo.maxGpuLayers) { + val = root.currentModelInfo.maxGpuLayers + gpuLayersField.text = val + } + MySettings.setModelGpuLayers(root.currentModelInfo, val) + focus = false + } + } + Accessible.role: Accessible.EditableText + Accessible.name: gpuLayersLabel.text + Accessible.description: ToolTip.text + } } Rectangle { From b11c3f679e2e228f069528e97725fbaba278cd24 Mon Sep 17 00:00:00 2001 From: Jared Van Bortel Date: Wed, 31 Jan 2024 15:02:34 -0500 Subject: [PATCH 17/27] bump llama.cpp-mainline for C++11 compat Signed-off-by: Jared Van Bortel --- gpt4all-backend/llama.cpp-mainline | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gpt4all-backend/llama.cpp-mainline b/gpt4all-backend/llama.cpp-mainline index 997a733992d3..d2e3b5fd464a 160000 --- a/gpt4all-backend/llama.cpp-mainline +++ b/gpt4all-backend/llama.cpp-mainline @@ -1 +1 @@ -Subproject commit 997a733992d38fe5851f68f4e69cf24fa983e8f1 +Subproject commit d2e3b5fd464aa95d44b3aeb6d532ad7374f7478b From 0a40e716520f3438a331426ca8afd1df6dc4f397 Mon Sep 17 00:00:00 2001 From: Jared Van Bortel Date: Wed, 31 Jan 2024 16:32:32 -0500 Subject: [PATCH 18/27] Maxwell/Pascal GPU support and crash fix (#1895) Signed-off-by: Jared Van Bortel --- gpt4all-backend/llama.cpp-mainline | 2 +- gpt4all-chat/chatlistmodel.h | 1 + gpt4all-chat/main.cpp | 4 +++- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/gpt4all-backend/llama.cpp-mainline b/gpt4all-backend/llama.cpp-mainline index d2e3b5fd464a..b0df9e6fad73 160000 --- a/gpt4all-backend/llama.cpp-mainline +++ b/gpt4all-backend/llama.cpp-mainline @@ -1 +1 @@ -Subproject commit d2e3b5fd464aa95d44b3aeb6d532ad7374f7478b +Subproject commit b0df9e6fad734a4cff48e80b120b72f1f2c39094 diff --git a/gpt4all-chat/chatlistmodel.h b/gpt4all-chat/chatlistmodel.h index 24a95c3dc62a..3f99c622894e 100644 --- a/gpt4all-chat/chatlistmodel.h +++ b/gpt4all-chat/chatlistmodel.h @@ -196,6 +196,7 @@ class ChatListModel : public QAbstractListModel m_newChat = nullptr; m_serverChat = nullptr; m_currentChat = nullptr; + for (auto * chat: m_chats) { delete chat; } m_chats.clear(); } diff --git a/gpt4all-chat/main.cpp b/gpt4all-chat/main.cpp index d015581aba1d..7debe8bef251 100644 --- a/gpt4all-chat/main.cpp +++ b/gpt4all-chat/main.cpp @@ -63,9 +63,11 @@ int main(int argc, char *argv[]) } #endif + int res = app.exec(); + // Make sure ChatLLM threads are joined before global destructors run. // Otherwise, we can get a heap-use-after-free inside of llama.cpp. ChatListModel::globalInstance()->clearChats(); - return app.exec(); + return res; } From 6db53077306255cc4e086eb2ef57e02961f69653 Mon Sep 17 00:00:00 2001 From: Jared Van Bortel Date: Wed, 31 Jan 2024 16:44:58 -0500 Subject: [PATCH 19/27] update llama.cpp for unhandled Vulkan OOM exception fix Signed-off-by: Jared Van Bortel --- gpt4all-backend/llama.cpp-mainline | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gpt4all-backend/llama.cpp-mainline b/gpt4all-backend/llama.cpp-mainline index b0df9e6fad73..e18ff04f9fcf 160000 --- a/gpt4all-backend/llama.cpp-mainline +++ b/gpt4all-backend/llama.cpp-mainline @@ -1 +1 @@ -Subproject commit b0df9e6fad734a4cff48e80b120b72f1f2c39094 +Subproject commit e18ff04f9fcff1c56fa50e455e3da6807a057612 From eadc3b8d80d69f3414ead0cc970483f98d5a7b1b Mon Sep 17 00:00:00 2001 From: Jared Van Bortel Date: Wed, 31 Jan 2024 17:24:01 -0500 Subject: [PATCH 20/27] backend: bump llama.cpp for VRAM leak fix when switching models Signed-off-by: Jared Van Bortel --- gpt4all-backend/llama.cpp-mainline | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gpt4all-backend/llama.cpp-mainline b/gpt4all-backend/llama.cpp-mainline index e18ff04f9fcf..47aec1bcc09e 160000 --- a/gpt4all-backend/llama.cpp-mainline +++ b/gpt4all-backend/llama.cpp-mainline @@ -1 +1 @@ -Subproject commit e18ff04f9fcff1c56fa50e455e3da6807a057612 +Subproject commit 47aec1bcc09e090f0b8f196dc0a4e43b89507e4a From d14b95f4bde46e671e51695aa3bb4e9354f2b29f Mon Sep 17 00:00:00 2001 From: Adam Treat Date: Mon, 22 Jan 2024 12:36:01 -0500 Subject: [PATCH 21/27] Add Nomic Embed model for atlas with localdocs. --- gpt4all-chat/chatllm.cpp | 2 +- gpt4all-chat/database.cpp | 61 ++++- gpt4all-chat/database.h | 13 +- gpt4all-chat/embeddings.cpp | 3 + gpt4all-chat/embllm.cpp | 264 ++++++++++++++++++++- gpt4all-chat/embllm.h | 66 +++++- gpt4all-chat/localdocs.cpp | 6 + gpt4all-chat/localdocsmodel.cpp | 27 +++ gpt4all-chat/localdocsmodel.h | 9 +- gpt4all-chat/main.qml | 2 +- gpt4all-chat/metadata/models2.json | 2 +- gpt4all-chat/modellist.cpp | 57 ++++- gpt4all-chat/modellist.h | 10 +- gpt4all-chat/qml/CollectionsDialog.qml | 14 +- gpt4all-chat/qml/ModelDownloaderDialog.qml | 40 ++-- 15 files changed, 502 insertions(+), 74 deletions(-) diff --git a/gpt4all-chat/chatllm.cpp b/gpt4all-chat/chatllm.cpp index eac7b0c23e32..11cc25592b1d 100644 --- a/gpt4all-chat/chatllm.cpp +++ b/gpt4all-chat/chatllm.cpp @@ -156,7 +156,7 @@ bool ChatLLM::loadModel(const ModelInfo &modelInfo) if (isModelLoaded() && this->modelInfo() == modelInfo) return true; - bool isChatGPT = modelInfo.isChatGPT; + bool isChatGPT = modelInfo.isOnline; // right now only chatgpt is offered for online chat models... QString filePath = modelInfo.dirpath + modelInfo.filename(); QFileInfo fileInfo(filePath); diff --git a/gpt4all-chat/database.cpp b/gpt4all-chat/database.cpp index 8369db5be876..3fdde3acd9c5 100644 --- a/gpt4all-chat/database.cpp +++ b/gpt4all-chat/database.cpp @@ -558,7 +558,6 @@ void Database::scheduleNext(int folder_id, size_t countForFolder) if (!countForFolder) { emit updateIndexing(folder_id, false); emit updateInstalled(folder_id, true); - m_embeddings->save(); } if (!m_docsToScan.isEmpty()) QTimer::singleShot(0, this, &Database::scanQueue); @@ -570,7 +569,7 @@ void Database::handleDocumentError(const QString &errorMessage, qWarning() << errorMessage << document_id << document_path << error.text(); } -size_t Database::chunkStream(QTextStream &stream, int document_id, const QString &file, +size_t Database::chunkStream(QTextStream &stream, int folder_id, int document_id, const QString &file, const QString &title, const QString &author, const QString &subject, const QString &keywords, int page, int maxChunks) { @@ -580,6 +579,8 @@ size_t Database::chunkStream(QTextStream &stream, int document_id, const QString QList words; int chunks = 0; + QVector chunkList; + while (!stream.atEnd()) { QString word; stream >> word; @@ -605,9 +606,22 @@ size_t Database::chunkStream(QTextStream &stream, int document_id, const QString qWarning() << "ERROR: Could not insert chunk into db" << q.lastError(); } +#if 1 + EmbeddingChunk toEmbed; + toEmbed.folder_id = folder_id; + toEmbed.chunk_id = chunk_id; + toEmbed.chunk = chunk; + chunkList << toEmbed; + if (chunkList.count() == 100) { + m_embLLM->generateAsyncEmbeddings(chunkList); + emit updateTotalEmbeddingsToIndex(folder_id, 100); + chunkList.clear(); + } +#else const std::vector result = m_embLLM->generateEmbeddings(chunk); if (!m_embeddings->add(result, chunk_id)) qWarning() << "ERROR: Cannot add point to embeddings index"; +#endif ++chunks; @@ -615,12 +629,39 @@ size_t Database::chunkStream(QTextStream &stream, int document_id, const QString charCount = 0; if (maxChunks > 0 && chunks == maxChunks) - return stream.pos(); + break; } } + + if (!chunkList.isEmpty()) { + m_embLLM->generateAsyncEmbeddings(chunkList); + emit updateTotalEmbeddingsToIndex(folder_id, chunkList.count()); + chunkList.clear(); + } + return stream.pos(); } +void Database::handleEmbeddingsGenerated(const QVector &embeddings) +{ + if (embeddings.isEmpty()) + return; + + int folder_id = 0; + for (auto e : embeddings) { + folder_id = e.folder_id; + if (!m_embeddings->add(e.embedding, e.chunk_id)) + qWarning() << "ERROR: Cannot add point to embeddings index"; + } + emit updateCurrentEmbeddingsToIndex(folder_id, embeddings.count()); + m_embeddings->save(); +} + +void Database::handleErrorGenerated(int folder_id, const QString &error) +{ + emit updateError(folder_id, error); +} + void Database::removeEmbeddingsByDocumentId(int document_id) { QSqlQuery q; @@ -792,14 +833,13 @@ void Database::scanQueue() const QPdfSelection selection = doc.getAllText(pageIndex); QString text = selection.text(); QTextStream stream(&text); - chunkStream(stream, document_id, info.doc.fileName(), + chunkStream(stream, info.folder, document_id, info.doc.fileName(), doc.metaData(QPdfDocument::MetaDataField::Title).toString(), doc.metaData(QPdfDocument::MetaDataField::Author).toString(), doc.metaData(QPdfDocument::MetaDataField::Subject).toString(), doc.metaData(QPdfDocument::MetaDataField::Keywords).toString(), pageIndex + 1 ); - m_embeddings->save(); emit subtractCurrentBytesToIndex(info.folder, bytesPerPage); if (info.currentPage < doc.pageCount()) { info.currentPage += 1; @@ -828,9 +868,8 @@ void Database::scanQueue() #if defined(DEBUG) qDebug() << "scanning byteIndex" << byteIndex << "of" << bytes << document_path; #endif - int pos = chunkStream(stream, document_id, info.doc.fileName(), QString() /*title*/, QString() /*author*/, - QString() /*subject*/, QString() /*keywords*/, -1 /*page*/, 5 /*maxChunks*/); - m_embeddings->save(); + int pos = chunkStream(stream, info.folder, document_id, info.doc.fileName(), QString() /*title*/, QString() /*author*/, + QString() /*subject*/, QString() /*keywords*/, -1 /*page*/, 100 /*maxChunks*/); file.close(); const size_t bytesChunked = pos - byteIndex; emit subtractCurrentBytesToIndex(info.folder, bytesChunked); @@ -892,6 +931,8 @@ void Database::scanDocuments(int folder_id, const QString &folder_path) void Database::start() { connect(m_watcher, &QFileSystemWatcher::directoryChanged, this, &Database::directoryChanged); + connect(m_embLLM, &EmbeddingLLM::embeddingsGenerated, this, &Database::handleEmbeddingsGenerated); + connect(m_embLLM, &EmbeddingLLM::errorGenerated, this, &Database::handleErrorGenerated); connect(this, &Database::docsToScanChanged, this, &Database::scanQueue); if (!QSqlDatabase::drivers().contains("QSQLITE")) { qWarning() << "ERROR: missing sqllite driver"; @@ -1081,6 +1122,10 @@ void Database::retrieveFromDB(const QList &collections, const QString & QSqlQuery q; if (m_embeddings->isLoaded()) { std::vector result = m_embLLM->generateEmbeddings(text); + if (result.empty()) { + qDebug() << "ERROR: generating embeddings returned a null result"; + return; + } std::vector embeddings = m_embeddings->search(result, retrievalSize); if (!selectChunk(q, collections, embeddings, retrievalSize)) { qDebug() << "ERROR: selecting chunks:" << q.lastError().text(); diff --git a/gpt4all-chat/database.h b/gpt4all-chat/database.h index b217758bf1bd..9d10fd000821 100644 --- a/gpt4all-chat/database.h +++ b/gpt4all-chat/database.h @@ -8,8 +8,9 @@ #include #include +#include "embllm.h" + class Embeddings; -class EmbeddingLLM; struct DocumentInfo { int folder; @@ -39,10 +40,13 @@ struct CollectionItem { int folder_id = -1; bool installed = false; bool indexing = false; + QString error; int currentDocsToIndex = 0; int totalDocsToIndex = 0; size_t currentBytesToIndex = 0; size_t totalBytesToIndex = 0; + size_t currentEmbeddingsToIndex = 0; + size_t totalEmbeddingsToIndex = 0; }; Q_DECLARE_METATYPE(CollectionItem) @@ -66,11 +70,14 @@ public Q_SLOTS: void docsToScanChanged(); void updateInstalled(int folder_id, bool b); void updateIndexing(int folder_id, bool b); + void updateError(int folder_id, const QString &error); void updateCurrentDocsToIndex(int folder_id, size_t currentDocsToIndex); void updateTotalDocsToIndex(int folder_id, size_t totalDocsToIndex); void subtractCurrentBytesToIndex(int folder_id, size_t subtractedBytes); void updateCurrentBytesToIndex(int folder_id, size_t currentBytesToIndex); void updateTotalBytesToIndex(int folder_id, size_t totalBytesToIndex); + void updateCurrentEmbeddingsToIndex(int folder_id, size_t currentBytesToIndex); + void updateTotalEmbeddingsToIndex(int folder_id, size_t totalBytesToIndex); void addCollectionItem(const CollectionItem &item); void removeFolderById(int folder_id); void removeCollectionItem(const QString &collectionName); @@ -82,10 +89,12 @@ private Q_SLOTS: bool addFolderToWatch(const QString &path); bool removeFolderFromWatch(const QString &path); void addCurrentFolders(); + void handleEmbeddingsGenerated(const QVector &embeddings); + void handleErrorGenerated(int folder_id, const QString &error); private: void removeFolderInternal(const QString &collection, int folder_id, const QString &path); - size_t chunkStream(QTextStream &stream, int document_id, const QString &file, + size_t chunkStream(QTextStream &stream, int folder_id, int document_id, const QString &file, const QString &title, const QString &author, const QString &subject, const QString &keywords, int page, int maxChunks = -1); void removeEmbeddingsByDocumentId(int document_id); diff --git a/gpt4all-chat/embeddings.cpp b/gpt4all-chat/embeddings.cpp index 6bfe256a8464..cd157033e959 100644 --- a/gpt4all-chat/embeddings.cpp +++ b/gpt4all-chat/embeddings.cpp @@ -129,6 +129,9 @@ bool Embeddings::add(const std::vector &embedding, qint64 label) } } + if (embedding.empty()) + return false; + try { m_hnsw->addPoint(embedding.data(), label, false); } catch (const std::exception &e) { diff --git a/gpt4all-chat/embllm.cpp b/gpt4all-chat/embllm.cpp index bc7b2ef9089c..7a50fa01f0ae 100644 --- a/gpt4all-chat/embllm.cpp +++ b/gpt4all-chat/embllm.cpp @@ -1,19 +1,31 @@ #include "embllm.h" #include "modellist.h" -EmbeddingLLM::EmbeddingLLM() - : QObject{nullptr} - , m_model{nullptr} +EmbeddingLLMWorker::EmbeddingLLMWorker() + : QObject(nullptr) + , m_networkManager(new QNetworkAccessManager(this)) + , m_model(nullptr) { + moveToThread(&m_workerThread); + connect(this, &EmbeddingLLMWorker::finished, &m_workerThread, &QThread::quit, Qt::DirectConnection); + m_workerThread.setObjectName("embedding"); + m_workerThread.start(); } -EmbeddingLLM::~EmbeddingLLM() +EmbeddingLLMWorker::~EmbeddingLLMWorker() { - delete m_model; - m_model = nullptr; + if (m_model) { + delete m_model; + m_model = nullptr; + } } -bool EmbeddingLLM::loadModel() +void EmbeddingLLMWorker::wait() +{ + m_workerThread.wait(); +} + +bool EmbeddingLLMWorker::loadModel() { const EmbeddingModels *embeddingModels = ModelList::globalInstance()->embeddingModels(); if (!embeddingModels->count()) @@ -29,6 +41,16 @@ bool EmbeddingLLM::loadModel() return false; } + bool isNomic = fileInfo.fileName().startsWith("nomic"); + if (isNomic) { + QFile file(filePath); + file.open(QIODeviceBase::ReadOnly | QIODeviceBase::Text); + QTextStream stream(&file); + m_nomicAPIKey = stream.readAll(); + file.close(); + return true; + } + m_model = LLModel::Implementation::construct(filePath.toStdString()); bool success = m_model->loadModel(filePath.toStdString(), 2048, 0); if (!success) { @@ -47,18 +69,236 @@ bool EmbeddingLLM::loadModel() return true; } -bool EmbeddingLLM::hasModel() const +bool EmbeddingLLMWorker::hasModel() const { - return m_model; + return m_model || !m_nomicAPIKey.isEmpty(); } -std::vector EmbeddingLLM::generateEmbeddings(const QString &text) +bool EmbeddingLLMWorker::isNomic() const +{ + return !m_nomicAPIKey.isEmpty(); +} + +std::vector EmbeddingLLMWorker::generateSyncEmbedding(const QString &text) { if (!hasModel() && !loadModel()) { - qWarning() << "WARNING: Could not load sbert model for embeddings"; + qWarning() << "WARNING: Could not load model for embeddings"; + return std::vector(); + } + + if (isNomic()) { + qWarning() << "WARNING: Request to generate sync embeddings for non-local model invalid"; return std::vector(); } - Q_ASSERT(hasModel()); return m_model->embedding(text.toStdString()); } + +void EmbeddingLLMWorker::requestSyncEmbedding(const QString &text) +{ + if (!hasModel() && !loadModel()) { + qWarning() << "WARNING: Could not load model for embeddings"; + return; + } + + if (!isNomic()) { + qWarning() << "WARNING: Request to generate sync embeddings for local model invalid"; + return; + } + + Q_ASSERT(hasModel()); + + QJsonObject root; + root.insert("model", "nomic-embed-text-v1"); + QJsonArray texts; + texts.append(text); + root.insert("texts", texts); + + QJsonDocument doc(root); + + QUrl nomicUrl("https://api-atlas.nomic.ai/v1/embedding/text"); + const QString authorization = QString("Bearer %1").arg(m_nomicAPIKey).trimmed(); + QNetworkRequest request(nomicUrl); + request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); + request.setRawHeader("Authorization", authorization.toUtf8()); + QNetworkReply *reply = m_networkManager->post(request, doc.toJson(QJsonDocument::Compact)); + connect(qApp, &QCoreApplication::aboutToQuit, reply, &QNetworkReply::abort); + connect(reply, &QNetworkReply::finished, this, &EmbeddingLLMWorker::handleFinished); +} + +void EmbeddingLLMWorker::requestAsyncEmbedding(const QVector &chunks) +{ + if (!hasModel() && !loadModel()) { + qWarning() << "WARNING: Could not load model for embeddings"; + return; + } + + if (m_nomicAPIKey.isEmpty()) { + QVector results; + results.reserve(chunks.size()); + for (auto c : chunks) { + EmbeddingResult result; + result.folder_id = c.folder_id; + result.chunk_id = c.chunk_id; + result.embedding = m_model->embedding(c.chunk.toStdString()); + results << result; + } + emit embeddingsGenerated(results); + return; + }; + + QJsonObject root; + root.insert("model", "nomic-embed-text-v1"); + QJsonArray texts; + + for (auto c : chunks) + texts.append(c.chunk); + root.insert("texts", texts); + + QJsonDocument doc(root); + + QUrl nomicUrl("https://api-atlas.nomic.ai/v1/embedding/text"); + const QString authorization = QString("Bearer %1").arg(m_nomicAPIKey).trimmed(); + QNetworkRequest request(nomicUrl); + request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); + request.setRawHeader("Authorization", authorization.toUtf8()); + request.setAttribute(QNetworkRequest::User, QVariant::fromValue(chunks)); + + QNetworkReply *reply = m_networkManager->post(request, doc.toJson(QJsonDocument::Compact)); + connect(qApp, &QCoreApplication::aboutToQuit, reply, &QNetworkReply::abort); + connect(reply, &QNetworkReply::finished, this, &EmbeddingLLMWorker::handleFinished); +} + +std::vector jsonArrayToVector(const QJsonArray &jsonArray) { + std::vector result; + + for (const QJsonValue &innerValue : jsonArray) { + if (innerValue.isArray()) { + QJsonArray innerArray = innerValue.toArray(); + result.reserve(result.size() + innerArray.size()); + for (const QJsonValue &value : innerArray) { + result.push_back(static_cast(value.toDouble())); + } + } + } + + return result; +} + +QVector jsonArrayToEmbeddingResults(const QVector& chunks, const QJsonArray& embeddings) { + QVector results; + + if (chunks.size() != embeddings.size()) { + qWarning() << "WARNING: Size of json array result does not match input!"; + return results; + } + + for (int i = 0; i < chunks.size(); ++i) { + const EmbeddingChunk& chunk = chunks.at(i); + const QJsonArray embeddingArray = embeddings.at(i).toArray(); + + std::vector embeddingVector; + for (const QJsonValue& value : embeddingArray) + embeddingVector.push_back(static_cast(value.toDouble())); + + EmbeddingResult result; + result.folder_id = chunk.folder_id; + result.chunk_id = chunk.chunk_id; + result.embedding = std::move(embeddingVector); + results.push_back(std::move(result)); + } + + return results; +} + +void EmbeddingLLMWorker::handleFinished() +{ + QNetworkReply *reply = qobject_cast(sender()); + if (!reply) + return; + + QVariant retrievedData = reply->request().attribute(QNetworkRequest::User); + QVector chunks; + if (retrievedData.isValid() && retrievedData.canConvert>()) + chunks = retrievedData.value>(); + + int folder_id = 0; + if (!chunks.isEmpty()) + folder_id = chunks.first().folder_id; + + QVariant response = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute); + Q_ASSERT(response.isValid()); + bool ok; + int code = response.toInt(&ok); + if (!ok || code != 200) { + QString errorDetails; + QString replyErrorString = reply->errorString().trimmed(); + QByteArray replyContent = reply->readAll().trimmed(); + errorDetails = QString("ERROR: Nomic Atlas responded with error code \"%1\"").arg(code); + if (!replyErrorString.isEmpty()) + errorDetails += QString(". Error Details: \"%1\"").arg(replyErrorString); + if (!replyContent.isEmpty()) + errorDetails += QString(". Response Content: \"%1\"").arg(QString::fromUtf8(replyContent)); + qWarning() << errorDetails; + emit errorGenerated(folder_id, errorDetails); + return; + } + + QByteArray jsonData = reply->readAll(); + + QJsonParseError err; + QJsonDocument document = QJsonDocument::fromJson(jsonData, &err); + if (err.error != QJsonParseError::NoError) { + qWarning() << "ERROR: Couldn't parse Nomic Atlas response: " << jsonData << err.errorString(); + return; + } + + const QJsonObject root = document.object(); + const QJsonArray embeddings = root.value("embeddings").toArray(); + + if (!chunks.isEmpty()) { + emit embeddingsGenerated(jsonArrayToEmbeddingResults(chunks, embeddings)); + } else { + m_lastResponse = jsonArrayToVector(embeddings); + emit finished(); + } + + reply->deleteLater(); +} + +EmbeddingLLM::EmbeddingLLM() + : QObject(nullptr) + , m_embeddingWorker(new EmbeddingLLMWorker) +{ + connect(this, &EmbeddingLLM::requestAsyncEmbedding, m_embeddingWorker, + &EmbeddingLLMWorker::requestAsyncEmbedding, Qt::QueuedConnection); + connect(m_embeddingWorker, &EmbeddingLLMWorker::embeddingsGenerated, this, + &EmbeddingLLM::embeddingsGenerated, Qt::QueuedConnection); + connect(m_embeddingWorker, &EmbeddingLLMWorker::errorGenerated, this, + &EmbeddingLLM::errorGenerated, Qt::QueuedConnection); +} + +EmbeddingLLM::~EmbeddingLLM() +{ + delete m_embeddingWorker; + m_embeddingWorker = nullptr; +} + +std::vector EmbeddingLLM::generateEmbeddings(const QString &text) +{ + if (!m_embeddingWorker->isNomic()) { + return m_embeddingWorker->generateSyncEmbedding(text); + } else { + EmbeddingLLMWorker worker; + connect(this, &EmbeddingLLM::requestSyncEmbedding, &worker, + &EmbeddingLLMWorker::requestSyncEmbedding, Qt::QueuedConnection); + emit requestSyncEmbedding(text); + worker.wait(); + return worker.lastResponse(); + } +} + +void EmbeddingLLM::generateAsyncEmbeddings(const QVector &chunks) +{ + emit requestAsyncEmbedding(chunks); +} diff --git a/gpt4all-chat/embllm.h b/gpt4all-chat/embllm.h index 29148546153f..cde30c60793a 100644 --- a/gpt4all-chat/embllm.h +++ b/gpt4all-chat/embllm.h @@ -3,8 +3,61 @@ #include #include +#include +#include + #include "../gpt4all-backend/llmodel.h" +struct EmbeddingChunk { + int folder_id; + int chunk_id; + QString chunk; +}; + +Q_DECLARE_METATYPE(EmbeddingChunk) + +struct EmbeddingResult { + int folder_id; + int chunk_id; + std::vector embedding; +}; + +class EmbeddingLLMWorker : public QObject { + Q_OBJECT +public: + EmbeddingLLMWorker(); + virtual ~EmbeddingLLMWorker(); + + void wait(); + + std::vector lastResponse() const { return m_lastResponse; } + + bool loadModel(); + bool hasModel() const; + bool isNomic() const; + + std::vector generateSyncEmbedding(const QString &text); + +public Q_SLOTS: + void requestSyncEmbedding(const QString &text); + void requestAsyncEmbedding(const QVector &chunks); + +Q_SIGNALS: + void embeddingsGenerated(const QVector &embeddings); + void errorGenerated(int folder_id, const QString &error); + void finished(); + +private Q_SLOTS: + void handleFinished(); + +private: + QString m_nomicAPIKey; + QNetworkAccessManager *m_networkManager; + std::vector m_lastResponse; + LLModel *m_model = nullptr; + QThread m_workerThread; +}; + class EmbeddingLLM : public QObject { Q_OBJECT @@ -12,16 +65,21 @@ class EmbeddingLLM : public QObject EmbeddingLLM(); virtual ~EmbeddingLLM(); + bool loadModel(); bool hasModel() const; public Q_SLOTS: - std::vector generateEmbeddings(const QString &text); + std::vector generateEmbeddings(const QString &text); // synchronous + void generateAsyncEmbeddings(const QVector &chunks); -private: - bool loadModel(); +Q_SIGNALS: + void requestSyncEmbedding(const QString &text); + void requestAsyncEmbedding(const QVector &chunks); + void embeddingsGenerated(const QVector &embeddings); + void errorGenerated(int folder_id, const QString &error); private: - LLModel *m_model = nullptr; + EmbeddingLLMWorker *m_embeddingWorker; }; #endif // EMBLLM_H diff --git a/gpt4all-chat/localdocs.cpp b/gpt4all-chat/localdocs.cpp index 1f37a2078a6f..3baaa983a03b 100644 --- a/gpt4all-chat/localdocs.cpp +++ b/gpt4all-chat/localdocs.cpp @@ -30,6 +30,8 @@ LocalDocs::LocalDocs() m_localDocsModel, &LocalDocsModel::updateInstalled, Qt::QueuedConnection); connect(m_database, &Database::updateIndexing, m_localDocsModel, &LocalDocsModel::updateIndexing, Qt::QueuedConnection); + connect(m_database, &Database::updateError, + m_localDocsModel, &LocalDocsModel::updateError, Qt::QueuedConnection); connect(m_database, &Database::updateCurrentDocsToIndex, m_localDocsModel, &LocalDocsModel::updateCurrentDocsToIndex, Qt::QueuedConnection); connect(m_database, &Database::updateTotalDocsToIndex, @@ -40,6 +42,10 @@ LocalDocs::LocalDocs() m_localDocsModel, &LocalDocsModel::updateCurrentBytesToIndex, Qt::QueuedConnection); connect(m_database, &Database::updateTotalBytesToIndex, m_localDocsModel, &LocalDocsModel::updateTotalBytesToIndex, Qt::QueuedConnection); + connect(m_database, &Database::updateCurrentEmbeddingsToIndex, + m_localDocsModel, &LocalDocsModel::updateCurrentEmbeddingsToIndex, Qt::QueuedConnection); + connect(m_database, &Database::updateTotalEmbeddingsToIndex, + m_localDocsModel, &LocalDocsModel::updateTotalEmbeddingsToIndex, Qt::QueuedConnection); connect(m_database, &Database::addCollectionItem, m_localDocsModel, &LocalDocsModel::addCollectionItem, Qt::QueuedConnection); connect(m_database, &Database::removeFolderById, diff --git a/gpt4all-chat/localdocsmodel.cpp b/gpt4all-chat/localdocsmodel.cpp index b2bea0faec23..56730169fc04 100644 --- a/gpt4all-chat/localdocsmodel.cpp +++ b/gpt4all-chat/localdocsmodel.cpp @@ -48,6 +48,8 @@ QVariant LocalDocsModel::data(const QModelIndex &index, int role) const return item.installed; case IndexingRole: return item.indexing; + case ErrorRole: + return item.error; case CurrentDocsToIndexRole: return item.currentDocsToIndex; case TotalDocsToIndexRole: @@ -56,6 +58,10 @@ QVariant LocalDocsModel::data(const QModelIndex &index, int role) const return quint64(item.currentBytesToIndex); case TotalBytesToIndexRole: return quint64(item.totalBytesToIndex); + case CurrentEmbeddingsToIndexRole: + return quint64(item.currentEmbeddingsToIndex); + case TotalEmbeddingsToIndexRole: + return quint64(item.totalEmbeddingsToIndex); } return QVariant(); @@ -68,10 +74,13 @@ QHash LocalDocsModel::roleNames() const roles[FolderPathRole] = "folder_path"; roles[InstalledRole] = "installed"; roles[IndexingRole] = "indexing"; + roles[ErrorRole] = "error"; roles[CurrentDocsToIndexRole] = "currentDocsToIndex"; roles[TotalDocsToIndexRole] = "totalDocsToIndex"; roles[CurrentBytesToIndexRole] = "currentBytesToIndex"; roles[TotalBytesToIndexRole] = "totalBytesToIndex"; + roles[CurrentEmbeddingsToIndexRole] = "currentEmbeddingsToIndex"; + roles[TotalEmbeddingsToIndexRole] = "totalEmbeddingsToIndex"; return roles; } @@ -101,6 +110,12 @@ void LocalDocsModel::updateIndexing(int folder_id, bool b) [](CollectionItem& item, bool val) { item.indexing = val; }, {IndexingRole}); } +void LocalDocsModel::updateError(int folder_id, const QString &error) +{ + updateField(folder_id, error, + [](CollectionItem& item, QString val) { item.error = val; }, {ErrorRole}); +} + void LocalDocsModel::updateCurrentDocsToIndex(int folder_id, size_t currentDocsToIndex) { updateField(folder_id, currentDocsToIndex, @@ -131,6 +146,18 @@ void LocalDocsModel::updateTotalBytesToIndex(int folder_id, size_t totalBytesToI [](CollectionItem& item, size_t val) { item.totalBytesToIndex = val; }, {TotalBytesToIndexRole}); } +void LocalDocsModel::updateCurrentEmbeddingsToIndex(int folder_id, size_t currentEmbeddingsToIndex) +{ + updateField(folder_id, currentEmbeddingsToIndex, + [](CollectionItem& item, size_t val) { item.currentEmbeddingsToIndex += val; }, {CurrentEmbeddingsToIndexRole}); +} + +void LocalDocsModel::updateTotalEmbeddingsToIndex(int folder_id, size_t totalEmbeddingsToIndex) +{ + updateField(folder_id, totalEmbeddingsToIndex, + [](CollectionItem& item, size_t val) { item.totalEmbeddingsToIndex += val; }, {TotalEmbeddingsToIndexRole}); +} + void LocalDocsModel::addCollectionItem(const CollectionItem &item) { beginInsertRows(QModelIndex(), m_collectionList.size(), m_collectionList.size()); diff --git a/gpt4all-chat/localdocsmodel.h b/gpt4all-chat/localdocsmodel.h index 47997143c4a7..4db836a342c8 100644 --- a/gpt4all-chat/localdocsmodel.h +++ b/gpt4all-chat/localdocsmodel.h @@ -30,11 +30,13 @@ class LocalDocsModel : public QAbstractListModel FolderPathRole, InstalledRole, IndexingRole, - EmbeddingRole, + ErrorRole, CurrentDocsToIndexRole, TotalDocsToIndexRole, CurrentBytesToIndexRole, - TotalBytesToIndexRole + TotalBytesToIndexRole, + CurrentEmbeddingsToIndexRole, + TotalEmbeddingsToIndexRole }; explicit LocalDocsModel(QObject *parent = nullptr); @@ -45,11 +47,14 @@ class LocalDocsModel : public QAbstractListModel public Q_SLOTS: void updateInstalled(int folder_id, bool b); void updateIndexing(int folder_id, bool b); + void updateError(int folder_id, const QString &error); void updateCurrentDocsToIndex(int folder_id, size_t currentDocsToIndex); void updateTotalDocsToIndex(int folder_id, size_t totalDocsToIndex); void subtractCurrentBytesToIndex(int folder_id, size_t subtractedBytes); void updateCurrentBytesToIndex(int folder_id, size_t currentBytesToIndex); void updateTotalBytesToIndex(int folder_id, size_t totalBytesToIndex); + void updateCurrentEmbeddingsToIndex(int folder_id, size_t currentBytesToIndex); + void updateTotalEmbeddingsToIndex(int folder_id, size_t totalBytesToIndex); void addCollectionItem(const CollectionItem &item); void removeFolderById(int folder_id); void removeCollectionPath(const QString &name, const QString &path); diff --git a/gpt4all-chat/main.qml b/gpt4all-chat/main.qml index 6e317062b3fa..ea79b1e0db0b 100644 --- a/gpt4all-chat/main.qml +++ b/gpt4all-chat/main.qml @@ -1129,7 +1129,7 @@ Window { } Image { - visible: currentChat.isServer || currentChat.modelInfo.isChatGPT + visible: currentChat.isServer || currentChat.modelInfo.isOnline anchors.fill: parent sourceSize.width: 1024 sourceSize.height: 1024 diff --git a/gpt4all-chat/metadata/models2.json b/gpt4all-chat/metadata/models2.json index 5105b3eb51c0..98bc4440ffa4 100644 --- a/gpt4all-chat/metadata/models2.json +++ b/gpt4all-chat/metadata/models2.json @@ -218,7 +218,7 @@ "quant": "f16", "type": "Bert", "systemPrompt": " ", - "description": "LocalDocs text embeddings model
    • Necessary for LocalDocs feature
    • Used for retrieval augmented generation (RAG)", + "description": "LocalDocs text embeddings model
      • For use with LocalDocs feature
      • Used for retrieval augmented generation (RAG)", "url": "https://gpt4all.io/models/gguf/all-MiniLM-L6-v2-f16.gguf" }, { diff --git a/gpt4all-chat/modellist.cpp b/gpt4all-chat/modellist.cpp index 97403594c2e3..7d07e4c5fc88 100644 --- a/gpt4all-chat/modellist.cpp +++ b/gpt4all-chat/modellist.cpp @@ -10,6 +10,7 @@ //#define USE_LOCAL_MODELSJSON #define DEFAULT_EMBEDDING_MODEL "all-MiniLM-L6-v2-f16.gguf" +#define NOMIC_EMBEDDING_MODEL "nomic-embed-text-v1.txt" QString ModelInfo::id() const { @@ -202,7 +203,8 @@ bool EmbeddingModels::filterAcceptsRow(int sourceRow, { QModelIndex index = sourceModel()->index(sourceRow, 0, sourceParent); bool isInstalled = sourceModel()->data(index, ModelList::InstalledRole).toBool(); - bool isEmbedding = sourceModel()->data(index, ModelList::FilenameRole).toString() == DEFAULT_EMBEDDING_MODEL; + bool isEmbedding = sourceModel()->data(index, ModelList::FilenameRole).toString() == DEFAULT_EMBEDDING_MODEL || + sourceModel()->data(index, ModelList::FilenameRole).toString() == NOMIC_EMBEDDING_MODEL; return isInstalled && isEmbedding; } @@ -405,7 +407,7 @@ ModelInfo ModelList::defaultModelInfo() const const size_t ramrequired = defaultModel->ramrequired; // If we don't have either setting, then just use the first model that requires less than 16GB that is installed - if (!hasUserDefaultName && !info->isChatGPT && ramrequired > 0 && ramrequired < 16) + if (!hasUserDefaultName && !info->isOnline && ramrequired > 0 && ramrequired < 16) break; // If we have a user specified default and match, then use it @@ -526,8 +528,8 @@ QVariant ModelList::dataInternal(const ModelInfo *info, int role) const return info->installed; case DefaultRole: return info->isDefault; - case ChatGPTRole: - return info->isChatGPT; + case OnlineRole: + return info->isOnline; case DisableGUIRole: return info->disableGUI; case DescriptionRole: @@ -655,8 +657,8 @@ void ModelList::updateData(const QString &id, int role, const QVariant &value) info->installed = value.toBool(); break; case DefaultRole: info->isDefault = value.toBool(); break; - case ChatGPTRole: - info->isChatGPT = value.toBool(); break; + case OnlineRole: + info->isOnline = value.toBool(); break; case DisableGUIRole: info->disableGUI = value.toBool(); break; case DescriptionRole: @@ -791,7 +793,7 @@ QString ModelList::clone(const ModelInfo &model) updateData(id, ModelList::FilenameRole, model.filename()); updateData(id, ModelList::DirpathRole, model.dirpath); updateData(id, ModelList::InstalledRole, model.installed); - updateData(id, ModelList::ChatGPTRole, model.isChatGPT); + updateData(id, ModelList::OnlineRole, model.isOnline); updateData(id, ModelList::TemperatureRole, model.temperature()); updateData(id, ModelList::TopPRole, model.topP()); updateData(id, ModelList::TopKRole, model.topK()); @@ -873,10 +875,10 @@ QString ModelList::uniqueModelName(const ModelInfo &model) const return baseName; } -QString ModelList::modelDirPath(const QString &modelName, bool isChatGPT) +QString ModelList::modelDirPath(const QString &modelName, bool isOnline) { QVector possibleFilePaths; - if (isChatGPT) + if (isOnline) possibleFilePaths << "/" + modelName + ".txt"; else { possibleFilePaths << "/ggml-" + modelName + ".bin"; @@ -911,7 +913,7 @@ void ModelList::updateModelsFromDirectory() // All files that end with .bin and have 'ggml' somewhere in the name if (((filename.endsWith(".bin") || filename.endsWith(".gguf")) && (/*filename.contains("ggml") ||*/ filename.contains("gguf")) && !filename.startsWith("incomplete")) - || (filename.endsWith(".txt") && filename.startsWith("chatgpt-"))) { + || (filename.endsWith(".txt") && (filename.startsWith("chatgpt-") || filename.startsWith("nomic-")))) { QString filePath = it.filePath(); QFileInfo info(filePath); @@ -934,7 +936,8 @@ void ModelList::updateModelsFromDirectory() for (const QString &id : modelsById) { updateData(id, FilenameRole, filename); - updateData(id, ChatGPTRole, filename.startsWith("chatgpt-")); + // FIXME: WE should change this to use a consistent filename for online models + updateData(id, OnlineRole, filename.startsWith("chatgpt-") || filename.startsWith("nomic-")); updateData(id, DirpathRole, info.dir().absolutePath() + "/"); updateData(id, FilesizeRole, toFileSize(info.size())); } @@ -1195,7 +1198,7 @@ void ModelList::parseModelsJsonFile(const QByteArray &jsonData, bool save) updateData(id, ModelList::NameRole, modelName); updateData(id, ModelList::FilenameRole, modelFilename); updateData(id, ModelList::FilesizeRole, "minimal"); - updateData(id, ModelList::ChatGPTRole, true); + updateData(id, ModelList::OnlineRole, true); updateData(id, ModelList::DescriptionRole, tr("OpenAI's ChatGPT model GPT-3.5 Turbo
        ") + chatGPTDesc); updateData(id, ModelList::RequiresVersionRole, "2.4.2"); @@ -1219,7 +1222,7 @@ void ModelList::parseModelsJsonFile(const QByteArray &jsonData, bool save) updateData(id, ModelList::NameRole, modelName); updateData(id, ModelList::FilenameRole, modelFilename); updateData(id, ModelList::FilesizeRole, "minimal"); - updateData(id, ModelList::ChatGPTRole, true); + updateData(id, ModelList::OnlineRole, true); updateData(id, ModelList::DescriptionRole, tr("OpenAI's ChatGPT model GPT-4
        ") + chatGPTDesc + chatGPT4Warn); updateData(id, ModelList::RequiresVersionRole, "2.4.2"); @@ -1229,6 +1232,34 @@ void ModelList::parseModelsJsonFile(const QByteArray &jsonData, bool save) updateData(id, ModelList::QuantRole, "NA"); updateData(id, ModelList::TypeRole, "GPT"); } + + { + const QString nomicEmbedDesc = tr("
        • For use with LocalDocs feature
        • " + "
        • Used for retrieval augmented generation (RAG)
        • " + "
        • Requires personal Nomic API key.
        • " + "
        • WARNING: Will send your localdocs to Nomic Atlas!
        • " + "
        • You can apply for an API key with Nomic Atlas.
        • "); + const QString modelName = "Nomic Embed"; + const QString id = modelName; + const QString modelFilename = "nomic-embed-text-v1.txt"; + if (contains(modelFilename)) + changeId(modelFilename, id); + if (!contains(id)) + addModel(id); + updateData(id, ModelList::NameRole, modelName); + updateData(id, ModelList::FilenameRole, modelFilename); + updateData(id, ModelList::FilesizeRole, "minimal"); + updateData(id, ModelList::OnlineRole, true); + updateData(id, ModelList::DisableGUIRole, true); + updateData(id, ModelList::DescriptionRole, + tr("LocalDocs Nomic Atlas Embed
          ") + nomicEmbedDesc); + updateData(id, ModelList::RequiresVersionRole, "2.6.3"); + updateData(id, ModelList::OrderRole, "na"); + updateData(id, ModelList::RamrequiredRole, 0); + updateData(id, ModelList::ParametersRole, "?"); + updateData(id, ModelList::QuantRole, "NA"); + updateData(id, ModelList::TypeRole, "Bert"); + } } void ModelList::updateModelsFromSettings() diff --git a/gpt4all-chat/modellist.h b/gpt4all-chat/modellist.h index 475d6a40bb10..8ffd81639547 100644 --- a/gpt4all-chat/modellist.h +++ b/gpt4all-chat/modellist.h @@ -16,7 +16,7 @@ struct ModelInfo { Q_PROPERTY(bool installed MEMBER installed) Q_PROPERTY(bool isDefault MEMBER isDefault) Q_PROPERTY(bool disableGUI MEMBER disableGUI) - Q_PROPERTY(bool isChatGPT MEMBER isChatGPT) + Q_PROPERTY(bool isOnline MEMBER isOnline) Q_PROPERTY(QString description MEMBER description) Q_PROPERTY(QString requiresVersion MEMBER requiresVersion) Q_PROPERTY(QString deprecatedVersion MEMBER deprecatedVersion) @@ -64,7 +64,7 @@ struct ModelInfo { bool calcHash = false; bool installed = false; bool isDefault = false; - bool isChatGPT = false; + bool isOnline = false; bool disableGUI = false; QString description; QString requiresVersion; @@ -217,7 +217,7 @@ class ModelList : public QAbstractListModel CalcHashRole, InstalledRole, DefaultRole, - ChatGPTRole, + OnlineRole, DisableGUIRole, DescriptionRole, RequiresVersionRole, @@ -261,7 +261,7 @@ class ModelList : public QAbstractListModel roles[CalcHashRole] = "calcHash"; roles[InstalledRole] = "installed"; roles[DefaultRole] = "isDefault"; - roles[ChatGPTRole] = "isChatGPT"; + roles[OnlineRole] = "isOnline"; roles[DisableGUIRole] = "disableGUI"; roles[DescriptionRole] = "description"; roles[RequiresVersionRole] = "requiresVersion"; @@ -359,7 +359,7 @@ private Q_SLOTS: void handleSslErrors(QNetworkReply *reply, const QList &errors); private: - QString modelDirPath(const QString &modelName, bool isChatGPT); + QString modelDirPath(const QString &modelName, bool isOnline); int indexForModel(ModelInfo *model); QVariant dataInternal(const ModelInfo *info, int role) const; static bool lessThan(const ModelInfo* a, const ModelInfo* b); diff --git a/gpt4all-chat/qml/CollectionsDialog.qml b/gpt4all-chat/qml/CollectionsDialog.qml index a23db45e83db..2374fa3de701 100644 --- a/gpt4all-chat/qml/CollectionsDialog.qml +++ b/gpt4all-chat/qml/CollectionsDialog.qml @@ -94,11 +94,13 @@ MyDialog { anchors.right: parent.right anchors.margins: 20 anchors.leftMargin: 40 - visible: model.indexing - value: (model.totalBytesToIndex - model.currentBytesToIndex) / model.totalBytesToIndex + visible: model.indexing || model.currentEmbeddingsToIndex !== model.totalEmbeddingsToIndex || model.error !== "" + value: model.error !== "" ? 0 : model.indexing ? + (model.totalBytesToIndex - model.currentBytesToIndex) / model.totalBytesToIndex : + (model.currentEmbeddingsToIndex / model.totalEmbeddingsToIndex) background: Rectangle { implicitHeight: 45 - color: theme.progressBackground + color: model.error ? theme.textErrorColor : theme.progressBackground radius: 3 } contentItem: Item { @@ -114,16 +116,18 @@ MyDialog { Accessible.role: Accessible.ProgressBar Accessible.name: qsTr("Indexing progressBar") Accessible.description: qsTr("Shows the progress made in the indexing") + ToolTip.text: model.error + ToolTip.visible: hovered && model.error !== "" } Label { id: speedLabel color: theme.textColor - visible: model.indexing + visible: model.indexing || model.currentEmbeddingsToIndex !== model.totalEmbeddingsToIndex anchors.verticalCenter: itemProgressBar.verticalCenter anchors.left: itemProgressBar.left anchors.right: itemProgressBar.right horizontalAlignment: Text.AlignHCenter - text: qsTr("indexing...") + text: model.error !== "" ? qsTr("error...") : (model.indexing ? qsTr("indexing...") : qsTr("embeddings...")) elide: Text.ElideRight font.pixelSize: theme.fontSizeLarge } diff --git a/gpt4all-chat/qml/ModelDownloaderDialog.qml b/gpt4all-chat/qml/ModelDownloaderDialog.qml index 8a79a7cba12f..dac1798b28c1 100644 --- a/gpt4all-chat/qml/ModelDownloaderDialog.qml +++ b/gpt4all-chat/qml/ModelDownloaderDialog.qml @@ -135,10 +135,10 @@ MyDialog { font.pixelSize: theme.fontSizeLarge Layout.topMargin: 20 Layout.leftMargin: 20 - Layout.minimumWidth: openaiKey.width + Layout.minimumWidth: apiKey.width Layout.fillWidth: true Layout.alignment: Qt.AlignTop | Qt.AlignHCenter - visible: !isChatGPT && !installed && !calcHash && downloadError === "" + visible: !isOnline && !installed && !calcHash && downloadError === "" Accessible.description: qsTr("Stop/restart/start the download") onClicked: { if (!isDownloading) { @@ -154,7 +154,7 @@ MyDialog { text: qsTr("Remove") Layout.topMargin: 20 Layout.leftMargin: 20 - Layout.minimumWidth: openaiKey.width + Layout.minimumWidth: apiKey.width Layout.fillWidth: true Layout.alignment: Qt.AlignTop | Qt.AlignHCenter visible: installed || downloadError !== "" @@ -166,23 +166,23 @@ MyDialog { MySettingsButton { id: installButton - visible: !installed && isChatGPT + visible: !installed && isOnline Layout.topMargin: 20 Layout.leftMargin: 20 - Layout.minimumWidth: openaiKey.width + Layout.minimumWidth: apiKey.width Layout.fillWidth: true Layout.alignment: Qt.AlignTop | Qt.AlignHCenter text: qsTr("Install") font.pixelSize: theme.fontSizeLarge onClicked: { - if (openaiKey.text === "") - openaiKey.showError(); + if (apiKey.text === "") + apiKey.showError(); else - Download.installModel(filename, openaiKey.text); + Download.installModel(filename, apiKey.text); } Accessible.role: Accessible.Button Accessible.name: qsTr("Install") - Accessible.description: qsTr("Install chatGPT model") + Accessible.description: qsTr("Install online model") } ColumnLayout { @@ -238,7 +238,7 @@ MyDialog { visible: LLM.systemTotalRAMInGB() < ramrequired Layout.topMargin: 20 Layout.leftMargin: 20 - Layout.maximumWidth: openaiKey.width + Layout.maximumWidth: apiKey.width textFormat: Text.StyledText text: qsTr("WARNING: Not recommended for your hardware.") + qsTr(" Model requires more memory (") + ramrequired @@ -261,7 +261,7 @@ MyDialog { visible: isDownloading && !calcHash Layout.topMargin: 20 Layout.leftMargin: 20 - Layout.minimumWidth: openaiKey.width + Layout.minimumWidth: apiKey.width Layout.fillWidth: true Layout.alignment: Qt.AlignTop | Qt.AlignHCenter spacing: 20 @@ -269,7 +269,7 @@ MyDialog { ProgressBar { id: itemProgressBar Layout.fillWidth: true - width: openaiKey.width + width: apiKey.width value: bytesReceived / bytesTotal background: Rectangle { implicitHeight: 45 @@ -307,7 +307,7 @@ MyDialog { visible: calcHash Layout.topMargin: 20 Layout.leftMargin: 20 - Layout.minimumWidth: openaiKey.width + Layout.minimumWidth: apiKey.width Layout.fillWidth: true Layout.alignment: Qt.AlignTop | Qt.AlignHCenter @@ -331,8 +331,8 @@ MyDialog { } MyTextField { - id: openaiKey - visible: !installed && isChatGPT + id: apiKey + visible: !installed && isOnline Layout.topMargin: 20 Layout.leftMargin: 20 Layout.minimumWidth: 150 @@ -340,19 +340,19 @@ MyDialog { Layout.alignment: Qt.AlignTop | Qt.AlignHCenter wrapMode: Text.WrapAnywhere function showError() { - openaiKey.placeholderTextColor = theme.textErrorColor + apiKey.placeholderTextColor = theme.textErrorColor } onTextChanged: { - openaiKey.placeholderTextColor = theme.mutedTextColor + apiKey.placeholderTextColor = theme.mutedTextColor } - placeholderText: qsTr("enter $OPENAI_API_KEY") + placeholderText: qsTr("enter $API_KEY") Accessible.role: Accessible.EditableText Accessible.name: placeholderText Accessible.description: qsTr("Whether the file hash is being calculated") TextMetrics { id: textMetrics - font: openaiKey.font - text: openaiKey.placeholderText + font: apiKey.font + text: apiKey.placeholderText } } } From cdf0fedae2e6917383507f2c36ca0f29fb2dd98a Mon Sep 17 00:00:00 2001 From: Adam Treat Date: Wed, 31 Jan 2024 22:44:16 -0500 Subject: [PATCH 22/27] Make sure to use the search_query tag for nomic embed. Signed-off-by: Adam Treat --- gpt4all-chat/embllm.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/gpt4all-chat/embllm.cpp b/gpt4all-chat/embllm.cpp index 7a50fa01f0ae..2bfb226d2ca0 100644 --- a/gpt4all-chat/embllm.cpp +++ b/gpt4all-chat/embllm.cpp @@ -113,6 +113,7 @@ void EmbeddingLLMWorker::requestSyncEmbedding(const QString &text) QJsonArray texts; texts.append(text); root.insert("texts", texts); + root.insert("task_type", "search_query"); QJsonDocument doc(root); From a5275ea9e7e856eef5814b09c527f682486ec25d Mon Sep 17 00:00:00 2001 From: Adam Treat Date: Wed, 31 Jan 2024 23:25:48 -0500 Subject: [PATCH 23/27] Bump the version and release notes for v2.6.2. Signed-off-by: Adam Treat --- gpt4all-chat/CMakeLists.txt | 2 +- gpt4all-chat/metadata/release.json | 21 +++++++++++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/gpt4all-chat/CMakeLists.txt b/gpt4all-chat/CMakeLists.txt index 7eecf36cc844..301f6f3c110c 100644 --- a/gpt4all-chat/CMakeLists.txt +++ b/gpt4all-chat/CMakeLists.txt @@ -18,7 +18,7 @@ endif() set(APP_VERSION_MAJOR 2) set(APP_VERSION_MINOR 6) -set(APP_VERSION_PATCH 2) +set(APP_VERSION_PATCH 3) set(APP_VERSION "${APP_VERSION_MAJOR}.${APP_VERSION_MINOR}.${APP_VERSION_PATCH}") # Include the binary directory for the generated header file diff --git a/gpt4all-chat/metadata/release.json b/gpt4all-chat/metadata/release.json index a4a62e909550..7c1a5bf40ffe 100644 --- a/gpt4all-chat/metadata/release.json +++ b/gpt4all-chat/metadata/release.json @@ -636,6 +636,27 @@ * Jared Van Bortel (Nomic AI) * Adam Treat (Nomic AI) * Community (beta testers, bug reporters, bindings authors) +" + }, + { + "version": "2.6.2", + "notes": +" +* Update to latest llama.cpp +* Update to newly merged vulkan backend +* Partial GPU offloading support +* New localdocs speed increases and features +* New GUI settings option for configuring how many layers to put on GPU +* New lightmode theme, darkmode theme and legacy theme +* Lots of UI updates and enhancements +* Scores of bugfixes for stability and usability +", + "contributors": +" +* Jared Van Bortel (Nomic AI) +* Adam Treat (Nomic AI) +* Karthik Nair +* Community (beta testers, bug reporters, bindings authors) " } ] From ffed2ff823c6f7b9a43f97e0ffb000181a8cc7cf Mon Sep 17 00:00:00 2001 From: Adam Treat Date: Thu, 1 Feb 2024 08:29:36 -0500 Subject: [PATCH 24/27] Fix for progress bar color on legacy theme. Signed-off-by: Adam Treat --- gpt4all-chat/qml/Theme.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gpt4all-chat/qml/Theme.qml b/gpt4all-chat/qml/Theme.qml index 44b006b4503b..042d4ad39894 100644 --- a/gpt4all-chat/qml/Theme.qml +++ b/gpt4all-chat/qml/Theme.qml @@ -203,7 +203,7 @@ QtObject { property color progressForeground: { switch (MySettings.chatTheme) { case "LegacyDark": - return accentColor; + return purple400; case "Dark": return accentColor; default: From 111e152a5d804569f7891317e7b5256995d77c2c Mon Sep 17 00:00:00 2001 From: Adam Treat Date: Thu, 1 Feb 2024 15:39:28 -0500 Subject: [PATCH 25/27] Fix the sizing for model download. Signed-off-by: Adam Treat --- gpt4all-chat/qml/ModelDownloaderDialog.qml | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/gpt4all-chat/qml/ModelDownloaderDialog.qml b/gpt4all-chat/qml/ModelDownloaderDialog.qml index dac1798b28c1..6bf2b2da53db 100644 --- a/gpt4all-chat/qml/ModelDownloaderDialog.qml +++ b/gpt4all-chat/qml/ModelDownloaderDialog.qml @@ -135,7 +135,7 @@ MyDialog { font.pixelSize: theme.fontSizeLarge Layout.topMargin: 20 Layout.leftMargin: 20 - Layout.minimumWidth: apiKey.width + Layout.minimumWidth: 200 Layout.fillWidth: true Layout.alignment: Qt.AlignTop | Qt.AlignHCenter visible: !isOnline && !installed && !calcHash && downloadError === "" @@ -154,7 +154,7 @@ MyDialog { text: qsTr("Remove") Layout.topMargin: 20 Layout.leftMargin: 20 - Layout.minimumWidth: apiKey.width + Layout.minimumWidth: 200 Layout.fillWidth: true Layout.alignment: Qt.AlignTop | Qt.AlignHCenter visible: installed || downloadError !== "" @@ -169,7 +169,7 @@ MyDialog { visible: !installed && isOnline Layout.topMargin: 20 Layout.leftMargin: 20 - Layout.minimumWidth: apiKey.width + Layout.minimumWidth: 200 Layout.fillWidth: true Layout.alignment: Qt.AlignTop | Qt.AlignHCenter text: qsTr("Install") @@ -238,7 +238,7 @@ MyDialog { visible: LLM.systemTotalRAMInGB() < ramrequired Layout.topMargin: 20 Layout.leftMargin: 20 - Layout.maximumWidth: apiKey.width + Layout.maximumWidth: 300 textFormat: Text.StyledText text: qsTr("WARNING: Not recommended for your hardware.") + qsTr(" Model requires more memory (") + ramrequired @@ -261,7 +261,7 @@ MyDialog { visible: isDownloading && !calcHash Layout.topMargin: 20 Layout.leftMargin: 20 - Layout.minimumWidth: apiKey.width + Layout.minimumWidth: 200 Layout.fillWidth: true Layout.alignment: Qt.AlignTop | Qt.AlignHCenter spacing: 20 @@ -269,7 +269,7 @@ MyDialog { ProgressBar { id: itemProgressBar Layout.fillWidth: true - width: apiKey.width + width: 200 value: bytesReceived / bytesTotal background: Rectangle { implicitHeight: 45 @@ -307,14 +307,16 @@ MyDialog { visible: calcHash Layout.topMargin: 20 Layout.leftMargin: 20 - Layout.minimumWidth: apiKey.width + Layout.minimumWidth: 200 + Layout.maximumWidth: 200 Layout.fillWidth: true Layout.alignment: Qt.AlignTop | Qt.AlignHCenter + clip: true Label { id: calcHashLabel color: theme.textColor - text: qsTr("Calculating MD5...") + text: qsTr("Calculating...") font.pixelSize: theme.fontSizeLarge Accessible.role: Accessible.Paragraph Accessible.name: text @@ -335,8 +337,7 @@ MyDialog { visible: !installed && isOnline Layout.topMargin: 20 Layout.leftMargin: 20 - Layout.minimumWidth: 150 - Layout.maximumWidth: textMetrics.width + 25 + Layout.minimumWidth: 200 Layout.alignment: Qt.AlignTop | Qt.AlignHCenter wrapMode: Text.WrapAnywhere function showError() { From e1eac00ee067a9fd336e89a3191d89fd01daa934 Mon Sep 17 00:00:00 2001 From: Adam Treat Date: Thu, 1 Feb 2024 15:43:34 -0500 Subject: [PATCH 26/27] Fix the download and settings dialog to take more real estate if available on large monitors. Signed-off-by: Adam Treat --- gpt4all-chat/main.qml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gpt4all-chat/main.qml b/gpt4all-chat/main.qml index ea79b1e0db0b..3aafa1a4adf5 100644 --- a/gpt4all-chat/main.qml +++ b/gpt4all-chat/main.qml @@ -410,7 +410,7 @@ Window { SettingsDialog { id: settingsDialog anchors.centerIn: parent - width: Math.min(1280, window.width - (window.width * .1)) + width: Math.min(1920, window.width - (window.width * .1)) height: window.height - (window.height * .1) onDownloadClicked: { downloadNewModels.showEmbeddingModels = true @@ -736,7 +736,7 @@ Window { ModelDownloaderDialog { id: downloadNewModels anchors.centerIn: parent - width: Math.min(1280, window.width - (window.width * .1)) + width: Math.min(1920, window.width - (window.width * .1)) height: window.height - (window.height * .1) Item { Accessible.role: Accessible.Dialog From 10e3f7bbf5c4fdfb432fb5653781edadb129f120 Mon Sep 17 00:00:00 2001 From: Jared Van Bortel Date: Thu, 1 Feb 2024 15:45:45 -0500 Subject: [PATCH 27/27] Fix VRAM leak when model loading fails (#1901) Signed-off-by: Jared Van Bortel --- gpt4all-backend/llama.cpp-mainline | 2 +- gpt4all-backend/llamamodel.cpp | 16 ++++++++++++++-- gpt4all-chat/chatllm.cpp | 1 + 3 files changed, 16 insertions(+), 3 deletions(-) diff --git a/gpt4all-backend/llama.cpp-mainline b/gpt4all-backend/llama.cpp-mainline index 47aec1bcc09e..cd1b5a104b9d 160000 --- a/gpt4all-backend/llama.cpp-mainline +++ b/gpt4all-backend/llama.cpp-mainline @@ -1 +1 @@ -Subproject commit 47aec1bcc09e090f0b8f196dc0a4e43b89507e4a +Subproject commit cd1b5a104b9d3e211a50b9f6c261aced3bf09834 diff --git a/gpt4all-backend/llamamodel.cpp b/gpt4all-backend/llamamodel.cpp index 1fab51381ad5..0767accbde0e 100644 --- a/gpt4all-backend/llamamodel.cpp +++ b/gpt4all-backend/llamamodel.cpp @@ -150,7 +150,15 @@ size_t LLamaModel::requiredMem(const std::string &modelPath, int n_ctx, int ngl) bool LLamaModel::loadModel(const std::string &modelPath, int n_ctx, int ngl) { - gpt_params params; + // clean up after previous loadModel() + if (d_ptr->model) { + llama_free_model(d_ptr->model); + d_ptr->model = nullptr; + } + if (d_ptr->ctx) { + llama_free(d_ptr->ctx); + d_ptr->ctx = nullptr; + } if (n_ctx < 8) { std::cerr << "warning: minimum context size is 8, using minimum size.\n"; @@ -159,6 +167,8 @@ bool LLamaModel::loadModel(const std::string &modelPath, int n_ctx, int ngl) // -- load the model -- + gpt_params params; + d_ptr->model_params = llama_model_default_params(); d_ptr->model_params.use_mmap = params.use_mmap; @@ -215,8 +225,10 @@ bool LLamaModel::loadModel(const std::string &modelPath, int n_ctx, int ngl) d_ptr->ctx = llama_new_context_with_model(d_ptr->model, d_ptr->ctx_params); if (!d_ptr->ctx) { - d_ptr->device = -1; std::cerr << "LLAMA ERROR: failed to init context for model " << modelPath << std::endl; + llama_free_model(d_ptr->model); + d_ptr->model = nullptr; + d_ptr->device = -1; return false; } diff --git a/gpt4all-chat/chatllm.cpp b/gpt4all-chat/chatllm.cpp index 11cc25592b1d..bb399cbb17dc 100644 --- a/gpt4all-chat/chatllm.cpp +++ b/gpt4all-chat/chatllm.cpp @@ -228,6 +228,7 @@ bool ChatLLM::loadModel(const ModelInfo &modelInfo) LLModelStore::globalInstance()->releaseModel(m_llModelInfo); // release back into the store m_llModelInfo = LLModelInfo(); emit modelLoadingError(QString("Previous attempt to load model resulted in crash for `%1` most likely due to insufficient memory. You should either remove this model or decrease your system RAM usage by closing other applications.").arg(modelInfo.filename())); + return false; } if (fileInfo.exists()) {