diff --git a/.github/ISSUE_TEMPLATE/epic-request.md b/.github/ISSUE_TEMPLATE/epic-request.md index c52fbc6c1..51941c412 100644 --- a/.github/ISSUE_TEMPLATE/epic-request.md +++ b/.github/ISSUE_TEMPLATE/epic-request.md @@ -7,22 +7,19 @@ assignees: '' --- -**Motivation** +## Motivation - -**Specs & Designs** +## Specs - -**In Scope** +## Designs +[Figma](link) + +## Tasklist +- [ ] + +## Not in Scope - -**Not in Scope** -- - -**Tasklist** -> Note: All issues need to share the same `milestone` as this epic -- - -**Related Milestones** -- Past -- Future +## Appendix diff --git a/README.md b/README.md index e5b9c4a4f..f5761903a 100644 --- a/README.md +++ b/README.md @@ -70,7 +70,7 @@ Jan is an open-source ChatGPT alternative that runs 100% offline on your compute Experimental (Nighlty Build) - + Github action artifactory diff --git a/docs/docs/guides/04-using-models/02-import-manually.mdx b/docs/docs/guides/04-using-models/02-import-manually.mdx index cae294b40..6fc7e04a3 100644 --- a/docs/docs/guides/04-using-models/02-import-manually.mdx +++ b/docs/docs/guides/04-using-models/02-import-manually.mdx @@ -1,24 +1,42 @@ --- title: Import Models Manually +slug: /guides/using-models/import-manually +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, + import-models-manually, + ] --- +:::caution +This is currently under development. +::: + {/* Imports */} import Tabs from "@theme/Tabs"; import TabItem from "@theme/TabItem"; Jan is compatible with all GGUF models. -If you don't see the model you want in the Hub, or if you have a custom model, you can add it to Jan. +If you can not find the model you want in the Hub or have a custom model you want to use, you can import it manually. -In this guide we will use our latest model, [Trinity](https://huggingface.co/janhq/trinity-v1-GGUF), as an example. +In this guide, we will show you how to import a GGUF model from [HuggingFace](https://huggingface.co/), using our lastest model, [Trinity](https://huggingface.co/janhq/trinity-v1-GGUF), as an example. > We are fast shipping a UI to make this easier, but it's a bit manual for now. Apologies. -## 1. Create a model folder +## Steps to Manually Import a Model -Navigate to the `~/jan/models` folder on your computer. +### 1. Create a Model Folder -In `App Settings`, go to `Advanced`, then `Open App Directory`. +Navigate to the `~/jan/models` folder. You can find this folder by going to `App Settings` > `Advanced` > `Open App Directory`. @@ -70,11 +88,11 @@ In the `models` folder, create a folder with the name of the model. -## 2. Create a model JSON +### 2. Create a Model JSON -Jan follows a folder-based, [standard model template](/specs/models) called a `model.json` to persist the model configurations on your local filesystem. +Jan follows a folder-based, [standard model template](/docs/engineering/models) called a `model.json` to persist the model configurations on your local filesystem. -This means you can easily & transparently reconfigure your models and export and share your preferences. +This means that you can easily reconfigure your models, export them, and share your preferences transparently. @@ -89,7 +107,7 @@ This means you can easily & transparently reconfigure your models and export and ```sh cd trinity-v1-7b - touch model.json + echo {} > model.json ``` @@ -103,48 +121,53 @@ This means you can easily & transparently reconfigure your models and export and -Copy the following configurations into the `model.json`. +Edit `model.json` and include the following configurations: -1. Make sure the `id` property is the same as the folder name you created. -2. Make sure the `source_url` property is the direct binary download link ending in `.gguf`. In HuggingFace, you can find the directl links in `Files and versions` tab. -3. Ensure you are using the correct `prompt_template`. This is usually provided in the HuggingFace model's description page. - -> Note: Currently, the filename must be `model.json` and the ID has to be equal to the foldername. In the `model.json`, you have to include the `state` property and set it to `ready` for Jan to recognize the model. +- Ensure the filename must be `model.json`. +- Ensure the `id` property matches the folder name you created. +- Ensure the GGUF filename should match the `id` property exactly. +- Ensure the `source_url` property is the direct binary download link ending in `.gguf`. In HuggingFace, you can find the direct links in `Files and versions` tab. +- Ensure you are using the correct `prompt_template`. This is usually provided in the HuggingFace model's description page. +- Ensure the `state` property is set to `ready`. ```js { + // highlight-start "source_url": "https://huggingface.co/janhq/trinity-v1-GGUF/resolve/main/trinity-v1.Q4_K_M.gguf", "id": "trinity-v1-7b", + // highlight-end "object": "model", - "name": "Trinity 7B Q4", + "name": "Trinity-v1 7B Q4", "version": "1.0", "description": "Trinity is an experimental model merge of GreenNodeLM & LeoScorpius using the Slerp method. Recommended for daily assistance purposes.", "format": "gguf", "settings": { - "ctx_len": 2048, - "prompt_template": "<|im_start|>system\n{system_message}<|im_end|>\n<|im_start|>user\n{prompt}<|im_end|>\n<|im_start|>assistant" + "ctx_len": 4096, + // highlight-next-line + "prompt_template": "{system_message}\n### Instruction:\n{prompt}\n### Response:" }, "parameters": { - "max_tokens": 2048 + "max_tokens": 4096 }, "metadata": { "author": "Jan", - "tags": ["7B", "Merged", "Featured"], + "tags": ["7B", "Merged"], "size": 4370000000 }, + // highlight-next-line "state": "ready", "engine": "nitro" } ``` -## 3. Download your model +### 3. Download the Model -Restart the Jan application and look for your model in the Hub. +Restart Jan and navigate to the Hub. Locate your model and click the `Download` button to download the model binary. -Click the green `download` button to download your actual model binary. This pulls from the `source_url` you provided above. +![image](assets/download-model.png) -![image](https://hackmd.io/_uploads/HJLAqvwI6.png) +Your model is now ready to use in Jan. -There you go! You are ready to use your model. +## Assistance and Support -If you have any questions or want to request for more preconfigured GGUF models, please message us in [Discord](https://discord.gg/Dt7MxDyNNZ). +If you have questions or are looking for more preconfigured GGUF models, please feel free to join our [Discord community](https://discord.gg/Dt7MxDyNNZ) for support, updates, and discussions. diff --git a/docs/docs/guides/04-using-models/assets/download-model.png b/docs/docs/guides/04-using-models/assets/download-model.png new file mode 100644 index 000000000..b2fddaee9 Binary files /dev/null and b/docs/docs/guides/04-using-models/assets/download-model.png differ diff --git a/web/containers/Checkbox/index.tsx b/web/containers/Checkbox/index.tsx index 8cb2f5d70..de18ca052 100644 --- a/web/containers/Checkbox/index.tsx +++ b/web/containers/Checkbox/index.tsx @@ -1,48 +1,33 @@ -import { FieldValues, UseFormRegister } from 'react-hook-form' +import React from 'react' -import { ModelRuntimeParams } from '@janhq/core' import { Switch } from '@janhq/uikit' import { useAtomValue } from 'jotai' import useUpdateModelParameters from '@/hooks/useUpdateModelParameters' -import { - getActiveThreadIdAtom, - getActiveThreadModelRuntimeParamsAtom, -} from '@/helpers/atoms/Thread.atom' +import { getActiveThreadIdAtom } from '@/helpers/atoms/Thread.atom' type Props = { name: string title: string checked: boolean - register: UseFormRegister } -const Checkbox: React.FC = ({ name, title, checked, register }) => { +const Checkbox: React.FC = ({ name, title, checked }) => { const { updateModelParameter } = useUpdateModelParameters() const threadId = useAtomValue(getActiveThreadIdAtom) - const activeModelParams = useAtomValue(getActiveThreadModelRuntimeParamsAtom) const onCheckedChange = (checked: boolean) => { - if (!threadId || !activeModelParams) return + if (!threadId) return - const updatedModelParams: ModelRuntimeParams = { - ...activeModelParams, - [name]: checked, - } - - updateModelParameter(threadId, updatedModelParams) + updateModelParameter(threadId, name, checked) } return (
- - +

{title}

+
) } diff --git a/web/containers/DropdownListSidebar/index.tsx b/web/containers/DropdownListSidebar/index.tsx index 86203ddff..e40599344 100644 --- a/web/containers/DropdownListSidebar/index.tsx +++ b/web/containers/DropdownListSidebar/index.tsx @@ -9,10 +9,6 @@ import { SelectItem, SelectTrigger, SelectValue, - Tooltip, - TooltipContent, - TooltipTrigger, - TooltipArrow, Input, } from '@janhq/uikit' @@ -32,14 +28,22 @@ import useRecommendedModel from '@/hooks/useRecommendedModel' import { toGigabytes } from '@/utils/converter' -import { activeThreadAtom, threadStatesAtom } from '@/helpers/atoms/Thread.atom' +import { + activeThreadAtom, + getActiveThreadIdAtom, + setThreadModelParamsAtom, + threadStatesAtom, +} from '@/helpers/atoms/Thread.atom' export const selectedModelAtom = atom(undefined) export default function DropdownListSidebar() { - const setSelectedModel = useSetAtom(selectedModelAtom) - const threadStates = useAtomValue(threadStatesAtom) + const activeThreadId = useAtomValue(getActiveThreadIdAtom) const activeThread = useAtomValue(activeThreadAtom) + const threadStates = useAtomValue(threadStatesAtom) + const setSelectedModel = useSetAtom(selectedModelAtom) + const setThreadModelParams = useSetAtom(setThreadModelParamsAtom) + const [selected, setSelected] = useState() const { setMainViewState } = useMainViewState() const [openAISettings, setOpenAISettings] = useState< @@ -58,83 +62,93 @@ export default function DropdownListSidebar() { useEffect(() => { setSelected(recommendedModel) setSelectedModel(recommendedModel) - }, [recommendedModel, setSelectedModel]) + + if (activeThread) { + const finishInit = threadStates[activeThread.id].isFinishInit ?? true + if (finishInit) return + const modelParams = { + ...recommendedModel?.parameters, + ...recommendedModel?.settings, + } + setThreadModelParams(activeThread.id, modelParams) + } + }, [ + recommendedModel, + activeThread, + setSelectedModel, + setThreadModelParams, + threadStates, + ]) const onValueSelected = useCallback( (modelId: string) => { const model = downloadedModels.find((m) => m.id === modelId) setSelected(model) setSelectedModel(model) + + if (activeThreadId) { + const modelParams = { + ...model?.parameters, + ...model?.settings, + } + setThreadModelParams(activeThreadId, modelParams) + } }, - [downloadedModels, setSelectedModel] + [downloadedModels, activeThreadId, setSelectedModel, setThreadModelParams] ) if (!activeThread) { return null } - const finishInit = threadStates[activeThread.id].isFinishInit ?? true return ( - - - + + + {downloadedModels.filter((x) => x.id === selected?.id)[0]?.name} + + + +
+ + Local +
+
+ {downloadedModels.length === 0 ? ( +
+

{`Oops, you don't have a model yet.`}

-
- {downloadedModels.length === 0 ? ( -
-

{`Oops, you don't have a model yet.`}

-
- ) : ( - - {downloadedModels.map((x, i) => ( - -
- {x.name} - - {toGigabytes(x.metadata.size)} - -
-
- ))} -
- )} -
-
- -
- - - - - {finishInit && ( - - Start a new thread to change the model - - - )} + ) : ( + + {downloadedModels.map((x, i) => ( + +
+ {x.name} + + {toGigabytes(x.metadata.size)} + +
+
+ ))} +
+ )} +
+
+ +
+ + {selected?.engine === InferenceEngine.openai && (
@@ -154,6 +168,6 @@ export default function DropdownListSidebar() { />
)} - + ) } diff --git a/web/containers/ModelConfigInput/index.tsx b/web/containers/ModelConfigInput/index.tsx new file mode 100644 index 000000000..caa4f7fa3 --- /dev/null +++ b/web/containers/ModelConfigInput/index.tsx @@ -0,0 +1,43 @@ +import { Textarea } from '@janhq/uikit' + +import { useAtomValue } from 'jotai' + +import useUpdateModelParameters from '@/hooks/useUpdateModelParameters' + +import { getActiveThreadIdAtom } from '@/helpers/atoms/Thread.atom' + +type Props = { + title: string + name: string + placeholder: string + value: string +} + +const ModelConfigInput: React.FC = ({ + title, + name, + value, + placeholder, +}) => { + const { updateModelParameter } = useUpdateModelParameters() + const threadId = useAtomValue(getActiveThreadIdAtom) + + const onValueChanged = (e: React.ChangeEvent) => { + if (!threadId) return + + updateModelParameter(threadId, name, e.target.value) + } + + return ( +
+

{title}

+