Compare commits
20 Commits
dev
...
release/v0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6d6a1fd009 | ||
|
|
dc4de43516 | ||
|
|
9046927bf3 | ||
|
|
7fd83e0bf8 | ||
|
|
d3ea61b2e9 | ||
|
|
651ba446bf | ||
|
|
7bee282875 | ||
|
|
86c7122f90 | ||
|
|
c378d765fc | ||
|
|
fdf239352a | ||
|
|
40c5953fea | ||
|
|
5382e9666e | ||
|
|
404f40cc23 | ||
|
|
3ac83fdc08 | ||
|
|
a568afdff1 | ||
|
|
14abd6068a | ||
|
|
d3a5ff9a74 | ||
|
|
d7a1a9a529 | ||
|
|
4c4e795a3d | ||
|
|
c14e1ea00f |
@ -49,6 +49,8 @@ jobs:
|
||||
# Update tauri.conf.json
|
||||
jq --arg version "${{ inputs.new_version }}" '.version = $version | .bundle.createUpdaterArtifacts = false' ./src-tauri/tauri.conf.json > /tmp/tauri.conf.json
|
||||
mv /tmp/tauri.conf.json ./src-tauri/tauri.conf.json
|
||||
jq '.bundle.windows.nsis.template = "tauri.bundle.windows.nsis.template"' ./src-tauri/tauri.windows.conf.json > /tmp/tauri.windows.conf.json
|
||||
mv /tmp/tauri.windows.conf.json ./src-tauri/tauri.windows.conf.json
|
||||
jq '.bundle.windows.signCommand = "echo External build - skipping signature: %1"' ./src-tauri/tauri.windows.conf.json > /tmp/tauri.windows.conf.json
|
||||
mv /tmp/tauri.windows.conf.json ./src-tauri/tauri.windows.conf.json
|
||||
jq --arg version "${{ inputs.new_version }}" '.version = $version' web-app/package.json > /tmp/package.json
|
||||
@ -80,6 +82,36 @@ jobs:
|
||||
echo "---------./src-tauri/Cargo.toml---------"
|
||||
cat ./src-tauri/Cargo.toml
|
||||
|
||||
generate_build_version() {
|
||||
### Examble
|
||||
### input 0.5.6 output will be 0.5.6 and 0.5.6.0
|
||||
### input 0.5.6-rc2-beta output will be 0.5.6 and 0.5.6.2
|
||||
### input 0.5.6-1213 output will be 0.5.6 and and 0.5.6.1213
|
||||
local new_version="$1"
|
||||
local base_version
|
||||
local t_value
|
||||
# Check if it has a "-"
|
||||
if [[ "$new_version" == *-* ]]; then
|
||||
base_version="${new_version%%-*}" # part before -
|
||||
suffix="${new_version#*-}" # part after -
|
||||
# Check if it is rcX-beta
|
||||
if [[ "$suffix" =~ ^rc([0-9]+)-beta$ ]]; then
|
||||
t_value="${BASH_REMATCH[1]}"
|
||||
else
|
||||
t_value="$suffix"
|
||||
fi
|
||||
else
|
||||
base_version="$new_version"
|
||||
t_value="0"
|
||||
fi
|
||||
# Export two values
|
||||
new_base_version="$base_version"
|
||||
new_build_version="${base_version}.${t_value}"
|
||||
}
|
||||
generate_build_version ${{ inputs.new_version }}
|
||||
sed -i "s/jan_version/$new_base_version/g" ./src-tauri/tauri.bundle.windows.nsis.template
|
||||
sed -i "s/jan_build/$new_build_version/g" ./src-tauri/tauri.bundle.windows.nsis.template
|
||||
|
||||
if [ "${{ inputs.channel }}" != "stable" ]; then
|
||||
jq '.plugins.updater.endpoints = ["https://delta.jan.ai/${{ inputs.channel }}/latest.json"]' ./src-tauri/tauri.conf.json > /tmp/tauri.conf.json
|
||||
mv /tmp/tauri.conf.json ./src-tauri/tauri.conf.json
|
||||
@ -103,7 +135,14 @@ jobs:
|
||||
chmod +x .github/scripts/rename-workspace.sh
|
||||
.github/scripts/rename-workspace.sh ./package.json ${{ inputs.channel }}
|
||||
cat ./package.json
|
||||
sed -i "s/jan_productname/Jan-${{ inputs.channel }}/g" ./src-tauri/tauri.bundle.windows.nsis.template
|
||||
sed -i "s/jan_mainbinaryname/jan-${{ inputs.channel }}/g" ./src-tauri/tauri.bundle.windows.nsis.template
|
||||
else
|
||||
sed -i "s/jan_productname/Jan/g" ./src-tauri/tauri.bundle.windows.nsis.template
|
||||
sed -i "s/jan_mainbinaryname/jan/g" ./src-tauri/tauri.bundle.windows.nsis.template
|
||||
fi
|
||||
echo "---------nsis.template---------"
|
||||
cat ./src-tauri/tauri.bundle.windows.nsis.template
|
||||
- name: Build app
|
||||
shell: bash
|
||||
run: |
|
||||
|
||||
@ -98,9 +98,15 @@ jobs:
|
||||
# Update tauri.conf.json
|
||||
jq --arg version "${{ inputs.new_version }}" '.version = $version | .bundle.createUpdaterArtifacts = true' ./src-tauri/tauri.conf.json > /tmp/tauri.conf.json
|
||||
mv /tmp/tauri.conf.json ./src-tauri/tauri.conf.json
|
||||
jq '.bundle.windows.nsis.template = "tauri.bundle.windows.nsis.template"' ./src-tauri/tauri.windows.conf.json > /tmp/tauri.windows.conf.json
|
||||
mv /tmp/tauri.windows.conf.json ./src-tauri/tauri.windows.conf.json
|
||||
jq --arg version "${{ inputs.new_version }}" '.version = $version' web-app/package.json > /tmp/package.json
|
||||
mv /tmp/package.json web-app/package.json
|
||||
|
||||
# Add sign commands to tauri.windows.conf.json
|
||||
jq '.bundle.windows.signCommand = "powershell -ExecutionPolicy Bypass -File ./sign.ps1 %1"' ./src-tauri/tauri.windows.conf.json > /tmp/tauri.windows.conf.json
|
||||
mv /tmp/tauri.windows.conf.json ./src-tauri/tauri.windows.conf.json
|
||||
|
||||
# Update tauri plugin versions
|
||||
|
||||
jq --arg version "${{ inputs.new_version }}" '.version = $version' ./src-tauri/plugins/tauri-plugin-hardware/package.json > /tmp/package.json
|
||||
@ -127,9 +133,35 @@ jobs:
|
||||
echo "---------./src-tauri/Cargo.toml---------"
|
||||
cat ./src-tauri/Cargo.toml
|
||||
|
||||
# Add sign commands to tauri.windows.conf.json
|
||||
jq '.bundle.windows.signCommand = "powershell -ExecutionPolicy Bypass -File ./sign.ps1 %1"' ./src-tauri/tauri.windows.conf.json > /tmp/tauri.windows.conf.json
|
||||
mv /tmp/tauri.windows.conf.json ./src-tauri/tauri.windows.conf.json
|
||||
generate_build_version() {
|
||||
### Examble
|
||||
### input 0.5.6 output will be 0.5.6 and 0.5.6.0
|
||||
### input 0.5.6-rc2-beta output will be 0.5.6 and 0.5.6.2
|
||||
### input 0.5.6-1213 output will be 0.5.6 and and 0.5.6.1213
|
||||
local new_version="$1"
|
||||
local base_version
|
||||
local t_value
|
||||
# Check if it has a "-"
|
||||
if [[ "$new_version" == *-* ]]; then
|
||||
base_version="${new_version%%-*}" # part before -
|
||||
suffix="${new_version#*-}" # part after -
|
||||
# Check if it is rcX-beta
|
||||
if [[ "$suffix" =~ ^rc([0-9]+)-beta$ ]]; then
|
||||
t_value="${BASH_REMATCH[1]}"
|
||||
else
|
||||
t_value="$suffix"
|
||||
fi
|
||||
else
|
||||
base_version="$new_version"
|
||||
t_value="0"
|
||||
fi
|
||||
# Export two values
|
||||
new_base_version="$base_version"
|
||||
new_build_version="${base_version}.${t_value}"
|
||||
}
|
||||
generate_build_version ${{ inputs.new_version }}
|
||||
sed -i "s/jan_version/$new_base_version/g" ./src-tauri/tauri.bundle.windows.nsis.template
|
||||
sed -i "s/jan_build/$new_build_version/g" ./src-tauri/tauri.bundle.windows.nsis.template
|
||||
|
||||
echo "---------tauri.windows.conf.json---------"
|
||||
cat ./src-tauri/tauri.windows.conf.json
|
||||
@ -163,7 +195,14 @@ jobs:
|
||||
chmod +x .github/scripts/rename-workspace.sh
|
||||
.github/scripts/rename-workspace.sh ./package.json ${{ inputs.channel }}
|
||||
cat ./package.json
|
||||
sed -i "s/jan_productname/Jan-${{ inputs.channel }}/g" ./src-tauri/tauri.bundle.windows.nsis.template
|
||||
sed -i "s/jan_mainbinaryname/jan-${{ inputs.channel }}/g" ./src-tauri/tauri.bundle.windows.nsis.template
|
||||
else
|
||||
sed -i "s/jan_productname/Jan/g" ./src-tauri/tauri.bundle.windows.nsis.template
|
||||
sed -i "s/jan_mainbinaryname/jan/g" ./src-tauri/tauri.bundle.windows.nsis.template
|
||||
fi
|
||||
echo "---------nsis.template---------"
|
||||
cat ./src-tauri/tauri.bundle.windows.nsis.template
|
||||
|
||||
- name: Install AzureSignTool
|
||||
run: |
|
||||
@ -234,8 +273,6 @@ jobs:
|
||||
# Upload for tauri updater
|
||||
aws s3 cp ./${{ steps.metadata.outputs.FILE_NAME }} s3://${{ secrets.DELTA_AWS_S3_BUCKET_NAME }}/temp-${{ inputs.channel }}/${{ steps.metadata.outputs.FILE_NAME }}
|
||||
aws s3 cp ./${{ steps.metadata.outputs.FILE_NAME }}.sig s3://${{ secrets.DELTA_AWS_S3_BUCKET_NAME }}/temp-${{ inputs.channel }}/${{ steps.metadata.outputs.FILE_NAME }}.sig
|
||||
|
||||
aws s3 cp ./src-tauri/target/release/bundle/msi/${{ steps.metadata.outputs.MSI_FILE_NAME }} s3://${{ secrets.DELTA_AWS_S3_BUCKET_NAME }}/temp-${{ inputs.channel }}/${{ steps.metadata.outputs.MSI_FILE_NAME }}
|
||||
env:
|
||||
AWS_ACCESS_KEY_ID: ${{ secrets.DELTA_AWS_ACCESS_KEY_ID }}
|
||||
AWS_SECRET_ACCESS_KEY: ${{ secrets.DELTA_AWS_SECRET_ACCESS_KEY }}
|
||||
|
||||
3
Makefile
3
Makefile
@ -72,9 +72,6 @@ lint: install-and-build
|
||||
test: lint
|
||||
yarn download:bin
|
||||
yarn download:lib
|
||||
ifeq ($(OS),Windows_NT)
|
||||
yarn download:windows-installer
|
||||
endif
|
||||
yarn test
|
||||
yarn copy:assets:tauri
|
||||
yarn build:icon
|
||||
|
||||
@ -30,7 +30,7 @@
|
||||
"@vitest/coverage-v8": "^2.1.8",
|
||||
"@vitest/ui": "^2.1.8",
|
||||
"eslint": "8.57.0",
|
||||
"happy-dom": "^15.11.6",
|
||||
"happy-dom": "^20.0.0",
|
||||
"pacote": "^21.0.0",
|
||||
"request": "^2.88.2",
|
||||
"request-progress": "^3.0.0",
|
||||
|
||||
@ -332,12 +332,14 @@ export default class llamacpp_extension extends AIEngine {
|
||||
)
|
||||
// Clear the invalid stored preference
|
||||
this.clearStoredBackendType()
|
||||
bestAvailableBackendString =
|
||||
await this.determineBestBackend(version_backends)
|
||||
bestAvailableBackendString = await this.determineBestBackend(
|
||||
version_backends
|
||||
)
|
||||
}
|
||||
} else {
|
||||
bestAvailableBackendString =
|
||||
await this.determineBestBackend(version_backends)
|
||||
bestAvailableBackendString = await this.determineBestBackend(
|
||||
version_backends
|
||||
)
|
||||
}
|
||||
|
||||
let settings = structuredClone(SETTINGS)
|
||||
@ -2151,7 +2153,12 @@ export default class llamacpp_extension extends AIEngine {
|
||||
if (mmprojPath && !this.isAbsolutePath(mmprojPath))
|
||||
mmprojPath = await joinPath([await getJanDataFolderPath(), path])
|
||||
try {
|
||||
const result = await planModelLoadInternal(path, this.memoryMode, mmprojPath, requestedCtx)
|
||||
const result = await planModelLoadInternal(
|
||||
path,
|
||||
this.memoryMode,
|
||||
mmprojPath,
|
||||
requestedCtx
|
||||
)
|
||||
return result
|
||||
} catch (e) {
|
||||
throw new Error(String(e))
|
||||
@ -2279,12 +2286,18 @@ export default class llamacpp_extension extends AIEngine {
|
||||
}
|
||||
|
||||
// Calculate text tokens
|
||||
const messages = JSON.stringify({ messages: opts.messages })
|
||||
// Use chat_template_kwargs from opts if provided, otherwise default to disable enable_thinking
|
||||
const tokenizeRequest = {
|
||||
messages: opts.messages,
|
||||
chat_template_kwargs: opts.chat_template_kwargs || {
|
||||
enable_thinking: false,
|
||||
},
|
||||
}
|
||||
|
||||
let parseResponse = await fetch(`${baseUrl}/apply-template`, {
|
||||
method: 'POST',
|
||||
headers: headers,
|
||||
body: messages,
|
||||
body: JSON.stringify(tokenizeRequest),
|
||||
})
|
||||
|
||||
if (!parseResponse.ok) {
|
||||
|
||||
@ -27,8 +27,7 @@
|
||||
"copy:assets:tauri": "cpx \"pre-install/*.tgz\" \"src-tauri/resources/pre-install/\" && cpx \"LICENSE\" \"src-tauri/resources/\"",
|
||||
"download:lib": "node ./scripts/download-lib.mjs",
|
||||
"download:bin": "node ./scripts/download-bin.mjs",
|
||||
"download:windows-installer": "node ./scripts/download-win-installer-deps.mjs",
|
||||
"build:tauri:win32": "yarn download:bin && yarn download:lib && yarn download:windows-installer && yarn tauri build",
|
||||
"build:tauri:win32": "yarn download:bin && yarn download:lib && yarn tauri build",
|
||||
"build:tauri:linux": "yarn download:bin && yarn download:lib && NO_STRIP=1 ./src-tauri/build-utils/shim-linuxdeploy.sh yarn tauri build && ./src-tauri/build-utils/buildAppImage.sh",
|
||||
"build:tauri:darwin": "yarn download:bin && yarn tauri build --target universal-apple-darwin",
|
||||
"build:tauri": "yarn build:icon && yarn copy:assets:tauri && run-script-os",
|
||||
|
||||
@ -1,83 +0,0 @@
|
||||
console.log('Downloading Windows installer dependencies...')
|
||||
// scripts/download-win-installer-deps.mjs
|
||||
import https from 'https'
|
||||
import fs, { mkdirSync } from 'fs'
|
||||
import os from 'os'
|
||||
import path from 'path'
|
||||
import { copySync } from 'cpx'
|
||||
|
||||
function download(url, dest) {
|
||||
return new Promise((resolve, reject) => {
|
||||
console.log(`Downloading ${url} to ${dest}`)
|
||||
const file = fs.createWriteStream(dest)
|
||||
https
|
||||
.get(url, (response) => {
|
||||
console.log(`Response status code: ${response.statusCode}`)
|
||||
if (
|
||||
response.statusCode >= 300 &&
|
||||
response.statusCode < 400 &&
|
||||
response.headers.location
|
||||
) {
|
||||
// Handle redirect
|
||||
const redirectURL = response.headers.location
|
||||
console.log(`Redirecting to ${redirectURL}`)
|
||||
download(redirectURL, dest).then(resolve, reject) // Recursive call
|
||||
return
|
||||
} else if (response.statusCode !== 200) {
|
||||
reject(`Failed to get '${url}' (${response.statusCode})`)
|
||||
return
|
||||
}
|
||||
response.pipe(file)
|
||||
file.on('finish', () => {
|
||||
file.close(resolve)
|
||||
})
|
||||
})
|
||||
.on('error', (err) => {
|
||||
fs.unlink(dest, () => reject(err.message))
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
async function main() {
|
||||
console.log('Starting Windows installer dependencies download')
|
||||
const platform = os.platform() // 'darwin', 'linux', 'win32'
|
||||
const arch = os.arch() // 'x64', 'arm64', etc.
|
||||
|
||||
if (arch != 'x64') return
|
||||
|
||||
|
||||
const libDir = 'src-tauri/resources/lib'
|
||||
const tempDir = 'scripts/dist'
|
||||
|
||||
try {
|
||||
mkdirSync('scripts/dist')
|
||||
} catch (err) {
|
||||
// Expect EEXIST error if the directory already exists
|
||||
}
|
||||
|
||||
// Download VC++ Redistributable 17
|
||||
if (platform == 'win32') {
|
||||
const vcFilename = 'vc_redist.x64.exe'
|
||||
const vcUrl = 'https://aka.ms/vs/17/release/vc_redist.x64.exe'
|
||||
|
||||
console.log(`Downloading VC++ Redistributable...`)
|
||||
const vcSavePath = path.join(tempDir, vcFilename)
|
||||
if (!fs.existsSync(vcSavePath)) {
|
||||
await download(vcUrl, vcSavePath)
|
||||
}
|
||||
|
||||
// copy to tauri resources
|
||||
try {
|
||||
copySync(vcSavePath, libDir)
|
||||
} catch (err) {
|
||||
// Expect EEXIST error
|
||||
}
|
||||
}
|
||||
|
||||
console.log('Windows installer dependencies downloads completed.')
|
||||
}
|
||||
|
||||
main().catch((err) => {
|
||||
console.error('Error:', err)
|
||||
process.exit(1)
|
||||
})
|
||||
1007
src-tauri/tauri.bundle.windows.nsis.template
Normal file
1007
src-tauri/tauri.bundle.windows.nsis.template
Normal file
File diff suppressed because it is too large
Load Diff
@ -1,13 +1,9 @@
|
||||
{
|
||||
"bundle": {
|
||||
"targets": ["nsis", "msi"],
|
||||
"resources": ["resources/pre-install/**/*", "resources/lib/vulkan-1.dll", "resources/LICENSE", "resources/lib/vc_redist.x64.exe"],
|
||||
"resources": ["resources/pre-install/**/*", "resources/lib/vulkan-1.dll", "resources/LICENSE"],
|
||||
"externalBin": ["resources/bin/bun", "resources/bin/uv"],
|
||||
"windows": {
|
||||
"nsis": {
|
||||
"installerHooks": "./windows/hooks.nsh",
|
||||
"installerIcon": "icons/icon.ico"
|
||||
},
|
||||
"webviewInstallMode": {
|
||||
"silent": true,
|
||||
"type": "downloadBootstrapper"
|
||||
|
||||
@ -1,65 +0,0 @@
|
||||
!macro NSIS_HOOK_POSTINSTALL
|
||||
; Check if Visual C++ Redistributable is already installed
|
||||
ReadRegStr $0 HKLM "SOFTWARE\Microsoft\VisualStudio\14.0\VC\Runtimes\x64" "Version"
|
||||
${If} $0 == ""
|
||||
; Try alternative registry location
|
||||
ReadRegStr $0 HKLM "SOFTWARE\WOW6432Node\Microsoft\VisualStudio\14.0\VC\Runtimes\x64" "Version"
|
||||
${EndIf}
|
||||
|
||||
${If} $0 == ""
|
||||
; VC++ Redistributable not found, need to install
|
||||
DetailPrint "Visual C++ Redistributable not found, installing from bundled file..."
|
||||
|
||||
; Install from bundled EXE if not installed
|
||||
${If} ${FileExists} "$INSTDIR\resources\lib\vc_redist.x64.exe"
|
||||
DetailPrint "Installing Visual C++ Redistributable..."
|
||||
; Copy to TEMP folder and then execute installer
|
||||
CopyFiles "$INSTDIR\resources\lib\vc_redist.x64.exe" "$TEMP\vc_redist.x64.exe"
|
||||
ExecWait '"$TEMP\vc_redist.x64.exe" /quiet /norestart' $1
|
||||
|
||||
; Check whether installation process exited successfully (code 0) or not
|
||||
${If} $1 == 0
|
||||
DetailPrint "Visual C++ Redistributable installed successfully"
|
||||
${ElseIf} $1 == 1638
|
||||
DetailPrint "Visual C++ Redistributable already installed (newer version)"
|
||||
${ElseIf} $1 == 3010
|
||||
DetailPrint "Visual C++ Redistributable installed successfully (restart required)"
|
||||
${Else}
|
||||
DetailPrint "Visual C++ installation failed with exit code: $1"
|
||||
${EndIf}
|
||||
|
||||
; Clean up setup files from TEMP and your installed app
|
||||
Delete "$TEMP\vc_redist.x64.exe"
|
||||
Delete "$INSTDIR\resources\lib\vc_redist.x64.exe"
|
||||
${Else}
|
||||
DetailPrint "Visual C++ Redistributable not found at expected location: $INSTDIR\resources\lib\vc_redist.x64.exe"
|
||||
${EndIf}
|
||||
${Else}
|
||||
DetailPrint "Visual C++ Redistributable already installed (version: $0)"
|
||||
${EndIf}
|
||||
|
||||
; ---- Copy LICENSE to install root ----
|
||||
${If} ${FileExists} "$INSTDIR\resources\LICENSE"
|
||||
CopyFiles /SILENT "$INSTDIR\resources\LICENSE" "$INSTDIR\LICENSE"
|
||||
DetailPrint "Copied LICENSE to install root"
|
||||
|
||||
; Optional cleanup - remove from resources folder
|
||||
Delete "$INSTDIR\resources\LICENSE"
|
||||
${Else}
|
||||
DetailPrint "LICENSE not found at expected location: $INSTDIR\resources\LICENSE"
|
||||
${EndIf}
|
||||
|
||||
; ---- Copy vulkan-1.dll to install root ----
|
||||
${If} ${FileExists} "$INSTDIR\resources\lib\vulkan-1.dll"
|
||||
CopyFiles /SILENT "$INSTDIR\resources\lib\vulkan-1.dll" "$INSTDIR\vulkan-1.dll"
|
||||
DetailPrint "Copied vulkan-1.dll to install root"
|
||||
|
||||
; Optional cleanup - remove from resources folder
|
||||
Delete "$INSTDIR\resources\lib\vulkan-1.dll"
|
||||
|
||||
; Only remove the lib directory if it's empty after removing both files
|
||||
RMDir "$INSTDIR\resources\lib"
|
||||
${Else}
|
||||
DetailPrint "vulkan-1.dll not found at expected location: $INSTDIR\resources\lib\vulkan-1.dll"
|
||||
${EndIf}
|
||||
!macroend
|
||||
@ -82,7 +82,7 @@
|
||||
"remark-math": "6.0.0",
|
||||
"sonner": "2.0.5",
|
||||
"tailwindcss": "4.1.4",
|
||||
"token.js": "npm:token.js-fork@0.7.27",
|
||||
"token.js": "npm:token.js-fork@0.7.29",
|
||||
"tw-animate-css": "1.2.8",
|
||||
"ulidx": "2.4.1",
|
||||
"unified": "11.0.5",
|
||||
|
||||
@ -96,7 +96,7 @@ export const predefinedProviders = [
|
||||
{
|
||||
active: true,
|
||||
api_key: '',
|
||||
base_url: 'https://api.anthropic.com',
|
||||
base_url: 'https://api.anthropic.com/v1',
|
||||
provider: 'anthropic',
|
||||
explore_models_url:
|
||||
'https://docs.anthropic.com/en/docs/about-claude/models',
|
||||
@ -127,11 +127,21 @@ export const predefinedProviders = [
|
||||
},
|
||||
],
|
||||
models: [],
|
||||
custom_header: [
|
||||
{
|
||||
header: 'anthropic-version',
|
||||
value: '2023-06-01'
|
||||
},
|
||||
{
|
||||
header: 'anthropic-dangerous-direct-browser-access',
|
||||
value: 'true'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
active: true,
|
||||
api_key: '',
|
||||
base_url: 'https://api.cohere.ai/compatibility/v1',
|
||||
base_url: 'https://api.cohere.ai/v1',
|
||||
explore_models_url: 'https://docs.cohere.com/v2/docs/models',
|
||||
provider: 'cohere',
|
||||
settings: [
|
||||
|
||||
@ -129,7 +129,10 @@ const ChatInput = ({
|
||||
const activeModels = await serviceHub
|
||||
.models()
|
||||
.getActiveModels('llamacpp')
|
||||
setHasActiveModels(activeModels.length > 0)
|
||||
const hasMatchingActiveModel = activeModels.some(
|
||||
(model) => String(model) === selectedModel?.id
|
||||
)
|
||||
setHasActiveModels(activeModels.length > 0 && hasMatchingActiveModel)
|
||||
} catch (error) {
|
||||
console.error('Failed to get active models:', error)
|
||||
setHasActiveModels(false)
|
||||
@ -142,7 +145,7 @@ const ChatInput = ({
|
||||
const intervalId = setInterval(checkActiveModels, 3000)
|
||||
|
||||
return () => clearInterval(intervalId)
|
||||
}, [serviceHub])
|
||||
}, [serviceHub, selectedModel?.id])
|
||||
|
||||
// Check for mmproj existence or vision capability when model changes
|
||||
useEffect(() => {
|
||||
|
||||
@ -15,8 +15,7 @@ import { IconPlus } from '@tabler/icons-react'
|
||||
import { useState } from 'react'
|
||||
import { getProviderTitle } from '@/lib/utils'
|
||||
import { useTranslation } from '@/i18n/react-i18next-compat'
|
||||
import { ModelCapabilities } from '@/types/models'
|
||||
import { models as providerModels } from 'token.js'
|
||||
import { getModelCapabilities } from '@/lib/models'
|
||||
import { toast } from 'sonner'
|
||||
|
||||
type DialogAddModelProps = {
|
||||
@ -52,23 +51,7 @@ export const DialogAddModel = ({ provider, trigger }: DialogAddModelProps) => {
|
||||
id: modelId,
|
||||
model: modelId,
|
||||
name: modelId,
|
||||
capabilities: [
|
||||
ModelCapabilities.COMPLETION,
|
||||
(
|
||||
providerModels[
|
||||
provider.provider as unknown as keyof typeof providerModels
|
||||
]?.supportsToolCalls as unknown as string[]
|
||||
)?.includes(modelId)
|
||||
? ModelCapabilities.TOOLS
|
||||
: undefined,
|
||||
(
|
||||
providerModels[
|
||||
provider.provider as unknown as keyof typeof providerModels
|
||||
]?.supportsImages as unknown as string[]
|
||||
)?.includes(modelId)
|
||||
? ModelCapabilities.VISION
|
||||
: undefined,
|
||||
].filter(Boolean) as string[],
|
||||
capabilities: getModelCapabilities(provider.provider, modelId),
|
||||
version: '1.0',
|
||||
}
|
||||
|
||||
|
||||
@ -320,9 +320,82 @@ export const useModelProvider = create<ModelProviderState>()(
|
||||
})
|
||||
}
|
||||
|
||||
if (version <= 3 && state?.providers) {
|
||||
state.providers.forEach((provider) => {
|
||||
// Migrate Anthropic provider base URL and add custom headers
|
||||
if (provider.provider === 'anthropic') {
|
||||
if (provider.base_url === 'https://api.anthropic.com') {
|
||||
provider.base_url = 'https://api.anthropic.com/v1'
|
||||
}
|
||||
|
||||
// Update base-url in settings
|
||||
if (provider.settings) {
|
||||
const baseUrlSetting = provider.settings.find(
|
||||
(s) => s.key === 'base-url'
|
||||
)
|
||||
if (
|
||||
baseUrlSetting?.controller_props?.value ===
|
||||
'https://api.anthropic.com'
|
||||
) {
|
||||
baseUrlSetting.controller_props.value =
|
||||
'https://api.anthropic.com/v1'
|
||||
}
|
||||
if (
|
||||
baseUrlSetting?.controller_props?.placeholder ===
|
||||
'https://api.anthropic.com'
|
||||
) {
|
||||
baseUrlSetting.controller_props.placeholder =
|
||||
'https://api.anthropic.com/v1'
|
||||
}
|
||||
}
|
||||
|
||||
if (!provider.custom_header) {
|
||||
provider.custom_header = [
|
||||
{
|
||||
header: 'anthropic-version',
|
||||
value: '2023-06-01',
|
||||
},
|
||||
{
|
||||
header: 'anthropic-dangerous-direct-browser-access',
|
||||
value: 'true',
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
if (provider.provider === 'cohere') {
|
||||
if (provider.base_url === 'https://api.cohere.ai/compatibility/v1') {
|
||||
provider.base_url = 'https://api.cohere.ai/v1'
|
||||
}
|
||||
|
||||
// Update base-url in settings
|
||||
if (provider.settings) {
|
||||
const baseUrlSetting = provider.settings.find(
|
||||
(s) => s.key === 'base-url'
|
||||
)
|
||||
if (
|
||||
baseUrlSetting?.controller_props?.value ===
|
||||
'https://api.cohere.ai/compatibility/v1'
|
||||
) {
|
||||
baseUrlSetting.controller_props.value =
|
||||
'https://api.cohere.ai/v1'
|
||||
}
|
||||
if (
|
||||
baseUrlSetting?.controller_props?.placeholder ===
|
||||
'https://api.cohere.ai/compatibility/v1'
|
||||
) {
|
||||
baseUrlSetting.controller_props.placeholder =
|
||||
'https://api.cohere.ai/v1'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
return state
|
||||
},
|
||||
version: 3,
|
||||
version: 4,
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
@ -5,19 +5,30 @@ import {
|
||||
removeYamlFrontMatter,
|
||||
extractModelName,
|
||||
extractModelRepo,
|
||||
getModelCapabilities,
|
||||
} from '../models'
|
||||
import { ModelCapabilities } from '@/types/models'
|
||||
|
||||
// Mock the token.js module
|
||||
vi.mock('token.js', () => ({
|
||||
models: {
|
||||
openai: {
|
||||
models: ['gpt-3.5-turbo', 'gpt-4'],
|
||||
supportsToolCalls: ['gpt-3.5-turbo', 'gpt-4'],
|
||||
supportsImages: ['gpt-4-vision-preview'],
|
||||
},
|
||||
anthropic: {
|
||||
models: ['claude-3-sonnet', 'claude-3-haiku'],
|
||||
supportsToolCalls: ['claude-3-sonnet'],
|
||||
supportsImages: ['claude-3-sonnet', 'claude-3-haiku'],
|
||||
},
|
||||
mistral: {
|
||||
models: ['mistral-7b', 'mistral-8x7b'],
|
||||
supportsToolCalls: ['mistral-8x7b'],
|
||||
},
|
||||
// Provider with no capability arrays
|
||||
cohere: {
|
||||
models: ['command', 'command-light'],
|
||||
},
|
||||
},
|
||||
}))
|
||||
@ -223,3 +234,74 @@ describe('extractModelRepo', () => {
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe('getModelCapabilities', () => {
|
||||
it('returns completion capability for all models', () => {
|
||||
const capabilities = getModelCapabilities('openai', 'gpt-3.5-turbo')
|
||||
expect(capabilities).toContain(ModelCapabilities.COMPLETION)
|
||||
})
|
||||
|
||||
it('includes tools capability when model supports it', () => {
|
||||
const capabilities = getModelCapabilities('openai', 'gpt-3.5-turbo')
|
||||
expect(capabilities).toContain(ModelCapabilities.TOOLS)
|
||||
expect(capabilities).toContain(ModelCapabilities.COMPLETION)
|
||||
})
|
||||
|
||||
it('excludes tools capability when model does not support it', () => {
|
||||
const capabilities = getModelCapabilities('mistral', 'mistral-7b')
|
||||
expect(capabilities).not.toContain(ModelCapabilities.TOOLS)
|
||||
expect(capabilities).toContain(ModelCapabilities.COMPLETION)
|
||||
})
|
||||
|
||||
it('includes vision capability when model supports it', () => {
|
||||
const capabilities = getModelCapabilities('openai', 'gpt-4-vision-preview')
|
||||
expect(capabilities).toContain(ModelCapabilities.VISION)
|
||||
expect(capabilities).toContain(ModelCapabilities.COMPLETION)
|
||||
})
|
||||
|
||||
it('excludes vision capability when model does not support it', () => {
|
||||
const capabilities = getModelCapabilities('openai', 'gpt-3.5-turbo')
|
||||
expect(capabilities).not.toContain(ModelCapabilities.VISION)
|
||||
})
|
||||
|
||||
it('includes both tools and vision when model supports both', () => {
|
||||
const capabilities = getModelCapabilities('anthropic', 'claude-3-sonnet')
|
||||
expect(capabilities).toContain(ModelCapabilities.COMPLETION)
|
||||
expect(capabilities).toContain(ModelCapabilities.TOOLS)
|
||||
expect(capabilities).toContain(ModelCapabilities.VISION)
|
||||
})
|
||||
|
||||
it('handles provider with no capability arrays gracefully', () => {
|
||||
const capabilities = getModelCapabilities('cohere', 'command')
|
||||
expect(capabilities).toEqual([ModelCapabilities.COMPLETION])
|
||||
expect(capabilities).not.toContain(ModelCapabilities.TOOLS)
|
||||
expect(capabilities).not.toContain(ModelCapabilities.VISION)
|
||||
})
|
||||
|
||||
it('handles unknown provider gracefully', () => {
|
||||
const capabilities = getModelCapabilities('openrouter', 'some-model')
|
||||
expect(capabilities).toEqual([ModelCapabilities.COMPLETION])
|
||||
expect(capabilities).not.toContain(ModelCapabilities.TOOLS)
|
||||
expect(capabilities).not.toContain(ModelCapabilities.VISION)
|
||||
})
|
||||
|
||||
it('handles model not in capability list', () => {
|
||||
const capabilities = getModelCapabilities('anthropic', 'claude-3-haiku')
|
||||
expect(capabilities).toContain(ModelCapabilities.COMPLETION)
|
||||
expect(capabilities).toContain(ModelCapabilities.VISION)
|
||||
expect(capabilities).not.toContain(ModelCapabilities.TOOLS)
|
||||
})
|
||||
|
||||
it('returns only completion for provider with partial capability data', () => {
|
||||
// Mistral has supportsToolCalls but no supportsImages
|
||||
const capabilities = getModelCapabilities('mistral', 'mistral-7b')
|
||||
expect(capabilities).toEqual([ModelCapabilities.COMPLETION])
|
||||
})
|
||||
|
||||
it('handles model that supports tools but not vision', () => {
|
||||
const capabilities = getModelCapabilities('mistral', 'mistral-8x7b')
|
||||
expect(capabilities).toContain(ModelCapabilities.COMPLETION)
|
||||
expect(capabilities).toContain(ModelCapabilities.TOOLS)
|
||||
expect(capabilities).not.toContain(ModelCapabilities.VISION)
|
||||
})
|
||||
})
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import { models } from 'token.js'
|
||||
import { ModelCapabilities } from '@/types/models'
|
||||
|
||||
export const defaultModel = (provider?: string) => {
|
||||
if (!provider || !Object.keys(models).includes(provider)) {
|
||||
@ -10,6 +11,38 @@ export const defaultModel = (provider?: string) => {
|
||||
)[0]
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines model capabilities based on provider configuration from token.js
|
||||
* @param providerName - The provider name (e.g., 'openai', 'anthropic', 'openrouter')
|
||||
* @param modelId - The model ID to check capabilities for
|
||||
* @returns Array of model capabilities
|
||||
*/
|
||||
export const getModelCapabilities = (
|
||||
providerName: string,
|
||||
modelId: string
|
||||
): string[] => {
|
||||
const providerConfig =
|
||||
models[providerName as unknown as keyof typeof models]
|
||||
|
||||
const supportsToolCalls = Array.isArray(
|
||||
providerConfig?.supportsToolCalls as unknown
|
||||
)
|
||||
? (providerConfig.supportsToolCalls as unknown as string[])
|
||||
: []
|
||||
|
||||
const supportsImages = Array.isArray(
|
||||
providerConfig?.supportsImages as unknown
|
||||
)
|
||||
? (providerConfig.supportsImages as unknown as string[])
|
||||
: []
|
||||
|
||||
return [
|
||||
ModelCapabilities.COMPLETION,
|
||||
supportsToolCalls.includes(modelId) ? ModelCapabilities.TOOLS : undefined,
|
||||
supportsImages.includes(modelId) ? ModelCapabilities.VISION : undefined,
|
||||
].filter(Boolean) as string[]
|
||||
}
|
||||
|
||||
/**
|
||||
* This utility is to extract cortexso model description from README.md file
|
||||
* @returns
|
||||
|
||||
@ -39,7 +39,7 @@ function ModelProviders() {
|
||||
toast.error(t('providerAlreadyExists', { name }))
|
||||
return
|
||||
}
|
||||
const newProvider = {
|
||||
const newProvider: ProviderObject = {
|
||||
provider: name,
|
||||
active: true,
|
||||
models: [],
|
||||
|
||||
@ -578,6 +578,9 @@ export class DefaultModelsService implements ModelsService {
|
||||
}
|
||||
}>
|
||||
}>
|
||||
chat_template_kwargs?: {
|
||||
enable_thinking: boolean
|
||||
}
|
||||
}) => Promise<number>
|
||||
}
|
||||
|
||||
@ -654,6 +657,9 @@ export class DefaultModelsService implements ModelsService {
|
||||
return await engine.getTokensCount({
|
||||
model: modelId,
|
||||
messages: transformedMessages,
|
||||
chat_template_kwargs: {
|
||||
enable_thinking: false,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@ -10,6 +10,7 @@ import { modelSettings } from '@/lib/predefined'
|
||||
import { ExtensionManager } from '@/lib/extension'
|
||||
import { fetch as fetchTauri } from '@tauri-apps/plugin-http'
|
||||
import { DefaultProvidersService } from './default'
|
||||
import { getModelCapabilities } from '@/lib/models'
|
||||
|
||||
export class TauriProvidersService extends DefaultProvidersService {
|
||||
fetch(): typeof fetch {
|
||||
@ -26,32 +27,16 @@ export class TauriProvidersService extends DefaultProvidersService {
|
||||
provider.provider as unknown as keyof typeof providerModels
|
||||
].models as unknown as string[]
|
||||
|
||||
if (Array.isArray(builtInModels))
|
||||
if (Array.isArray(builtInModels)) {
|
||||
models = builtInModels.map((model) => {
|
||||
const modelManifest = models.find((e) => e.id === model)
|
||||
// TODO: Check chat_template for tool call support
|
||||
const capabilities = [
|
||||
ModelCapabilities.COMPLETION,
|
||||
(
|
||||
providerModels[
|
||||
provider.provider as unknown as keyof typeof providerModels
|
||||
]?.supportsToolCalls as unknown as string[]
|
||||
)?.includes(model)
|
||||
? ModelCapabilities.TOOLS
|
||||
: undefined,
|
||||
(
|
||||
providerModels[
|
||||
provider.provider as unknown as keyof typeof providerModels
|
||||
]?.supportsImages as unknown as string[]
|
||||
)?.includes(model)
|
||||
? ModelCapabilities.VISION
|
||||
: undefined,
|
||||
].filter(Boolean) as string[]
|
||||
return {
|
||||
...(modelManifest ?? { id: model, name: model }),
|
||||
capabilities,
|
||||
capabilities: getModelCapabilities(provider.provider, model),
|
||||
} as Model
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
@ -166,6 +151,12 @@ export class TauriProvidersService extends DefaultProvidersService {
|
||||
headers['Authorization'] = `Bearer ${provider.api_key}`
|
||||
}
|
||||
|
||||
if (provider.custom_header) {
|
||||
provider.custom_header.forEach((header) => {
|
||||
headers[header.header] = header.value
|
||||
})
|
||||
}
|
||||
|
||||
// Always use Tauri's fetch to avoid CORS issues
|
||||
const response = await fetchTauri(`${provider.base_url}/models`, {
|
||||
method: 'GET',
|
||||
|
||||
@ -11,6 +11,7 @@ import { ExtensionManager } from '@/lib/extension'
|
||||
import type { ProvidersService } from './types'
|
||||
import { PlatformFeatures } from '@/lib/platform/const'
|
||||
import { PlatformFeature } from '@/lib/platform/types'
|
||||
import { getModelCapabilities } from '@/lib/models'
|
||||
|
||||
export class WebProvidersService implements ProvidersService {
|
||||
async getProviders(): Promise<ModelProvider[]> {
|
||||
@ -88,19 +89,9 @@ export class WebProvidersService implements ProvidersService {
|
||||
models = builtInModels.map((model) => {
|
||||
const modelManifest = models.find((e) => e.id === model)
|
||||
// TODO: Check chat_template for tool call support
|
||||
const capabilities = [
|
||||
ModelCapabilities.COMPLETION,
|
||||
(
|
||||
providerModels[
|
||||
provider.provider as unknown as keyof typeof providerModels
|
||||
]?.supportsToolCalls as unknown as string[]
|
||||
)?.includes(model)
|
||||
? ModelCapabilities.TOOLS
|
||||
: undefined,
|
||||
].filter(Boolean) as string[]
|
||||
return {
|
||||
...(modelManifest ?? { id: model, name: model }),
|
||||
capabilities,
|
||||
capabilities: getModelCapabilities(provider.provider, model),
|
||||
} as Model
|
||||
})
|
||||
}
|
||||
|
||||
6
web-app/src/types/modelProviders.d.ts
vendored
6
web-app/src/types/modelProviders.d.ts
vendored
@ -48,6 +48,7 @@ type ProviderObject = {
|
||||
settings: ProviderSetting[]
|
||||
models: Model[]
|
||||
persist?: boolean
|
||||
custom_header?: ProviderCustomHeader[] | null
|
||||
}
|
||||
|
||||
/**
|
||||
@ -71,3 +72,8 @@ type ProxyOptions = {
|
||||
verifyHostSSL: boolean
|
||||
noProxy: string
|
||||
}
|
||||
|
||||
type ProviderCustomHeader = {
|
||||
header: string
|
||||
value: string
|
||||
}
|
||||
31
yarn.lock
31
yarn.lock
@ -3490,7 +3490,7 @@ __metadata:
|
||||
"@vitest/coverage-v8": "npm:^2.1.8"
|
||||
"@vitest/ui": "npm:^2.1.8"
|
||||
eslint: "npm:8.57.0"
|
||||
happy-dom: "npm:^15.11.6"
|
||||
happy-dom: "npm:^20.0.0"
|
||||
pacote: "npm:^21.0.0"
|
||||
request: "npm:^2.88.2"
|
||||
request-progress: "npm:^3.0.0"
|
||||
@ -3600,7 +3600,7 @@ __metadata:
|
||||
sonner: "npm:2.0.5"
|
||||
tailwind-merge: "npm:3.3.1"
|
||||
tailwindcss: "npm:4.1.4"
|
||||
token.js: "npm:token.js-fork@0.7.27"
|
||||
token.js: "npm:token.js-fork@0.7.29"
|
||||
tw-animate-css: "npm:1.2.8"
|
||||
typescript: "npm:5.9.2"
|
||||
typescript-eslint: "npm:8.31.0"
|
||||
@ -10630,13 +10630,6 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"entities@npm:^4.5.0":
|
||||
version: 4.5.0
|
||||
resolution: "entities@npm:4.5.0"
|
||||
checksum: 10c0/5b039739f7621f5d1ad996715e53d964035f75ad3b9a4d38c6b3804bb226e282ffeae2443624d8fdd9c47d8e926ae9ac009c54671243f0c3294c26af7cc85250
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"entities@npm:^6.0.0":
|
||||
version: 6.0.1
|
||||
resolution: "entities@npm:6.0.1"
|
||||
@ -12350,14 +12343,14 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"happy-dom@npm:^15.11.6":
|
||||
version: 15.11.7
|
||||
resolution: "happy-dom@npm:15.11.7"
|
||||
"happy-dom@npm:^20.0.0":
|
||||
version: 20.0.1
|
||||
resolution: "happy-dom@npm:20.0.1"
|
||||
dependencies:
|
||||
entities: "npm:^4.5.0"
|
||||
webidl-conversions: "npm:^7.0.0"
|
||||
"@types/node": "npm:^20.0.0"
|
||||
"@types/whatwg-mimetype": "npm:^3.0.2"
|
||||
whatwg-mimetype: "npm:^3.0.0"
|
||||
checksum: 10c0/22b08cac20192b08edf2e9c857ceeda8333a3301c4b5965a9550787b00db60d6d107c726390bd45a35305cd12ab086abd656bf957a408be0fcdc9fcd389f1973
|
||||
checksum: 10c0/fb867fcca270ebb185b6f2031721d3ea43c99e0699069187dceee99b14683baca243157feed2ce0da3ba8905b914262caa7bc8403384175a0ad2c81e19bf2f5a
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@ -19624,9 +19617,9 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"token.js@npm:token.js-fork@0.7.27":
|
||||
version: 0.7.27
|
||||
resolution: "token.js-fork@npm:0.7.27"
|
||||
"token.js@npm:token.js-fork@0.7.29":
|
||||
version: 0.7.29
|
||||
resolution: "token.js-fork@npm:0.7.29"
|
||||
dependencies:
|
||||
"@anthropic-ai/sdk": "npm:0.24.3"
|
||||
"@aws-sdk/client-bedrock-runtime": "npm:3.609.0"
|
||||
@ -19637,7 +19630,7 @@ __metadata:
|
||||
mime-types: "npm:^2.1.35"
|
||||
nanoid: "npm:^5.0.7"
|
||||
openai: "npm:4.91.1"
|
||||
checksum: 10c0/ec4e8e441b6747db29eed0d21e364eaf8d4636e3d8376bdd63d836499970de15357e8c0b2ef1e470027e7a2c8bc4924138a86f6d207469b6f0b6fb0f24f6d035
|
||||
checksum: 10c0/b045de56e06a1066b1fdfcca24bc57e7b10aa6cd1995b9ded27af699afcf0e72e216c3672cc3a85b10ce5b6ea81e7d1d453859f073861176b0c816e8f91e6627
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user