Merge branch 'dev' into main
This commit is contained in:
commit
9369ac3e8b
@ -54,6 +54,7 @@ jobs:
|
|||||||
- name: "Cleanup cache"
|
- name: "Cleanup cache"
|
||||||
continue-on-error: true
|
continue-on-error: true
|
||||||
run: |
|
run: |
|
||||||
|
rm -rf ~/jan
|
||||||
make clean
|
make clean
|
||||||
|
|
||||||
- name: Get Commit Message for PR
|
- name: Get Commit Message for PR
|
||||||
@ -98,6 +99,7 @@ jobs:
|
|||||||
- name: "Cleanup cache"
|
- name: "Cleanup cache"
|
||||||
continue-on-error: true
|
continue-on-error: true
|
||||||
run: |
|
run: |
|
||||||
|
rm -rf ~/jan
|
||||||
make clean
|
make clean
|
||||||
|
|
||||||
- name: Linter and test
|
- name: Linter and test
|
||||||
@ -109,7 +111,7 @@ jobs:
|
|||||||
CSC_IDENTITY_AUTO_DISCOVERY: "false"
|
CSC_IDENTITY_AUTO_DISCOVERY: "false"
|
||||||
|
|
||||||
test-on-windows:
|
test-on-windows:
|
||||||
if: github.event_name == 'push' || github.event_name == 'workflow_dispatch'
|
if: github.event_name == 'push'
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
@ -131,6 +133,12 @@ jobs:
|
|||||||
shell: powershell
|
shell: powershell
|
||||||
continue-on-error: true
|
continue-on-error: true
|
||||||
run: |
|
run: |
|
||||||
|
$path = "$Env:APPDATA\jan"
|
||||||
|
if (Test-Path $path) {
|
||||||
|
Remove-Item "\\?\$path" -Recurse -Force
|
||||||
|
} else {
|
||||||
|
Write-Output "Folder does not exist."
|
||||||
|
}
|
||||||
make clean
|
make clean
|
||||||
|
|
||||||
- name: Get Commit Message for push event
|
- name: Get Commit Message for push event
|
||||||
@ -173,6 +181,12 @@ jobs:
|
|||||||
shell: powershell
|
shell: powershell
|
||||||
continue-on-error: true
|
continue-on-error: true
|
||||||
run: |
|
run: |
|
||||||
|
$path = "$Env:APPDATA\jan"
|
||||||
|
if (Test-Path $path) {
|
||||||
|
Remove-Item "\\?\$path" -Recurse -Force
|
||||||
|
} else {
|
||||||
|
Write-Output "Folder does not exist."
|
||||||
|
}
|
||||||
make clean
|
make clean
|
||||||
|
|
||||||
- name: Get Commit Message for PR
|
- name: Get Commit Message for PR
|
||||||
@ -216,6 +230,12 @@ jobs:
|
|||||||
shell: powershell
|
shell: powershell
|
||||||
continue-on-error: true
|
continue-on-error: true
|
||||||
run: |
|
run: |
|
||||||
|
$path = "$Env:APPDATA\jan"
|
||||||
|
if (Test-Path $path) {
|
||||||
|
Remove-Item "\\?\$path" -Recurse -Force
|
||||||
|
} else {
|
||||||
|
Write-Output "Folder does not exist."
|
||||||
|
}
|
||||||
make clean
|
make clean
|
||||||
|
|
||||||
- name: Linter and test
|
- name: Linter and test
|
||||||
@ -243,6 +263,7 @@ jobs:
|
|||||||
- name: "Cleanup cache"
|
- name: "Cleanup cache"
|
||||||
continue-on-error: true
|
continue-on-error: true
|
||||||
run: |
|
run: |
|
||||||
|
rm -rf ~/jan
|
||||||
make clean
|
make clean
|
||||||
|
|
||||||
- name: Get Commit Message for PR
|
- name: Get Commit Message for PR
|
||||||
@ -289,6 +310,7 @@ jobs:
|
|||||||
- name: "Cleanup cache"
|
- name: "Cleanup cache"
|
||||||
continue-on-error: true
|
continue-on-error: true
|
||||||
run: |
|
run: |
|
||||||
|
rm -rf ~/jan
|
||||||
make clean
|
make clean
|
||||||
|
|
||||||
- name: Linter and test
|
- name: Linter and test
|
||||||
|
|||||||
@ -47,11 +47,11 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
args: |
|
args: |
|
||||||
Jan App ${{ inputs.build_reason }} build artifact version {{ VERSION }}:
|
Jan App ${{ inputs.build_reason }} build artifact version {{ VERSION }}:
|
||||||
- Windows: https://app.jan.ai/download/nightly/win-x64
|
- Windows: https://delta.jan.ai/latest/jan-win-x64-{{ VERSION }}.exe
|
||||||
- macOS Intel: https://app.jan.ai/download/nightly/mac-x64
|
- macOS Intel: https://delta.jan.ai/latest/jan-mac-x64-{{ VERSION }}.dmg
|
||||||
- macOS Apple Silicon: https://app.jan.ai/download/nightly/mac-arm64
|
- macOS Apple Silicon: https://delta.jan.ai/latest/jan-mac-arm64-{{ VERSION }}.dmg
|
||||||
- Linux Deb: https://app.jan.ai/download/nightly/linux-amd64-deb
|
- Linux Deb: https://delta.jan.ai/latest/jan-linux-amd64-{{ VERSION }}.deb
|
||||||
- Linux AppImage: https://app.jan.ai/download/nightly/linux-amd64-appimage
|
- Linux AppImage: https://delta.jan.ai/latest/jan-linux-x86_64-{{ VERSION }}.AppImage
|
||||||
- Github action run: https://github.com/janhq/jan/actions/runs/{{ GITHUB_RUN_ID }}
|
- Github action run: https://github.com/janhq/jan/actions/runs/{{ GITHUB_RUN_ID }}
|
||||||
env:
|
env:
|
||||||
DISCORD_WEBHOOK: ${{ secrets.DISCORD_WEBHOOK }}
|
DISCORD_WEBHOOK: ${{ secrets.DISCORD_WEBHOOK }}
|
||||||
@ -79,7 +79,7 @@ common:
|
|||||||
securityContext: {}
|
securityContext: {}
|
||||||
|
|
||||||
service:
|
service:
|
||||||
extenalLabel: {}
|
externalLabel: {}
|
||||||
type: ClusterIP
|
type: ClusterIP
|
||||||
port: 1337
|
port: 1337
|
||||||
targetPort: 1337
|
targetPort: 1337
|
||||||
@ -193,7 +193,7 @@ common:
|
|||||||
securityContext: {}
|
securityContext: {}
|
||||||
|
|
||||||
service:
|
service:
|
||||||
extenalLabel: {}
|
externalLabel: {}
|
||||||
type: ClusterIP
|
type: ClusterIP
|
||||||
port: 3000
|
port: 3000
|
||||||
targetPort: 3000
|
targetPort: 3000
|
||||||
|
|||||||
@ -60,7 +60,7 @@ const joinPath: (paths: string[]) => Promise<string> = (paths) =>
|
|||||||
globalThis.core.api?.joinPath(paths)
|
globalThis.core.api?.joinPath(paths)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrive the basename from an url.
|
* Retrieve the basename from an url.
|
||||||
* @param path - The path to retrieve.
|
* @param path - The path to retrieve.
|
||||||
* @returns {Promise<string>} A promise that resolves with the basename.
|
* @returns {Promise<string>} A promise that resolves with the basename.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@ -4,7 +4,7 @@ import { log } from './logger'
|
|||||||
|
|
||||||
export const getSystemResourceInfo = async (): Promise<SystemResourceInfo> => {
|
export const getSystemResourceInfo = async (): Promise<SystemResourceInfo> => {
|
||||||
const cpu = await physicalCpuCount()
|
const cpu = await physicalCpuCount()
|
||||||
log(`[NITRO]::CPU informations - ${cpu}`)
|
log(`[NITRO]::CPU information - ${cpu}`)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
numCpuPhysicalCore: cpu,
|
numCpuPhysicalCore: cpu,
|
||||||
|
|||||||
@ -20,6 +20,7 @@ export type InputComponentProps = {
|
|||||||
placeholder: string
|
placeholder: string
|
||||||
value: string
|
value: string
|
||||||
type?: InputType
|
type?: InputType
|
||||||
|
textAlign?: 'left' | 'right'
|
||||||
}
|
}
|
||||||
|
|
||||||
export type SliderComponentProps = {
|
export type SliderComponentProps = {
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { test, appInfo } from '../config/fixtures'
|
import { test, appInfo, page, TIMEOUT } from '../config/fixtures'
|
||||||
import { expect } from '@playwright/test'
|
import { expect } from '@playwright/test'
|
||||||
|
|
||||||
test.beforeAll(async () => {
|
test.beforeAll(async () => {
|
||||||
@ -16,4 +16,9 @@ test.beforeAll(async () => {
|
|||||||
test('explores hub', async ({ hubPage }) => {
|
test('explores hub', async ({ hubPage }) => {
|
||||||
await hubPage.navigateByMenu()
|
await hubPage.navigateByMenu()
|
||||||
await hubPage.verifyContainerVisible()
|
await hubPage.verifyContainerVisible()
|
||||||
|
const useModelBtn= page.getByTestId(/^use-model-btn-.*/).first()
|
||||||
|
|
||||||
|
await expect(useModelBtn).toBeVisible({
|
||||||
|
timeout: TIMEOUT,
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
35
electron/tests/e2e/thread.e2e.spec.ts
Normal file
35
electron/tests/e2e/thread.e2e.spec.ts
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
import { expect } from '@playwright/test'
|
||||||
|
import { page, test, TIMEOUT } from '../config/fixtures'
|
||||||
|
|
||||||
|
test('Select GPT model from Hub and Chat with Invalid API Key', async ({ hubPage }) => {
|
||||||
|
await hubPage.navigateByMenu()
|
||||||
|
await hubPage.verifyContainerVisible()
|
||||||
|
|
||||||
|
// Select the first GPT model
|
||||||
|
await page
|
||||||
|
.locator('[data-testid^="use-model-btn"][data-testid*="gpt"]')
|
||||||
|
.first().click()
|
||||||
|
|
||||||
|
// Attempt to create thread and chat in Thread page
|
||||||
|
await page
|
||||||
|
.getByTestId('btn-create-thread')
|
||||||
|
.click()
|
||||||
|
|
||||||
|
await page
|
||||||
|
.getByTestId('txt-input-chat')
|
||||||
|
.fill('dummy value')
|
||||||
|
|
||||||
|
await page
|
||||||
|
.getByTestId('btn-send-chat')
|
||||||
|
.click()
|
||||||
|
|
||||||
|
await page.waitForFunction(() => {
|
||||||
|
const loaders = document.querySelectorAll('[data-testid$="loader"]');
|
||||||
|
return !loaders.length;
|
||||||
|
}, { timeout: TIMEOUT });
|
||||||
|
|
||||||
|
const APIKeyError = page.getByTestId('invalid-API-key-error')
|
||||||
|
await expect(APIKeyError).toBeVisible({
|
||||||
|
timeout: TIMEOUT,
|
||||||
|
})
|
||||||
|
})
|
||||||
@ -8,8 +8,9 @@ export class BasePage {
|
|||||||
constructor(
|
constructor(
|
||||||
protected readonly page: Page,
|
protected readonly page: Page,
|
||||||
readonly action: CommonActions,
|
readonly action: CommonActions,
|
||||||
protected containerId: string
|
protected containerId: string,
|
||||||
) {}
|
) {
|
||||||
|
}
|
||||||
|
|
||||||
public getValue(key: string) {
|
public getValue(key: string) {
|
||||||
return this.action.getValue(key)
|
return this.action.getValue(key)
|
||||||
@ -24,7 +25,11 @@ export class BasePage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async navigateByMenu() {
|
async navigateByMenu() {
|
||||||
await this.page.getByTestId(this.menuId).first().click()
|
await this.clickFirstElement(this.menuId)
|
||||||
|
}
|
||||||
|
|
||||||
|
async clickFirstElement(testId: string) {
|
||||||
|
await this.page.getByTestId(testId).first().click()
|
||||||
}
|
}
|
||||||
|
|
||||||
async verifyContainerVisible() {
|
async verifyContainerVisible() {
|
||||||
@ -36,7 +41,7 @@ export class BasePage {
|
|||||||
await this.isElementVisible('img[alt="Jan - Logo"]')
|
await this.isElementVisible('img[alt="Jan - Logo"]')
|
||||||
}
|
}
|
||||||
|
|
||||||
//wait and find a specific element with it's selector and return Visible
|
//wait and find a specific element with its selector and return Visible
|
||||||
async isElementVisible(selector: any) {
|
async isElementVisible(selector: any) {
|
||||||
let isVisible = true
|
let isVisible = true
|
||||||
await this.page
|
await this.page
|
||||||
|
|||||||
@ -22,7 +22,7 @@ export const getSelectedText = async () => {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Registers a global shortcut of `accelerator`. The `callback` is called
|
* Registers a global shortcut of `accelerator`. The `callback` is called
|
||||||
* with the selected text when the registered shorcut is pressed by the user
|
* with the selected text when the registered shortcut is pressed by the user
|
||||||
*
|
*
|
||||||
* Returns `true` if the shortcut was registered successfully
|
* Returns `true` if the shortcut was registered successfully
|
||||||
*/
|
*/
|
||||||
|
|||||||
@ -3,7 +3,7 @@ import { app } from 'electron'
|
|||||||
export const setupCore = async () => {
|
export const setupCore = async () => {
|
||||||
// Setup core api for main process
|
// Setup core api for main process
|
||||||
global.core = {
|
global.core = {
|
||||||
// Define appPath function for app to retrieve app path globaly
|
// Define appPath function for app to retrieve app path globally
|
||||||
appPath: () => app.getPath('userData'),
|
appPath: () => app.getPath('userData'),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -42,7 +42,7 @@ export default class JanInferenceNitroExtension extends LocalOAIEngine {
|
|||||||
/**
|
/**
|
||||||
* The interval id for the health check. Used to stop the health check.
|
* The interval id for the health check. Used to stop the health check.
|
||||||
*/
|
*/
|
||||||
private getNitroProcesHealthIntervalId: NodeJS.Timeout | undefined = undefined
|
private getNitroProcessHealthIntervalId: NodeJS.Timeout | undefined = undefined
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tracking the current state of nitro process.
|
* Tracking the current state of nitro process.
|
||||||
@ -65,7 +65,7 @@ export default class JanInferenceNitroExtension extends LocalOAIEngine {
|
|||||||
this.inferenceUrl = `${window.core?.api?.baseApiUrl}/v1/chat/completions`
|
this.inferenceUrl = `${window.core?.api?.baseApiUrl}/v1/chat/completions`
|
||||||
}
|
}
|
||||||
|
|
||||||
this.getNitroProcesHealthIntervalId = setInterval(
|
this.getNitroProcessHealthIntervalId = setInterval(
|
||||||
() => this.periodicallyGetNitroHealth(),
|
() => this.periodicallyGetNitroHealth(),
|
||||||
JanInferenceNitroExtension._intervalHealthCheck
|
JanInferenceNitroExtension._intervalHealthCheck
|
||||||
)
|
)
|
||||||
@ -95,7 +95,7 @@ export default class JanInferenceNitroExtension extends LocalOAIEngine {
|
|||||||
|
|
||||||
override loadModel(model: Model): Promise<void> {
|
override loadModel(model: Model): Promise<void> {
|
||||||
if (model.engine !== this.provider) return Promise.resolve()
|
if (model.engine !== this.provider) return Promise.resolve()
|
||||||
this.getNitroProcesHealthIntervalId = setInterval(
|
this.getNitroProcessHealthIntervalId = setInterval(
|
||||||
() => this.periodicallyGetNitroHealth(),
|
() => this.periodicallyGetNitroHealth(),
|
||||||
JanInferenceNitroExtension._intervalHealthCheck
|
JanInferenceNitroExtension._intervalHealthCheck
|
||||||
)
|
)
|
||||||
@ -106,9 +106,9 @@ export default class JanInferenceNitroExtension extends LocalOAIEngine {
|
|||||||
if (model?.engine && model.engine !== this.provider) return
|
if (model?.engine && model.engine !== this.provider) return
|
||||||
|
|
||||||
// stop the periocally health check
|
// stop the periocally health check
|
||||||
if (this.getNitroProcesHealthIntervalId) {
|
if (this.getNitroProcessHealthIntervalId) {
|
||||||
clearInterval(this.getNitroProcesHealthIntervalId)
|
clearInterval(this.getNitroProcessHealthIntervalId)
|
||||||
this.getNitroProcesHealthIntervalId = undefined
|
this.getNitroProcessHealthIntervalId = undefined
|
||||||
}
|
}
|
||||||
return super.unloadModel(model)
|
return super.unloadModel(model)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -15,7 +15,8 @@
|
|||||||
"controllerType": "input",
|
"controllerType": "input",
|
||||||
"controllerProps": {
|
"controllerProps": {
|
||||||
"value": "120000",
|
"value": "120000",
|
||||||
"placeholder": "Interval in milliseconds. E.g. 120000"
|
"placeholder": "Interval in milliseconds. E.g. 120000",
|
||||||
|
"textAlign": "right"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|||||||
@ -138,7 +138,7 @@ export const getCurrentLoad = () =>
|
|||||||
})
|
})
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This will retrive GPU informations and persist settings.json
|
* This will retrieve GPU information and persist settings.json
|
||||||
* Will be called when the extension is loaded to turn on GPU acceleration if supported
|
* Will be called when the extension is loaded to turn on GPU acceleration if supported
|
||||||
*/
|
*/
|
||||||
export const updateNvidiaInfo = async () => {
|
export const updateNvidiaInfo = async () => {
|
||||||
|
|||||||
@ -53,7 +53,7 @@ export class FileLogger extends Logger {
|
|||||||
daysToKeep?: number | undefined
|
daysToKeep?: number | undefined
|
||||||
): void {
|
): void {
|
||||||
// clear existing timeout
|
// clear existing timeout
|
||||||
// incase we rerun it with different values
|
// in case we rerun it with different values
|
||||||
if (this.timeout) clearTimeout(this.timeout)
|
if (this.timeout) clearTimeout(this.timeout)
|
||||||
this.timeout = undefined
|
this.timeout = undefined
|
||||||
|
|
||||||
|
|||||||
@ -12,7 +12,7 @@ export async function setup() {
|
|||||||
if (!existsSync(appDir)) mkdirSync(appDir)
|
if (!existsSync(appDir)) mkdirSync(appDir)
|
||||||
//@ts-ignore
|
//@ts-ignore
|
||||||
global.core = {
|
global.core = {
|
||||||
// Define appPath function for app to retrieve app path globaly
|
// Define appPath function for app to retrieve app path globally
|
||||||
appPath: () => appDir,
|
appPath: () => appDir,
|
||||||
}
|
}
|
||||||
init({
|
init({
|
||||||
|
|||||||
@ -22,7 +22,7 @@
|
|||||||
|
|
||||||
* This ADR aims to outline design decisions for deploying Jan in cloud native environments such as: Runpod, AWS, Azure, GCP in a fast and simple way.
|
* This ADR aims to outline design decisions for deploying Jan in cloud native environments such as: Runpod, AWS, Azure, GCP in a fast and simple way.
|
||||||
* The current code-base should not change too much.
|
* The current code-base should not change too much.
|
||||||
* The current plugins must be reusable across enviroments (Desktop, Cloud-native).
|
* The current plugins must be reusable across environments (Desktop, Cloud-native).
|
||||||
|
|
||||||
|
|
||||||
### Key Design Decisions
|
### Key Design Decisions
|
||||||
|
|||||||
@ -14,8 +14,8 @@ Modular Architecture w/ Plugins:
|
|||||||
|
|
||||||
- Jan will have an architecture similar to VSCode or k8Lens
|
- Jan will have an architecture similar to VSCode or k8Lens
|
||||||
- "Desktop Application" whose functionality can be extended thru plugins
|
- "Desktop Application" whose functionality can be extended thru plugins
|
||||||
- Jan's architecture will need to accomodate plugins for (a) Persistence(b) IAM(c) Teams and RBAC(d) Policy engines(e) "Apps" (i.e. higher-order business logic)(f) Themes (UI)
|
- Jan's architecture will need to accommodate plugins for (a) Persistence(b) IAM(c) Teams and RBAC(d) Policy engines(e) "Apps" (i.e. higher-order business logic)(f) Themes (UI)
|
||||||
- Nitro's architecture will need to accomodate plugins for different "model backends"(a) llama.cpp(b) rkwk (and others)(c) 3rd-party AIs
|
- Nitro's architecture will need to accommodate plugins for different "model backends"(a) llama.cpp(b) rkwk (and others)(c) 3rd-party AIs
|
||||||
|
|
||||||
## Decision
|
## Decision
|
||||||
|
|
||||||
|
|||||||
@ -14,7 +14,7 @@ Proposed
|
|||||||
## Context
|
## Context
|
||||||
|
|
||||||
What is the issue that we're seeing that is motivating this decision or change?
|
What is the issue that we're seeing that is motivating this decision or change?
|
||||||
- The A.I world is moving fast with multiple runtime/ prebaked environment. We or the builder cannot cover just everything but rather we should adopt it and facillitate it as much as possible within Jan.
|
- The A.I world is moving fast with multiple runtime/ prebaked environment. We or the builder cannot cover just everything but rather we should adopt it and facilitate it as much as possible within Jan.
|
||||||
- For `Run your own A.I`: Builder can build app on Jan (NodeJS env) and connect to external endpoint which serves the real A.I
|
- For `Run your own A.I`: Builder can build app on Jan (NodeJS env) and connect to external endpoint which serves the real A.I
|
||||||
- e.g 1: Nitro acting as proxy to `triton-inference-server` running within a Docker container controlled by Jan app
|
- e.g 1: Nitro acting as proxy to `triton-inference-server` running within a Docker container controlled by Jan app
|
||||||
- e.g 2: Original models can be in many formats (pytorch, paddlepaddle). In order to run it with the most optimized version locally, there must be a step to transpile the model ([Ollama import model](https://github.com/jmorganca/ollama/blob/main/docs/import.md), Tensorrt). Btw Jan can prebuilt it and let user pull but later
|
- e.g 2: Original models can be in many formats (pytorch, paddlepaddle). In order to run it with the most optimized version locally, there must be a step to transpile the model ([Ollama import model](https://github.com/jmorganca/ollama/blob/main/docs/import.md), Tensorrt). Btw Jan can prebuilt it and let user pull but later
|
||||||
|
|||||||
@ -2,14 +2,20 @@ import { forwardRef } from 'react'
|
|||||||
import { twMerge } from 'tailwind-merge'
|
import { twMerge } from 'tailwind-merge'
|
||||||
|
|
||||||
export interface InputProps
|
export interface InputProps
|
||||||
extends React.InputHTMLAttributes<HTMLInputElement> {}
|
extends React.InputHTMLAttributes<HTMLInputElement> {
|
||||||
|
textAlign?: 'left' | 'right'
|
||||||
|
}
|
||||||
|
|
||||||
const Input = forwardRef<HTMLInputElement, InputProps>(
|
const Input = forwardRef<HTMLInputElement, InputProps>(
|
||||||
({ className, type, ...props }, ref) => {
|
({ className, type, textAlign, ...props }, ref) => {
|
||||||
return (
|
return (
|
||||||
<input
|
<input
|
||||||
type={type}
|
type={type}
|
||||||
className={twMerge('input', className)}
|
className={twMerge(
|
||||||
|
'input',
|
||||||
|
className,
|
||||||
|
textAlign === 'right' && 'text-right'
|
||||||
|
)}
|
||||||
ref={ref}
|
ref={ref}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@ -3,4 +3,7 @@
|
|||||||
@apply disabled:text-muted-foreground disabled:cursor-not-allowed disabled:bg-zinc-100 disabled:dark:bg-zinc-800 disabled:dark:text-zinc-600;
|
@apply disabled:text-muted-foreground disabled:cursor-not-allowed disabled:bg-zinc-100 disabled:dark:bg-zinc-800 disabled:dark:text-zinc-600;
|
||||||
@apply focus-within:outline-none focus-visible:outline-0 focus-visible:ring-2 focus-visible:ring-blue-500 focus-visible:ring-offset-1;
|
@apply focus-within:outline-none focus-visible:outline-0 focus-visible:ring-2 focus-visible:ring-blue-500 focus-visible:ring-offset-1;
|
||||||
@apply file:border-0 file:bg-transparent file:font-medium;
|
@apply file:border-0 file:bg-transparent file:font-medium;
|
||||||
|
&.text-right {
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,7 +4,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
&-content {
|
&-content {
|
||||||
@apply bg-background border-border fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border p-4 shadow-lg duration-200 sm:rounded-lg md:w-full;
|
@apply bg-background border-border fixed left-[50%] top-[50%] z-50 grid max-h-[calc(100%-48px)] w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 overflow-y-auto border p-4 shadow-lg duration-200 sm:rounded-lg md:w-full;
|
||||||
}
|
}
|
||||||
|
|
||||||
&-close {
|
&-close {
|
||||||
|
|||||||
@ -62,7 +62,7 @@ export default function Error({
|
|||||||
>
|
>
|
||||||
contact us
|
contact us
|
||||||
</a>{' '}
|
</a>{' '}
|
||||||
if the problem presists.
|
if the problem persists.
|
||||||
</p>
|
</p>
|
||||||
<div
|
<div
|
||||||
className="mt-5 w-full rounded border border-red-400 bg-red-100 px-4 py-3 text-red-700"
|
className="mt-5 w-full rounded border border-red-400 bg-red-100 px-4 py-3 text-red-700"
|
||||||
|
|||||||
@ -44,8 +44,8 @@ export enum UsecaseTag {
|
|||||||
UsecaseDefault = 'UsecaseDefault',
|
UsecaseDefault = 'UsecaseDefault',
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum MiscellanousTag {
|
export enum MiscellaneousTag {
|
||||||
MiscellanousDefault = 'MiscellanousDefault',
|
MiscellaneousDefault = 'MiscellaneousDefault',
|
||||||
}
|
}
|
||||||
|
|
||||||
export type TagType =
|
export type TagType =
|
||||||
@ -59,4 +59,4 @@ export type TagType =
|
|||||||
| NumOfBit
|
| NumOfBit
|
||||||
| RamRequired
|
| RamRequired
|
||||||
| UsecaseTag
|
| UsecaseTag
|
||||||
| MiscellanousTag
|
| MiscellaneousTag
|
||||||
|
|||||||
@ -138,7 +138,7 @@ const DropdownListSidebar = ({
|
|||||||
...model?.parameters,
|
...model?.parameters,
|
||||||
...model?.settings,
|
...model?.settings,
|
||||||
}
|
}
|
||||||
// Update model paramter to the thread state
|
// Update model parameter to the thread state
|
||||||
setThreadModelParams(activeThread.id, modelParams)
|
setThreadModelParams(activeThread.id, modelParams)
|
||||||
|
|
||||||
// Update model parameter to the thread file
|
// Update model parameter to the thread file
|
||||||
|
|||||||
@ -31,6 +31,7 @@ export default function GenerateResponse() {
|
|||||||
<div
|
<div
|
||||||
className="absolute left-0 top-0 h-full bg-gray-200"
|
className="absolute left-0 top-0 h-full bg-gray-200"
|
||||||
style={{ width: `${loader}%` }}
|
style={{ width: `${loader}%` }}
|
||||||
|
data-testid="response-loader"
|
||||||
/>
|
/>
|
||||||
<span className="relative z-10">Generating response...</span>
|
<span className="relative z-10">Generating response...</span>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -40,6 +40,7 @@ export default function ModelStart() {
|
|||||||
<div
|
<div
|
||||||
className="absolute left-0 top-0 h-full bg-blue-200"
|
className="absolute left-0 top-0 h-full bg-blue-200"
|
||||||
style={{ width: `${loader}%` }}
|
style={{ width: `${loader}%` }}
|
||||||
|
data-testid="model-loader"
|
||||||
/>
|
/>
|
||||||
<span className="relative z-10">
|
<span className="relative z-10">
|
||||||
{stateModel.state === 'start' ? 'Starting' : 'Stopping'}
|
{stateModel.state === 'start' ? 'Starting' : 'Stopping'}
|
||||||
|
|||||||
@ -81,6 +81,7 @@ const SliderRightPanel: React.FC<Props> = ({
|
|||||||
max={max}
|
max={max}
|
||||||
value={String(value)}
|
value={String(value)}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
|
textAlign="right"
|
||||||
onBlur={(e) => {
|
onBlur={(e) => {
|
||||||
if (Number(e.target.value) > Number(max)) {
|
if (Number(e.target.value) > Number(max)) {
|
||||||
onValueChanged?.(Number(max))
|
onValueChanged?.(Number(max))
|
||||||
|
|||||||
@ -107,7 +107,7 @@ export default function useGetSystemResources() {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
/**
|
/**
|
||||||
* Fetch resource informations once
|
* Fetch resource information once
|
||||||
*/
|
*/
|
||||||
getSystemResources,
|
getSystemResources,
|
||||||
/**
|
/**
|
||||||
|
|||||||
@ -148,6 +148,7 @@ const ChatInput: React.FC = () => {
|
|||||||
'max-h-[400px] resize-none pr-20',
|
'max-h-[400px] resize-none pr-20',
|
||||||
fileUpload.length && 'rounded-t-none'
|
fileUpload.length && 'rounded-t-none'
|
||||||
)}
|
)}
|
||||||
|
data-testid="txt-input-chat"
|
||||||
style={{ height: '40px' }}
|
style={{ height: '40px' }}
|
||||||
ref={textareaRef}
|
ref={textareaRef}
|
||||||
onKeyDown={onKeyDown}
|
onKeyDown={onKeyDown}
|
||||||
@ -320,6 +321,7 @@ const ChatInput: React.FC = () => {
|
|||||||
}
|
}
|
||||||
themes="primary"
|
themes="primary"
|
||||||
className="min-w-[100px]"
|
className="min-w-[100px]"
|
||||||
|
data-testid="btn-send-chat"
|
||||||
onClick={() => sendChatMessage(currentPrompt)}
|
onClick={() => sendChatMessage(currentPrompt)}
|
||||||
>
|
>
|
||||||
Send
|
Send
|
||||||
|
|||||||
@ -43,7 +43,7 @@ const ErrorMessage = ({ message }: { message: ThreadMessage }) => {
|
|||||||
case ErrorCode.InvalidApiKey:
|
case ErrorCode.InvalidApiKey:
|
||||||
case ErrorCode.InvalidRequestError:
|
case ErrorCode.InvalidRequestError:
|
||||||
return (
|
return (
|
||||||
<span>
|
<span data-testid="invalid-API-key-error">
|
||||||
Invalid API key. Please check your API key from{' '}
|
Invalid API key. Please check your API key from{' '}
|
||||||
<button
|
<button
|
||||||
className="font-medium text-primary dark:text-blue-400"
|
className="font-medium text-primary dark:text-blue-400"
|
||||||
|
|||||||
@ -113,6 +113,7 @@ const ExploreModelItemHeader: React.FC<Props> = ({ model, onClick, open }) => {
|
|||||||
className="min-w-[98px]"
|
className="min-w-[98px]"
|
||||||
onClick={onUseModelClick}
|
onClick={onUseModelClick}
|
||||||
disabled={serverEnabled}
|
disabled={serverEnabled}
|
||||||
|
data-testid={`use-model-btn-${model.id}`}
|
||||||
>
|
>
|
||||||
Use
|
Use
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
@ -18,7 +18,7 @@ export const HuggingFaceConvertingErrorModal = () => {
|
|||||||
</div>
|
</div>
|
||||||
<div className="flex flex-col items-center justify-center gap-1">
|
<div className="flex flex-col items-center justify-center gap-1">
|
||||||
<p className="text-center">
|
<p className="text-center">
|
||||||
An error occured while {conversionStatus} model {repoData.id}.
|
An error occurred while {conversionStatus} model {repoData.id}.
|
||||||
</p>
|
</p>
|
||||||
<p>Please close this modal and try again.</p>
|
<p>Please close this modal and try again.</p>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -21,7 +21,7 @@ export const HuggingFaceSearchModal = () => {
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className="flex flex-col items-center justify-center gap-1">
|
<div className="flex flex-col items-center justify-center gap-1">
|
||||||
<p className="text-2xl font-bold">Hugging Face Convertor</p>
|
<p className="text-2xl font-bold">Hugging Face Converter</p>
|
||||||
<p className="text-gray-500">Type the repository id below</p>
|
<p className="text-gray-500">Type the repository id below</p>
|
||||||
</div>
|
</div>
|
||||||
<Input
|
<Input
|
||||||
|
|||||||
@ -21,7 +21,7 @@ const SettingDetailTextInputItem: React.FC<Props> = ({
|
|||||||
settingProps,
|
settingProps,
|
||||||
onValueChanged,
|
onValueChanged,
|
||||||
}) => {
|
}) => {
|
||||||
const { value, type, placeholder } =
|
const { value, type, placeholder, textAlign } =
|
||||||
settingProps.controllerProps as InputComponentProps
|
settingProps.controllerProps as InputComponentProps
|
||||||
|
|
||||||
const description = marked.parse(settingProps.description ?? '', {
|
const description = marked.parse(settingProps.description ?? '', {
|
||||||
@ -43,6 +43,7 @@ const SettingDetailTextInputItem: React.FC<Props> = ({
|
|||||||
<Input
|
<Input
|
||||||
placeholder={placeholder}
|
placeholder={placeholder}
|
||||||
type={type}
|
type={type}
|
||||||
|
textAlign={textAlign}
|
||||||
value={value}
|
value={value}
|
||||||
className="ml-4 w-[360px]"
|
className="ml-4 w-[360px]"
|
||||||
onChange={(e) => onValueChanged?.(e.target.value)}
|
onChange={(e) => onValueChanged?.(e.target.value)}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user