Merge branch 'dev' into docs-add-integration-lmstudio

This commit is contained in:
Henry 2024-02-21 13:20:04 +07:00 committed by GitHub
commit db9ec320a3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
17 changed files with 456 additions and 31 deletions

View File

@ -0,0 +1,40 @@
name: Jan Build Docker Nightly or Manual
on:
push:
branches:
- main
- feature/helmchart-and-ci-jan-server
paths-ignore:
- 'README.md'
- 'docs/**'
schedule:
- cron: '0 20 * * 1,2,3' # At 8 PM UTC on Monday, Tuesday, and Wednesday which is 3 AM UTC+7 Tuesday, Wednesday, and Thursday
workflow_dispatch:
jobs:
# Job create Update app version based on latest release tag with build number and save to output
get-update-version:
uses: ./.github/workflows/template-get-update-version.yml
build-cpu:
uses: ./.github/workflows/template-build-jan-server.yml
permissions:
packages: write
secrets: inherit
needs: [get-update-version]
with:
dockerfile_path: ./Dockerfile
docker_image_tag: "ghcr.io/janhq/jan-server:dev-cpu-latest,ghcr.io/janhq/jan-server:dev-cpu-${{ needs.get-update-version.outputs.new_version }}"
build-gpu:
uses: ./.github/workflows/template-build-jan-server.yml
permissions:
packages: write
secrets: inherit
needs: [get-update-version]
with:
dockerfile_path: ./Dockerfile.gpu
docker_image_tag: "ghcr.io/janhq/jan-server:dev-cuda-12.2-latest,ghcr.io/janhq/jan-server:dev-cuda-12.2-${{ needs.get-update-version.outputs.new_version }}"

30
.github/workflows/jan-server-build.yml vendored Normal file
View File

@ -0,0 +1,30 @@
name: Jan Build Docker
on:
push:
tags: ["v[0-9]+.[0-9]+.[0-9]+"]
jobs:
# Job create Update app version based on latest release tag with build number and save to output
get-update-version:
uses: ./.github/workflows/template-get-update-version.yml
build-cpu:
permissions:
packages: write
uses: ./.github/workflows/template-build-jan-server.yml
secrets: inherit
needs: [get-update-version]
with:
dockerfile_path: ./Dockerfile
docker_image_tag: "ghcr.io/janhq/jan-server:cpu-latest,ghcr.io/janhq/jan-server:cpu-${{ needs.get-update-version.outputs.new_version }}"
build-gpu:
permissions:
packages: write
uses: ./.github/workflows/template-build-jan-server.yml
secrets: inherit
needs: [get-update-version]
with:
dockerfile_path: ./Dockerfile.gpu
docker_image_tag: "ghcr.io/janhq/jan-server:cuda-12.2-latest,ghcr.io/janhq/jan-server:cuda-12.2-${{ needs.get-update-version.outputs.new_version }}"

View File

@ -0,0 +1,39 @@
name: build-jan-server
on:
workflow_call:
inputs:
dockerfile_path:
required: false
type: string
default: './Dockerfile'
docker_image_tag:
required: true
type: string
default: 'ghcr.io/janhq/jan-server:dev-latest'
jobs:
build:
runs-on: ubuntu-latest
env:
REGISTRY: ghcr.io
IMAGE_NAME: janhq/jan-server
permissions:
packages: write
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Log in to the Container registry
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and push Docker image
uses: docker/build-push-action@v3
with:
context: .
file: ${{ inputs.dockerfile_path }}
push: true
tags: ${{ inputs.docker_image_tag }}

1
.gitignore vendored
View File

@ -5,6 +5,7 @@
error.log error.log
node_modules node_modules
*.tgz *.tgz
!charts/server/charts/*.tgz
yarn.lock yarn.lock
dist dist
build build

View File

@ -76,31 +76,31 @@ Jan is an open-source ChatGPT alternative that runs 100% offline on your compute
<tr style="text-align:center"> <tr style="text-align:center">
<td style="text-align:center"><b>Experimental (Nightly Build)</b></td> <td style="text-align:center"><b>Experimental (Nightly Build)</b></td>
<td style="text-align:center"> <td style="text-align:center">
<a href='https://delta.jan.ai/latest/jan-win-x64-0.4.6-276.exe'> <a href='https://delta.jan.ai/latest/jan-win-x64-0.4.6-279.exe'>
<img src='./docs/static/img/windows.png' style="height:14px; width: 14px" /> <img src='./docs/static/img/windows.png' style="height:14px; width: 14px" />
<b>jan.exe</b> <b>jan.exe</b>
</a> </a>
</td> </td>
<td style="text-align:center"> <td style="text-align:center">
<a href='https://delta.jan.ai/latest/jan-mac-x64-0.4.6-276.dmg'> <a href='https://delta.jan.ai/latest/jan-mac-x64-0.4.6-279.dmg'>
<img src='./docs/static/img/mac.png' style="height:15px; width: 15px" /> <img src='./docs/static/img/mac.png' style="height:15px; width: 15px" />
<b>Intel</b> <b>Intel</b>
</a> </a>
</td> </td>
<td style="text-align:center"> <td style="text-align:center">
<a href='https://delta.jan.ai/latest/jan-mac-arm64-0.4.6-276.dmg'> <a href='https://delta.jan.ai/latest/jan-mac-arm64-0.4.6-279.dmg'>
<img src='./docs/static/img/mac.png' style="height:15px; width: 15px" /> <img src='./docs/static/img/mac.png' style="height:15px; width: 15px" />
<b>M1/M2</b> <b>M1/M2</b>
</a> </a>
</td> </td>
<td style="text-align:center"> <td style="text-align:center">
<a href='https://delta.jan.ai/latest/jan-linux-amd64-0.4.6-276.deb'> <a href='https://delta.jan.ai/latest/jan-linux-amd64-0.4.6-279.deb'>
<img src='./docs/static/img/linux.png' style="height:14px; width: 14px" /> <img src='./docs/static/img/linux.png' style="height:14px; width: 14px" />
<b>jan.deb</b> <b>jan.deb</b>
</a> </a>
</td> </td>
<td style="text-align:center"> <td style="text-align:center">
<a href='https://delta.jan.ai/latest/jan-linux-x86_64-0.4.6-276.AppImage'> <a href='https://delta.jan.ai/latest/jan-linux-x86_64-0.4.6-279.AppImage'>
<img src='./docs/static/img/linux.png' style="height:14px; width: 14px" /> <img src='./docs/static/img/linux.png' style="height:14px; width: 14px" />
<b>jan.AppImage</b> <b>jan.AppImage</b>
</a> </a>

6
charts/server/Chart.lock Normal file
View File

@ -0,0 +1,6 @@
dependencies:
- name: common
repository: oci://ghcr.io/janhq/charts
version: 0.1.2
digest: sha256:35e98bde174130787755b0f8ea2359b7b6790d965a7157c2f7cabf1bc8c04471
generated: "2024-02-20T16:20:37.6530108+07:00"

10
charts/server/Chart.yaml Normal file
View File

@ -0,0 +1,10 @@
apiVersion: v2
name: jan-server
description: A Helm chart for Kubernetes
type: application
version: 0.1.0
appVersion: '1.0.0'
dependencies:
- name: common
version: 0.1.2 # common-chart-version
repository: oci://ghcr.io/janhq/charts

Binary file not shown.

View File

@ -0,0 +1,4 @@
{
"image-list": "server=ghcr.io/janhq/jan-server",
"platforms": "linux/amd64"
}

256
charts/server/values.yaml Normal file
View File

@ -0,0 +1,256 @@
common:
imageTag: v0.4.6-cpu
# DO NOT CHANGE THE LINE ABOVE. MAKE ALL CHANGES BELOW
# Global pvc for all workload
pvc:
enabled: false
name: 'janroot'
accessModes: 'ReadWriteOnce'
storageClassName: ''
capacity: '50Gi'
# Global image pull secret
imagePullSecrets: []
externalSecret:
create: false
name: ''
annotations: {}
nameOverride: 'jan-server'
fullnameOverride: 'jan-server'
serviceAccount:
create: true
annotations: {}
name: 'jan-server-service-account'
podDisruptionBudget:
create: false
minAvailable: 1
workloads:
- name: server
image:
repository: ghcr.io/janhq/jan-server
pullPolicy: Always
command: ['/bin/sh', '-c']
args: ['cd server && node build/main.js']
replicaCount: 1
ports:
containerPort: 1337
strategy:
canary:
steps:
- setWeight: 50
- pause: { duration: 1m }
ingress:
enabled: true
className: 'nginx'
annotations:
nginx.ingress.kubernetes.io/proxy-body-size: '100m'
nginx.ingress.kubernetes.io/proxy-read-timeout: '1800'
nginx.ingress.kubernetes.io/proxy-send-timeout: '1800'
# cert-manager.io/cluster-issuer: 'jan-ai-dns01-cluster-issuer'
# nginx.ingress.kubernetes.io/force-ssl-redirect: 'true'
nginx.ingress.kubernetes.io/backend-protocol: HTTP
hosts:
- host: server.local
paths:
- path: /
pathType: Prefix
tls:
[]
# - hosts:
# - server-dev.jan.ai
# secretName: jan-server-prod-tls-v2
instrumentation:
enabled: false
podAnnotations: {}
podSecurityContext: {}
securityContext: {}
service:
extenalLabel: {}
type: ClusterIP
port: 1337
targetPort: 1337
# If you want to use GPU, please uncomment the following lines and change imageTag to the one with GPU support
resources:
# limits:
# nvidia.com/gpu: 1
requests:
cpu: 2000m
memory: 8192M
# If you want to use pv, please uncomment the following lines and enable pvc.enabled
volumes:
[]
# - name: janroot
# persistentVolumeClaim:
# claimName: janroot
volumeMounts:
[]
# - name: janroot
# mountPath: /app/server/build/jan
# AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, S3_BUCKET_NAME, AWS_ENDPOINT, AWS_REGION should mount as a secret env instead of plain text here
# Change API_BASE_URL to your server's public domain
env:
- name: API_BASE_URL
value: 'http://server.local'
lifecycle: {}
autoscaling:
enabled: false
minReplicas: 2
maxReplicas: 3
targetCPUUtilizationPercentage: 95
targetMemoryUtilizationPercentage: 95
kedaScaling:
enabled: false # ignore if autoscaling.enable = true
cooldownPeriod: 30
pollingInterval: 2
minReplicas: 1
maxReplicas: 5
metricName: celery_queue_length
query: celery_queue_length{queue_name="myqueue"} # change queue_name here
serverAddress: http://prometheus-prod-kube-prome-prometheus.monitoring.svc:9090
threshold: '3'
nodeSelector: {}
tolerations: []
podSecurityGroup:
enabled: false
securitygroupid: []
# Reloader Option
reloader: 'false'
vpa:
enabled: false
- name: web
image:
repository: ghcr.io/janhq/jan-server
pullPolicy: Always
command: ['/bin/sh', '-c']
args:
[
'export NODE_ENV=production && yarn workspace jan-web build && cd web && npx serve out',
]
replicaCount: 1
ports:
containerPort: 3000
strategy:
canary:
steps:
- setWeight: 50
- pause: { duration: 1m }
ingress:
enabled: true
className: 'nginx'
annotations:
nginx.ingress.kubernetes.io/proxy-body-size: '100m'
nginx.ingress.kubernetes.io/proxy-read-timeout: '1800'
nginx.ingress.kubernetes.io/proxy-send-timeout: '1800'
# cert-manager.io/cluster-issuer: 'jan-ai-dns01-cluster-issuer'
# nginx.ingress.kubernetes.io/force-ssl-redirect: 'true'
nginx.ingress.kubernetes.io/backend-protocol: HTTP
hosts:
- host: web.local
paths:
- path: /
pathType: Prefix
tls:
[]
# - hosts:
# - server-dev.jan.ai
# secretName: jan-server-prod-tls-v2
instrumentation:
enabled: false
podAnnotations: {}
podSecurityContext: {}
securityContext: {}
service:
extenalLabel: {}
type: ClusterIP
port: 3000
targetPort: 3000
resources:
limits:
cpu: 1000m
memory: 2048M
requests:
cpu: 50m
memory: 500M
volumes:
[]
# - name: janroot
# persistentVolumeClaim:
# claimName: janroot
volumeMounts:
[]
# - name: janroot
# mountPath: /app/server/build/jan
# AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, S3_BUCKET_NAME, AWS_ENDPOINT, AWS_REGION should mount as a secret env instead of plain text here
# Change API_BASE_URL to your server's public domain
env:
- name: API_BASE_URL
value: 'http://server.local'
lifecycle: {}
autoscaling:
enabled: true
minReplicas: 1
maxReplicas: 3
targetCPUUtilizationPercentage: 95
targetMemoryUtilizationPercentage: 95
kedaScaling:
enabled: false # ignore if autoscaling.enable = true
cooldownPeriod: 30
pollingInterval: 2
minReplicas: 1
maxReplicas: 5
metricName: celery_queue_length
query: celery_queue_length{queue_name="myqueue"} # change queue_name here
serverAddress: http://prometheus-prod-kube-prome-prometheus.monitoring.svc:9090
threshold: '3'
nodeSelector: {}
tolerations: []
podSecurityGroup:
enabled: false
securitygroupid: []
# Reloader Option
reloader: 'false'
vpa:
enabled: false

View File

@ -154,7 +154,10 @@ export default class JanInferenceNitroExtension extends InferenceExtension {
}) })
if (nitroInitResult?.error) { if (nitroInitResult?.error) {
events.emit(ModelEvent.OnModelFail, model) events.emit(ModelEvent.OnModelFail, {
...model,
error: nitroInitResult.error,
})
return return
} }

View File

@ -310,9 +310,15 @@ async function killSubprocess(): Promise<void> {
subprocess?.kill() subprocess?.kill()
subprocess = undefined subprocess = undefined
}) })
.catch(() => {}) .catch(() => {}) // Do nothing with this attempt
.then(() => tcpPortUsed.waitUntilFree(PORT, 300, 5000)) .then(() => tcpPortUsed.waitUntilFree(PORT, 300, 5000))
.then(() => log(`[NITRO]::Debug: Nitro process is terminated`)) .then(() => log(`[NITRO]::Debug: Nitro process is terminated`))
.catch((err) => {
log(
`[NITRO]::Debug: Could not kill running process on port ${PORT}. Might be another process running on the same port? ${err}`
)
throw 'PORT_NOT_AVAILABLE'
})
} }
/** /**

View File

@ -195,7 +195,12 @@ const DropdownListSidebar = ({
</SelectValue> </SelectValue>
</SelectTrigger> </SelectTrigger>
<SelectPortal> <SelectPortal>
<SelectContent className="right-2 block w-full min-w-[450px] pr-0"> <SelectContent
className={twMerge(
'right-2 block w-full min-w-[450px] pr-0',
isTabActive === 1 && '[&_.select-scroll-down-button]:hidden'
)}
>
<div className="relative px-2 py-2 dark:bg-secondary/50"> <div className="relative px-2 py-2 dark:bg-secondary/50">
<ul className="inline-flex w-full space-x-2 rounded-lg bg-zinc-100 px-1 dark:bg-secondary"> <ul className="inline-flex w-full space-x-2 rounded-lg bg-zinc-100 px-1 dark:bg-secondary">
{engineOptions.map((name, i) => { {engineOptions.map((name, i) => {

View File

@ -152,7 +152,7 @@ const BottomBar = () => {
{gpus.length > 0 && ( {gpus.length > 0 && (
<Tooltip> <Tooltip>
<TooltipTrigger> <TooltipTrigger>
<div className="flex cursor-pointer items-center"> <div className="flex items-center">
<SystemItem <SystemItem
name={`${gpus.length} GPU `} name={`${gpus.length} GPU `}
value={`${calculateUtilization()}% `} value={`${calculateUtilization()}% `}

View File

@ -114,8 +114,8 @@ export default function EventHandler({ children }: { children: ReactNode }) {
const onModelInitFailed = useCallback( const onModelInitFailed = useCallback(
(res: any) => { (res: any) => {
const errorMessage = `${res.error}` const errorMessage = res?.error ?? res
console.error('Failed to load model: ' + errorMessage) console.error('Failed to load model: ', errorMessage)
setStateModel(() => ({ setStateModel(() => ({
state: 'start', state: 'start',
loading: false, loading: false,

View File

@ -7,6 +7,7 @@ import ModalTroubleShooting, {
modalTroubleShootingAtom, modalTroubleShootingAtom,
} from '@/containers/ModalTroubleShoot' } from '@/containers/ModalTroubleShoot'
import { loadModelErrorAtom } from '@/hooks/useActiveModel'
import useSendChatMessage from '@/hooks/useSendChatMessage' import useSendChatMessage from '@/hooks/useSendChatMessage'
import { getCurrentChatMessagesAtom } from '@/helpers/atoms/ChatMessage.atom' import { getCurrentChatMessagesAtom } from '@/helpers/atoms/ChatMessage.atom'
@ -15,6 +16,8 @@ const ErrorMessage = ({ message }: { message: ThreadMessage }) => {
const messages = useAtomValue(getCurrentChatMessagesAtom) const messages = useAtomValue(getCurrentChatMessagesAtom)
const { resendChatMessage } = useSendChatMessage() const { resendChatMessage } = useSendChatMessage()
const setModalTroubleShooting = useSetAtom(modalTroubleShootingAtom) const setModalTroubleShooting = useSetAtom(modalTroubleShootingAtom)
const loadModelError = useAtomValue(loadModelErrorAtom)
const PORT_NOT_AVAILABLE = 'PORT_NOT_AVAILABLE'
const regenerateMessage = async () => { const regenerateMessage = async () => {
const lastMessageIndex = messages.length - 1 const lastMessageIndex = messages.length - 1
@ -23,9 +26,9 @@ const ErrorMessage = ({ message }: { message: ThreadMessage }) => {
} }
return ( return (
<> <div className="mt-10">
{message.status === MessageStatus.Stopped && ( {message.status === MessageStatus.Stopped && (
<div key={message.id} className="mt-10 flex flex-col items-center"> <div key={message.id} className="flex flex-col items-center">
<span className="mb-3 text-center text-sm font-medium text-gray-500"> <span className="mb-3 text-center text-sm font-medium text-gray-500">
Oops! The generation was interrupted. Let&apos;s give it another go! Oops! The generation was interrupted. Let&apos;s give it another go!
</span> </span>
@ -41,6 +44,26 @@ const ErrorMessage = ({ message }: { message: ThreadMessage }) => {
</div> </div>
)} )}
{message.status === MessageStatus.Error && ( {message.status === MessageStatus.Error && (
<>
{loadModelError === PORT_NOT_AVAILABLE ? (
<div
key={message.id}
className="flex flex-col items-center text-center text-sm font-medium text-gray-500 w-full"
>
<p className="w-[90%]">
Port 3928 is currently unavailable. Check for conflicting apps,
or access&nbsp;
<span
className="cursor-pointer text-primary dark:text-blue-400"
onClick={() => setModalTroubleShooting(true)}
>
troubleshooting assistance
</span>
&nbsp;for further support.
</p>
<ModalTroubleShooting />
</div>
) : (
<div <div
key={message.id} key={message.id}
className="flex flex-col items-center text-center text-sm font-medium text-gray-500" className="flex flex-col items-center text-center text-sm font-medium text-gray-500"
@ -60,6 +83,8 @@ const ErrorMessage = ({ message }: { message: ThreadMessage }) => {
</div> </div>
)} )}
</> </>
)}
</div>
) )
} }
export default ErrorMessage export default ErrorMessage

View File

@ -197,7 +197,7 @@ const Sidebar: React.FC = () => {
<div> <div>
{activeThread?.assistants[0]?.tools && {activeThread?.assistants[0]?.tools &&
componentDataAssistantSetting.length > 0 && ( componentDataAssistantSetting.length > 0 && (
<CardSidebar title="Tools"> <CardSidebar title="Tools" isShow={true}>
<div className="px-2 pt-4"> <div className="px-2 pt-4">
<div className="mb-2"> <div className="mb-2">
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">