Merge branch 'main' into release/v0.5.14
# Conflicts: # extensions/inference-cortex-extension/download.bat # extensions/inference-cortex-extension/download.sh # extensions/inference-cortex-extension/rollup.config.ts
1
.github/scripts/rename-uninstaller.sh
vendored
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
8
.github/workflows/publish-npm-core.yml
vendored
@ -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'
|
||||
|
||||
8
.github/workflows/publish-npm-joi.yml
vendored
@ -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
@ -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
@ -0,0 +1,3 @@
|
||||
nmHoistingLimits: workspaces
|
||||
nodeLinker: node-modules
|
||||
checksumBehavior: update
|
||||
31
Makefile
@ -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
@ -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
@ -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>
|
||||
@ -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
@ -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',
|
||||
},
|
||||
])
|
||||
@ -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(),
|
||||
],
|
||||
},
|
||||
]
|
||||
@ -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,
|
||||
}
|
||||
|
||||
@ -11,6 +11,7 @@ export enum ExtensionTypeEnum {
|
||||
Model = 'model',
|
||||
SystemMonitoring = 'systemMonitoring',
|
||||
HuggingFace = 'huggingFace',
|
||||
Engine = 'engine',
|
||||
}
|
||||
|
||||
export interface ExtensionType {
|
||||
|
||||
@ -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
|
||||
@ -77,8 +91,12 @@ export function requestInference(
|
||||
const toParse = cachedLines + line
|
||||
if (!line.includes('data: [DONE]')) {
|
||||
const data = JSON.parse(toParse.replace('data: ', ''))
|
||||
if ('error' in data) {
|
||||
subscriber.error(data.error)
|
||||
if (
|
||||
'error' in data ||
|
||||
'message' in data ||
|
||||
'detail' in data
|
||||
) {
|
||||
subscriber.error(data.error ?? data)
|
||||
subscriber.complete()
|
||||
return
|
||||
}
|
||||
|
||||
110
core/src/browser/extensions/enginesManagement.ts
Normal 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>
|
||||
}
|
||||
@ -28,3 +28,8 @@ export { ModelExtension } from './model'
|
||||
* Base AI Engines.
|
||||
*/
|
||||
export * from './engines'
|
||||
|
||||
/**
|
||||
* Engines Management
|
||||
*/
|
||||
export * from './enginesManagement'
|
||||
|
||||
@ -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'
|
||||
)
|
||||
})
|
||||
|
||||
@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
@ -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
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -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()
|
||||
})
|
||||
|
||||
@ -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'
|
||||
|
||||
@ -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}`);
|
||||
});
|
||||
})
|
||||
})
|
||||
|
||||
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@ -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',
|
||||
|
||||
54
core/src/types/engine/index.ts
Normal 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',
|
||||
}
|
||||
@ -10,3 +10,4 @@ export * from './huggingface'
|
||||
export * from './miscellaneous'
|
||||
export * from './api'
|
||||
export * from './setting'
|
||||
export * from './engine'
|
||||
|
||||
@ -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'
|
||||
}
|
||||
|
||||
@ -1,5 +1,4 @@
|
||||
export type SystemResourceInfo = {
|
||||
numCpuPhysicalCore: number
|
||||
memAvailable: number
|
||||
}
|
||||
|
||||
|
||||
@ -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[]
|
||||
}
|
||||
|
||||
@ -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"]
|
||||
}
|
||||
|
||||
@ -58,5 +58,5 @@
|
||||
"prettier": "^3.2.5",
|
||||
"typescript": "^5"
|
||||
},
|
||||
"packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e"
|
||||
"packageManager": "yarn@1.22.22"
|
||||
}
|
||||
|
||||
BIN
docs/public/assets/images/changelog/jan-v0.5.10.gif
Normal file
|
After Width: | Height: | Size: 4.9 MiB |
BIN
docs/public/assets/images/changelog/jan-v0.5.11.gif
Normal file
|
After Width: | Height: | Size: 2.0 MiB |
BIN
docs/public/assets/images/changelog/jan-v0.5.12.gif
Normal file
|
After Width: | Height: | Size: 3.7 MiB |
BIN
docs/public/assets/images/changelog/jan-v0.5.8.gif
Normal file
|
After Width: | Height: | Size: 7.3 MiB |
|
Before Width: | Height: | Size: 20 MiB After Width: | Height: | Size: 18 MiB |
@ -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>
|
||||
|
||||
25
docs/src/pages/changelog/2024-11-22-jan-bugs.mdx
Normal 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).
|
||||
@ -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).
|
||||
22
docs/src/pages/changelog/2024-12-03-jan-is-faster.mdx
Normal 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).
|
||||
26
docs/src/pages/changelog/2024-12-05-jan-hot-fix-mac.mdx
Normal 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).
|
||||
28
docs/src/pages/changelog/2024-12-30-jan-new-privacy.mdx
Normal 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).
|
||||
|
Before Width: | Height: | Size: 1.1 MiB |
|
Before Width: | Height: | Size: 1.4 MiB |
|
Before Width: | Height: | Size: 972 KiB |
|
Before Width: | Height: | Size: 1.0 MiB |
|
Before Width: | Height: | Size: 721 KiB |
|
Before Width: | Height: | Size: 900 KiB |
|
Before Width: | Height: | Size: 658 KiB |
|
Before Width: | Height: | Size: 1.1 MiB |
|
Before Width: | Height: | Size: 412 KiB |
|
Before Width: | Height: | Size: 763 KiB |
|
Before Width: | Height: | Size: 1000 KiB |
|
Before Width: | Height: | Size: 788 KiB |
|
Before Width: | Height: | Size: 908 KiB |
|
Before Width: | Height: | Size: 732 KiB |
|
Before Width: | Height: | Size: 627 KiB |
|
Before Width: | Height: | Size: 345 KiB |
|
Before Width: | Height: | Size: 782 KiB |
|
Before Width: | Height: | Size: 1.1 MiB |
|
Before Width: | Height: | Size: 907 KiB |
|
Before Width: | Height: | Size: 1.1 MiB |
|
Before Width: | Height: | Size: 159 KiB |
|
Before Width: | Height: | Size: 106 KiB |
|
Before Width: | Height: | Size: 2.0 MiB |
BIN
docs/src/pages/docs/_assets/anthropic.png
Normal file
|
After Width: | Height: | Size: 148 KiB |
BIN
docs/src/pages/docs/_assets/api-server.png
Normal file
|
After Width: | Height: | Size: 237 KiB |
|
Before Width: | Height: | Size: 106 KiB |
BIN
docs/src/pages/docs/_assets/assistant-01.png
Normal file
|
After Width: | Height: | Size: 153 KiB |
|
Before Width: | Height: | Size: 43 KiB |
|
Before Width: | Height: | Size: 768 KiB |
|
Before Width: | Height: | Size: 1.7 MiB |
|
Before Width: | Height: | Size: 271 KiB |
|
Before Width: | Height: | Size: 97 KiB |
|
Before Width: | Height: | Size: 144 KiB |
|
Before Width: | Height: | Size: 462 KiB |
|
Before Width: | Height: | Size: 85 KiB |
|
Before Width: | Height: | Size: 152 KiB |
|
Before Width: | Height: | Size: 2.0 MiB |
BIN
docs/src/pages/docs/_assets/cohere.png
Normal file
|
After Width: | Height: | Size: 145 KiB |
|
Before Width: | Height: | Size: 876 KiB |
|
Before Width: | Height: | Size: 159 KiB |
|
Before Width: | Height: | Size: 733 KiB |
|
Before Width: | Height: | Size: 588 KiB |
|
Before Width: | Height: | Size: 18 KiB |
|
Before Width: | Height: | Size: 1.9 MiB |
|
Before Width: | Height: | Size: 84 KiB |
|
Before Width: | Height: | Size: 125 KiB |
|
Before Width: | Height: | Size: 3.4 MiB |
|
Before Width: | Height: | Size: 382 KiB |
|
Before Width: | Height: | Size: 134 KiB |
|
Before Width: | Height: | Size: 138 KiB |
|
Before Width: | Height: | Size: 212 KiB |
|
Before Width: | Height: | Size: 928 KiB |
|
Before Width: | Height: | Size: 1.7 MiB |
|
Before Width: | Height: | Size: 714 KiB |
|
Before Width: | Height: | Size: 774 KiB |
|
Before Width: | Height: | Size: 50 KiB |
|
Before Width: | Height: | Size: 151 KiB |