diff --git a/README.md b/README.md index d91366581..035126faa 100644 --- a/README.md +++ b/README.md @@ -76,31 +76,31 @@ Jan is an open-source ChatGPT alternative that runs 100% offline on your compute Experimental (Nightly Build) - + jan.exe - + Intel - + M1/M2 - + jan.deb - + jan.AppImage diff --git a/core/src/api/index.ts b/core/src/api/index.ts index c7dd9146e..7fb8eeb38 100644 --- a/core/src/api/index.ts +++ b/core/src/api/index.ts @@ -49,7 +49,7 @@ export enum DownloadEvent { export enum LocalImportModelEvent { onLocalImportModelUpdate = 'onLocalImportModelUpdate', - onLocalImportModelError = 'onLocalImportModelError', + onLocalImportModelFailed = 'onLocalImportModelFailed', onLocalImportModelSuccess = 'onLocalImportModelSuccess', onLocalImportModelFinished = 'onLocalImportModelFinished', } diff --git a/core/src/core.ts b/core/src/core.ts index 8831c6001..6e2442c2b 100644 --- a/core/src/core.ts +++ b/core/src/core.ts @@ -65,7 +65,7 @@ const joinPath: (paths: string[]) => Promise = (paths) => global.core.ap * @param path - The path to retrieve. * @returns {Promise} A promise that resolves with the basename. */ -const baseName: (paths: string[]) => Promise = (path) => global.core.api?.baseName(path) +const baseName: (paths: string) => Promise = (path) => global.core.api?.baseName(path) /** * Opens an external URL in the default web browser. diff --git a/core/src/node/api/processors/download.ts b/core/src/node/api/processors/download.ts index bff6f47f0..4ddeff160 100644 --- a/core/src/node/api/processors/download.ts +++ b/core/src/node/api/processors/download.ts @@ -42,6 +42,24 @@ export class Downloader implements Processor { // Downloading file to a temp file first const downloadingTempFile = `${destination}.download` + // adding initial download state + const initialDownloadState: DownloadState = { + modelId, + fileName, + time: { + elapsed: 0, + remaining: 0, + }, + speed: 0, + percent: 0, + size: { + total: 0, + transferred: 0, + }, + downloadState: 'downloading', + } + DownloadManager.instance.downloadProgressMap[modelId] = initialDownloadState + progress(rq, {}) .on('progress', (state: any) => { const downloadState: DownloadState = { diff --git a/core/src/types/model/modelImport.ts b/core/src/types/model/modelImport.ts index 8977c42a0..7c72a691b 100644 --- a/core/src/types/model/modelImport.ts +++ b/core/src/types/model/modelImport.ts @@ -19,4 +19,5 @@ export type ImportingModel = { status: ImportingModelStatus format: string percentage?: number + error?: string } diff --git a/docs/docs/community/community.md b/docs/docs/community/community.mdx similarity index 59% rename from docs/docs/community/community.md rename to docs/docs/community/community.mdx index 24a87daf0..d4866490e 100644 --- a/docs/docs/community/community.md +++ b/docs/docs/community/community.mdx @@ -29,3 +29,18 @@ keywords: ## Careers - [Jobs](https://janai.bamboohr.com/careers) + +## Newsletter + + diff --git a/docs/docs/guides/02-installation/05-docker.md b/docs/docs/guides/02-installation/05-docker.md index 5973e9771..1cdc829df 100644 --- a/docs/docs/guides/02-installation/05-docker.md +++ b/docs/docs/guides/02-installation/05-docker.md @@ -108,7 +108,7 @@ sudo sh ./get-docker.sh --dry-run ```bash # GPU mode with default file system - docker compose --profile gpu up -d + docker compose --profile gpu-fs up -d # GPU mode with S3 file system docker compose --profile gpu-s3fs up -d diff --git a/docs/docs/guides/02-installation/08-antivirus-compatibility-testing.md b/docs/docs/guides/02-installation/08-antivirus-compatibility-testing.md index 6dd82138b..159e89975 100644 --- a/docs/docs/guides/02-installation/08-antivirus-compatibility-testing.md +++ b/docs/docs/guides/02-installation/08-antivirus-compatibility-testing.md @@ -1,7 +1,7 @@ --- -title: Antivirus Compatibility Testing +title: Antivirus Testing slug: /guides/install/antivirus-compatibility-testing -description: Antivirus compatibility testing documentation for the Jan App v0.4.4 release. +description: Antivirus compatibility testing documentation keywords: [ Jan AI, @@ -16,18 +16,16 @@ keywords: ] --- -This documentation outlines the antivirus compatibility testing conducted for the Jan App v0.4.4 release. This documentation includes a matrix that correlates the Jan App version with the tested antivirus versions. +As a part of our release process, we run antivirus compatibility tests for Jan v0.4.4 and onwards. This documentation includes a matrix that correlates the Jan App version with the tested antivirus versions. -## Tested Antivirus Versions +## Antivirus Software Tested -The Jan App v0.4.4 release has undergone automatic testing through CI with a selection of popular antivirus software to ensure compatibility and safety. The following summarizes the testing results: +The following summarizes ongoing testing targets: -| Antivirus | Version | Result | +| Antivirus | Version | Target Result | | ------------------ | ------------ | -------------------------------- | | Bitdefender | 27.0.27.125 | Scanned and 0 threat(s) detected | | McAfee | 4.21.0.0 | Scanned and 0 threat(s) detected | | Microsoft Defender | 1.403.2259.0 | Scanned and 0 threat(s) detected | -## Conclusion - -The testing indicates that Jan App v0.4.4 is compatible with Bitdefender, Microsoft Defender, and McAfee. Any updates or changes to compatibility status will be promptly documented. +To report issues, false positives, or to request additional testing, please email devops@jan.ai diff --git a/docs/docs/guides/07-integrations/01-integrate-continue.mdx b/docs/docs/guides/07-integrations/01-integrate-continue.mdx index 1fa0397e2..1cef6ec46 100644 --- a/docs/docs/guides/07-integrations/01-integrate-continue.mdx +++ b/docs/docs/guides/07-integrations/01-integrate-continue.mdx @@ -85,26 +85,28 @@ Edit the `config.json` file and include the following configuration. ``` - Ensure that the `provider` is `openai`. -- Ensure that the `model` is the same as the one you enabled in the Jan API Server. +- Ensure that the `model` is the ID of the running model. You can check for the respective ID in System Monitor. - Ensure that the `apiBase` is `http://localhost:1337/v1`. - Ensure that the `apiKey` is `EMPTY`. -### 4. Ensure the Using Model Is Activated in Jan +### 4. Double Check the Model is Running -Navigate to `Settings` > `Models`. Activate the model that you want to use in Jan by clicking the **three dots (⋮)** and **start model**. +Open up the `System Monitor` to check that your model is currently running. + +If there are not active models, go to `Settings` > `My Models`. Click on the **three dots (⋮)** and **start model**. ![Active Models](assets/01-start-model.png) -### 5. Try Out the Integration of Jan and Continue in VS Code +### 5. Use Continue in VS Code #### Asking questions about the code -- Highlight a code snippet and press `Command + Shift + M` to open the **Left Panel**. +- Highlight a code snippet and press `Command + M` to open the Continue Extension in VSCode. - Select Jan at the bottom and ask a question about the code, for example, `Explain this code`. ![Continue Interactions](assets/01-continue-ask.png) -#### Editing the code with the help of a large language model +#### Editing the code directly - Highlight a code snippet and press `Command + Shift + L` and input your edit request, for example, `Write comments for this code`. diff --git a/docs/docs/template/QA_script.md b/docs/docs/template/QA_script.md index bba667bcd..9c7eeaf18 100644 --- a/docs/docs/template/QA_script.md +++ b/docs/docs/template/QA_script.md @@ -1,8 +1,8 @@ -# [Release Version] QA Script +# Regression test -**Release Version:** v0.4.6 +**Release Version:** v0.4.7 -**Operating System:** +**Operating System:** MacOS --- @@ -10,78 +10,64 @@ ### 1. Users install app -- [ ] :key: Test for clear user installation instructions. -- [ ] :key: Verify that the installation path is correct for each OS. - [ ] Check that the installation package is not corrupted and passes all security checks. -- [ ] Validate that the app is correctly installed in the default or user-specified directory. -- [ ] Ensure that all necessary dependencies are installed along with the app. -- [ ] :key: :rocket: Confirm that the app launches successfully after installation. +- [ ] :key: Confirm that the app launches successfully after installation. ### 2. Users update app -- [ ] :key: Test that the updated version includes the new features or fixes outlined in the update notes. - [ ] :key: Validate that the update does not corrupt user data or settings. - [ ] :key: Confirm that the app restarts or prompts the user to restart after an update. - -### 3. Users uninstall app - -- [ ] :key::warning: Check that the uninstallation process removes the app successfully from the system. -- [ ] Clean the Jan root directory and open the app to check if it creates all the necessary folders, especially models and extensions. - [ ] When updating the app, check if the `/models` directory has any JSON files that change according to the update. - [ ] Verify if updating the app also updates extensions correctly (test functionality changes, support notifications for necessary tests with each version related to extensions update). -### 4. Users close app +### 3. Users uninstall / close app - [ ] :key: Ensure that after closing the app, all models are unloaded. +- [ ] :key::warning: Check that the uninstallation process removes the app successfully from the system. +- [ ] Clean the Jan root directory and open the app to check if it creates all the necessary folders, especially models and extensions. + ## B. Overview -### 1. Users use shortcut keys +### 1. Shortcut key, memory usage / CPU usage - [ ] :key: Test each shortcut key to confirm it works as described (My models, navigating, opening, closing, etc.). - -### 2. Users check the memory usage and CPU usage - - [ ] :key: Ensure that the interface presents the correct numbers for memory and CPU usage. -### 3. Users check the `active model` +### 2. Users check the `active model` - [ ] :key: Verify that the app correctly displays the state of the loading model (e.g., loading, ready, error). - [ ] :key: Confirm that the app allows users to switch between models if multiple are available. - [ ] Check that the app provides feedback or instructions if the model fails to load. +- [ ] Verify the troubleshooting assistant correctly capture hardware / log info #1784 ## C. Thread ### 1. Users can chat with Jan, the default assistant -- [ ] Verify that the input box for messages is present and functional. -- [ ] :key: Check if typing a message and hitting `Send` results in the message appearing in the chat window. -- [ ] :key: Confirm that Jan, the default assistant, replies to user inputs. +- [ ] :key: Verify sending a message enables users to receive responses from model. - [ ] :key: Ensure that the conversation thread is maintained without any loss of data upon sending multiple messages. +- [ ] ‌Users should be able to edit msg and the assistant will re-generate the answer based on the edited version of the message. - [ ] Test for the ability to send different types of messages (e.g., text, emojis, code blocks). -- [ ] :key: Validate the scroll functionality in the chat window for lengthy conversations. -- [ ] Check if the user can copy the response. -- [ ] Check if the user can delete responses. -- [ ] :key: Check the `clear message` button works. -- [ ] :key: Check the `delete entire chat` works. -- [ ] Check if deleting all the chat retains the system prompt. - [ ] Check the output format of the AI (code blocks, JSON, markdown, ...). +- [ ] :key: Validate the scroll functionality in the chat window for lengthy conversations. +- [ ] Check if the user can copy / delete the response. +- [ ] :key: Check the `clear message` / `delete entire chat` button works. +- [ ] Check if deleting all the chat retains the system prompt. - [ ] :key: Validate that there is appropriate error handling and messaging if the assistant fails to respond. - [ ] Test assistant's ability to maintain context over multiple exchanges. -- [ ] :key: Check the `create new chat` button works correctly +- [ ] :key: Check the `create new chat` button, and new conversation will have an automatically generated thread title based on users msg. - [ ] Confirm that by changing `models` mid-thread the app can still handle it. - [ ] Check the `regenerate` button renews the response (single / multiple times). - [ ] Check the `Instructions` update correctly after the user updates it midway (mid-thread). ### 2. Users can customize chat settings like model parameters via both the GUI & thread.json -- [ ] :key: Confirm that the Threads settings options are accessible. - [ ] Test the functionality to adjust model parameters (e.g., Temperature, Top K, Top P) from the GUI and verify they are reflected in the chat behavior. - [ ] :key: Ensure that changes can be saved and persisted between sessions. - [ ] Validate that users can access and modify the thread.json file. - [ ] :key: Check that changes made in thread.json are correctly applied to the chat session upon reload or restart. - [ ] Check the maximum and minimum limits of the adjustable parameters and how they affect the assistant's responses. -- [ ] :key: Validate user permissions for those who can change settings and persist them. - [ ] :key: Ensure that users switch between threads with different models, the app can handle it. ### 3. Model dropdown @@ -89,25 +75,16 @@ - [ ] Model size should display (for both installed and imported models) ### 4. Users can click on a history thread -- [ ] Test the ability to click on any thread in the history panel. -- [ ] :key: Verify that clicking a thread brings up the past conversation in the main chat window. -- [ ] :key: Ensure that the selected thread is highlighted or otherwise indicated in the history panel. - [ ] Confirm that the chat window displays the entire conversation from the selected history thread without any missing messages. - [ ] :key: Check the performance and accuracy of the history feature when dealing with a large number of threads. - [ ] Validate that historical threads reflect the exact state of the chat at that time, including settings. - [ ] :key: Verify the ability to delete or clean old threads. -- [ ] :key: Confirm that changing the title of the thread updates correctly. +- [ ] Confirm that changing the title of the thread updates correctly. ### 5. Users can config instructions for the assistant. -- [ ] Ensure there is a clear interface to input or change instructions for the assistant. - [ ] Test if the instructions set by the user are being followed by the assistant in subsequent conversations. - [ ] :key: Validate that changes to instructions are updated in real time and do not require a restart of the application or session. -- [ ] :key: Confirm that the assistant's behavior changes in accordance with the new instructions provided. - [ ] :key: Check for the ability to reset instructions to default or clear them completely. -- [ ] :key: Test the feature that allows users to save custom sets of instructions for different scenarios. -- [ ] Validate that instructions can be saved with descriptive names for easy retrieval. -- [ ] :key: Check if the assistant can handle conflicting instructions and how it resolves them. -- [ ] Ensure that instruction configurations are documented for user reference. - [ ] :key: RAG - Users can import documents and the system should process queries about the uploaded file, providing accurate and appropriate responses in the conversation thread. @@ -115,7 +92,6 @@ ### 1. Users can discover recommended models (Jan ships with a few preconfigured model.json files) -- [ ] :key: Verify that recommended models are displayed prominently on the main page. - [ ] :key: Ensure that each model's recommendations are consistent with the user’s activity and preferences. - [ ] Test the functionality of any filters that refine model recommendations. @@ -123,7 +99,6 @@ - [ ] Display the best model for their RAM at the top. - [ ] :key: Ensure that models are labeled with RAM requirements and compatibility. -- [ ] :warning: Test that the platform provides alternative recommendations for models not suitable due to RAM limitations. - [ ] :key: Check the download model functionality and validate if the cancel download feature works correctly. ### 3. Users can download models via a HuggingFace URL (coming soon) @@ -132,22 +107,21 @@ - [ ] :key: Check the progress bar reflects the right process. - [ ] Validate the error handling for invalid or inaccessible URLs. -### 4. Users can add a new model to the Hub +### 4. Users can import new models to the Hub -- [ ] :key: Have clear instructions so users can do their own. +- [ ] :key: Ensure import successfully via drag / drop or upload GGUF. +- [ ] :key: Verify Move model binary file / Keep Original Files & Symlink option are working +- [ ] :warning: Ensure it raises clear errors for users to fix the problem while adding a new model. +- [ ] Users can add more info to the imported model / edit name - [ ] :key: Ensure the new model updates after restarting the app. -- [ ] :warning:Ensure it raises clear errors for users to fix the problem while adding a new model. ### 5. Users can use the model as they want -- [ ] :key: Check `start` button response exactly what it does. -- [ ] :key: Check `stop` button response exactly what it does. -- [ ] :key: Check `delete` button response exactly what it does. +- [ ] :key: Check `start` / `stop` / `delete` button response exactly what it does. - [ ] Check if starting another model stops the other model entirely. -- [ ] Check the `Explore models` navigate correctly to the model panel. +- [x] :rocket: Check the `Explore models` navigate correctly to the model panel. - [ ] :key: Check when deleting a model it will delete all the files on the user's computer. - [ ] :warning:The recommended tags should present right for the user's hardware. -- [ ] Assess that the descriptions of models are accurate and informative. ### 6. Users can Integrate With a Remote Server - [ ] :key: Import openAI GPT model https://jan.ai/guides/using-models/integrate-with-remote-server/ and the model displayed in Hub / Thread dropdown @@ -166,53 +140,45 @@ - [ ] :key: Test the 'Start' action for a model to ensure it initiates and the system resource usage reflects this change. - [ ] :key: Verify the 'Stop' action for a model to confirm it ceases operation and frees up the system resources accordingly. -- [ ] :key: Check the functionality that allows starting a model based on available system resources. -- [ ] :key: Validate that the system prevents starting a new model if it exceeds safe resource utilization thresholds. -- [ ] Ensure that the system provides warnings or recommendations when resource utilization is high before starting new models. -- [ ] Test the ease of accessing model settings from the system monitor for resource management. - [ ] Confirm that any changes in model status (start/stop) are logged or reported to the user for transparency. ## F. Settings -### 1. Users can set color themes and dark/ light modes +### 1. Appearance -- [ ] Verify that the theme setting is easily accessible in the `Appearance` tab. -- [ ] :key: Check that the theme change is reflected immediately upon selection. - [ ] :key: Test the `Light`, `Dark`, and `System` theme settings to ensure they are functioning as expected. - [ ] Confirm that the application saves the theme preference and persists it across sessions. - [ ] Validate that all elements of the UI are compatible with the theme changes and maintain legibility and contrast. -### 2. Users change the extensions [TBU] +### 2. Extensions [TBU] - [ ] Confirm that the `Extensions` tab lists all available plugins. -- [ ] :key: Test the toggle switch for each plugin to ensure it enables or disables the plugin correctly. -- [ ] Verify that plugin changes take effect without needing to restart the application unless specified. -- [ ] :key: Check that the plugin's status (`Installed the latest version`) updates accurately after any changes. -- [ ] Validate the `Manual Installation` process by selecting and installing a plugin file. -- [ ] Test for proper error handling and user feedback when a plugin installation fails. +- [x] :key: Test the toggle switch for each plugin to ensure it enables or disables the plugin correctly. +- [x] Verify that plugin changes take effect without needing to restart the application unless specified. +- [x] :key: Check that the plugin's status (`Installed the latest version`) updates accurately after any changes. +- [x] Validate the `Manual Installation` process by selecting and installing a plugin file. +- [x] Test for proper error handling and user feedback when a plugin installation fails. -### 3. Users change the advanced settings +### 3. Users can add custom plugins via manual installation [TBU] + +- [x] Verify that the `Manual Installation` option is clearly visible and accessible in the `Extensions` section. +- [x] Test the functionality of the `Select` button within the `Manual Installation` area. +- [x] :warning: Check that the file picker dialog allows for the correct plugin file types (e.g., .tgz). +- [x] :key: Validate that the selected plugin file installs correctly and the plugin becomes functional. +- [x] Ensure that there is a progress indicator or confirmation message once the installation is complete. +- [x] Confirm that if the installation is interrupted or fails, the user is given a clear error message. +- [x] :key: Test that the application prevents the installation of incompatible or corrupt plugin files. +- [x] :key: Check that the user can uninstall or disable custom plugins as easily as pre-installed ones. +- [x] Verify that the application's performance remains stable after the installation of custom plugins. + +### 4. Advanced settings - [ ] :key: Test the `Experimental Mode` toggle to confirm it enables or disables experimental features as intended. - [ ] :key: Check the functionality of `Open App Directory` to ensure it opens the correct folder in the system file explorer. -- [ ] Validate that changes in advanced settings are applied immediately or provide appropriate instructions if a restart is needed. -- [ ] Test the application's stability when experimental features are enabled. - -### 4. Users can add custom plugins via manual installation [TBU] - -- [ ] Verify that the `Manual Installation` option is clearly visible and accessible in the `Extensions` section. -- [ ] Test the functionality of the `Select` button within the `Manual Installation` area. -- [ ] :warning: Check that the file picker dialog allows for the correct plugin file types (e.g., .tgz). -- [ ] :key: Validate that the selected plugin file installs correctly and the plugin becomes functional. -- [ ] Ensure that there is a progress indicator or confirmation message once the installation is complete. -- [ ] Confirm that if the installation is interrupted or fails, the user is given a clear error message. -- [ ] :key: Test that the application prevents the installation of incompatible or corrupt plugin files. -- [ ] :key: Check that the user can uninstall or disable custom plugins as easily as pre-installed ones. -- [ ] Verify that the application's performance remains stable after the installation of custom plugins. - -### 5. Advanced Settings -- [ ] Attemp to test downloading model from hub using **HTTP Proxy** [guideline](https://github.com/janhq/jan/pull/1562) - [ ] Users can move **Jan data folder** +- [ ] Validate that changes in advanced settings are applied immediately or provide appropriate instructions if a restart is needed. +- [ ] Attemp to test downloading model from hub using **HTTP Proxy** [guideline](https://github.com/janhq/jan/pull/1562) +- [ ] Logs that are older than 7 days or exceed 1MB in size will be automatically cleared upon starting the application. - [ ] Users can click on Reset button to **factory reset** app settings to its original state & delete all usage data. ## G. Local API server diff --git a/docs/docs/wall-of-love.md b/docs/docs/wall-of-love.md index f196c90e9..f6bfe79d8 100644 --- a/docs/docs/wall-of-love.md +++ b/docs/docs/wall-of-love.md @@ -1,3 +1,95 @@ --- title: Wall of Love ❤️ ---- \ No newline at end of file +--- + +## Twitter + +Check out our amazing users and what they are saying about Jan! + +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +Please share your love for Jan on Twitter and tag us [@janframework](https://twitter.com/janframework)! We would love to hear from you! + +## YouTube + +Watch these amazing videos to see how Jan is being used and loved by the community! + +### Run Any Chatbot FREE Locally on Your Computer + +
+ +
+ +

+ +### Jan AI: Run Open Source LLM 100% Local with OpenAI endpoints + +
+ +
+ +

+ +### Setup Tutorial on Jan.ai. JAN AI: Run open source LLM on local Windows PC. 100% offline LLM and AI. + +
+ +
+ +

+ +### Jan.ai: Like Offline ChatGPT on Your Computer 💡 + +
+ +
+ +

+ +### Jan: Bring AI to your Desktop With 100% Offline AI + +
+ +
+ +

+ +### AI on Your Local PC: Install JanAI (ChatGPT alternative) for Enhanced Privacy + +
+ +
+ +

+ +### Install Jan to Run LLM Offline and Local First + +
+ +
diff --git a/docs/src/containers/Footer/index.js b/docs/src/containers/Footer/index.js index 7cd648149..3e62f579a 100644 --- a/docs/src/containers/Footer/index.js +++ b/docs/src/containers/Footer/index.js @@ -86,6 +86,10 @@ const menus = [ path: "https://janai.bamboohr.com/careers", external: true, }, + { + menu: "Newsletter", + path: "/community#newsletter", + } ], }, ]; diff --git a/extensions/inference-nitro-extension/bin/version.txt b/extensions/inference-nitro-extension/bin/version.txt index e4737652c..0b69c00c5 100644 --- a/extensions/inference-nitro-extension/bin/version.txt +++ b/extensions/inference-nitro-extension/bin/version.txt @@ -1 +1 @@ -0.3.13 +0.3.14 diff --git a/extensions/model-extension/src/index.ts b/extensions/model-extension/src/index.ts index dd5bcdf26..fb1f26885 100644 --- a/extensions/model-extension/src/index.ts +++ b/extensions/model-extension/src/index.ts @@ -16,6 +16,7 @@ import { OptionType, ImportingModel, LocalImportModelEvent, + baseName, } from '@janhq/core' import { extractFileName } from './helpers/path' @@ -488,7 +489,7 @@ export default class JanModelExtension extends ModelExtension { return } - const binaryFileName = extractFileName(modelBinaryPath, '') + const binaryFileName = await baseName(modelBinaryPath) const model: Model = { ...defaultModel, @@ -555,7 +556,7 @@ export default class JanModelExtension extends ModelExtension { model: ImportingModel, optionType: OptionType ): Promise { - const binaryName = extractFileName(model.path, '').replace(/\s/g, '') + const binaryName = (await baseName(model.path)).replace(/\s/g, '') let modelFolderName = binaryName if (binaryName.endsWith(JanModelExtension._supportedModelFormat)) { @@ -568,7 +569,7 @@ export default class JanModelExtension extends ModelExtension { const modelFolderPath = await this.getModelFolderName(modelFolderName) await fs.mkdirSync(modelFolderPath) - const uniqueFolderName = modelFolderPath.split('/').pop() + const uniqueFolderName = await baseName(modelFolderPath) const modelBinaryFile = binaryName.endsWith( JanModelExtension._supportedModelFormat ) @@ -637,14 +638,21 @@ export default class JanModelExtension extends ModelExtension { for (const model of models) { events.emit(LocalImportModelEvent.onLocalImportModelUpdate, model) - const importedModel = await this.importModel(model, optionType) - - events.emit(LocalImportModelEvent.onLocalImportModelSuccess, { - ...model, - modelId: importedModel.id, - }) - importedModels.push(importedModel) + try { + const importedModel = await this.importModel(model, optionType) + events.emit(LocalImportModelEvent.onLocalImportModelSuccess, { + ...model, + modelId: importedModel.id, + }) + importedModels.push(importedModel) + } catch (err) { + events.emit(LocalImportModelEvent.onLocalImportModelFailed, { + ...model, + error: err, + }) + } } + events.emit( LocalImportModelEvent.onLocalImportModelFinished, importedModels diff --git a/uikit/src/button/styles.scss b/uikit/src/button/styles.scss index 003df5b4d..c97bec9e0 100644 --- a/uikit/src/button/styles.scss +++ b/uikit/src/button/styles.scss @@ -5,11 +5,11 @@ @apply disabled:pointer-events-none disabled:bg-zinc-100 disabled:text-zinc-400; &-primary { - @apply bg-primary hover:bg-primary/90 text-white; + @apply bg-blue-600 text-white hover:bg-blue-600/90; } &-secondary-blue { - @apply bg-blue-200 text-blue-600 hover:bg-blue-300/50 dark:hover:bg-blue-200/80; + @apply bg-blue-200 text-blue-600 hover:bg-blue-300/50; } &-danger { @@ -17,7 +17,7 @@ } &-secondary-danger { - @apply bg-red-200 text-red-600 hover:bg-red-300/50 dark:hover:bg-red-200/80; + @apply bg-red-200 text-red-600 hover:bg-red-300/50; } &-outline { @@ -66,7 +66,7 @@ [type='reset'], [type='submit'] { &.btn-primary { - @apply bg-primary hover:bg-primary/90; + @apply bg-blue-600 hover:bg-blue-600/90; @apply disabled:pointer-events-none disabled:bg-zinc-100 disabled:text-zinc-400; } &.btn-secondary { diff --git a/uikit/src/checkbox/styles.scss b/uikit/src/checkbox/styles.scss index 33610f837..cf35ed5ca 100644 --- a/uikit/src/checkbox/styles.scss +++ b/uikit/src/checkbox/styles.scss @@ -1,5 +1,5 @@ .checkbox { - @apply border-border data-[state=checked]:bg-primary h-5 w-5 flex-shrink-0 rounded-md border data-[state=checked]:text-white; + @apply border-border h-5 w-5 flex-shrink-0 rounded-md border data-[state=checked]:bg-blue-600 data-[state=checked]:text-white; &--icon { @apply h-4 w-4; diff --git a/uikit/src/input/styles.scss b/uikit/src/input/styles.scss index e649f494d..51efd8e57 100644 --- a/uikit/src/input/styles.scss +++ b/uikit/src/input/styles.scss @@ -1,6 +1,6 @@ .input { @apply border-border placeholder:text-muted-foreground flex h-9 w-full rounded-lg border bg-transparent px-3 py-1 transition-colors; - @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; @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; } diff --git a/uikit/src/main.scss b/uikit/src/main.scss index f3294e12e..e31b53c68 100644 --- a/uikit/src/main.scss +++ b/uikit/src/main.scss @@ -42,69 +42,10 @@ --danger: 346.8 77.2% 49.8%; --danger-foreground: 355.7 100% 97.3%; + --secondary: 60 4.8% 95.9%; + --secondary-foreground: 24 9.8% 10%; + --border: 20 5.9% 90%; --input: 20 5.9% 90%; --ring: 20 14.3% 4.1%; - - .primary-blue { - --primary: 221 83% 53%; - --primary-foreground: 210 40% 98%; - - --secondary: 60 4.8% 95.9%; - --secondary-foreground: 24 9.8% 10%; - } - - .primary-green { - --primary: 142.1 76.2% 36.3%; - --primary-foreground: 355.7 100% 97.3%; - - --secondary: 240 4.8% 95.9%; - --secondary-foreground: 240 5.9% 10%; - } - - .primary-purple { - --primary: 262.1 83.3% 57.8%; - --primary-foreground: 210 20% 98%; - - --secondary: 220 14.3% 95.9%; - --secondary-foreground: 220.9 39.3% 11%; - } -} - -.dark { - --background: 20 14.3% 4.1%; - --foreground: 60 9.1% 97.8%; - - --muted: 12 6.5% 15.1%; - --muted-foreground: 24 5.4% 63.9%; - - --danger: 346.8 77.2% 49.8%; - --danger-foreground: 355.7 100% 97.3%; - - --border: 12 6.5% 15.1%; - --input: 12 6.5% 15.1%; - --ring: 35.5 91.7% 32.9%; - - .primary-blue { - --primary: 221 83% 53%; - --primary-foreground: 222.2 47.4% 11.2%; - - --secondary: 12 6.5% 15.1%; - --secondary-foreground: 60 9.1% 97.8%; - } - - .primary-green { - --primary: 142.1 70.6% 45.3%; - --primary-foreground: 144.9 80.4% 10%; - --secondary: 240 3.7% 15.9%; - --secondary-foreground: 0 0% 98%; - } - - .primary-purple { - --primary: 263.4 70% 50.4%; - --primary-foreground: 210 20% 98%; - - --secondary: 215 27.9% 16.9%; - --secondary-foreground: 210 20% 98%; - } } diff --git a/uikit/src/progress/styles.scss b/uikit/src/progress/styles.scss index 0b7078f48..1a8483c47 100644 --- a/uikit/src/progress/styles.scss +++ b/uikit/src/progress/styles.scss @@ -1,7 +1,7 @@ .progress { - @apply bg-secondary relative h-4 w-full overflow-hidden rounded-full; + @apply relative h-4 w-full overflow-hidden rounded-full bg-gray-100; &-indicator { - @apply bg-primary h-full w-full flex-1 transition-all; + @apply h-full w-full flex-1 bg-blue-600 transition-all; } } diff --git a/uikit/src/select/styles.scss b/uikit/src/select/styles.scss index 90485723a..99db49766 100644 --- a/uikit/src/select/styles.scss +++ b/uikit/src/select/styles.scss @@ -1,6 +1,6 @@ .select { @apply placeholder:text-muted-foreground border-border flex h-9 w-full items-center justify-between whitespace-nowrap rounded-md border bg-transparent px-3 py-2 text-sm shadow-sm disabled:cursor-not-allowed [&>span]:line-clamp-1; - @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; @apply focus-within:outline-none focus-visible:outline-0 focus-visible:ring-2 focus-visible:ring-blue-500 focus-visible:ring-offset-1; &-caret { diff --git a/uikit/src/slider/styles.scss b/uikit/src/slider/styles.scss index 718972efb..465392419 100644 --- a/uikit/src/slider/styles.scss +++ b/uikit/src/slider/styles.scss @@ -2,7 +2,7 @@ @apply relative flex w-full touch-none select-none items-center; &-track { - @apply relative h-1.5 w-full grow overflow-hidden rounded-full bg-gray-200 dark:bg-gray-800; + @apply relative h-1.5 w-full grow overflow-hidden rounded-full bg-gray-200; [data-disabled] { @apply cursor-not-allowed opacity-50; } @@ -13,6 +13,6 @@ } &-thumb { - @apply border-primary/50 bg-background focus-visible:ring-ring block h-4 w-4 rounded-full border shadow transition-colors focus-visible:outline-none focus-visible:ring-1 disabled:pointer-events-none disabled:opacity-50; + @apply bg-background focus-visible:ring-ring block h-4 w-4 rounded-full border border-blue-600/50 shadow transition-colors focus-visible:outline-none focus-visible:ring-1 disabled:pointer-events-none disabled:opacity-50; } } diff --git a/uikit/src/switch/styles.scss b/uikit/src/switch/styles.scss index c8a12cdf5..57fa128ba 100644 --- a/uikit/src/switch/styles.scss +++ b/uikit/src/switch/styles.scss @@ -1,7 +1,7 @@ .switch { @apply inline-flex h-[20px] w-[36px] shrink-0 cursor-pointer items-center rounded-full border-2 border-transparent; @apply focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2; - @apply data-[state=checked]:bg-primary data-[state=unchecked]:bg-input; + @apply data-[state=unchecked]:bg-input data-[state=checked]:bg-blue-600; @apply disabled:cursor-not-allowed disabled:opacity-50; &-toggle { diff --git a/uikit/src/tooltip/styles.scss b/uikit/src/tooltip/styles.scss index 8ae645cee..169e081b7 100644 --- a/uikit/src/tooltip/styles.scss +++ b/uikit/src/tooltip/styles.scss @@ -1,6 +1,6 @@ .tooltip { - @apply dark:bg-input dark:text-foreground z-50 overflow-hidden rounded-md bg-gray-950 px-2 py-1.5 text-xs font-medium text-gray-200 shadow-md; + @apply z-50 overflow-hidden rounded-md bg-gray-950 px-2 py-1.5 text-xs font-medium text-gray-200 shadow-md; &-arrow { - @apply dark:fill-input fill-gray-950; + @apply fill-gray-950; } } diff --git a/web/app/layout.tsx b/web/app/layout.tsx index 6c6fc65ab..37bcdf53e 100644 --- a/web/app/layout.tsx +++ b/web/app/layout.tsx @@ -15,7 +15,7 @@ export const metadata: Metadata = { export default function RootLayout({ children }: PropsWithChildren) { return ( - +
{children} diff --git a/web/containers/CardSidebar/index.tsx b/web/containers/CardSidebar/index.tsx index 132494d48..3013360e9 100644 --- a/web/containers/CardSidebar/index.tsx +++ b/web/containers/CardSidebar/index.tsx @@ -45,7 +45,7 @@ export default function CardSidebar({ return (
@@ -61,7 +61,7 @@ export default function CardSidebar({ if (!children) return setShow(!show) }} - className="flex w-full flex-1 items-center space-x-2 rounded-lg bg-zinc-100 py-2 pr-2 dark:bg-zinc-900" + className="flex w-full flex-1 items-center space-x-2 rounded-lg bg-zinc-100 py-2 pr-2" > setMore(!more)} > @@ -114,7 +114,7 @@ export default function CardSidebar({ <> {title === 'Model' ? (
- + {openFileTitle()} @@ -122,7 +122,7 @@ export default function CardSidebar({
) : ( - + {openFileTitle()} )} @@ -141,7 +141,7 @@ export default function CardSidebar({ /> <>
- + Edit Global Defaults for{' '} diff --git a/web/containers/Checkbox/index.tsx b/web/containers/Checkbox/index.tsx index a545771b6..1ced3e19d 100644 --- a/web/containers/Checkbox/index.tsx +++ b/web/containers/Checkbox/index.tsx @@ -34,12 +34,10 @@ const Checkbox: React.FC = ({ return (
-

- {title} -

+

{title}

- + diff --git a/web/containers/DropdownListSidebar/index.tsx b/web/containers/DropdownListSidebar/index.tsx index c05d26e51..dc5ee2605 100644 --- a/web/containers/DropdownListSidebar/index.tsx +++ b/web/containers/DropdownListSidebar/index.tsx @@ -203,15 +203,14 @@ const DropdownListSidebar = ({ isTabActive === 1 && '[&_.select-scroll-down-button]:hidden' )} > -
-
    +
    +
      {engineOptions.map((name, i) => { return (
    • setIsTabActive(i)} @@ -230,8 +229,7 @@ const DropdownListSidebar = ({ {name} diff --git a/web/containers/GPUDriverPromptModal/index.tsx b/web/containers/GPUDriverPromptModal/index.tsx index bdcf1b2f8..8d11b4efa 100644 --- a/web/containers/GPUDriverPromptModal/index.tsx +++ b/web/containers/GPUDriverPromptModal/index.tsx @@ -60,7 +60,7 @@ const GPUDriverPrompt: React.FC = () => { id="default-checkbox" type="checkbox" onChange={onDoNotShowAgainChange} - className="h-4 w-4 rounded border-gray-300 bg-gray-100 text-blue-600 focus:ring-2 focus:ring-blue-500 dark:border-gray-600 dark:bg-gray-700 dark:ring-offset-gray-800 dark:focus:ring-blue-600" + className="h-4 w-4 rounded border-gray-300 bg-gray-100 text-blue-600 focus:ring-2 focus:ring-blue-500" /> Don't show again
    diff --git a/web/containers/Layout/BottomBar/DownloadingState/index.tsx b/web/containers/Layout/BottomBar/DownloadingState/index.tsx index dcebacd3c..4c3d596b0 100644 --- a/web/containers/Layout/BottomBar/DownloadingState/index.tsx +++ b/web/containers/Layout/BottomBar/DownloadingState/index.tsx @@ -47,7 +47,7 @@ export default function DownloadingState() { { className="h-2 w-24" value={transferredSize / totalSize} /> - + {progress.toFixed(2)}%
diff --git a/web/containers/Layout/BottomBar/SystemMonitor/index.tsx b/web/containers/Layout/BottomBar/SystemMonitor/index.tsx index aec91bf6e..989ae7777 100644 --- a/web/containers/Layout/BottomBar/SystemMonitor/index.tsx +++ b/web/containers/Layout/BottomBar/SystemMonitor/index.tsx @@ -57,17 +57,6 @@ const SystemMonitor = () => { // eslint-disable-next-line react-hooks/exhaustive-deps }, []) - const calculateUtilization = () => { - let sum = 0 - const util = gpus.map((x) => { - return Number(x['utilization']) - }) - util.forEach((num) => { - sum += num - }) - return sum - } - return (
{
-
+
Memory
- - {toGibibytes(usedRam)} of {toGibibytes(totalRam)} used + + {toGibibytes(usedRam, { hideUnit: true })}/ + {toGibibytes(totalRam, { hideUnit: true })} GB
@@ -148,30 +138,29 @@ const SystemMonitor = () => {
{gpus.length > 0 && ( -
-
GPU
-
- - - {calculateUtilization()}% - -
+
{gpus.map((gpu, index) => ( -
- - {gpu.name} - -
- +
+
+ + {gpu.name} + +
+
+ + {gpu.memoryTotal - gpu.memoryFree}/ + {gpu.memoryTotal} + + MB +
+
+
+ +
+ + {gpu.utilization}% -
- {gpu.vram} - MB VRAM -
))} diff --git a/web/containers/Layout/Ribbon/index.tsx b/web/containers/Layout/Ribbon/index.tsx index c0bc46586..dc6191f09 100644 --- a/web/containers/Layout/Ribbon/index.tsx +++ b/web/containers/Layout/Ribbon/index.tsx @@ -45,7 +45,7 @@ export default function RibbonNav() { size={20} className={twMerge( 'flex-shrink-0 text-muted-foreground', - serverEnabled && 'text-gray-300 dark:text-gray-700' + serverEnabled && 'text-gray-300' )} /> ), @@ -114,7 +114,7 @@ export default function RibbonNav() {
{isActive && ( )} @@ -166,7 +166,7 @@ export default function RibbonNav() {
{isActive && ( )} diff --git a/web/containers/Layout/TopBar/index.tsx b/web/containers/Layout/TopBar/index.tsx index 605d8e44d..9686a7fd9 100644 --- a/web/containers/Layout/TopBar/index.tsx +++ b/web/containers/Layout/TopBar/index.tsx @@ -159,7 +159,7 @@ const TopBar = () => { size={16} className="text-muted-foreground" /> - + {openFileTitle()}
@@ -175,7 +175,7 @@ const TopBar = () => { className="mt-0.5 flex-shrink-0 text-muted-foreground" />
- + Edit Threads Settings @@ -204,7 +204,7 @@ const TopBar = () => { className="text-muted-foreground" />
- + {openFileTitle()}
diff --git a/web/containers/Loader/index.tsx b/web/containers/Loader/index.tsx index dcf1bec65..cf6604fc8 100644 --- a/web/containers/Loader/index.tsx +++ b/web/containers/Loader/index.tsx @@ -7,12 +7,12 @@ export default function Loader({ description }: Props) {
-

{description}

diff --git a/web/containers/ModalTroubleShoot/AppLogs.tsx b/web/containers/ModalTroubleShoot/AppLogs.tsx index d4f6bddb8..98f076599 100644 --- a/web/containers/ModalTroubleShoot/AppLogs.tsx +++ b/web/containers/ModalTroubleShoot/AppLogs.tsx @@ -28,7 +28,7 @@ const AppLogs = () => {
{experimentalFeature && ( diff --git a/web/screens/LocalServer/index.tsx b/web/screens/LocalServer/index.tsx index f9c2cf719..3a8668770 100644 --- a/web/screens/LocalServer/index.tsx +++ b/web/screens/LocalServer/index.tsx @@ -181,7 +181,7 @@ const LocalServerScreen = () => {
-

+

Server Options

@@ -231,15 +231,12 @@ const LocalServerScreen = () => {
) diff --git a/web/screens/Settings/EditModelInfoModal/index.tsx b/web/screens/Settings/EditModelInfoModal/index.tsx index bb87b7ed9..bc9d6521d 100644 --- a/web/screens/Settings/EditModelInfoModal/index.tsx +++ b/web/screens/Settings/EditModelInfoModal/index.tsx @@ -1,6 +1,12 @@ -import { useCallback, useEffect, useMemo, useState } from 'react' +import { useCallback, useEffect, useState } from 'react' -import { Model, ModelEvent, events, openFileExplorer } from '@janhq/core' +import { + Model, + ModelEvent, + events, + joinPath, + openFileExplorer, +} from '@janhq/core' import { Modal, ModalContent, @@ -47,6 +53,7 @@ const EditModelInfoModal: React.FC = () => { const janDataFolder = useAtomValue(janDataFolderPathAtom) const updateImportingModel = useSetAtom(updateImportingModelAtom) const { updateModelInfo } = useImportModel() + const [modelPath, setModelPath] = useState('') const editingModel = importingModels.find( (model) => model.importId === editingModelId @@ -88,13 +95,19 @@ const EditModelInfoModal: React.FC = () => { setEditingModelId(undefined) } - const modelFolderPath = useMemo(() => { - return `${janDataFolder}/models/${editingModel?.modelId}` + useEffect(() => { + const getModelPath = async () => { + const modelId = editingModel?.modelId + if (!modelId) return '' + const path = await joinPath([janDataFolder, 'models', modelId]) + setModelPath(path) + } + getModelPath() }, [janDataFolder, editingModel]) const onShowInFinderClick = useCallback(() => { - openFileExplorer(modelFolderPath) - }, [modelFolderPath]) + openFileExplorer(modelPath) + }, [modelPath]) if (!editingModel) { setImportModelStage('IMPORTING_MODEL') @@ -104,7 +117,10 @@ const EditModelInfoModal: React.FC = () => { } return ( - + Edit Model Information @@ -130,7 +146,7 @@ const EditModelInfoModal: React.FC = () => {
- {modelFolderPath} + {modelPath}