Merge pull request #4468 from janhq/dev

release: Jan 0.5.14 release cut
This commit is contained in:
Louis 2025-01-16 17:14:15 +07:00 committed by GitHub
commit b9ea4baaa8
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
507 changed files with 44227 additions and 8638 deletions

View File

@ -18,6 +18,7 @@ if [ ! -f "$FILE_PATH" ]; then
fi
# Perform the replacements
sed -i -e "s#Jan#Jan-$CHANNEL#g" "$FILE_PATH"
sed -i -e "s#jan#jan-$CHANNEL#g" "$FILE_PATH"
# Notify completion

View File

@ -44,16 +44,17 @@ jobs:
- uses: actions/checkout@v3
with:
ref: ${{ github.base_ref }}
- name: Use Node.js v20.9.0
- name: Use Node.js 20.x
uses: actions/setup-node@v3
with:
node-version: v20.9.0
node-version: 20
- name: Install dependencies
run: |
make config-yarn
yarn
yarn build:core
yarn build:joi
yarn build:core
- name: Run test coverage
run: yarn test:coverage
@ -66,7 +67,7 @@ jobs:
test-on-macos:
if: (github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository) || github.event_name == 'push' || github.event_name == 'workflow_dispatch'
runs-on: [self-hosted, macOS, macos-desktop]
runs-on: macos-latest
steps:
- name: Getting the repo
uses: actions/checkout@v3
@ -78,6 +79,10 @@ jobs:
with:
node-version: 20
- name: Set IS_TEST environment variable
run: |
echo "IS_TEST=true" >> $GITHUB_ENV
- name: 'Cleanup cache'
continue-on-error: true
run: |
@ -94,24 +99,19 @@ jobs:
run: |
echo "REPORT_PORTAL_DESCRIPTION=${{github.sha}})" >> $GITHUB_ENV
- name: 'Config report portal'
run: |
make update-playwright-config REPORT_PORTAL_URL=${{ secrets.REPORT_PORTAL_URL }} REPORT_PORTAL_API_KEY=${{ secrets.REPORT_PORTAL_API_KEY }} REPORT_PORTAL_PROJECT_NAME=${{ secrets.REPORT_PORTAL_PROJECT_NAME }} REPORT_PORTAL_LAUNCH_NAME="Jan App macos" REPORT_PORTAL_DESCRIPTION="${{env.REPORT_PORTAL_DESCRIPTION}}"
# - name: 'Config report portal'
# run: |
# make update-playwright-config REPORT_PORTAL_URL=${{ secrets.REPORT_PORTAL_URL }} REPORT_PORTAL_API_KEY=${{ secrets.REPORT_PORTAL_API_KEY }} REPORT_PORTAL_PROJECT_NAME=${{ secrets.REPORT_PORTAL_PROJECT_NAME }} REPORT_PORTAL_LAUNCH_NAME="Jan App macos" REPORT_PORTAL_DESCRIPTION="${{env.REPORT_PORTAL_DESCRIPTION}}"
- name: Linter and test
run: |
npm config set registry ${{ secrets.NPM_PROXY }} --global
yarn config set registry ${{ secrets.NPM_PROXY }} --global
make test
env:
CSC_IDENTITY_AUTO_DISCOVERY: 'false'
# TURBO_API: '${{ secrets.TURBO_API }}'
# TURBO_TEAM: 'macos'
# TURBO_TOKEN: '${{ secrets.TURBO_TOKEN }}'
test-on-macos-pr-target:
if: github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name != github.repository
runs-on: [self-hosted, macOS, macos-desktop]
runs-on: macos-latest
steps:
- name: Getting the repo
uses: actions/checkout@v3
@ -131,8 +131,6 @@ jobs:
- name: Linter and test
run: |
npm config set registry https://registry.npmjs.org --global
yarn config set registry https://registry.npmjs.org --global
make test
env:
CSC_IDENTITY_AUTO_DISCOVERY: 'false'
@ -174,24 +172,19 @@ jobs:
run: |
echo "REPORT_PORTAL_DESCRIPTION=${{github.sha}}" >> $GITHUB_ENV
- name: 'Config report portal'
shell: bash
run: |
make update-playwright-config REPORT_PORTAL_URL=${{ secrets.REPORT_PORTAL_URL }} REPORT_PORTAL_API_KEY=${{ secrets.REPORT_PORTAL_API_KEY }} REPORT_PORTAL_PROJECT_NAME=${{ secrets.REPORT_PORTAL_PROJECT_NAME }} REPORT_PORTAL_LAUNCH_NAME="Jan App Windows ${{ matrix.antivirus-tools }}" REPORT_PORTAL_DESCRIPTION="${{env.REPORT_PORTAL_DESCRIPTION}}"
# - name: 'Config report portal'
# shell: bash
# run: |
# make update-playwright-config REPORT_PORTAL_URL=${{ secrets.REPORT_PORTAL_URL }} REPORT_PORTAL_API_KEY=${{ secrets.REPORT_PORTAL_API_KEY }} REPORT_PORTAL_PROJECT_NAME=${{ secrets.REPORT_PORTAL_PROJECT_NAME }} REPORT_PORTAL_LAUNCH_NAME="Jan App Windows ${{ matrix.antivirus-tools }}" REPORT_PORTAL_DESCRIPTION="${{env.REPORT_PORTAL_DESCRIPTION}}"
- name: Linter and test
shell: powershell
run: |
npm config set registry ${{ secrets.NPM_PROXY }} --global
yarn config set registry ${{ secrets.NPM_PROXY }} --global
make test
# env:
# TURBO_API: '${{ secrets.TURBO_API }}'
# TURBO_TEAM: 'windows'
# TURBO_TOKEN: '${{ secrets.TURBO_TOKEN }}'
test-on-windows-pr:
if: (github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository)
runs-on: windows-desktop-default-windows-security
if: (github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository) || github.event_name == 'workflow_dispatch'
runs-on: windows-latest
steps:
- name: Getting the repo
uses: actions/checkout@v3
@ -199,7 +192,7 @@ jobs:
fetch-depth: 0
- name: Installing node
uses: actions/setup-node@v1
uses: actions/setup-node@v3
with:
node-version: 20
@ -222,25 +215,19 @@ jobs:
run: |
echo "REPORT_PORTAL_DESCRIPTION=${{github.event.after}}" >> $GITHUB_ENV
- name: 'Config report portal'
shell: bash
run: |
make update-playwright-config REPORT_PORTAL_URL=${{ secrets.REPORT_PORTAL_URL }} REPORT_PORTAL_API_KEY=${{ secrets.REPORT_PORTAL_API_KEY }} REPORT_PORTAL_PROJECT_NAME=${{ secrets.REPORT_PORTAL_PROJECT_NAME }} REPORT_PORTAL_LAUNCH_NAME="Jan App Windows" REPORT_PORTAL_DESCRIPTION="${{env.REPORT_PORTAL_DESCRIPTION}}"
# - name: 'Config report portal'
# shell: bash
# run: |
# make update-playwright-config REPORT_PORTAL_URL=${{ secrets.REPORT_PORTAL_URL }} REPORT_PORTAL_API_KEY=${{ secrets.REPORT_PORTAL_API_KEY }} REPORT_PORTAL_PROJECT_NAME=${{ secrets.REPORT_PORTAL_PROJECT_NAME }} REPORT_PORTAL_LAUNCH_NAME="Jan App Windows" REPORT_PORTAL_DESCRIPTION="${{env.REPORT_PORTAL_DESCRIPTION}}"
- name: Linter and test
shell: powershell
run: |
npm config set registry ${{ secrets.NPM_PROXY }} --global
yarn config set registry ${{ secrets.NPM_PROXY }} --global
make test
# env:
# TURBO_API: '${{ secrets.TURBO_API }}'
# TURBO_TEAM: 'windows'
# TURBO_TOKEN: '${{ secrets.TURBO_TOKEN }}'
test-on-windows-pr-target:
if: github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name != github.repository
runs-on: windows-desktop-default-windows-security
runs-on: windows-latest
steps:
- name: Getting the repo
uses: actions/checkout@v3
@ -268,12 +255,10 @@ jobs:
- name: Linter and test
shell: powershell
run: |
npm config set registry https://registry.npmjs.org --global
yarn config set registry https://registry.npmjs.org --global
make test
test-on-ubuntu:
runs-on: [self-hosted, Linux, ubuntu-desktop]
runs-on: ubuntu-latest
if: (github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository) || github.event_name == 'push' || github.event_name == 'workflow_dispatch'
steps:
- name: Getting the repo
@ -302,22 +287,16 @@ jobs:
run: |
echo "REPORT_PORTAL_DESCRIPTION=${{github.sha}}" >> $GITHUB_ENV
- name: 'Config report portal'
shell: bash
run: |
make update-playwright-config REPORT_PORTAL_URL=${{ secrets.REPORT_PORTAL_URL }} REPORT_PORTAL_API_KEY=${{ secrets.REPORT_PORTAL_API_KEY }} REPORT_PORTAL_PROJECT_NAME=${{ secrets.REPORT_PORTAL_PROJECT_NAME }} REPORT_PORTAL_LAUNCH_NAME="Jan App Linux" REPORT_PORTAL_DESCRIPTION="${{env.REPORT_PORTAL_DESCRIPTION}}"
# - name: 'Config report portal'
# shell: bash
# run: |
# make update-playwright-config REPORT_PORTAL_URL=${{ secrets.REPORT_PORTAL_URL }} REPORT_PORTAL_API_KEY=${{ secrets.REPORT_PORTAL_API_KEY }} REPORT_PORTAL_PROJECT_NAME=${{ secrets.REPORT_PORTAL_PROJECT_NAME }} REPORT_PORTAL_LAUNCH_NAME="Jan App Linux" REPORT_PORTAL_DESCRIPTION="${{env.REPORT_PORTAL_DESCRIPTION}}"
- name: Linter and test
run: |
export DISPLAY=$(w -h | awk 'NR==1 {print $2}')
echo -e "Display ID: $DISPLAY"
npm config set registry ${{ secrets.NPM_PROXY }} --global
yarn config set registry ${{ secrets.NPM_PROXY }} --global
make test
# env:
# TURBO_API: '${{ secrets.TURBO_API }}'
# TURBO_TEAM: 'linux'
# TURBO_TOKEN: '${{ secrets.TURBO_TOKEN }}'
- uses: actions/upload-artifact@v4
if: always()
@ -327,7 +306,7 @@ jobs:
retention-days: 2
coverage-check:
runs-on: [self-hosted, Linux, ubuntu-desktop]
runs-on: ubuntu-latest
needs: base_branch_cov
if: (github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository) || github.event_name == 'push' || github.event_name == 'workflow_dispatch'
steps:
@ -341,6 +320,9 @@ jobs:
with:
node-version: 20
- name: Install yarn
run: npm install -g yarn
- name: 'Cleanup cache'
continue-on-error: true
run: |
@ -356,15 +338,9 @@ jobs:
run: |
export DISPLAY=$(w -h | awk 'NR==1 {print $2}')
echo -e "Display ID: $DISPLAY"
npm config set registry ${{ secrets.NPM_PROXY }} --global
yarn config set registry ${{ secrets.NPM_PROXY }} --global
make lint
yarn build:test
yarn test:coverage
# env:
# TURBO_API: '${{ secrets.TURBO_API }}'
# TURBO_TEAM: 'linux'
# TURBO_TOKEN: '${{ secrets.TURBO_TOKEN }}'
- name: Generate Code Coverage report
id: code-coverage
@ -377,7 +353,7 @@ jobs:
show-annotations: 'warning'
test-on-ubuntu-pr-target:
runs-on: [self-hosted, Linux, ubuntu-desktop]
runs-on: ubuntu-latest
if: github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name != github.repository
steps:
- name: Getting the repo
@ -400,6 +376,4 @@ jobs:
run: |
export DISPLAY=$(w -h | awk 'NR==1 {print $2}')
echo -e "Display ID: $DISPLAY"
npm config set registry https://registry.npmjs.org --global
yarn config set registry https://registry.npmjs.org --global
make test

View File

@ -1,10 +1,10 @@
name: Publish plugin models Package to npmjs
name: Publish core Package to npmjs
on:
push:
tags: ["v[0-9]+.[0-9]+.[0-9]+-core"]
paths: ["core/**"]
paths: ["core/**", ".github/workflows/publish-npm-core.yml"]
pull_request:
paths: ["core/**"]
paths: ["core/**", ".github/workflows/publish-npm-core.yml"]
jobs:
build-and-publish-plugins:
environment: production
@ -45,7 +45,7 @@ jobs:
node-version: "20.x"
registry-url: "https://registry.npmjs.org"
- run: cd core && yarn install && yarn build
- run: cd core && corepack enable && corepack prepare yarn@4.5.3 --activate && yarn --version && yarn config set -H enableImmutableInstalls false && yarn install && yarn build
- run: cd core && yarn publish --access public
if: github.event_name == 'push'

View File

@ -1,10 +1,10 @@
name: Publish plugin models Package to npmjs
name: Publish joi Package to npmjs
on:
push:
tags: ["v[0-9]+.[0-9]+.[0-9]+-joi"]
paths: ["joi/**"]
paths: ["joi/**", ".github/workflows/publish-npm-joi.yml"]
pull_request:
paths: ["joi/**"]
paths: ["joi/**", ".github/workflows/publish-npm-joi.yml"]
jobs:
build-and-publish-plugins:
environment: production
@ -45,7 +45,7 @@ jobs:
node-version: "20.x"
registry-url: "https://registry.npmjs.org"
- run: cd joi && yarn install && yarn build
- run: cd joi && corepack enable && corepack prepare yarn@4.5.3 --activate && yarn --version && yarn config set -H enableImmutableInstalls false && yarn install && yarn build
- run: cd joi && yarn publish --access public
if: github.event_name == 'push'

5
.gitignore vendored
View File

@ -8,7 +8,6 @@ error.log
node_modules
*.tgz
!charts/server/charts/*.tgz
yarn.lock
dist
build
.DS_Store
@ -48,3 +47,7 @@ coverage
test_results.html
*.tsbuildinfo
electron/shared/**
# docs
docs/yarn.lock
electron/.version.bak

3
.yarnrc.yml Normal file
View File

@ -0,0 +1,3 @@
nmHoistingLimits: workspaces
nodeLinker: node-modules
checksumBehavior: update

View File

@ -10,23 +10,23 @@ REPORT_PORTAL_DESCRIPTION ?= "Jan App report"
all:
@echo "Specify a target to run"
# Builds the UI kit
build-joi:
ifeq ($(OS),Windows_NT)
cd joi && yarn config set network-timeout 300000 && yarn install && yarn build
else
cd joi && yarn install && yarn build
endif
# Config yarn version
config-yarn:
corepack enable
corepack prepare yarn@4.5.3 --activate
yarn --version
yarn config set -H enableImmutableInstalls false
# Installs yarn dependencies and builds core and extensions
install-and-build: build-joi
install-and-build: config-yarn
ifeq ($(OS),Windows_NT)
yarn config set network-timeout 300000
echo "skip"
endif
yarn global add turbo@1.13.2
yarn install
yarn build:joi
yarn build:core
yarn build:server
yarn install
yarn build:extensions
check-file-counts: install-and-build
@ -117,9 +117,8 @@ build: check-file-counts
clean:
ifeq ($(OS),Windows_NT)
-powershell -Command "Get-ChildItem -Path . -Include node_modules, .next, dist, build, out, .turbo -Recurse -Directory | Remove-Item -Recurse -Force"
-powershell -Command "Get-ChildItem -Path . -Include package-lock.json -Recurse -File | Remove-Item -Recurse -Force"
-powershell -Command "Get-ChildItem -Path . -Include yarn.lock -Recurse -File | Remove-Item -Recurse -Force"
-powershell -Command "Get-ChildItem -Path . -Include node_modules, .next, dist, build, out, .turbo, .yarn -Recurse -Directory | Remove-Item -Recurse -Force"
-powershell -Command "Get-ChildItem -Path . -Include package-lock.json, tsconfig.tsbuildinfo -Recurse -File | Remove-Item -Recurse -Force"
-powershell -Command "Remove-Item -Recurse -Force ./pre-install/*.tgz"
-powershell -Command "Remove-Item -Recurse -Force ./extensions/*/*.tgz"
-powershell -Command "Remove-Item -Recurse -Force ./electron/pre-install/*.tgz"
@ -131,8 +130,8 @@ else ifeq ($(shell uname -s),Linux)
find . -name "build" -type d -exec rm -rf '{}' +
find . -name "out" -type d -exec rm -rf '{}' +
find . -name ".turbo" -type d -exec rm -rf '{}' +
find . -name ".yarn" -type d -exec rm -rf '{}' +
find . -name "packake-lock.json" -type f -exec rm -rf '{}' +
find . -name "yarn.lock" -type f -exec rm -rf '{}' +
find . -name "package-lock.json" -type f -exec rm -rf '{}' +
rm -rf ./pre-install/*.tgz
rm -rf ./extensions/*/*.tgz
@ -146,8 +145,8 @@ else
find . -name "build" -type d -exec rm -rf '{}' +
find . -name "out" -type d -exec rm -rf '{}' +
find . -name ".turbo" -type d -exec rm -rf '{}' +
find . -name ".yarn" -type d -exec rm -rf '{}' +
find . -name "package-lock.json" -type f -exec rm -rf '{}' +
find . -name "yarn.lock" -type f -exec rm -rf '{}' +
rm -rf ./pre-install/*.tgz
rm -rf ./extensions/*/*.tgz
rm -rf ./electron/pre-install/*.tgz

9
ai.menlo.jan.desktop Normal file
View File

@ -0,0 +1,9 @@
[Desktop Entry]
Name=Jan
Comment=Local AI Assistant that runs 100% offline
Exec=run.sh
Icon=ai.menlo.jan
Type=Application
Categories=Development;
Keywords=AI;Assistant;LLM;ChatGPT;Local;Offline;
StartupNotify=true

42
ai.menlo.jan.metainfo.xml Normal file
View File

@ -0,0 +1,42 @@
<?xml version="1.0" encoding="UTF-8"?>
<component type="desktop-application">
<id>ai.menlo.jan</id>
<metadata_license>FSFAP</metadata_license>
<project_license>AGPL-3.0-only</project_license>
<name>Jan</name>
<summary>Local AI Assistant that runs 100% offline on your device</summary>
<description>
<p>
Jan is a ChatGPT-alternative that runs 100% offline on your device. Our goal is to make it easy for anyone to download and run LLMs and use AI with full control and privacy.
</p>
<p>Features:</p>
<ul>
<li>Model Library with popular LLMs like Llama, Gemma, Mistral, or Qwen</li>
<li>Connect to Remote AI APIs like Groq and OpenRouter</li>
<li>Local API Server with OpenAI-equivalent API</li>
<li>Extensions for customizing Jan</li>
</ul>
</description>
<launchable type="desktop-id">ai.menlo.jan.desktop</launchable>
<screenshots>
<screenshot type="default">
<image>https://catalog.jan.ai/flatpak/demo.gif</image>
</screenshot>
</screenshots>
<url type="homepage">https://jan.ai/</url>
<url type="bugtracker">https://github.com/janhq/jan/issues</url>
<content_rating type="oars-1.1" />
<releases>
<release version="0.5.12" date="2024-01-02">
<description>
<p>Latest stable release of Jan AI</p>
</description>
</release>
</releases>
</component>

View File

@ -1,15 +1,16 @@
{
"name": "@janhq/core",
"version": "0.1.10",
"description": "Jan app core lib",
"description": "Core library for the Jan AI application framework",
"keywords": [
"jan",
"core"
],
"homepage": "https://jan.ai",
"license": "AGPL-3.0",
"main": "dist/core.es5.js",
"module": "dist/core.cjs.js",
"browser": "dist/index.js",
"main": "dist/index.js",
"module": "dist/node/index.cjs.js",
"typings": "dist/types/index.d.ts",
"files": [
"dist",
@ -17,13 +18,13 @@
],
"author": "Jan <service@jan.ai>",
"exports": {
".": "./dist/core.es5.js",
".": "./dist/index.js",
"./node": "./dist/node/index.cjs.js"
},
"typesVersions": {
"*": {
".": [
"./dist/core.es5.js.map",
"./dist/index.js.map",
"./dist/types/index.d.ts"
],
"node": [
@ -36,25 +37,25 @@
"lint": "tslint --project tsconfig.json -t codeFrame 'src/**/*.ts' 'test/**/*.ts'",
"test": "jest",
"prebuild": "rimraf dist",
"build": "tsc --module commonjs && rollup -c rollup.config.ts",
"start": "rollup -c rollup.config.ts -w"
"build": "tsc -p . && rolldown -c rolldown.config.mjs"
},
"devDependencies": {
"@rollup/plugin-replace": "^5.0.5",
"@types/jest": "^29.5.12",
"@types/node": "^20.11.4",
"@npmcli/arborist": "^7.1.0",
"@types/jest": "^29.5.14",
"@types/node": "^22.10.0",
"@types/pacote": "^11.1.7",
"@types/request": "^2.48.12",
"electron": "33.2.1",
"eslint": "8.57.0",
"eslint-plugin-jest": "^27.9.0",
"jest": "^29.7.0",
"jest-junit": "^16.0.0",
"jest-runner": "^29.7.0",
"pacote": "^21.0.0",
"request": "^2.88.2",
"request-progress": "^3.0.0",
"rimraf": "^3.0.2",
"rollup": "^2.38.5",
"rollup-plugin-commonjs": "^9.1.8",
"rollup-plugin-json": "^3.1.0",
"rollup-plugin-node-resolve": "^5.2.0",
"rollup-plugin-sourcemaps": "^0.6.3",
"rollup-plugin-typescript2": "^0.36.0",
"rolldown": "1.0.0-beta.1",
"ts-jest": "^29.2.5",
"tslib": "^2.6.2",
"typescript": "^5.3.3"
@ -62,5 +63,6 @@
"dependencies": {
"rxjs": "^7.8.1",
"ulidx": "^2.3.0"
}
},
"packageManager": "yarn@4.5.3"
}

51
core/rolldown.config.mjs Normal file
View File

@ -0,0 +1,51 @@
import { defineConfig } from 'rolldown'
import pkgJson from './package.json' with { type: 'json' }
export default defineConfig([
{
input: 'src/index.ts',
output: {
format: 'esm',
file: 'dist/index.js',
sourcemap: true,
},
platform: 'browser',
external: ['path'],
define: {
NODE: JSON.stringify(`${pkgJson.name}/${pkgJson.node}`),
VERSION: JSON.stringify(pkgJson.version),
},
},
{
input: 'src/node/index.ts',
external: [
'fs/promises',
'path',
'pacote',
'@types/pacote',
'@npmcli/arborist',
'ulidx',
'node-fetch',
'fs',
'request',
'crypto',
'url',
'http',
'os',
'util',
'child_process',
'electron',
'request-progress',
],
output: {
format: 'cjs',
file: 'dist/node/index.cjs.js',
sourcemap: true,
inlineDynamicImports: true,
},
resolve: {
extensions: ['.js', '.ts'],
},
platform: 'node',
},
])

View File

@ -1,85 +0,0 @@
import resolve from 'rollup-plugin-node-resolve'
import commonjs from 'rollup-plugin-commonjs'
import sourceMaps from 'rollup-plugin-sourcemaps'
import typescript from 'rollup-plugin-typescript2'
import json from 'rollup-plugin-json'
import replace from '@rollup/plugin-replace'
const pkg = require('./package.json')
export default [
{
input: `src/index.ts`,
output: [
// { file: pkg.main, name: libraryName, format: 'umd', sourcemap: true },
{ file: pkg.main, format: 'es', sourcemap: true },
],
// Indicate here external modules you don't wanna include in your bundle (i.e.: 'lodash')
external: ['path'],
watch: {
include: 'src/**',
},
plugins: [
// Allow json resolution
json(),
// Compile TypeScript files
typescript({ useTsconfigDeclarationDir: true }),
// Allow bundling cjs modules (unlike webpack, rollup doesn't understand cjs)
commonjs(),
// Allow node_modules resolution, so you can use 'external' to control
// which external modules to include in the bundle
// https://github.com/rollup/rollup-plugin-node-resolve#usage
replace({
'preventAssignment': true,
'node:crypto': 'crypto',
'delimiters': ['"', '"'],
}),
resolve({
browser: true,
}),
// Resolve source maps to the original source
sourceMaps(),
],
},
{
input: `src/node/index.ts`,
output: [{ file: 'dist/node/index.cjs.js', format: 'cjs', sourcemap: true }],
// Indicate here external modules you don't wanna include in your bundle (i.e.: 'lodash')
external: [
'fs/promises',
'path',
'pacote',
'@types/pacote',
'@npmcli/arborist',
'ulidx',
'node-fetch',
'fs',
'request',
'crypto',
'url',
'http',
'os',
'util',
'child_process',
],
watch: {
include: 'src/node/**',
},
plugins: [
// Allow json resolution
json(),
// Compile TypeScript files
typescript({ useTsconfigDeclarationDir: true }),
// Allow bundling cjs modules (unlike webpack, rollup doesn't understand cjs)
commonjs(),
// Allow node_modules resolution, so you can use 'external' to control
// which external modules to include in the bundle
// https://github.com/rollup/rollup-plugin-node-resolve#usage
resolve(),
// Resolve source maps to the original source
sourceMaps(),
],
},
]

View File

@ -1,4 +1,9 @@
import { DownloadRequest, FileStat, NetworkConfig, SystemInformation } from '../types'
import {
DownloadRequest,
FileStat,
NetworkConfig,
SystemInformation,
} from '../types'
/**
* Execute a extension module function in main process
@ -9,11 +14,12 @@ import { DownloadRequest, FileStat, NetworkConfig, SystemInformation } from '../
* @returns Promise<any>
*
*/
const executeOnMain: (extension: string, method: string, ...args: any[]) => Promise<any> = (
extension,
method,
...args
) => globalThis.core?.api?.invokeExtensionFunc(extension, method, ...args)
const executeOnMain: (
extension: string,
method: string,
...args: any[]
) => Promise<any> = (extension, method, ...args) =>
globalThis.core?.api?.invokeExtensionFunc(extension, method, ...args)
/**
* Downloads a file from a URL and saves it to the local file system.
@ -23,10 +29,11 @@ const executeOnMain: (extension: string, method: string, ...args: any[]) => Prom
*
* @returns {Promise<any>} A promise that resolves when the file is downloaded.
*/
const downloadFile: (downloadRequest: DownloadRequest, network?: NetworkConfig) => Promise<any> = (
downloadRequest,
network
) => globalThis.core?.api?.downloadFile(downloadRequest, network)
const downloadFile: (
downloadRequest: DownloadRequest,
network?: NetworkConfig
) => Promise<any> = (downloadRequest, network) =>
globalThis.core?.api?.downloadFile(downloadRequest, network)
/**
* Aborts the download of a specific file.
@ -41,7 +48,8 @@ const abortDownload: (fileName: string) => Promise<any> = (fileName) =>
*
* @returns {Promise<string>} A Promise that resolves with Jan's data folder path.
*/
const getJanDataFolderPath = (): Promise<string> => globalThis.core.api?.getJanDataFolderPath()
const getJanDataFolderPath = (): Promise<string> =>
globalThis.core.api?.getJanDataFolderPath()
/**
* Opens the file explorer at a specific path.
@ -64,14 +72,16 @@ const joinPath: (paths: string[]) => Promise<string> = (paths) =>
* @param path - The file path to retrieve dirname.
* @returns {Promise<string>} A promise that resolves the dirname.
*/
const dirName: (path: string) => Promise<string> = (path) => globalThis.core.api?.dirName(path)
const dirName: (path: string) => Promise<string> = (path) =>
globalThis.core.api?.dirName(path)
/**
* Retrieve the basename from an url.
* @param path - The path to retrieve.
* @returns {Promise<string>} A promise that resolves with the basename.
*/
const baseName: (paths: string) => Promise<string> = (path) => globalThis.core.api?.baseName(path)
const baseName: (paths: string) => Promise<string> = (path) =>
globalThis.core.api?.baseName(path)
/**
* Opens an external URL in the default web browser.
@ -87,13 +97,15 @@ const openExternalUrl: (url: string) => Promise<any> = (url) =>
*
* @returns {Promise<string>} - A promise that resolves with the resource path.
*/
const getResourcePath: () => Promise<string> = () => globalThis.core.api?.getResourcePath()
const getResourcePath: () => Promise<string> = () =>
globalThis.core.api?.getResourcePath()
/**
* Gets the user's home path.
* @returns return user's home path
*/
const getUserHomePath = (): Promise<string> => globalThis.core.api?.getUserHomePath()
const getUserHomePath = (): Promise<string> =>
globalThis.core.api?.getUserHomePath()
/**
* Log to file from browser processes.
@ -111,8 +123,10 @@ const log: (message: string, fileName?: string) => void = (message, fileName) =>
*
* @returns {Promise<boolean>} - A promise that resolves with a boolean indicating whether the path is a subdirectory.
*/
const isSubdirectory: (from: string, to: string) => Promise<boolean> = (from: string, to: string) =>
globalThis.core.api?.isSubdirectory(from, to)
const isSubdirectory: (from: string, to: string) => Promise<boolean> = (
from: string,
to: string
) => globalThis.core.api?.isSubdirectory(from, to)
/**
* Get system information
@ -159,5 +173,4 @@ export {
systemInformation,
showToast,
dirName,
FileStat,
}

View File

@ -11,6 +11,7 @@ export enum ExtensionTypeEnum {
Model = 'model',
SystemMonitoring = 'systemMonitoring',
HuggingFace = 'huggingFace',
Engine = 'engine',
}
export interface ExtensionType {

View File

@ -22,7 +22,9 @@ export function requestInference(
headers: {
'Content-Type': 'application/json',
'Access-Control-Allow-Origin': '*',
'Accept': model.parameters?.stream ? 'text/event-stream' : 'application/json',
'Accept': model.parameters?.stream
? 'text/event-stream'
: 'application/json',
...headers,
},
body: JSON.stringify(requestBody),
@ -47,12 +49,24 @@ export function requestInference(
}
// There could be overriden stream parameter in the model
// that is set in request body (transformed payload)
if (requestBody?.stream === false || model.parameters?.stream === false) {
if (
requestBody?.stream === false ||
model.parameters?.stream === false
) {
const data = await response.json()
if (data.error || data.message) {
subscriber.error(data.error ?? data)
subscriber.complete()
return
}
if (transformResponse) {
subscriber.next(transformResponse(data))
} else {
subscriber.next(data.choices[0]?.message?.content ?? '')
subscriber.next(
data.choices
? data.choices[0]?.message?.content
: (data.content[0]?.text ?? '')
)
}
} else {
const stream = response.body

View File

@ -0,0 +1,110 @@
import {
InferenceEngine,
Engines,
EngineVariant,
EngineReleased,
EngineConfig,
DefaultEngineVariant,
} from '../../types'
import { BaseExtension, ExtensionTypeEnum } from '../extension'
/**
* Engine management extension. Persists and retrieves engine management.
* @abstract
* @extends BaseExtension
*/
export abstract class EngineManagementExtension extends BaseExtension {
type(): ExtensionTypeEnum | undefined {
return ExtensionTypeEnum.Engine
}
/**
* @returns A Promise that resolves to an object of list engines.
*/
abstract getEngines(): Promise<Engines>
/**
* @param name - Inference engine name.
* @returns A Promise that resolves to an array of installed engine.
*/
abstract getInstalledEngines(name: InferenceEngine): Promise<EngineVariant[]>
/**
* @param name - Inference engine name.
* @param version - Version of the engine.
* @param platform - Optional to sort by operating system. macOS, linux, windows.
* @returns A Promise that resolves to an array of latest released engine by version.
*/
abstract getReleasedEnginesByVersion(
name: InferenceEngine,
version: string,
platform?: string
): Promise<EngineReleased[]>
/**
* @param name - Inference engine name.
* @param platform - Optional to sort by operating system. macOS, linux, windows.
* @returns A Promise that resolves to an array of latest released engine.
*/
abstract getLatestReleasedEngine(
name: InferenceEngine,
platform?: string
): Promise<EngineReleased[]>
/**
* @param name - Inference engine name.
* @returns A Promise that resolves to intall of engine.
*/
abstract installEngine(
name: string,
engineConfig: EngineConfig
): Promise<{ messages: string }>
/**
* Add a new remote engine
* @returns A Promise that resolves to intall of engine.
*/
abstract addRemoteEngine(
engineConfig: EngineConfig
): Promise<{ messages: string }>
/**
* @param name - Inference engine name.
* @returns A Promise that resolves to unintall of engine.
*/
abstract uninstallEngine(
name: InferenceEngine,
engineConfig: EngineConfig
): Promise<{ messages: string }>
/**
* @param name - Inference engine name.
* @returns A Promise that resolves to an object of default engine.
*/
abstract getDefaultEngineVariant(
name: InferenceEngine
): Promise<DefaultEngineVariant>
/**
* @body variant - string
* @body version - string
* @returns A Promise that resolves to set default engine.
*/
abstract setDefaultEngineVariant(
name: InferenceEngine,
engineConfig: EngineConfig
): Promise<{ messages: string }>
/**
* @returns A Promise that resolves to update engine.
*/
abstract updateEngine(
name: InferenceEngine,
engineConfig?: EngineConfig
): Promise<{ messages: string }>
/**
* @returns A Promise that resolves to an object of remote models list .
*/
abstract getRemoteModels(name: InferenceEngine | string): Promise<any>
}

View File

@ -28,3 +28,8 @@ export { ModelExtension } from './model'
* Base AI Engines.
*/
export * from './engines'
/**
* Engines Management
*/
export * from './enginesManagement'

View File

@ -4,16 +4,6 @@ jest.mock('../../helper', () => ({
}))
import { App } from './app'
it('should call stopServer', () => {
const app = new App()
const stopServerMock = jest.fn().mockResolvedValue('Server stopped')
jest.mock('@janhq/server', () => ({
stopServer: stopServerMock,
}))
app.stopServer()
expect(stopServerMock).toHaveBeenCalled()
})
it('should correctly retrieve basename', () => {
const app = new App()
const result = app.baseName('/path/to/file.txt')
@ -23,7 +13,8 @@ it('should correctly retrieve basename', () => {
it('should correctly identify subdirectories', () => {
const app = new App()
const basePath = process.platform === 'win32' ? 'C:\\path\\to' : '/path/to'
const subPath = process.platform === 'win32' ? 'C:\\path\\to\\subdir' : '/path/to/subdir'
const subPath =
process.platform === 'win32' ? 'C:\\path\\to\\subdir' : '/path/to/subdir'
const result = app.isSubdirectory(basePath, subPath)
expect(result).toBe(true)
})
@ -31,7 +22,8 @@ it('should correctly identify subdirectories', () => {
it('should correctly join multiple paths', () => {
const app = new App()
const result = app.joinPath(['path', 'to', 'file'])
const expectedPath = process.platform === 'win32' ? 'path\\to\\file' : 'path/to/file'
const expectedPath =
process.platform === 'win32' ? 'path\\to\\file' : 'path/to/file'
expect(result).toBe(expectedPath)
})
@ -52,5 +44,7 @@ it('should retrieve the directory name from a file path (Unix/Windows)', async (
it('should retrieve the directory name when using file protocol', async () => {
const app = new App()
const path = 'file:/models/file.txt'
expect(await app.dirName(path)).toBe(process.platform === 'win32' ? 'app\\models' : 'app/models')
expect(await app.dirName(path)).toBe(
process.platform === 'win32' ? 'app\\models' : 'app/models'
)
})

View File

@ -44,11 +44,8 @@ export class App implements Processor {
/**
* Checks if the given path is a subdirectory of the given directory.
*
* @param _event - The IPC event object.
* @param from - The path to check.
* @param to - The directory to check against.
*
* @returns {Promise<boolean>} - A promise that resolves with the result.
*/
isSubdirectory(from: any, to: any) {
const rel = relative(from, to)
@ -79,26 +76,4 @@ export class App implements Processor {
async updateAppConfiguration(args: any) {
await updateAppConfiguration(args)
}
/**
* Start Jan API Server.
*/
async startServer(args?: any) {
const { startServer } = require('@janhq/server')
return startServer({
host: args?.host,
port: args?.port,
isCorsEnabled: args?.isCorsEnabled,
isVerboseEnabled: args?.isVerboseEnabled,
prefix: args?.prefix,
})
}
/**
* Stop Jan API Server.
*/
stopServer() {
const { stopServer } = require('@janhq/server')
return stopServer()
}
}

View File

@ -57,7 +57,10 @@ export default class Extension {
* @type {string}
*/
get specifier() {
return this.origin + (this.installOptions.version ? '@' + this.installOptions.version : '')
return (
this.origin +
(this.installOptions.version ? '@' + this.installOptions.version : '')
)
}
/**
@ -75,8 +78,10 @@ export default class Extension {
async getManifest() {
// Get the package's manifest (package.json object)
try {
await import('pacote').then((pacote) => {
return pacote.manifest(this.specifier, this.installOptions).then((mnf) => {
const pacote = require('pacote')
return pacote
.manifest(this.specifier, this.installOptions)
.then((mnf: any) => {
// set the Package properties based on the it's manifest
this.name = mnf.name
this.productName = mnf.productName as string | undefined
@ -84,9 +89,10 @@ export default class Extension {
this.main = mnf.main
this.description = mnf.description
})
})
} catch (error) {
throw new Error(`Package ${this.origin} does not contain a valid manifest: ${error}`)
throw new Error(
`Package ${this.origin} does not contain a valid manifest: ${error}`
)
}
return true
@ -103,10 +109,13 @@ export default class Extension {
await this.getManifest()
// Install the package in a child folder of the given folder
const pacote = await import('pacote')
const pacote = require('pacote')
await pacote.extract(
this.specifier,
join(ExtensionManager.instance.getExtensionsPath() ?? '', this.name ?? ''),
join(
ExtensionManager.instance.getExtensionsPath() ?? '',
this.name ?? ''
),
this.installOptions
)
@ -169,13 +178,12 @@ export default class Extension {
* @returns the latest available version if a new version is available or false if not.
*/
async isUpdateAvailable() {
return import('pacote').then((pacote) => {
if (this.origin) {
return pacote.manifest(this.origin).then((mnf) => {
return mnf.version !== this.version ? mnf.version : false
})
}
})
const pacote = require('pacote')
if (this.origin) {
return pacote.manifest(this.origin).then((mnf: any) => {
return mnf.version !== this.version ? mnf.version : false
})
}
}
/**

View File

@ -1,28 +1,19 @@
import { getEngineConfiguration } from './config';
import { getAppConfigurations, defaultAppConfig } from './config';
import { getJanExtensionsPath } from './config';
import { getJanDataFolderPath } from './config';
it('should return undefined for invalid engine ID', async () => {
const config = await getEngineConfiguration('invalid_engine');
expect(config).toBeUndefined();
});
import { getAppConfigurations, defaultAppConfig } from './config'
import { getJanExtensionsPath, getJanDataFolderPath } from './config'
it('should return default config when CI is e2e', () => {
process.env.CI = 'e2e';
const config = getAppConfigurations();
expect(config).toEqual(defaultAppConfig());
});
process.env.CI = 'e2e'
const config = getAppConfigurations()
expect(config).toEqual(defaultAppConfig())
})
it('should return extensions path when retrieved successfully', () => {
const extensionsPath = getJanExtensionsPath();
expect(extensionsPath).not.toBeUndefined();
});
const extensionsPath = getJanExtensionsPath()
expect(extensionsPath).not.toBeUndefined()
})
it('should return data folder path when retrieved successfully', () => {
const dataFolderPath = getJanDataFolderPath();
expect(dataFolderPath).not.toBeUndefined();
});
const dataFolderPath = getJanDataFolderPath()
expect(dataFolderPath).not.toBeUndefined()
})

View File

@ -1,8 +1,7 @@
import { AppConfiguration, SettingComponentProps } from '../../types'
import { AppConfiguration } from '../../types'
import { join, resolve } from 'path'
import fs from 'fs'
import os from 'os'
import childProcess from 'child_process'
const configurationFileName = 'settings.json'
/**
@ -19,7 +18,9 @@ export const getAppConfigurations = (): AppConfiguration => {
if (!fs.existsSync(configurationFile)) {
// create default app config if we don't have one
console.debug(`App config not found, creating default config at ${configurationFile}`)
console.debug(
`App config not found, creating default config at ${configurationFile}`
)
fs.writeFileSync(configurationFile, JSON.stringify(appDefaultConfiguration))
return appDefaultConfiguration
}
@ -30,20 +31,28 @@ export const getAppConfigurations = (): AppConfiguration => {
)
return appConfigurations
} catch (err) {
console.error(`Failed to read app config, return default config instead! Err: ${err}`)
console.error(
`Failed to read app config, return default config instead! Err: ${err}`
)
return defaultAppConfig()
}
}
const getConfigurationFilePath = () =>
join(
global.core?.appPath() || process.env[process.platform == 'win32' ? 'USERPROFILE' : 'HOME'],
global.core?.appPath() ||
process.env[process.platform == 'win32' ? 'USERPROFILE' : 'HOME'],
configurationFileName
)
export const updateAppConfiguration = (configuration: AppConfiguration): Promise<void> => {
export const updateAppConfiguration = (
configuration: AppConfiguration
): Promise<void> => {
const configurationFile = getConfigurationFilePath()
console.debug('updateAppConfiguration, configurationFile: ', configurationFile)
console.debug(
'updateAppConfiguration, configurationFile: ',
configurationFile
)
fs.writeFileSync(configurationFile, JSON.stringify(configuration))
return Promise.resolve()
@ -69,86 +78,6 @@ export const getJanExtensionsPath = (): string => {
return join(appConfigurations.data_folder, 'extensions')
}
/**
* Utility function to physical cpu count
*
* @returns {number} The physical cpu count.
*/
export const physicalCpuCount = async (): Promise<number> => {
const platform = os.platform()
try {
if (platform === 'linux') {
const output = await exec('lscpu -p | egrep -v "^#" | sort -u -t, -k 2,4 | wc -l')
return parseInt(output.trim(), 10)
} else if (platform === 'darwin') {
const output = await exec('sysctl -n hw.physicalcpu_max')
return parseInt(output.trim(), 10)
} else if (platform === 'win32') {
const output = await exec('WMIC CPU Get NumberOfCores')
return output
.split(os.EOL)
.map((line: string) => parseInt(line))
.filter((value: number) => !isNaN(value))
.reduce((sum: number, number: number) => sum + number, 1)
} else {
const cores = os.cpus().filter((cpu: any, index: number) => {
const hasHyperthreading = cpu.model.includes('Intel')
const isOdd = index % 2 === 1
return !hasHyperthreading || isOdd
})
return cores.length
}
} catch (err) {
console.warn('Failed to get physical CPU count', err)
// Divide by 2 to get rid of hyper threading
const coreCount = Math.ceil(os.cpus().length / 2)
console.debug('Using node API to get physical CPU count:', coreCount)
return coreCount
}
}
const exec = async (command: string): Promise<string> => {
return new Promise((resolve, reject) => {
childProcess.exec(command, { encoding: 'utf8' }, (error, stdout) => {
if (error) {
reject(error)
} else {
resolve(stdout)
}
})
})
}
// a hacky way to get the api key. we should comes up with a better
// way to handle this
export const getEngineConfiguration = async (engineId: string) => {
if (engineId !== 'openai' && engineId !== 'groq') return undefined
const settingDirectoryPath = join(
getJanDataFolderPath(),
'settings',
'@janhq',
engineId === 'openai' ? 'inference-openai-extension' : 'inference-groq-extension',
'settings.json'
)
const content = fs.readFileSync(settingDirectoryPath, 'utf-8')
const settings: SettingComponentProps[] = JSON.parse(content)
const apiKeyId = engineId === 'openai' ? 'openai-api-key' : 'groq-api-key'
const keySetting = settings.find((setting) => setting.key === apiKeyId)
let fullUrl = settings.find((setting) => setting.key === 'chat-completions-endpoint')
?.controllerProps.value
let apiKey = keySetting?.controllerProps.value
if (typeof apiKey !== 'string') apiKey = ''
if (typeof fullUrl !== 'string') fullUrl = ''
return {
api_key: apiKey,
full_url: fullUrl,
}
}
/**
* Default app configurations
* App Data Folder default to Electron's userData
@ -158,7 +87,10 @@ export const getEngineConfiguration = async (engineId: string) => {
*/
export const defaultAppConfig = (): AppConfiguration => {
const { app } = require('electron')
const defaultJanDataFolder = join(app?.getPath('userData') ?? os?.homedir() ?? '', 'data')
const defaultJanDataFolder = join(
app?.getPath('userData') ?? os?.homedir() ?? '',
'data'
)
return {
data_folder:
process.env.CI === 'e2e'

View File

@ -1,15 +1,9 @@
import { getSystemResourceInfo } from './resource';
import { getSystemResourceInfo } from './resource'
it('should return the correct system resource information with a valid CPU count', async () => {
const mockCpuCount = 4;
jest.spyOn(require('./config'), 'physicalCpuCount').mockResolvedValue(mockCpuCount);
const logSpy = jest.spyOn(require('./logger'), 'log').mockImplementation(() => {});
const result = await getSystemResourceInfo();
const result = await getSystemResourceInfo()
expect(result).toEqual({
numCpuPhysicalCore: mockCpuCount,
memAvailable: 0,
});
expect(logSpy).toHaveBeenCalledWith(`[CORTEX]::CPU information - ${mockCpuCount}`);
});
})
})

View File

@ -1,13 +1,7 @@
import { SystemResourceInfo } from '../../types'
import { physicalCpuCount } from './config'
import { log } from './logger'
export const getSystemResourceInfo = async (): Promise<SystemResourceInfo> => {
const cpu = await physicalCpuCount()
log(`[CORTEX]::CPU information - ${cpu}`)
return {
numCpuPhysicalCore: cpu,
memAvailable: 0, // TODO: this should not be 0
}
}

View File

@ -27,7 +27,10 @@ export enum NativeRoute {
quickAskSizeUpdated = 'quickAskSizeUpdated',
ackDeepLink = 'ackDeepLink',
factoryReset = 'factoryReset'
factoryReset = 'factoryReset',
startServer = 'startServer',
stopServer = 'stopServer',
}
/**
@ -41,8 +44,6 @@ export enum AppRoute {
dirName = 'dirName',
isSubdirectory = 'isSubdirectory',
baseName = 'baseName',
startServer = 'startServer',
stopServer = 'stopServer',
log = 'log',
systemInformation = 'systemInformation',
showToast = 'showToast',

View File

@ -0,0 +1,54 @@
import { InferenceEngine } from '../../types'
export type Engines = {
[key in InferenceEngine]: (EngineVariant & EngineConfig)[]
}
export type EngineMetadata = {
get_models_url?: string
header_template?: string
transform_req?: {
chat_completions?: {
url?: string
template?: string
}
}
transform_resp?: {
chat_completions?: {
template?: string
}
}
}
export type EngineVariant = {
engine: InferenceEngine
name: string
version: string
}
export type DefaultEngineVariant = {
engine: InferenceEngine
variant: string
version: string
}
export type EngineReleased = {
created_at: string
download_count: number
name: string
size: number
}
export type EngineConfig = {
engine?: string
version?: string
variant?: string
type?: string
url?: string
api_key?: string
metadata?: EngineMetadata
}
export enum EngineEvent {
OnEngineUpdate = 'OnEngineUpdate',
}

View File

@ -10,3 +10,4 @@ export * from './huggingface'
export * from './miscellaneous'
export * from './api'
export * from './setting'
export * from './engine'

View File

@ -32,9 +32,8 @@ export type ThreadMessage = {
completed_at: number
/** The additional metadata of this message. **/
metadata?: Record<string, unknown>
/** Type of the message */
type?: string
/** The error code which explain what error type. Used in conjunction with MessageStatus.Error */
error_code?: ErrorCode
}
@ -72,6 +71,10 @@ export type MessageRequest = {
// TODO: deprecate threadId field
thread?: Thread
/** Engine name to process */
engine?: string
/** Message type */
type?: string
}
@ -147,7 +150,9 @@ export interface Attachment {
/**
* The tools to add this file to.
*/
tools?: Array<CodeInterpreterTool | Attachment.AssistantToolsFileSearchTypeOnly>
tools?: Array<
CodeInterpreterTool | Attachment.AssistantToolsFileSearchTypeOnly
>
}
export namespace Attachment {
@ -166,5 +171,10 @@ export interface IncompleteDetails {
/**
* The reason the message is incomplete.
*/
reason: 'content_filter' | 'max_tokens' | 'run_cancelled' | 'run_expired' | 'run_failed'
reason:
| 'content_filter'
| 'max_tokens'
| 'run_cancelled'
| 'run_expired'
| 'run_failed'
}

View File

@ -1,5 +1,4 @@
export type SystemResourceInfo = {
numCpuPhysicalCore: number
memAvailable: number
}

View File

@ -3,7 +3,11 @@ export type SettingComponentProps = {
title: string
description: string
controllerType: ControllerType
controllerProps: SliderComponentProps | CheckboxComponentProps | InputComponentProps
controllerProps:
| SliderComponentProps
| CheckboxComponentProps
| InputComponentProps
| DropdownComponentProps
extensionName?: string
requireModelReload?: boolean
@ -12,13 +16,26 @@ export type SettingComponentProps = {
export type ConfigType = 'runtime' | 'setting'
export type ControllerType = 'slider' | 'checkbox' | 'input' | 'tag'
export type ControllerType =
| 'slider'
| 'checkbox'
| 'input'
| 'tag'
| 'dropdown'
export type InputType = 'password' | 'text' | 'email' | 'number' | 'tel' | 'url'
export type InputType =
| 'password'
| 'text'
| 'email'
| 'number'
| 'tel'
| 'url'
| 'dropdown'
const InputActions = ['unobscure', 'copy'] as const
export type InputActionsTuple = typeof InputActions
export type InputAction = InputActionsTuple[number]
export type DropdownOption = { name: string; value: string }
export type InputComponentProps = {
placeholder: string
@ -38,3 +55,9 @@ export type SliderComponentProps = {
export type CheckboxComponentProps = {
value: boolean
}
export type DropdownComponentProps = {
value: string
type?: InputType
options?: DropdownOption[]
}

View File

@ -11,11 +11,10 @@
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
"declarationDir": "dist/types",
"outDir": "dist/lib",
"outDir": "dist",
"importHelpers": true,
"types": ["@types/jest"],
"resolveJsonModule": true
"types": ["jest", "node"]
},
"include": ["src"],
"exclude": ["**/*.test.ts"]
"exclude": ["src/**/*.test.ts"]
}

View File

@ -58,5 +58,5 @@
"prettier": "^3.2.5",
"typescript": "^5"
},
"packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e"
"packageManager": "yarn@1.22.22"
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 MiB

After

Width:  |  Height:  |  Size: 18 MiB

View File

@ -93,7 +93,7 @@
<url><loc>https://jan.ai/docs/desktop/windows</loc><lastmod>2024-09-09T08:19:45.722Z</lastmod><changefreq>daily</changefreq><priority>1</priority></url>
<url><loc>https://jan.ai/docs/error-codes</loc><lastmod>2024-09-09T08:19:45.722Z</lastmod><changefreq>daily</changefreq><priority>1</priority></url>
<url><loc>https://jan.ai/docs/extensions</loc><lastmod>2024-09-09T08:19:45.722Z</lastmod><changefreq>daily</changefreq><priority>1</priority></url>
<url><loc>https://jan.ai/docs/installing-extension</loc><lastmod>2024-09-09T08:19:45.722Z</lastmod><changefreq>daily</changefreq><priority>1</priority></url>
<url><loc>https://jan.ai/docs/install-extensions</loc><lastmod>2024-09-09T08:19:45.722Z</lastmod><changefreq>daily</changefreq><priority>1</priority></url>
<url><loc>https://jan.ai/docs/models</loc><lastmod>2024-09-09T08:19:45.722Z</lastmod><changefreq>daily</changefreq><priority>1</priority></url>
<url><loc>https://jan.ai/docs/models/manage-models</loc><lastmod>2024-09-09T08:19:45.722Z</lastmod><changefreq>daily</changefreq><priority>1</priority></url>
<url><loc>https://jan.ai/docs/models/model-parameters</loc><lastmod>2024-09-09T08:19:45.722Z</lastmod><changefreq>daily</changefreq><priority>1</priority></url>

View File

@ -0,0 +1,25 @@
---
title: "Model downloads & running issues fixed"
version: 0.5.9
description: "Jan v0.5.9 is here: fixing what needed fixing."
date: 2024-11-22
ogImage: "/assets/images/changelog/jan-v0.5.9.gif"
---
import ChangelogHeader from "@/components/Changelog/ChangelogHeader"
<ChangelogHeader title= "Jan v0.5.9 is here:fixing what needed fixing" date="2024-11-22" ogImage= "/assets/images/changelog/jan-v0.5.9.gif" />
Jan v0.5.9 is here: fixing what needed fixing
### Highlights 🎉
- Model downloads & running issues fixed
- Document upload bugs resolved
- System glitches addressed: Factory Reset, HTTP Proxy, Hugging Face tokens
- Fixed issues with code blocks in streaming responses
- Improved the UX of the Local API Server page
Update your product or download the latest: https://jan.ai
For more details, see the [GitHub release notes](https://github.com/janhq/jan/releases/tag/v0.5.9).

View File

@ -0,0 +1,25 @@
---
title: "Jan supports Qwen2.5-Coder 14B & 32B"
version: 0.5.8
description: "Jan v0.5.8 is out: Jan supports Qwen2.5-Coder 14B & 32B through Cortex"
date: 2024-11-14
ogImage: "/assets/images/changelog/jan-v0.5.8.gif"
---
import ChangelogHeader from "@/components/Changelog/ChangelogHeader"
<ChangelogHeader title= "Jan supports Qwen2.5-Coder 14B & 32B" date="2024-11-14" ogImage= "/assets/images/changelog/jan-v0.5.7.gif" />
Jan v0.5.8 is out: Jan supports Qwen2.5-Coder 14B & 32B through Cortex
### Highlights 🎉
- A new engine: Jan now run models via [Cortex](https://cortex.so)
- SupportsAlibaba_Qwen's Coder 14B & 32B Support
- Supports markdown rendering on user messages
and various UI/UX enhancements 💫
Update your product or download the latest: https://jan.ai
For more details, see the [GitHub release notes](https://github.com/janhq/jan/releases/tag/v0.5.8).

View File

@ -0,0 +1,22 @@
---
title: "Jan v0.5.10 is live"
version: 0.5.10
description: "Jan is faster, smoother, and more reliable."
date: 2024-12-03
ogImage: "/assets/images/changelog/jan-v0.5.10.gif"
---
import ChangelogHeader from "@/components/Changelog/ChangelogHeader"
<ChangelogHeader title= "Jan v0.5.10: Jan is faster, smoother, and more reliable." date="2024-12-03" ogImage= "/assets/images/changelog/jan-v0.5.10.gif" />
Jan v0.5.10 is live: Jan is faster, smoother, and more reliable.
### Highlights 🎉
- Resolved model startup issues, memory leaks, and improved token limits
- Clearer error messages and subtle UX improvements
Update your product or download the latest: https://jan.ai
For more details, see the [GitHub release notes](https://github.com/janhq/jan/releases/tag/v0.5.10).

View File

@ -0,0 +1,26 @@
---
title: "Jan v0.5.11 is here!"
version: 0.5.11
description: "Critical issues fixed, Mac installation updated."
date: 2024-12-05
ogImage: "/assets/images/changelog/jan-v0.5.11.gif"
---
import ChangelogHeader from "@/components/Changelog/ChangelogHeader"
<ChangelogHeader title= "Jan v0.5.11: Jan is faster, smoother, and more reliable." date="2024-12-05" ogImage= "/assets/images/changelog/jan-v0.5.11.gif" />
Jan v0.5.11 is here - critical issues fixed, Mac installation updated.
### Highlights 🎉
- Crashes (markdown & code highlighting)
- Thread switching & auto-scroll
- Syntax highlighting bugs
- API issues (Anthropic, OpenRouter)
- Title glitches with special characters
- Model settings inconsistencies
Update your product or download the latest: https://jan.ai
For more details, see the [GitHub release notes](https://github.com/janhq/jan/releases/tag/v0.5.11).

View File

@ -0,0 +1,28 @@
---
title: "Jan gives you full control over your privacy"
version: 0.5.12
description: "Improved Privacy settings to give full control over analytics."
date: 2024-12-30
ogImage: "/assets/images/changelog/jan-v0.5.12.gif"
---
import ChangelogHeader from "@/components/Changelog/ChangelogHeader"
<ChangelogHeader title= "Jan v0.5.12: Improved Privacy settings to give full control over analytics." date="2024-12-30" ogImage= "/assets/images/changelog/jan-v0.5.12.gif" />
Jan v0.5.11 is here - critical issues fixed, Mac installation updated.
### Highlights 🎉
- Updated privacy settings with opt-in/out options for Jan Analytics
- Adjustable chat width
- The right sidebar and input box are now optimized for new users
### Fixes 💫
- Updated privacy settings with opt-in/out options for Jan Analytics
- Adjustable chat width
- The right sidebar and input box are now optimized for new users
Update your product or download the latest: https://jan.ai
For more details, see the [GitHub release notes](https://github.com/janhq/jan/releases/tag/v0.5.12).

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 972 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 721 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 900 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 658 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 412 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 763 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1000 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 788 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 908 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 732 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 627 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 345 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 782 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 907 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 159 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 106 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 148 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 237 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 106 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 153 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 768 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 271 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 97 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 144 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 462 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 85 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 152 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 145 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 876 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 159 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 733 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 588 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 84 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 125 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.4 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 382 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 134 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 138 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 212 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 928 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 714 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 774 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 151 KiB

Some files were not shown because too many files have changed in this diff Show More