From 0f8deddcdfac92913ac6a9682d16396e8b0a7692 Mon Sep 17 00:00:00 2001 From: hieu-jan <150573299+hieu-jan@users.noreply.github.com> Date: Tue, 23 Jan 2024 00:27:13 +0900 Subject: [PATCH 01/53] docs: add requirements to installation guides --- docs/docs/guides/02-installation/01-mac.md | 4 ++++ .../docs/guides/02-installation/02-windows.md | 21 ++++++++++--------- docs/docs/guides/02-installation/03-linux.md | 17 +++++++++++++-- 3 files changed, 30 insertions(+), 12 deletions(-) diff --git a/docs/docs/guides/02-installation/01-mac.md b/docs/docs/guides/02-installation/01-mac.md index 8e67b5bed..a719cd913 100644 --- a/docs/docs/guides/02-installation/01-mac.md +++ b/docs/docs/guides/02-installation/01-mac.md @@ -17,6 +17,10 @@ keywords: # Installing Jan on MacOS +## Requirements + +Ensure that your MacOS version is 13 or higher to run Jan. + ## Installation Jan is available for download via our homepage, [https://jan.ai/](https://jan.ai/). diff --git a/docs/docs/guides/02-installation/02-windows.md b/docs/docs/guides/02-installation/02-windows.md index b200554d2..6623fba62 100644 --- a/docs/docs/guides/02-installation/02-windows.md +++ b/docs/docs/guides/02-installation/02-windows.md @@ -17,6 +17,17 @@ keywords: # Installing Jan on Windows +## System Requirements + +Ensure that your system meets the following requirements: + +- Windows 10 or higher is required to run Jan. + +To enable GPU support, you will need: + +- NVIDIA GPU with CUDA Toolkit 11.7 or higher +- NVIDIA driver 470.63.01 or higher + ## Installation Jan is available for download via our homepage, [https://jan.ai](https://jan.ai/). @@ -59,13 +70,3 @@ To remove all user data associated with Jan, you can delete the `/jan` directory cd C:\Users\%USERNAME%\AppData\Roaming rmdir /S jan ``` - -## Troubleshooting - -### Microsoft Defender - -**Error: "Microsoft Defender SmartScreen prevented an unrecognized app from starting"** - -Windows Defender may display the above warning when running the Jan Installer, as a standard security measure. - -To proceed, select the "More info" option and select the "Run Anyway" option to continue with the installation. diff --git a/docs/docs/guides/02-installation/03-linux.md b/docs/docs/guides/02-installation/03-linux.md index 21dfac1a9..bb93a8a3b 100644 --- a/docs/docs/guides/02-installation/03-linux.md +++ b/docs/docs/guides/02-installation/03-linux.md @@ -17,6 +17,18 @@ keywords: # Installing Jan on Linux +## Requirements + +Ensure that your system meets the following requirements: + +- glibc 2.27 or higher (check with `ldd --version`) +- gcc 11, g++ 11, cpp 11, or higher, refer to this [link](https://jan.ai/guides/troubleshooting/gpu-not-used/#specific-requirements-for-linux) for more information. + +To enable GPU support, you will need: + +- NVIDIA GPU with CUDA Toolkit 11.7 or higher +- NVIDIA driver 470.63.01 or higher + ## Installation Jan is available for download via our homepage, [https://jan.ai](https://jan.ai/). @@ -66,7 +78,8 @@ jan-linux-amd64-{version}.deb # AppImage jan-linux-x86_64-{version}.AppImage ``` -``` + +```` ## Uninstall Jan @@ -75,7 +88,7 @@ To uninstall Jan on Linux, you should use your package manager's uninstall or re ```bash sudo apt-get remove jan # where jan is the name of Jan package -``` +```` For other Linux distributions, if you installed Jan via the `.AppImage` file, you can uninstall Jan by deleting the `.AppImage` file. From cef149a11b3cd29c4285793b2e02ca60dfb47894 Mon Sep 17 00:00:00 2001 From: hieu-jan <150573299+hieu-jan@users.noreply.github.com> Date: Tue, 23 Jan 2024 00:33:18 +0900 Subject: [PATCH 02/53] docs: finalize installation guide --- docs/docs/guides/02-installation/01-mac.md | 3 ++- docs/docs/guides/02-installation/02-windows.md | 1 + docs/docs/guides/02-installation/03-linux.md | 7 +++---- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/docs/docs/guides/02-installation/01-mac.md b/docs/docs/guides/02-installation/01-mac.md index a719cd913..7a3961384 100644 --- a/docs/docs/guides/02-installation/01-mac.md +++ b/docs/docs/guides/02-installation/01-mac.md @@ -12,12 +12,13 @@ keywords: conversational AI, no-subscription fee, large language model, + installation guide, ] --- # Installing Jan on MacOS -## Requirements +## System Requirements Ensure that your MacOS version is 13 or higher to run Jan. diff --git a/docs/docs/guides/02-installation/02-windows.md b/docs/docs/guides/02-installation/02-windows.md index 6623fba62..d60ab86f7 100644 --- a/docs/docs/guides/02-installation/02-windows.md +++ b/docs/docs/guides/02-installation/02-windows.md @@ -12,6 +12,7 @@ keywords: conversational AI, no-subscription fee, large language model, + installation guide, ] --- diff --git a/docs/docs/guides/02-installation/03-linux.md b/docs/docs/guides/02-installation/03-linux.md index bb93a8a3b..0ec7fea60 100644 --- a/docs/docs/guides/02-installation/03-linux.md +++ b/docs/docs/guides/02-installation/03-linux.md @@ -12,12 +12,13 @@ keywords: conversational AI, no-subscription fee, large language model, + installation guide, ] --- # Installing Jan on Linux -## Requirements +## System Requirements Ensure that your system meets the following requirements: @@ -79,8 +80,6 @@ jan-linux-amd64-{version}.deb jan-linux-x86_64-{version}.AppImage ``` -```` - ## Uninstall Jan To uninstall Jan on Linux, you should use your package manager's uninstall or remove option. For Debian/Ubuntu-based distributions, if you installed Jan via the `.deb` package, you can uninstall Jan using the following command: @@ -88,7 +87,7 @@ To uninstall Jan on Linux, you should use your package manager's uninstall or re ```bash sudo apt-get remove jan # where jan is the name of Jan package -```` +``` For other Linux distributions, if you installed Jan via the `.AppImage` file, you can uninstall Jan by deleting the `.AppImage` file. From f23e9115fc5cce5e2a85e2bbc5b5392bad876f84 Mon Sep 17 00:00:00 2001 From: hieu-jan <150573299+hieu-jan@users.noreply.github.com> Date: Tue, 23 Jan 2024 01:22:36 +0900 Subject: [PATCH 03/53] docs: add developer/install-and-prerequisites --- .../04-install-and-prerequisites.md | 65 +++++++++++++++++++ 1 file changed, 65 insertions(+) create mode 100644 docs/docs/developer/01-overview/04-install-and-prerequisites.md diff --git a/docs/docs/developer/01-overview/04-install-and-prerequisites.md b/docs/docs/developer/01-overview/04-install-and-prerequisites.md new file mode 100644 index 000000000..dbc39fccd --- /dev/null +++ b/docs/docs/developer/01-overview/04-install-and-prerequisites.md @@ -0,0 +1,65 @@ +--- +title: Installation and Prerequisites +slug: /developer/install-and-prerequisites +description: Jan is a ChatGPT-alternative that runs on your own computer, with a local API server. +keywords: + [ + Jan AI, + Jan, + ChatGPT alternative, + local AI, + private AI, + conversational AI, + no-subscription fee, + large language model, + installation, + prerequisites, + developer setup, + ] +--- + +## Requirements: + +- [Hardware Requirements](../../guides/02-installation/06-hardware.md) + +- System Requirements: + - [Windows](../../install/windows/#system-requirements) + - [MacOS](../../install/mac/#system-requirements) + - [Linux](../../install/linux/#system-requirements) + +## Prerequisites + +Before installing Jan, make sure you have the following installed on your computer: + +- [Node.js](https://nodejs.org/en/) (version 20.0.0 or higher) +- [yarn](https://yarnpkg.com/) (version 1.22.0 or higher) +- [make](https://www.gnu.org/software/make/) (version 3.81 or higher) + +## Instructions + +1. Clone the repository and install dependencies + +```bash +git clone https://github.com/janhq/jan +cd jan +git checkout -b DESIRED_BRANCH +yarn install +``` + +2. Run development and use Jan Desktop + +```bash +make dev +``` + +This will start the development server and open the Jan Desktop app. + +## For Production Build + +```bash +# Do steps 1 and 2 in the previous section +# Build the app +make build +``` + +This will build the app MacOS (M1/M2/M3) for production (with code signing already done) and put the result in dist folder. From 7c0c6924d831bf6d070e8aa749ec8607dfeca6b9 Mon Sep 17 00:00:00 2001 From: hieu-jan <150573299+hieu-jan@users.noreply.github.com> Date: Tue, 23 Jan 2024 02:28:37 +0900 Subject: [PATCH 04/53] docs: finalize-installation-and-prerequisites --- .../01-overview/04-install-and-prerequisites.md | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/docs/docs/developer/01-overview/04-install-and-prerequisites.md b/docs/docs/developer/01-overview/04-install-and-prerequisites.md index dbc39fccd..a5f7c985b 100644 --- a/docs/docs/developer/01-overview/04-install-and-prerequisites.md +++ b/docs/docs/developer/01-overview/04-install-and-prerequisites.md @@ -18,7 +18,7 @@ keywords: ] --- -## Requirements: +## Requirements - [Hardware Requirements](../../guides/02-installation/06-hardware.md) @@ -29,8 +29,6 @@ keywords: ## Prerequisites -Before installing Jan, make sure you have the following installed on your computer: - - [Node.js](https://nodejs.org/en/) (version 20.0.0 or higher) - [yarn](https://yarnpkg.com/) (version 1.22.0 or higher) - [make](https://www.gnu.org/software/make/) (version 3.81 or higher) @@ -43,10 +41,15 @@ Before installing Jan, make sure you have the following installed on your comput git clone https://github.com/janhq/jan cd jan git checkout -b DESIRED_BRANCH +``` + +2. Install dependencies + +```bash yarn install ``` -2. Run development and use Jan Desktop +3. Run development and use Jan Desktop ```bash make dev @@ -62,4 +65,4 @@ This will start the development server and open the Jan Desktop app. make build ``` -This will build the app MacOS (M1/M2/M3) for production (with code signing already done) and put the result in dist folder. +This will build the app MacOS (M1/M2/M3) for production (with code signing already done) and put the result in `dist` folder. From b14ff4cdb5fa35fa565397a5f8698c40d8bba24d Mon Sep 17 00:00:00 2001 From: hieu-jan <150573299+hieu-jan@users.noreply.github.com> Date: Tue, 23 Jan 2024 03:00:51 +0900 Subject: [PATCH 05/53] docs: finalize install and prerequisitites --- .../01-overview/04-install-and-prerequisites.md | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/docs/docs/developer/01-overview/04-install-and-prerequisites.md b/docs/docs/developer/01-overview/04-install-and-prerequisites.md index a5f7c985b..b418609f6 100644 --- a/docs/docs/developer/01-overview/04-install-and-prerequisites.md +++ b/docs/docs/developer/01-overview/04-install-and-prerequisites.md @@ -35,7 +35,7 @@ keywords: ## Instructions -1. Clone the repository and install dependencies +1. **Clone the Repository:** ```bash git clone https://github.com/janhq/jan @@ -43,19 +43,19 @@ cd jan git checkout -b DESIRED_BRANCH ``` -2. Install dependencies +2. **Install Dependencie:s** ```bash yarn install ``` -3. Run development and use Jan Desktop +3. **Run Development and Use Jan Desktop** ```bash make dev ``` -This will start the development server and open the Jan Desktop app. +This command starts the development server and opens the Jan Desktop app. ## For Production Build @@ -65,4 +65,8 @@ This will start the development server and open the Jan Desktop app. make build ``` -This will build the app MacOS (M1/M2/M3) for production (with code signing already done) and put the result in `dist` folder. +This will build the app MacOS (M1/M2/M3) for production (with code signing already done) and place the result in `/electron/dist` folder. + +## Troubleshooting + +If you run into any issues due to a broken build, please check the [Stuck on a Broken Build](../../troubleshooting/stuck-on-broken-build) guide. From b946c5d3e029c88840c445930875b300989a00e7 Mon Sep 17 00:00:00 2001 From: hieu-jan <150573299+hieu-jan@users.noreply.github.com> Date: Tue, 23 Jan 2024 03:05:48 +0900 Subject: [PATCH 06/53] docs: correct slug --- .../developer/01-overview/04-install-and-prerequisites.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/docs/developer/01-overview/04-install-and-prerequisites.md b/docs/docs/developer/01-overview/04-install-and-prerequisites.md index b418609f6..25fe2550e 100644 --- a/docs/docs/developer/01-overview/04-install-and-prerequisites.md +++ b/docs/docs/developer/01-overview/04-install-and-prerequisites.md @@ -1,7 +1,7 @@ --- title: Installation and Prerequisites -slug: /developer/install-and-prerequisites -description: Jan is a ChatGPT-alternative that runs on your own computer, with a local API server. +slug: /developer/prereq +description: Guide to install and setup Jan for development. keywords: [ Jan AI, From 0afdee4a9856d419499c74099b7dbe986d941c8b Mon Sep 17 00:00:00 2001 From: hieu-jan <150573299+hieu-jan@users.noreply.github.com> Date: Wed, 24 Jan 2024 01:40:08 +0900 Subject: [PATCH 07/53] docs: update API Reference page --- docs/openapi/jan.yaml | 55 +++-- docs/openapi/specs/models.yaml | 370 ++++++++++----------------------- 2 files changed, 150 insertions(+), 275 deletions(-) diff --git a/docs/openapi/jan.yaml b/docs/openapi/jan.yaml index bfff0ad73..9981f7308 100644 --- a/docs/openapi/jan.yaml +++ b/docs/openapi/jan.yaml @@ -67,21 +67,32 @@ paths: x-codeSamples: - lang: cURL source: | - curl http://localhost:1337/v1/chat/completions \ - -H "Content-Type: application/json" \ + curl -X 'POST' \ + 'http://127.0.0.1:1337/v1/chat/completions' \ + -H 'accept: application/json' \ + -H 'Content-Type: application/json' \ -d '{ - "model": "tinyllama-1.1b", - "messages": [ - { - "role": "system", - "content": "You are a helpful assistant." - }, - { - "role": "user", - "content": "Hello!" - } - ] - }' + "messages": [ + { + "content": "You are a helpful assistant.", + "role": "system" + }, + { + "content": "Hello!", + "role": "user" + } + ], + "model": "tinyllama-1.1b", + "stream": true, + "max_tokens": 2048, + "stop": [ + "hello" + ], + "frequency_penalty": 0, + "presence_penalty": 0, + "temperature": 0.7, + "top_p": 0.95 + }' /models: get: operationId: listModels @@ -103,7 +114,9 @@ paths: x-codeSamples: - lang: cURL source: | - curl http://localhost:1337/v1/models + curl -X 'GET' \ + 'http://127.0.0.1:1337/v1/models' \ + -H 'accept: application/json' "/models/download/{model_id}": get: operationId: downloadModel @@ -131,7 +144,9 @@ paths: x-codeSamples: - lang: cURL source: | - curl -X POST http://localhost:1337/v1/models/download/{model_id} + curl -X 'GET' \ + 'http://127.0.0.1:1337/v1/models/download/{model_id}' \ + -H 'accept: application/json' "/models/{model_id}": get: operationId: retrieveModel @@ -162,7 +177,9 @@ paths: x-codeSamples: - lang: cURL source: | - curl http://localhost:1337/v1/models/{model_id} + curl -X 'GET' \ + 'http://127.0.0.1:1337/v1/models/{model_id}' \ + -H 'accept: application/json' delete: operationId: deleteModel tags: @@ -191,7 +208,9 @@ paths: x-codeSamples: - lang: cURL source: | - curl -X DELETE http://localhost:1337/v1/models/{model_id} + curl -X 'DELETE' \ + 'http://127.0.0.1:1337/v1/models/{model_id}' \ + -H 'accept: application/json' /threads: post: operationId: createThread diff --git a/docs/openapi/specs/models.yaml b/docs/openapi/specs/models.yaml index 418be9563..791d14880 100644 --- a/docs/openapi/specs/models.yaml +++ b/docs/openapi/specs/models.yaml @@ -18,106 +18,77 @@ components: Model: type: object properties: - type: - type: string - default: model - description: The type of the object. - version: - type: string - default: "1" - description: The version number of the model. - id: - type: string - description: Unique identifier used in chat-completions model_name, matches - folder name. - example: zephyr-7b - name: - type: string - description: Name of the model. - example: Zephyr 7B - owned_by: - type: string - description: Compatibility field for OpenAI. - default: "" - created: - type: integer - format: int64 - description: Unix timestamp representing the creation time. - description: - type: string - description: Description of the model. - state: - type: string - enum: - - null - - downloading - - ready - - starting - - stopping - description: Current state of the model. - format: - type: string - description: State format of the model, distinct from the engine. - example: ggufv3 source_url: type: string format: uri description: URL to the source of the model. - example: https://huggingface.co/TheBloke/zephyr-7B-beta-GGUF/blob/main/zephyr-7b-beta.Q4_K_M.gguf + example: https://huggingface.co/janhq/trinity-v1.2-GGUF/resolve/main/trinity-v1.2.Q4_K_M.gguf + id: + type: string + description: + Unique identifier used in chat-completions model_name, matches + folder name. + example: trinity-v1.2-7b + object: + type: string + example: model + name: + type: string + description: Name of the model. + example: Trinity-v1.2 7B Q4 + version: + type: string + default: "1.0" + description: The version number of the model. + description: + type: string + description: Description of the model. + example: Trinity is an experimental model merge using the Slerp method. Recommended for daily assistance purposes. + format: + type: string + description: State format of the model, distinct from the engine. + example: gguf settings: type: object properties: ctx_len: - type: string + type: integer description: Context length. - example: "2048" - ngl: + example: 4096 + prompt_template: type: string - description: Number of layers. - example: "100" - embedding: - type: string - description: Indicates if embedding is enabled. - example: "true" - n_parallel: - type: string - description: Number of parallel processes. - example: "4" + example: "<|im_start|>system\n{system_message}<|im_end|>\n<|im_start|>user\n{prompt}<|im_end|>\n<|im_start|>assistant" additionalProperties: false parameters: type: object properties: temperature: - type: string - description: Temperature setting for the model. - example: "0.7" - token_limit: - type: string - description: Token limit for the model. - example: "2048" - top_k: - type: string - description: Top-k setting for the model. - example: "0" + example: 0.7 top_p: - type: string - description: Top-p setting for the model. - example: "1" + example: 0.95 stream: - type: string - description: Indicates if streaming is enabled. - example: "true" + example: true + max_tokens: + example: 4096 + stop: + example: [] + frequency_penalty: + example: 0 + presence_penalty: + example: 0 additionalProperties: false metadata: - type: object - description: Additional metadata. - assets: - type: array - items: + author: type: string - description: List of assets related to the model. - required: - - source_url + example: Jan + tags: + example: ["7B", "Merged", "Featured"] + size: + example: 4370000000, + cover: + example: "https://raw.githubusercontent.com/janhq/jan/main/models/trinity-v1.2-7b/cover.png" + engine: + example: nitro ModelObject: type: object properties: @@ -125,7 +96,7 @@ components: type: string description: | The identifier of the model. - example: zephyr-7b + example: ztrinity-v1.2-7b object: type: string description: | @@ -145,197 +116,82 @@ components: GetModelResponse: type: object properties: - id: - type: string - description: The identifier of the model. - example: zephyr-7b - object: - type: string - description: Type of the object, indicating it's a model. - default: model - created: - type: integer - format: int64 - description: Unix timestamp representing the creation time of the model. - owned_by: - type: string - description: The entity that owns the model. - example: _ - state: - type: string - enum: - - not_downloaded - - downloaded - - running - - stopped - description: The current state of the model. source_url: type: string format: uri description: URL to the source of the model. - example: https://huggingface.co/TheBloke/zephyr-7B-beta-GGUF/blob/main/zephyr-7b-beta.Q4_K_M.gguf - engine_parameters: - type: object - properties: - pre_prompt: - type: string - description: Predefined prompt used for setting up internal configurations. - default: "" - example: Initial setup complete. - system_prompt: - type: string - description: Prefix used for system-level prompts. - default: "SYSTEM: " - user_prompt: - type: string - description: Prefix used for user prompts. - default: "USER: " - ai_prompt: - type: string - description: Prefix used for assistant prompts. - default: "ASSISTANT: " - ngl: - type: integer - description: Number of neural network layers loaded onto the GPU for - acceleration. - minimum: 0 - maximum: 100 - default: 100 - example: 100 - ctx_len: - type: integer - description: Context length for model operations, varies based on the specific - model. - minimum: 128 - maximum: 4096 - default: 2048 - example: 2048 - n_parallel: - type: integer - description: Number of parallel operations, relevant when continuous batching is - enabled. - minimum: 1 - maximum: 10 - default: 1 - example: 4 - cont_batching: - type: boolean - description: Indicates if continuous batching is used for processing. - default: false - example: false - cpu_threads: - type: integer - description: Number of threads allocated for CPU-based inference. - minimum: 1 - example: 8 - embedding: - type: boolean - description: Indicates if embedding layers are enabled in the model. - default: true - example: true - model_parameters: + example: https://huggingface.co/TheBloke/Mistral-7B-Instruct-v0.2-GGUF/resolve/main/mistral-7b-instruct-v0.2.Q4_K_M.gguf + id: + type: string + description: + Unique identifier used in chat-completions model_name, matches + folder name. + example: mistral-ins-7b-q4 + object: + type: string + example: model + name: + type: string + description: Name of the model. + example: Mistral Instruct 7B Q4 + version: + type: string + default: "1.0" + description: The version number of the model. + description: + type: string + description: Description of the model. + example: Trinity is an experimental model merge using the Slerp method. Recommended for daily assistance purposes. + format: + type: string + description: State format of the model, distinct from the engine. + example: gguf + settings: type: object properties: ctx_len: type: integer - description: Maximum context length the model can handle. - minimum: 0 - maximum: 4096 - default: 2048 - example: 2048 - ngl: - type: integer - description: Number of layers in the neural network. - minimum: 1 - maximum: 100 - default: 100 - example: 100 - embedding: - type: boolean - description: Indicates if embedding layers are used. - default: true - example: true - n_parallel: - type: integer - description: Number of parallel processes the model can run. - minimum: 1 - maximum: 10 - default: 1 - example: 4 + description: Context length. + example: 4096 + prompt_template: + type: string + example: "[INST] {prompt} [/INST]" + additionalProperties: false + parameters: + type: object + properties: temperature: - type: number - description: Controls randomness in model's responses. Higher values lead to - more random responses. - minimum: 0 - maximum: 2 - default: 0.7 example: 0.7 - token_limit: - type: integer - description: Maximum number of tokens the model can generate in a single - response. - minimum: 1 - maximum: 4096 - default: 2048 - example: 2048 - top_k: - type: integer - description: Limits the model to consider only the top k most likely next tokens - at each step. - minimum: 0 - maximum: 100 - default: 0 - example: 0 top_p: - type: number - description: Nucleus sampling parameter. The model considers the smallest set of - tokens whose cumulative probability exceeds the top_p value. - minimum: 0 - maximum: 1 - default: 1 - example: 1 + example: 0.95 + stream: + example: true + max_tokens: + example: 4096 + stop: + example: [] + frequency_penalty: + example: 0 + presence_penalty: + example: 0 + additionalProperties: false metadata: - type: object - properties: - engine: - type: string - description: The engine used by the model. - enum: - - nitro - - openai - - hf_inference - quantization: - type: string - description: Quantization parameter of the model. - example: Q3_K_L - size: - type: string - description: Size of the model. - example: 7B - required: - - id - - object - - created - - owned_by - - state - - source_url - - parameters - - metadata + author: + type: string + example: MistralAI + tags: + example: ["7B", "Featured", "Foundation Model"] + size: + example: 4370000000, + cover: + example: "https://raw.githubusercontent.com/janhq/jan/main/models/mistral-ins-7b-q4/cover.png" + engine: + example: nitro DeleteModelResponse: type: object properties: - id: - type: string - description: The identifier of the model that was deleted. - example: model-zephyr-7B - object: - type: string - description: Type of the object, indicating it's a model. - default: model - deleted: - type: boolean - description: Indicates whether the model was successfully deleted. - example: true + message: + example: Not found StartModelResponse: type: object properties: From ce68ccf178c776dbdcda68132b95539fad93c383 Mon Sep 17 00:00:00 2001 From: hieu-jan <150573299+hieu-jan@users.noreply.github.com> Date: Wed, 24 Jan 2024 01:47:49 +0900 Subject: [PATCH 08/53] docs: change 127.0.0.1 to localhost --- docs/openapi/jan.yaml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/openapi/jan.yaml b/docs/openapi/jan.yaml index 9981f7308..90e6b9945 100644 --- a/docs/openapi/jan.yaml +++ b/docs/openapi/jan.yaml @@ -68,7 +68,7 @@ paths: - lang: cURL source: | curl -X 'POST' \ - 'http://127.0.0.1:1337/v1/chat/completions' \ + 'http://localhost:1337/v1/chat/completions' \ -H 'accept: application/json' \ -H 'Content-Type: application/json' \ -d '{ @@ -115,7 +115,7 @@ paths: - lang: cURL source: | curl -X 'GET' \ - 'http://127.0.0.1:1337/v1/models' \ + 'http://localhost:1337/v1/models' \ -H 'accept: application/json' "/models/download/{model_id}": get: @@ -145,7 +145,7 @@ paths: - lang: cURL source: | curl -X 'GET' \ - 'http://127.0.0.1:1337/v1/models/download/{model_id}' \ + 'http://localhost:1337/v1/models/download/{model_id}' \ -H 'accept: application/json' "/models/{model_id}": get: @@ -178,7 +178,7 @@ paths: - lang: cURL source: | curl -X 'GET' \ - 'http://127.0.0.1:1337/v1/models/{model_id}' \ + 'http://localhost:1337/v1/models/{model_id}' \ -H 'accept: application/json' delete: operationId: deleteModel @@ -209,7 +209,7 @@ paths: - lang: cURL source: | curl -X 'DELETE' \ - 'http://127.0.0.1:1337/v1/models/{model_id}' \ + 'http://localhost:1337/v1/models/{model_id}' \ -H 'accept: application/json' /threads: post: From b2d0add0060b29b61b1f3520a0a578d4c9c33471 Mon Sep 17 00:00:00 2001 From: hieu-jan <150573299+hieu-jan@users.noreply.github.com> Date: Wed, 24 Jan 2024 10:33:44 +0900 Subject: [PATCH 09/53] docs: add more details --- .../01-overview/04-install-and-prerequisites.md | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/docs/docs/developer/01-overview/04-install-and-prerequisites.md b/docs/docs/developer/01-overview/04-install-and-prerequisites.md index 25fe2550e..6d9feeb14 100644 --- a/docs/docs/developer/01-overview/04-install-and-prerequisites.md +++ b/docs/docs/developer/01-overview/04-install-and-prerequisites.md @@ -20,12 +20,19 @@ keywords: ## Requirements +### Hardware Requirements + +Ensure your system meets the following specifications to guarantee a smooth development experience: + - [Hardware Requirements](../../guides/02-installation/06-hardware.md) -- System Requirements: - - [Windows](../../install/windows/#system-requirements) - - [MacOS](../../install/mac/#system-requirements) - - [Linux](../../install/linux/#system-requirements) +### System Requirements + +Make sure your operating system meets the specific requirements for Jan development: + +- [Windows](../../install/windows/#system-requirements) +- [MacOS](../../install/mac/#system-requirements) +- [Linux](../../install/linux/#system-requirements) ## Prerequisites From 89bec2a8a9d5703c065083c540306e7da2bb3831 Mon Sep 17 00:00:00 2001 From: hieu-jan <150573299+hieu-jan@users.noreply.github.com> Date: Wed, 24 Jan 2024 10:44:05 +0900 Subject: [PATCH 10/53] docs: fix typo --- docs/docs/developer/01-overview/04-install-and-prerequisites.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/docs/developer/01-overview/04-install-and-prerequisites.md b/docs/docs/developer/01-overview/04-install-and-prerequisites.md index 6d9feeb14..110f62e36 100644 --- a/docs/docs/developer/01-overview/04-install-and-prerequisites.md +++ b/docs/docs/developer/01-overview/04-install-and-prerequisites.md @@ -50,7 +50,7 @@ cd jan git checkout -b DESIRED_BRANCH ``` -2. **Install Dependencie:s** +2. **Install Dependencies** ```bash yarn install From 45c08597fefadf35d76d187e05578b72a77288ea Mon Sep 17 00:00:00 2001 From: hieu-jan <150573299+hieu-jan@users.noreply.github.com> Date: Wed, 24 Jan 2024 10:52:48 +0900 Subject: [PATCH 11/53] docs: update DeleteModelResponse --- docs/openapi/specs/models.yaml | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/docs/openapi/specs/models.yaml b/docs/openapi/specs/models.yaml index 791d14880..776ffbd6d 100644 --- a/docs/openapi/specs/models.yaml +++ b/docs/openapi/specs/models.yaml @@ -190,8 +190,18 @@ components: DeleteModelResponse: type: object properties: - message: - example: Not found + id: + type: string + description: The identifier of the model that was deleted. + example: mistral-ins-7b-q4 + object: + type: string + description: Type of the object, indicating it's a model. + default: model + deleted: + type: boolean + description: Indicates whether the model was successfully deleted. + example: true StartModelResponse: type: object properties: From b4ffe006fed494f610c3248892c57ec711b6799f Mon Sep 17 00:00:00 2001 From: hieu-jan <150573299+hieu-jan@users.noreply.github.com> Date: Wed, 24 Jan 2024 10:55:24 +0900 Subject: [PATCH 12/53] docs: format .yaml --- docs/openapi/specs/assistants.yaml | 2 +- docs/openapi/specs/chat.yaml | 17 +++++++++++------ docs/openapi/specs/messages.yaml | 2 +- docs/openapi/specs/models.yaml | 2 +- docs/openapi/specs/threads.yaml | 2 +- 5 files changed, 15 insertions(+), 10 deletions(-) diff --git a/docs/openapi/specs/assistants.yaml b/docs/openapi/specs/assistants.yaml index d784c315a..5db1f6a97 100644 --- a/docs/openapi/specs/assistants.yaml +++ b/docs/openapi/specs/assistants.yaml @@ -316,4 +316,4 @@ components: deleted: type: boolean description: Indicates whether the assistant was successfully deleted. - example: true \ No newline at end of file + example: true diff --git a/docs/openapi/specs/chat.yaml b/docs/openapi/specs/chat.yaml index b324501a8..3aeff380f 100644 --- a/docs/openapi/specs/chat.yaml +++ b/docs/openapi/specs/chat.yaml @@ -16,7 +16,8 @@ components: stream: type: boolean default: true - description: Enables continuous output generation, allowing for streaming of + description: + Enables continuous output generation, allowing for streaming of model responses. model: type: string @@ -25,23 +26,27 @@ components: max_tokens: type: number default: 2048 - description: The maximum number of tokens the model will generate in a single + description: + The maximum number of tokens the model will generate in a single response. stop: type: arrays example: - hello - description: Defines specific tokens or phrases at which the model will stop + description: + Defines specific tokens or phrases at which the model will stop generating further output/ frequency_penalty: type: number default: 0 - description: Adjusts the likelihood of the model repeating words or phrases in + description: + Adjusts the likelihood of the model repeating words or phrases in its output. presence_penalty: type: number default: 0 - description: Influences the generation of new and varied concepts in the model's + description: + Influences the generation of new and varied concepts in the model's output. temperature: type: number @@ -188,4 +193,4 @@ components: total_tokens: type: integer example: 533 - description: Total number of tokens used \ No newline at end of file + description: Total number of tokens used diff --git a/docs/openapi/specs/messages.yaml b/docs/openapi/specs/messages.yaml index d9d7d87a4..9a0799f6a 100644 --- a/docs/openapi/specs/messages.yaml +++ b/docs/openapi/specs/messages.yaml @@ -309,4 +309,4 @@ components: data: type: array items: - $ref: "#/components/schemas/MessageFileObject" \ No newline at end of file + $ref: "#/components/schemas/MessageFileObject" diff --git a/docs/openapi/specs/models.yaml b/docs/openapi/specs/models.yaml index 776ffbd6d..21276f899 100644 --- a/docs/openapi/specs/models.yaml +++ b/docs/openapi/specs/models.yaml @@ -96,7 +96,7 @@ components: type: string description: | The identifier of the model. - example: ztrinity-v1.2-7b + example: trinity-v1.2-7b object: type: string description: | diff --git a/docs/openapi/specs/threads.yaml b/docs/openapi/specs/threads.yaml index fe00f7588..825f166f1 100644 --- a/docs/openapi/specs/threads.yaml +++ b/docs/openapi/specs/threads.yaml @@ -224,4 +224,4 @@ components: deleted: type: boolean description: Indicates whether the thread was successfully deleted. - example: true \ No newline at end of file + example: true From 64cabe3d56c6759688cf71555a43b6d469d05818 Mon Sep 17 00:00:00 2001 From: hieu-jan <150573299+hieu-jan@users.noreply.github.com> Date: Wed, 24 Jan 2024 11:37:08 +0900 Subject: [PATCH 13/53] chore: lint .yaml --- docs/openapi/jan.yaml | 42 ++++++++++++++--------------- docs/openapi/specs/chat.yaml | 15 ++++------- docs/openapi/specs/messages.yaml | 45 ++++++++++++++++---------------- docs/openapi/specs/models.yaml | 22 +++++++++++----- docs/openapi/specs/threads.yaml | 2 +- 5 files changed, 66 insertions(+), 60 deletions(-) diff --git a/docs/openapi/jan.yaml b/docs/openapi/jan.yaml index 90e6b9945..864c80fdf 100644 --- a/docs/openapi/jan.yaml +++ b/docs/openapi/jan.yaml @@ -72,27 +72,27 @@ paths: -H 'accept: application/json' \ -H 'Content-Type: application/json' \ -d '{ - "messages": [ - { - "content": "You are a helpful assistant.", - "role": "system" - }, - { - "content": "Hello!", - "role": "user" - } - ], - "model": "tinyllama-1.1b", - "stream": true, - "max_tokens": 2048, - "stop": [ - "hello" - ], - "frequency_penalty": 0, - "presence_penalty": 0, - "temperature": 0.7, - "top_p": 0.95 - }' + "messages": [ + { + "content": "You are a helpful assistant.", + "role": "system" + }, + { + "content": "Hello!", + "role": "user" + } + ], + "model": "tinyllama-1.1b", + "stream": true, + "max_tokens": 2048, + "stop": [ + "hello" + ], + "frequency_penalty": 0, + "presence_penalty": 0, + "temperature": 0.7, + "top_p": 0.95 + }' /models: get: operationId: listModels diff --git a/docs/openapi/specs/chat.yaml b/docs/openapi/specs/chat.yaml index 3aeff380f..cfa391598 100644 --- a/docs/openapi/specs/chat.yaml +++ b/docs/openapi/specs/chat.yaml @@ -16,8 +16,7 @@ components: stream: type: boolean default: true - description: - Enables continuous output generation, allowing for streaming of + description: Enables continuous output generation, allowing for streaming of model responses. model: type: string @@ -26,27 +25,23 @@ components: max_tokens: type: number default: 2048 - description: - The maximum number of tokens the model will generate in a single + description: The maximum number of tokens the model will generate in a single response. stop: type: arrays example: - hello - description: - Defines specific tokens or phrases at which the model will stop + description: Defines specific tokens or phrases at which the model will stop generating further output/ frequency_penalty: type: number default: 0 - description: - Adjusts the likelihood of the model repeating words or phrases in + description: Adjusts the likelihood of the model repeating words or phrases in its output. presence_penalty: type: number default: 0 - description: - Influences the generation of new and varied concepts in the model's + description: Influences the generation of new and varied concepts in the model's output. temperature: type: number diff --git a/docs/openapi/specs/messages.yaml b/docs/openapi/specs/messages.yaml index 9a0799f6a..6f5fe1a58 100644 --- a/docs/openapi/specs/messages.yaml +++ b/docs/openapi/specs/messages.yaml @@ -1,3 +1,4 @@ +--- components: schemas: MessageObject: @@ -75,7 +76,7 @@ components: example: msg_abc123 object: type: string - description: "Type of the object, indicating it's a thread message." + description: Type of the object, indicating it's a thread message. default: thread.message created_at: type: integer @@ -88,7 +89,7 @@ components: example: thread_abc123 role: type: string - description: "Role of the sender, either 'user' or 'assistant'." + description: Role of the sender, either 'user' or 'assistant'. example: user content: type: array @@ -97,7 +98,7 @@ components: properties: type: type: string - description: "Type of content, e.g., 'text'." + description: Type of content, e.g., 'text'. example: text text: type: object @@ -110,21 +111,21 @@ components: type: array items: type: string - description: "Annotations for the text content, if any." + description: Annotations for the text content, if any. example: [] file_ids: type: array items: type: string - description: "Array of file IDs associated with the message, if any." + description: Array of file IDs associated with the message, if any. example: [] assistant_id: type: string - description: "Identifier of the assistant involved in the message, if applicable." + description: Identifier of the assistant involved in the message, if applicable. example: null run_id: type: string - description: "Run ID associated with the message, if applicable." + description: Run ID associated with the message, if applicable. example: null metadata: type: object @@ -139,7 +140,7 @@ components: example: msg_abc123 object: type: string - description: "Type of the object, indicating it's a thread message." + description: Type of the object, indicating it's a thread message. example: thread.message created_at: type: integer @@ -152,7 +153,7 @@ components: example: thread_abc123 role: type: string - description: "Role of the sender, either 'user' or 'assistant'." + description: Role of the sender, either 'user' or 'assistant'. example: user content: type: array @@ -161,7 +162,7 @@ components: properties: type: type: string - description: "Type of content, e.g., 'text'." + description: Type of content, e.g., 'text'. example: text text: type: object @@ -174,21 +175,21 @@ components: type: array items: type: string - description: "Annotations for the text content, if any." + description: Annotations for the text content, if any. example: [] file_ids: type: array items: type: string - description: "Array of file IDs associated with the message, if any." + description: Array of file IDs associated with the message, if any. example: [] assistant_id: type: string - description: "Identifier of the assistant involved in the message, if applicable." + description: Identifier of the assistant involved in the message, if applicable. example: null run_id: type: string - description: "Run ID associated with the message, if applicable." + description: Run ID associated with the message, if applicable. example: null metadata: type: object @@ -199,7 +200,7 @@ components: properties: object: type: string - description: "Type of the object, indicating it's a list." + description: Type of the object, indicating it's a list. default: list data: type: array @@ -226,7 +227,7 @@ components: example: msg_abc123 object: type: string - description: "Type of the object, indicating it's a thread message." + description: Type of the object, indicating it's a thread message. example: thread.message created_at: type: integer @@ -239,7 +240,7 @@ components: example: thread_abc123 role: type: string - description: "Role of the sender, either 'user' or 'assistant'." + description: Role of the sender, either 'user' or 'assistant'. example: user content: type: array @@ -248,7 +249,7 @@ components: properties: type: type: string - description: "Type of content, e.g., 'text'." + description: Type of content, e.g., 'text'. text: type: object properties: @@ -260,20 +261,20 @@ components: type: array items: type: string - description: "Annotations for the text content, if any." + description: Annotations for the text content, if any. file_ids: type: array items: type: string - description: "Array of file IDs associated with the message, if any." + description: Array of file IDs associated with the message, if any. example: [] assistant_id: type: string - description: "Identifier of the assistant involved in the message, if applicable." + description: Identifier of the assistant involved in the message, if applicable. example: null run_id: type: string - description: "Run ID associated with the message, if applicable." + description: Run ID associated with the message, if applicable. example: null metadata: type: object diff --git a/docs/openapi/specs/models.yaml b/docs/openapi/specs/models.yaml index 21276f899..40e6abaaf 100644 --- a/docs/openapi/specs/models.yaml +++ b/docs/openapi/specs/models.yaml @@ -43,7 +43,9 @@ components: description: type: string description: Description of the model. - example: Trinity is an experimental model merge using the Slerp method. Recommended for daily assistance purposes. + example: + Trinity is an experimental model merge using the Slerp method. + Recommended for daily assistance purposes. format: type: string description: State format of the model, distinct from the engine. @@ -82,11 +84,14 @@ components: type: string example: Jan tags: - example: ["7B", "Merged", "Featured"] + example: + - 7B + - Merged + - Featured size: example: 4370000000, cover: - example: "https://raw.githubusercontent.com/janhq/jan/main/models/trinity-v1.2-7b/cover.png" + example: https://raw.githubusercontent.com/janhq/jan/main/models/trinity-v1.2-7b/cover.png engine: example: nitro ModelObject: @@ -141,7 +146,9 @@ components: description: type: string description: Description of the model. - example: Trinity is an experimental model merge using the Slerp method. Recommended for daily assistance purposes. + example: + Trinity is an experimental model merge using the Slerp method. + Recommended for daily assistance purposes. format: type: string description: State format of the model, distinct from the engine. @@ -180,11 +187,14 @@ components: type: string example: MistralAI tags: - example: ["7B", "Featured", "Foundation Model"] + example: + - 7B + - Featured + - Foundation Model size: example: 4370000000, cover: - example: "https://raw.githubusercontent.com/janhq/jan/main/models/mistral-ins-7b-q4/cover.png" + example: https://raw.githubusercontent.com/janhq/jan/main/models/mistral-ins-7b-q4/cover.png engine: example: nitro DeleteModelResponse: diff --git a/docs/openapi/specs/threads.yaml b/docs/openapi/specs/threads.yaml index 825f166f1..40b2463fa 100644 --- a/docs/openapi/specs/threads.yaml +++ b/docs/openapi/specs/threads.yaml @@ -142,7 +142,7 @@ components: example: Jan instructions: type: string - description: | + description: > The instruction of assistant, defaults to "Be my grammar corrector" model: type: object From 49670a84c3d0e96fccb7abe2016f1665972d1231 Mon Sep 17 00:00:00 2001 From: hiento09 <136591877+hiento09@users.noreply.github.com> Date: Thu, 25 Jan 2024 21:49:44 +0700 Subject: [PATCH 14/53] Update 06-unexpected-token.mdx --- docs/docs/guides/08-troubleshooting/06-unexpected-token.mdx | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/docs/docs/guides/08-troubleshooting/06-unexpected-token.mdx b/docs/docs/guides/08-troubleshooting/06-unexpected-token.mdx index 973001f1b..1de609ffa 100644 --- a/docs/docs/guides/08-troubleshooting/06-unexpected-token.mdx +++ b/docs/docs/guides/08-troubleshooting/06-unexpected-token.mdx @@ -17,4 +17,8 @@ keywords: ] --- -1. You may receive an error response `Error occurred: Unexpected token '<', " Date: Fri, 26 Jan 2024 01:09:24 +0900 Subject: [PATCH 15/53] docs: add additional reasons to somethings amiss --- docs/docs/guides/08-troubleshooting/02-somethings-amiss.mdx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/docs/guides/08-troubleshooting/02-somethings-amiss.mdx b/docs/docs/guides/08-troubleshooting/02-somethings-amiss.mdx index a5669e36d..4e16e362a 100644 --- a/docs/docs/guides/08-troubleshooting/02-somethings-amiss.mdx +++ b/docs/docs/guides/08-troubleshooting/02-somethings-amiss.mdx @@ -45,7 +45,9 @@ This may occur due to several reasons. Please follow these steps to resolve it: 5. If you are on Nvidia GPUs, please download [Cuda](https://developer.nvidia.com/cuda-downloads). -6. When [checking app logs](https://jan.ai/troubleshooting/how-to-get-error-logs/), if you encounter the error log `Bind address failed at 127.0.0.1:3928`, it indicates that the port used by Nitro might already be in use. Use the following commands to check the port status: +6. If you're using Linux, please ensure that your system meets the following requirements gcc 11, g++ 11, cpp 11, or higher, refer to this [link](https://jan.ai/guides/troubleshooting/gpu-not-used/#specific-requirements-for-linux) for more information. + +7. When [checking app logs](https://jan.ai/troubleshooting/how-to-get-error-logs/), if you encounter the error log `Bind address failed at 127.0.0.1:3928`, it indicates that the port used by Nitro might already be in use. Use the following commands to check the port status: From d26e97f4a7b13dde738074d41757c1f0dd0be431 Mon Sep 17 00:00:00 2001 From: hieu-jan <150573299+hieu-jan@users.noreply.github.com> Date: Fri, 26 Jan 2024 01:33:45 +0900 Subject: [PATCH 16/53] docs: add OpenAI model list --- .../04-using-models/03-integrate-with-remote-server.mdx | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/docs/docs/guides/04-using-models/03-integrate-with-remote-server.mdx b/docs/docs/guides/04-using-models/03-integrate-with-remote-server.mdx index 533797fca..f0db1bd55 100644 --- a/docs/docs/guides/04-using-models/03-integrate-with-remote-server.mdx +++ b/docs/docs/guides/04-using-models/03-integrate-with-remote-server.mdx @@ -65,6 +65,13 @@ Navigate to the `~/jan/models` folder. Create a folder named `gpt-3.5-turbo-16k` } ``` +:::tip + +- You can find the list of available models in the [OpenAI Platform](https://platform.openai.com/docs/models/overview). +- Please note that the `id` property need to match the model name in the list. For example, if you want to use the [GPT-4 Turbo](https://platform.openai.com/docs/models/gpt-4-and-gpt-4-turbo), you need to set the `id` property as `gpt-4-1106-preview`. + +::: + ### 2. Configure OpenAI API Keys You can find your API keys in the [OpenAI Platform](https://platform.openai.com/api-keys) and set the OpenAI API keys in `~/jan/engines/openai.json` file. From ae23127ddb5a25ce5e55772481fde44e72585d3e Mon Sep 17 00:00:00 2001 From: hieu-jan <150573299+hieu-jan@users.noreply.github.com> Date: Fri, 2 Feb 2024 07:53:01 +0900 Subject: [PATCH 17/53] docs: add trouble 07-undefined-issue --- .../08-troubleshooting/07-undefined-issue.mdx | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 docs/docs/guides/08-troubleshooting/07-undefined-issue.mdx diff --git a/docs/docs/guides/08-troubleshooting/07-undefined-issue.mdx b/docs/docs/guides/08-troubleshooting/07-undefined-issue.mdx new file mode 100644 index 000000000..b45bfd86e --- /dev/null +++ b/docs/docs/guides/08-troubleshooting/07-undefined-issue.mdx @@ -0,0 +1,26 @@ +--- +title: Undefined Issue +slug: /troubleshooting/undefined-issue +description: Undefined issue troubleshooting guide. +keywords: + [ + Jan AI, + Jan, + ChatGPT alternative, + local AI, + private AI, + conversational AI, + no-subscription fee, + large language model, + troubleshooting, + undefined issue, + ] +--- + +You may encounter an "undefined" issue when using Jan. Here are some troubleshooting steps to help you resolve the issue. + +1. Try wiping the Jan folder and reopening the Jan app and see if the issue persists. +2. If the issue persists, try to go `~/jan/extensions/@janhq/inference-nitro-extensions/dist/bin//nitro` and run the nitro manually and see if you get any error messages. +3. Resolve the error messages you get from the nitro and see if the issue persists. +4. Reopen the Jan app and see if the issue is resolved. +5. If the issue persists, please contact us at [Jan Discord](https://discord.gg/mY69SZaMaC). From 8ff04f4db96340546809af7e027a4105abc043c0 Mon Sep 17 00:00:00 2001 From: hieu-jan <150573299+hieu-jan@users.noreply.github.com> Date: Fri, 2 Feb 2024 10:03:39 +0900 Subject: [PATCH 18/53] docs: add app log --- docs/docs/guides/08-troubleshooting/07-undefined-issue.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/docs/guides/08-troubleshooting/07-undefined-issue.mdx b/docs/docs/guides/08-troubleshooting/07-undefined-issue.mdx index b45bfd86e..4aba6438d 100644 --- a/docs/docs/guides/08-troubleshooting/07-undefined-issue.mdx +++ b/docs/docs/guides/08-troubleshooting/07-undefined-issue.mdx @@ -23,4 +23,4 @@ You may encounter an "undefined" issue when using Jan. Here are some troubleshoo 2. If the issue persists, try to go `~/jan/extensions/@janhq/inference-nitro-extensions/dist/bin//nitro` and run the nitro manually and see if you get any error messages. 3. Resolve the error messages you get from the nitro and see if the issue persists. 4. Reopen the Jan app and see if the issue is resolved. -5. If the issue persists, please contact us at [Jan Discord](https://discord.gg/mY69SZaMaC). +5. If the issue persists, please share with us the [app logs](https://jan.ai/troubleshooting/how-to-get-error-logs/) via [Jan Discord](https://discord.gg/mY69SZaMaC). From bef8dcd6d5dcc8fba9e4269d9f67a43690195dbd Mon Sep 17 00:00:00 2001 From: Louis Date: Fri, 2 Feb 2024 10:26:31 +0700 Subject: [PATCH 19/53] fix: load model fail overlays thread message error (#1901) --- web/hooks/useSetActiveThread.ts | 4 ++ web/screens/Chat/ChatBody/index.tsx | 6 +-- .../Chat/LoadModelErrorMessage/index.tsx | 48 ------------------- web/screens/Chat/index.tsx | 5 +- 4 files changed, 7 insertions(+), 56 deletions(-) delete mode 100644 web/screens/Chat/LoadModelErrorMessage/index.tsx diff --git a/web/hooks/useSetActiveThread.ts b/web/hooks/useSetActiveThread.ts index 3545d0d23..f5649ccaf 100644 --- a/web/hooks/useSetActiveThread.ts +++ b/web/hooks/useSetActiveThread.ts @@ -8,6 +8,8 @@ import { import { useAtomValue, useSetAtom } from 'jotai' +import { loadModelErrorAtom } from './useActiveModel' + import { extensionManager } from '@/extension' import { setConvoMessagesAtom } from '@/helpers/atoms/ChatMessage.atom' import { @@ -24,6 +26,7 @@ export default function useSetActiveThread() { const setThreadMessage = useSetAtom(setConvoMessagesAtom) const setThreadModelParams = useSetAtom(setThreadModelParamsAtom) const setIsGeneratingResponse = useSetAtom(isGeneratingResponseAtom) + const setLoadModelError = useSetAtom(loadModelErrorAtom) const setActiveThread = async (thread: Thread) => { if (activeThreadId === thread.id) { @@ -32,6 +35,7 @@ export default function useSetActiveThread() { } setIsGeneratingResponse(false) + setLoadModelError(undefined) events.emit(InferenceEvent.OnInferenceStopped, thread.id) // load the corresponding messages diff --git a/web/screens/Chat/ChatBody/index.tsx b/web/screens/Chat/ChatBody/index.tsx index 1ce6b591f..66f14d076 100644 --- a/web/screens/Chat/ChatBody/index.tsx +++ b/web/screens/Chat/ChatBody/index.tsx @@ -25,7 +25,6 @@ const ChatBody: React.FC = () => { const messages = useAtomValue(getCurrentChatMessagesAtom) const { downloadedModels } = useGetDownloadedModels() const { setMainViewState } = useMainViewState() - const loadModelError = useAtomValue(loadModelErrorAtom) if (downloadedModels.length === 0) return ( @@ -86,9 +85,8 @@ const ChatBody: React.FC = () => { message.content.length > 0) && ( )} - {!loadModelError && - (message.status === MessageStatus.Error || - message.status === MessageStatus.Stopped) && + {(message.status === MessageStatus.Error || + message.status === MessageStatus.Stopped) && index === messages.length - 1 && ( )} diff --git a/web/screens/Chat/LoadModelErrorMessage/index.tsx b/web/screens/Chat/LoadModelErrorMessage/index.tsx deleted file mode 100644 index d3c4a704d..000000000 --- a/web/screens/Chat/LoadModelErrorMessage/index.tsx +++ /dev/null @@ -1,48 +0,0 @@ -import { MessageStatus, ThreadMessage } from '@janhq/core' -import { useAtomValue } from 'jotai' - -import { useActiveModel } from '@/hooks/useActiveModel' - -import { totalRamAtom } from '@/helpers/atoms/SystemBar.atom' - -const LoadModelErrorMessage = () => { - const { activeModel } = useActiveModel() - const availableRam = useAtomValue(totalRamAtom) - - return ( - <> -
- - {Number(activeModel?.metadata.size) > availableRam ? ( - <> - Oops! Model size exceeds available RAM. Consider selecting a - smaller model or upgrading your RAM for smoother performance. - - ) : ( - <> -

Apologies, something's amiss!

- Jan's in beta. Find troubleshooting guides{' '} - - here - {' '} - or reach out to us on{' '} - - Discord - {' '} - for assistance. - - )} -
-
- - ) -} -export default LoadModelErrorMessage diff --git a/web/screens/Chat/index.tsx b/web/screens/Chat/index.tsx index 1f7896604..e3eedb6c1 100644 --- a/web/screens/Chat/index.tsx +++ b/web/screens/Chat/index.tsx @@ -20,7 +20,7 @@ import { snackbar } from '@/containers/Toast' import { FeatureToggleContext } from '@/context/FeatureToggle' -import { activeModelAtom, loadModelErrorAtom } from '@/hooks/useActiveModel' +import { activeModelAtom } from '@/hooks/useActiveModel' import { queuedMessageAtom, reloadModelAtom } from '@/hooks/useSendChatMessage' import ChatBody from '@/screens/Chat/ChatBody' @@ -28,7 +28,6 @@ import ChatBody from '@/screens/Chat/ChatBody' import ThreadList from '@/screens/Chat/ThreadList' import ChatInput from './ChatInput' -import LoadModelErrorMessage from './LoadModelErrorMessage' import RequestDownloadModel from './RequestDownloadModel' import Sidebar from './Sidebar' @@ -70,7 +69,6 @@ const ChatScreen: React.FC = () => { const activeModel = useAtomValue(activeModelAtom) const isGeneratingResponse = useAtomValue(isGeneratingResponseAtom) - const loadModelError = useAtomValue(loadModelErrorAtom) const { getRootProps, isDragReject } = useDropzone({ noClick: true, @@ -213,7 +211,6 @@ const ChatScreen: React.FC = () => { )} {activeModel && isGeneratingResponse && } - {loadModelError && } From ce63e1805e403d83f746e60912455e1f5c9b33e2 Mon Sep 17 00:00:00 2001 From: hieu-jan <150573299+hieu-jan@users.noreply.github.com> Date: Fri, 2 Feb 2024 15:03:18 +0900 Subject: [PATCH 20/53] docs: update what is tracked --- docs/docs/about/01-README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/docs/about/01-README.md b/docs/docs/about/01-README.md index 3b2759513..c69a35751 100644 --- a/docs/docs/about/01-README.md +++ b/docs/docs/about/01-README.md @@ -111,8 +111,9 @@ Adhering to Jan's privacy preserving philosophy, our analytics philosophy is to #### What is tracked 1. By default, Github tracks downloads and device metadata for all public Github repos. This helps us troubleshoot & ensure cross platform support. -1. We use Posthog to track a single `app.opened` event without additional user metadata, in order to understand retention. -1. Additionally, we plan to enable a `Settings` feature for users to turn off all tracking. +2. We use [Umami](https://umami.is/) to collect, analyze, and understand application data while maintaining visitor privacy and data ownership. We are using the Umami Cloud in Europe to ensure GDPR compliant. Please see [Umami Privacy Policy](https://umami.is/privacy) for more details. +3. We use Umami to track a single `app.opened` event without additional user metadata, in order to understand retention. In addition, we track `app.event` to understand app version usage. +4. Additionally, we plan to enable a `Settings` feature for users to turn off all tracking. #### Request for help From f7c591eca994d0719e0c94bfc0fffdfc7d494722 Mon Sep 17 00:00:00 2001 From: hieu-jan <150573299+hieu-jan@users.noreply.github.com> Date: Fri, 2 Feb 2024 15:08:39 +0900 Subject: [PATCH 21/53] docs: fix typo --- docs/docs/about/01-README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/docs/about/01-README.md b/docs/docs/about/01-README.md index c69a35751..d5d3b8dc2 100644 --- a/docs/docs/about/01-README.md +++ b/docs/docs/about/01-README.md @@ -110,8 +110,8 @@ Adhering to Jan's privacy preserving philosophy, our analytics philosophy is to #### What is tracked -1. By default, Github tracks downloads and device metadata for all public Github repos. This helps us troubleshoot & ensure cross platform support. -2. We use [Umami](https://umami.is/) to collect, analyze, and understand application data while maintaining visitor privacy and data ownership. We are using the Umami Cloud in Europe to ensure GDPR compliant. Please see [Umami Privacy Policy](https://umami.is/privacy) for more details. +1. By default, Github tracks downloads and device metadata for all public GitHub repositories. This helps us troubleshoot & ensure cross-platform support. +2. We use [Umami](https://umami.is/) to collect, analyze, and understand application data while maintaining visitor privacy and data ownership. We are using the Umami Cloud in Europe to ensure GDPR compliance. Please see [Umami Privacy Policy](https://umami.is/privacy) for more details. 3. We use Umami to track a single `app.opened` event without additional user metadata, in order to understand retention. In addition, we track `app.event` to understand app version usage. 4. Additionally, we plan to enable a `Settings` feature for users to turn off all tracking. From eaa3053d404f9926728e0da9c239853669027c87 Mon Sep 17 00:00:00 2001 From: Louis Date: Fri, 2 Feb 2024 13:28:21 +0700 Subject: [PATCH 22/53] fix: openAIEmbedding now requires top level API Key configuration (#1902) * fix: openAIEmbedding now requires top level API Key configuration * chore: typo --- .../assistant-extension/src/node/tools/retrieval/index.ts | 8 +++----- extensions/inference-nitro-extension/src/index.ts | 3 --- web/screens/Settings/Advanced/index.tsx | 4 ++-- 3 files changed, 5 insertions(+), 10 deletions(-) diff --git a/extensions/assistant-extension/src/node/tools/retrieval/index.ts b/extensions/assistant-extension/src/node/tools/retrieval/index.ts index cd7e9abb1..8c7a6aa2b 100644 --- a/extensions/assistant-extension/src/node/tools/retrieval/index.ts +++ b/extensions/assistant-extension/src/node/tools/retrieval/index.ts @@ -35,21 +35,19 @@ export class Retrieval { if (engine === "nitro") { this.embeddingModel = new OpenAIEmbeddings( { openAIApiKey: "nitro-embedding" }, - { basePath: "http://127.0.0.1:3928/v1" } + { basePath: "http://127.0.0.1:3928/v1" }, ); } else { // Fallback to OpenAI Settings this.embeddingModel = new OpenAIEmbeddings({ - configuration: { - apiKey: settings.api_key, - }, + openAIApiKey: settings.api_key, }); } } public ingestAgentKnowledge = async ( filePath: string, - memoryPath: string + memoryPath: string, ): Promise => { const loader = new PDFLoader(filePath, { splitPages: true, diff --git a/extensions/inference-nitro-extension/src/index.ts b/extensions/inference-nitro-extension/src/index.ts index 9f1f00263..2b0021ba0 100644 --- a/extensions/inference-nitro-extension/src/index.ts +++ b/extensions/inference-nitro-extension/src/index.ts @@ -226,9 +226,6 @@ export default class JanInferenceNitroExtension extends InferenceExtension { */ private async onMessageRequest(data: MessageRequest) { if (data.model?.engine !== InferenceEngine.nitro || !this._currentModel) { - console.log( - `Model is not nitro or no model loaded ${data.model?.engine} ${this._currentModel}` - ); return; } diff --git a/web/screens/Settings/Advanced/index.tsx b/web/screens/Settings/Advanced/index.tsx index 109431515..d2f7d81ee 100644 --- a/web/screens/Settings/Advanced/index.tsx +++ b/web/screens/Settings/Advanced/index.tsx @@ -109,10 +109,10 @@ const Advanced = () => {
-
NVidia GPU
+
Nvidia GPU

- Enable GPU acceleration for NVidia GPUs. + Enable GPU acceleration for Nvidia GPUs.

Date: Sat, 3 Feb 2024 15:20:42 +0700 Subject: [PATCH 23/53] fix: broken manual import model with NA fields (#1912) --- extensions/model-extension/src/index.ts | 31 +++++++++++++++++-------- 1 file changed, 21 insertions(+), 10 deletions(-) diff --git a/extensions/model-extension/src/index.ts b/extensions/model-extension/src/index.ts index 5640177a0..b9fa7731e 100644 --- a/extensions/model-extension/src/index.ts +++ b/extensions/model-extension/src/index.ts @@ -286,6 +286,7 @@ export default class JanModelExtension extends ModelExtension { * model.json file associated with it. * * This function will create a model.json file for the model. + * It works only with single binary file model. * * @param dirName the director which reside in ~/jan/models but does not have model.json file. */ @@ -302,15 +303,14 @@ export default class JanModelExtension extends ModelExtension { let binaryFileSize: number | undefined = undefined for (const file of files) { - if (file.endsWith(JanModelExtension._incompletedModelFileName)) continue - if (file.endsWith('.json')) continue - - const path = await joinPath([JanModelExtension._homeDir, dirName, file]) - const fileStats = await fs.fileStat(path) - if (fileStats.isDirectory) continue - binaryFileSize = fileStats.size - binaryFileName = file - break + if (file.endsWith(JanModelExtension._supportedModelFormat)) { + const path = await joinPath([JanModelExtension._homeDir, dirName, file]) + const fileStats = await fs.fileStat(path) + if (fileStats.isDirectory) continue + binaryFileSize = fileStats.size + binaryFileName = file + break + } } if (!binaryFileName) { @@ -318,7 +318,7 @@ export default class JanModelExtension extends ModelExtension { return } - const defaultModel = await this.getDefaultModel() + const defaultModel = await this.getDefaultModel() as Model if (!defaultModel) { console.error('Unable to find default model') return @@ -326,8 +326,19 @@ export default class JanModelExtension extends ModelExtension { const model: Model = { ...defaultModel, + // Overwrite default N/A fields id: dirName, name: dirName, + sources: [ + { + url: binaryFileName, + filename: binaryFileName, + }, + ], + settings: { + ...defaultModel.settings, + llama_model_path: binaryFileName, + }, created: Date.now(), description: `${dirName} - user self import model`, metadata: { From 6ea7d8f6cf985b4811d7f27d3b92dad9db3da350 Mon Sep 17 00:00:00 2001 From: Louis Date: Sat, 3 Feb 2024 16:45:34 +0700 Subject: [PATCH 24/53] fix: migration loading indicator (#1913) --- web/containers/Providers/index.tsx | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/web/containers/Providers/index.tsx b/web/containers/Providers/index.tsx index 895c22177..c8a20bca7 100644 --- a/web/containers/Providers/index.tsx +++ b/web/containers/Providers/index.tsx @@ -21,6 +21,8 @@ import { import Umami from '@/utils/umami' +import Loader from '../Loader' + import KeyListener from './KeyListener' import { extensionManager } from '@/extension' @@ -30,6 +32,7 @@ const Providers = (props: PropsWithChildren) => { const [setupCore, setSetupCore] = useState(false) const [activated, setActivated] = useState(false) + const [settingUp, setSettingUp] = useState(false) async function setupExtensions() { // Register all active extensions @@ -37,11 +40,13 @@ const Providers = (props: PropsWithChildren) => { setTimeout(async () => { if (!isCoreExtensionInstalled()) { - setupBaseExtensions() + setSettingUp(true) + await setupBaseExtensions() return } extensionManager.load() + setSettingUp(false) setActivated(true) }, 500) } @@ -71,6 +76,7 @@ const Providers = (props: PropsWithChildren) => { + {settingUp && } {setupCore && activated && ( From 4f751338b7810573dbb0b29d6292386bf6dc637b Mon Sep 17 00:00:00 2001 From: hiento09 <136591877+hiento09@users.noreply.github.com> Date: Sun, 4 Feb 2024 15:44:57 +0700 Subject: [PATCH 25/53] Regression fix assitant extension codesign (#1918) * Separated build macos arm64 and x86_x64 * Update dependencies jobs name * Remove arch from electron-builder command for mac * Add combine latest-mac.yml jobs * Remove unuse workflow job * Add prefix to install js-yaml * Run merge script from /tmp * workflow update release draft depend on combile-latest-mac * correct template path * upload assert release latest-mac.yml overwrite set to true * upload assert release latest-mac.yml overwrite set to true * Grant permission for combine latest-mac jobs --------- Co-authored-by: Hien To --- .github/scripts/auto-sign.sh | 2 + .../workflows/jan-electron-build-nightly.yml | 62 ++++++- .github/workflows/jan-electron-build.yml | 84 ++++++++- .../workflows/template-build-macos-arm64.yml | 160 ++++++++++++++++++ ...macos.yml => template-build-macos-x64.yml} | 5 +- electron/merge-latest-ymls.js | 27 +++ electron/package.json | 4 +- 7 files changed, 331 insertions(+), 13 deletions(-) create mode 100644 .github/workflows/template-build-macos-arm64.yml rename .github/workflows/{template-build-macos.yml => template-build-macos-x64.yml} (97%) create mode 100644 electron/merge-latest-ymls.js diff --git a/.github/scripts/auto-sign.sh b/.github/scripts/auto-sign.sh index 5e6ef9750..a2130e791 100755 --- a/.github/scripts/auto-sign.sh +++ b/.github/scripts/auto-sign.sh @@ -8,3 +8,5 @@ fi # If both variables are set, execute the following commands find "$APP_PATH" \( -type f -perm +111 -o -name "*.node" \) -exec codesign -s "$DEVELOPER_ID" --options=runtime {} \; + +find "$APP_PATH" -type f -name "*.o" -exec codesign -s "$DEVELOPER_ID" --options=runtime {} \; diff --git a/.github/workflows/jan-electron-build-nightly.yml b/.github/workflows/jan-electron-build-nightly.yml index cad2ac227..bc32f9ccc 100644 --- a/.github/workflows/jan-electron-build-nightly.yml +++ b/.github/workflows/jan-electron-build-nightly.yml @@ -48,8 +48,17 @@ jobs: get-update-version: uses: ./.github/workflows/template-get-update-version.yml - build-macos: - uses: ./.github/workflows/template-build-macos.yml + build-macos-x64: + uses: ./.github/workflows/template-build-macos-x64.yml + needs: [get-update-version, set-public-provider] + secrets: inherit + with: + ref: ${{ needs.set-public-provider.outputs.ref }} + public_provider: ${{ needs.set-public-provider.outputs.public_provider }} + new_version: ${{ needs.get-update-version.outputs.new_version }} + + build-macos-arm64: + uses: ./.github/workflows/template-build-macos-arm64.yml needs: [get-update-version, set-public-provider] secrets: inherit with: @@ -76,8 +85,51 @@ jobs: public_provider: ${{ needs.set-public-provider.outputs.public_provider }} new_version: ${{ needs.get-update-version.outputs.new_version }} + combine-latest-mac-yml: + needs: [set-public-provider, build-macos-x64, build-macos-arm64] + runs-on: ubuntu-latest + steps: + - name: Getting the repo + uses: actions/checkout@v3 + with: + ref: ${{ needs.set-public-provider.outputs.ref }} + - name: Download mac-x64 artifacts + uses: actions/download-artifact@v3 + with: + name: latest-mac-x64 + path: ./latest-mac-x64 + - name: Download mac-arm artifacts + uses: actions/download-artifact@v3 + with: + name: latest-mac-arm64 + path: ./latest-mac-arm64 + + - name: 'Merge latest-mac.yml' + # unfortunately electron-builder doesn't understand that we have two different releases for mac-x64 and mac-arm, so we need to manually merge the latest files + # see https://github.com/electron-userland/electron-builder/issues/5592 + run: | + ls -la . + ls -la ./latest-mac-x64 + ls -la ./latest-mac-arm64 + ls -la ./electron + cp ./electron/merge-latest-ymls.js /tmp/merge-latest-ymls.js + npm install js-yaml --prefix /tmp + node /tmp/merge-latest-ymls.js ./latest-mac-x64/latest-mac.yml ./latest-mac-arm64/latest-mac.yml ./latest-mac.yml + cat ./latest-mac.yml + + - name: Upload latest-mac.yml + if: ${{ needs.set-public-provider.outputs.public_provider == 'cloudflare-r2' }} + run: | + aws s3api put-object --endpoint-url https://${{ secrets.CLOUDFLARE_ACCOUNT_ID }}.r2.cloudflarestorage.com --bucket ${{ secrets.CLOUDFLARE_R2_BUCKET_NAME }} --key "latest/latest-mac.yml" --body "./latest-mac.yml" + env: + AWS_ACCESS_KEY_ID: ${{ secrets.CLOUDFLARE_R2_ACCESS_KEY_ID }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.CLOUDFLARE_R2_SECRET_ACCESS_KEY }} + AWS_DEFAULT_REGION: auto + AWS_EC2_METADATA_DISABLED: "true" + + noti-discord-nightly-and-update-url-readme: - needs: [build-macos, build-windows-x64, build-linux-x64, get-update-version, set-public-provider] + needs: [build-macos-x64, build-macos-arm64, build-windows-x64, build-linux-x64, get-update-version, set-public-provider, combine-latest-mac-yml] secrets: inherit if: github.event_name == 'schedule' uses: ./.github/workflows/template-noti-discord-and-update-url-readme.yml @@ -88,7 +140,7 @@ jobs: new_version: ${{ needs.get-update-version.outputs.new_version }} noti-discord-pre-release-and-update-url-readme: - needs: [build-macos, build-windows-x64, build-linux-x64, get-update-version, set-public-provider] + needs: [build-macos-x64, build-macos-arm64, build-windows-x64, build-linux-x64, get-update-version, set-public-provider, combine-latest-mac-yml] secrets: inherit if: github.event_name == 'push' uses: ./.github/workflows/template-noti-discord-and-update-url-readme.yml @@ -99,7 +151,7 @@ jobs: new_version: ${{ needs.get-update-version.outputs.new_version }} noti-discord-manual-and-update-url-readme: - needs: [build-macos, build-windows-x64, build-linux-x64, get-update-version, set-public-provider] + needs: [build-macos-x64, build-macos-arm64, build-windows-x64, build-linux-x64, get-update-version, set-public-provider, combine-latest-mac-yml] secrets: inherit if: github.event_name == 'workflow_dispatch' && github.event.inputs.public_provider == 'cloudflare-r2' uses: ./.github/workflows/template-noti-discord-and-update-url-readme.yml diff --git a/.github/workflows/jan-electron-build.yml b/.github/workflows/jan-electron-build.yml index 20102447b..89e130bbd 100644 --- a/.github/workflows/jan-electron-build.yml +++ b/.github/workflows/jan-electron-build.yml @@ -9,8 +9,42 @@ jobs: get-update-version: uses: ./.github/workflows/template-get-update-version.yml - build-macos: - uses: ./.github/workflows/template-build-macos.yml + create-draft-release: + runs-on: ubuntu-latest + if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/') + outputs: + upload_url: ${{ steps.create_release.outputs.upload_url }} + version: ${{ steps.get_version.outputs.version }} + permissions: + contents: write + steps: + - name: Extract tag name without v prefix + id: get_version + run: echo "VERSION=${GITHUB_REF#refs/tags/v}" >> $GITHUB_ENV && echo "::set-output name=version::${GITHUB_REF#refs/tags/v}" + env: + GITHUB_REF: ${{ github.ref }} + - name: Create Draft Release + id: create_release + uses: actions/create-release@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + tag_name: ${{ github.ref_name }} + release_name: "${{ env.VERSION }}" + draft: true + prerelease: false + + build-macos-x64: + uses: ./.github/workflows/template-build-macos-x64.yml + secrets: inherit + needs: [get-update-version] + with: + ref: ${{ github.ref }} + public_provider: github + new_version: ${{ needs.get-update-version.outputs.new_version }} + + build-macos-arm64: + uses: ./.github/workflows/template-build-macos-arm64.yml secrets: inherit needs: [get-update-version] with: @@ -36,8 +70,52 @@ jobs: public_provider: github new_version: ${{ needs.get-update-version.outputs.new_version }} + combine-latest-mac-yml: + needs: [build-macos-x64, build-macos-arm64, create-draft-release] + runs-on: ubuntu-latest + permissions: + contents: write + steps: + - name: Getting the repo + uses: actions/checkout@v3 + + - name: Download mac-x64 artifacts + uses: actions/download-artifact@v3 + with: + name: latest-mac-x64 + path: ./latest-mac-x64 + - name: Download mac-arm artifacts + uses: actions/download-artifact@v3 + with: + name: latest-mac-arm64 + path: ./latest-mac-arm64 + + - name: 'Merge latest-mac.yml' + # unfortunately electron-builder doesn't understand that we have two different releases for mac-x64 and mac-arm, so we need to manually merge the latest files + # see https://github.com/electron-userland/electron-builder/issues/5592 + run: | + ls -la . + ls -la ./latest-mac-x64 + ls -la ./latest-mac-arm64 + ls -la ./electron + cp ./electron/merge-latest-ymls.js /tmp/merge-latest-ymls.js + npm install js-yaml --prefix /tmp + node /tmp/merge-latest-ymls.js ./latest-mac-x64/latest-mac.yml ./latest-mac-arm64/latest-mac.yml ./latest-mac.yml + cat ./latest-mac.yml + + - name: Yet Another Upload Release Asset Action + uses: shogo82148/actions-upload-release-asset@v1.7.2 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ needs.create-draft-release.outputs.upload_url }} + asset_path: ./latest-mac.yml + asset_name: latest-mac.yml + asset_content_type: text/yaml + overwrite: true + update_release_draft: - needs: [build-macos, build-windows-x64, build-linux-x64] + needs: [build-macos-x64, build-macos-arm64, build-windows-x64, build-linux-x64, combine-latest-mac-yml] permissions: # write permission is required to create a github release contents: write diff --git a/.github/workflows/template-build-macos-arm64.yml b/.github/workflows/template-build-macos-arm64.yml new file mode 100644 index 000000000..54355d55c --- /dev/null +++ b/.github/workflows/template-build-macos-arm64.yml @@ -0,0 +1,160 @@ +name: build-macos +on: + workflow_call: + inputs: + ref: + required: true + type: string + default: 'refs/heads/main' + public_provider: + required: true + type: string + default: none + description: 'none: build only, github: build and publish to github, cloudflare: build and publish to cloudflare' + new_version: + required: true + type: string + default: '' + cloudflare_r2_path: + required: false + type: string + default: '/latest/' + secrets: + CLOUDFLARE_R2_BUCKET_NAME: + required: false + CLOUDFLARE_R2_ACCESS_KEY_ID: + required: false + CLOUDFLARE_R2_SECRET_ACCESS_KEY: + required: false + CLOUDFLARE_ACCOUNT_ID: + required: false + CODE_SIGN_P12_BASE64: + required: false + CODE_SIGN_P12_PASSWORD: + required: false + APPLE_ID: + required: false + APPLE_APP_SPECIFIC_PASSWORD: + required: false + DEVELOPER_ID: + required: false + +jobs: + build-macos: + runs-on: macos-silicon + environment: production + permissions: + contents: write + steps: + - name: Getting the repo + uses: actions/checkout@v3 + with: + ref: ${{ inputs.ref }} + + - name: Installing node + uses: actions/setup-node@v1 + with: + node-version: 20 + - name: Unblock keychain + run: | + security unlock-keychain -p ${{ secrets.KEYCHAIN_PASSWORD }} ~/Library/Keychains/login.keychain-db + # - uses: actions/setup-python@v5 + # with: + # python-version: '3.11' + + # - name: Install jq + # uses: dcarbone/install-jq-action@v2.0.1 + + - name: Update app version based on latest release tag with build number + if: inputs.public_provider != 'github' + run: | + echo "Version: ${{ inputs.new_version }}" + # Update the version in electron/package.json + jq --arg version "${{ inputs.new_version }}" '.version = $version' electron/package.json > /tmp/package.json + mv /tmp/package.json electron/package.json + + jq --arg version "${{ inputs.new_version }}" '.version = $version' web/package.json > /tmp/package.json + mv /tmp/package.json web/package.json + + jq '.build.publish = [{"provider": "generic", "url": "${{ secrets.CLOUDFLARE_R2_PUBLIC_URL }}", "channel": "latest"}, {"provider": "s3", "bucket": "${{ secrets.CLOUDFLARE_R2_BUCKET_NAME }}", "region": "auto", "endpoint": "https://${{ secrets.CLOUDFLARE_ACCOUNT_ID }}.r2.cloudflarestorage.com", "path": "${{ inputs.cloudflare_r2_path }}", "channel": "latest"}]' electron/package.json > /tmp/package.json + mv /tmp/package.json electron/package.json + cat electron/package.json + + - name: Update app version base on tag + if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/') && inputs.public_provider == 'github' + run: | + if [[ ! "${VERSION_TAG}" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then + echo "Error: Tag is not valid!" + exit 1 + fi + jq --arg version "${VERSION_TAG#v}" '.version = $version' electron/package.json > /tmp/package.json + mv /tmp/package.json electron/package.json + jq --arg version "${VERSION_TAG#v}" '.version = $version' web/package.json > /tmp/package.json + mv /tmp/package.json web/package.json + env: + VERSION_TAG: ${{ inputs.new_version }} + + # - name: Get Cer for code signing + # run: base64 -d <<< "$CODE_SIGN_P12_BASE64" > /tmp/codesign.p12 + # shell: bash + # env: + # CODE_SIGN_P12_BASE64: ${{ secrets.CODE_SIGN_P12_BASE64 }} + + # - uses: apple-actions/import-codesign-certs@v2 + # continue-on-error: true + # with: + # p12-file-base64: ${{ secrets.CODE_SIGN_P12_BASE64 }} + # p12-password: ${{ secrets.CODE_SIGN_P12_PASSWORD }} + + - name: Build and publish app to cloudflare r2 or github artifactory + if: inputs.public_provider != 'github' + run: | + # check public_provider is true or not + echo "public_provider is ${{ inputs.public_provider }}" + if [ "${{ inputs.public_provider }}" == "none" ]; then + make build + else + make build-and-publish + fi + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + # CSC_LINK: "/tmp/codesign.p12" + # CSC_KEY_PASSWORD: ${{ secrets.CODE_SIGN_P12_PASSWORD }} + # CSC_IDENTITY_AUTO_DISCOVERY: "true" + APPLE_ID: ${{ secrets.APPLE_ID }} + APPLE_APP_SPECIFIC_PASSWORD: ${{ secrets.APPLE_APP_SPECIFIC_PASSWORD }} + APP_PATH: "." + DEVELOPER_ID: ${{ secrets.DEVELOPER_ID }} + AWS_ACCESS_KEY_ID: ${{ secrets.CLOUDFLARE_R2_ACCESS_KEY_ID }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.CLOUDFLARE_R2_SECRET_ACCESS_KEY }} + AWS_DEFAULT_REGION: auto + AWS_EC2_METADATA_DISABLED: "true" + + - name: Build and publish app to github + if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/') && inputs.public_provider == 'github' + run: | + make build-and-publish + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + # CSC_LINK: "/tmp/codesign.p12" + # CSC_KEY_PASSWORD: ${{ secrets.CODE_SIGN_P12_PASSWORD }} + # CSC_IDENTITY_AUTO_DISCOVERY: "true" + APPLE_ID: ${{ secrets.APPLE_ID }} + APPLE_APP_SPECIFIC_PASSWORD: ${{ secrets.APPLE_APP_SPECIFIC_PASSWORD }} + APP_PATH: "." + DEVELOPER_ID: ${{ secrets.DEVELOPER_ID }} + ANALYTICS_ID: ${{ secrets.JAN_APP_UMAMI_PROJECT_API_KEY }} + ANALYTICS_HOST: ${{ secrets.JAN_APP_UMAMI_URL }} + + - name: Upload Artifact + if: inputs.public_provider != 'github' + uses: actions/upload-artifact@v2 + with: + name: jan-mac-arm64-${{ inputs.new_version }} + path: ./electron/dist/jan-mac-arm64-${{ inputs.new_version }}.dmg + + - name: Upload Artifact + uses: actions/upload-artifact@v2 + with: + name: latest-mac-arm64 + path: ./electron/dist/latest-mac.yml \ No newline at end of file diff --git a/.github/workflows/template-build-macos.yml b/.github/workflows/template-build-macos-x64.yml similarity index 97% rename from .github/workflows/template-build-macos.yml rename to .github/workflows/template-build-macos-x64.yml index 0ad1d3a6a..e313c2947 100644 --- a/.github/workflows/template-build-macos.yml +++ b/.github/workflows/template-build-macos-x64.yml @@ -148,9 +148,8 @@ jobs: path: ./electron/dist/jan-mac-x64-${{ inputs.new_version }}.dmg - name: Upload Artifact - if: inputs.public_provider != 'github' uses: actions/upload-artifact@v2 with: - name: jan-mac-arm64-${{ inputs.new_version }} - path: ./electron/dist/jan-mac-arm64-${{ inputs.new_version }}.dmg + name: latest-mac-x64 + path: ./electron/dist/latest-mac.yml diff --git a/electron/merge-latest-ymls.js b/electron/merge-latest-ymls.js new file mode 100644 index 000000000..8172a3176 --- /dev/null +++ b/electron/merge-latest-ymls.js @@ -0,0 +1,27 @@ +const yaml = require('js-yaml') +const fs = require('fs') + +// get two file paths from arguments: +const [, , ...args] = process.argv +const file1 = args[0] +const file2 = args[1] +const file3 = args[2] + +// check that all arguments are present and throw error instead +if (!file1 || !file2 || !file3) { + throw new Error('Please provide 3 file paths as arguments: path to file1, to file2 and destination path') +} + +const doc1 = yaml.load(fs.readFileSync(file1, 'utf8')) +console.log('doc1: ', doc1) + +const doc2 = yaml.load(fs.readFileSync(file2, 'utf8')) +console.log('doc2: ', doc2) + +const merged = { ...doc1, ...doc2 } +merged.files.push(...doc1.files) + +console.log('merged', merged) + +const mergedYml = yaml.dump(merged) +fs.writeFileSync(file3, mergedYml, 'utf8') diff --git a/electron/package.json b/electron/package.json index 2892fedc6..08f15b262 100644 --- a/electron/package.json +++ b/electron/package.json @@ -63,11 +63,11 @@ "build:test:darwin": "tsc -p . && electron-builder -p never -m --dir", "build:test:win32": "tsc -p . && electron-builder -p never -w --dir", "build:test:linux": "tsc -p . && electron-builder -p never -l --dir", - "build:darwin": "tsc -p . && electron-builder -p never -m --x64 --arm64", + "build:darwin": "tsc -p . && electron-builder -p never -m", "build:win32": "tsc -p . && electron-builder -p never -w", "build:linux": "tsc -p . && electron-builder -p never -l deb -l AppImage", "build:publish": "run-script-os", - "build:publish:darwin": "tsc -p . && electron-builder -p always -m --x64 --arm64", + "build:publish:darwin": "tsc -p . && electron-builder -p always -m", "build:publish:win32": "tsc -p . && electron-builder -p always -w", "build:publish:linux": "tsc -p . && electron-builder -p always -l deb -l AppImage" }, From ccbe18e5b8417fb44dc8e130782c7da60c348ee8 Mon Sep 17 00:00:00 2001 From: Service Account Date: Mon, 5 Feb 2024 03:29:06 +0000 Subject: [PATCH 26/53] Update README.md with Stable Download URLs --- README.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 02a5aa6e9..e1a06820e 100644 --- a/README.md +++ b/README.md @@ -43,31 +43,31 @@ Jan is an open-source ChatGPT alternative that runs 100% offline on your compute Stable (Recommended) - + jan.exe - + Intel - + M1/M2 - + jan.deb - + jan.AppImage From 0b7e634855a7d7820860026a64bde0af2d841210 Mon Sep 17 00:00:00 2001 From: Faisal Amir Date: Mon, 5 Feb 2024 11:04:50 +0700 Subject: [PATCH 27/53] fix: download model will close panel item hub --- .../ExploreModels/ExploreModelItemHeader/index.tsx | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/web/screens/ExploreModels/ExploreModelItemHeader/index.tsx b/web/screens/ExploreModels/ExploreModelItemHeader/index.tsx index 3ffe2cbac..378b9ffa8 100644 --- a/web/screens/ExploreModels/ExploreModelItemHeader/index.tsx +++ b/web/screens/ExploreModels/ExploreModelItemHeader/index.tsx @@ -66,7 +66,15 @@ const ExploreModelItemHeader: React.FC = ({ model, onClick, open }) => { const isDownloaded = downloadedModels.find((md) => md.id === model.id) != null let downloadButton = ( - + ) const onUseModelClick = useCallback(async () => { From 01fec497988f10b25f8f4ea18deb2d875eeb57a5 Mon Sep 17 00:00:00 2001 From: NamH Date: Mon, 5 Feb 2024 13:13:39 +0700 Subject: [PATCH 28/53] fix: reduce the number of api call (#1896) Signed-off-by: James Co-authored-by: James --- core/src/node/api/routes/common.ts | 11 +- core/src/node/api/routes/download.ts | 68 ++--- .../conversational-extension/src/index.ts | 43 ++-- web/containers/Layout/BottomBar/index.tsx | 6 +- .../CommandListDownloadedModel/index.tsx | 6 +- web/containers/Layout/TopBar/index.tsx | 11 +- web/containers/Providers/DataLoader.tsx | 21 ++ web/containers/Providers/EventHandler.tsx | 6 +- web/containers/Providers/EventListener.tsx | 10 +- web/containers/Providers/index.tsx | 6 +- web/helpers/atoms/Assistant.atom.ts | 4 + web/helpers/atoms/Model.atom.ts | 4 + web/hooks/useActiveModel.ts | 4 +- web/hooks/useAssistants.ts | 28 +++ web/hooks/useDeleteModel.ts | 7 +- web/hooks/useGetAssistants.ts | 27 -- web/hooks/useGetConfiguredModels.ts | 30 --- web/hooks/useGetDownloadedModels.ts | 27 -- web/hooks/useGetSystemResources.ts | 2 +- web/hooks/useModels.ts | 46 ++++ web/hooks/useRecommendedModel.ts | 15 +- web/hooks/useSetActiveThread.ts | 55 ++-- web/hooks/useThreads.ts | 26 +- web/screens/Chat/ChatBody/index.tsx | 6 +- web/screens/Chat/CleanThreadModal/index.tsx | 65 +++++ web/screens/Chat/DeleteThreadModal/index.tsx | 68 +++++ .../Chat/RequestDownloadModel/index.tsx | 7 +- web/screens/Chat/ThreadList/index.tsx | 234 ++++-------------- .../ExploreModelItemHeader/index.tsx | 8 +- .../ExploreModels/ModelVersionItem/index.tsx | 6 +- web/screens/ExploreModels/index.tsx | 26 +- web/screens/Settings/Models/index.tsx | 7 +- 32 files changed, 469 insertions(+), 421 deletions(-) create mode 100644 web/containers/Providers/DataLoader.tsx create mode 100644 web/helpers/atoms/Assistant.atom.ts create mode 100644 web/hooks/useAssistants.ts delete mode 100644 web/hooks/useGetAssistants.ts delete mode 100644 web/hooks/useGetConfiguredModels.ts delete mode 100644 web/hooks/useGetDownloadedModels.ts create mode 100644 web/hooks/useModels.ts create mode 100644 web/screens/Chat/CleanThreadModal/index.tsx create mode 100644 web/screens/Chat/DeleteThreadModal/index.tsx diff --git a/core/src/node/api/routes/common.ts b/core/src/node/api/routes/common.ts index 27385e561..8887755fe 100644 --- a/core/src/node/api/routes/common.ts +++ b/core/src/node/api/routes/common.ts @@ -12,6 +12,8 @@ import { import { JanApiRouteConfiguration } from '../common/configuration' import { startModel, stopModel } from '../common/startStopModel' import { ModelSettingParams } from '../../../types' +import { getJanDataFolderPath } from '../../utils' +import { normalizeFilePath } from '../../path' export const commonRouter = async (app: HttpServer) => { // Common Routes @@ -52,7 +54,14 @@ export const commonRouter = async (app: HttpServer) => { // App Routes app.post(`/app/${AppRoute.joinPath}`, async (request: any, reply: any) => { const args = JSON.parse(request.body) as any[] - reply.send(JSON.stringify(join(...args[0]))) + + const paths = args[0].map((arg: string) => + typeof arg === 'string' && (arg.startsWith(`file:/`) || arg.startsWith(`file:\\`)) + ? join(getJanDataFolderPath(), normalizeFilePath(arg)) + : arg + ) + + reply.send(JSON.stringify(join(...paths))) }) app.post(`/app/${AppRoute.baseName}`, async (request: any, reply: any) => { diff --git a/core/src/node/api/routes/download.ts b/core/src/node/api/routes/download.ts index b4e11f957..ab8c0bd37 100644 --- a/core/src/node/api/routes/download.ts +++ b/core/src/node/api/routes/download.ts @@ -4,55 +4,55 @@ import { DownloadManager } from '../../download' import { HttpServer } from '../HttpServer' import { createWriteStream } from 'fs' import { getJanDataFolderPath } from '../../utils' -import { normalizeFilePath } from "../../path"; +import { normalizeFilePath } from '../../path' export const downloadRouter = async (app: HttpServer) => { app.post(`/${DownloadRoute.downloadFile}`, async (req, res) => { - const strictSSL = !(req.query.ignoreSSL === "true"); - const proxy = req.query.proxy?.startsWith("http") ? req.query.proxy : undefined; - const body = JSON.parse(req.body as any); + const strictSSL = !(req.query.ignoreSSL === 'true') + const proxy = req.query.proxy?.startsWith('http') ? req.query.proxy : undefined + const body = JSON.parse(req.body as any) const normalizedArgs = body.map((arg: any) => { - if (typeof arg === "string") { - return join(getJanDataFolderPath(), normalizeFilePath(arg)); + if (typeof arg === 'string' && arg.startsWith('file:')) { + return join(getJanDataFolderPath(), normalizeFilePath(arg)) } - return arg; - }); + return arg + }) - const localPath = normalizedArgs[1]; - const fileName = localPath.split("/").pop() ?? ""; + const localPath = normalizedArgs[1] + const fileName = localPath.split('/').pop() ?? '' - const request = require("request"); - const progress = require("request-progress"); + const request = require('request') + const progress = require('request-progress') - const rq = request({ url: normalizedArgs[0], strictSSL, proxy }); + const rq = request({ url: normalizedArgs[0], strictSSL, proxy }) progress(rq, {}) - .on("progress", function (state: any) { - console.log("download onProgress", state); + .on('progress', function (state: any) { + console.log('download onProgress', state) }) - .on("error", function (err: Error) { - console.log("download onError", err); + .on('error', function (err: Error) { + console.log('download onError', err) }) - .on("end", function () { - console.log("download onEnd"); + .on('end', function () { + console.log('download onEnd') }) - .pipe(createWriteStream(normalizedArgs[1])); + .pipe(createWriteStream(normalizedArgs[1])) - DownloadManager.instance.setRequest(fileName, rq); - }); + DownloadManager.instance.setRequest(fileName, rq) + }) app.post(`/${DownloadRoute.abortDownload}`, async (req, res) => { - const body = JSON.parse(req.body as any); + const body = JSON.parse(req.body as any) const normalizedArgs = body.map((arg: any) => { - if (typeof arg === "string") { - return join(getJanDataFolderPath(), normalizeFilePath(arg)); + if (typeof arg === 'string' && arg.startsWith('file:')) { + return join(getJanDataFolderPath(), normalizeFilePath(arg)) } - return arg; - }); + return arg + }) - const localPath = normalizedArgs[0]; - const fileName = localPath.split("/").pop() ?? ""; - const rq = DownloadManager.instance.networkRequests[fileName]; - DownloadManager.instance.networkRequests[fileName] = undefined; - rq?.abort(); - }); -}; + const localPath = normalizedArgs[0] + const fileName = localPath.split('/').pop() ?? '' + const rq = DownloadManager.instance.networkRequests[fileName] + DownloadManager.instance.networkRequests[fileName] = undefined + rq?.abort() + }) +} diff --git a/extensions/conversational-extension/src/index.ts b/extensions/conversational-extension/src/index.ts index 3d28a9c1d..bf8c213ad 100644 --- a/extensions/conversational-extension/src/index.ts +++ b/extensions/conversational-extension/src/index.ts @@ -12,7 +12,7 @@ import { * functionality for managing threads. */ export default class JSONConversationalExtension extends ConversationalExtension { - private static readonly _homeDir = 'file://threads' + private static readonly _threadFolder = 'file://threads' private static readonly _threadInfoFileName = 'thread.json' private static readonly _threadMessagesFileName = 'messages.jsonl' @@ -20,8 +20,8 @@ export default class JSONConversationalExtension extends ConversationalExtension * Called when the extension is loaded. */ async onLoad() { - if (!(await fs.existsSync(JSONConversationalExtension._homeDir))) - await fs.mkdirSync(JSONConversationalExtension._homeDir) + if (!(await fs.existsSync(JSONConversationalExtension._threadFolder))) + await fs.mkdirSync(JSONConversationalExtension._threadFolder) console.debug('JSONConversationalExtension loaded') } @@ -68,7 +68,7 @@ export default class JSONConversationalExtension extends ConversationalExtension async saveThread(thread: Thread): Promise { try { const threadDirPath = await joinPath([ - JSONConversationalExtension._homeDir, + JSONConversationalExtension._threadFolder, thread.id, ]) const threadJsonPath = await joinPath([ @@ -92,7 +92,7 @@ export default class JSONConversationalExtension extends ConversationalExtension */ async deleteThread(threadId: string): Promise { const path = await joinPath([ - JSONConversationalExtension._homeDir, + JSONConversationalExtension._threadFolder, `${threadId}`, ]) try { @@ -109,7 +109,7 @@ export default class JSONConversationalExtension extends ConversationalExtension async addNewMessage(message: ThreadMessage): Promise { try { const threadDirPath = await joinPath([ - JSONConversationalExtension._homeDir, + JSONConversationalExtension._threadFolder, message.thread_id, ]) const threadMessagePath = await joinPath([ @@ -177,7 +177,7 @@ export default class JSONConversationalExtension extends ConversationalExtension ): Promise { try { const threadDirPath = await joinPath([ - JSONConversationalExtension._homeDir, + JSONConversationalExtension._threadFolder, threadId, ]) const threadMessagePath = await joinPath([ @@ -205,7 +205,7 @@ export default class JSONConversationalExtension extends ConversationalExtension private async readThread(threadDirName: string): Promise { return fs.readFileSync( await joinPath([ - JSONConversationalExtension._homeDir, + JSONConversationalExtension._threadFolder, threadDirName, JSONConversationalExtension._threadInfoFileName, ]), @@ -219,14 +219,14 @@ export default class JSONConversationalExtension extends ConversationalExtension */ private async getValidThreadDirs(): Promise { const fileInsideThread: string[] = await fs.readdirSync( - JSONConversationalExtension._homeDir + JSONConversationalExtension._threadFolder ) const threadDirs: string[] = [] for (let i = 0; i < fileInsideThread.length; i++) { if (fileInsideThread[i].includes('.DS_Store')) continue const path = await joinPath([ - JSONConversationalExtension._homeDir, + JSONConversationalExtension._threadFolder, fileInsideThread[i], ]) @@ -246,7 +246,7 @@ export default class JSONConversationalExtension extends ConversationalExtension async getAllMessages(threadId: string): Promise { try { const threadDirPath = await joinPath([ - JSONConversationalExtension._homeDir, + JSONConversationalExtension._threadFolder, threadId, ]) @@ -263,22 +263,17 @@ export default class JSONConversationalExtension extends ConversationalExtension JSONConversationalExtension._threadMessagesFileName, ]) - const result = await fs - .readFileSync(messageFilePath, 'utf-8') - .then((content) => - content - .toString() - .split('\n') - .filter((line) => line !== '') - ) + let readResult = await fs.readFileSync(messageFilePath, 'utf-8') + + if (typeof readResult === 'object') { + readResult = JSON.stringify(readResult) + } + + const result = readResult.split('\n').filter((line) => line !== '') const messages: ThreadMessage[] = [] result.forEach((line: string) => { - try { - messages.push(JSON.parse(line) as ThreadMessage) - } catch (err) { - console.error(err) - } + messages.push(JSON.parse(line)) }) return messages } catch (err) { diff --git a/web/containers/Layout/BottomBar/index.tsx b/web/containers/Layout/BottomBar/index.tsx index 6e334b9ef..7dc5a9444 100644 --- a/web/containers/Layout/BottomBar/index.tsx +++ b/web/containers/Layout/BottomBar/index.tsx @@ -26,11 +26,12 @@ import { MainViewState } from '@/constants/screens' import { useActiveModel } from '@/hooks/useActiveModel' import { useDownloadState } from '@/hooks/useDownloadState' -import { useGetDownloadedModels } from '@/hooks/useGetDownloadedModels' + import useGetSystemResources from '@/hooks/useGetSystemResources' import { useMainViewState } from '@/hooks/useMainViewState' import { serverEnabledAtom } from '@/helpers/atoms/LocalServer.atom' +import { downloadedModelsAtom } from '@/helpers/atoms/Model.atom' const menuLinks = [ { @@ -49,7 +50,8 @@ const BottomBar = () => { const { activeModel, stateModel } = useActiveModel() const { ram, cpu } = useGetSystemResources() const progress = useAtomValue(appDownloadProgress) - const { downloadedModels } = useGetDownloadedModels() + const downloadedModels = useAtomValue(downloadedModelsAtom) + const { setMainViewState } = useMainViewState() const { downloadStates } = useDownloadState() const setShowSelectModelModal = useSetAtom(showSelectModelModalAtom) diff --git a/web/containers/Layout/TopBar/CommandListDownloadedModel/index.tsx b/web/containers/Layout/TopBar/CommandListDownloadedModel/index.tsx index 3edce06eb..ac5756e9f 100644 --- a/web/containers/Layout/TopBar/CommandListDownloadedModel/index.tsx +++ b/web/containers/Layout/TopBar/CommandListDownloadedModel/index.tsx @@ -11,7 +11,7 @@ import { Badge, } from '@janhq/uikit' -import { useAtom } from 'jotai' +import { useAtom, useAtomValue } from 'jotai' import { DatabaseIcon, CpuIcon } from 'lucide-react' import { showSelectModelModalAtom } from '@/containers/Providers/KeyListener' @@ -19,14 +19,14 @@ import { showSelectModelModalAtom } from '@/containers/Providers/KeyListener' import { MainViewState } from '@/constants/screens' import { useActiveModel } from '@/hooks/useActiveModel' -import { useGetDownloadedModels } from '@/hooks/useGetDownloadedModels' import { useMainViewState } from '@/hooks/useMainViewState' import { serverEnabledAtom } from '@/helpers/atoms/LocalServer.atom' +import { downloadedModelsAtom } from '@/helpers/atoms/Model.atom' export default function CommandListDownloadedModel() { const { setMainViewState } = useMainViewState() - const { downloadedModels } = useGetDownloadedModels() + const downloadedModels = useAtomValue(downloadedModelsAtom) const { activeModel, startModel, stopModel } = useActiveModel() const [serverEnabled] = useAtom(serverEnabledAtom) const [showSelectModelModal, setShowSelectModelModal] = useAtom( diff --git a/web/containers/Layout/TopBar/index.tsx b/web/containers/Layout/TopBar/index.tsx index f72f5f066..206a9013d 100644 --- a/web/containers/Layout/TopBar/index.tsx +++ b/web/containers/Layout/TopBar/index.tsx @@ -20,7 +20,6 @@ import { MainViewState } from '@/constants/screens' import { useClickOutside } from '@/hooks/useClickOutside' import { useCreateNewThread } from '@/hooks/useCreateNewThread' -import useGetAssistants, { getAssistants } from '@/hooks/useGetAssistants' import { useMainViewState } from '@/hooks/useMainViewState' import { usePath } from '@/hooks/usePath' @@ -29,13 +28,14 @@ import { showRightSideBarAtom } from '@/screens/Chat/Sidebar' import { openFileTitle } from '@/utils/titleUtils' +import { assistantsAtom } from '@/helpers/atoms/Assistant.atom' import { activeThreadAtom } from '@/helpers/atoms/Thread.atom' const TopBar = () => { const activeThread = useAtomValue(activeThreadAtom) const { mainViewState } = useMainViewState() const { requestCreateNewThread } = useCreateNewThread() - const { assistants } = useGetAssistants() + const assistants = useAtomValue(assistantsAtom) const [showRightSideBar, setShowRightSideBar] = useAtom(showRightSideBarAtom) const [showLeftSideBar, setShowLeftSideBar] = useAtom(showLeftSideBarAtom) const showing = useAtomValue(showRightSideBarAtom) @@ -61,12 +61,7 @@ const TopBar = () => { const onCreateConversationClick = async () => { if (assistants.length === 0) { - const res = await getAssistants() - if (res.length === 0) { - alert('No assistant available') - return - } - requestCreateNewThread(res[0]) + alert('No assistant available') } else { requestCreateNewThread(assistants[0]) } diff --git a/web/containers/Providers/DataLoader.tsx b/web/containers/Providers/DataLoader.tsx new file mode 100644 index 000000000..2b6675d98 --- /dev/null +++ b/web/containers/Providers/DataLoader.tsx @@ -0,0 +1,21 @@ +'use client' + +import { Fragment, ReactNode } from 'react' + +import useAssistants from '@/hooks/useAssistants' +import useModels from '@/hooks/useModels' +import useThreads from '@/hooks/useThreads' + +type Props = { + children: ReactNode +} + +const DataLoader: React.FC = ({ children }) => { + useModels() + useThreads() + useAssistants() + + return {children} +} + +export default DataLoader diff --git a/web/containers/Providers/EventHandler.tsx b/web/containers/Providers/EventHandler.tsx index ec0fbfc90..e9d70d5d2 100644 --- a/web/containers/Providers/EventHandler.tsx +++ b/web/containers/Providers/EventHandler.tsx @@ -18,7 +18,6 @@ import { loadModelErrorAtom, stateModelAtom, } from '@/hooks/useActiveModel' -import { useGetDownloadedModels } from '@/hooks/useGetDownloadedModels' import { queuedMessageAtom } from '@/hooks/useSendChatMessage' @@ -29,6 +28,7 @@ import { addNewMessageAtom, updateMessageAtom, } from '@/helpers/atoms/ChatMessage.atom' +import { downloadedModelsAtom } from '@/helpers/atoms/Model.atom' import { updateThreadWaitingForResponseAtom, threadsAtom, @@ -38,7 +38,7 @@ import { export default function EventHandler({ children }: { children: ReactNode }) { const addNewMessage = useSetAtom(addNewMessageAtom) const updateMessage = useSetAtom(updateMessageAtom) - const { downloadedModels } = useGetDownloadedModels() + const downloadedModels = useAtomValue(downloadedModelsAtom) const setActiveModel = useSetAtom(activeModelAtom) const setStateModel = useSetAtom(stateModelAtom) const setQueuedMessage = useSetAtom(queuedMessageAtom) @@ -143,7 +143,7 @@ export default function EventHandler({ children }: { children: ReactNode }) { ?.addNewMessage(message) } }, - [updateMessage, updateThreadWaiting] + [updateMessage, updateThreadWaiting, setIsGeneratingResponse] ) useEffect(() => { diff --git a/web/containers/Providers/EventListener.tsx b/web/containers/Providers/EventListener.tsx index 62d4cacb6..5e8556f33 100644 --- a/web/containers/Providers/EventListener.tsx +++ b/web/containers/Providers/EventListener.tsx @@ -3,10 +3,9 @@ import { PropsWithChildren, useEffect, useRef } from 'react' import { baseName } from '@janhq/core' -import { useAtomValue, useSetAtom } from 'jotai' +import { useAtom, useAtomValue, useSetAtom } from 'jotai' import { useDownloadState } from '@/hooks/useDownloadState' -import { useGetDownloadedModels } from '@/hooks/useGetDownloadedModels' import { modelBinFileName } from '@/utils/model' @@ -14,14 +13,17 @@ import EventHandler from './EventHandler' import { appDownloadProgress } from './Jotai' -import { downloadingModelsAtom } from '@/helpers/atoms/Model.atom' +import { + downloadedModelsAtom, + downloadingModelsAtom, +} from '@/helpers/atoms/Model.atom' export default function EventListenerWrapper({ children }: PropsWithChildren) { const setProgress = useSetAtom(appDownloadProgress) const models = useAtomValue(downloadingModelsAtom) const modelsRef = useRef(models) - const { setDownloadedModels, downloadedModels } = useGetDownloadedModels() + const [downloadedModels, setDownloadedModels] = useAtom(downloadedModelsAtom) const { setDownloadState, setDownloadStateSuccess, diff --git a/web/containers/Providers/index.tsx b/web/containers/Providers/index.tsx index c8a20bca7..e7a179ec4 100644 --- a/web/containers/Providers/index.tsx +++ b/web/containers/Providers/index.tsx @@ -23,6 +23,8 @@ import Umami from '@/utils/umami' import Loader from '../Loader' +import DataLoader from './DataLoader' + import KeyListener from './KeyListener' import { extensionManager } from '@/extension' @@ -81,7 +83,9 @@ const Providers = (props: PropsWithChildren) => { - {children} + + {children} + {!isMac && } diff --git a/web/helpers/atoms/Assistant.atom.ts b/web/helpers/atoms/Assistant.atom.ts new file mode 100644 index 000000000..e90923d3d --- /dev/null +++ b/web/helpers/atoms/Assistant.atom.ts @@ -0,0 +1,4 @@ +import { Assistant } from '@janhq/core/.' +import { atom } from 'jotai' + +export const assistantsAtom = atom([]) diff --git a/web/helpers/atoms/Model.atom.ts b/web/helpers/atoms/Model.atom.ts index 6eb7f2ad6..5c9188ad7 100644 --- a/web/helpers/atoms/Model.atom.ts +++ b/web/helpers/atoms/Model.atom.ts @@ -24,3 +24,7 @@ export const removeDownloadingModelAtom = atom( ) } ) + +export const downloadedModelsAtom = atom([]) + +export const configuredModelsAtom = atom([]) diff --git a/web/hooks/useActiveModel.ts b/web/hooks/useActiveModel.ts index 54a1fdbe0..1b61a0dd1 100644 --- a/web/hooks/useActiveModel.ts +++ b/web/hooks/useActiveModel.ts @@ -3,9 +3,9 @@ import { atom, useAtom, useAtomValue, useSetAtom } from 'jotai' import { toaster } from '@/containers/Toast' -import { useGetDownloadedModels } from './useGetDownloadedModels' import { LAST_USED_MODEL_ID } from './useRecommendedModel' +import { downloadedModelsAtom } from '@/helpers/atoms/Model.atom' import { activeThreadAtom } from '@/helpers/atoms/Thread.atom' export const activeModelAtom = atom(undefined) @@ -21,7 +21,7 @@ export function useActiveModel() { const [activeModel, setActiveModel] = useAtom(activeModelAtom) const activeThread = useAtomValue(activeThreadAtom) const [stateModel, setStateModel] = useAtom(stateModelAtom) - const { downloadedModels } = useGetDownloadedModels() + const downloadedModels = useAtomValue(downloadedModelsAtom) const setLoadModelError = useSetAtom(loadModelErrorAtom) const startModel = async (modelId: string) => { diff --git a/web/hooks/useAssistants.ts b/web/hooks/useAssistants.ts new file mode 100644 index 000000000..8f2c4a92c --- /dev/null +++ b/web/hooks/useAssistants.ts @@ -0,0 +1,28 @@ +import { useEffect } from 'react' + +import { Assistant, AssistantExtension, ExtensionTypeEnum } from '@janhq/core' + +import { useSetAtom } from 'jotai' + +import { extensionManager } from '@/extension' +import { assistantsAtom } from '@/helpers/atoms/Assistant.atom' + +const useAssistants = () => { + const setAssistants = useSetAtom(assistantsAtom) + + useEffect(() => { + const getAssistants = async () => { + const assistants = await getLocalAssistants() + setAssistants(assistants) + } + + getAssistants() + }, [setAssistants]) +} + +const getLocalAssistants = async (): Promise => + extensionManager + .get(ExtensionTypeEnum.Assistant) + ?.getAssistants() ?? [] + +export default useAssistants diff --git a/web/hooks/useDeleteModel.ts b/web/hooks/useDeleteModel.ts index fa0cfb45e..d9f2b94be 100644 --- a/web/hooks/useDeleteModel.ts +++ b/web/hooks/useDeleteModel.ts @@ -1,13 +1,14 @@ import { ExtensionTypeEnum, ModelExtension, Model } from '@janhq/core' +import { useAtom } from 'jotai' + import { toaster } from '@/containers/Toast' -import { useGetDownloadedModels } from '@/hooks/useGetDownloadedModels' - import { extensionManager } from '@/extension/ExtensionManager' +import { downloadedModelsAtom } from '@/helpers/atoms/Model.atom' export default function useDeleteModel() { - const { setDownloadedModels, downloadedModels } = useGetDownloadedModels() + const [downloadedModels, setDownloadedModels] = useAtom(downloadedModelsAtom) const deleteModel = async (model: Model) => { await extensionManager diff --git a/web/hooks/useGetAssistants.ts b/web/hooks/useGetAssistants.ts deleted file mode 100644 index 2b34bfbd1..000000000 --- a/web/hooks/useGetAssistants.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { useEffect, useState } from 'react' - -import { Assistant, ExtensionTypeEnum, AssistantExtension } from '@janhq/core' - -import { extensionManager } from '@/extension/ExtensionManager' - -export const getAssistants = async (): Promise => - extensionManager - .get(ExtensionTypeEnum.Assistant) - ?.getAssistants() ?? [] - -/** - * Hooks for get assistants - * - * @returns assistants - */ -export default function useGetAssistants() { - const [assistants, setAssistants] = useState([]) - - useEffect(() => { - getAssistants() - .then((data) => setAssistants(data)) - .catch((err) => console.error(err)) - }, []) - - return { assistants } -} diff --git a/web/hooks/useGetConfiguredModels.ts b/web/hooks/useGetConfiguredModels.ts deleted file mode 100644 index 8be052ae2..000000000 --- a/web/hooks/useGetConfiguredModels.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { useCallback, useEffect, useState } from 'react' - -import { ExtensionTypeEnum, ModelExtension, Model } from '@janhq/core' - -import { extensionManager } from '@/extension/ExtensionManager' - -export function useGetConfiguredModels() { - const [loading, setLoading] = useState(false) - const [models, setModels] = useState([]) - - const fetchModels = useCallback(async () => { - setLoading(true) - const models = await getConfiguredModels() - setLoading(false) - setModels(models) - }, []) - - useEffect(() => { - fetchModels() - }, [fetchModels]) - - return { loading, models } -} - -const getConfiguredModels = async (): Promise => { - const models = await extensionManager - .get(ExtensionTypeEnum.Model) - ?.getConfiguredModels() - return models ?? [] -} diff --git a/web/hooks/useGetDownloadedModels.ts b/web/hooks/useGetDownloadedModels.ts deleted file mode 100644 index bba420858..000000000 --- a/web/hooks/useGetDownloadedModels.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { useEffect } from 'react' - -import { ExtensionTypeEnum, ModelExtension, Model } from '@janhq/core' - -import { atom, useAtom } from 'jotai' - -import { extensionManager } from '@/extension/ExtensionManager' - -export const downloadedModelsAtom = atom([]) - -export function useGetDownloadedModels() { - const [downloadedModels, setDownloadedModels] = useAtom(downloadedModelsAtom) - - useEffect(() => { - getDownloadedModels().then((downloadedModels) => { - setDownloadedModels(downloadedModels) - }) - // eslint-disable-next-line react-hooks/exhaustive-deps - }, []) - - return { downloadedModels, setDownloadedModels } -} - -export const getDownloadedModels = async (): Promise => - extensionManager - .get(ExtensionTypeEnum.Model) - ?.getDownloadedModels() ?? [] diff --git a/web/hooks/useGetSystemResources.ts b/web/hooks/useGetSystemResources.ts index de595ad7b..3429a93aa 100644 --- a/web/hooks/useGetSystemResources.ts +++ b/web/hooks/useGetSystemResources.ts @@ -58,7 +58,7 @@ export default function useGetSystemResources() { // There is a possibility that this will be removed and replaced by the process event hook? const intervalId = setInterval(() => { getSystemResources() - }, 500) + }, 5000) // clean up interval return () => clearInterval(intervalId) diff --git a/web/hooks/useModels.ts b/web/hooks/useModels.ts new file mode 100644 index 000000000..23e098007 --- /dev/null +++ b/web/hooks/useModels.ts @@ -0,0 +1,46 @@ +import { useEffect } from 'react' + +import { ExtensionTypeEnum, Model, ModelExtension } from '@janhq/core' + +import { useSetAtom } from 'jotai' + +import { extensionManager } from '@/extension' +import { + configuredModelsAtom, + downloadedModelsAtom, +} from '@/helpers/atoms/Model.atom' + +const useModels = () => { + const setDownloadedModels = useSetAtom(downloadedModelsAtom) + const setConfiguredModels = useSetAtom(configuredModelsAtom) + + useEffect(() => { + const getDownloadedModels = async () => { + const models = await getLocalDownloadedModels() + setDownloadedModels(models) + } + + getDownloadedModels() + }, [setDownloadedModels]) + + useEffect(() => { + const getConfiguredModels = async () => { + const models = await getLocalConfiguredModels() + setConfiguredModels(models) + } + + getConfiguredModels() + }, [setConfiguredModels]) +} + +const getLocalConfiguredModels = async (): Promise => + extensionManager + .get(ExtensionTypeEnum.Model) + ?.getConfiguredModels() ?? [] + +const getLocalDownloadedModels = async (): Promise => + extensionManager + .get(ExtensionTypeEnum.Model) + ?.getDownloadedModels() ?? [] + +export default useModels diff --git a/web/hooks/useRecommendedModel.ts b/web/hooks/useRecommendedModel.ts index 427d2bf73..8122e2b77 100644 --- a/web/hooks/useRecommendedModel.ts +++ b/web/hooks/useRecommendedModel.ts @@ -5,9 +5,9 @@ import { Model, InferenceEngine } from '@janhq/core' import { atom, useAtomValue } from 'jotai' import { activeModelAtom } from './useActiveModel' -import { getDownloadedModels } from './useGetDownloadedModels' -import { activeThreadAtom, threadStatesAtom } from '@/helpers/atoms/Thread.atom' +import { downloadedModelsAtom } from '@/helpers/atoms/Model.atom' +import { activeThreadAtom } from '@/helpers/atoms/Thread.atom' export const lastUsedModel = atom(undefined) @@ -24,19 +24,20 @@ export const LAST_USED_MODEL_ID = 'last-used-model-id' */ export default function useRecommendedModel() { const activeModel = useAtomValue(activeModelAtom) - const [downloadedModels, setDownloadedModels] = useState([]) + const [sortedModels, setSortedModels] = useState([]) const [recommendedModel, setRecommendedModel] = useState() const activeThread = useAtomValue(activeThreadAtom) + const downloadedModels = useAtomValue(downloadedModelsAtom) const getAndSortDownloadedModels = useCallback(async (): Promise => { - const models = (await getDownloadedModels()).sort((a, b) => + const models = downloadedModels.sort((a, b) => a.engine !== InferenceEngine.nitro && b.engine === InferenceEngine.nitro ? 1 : -1 ) - setDownloadedModels(models) + setSortedModels(models) return models - }, []) + }, [downloadedModels]) const getRecommendedModel = useCallback(async (): Promise< Model | undefined @@ -98,5 +99,5 @@ export default function useRecommendedModel() { getRecommendedModel() }, [getRecommendedModel]) - return { recommendedModel, downloadedModels } + return { recommendedModel, downloadedModels: sortedModels } } diff --git a/web/hooks/useSetActiveThread.ts b/web/hooks/useSetActiveThread.ts index f5649ccaf..6cf94d45d 100644 --- a/web/hooks/useSetActiveThread.ts +++ b/web/hooks/useSetActiveThread.ts @@ -1,3 +1,5 @@ +import { useCallback } from 'react' + import { InferenceEvent, ExtensionTypeEnum, @@ -6,7 +8,7 @@ import { ConversationalExtension, } from '@janhq/core' -import { useAtomValue, useSetAtom } from 'jotai' +import { useSetAtom } from 'jotai' import { loadModelErrorAtom } from './useActiveModel' @@ -14,43 +16,46 @@ import { extensionManager } from '@/extension' import { setConvoMessagesAtom } from '@/helpers/atoms/ChatMessage.atom' import { ModelParams, - getActiveThreadIdAtom, isGeneratingResponseAtom, setActiveThreadIdAtom, setThreadModelParamsAtom, } from '@/helpers/atoms/Thread.atom' export default function useSetActiveThread() { - const activeThreadId = useAtomValue(getActiveThreadIdAtom) const setActiveThreadId = useSetAtom(setActiveThreadIdAtom) const setThreadMessage = useSetAtom(setConvoMessagesAtom) const setThreadModelParams = useSetAtom(setThreadModelParamsAtom) const setIsGeneratingResponse = useSetAtom(isGeneratingResponseAtom) const setLoadModelError = useSetAtom(loadModelErrorAtom) - const setActiveThread = async (thread: Thread) => { - if (activeThreadId === thread.id) { - console.debug('Thread already active') - return - } + const setActiveThread = useCallback( + async (thread: Thread) => { + setIsGeneratingResponse(false) + events.emit(InferenceEvent.OnInferenceStopped, thread.id) - setIsGeneratingResponse(false) - setLoadModelError(undefined) - events.emit(InferenceEvent.OnInferenceStopped, thread.id) + // load the corresponding messages + const messages = await getLocalThreadMessage(thread.id) + setThreadMessage(thread.id, messages) - // load the corresponding messages - const messages = await extensionManager - .get(ExtensionTypeEnum.Conversational) - ?.getAllMessages(thread.id) - setThreadMessage(thread.id, messages ?? []) + setActiveThreadId(thread.id) + const modelParams: ModelParams = { + ...thread.assistants[0]?.model?.parameters, + ...thread.assistants[0]?.model?.settings, + } + setThreadModelParams(thread.id, modelParams) + }, + [ + setActiveThreadId, + setThreadMessage, + setThreadModelParams, + setIsGeneratingResponse, + ] + ) - setActiveThreadId(thread.id) - const modelParams: ModelParams = { - ...thread.assistants[0]?.model?.parameters, - ...thread.assistants[0]?.model?.settings, - } - setThreadModelParams(thread.id, modelParams) - } - - return { activeThreadId, setActiveThread } + return { setActiveThread } } + +const getLocalThreadMessage = async (threadId: string) => + extensionManager + .get(ExtensionTypeEnum.Conversational) + ?.getAllMessages(threadId) ?? [] diff --git a/web/hooks/useThreads.ts b/web/hooks/useThreads.ts index b7de014cc..1ac038b26 100644 --- a/web/hooks/useThreads.ts +++ b/web/hooks/useThreads.ts @@ -1,3 +1,5 @@ +import { useEffect } from 'react' + import { ExtensionTypeEnum, Thread, @@ -5,14 +7,13 @@ import { ConversationalExtension, } from '@janhq/core' -import { useAtomValue, useSetAtom } from 'jotai' +import { useSetAtom } from 'jotai' import useSetActiveThread from './useSetActiveThread' import { extensionManager } from '@/extension/ExtensionManager' import { ModelParams, - activeThreadAtom, threadModelParamsAtom, threadStatesAtom, threadsAtom, @@ -22,11 +23,10 @@ const useThreads = () => { const setThreadStates = useSetAtom(threadStatesAtom) const setThreads = useSetAtom(threadsAtom) const setThreadModelRuntimeParams = useSetAtom(threadModelParamsAtom) - const activeThread = useAtomValue(activeThreadAtom) const { setActiveThread } = useSetActiveThread() - const getThreads = async () => { - try { + useEffect(() => { + const getThreads = async () => { const localThreads = await getLocalThreads() const localThreadStates: Record = {} const threadModelParams: Record = {} @@ -54,17 +54,19 @@ const useThreads = () => { setThreadStates(localThreadStates) setThreads(localThreads) setThreadModelRuntimeParams(threadModelParams) - if (localThreads.length && !activeThread) { + + if (localThreads.length > 0) { setActiveThread(localThreads[0]) } - } catch (error) { - console.error(error) } - } - return { - getThreads, - } + getThreads() + }, [ + setActiveThread, + setThreadModelRuntimeParams, + setThreadStates, + setThreads, + ]) } const getLocalThreads = async (): Promise => diff --git a/web/screens/Chat/ChatBody/index.tsx b/web/screens/Chat/ChatBody/index.tsx index 66f14d076..c67d6a538 100644 --- a/web/screens/Chat/ChatBody/index.tsx +++ b/web/screens/Chat/ChatBody/index.tsx @@ -11,7 +11,6 @@ import LogoMark from '@/containers/Brand/Logo/Mark' import { MainViewState } from '@/constants/screens' import { loadModelErrorAtom } from '@/hooks/useActiveModel' -import { useGetDownloadedModels } from '@/hooks/useGetDownloadedModels' import { useMainViewState } from '@/hooks/useMainViewState' @@ -20,10 +19,13 @@ import ChatItem from '../ChatItem' import ErrorMessage from '../ErrorMessage' import { getCurrentChatMessagesAtom } from '@/helpers/atoms/ChatMessage.atom' +import { downloadedModelsAtom } from '@/helpers/atoms/Model.atom' const ChatBody: React.FC = () => { const messages = useAtomValue(getCurrentChatMessagesAtom) - const { downloadedModels } = useGetDownloadedModels() + + const downloadedModels = useAtomValue(downloadedModelsAtom) + const { setMainViewState } = useMainViewState() if (downloadedModels.length === 0) diff --git a/web/screens/Chat/CleanThreadModal/index.tsx b/web/screens/Chat/CleanThreadModal/index.tsx new file mode 100644 index 000000000..6ef505e6f --- /dev/null +++ b/web/screens/Chat/CleanThreadModal/index.tsx @@ -0,0 +1,65 @@ +import React, { useCallback } from 'react' + +import { + Button, + Modal, + ModalClose, + ModalContent, + ModalFooter, + ModalHeader, + ModalPortal, + ModalTitle, + ModalTrigger, +} from '@janhq/uikit' +import { Paintbrush } from 'lucide-react' + +import useDeleteThread from '@/hooks/useDeleteThread' + +type Props = { + threadId: string +} + +const CleanThreadModal: React.FC = ({ threadId }) => { + const { cleanThread } = useDeleteThread() + const onCleanThreadClick = useCallback( + (e: React.MouseEvent) => { + e.stopPropagation() + cleanThread(threadId) + }, + [cleanThread, threadId] + ) + + return ( + + e.stopPropagation()}> +
+ + + Clean thread + +
+
+ + + + Clean Thread + +

Are you sure you want to clean this thread?

+ +
+ e.stopPropagation()}> + + + + + +
+
+
+
+ ) +} + +export default React.memo(CleanThreadModal) diff --git a/web/screens/Chat/DeleteThreadModal/index.tsx b/web/screens/Chat/DeleteThreadModal/index.tsx new file mode 100644 index 000000000..edbdb09b4 --- /dev/null +++ b/web/screens/Chat/DeleteThreadModal/index.tsx @@ -0,0 +1,68 @@ +import React, { useCallback } from 'react' + +import { + Modal, + ModalTrigger, + ModalPortal, + ModalContent, + ModalHeader, + ModalTitle, + ModalFooter, + ModalClose, + Button, +} from '@janhq/uikit' +import { Trash2Icon } from 'lucide-react' + +import useDeleteThread from '@/hooks/useDeleteThread' + +type Props = { + threadId: string +} + +const DeleteThreadModal: React.FC = ({ threadId }) => { + const { deleteThread } = useDeleteThread() + const onDeleteThreadClick = useCallback( + (e: React.MouseEvent) => { + e.stopPropagation() + deleteThread(threadId) + }, + [deleteThread, threadId] + ) + + return ( + + e.stopPropagation()}> +
+ + + Delete thread + +
+
+ + + + Delete Thread + +

+ Are you sure you want to delete this thread? This action cannot be + undone. +

+ +
+ e.stopPropagation()}> + + + + + +
+
+
+
+ ) +} + +export default React.memo(DeleteThreadModal) diff --git a/web/screens/Chat/RequestDownloadModel/index.tsx b/web/screens/Chat/RequestDownloadModel/index.tsx index e62dc562d..88fdadd57 100644 --- a/web/screens/Chat/RequestDownloadModel/index.tsx +++ b/web/screens/Chat/RequestDownloadModel/index.tsx @@ -2,15 +2,18 @@ import React, { Fragment, useCallback } from 'react' import { Button } from '@janhq/uikit' +import { useAtomValue } from 'jotai' + import LogoMark from '@/containers/Brand/Logo/Mark' import { MainViewState } from '@/constants/screens' -import { useGetDownloadedModels } from '@/hooks/useGetDownloadedModels' import { useMainViewState } from '@/hooks/useMainViewState' +import { downloadedModelsAtom } from '@/helpers/atoms/Model.atom' + const RequestDownloadModel: React.FC = () => { - const { downloadedModels } = useGetDownloadedModels() + const downloadedModels = useAtomValue(downloadedModelsAtom) const { setMainViewState } = useMainViewState() const onClick = useCallback(() => { diff --git a/web/screens/Chat/ThreadList/index.tsx b/web/screens/Chat/ThreadList/index.tsx index b4a045b1d..8f5bfb8f2 100644 --- a/web/screens/Chat/ThreadList/index.tsx +++ b/web/screens/Chat/ThreadList/index.tsx @@ -1,76 +1,39 @@ -import { useEffect, useState } from 'react' +import { useCallback } from 'react' -import { - Modal, - ModalTrigger, - ModalClose, - ModalFooter, - ModalPortal, - ModalContent, - ModalHeader, - ModalTitle, - Button, -} from '@janhq/uikit' +import { Thread } from '@janhq/core/' import { motion as m } from 'framer-motion' import { useAtomValue } from 'jotai' -import { - GalleryHorizontalEndIcon, - MoreVerticalIcon, - Trash2Icon, - Paintbrush, -} from 'lucide-react' +import { GalleryHorizontalEndIcon, MoreVerticalIcon } from 'lucide-react' import { twMerge } from 'tailwind-merge' -import { useCreateNewThread } from '@/hooks/useCreateNewThread' -import useDeleteThread from '@/hooks/useDeleteThread' - -import useGetAssistants from '@/hooks/useGetAssistants' -import { useGetDownloadedModels } from '@/hooks/useGetDownloadedModels' import useSetActiveThread from '@/hooks/useSetActiveThread' -import useThreads from '@/hooks/useThreads' - import { displayDate } from '@/utils/datetime' +import CleanThreadModal from '../CleanThreadModal' + +import DeleteThreadModal from '../DeleteThreadModal' + import { - activeThreadAtom, + getActiveThreadIdAtom, threadStatesAtom, threadsAtom, } from '@/helpers/atoms/Thread.atom' export default function ThreadList() { - const threads = useAtomValue(threadsAtom) const threadStates = useAtomValue(threadStatesAtom) - const { getThreads } = useThreads() - const { assistants } = useGetAssistants() - const { requestCreateNewThread } = useCreateNewThread() - const activeThread = useAtomValue(activeThreadAtom) - const { deleteThread, cleanThread } = useDeleteThread() - const { downloadedModels } = useGetDownloadedModels() - const [isThreadsReady, setIsThreadsReady] = useState(false) + const threads = useAtomValue(threadsAtom) + const activeThreadId = useAtomValue(getActiveThreadIdAtom) + const { setActiveThread } = useSetActiveThread() - const { activeThreadId, setActiveThread: onThreadClick } = - useSetActiveThread() - - useEffect(() => { - getThreads().then(() => setIsThreadsReady(true)) - // eslint-disable-next-line react-hooks/exhaustive-deps - }, []) - - useEffect(() => { - if ( - isThreadsReady && - downloadedModels.length !== 0 && - threads.length === 0 && - assistants.length !== 0 && - !activeThread - ) { - requestCreateNewThread(assistants[0]) - } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [assistants, threads, downloadedModels, activeThread, isThreadsReady]) + const onThreadClick = useCallback( + (thread: Thread) => { + setActiveThread(thread) + }, + [setActiveThread] + ) return (
@@ -83,133 +46,44 @@ export default function ThreadList() {

No Thread History

) : ( - threads.map((thread, i) => { - const lastMessage = - threadStates[thread.id]?.lastMessage ?? 'No new message' - return ( -
{ - onThreadClick(thread) - }} - > -
-

- {thread.updated && displayDate(thread.updated)} -

-

{thread.title}

-

- {lastMessage || 'No new message'} -

-
-
- -
- - e.stopPropagation()}> -
- - - Clean thread - -
-
- - - - Clean Thread - -

Are you sure you want to clean this thread?

- -
- e.stopPropagation()} - > - - - - - -
-
-
-
- - e.stopPropagation()}> -
- - - Delete thread - -
-
- - - - Delete Thread - -

- Are you sure you want to delete this thread? This action - cannot be undone. -

- -
- e.stopPropagation()} - > - - - - - -
-
-
-
-
-
- {activeThreadId === thread.id && ( - - )} + threads.map((thread) => ( +
{ + onThreadClick(thread) + }} + > +
+

+ {thread.updated && displayDate(thread.updated)} +

+

{thread.title}

+

+ {threadStates[thread.id]?.lastMessage ?? 'No new message'} +

- ) - }) +
+ +
+ + +
+
+ {activeThreadId === thread.id && ( + + )} +
+ )) )}
) diff --git a/web/screens/ExploreModels/ExploreModelItemHeader/index.tsx b/web/screens/ExploreModels/ExploreModelItemHeader/index.tsx index 3ffe2cbac..755494ee3 100644 --- a/web/screens/ExploreModels/ExploreModelItemHeader/index.tsx +++ b/web/screens/ExploreModels/ExploreModelItemHeader/index.tsx @@ -27,14 +27,14 @@ import useDownloadModel from '@/hooks/useDownloadModel' import { useDownloadState } from '@/hooks/useDownloadState' -import { getAssistants } from '@/hooks/useGetAssistants' -import { downloadedModelsAtom } from '@/hooks/useGetDownloadedModels' import { useMainViewState } from '@/hooks/useMainViewState' import { toGibibytes } from '@/utils/converter' +import { assistantsAtom } from '@/helpers/atoms/Assistant.atom' import { serverEnabledAtom } from '@/helpers/atoms/LocalServer.atom' +import { downloadedModelsAtom } from '@/helpers/atoms/Model.atom' import { totalRamAtom } from '@/helpers/atoms/SystemBar.atom' type Props = { @@ -49,7 +49,9 @@ const ExploreModelItemHeader: React.FC = ({ model, onClick, open }) => { const { modelDownloadStateAtom } = useDownloadState() const { requestCreateNewThread } = useCreateNewThread() const totalRam = useAtomValue(totalRamAtom) + const serverEnabled = useAtomValue(serverEnabledAtom) + const assistants = useAtomValue(assistantsAtom) const downloadAtom = useMemo( () => atom((get) => get(modelDownloadStateAtom)[model.id]), @@ -60,7 +62,6 @@ const ExploreModelItemHeader: React.FC = ({ model, onClick, open }) => { const onDownloadClick = useCallback(() => { downloadModel(model) - // eslint-disable-next-line react-hooks/exhaustive-deps }, [model]) const isDownloaded = downloadedModels.find((md) => md.id === model.id) != null @@ -70,7 +71,6 @@ const ExploreModelItemHeader: React.FC = ({ model, onClick, open }) => { ) const onUseModelClick = useCallback(async () => { - const assistants = await getAssistants() if (assistants.length === 0) { alert('No assistant available') return diff --git a/web/screens/ExploreModels/ModelVersionItem/index.tsx b/web/screens/ExploreModels/ModelVersionItem/index.tsx index 50d71b161..3a9385670 100644 --- a/web/screens/ExploreModels/ModelVersionItem/index.tsx +++ b/web/screens/ExploreModels/ModelVersionItem/index.tsx @@ -10,9 +10,11 @@ import { MainViewState } from '@/constants/screens' import useDownloadModel from '@/hooks/useDownloadModel' import { useDownloadState } from '@/hooks/useDownloadState' -import { useGetDownloadedModels } from '@/hooks/useGetDownloadedModels' + import { useMainViewState } from '@/hooks/useMainViewState' +import { downloadedModelsAtom } from '@/helpers/atoms/Model.atom' + type Props = { model: Model isRecommended: boolean @@ -20,7 +22,7 @@ type Props = { const ModelVersionItem: React.FC = ({ model }) => { const { downloadModel } = useDownloadModel() - const { downloadedModels } = useGetDownloadedModels() + const downloadedModels = useAtomValue(downloadedModelsAtom) const { setMainViewState } = useMainViewState() const isDownloaded = downloadedModels.find( diff --git a/web/screens/ExploreModels/index.tsx b/web/screens/ExploreModels/index.tsx index 398b2db08..7002c60b7 100644 --- a/web/screens/ExploreModels/index.tsx +++ b/web/screens/ExploreModels/index.tsx @@ -1,4 +1,4 @@ -import { useState } from 'react' +import { useCallback, useState } from 'react' import { openExternalUrl } from '@janhq/core' import { @@ -12,24 +12,24 @@ import { SelectItem, } from '@janhq/uikit' +import { useAtomValue } from 'jotai' import { SearchIcon } from 'lucide-react' -import Loader from '@/containers/Loader' - -import { useGetConfiguredModels } from '@/hooks/useGetConfiguredModels' - -import { useGetDownloadedModels } from '@/hooks/useGetDownloadedModels' - import ExploreModelList from './ExploreModelList' +import { + configuredModelsAtom, + downloadedModelsAtom, +} from '@/helpers/atoms/Model.atom' + const ExploreModelsScreen = () => { - const { loading, models } = useGetConfiguredModels() + const configuredModels = useAtomValue(configuredModelsAtom) + const downloadedModels = useAtomValue(downloadedModelsAtom) const [searchValue, setsearchValue] = useState('') - const { downloadedModels } = useGetDownloadedModels() const [sortSelected, setSortSelected] = useState('All Models') const sortMenu = ['All Models', 'Recommended', 'Downloaded'] - const filteredModels = models.filter((x) => { + const filteredModels = configuredModels.filter((x) => { if (sortSelected === 'Downloaded') { return ( x.name.toLowerCase().includes(searchValue.toLowerCase()) && @@ -45,11 +45,9 @@ const ExploreModelsScreen = () => { } }) - const onHowToImportModelClick = () => { + const onHowToImportModelClick = useCallback(() => { openExternalUrl('https://jan.ai/guides/using-models/import-manually/') - } - - if (loading) return + }, []) return (
{ From ee5a44a799b42bab9e8b291e52c1bf6a4b7dd0e5 Mon Sep 17 00:00:00 2001 From: Louis Date: Mon, 5 Feb 2024 15:53:11 +0700 Subject: [PATCH 29/53] fix: umami analytics send app loaded event (#1928) --- web/utils/umami.tsx | 80 +++++++++++++-------------------------------- 1 file changed, 23 insertions(+), 57 deletions(-) diff --git a/web/utils/umami.tsx b/web/utils/umami.tsx index ac9e70304..277ae1223 100644 --- a/web/utils/umami.tsx +++ b/web/utils/umami.tsx @@ -1,65 +1,31 @@ import { useEffect } from 'react' -import Script from 'next/script' - -// Define the type for the umami data object -interface UmamiData { - version: string -} - -declare global { - interface Window { - umami: - | { - track: (event: string, data?: UmamiData) => void - } - | undefined - } -} - const Umami = () => { - const appVersion = VERSION - const analyticsHost = ANALYTICS_HOST - const analyticsId = ANALYTICS_ID - useEffect(() => { - if (!appVersion || !analyticsHost || !analyticsId) return - const ping = () => { - // Check if umami is defined before ping - if (window.umami !== null && typeof window.umami !== 'undefined') { - window.umami.track(appVersion, { - version: appVersion, - }) - } - } + if (!VERSION || !ANALYTICS_HOST || !ANALYTICS_ID) return + fetch(ANALYTICS_HOST, { + method: 'POST', + // eslint-disable-next-line @typescript-eslint/naming-convention + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + payload: { + website: ANALYTICS_ID, + hostname: 'jan.ai', + screen: `${screen.width}x${screen.height}`, + language: navigator.language, + referrer: 'index.html', + data: { version: VERSION }, + type: 'event', + title: document.title, + url: 'index.html', + name: VERSION, + }, + type: 'event', + }), + }) + }, []) - // Wait for umami to be defined before ping - if (window.umami !== null && typeof window.umami !== 'undefined') { - ping() - } else { - // Listen for umami script load event - document.addEventListener('umami:loaded', ping) - } - - // Cleanup function to remove event listener if the component unmounts - return () => { - document.removeEventListener('umami:loaded', ping) - } - }, [appVersion, analyticsHost, analyticsId]) - - return ( - <> - {appVersion && analyticsHost && analyticsId && ( -