mirror of
https://github.com/nomic-ai/gpt4all.git
synced 2026-07-17 10:58:08 +00:00
Compare commits
1 Commits
v3.7.0
...
lazy-load-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7a1559e3df |
@@ -81,10 +81,9 @@ jobs:
|
||||
-DGGML_METAL_MACOSX_VERSION_MIN=12.6 \
|
||||
-DMACDEPLOYQT=~/Qt/6.5.1/macos/bin/macdeployqt \
|
||||
-DGPT4ALL_OFFLINE_INSTALLER=ON \
|
||||
-DGPT4ALL_SIGN_INSTALL=ON \
|
||||
-DGPT4ALL_GEN_CPACK_CONFIG=ON
|
||||
~/Qt/Tools/CMake/CMake.app/Contents/bin/cmake --build . --target package
|
||||
~/Qt/Tools/CMake/CMake.app/Contents/bin/cmake . -DGPT4ALL_GEN_CPACK_CONFIG=OFF
|
||||
-DGPT4ALL_SIGN_INSTALL=ON
|
||||
~/Qt/Tools/CMake/CMake.app/Contents/bin/cmake --build . --target all
|
||||
~/Qt/Tools/CMake/CMake.app/Contents/bin/cmake --build . --target install
|
||||
~/Qt/Tools/CMake/CMake.app/Contents/bin/cmake --build . --target package
|
||||
ccache -s
|
||||
mkdir upload
|
||||
@@ -220,10 +219,9 @@ jobs:
|
||||
-DGGML_METAL_MACOSX_VERSION_MIN=12.6 \
|
||||
-DMACDEPLOYQT=~/Qt/6.5.1/macos/bin/macdeployqt \
|
||||
-DGPT4ALL_OFFLINE_INSTALLER=OFF \
|
||||
-DGPT4ALL_SIGN_INSTALL=ON \
|
||||
-DGPT4ALL_GEN_CPACK_CONFIG=ON
|
||||
~/Qt/Tools/CMake/CMake.app/Contents/bin/cmake --build . --target package
|
||||
~/Qt/Tools/CMake/CMake.app/Contents/bin/cmake . -DGPT4ALL_GEN_CPACK_CONFIG=OFF
|
||||
-DGPT4ALL_SIGN_INSTALL=ON
|
||||
~/Qt/Tools/CMake/CMake.app/Contents/bin/cmake --build . --target all
|
||||
~/Qt/Tools/CMake/CMake.app/Contents/bin/cmake --build . --target install
|
||||
~/Qt/Tools/CMake/CMake.app/Contents/bin/cmake --build . --target package
|
||||
ccache -s
|
||||
mkdir upload
|
||||
@@ -464,8 +462,7 @@ jobs:
|
||||
|
||||
build-offline-chat-installer-windows:
|
||||
machine:
|
||||
# we use 2024.04.01 because nvcc complains about the MSVC ver if we use anything newer
|
||||
image: windows-server-2022-gui:2024.04.1
|
||||
image: windows-server-2022-gui:current
|
||||
resource_class: windows.large
|
||||
shell: powershell.exe -ExecutionPolicy Bypass
|
||||
steps:
|
||||
@@ -556,7 +553,7 @@ jobs:
|
||||
|
||||
sign-offline-chat-installer-windows:
|
||||
machine:
|
||||
image: windows-server-2022-gui:2024.04.1
|
||||
image: windows-server-2022-gui:current
|
||||
resource_class: windows.large
|
||||
shell: powershell.exe -ExecutionPolicy Bypass
|
||||
steps:
|
||||
@@ -589,7 +586,7 @@ jobs:
|
||||
|
||||
build-online-chat-installer-windows:
|
||||
machine:
|
||||
image: windows-server-2022-gui:2024.04.1
|
||||
image: windows-server-2022-gui:current
|
||||
resource_class: windows.large
|
||||
shell: powershell.exe -ExecutionPolicy Bypass
|
||||
steps:
|
||||
@@ -687,7 +684,7 @@ jobs:
|
||||
|
||||
sign-online-chat-installer-windows:
|
||||
machine:
|
||||
image: windows-server-2022-gui:2024.04.1
|
||||
image: windows-server-2022-gui:current
|
||||
resource_class: windows.large
|
||||
shell: powershell.exe -ExecutionPolicy Bypass
|
||||
steps:
|
||||
@@ -723,261 +720,6 @@ jobs:
|
||||
- store_artifacts:
|
||||
path: build/upload
|
||||
|
||||
build-offline-chat-installer-windows-arm:
|
||||
machine:
|
||||
# we use 2024.04.01 because nvcc complains about the MSVC ver if we use anything newer
|
||||
image: windows-server-2022-gui:2024.04.1
|
||||
resource_class: windows.large
|
||||
shell: powershell.exe -ExecutionPolicy Bypass
|
||||
steps:
|
||||
- checkout
|
||||
- run:
|
||||
name: Update Submodules
|
||||
command: |
|
||||
git submodule sync
|
||||
git submodule update --init --recursive
|
||||
- restore_cache:
|
||||
keys:
|
||||
- ccache-gpt4all-win-aarch64-
|
||||
- run:
|
||||
name: Install dependencies
|
||||
command: choco install -y ccache wget
|
||||
- run:
|
||||
name: Installing Qt
|
||||
command: |
|
||||
wget.exe "https://gpt4all.io/ci/qt-unified-windows-x64-4.6.0-online.exe"
|
||||
# note: need x86_64 toolset for windeployqt.exe, which isn't provided with the ARM64 cross toolset
|
||||
& .\qt-unified-windows-x64-4.6.0-online.exe --no-force-installations --no-default-installations `
|
||||
--no-size-checking --default-answer --accept-licenses --confirm-command --accept-obligations `
|
||||
--email ${Env:QT_EMAIL} --password ${Env:QT_PASSWORD} install `
|
||||
qt.tools.cmake qt.tools.ifw.48 qt.tools.ninja qt.qt6.651.win64_msvc2019_64 `
|
||||
qt.qt6.651.win64_msvc2019_arm64 qt.qt6.651.qt5compat qt.qt6.651.debug_info qt.qt6.651.addons.qthttpserver
|
||||
- run:
|
||||
name: "Install Dotnet 8"
|
||||
command: |
|
||||
mkdir dotnet
|
||||
cd dotnet
|
||||
$dotnet_url="https://download.visualstudio.microsoft.com/download/pr/5af098e1-e433-4fda-84af-3f54fd27c108/6bd1c6e48e64e64871957289023ca590/dotnet-sdk-8.0.302-win-x64.zip"
|
||||
wget.exe "$dotnet_url"
|
||||
Expand-Archive -LiteralPath .\dotnet-sdk-8.0.302-win-x64.zip
|
||||
$Env:DOTNET_ROOT="$($(Get-Location).Path)\dotnet-sdk-8.0.302-win-x64"
|
||||
$Env:PATH="$Env:DOTNET_ROOT;$Env:PATH"
|
||||
$Env:DOTNET_SKIP_FIRST_TIME_EXPERIENCE=$true
|
||||
dotnet tool install --global AzureSignTool
|
||||
- run:
|
||||
name: Build
|
||||
no_output_timeout: 30m
|
||||
command: |
|
||||
$vsInstallPath = & "C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe" -property installationpath
|
||||
Import-Module "${vsInstallPath}\Common7\Tools\Microsoft.VisualStudio.DevShell.dll"
|
||||
Enter-VsDevShell -VsInstallPath "$vsInstallPath" -SkipAutomaticLocation -Arch arm64 -HostArch amd64 -DevCmdArguments '-no_logo'
|
||||
|
||||
$Env:PATH = "${Env:PATH};C:\Qt\Tools\QtInstallerFramework\4.8\bin"
|
||||
$Env:DOTNET_ROOT="$($(Get-Location).Path)\dotnet\dotnet-sdk-8.0.302-win-x64"
|
||||
$Env:PATH="$Env:DOTNET_ROOT;$Env:PATH"
|
||||
ccache -o "cache_dir=${pwd}\..\.ccache" -o max_size=500M -p -z
|
||||
mkdir build
|
||||
cd build
|
||||
& "C:\Qt\Tools\CMake_64\bin\cmake.exe" `
|
||||
-S ..\gpt4all-chat -B . -G Ninja `
|
||||
-DCMAKE_BUILD_TYPE=Release `
|
||||
"-DCMAKE_PREFIX_PATH:PATH=C:\Qt\6.5.1\msvc2019_arm64" `
|
||||
"-DCMAKE_MAKE_PROGRAM:FILEPATH=C:\Qt\Tools\Ninja\ninja.exe" `
|
||||
"-DCMAKE_TOOLCHAIN_FILE=C:\Qt\6.5.1\msvc2019_arm64\lib\cmake\Qt6\qt.toolchain.cmake" `
|
||||
-DCMAKE_C_COMPILER_LAUNCHER=ccache `
|
||||
-DCMAKE_CXX_COMPILER_LAUNCHER=ccache `
|
||||
-DLLMODEL_CUDA=OFF `
|
||||
-DLLMODEL_KOMPUTE=OFF `
|
||||
"-DWINDEPLOYQT=C:\Qt\6.5.1\msvc2019_64\bin\windeployqt.exe;--qtpaths;C:\Qt\6.5.1\msvc2019_arm64\bin\qtpaths.bat" `
|
||||
-DGPT4ALL_TEST=OFF `
|
||||
-DGPT4ALL_OFFLINE_INSTALLER=ON
|
||||
& "C:\Qt\Tools\Ninja\ninja.exe"
|
||||
& "C:\Qt\Tools\Ninja\ninja.exe" install
|
||||
& "C:\Qt\Tools\Ninja\ninja.exe" package
|
||||
ccache -s
|
||||
mkdir upload
|
||||
copy gpt4all-installer-win64-arm.exe upload
|
||||
- store_artifacts:
|
||||
path: build/upload
|
||||
# add workspace so signing jobs can connect & obtain dmg
|
||||
- save_cache:
|
||||
key: ccache-gpt4all-win-aarch64-{{ epoch }}
|
||||
when: always
|
||||
paths:
|
||||
- ..\.ccache
|
||||
- persist_to_workspace:
|
||||
root: build
|
||||
# specify path to only include components we want to persist
|
||||
# accross builds
|
||||
paths:
|
||||
- upload
|
||||
|
||||
sign-offline-chat-installer-windows-arm:
|
||||
machine:
|
||||
image: windows-server-2022-gui:2024.04.1
|
||||
resource_class: windows.large
|
||||
shell: powershell.exe -ExecutionPolicy Bypass
|
||||
steps:
|
||||
- checkout
|
||||
- attach_workspace:
|
||||
at: build
|
||||
- run:
|
||||
name: Install dependencies
|
||||
command: choco install -y wget
|
||||
- run:
|
||||
name: "Install Dotnet 8 && Azure Sign Tool"
|
||||
command: |
|
||||
mkdir dotnet
|
||||
cd dotnet
|
||||
$dotnet_url="https://download.visualstudio.microsoft.com/download/pr/5af098e1-e433-4fda-84af-3f54fd27c108/6bd1c6e48e64e64871957289023ca590/dotnet-sdk-8.0.302-win-x64.zip"
|
||||
wget.exe "$dotnet_url"
|
||||
Expand-Archive -LiteralPath .\dotnet-sdk-8.0.302-win-x64.zip
|
||||
$Env:DOTNET_ROOT="$($(Get-Location).Path)\dotnet-sdk-8.0.302-win-x64"
|
||||
$Env:PATH="$Env:DOTNET_ROOT;$Env:PATH"
|
||||
$Env:DOTNET_SKIP_FIRST_TIME_EXPERIENCE=$true
|
||||
dotnet tool install --global AzureSignTool
|
||||
- run:
|
||||
name: "Sign Windows Installer With AST"
|
||||
command: |
|
||||
$Env:DOTNET_ROOT="$($(Get-Location).Path)\dotnet\dotnet-sdk-8.0.302-win-x64"
|
||||
$Env:PATH="$Env:DOTNET_ROOT;$Env:PATH"
|
||||
AzureSignTool.exe sign -du "https://gpt4all.io/index.html" -kvu https://gpt4all.vault.azure.net -kvi "$Env:AZSignGUID" -kvs "$Env:AZSignPWD" -kvc "$Env:AZSignCertName" -kvt "$Env:AZSignTID" -tr http://timestamp.digicert.com -v "$($(Get-Location).Path)\build\upload\gpt4all-installer-win64-arm.exe"
|
||||
- store_artifacts:
|
||||
path: build/upload
|
||||
|
||||
build-online-chat-installer-windows-arm:
|
||||
machine:
|
||||
image: windows-server-2022-gui:2024.04.1
|
||||
resource_class: windows.large
|
||||
shell: powershell.exe -ExecutionPolicy Bypass
|
||||
steps:
|
||||
- checkout
|
||||
- run:
|
||||
name: Update Submodules
|
||||
command: |
|
||||
git submodule sync
|
||||
git submodule update --init --recursive
|
||||
- restore_cache:
|
||||
keys:
|
||||
- ccache-gpt4all-win-aarch64-
|
||||
- run:
|
||||
name: Install dependencies
|
||||
command: choco install -y ccache wget
|
||||
- run:
|
||||
name: Installing Qt
|
||||
command: |
|
||||
wget.exe "https://gpt4all.io/ci/qt-unified-windows-x64-4.6.0-online.exe"
|
||||
# note: need x86_64 toolset for windeployqt.exe, which isn't provided with the ARM64 cross toolset
|
||||
& .\qt-unified-windows-x64-4.6.0-online.exe --no-force-installations --no-default-installations `
|
||||
--no-size-checking --default-answer --accept-licenses --confirm-command --accept-obligations `
|
||||
--email ${Env:QT_EMAIL} --password ${Env:QT_PASSWORD} install `
|
||||
qt.tools.cmake qt.tools.ifw.48 qt.tools.ninja qt.qt6.651.win64_msvc2019_64 `
|
||||
qt.qt6.651.win64_msvc2019_arm64 qt.qt6.651.qt5compat qt.qt6.651.debug_info qt.qt6.651.addons.qthttpserver
|
||||
- run:
|
||||
name: "Install Dotnet 8"
|
||||
command: |
|
||||
mkdir dotnet
|
||||
cd dotnet
|
||||
$dotnet_url="https://download.visualstudio.microsoft.com/download/pr/5af098e1-e433-4fda-84af-3f54fd27c108/6bd1c6e48e64e64871957289023ca590/dotnet-sdk-8.0.302-win-x64.zip"
|
||||
wget.exe "$dotnet_url"
|
||||
Expand-Archive -LiteralPath .\dotnet-sdk-8.0.302-win-x64.zip
|
||||
$Env:DOTNET_ROOT="$($(Get-Location).Path)\dotnet-sdk-8.0.302-win-x64"
|
||||
$Env:PATH="$Env:DOTNET_ROOT;$Env:PATH"
|
||||
- run:
|
||||
name: "Setup Azure SignTool"
|
||||
command: |
|
||||
$Env:DOTNET_ROOT="$($(Get-Location).Path)\dotnet\dotnet-sdk-8.0.302-win-x64"
|
||||
$Env:PATH="$Env:DOTNET_ROOT;$Env:PATH"
|
||||
$Env:DOTNET_SKIP_FIRST_TIME_EXPERIENCE=$true
|
||||
dotnet tool install --global AzureSignTool
|
||||
- run:
|
||||
name: Build
|
||||
no_output_timeout: 30m
|
||||
command: |
|
||||
$vsInstallPath = & "C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe" -property installationpath
|
||||
Import-Module "${vsInstallPath}\Common7\Tools\Microsoft.VisualStudio.DevShell.dll"
|
||||
Enter-VsDevShell -VsInstallPath "$vsInstallPath" -SkipAutomaticLocation -Arch arm64 -HostArch amd64 -DevCmdArguments '-no_logo'
|
||||
|
||||
$Env:PATH = "${Env:PATH};C:\Qt\Tools\QtInstallerFramework\4.8\bin"
|
||||
$Env:DOTNET_ROOT="$($(Get-Location).Path)\dotnet\dotnet-sdk-8.0.302-win-x64"
|
||||
$Env:PATH="$Env:DOTNET_ROOT;$Env:PATH"
|
||||
ccache -o "cache_dir=${pwd}\..\.ccache" -o max_size=500M -p -z
|
||||
mkdir build
|
||||
cd build
|
||||
& "C:\Qt\Tools\CMake_64\bin\cmake.exe" `
|
||||
-S ..\gpt4all-chat -B . -G Ninja `
|
||||
-DCMAKE_BUILD_TYPE=Release `
|
||||
"-DCMAKE_PREFIX_PATH:PATH=C:\Qt\6.5.1\msvc2019_arm64" `
|
||||
"-DCMAKE_MAKE_PROGRAM:FILEPATH=C:\Qt\Tools\Ninja\ninja.exe" `
|
||||
"-DCMAKE_TOOLCHAIN_FILE=C:\Qt\6.5.1\msvc2019_arm64\lib\cmake\Qt6\qt.toolchain.cmake" `
|
||||
-DCMAKE_C_COMPILER_LAUNCHER=ccache `
|
||||
-DCMAKE_CXX_COMPILER_LAUNCHER=ccache `
|
||||
-DLLMODEL_CUDA=OFF `
|
||||
-DLLMODEL_KOMPUTE=OFF `
|
||||
"-DWINDEPLOYQT=C:\Qt\6.5.1\msvc2019_64\bin\windeployqt.exe;--qtpaths;C:\Qt\6.5.1\msvc2019_arm64\bin\qtpaths.bat" `
|
||||
-DGPT4ALL_TEST=OFF `
|
||||
-DGPT4ALL_OFFLINE_INSTALLER=OFF
|
||||
& "C:\Qt\Tools\Ninja\ninja.exe"
|
||||
& "C:\Qt\Tools\Ninja\ninja.exe" install
|
||||
& "C:\Qt\Tools\Ninja\ninja.exe" package
|
||||
ccache -s
|
||||
mkdir upload
|
||||
copy gpt4all-installer-win64-arm.exe upload
|
||||
Set-Location -Path "_CPack_Packages/win64/IFW/gpt4all-installer-win64-arm"
|
||||
Compress-Archive -Path 'repository' -DestinationPath '..\..\..\..\upload\repository.zip'
|
||||
- store_artifacts:
|
||||
path: build/upload
|
||||
- save_cache:
|
||||
key: ccache-gpt4all-win-aarch64-{{ epoch }}
|
||||
when: always
|
||||
paths:
|
||||
- ..\.ccache
|
||||
# add workspace so signing jobs can connect & obtain dmg
|
||||
- persist_to_workspace:
|
||||
root: build
|
||||
# specify path to only include components we want to persist
|
||||
# accross builds
|
||||
paths:
|
||||
- upload
|
||||
|
||||
sign-online-chat-installer-windows-arm:
|
||||
machine:
|
||||
image: windows-server-2022-gui:2024.04.1
|
||||
resource_class: windows.large
|
||||
shell: powershell.exe -ExecutionPolicy Bypass
|
||||
steps:
|
||||
- checkout
|
||||
- attach_workspace:
|
||||
at: build
|
||||
- run:
|
||||
name: Install dependencies
|
||||
command: choco install -y wget
|
||||
- run:
|
||||
name: "Install Dotnet 8"
|
||||
command: |
|
||||
mkdir dotnet
|
||||
cd dotnet
|
||||
$dotnet_url="https://download.visualstudio.microsoft.com/download/pr/5af098e1-e433-4fda-84af-3f54fd27c108/6bd1c6e48e64e64871957289023ca590/dotnet-sdk-8.0.302-win-x64.zip"
|
||||
wget.exe "$dotnet_url"
|
||||
Expand-Archive -LiteralPath .\dotnet-sdk-8.0.302-win-x64.zip
|
||||
$Env:DOTNET_ROOT="$($(Get-Location).Path)\dotnet-sdk-8.0.302-win-x64"
|
||||
$Env:PATH="$Env:DOTNET_ROOT;$Env:PATH"
|
||||
- run:
|
||||
name: "Setup Azure SignTool"
|
||||
command: |
|
||||
$Env:DOTNET_ROOT="$($(Get-Location).Path)\dotnet\dotnet-sdk-8.0.302-win-x64"
|
||||
$Env:PATH="$Env:DOTNET_ROOT;$Env:PATH"
|
||||
$Env:DOTNET_SKIP_FIRST_TIME_EXPERIENCE=$true
|
||||
dotnet tool install --global AzureSignTool
|
||||
- run:
|
||||
name: "Sign Windows Installer With AST"
|
||||
command: |
|
||||
$Env:DOTNET_ROOT="$($(Get-Location).Path)\dotnet\dotnet-sdk-8.0.302-win-x64"
|
||||
$Env:PATH="$Env:DOTNET_ROOT;$Env:PATH"
|
||||
AzureSignTool.exe sign -du "https://gpt4all.io/index.html" -kvu https://gpt4all.vault.azure.net -kvi "$Env:AZSignGUID" -kvs "$Env:AZSignPWD" -kvc "$Env:AZSignCertName" -kvt "$Env:AZSignTID" -tr http://timestamp.digicert.com -v "$($(Get-Location).Path)/build/upload/gpt4all-installer-win64-arm.exe"
|
||||
- store_artifacts:
|
||||
path: build/upload
|
||||
|
||||
build-gpt4all-chat-linux:
|
||||
machine:
|
||||
image: ubuntu-2204:current
|
||||
@@ -1042,7 +784,7 @@ jobs:
|
||||
|
||||
build-gpt4all-chat-windows:
|
||||
machine:
|
||||
image: windows-server-2022-gui:2024.04.1
|
||||
image: windows-server-2022-gui:current
|
||||
resource_class: windows.large
|
||||
shell: powershell.exe -ExecutionPolicy Bypass
|
||||
steps:
|
||||
@@ -1305,7 +1047,7 @@ jobs:
|
||||
|
||||
build-py-windows:
|
||||
machine:
|
||||
image: windows-server-2022-gui:2024.04.1
|
||||
image: windows-server-2022-gui:current
|
||||
resource_class: windows.large
|
||||
shell: powershell.exe -ExecutionPolicy Bypass
|
||||
steps:
|
||||
@@ -1502,7 +1244,7 @@ jobs:
|
||||
|
||||
build-bindings-backend-windows:
|
||||
machine:
|
||||
image: windows-server-2022-gui:2024.04.1
|
||||
image: windows-server-2022-gui:current
|
||||
resource_class: windows.large
|
||||
shell: powershell.exe -ExecutionPolicy Bypass
|
||||
steps:
|
||||
@@ -1752,35 +1494,21 @@ workflows:
|
||||
- hold:
|
||||
type: approval
|
||||
- build-offline-chat-installer-macos:
|
||||
context: gpt4all
|
||||
requires:
|
||||
- hold
|
||||
- sign-offline-chat-installer-macos:
|
||||
context: gpt4all
|
||||
requires:
|
||||
- build-offline-chat-installer-macos
|
||||
- notarize-offline-chat-installer-macos:
|
||||
context: gpt4all
|
||||
requires:
|
||||
- sign-offline-chat-installer-macos
|
||||
- build-offline-chat-installer-windows:
|
||||
context: gpt4all
|
||||
requires:
|
||||
- hold
|
||||
- sign-offline-chat-installer-windows:
|
||||
context: gpt4all
|
||||
requires:
|
||||
- build-offline-chat-installer-windows
|
||||
- build-offline-chat-installer-windows-arm:
|
||||
context: gpt4all
|
||||
requires:
|
||||
- hold
|
||||
- sign-offline-chat-installer-windows-arm:
|
||||
context: gpt4all
|
||||
requires:
|
||||
- build-offline-chat-installer-windows-arm
|
||||
- build-offline-chat-installer-linux:
|
||||
context: gpt4all
|
||||
requires:
|
||||
- hold
|
||||
build-chat-online-installers:
|
||||
@@ -1794,42 +1522,26 @@ workflows:
|
||||
type: approval
|
||||
- build-online-chat-installer-macos:
|
||||
<<: *main_only
|
||||
context: gpt4all
|
||||
requires:
|
||||
- hold
|
||||
- sign-online-chat-installer-macos:
|
||||
<<: *main_only
|
||||
context: gpt4all
|
||||
requires:
|
||||
- build-online-chat-installer-macos
|
||||
- notarize-online-chat-installer-macos:
|
||||
<<: *main_only
|
||||
context: gpt4all
|
||||
requires:
|
||||
- sign-online-chat-installer-macos
|
||||
- build-online-chat-installer-windows:
|
||||
<<: *main_only
|
||||
context: gpt4all
|
||||
requires:
|
||||
- hold
|
||||
- sign-online-chat-installer-windows:
|
||||
<<: *main_only
|
||||
context: gpt4all
|
||||
requires:
|
||||
- build-online-chat-installer-windows
|
||||
- build-online-chat-installer-windows-arm:
|
||||
<<: *main_only
|
||||
context: gpt4all
|
||||
requires:
|
||||
- hold
|
||||
- sign-online-chat-installer-windows-arm:
|
||||
<<: *main_only
|
||||
context: gpt4all
|
||||
requires:
|
||||
- build-online-chat-installer-windows-arm
|
||||
- build-online-chat-installer-linux:
|
||||
<<: *main_only
|
||||
context: gpt4all
|
||||
requires:
|
||||
- hold
|
||||
build-and-test-gpt4all-chat:
|
||||
@@ -1841,15 +1553,12 @@ workflows:
|
||||
- hold:
|
||||
type: approval
|
||||
- build-gpt4all-chat-linux:
|
||||
context: gpt4all
|
||||
requires:
|
||||
- hold
|
||||
- build-gpt4all-chat-windows:
|
||||
context: gpt4all
|
||||
requires:
|
||||
- hold
|
||||
- build-gpt4all-chat-macos:
|
||||
context: gpt4all
|
||||
requires:
|
||||
- hold
|
||||
deploy-docs:
|
||||
@@ -1862,7 +1571,6 @@ workflows:
|
||||
<<: *main_only
|
||||
- build-py-docs:
|
||||
<<: *main_only
|
||||
context: gpt4all
|
||||
build-python:
|
||||
when:
|
||||
or:
|
||||
@@ -1885,7 +1593,6 @@ workflows:
|
||||
- hold
|
||||
- publish-wheels:
|
||||
<<: *main_only
|
||||
context: gpt4all
|
||||
requires:
|
||||
- pypi-hold
|
||||
- build-py-windows
|
||||
|
||||
@@ -51,6 +51,11 @@ Thiago Ramos ([@thiagojramos](https://github.com/thiagojramos))<br/>
|
||||
E-mail: thiagojramos@outlook.com<br/>
|
||||
- pt\_BR translation
|
||||
|
||||
Victor Emanuel ([@SINAPSA-IC](https://github.com/SINAPSA-IC))<br/>
|
||||
E-mail: contact@sinapsaro.ro<br/>
|
||||
Discord: `@sinapsa_ic_56124_99632`
|
||||
- ro\_RO translation
|
||||
|
||||
不知火 Shiranui ([@supersonictw](https://github.com/supersonictw))<br/>
|
||||
E-mail: supersonic@livemail.tw<br/>
|
||||
Discord: `@supersonictw`
|
||||
|
||||
@@ -23,6 +23,9 @@ https://github.com/nomic-ai/gpt4all/assets/70534565/513a0f15-4964-4109-89e4-4f9a
|
||||
<p align="center">
|
||||
GPT4All is made possible by our compute partner <a href="https://www.paperspace.com/">Paperspace</a>.
|
||||
</p>
|
||||
<p align="center">
|
||||
<a href="https://www.phorm.ai/query?projectId=755eecd3-24ad-49cc-abf4-0ab84caacf63"><img src="https://img.shields.io/badge/Phorm-Ask_AI-%23F2777A.svg" alt="phorm.ai"></a>
|
||||
</p>
|
||||
|
||||
## Download Links
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ function(gpt4all_add_warning_options target)
|
||||
-Wextra-semi
|
||||
-Wformat=2
|
||||
-Wmissing-include-dirs
|
||||
-Wstrict-overflow=2
|
||||
-Wsuggest-override
|
||||
-Wvla
|
||||
# errors
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <expected>
|
||||
#include <filesystem>
|
||||
#include <functional>
|
||||
#include <optional>
|
||||
#include <span>
|
||||
@@ -19,6 +20,7 @@
|
||||
class Dlhandle;
|
||||
|
||||
using namespace std::string_literals;
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
#define LLMODEL_MAX_PROMPT_BATCH 128
|
||||
|
||||
@@ -94,12 +96,13 @@ public:
|
||||
|
||||
class Implementation {
|
||||
public:
|
||||
Implementation(std::string buildBackend, Dlhandle &&dlhandle);
|
||||
Implementation(const Implementation &) = delete;
|
||||
Implementation(Implementation &&);
|
||||
~Implementation();
|
||||
|
||||
std::string_view modelType() const { return m_modelType; }
|
||||
std::string_view buildVariant() const { return m_buildVariant; }
|
||||
const std::string &buildBackend() const { return m_buildBackend; }
|
||||
std::string_view modelType () const { return m_modelType; }
|
||||
|
||||
static LLModel *construct(const std::string &modelPath, const std::string &backend = "auto", int n_ctx = 2048);
|
||||
static std::vector<GPUDevice> availableGPUDevices(size_t memoryRequired = 0);
|
||||
@@ -114,19 +117,17 @@ public:
|
||||
static int cpuSupportsAVX2();
|
||||
|
||||
private:
|
||||
Implementation(Dlhandle &&);
|
||||
|
||||
static const std::vector<Implementation> &implementationList();
|
||||
static const Implementation *implementation(const char *fname, const std::string &buildVariant);
|
||||
static const Implementation *findImplementation(const char *fname, const std::string &buildBackend);
|
||||
static LLModel *constructGlobalLlama(const std::optional<std::string> &backend = std::nullopt);
|
||||
|
||||
char *(*m_getFileArch)(const char *fname);
|
||||
bool (*m_isArchSupported)(const char *arch);
|
||||
LLModel *(*m_construct)();
|
||||
std::string m_buildBackend;
|
||||
Dlhandle *m_dlhandle;
|
||||
|
||||
char *(*m_getFileArch) (const char *fname);
|
||||
bool (*m_isArchSupported)(const char *arch);
|
||||
LLModel *(*m_construct) ();
|
||||
|
||||
std::string_view m_modelType;
|
||||
std::string_view m_buildVariant;
|
||||
Dlhandle *m_dlhandle;
|
||||
};
|
||||
|
||||
struct PromptContext {
|
||||
@@ -141,6 +142,16 @@ public:
|
||||
float contextErase = 0.5f; // percent of context to erase if we exceed the context window
|
||||
};
|
||||
|
||||
private:
|
||||
struct LazyImplementation {
|
||||
std::string buildBackend;
|
||||
fs::path path;
|
||||
std::optional<Implementation> impl = {};
|
||||
|
||||
const Implementation &get();
|
||||
};
|
||||
|
||||
public:
|
||||
explicit LLModel() {}
|
||||
virtual ~LLModel() {}
|
||||
|
||||
@@ -267,6 +278,9 @@ protected:
|
||||
const PromptContext &promptCtx,
|
||||
int32_t nPast);
|
||||
|
||||
private:
|
||||
static std::vector<LazyImplementation> &getImplementations();
|
||||
|
||||
friend class LLMImplementation;
|
||||
};
|
||||
|
||||
|
||||
@@ -1278,21 +1278,12 @@ void LLamaModel::embedInternal(
|
||||
#endif
|
||||
|
||||
extern "C" {
|
||||
DLL_EXPORT bool is_g4a_backend_model_implementation()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
DLL_EXPORT const char *get_model_type()
|
||||
{
|
||||
return modelType_;
|
||||
}
|
||||
|
||||
DLL_EXPORT const char *get_build_variant()
|
||||
{
|
||||
return GGML_BUILD_VARIANT;
|
||||
}
|
||||
|
||||
DLL_EXPORT char *get_file_arch(const char *fname)
|
||||
{
|
||||
char *arch = nullptr;
|
||||
|
||||
@@ -10,9 +10,9 @@
|
||||
#include <iterator>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <regex>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
@@ -32,6 +32,8 @@
|
||||
# include "sysinfo.h" // for getSystemTotalRAMInBytes
|
||||
#endif
|
||||
|
||||
using namespace std::string_literals;
|
||||
using namespace std::string_view_literals;
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
#ifndef __APPLE__
|
||||
@@ -66,29 +68,30 @@ std::string s_implementations_search_path = ".";
|
||||
#define cpu_supports_avx2() !!__builtin_cpu_supports("avx2")
|
||||
#endif
|
||||
|
||||
LLModel::Implementation::Implementation(Dlhandle &&dlhandle_)
|
||||
: m_dlhandle(new Dlhandle(std::move(dlhandle_))) {
|
||||
LLModel::Implementation::Implementation(std::string buildBackend, Dlhandle &&dlhandle)
|
||||
: m_buildBackend(std::move(buildBackend))
|
||||
, m_dlhandle(new Dlhandle(std::move(dlhandle)))
|
||||
{
|
||||
auto get_model_type = m_dlhandle->get<const char *()>("get_model_type");
|
||||
assert(get_model_type);
|
||||
m_modelType = get_model_type();
|
||||
auto get_build_variant = m_dlhandle->get<const char *()>("get_build_variant");
|
||||
assert(get_build_variant);
|
||||
m_buildVariant = get_build_variant();
|
||||
m_getFileArch = m_dlhandle->get<char *(const char *)>("get_file_arch");
|
||||
assert(m_getFileArch);
|
||||
m_isArchSupported = m_dlhandle->get<bool(const char *)>("is_arch_supported");
|
||||
assert(m_isArchSupported);
|
||||
m_construct = m_dlhandle->get<LLModel *()>("construct");
|
||||
assert(m_construct);
|
||||
|
||||
m_modelType = get_model_type();
|
||||
}
|
||||
|
||||
LLModel::Implementation::Implementation(Implementation &&o)
|
||||
: m_getFileArch(o.m_getFileArch)
|
||||
: m_buildBackend(o.m_buildBackend)
|
||||
, m_dlhandle(o.m_dlhandle)
|
||||
, m_getFileArch(o.m_getFileArch)
|
||||
, m_isArchSupported(o.m_isArchSupported)
|
||||
, m_construct(o.m_construct)
|
||||
, m_modelType(o.m_modelType)
|
||||
, m_buildVariant(o.m_buildVariant)
|
||||
, m_dlhandle(o.m_dlhandle) {
|
||||
{
|
||||
o.m_dlhandle = nullptr;
|
||||
}
|
||||
|
||||
@@ -97,11 +100,6 @@ LLModel::Implementation::~Implementation()
|
||||
delete m_dlhandle;
|
||||
}
|
||||
|
||||
static bool isImplementation(const Dlhandle &dl)
|
||||
{
|
||||
return dl.get<bool(uint32_t)>("is_g4a_backend_model_implementation");
|
||||
}
|
||||
|
||||
// Add the CUDA Toolkit to the DLL search path on Windows.
|
||||
// This is necessary for chat.exe to find CUDA when started from Qt Creator.
|
||||
static void addCudaSearchPath()
|
||||
@@ -117,55 +115,43 @@ static void addCudaSearchPath()
|
||||
#endif
|
||||
}
|
||||
|
||||
const std::vector<LLModel::Implementation> &LLModel::Implementation::implementationList()
|
||||
auto LLModel::LazyImplementation::get() -> const Implementation &
|
||||
{
|
||||
if (!impl) impl.emplace(buildBackend, Dlhandle(path));
|
||||
return *impl;
|
||||
}
|
||||
|
||||
auto LLModel::getImplementations() -> std::vector<LazyImplementation> &
|
||||
{
|
||||
// in no particular order
|
||||
static const std::array ALL_BUILD_BACKENDS { "cpu"sv, "metal"sv, "kompute"sv, "vulkan"sv, "cuda"sv };
|
||||
static const std::string_view LIB_EXT(LIB_FILE_EXT);
|
||||
|
||||
if (cpu_supports_avx() == 0) {
|
||||
throw std::runtime_error("CPU does not support AVX");
|
||||
}
|
||||
|
||||
// NOTE: allocated on heap so we leak intentionally on exit so we have a chance to clean up the
|
||||
// individual models without the cleanup of the static list interfering
|
||||
static auto* libs = new std::vector<Implementation>([] () {
|
||||
std::vector<Implementation> fres;
|
||||
static auto* libs = new std::vector<LazyImplementation>([] () {
|
||||
std::vector<LazyImplementation> fres;
|
||||
|
||||
addCudaSearchPath();
|
||||
|
||||
std::string impl_name_re = "llamamodel-mainline-(cpu|metal|kompute|vulkan|cuda)";
|
||||
if (cpu_supports_avx2() == 0) {
|
||||
impl_name_re += "-avxonly";
|
||||
}
|
||||
std::regex re(impl_name_re);
|
||||
auto search_in_directory = [&](const std::string& paths) {
|
||||
std::stringstream ss(paths);
|
||||
std::string path;
|
||||
// Split the paths string by the delimiter and process each path.
|
||||
while (std::getline(ss, path, ';')) {
|
||||
std::u8string u8_path(path.begin(), path.end());
|
||||
// Iterate over all libraries
|
||||
for (const auto &f : fs::directory_iterator(u8_path)) {
|
||||
const fs::path &p = f.path();
|
||||
|
||||
if (p.extension() != LIB_FILE_EXT) continue;
|
||||
if (!std::regex_search(p.stem().string(), re)) continue;
|
||||
|
||||
// Add to list if model implementation
|
||||
Dlhandle dl;
|
||||
try {
|
||||
dl = Dlhandle(p);
|
||||
} catch (const Dlhandle::Exception &e) {
|
||||
std::cerr << "Failed to load " << p.filename().string() << ": " << e.what() << "\n";
|
||||
continue;
|
||||
}
|
||||
if (!isImplementation(dl)) {
|
||||
std::cerr << "Not an implementation: " << p.filename().string() << "\n";
|
||||
continue;
|
||||
}
|
||||
fres.emplace_back(Implementation(std::move(dl)));
|
||||
}
|
||||
bool avxonly = cpu_supports_avx2() == 0;
|
||||
std::stringstream ss(s_implementations_search_path);
|
||||
std::string piece;
|
||||
// Split the paths string by the delimiter and process each path.
|
||||
while (std::getline(ss, piece, ';')) {
|
||||
auto basePath = fs::path(std::u8string(piece.begin(), piece.end()));
|
||||
// Iterate over all libraries
|
||||
for (auto &buildBackend : ALL_BUILD_BACKENDS) {
|
||||
auto path = basePath /
|
||||
"llamamodel-mainline-"s.append(buildBackend).append(avxonly ? "-avxonly" : "").append(LIB_EXT);
|
||||
if (fs::exists(path))
|
||||
fres.push_back(LazyImplementation { std::string(buildBackend), path });
|
||||
}
|
||||
};
|
||||
|
||||
search_in_directory(s_implementations_search_path);
|
||||
}
|
||||
|
||||
return fres;
|
||||
}());
|
||||
@@ -173,22 +159,16 @@ const std::vector<LLModel::Implementation> &LLModel::Implementation::implementat
|
||||
return *libs;
|
||||
}
|
||||
|
||||
static std::string applyCPUVariant(const std::string &buildVariant)
|
||||
auto LLModel::Implementation::findImplementation(const char *fname, const std::string &buildBackend)
|
||||
-> const Implementation *
|
||||
{
|
||||
if (buildVariant != "metal" && cpu_supports_avx2() == 0) {
|
||||
return buildVariant + "-avxonly";
|
||||
}
|
||||
return buildVariant;
|
||||
}
|
||||
|
||||
const LLModel::Implementation* LLModel::Implementation::implementation(const char *fname, const std::string& buildVariant)
|
||||
{
|
||||
bool buildVariantMatched = false;
|
||||
bool buildBackendMatched = false;
|
||||
std::optional<std::string> archName;
|
||||
for (const auto& i : implementationList()) {
|
||||
if (buildVariant != i.m_buildVariant) continue;
|
||||
buildVariantMatched = true;
|
||||
for (auto &li : getImplementations()) {
|
||||
if (li.buildBackend != buildBackend) continue;
|
||||
buildBackendMatched = true;
|
||||
|
||||
auto &i = li.get();
|
||||
char *arch = i.m_getFileArch(fname);
|
||||
if (!arch) continue;
|
||||
archName = arch;
|
||||
@@ -198,7 +178,7 @@ const LLModel::Implementation* LLModel::Implementation::implementation(const cha
|
||||
if (archSupported) return &i;
|
||||
}
|
||||
|
||||
if (!buildVariantMatched)
|
||||
if (!buildBackendMatched)
|
||||
return nullptr;
|
||||
if (!archName)
|
||||
throw UnsupportedModelError("Unsupported file format");
|
||||
@@ -216,7 +196,7 @@ LLModel *LLModel::Implementation::construct(const std::string &modelPath, const
|
||||
}
|
||||
|
||||
for (const auto &desiredBackend: desiredBackends) {
|
||||
const auto *impl = implementation(modelPath.c_str(), applyCPUVariant(desiredBackend));
|
||||
const auto *impl = findImplementation(modelPath.c_str(), desiredBackend);
|
||||
|
||||
if (impl) {
|
||||
// Construct llmodel implementation
|
||||
@@ -251,11 +231,11 @@ LLModel *LLModel::Implementation::constructGlobalLlama(const std::optional<std::
|
||||
{
|
||||
static std::unordered_map<std::string, std::unique_ptr<LLModel>> implCache;
|
||||
|
||||
const std::vector<Implementation> *impls;
|
||||
std::vector<LazyImplementation> *impls;
|
||||
try {
|
||||
impls = &implementationList();
|
||||
impls = &getImplementations();
|
||||
} catch (const std::runtime_error &e) {
|
||||
std::cerr << __func__ << ": implementationList failed: " << e.what() << "\n";
|
||||
std::cerr << __func__ << ": getImplementations() failed: " << e.what() << "\n";
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@@ -268,13 +248,15 @@ LLModel *LLModel::Implementation::constructGlobalLlama(const std::optional<std::
|
||||
|
||||
const Implementation *impl = nullptr;
|
||||
|
||||
for (const auto &desiredBackend: desiredBackends) {
|
||||
for (const auto &desiredBackend : desiredBackends) {
|
||||
auto cacheIt = implCache.find(desiredBackend);
|
||||
if (cacheIt != implCache.end())
|
||||
return cacheIt->second.get(); // cached
|
||||
|
||||
for (const auto &i: *impls) {
|
||||
if (i.m_modelType == "LLaMA" && i.m_buildVariant == applyCPUVariant(desiredBackend)) {
|
||||
for (auto &li : *impls) {
|
||||
if (li.buildBackend == desiredBackend) {
|
||||
auto &i = li.get();
|
||||
assert(i.m_modelType == "LLaMA");
|
||||
impl = &i;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -8,10 +8,8 @@
|
||||
| --- | --- | --- |
|
||||
| **Theme** | Color theme for the application. Options are `Light`, `Dark`, and `LegacyDark` | `Light` |
|
||||
| **Font Size** | Font size setting for text throughout the application. Options are Small, Medium, and Large | Small |
|
||||
| **Language and Locale** | The language and locale of that language you wish to use | System Locale |
|
||||
| **Device** | Device that will run your models. Options are `Auto` (GPT4All chooses), `Metal` (Apple Silicon M1+), `CPU`, and `GPU` | `Auto` |
|
||||
| **Default Model** | Choose your preferred LLM to load by default on startup| Auto |
|
||||
| **Suggestion Mode** | Generate suggested follow up questions at the end of responses | When chatting with LocalDocs |
|
||||
| **Download Path** | Select a destination on your device to save downloaded models | Windows: `C:\Users\{username}\AppData\Local\nomic.ai\GPT4All`<br><br>Mac: `/Users/{username}/Library/Application Support/nomic.ai/GPT4All/`<br><br>Linux: `/home/{username}/.local/share/nomic.ai/GPT4All` |
|
||||
| **Enable Datalake** | Opt-in to sharing interactions with GPT4All community (**anonymous** and **optional**) | Off |
|
||||
|
||||
@@ -20,7 +18,7 @@
|
||||
| Setting | Description | Default Value |
|
||||
| --- | --- | --- |
|
||||
| **CPU Threads** | Number of concurrently running CPU threads (more can speed up responses) | 4 |
|
||||
| **Enable System Tray** | The application will minimize to the system tray / taskbar when the window is closed | Off |
|
||||
| **Save Chat Context** | Save chat context to disk to pick up exactly where a model left off. | Off |
|
||||
| **Enable Local Server** | Allow any application on your device to use GPT4All via an OpenAI-compatible GPT4All API | Off |
|
||||
| **API Server Port** | Local HTTP port for the local API server | 4891 |
|
||||
|
||||
@@ -31,11 +29,8 @@
|
||||
| Setting | Description | Default Value |
|
||||
| --- | --- | --- |
|
||||
| **Name** | Unique name of this model / character| set by model uploader |
|
||||
| **Model File** | Filename (.gguf) of the model | set by model uploader |
|
||||
| **System Message** | General instructions for the chats this model will be used for | set by model uploader |
|
||||
| **Chat Template** | Format of user <-> assistant interactions for the chats this model will be used for | set by model uploader |
|
||||
| **Chat Name Prompt** | Prompt used to automatically generate chat names | Describe the above conversation in seven words or less. |
|
||||
| **Suggested FollowUp Prompt** | Prompt used to automatically generate follow up questions after a chat response | Suggest three very short factual follow-up questions that have not been answered yet or cannot be found inspired by the previous conversation and excerpts. |
|
||||
| **System Prompt** | General instructions for the chats this model will be used for | set by model uploader |
|
||||
| **Prompt Template** | Format of user <-> assistant interactions for the chats this model will be used for | set by model uploader |
|
||||
|
||||
### Clone
|
||||
|
||||
|
||||
@@ -4,39 +4,6 @@ All notable changes to this project will be documented in this file.
|
||||
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
|
||||
|
||||
## [3.7.0] - 2025-01-21
|
||||
|
||||
### Added
|
||||
- Add support for the Windows ARM64 target platform (CPU-only) ([#3385](https://github.com/nomic-ai/gpt4all/pull/3385))
|
||||
|
||||
### Fixed
|
||||
- Fix the timeout error in code interpreter ([#3369](https://github.com/nomic-ai/gpt4all/pull/3369))
|
||||
- Fix code interpreter console.log not accepting multiple arguments ([#3371](https://github.com/nomic-ai/gpt4all/pull/3371))
|
||||
- Remove 'X is defined' checks from templates for better compatibility ([#3372](https://github.com/nomic-ai/gpt4all/pull/3372))
|
||||
- Jinja2Cpp: Add 'if' requirement for 'else' parsing to fix crash ([#3373](https://github.com/nomic-ai/gpt4all/pull/3373))
|
||||
- Save chats on quit, even if the window isn't closed first ([#3387](https://github.com/nomic-ai/gpt4all/pull/3387))
|
||||
- Add chat template replacements for five new models and fix EM German Mistral ([#3393](https://github.com/nomic-ai/gpt4all/pull/3393))
|
||||
- Fix crash when entering `{{ a["foo"(` as chat template ([#3394](https://github.com/nomic-ai/gpt4all/pull/3394))
|
||||
- Sign the maintenance tool on macOS to prevent crash on Sonoma ([#3391](https://github.com/nomic-ai/gpt4all/pull/3391))
|
||||
- Jinja2Cpp: Fix operator precedence in 'not X is defined' ([#3402](https://github.com/nomic-ai/gpt4all/pull/3402))
|
||||
|
||||
## [3.6.1] - 2024-12-20
|
||||
|
||||
### Fixed
|
||||
- Fix the stop generation button no longer working in v3.6.0 ([#3336](https://github.com/nomic-ai/gpt4all/pull/3336))
|
||||
- Fix the copy entire conversation button no longer working in v3.6.0 ([#3336](https://github.com/nomic-ai/gpt4all/pull/3336))
|
||||
|
||||
## [3.6.0] - 2024-12-19
|
||||
|
||||
### Added
|
||||
- Automatically substitute chat templates that are not compatible with Jinja2Cpp in GGUFs ([#3327](https://github.com/nomic-ai/gpt4all/pull/3327))
|
||||
- Built-in javascript code interpreter tool plus model ([#3173](https://github.com/nomic-ai/gpt4all/pull/3173))
|
||||
|
||||
### Fixed
|
||||
- Fix remote model template to allow for XML in messages ([#3318](https://github.com/nomic-ai/gpt4all/pull/3318))
|
||||
- Fix Jinja2Cpp bug that broke system message detection in chat templates ([#3325](https://github.com/nomic-ai/gpt4all/pull/3325))
|
||||
- Fix LocalDocs sources displaying in unconsolidated form after v3.5.0 ([#3328](https://github.com/nomic-ai/gpt4all/pull/3328))
|
||||
|
||||
## [3.5.3] - 2024-12-16
|
||||
|
||||
### Fixed
|
||||
@@ -261,8 +228,6 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
|
||||
- Fix several Vulkan resource management issues ([#2694](https://github.com/nomic-ai/gpt4all/pull/2694))
|
||||
- Fix crash/hang when some models stop generating, by showing special tokens ([#2701](https://github.com/nomic-ai/gpt4all/pull/2701))
|
||||
|
||||
[3.6.1]: https://github.com/nomic-ai/gpt4all/compare/v3.6.0...v3.6.1
|
||||
[3.6.0]: https://github.com/nomic-ai/gpt4all/compare/v3.5.3...v3.6.0
|
||||
[3.5.3]: https://github.com/nomic-ai/gpt4all/compare/v3.5.2...v3.5.3
|
||||
[3.5.2]: https://github.com/nomic-ai/gpt4all/compare/v3.5.1...v3.5.2
|
||||
[3.5.1]: https://github.com/nomic-ai/gpt4all/compare/v3.5.0...v3.5.1
|
||||
|
||||
@@ -3,8 +3,8 @@ cmake_minimum_required(VERSION 3.25) # for try_compile SOURCE_FROM_VAR
|
||||
include(../common/common.cmake)
|
||||
|
||||
set(APP_VERSION_MAJOR 3)
|
||||
set(APP_VERSION_MINOR 7)
|
||||
set(APP_VERSION_PATCH 0)
|
||||
set(APP_VERSION_MINOR 5)
|
||||
set(APP_VERSION_PATCH 3)
|
||||
set(APP_VERSION_BASE "${APP_VERSION_MAJOR}.${APP_VERSION_MINOR}.${APP_VERSION_PATCH}")
|
||||
set(APP_VERSION "${APP_VERSION_BASE}")
|
||||
|
||||
@@ -28,20 +28,7 @@ option(GPT4ALL_TEST "Build the tests" ${Python3_FOUND})
|
||||
option(GPT4ALL_LOCALHOST "Build installer for localhost repo" OFF)
|
||||
option(GPT4ALL_OFFLINE_INSTALLER "Build an offline installer" OFF)
|
||||
option(GPT4ALL_SIGN_INSTALL "Sign installed binaries and installers (requires signing identities)" OFF)
|
||||
option(GPT4ALL_GEN_CPACK_CONFIG "Generate the CPack config.xml in the package step and nothing else." OFF)
|
||||
set(GPT4ALL_USE_QTPDF "AUTO" CACHE STRING "Whether to Use QtPDF for LocalDocs. If OFF or not available on this platform, PDFium is used.")
|
||||
set_property(CACHE GPT4ALL_USE_QTPDF PROPERTY STRINGS AUTO ON OFF)
|
||||
|
||||
include(cmake/cpack_config.cmake)
|
||||
|
||||
if (GPT4ALL_GEN_CPACK_CONFIG)
|
||||
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/cmake/cpack-steal-config.cmake.in"
|
||||
"${CMAKE_BINARY_DIR}/cmake/cpack-steal-config.cmake" @ONLY)
|
||||
set(CPACK_POST_BUILD_SCRIPTS ${CMAKE_BINARY_DIR}/cmake/cpack-steal-config.cmake)
|
||||
include(CPack)
|
||||
include(CPackIFW)
|
||||
return()
|
||||
endif()
|
||||
|
||||
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
||||
set(CMAKE_CXX_STANDARD 23)
|
||||
@@ -93,18 +80,7 @@ configure_file(
|
||||
)
|
||||
|
||||
set(CMAKE_FIND_PACKAGE_TARGETS_GLOBAL ON)
|
||||
set(GPT4ALL_QT_COMPONENTS Core HttpServer LinguistTools Quick QuickDialogs2 Sql Svg)
|
||||
set(GPT4ALL_USING_QTPDF OFF)
|
||||
if (CMAKE_SYSTEM_NAME MATCHES Windows AND CMAKE_SYSTEM_PROCESSOR MATCHES "^(aarch64|AARCH64|arm64|ARM64)$")
|
||||
# QtPDF is not available.
|
||||
if (GPT4ALL_USE_QTPDF STREQUAL "ON")
|
||||
message(FATAL_ERROR "QtPDF is not available on Windows ARM64.")
|
||||
endif()
|
||||
elseif (GPT4ALL_USE_QTPDF MATCHES "^(ON|AUTO)$")
|
||||
set(GPT4ALL_USING_QTPDF ON)
|
||||
list(APPEND GPT4ALL_QT_COMPONENTS Pdf)
|
||||
endif()
|
||||
find_package(Qt6 6.5 COMPONENTS ${GPT4ALL_QT_COMPONENTS} REQUIRED)
|
||||
find_package(Qt6 6.5 COMPONENTS Core HttpServer LinguistTools Pdf Quick QuickDialogs2 Sql Svg REQUIRED)
|
||||
|
||||
if (QT_KNOWN_POLICY_QTP0004)
|
||||
qt_policy(SET QTP0004 NEW) # generate extra qmldir files on Qt 6.8+
|
||||
@@ -217,14 +193,12 @@ qt_add_executable(chat
|
||||
src/chatapi.cpp src/chatapi.h
|
||||
src/chatlistmodel.cpp src/chatlistmodel.h
|
||||
src/chatllm.cpp src/chatllm.h
|
||||
src/chatmodel.h src/chatmodel.cpp
|
||||
src/chatmodel.h
|
||||
src/chatviewtextprocessor.cpp src/chatviewtextprocessor.h
|
||||
src/codeinterpreter.cpp src/codeinterpreter.h
|
||||
src/database.cpp src/database.h
|
||||
src/download.cpp src/download.h
|
||||
src/embllm.cpp src/embllm.h
|
||||
src/jinja_helpers.cpp src/jinja_helpers.h
|
||||
src/jinja_replacements.cpp src/jinja_replacements.h
|
||||
src/llm.cpp src/llm.h
|
||||
src/localdocs.cpp src/localdocs.h
|
||||
src/localdocsmodel.cpp src/localdocsmodel.h
|
||||
@@ -233,9 +207,6 @@ qt_add_executable(chat
|
||||
src/mysettings.cpp src/mysettings.h
|
||||
src/network.cpp src/network.h
|
||||
src/server.cpp src/server.h
|
||||
src/tool.cpp src/tool.h
|
||||
src/toolcallparser.cpp src/toolcallparser.h
|
||||
src/toolmodel.cpp src/toolmodel.h
|
||||
src/xlsxtomd.cpp src/xlsxtomd.h
|
||||
${CHAT_EXE_RESOURCES}
|
||||
${MACOS_SOURCES}
|
||||
@@ -254,10 +225,8 @@ qt_add_qml_module(chat
|
||||
qml/AddHFModelView.qml
|
||||
qml/ApplicationSettings.qml
|
||||
qml/ChatDrawer.qml
|
||||
qml/ChatCollapsibleItem.qml
|
||||
qml/ChatItemView.qml
|
||||
qml/ChatMessageButton.qml
|
||||
qml/ChatTextItem.qml
|
||||
qml/ChatView.qml
|
||||
qml/CollectionsDrawer.qml
|
||||
qml/HomeView.qml
|
||||
@@ -428,14 +397,7 @@ target_include_directories(chat PRIVATE deps/usearch/include
|
||||
deps/usearch/fp16/include)
|
||||
|
||||
target_link_libraries(chat
|
||||
PRIVATE Qt6::Core Qt6::HttpServer Qt6::Quick Qt6::Sql Qt6::Svg)
|
||||
if (GPT4ALL_USING_QTPDF)
|
||||
target_compile_definitions(chat PRIVATE GPT4ALL_USE_QTPDF)
|
||||
target_link_libraries(chat PRIVATE Qt6::Pdf)
|
||||
else()
|
||||
# Link PDFium
|
||||
target_link_libraries(chat PRIVATE pdfium)
|
||||
endif()
|
||||
PRIVATE Qt6::Core Qt6::HttpServer Qt6::Pdf Qt6::Quick Qt6::Sql Qt6::Svg)
|
||||
target_link_libraries(chat
|
||||
PRIVATE llmodel SingleApplication fmt::fmt duckx::duckx QXlsx jinja2cpp)
|
||||
|
||||
@@ -445,6 +407,8 @@ endif()
|
||||
|
||||
# -- install --
|
||||
|
||||
set(COMPONENT_NAME_MAIN ${PROJECT_NAME})
|
||||
|
||||
install(TARGETS chat DESTINATION bin COMPONENT ${COMPONENT_NAME_MAIN})
|
||||
|
||||
install(
|
||||
@@ -517,38 +481,67 @@ if (LLMODEL_CUDA)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if (NOT GPT4ALL_USING_QTPDF)
|
||||
# Install PDFium
|
||||
if (WIN32)
|
||||
install(FILES "${PDFium_LIBRARY}" DESTINATION bin COMPONENT ${COMPONENT_NAME_MAIN})
|
||||
else()
|
||||
install(FILES "${PDFium_LIBRARY}" DESTINATION lib COMPONENT ${COMPONENT_NAME_MAIN})
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if (NOT APPLE)
|
||||
install(FILES "${LOCAL_EMBEDDING_MODEL_PATH}"
|
||||
DESTINATION resources
|
||||
COMPONENT ${COMPONENT_NAME_MAIN})
|
||||
endif()
|
||||
|
||||
if (CMAKE_SYSTEM_NAME MATCHES Linux)
|
||||
set(CPACK_GENERATOR "IFW")
|
||||
set(CPACK_VERBATIM_VARIABLES YES)
|
||||
set(CPACK_IFW_VERBOSE ON)
|
||||
|
||||
if(${CMAKE_SYSTEM_NAME} MATCHES Linux)
|
||||
find_program(LINUXDEPLOYQT linuxdeployqt HINTS "$ENV{HOME}/dev/linuxdeployqt/build/tools/linuxdeployqt" "$ENV{HOME}/project/linuxdeployqt/bin")
|
||||
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/cmake/deploy-qt-linux.cmake.in"
|
||||
"${CMAKE_BINARY_DIR}/cmake/deploy-qt-linux.cmake" @ONLY)
|
||||
set(CPACK_PRE_BUILD_SCRIPTS ${CMAKE_BINARY_DIR}/cmake/deploy-qt-linux.cmake)
|
||||
elseif (CMAKE_SYSTEM_NAME MATCHES Windows)
|
||||
find_program(WINDEPLOYQT windeployqt)
|
||||
set(CPACK_IFW_ROOT "~/Qt/Tools/QtInstallerFramework/4.6")
|
||||
set(CPACK_PACKAGE_FILE_NAME "${COMPONENT_NAME_MAIN}-installer-linux")
|
||||
set(CPACK_IFW_TARGET_DIRECTORY "@HomeDir@/${COMPONENT_NAME_MAIN}")
|
||||
elseif(${CMAKE_SYSTEM_NAME} MATCHES Windows)
|
||||
find_program(WINDEPLOYQT windeployqt HINTS ${_qt_bin_dir})
|
||||
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/cmake/deploy-qt-windows.cmake.in"
|
||||
"${CMAKE_BINARY_DIR}/cmake/deploy-qt-windows.cmake" @ONLY)
|
||||
set(CPACK_PRE_BUILD_SCRIPTS ${CMAKE_BINARY_DIR}/cmake/deploy-qt-windows.cmake)
|
||||
elseif (CMAKE_SYSTEM_NAME MATCHES Darwin)
|
||||
find_program(MACDEPLOYQT macdeployqt)
|
||||
set(CPACK_IFW_ROOT "C:/Qt/Tools/QtInstallerFramework/4.6")
|
||||
set(CPACK_IFW_PACKAGE_ICON "${CMAKE_CURRENT_SOURCE_DIR}/resources/gpt4all.ico")
|
||||
set(CPACK_PACKAGE_FILE_NAME "${COMPONENT_NAME_MAIN}-installer-win64")
|
||||
set(CPACK_IFW_TARGET_DIRECTORY "@HomeDir@\\${COMPONENT_NAME_MAIN}")
|
||||
elseif(${CMAKE_SYSTEM_NAME} MATCHES Darwin)
|
||||
find_program(MACDEPLOYQT macdeployqt HINTS ${_qt_bin_dir})
|
||||
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/cmake/deploy-qt-mac.cmake.in"
|
||||
"${CMAKE_BINARY_DIR}/cmake/deploy-qt-mac.cmake" @ONLY)
|
||||
set(CPACK_PRE_BUILD_SCRIPTS ${CMAKE_BINARY_DIR}/cmake/deploy-qt-mac.cmake)
|
||||
set(CPACK_IFW_ROOT "~/Qt/Tools/QtInstallerFramework/4.6")
|
||||
set(CPACK_IFW_PACKAGE_ICON "${CMAKE_CURRENT_SOURCE_DIR}/resources/gpt4all.icns")
|
||||
set(CPACK_PACKAGE_FILE_NAME "${COMPONENT_NAME_MAIN}-installer-darwin")
|
||||
set(CPACK_IFW_TARGET_DIRECTORY "@ApplicationsDir@/${COMPONENT_NAME_MAIN}")
|
||||
set(CPACK_BUNDLE_NAME ${COMPONENT_NAME_MAIN})
|
||||
set(CPACK_BUNDLE_ICON "${CMAKE_CURRENT_SOURCE_DIR}/resources/gpt4all.icns")
|
||||
endif()
|
||||
|
||||
set(CPACK_COMPONENTS_ALL gpt4all) # exclude development components
|
||||
set(CPACK_PACKAGE_INSTALL_DIRECTORY ${COMPONENT_NAME_MAIN})
|
||||
set(CPACK_PACKAGE_VERSION_MAJOR ${PROJECT_VERSION_MAJOR})
|
||||
set(CPACK_PACKAGE_VERSION_MINOR ${PROJECT_VERSION_MINOR})
|
||||
SET(CPACK_PACKAGE_VERSION_PATCH ${PROJECT_VERSION_PATCH})
|
||||
set(CPACK_PACKAGE_HOMEPAGE_URL "https://www.nomic.ai/gpt4all")
|
||||
set(CPACK_PACKAGE_ICON "${CMAKE_CURRENT_SOURCE_DIR}/icons/gpt4all-48.png")
|
||||
set(CPACK_RESOURCE_FILE_LICENSE ${CMAKE_CURRENT_SOURCE_DIR}/LICENSE)
|
||||
set(CPACK_RESOURCE_FILE_README ${CMAKE_CURRENT_SOURCE_DIR}/README.md)
|
||||
set(CPACK_PACKAGE_EXECUTABLES "GPT4All")
|
||||
set(CPACK_CREATE_DESKTOP_LINKS "GPT4All")
|
||||
set(CPACK_IFW_PACKAGE_NAME "GPT4All")
|
||||
set(CPACK_IFW_PACKAGE_TITLE "GPT4All Installer")
|
||||
set(CPACK_IFW_PACKAGE_PUBLISHER "Nomic, Inc.")
|
||||
set(CPACK_IFW_PRODUCT_URL "https://www.nomic.ai/gpt4all")
|
||||
set(CPACK_IFW_PACKAGE_WIZARD_STYLE "Aero")
|
||||
set(CPACK_IFW_PACKAGE_LOGO "${CMAKE_CURRENT_SOURCE_DIR}/icons/gpt4all-48.png")
|
||||
set(CPACK_IFW_PACKAGE_WINDOW_ICON "${CMAKE_CURRENT_SOURCE_DIR}/icons/gpt4all-32.png")
|
||||
set(CPACK_IFW_PACKAGE_WIZARD_SHOW_PAGE_LIST OFF)
|
||||
set(CPACK_IFW_PACKAGE_CONTROL_SCRIPT "${CMAKE_CURRENT_SOURCE_DIR}/cmake/installer_control.qs")
|
||||
|
||||
include(InstallRequiredSystemLibraries)
|
||||
include(CPack)
|
||||
include(CPackIFW)
|
||||
@@ -560,35 +553,20 @@ endif()
|
||||
cpack_ifw_configure_component(${COMPONENT_NAME_MAIN} ESSENTIAL FORCED_INSTALLATION)
|
||||
cpack_ifw_configure_component(${COMPONENT_NAME_MAIN} VERSION ${APP_VERSION})
|
||||
cpack_ifw_configure_component(${COMPONENT_NAME_MAIN} LICENSES "MIT LICENSE" ${CPACK_RESOURCE_FILE_LICENSE})
|
||||
cpack_ifw_configure_component(${COMPONENT_NAME_MAIN} SCRIPT "${CMAKE_CURRENT_SOURCE_DIR}/cmake/installer_gpt4all_component.qs")
|
||||
cpack_ifw_configure_component(${COMPONENT_NAME_MAIN} SCRIPT "${CMAKE_CURRENT_SOURCE_DIR}/cmake/installer_component.qs")
|
||||
cpack_ifw_configure_component(${COMPONENT_NAME_MAIN} REPLACES "gpt4all-chat") #Was used in very earliest prototypes
|
||||
|
||||
if (APPLE AND GPT4ALL_SIGN_INSTALL)
|
||||
if (GPT4ALL_OFFLINE_INSTALLER)
|
||||
cpack_add_component(maintenancetool HIDDEN)
|
||||
else()
|
||||
cpack_add_component(maintenancetool HIDDEN DOWNLOADED)
|
||||
endif()
|
||||
cpack_ifw_configure_component(maintenancetool ESSENTIAL FORCED_INSTALLATION)
|
||||
cpack_ifw_configure_component(maintenancetool VERSION ${APP_VERSION})
|
||||
cpack_ifw_configure_component(maintenancetool SCRIPT "${CMAKE_CURRENT_SOURCE_DIR}/cmake/installer_maintenancetool_component.qs")
|
||||
endif()
|
||||
|
||||
if (GPT4ALL_LOCALHOST)
|
||||
cpack_ifw_add_repository("GPT4AllRepository" URL "http://localhost/repository")
|
||||
elseif (GPT4ALL_OFFLINE_INSTALLER)
|
||||
add_compile_definitions(GPT4ALL_OFFLINE_INSTALLER)
|
||||
elseif(GPT4ALL_OFFLINE_INSTALLER)
|
||||
add_compile_definitions(GPT4ALL_OFFLINE_INSTALLER)
|
||||
else()
|
||||
if (CMAKE_SYSTEM_NAME MATCHES Linux)
|
||||
cpack_ifw_add_repository("GPT4AllRepository" URL "https://gpt4all.io/installer_repos/linux/repository")
|
||||
elseif (CMAKE_SYSTEM_NAME MATCHES Windows)
|
||||
# To sign the target on windows have to create a batch script add use it as a custom target and then use CPACK_IFW_EXTRA_TARGETS to set this extra target
|
||||
if (CMAKE_SYSTEM_PROCESSOR MATCHES "^(x86_64|AMD64|amd64)$")
|
||||
cpack_ifw_add_repository("GPT4AllRepository" URL "https://gpt4all.io/installer_repos/windows/repository")
|
||||
elseif (CMAKE_SYSTEM_PROCESSOR MATCHES "^(aarch64|AARCH64|arm64|ARM64)$")
|
||||
cpack_ifw_add_repository("GPT4AllRepository" URL "https://gpt4all.io/installer_repos/windows_arm/repository")
|
||||
endif()
|
||||
elseif (CMAKE_SYSTEM_NAME MATCHES Darwin)
|
||||
cpack_ifw_add_repository("GPT4AllRepository" URL "https://gpt4all.io/installer_repos/mac/repository")
|
||||
endif()
|
||||
if(${CMAKE_SYSTEM_NAME} MATCHES Linux)
|
||||
cpack_ifw_add_repository("GPT4AllRepository" URL "https://gpt4all.io/installer_repos/linux/repository")
|
||||
elseif(${CMAKE_SYSTEM_NAME} MATCHES Windows)
|
||||
#To sign the target on windows have to create a batch script add use it as a custom target and then use CPACK_IFW_EXTRA_TARGETS to set this extra target
|
||||
cpack_ifw_add_repository("GPT4AllRepository" URL "https://gpt4all.io/installer_repos/windows/repository")
|
||||
elseif(${CMAKE_SYSTEM_NAME} MATCHES Darwin)
|
||||
cpack_ifw_add_repository("GPT4AllRepository" URL "https://gpt4all.io/installer_repos/mac/repository")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
set(OUTPUT_DIR "@CMAKE_BINARY_DIR@")
|
||||
file(COPY ${CPACK_TEMPORARY_INSTALL_DIRECTORY}/config DESTINATION ${OUTPUT_DIR}/cpack-config)
|
||||
@@ -1,51 +0,0 @@
|
||||
set(COMPONENT_NAME_MAIN "gpt4all")
|
||||
|
||||
set(CPACK_GENERATOR "IFW")
|
||||
set(CPACK_VERBATIM_VARIABLES YES)
|
||||
set(CPACK_IFW_VERBOSE ON)
|
||||
|
||||
if (CMAKE_SYSTEM_NAME MATCHES Linux)
|
||||
set(CPACK_IFW_ROOT "~/Qt/Tools/QtInstallerFramework/4.6")
|
||||
set(CPACK_PACKAGE_FILE_NAME "${COMPONENT_NAME_MAIN}-installer-linux")
|
||||
set(CPACK_IFW_TARGET_DIRECTORY "@HomeDir@/${COMPONENT_NAME_MAIN}")
|
||||
elseif (CMAKE_SYSTEM_NAME MATCHES Windows)
|
||||
set(CPACK_IFW_ROOT "C:/Qt/Tools/QtInstallerFramework/4.6")
|
||||
set(CPACK_IFW_PACKAGE_ICON "${CMAKE_CURRENT_SOURCE_DIR}/resources/gpt4all.ico")
|
||||
if (CMAKE_SYSTEM_PROCESSOR MATCHES "^(x86_64|AMD64|amd64)$")
|
||||
set(CPACK_PACKAGE_FILE_NAME "${COMPONENT_NAME_MAIN}-installer-win64")
|
||||
elseif (CMAKE_SYSTEM_PROCESSOR MATCHES "^(aarch64|AARCH64|arm64|ARM64)$")
|
||||
set(CPACK_PACKAGE_FILE_NAME "${COMPONENT_NAME_MAIN}-installer-win64-arm")
|
||||
else()
|
||||
message(FATAL_ERROR "Unrecognized processor: ${CMAKE_SYSTEM_PROCESSOR}")
|
||||
endif()
|
||||
set(CPACK_IFW_TARGET_DIRECTORY "@HomeDir@\\${COMPONENT_NAME_MAIN}")
|
||||
elseif (CMAKE_SYSTEM_NAME MATCHES Darwin)
|
||||
set(CPACK_IFW_ROOT "~/Qt/Tools/QtInstallerFramework/4.6")
|
||||
set(CPACK_IFW_PACKAGE_ICON "${CMAKE_CURRENT_SOURCE_DIR}/resources/gpt4all.icns")
|
||||
set(CPACK_PACKAGE_FILE_NAME "${COMPONENT_NAME_MAIN}-installer-darwin")
|
||||
set(CPACK_IFW_TARGET_DIRECTORY "@ApplicationsDir@/${COMPONENT_NAME_MAIN}")
|
||||
endif()
|
||||
|
||||
set(CPACK_COMPONENTS_ALL ${COMPONENT_NAME_MAIN}) # exclude development components
|
||||
if (APPLE AND GPT4ALL_SIGN_INSTALL)
|
||||
list(APPEND CPACK_COMPONENTS_ALL maintenancetool)
|
||||
endif()
|
||||
set(CPACK_PACKAGE_INSTALL_DIRECTORY ${COMPONENT_NAME_MAIN})
|
||||
set(CPACK_PACKAGE_VERSION_MAJOR ${PROJECT_VERSION_MAJOR})
|
||||
set(CPACK_PACKAGE_VERSION_MINOR ${PROJECT_VERSION_MINOR})
|
||||
set(CPACK_PACKAGE_VERSION_PATCH ${PROJECT_VERSION_PATCH})
|
||||
set(CPACK_PACKAGE_HOMEPAGE_URL "https://www.nomic.ai/gpt4all")
|
||||
set(CPACK_PACKAGE_ICON "${CMAKE_CURRENT_SOURCE_DIR}/icons/gpt4all-48.png")
|
||||
set(CPACK_RESOURCE_FILE_LICENSE ${CMAKE_CURRENT_SOURCE_DIR}/LICENSE)
|
||||
set(CPACK_RESOURCE_FILE_README ${CMAKE_CURRENT_SOURCE_DIR}/README.md)
|
||||
set(CPACK_PACKAGE_EXECUTABLES "GPT4All")
|
||||
set(CPACK_CREATE_DESKTOP_LINKS "GPT4All")
|
||||
set(CPACK_IFW_PACKAGE_NAME "GPT4All")
|
||||
set(CPACK_IFW_PACKAGE_TITLE "GPT4All Installer")
|
||||
set(CPACK_IFW_PACKAGE_PUBLISHER "Nomic, Inc.")
|
||||
set(CPACK_IFW_PRODUCT_URL "https://www.nomic.ai/gpt4all")
|
||||
set(CPACK_IFW_PACKAGE_WIZARD_STYLE "Aero")
|
||||
set(CPACK_IFW_PACKAGE_LOGO "${CMAKE_CURRENT_SOURCE_DIR}/icons/gpt4all-48.png")
|
||||
set(CPACK_IFW_PACKAGE_WINDOW_ICON "${CMAKE_CURRENT_SOURCE_DIR}/icons/gpt4all-32.png")
|
||||
set(CPACK_IFW_PACKAGE_WIZARD_SHOW_PAGE_LIST OFF)
|
||||
set(CPACK_IFW_PACKAGE_CONTROL_SCRIPT "${CMAKE_CURRENT_SOURCE_DIR}/cmake/installer_control.qs")
|
||||
@@ -1,10 +1,8 @@
|
||||
set(MACDEPLOYQT "@MACDEPLOYQT@")
|
||||
set(COMPONENT_NAME_MAIN "@COMPONENT_NAME_MAIN@")
|
||||
set(CMAKE_CURRENT_SOURCE_DIR "@CMAKE_CURRENT_SOURCE_DIR@")
|
||||
set(GPT4ALL_SIGN_INSTALL "@GPT4ALL_SIGN_INSTALL@")
|
||||
set(GPT4ALL_SIGNING_ID "@MAC_SIGNING_IDENTITY@")
|
||||
set(CPACK_CONFIG_DIR "@CMAKE_BINARY_DIR@")
|
||||
if (GPT4ALL_SIGN_INSTALL)
|
||||
if (GPT4ALL_SIGNING_ID)
|
||||
set(MAC_NOTARIZE -sign-for-notarization=${GPT4ALL_SIGNING_ID})
|
||||
endif()
|
||||
execute_process(COMMAND ${MACDEPLOYQT} ${CPACK_TEMPORARY_INSTALL_DIRECTORY}/packages/${COMPONENT_NAME_MAIN}/data/bin/gpt4all.app -qmldir=${CMAKE_CURRENT_SOURCE_DIR} -verbose=2 ${MAC_NOTARIZE})
|
||||
@@ -20,13 +18,3 @@ file(COPY "${CMAKE_CURRENT_SOURCE_DIR}/icons/gpt4all-48.png"
|
||||
DESTINATION ${CPACK_TEMPORARY_INSTALL_DIRECTORY}/packages/${COMPONENT_NAME_MAIN}/data)
|
||||
file(COPY "${CMAKE_CURRENT_SOURCE_DIR}/resources/gpt4all.icns"
|
||||
DESTINATION ${CPACK_TEMPORARY_INSTALL_DIRECTORY}/packages/${COMPONENT_NAME_MAIN}/data)
|
||||
|
||||
if (GPT4ALL_SIGN_INSTALL)
|
||||
# Create signed MaintenanceTool
|
||||
set(MT_DATA_DIR ${CPACK_TEMPORARY_INSTALL_DIRECTORY}/packages/maintenancetool/data)
|
||||
file(MAKE_DIRECTORY ${MT_DATA_DIR})
|
||||
execute_process(
|
||||
COMMAND binarycreator --config ${CPACK_CONFIG_DIR}/cpack-config/config/config.xml --create-maintenancetool --sign ${GPT4ALL_SIGNING_ID}
|
||||
WORKING_DIRECTORY ${MT_DATA_DIR}
|
||||
)
|
||||
endif()
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
function Component()
|
||||
{
|
||||
component.ifwVersion = installer.value("FrameworkVersion");
|
||||
installer.installationStarted.connect(this, Component.prototype.onInstallationStarted);
|
||||
}
|
||||
|
||||
Component.prototype.onInstallationStarted = function()
|
||||
{
|
||||
if (component.updateRequested() || component.installationRequested()) {
|
||||
if (installer.value("os") == "win") {
|
||||
component.installerbaseBinaryPath = "@TargetDir@/installerbase.exe";
|
||||
} else if (installer.value("os") == "x11") {
|
||||
component.installerbaseBinaryPath = "@TargetDir@/installerbase";
|
||||
} else if (installer.value("os") == "mac") {
|
||||
component.installerbaseBinaryPath = "@TargetDir@/MaintenanceTool.app";
|
||||
}
|
||||
installer.setInstallerBaseBinary(component.installerbaseBinaryPath);
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,3 @@
|
||||
include(FetchContent)
|
||||
|
||||
|
||||
set(BUILD_SHARED_LIBS OFF)
|
||||
|
||||
set(FMT_INSTALL OFF)
|
||||
@@ -23,38 +20,3 @@ set(RAPIDJSON_ENABLE_INSTRUMENTATION_OPT OFF)
|
||||
add_subdirectory(rapidjson)
|
||||
|
||||
add_subdirectory(Jinja2Cpp)
|
||||
|
||||
if (NOT GPT4ALL_USING_QTPDF)
|
||||
# If we do not use QtPDF, we need to get PDFium.
|
||||
set(GPT4ALL_PDFIUM_TAG "chromium/6954")
|
||||
if (CMAKE_SYSTEM_NAME MATCHES Linux)
|
||||
FetchContent_Declare(
|
||||
pdfium
|
||||
URL "https://github.com/bblanchon/pdfium-binaries/releases/download/${GPT4ALL_PDFIUM_TAG}/pdfium-linux-x64.tgz"
|
||||
URL_HASH "SHA256=69917fd9543befc6c806254aff6c8a604d9e7cd3999a3e70fc32b8690d372da2"
|
||||
)
|
||||
elseif (CMAKE_SYSTEM_NAME MATCHES Windows)
|
||||
if (CMAKE_SYSTEM_PROCESSOR MATCHES "^(x86_64|AMD64|amd64)$")
|
||||
FetchContent_Declare(
|
||||
pdfium
|
||||
URL "https://github.com/bblanchon/pdfium-binaries/releases/download/${GPT4ALL_PDFIUM_TAG}/pdfium-win-x64.tgz"
|
||||
URL_HASH "SHA256=62ecac78fbaf658457beaffcc05eb147f493d435a2e1309e6a731808b4e80d38"
|
||||
)
|
||||
elseif (CMAKE_SYSTEM_PROCESSOR MATCHES "^(aarch64|AARCH64|arm64|ARM64)$")
|
||||
FetchContent_Declare(
|
||||
pdfium
|
||||
URL "https://github.com/bblanchon/pdfium-binaries/releases/download/${GPT4ALL_PDFIUM_TAG}/pdfium-win-arm64.tgz"
|
||||
URL_HASH "SHA256=a0b69014467f2b9824776c064920bc95359c9ba0d88793bdda1894a0f22206f8"
|
||||
)
|
||||
endif()
|
||||
elseif (CMAKE_SYSTEM_NAME MATCHES Darwin)
|
||||
FetchContent_Declare(
|
||||
pdfium
|
||||
URL "https://github.com/bblanchon/pdfium-binaries/releases/download/${GPT4ALL_PDFIUM_TAG}/pdfium-mac-univ.tgz"
|
||||
URL_HASH "SHA256=7442f1dc6bef90898b2b7bd38dbec369ddd81bbf66c1c5aac3a1b60e107098f9"
|
||||
)
|
||||
endif()
|
||||
|
||||
FetchContent_MakeAvailable(pdfium)
|
||||
find_package(PDFium REQUIRED PATHS "${pdfium_SOURCE_DIR}" NO_DEFAULT_PATH)
|
||||
endif()
|
||||
|
||||
Submodule gpt4all-chat/deps/Jinja2Cpp updated: ce10f783ba...bcf2f82ae1
@@ -54,7 +54,7 @@ Window {
|
||||
systemTrayIcon.shouldClose = true;
|
||||
window.shouldClose = true;
|
||||
savingPopup.open();
|
||||
ChatListModel.saveChatsForQuit();
|
||||
ChatListModel.saveChats();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -231,8 +231,8 @@ Window {
|
||||
|
||||
window.shouldClose = true;
|
||||
savingPopup.open();
|
||||
ChatListModel.saveChatsForQuit();
|
||||
close.accepted = false;
|
||||
ChatListModel.saveChats();
|
||||
close.accepted = false
|
||||
}
|
||||
|
||||
Connections {
|
||||
|
||||
@@ -1,16 +1,20 @@
|
||||
## Latest News
|
||||
|
||||
GPT4All v3.6.1 was released on December 20th and fixes issues with the stop generation and copy conversation buttons which were broken in v3.6.0.
|
||||
GPT4All v3.5.2 was released on December 13th. It changes the "Explore Models" page and fixes issues with the API server and cloned models.
|
||||
|
||||
GPT4All v3.5.1 was released on December 10th and fixes several issues with the new chat templates. Additionally, it fixes a bug with the default model button as well an issue with remote models.
|
||||
|
||||
---
|
||||
|
||||
GPT4All v3.6.0 was released on December 19th. Changes include:
|
||||
GPT4All v3.5.0 was released on December 9th. Changes include:
|
||||
|
||||
* **Reasoner v1:**
|
||||
* Built-in javascript code interpreter tool.
|
||||
* Custom curated model that utilizes the code interpreter to break down, analyze, perform, and verify complex reasoning tasks.
|
||||
* **Templates:** Automatically substitute chat templates that are not compatible with Jinja2Cpp in GGUFs.
|
||||
* **Fixes:**
|
||||
* Remote model template to allow for XML in messages.
|
||||
* Jinja2Cpp bug that broke system message detection in chat templates.
|
||||
* LocalDocs sources displaying in unconsolidated form after v3.5.0.
|
||||
* **Message Editing:**
|
||||
* You can now edit any message you've sent by clicking the pencil icon below it.
|
||||
* You can now redo earlier responses in the conversation.
|
||||
* **Templates:** Chat templates have been completely overhauled! They now use Jinja-style syntax. You may notice warnings or errors in the UI. Read the linked docs, and if you have any questions, please ask on the Discord.
|
||||
* **File Attachments:** Markdown and plain text files are now supported as file attachments.
|
||||
* **System Tray:** There is now an option in Application Settings to allow GPT4All to minimize to the system tray instead of closing.
|
||||
* **Local API Server:**
|
||||
* The API server now supports system messages from the client and no longer uses the system message in settings.
|
||||
* You can now send messages to the API server in any order supported by the model instead of just user/assistant pairs.
|
||||
* **Translations:** The Italian and Romanian translations have been improved.
|
||||
|
||||
@@ -1,22 +1,6 @@
|
||||
[
|
||||
{
|
||||
"order": "a",
|
||||
"md5sum": "a54c08a7b90e4029a8c2ab5b5dc936aa",
|
||||
"name": "Reasoner v1",
|
||||
"filename": "qwen2.5-coder-7b-instruct-q4_0.gguf",
|
||||
"filesize": "4431390720",
|
||||
"requires": "3.6.0",
|
||||
"ramrequired": "8",
|
||||
"parameters": "8 billion",
|
||||
"quant": "q4_0",
|
||||
"type": "qwen2",
|
||||
"description": "<ul><li>Based on <a href=\"https://huggingface.co/Qwen/Qwen2.5-Coder-7B-Instruct\">Qwen2.5-Coder 7B</a></li><li>Uses built-in javascript code interpreter</li><li>Use for complex reasoning tasks that can be aided by computation analysis</li><li>License: <a href=\"https://huggingface.co/Qwen/Qwen2.5-Coder-7B-Instruct/blob/main/LICENSE\">Apache License Version 2.0</a></li><li>#reasoning</li></ul>",
|
||||
"url": "https://huggingface.co/Qwen/Qwen2.5-Coder-7B-Instruct-GGUF/resolve/main/qwen2.5-coder-7b-instruct-q4_0.gguf",
|
||||
"chatTemplate": "{{- '<|im_start|>system\\n' }}\n{% if toolList|length > 0 %}You have access to the following functions:\n{% for tool in toolList %}\nUse the function '{{tool.function}}' to: '{{tool.description}}'\n{% if tool.parameters|length > 0 %}\nparameters:\n{% for info in tool.parameters %}\n {{info.name}}:\n type: {{info.type}}\n description: {{info.description}}\n required: {{info.required}}\n{% endfor %}\n{% endif %}\n# Tool Instructions\nIf you CHOOSE to call this function ONLY reply with the following format:\n'{{tool.symbolicFormat}}'\nHere is an example. If the user says, '{{tool.examplePrompt}}', then you reply\n'{{tool.exampleCall}}'\nAfter the result you might reply with, '{{tool.exampleReply}}'\n{% endfor %}\nYou MUST include both the start and end tags when you use a function.\n\nYou are a helpful AI assistant who uses the functions to break down, analyze, perform, and verify complex reasoning tasks. You SHOULD try to verify your answers using the functions where possible.\n{% endif %}\n{{- '<|im_end|>\\n' }}\n{% for message in messages %}\n{{'<|im_start|>' + message['role'] + '\\n' + message['content'] + '<|im_end|>\\n' }}\n{% endfor %}\n{% if add_generation_prompt %}\n{{ '<|im_start|>assistant\\n' }}\n{% endif %}\n",
|
||||
"systemPrompt": ""
|
||||
},
|
||||
{
|
||||
"order": "aa",
|
||||
"md5sum": "c87ad09e1e4c8f9c35a5fcef52b6f1c9",
|
||||
"name": "Llama 3 8B Instruct",
|
||||
"filename": "Meta-Llama-3-8B-Instruct.Q4_0.gguf",
|
||||
@@ -29,8 +13,7 @@
|
||||
"description": "<ul><li>Fast responses</li><li>Chat based model</li><li>Accepts system prompts in Llama 3 format</li><li>Trained by Meta</li><li>License: <a href=\"https://llama.meta.com/llama3/license/\">Meta Llama 3 Community License</a></li></ul>",
|
||||
"url": "https://gpt4all.io/models/gguf/Meta-Llama-3-8B-Instruct.Q4_0.gguf",
|
||||
"promptTemplate": "<|start_header_id|>user<|end_header_id|>\n\n%1<|eot_id|><|start_header_id|>assistant<|end_header_id|>\n\n%2<|eot_id|>",
|
||||
"systemPrompt": "",
|
||||
"chatTemplate": "{%- set loop_messages = messages %}\n{%- for message in loop_messages %}\n {%- set content = '<|start_header_id|>' + message['role'] + '<|end_header_id|>\\n\\n'+ message['content'] | trim + '<|eot_id|>' %}\n {{- content }}\n{%- endfor %}\n{%- if add_generation_prompt %}\n {{- '<|start_header_id|>assistant<|end_header_id|>\\n\\n' }}\n{%- endif %}"
|
||||
"systemPrompt": ""
|
||||
},
|
||||
{
|
||||
"order": "b",
|
||||
@@ -47,7 +30,7 @@
|
||||
"url": "https://huggingface.co/bartowski/Llama-3.2-3B-Instruct-GGUF/resolve/main/Llama-3.2-3B-Instruct-Q4_0.gguf",
|
||||
"promptTemplate": "<|start_header_id|>user<|end_header_id|>\n\n%1<|eot_id|><|start_header_id|>assistant<|end_header_id|>\n\n%2",
|
||||
"systemPrompt": "<|start_header_id|>system<|end_header_id|>\nCutting Knowledge Date: December 2023\n\nYou are a helpful assistant.<|eot_id|>",
|
||||
"chatTemplate": "{{- bos_token }}\n{%- set date_string = strftime_now('%d %b %Y') %}\n\n{#- This block extracts the system message, so we can slot it into the right place. #}\n{%- if messages[0]['role'] == 'system' %}\n {%- set system_message = messages[0]['content'] | trim %}\n {%- set loop_start = 1 %}\n{%- else %}\n {%- set system_message = '' %}\n {%- set loop_start = 0 %}\n{%- endif %}\n\n{#- System message #}\n{{- '<|start_header_id|>system<|end_header_id|>\\n\\n' }}\n{{- 'Cutting Knowledge Date: December 2023\\n' }}\n{{- 'Today Date: ' + date_string + '\\n\\n' }}\n{{- system_message }}\n{{- '<|eot_id|>' }}\n\n{%- for message in messages %}\n {%- if loop.index0 >= loop_start %}\n {{- '<|start_header_id|>' + message['role'] + '<|end_header_id|>\\n\\n' + message['content'] | trim + '<|eot_id|>' }}\n {%- endif %}\n{%- endfor %}\n{%- if add_generation_prompt %}\n {{- '<|start_header_id|>assistant<|end_header_id|>\\n\\n' }}\n{%- endif %}"
|
||||
"chatTemplate": "{{- bos_token }}\n{%- if not date_string is defined %}\n {%- if strftime_now is defined %}\n {%- set date_string = strftime_now('%d %b %Y') %}\n {%- else %}\n {%- set date_string = '26 Jul 2024' %}\n {%- endif %}\n{%- endif %}\n\n{#- This block extracts the system message, so we can slot it into the right place. #}\n{%- if messages[0]['role'] == 'system' %}\n {%- set system_message = messages[0]['content'] | trim %}\n {%- set loop_start = 1 %}\n{%- else %}\n {%- set system_message = '' %}\n {%- set loop_start = 0 %}\n{%- endif %}\n\n{#- System message #}\n{{- '<|start_header_id|>system<|end_header_id|>\\n\\n' }}\n{{- 'Cutting Knowledge Date: December 2023\\n' }}\n{{- 'Today Date: ' + date_string + '\\n\\n' }}\n{{- system_message }}\n{{- '<|eot_id|>' }}\n\n{%- for message in messages %}\n {%- if loop.index0 >= loop_start %}\n {{- '<|start_header_id|>' + message['role'] + '<|end_header_id|>\\n\\n' + message['content'] | trim + '<|eot_id|>' }}\n {%- endif %}\n{%- endfor %}\n{%- if add_generation_prompt %}\n {{- '<|start_header_id|>assistant<|end_header_id|>\\n\\n' }}\n{%- endif %}"
|
||||
},
|
||||
{
|
||||
"order": "c",
|
||||
@@ -64,7 +47,7 @@
|
||||
"url": "https://huggingface.co/bartowski/Llama-3.2-1B-Instruct-GGUF/resolve/main/Llama-3.2-1B-Instruct-Q4_0.gguf",
|
||||
"promptTemplate": "<|start_header_id|>user<|end_header_id|>\n\n%1<|eot_id|><|start_header_id|>assistant<|end_header_id|>\n\n%2",
|
||||
"systemPrompt": "<|start_header_id|>system<|end_header_id|>\nCutting Knowledge Date: December 2023\n\nYou are a helpful assistant.<|eot_id|>",
|
||||
"chatTemplate": "{{- bos_token }}\n{%- set date_string = strftime_now('%d %b %Y') %}\n\n{#- This block extracts the system message, so we can slot it into the right place. #}\n{%- if messages[0]['role'] == 'system' %}\n {%- set system_message = messages[0]['content'] | trim %}\n {%- set loop_start = 1 %}\n{%- else %}\n {%- set system_message = '' %}\n {%- set loop_start = 0 %}\n{%- endif %}\n\n{#- System message #}\n{{- '<|start_header_id|>system<|end_header_id|>\\n\\n' }}\n{{- 'Cutting Knowledge Date: December 2023\\n' }}\n{{- 'Today Date: ' + date_string + '\\n\\n' }}\n{{- system_message }}\n{{- '<|eot_id|>' }}\n\n{%- for message in messages %}\n {%- if loop.index0 >= loop_start %}\n {{- '<|start_header_id|>' + message['role'] + '<|end_header_id|>\\n\\n' + message['content'] | trim + '<|eot_id|>' }}\n {%- endif %}\n{%- endfor %}\n{%- if add_generation_prompt %}\n {{- '<|start_header_id|>assistant<|end_header_id|>\\n\\n' }}\n{%- endif %}"
|
||||
"chatTemplate": "{{- bos_token }}\n{%- if not date_string is defined %}\n {%- if strftime_now is defined %}\n {%- set date_string = strftime_now('%d %b %Y') %}\n {%- else %}\n {%- set date_string = '26 Jul 2024' %}\n {%- endif %}\n{%- endif %}\n\n{#- This block extracts the system message, so we can slot it into the right place. #}\n{%- if messages[0]['role'] == 'system' %}\n {%- set system_message = messages[0]['content'] | trim %}\n {%- set loop_start = 1 %}\n{%- else %}\n {%- set system_message = '' %}\n {%- set loop_start = 0 %}\n{%- endif %}\n\n{#- System message #}\n{{- '<|start_header_id|>system<|end_header_id|>\\n\\n' }}\n{{- 'Cutting Knowledge Date: December 2023\\n' }}\n{{- 'Today Date: ' + date_string + '\\n\\n' }}\n{{- system_message }}\n{{- '<|eot_id|>' }}\n\n{%- for message in messages %}\n {%- if loop.index0 >= loop_start %}\n {{- '<|start_header_id|>' + message['role'] + '<|end_header_id|>\\n\\n' + message['content'] | trim + '<|eot_id|>' }}\n {%- endif %}\n{%- endfor %}\n{%- if add_generation_prompt %}\n {{- '<|start_header_id|>assistant<|end_header_id|>\\n\\n' }}\n{%- endif %}"
|
||||
},
|
||||
{
|
||||
"order": "d",
|
||||
@@ -81,7 +64,7 @@
|
||||
"url": "https://huggingface.co/NousResearch/Nous-Hermes-2-Mistral-7B-DPO-GGUF/resolve/main/Nous-Hermes-2-Mistral-7B-DPO.Q4_0.gguf",
|
||||
"promptTemplate": "<|im_start|>user\n%1<|im_end|>\n<|im_start|>assistant\n%2<|im_end|>\n",
|
||||
"systemPrompt": "",
|
||||
"chatTemplate": "{%- for message in messages %}\n {{- '<|im_start|>' + message['role'] + '\\n' + message['content'] + '<|im_end|>\\n' }}\n{%- endfor %}\n{%- if add_generation_prompt %}\n {{- '<|im_start|>assistant\\n' }}\n{%- endif %}"
|
||||
"chatTemplate": "{%- for message in messages %}\n {{- '<|im_start|>' + message['role'] + '\\n' + message['content'] + '<|im_end|>' + '\\n' }}\n{%- endfor %}\n{%- if add_generation_prompt %}\n {{- '<|im_start|>assistant\\n' }}\n{%- endif %}"
|
||||
},
|
||||
{
|
||||
"order": "e",
|
||||
@@ -98,7 +81,7 @@
|
||||
"description": "<strong>Strong overall fast instruction following model</strong><br><ul><li>Fast responses</li><li>Trained by Mistral AI<li>Uncensored</li><li>Licensed for commercial use</li></ul>",
|
||||
"url": "https://gpt4all.io/models/gguf/mistral-7b-instruct-v0.1.Q4_0.gguf",
|
||||
"promptTemplate": "[INST] %1 [/INST]",
|
||||
"chatTemplate": "{%- if messages[0]['role'] == 'system' %}\n {%- set system_message = messages[0]['content'] %}\n {%- set loop_start = 1 %}\n{%- else %}\n {%- set loop_start = 0 %}\n{%- endif %}\n{%- for message in messages %}\n {%- if loop.index0 >= loop_start %}\n {%- if (message['role'] == 'user') != ((loop.index0 - loop_start) % 2 == 0) %}\n {{- raise_exception('After the optional system message, conversation roles must alternate user/assistant/user/assistant/...') }}\n {%- endif %}\n {%- if message['role'] == 'user' %}\n {%- if loop.index0 == loop_start and loop_start == 1 %}\n {{- ' [INST] ' + system_message + '\\n\\n' + message['content'] + ' [/INST]' }}\n {%- else %}\n {{- ' [INST] ' + message['content'] + ' [/INST]' }}\n {%- endif %}\n {%- elif message['role'] == 'assistant' %}\n {{- ' ' + message['content'] + eos_token }}\n {%- else %}\n {{- raise_exception('Only user and assistant roles are supported, with the exception of an initial optional system message!') }}\n {%- endif %}\n {%- endif %}\n{%- endfor %}"
|
||||
"chatTemplate": "{%- if messages[0]['role'] == 'system' %}\n {%- set system_message = messages[0]['content'] %}\n {%- set loop_start = 1 %}\n{%- else %}\n {%- set loop_start = 0 %}\n{%- endif %}\n{%- for message in messages %}\n {%- if loop.index0 >= loop_start %}\n {%- if (message['role'] == 'user') != ((loop.index0 - loop_start) % 2 == 0) %}\n {{- raise_exception('After the optional system message, conversation roles must alternate user/assistant/user/assistant/...') }}\n {%- endif %}\n {%- if message['role'] == 'user' %}\n {%- if loop.first and system_message is defined %}\n {{- ' [INST] ' + system_message + '\\n\\n' + message['content'] + ' [/INST]' }}\n {%- else %}\n {{- ' [INST] ' + message['content'] + ' [/INST]' }}\n {%- endif %}\n {%- elif message['role'] == 'assistant' %}\n {{- ' ' + message['content'] + eos_token }}\n {%- else %}\n {{- raise_exception('Only user and assistant roles are supported, with the exception of an initial optional system message!') }}\n {%- endif %}\n {%- endif %}\n{%- endfor %}"
|
||||
},
|
||||
{
|
||||
"order": "f",
|
||||
@@ -114,8 +97,7 @@
|
||||
"description": "<ul><li><strong>For advanced users only. Not recommended for use on Windows or Linux without selecting CUDA due to speed issues.</strong></li><li>Fast responses</li><li>Chat based model</li><li>Large context size of 128k</li><li>Accepts agentic system prompts in Llama 3.1 format</li><li>Trained by Meta</li><li>License: <a href=\"https://llama.meta.com/llama3_1/license/\">Meta Llama 3.1 Community License</a></li></ul>",
|
||||
"url": "https://huggingface.co/GPT4All-Community/Meta-Llama-3.1-8B-Instruct-128k/resolve/main/Meta-Llama-3.1-8B-Instruct-128k-Q4_0.gguf",
|
||||
"promptTemplate": "<|start_header_id|>user<|end_header_id|>\n\n%1<|eot_id|><|start_header_id|>assistant<|end_header_id|>\n\n%2",
|
||||
"systemPrompt": "<|start_header_id|>system<|end_header_id|>\nCutting Knowledge Date: December 2023\n\nYou are a helpful assistant.<|eot_id|>",
|
||||
"chatTemplate": "{%- set loop_messages = messages %}\n{%- for message in loop_messages %}\n {%- set content = '<|start_header_id|>' + message['role'] + '<|end_header_id|>\\n\\n'+ message['content'] | trim + '<|eot_id|>' %}\n {%- if loop.index0 == 0 %}\n {%- set content = bos_token + content %}\n {%- endif %}\n {{- content }}\n{%- endfor %}\n{{- '<|start_header_id|>assistant<|end_header_id|>\\n\\n' }}"
|
||||
"systemPrompt": "<|start_header_id|>system<|end_header_id|>\nCutting Knowledge Date: December 2023\n\nYou are a helpful assistant.<|eot_id|>"
|
||||
},
|
||||
{
|
||||
"order": "g",
|
||||
@@ -132,7 +114,7 @@
|
||||
"url": "https://gpt4all.io/models/gguf/mistral-7b-openorca.gguf2.Q4_0.gguf",
|
||||
"promptTemplate": "<|im_start|>user\n%1<|im_end|>\n<|im_start|>assistant\n%2<|im_end|>\n",
|
||||
"systemPrompt": "<|im_start|>system\nYou are MistralOrca, a large language model trained by Alignment Lab AI.\n<|im_end|>\n",
|
||||
"chatTemplate": "{%- for message in messages %}\n {{- '<|im_start|>' + message['role'] + '\\n' + message['content'] + '<|im_end|>\\n' }}\n{%- endfor %}\n{%- if add_generation_prompt %}\n {{- '<|im_start|>assistant\\n' }}\n{%- endif %}"
|
||||
"chatTemplate": "{%- if not add_generation_prompt is defined %}\n {%- set add_generation_prompt = false %}\n{%- endif %}\n{%- for message in messages %}\n {{- '<|im_start|>' + message['role'] + '\\n' + message['content'] + '<|im_end|>' + '\\n' }}\n{%- endfor %}\n{%- if add_generation_prompt %}\n {{- '<|im_start|>assistant\\n' }}\n{%- endif %}"
|
||||
},
|
||||
{
|
||||
"order": "h",
|
||||
@@ -215,7 +197,6 @@
|
||||
"url": "https://huggingface.co/lamhieu/ghost-7b-v0.9.1-gguf/resolve/main/ghost-7b-v0.9.1-Q4_0.gguf",
|
||||
"promptTemplate": "<|user|>\n%1</s>\n<|assistant|>\n%2</s>\n",
|
||||
"systemPrompt": "<|system|>\nYou are Ghost created by Lam Hieu. You are a helpful and knowledgeable assistant. You like to help and always give honest information, in its original language. In communication, you are always respectful, equal and promote positive behavior.\n</s>",
|
||||
"chatTemplate": "{%- for message in messages %}\n {%- if message['role'] == 'user' %}\n {{- '<|user|>\\n' + message['content'] + eos_token }}\n {%- elif message['role'] == 'system' %}\n {{- '<|system|>\\n' + message['content'] + eos_token }}\n {%- elif message['role'] == 'assistant' %}\n {{- '<|assistant|>\\n' + message['content'] + eos_token }}\n {%- endif %}\n {%- if loop.last and add_generation_prompt %}\n {{- '<|assistant|>' }}\n {%- endif %}\n{%- endfor %}",
|
||||
"systemMessage": "You are Ghost created by Lam Hieu. You are a helpful and knowledgeable assistant. You like to help and always give honest information, in its original language. In communication, you are always respectful, equal and promote positive behavior."
|
||||
},
|
||||
{
|
||||
@@ -301,8 +282,7 @@
|
||||
"description": "<ul><li>Very fast responses</li><li>Chat based model</li><li>Accepts system prompts in Phi-3 format</li><li>Trained by Microsoft</li><li>License: <a href=\"https://opensource.org/license/mit\">MIT</a></li><li>No restrictions on commercial use</li></ul>",
|
||||
"url": "https://gpt4all.io/models/gguf/Phi-3-mini-4k-instruct.Q4_0.gguf",
|
||||
"promptTemplate": "<|user|>\n%1<|end|>\n<|assistant|>\n%2<|end|>\n",
|
||||
"systemPrompt": "",
|
||||
"chatTemplate": "{{- bos_token }}\n{%- for message in messages %}\n {{- '<|' + message['role'] + '|>\\n' + message['content'] + '<|end|>\\n' }}\n{%- endfor %}\n{%- if add_generation_prompt %}\n {{- '<|assistant|>\\n' }}\n{%- else %}\n {{- eos_token }}\n{%- endif %}"
|
||||
"systemPrompt": ""
|
||||
},
|
||||
{
|
||||
"order": "r",
|
||||
@@ -426,7 +406,7 @@
|
||||
"url": "https://huggingface.co/TheBloke/em_german_mistral_v01-GGUF/resolve/main/em_german_mistral_v01.Q4_0.gguf",
|
||||
"promptTemplate": "USER: %1 ASSISTANT: ",
|
||||
"systemPrompt": "Du bist ein hilfreicher Assistent. ",
|
||||
"chatTemplate": "{%- if messages[0]['role'] == 'system' %}\n {%- set loop_start = 1 %}\n {{- messages[0]['content'] }}\n{%- else %}\n {%- set loop_start = 0 %}\n{%- endif %}\n{%- for message in messages %}\n {%- if loop.index0 >= loop_start %}\n {%- if not loop.first %}\n {{- ' ' }}\n {%- endif %}\n {%- if message['role'] == 'user' %}\n {{- 'USER: ' + message['content'] }}\n {%- elif message['role'] == 'assistant' %}\n {{- 'ASSISTANT: ' + message['content'] }}\n {%- else %}\n {{- raise_exception('After the optional system message, conversation roles must be either user or assistant.') }}\n {%- endif %}\n {%- endif %}\n{%- endfor %}\n{%- if add_generation_prompt %}\n {%- if messages %}\n {{- ' ' }}\n {%- endif %}\n {{- 'ASSISTANT:' }}\n{%- endif %}",
|
||||
"chatTemplate": "{%- set system_message = false %}\n{%- if messages[0]['role'] == 'system' %}\n {%- set loop_start = 1 %}\n {%- set system_message = true %}\n {{- messages[0]['content'] }}\n{%- else %}\n {%- set loop_start = 0 %}\n{%- endif %}\n{%- for message in messages %}\n {%- if loop.index0 >= loop_start %}\n {%- if (not loop.first) or (system_message is not none) %}\n {{- ' ' }}\n {%- endif %}\n {%- if message['role'] == 'user' %}\n {{- 'USER: ' + message['content'] }}\n {%- elif message['role'] == 'assistant' %}\n {{- 'ASSISTANT: ' + message['content'] }}\n {%- else %}\n {{- raise_exception('After the optional system message, conversation roles must be either user or assistant.') }}\n {%- endif %}\n {%- endif %}\n{%- endfor %}\n{%- if add_generation_prompt %}\n {%- if messages %}\n {{- ' ' }}\n {%- endif %}\n {{- 'ASSISTANT:' }}\n{%- endif %}",
|
||||
"systemMessage": "Du bist ein hilfreicher Assistent."
|
||||
},
|
||||
{
|
||||
@@ -480,6 +460,6 @@
|
||||
"url": "https://huggingface.co/Qwen/Qwen2-1.5B-Instruct-GGUF/resolve/main/qwen2-1_5b-instruct-q4_0.gguf",
|
||||
"promptTemplate": "<|im_start|>user\n%1<|im_end|>\n<|im_start|>assistant\n%2<|im_end|>",
|
||||
"systemPrompt": "<|im_start|>system\nBelow is an instruction that describes a task. Write a response that appropriately completes the request.<|im_end|>\n",
|
||||
"chatTemplate": "{%- for message in messages %}\n {%- if loop.first and messages[0]['role'] != 'system' %}\n {{- '<|im_start|>system\\nYou are a helpful assistant.<|im_end|>\\n' }}\n {%- endif %}\n {{- '<|im_start|>' + message['role'] + '\\n' + message['content'] + '<|im_end|>\\n' }}\n{%- endfor %}\n{%- if add_generation_prompt %}\n {{- '<|im_start|>assistant\\n' }}\n{%- endif %}"
|
||||
"chatTemplate": "{%- for message in messages %}\n {%- if loop.first and messages[0]['role'] != 'system' %}\n {{- '<|im_start|>system\\nYou are a helpful assistant.<|im_end|>\\n' }}\n {%- endif %}\n {{- '<|im_start|>' + message['role'] + '\\n' + message['content'] + '<|im_end|>' + '\\n' }}\n{%- endfor %}\n{%- if add_generation_prompt %}\n {{- '<|im_start|>assistant\\n' }}\n{%- endif %}"
|
||||
}
|
||||
]
|
||||
|
||||
@@ -243,20 +243,5 @@
|
||||
"version": "3.5.2",
|
||||
"notes": "* **Model Search:** There are now separate tabs for official and third-party models.\n* **Local Server Fixes:** Several mistakes in v3.5's changes to the API server have been corrected.\n* **Cloned Model Fixes:** The chat template and system message of cloned models now manage their defaults correctly.\n* **Translation Improvements:** The Romanian and Italian translations have been updated.\n",
|
||||
"contributors": "* Jared Van Bortel (Nomic AI)\n* Adam Treat (Nomic AI)\n* Riccardo Giovanetti (`@Harvester62`)\n* Victor Emanuel (`@SINAPSA-IC`)"
|
||||
},
|
||||
{
|
||||
"version": "3.5.3",
|
||||
"notes": "* **LocalDocs Fix:** A serious issue causing LocalDocs to not work properly in v3.5.2 has been fixed.\n",
|
||||
"contributors": "* Jared Van Bortel (Nomic AI)\n* Adam Treat (Nomic AI)"
|
||||
},
|
||||
{
|
||||
"version": "3.6.0",
|
||||
"notes": "* **Reasoner v1:**\n * Built-in javascript code interpreter tool.\n * Custom curated model that utilizes the code interpreter to break down, analyze, perform, and verify complex reasoning tasks.\n* **Templates:** Automatically substitute chat templates that are not compatible with Jinja2Cpp in GGUFs.\n* **Fixes:**\n * Remote model template to allow for XML in messages.\n * Jinja2Cpp bug that broke system message detection in chat templates.\n * LocalDocs sources displaying in unconsolidated form after v3.5.0.",
|
||||
"contributors": "* Adam Treat (Nomic AI)\n* Jared Van Bortel (Nomic AI)"
|
||||
},
|
||||
{
|
||||
"version": "3.6.1",
|
||||
"notes": "* **Fixes:**\n * The stop generation button no longer working in v3.6.0.\n * The copy entire conversation button no longer working in v3.6.0.",
|
||||
"contributors": "* Adam Treat (Nomic AI)"
|
||||
}
|
||||
]
|
||||
|
||||
@@ -56,52 +56,6 @@ ColumnLayout {
|
||||
Accessible.description: qsTr("Displayed when the models request is ongoing")
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
ButtonGroup {
|
||||
id: buttonGroup
|
||||
exclusive: true
|
||||
}
|
||||
MyButton {
|
||||
text: qsTr("All")
|
||||
checked: true
|
||||
borderWidth: 0
|
||||
backgroundColor: checked ? theme.lightButtonBackground : "transparent"
|
||||
backgroundColorHovered: theme.lighterButtonBackgroundHovered
|
||||
backgroundRadius: 5
|
||||
padding: 15
|
||||
topPadding: 8
|
||||
bottomPadding: 8
|
||||
textColor: theme.lighterButtonForeground
|
||||
fontPixelSize: theme.fontSizeLarge
|
||||
fontPixelBold: true
|
||||
checkable: true
|
||||
ButtonGroup.group: buttonGroup
|
||||
onClicked: {
|
||||
ModelList.gpt4AllDownloadableModels.filter("");
|
||||
}
|
||||
|
||||
}
|
||||
MyButton {
|
||||
text: qsTr("Reasoning")
|
||||
borderWidth: 0
|
||||
backgroundColor: checked ? theme.lightButtonBackground : "transparent"
|
||||
backgroundColorHovered: theme.lighterButtonBackgroundHovered
|
||||
backgroundRadius: 5
|
||||
padding: 15
|
||||
topPadding: 8
|
||||
bottomPadding: 8
|
||||
textColor: theme.lighterButtonForeground
|
||||
fontPixelSize: theme.fontSizeLarge
|
||||
fontPixelBold: true
|
||||
checkable: true
|
||||
ButtonGroup.group: buttonGroup
|
||||
onClicked: {
|
||||
ModelList.gpt4AllDownloadableModels.filter("#reasoning");
|
||||
}
|
||||
}
|
||||
Layout.bottomMargin: 10
|
||||
}
|
||||
|
||||
ScrollView {
|
||||
id: scrollView
|
||||
ScrollBar.vertical.policy: ScrollBar.AsNeeded
|
||||
|
||||
@@ -1,160 +0,0 @@
|
||||
import Qt5Compat.GraphicalEffects
|
||||
import QtCore
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Controls.Basic
|
||||
import QtQuick.Layouts
|
||||
|
||||
import gpt4all
|
||||
import mysettings
|
||||
import toolenums
|
||||
|
||||
ColumnLayout {
|
||||
property alias textContent: innerTextItem.textContent
|
||||
property bool isCurrent: false
|
||||
property bool isError: false
|
||||
|
||||
Layout.topMargin: 10
|
||||
Layout.bottomMargin: 10
|
||||
|
||||
Item {
|
||||
Layout.preferredWidth: childrenRect.width
|
||||
Layout.preferredHeight: 38
|
||||
RowLayout {
|
||||
anchors.left: parent.left
|
||||
anchors.top: parent.top
|
||||
anchors.bottom: parent.bottom
|
||||
|
||||
Item {
|
||||
width: myTextArea.width
|
||||
height: myTextArea.height
|
||||
TextArea {
|
||||
id: myTextArea
|
||||
text: {
|
||||
if (isError)
|
||||
return qsTr("Analysis encountered error");
|
||||
if (isCurrent)
|
||||
return qsTr("Analyzing");
|
||||
return qsTr("Analyzed");
|
||||
}
|
||||
padding: 0
|
||||
font.pixelSize: theme.fontSizeLarger
|
||||
enabled: false
|
||||
focus: false
|
||||
readOnly: true
|
||||
color: headerMA.containsMouse ? theme.mutedDarkTextColorHovered : theme.mutedTextColor
|
||||
hoverEnabled: false
|
||||
}
|
||||
|
||||
Item {
|
||||
id: textColorOverlay
|
||||
anchors.fill: parent
|
||||
clip: true
|
||||
visible: false
|
||||
Rectangle {
|
||||
id: animationRec
|
||||
width: myTextArea.width * 0.3
|
||||
anchors.top: parent.top
|
||||
anchors.bottom: parent.bottom
|
||||
color: theme.textColor
|
||||
|
||||
SequentialAnimation {
|
||||
running: isCurrent
|
||||
loops: Animation.Infinite
|
||||
NumberAnimation {
|
||||
target: animationRec;
|
||||
property: "x";
|
||||
from: -animationRec.width;
|
||||
to: myTextArea.width * 3;
|
||||
duration: 2000
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
OpacityMask {
|
||||
visible: isCurrent
|
||||
anchors.fill: parent
|
||||
maskSource: myTextArea
|
||||
source: textColorOverlay
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
id: caret
|
||||
Layout.preferredWidth: contentCaret.width
|
||||
Layout.preferredHeight: contentCaret.height
|
||||
Image {
|
||||
id: contentCaret
|
||||
anchors.centerIn: parent
|
||||
visible: false
|
||||
sourceSize.width: theme.fontSizeLarge
|
||||
sourceSize.height: theme.fontSizeLarge
|
||||
mipmap: true
|
||||
source: {
|
||||
if (contentLayout.state === "collapsed")
|
||||
return "qrc:/gpt4all/icons/caret_right.svg";
|
||||
else
|
||||
return "qrc:/gpt4all/icons/caret_down.svg";
|
||||
}
|
||||
}
|
||||
|
||||
ColorOverlay {
|
||||
anchors.fill: contentCaret
|
||||
source: contentCaret
|
||||
color: headerMA.containsMouse ? theme.mutedDarkTextColorHovered : theme.mutedTextColor
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: headerMA
|
||||
hoverEnabled: true
|
||||
anchors.fill: parent
|
||||
onClicked: {
|
||||
if (contentLayout.state === "collapsed")
|
||||
contentLayout.state = "expanded";
|
||||
else
|
||||
contentLayout.state = "collapsed";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
id: contentLayout
|
||||
spacing: 0
|
||||
state: "collapsed"
|
||||
clip: true
|
||||
|
||||
states: [
|
||||
State {
|
||||
name: "expanded"
|
||||
PropertyChanges { target: contentLayout; Layout.preferredHeight: innerContentLayout.height }
|
||||
},
|
||||
State {
|
||||
name: "collapsed"
|
||||
PropertyChanges { target: contentLayout; Layout.preferredHeight: 0 }
|
||||
}
|
||||
]
|
||||
|
||||
transitions: [
|
||||
Transition {
|
||||
SequentialAnimation {
|
||||
PropertyAnimation {
|
||||
target: contentLayout
|
||||
property: "Layout.preferredHeight"
|
||||
duration: 300
|
||||
easing.type: Easing.InOutQuad
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
ColumnLayout {
|
||||
id: innerContentLayout
|
||||
Layout.leftMargin: 30
|
||||
ChatTextItem {
|
||||
id: innerTextItem
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,11 +4,9 @@ import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Controls.Basic
|
||||
import QtQuick.Layouts
|
||||
import Qt.labs.qmlmodels
|
||||
|
||||
import gpt4all
|
||||
import mysettings
|
||||
import toolenums
|
||||
|
||||
ColumnLayout {
|
||||
|
||||
@@ -35,8 +33,6 @@ GridLayout {
|
||||
Layout.alignment: Qt.AlignVCenter | Qt.AlignRight
|
||||
Layout.preferredWidth: 32
|
||||
Layout.preferredHeight: 32
|
||||
Layout.topMargin: model.index > 0 ? 25 : 0
|
||||
|
||||
Image {
|
||||
id: logo
|
||||
sourceSize: Qt.size(32, 32)
|
||||
@@ -69,8 +65,6 @@ GridLayout {
|
||||
Layout.column: 1
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: 38
|
||||
Layout.topMargin: model.index > 0 ? 25 : 0
|
||||
|
||||
RowLayout {
|
||||
spacing: 5
|
||||
anchors.left: parent.left
|
||||
@@ -78,11 +72,7 @@ GridLayout {
|
||||
anchors.bottom: parent.bottom
|
||||
|
||||
TextArea {
|
||||
text: {
|
||||
if (name === "Response: ")
|
||||
return qsTr("GPT4All");
|
||||
return qsTr("You");
|
||||
}
|
||||
text: name === "Response: " ? qsTr("GPT4All") : qsTr("You")
|
||||
padding: 0
|
||||
font.pixelSize: theme.fontSizeLarger
|
||||
font.bold: true
|
||||
@@ -98,7 +88,7 @@ GridLayout {
|
||||
color: theme.mutedTextColor
|
||||
}
|
||||
RowLayout {
|
||||
visible: isCurrentResponse && (content === "" && currentChat.responseInProgress)
|
||||
visible: isCurrentResponse && (value === "" && currentChat.responseInProgress)
|
||||
Text {
|
||||
color: theme.mutedTextColor
|
||||
font.pixelSize: theme.fontSizeLarger
|
||||
@@ -166,36 +156,131 @@ GridLayout {
|
||||
}
|
||||
}
|
||||
|
||||
Repeater {
|
||||
model: childItems
|
||||
TextArea {
|
||||
id: myTextArea
|
||||
Layout.fillWidth: true
|
||||
padding: 0
|
||||
color: {
|
||||
if (!currentChat.isServer)
|
||||
return theme.textColor
|
||||
return theme.white
|
||||
}
|
||||
wrapMode: Text.WordWrap
|
||||
textFormat: TextEdit.PlainText
|
||||
focus: false
|
||||
readOnly: true
|
||||
font.pixelSize: theme.fontSizeLarge
|
||||
cursorVisible: isCurrentResponse ? currentChat.responseInProgress : false
|
||||
cursorPosition: text.length
|
||||
TapHandler {
|
||||
id: tapHandler
|
||||
onTapped: function(eventPoint, button) {
|
||||
var clickedPos = myTextArea.positionAt(eventPoint.position.x, eventPoint.position.y);
|
||||
var success = textProcessor.tryCopyAtPosition(clickedPos);
|
||||
if (success)
|
||||
copyCodeMessage.open();
|
||||
}
|
||||
}
|
||||
|
||||
DelegateChooser {
|
||||
id: chooser
|
||||
role: "name"
|
||||
DelegateChoice {
|
||||
roleValue: "Text: ";
|
||||
ChatTextItem {
|
||||
Layout.fillWidth: true
|
||||
textContent: modelData.content
|
||||
MouseArea {
|
||||
id: conversationMouseArea
|
||||
anchors.fill: parent
|
||||
acceptedButtons: Qt.RightButton
|
||||
|
||||
onClicked: (mouse) => {
|
||||
if (mouse.button === Qt.RightButton) {
|
||||
conversationContextMenu.x = conversationMouseArea.mouseX
|
||||
conversationContextMenu.y = conversationMouseArea.mouseY
|
||||
conversationContextMenu.open()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onLinkActivated: function(link) {
|
||||
if (!isCurrentResponse || !currentChat.responseInProgress)
|
||||
Qt.openUrlExternally(link)
|
||||
}
|
||||
|
||||
onLinkHovered: function (link) {
|
||||
if (!isCurrentResponse || !currentChat.responseInProgress)
|
||||
statusBar.externalHoveredLink = link
|
||||
}
|
||||
|
||||
MyMenu {
|
||||
id: conversationContextMenu
|
||||
MyMenuItem {
|
||||
text: qsTr("Copy")
|
||||
enabled: myTextArea.selectedText !== ""
|
||||
height: enabled ? implicitHeight : 0
|
||||
onTriggered: myTextArea.copy()
|
||||
}
|
||||
MyMenuItem {
|
||||
text: qsTr("Copy Message")
|
||||
enabled: myTextArea.selectedText === ""
|
||||
height: enabled ? implicitHeight : 0
|
||||
onTriggered: {
|
||||
myTextArea.selectAll()
|
||||
myTextArea.copy()
|
||||
myTextArea.deselect()
|
||||
}
|
||||
}
|
||||
DelegateChoice {
|
||||
roleValue: "ToolCall: ";
|
||||
ChatCollapsibleItem {
|
||||
Layout.fillWidth: true
|
||||
textContent: modelData.content
|
||||
isCurrent: modelData.isCurrentResponse
|
||||
isError: modelData.isToolCallError
|
||||
MyMenuItem {
|
||||
text: textProcessor.shouldProcessText ? qsTr("Disable markdown") : qsTr("Enable markdown")
|
||||
height: enabled ? implicitHeight : 0
|
||||
onTriggered: {
|
||||
textProcessor.shouldProcessText = !textProcessor.shouldProcessText;
|
||||
textProcessor.setValue(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
delegate: chooser
|
||||
}
|
||||
ChatViewTextProcessor {
|
||||
id: textProcessor
|
||||
}
|
||||
|
||||
ChatTextItem {
|
||||
Layout.fillWidth: true
|
||||
textContent: content
|
||||
function resetChatViewTextProcessor() {
|
||||
textProcessor.fontPixelSize = myTextArea.font.pixelSize
|
||||
textProcessor.codeColors.defaultColor = theme.codeDefaultColor
|
||||
textProcessor.codeColors.keywordColor = theme.codeKeywordColor
|
||||
textProcessor.codeColors.functionColor = theme.codeFunctionColor
|
||||
textProcessor.codeColors.functionCallColor = theme.codeFunctionCallColor
|
||||
textProcessor.codeColors.commentColor = theme.codeCommentColor
|
||||
textProcessor.codeColors.stringColor = theme.codeStringColor
|
||||
textProcessor.codeColors.numberColor = theme.codeNumberColor
|
||||
textProcessor.codeColors.headerColor = theme.codeHeaderColor
|
||||
textProcessor.codeColors.backgroundColor = theme.codeBackgroundColor
|
||||
textProcessor.textDocument = textDocument
|
||||
textProcessor.setValue(value);
|
||||
}
|
||||
|
||||
property bool textProcessorReady: false
|
||||
|
||||
Component.onCompleted: {
|
||||
resetChatViewTextProcessor();
|
||||
textProcessorReady = true;
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: chatModel
|
||||
function onValueChanged(i, value) {
|
||||
if (myTextArea.textProcessorReady && index === i)
|
||||
textProcessor.setValue(value);
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: MySettings
|
||||
function onFontSizeChanged() {
|
||||
myTextArea.resetChatViewTextProcessor();
|
||||
}
|
||||
function onChatThemeChanged() {
|
||||
myTextArea.resetChatViewTextProcessor();
|
||||
}
|
||||
}
|
||||
|
||||
Accessible.role: Accessible.Paragraph
|
||||
Accessible.name: text
|
||||
Accessible.description: name === "Response: " ? "The response by the model" : "The prompt by the user"
|
||||
}
|
||||
|
||||
ThumbsDownDialog {
|
||||
@@ -204,16 +289,16 @@ GridLayout {
|
||||
y: Math.round((parent.height - height) / 2)
|
||||
width: 640
|
||||
height: 300
|
||||
property string text: content
|
||||
property string text: value
|
||||
response: newResponse === undefined || newResponse === "" ? text : newResponse
|
||||
onAccepted: {
|
||||
var responseHasChanged = response !== text && response !== newResponse
|
||||
if (thumbsDownState && !thumbsUpState && !responseHasChanged)
|
||||
return
|
||||
|
||||
chatModel.updateNewResponse(model.index, response)
|
||||
chatModel.updateThumbsUpState(model.index, false)
|
||||
chatModel.updateThumbsDownState(model.index, true)
|
||||
chatModel.updateNewResponse(index, response)
|
||||
chatModel.updateThumbsUpState(index, false)
|
||||
chatModel.updateThumbsDownState(index, true)
|
||||
Network.sendConversation(currentChat.id, getConversationJson());
|
||||
}
|
||||
}
|
||||
@@ -331,7 +416,7 @@ GridLayout {
|
||||
states: [
|
||||
State {
|
||||
name: "expanded"
|
||||
PropertyChanges { target: sourcesLayout; Layout.preferredHeight: sourcesFlow.height }
|
||||
PropertyChanges { target: sourcesLayout; Layout.preferredHeight: flow.height }
|
||||
},
|
||||
State {
|
||||
name: "collapsed"
|
||||
@@ -353,7 +438,7 @@ GridLayout {
|
||||
]
|
||||
|
||||
Flow {
|
||||
id: sourcesFlow
|
||||
id: flow
|
||||
Layout.fillWidth: true
|
||||
spacing: 10
|
||||
visible: consolidatedSources.length !== 0
|
||||
@@ -532,7 +617,9 @@ GridLayout {
|
||||
name: qsTr("Copy")
|
||||
source: "qrc:/gpt4all/icons/copy.svg"
|
||||
onClicked: {
|
||||
chatModel.copyToClipboard(index);
|
||||
myTextArea.selectAll();
|
||||
myTextArea.copy();
|
||||
myTextArea.deselect();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,139 +0,0 @@
|
||||
import Qt5Compat.GraphicalEffects
|
||||
import QtCore
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Controls.Basic
|
||||
import QtQuick.Layouts
|
||||
|
||||
import gpt4all
|
||||
import mysettings
|
||||
import toolenums
|
||||
|
||||
TextArea {
|
||||
id: myTextArea
|
||||
property string textContent: ""
|
||||
visible: textContent != ""
|
||||
Layout.fillWidth: true
|
||||
padding: 0
|
||||
color: {
|
||||
if (!currentChat.isServer)
|
||||
return theme.textColor
|
||||
return theme.white
|
||||
}
|
||||
wrapMode: Text.WordWrap
|
||||
textFormat: TextEdit.PlainText
|
||||
focus: false
|
||||
readOnly: true
|
||||
font.pixelSize: theme.fontSizeLarge
|
||||
cursorVisible: isCurrentResponse ? currentChat.responseInProgress : false
|
||||
cursorPosition: text.length
|
||||
TapHandler {
|
||||
id: tapHandler
|
||||
onTapped: function(eventPoint, button) {
|
||||
var clickedPos = myTextArea.positionAt(eventPoint.position.x, eventPoint.position.y);
|
||||
var success = textProcessor.tryCopyAtPosition(clickedPos);
|
||||
if (success)
|
||||
copyCodeMessage.open();
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: conversationMouseArea
|
||||
anchors.fill: parent
|
||||
acceptedButtons: Qt.RightButton
|
||||
|
||||
onClicked: (mouse) => {
|
||||
if (mouse.button === Qt.RightButton) {
|
||||
conversationContextMenu.x = conversationMouseArea.mouseX
|
||||
conversationContextMenu.y = conversationMouseArea.mouseY
|
||||
conversationContextMenu.open()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onLinkActivated: function(link) {
|
||||
if (!isCurrentResponse || !currentChat.responseInProgress)
|
||||
Qt.openUrlExternally(link)
|
||||
}
|
||||
|
||||
onLinkHovered: function (link) {
|
||||
if (!isCurrentResponse || !currentChat.responseInProgress)
|
||||
statusBar.externalHoveredLink = link
|
||||
}
|
||||
|
||||
MyMenu {
|
||||
id: conversationContextMenu
|
||||
MyMenuItem {
|
||||
text: qsTr("Copy")
|
||||
enabled: myTextArea.selectedText !== ""
|
||||
height: enabled ? implicitHeight : 0
|
||||
onTriggered: myTextArea.copy()
|
||||
}
|
||||
MyMenuItem {
|
||||
text: qsTr("Copy Message")
|
||||
enabled: myTextArea.selectedText === ""
|
||||
height: enabled ? implicitHeight : 0
|
||||
onTriggered: {
|
||||
myTextArea.selectAll()
|
||||
myTextArea.copy()
|
||||
myTextArea.deselect()
|
||||
}
|
||||
}
|
||||
MyMenuItem {
|
||||
text: textProcessor.shouldProcessText ? qsTr("Disable markdown") : qsTr("Enable markdown")
|
||||
height: enabled ? implicitHeight : 0
|
||||
onTriggered: {
|
||||
textProcessor.shouldProcessText = !textProcessor.shouldProcessText;
|
||||
textProcessor.setValue(textContent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ChatViewTextProcessor {
|
||||
id: textProcessor
|
||||
}
|
||||
|
||||
function resetChatViewTextProcessor() {
|
||||
textProcessor.fontPixelSize = myTextArea.font.pixelSize
|
||||
textProcessor.codeColors.defaultColor = theme.codeDefaultColor
|
||||
textProcessor.codeColors.keywordColor = theme.codeKeywordColor
|
||||
textProcessor.codeColors.functionColor = theme.codeFunctionColor
|
||||
textProcessor.codeColors.functionCallColor = theme.codeFunctionCallColor
|
||||
textProcessor.codeColors.commentColor = theme.codeCommentColor
|
||||
textProcessor.codeColors.stringColor = theme.codeStringColor
|
||||
textProcessor.codeColors.numberColor = theme.codeNumberColor
|
||||
textProcessor.codeColors.headerColor = theme.codeHeaderColor
|
||||
textProcessor.codeColors.backgroundColor = theme.codeBackgroundColor
|
||||
textProcessor.textDocument = textDocument
|
||||
textProcessor.setValue(textContent);
|
||||
}
|
||||
|
||||
property bool textProcessorReady: false
|
||||
|
||||
Component.onCompleted: {
|
||||
resetChatViewTextProcessor();
|
||||
textProcessorReady = true;
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: myTextArea
|
||||
function onTextContentChanged() {
|
||||
if (myTextArea.textProcessorReady)
|
||||
textProcessor.setValue(textContent);
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: MySettings
|
||||
function onFontSizeChanged() {
|
||||
myTextArea.resetChatViewTextProcessor();
|
||||
}
|
||||
function onChatThemeChanged() {
|
||||
myTextArea.resetChatViewTextProcessor();
|
||||
}
|
||||
}
|
||||
|
||||
Accessible.role: Accessible.Paragraph
|
||||
Accessible.name: text
|
||||
Accessible.description: name === "Response: " ? "The response by the model" : "The prompt by the user"
|
||||
}
|
||||
@@ -37,11 +37,10 @@ Rectangle {
|
||||
|
||||
Connections {
|
||||
target: currentChat
|
||||
// FIXME: https://github.com/nomic-ai/gpt4all/issues/3334
|
||||
// function onResponseInProgressChanged() {
|
||||
// if (MySettings.networkIsActive && !currentChat.responseInProgress)
|
||||
// Network.sendConversation(currentChat.id, getConversationJson());
|
||||
// }
|
||||
function onResponseInProgressChanged() {
|
||||
if (MySettings.networkIsActive && !currentChat.responseInProgress)
|
||||
Network.sendConversation(currentChat.id, getConversationJson());
|
||||
}
|
||||
function onModelLoadingErrorChanged() {
|
||||
if (currentChat.modelLoadingError !== "")
|
||||
modelLoadingErrorPopup.open()
|
||||
@@ -117,44 +116,42 @@ Rectangle {
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: https://github.com/nomic-ai/gpt4all/issues/3334
|
||||
// function getConversation() {
|
||||
// var conversation = "";
|
||||
// for (var i = 0; i < chatModel.count; i++) {
|
||||
// var item = chatModel.get(i)
|
||||
// var string = item.name;
|
||||
// var isResponse = item.name === "Response: "
|
||||
// string += chatModel.get(i).value
|
||||
// if (isResponse && item.stopped)
|
||||
// string += " <stopped>"
|
||||
// string += "\n"
|
||||
// conversation += string
|
||||
// }
|
||||
// return conversation
|
||||
// }
|
||||
function getConversation() {
|
||||
var conversation = "";
|
||||
for (var i = 0; i < chatModel.count; i++) {
|
||||
var item = chatModel.get(i)
|
||||
var string = item.name;
|
||||
var isResponse = item.name === "Response: "
|
||||
string += chatModel.get(i).value
|
||||
if (isResponse && item.stopped)
|
||||
string += " <stopped>"
|
||||
string += "\n"
|
||||
conversation += string
|
||||
}
|
||||
return conversation
|
||||
}
|
||||
|
||||
// FIXME: https://github.com/nomic-ai/gpt4all/issues/3334
|
||||
// function getConversationJson() {
|
||||
// var str = "{\"conversation\": [";
|
||||
// for (var i = 0; i < chatModel.count; i++) {
|
||||
// var item = chatModel.get(i)
|
||||
// var isResponse = item.name === "Response: "
|
||||
// str += "{\"content\": ";
|
||||
// str += JSON.stringify(item.value)
|
||||
// str += ", \"role\": \"" + (isResponse ? "assistant" : "user") + "\"";
|
||||
// if (isResponse && item.thumbsUpState !== item.thumbsDownState)
|
||||
// str += ", \"rating\": \"" + (item.thumbsUpState ? "positive" : "negative") + "\"";
|
||||
// if (isResponse && item.newResponse !== "")
|
||||
// str += ", \"edited_content\": " + JSON.stringify(item.newResponse);
|
||||
// if (isResponse && item.stopped)
|
||||
// str += ", \"stopped\": \"true\""
|
||||
// if (!isResponse)
|
||||
// str += "},"
|
||||
// else
|
||||
// str += ((i < chatModel.count - 1) ? "}," : "}")
|
||||
// }
|
||||
// return str + "]}"
|
||||
// }
|
||||
function getConversationJson() {
|
||||
var str = "{\"conversation\": [";
|
||||
for (var i = 0; i < chatModel.count; i++) {
|
||||
var item = chatModel.get(i)
|
||||
var isResponse = item.name === "Response: "
|
||||
str += "{\"content\": ";
|
||||
str += JSON.stringify(item.value)
|
||||
str += ", \"role\": \"" + (isResponse ? "assistant" : "user") + "\"";
|
||||
if (isResponse && item.thumbsUpState !== item.thumbsDownState)
|
||||
str += ", \"rating\": \"" + (item.thumbsUpState ? "positive" : "negative") + "\"";
|
||||
if (isResponse && item.newResponse !== "")
|
||||
str += ", \"edited_content\": " + JSON.stringify(item.newResponse);
|
||||
if (isResponse && item.stopped)
|
||||
str += ", \"stopped\": \"true\""
|
||||
if (!isResponse)
|
||||
str += "},"
|
||||
else
|
||||
str += ((i < chatModel.count - 1) ? "}," : "}")
|
||||
}
|
||||
return str + "]}"
|
||||
}
|
||||
|
||||
ChatDrawer {
|
||||
id: chatDrawer
|
||||
@@ -827,8 +824,6 @@ Rectangle {
|
||||
textInput.forceActiveFocus();
|
||||
textInput.cursorPosition = text.length;
|
||||
}
|
||||
height: visible ? implicitHeight : 0
|
||||
visible: name !== "ToolResponse: "
|
||||
}
|
||||
|
||||
remove: Transition {
|
||||
@@ -935,7 +930,10 @@ Rectangle {
|
||||
visible: false
|
||||
}
|
||||
onClicked: {
|
||||
chatModel.copyToClipboard()
|
||||
var conversation = getConversation()
|
||||
copyEdit.text = conversation
|
||||
copyEdit.selectAll()
|
||||
copyEdit.copy()
|
||||
copyMessage.open()
|
||||
}
|
||||
ToolTip.visible: copyChatButton.hovered
|
||||
@@ -1354,10 +1352,9 @@ Rectangle {
|
||||
ToolTip.text: Accessible.description
|
||||
|
||||
onClicked: {
|
||||
// FIXME: This no longer sets a 'stopped' field so conversations that
|
||||
// are copied to clipboard or to datalake don't indicate if the user
|
||||
// has prematurely stopped the response. This has been broken since
|
||||
// v3.0.0 at least.
|
||||
var index = Math.max(0, chatModel.count - 1);
|
||||
var listElement = chatModel.get(index);
|
||||
listElement.stopped = true
|
||||
currentChat.stopGenerating()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,19 +3,12 @@
|
||||
#include "chatlistmodel.h"
|
||||
#include "network.h"
|
||||
#include "server.h"
|
||||
#include "tool.h"
|
||||
#include "toolcallparser.h"
|
||||
#include "toolmodel.h"
|
||||
|
||||
#include <QBuffer>
|
||||
#include <QDataStream>
|
||||
#include <QDebug>
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonObject>
|
||||
#include <QJsonValue>
|
||||
#include <QLatin1String>
|
||||
#include <QMap>
|
||||
#include <QRegularExpression>
|
||||
#include <QString>
|
||||
#include <QVariant>
|
||||
#include <Qt>
|
||||
@@ -23,8 +16,6 @@
|
||||
|
||||
#include <utility>
|
||||
|
||||
using namespace ToolEnums;
|
||||
|
||||
Chat::Chat(QObject *parent)
|
||||
: QObject(parent)
|
||||
, m_id(Network::globalInstance()->generateUniqueId())
|
||||
@@ -63,6 +54,7 @@ void Chat::connectLLM()
|
||||
// Should be in different threads
|
||||
connect(m_llmodel, &ChatLLM::modelLoadingPercentageChanged, this, &Chat::handleModelLoadingPercentageChanged, Qt::QueuedConnection);
|
||||
connect(m_llmodel, &ChatLLM::responseChanged, this, &Chat::handleResponseChanged, Qt::QueuedConnection);
|
||||
connect(m_llmodel, &ChatLLM::responseFailed, this, &Chat::handleResponseFailed, Qt::QueuedConnection);
|
||||
connect(m_llmodel, &ChatLLM::promptProcessing, this, &Chat::promptProcessing, Qt::QueuedConnection);
|
||||
connect(m_llmodel, &ChatLLM::generatingQuestions, this, &Chat::generatingQuestions, Qt::QueuedConnection);
|
||||
connect(m_llmodel, &ChatLLM::responseStopped, this, &Chat::responseStopped, Qt::QueuedConnection);
|
||||
@@ -189,12 +181,23 @@ Chat::ResponseState Chat::responseState() const
|
||||
return m_responseState;
|
||||
}
|
||||
|
||||
void Chat::handleResponseChanged()
|
||||
void Chat::handleResponseChanged(const QString &response)
|
||||
{
|
||||
if (m_responseState != Chat::ResponseGeneration) {
|
||||
m_responseState = Chat::ResponseGeneration;
|
||||
emit responseStateChanged();
|
||||
}
|
||||
|
||||
const int index = m_chatModel->count() - 1;
|
||||
m_chatModel->updateValue(index, response);
|
||||
}
|
||||
|
||||
void Chat::handleResponseFailed(const QString &error)
|
||||
{
|
||||
const int index = m_chatModel->count() - 1;
|
||||
m_chatModel->updateValue(index, error);
|
||||
m_chatModel->setError();
|
||||
responseStopped(0);
|
||||
}
|
||||
|
||||
void Chat::handleModelLoadingPercentageChanged(float loadingPercentage)
|
||||
@@ -239,54 +242,9 @@ void Chat::responseStopped(qint64 promptResponseMs)
|
||||
m_responseState = Chat::ResponseStopped;
|
||||
emit responseInProgressChanged();
|
||||
emit responseStateChanged();
|
||||
|
||||
const QString possibleToolcall = m_chatModel->possibleToolcall();
|
||||
|
||||
ToolCallParser parser;
|
||||
parser.update(possibleToolcall);
|
||||
|
||||
if (parser.state() == ToolEnums::ParseState::Complete) {
|
||||
const QString toolCall = parser.toolCall();
|
||||
|
||||
// Regex to remove the formatting around the code
|
||||
static const QRegularExpression regex("^\\s*```javascript\\s*|\\s*```\\s*$");
|
||||
QString code = toolCall;
|
||||
code.remove(regex);
|
||||
code = code.trimmed();
|
||||
|
||||
// Right now the code interpreter is the only available tool
|
||||
Tool *toolInstance = ToolModel::globalInstance()->get(ToolCallConstants::CodeInterpreterFunction);
|
||||
Q_ASSERT(toolInstance);
|
||||
|
||||
// The param is the code
|
||||
const ToolParam param = { "code", ToolEnums::ParamType::String, code };
|
||||
const QString result = toolInstance->run({param}, 10000 /*msecs to timeout*/);
|
||||
const ToolEnums::Error error = toolInstance->error();
|
||||
const QString errorString = toolInstance->errorString();
|
||||
|
||||
// Update the current response with meta information about toolcall and re-parent
|
||||
m_chatModel->updateToolCall({
|
||||
ToolCallConstants::CodeInterpreterFunction,
|
||||
{ param },
|
||||
result,
|
||||
error,
|
||||
errorString
|
||||
});
|
||||
|
||||
++m_consecutiveToolCalls;
|
||||
|
||||
// We limit the number of consecutive toolcalls otherwise we get into a potentially endless loop
|
||||
if (m_consecutiveToolCalls < 3 || error == ToolEnums::Error::NoError) {
|
||||
resetResponseState();
|
||||
emit promptRequested(m_collections); // triggers a new response
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (m_generatedName.isEmpty())
|
||||
emit generateNameRequested();
|
||||
|
||||
m_consecutiveToolCalls = 0;
|
||||
Network::globalInstance()->trackChatEvent("response_complete", {
|
||||
{"first", m_firstResponse},
|
||||
{"message_count", chatModel()->count()},
|
||||
|
||||
@@ -161,7 +161,8 @@ Q_SIGNALS:
|
||||
void generatedQuestionsChanged();
|
||||
|
||||
private Q_SLOTS:
|
||||
void handleResponseChanged();
|
||||
void handleResponseChanged(const QString &response);
|
||||
void handleResponseFailed(const QString &error);
|
||||
void handleModelLoadingPercentageChanged(float);
|
||||
void promptProcessing();
|
||||
void generatingQuestions();
|
||||
@@ -204,7 +205,6 @@ private:
|
||||
// - The chat was freshly created during this launch.
|
||||
// - The chat was changed after loading it from disk.
|
||||
bool m_needsSave = true;
|
||||
int m_consecutiveToolCalls = 0;
|
||||
};
|
||||
|
||||
#endif // CHAT_H
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
#include <memory>
|
||||
|
||||
static constexpr quint32 CHAT_FORMAT_MAGIC = 0xF5D553CC;
|
||||
static constexpr qint32 CHAT_FORMAT_VERSION = 12;
|
||||
static constexpr qint32 CHAT_FORMAT_VERSION = 11;
|
||||
|
||||
class MyChatListModel: public ChatListModel { };
|
||||
Q_GLOBAL_STATIC(MyChatListModel, chatListModelInstance)
|
||||
@@ -52,11 +52,9 @@ void ChatListModel::loadChats()
|
||||
connect(thread, &ChatsRestoreThread::finished, thread, &QObject::deleteLater);
|
||||
thread->start();
|
||||
|
||||
m_chatSaver = std::make_unique<ChatSaver>();
|
||||
connect(this, &ChatListModel::requestSaveChats, m_chatSaver.get(), &ChatSaver::saveChats, Qt::QueuedConnection);
|
||||
connect(m_chatSaver.get(), &ChatSaver::saveChatsFinished, this, &ChatListModel::saveChatsFinished, Qt::QueuedConnection);
|
||||
// save chats on application quit
|
||||
connect(QCoreApplication::instance(), &QCoreApplication::aboutToQuit, this, &ChatListModel::saveChatsSync);
|
||||
ChatSaver *saver = new ChatSaver;
|
||||
connect(this, &ChatListModel::requestSaveChats, saver, &ChatSaver::saveChats, Qt::QueuedConnection);
|
||||
connect(saver, &ChatSaver::saveChatsFinished, this, &ChatListModel::saveChatsFinished, Qt::QueuedConnection);
|
||||
|
||||
connect(MySettings::globalInstance(), &MySettings::serverChatChanged, this, &ChatListModel::handleServerEnabledChanged);
|
||||
}
|
||||
@@ -80,24 +78,16 @@ ChatSaver::ChatSaver()
|
||||
m_thread.start();
|
||||
}
|
||||
|
||||
ChatSaver::~ChatSaver()
|
||||
{
|
||||
m_thread.quit();
|
||||
m_thread.wait();
|
||||
}
|
||||
|
||||
QVector<Chat *> ChatListModel::getChatsToSave() const
|
||||
{
|
||||
QVector<Chat *> toSave;
|
||||
for (auto *chat : m_chats)
|
||||
if (chat != m_serverChat && !chat->isNewChat())
|
||||
toSave << chat;
|
||||
return toSave;
|
||||
}
|
||||
|
||||
void ChatListModel::saveChats()
|
||||
{
|
||||
auto toSave = getChatsToSave();
|
||||
QVector<Chat*> toSave;
|
||||
for (Chat *chat : m_chats) {
|
||||
if (chat == m_serverChat)
|
||||
continue;
|
||||
if (chat->isNewChat())
|
||||
continue;
|
||||
toSave.append(chat);
|
||||
}
|
||||
if (toSave.isEmpty()) {
|
||||
emit saveChatsFinished();
|
||||
return;
|
||||
@@ -106,24 +96,8 @@ void ChatListModel::saveChats()
|
||||
emit requestSaveChats(toSave);
|
||||
}
|
||||
|
||||
void ChatListModel::saveChatsForQuit()
|
||||
{
|
||||
saveChats();
|
||||
m_startedFinalSave = true;
|
||||
}
|
||||
|
||||
void ChatListModel::saveChatsSync()
|
||||
{
|
||||
auto toSave = getChatsToSave();
|
||||
if (!m_startedFinalSave && !toSave.isEmpty())
|
||||
m_chatSaver->saveChats(toSave);
|
||||
}
|
||||
|
||||
void ChatSaver::saveChats(const QVector<Chat *> &chats)
|
||||
{
|
||||
// we can be called from the main thread instead of a worker thread at quit time, so take a lock
|
||||
QMutexLocker locker(&m_mutex);
|
||||
|
||||
QElapsedTimer timer;
|
||||
timer.start();
|
||||
const QString savePath = MySettings::globalInstance()->modelPath();
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
#include <QDebug>
|
||||
#include <QHash>
|
||||
#include <QList>
|
||||
#include <QMutex>
|
||||
#include <QObject>
|
||||
#include <QThread>
|
||||
#include <QVariant>
|
||||
@@ -19,9 +18,6 @@
|
||||
#include <QtGlobal>
|
||||
#include <QtLogging>
|
||||
|
||||
#include <memory>
|
||||
|
||||
|
||||
class ChatsRestoreThread : public QThread
|
||||
{
|
||||
Q_OBJECT
|
||||
@@ -37,7 +33,6 @@ class ChatSaver : public QObject
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit ChatSaver();
|
||||
~ChatSaver() override;
|
||||
|
||||
Q_SIGNALS:
|
||||
void saveChatsFinished();
|
||||
@@ -47,7 +42,6 @@ public Q_SLOTS:
|
||||
|
||||
private:
|
||||
QThread m_thread;
|
||||
QMutex m_mutex;
|
||||
};
|
||||
|
||||
class ChatListModel : public QAbstractListModel
|
||||
@@ -234,7 +228,6 @@ public:
|
||||
|
||||
void removeChatFile(Chat *chat) const;
|
||||
Q_INVOKABLE void saveChats();
|
||||
Q_INVOKABLE void saveChatsForQuit();
|
||||
void restoreChat(Chat *chat);
|
||||
void chatsRestoredFinished();
|
||||
|
||||
@@ -251,9 +244,6 @@ protected:
|
||||
bool eventFilter(QObject *obj, QEvent *ev) override;
|
||||
|
||||
private Q_SLOTS:
|
||||
// Used with QCoreApplication::aboutToQuit. Does not require an event loop.
|
||||
void saveChatsSync();
|
||||
|
||||
void newChatCountChanged()
|
||||
{
|
||||
Q_ASSERT(m_newChat && m_newChat->chatModel()->count());
|
||||
@@ -284,16 +274,11 @@ private Q_SLOTS:
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
QVector<Chat *> getChatsToSave() const;
|
||||
|
||||
private:
|
||||
Chat* m_newChat = nullptr;
|
||||
Chat* m_serverChat = nullptr;
|
||||
Chat* m_currentChat = nullptr;
|
||||
QList<Chat*> m_chats;
|
||||
std::unique_ptr<ChatSaver> m_chatSaver;
|
||||
bool m_startedFinalSave = false;
|
||||
|
||||
private:
|
||||
explicit ChatListModel();
|
||||
|
||||
@@ -7,9 +7,6 @@
|
||||
#include "localdocs.h"
|
||||
#include "mysettings.h"
|
||||
#include "network.h"
|
||||
#include "tool.h"
|
||||
#include "toolmodel.h"
|
||||
#include "toolcallparser.h"
|
||||
|
||||
#include <fmt/format.h>
|
||||
|
||||
@@ -58,7 +55,6 @@
|
||||
#include <vector>
|
||||
|
||||
using namespace Qt::Literals::StringLiterals;
|
||||
using namespace ToolEnums;
|
||||
namespace ranges = std::ranges;
|
||||
|
||||
//#define DEBUG
|
||||
@@ -532,7 +528,7 @@ bool ChatLLM::loadNewModel(const ModelInfo &modelInfo, QVariantMap &modelLoadPro
|
||||
bool actualDeviceIsCPU = true;
|
||||
|
||||
#if defined(Q_OS_MAC) && defined(__aarch64__)
|
||||
if (m_llModelInfo.model->implementation().buildVariant() == "metal")
|
||||
if (m_llModelInfo.model->implementation().buildBackend() == "metal")
|
||||
actualDeviceIsCPU = false;
|
||||
#else
|
||||
if (requestedDevice != "CPU") {
|
||||
@@ -647,16 +643,40 @@ bool isAllSpace(R &&r)
|
||||
void ChatLLM::regenerateResponse(int index)
|
||||
{
|
||||
Q_ASSERT(m_chatModel);
|
||||
if (m_chatModel->regenerateResponse(index)) {
|
||||
emit responseChanged();
|
||||
prompt(m_chat->collectionList());
|
||||
int promptIdx;
|
||||
{
|
||||
auto items = m_chatModel->chatItems(); // holds lock
|
||||
if (index < 1 || index >= items.size() || items[index].type() != ChatItem::Type::Response)
|
||||
return;
|
||||
promptIdx = m_chatModel->getPeerUnlocked(index).value_or(-1);
|
||||
}
|
||||
|
||||
emit responseChanged({});
|
||||
m_chatModel->truncate(index + 1);
|
||||
m_chatModel->updateCurrentResponse(index, true );
|
||||
m_chatModel->updateNewResponse (index, {} );
|
||||
m_chatModel->updateStopped (index, false);
|
||||
m_chatModel->updateThumbsUpState (index, false);
|
||||
m_chatModel->updateThumbsDownState(index, false);
|
||||
m_chatModel->setError(false);
|
||||
if (promptIdx >= 0)
|
||||
m_chatModel->updateSources(promptIdx, {});
|
||||
|
||||
prompt(m_chat->collectionList());
|
||||
}
|
||||
|
||||
std::optional<QString> ChatLLM::popPrompt(int index)
|
||||
{
|
||||
Q_ASSERT(m_chatModel);
|
||||
return m_chatModel->popPrompt(index);
|
||||
QString content;
|
||||
{
|
||||
auto items = m_chatModel->chatItems(); // holds lock
|
||||
if (index < 0 || index >= items.size() || items[index].type() != ChatItem::Type::Prompt)
|
||||
return std::nullopt;
|
||||
content = items[index].value;
|
||||
}
|
||||
m_chatModel->truncate(index);
|
||||
return content;
|
||||
}
|
||||
|
||||
ModelInfo ChatLLM::modelInfo() const
|
||||
@@ -717,28 +737,28 @@ void ChatLLM::prompt(const QStringList &enabledCollections)
|
||||
promptInternalChat(enabledCollections, promptContextFromSettings(m_modelInfo));
|
||||
} catch (const std::exception &e) {
|
||||
// FIXME(jared): this is neither translated nor serialized
|
||||
m_chatModel->setResponseValue(u"Error: %1"_s.arg(QString::fromUtf8(e.what())));
|
||||
m_chatModel->setError();
|
||||
emit responseFailed(u"Error: %1"_s.arg(QString::fromUtf8(e.what())));
|
||||
emit responseStopped(0);
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<MessageItem> ChatLLM::forkConversation(const QString &prompt) const
|
||||
// FIXME(jared): We can avoid this potentially expensive copy if we use ChatItem pointers, but this is only safe if we
|
||||
// hold the lock while generating. We can't do that now because Chat is actually in charge of updating the response, not
|
||||
// ChatLLM.
|
||||
std::vector<ChatItem> ChatLLM::forkConversation(const QString &prompt) const
|
||||
{
|
||||
Q_ASSERT(m_chatModel);
|
||||
if (m_chatModel->hasError())
|
||||
throw std::logic_error("cannot continue conversation with an error");
|
||||
|
||||
std::vector<MessageItem> conversation;
|
||||
std::vector<ChatItem> conversation;
|
||||
{
|
||||
auto items = m_chatModel->messageItems();
|
||||
// It is possible the main thread could have erased the conversation while the llm thread,
|
||||
// is busy forking the conversatoin but it must have set stop generating first
|
||||
Q_ASSERT(items.size() >= 2 || m_stopGenerating); // should be prompt/response pairs
|
||||
auto items = m_chatModel->chatItems(); // holds lock
|
||||
Q_ASSERT(items.size() >= 2); // should be prompt/response pairs
|
||||
conversation.reserve(items.size() + 1);
|
||||
conversation.assign(items.begin(), items.end());
|
||||
}
|
||||
conversation.emplace_back(MessageItem::Type::Prompt, prompt.toUtf8());
|
||||
conversation.emplace_back(ChatItem::prompt_tag, prompt);
|
||||
return conversation;
|
||||
}
|
||||
|
||||
@@ -773,7 +793,7 @@ std::optional<std::string> ChatLLM::checkJinjaTemplateError(const std::string &s
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::string ChatLLM::applyJinjaTemplate(std::span<const MessageItem> items) const
|
||||
std::string ChatLLM::applyJinjaTemplate(std::span<const ChatItem> items) const
|
||||
{
|
||||
Q_ASSERT(items.size() >= 1);
|
||||
|
||||
@@ -800,33 +820,25 @@ std::string ChatLLM::applyJinjaTemplate(std::span<const MessageItem> items) cons
|
||||
|
||||
uint version = parseJinjaTemplateVersion(chatTemplate);
|
||||
|
||||
auto makeMap = [version](const MessageItem &item) {
|
||||
auto makeMap = [version](const ChatItem &item) {
|
||||
return jinja2::GenericMap([msg = std::make_shared<JinjaMessage>(version, item)] { return msg.get(); });
|
||||
};
|
||||
|
||||
std::unique_ptr<MessageItem> systemItem;
|
||||
std::unique_ptr<ChatItem> systemItem;
|
||||
bool useSystem = !isAllSpace(systemMessage);
|
||||
|
||||
jinja2::ValuesList messages;
|
||||
messages.reserve(useSystem + items.size());
|
||||
if (useSystem) {
|
||||
systemItem = std::make_unique<MessageItem>(MessageItem::Type::System, systemMessage.toUtf8());
|
||||
systemItem = std::make_unique<ChatItem>(ChatItem::system_tag, systemMessage);
|
||||
messages.emplace_back(makeMap(*systemItem));
|
||||
}
|
||||
for (auto &item : items)
|
||||
messages.emplace_back(makeMap(item));
|
||||
|
||||
jinja2::ValuesList toolList;
|
||||
const int toolCount = ToolModel::globalInstance()->count();
|
||||
for (int i = 0; i < toolCount; ++i) {
|
||||
Tool *t = ToolModel::globalInstance()->get(i);
|
||||
toolList.push_back(t->jinjaValue());
|
||||
}
|
||||
|
||||
jinja2::ValuesMap params {
|
||||
{ "messages", std::move(messages) },
|
||||
{ "add_generation_prompt", true },
|
||||
{ "toolList", toolList },
|
||||
};
|
||||
for (auto &[name, token] : model->specialTokens())
|
||||
params.emplace(std::move(name), std::move(token));
|
||||
@@ -840,44 +852,48 @@ std::string ChatLLM::applyJinjaTemplate(std::span<const MessageItem> items) cons
|
||||
}
|
||||
|
||||
auto ChatLLM::promptInternalChat(const QStringList &enabledCollections, const LLModel::PromptContext &ctx,
|
||||
qsizetype startOffset) -> ChatPromptResult
|
||||
std::optional<std::pair<int, int>> subrange) -> ChatPromptResult
|
||||
{
|
||||
Q_ASSERT(isModelLoaded());
|
||||
Q_ASSERT(m_chatModel);
|
||||
|
||||
// Return a vector of relevant messages for this chat.
|
||||
// "startOffset" is used to select only local server messages from the current chat session.
|
||||
// Return a (ChatModelAccessor, std::span) pair where the span represents the relevant messages for this chat.
|
||||
// "subrange" is used to select only local server messages from the current chat session.
|
||||
auto getChat = [&]() {
|
||||
auto items = m_chatModel->messageItems();
|
||||
if (startOffset > 0)
|
||||
items.erase(items.begin(), items.begin() + startOffset);
|
||||
Q_ASSERT(items.size() >= 2);
|
||||
return items;
|
||||
auto items = m_chatModel->chatItems(); // holds lock
|
||||
std::span view(items);
|
||||
if (subrange)
|
||||
view = view.subspan(subrange->first, subrange->second);
|
||||
Q_ASSERT(view.size() >= 2);
|
||||
return std::pair(std::move(items), view);
|
||||
};
|
||||
|
||||
QList<ResultInfo> databaseResults;
|
||||
if (!enabledCollections.isEmpty()) {
|
||||
std::optional<std::pair<int, QString>> query;
|
||||
{
|
||||
// Find the prompt that represents the query. Server chats are flexible and may not have one.
|
||||
auto items = getChat();
|
||||
if (auto peer = m_chatModel->getPeer(items, items.end() - 1)) // peer of response
|
||||
query = { *peer - items.begin(), (*peer)->content() };
|
||||
}
|
||||
|
||||
if (query) {
|
||||
auto &[promptIndex, queryStr] = *query;
|
||||
const int retrievalSize = MySettings::globalInstance()->localDocsRetrievalSize();
|
||||
emit requestRetrieveFromDB(enabledCollections, queryStr, retrievalSize, &databaseResults); // blocks
|
||||
m_chatModel->updateSources(promptIndex, databaseResults);
|
||||
emit databaseResultsChanged(databaseResults);
|
||||
}
|
||||
// copy messages for safety (since we can't hold the lock the whole time)
|
||||
std::optional<std::pair<int, QString>> query;
|
||||
{
|
||||
// Find the prompt that represents the query. Server chats are flexible and may not have one.
|
||||
auto [_, view] = getChat(); // holds lock
|
||||
if (auto peer = m_chatModel->getPeer(view, view.end() - 1)) // peer of response
|
||||
query = { *peer - view.begin(), (*peer)->value };
|
||||
}
|
||||
|
||||
auto messageItems = getChat();
|
||||
messageItems.pop_back(); // exclude new response
|
||||
QList<ResultInfo> databaseResults;
|
||||
if (query && !enabledCollections.isEmpty()) {
|
||||
auto &[promptIndex, queryStr] = *query;
|
||||
const int retrievalSize = MySettings::globalInstance()->localDocsRetrievalSize();
|
||||
emit requestRetrieveFromDB(enabledCollections, queryStr, retrievalSize, &databaseResults); // blocks
|
||||
m_chatModel->updateSources(promptIndex, databaseResults);
|
||||
emit databaseResultsChanged(databaseResults);
|
||||
}
|
||||
|
||||
auto result = promptInternal(messageItems, ctx, !databaseResults.isEmpty());
|
||||
// copy messages for safety (since we can't hold the lock the whole time)
|
||||
std::vector<ChatItem> chatItems;
|
||||
{
|
||||
auto [_, view] = getChat(); // holds lock
|
||||
chatItems.assign(view.begin(), view.end() - 1); // exclude new response
|
||||
}
|
||||
|
||||
auto result = promptInternal(chatItems, ctx, !databaseResults.isEmpty());
|
||||
return {
|
||||
/*PromptResult*/ {
|
||||
.response = std::move(result.response),
|
||||
@@ -889,7 +905,7 @@ auto ChatLLM::promptInternalChat(const QStringList &enabledCollections, const LL
|
||||
}
|
||||
|
||||
auto ChatLLM::promptInternal(
|
||||
const std::variant<std::span<const MessageItem>, std::string_view> &prompt,
|
||||
const std::variant<std::span<const ChatItem>, std::string_view> &prompt,
|
||||
const LLModel::PromptContext &ctx,
|
||||
bool usedLocalDocs
|
||||
) -> PromptResult
|
||||
@@ -899,14 +915,14 @@ auto ChatLLM::promptInternal(
|
||||
auto *mySettings = MySettings::globalInstance();
|
||||
|
||||
// unpack prompt argument
|
||||
const std::span<const MessageItem> *messageItems = nullptr;
|
||||
const std::span<const ChatItem> *chatItems = nullptr;
|
||||
std::string jinjaBuffer;
|
||||
std::string_view conversation;
|
||||
if (auto *nonChat = std::get_if<std::string_view>(&prompt)) {
|
||||
conversation = *nonChat; // complete the string without a template
|
||||
} else {
|
||||
messageItems = &std::get<std::span<const MessageItem>>(prompt);
|
||||
jinjaBuffer = applyJinjaTemplate(*messageItems);
|
||||
chatItems = &std::get<std::span<const ChatItem>>(prompt);
|
||||
jinjaBuffer = applyJinjaTemplate(*chatItems);
|
||||
conversation = jinjaBuffer;
|
||||
}
|
||||
|
||||
@@ -914,8 +930,8 @@ auto ChatLLM::promptInternal(
|
||||
if (!dynamic_cast<const ChatAPI *>(m_llModelInfo.model.get())) {
|
||||
auto nCtx = m_llModelInfo.model->contextLength();
|
||||
std::string jinjaBuffer2;
|
||||
auto lastMessageRendered = (messageItems && messageItems->size() > 1)
|
||||
? std::string_view(jinjaBuffer2 = applyJinjaTemplate({ &messageItems->back(), 1 }))
|
||||
auto lastMessageRendered = (chatItems && chatItems->size() > 1)
|
||||
? std::string_view(jinjaBuffer2 = applyJinjaTemplate({ &chatItems->back(), 1 }))
|
||||
: conversation;
|
||||
int32_t lastMessageLength = m_llModelInfo.model->countPromptTokens(lastMessageRendered);
|
||||
if (auto limit = nCtx - 4; lastMessageLength > limit) {
|
||||
@@ -935,42 +951,14 @@ auto ChatLLM::promptInternal(
|
||||
return !m_stopGenerating;
|
||||
};
|
||||
|
||||
ToolCallParser toolCallParser;
|
||||
auto handleResponse = [this, &result, &toolCallParser](LLModel::Token token, std::string_view piece) -> bool {
|
||||
auto handleResponse = [this, &result](LLModel::Token token, std::string_view piece) -> bool {
|
||||
Q_UNUSED(token)
|
||||
result.responseTokens++;
|
||||
m_timer->inc();
|
||||
|
||||
// FIXME: This is *not* necessarily fully formed utf data because it can be partial at this point
|
||||
// handle this like below where we have a QByteArray
|
||||
toolCallParser.update(QString::fromStdString(piece.data()));
|
||||
|
||||
// Create a toolcall and split the response if needed
|
||||
if (!toolCallParser.hasSplit() && toolCallParser.state() == ToolEnums::ParseState::Partial) {
|
||||
const QPair<QString, QString> pair = toolCallParser.split();
|
||||
m_chatModel->splitToolCall(pair);
|
||||
}
|
||||
|
||||
result.response.append(piece.data(), piece.size());
|
||||
auto respStr = QString::fromUtf8(result.response);
|
||||
|
||||
try {
|
||||
if (toolCallParser.hasSplit())
|
||||
m_chatModel->setResponseValue(toolCallParser.buffer());
|
||||
else
|
||||
m_chatModel->setResponseValue(removeLeadingWhitespace(respStr));
|
||||
} catch (const std::exception &e) {
|
||||
// We have a try/catch here because the main thread might have removed the response from
|
||||
// the chatmodel by erasing the conversation during the response... the main thread sets
|
||||
// m_stopGenerating before doing so, but it doesn't wait after that to reset the chatmodel
|
||||
Q_ASSERT(m_stopGenerating);
|
||||
return false;
|
||||
}
|
||||
|
||||
emit responseChanged();
|
||||
|
||||
const bool foundToolCall = toolCallParser.state() == ToolEnums::ParseState::Complete;
|
||||
return !foundToolCall && !m_stopGenerating;
|
||||
emit responseChanged(removeLeadingWhitespace(respStr));
|
||||
return !m_stopGenerating;
|
||||
};
|
||||
|
||||
QElapsedTimer totalTime;
|
||||
@@ -990,20 +978,13 @@ auto ChatLLM::promptInternal(
|
||||
m_timer->stop();
|
||||
qint64 elapsed = totalTime.elapsed();
|
||||
|
||||
const bool foundToolCall = toolCallParser.state() == ToolEnums::ParseState::Complete;
|
||||
|
||||
// trim trailing whitespace
|
||||
auto respStr = QString::fromUtf8(result.response);
|
||||
if (!respStr.isEmpty() && (std::as_const(respStr).back().isSpace() || foundToolCall)) {
|
||||
if (toolCallParser.hasSplit())
|
||||
m_chatModel->setResponseValue(toolCallParser.buffer());
|
||||
else
|
||||
m_chatModel->setResponseValue(respStr.trimmed());
|
||||
emit responseChanged();
|
||||
}
|
||||
if (!respStr.isEmpty() && std::as_const(respStr).back().isSpace())
|
||||
emit responseChanged(respStr.trimmed());
|
||||
|
||||
bool doQuestions = false;
|
||||
if (!m_isServer && messageItems && !foundToolCall) {
|
||||
if (!m_isServer && chatItems) {
|
||||
switch (mySettings->suggestionMode()) {
|
||||
case SuggestionMode::On: doQuestions = true; break;
|
||||
case SuggestionMode::LocalDocsOnly: doQuestions = usedLocalDocs; break;
|
||||
|
||||
@@ -220,8 +220,8 @@ Q_SIGNALS:
|
||||
void modelLoadingPercentageChanged(float);
|
||||
void modelLoadingError(const QString &error);
|
||||
void modelLoadingWarning(const QString &warning);
|
||||
void responseChanged();
|
||||
void responseFailed();
|
||||
void responseChanged(const QString &response);
|
||||
void responseFailed(const QString &error);
|
||||
void promptProcessing();
|
||||
void generatingQuestions();
|
||||
void responseStopped(qint64 promptResponseMs);
|
||||
@@ -251,20 +251,20 @@ protected:
|
||||
};
|
||||
|
||||
ChatPromptResult promptInternalChat(const QStringList &enabledCollections, const LLModel::PromptContext &ctx,
|
||||
qsizetype startOffset = 0);
|
||||
std::optional<std::pair<int, int>> subrange = {});
|
||||
// passing a string_view directly skips templating and uses the raw string
|
||||
PromptResult promptInternal(const std::variant<std::span<const MessageItem>, std::string_view> &prompt,
|
||||
PromptResult promptInternal(const std::variant<std::span<const ChatItem>, std::string_view> &prompt,
|
||||
const LLModel::PromptContext &ctx,
|
||||
bool usedLocalDocs);
|
||||
|
||||
private:
|
||||
bool loadNewModel(const ModelInfo &modelInfo, QVariantMap &modelLoadProps);
|
||||
|
||||
std::vector<MessageItem> forkConversation(const QString &prompt) const;
|
||||
std::vector<ChatItem> forkConversation(const QString &prompt) const;
|
||||
|
||||
// Applies the Jinja template. Query mode returns only the last message without special tokens.
|
||||
// Returns a (# of messages, rendered prompt) pair.
|
||||
std::string applyJinjaTemplate(std::span<const MessageItem> items) const;
|
||||
std::string applyJinjaTemplate(std::span<const ChatItem> items) const;
|
||||
|
||||
void generateQuestions(qint64 elapsed);
|
||||
|
||||
|
||||
@@ -1,351 +0,0 @@
|
||||
#include "chatmodel.h"
|
||||
|
||||
#include <QDebug>
|
||||
#include <QMap>
|
||||
#include <QtGlobal>
|
||||
#include <QtLogging>
|
||||
|
||||
|
||||
QList<ResultInfo> ChatItem::consolidateSources(const QList<ResultInfo> &sources)
|
||||
{
|
||||
QMap<QString, ResultInfo> groupedData;
|
||||
for (const ResultInfo &info : sources) {
|
||||
if (groupedData.contains(info.file)) {
|
||||
groupedData[info.file].text += "\n---\n" + info.text;
|
||||
} else {
|
||||
groupedData[info.file] = info;
|
||||
}
|
||||
}
|
||||
QList<ResultInfo> consolidatedSources = groupedData.values();
|
||||
return consolidatedSources;
|
||||
}
|
||||
|
||||
void ChatItem::serializeResponse(QDataStream &stream, int version)
|
||||
{
|
||||
stream << value;
|
||||
}
|
||||
|
||||
void ChatItem::serializeToolCall(QDataStream &stream, int version)
|
||||
{
|
||||
stream << value;
|
||||
toolCallInfo.serialize(stream, version);
|
||||
}
|
||||
|
||||
void ChatItem::serializeToolResponse(QDataStream &stream, int version)
|
||||
{
|
||||
stream << value;
|
||||
}
|
||||
|
||||
void ChatItem::serializeText(QDataStream &stream, int version)
|
||||
{
|
||||
stream << value;
|
||||
}
|
||||
|
||||
void ChatItem::serializeSubItems(QDataStream &stream, int version)
|
||||
{
|
||||
stream << name;
|
||||
switch (auto typ = type()) {
|
||||
using enum ChatItem::Type;
|
||||
case Response: { serializeResponse(stream, version); break; }
|
||||
case ToolCall: { serializeToolCall(stream, version); break; }
|
||||
case ToolResponse: { serializeToolResponse(stream, version); break; }
|
||||
case Text: { serializeText(stream, version); break; }
|
||||
case System:
|
||||
case Prompt:
|
||||
throw std::invalid_argument(fmt::format("cannot serialize subitem type {}", int(typ)));
|
||||
}
|
||||
|
||||
stream << qsizetype(subItems.size());
|
||||
for (ChatItem *item :subItems)
|
||||
item->serializeSubItems(stream, version);
|
||||
}
|
||||
|
||||
void ChatItem::serialize(QDataStream &stream, int version)
|
||||
{
|
||||
stream << name;
|
||||
stream << value;
|
||||
stream << newResponse;
|
||||
stream << isCurrentResponse;
|
||||
stream << stopped;
|
||||
stream << thumbsUpState;
|
||||
stream << thumbsDownState;
|
||||
if (version >= 11 && type() == ChatItem::Type::Response)
|
||||
stream << isError;
|
||||
if (version >= 8) {
|
||||
stream << sources.size();
|
||||
for (const ResultInfo &info : sources) {
|
||||
Q_ASSERT(!info.file.isEmpty());
|
||||
stream << info.collection;
|
||||
stream << info.path;
|
||||
stream << info.file;
|
||||
stream << info.title;
|
||||
stream << info.author;
|
||||
stream << info.date;
|
||||
stream << info.text;
|
||||
stream << info.page;
|
||||
stream << info.from;
|
||||
stream << info.to;
|
||||
}
|
||||
} else if (version >= 3) {
|
||||
QList<QString> references;
|
||||
QList<QString> referencesContext;
|
||||
int validReferenceNumber = 1;
|
||||
for (const ResultInfo &info : sources) {
|
||||
if (info.file.isEmpty())
|
||||
continue;
|
||||
|
||||
QString reference;
|
||||
{
|
||||
QTextStream stream(&reference);
|
||||
stream << (validReferenceNumber++) << ". ";
|
||||
if (!info.title.isEmpty())
|
||||
stream << "\"" << info.title << "\". ";
|
||||
if (!info.author.isEmpty())
|
||||
stream << "By " << info.author << ". ";
|
||||
if (!info.date.isEmpty())
|
||||
stream << "Date: " << info.date << ". ";
|
||||
stream << "In " << info.file << ". ";
|
||||
if (info.page != -1)
|
||||
stream << "Page " << info.page << ". ";
|
||||
if (info.from != -1) {
|
||||
stream << "Lines " << info.from;
|
||||
if (info.to != -1)
|
||||
stream << "-" << info.to;
|
||||
stream << ". ";
|
||||
}
|
||||
stream << "[Context](context://" << validReferenceNumber - 1 << ")";
|
||||
}
|
||||
references.append(reference);
|
||||
referencesContext.append(info.text);
|
||||
}
|
||||
|
||||
stream << references.join("\n");
|
||||
stream << referencesContext;
|
||||
}
|
||||
if (version >= 10) {
|
||||
stream << promptAttachments.size();
|
||||
for (const PromptAttachment &a : promptAttachments) {
|
||||
Q_ASSERT(!a.url.isEmpty());
|
||||
stream << a.url;
|
||||
stream << a.content;
|
||||
}
|
||||
}
|
||||
|
||||
if (version >= 12) {
|
||||
stream << qsizetype(subItems.size());
|
||||
for (ChatItem *item :subItems)
|
||||
item->serializeSubItems(stream, version);
|
||||
}
|
||||
}
|
||||
|
||||
bool ChatItem::deserializeToolCall(QDataStream &stream, int version)
|
||||
{
|
||||
stream >> value;
|
||||
return toolCallInfo.deserialize(stream, version);;
|
||||
}
|
||||
|
||||
bool ChatItem::deserializeToolResponse(QDataStream &stream, int version)
|
||||
{
|
||||
stream >> value;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ChatItem::deserializeText(QDataStream &stream, int version)
|
||||
{
|
||||
stream >> value;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ChatItem::deserializeResponse(QDataStream &stream, int version)
|
||||
{
|
||||
stream >> value;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ChatItem::deserializeSubItems(QDataStream &stream, int version)
|
||||
{
|
||||
stream >> name;
|
||||
try {
|
||||
type(); // check name
|
||||
} catch (const std::exception &e) {
|
||||
qWarning() << "ChatModel ERROR:" << e.what();
|
||||
return false;
|
||||
}
|
||||
switch (auto typ = type()) {
|
||||
using enum ChatItem::Type;
|
||||
case Response: { deserializeResponse(stream, version); break; }
|
||||
case ToolCall: { deserializeToolCall(stream, version); break; }
|
||||
case ToolResponse: { deserializeToolResponse(stream, version); break; }
|
||||
case Text: { deserializeText(stream, version); break; }
|
||||
case System:
|
||||
case Prompt:
|
||||
throw std::invalid_argument(fmt::format("cannot serialize subitem type {}", int(typ)));
|
||||
}
|
||||
|
||||
qsizetype count;
|
||||
stream >> count;
|
||||
for (int i = 0; i < count; ++i) {
|
||||
ChatItem *c = new ChatItem(this);
|
||||
if (!c->deserializeSubItems(stream, version)) {
|
||||
delete c;
|
||||
return false;
|
||||
}
|
||||
subItems.push_back(c);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ChatItem::deserialize(QDataStream &stream, int version)
|
||||
{
|
||||
if (version < 12) {
|
||||
int id;
|
||||
stream >> id;
|
||||
}
|
||||
stream >> name;
|
||||
try {
|
||||
type(); // check name
|
||||
} catch (const std::exception &e) {
|
||||
qWarning() << "ChatModel ERROR:" << e.what();
|
||||
return false;
|
||||
}
|
||||
stream >> value;
|
||||
if (version < 10) {
|
||||
// This is deprecated and no longer used
|
||||
QString prompt;
|
||||
stream >> prompt;
|
||||
}
|
||||
stream >> newResponse;
|
||||
stream >> isCurrentResponse;
|
||||
stream >> stopped;
|
||||
stream >> thumbsUpState;
|
||||
stream >> thumbsDownState;
|
||||
if (version >= 11 && type() == ChatItem::Type::Response)
|
||||
stream >> isError;
|
||||
if (version >= 8) {
|
||||
qsizetype count;
|
||||
stream >> count;
|
||||
for (int i = 0; i < count; ++i) {
|
||||
ResultInfo info;
|
||||
stream >> info.collection;
|
||||
stream >> info.path;
|
||||
stream >> info.file;
|
||||
stream >> info.title;
|
||||
stream >> info.author;
|
||||
stream >> info.date;
|
||||
stream >> info.text;
|
||||
stream >> info.page;
|
||||
stream >> info.from;
|
||||
stream >> info.to;
|
||||
sources.append(info);
|
||||
}
|
||||
consolidatedSources = ChatItem::consolidateSources(sources);
|
||||
} else if (version >= 3) {
|
||||
QString references;
|
||||
QList<QString> referencesContext;
|
||||
stream >> references;
|
||||
stream >> referencesContext;
|
||||
|
||||
if (!references.isEmpty()) {
|
||||
QList<QString> referenceList = references.split("\n");
|
||||
|
||||
// Ignore empty lines and those that begin with "---" which is no longer used
|
||||
for (auto it = referenceList.begin(); it != referenceList.end();) {
|
||||
if (it->trimmed().isEmpty() || it->trimmed().startsWith("---"))
|
||||
it = referenceList.erase(it);
|
||||
else
|
||||
++it;
|
||||
}
|
||||
|
||||
Q_ASSERT(referenceList.size() == referencesContext.size());
|
||||
for (int j = 0; j < referenceList.size(); ++j) {
|
||||
QString reference = referenceList[j];
|
||||
QString context = referencesContext[j];
|
||||
ResultInfo info;
|
||||
QTextStream refStream(&reference);
|
||||
QString dummy;
|
||||
int validReferenceNumber;
|
||||
refStream >> validReferenceNumber >> dummy;
|
||||
// Extract title (between quotes)
|
||||
if (reference.contains("\"")) {
|
||||
int startIndex = reference.indexOf('"') + 1;
|
||||
int endIndex = reference.indexOf('"', startIndex);
|
||||
info.title = reference.mid(startIndex, endIndex - startIndex);
|
||||
}
|
||||
|
||||
// Extract author (after "By " and before the next period)
|
||||
if (reference.contains("By ")) {
|
||||
int startIndex = reference.indexOf("By ") + 3;
|
||||
int endIndex = reference.indexOf('.', startIndex);
|
||||
info.author = reference.mid(startIndex, endIndex - startIndex).trimmed();
|
||||
}
|
||||
|
||||
// Extract date (after "Date: " and before the next period)
|
||||
if (reference.contains("Date: ")) {
|
||||
int startIndex = reference.indexOf("Date: ") + 6;
|
||||
int endIndex = reference.indexOf('.', startIndex);
|
||||
info.date = reference.mid(startIndex, endIndex - startIndex).trimmed();
|
||||
}
|
||||
|
||||
// Extract file name (after "In " and before the "[Context]")
|
||||
if (reference.contains("In ") && reference.contains(". [Context]")) {
|
||||
int startIndex = reference.indexOf("In ") + 3;
|
||||
int endIndex = reference.indexOf(". [Context]", startIndex);
|
||||
info.file = reference.mid(startIndex, endIndex - startIndex).trimmed();
|
||||
}
|
||||
|
||||
// Extract page number (after "Page " and before the next space)
|
||||
if (reference.contains("Page ")) {
|
||||
int startIndex = reference.indexOf("Page ") + 5;
|
||||
int endIndex = reference.indexOf(' ', startIndex);
|
||||
if (endIndex == -1) endIndex = reference.length();
|
||||
info.page = reference.mid(startIndex, endIndex - startIndex).toInt();
|
||||
}
|
||||
|
||||
// Extract lines (after "Lines " and before the next space or hyphen)
|
||||
if (reference.contains("Lines ")) {
|
||||
int startIndex = reference.indexOf("Lines ") + 6;
|
||||
int endIndex = reference.indexOf(' ', startIndex);
|
||||
if (endIndex == -1) endIndex = reference.length();
|
||||
int hyphenIndex = reference.indexOf('-', startIndex);
|
||||
if (hyphenIndex != -1 && hyphenIndex < endIndex) {
|
||||
info.from = reference.mid(startIndex, hyphenIndex - startIndex).toInt();
|
||||
info.to = reference.mid(hyphenIndex + 1, endIndex - hyphenIndex - 1).toInt();
|
||||
} else {
|
||||
info.from = reference.mid(startIndex, endIndex - startIndex).toInt();
|
||||
}
|
||||
}
|
||||
info.text = context;
|
||||
sources.append(info);
|
||||
}
|
||||
|
||||
consolidatedSources = ChatItem::consolidateSources(sources);
|
||||
}
|
||||
}
|
||||
if (version >= 10) {
|
||||
qsizetype count;
|
||||
stream >> count;
|
||||
QList<PromptAttachment> attachments;
|
||||
for (int i = 0; i < count; ++i) {
|
||||
PromptAttachment a;
|
||||
stream >> a.url;
|
||||
stream >> a.content;
|
||||
attachments.append(a);
|
||||
}
|
||||
promptAttachments = attachments;
|
||||
}
|
||||
|
||||
if (version >= 12) {
|
||||
qsizetype count;
|
||||
stream >> count;
|
||||
for (int i = 0; i < count; ++i) {
|
||||
ChatItem *c = new ChatItem(this);
|
||||
if (!c->deserializeSubItems(stream, version)) {
|
||||
delete c;
|
||||
return false;
|
||||
}
|
||||
subItems.push_back(c);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,151 +0,0 @@
|
||||
#include "codeinterpreter.h"
|
||||
|
||||
#include <QJSValue>
|
||||
#include <QStringList>
|
||||
#include <QThread>
|
||||
#include <QVariant>
|
||||
|
||||
using namespace Qt::Literals::StringLiterals;
|
||||
|
||||
|
||||
QString CodeInterpreter::run(const QList<ToolParam> ¶ms, qint64 timeout)
|
||||
{
|
||||
m_error = ToolEnums::Error::NoError;
|
||||
m_errorString = QString();
|
||||
|
||||
Q_ASSERT(params.count() == 1
|
||||
&& params.first().name == "code"
|
||||
&& params.first().type == ToolEnums::ParamType::String);
|
||||
|
||||
const QString code = params.first().value.toString();
|
||||
|
||||
QThread workerThread;
|
||||
CodeInterpreterWorker worker;
|
||||
worker.moveToThread(&workerThread);
|
||||
connect(&worker, &CodeInterpreterWorker::finished, &workerThread, &QThread::quit, Qt::DirectConnection);
|
||||
connect(&workerThread, &QThread::started, [&worker, code]() {
|
||||
worker.request(code);
|
||||
});
|
||||
workerThread.start();
|
||||
bool timedOut = !workerThread.wait(timeout);
|
||||
if (timedOut) {
|
||||
worker.interrupt(timeout); // thread safe
|
||||
m_error = ToolEnums::Error::TimeoutError;
|
||||
}
|
||||
workerThread.quit();
|
||||
workerThread.wait();
|
||||
if (!timedOut) {
|
||||
m_error = worker.error();
|
||||
m_errorString = worker.errorString();
|
||||
}
|
||||
return worker.response();
|
||||
}
|
||||
|
||||
QList<ToolParamInfo> CodeInterpreter::parameters() const
|
||||
{
|
||||
return {{
|
||||
"code",
|
||||
ToolEnums::ParamType::String,
|
||||
"javascript code to compute",
|
||||
true
|
||||
}};
|
||||
}
|
||||
|
||||
QString CodeInterpreter::symbolicFormat() const
|
||||
{
|
||||
return "{human readable plan to complete the task}\n" + ToolCallConstants::CodeInterpreterPrefix + "{code}\n" + ToolCallConstants::CodeInterpreterSuffix;
|
||||
}
|
||||
|
||||
QString CodeInterpreter::examplePrompt() const
|
||||
{
|
||||
return R"(Write code to check if a number is prime, use that to see if the number 7 is prime)";
|
||||
}
|
||||
|
||||
QString CodeInterpreter::exampleCall() const
|
||||
{
|
||||
static const QString example = R"(function isPrime(n) {
|
||||
if (n <= 1) {
|
||||
return false;
|
||||
}
|
||||
for (let i = 2; i <= Math.sqrt(n); i++) {
|
||||
if (n % i === 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
const number = 7;
|
||||
console.log(`The number ${number} is prime: ${isPrime(number)}`);
|
||||
)";
|
||||
|
||||
return "Certainly! Let's compute the answer to whether the number 7 is prime.\n" + ToolCallConstants::CodeInterpreterPrefix + example + ToolCallConstants::CodeInterpreterSuffix;
|
||||
}
|
||||
|
||||
QString CodeInterpreter::exampleReply() const
|
||||
{
|
||||
return R"("The computed result shows that 7 is a prime number.)";
|
||||
}
|
||||
|
||||
CodeInterpreterWorker::CodeInterpreterWorker()
|
||||
: QObject(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
void CodeInterpreterWorker::request(const QString &code)
|
||||
{
|
||||
JavaScriptConsoleCapture consoleCapture;
|
||||
QJSValue consoleInternalObject = m_engine.newQObject(&consoleCapture);
|
||||
m_engine.globalObject().setProperty("console_internal", consoleInternalObject);
|
||||
|
||||
// preprocess console.log args in JS since Q_INVOKE doesn't support varargs
|
||||
auto consoleObject = m_engine.evaluate(uR"(
|
||||
class Console {
|
||||
log(...args) {
|
||||
if (args.length && typeof args[0] === 'string')
|
||||
throw new Error('console.log string formatting not supported');
|
||||
let cat = '';
|
||||
for (const arg of args) {
|
||||
cat += String(arg);
|
||||
}
|
||||
console_internal.log(cat);
|
||||
}
|
||||
}
|
||||
|
||||
new Console();
|
||||
)"_s);
|
||||
m_engine.globalObject().setProperty("console", consoleObject);
|
||||
|
||||
const QJSValue result = m_engine.evaluate(code);
|
||||
|
||||
QString resultString;
|
||||
|
||||
if (m_engine.isInterrupted()) {
|
||||
resultString = QString("Error: code execution was timed out as it exceeded %1 ms. Code must be written to ensure execution does not timeout.").arg(m_timeout);
|
||||
} else if (result.isError()) {
|
||||
// NOTE: We purposely do not set the m_error or m_errorString for the code interpreter since
|
||||
// we *want* the model to see the response has an error so it can hopefully correct itself. The
|
||||
// error member variables are intended for tools that have error conditions that cannot be corrected.
|
||||
// For instance, a tool depending upon the network might set these error variables if the network
|
||||
// is not available.
|
||||
const QStringList lines = code.split('\n');
|
||||
const int line = result.property("lineNumber").toInt();
|
||||
const int index = line - 1;
|
||||
const QString lineContent = (index >= 0 && index < lines.size()) ? lines.at(index) : "Line not found in code.";
|
||||
resultString = QString("Uncaught exception at line %1: %2\n\t%3")
|
||||
.arg(line)
|
||||
.arg(result.toString())
|
||||
.arg(lineContent);
|
||||
m_error = ToolEnums::Error::UnknownError;
|
||||
m_errorString = resultString;
|
||||
} else {
|
||||
resultString = result.isUndefined() ? QString() : result.toString();
|
||||
}
|
||||
|
||||
if (resultString.isEmpty())
|
||||
resultString = consoleCapture.output;
|
||||
else if (!consoleCapture.output.isEmpty())
|
||||
resultString += "\n" + consoleCapture.output;
|
||||
m_response = resultString;
|
||||
emit finished();
|
||||
}
|
||||
@@ -1,85 +0,0 @@
|
||||
#ifndef CODEINTERPRETER_H
|
||||
#define CODEINTERPRETER_H
|
||||
|
||||
#include "tool.h"
|
||||
#include "toolcallparser.h"
|
||||
|
||||
#include <QJSEngine>
|
||||
#include <QObject>
|
||||
#include <QString>
|
||||
#include <QtGlobal>
|
||||
|
||||
class JavaScriptConsoleCapture : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
QString output;
|
||||
Q_INVOKABLE void log(const QString &message)
|
||||
{
|
||||
const int maxLength = 1024;
|
||||
if (output.length() >= maxLength)
|
||||
return;
|
||||
|
||||
if (output.length() + message.length() + 1 > maxLength) {
|
||||
static const QString trunc = "\noutput truncated at " + QString::number(maxLength) + " characters...";
|
||||
int remainingLength = maxLength - output.length();
|
||||
if (remainingLength > 0)
|
||||
output.append(message.left(remainingLength));
|
||||
output.append(trunc);
|
||||
Q_ASSERT(output.length() > maxLength);
|
||||
} else {
|
||||
output.append(message + "\n");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class CodeInterpreterWorker : public QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
CodeInterpreterWorker();
|
||||
virtual ~CodeInterpreterWorker() {}
|
||||
|
||||
QString response() const { return m_response; }
|
||||
|
||||
void request(const QString &code);
|
||||
void interrupt(qint64 timeout) { m_timeout = timeout; m_engine.setInterrupted(true); }
|
||||
ToolEnums::Error error() const { return m_error; }
|
||||
QString errorString() const { return m_errorString; }
|
||||
|
||||
Q_SIGNALS:
|
||||
void finished();
|
||||
|
||||
private:
|
||||
qint64 m_timeout = 0;
|
||||
QJSEngine m_engine;
|
||||
QString m_response;
|
||||
ToolEnums::Error m_error = ToolEnums::Error::NoError;
|
||||
QString m_errorString;
|
||||
};
|
||||
|
||||
class CodeInterpreter : public Tool
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit CodeInterpreter() : Tool(), m_error(ToolEnums::Error::NoError) {}
|
||||
virtual ~CodeInterpreter() {}
|
||||
|
||||
QString run(const QList<ToolParam> ¶ms, qint64 timeout = 2000) override;
|
||||
ToolEnums::Error error() const override { return m_error; }
|
||||
QString errorString() const override { return m_errorString; }
|
||||
|
||||
QString name() const override { return tr("Code Interpreter"); }
|
||||
QString description() const override { return tr("compute javascript code using console.log as output"); }
|
||||
QString function() const override { return ToolCallConstants::CodeInterpreterFunction; }
|
||||
QList<ToolParamInfo> parameters() const override;
|
||||
virtual QString symbolicFormat() const override;
|
||||
QString examplePrompt() const override;
|
||||
QString exampleCall() const override;
|
||||
QString exampleReply() const override;
|
||||
|
||||
private:
|
||||
ToolEnums::Error m_error = ToolEnums::Error::NoError;
|
||||
QString m_errorString;
|
||||
};
|
||||
|
||||
#endif // CODEINTERPRETER_H
|
||||
@@ -7,13 +7,14 @@
|
||||
#include <fmt/format.h>
|
||||
#include <usearch/index_plugins.hpp>
|
||||
|
||||
#include <QByteArrayView>
|
||||
#include <QDebug>
|
||||
#include <QDir>
|
||||
#include <QDirIterator>
|
||||
#include <QFile>
|
||||
#include <QFileSystemWatcher>
|
||||
#include <QIODevice>
|
||||
#include <QPdfDocument>
|
||||
#include <QPdfSelection>
|
||||
#include <QRegularExpression>
|
||||
#include <QSqlError>
|
||||
#include <QSqlQuery>
|
||||
@@ -30,15 +31,6 @@
|
||||
#include <optional>
|
||||
#include <stdexcept>
|
||||
|
||||
#ifdef GPT4ALL_USE_QTPDF
|
||||
# include <QPdfDocument>
|
||||
# include <QPdfSelection>
|
||||
#else
|
||||
# include <fpdfview.h>
|
||||
# include <fpdf_doc.h>
|
||||
# include <fpdf_text.h>
|
||||
#endif
|
||||
|
||||
using namespace Qt::Literals::StringLiterals;
|
||||
namespace ranges = std::ranges;
|
||||
namespace us = unum::usearch;
|
||||
@@ -1141,7 +1133,6 @@ protected:
|
||||
|
||||
namespace {
|
||||
|
||||
#ifdef GPT4ALL_USE_QTPDF
|
||||
class PdfDocumentReader final : public DocumentReader {
|
||||
public:
|
||||
explicit PdfDocumentReader(const DocumentInfo &info)
|
||||
@@ -1182,99 +1173,6 @@ private:
|
||||
QString m_pageText;
|
||||
std::optional<QTextStream> m_stream;
|
||||
};
|
||||
#else
|
||||
class PdfDocumentReader final : public DocumentReader {
|
||||
public:
|
||||
explicit PdfDocumentReader(const DocumentInfo &info)
|
||||
: DocumentReader(info)
|
||||
{
|
||||
QString path = info.file.canonicalFilePath();
|
||||
m_doc = FPDF_LoadDocument(path.toUtf8().constData(), nullptr);
|
||||
if (!m_doc)
|
||||
throw std::runtime_error(fmt::format("Failed to load PDF: {}", path));
|
||||
|
||||
// Extract metadata
|
||||
Metadata metadata {
|
||||
.title = getMetadata("Title" ),
|
||||
.author = getMetadata("Author" ),
|
||||
.subject = getMetadata("Subject" ),
|
||||
.keywords = getMetadata("Keywords"),
|
||||
};
|
||||
postInit(std::move(metadata));
|
||||
}
|
||||
|
||||
~PdfDocumentReader() override
|
||||
{
|
||||
if (m_page)
|
||||
FPDF_ClosePage(m_page);
|
||||
if (m_doc)
|
||||
FPDF_CloseDocument(m_doc);
|
||||
FPDF_DestroyLibrary();
|
||||
}
|
||||
|
||||
int page() const override { return m_currentPage; }
|
||||
|
||||
private:
|
||||
std::optional<QString> advance() override
|
||||
{
|
||||
QString word;
|
||||
do {
|
||||
while (!m_stream || m_stream->atEnd()) {
|
||||
if (m_currentPage >= FPDF_GetPageCount(m_doc))
|
||||
return std::nullopt;
|
||||
|
||||
if (m_page)
|
||||
FPDF_ClosePage(m_page);
|
||||
m_page = FPDF_LoadPage(m_doc, m_currentPage++);
|
||||
if (!m_page)
|
||||
throw std::runtime_error("Failed to load page.");
|
||||
|
||||
m_pageText = extractTextFromPage(m_page);
|
||||
m_stream.emplace(&m_pageText);
|
||||
}
|
||||
*m_stream >> word;
|
||||
} while (word.isEmpty());
|
||||
return word;
|
||||
}
|
||||
|
||||
QString getMetadata(FPDF_BYTESTRING key)
|
||||
{
|
||||
// FPDF_GetMetaText includes a 2-byte null terminator
|
||||
ulong nBytes = FPDF_GetMetaText(m_doc, key, nullptr, 0);
|
||||
if (nBytes <= sizeof (FPDF_WCHAR))
|
||||
return { "" };
|
||||
QByteArray buffer(nBytes, Qt::Uninitialized);
|
||||
ulong nResultBytes = FPDF_GetMetaText(m_doc, key, buffer.data(), buffer.size());
|
||||
Q_ASSERT(nResultBytes % 2 == 0);
|
||||
Q_ASSERT(nResultBytes <= nBytes);
|
||||
return QString::fromUtf16(reinterpret_cast<const char16_t *>(buffer.data()), nResultBytes / 2 - 1);
|
||||
}
|
||||
|
||||
QString extractTextFromPage(FPDF_PAGE page)
|
||||
{
|
||||
FPDF_TEXTPAGE textPage = FPDFText_LoadPage(page);
|
||||
if (!textPage)
|
||||
throw std::runtime_error("Failed to load text page.");
|
||||
|
||||
int nChars = FPDFText_CountChars(textPage);
|
||||
if (!nChars)
|
||||
return {};
|
||||
// FPDFText_GetText includes a 2-byte null terminator
|
||||
QByteArray buffer((nChars + 1) * sizeof (FPDF_WCHAR), Qt::Uninitialized);
|
||||
int nResultChars = FPDFText_GetText(textPage, 0, nChars, reinterpret_cast<ushort *>(buffer.data()));
|
||||
Q_ASSERT(nResultChars <= nChars + 1);
|
||||
|
||||
FPDFText_ClosePage(textPage);
|
||||
return QString::fromUtf16(reinterpret_cast<const char16_t *>(buffer.data()), nResultChars - 1);
|
||||
}
|
||||
|
||||
FPDF_DOCUMENT m_doc = nullptr;
|
||||
FPDF_PAGE m_page = nullptr;
|
||||
int m_currentPage = 0;
|
||||
QString m_pageText;
|
||||
std::optional<QTextStream> m_stream;
|
||||
};
|
||||
#endif // !defined(GPT4ALL_USE_QTPDF)
|
||||
|
||||
class WordDocumentReader final : public DocumentReader {
|
||||
public:
|
||||
|
||||
@@ -108,7 +108,7 @@ bool EmbeddingLLMWorker::loadModel()
|
||||
bool actualDeviceIsCPU = true;
|
||||
|
||||
#if defined(Q_OS_MAC) && defined(__aarch64__)
|
||||
if (m_model->implementation().buildVariant() == "metal")
|
||||
if (m_model->implementation().buildBackend() == "metal")
|
||||
actualDeviceIsCPU = false;
|
||||
#else
|
||||
if (requestedDevice != "CPU") {
|
||||
|
||||
@@ -51,14 +51,12 @@ auto JinjaMessage::keys() const -> const std::unordered_set<std::string_view> &
|
||||
static const std::unordered_set<std::string_view> userKeys
|
||||
{ "role", "content", "sources", "prompt_attachments" };
|
||||
switch (m_item->type()) {
|
||||
using enum MessageItem::Type;
|
||||
using enum ChatItem::Type;
|
||||
case System:
|
||||
case Response:
|
||||
case ToolResponse:
|
||||
return baseKeys;
|
||||
case Prompt:
|
||||
return userKeys;
|
||||
break;
|
||||
}
|
||||
Q_UNREACHABLE();
|
||||
}
|
||||
@@ -69,18 +67,16 @@ bool operator==(const JinjaMessage &a, const JinjaMessage &b)
|
||||
return true;
|
||||
const auto &[ia, ib] = std::tie(*a.m_item, *b.m_item);
|
||||
auto type = ia.type();
|
||||
if (type != ib.type() || ia.content() != ib.content())
|
||||
if (type != ib.type() || ia.value != ib.value)
|
||||
return false;
|
||||
|
||||
switch (type) {
|
||||
using enum MessageItem::Type;
|
||||
using enum ChatItem::Type;
|
||||
case System:
|
||||
case Response:
|
||||
case ToolResponse:
|
||||
return true;
|
||||
case Prompt:
|
||||
return ia.sources() == ib.sources() && ia.promptAttachments() == ib.promptAttachments();
|
||||
break;
|
||||
return ia.sources == ib.sources && ia.promptAttachments == ib.promptAttachments;
|
||||
}
|
||||
Q_UNREACHABLE();
|
||||
}
|
||||
@@ -88,28 +84,26 @@ bool operator==(const JinjaMessage &a, const JinjaMessage &b)
|
||||
const JinjaFieldMap<JinjaMessage> JinjaMessage::s_fields = {
|
||||
{ "role", [](auto &m) {
|
||||
switch (m.item().type()) {
|
||||
using enum MessageItem::Type;
|
||||
using enum ChatItem::Type;
|
||||
case System: return "system"sv;
|
||||
case Prompt: return "user"sv;
|
||||
case Response: return "assistant"sv;
|
||||
case ToolResponse: return "tool"sv;
|
||||
break;
|
||||
}
|
||||
Q_UNREACHABLE();
|
||||
} },
|
||||
{ "content", [](auto &m) {
|
||||
if (m.version() == 0 && m.item().type() == MessageItem::Type::Prompt)
|
||||
if (m.version() == 0 && m.item().type() == ChatItem::Type::Prompt)
|
||||
return m.item().bakedPrompt().toStdString();
|
||||
return m.item().content().toStdString();
|
||||
return m.item().value.toStdString();
|
||||
} },
|
||||
{ "sources", [](auto &m) {
|
||||
auto sources = m.item().sources() | views::transform([](auto &r) {
|
||||
auto sources = m.item().sources | views::transform([](auto &r) {
|
||||
return jinja2::GenericMap([map = std::make_shared<JinjaResultInfo>(r)] { return map.get(); });
|
||||
});
|
||||
return jinja2::ValuesList(sources.begin(), sources.end());
|
||||
} },
|
||||
{ "prompt_attachments", [](auto &m) {
|
||||
auto attachments = m.item().promptAttachments() | views::transform([](auto &pa) {
|
||||
auto attachments = m.item().promptAttachments | views::transform([](auto &pa) {
|
||||
return jinja2::GenericMap([map = std::make_shared<JinjaPromptAttachment>(pa)] { return map.get(); });
|
||||
});
|
||||
return jinja2::ValuesList(attachments.begin(), attachments.end());
|
||||
|
||||
@@ -86,12 +86,12 @@ private:
|
||||
|
||||
class JinjaMessage : public JinjaHelper<JinjaMessage> {
|
||||
public:
|
||||
explicit JinjaMessage(uint version, const MessageItem &item) noexcept
|
||||
explicit JinjaMessage(uint version, const ChatItem &item) noexcept
|
||||
: m_version(version), m_item(&item) {}
|
||||
|
||||
const JinjaMessage &value () const { return *this; }
|
||||
uint version() const { return m_version; }
|
||||
const MessageItem &item () const { return *m_item; }
|
||||
const ChatItem &item () const { return *m_item; }
|
||||
|
||||
size_t GetSize() const override { return keys().size(); }
|
||||
bool HasValue(const std::string &name) const override { return keys().contains(name); }
|
||||
@@ -107,7 +107,7 @@ private:
|
||||
private:
|
||||
static const JinjaFieldMap<JinjaMessage> s_fields;
|
||||
uint m_version;
|
||||
const MessageItem *m_item;
|
||||
const ChatItem *m_item;
|
||||
|
||||
friend class JinjaHelper<JinjaMessage>;
|
||||
friend bool operator==(const JinjaMessage &a, const JinjaMessage &b);
|
||||
|
||||
@@ -1,680 +0,0 @@
|
||||
// The map in this file is automatically generated by Jared. Do not hand-edit it.
|
||||
|
||||
#include "jinja_replacements.h"
|
||||
|
||||
// This is a list of prompt templates known to GPT4All and their associated replacements which are automatically used
|
||||
// instead when loading the chat template from GGUF. These exist for two primary reasons:
|
||||
// - HuggingFace model authors make ugly chat templates because they do not expect the end user to see them;
|
||||
// - and our Jinja2Cpp-based template parsing is not fully compatible with HuggingFace transformers and jinja2.
|
||||
|
||||
// Below is a list of known incompatibilities with the official HF jinja2 implementation. These are not all necessarily
|
||||
// reflected in the below substitution list, and this cannot be an exhaustive list because there are a plethora of edge
|
||||
// cases in template parsing in which jinja2 and Jinja2Cpp differ. These are differences that could be reasonably
|
||||
// expected to affect chat templates that could be seen in the wild, or that cause a crash:
|
||||
// - Jinja2Cpp crashes (in debug builds) if given the template `a[""(`
|
||||
// - Jinja2Cpp does not support these jinja2 constructs:
|
||||
// - `is not none`
|
||||
// - list slicing, e.g. `messages[1:]`
|
||||
// - the jinja2.ext.loopcontrols extension, which HF enables by default
|
||||
// - a missing space after a quote in substitution (e.g. `{{ 'foo'}}`), which *has* been seen in the wild
|
||||
// - GPT4All does not currently support these HuggingFace template features:
|
||||
// - customized "tojson" filter (we provide the built-in Jinja2Cpp one)
|
||||
// - the AssistantTracker extension
|
||||
|
||||
|
||||
// The substitution list.
|
||||
// For templates that apply to models listed in models3.json, these should be copied there as well for best
|
||||
// compatibility with older versions of GPT4All.
|
||||
|
||||
const std::unordered_map<std::string_view, std::string_view> CHAT_TEMPLATE_SUBSTITUTIONS {
|
||||
// calme-2.1-phi3.5-4b.Q6_K.gguf (reported by ThilotE on Discord), Phi-3.5-mini-instruct-Q4_0.gguf (nomic-ai/gpt4all#3345)
|
||||
{
|
||||
// original
|
||||
R"TEMPLATE({% for message in messages %}{% if message['role'] == 'system' and message['content'] %}{{'<|system|>
|
||||
' + message['content'] + '<|end|>
|
||||
'}}{% elif message['role'] == 'user' %}{{'<|user|>
|
||||
' + message['content'] + '<|end|>
|
||||
'}}{% elif message['role'] == 'assistant' %}{{'<|assistant|>
|
||||
' + message['content'] + '<|end|>
|
||||
'}}{% endif %}{% endfor %}{% if add_generation_prompt %}{{ '<|assistant|>
|
||||
' }}{% else %}{{ eos_token }}{% endif %})TEMPLATE",
|
||||
// replacement
|
||||
R"TEMPLATE({%- for message in messages %}
|
||||
{%- if message['role'] == 'system' and message['content'] %}
|
||||
{{- '<|system|>\n' + message['content'] + '<|end|>\n' }}
|
||||
{%- elif message['role'] == 'user' %}
|
||||
{{- '<|user|>\n' + message['content'] + '<|end|>\n' }}
|
||||
{%- elif message['role'] == 'assistant' %}
|
||||
{{- '<|assistant|>\n' + message['content'] + '<|end|>\n' }}
|
||||
{%- endif %}
|
||||
{%- endfor %}
|
||||
{%- if add_generation_prompt %}
|
||||
{{- '<|assistant|>\n' }}
|
||||
{%- else %}
|
||||
{{- eos_token }}
|
||||
{%- endif %})TEMPLATE",
|
||||
},
|
||||
// gemma-2-9b-it-Q4_0.gguf (nomic-ai/gpt4all#3282)
|
||||
{
|
||||
// original
|
||||
R"TEMPLATE({{ bos_token }}{% if messages[0]['role'] == 'system' %}{{ raise_exception('System role not supported') }}{% endif %}{% for message in messages %}{% if (message['role'] == 'user') != (loop.index0 % 2 == 0) %}{{ raise_exception('Conversation roles must alternate user/assistant/user/assistant/...') }}{% endif %}{% if (message['role'] == 'assistant') %}{% set role = 'model' %}{% else %}{% set role = message['role'] %}{% endif %}{{ '<start_of_turn>' + role + '
|
||||
' + message['content'] | trim + '<end_of_turn>
|
||||
' }}{% endfor %}{% if add_generation_prompt %}{{'<start_of_turn>model
|
||||
'}}{% endif %})TEMPLATE",
|
||||
// replacement
|
||||
R"TEMPLATE({{- bos_token }}
|
||||
{%- if messages[0]['role'] == 'system' %}
|
||||
{{- raise_exception('System role not supported') }}
|
||||
{%- endif %}
|
||||
{%- for message in messages %}
|
||||
{%- if (message['role'] == 'user') != (loop.index0 % 2 == 0) %}
|
||||
{{- raise_exception('Conversation roles must alternate user/assistant/user/assistant/...') }}
|
||||
{%- endif %}
|
||||
{%- if message['role'] == 'assistant' %}
|
||||
{%- set role = 'model' %}
|
||||
{%- else %}
|
||||
{%- set role = message['role'] %}
|
||||
{%- endif %}
|
||||
{{- '<start_of_turn>' + role + '\n' + message['content'] | trim + '<end_of_turn>\n' }}
|
||||
{%- endfor %}
|
||||
{%- if add_generation_prompt %}
|
||||
{{- '<start_of_turn>model\n' }}
|
||||
{%- endif %})TEMPLATE",
|
||||
},
|
||||
// ghost-7b-v0.9.1-Q4_0.gguf
|
||||
{
|
||||
// original
|
||||
R"TEMPLATE({% for message in messages %}
|
||||
{% if message['role'] == 'user' %}
|
||||
{{ '<|user|>
|
||||
' + message['content'] + eos_token }}
|
||||
{% elif message['role'] == 'system' %}
|
||||
{{ '<|system|>
|
||||
' + message['content'] + eos_token }}
|
||||
{% elif message['role'] == 'assistant' %}
|
||||
{{ '<|assistant|>
|
||||
' + message['content'] + eos_token }}
|
||||
{% endif %}
|
||||
{% if loop.last and add_generation_prompt %}
|
||||
{{ '<|assistant|>' }}
|
||||
{% endif %}
|
||||
{% endfor %})TEMPLATE",
|
||||
// replacement
|
||||
R"TEMPLATE({%- for message in messages %}
|
||||
{%- if message['role'] == 'user' %}
|
||||
{{- '<|user|>\n' + message['content'] + eos_token }}
|
||||
{%- elif message['role'] == 'system' %}
|
||||
{{- '<|system|>\n' + message['content'] + eos_token }}
|
||||
{%- elif message['role'] == 'assistant' %}
|
||||
{{- '<|assistant|>\n' + message['content'] + eos_token }}
|
||||
{%- endif %}
|
||||
{%- if loop.last and add_generation_prompt %}
|
||||
{{- '<|assistant|>' }}
|
||||
{%- endif %}
|
||||
{%- endfor %})TEMPLATE",
|
||||
},
|
||||
// Hermes-3-Llama-3.2-3B.Q4_0.gguf, mistral-7b-openorca.gguf2.Q4_0.gguf
|
||||
{
|
||||
// original
|
||||
R"TEMPLATE({% if not add_generation_prompt is defined %}{% set add_generation_prompt = false %}{% endif %}{% for message in messages %}{{'<|im_start|>' + message['role'] + '
|
||||
' + message['content'] + '<|im_end|>' + '
|
||||
'}}{% endfor %}{% if add_generation_prompt %}{{ '<|im_start|>assistant
|
||||
' }}{% endif %})TEMPLATE",
|
||||
// replacement
|
||||
R"TEMPLATE({%- for message in messages %}
|
||||
{{- '<|im_start|>' + message['role'] + '\n' + message['content'] + '<|im_end|>\n' }}
|
||||
{%- endfor %}
|
||||
{%- if add_generation_prompt %}
|
||||
{{- '<|im_start|>assistant\n' }}
|
||||
{%- endif %})TEMPLATE",
|
||||
},
|
||||
// Llama-3.2-1B-Instruct-Q4_0.gguf, Llama-3.2-3B-Instruct-Q4_0.gguf, SummLlama3.2-3B-Q4_0.gguf (nomic-ai/gpt4all#3309)
|
||||
{
|
||||
// original
|
||||
R"TEMPLATE({{- bos_token }}
|
||||
{%- if custom_tools is defined %}
|
||||
{%- set tools = custom_tools %}
|
||||
{%- endif %}
|
||||
{%- if not tools_in_user_message is defined %}
|
||||
{%- set tools_in_user_message = true %}
|
||||
{%- endif %}
|
||||
{%- if not date_string is defined %}
|
||||
{%- if strftime_now is defined %}
|
||||
{%- set date_string = strftime_now("%d %b %Y") %}
|
||||
{%- else %}
|
||||
{%- set date_string = "26 Jul 2024" %}
|
||||
{%- endif %}
|
||||
{%- endif %}
|
||||
{%- if not tools is defined %}
|
||||
{%- set tools = none %}
|
||||
{%- endif %}
|
||||
|
||||
{#- This block extracts the system message, so we can slot it into the right place. #}
|
||||
{%- if messages[0]['role'] == 'system' %}
|
||||
{%- set system_message = messages[0]['content']|trim %}
|
||||
{%- set messages = messages[1:] %}
|
||||
{%- else %}
|
||||
{%- set system_message = "" %}
|
||||
{%- endif %}
|
||||
|
||||
{#- System message #}
|
||||
{{- "<|start_header_id|>system<|end_header_id|>\n\n" }}
|
||||
{%- if tools is not none %}
|
||||
{{- "Environment: ipython\n" }}
|
||||
{%- endif %}
|
||||
{{- "Cutting Knowledge Date: December 2023\n" }}
|
||||
{{- "Today Date: " + date_string + "\n\n" }}
|
||||
{%- if tools is not none and not tools_in_user_message %}
|
||||
{{- "You have access to the following functions. To call a function, please respond with JSON for a function call." }}
|
||||
{{- 'Respond in the format {"name": function name, "parameters": dictionary of argument name and its value}.' }}
|
||||
{{- "Do not use variables.\n\n" }}
|
||||
{%- for t in tools %}
|
||||
{{- t | tojson(indent=4) }}
|
||||
{{- "\n\n" }}
|
||||
{%- endfor %}
|
||||
{%- endif %}
|
||||
{{- system_message }}
|
||||
{{- "<|eot_id|>" }}
|
||||
|
||||
{#- Custom tools are passed in a user message with some extra guidance #}
|
||||
{%- if tools_in_user_message and not tools is none %}
|
||||
{#- Extract the first user message so we can plug it in here #}
|
||||
{%- if messages | length != 0 %}
|
||||
{%- set first_user_message = messages[0]['content']|trim %}
|
||||
{%- set messages = messages[1:] %}
|
||||
{%- else %}
|
||||
{{- raise_exception("Cannot put tools in the first user message when there's no first user message!") }}
|
||||
{%- endif %}
|
||||
{{- '<|start_header_id|>user<|end_header_id|>\n\n' -}}
|
||||
{{- "Given the following functions, please respond with a JSON for a function call " }}
|
||||
{{- "with its proper arguments that best answers the given prompt.\n\n" }}
|
||||
{{- 'Respond in the format {"name": function name, "parameters": dictionary of argument name and its value}.' }}
|
||||
{{- "Do not use variables.\n\n" }}
|
||||
{%- for t in tools %}
|
||||
{{- t | tojson(indent=4) }}
|
||||
{{- "\n\n" }}
|
||||
{%- endfor %}
|
||||
{{- first_user_message + "<|eot_id|>"}}
|
||||
{%- endif %}
|
||||
|
||||
{%- for message in messages %}
|
||||
{%- if not (message.role == 'ipython' or message.role == 'tool' or 'tool_calls' in message) %}
|
||||
{{- '<|start_header_id|>' + message['role'] + '<|end_header_id|>\n\n'+ message['content'] | trim + '<|eot_id|>' }}
|
||||
{%- elif 'tool_calls' in message %}
|
||||
{%- if not message.tool_calls|length == 1 %}
|
||||
{{- raise_exception("This model only supports single tool-calls at once!") }}
|
||||
{%- endif %}
|
||||
{%- set tool_call = message.tool_calls[0].function %}
|
||||
{{- '<|start_header_id|>assistant<|end_header_id|>\n\n' -}}
|
||||
{{- '{"name": "' + tool_call.name + '", ' }}
|
||||
{{- '"parameters": ' }}
|
||||
{{- tool_call.arguments | tojson }}
|
||||
{{- "}" }}
|
||||
{{- "<|eot_id|>" }}
|
||||
{%- elif message.role == "tool" or message.role == "ipython" %}
|
||||
{{- "<|start_header_id|>ipython<|end_header_id|>\n\n" }}
|
||||
{%- if message.content is mapping or message.content is iterable %}
|
||||
{{- message.content | tojson }}
|
||||
{%- else %}
|
||||
{{- message.content }}
|
||||
{%- endif %}
|
||||
{{- "<|eot_id|>" }}
|
||||
{%- endif %}
|
||||
{%- endfor %}
|
||||
{%- if add_generation_prompt %}
|
||||
{{- '<|start_header_id|>assistant<|end_header_id|>\n\n' }}
|
||||
{%- endif %})TEMPLATE",
|
||||
// replacement
|
||||
R"TEMPLATE({{- bos_token }}
|
||||
{%- set date_string = strftime_now('%d %b %Y') %}
|
||||
|
||||
{#- This block extracts the system message, so we can slot it into the right place. #}
|
||||
{%- if messages[0]['role'] == 'system' %}
|
||||
{%- set system_message = messages[0]['content'] | trim %}
|
||||
{%- set loop_start = 1 %}
|
||||
{%- else %}
|
||||
{%- set system_message = '' %}
|
||||
{%- set loop_start = 0 %}
|
||||
{%- endif %}
|
||||
|
||||
{#- System message #}
|
||||
{{- '<|start_header_id|>system<|end_header_id|>\n\n' }}
|
||||
{{- 'Cutting Knowledge Date: December 2023\n' }}
|
||||
{{- 'Today Date: ' + date_string + '\n\n' }}
|
||||
{{- system_message }}
|
||||
{{- '<|eot_id|>' }}
|
||||
|
||||
{%- for message in messages %}
|
||||
{%- if loop.index0 >= loop_start %}
|
||||
{{- '<|start_header_id|>' + message['role'] + '<|end_header_id|>\n\n' + message['content'] | trim + '<|eot_id|>' }}
|
||||
{%- endif %}
|
||||
{%- endfor %}
|
||||
{%- if add_generation_prompt %}
|
||||
{{- '<|start_header_id|>assistant<|end_header_id|>\n\n' }}
|
||||
{%- endif %})TEMPLATE",
|
||||
},
|
||||
// Llama-3.3-70B-Instruct-Q4_0.gguf (nomic-ai/gpt4all#3305)
|
||||
{
|
||||
// original
|
||||
R"TEMPLATE({{- bos_token }}
|
||||
{%- if custom_tools is defined %}
|
||||
{%- set tools = custom_tools %}
|
||||
{%- endif %}
|
||||
{%- if not tools_in_user_message is defined %}
|
||||
{%- set tools_in_user_message = true %}
|
||||
{%- endif %}
|
||||
{%- if not date_string is defined %}
|
||||
{%- set date_string = "26 Jul 2024" %}
|
||||
{%- endif %}
|
||||
{%- if not tools is defined %}
|
||||
{%- set tools = none %}
|
||||
{%- endif %}
|
||||
|
||||
{#- This block extracts the system message, so we can slot it into the right place. #}
|
||||
{%- if messages[0]['role'] == 'system' %}
|
||||
{%- set system_message = messages[0]['content']|trim %}
|
||||
{%- set messages = messages[1:] %}
|
||||
{%- else %}
|
||||
{%- set system_message = "" %}
|
||||
{%- endif %}
|
||||
|
||||
{#- System message + builtin tools #}
|
||||
{{- "<|start_header_id|>system<|end_header_id|>\n\n" }}
|
||||
{%- if builtin_tools is defined or tools is not none %}
|
||||
{{- "Environment: ipython\n" }}
|
||||
{%- endif %}
|
||||
{%- if builtin_tools is defined %}
|
||||
{{- "Tools: " + builtin_tools | reject('equalto', 'code_interpreter') | join(", ") + "\n\n"}}
|
||||
{%- endif %}
|
||||
{{- "Cutting Knowledge Date: December 2023\n" }}
|
||||
{{- "Today Date: " + date_string + "\n\n" }}
|
||||
{%- if tools is not none and not tools_in_user_message %}
|
||||
{{- "You have access to the following functions. To call a function, please respond with JSON for a function call." }}
|
||||
{{- 'Respond in the format {"name": function name, "parameters": dictionary of argument name and its value}.' }}
|
||||
{{- "Do not use variables.\n\n" }}
|
||||
{%- for t in tools %}
|
||||
{{- t | tojson(indent=4) }}
|
||||
{{- "\n\n" }}
|
||||
{%- endfor %}
|
||||
{%- endif %}
|
||||
{{- system_message }}
|
||||
{{- "<|eot_id|>" }}
|
||||
|
||||
{#- Custom tools are passed in a user message with some extra guidance #}
|
||||
{%- if tools_in_user_message and not tools is none %}
|
||||
{#- Extract the first user message so we can plug it in here #}
|
||||
{%- if messages | length != 0 %}
|
||||
{%- set first_user_message = messages[0]['content']|trim %}
|
||||
{%- set messages = messages[1:] %}
|
||||
{%- else %}
|
||||
{{- raise_exception("Cannot put tools in the first user message when there's no first user message!") }}
|
||||
{%- endif %}
|
||||
{{- '<|start_header_id|>user<|end_header_id|>\n\n' -}}
|
||||
{{- "Given the following functions, please respond with a JSON for a function call " }}
|
||||
{{- "with its proper arguments that best answers the given prompt.\n\n" }}
|
||||
{{- 'Respond in the format {"name": function name, "parameters": dictionary of argument name and its value}.' }}
|
||||
{{- "Do not use variables.\n\n" }}
|
||||
{%- for t in tools %}
|
||||
{{- t | tojson(indent=4) }}
|
||||
{{- "\n\n" }}
|
||||
{%- endfor %}
|
||||
{{- first_user_message + "<|eot_id|>"}}
|
||||
{%- endif %}
|
||||
|
||||
{%- for message in messages %}
|
||||
{%- if not (message.role == 'ipython' or message.role == 'tool' or 'tool_calls' in message) %}
|
||||
{{- '<|start_header_id|>' + message['role'] + '<|end_header_id|>\n\n'+ message['content'] | trim + '<|eot_id|>' }}
|
||||
{%- elif 'tool_calls' in message %}
|
||||
{%- if not message.tool_calls|length == 1 %}
|
||||
{{- raise_exception("This model only supports single tool-calls at once!") }}
|
||||
{%- endif %}
|
||||
{%- set tool_call = message.tool_calls[0].function %}
|
||||
{%- if builtin_tools is defined and tool_call.name in builtin_tools %}
|
||||
{{- '<|start_header_id|>assistant<|end_header_id|>\n\n' -}}
|
||||
{{- "<|python_tag|>" + tool_call.name + ".call(" }}
|
||||
{%- for arg_name, arg_val in tool_call.arguments | items %}
|
||||
{{- arg_name + '="' + arg_val + '"' }}
|
||||
{%- if not loop.last %}
|
||||
{{- ", " }}
|
||||
{%- endif %}
|
||||
{%- endfor %}
|
||||
{{- ")" }}
|
||||
{%- else %}
|
||||
{{- '<|start_header_id|>assistant<|end_header_id|>\n\n' -}}
|
||||
{{- '{"name": "' + tool_call.name + '", ' }}
|
||||
{{- '"parameters": ' }}
|
||||
{{- tool_call.arguments | tojson }}
|
||||
{{- "}" }}
|
||||
{%- endif %}
|
||||
{%- if builtin_tools is defined %}
|
||||
{#- This means we're in ipython mode #}
|
||||
{{- "<|eom_id|>" }}
|
||||
{%- else %}
|
||||
{{- "<|eot_id|>" }}
|
||||
{%- endif %}
|
||||
{%- elif message.role == "tool" or message.role == "ipython" %}
|
||||
{{- "<|start_header_id|>ipython<|end_header_id|>\n\n" }}
|
||||
{%- if message.content is mapping or message.content is iterable %}
|
||||
{{- message.content | tojson }}
|
||||
{%- else %}
|
||||
{{- message.content }}
|
||||
{%- endif %}
|
||||
{{- "<|eot_id|>" }}
|
||||
{%- endif %}
|
||||
{%- endfor %}
|
||||
{%- if add_generation_prompt %}
|
||||
{{- '<|start_header_id|>assistant<|end_header_id|>\n\n' }}
|
||||
{%- endif %})TEMPLATE",
|
||||
// replacement
|
||||
R"TEMPLATE({{- bos_token }}
|
||||
{%- set date_string = strftime_now('%d %b %Y') %}
|
||||
|
||||
{#- This block extracts the system message, so we can slot it into the right place. #}
|
||||
{%- if messages[0]['role'] == 'system' %}
|
||||
{%- set system_message = messages[0]['content'] | trim %}
|
||||
{%- set loop_start = 1 %}
|
||||
{%- else %}
|
||||
{%- set system_message = '' %}
|
||||
{%- set loop_start = 0 %}
|
||||
{%- endif %}
|
||||
|
||||
{#- System message #}
|
||||
{{- '<|start_header_id|>system<|end_header_id|>\n\n' }}
|
||||
{{- 'Cutting Knowledge Date: December 2023\n' }}
|
||||
{{- 'Today Date: ' + date_string + '\n\n' }}
|
||||
{{- system_message }}
|
||||
{{- '<|eot_id|>' }}
|
||||
|
||||
{%- for message in messages %}
|
||||
{%- if loop.index0 >= loop_start %}
|
||||
{{- '<|start_header_id|>' + message['role'] + '<|end_header_id|>\n\n' + message['content'] | trim + '<|eot_id|>' }}
|
||||
{%- endif %}
|
||||
{%- endfor %}
|
||||
{%- if add_generation_prompt %}
|
||||
{{- '<|start_header_id|>assistant<|end_header_id|>\n\n' }}
|
||||
{%- endif %})TEMPLATE",
|
||||
},
|
||||
// Llama3-DiscoLeo-Instruct-8B-32k-v0.1-Q4_0.gguf (nomic-ai/gpt4all#3347)
|
||||
{
|
||||
// original
|
||||
R"TEMPLATE({% set loop_messages = messages %}{% for message in loop_messages %}{% set content = '<|start_header_id|>' + message['role'] + '<|end_header_id|>
|
||||
|
||||
'+ message['content'] | trim + '<|eot_id|>' %}{% if loop.index0 == 0 %}{% set content = bos_token + content %}{% endif %}{{ content }}{% endfor %}{% if add_generation_prompt %}{{ '<|start_header_id|>assistant<|end_header_id|>
|
||||
|
||||
' }}{% endif %})TEMPLATE",
|
||||
// replacement
|
||||
R"TEMPLATE({%- for message in messages %}
|
||||
{%- set content = '<|start_header_id|>' + message['role'] + '<|end_header_id|>\n\n' + message['content'] | trim + '<|eot_id|>' %}
|
||||
{%- if loop.index0 == 0 %}
|
||||
{%- set content = bos_token + content %}
|
||||
{%- endif %}
|
||||
{{- content }}
|
||||
{%- endfor %}
|
||||
{%- if add_generation_prompt %}
|
||||
{{- '<|start_header_id|>assistant<|end_header_id|>\n\n' }}
|
||||
{%- endif %})TEMPLATE",
|
||||
},
|
||||
// Meta-Llama-3.1-8B-Instruct-128k-Q4_0.gguf
|
||||
{
|
||||
// original
|
||||
R"TEMPLATE({% set loop_messages = messages %}{% for message in loop_messages %}{% set content = '<|start_header_id|>' + message['role'] + '<|end_header_id|>
|
||||
|
||||
'+ message['content'] | trim + '<|eot_id|>' %}{% if loop.index0 == 0 %}{% set content = bos_token + content %}{% endif %}{{ content }}{% endfor %}{{ '<|start_header_id|>assistant<|end_header_id|>
|
||||
|
||||
' }})TEMPLATE",
|
||||
// replacement
|
||||
R"TEMPLATE({%- set loop_messages = messages %}
|
||||
{%- for message in loop_messages %}
|
||||
{%- set content = '<|start_header_id|>' + message['role'] + '<|end_header_id|>\n\n'+ message['content'] | trim + '<|eot_id|>' %}
|
||||
{%- if loop.index0 == 0 %}
|
||||
{%- set content = bos_token + content %}
|
||||
{%- endif %}
|
||||
{{- content }}
|
||||
{%- endfor %}
|
||||
{{- '<|start_header_id|>assistant<|end_header_id|>\n\n' }})TEMPLATE",
|
||||
},
|
||||
// Meta-Llama-3-8B-Instruct.Q4_0.gguf
|
||||
{
|
||||
// original
|
||||
R"TEMPLATE({% set loop_messages = messages %}{% for message in loop_messages %}{% set content = '<|start_header_id|>' + message['role'] + '<|end_header_id|>
|
||||
|
||||
'+ message['content'] | trim + '<|eot_id|>' %}{{ content }}{% endfor %}{% if add_generation_prompt %}{{ '<|start_header_id|>assistant<|end_header_id|>
|
||||
|
||||
' }}{% endif %})TEMPLATE",
|
||||
// replacement
|
||||
R"TEMPLATE({%- set loop_messages = messages %}
|
||||
{%- for message in loop_messages %}
|
||||
{%- set content = '<|start_header_id|>' + message['role'] + '<|end_header_id|>\n\n'+ message['content'] | trim + '<|eot_id|>' %}
|
||||
{{- content }}
|
||||
{%- endfor %}
|
||||
{%- if add_generation_prompt %}
|
||||
{{- '<|start_header_id|>assistant<|end_header_id|>\n\n' }}
|
||||
{%- endif %})TEMPLATE",
|
||||
},
|
||||
// Mistral-Nemo-Instruct-2407-Q4_0.gguf (nomic-ai/gpt4all#3284)
|
||||
{
|
||||
// original
|
||||
R"TEMPLATE({%- if messages[0]["role"] == "system" %}
|
||||
{%- set system_message = messages[0]["content"] %}
|
||||
{%- set loop_messages = messages[1:] %}
|
||||
{%- else %}
|
||||
{%- set loop_messages = messages %}
|
||||
{%- endif %}
|
||||
{%- if not tools is defined %}
|
||||
{%- set tools = none %}
|
||||
{%- endif %}
|
||||
{%- set user_messages = loop_messages | selectattr("role", "equalto", "user") | list %}
|
||||
|
||||
{#- This block checks for alternating user/assistant messages, skipping tool calling messages #}
|
||||
{%- set ns = namespace() %}
|
||||
{%- set ns.index = 0 %}
|
||||
{%- for message in loop_messages %}
|
||||
{%- if not (message.role == "tool" or message.role == "tool_results" or (message.tool_calls is defined and message.tool_calls is not none)) %}
|
||||
{%- if (message["role"] == "user") != (ns.index % 2 == 0) %}
|
||||
{{- raise_exception("After the optional system message, conversation roles must alternate user/assistant/user/assistant/...") }}
|
||||
{%- endif %}
|
||||
{%- set ns.index = ns.index + 1 %}
|
||||
{%- endif %}
|
||||
{%- endfor %}
|
||||
|
||||
{{- bos_token }}
|
||||
{%- for message in loop_messages %}
|
||||
{%- if message["role"] == "user" %}
|
||||
{%- if tools is not none and (message == user_messages[-1]) %}
|
||||
{{- "[AVAILABLE_TOOLS][" }}
|
||||
{%- for tool in tools %}
|
||||
{%- set tool = tool.function %}
|
||||
{{- '{"type": "function", "function": {' }}
|
||||
{%- for key, val in tool.items() if key != "return" %}
|
||||
{%- if val is string %}
|
||||
{{- '"' + key + '": "' + val + '"' }}
|
||||
{%- else %}
|
||||
{{- '"' + key + '": ' + val|tojson }}
|
||||
{%- endif %}
|
||||
{%- if not loop.last %}
|
||||
{{- ", " }}
|
||||
{%- endif %}
|
||||
{%- endfor %}
|
||||
{{- "}}" }}
|
||||
{%- if not loop.last %}
|
||||
{{- ", " }}
|
||||
{%- else %}
|
||||
{{- "]" }}
|
||||
{%- endif %}
|
||||
{%- endfor %}
|
||||
{{- "[/AVAILABLE_TOOLS]" }}
|
||||
{%- endif %}
|
||||
{%- if loop.last and system_message is defined %}
|
||||
{{- "[INST]" + system_message + "\n\n" + message["content"] + "[/INST]" }}
|
||||
{%- else %}
|
||||
{{- "[INST]" + message["content"] + "[/INST]" }}
|
||||
{%- endif %}
|
||||
{%- elif (message.tool_calls is defined and message.tool_calls is not none) %}
|
||||
{{- "[TOOL_CALLS][" }}
|
||||
{%- for tool_call in message.tool_calls %}
|
||||
{%- set out = tool_call.function|tojson %}
|
||||
{{- out[:-1] }}
|
||||
{%- if not tool_call.id is defined or tool_call.id|length != 9 %}
|
||||
{{- raise_exception("Tool call IDs should be alphanumeric strings with length 9!") }}
|
||||
{%- endif %}
|
||||
{{- ', "id": "' + tool_call.id + '"}' }}
|
||||
{%- if not loop.last %}
|
||||
{{- ", " }}
|
||||
{%- else %}
|
||||
{{- "]" + eos_token }}
|
||||
{%- endif %}
|
||||
{%- endfor %}
|
||||
{%- elif message["role"] == "assistant" %}
|
||||
{{- message["content"] + eos_token}}
|
||||
{%- elif message["role"] == "tool_results" or message["role"] == "tool" %}
|
||||
{%- if message.content is defined and message.content.content is defined %}
|
||||
{%- set content = message.content.content %}
|
||||
{%- else %}
|
||||
{%- set content = message.content %}
|
||||
{%- endif %}
|
||||
{{- '[TOOL_RESULTS]{"content": ' + content|string + ", " }}
|
||||
{%- if not message.tool_call_id is defined or message.tool_call_id|length != 9 %}
|
||||
{{- raise_exception("Tool call IDs should be alphanumeric strings with length 9!") }}
|
||||
{%- endif %}
|
||||
{{- '"call_id": "' + message.tool_call_id + '"}[/TOOL_RESULTS]' }}
|
||||
{%- else %}
|
||||
{{- raise_exception("Only user and assistant roles are supported, with the exception of an initial optional system message!") }}
|
||||
{%- endif %}
|
||||
{%- endfor %})TEMPLATE",
|
||||
// replacement
|
||||
R"TEMPLATE({%- if messages[0]['role'] == 'system' %}
|
||||
{%- set system_message = messages[0]['content'] %}
|
||||
{%- set loop_start = 1 %}
|
||||
{%- else %}
|
||||
{%- set loop_start = 0 %}
|
||||
{%- endif %}
|
||||
|
||||
{{- bos_token }}
|
||||
{%- for message in messages %}
|
||||
{#- This block checks for alternating user/assistant messages, skipping tool calling messages #}
|
||||
{%- if (message['role'] == 'user') != (loop.index0 % 2 == 0) %}
|
||||
{{- raise_exception('After the optional system message, conversation roles must alternate user/assistant/user/assistant/...') }}
|
||||
{%- endif %}
|
||||
|
||||
{%- if message['role'] == 'user' %}
|
||||
{%- if loop.last and loop_start == 1 %}
|
||||
{{- '[INST]' + system_message + '\n\n' + message['content'] + '[/INST]' }}
|
||||
{%- else %}
|
||||
{{- '[INST]' + message['content'] + '[/INST]' }}
|
||||
{%- endif %}
|
||||
{%- elif message['role'] == 'assistant' %}
|
||||
{{- message['content'] + eos_token }}
|
||||
{%- else %}
|
||||
{{- raise_exception('Only user and assistant roles are supported, with the exception of an initial optional system message!') }}
|
||||
{%- endif %}
|
||||
{%- endfor %})TEMPLATE",
|
||||
},
|
||||
// Nous-Hermes-2-Mistral-7B-DPO.Q4_0.gguf
|
||||
{
|
||||
// original
|
||||
R"TEMPLATE({% for message in messages %}{{'<|im_start|>' + message['role'] + '
|
||||
' + message['content'] + '<|im_end|>' + '
|
||||
'}}{% endfor %}{% if add_generation_prompt %}{{ '<|im_start|>assistant
|
||||
' }}{% endif %})TEMPLATE",
|
||||
// replacement
|
||||
R"TEMPLATE({%- for message in messages %}
|
||||
{{- '<|im_start|>' + message['role'] + '\n' + message['content'] + '<|im_end|>\n' }}
|
||||
{%- endfor %}
|
||||
{%- if add_generation_prompt %}
|
||||
{{- '<|im_start|>assistant\n' }}
|
||||
{%- endif %})TEMPLATE",
|
||||
},
|
||||
// occiglot-7b-de-en-instruct.Q4_0.gguf (nomic-ai/gpt4all#3283)
|
||||
{
|
||||
// original
|
||||
R"TEMPLATE({{'<s>'}}{% if messages[0]['role'] == 'system' %}{% set loop_messages = messages[1:] %}{% set system_message = messages[0]['content'] %}{% else %}{% set loop_messages = messages %}{% set system_message = 'You are a helpful assistant. Please give a long and detailed answer.' %}{% endif %}{% if not add_generation_prompt is defined %}{% set add_generation_prompt = false %}{% endif %}{% for message in loop_messages %}{% if loop.index0 == 0 %}{{'<|im_start|>system
|
||||
' + system_message + '<|im_end|>
|
||||
'}}{% endif %}{{'<|im_start|>' + message['role'] + '
|
||||
' + message['content'] + '<|im_end|>' + '
|
||||
'}}{% endfor %}{% if add_generation_prompt %}{{ '<|im_start|>assistant
|
||||
' }}{% endif %})TEMPLATE",
|
||||
// replacement
|
||||
R"TEMPLATE({{- bos_token }}
|
||||
{%- if messages[0]['role'] == 'system' %}
|
||||
{%- set loop_start = 1 %}
|
||||
{%- set system_message = messages[0]['content'] %}
|
||||
{%- else %}
|
||||
{%- set loop_start = 0 %}
|
||||
{%- set system_message = 'You are a helpful assistant. Please give a long and detailed answer.' %}
|
||||
{%- endif %}
|
||||
{{- '<|im_start|>system\n' + system_message + '<|im_end|>\n' }}
|
||||
{%- for message in messages %}
|
||||
{%- if loop.index0 >= loop_start %}
|
||||
{{- '<|im_start|>' + message['role'] + '\n' + message['content'] + '<|im_end|>\n' }}
|
||||
{%- endif %}
|
||||
{%- endfor %}
|
||||
{%- if add_generation_prompt %}
|
||||
{{- '<|im_start|>assistant\n' }}
|
||||
{%- endif %})TEMPLATE",
|
||||
},
|
||||
// Phi-3.1-mini-128k-instruct-Q4_0.gguf (nomic-ai/gpt4all#3346)
|
||||
{
|
||||
// original
|
||||
R"TEMPLATE({% for message in messages %}{% if message['role'] == 'system' %}{{'<|system|>
|
||||
' + message['content'] + '<|end|>
|
||||
'}}{% elif message['role'] == 'user' %}{{'<|user|>
|
||||
' + message['content'] + '<|end|>
|
||||
'}}{% elif message['role'] == 'assistant' %}{{'<|assistant|>
|
||||
' + message['content'] + '<|end|>
|
||||
'}}{% endif %}{% endfor %}{% if add_generation_prompt %}{{ '<|assistant|>
|
||||
' }}{% else %}{{ eos_token }}{% endif %})TEMPLATE",
|
||||
// replacement
|
||||
R"TEMPLATE({%- for message in messages %}
|
||||
{%- if message['role'] == 'system' %}
|
||||
{{-'<|system|>\n' + message['content'] + '<|end|>\n'}}
|
||||
{%- elif message['role'] == 'user' %}
|
||||
{{- '<|user|>\n' + message['content'] + '<|end|>\n' }}
|
||||
{%- elif message['role'] == 'assistant' %}
|
||||
{{- '<|assistant|>\n' + message['content'] + '<|end|>\n' }}
|
||||
{%- endif %}
|
||||
{%- endfor %}
|
||||
{%- if add_generation_prompt %}
|
||||
{{- '<|assistant|>\n' }}
|
||||
{%- else %}
|
||||
{{- eos_token }}
|
||||
{%- endif %})TEMPLATE",
|
||||
},
|
||||
// Phi-3-mini-4k-instruct.Q4_0.gguf
|
||||
{
|
||||
// original
|
||||
R"TEMPLATE({{ bos_token }}{% for message in messages %}{{'<|' + message['role'] + '|>' + '
|
||||
' + message['content'] + '<|end|>
|
||||
' }}{% endfor %}{% if add_generation_prompt %}{{ '<|assistant|>
|
||||
' }}{% else %}{{ eos_token }}{% endif %})TEMPLATE",
|
||||
// replacement
|
||||
R"TEMPLATE({{- bos_token }}
|
||||
{%- for message in messages %}
|
||||
{{- '<|' + message['role'] + '|>\n' + message['content'] + '<|end|>\n' }}
|
||||
{%- endfor %}
|
||||
{%- if add_generation_prompt %}
|
||||
{{- '<|assistant|>\n' }}
|
||||
{%- else %}
|
||||
{{- eos_token }}
|
||||
{%- endif %})TEMPLATE",
|
||||
},
|
||||
// qwen2-1_5b-instruct-q4_0.gguf (nomic-ai/gpt4all#3263), qwen2-72b-instruct-q4_0.gguf
|
||||
{
|
||||
// original
|
||||
R"TEMPLATE({% for message in messages %}{% if loop.first and messages[0]['role'] != 'system' %}{{ '<|im_start|>system
|
||||
You are a helpful assistant.<|im_end|>
|
||||
' }}{% endif %}{{'<|im_start|>' + message['role'] + '
|
||||
' + message['content'] + '<|im_end|>' + '
|
||||
'}}{% endfor %}{% if add_generation_prompt %}{{ '<|im_start|>assistant
|
||||
' }}{% endif %})TEMPLATE",
|
||||
// replacement
|
||||
R"TEMPLATE({%- for message in messages %}
|
||||
{%- if loop.first and messages[0]['role'] != 'system' %}
|
||||
{{- '<|im_start|>system\nYou are a helpful assistant.<|im_end|>\n' }}
|
||||
{%- endif %}
|
||||
{{- '<|im_start|>' + message['role'] + '\n' + message['content'] + '<|im_end|>\n' }}
|
||||
{%- endfor %}
|
||||
{%- if add_generation_prompt %}
|
||||
{{- '<|im_start|>assistant\n' }}
|
||||
{%- endif %})TEMPLATE",
|
||||
},
|
||||
};
|
||||
@@ -1,6 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <string_view>
|
||||
#include <unordered_map>
|
||||
|
||||
extern const std::unordered_map<std::string_view, std::string_view> CHAT_TEMPLATE_SUBSTITUTIONS;
|
||||
@@ -7,7 +7,6 @@
|
||||
#include "modellist.h"
|
||||
#include "mysettings.h"
|
||||
#include "network.h"
|
||||
#include "toolmodel.h"
|
||||
|
||||
#include <gpt4all-backend/llmodel.h>
|
||||
#include <singleapplication.h>
|
||||
@@ -25,10 +24,6 @@
|
||||
#include <QVariant>
|
||||
#include <Qt>
|
||||
|
||||
#ifndef GPT4ALL_USE_QTPDF
|
||||
# include <fpdfview.h>
|
||||
#endif
|
||||
|
||||
#ifdef Q_OS_LINUX
|
||||
# include <QIcon>
|
||||
#endif
|
||||
@@ -62,10 +57,6 @@ static void raiseWindow(QWindow *window)
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
#ifndef GPT4ALL_USE_QTPDF
|
||||
FPDF_InitLibrary();
|
||||
#endif
|
||||
|
||||
QCoreApplication::setOrganizationName("nomic.ai");
|
||||
QCoreApplication::setOrganizationDomain("gpt4all.io");
|
||||
QCoreApplication::setApplicationName("GPT4All");
|
||||
@@ -125,8 +116,6 @@ int main(int argc, char *argv[])
|
||||
qmlRegisterSingletonInstance("download", 1, 0, "Download", Download::globalInstance());
|
||||
qmlRegisterSingletonInstance("network", 1, 0, "Network", Network::globalInstance());
|
||||
qmlRegisterSingletonInstance("localdocs", 1, 0, "LocalDocs", LocalDocs::globalInstance());
|
||||
qmlRegisterSingletonInstance("toollist", 1, 0, "ToolList", ToolModel::globalInstance());
|
||||
qmlRegisterUncreatableMetaObject(ToolEnums::staticMetaObject, "toolenums", 1, 0, "ToolEnums", "Error: only enums");
|
||||
qmlRegisterUncreatableMetaObject(MySettingsEnums::staticMetaObject, "mysettingsenums", 1, 0, "MySettingsEnums", "Error: only enums");
|
||||
|
||||
{
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
#include "modellist.h"
|
||||
|
||||
#include "download.h"
|
||||
#include "jinja_replacements.h"
|
||||
#include "mysettings.h"
|
||||
#include "network.h"
|
||||
|
||||
@@ -63,15 +62,15 @@ static const QString RMODEL_CHAT_TEMPLATE = uR"(<chat>
|
||||
{%- if loop.first %}
|
||||
{{- '### Context:\n' }}
|
||||
{%- endif %}
|
||||
{{- ('Collection: ' + source.collection + '\n' +
|
||||
'Path: ' + source.path + '\n' +
|
||||
'Excerpt: ' + source.text + '\n\n') | escape }}
|
||||
{{- 'Collection: ' + source.collection + '\n' +
|
||||
'Path: ' + source.path + '\n' +
|
||||
'Excerpt: ' + source.text + '\n\n' }}
|
||||
{%- endfor %}
|
||||
{%- endif %}
|
||||
{%- for attachment in message.prompt_attachments %}
|
||||
{{- (attachment.processed_content + '\n\n') | escape }}
|
||||
{{- attachment.processed_content + '\n\n' }}
|
||||
{%- endfor %}
|
||||
{{- message.content | escape }}
|
||||
{{- message.content }}
|
||||
{{- '</' + message['role'] + '>' }}
|
||||
{%- endfor %}
|
||||
</chat>)"_s;
|
||||
@@ -353,18 +352,7 @@ QVariant ModelInfo::defaultChatTemplate() const
|
||||
auto path = (dirpath + filename()).toUtf8();
|
||||
auto res = LLModel::Implementation::chatTemplate(path.constData());
|
||||
if (res) {
|
||||
std::string ggufTmpl(std::move(*res));
|
||||
if (ggufTmpl.size() >= 2 && ggufTmpl.end()[-2] != '\n' && ggufTmpl.end()[-1] == '\n')
|
||||
ggufTmpl.erase(ggufTmpl.end() - 1); // strip trailing newline for e.g. Llama-3.2-3B-Instruct
|
||||
if (
|
||||
auto replacement = CHAT_TEMPLATE_SUBSTITUTIONS.find(ggufTmpl);
|
||||
replacement != CHAT_TEMPLATE_SUBSTITUTIONS.end()
|
||||
) {
|
||||
qWarning() << "automatically substituting chat template for" << filename();
|
||||
auto &[badTemplate, goodTemplate] = *replacement;
|
||||
ggufTmpl = goodTemplate;
|
||||
}
|
||||
m_modelChatTemplate = QString::fromStdString(ggufTmpl);
|
||||
m_modelChatTemplate = QString::fromStdString(*res);
|
||||
} else {
|
||||
qWarning().nospace() << "failed to get chat template for " << filename() << ": " << res.error().c_str();
|
||||
m_modelChatTemplate = QString(); // do not retry
|
||||
@@ -485,24 +473,14 @@ GPT4AllDownloadableModels::GPT4AllDownloadableModels(QObject *parent)
|
||||
connect(this, &GPT4AllDownloadableModels::modelReset, this, &GPT4AllDownloadableModels::countChanged);
|
||||
}
|
||||
|
||||
void GPT4AllDownloadableModels::filter(const QVector<QString> &keywords)
|
||||
{
|
||||
m_keywords = keywords;
|
||||
invalidateFilter();
|
||||
}
|
||||
|
||||
bool GPT4AllDownloadableModels::filterAcceptsRow(int sourceRow,
|
||||
const QModelIndex &sourceParent) const
|
||||
{
|
||||
QModelIndex index = sourceModel()->index(sourceRow, 0, sourceParent);
|
||||
const QString description = sourceModel()->data(index, ModelList::DescriptionRole).toString();
|
||||
bool hasDescription = !description.isEmpty();
|
||||
bool hasDescription = !sourceModel()->data(index, ModelList::DescriptionRole).toString().isEmpty();
|
||||
bool isClone = sourceModel()->data(index, ModelList::IsCloneRole).toBool();
|
||||
bool isDiscovered = sourceModel()->data(index, ModelList::IsDiscoveredRole).toBool();
|
||||
bool satisfiesKeyword = m_keywords.isEmpty();
|
||||
for (const QString &k : m_keywords)
|
||||
satisfiesKeyword = description.contains(k) ? true : satisfiesKeyword;
|
||||
return !isDiscovered && hasDescription && !isClone && satisfiesKeyword;
|
||||
return !isDiscovered && hasDescription && !isClone;
|
||||
}
|
||||
|
||||
int GPT4AllDownloadableModels::count() const
|
||||
|
||||
@@ -302,16 +302,11 @@ public:
|
||||
explicit GPT4AllDownloadableModels(QObject *parent);
|
||||
int count() const;
|
||||
|
||||
Q_INVOKABLE void filter(const QVector<QString> &keywords);
|
||||
|
||||
Q_SIGNALS:
|
||||
void countChanged();
|
||||
|
||||
protected:
|
||||
bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override;
|
||||
|
||||
private:
|
||||
QVector<QString> m_keywords;
|
||||
};
|
||||
|
||||
class HuggingFaceDownloadableModels : public QSortFilterProxyModel
|
||||
|
||||
@@ -694,8 +694,7 @@ auto Server::handleCompletionRequest(const CompletionRequest &request)
|
||||
promptCtx,
|
||||
/*usedLocalDocs*/ false);
|
||||
} catch (const std::exception &e) {
|
||||
m_chatModel->setResponseValue(e.what());
|
||||
m_chatModel->setError();
|
||||
emit responseChanged(e.what());
|
||||
emit responseStopped(0);
|
||||
return makeError(QHttpServerResponder::StatusCode::InternalServerError);
|
||||
}
|
||||
@@ -773,16 +772,16 @@ auto Server::handleChatRequest(const ChatRequest &request)
|
||||
Q_ASSERT(!request.messages.isEmpty());
|
||||
|
||||
// adds prompt/response items to GUI
|
||||
std::vector<MessageInput> messages;
|
||||
QList<ChatItem> chatItems;
|
||||
for (auto &message : request.messages) {
|
||||
using enum ChatRequest::Message::Role;
|
||||
switch (message.role) {
|
||||
case System: messages.push_back({ MessageInput::Type::System, message.content }); break;
|
||||
case User: messages.push_back({ MessageInput::Type::Prompt, message.content }); break;
|
||||
case Assistant: messages.push_back({ MessageInput::Type::Response, message.content }); break;
|
||||
case System: chatItems.emplace_back(ChatItem::system_tag, message.content); break;
|
||||
case User: chatItems.emplace_back(ChatItem::prompt_tag, message.content); break;
|
||||
case Assistant: chatItems.emplace_back(ChatItem::response_tag, message.content); break;
|
||||
}
|
||||
}
|
||||
auto startOffset = m_chatModel->appendResponseWithHistory(messages);
|
||||
auto subrange = m_chatModel->appendResponseWithHistory(chatItems);
|
||||
|
||||
// FIXME(jared): taking parameters from the UI inhibits reproducibility of results
|
||||
LLModel::PromptContext promptCtx {
|
||||
@@ -802,10 +801,9 @@ auto Server::handleChatRequest(const ChatRequest &request)
|
||||
for (int i = 0; i < request.n; ++i) {
|
||||
ChatPromptResult result;
|
||||
try {
|
||||
result = promptInternalChat(m_collections, promptCtx, startOffset);
|
||||
result = promptInternalChat(m_collections, promptCtx, subrange);
|
||||
} catch (const std::exception &e) {
|
||||
m_chatModel->setResponseValue(e.what());
|
||||
m_chatModel->setError();
|
||||
emit responseChanged(e.what());
|
||||
emit responseStopped(0);
|
||||
return makeError(QHttpServerResponder::StatusCode::InternalServerError);
|
||||
}
|
||||
|
||||
@@ -1,74 +0,0 @@
|
||||
#include "tool.h"
|
||||
|
||||
#include <jinja2cpp/value.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
jinja2::Value Tool::jinjaValue() const
|
||||
{
|
||||
jinja2::ValuesList paramList;
|
||||
const QList<ToolParamInfo> p = parameters();
|
||||
for (auto &info : p) {
|
||||
std::string typeStr;
|
||||
switch (info.type) {
|
||||
using enum ToolEnums::ParamType;
|
||||
case String: typeStr = "string"; break;
|
||||
case Number: typeStr = "number"; break;
|
||||
case Integer: typeStr = "integer"; break;
|
||||
case Object: typeStr = "object"; break;
|
||||
case Array: typeStr = "array"; break;
|
||||
case Boolean: typeStr = "boolean"; break;
|
||||
case Null: typeStr = "null"; break;
|
||||
}
|
||||
jinja2::ValuesMap infoMap {
|
||||
{ "name", info.name.toStdString() },
|
||||
{ "type", typeStr},
|
||||
{ "description", info.description.toStdString() },
|
||||
{ "required", info.required }
|
||||
};
|
||||
paramList.push_back(infoMap);
|
||||
}
|
||||
|
||||
jinja2::ValuesMap params {
|
||||
{ "name", name().toStdString() },
|
||||
{ "description", description().toStdString() },
|
||||
{ "function", function().toStdString() },
|
||||
{ "parameters", paramList },
|
||||
{ "symbolicFormat", symbolicFormat().toStdString() },
|
||||
{ "examplePrompt", examplePrompt().toStdString() },
|
||||
{ "exampleCall", exampleCall().toStdString() },
|
||||
{ "exampleReply", exampleReply().toStdString() }
|
||||
};
|
||||
return params;
|
||||
}
|
||||
|
||||
void ToolCallInfo::serialize(QDataStream &stream, int version)
|
||||
{
|
||||
stream << name;
|
||||
stream << params.size();
|
||||
for (auto param : params) {
|
||||
stream << param.name;
|
||||
stream << param.type;
|
||||
stream << param.value;
|
||||
}
|
||||
stream << result;
|
||||
stream << error;
|
||||
stream << errorString;
|
||||
}
|
||||
|
||||
bool ToolCallInfo::deserialize(QDataStream &stream, int version)
|
||||
{
|
||||
stream >> name;
|
||||
qsizetype count;
|
||||
stream >> count;
|
||||
for (int i = 0; i < count; ++i) {
|
||||
ToolParam p;
|
||||
stream >> p.name;
|
||||
stream >> p.type;
|
||||
stream >> p.value;
|
||||
}
|
||||
stream >> result;
|
||||
stream >> error;
|
||||
stream >> errorString;
|
||||
return true;
|
||||
}
|
||||
@@ -1,127 +0,0 @@
|
||||
#ifndef TOOL_H
|
||||
#define TOOL_H
|
||||
|
||||
#include <QList>
|
||||
#include <QObject>
|
||||
#include <QString>
|
||||
#include <QVariant>
|
||||
#include <QtGlobal>
|
||||
|
||||
#include <jinja2cpp/value.h>
|
||||
|
||||
namespace ToolEnums
|
||||
{
|
||||
Q_NAMESPACE
|
||||
enum class Error
|
||||
{
|
||||
NoError = 0,
|
||||
TimeoutError = 2,
|
||||
UnknownError = 499,
|
||||
};
|
||||
Q_ENUM_NS(Error)
|
||||
|
||||
enum class ParamType { String, Number, Integer, Object, Array, Boolean, Null }; // json schema types
|
||||
Q_ENUM_NS(ParamType)
|
||||
|
||||
enum class ParseState {
|
||||
None,
|
||||
InStart,
|
||||
Partial,
|
||||
Complete,
|
||||
};
|
||||
Q_ENUM_NS(ParseState)
|
||||
}
|
||||
|
||||
struct ToolParamInfo
|
||||
{
|
||||
QString name;
|
||||
ToolEnums::ParamType type;
|
||||
QString description;
|
||||
bool required;
|
||||
};
|
||||
Q_DECLARE_METATYPE(ToolParamInfo)
|
||||
|
||||
struct ToolParam
|
||||
{
|
||||
QString name;
|
||||
ToolEnums::ParamType type;
|
||||
QVariant value;
|
||||
bool operator==(const ToolParam& other) const
|
||||
{
|
||||
return name == other.name && type == other.type && value == other.value;
|
||||
}
|
||||
};
|
||||
Q_DECLARE_METATYPE(ToolParam)
|
||||
|
||||
struct ToolCallInfo
|
||||
{
|
||||
QString name;
|
||||
QList<ToolParam> params;
|
||||
QString result;
|
||||
ToolEnums::Error error = ToolEnums::Error::NoError;
|
||||
QString errorString;
|
||||
|
||||
void serialize(QDataStream &stream, int version);
|
||||
bool deserialize(QDataStream &stream, int version);
|
||||
|
||||
bool operator==(const ToolCallInfo& other) const
|
||||
{
|
||||
return name == other.name && result == other.result && params == other.params
|
||||
&& error == other.error && errorString == other.errorString;
|
||||
}
|
||||
};
|
||||
Q_DECLARE_METATYPE(ToolCallInfo)
|
||||
|
||||
class Tool : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(QString name READ name CONSTANT)
|
||||
Q_PROPERTY(QString description READ description CONSTANT)
|
||||
Q_PROPERTY(QString function READ function CONSTANT)
|
||||
Q_PROPERTY(QList<ToolParamInfo> parameters READ parameters CONSTANT)
|
||||
Q_PROPERTY(QString examplePrompt READ examplePrompt CONSTANT)
|
||||
Q_PROPERTY(QString exampleCall READ exampleCall CONSTANT)
|
||||
Q_PROPERTY(QString exampleReply READ exampleReply CONSTANT)
|
||||
|
||||
public:
|
||||
Tool() : QObject(nullptr) {}
|
||||
virtual ~Tool() {}
|
||||
|
||||
virtual QString run(const QList<ToolParam> ¶ms, qint64 timeout = 2000) = 0;
|
||||
|
||||
// Tools should set these if they encounter errors. For instance, a tool depending upon the network
|
||||
// might set these error variables if the network is not available.
|
||||
virtual ToolEnums::Error error() const { return ToolEnums::Error::NoError; }
|
||||
virtual QString errorString() const { return QString(); }
|
||||
|
||||
// [Required] Human readable name of the tool.
|
||||
virtual QString name() const = 0;
|
||||
|
||||
// [Required] Human readable description of what the tool does. Use this tool to: {{description}}
|
||||
virtual QString description() const = 0;
|
||||
|
||||
// [Required] Must be unique. Name of the function to invoke. Must be a-z, A-Z, 0-9, or contain underscores and dashes, with a maximum length of 64.
|
||||
virtual QString function() const = 0;
|
||||
|
||||
// [Optional] List describing the tool's parameters. An empty list specifies no parameters.
|
||||
virtual QList<ToolParamInfo> parameters() const { return {}; }
|
||||
|
||||
// [Optional] The symbolic format of the toolcall.
|
||||
virtual QString symbolicFormat() const { return QString(); }
|
||||
|
||||
// [Optional] A human generated example of a prompt that could result in this tool being called.
|
||||
virtual QString examplePrompt() const { return QString(); }
|
||||
|
||||
// [Optional] An example of this tool call that pairs with the example query. It should be the
|
||||
// complete string that the model must generate.
|
||||
virtual QString exampleCall() const { return QString(); }
|
||||
|
||||
// [Optional] An example of the reply the model might generate given the result of the tool call.
|
||||
virtual QString exampleReply() const { return QString(); }
|
||||
|
||||
bool operator==(const Tool &other) const { return function() == other.function(); }
|
||||
|
||||
jinja2::Value jinjaValue() const;
|
||||
};
|
||||
|
||||
#endif // TOOL_H
|
||||
@@ -1,111 +0,0 @@
|
||||
#include "toolcallparser.h"
|
||||
|
||||
#include <QDebug>
|
||||
#include <QtGlobal>
|
||||
#include <QtLogging>
|
||||
|
||||
#include <cstddef>
|
||||
|
||||
static const QString ToolCallStart = ToolCallConstants::CodeInterpreterTag;
|
||||
static const QString ToolCallEnd = ToolCallConstants::CodeInterpreterEndTag;
|
||||
|
||||
ToolCallParser::ToolCallParser()
|
||||
{
|
||||
reset();
|
||||
}
|
||||
|
||||
void ToolCallParser::reset()
|
||||
{
|
||||
// Resets the search state, but not the buffer or global state
|
||||
resetSearchState();
|
||||
|
||||
// These are global states maintained between update calls
|
||||
m_buffer.clear();
|
||||
m_hasSplit = false;
|
||||
}
|
||||
|
||||
void ToolCallParser::resetSearchState()
|
||||
{
|
||||
m_expected = ToolCallStart.at(0);
|
||||
m_expectedIndex = 0;
|
||||
m_state = ToolEnums::ParseState::None;
|
||||
m_toolCall.clear();
|
||||
m_endTagBuffer.clear();
|
||||
m_startIndex = -1;
|
||||
}
|
||||
|
||||
// This method is called with an arbitrary string and a current state. This method should take the
|
||||
// current state into account and then parse through the update character by character to arrive at
|
||||
// the new state.
|
||||
void ToolCallParser::update(const QString &update)
|
||||
{
|
||||
Q_ASSERT(m_state != ToolEnums::ParseState::Complete);
|
||||
if (m_state == ToolEnums::ParseState::Complete) {
|
||||
qWarning() << "ERROR: ToolCallParser::update already found a complete toolcall!";
|
||||
return;
|
||||
}
|
||||
|
||||
m_buffer.append(update);
|
||||
|
||||
for (size_t i = m_buffer.size() - update.size(); i < m_buffer.size(); ++i) {
|
||||
const QChar c = m_buffer[i];
|
||||
const bool foundMatch = m_expected.isNull() || c == m_expected;
|
||||
if (!foundMatch) {
|
||||
resetSearchState();
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (m_state) {
|
||||
case ToolEnums::ParseState::None:
|
||||
{
|
||||
m_expectedIndex = 1;
|
||||
m_expected = ToolCallStart.at(1);
|
||||
m_state = ToolEnums::ParseState::InStart;
|
||||
m_startIndex = i;
|
||||
break;
|
||||
}
|
||||
case ToolEnums::ParseState::InStart:
|
||||
{
|
||||
if (m_expectedIndex == ToolCallStart.size() - 1) {
|
||||
m_expectedIndex = 0;
|
||||
m_expected = QChar();
|
||||
m_state = ToolEnums::ParseState::Partial;
|
||||
} else {
|
||||
++m_expectedIndex;
|
||||
m_expected = ToolCallStart.at(m_expectedIndex);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ToolEnums::ParseState::Partial:
|
||||
{
|
||||
m_toolCall.append(c);
|
||||
m_endTagBuffer.append(c);
|
||||
if (m_endTagBuffer.size() > ToolCallEnd.size())
|
||||
m_endTagBuffer.remove(0, 1);
|
||||
if (m_endTagBuffer == ToolCallEnd) {
|
||||
m_toolCall.chop(ToolCallEnd.size());
|
||||
m_state = ToolEnums::ParseState::Complete;
|
||||
m_endTagBuffer.clear();
|
||||
}
|
||||
}
|
||||
case ToolEnums::ParseState::Complete:
|
||||
{
|
||||
// Already complete, do nothing further
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QPair<QString, QString> ToolCallParser::split()
|
||||
{
|
||||
Q_ASSERT(m_state == ToolEnums::ParseState::Partial
|
||||
|| m_state == ToolEnums::ParseState::Complete);
|
||||
|
||||
Q_ASSERT(m_startIndex >= 0);
|
||||
m_hasSplit = true;
|
||||
const QString beforeToolCall = m_buffer.left(m_startIndex);
|
||||
m_buffer = m_buffer.mid(m_startIndex);
|
||||
m_startIndex = 0;
|
||||
return { beforeToolCall, m_buffer };
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
#ifndef TOOLCALLPARSER_H
|
||||
#define TOOLCALLPARSER_H
|
||||
|
||||
#include "tool.h"
|
||||
|
||||
#include <QChar>
|
||||
#include <QString>
|
||||
#include <QPair>
|
||||
|
||||
namespace ToolCallConstants
|
||||
{
|
||||
const QString CodeInterpreterFunction = R"(javascript_interpret)";
|
||||
const QString CodeInterpreterTag = R"(<)" + CodeInterpreterFunction + R"(>)";
|
||||
const QString CodeInterpreterEndTag = R"(</)" + CodeInterpreterFunction + R"(>)";
|
||||
const QString CodeInterpreterPrefix = CodeInterpreterTag + "\n```javascript\n";
|
||||
const QString CodeInterpreterSuffix = "```\n" + CodeInterpreterEndTag;
|
||||
}
|
||||
|
||||
class ToolCallParser
|
||||
{
|
||||
public:
|
||||
ToolCallParser();
|
||||
void reset();
|
||||
void update(const QString &update);
|
||||
QString buffer() const { return m_buffer; }
|
||||
QString toolCall() const { return m_toolCall; }
|
||||
int startIndex() const { return m_startIndex; }
|
||||
ToolEnums::ParseState state() const { return m_state; }
|
||||
|
||||
// Splits
|
||||
QPair<QString, QString> split();
|
||||
bool hasSplit() const { return m_hasSplit; }
|
||||
|
||||
private:
|
||||
void resetSearchState();
|
||||
|
||||
QChar m_expected;
|
||||
int m_expectedIndex;
|
||||
ToolEnums::ParseState m_state;
|
||||
QString m_buffer;
|
||||
QString m_toolCall;
|
||||
QString m_endTagBuffer;
|
||||
int m_startIndex;
|
||||
bool m_hasSplit;
|
||||
};
|
||||
|
||||
#endif // TOOLCALLPARSER_H
|
||||
@@ -1,31 +0,0 @@
|
||||
#include "toolmodel.h"
|
||||
|
||||
#include "codeinterpreter.h"
|
||||
|
||||
#include <QCoreApplication>
|
||||
#include <QEvent>
|
||||
#include <QGlobalStatic>
|
||||
|
||||
class MyToolModel: public ToolModel { };
|
||||
Q_GLOBAL_STATIC(MyToolModel, toolModelInstance)
|
||||
ToolModel *ToolModel::globalInstance()
|
||||
{
|
||||
return toolModelInstance();
|
||||
}
|
||||
|
||||
ToolModel::ToolModel()
|
||||
: QAbstractListModel(nullptr)
|
||||
{
|
||||
QCoreApplication::instance()->installEventFilter(this);
|
||||
|
||||
Tool* codeInterpreter = new CodeInterpreter;
|
||||
m_tools.append(codeInterpreter);
|
||||
m_toolMap.insert(codeInterpreter->function(), codeInterpreter);
|
||||
}
|
||||
|
||||
bool ToolModel::eventFilter(QObject *obj, QEvent *ev)
|
||||
{
|
||||
if (obj == QCoreApplication::instance() && ev->type() == QEvent::LanguageChange)
|
||||
emit dataChanged(index(0, 0), index(m_tools.size() - 1, 0));
|
||||
return false;
|
||||
}
|
||||
@@ -1,110 +0,0 @@
|
||||
#ifndef TOOLMODEL_H
|
||||
#define TOOLMODEL_H
|
||||
|
||||
#include "tool.h"
|
||||
|
||||
#include <QAbstractListModel>
|
||||
#include <QByteArray>
|
||||
#include <QHash>
|
||||
#include <QList>
|
||||
#include <QString>
|
||||
#include <QVariant>
|
||||
#include <QtGlobal>
|
||||
|
||||
class ToolModel : public QAbstractListModel
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(int count READ count NOTIFY countChanged)
|
||||
|
||||
public:
|
||||
static ToolModel *globalInstance();
|
||||
|
||||
enum Roles {
|
||||
NameRole = Qt::UserRole + 1,
|
||||
DescriptionRole,
|
||||
FunctionRole,
|
||||
ParametersRole,
|
||||
SymbolicFormatRole,
|
||||
ExamplePromptRole,
|
||||
ExampleCallRole,
|
||||
ExampleReplyRole,
|
||||
};
|
||||
|
||||
int rowCount(const QModelIndex &parent = QModelIndex()) const override
|
||||
{
|
||||
Q_UNUSED(parent)
|
||||
return m_tools.size();
|
||||
}
|
||||
|
||||
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override
|
||||
{
|
||||
if (!index.isValid() || index.row() < 0 || index.row() >= m_tools.size())
|
||||
return QVariant();
|
||||
|
||||
const Tool *item = m_tools.at(index.row());
|
||||
switch (role) {
|
||||
case NameRole:
|
||||
return item->name();
|
||||
case DescriptionRole:
|
||||
return item->description();
|
||||
case FunctionRole:
|
||||
return item->function();
|
||||
case ParametersRole:
|
||||
return QVariant::fromValue(item->parameters());
|
||||
case SymbolicFormatRole:
|
||||
return item->symbolicFormat();
|
||||
case ExamplePromptRole:
|
||||
return item->examplePrompt();
|
||||
case ExampleCallRole:
|
||||
return item->exampleCall();
|
||||
case ExampleReplyRole:
|
||||
return item->exampleReply();
|
||||
}
|
||||
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
QHash<int, QByteArray> roleNames() const override
|
||||
{
|
||||
QHash<int, QByteArray> roles;
|
||||
roles[NameRole] = "name";
|
||||
roles[DescriptionRole] = "description";
|
||||
roles[FunctionRole] = "function";
|
||||
roles[ParametersRole] = "parameters";
|
||||
roles[SymbolicFormatRole] = "symbolicFormat";
|
||||
roles[ExamplePromptRole] = "examplePrompt";
|
||||
roles[ExampleCallRole] = "exampleCall";
|
||||
roles[ExampleReplyRole] = "exampleReply";
|
||||
return roles;
|
||||
}
|
||||
|
||||
Q_INVOKABLE Tool* get(int index) const
|
||||
{
|
||||
if (index < 0 || index >= m_tools.size()) return nullptr;
|
||||
return m_tools.at(index);
|
||||
}
|
||||
|
||||
Q_INVOKABLE Tool *get(const QString &id) const
|
||||
{
|
||||
if (!m_toolMap.contains(id)) return nullptr;
|
||||
return m_toolMap.value(id);
|
||||
}
|
||||
|
||||
int count() const { return m_tools.size(); }
|
||||
|
||||
Q_SIGNALS:
|
||||
void countChanged();
|
||||
void valueChanged(int index, const QString &value);
|
||||
|
||||
protected:
|
||||
bool eventFilter(QObject *obj, QEvent *ev) override;
|
||||
|
||||
private:
|
||||
explicit ToolModel();
|
||||
~ToolModel() {}
|
||||
friend class MyToolModel;
|
||||
QList<Tool*> m_tools;
|
||||
QHash<QString, Tool*> m_toolMap;
|
||||
};
|
||||
|
||||
#endif // TOOLMODEL_H
|
||||
@@ -64,94 +64,94 @@
|
||||
<message>
|
||||
<location filename="../qml/AddGPT4AllModelView.qml" line="31"/>
|
||||
<source>These models have been specifically configured for use in GPT4All. The first few models on the list are known to work the best, but you should only attempt to use models that will fit in your available memory.</source>
|
||||
<translation>Questi modelli sono stati specificamente configurati per l'uso in GPT4All. I primi modelli dell'elenco sono noti per funzionare meglio, ma dovresti utilizzare solo modelli che possano rientrare nella memoria disponibile.</translation>
|
||||
<translation>Questi modelli sono stati specificamente configurati per l'uso in GPT4All. I primi modelli dell'elenco sono noti per funzionare meglio, ma dovresti utilizzare solo modelli che possano rientrare nella memoria disponibile.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/AddGPT4AllModelView.qml" line="45"/>
|
||||
<source>Network error: could not retrieve %1</source>
|
||||
<translation>Errore di rete: impossibile recuperare %1</translation>
|
||||
<translation type="unfinished">Errore di rete: impossibile recuperare %1</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/AddGPT4AllModelView.qml" line="55"/>
|
||||
<location filename="../qml/AddGPT4AllModelView.qml" line="343"/>
|
||||
<source>Busy indicator</source>
|
||||
<translation>Indicatore di occupato</translation>
|
||||
<translation type="unfinished">Indicatore di occupato</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/AddGPT4AllModelView.qml" line="56"/>
|
||||
<source>Displayed when the models request is ongoing</source>
|
||||
<translation>Visualizzato quando la richiesta dei modelli è in corso</translation>
|
||||
<translation type="unfinished">Visualizzato quando la richiesta dei modelli è in corso</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/AddGPT4AllModelView.qml" line="96"/>
|
||||
<source>Model file</source>
|
||||
<translation>File del modello</translation>
|
||||
<translation type="unfinished">File del modello</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/AddGPT4AllModelView.qml" line="97"/>
|
||||
<source>Model file to be downloaded</source>
|
||||
<translation>File del modello da scaricare</translation>
|
||||
<translation type="unfinished">File del modello da scaricare</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/AddGPT4AllModelView.qml" line="120"/>
|
||||
<source>Description</source>
|
||||
<translation>Descrizione</translation>
|
||||
<translation type="unfinished">Descrizione</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/AddGPT4AllModelView.qml" line="121"/>
|
||||
<source>File description</source>
|
||||
<translation>Descrizione del file</translation>
|
||||
<translation type="unfinished">Descrizione del file</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/AddGPT4AllModelView.qml" line="154"/>
|
||||
<source>Cancel</source>
|
||||
<translation>Annulla</translation>
|
||||
<translation type="unfinished">Annulla</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/AddGPT4AllModelView.qml" line="154"/>
|
||||
<source>Resume</source>
|
||||
<translation>Riprendi</translation>
|
||||
<translation type="unfinished">Riprendi</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/AddGPT4AllModelView.qml" line="154"/>
|
||||
<source>Download</source>
|
||||
<translation>Scarica</translation>
|
||||
<translation type="unfinished">Scarica</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/AddGPT4AllModelView.qml" line="162"/>
|
||||
<source>Stop/restart/start the download</source>
|
||||
<translation>Arresta/riavvia/avvia il download</translation>
|
||||
<translation type="unfinished">Arresta/riavvia/avvia il download</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/AddGPT4AllModelView.qml" line="174"/>
|
||||
<source>Remove</source>
|
||||
<translation>Rimuovi</translation>
|
||||
<translation type="unfinished">Rimuovi</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/AddGPT4AllModelView.qml" line="181"/>
|
||||
<source>Remove model from filesystem</source>
|
||||
<translation>Rimuovi il modello dal sistema dei file</translation>
|
||||
<translation type="unfinished">Rimuovi il modello dal sistema dei file</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/AddGPT4AllModelView.qml" line="195"/>
|
||||
<location filename="../qml/AddGPT4AllModelView.qml" line="229"/>
|
||||
<source>Install</source>
|
||||
<translation>Installa</translation>
|
||||
<translation type="unfinished">Installa</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/AddGPT4AllModelView.qml" line="230"/>
|
||||
<source>Install online model</source>
|
||||
<translation>Installa il modello online</translation>
|
||||
<translation type="unfinished">Installa il modello online</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/AddGPT4AllModelView.qml" line="240"/>
|
||||
<source><strong><font size="1"><a href="#error">Error</a></strong></font></source>
|
||||
<translation><strong><font size="1"><a href="#error">Errore</a></strong></font></translation>
|
||||
<translation type="unfinished"><strong><font size="1"><a href="#error">Errore</a></strong></font></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/AddGPT4AllModelView.qml" line="246"/>
|
||||
<source>Describes an error that occurred when downloading</source>
|
||||
<translation>Descrive un errore che si è verificato durante lo scaricamento</translation>
|
||||
<translation type="unfinished">Descrive un errore che si è verificato durante lo scaricamento</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/AddGPT4AllModelView.qml" line="259"/>
|
||||
@@ -161,32 +161,32 @@
|
||||
<message>
|
||||
<location filename="../qml/AddGPT4AllModelView.qml" line="265"/>
|
||||
<source>Error for incompatible hardware</source>
|
||||
<translation>Errore per hardware incompatibile</translation>
|
||||
<translation type="unfinished">Errore per hardware incompatibile</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/AddGPT4AllModelView.qml" line="303"/>
|
||||
<source>Download progressBar</source>
|
||||
<translation>Barra di avanzamento dello scaricamento</translation>
|
||||
<translation type="unfinished">Barra di avanzamento dello scaricamento</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/AddGPT4AllModelView.qml" line="304"/>
|
||||
<source>Shows the progress made in the download</source>
|
||||
<translation>Mostra lo stato di avanzamento dello scaricamento</translation>
|
||||
<translation type="unfinished">Mostra lo stato di avanzamento dello scaricamento</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/AddGPT4AllModelView.qml" line="314"/>
|
||||
<source>Download speed</source>
|
||||
<translation>Velocità di scaricamento</translation>
|
||||
<translation type="unfinished">Velocità di scaricamento</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/AddGPT4AllModelView.qml" line="315"/>
|
||||
<source>Download speed in bytes/kilobytes/megabytes per second</source>
|
||||
<translation>Velocità di scaricamento in byte/kilobyte/megabyte al secondo</translation>
|
||||
<translation type="unfinished">Velocità di scaricamento in byte/kilobyte/megabyte al secondo</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/AddGPT4AllModelView.qml" line="332"/>
|
||||
<source>Calculating...</source>
|
||||
<translation>Calcolo in corso...</translation>
|
||||
<translation type="unfinished">Calcolo in corso...</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/AddGPT4AllModelView.qml" line="336"/>
|
||||
@@ -194,52 +194,52 @@
|
||||
<location filename="../qml/AddGPT4AllModelView.qml" line="387"/>
|
||||
<location filename="../qml/AddGPT4AllModelView.qml" line="408"/>
|
||||
<source>Whether the file hash is being calculated</source>
|
||||
<translation>Se viene calcolato l'hash del file</translation>
|
||||
<translation type="unfinished">Se viene calcolato l'hash del file</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/AddGPT4AllModelView.qml" line="344"/>
|
||||
<source>Displayed when the file hash is being calculated</source>
|
||||
<translation>Visualizzato durante il calcolo dell'hash del file</translation>
|
||||
<translation type="unfinished">Visualizzato durante il calcolo dell'hash del file</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/AddGPT4AllModelView.qml" line="357"/>
|
||||
<source>ERROR: $API_KEY is empty.</source>
|
||||
<translation>ERRORE: $API_KEY è vuoto.</translation>
|
||||
<translation type="unfinished">ERRORE: $API_KEY è vuoto.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/AddGPT4AllModelView.qml" line="363"/>
|
||||
<source>enter $API_KEY</source>
|
||||
<translation>Inserire $API_KEY</translation>
|
||||
<translation type="unfinished">Inserire $API_KEY</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/AddGPT4AllModelView.qml" line="378"/>
|
||||
<source>ERROR: $BASE_URL is empty.</source>
|
||||
<translation>ERRORE: $BASE_URL non è valido.</translation>
|
||||
<translation type="unfinished">ERRORE: $BASE_URL non è valido.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/AddGPT4AllModelView.qml" line="384"/>
|
||||
<source>enter $BASE_URL</source>
|
||||
<translation>inserisci $BASE_URL</translation>
|
||||
<translation type="unfinished">inserisci $BASE_URL</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/AddGPT4AllModelView.qml" line="399"/>
|
||||
<source>ERROR: $MODEL_NAME is empty.</source>
|
||||
<translation>ERRORE: $MODEL_NAME è vuoto.</translation>
|
||||
<translation type="unfinished">ERRORE: $MODEL_NAME è vuoto.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/AddGPT4AllModelView.qml" line="405"/>
|
||||
<source>enter $MODEL_NAME</source>
|
||||
<translation>inserisci $MODEL_NAME</translation>
|
||||
<translation type="unfinished">inserisci $MODEL_NAME</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/AddGPT4AllModelView.qml" line="427"/>
|
||||
<source>File size</source>
|
||||
<translation>Dimensione del file</translation>
|
||||
<translation type="unfinished">Dimensione del file</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/AddGPT4AllModelView.qml" line="449"/>
|
||||
<source>RAM required</source>
|
||||
<translation>RAM richiesta</translation>
|
||||
<translation type="unfinished">RAM richiesta</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/AddGPT4AllModelView.qml" line="454"/>
|
||||
@@ -255,17 +255,17 @@
|
||||
<message>
|
||||
<location filename="../qml/AddGPT4AllModelView.qml" line="471"/>
|
||||
<source>Parameters</source>
|
||||
<translation>Parametri</translation>
|
||||
<translation type="unfinished">Parametri</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/AddGPT4AllModelView.qml" line="493"/>
|
||||
<source>Quant</source>
|
||||
<translation>Quant</translation>
|
||||
<translation type="unfinished">Quant</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/AddGPT4AllModelView.qml" line="515"/>
|
||||
<source>Type</source>
|
||||
<translation>Tipo</translation>
|
||||
<translation type="unfinished">Tipo</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
@@ -273,153 +273,153 @@
|
||||
<message>
|
||||
<location filename="../qml/AddHFModelView.qml" line="32"/>
|
||||
<source>Use the search to find and download models from HuggingFace. There is NO GUARANTEE that these will work. Many will require additional configuration before they can be used.</source>
|
||||
<translation>Usa la ricerca per trovare e scaricare modelli da HuggingFace. NON C'È ALCUNA GARANZIA che funzioneranno. Molti richiederanno configurazioni aggiuntive prima di poter essere utilizzati.</translation>
|
||||
<translation>Usa la ricerca per trovare e scaricare modelli da HuggingFace. NON C'È ALCUNA GARANZIA che funzioneranno. Molti richiederanno configurazioni aggiuntive prima di poter essere utilizzati.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/AddHFModelView.qml" line="52"/>
|
||||
<source>Discover and download models by keyword search...</source>
|
||||
<translation>Scopri e scarica i modelli tramite ricerca per parole chiave...</translation>
|
||||
<translation type="unfinished">Scopri e scarica i modelli tramite ricerca per parole chiave...</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/AddHFModelView.qml" line="55"/>
|
||||
<source>Text field for discovering and filtering downloadable models</source>
|
||||
<translation>Campo di testo per scoprire e filtrare i modelli scaricabili</translation>
|
||||
<translation type="unfinished">Campo di testo per scoprire e filtrare i modelli scaricabili</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/AddHFModelView.qml" line="61"/>
|
||||
<source>Searching · %1</source>
|
||||
<translation>Ricerca · %1</translation>
|
||||
<translation type="unfinished">Ricerca · %1</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/AddHFModelView.qml" line="131"/>
|
||||
<source>Initiate model discovery and filtering</source>
|
||||
<translation>Avvia rilevamento e filtraggio dei modelli</translation>
|
||||
<translation type="unfinished">Avvia rilevamento e filtraggio dei modelli</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/AddHFModelView.qml" line="132"/>
|
||||
<source>Triggers discovery and filtering of models</source>
|
||||
<translation>Attiva la scoperta e il filtraggio dei modelli</translation>
|
||||
<translation type="unfinished">Attiva la scoperta e il filtraggio dei modelli</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/AddHFModelView.qml" line="151"/>
|
||||
<source>Default</source>
|
||||
<translation>Predefinito</translation>
|
||||
<translation type="unfinished">Predefinito</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/AddHFModelView.qml" line="152"/>
|
||||
<source>Likes</source>
|
||||
<translation>Mi piace</translation>
|
||||
<translation type="unfinished">Mi piace</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/AddHFModelView.qml" line="153"/>
|
||||
<source>Downloads</source>
|
||||
<translation>Scaricamenti</translation>
|
||||
<translation type="unfinished">Scaricamenti</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/AddHFModelView.qml" line="154"/>
|
||||
<source>Recent</source>
|
||||
<translation>Recenti</translation>
|
||||
<translation type="unfinished">Recenti</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/AddHFModelView.qml" line="162"/>
|
||||
<source>Sort by: %1</source>
|
||||
<translation>Ordina per: %1</translation>
|
||||
<translation type="unfinished">Ordina per: %1</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/AddHFModelView.qml" line="176"/>
|
||||
<source>Asc</source>
|
||||
<translation>Asc</translation>
|
||||
<translation type="unfinished">Asc</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/AddHFModelView.qml" line="177"/>
|
||||
<source>Desc</source>
|
||||
<translation>Disc</translation>
|
||||
<translation type="unfinished">Disc</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/AddHFModelView.qml" line="190"/>
|
||||
<source>Sort dir: %1</source>
|
||||
<translation>Direzione ordinamento: %1</translation>
|
||||
<translation type="unfinished">Direzione ordinamento: %1</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/AddHFModelView.qml" line="212"/>
|
||||
<source>None</source>
|
||||
<translation>Niente</translation>
|
||||
<translation type="unfinished">Niente</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/AddHFModelView.qml" line="234"/>
|
||||
<source>Limit: %1</source>
|
||||
<translation>Limite: %1</translation>
|
||||
<translation type="unfinished">Limite: %1</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/AddHFModelView.qml" line="297"/>
|
||||
<source>Model file</source>
|
||||
<translation>File del modello</translation>
|
||||
<translation type="unfinished">File del modello</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/AddHFModelView.qml" line="298"/>
|
||||
<source>Model file to be downloaded</source>
|
||||
<translation>File del modello da scaricare</translation>
|
||||
<translation type="unfinished">File del modello da scaricare</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/AddHFModelView.qml" line="321"/>
|
||||
<source>Description</source>
|
||||
<translation>Descrizione</translation>
|
||||
<translation type="unfinished">Descrizione</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/AddHFModelView.qml" line="322"/>
|
||||
<source>File description</source>
|
||||
<translation>Descrizione del file</translation>
|
||||
<translation type="unfinished">Descrizione del file</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/AddHFModelView.qml" line="355"/>
|
||||
<source>Cancel</source>
|
||||
<translation>Annulla</translation>
|
||||
<translation type="unfinished">Annulla</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/AddHFModelView.qml" line="355"/>
|
||||
<source>Resume</source>
|
||||
<translation>Riprendi</translation>
|
||||
<translation type="unfinished">Riprendi</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/AddHFModelView.qml" line="355"/>
|
||||
<source>Download</source>
|
||||
<translation>Scarica</translation>
|
||||
<translation type="unfinished">Scarica</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/AddHFModelView.qml" line="363"/>
|
||||
<source>Stop/restart/start the download</source>
|
||||
<translation>Arresta/riavvia/avvia il download</translation>
|
||||
<translation type="unfinished">Arresta/riavvia/avvia il download</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/AddHFModelView.qml" line="375"/>
|
||||
<source>Remove</source>
|
||||
<translation>Rimuovi</translation>
|
||||
<translation type="unfinished">Rimuovi</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/AddHFModelView.qml" line="382"/>
|
||||
<source>Remove model from filesystem</source>
|
||||
<translation>Rimuovi il modello dal sistema dei file</translation>
|
||||
<translation type="unfinished">Rimuovi il modello dal sistema dei file</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/AddHFModelView.qml" line="396"/>
|
||||
<location filename="../qml/AddHFModelView.qml" line="430"/>
|
||||
<source>Install</source>
|
||||
<translation>Installa</translation>
|
||||
<translation type="unfinished">Installa</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/AddHFModelView.qml" line="431"/>
|
||||
<source>Install online model</source>
|
||||
<translation>Installa il modello online</translation>
|
||||
<translation type="unfinished">Installa il modello online</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/AddHFModelView.qml" line="441"/>
|
||||
<source><strong><font size="1"><a href="#error">Error</a></strong></font></source>
|
||||
<translation><strong><font size="1"><a href="#error">Errore</a></strong></font></translation>
|
||||
<translation type="unfinished"><strong><font size="1"><a href="#error">Errore</a></strong></font></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/AddHFModelView.qml" line="447"/>
|
||||
<source>Describes an error that occurred when downloading</source>
|
||||
<translation>Descrive un errore che si è verificato durante lo scaricamento</translation>
|
||||
<translation type="unfinished">Descrive un errore che si è verificato durante lo scaricamento</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/AddHFModelView.qml" line="460"/>
|
||||
@@ -429,32 +429,32 @@
|
||||
<message>
|
||||
<location filename="../qml/AddHFModelView.qml" line="466"/>
|
||||
<source>Error for incompatible hardware</source>
|
||||
<translation>Errore per hardware incompatibile</translation>
|
||||
<translation type="unfinished">Errore per hardware incompatibile</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/AddHFModelView.qml" line="504"/>
|
||||
<source>Download progressBar</source>
|
||||
<translation>Barra di avanzamento dello scaricamento</translation>
|
||||
<translation type="unfinished">Barra di avanzamento dello scaricamento</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/AddHFModelView.qml" line="505"/>
|
||||
<source>Shows the progress made in the download</source>
|
||||
<translation>Mostra lo stato di avanzamento dello scaricamento</translation>
|
||||
<translation type="unfinished">Mostra lo stato di avanzamento dello scaricamento</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/AddHFModelView.qml" line="515"/>
|
||||
<source>Download speed</source>
|
||||
<translation>Velocità di scaricamento</translation>
|
||||
<translation type="unfinished">Velocità di scaricamento</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/AddHFModelView.qml" line="516"/>
|
||||
<source>Download speed in bytes/kilobytes/megabytes per second</source>
|
||||
<translation>Velocità di scaricamento in byte/kilobyte/megabyte al secondo</translation>
|
||||
<translation type="unfinished">Velocità di scaricamento in byte/kilobyte/megabyte al secondo</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/AddHFModelView.qml" line="533"/>
|
||||
<source>Calculating...</source>
|
||||
<translation>Calcolo in corso...</translation>
|
||||
<translation type="unfinished">Calcolo in corso...</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/AddHFModelView.qml" line="537"/>
|
||||
@@ -462,62 +462,62 @@
|
||||
<location filename="../qml/AddHFModelView.qml" line="588"/>
|
||||
<location filename="../qml/AddHFModelView.qml" line="609"/>
|
||||
<source>Whether the file hash is being calculated</source>
|
||||
<translation>Se viene calcolato l'hash del file</translation>
|
||||
<translation type="unfinished">Se viene calcolato l'hash del file</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/AddHFModelView.qml" line="544"/>
|
||||
<source>Busy indicator</source>
|
||||
<translation>Indicatore di occupato</translation>
|
||||
<translation type="unfinished">Indicatore di occupato</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/AddHFModelView.qml" line="545"/>
|
||||
<source>Displayed when the file hash is being calculated</source>
|
||||
<translation>Visualizzato durante il calcolo dell'hash del file</translation>
|
||||
<translation type="unfinished">Visualizzato durante il calcolo dell'hash del file</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/AddHFModelView.qml" line="558"/>
|
||||
<source>ERROR: $API_KEY is empty.</source>
|
||||
<translation>ERRORE: $API_KEY è vuoto.</translation>
|
||||
<translation type="unfinished">ERRORE: $API_KEY è vuoto.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/AddHFModelView.qml" line="564"/>
|
||||
<source>enter $API_KEY</source>
|
||||
<translation>Inserire $API_KEY</translation>
|
||||
<translation type="unfinished">Inserire $API_KEY</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/AddHFModelView.qml" line="579"/>
|
||||
<source>ERROR: $BASE_URL is empty.</source>
|
||||
<translation>ERRORE: $BASE_URL non è valido.</translation>
|
||||
<translation type="unfinished">ERRORE: $BASE_URL non è valido.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/AddHFModelView.qml" line="585"/>
|
||||
<source>enter $BASE_URL</source>
|
||||
<translation>inserisci $BASE_URL</translation>
|
||||
<translation type="unfinished">inserisci $BASE_URL</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/AddHFModelView.qml" line="600"/>
|
||||
<source>ERROR: $MODEL_NAME is empty.</source>
|
||||
<translation>ERRORE: $MODEL_NAME è vuoto.</translation>
|
||||
<translation type="unfinished">ERRORE: $MODEL_NAME è vuoto.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/AddHFModelView.qml" line="606"/>
|
||||
<source>enter $MODEL_NAME</source>
|
||||
<translation>inserisci $MODEL_NAME</translation>
|
||||
<translation type="unfinished">inserisci $MODEL_NAME</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/AddHFModelView.qml" line="628"/>
|
||||
<source>File size</source>
|
||||
<translation>Dimensione del file</translation>
|
||||
<translation type="unfinished">Dimensione del file</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/AddHFModelView.qml" line="650"/>
|
||||
<source>Quant</source>
|
||||
<translation>Quant</translation>
|
||||
<translation type="unfinished">Quant</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/AddHFModelView.qml" line="672"/>
|
||||
<source>Type</source>
|
||||
<translation>Tipo</translation>
|
||||
<translation type="unfinished">Tipo</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
@@ -2846,7 +2846,7 @@ NOTA: attivando questa funzione, invierai i tuoi dati al Datalake Open Source di
|
||||
<message>
|
||||
<location filename="../qml/StartupDialog.qml" line="118"/>
|
||||
<source>Opt-in to anonymous usage analytics used to improve GPT4All</source>
|
||||
<translation>Acconsenti all'analisi anonima dell'uso per migliorare GPT4All</translation>
|
||||
<translation>Acconsenti all'analisi anonima dell'uso per migliorare GPT4All</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/StartupDialog.qml" line="124"/>
|
||||
|
||||
Reference in New Issue
Block a user