parseInt(v),
- })}
value={[value]}
onValueChange={onValueChanged}
min={min}
diff --git a/web/helpers/atoms/Thread.atom.ts b/web/helpers/atoms/Thread.atom.ts
index 5ec7173b1..c91ec30ef 100644
--- a/web/helpers/atoms/Thread.atom.ts
+++ b/web/helpers/atoms/Thread.atom.ts
@@ -1,5 +1,6 @@
import {
ModelRuntimeParams,
+ ModelSettingParams,
Thread,
ThreadContent,
ThreadState,
@@ -110,30 +111,26 @@ export const activeThreadAtom = atom((get) =>
/**
* Store model params at thread level settings
*/
-export const threadModelRuntimeParamsAtom = atom<
- Record
->({})
+export const threadModelParamsAtom = atom>({})
-export const getActiveThreadModelRuntimeParamsAtom = atom<
- ModelRuntimeParams | undefined
->((get) => {
- const threadId = get(activeThreadIdAtom)
- if (!threadId) {
- console.debug('Active thread id is undefined')
- return undefined
+export type ModelParams = ModelRuntimeParams | ModelSettingParams
+
+export const getActiveThreadModelParamsAtom = atom(
+ (get) => {
+ const threadId = get(activeThreadIdAtom)
+ if (!threadId) {
+ console.debug('Active thread id is undefined')
+ return undefined
+ }
+
+ return get(threadModelParamsAtom)[threadId]
}
-
- return get(threadModelRuntimeParamsAtom)[threadId]
-})
-
-export const getThreadModelRuntimeParamsAtom = atom(
- (get, threadId: string) => get(threadModelRuntimeParamsAtom)[threadId]
)
-export const setThreadModelRuntimeParamsAtom = atom(
+export const setThreadModelParamsAtom = atom(
null,
- (get, set, threadId: string, params: ModelRuntimeParams) => {
- const currentState = { ...get(threadModelRuntimeParamsAtom) }
+ (get, set, threadId: string, params: ModelParams) => {
+ const currentState = { ...get(threadModelParamsAtom) }
currentState[threadId] = params
console.debug(
`Update model params for thread ${threadId}, ${JSON.stringify(
@@ -142,6 +139,6 @@ export const setThreadModelRuntimeParamsAtom = atom(
2
)}`
)
- set(threadModelRuntimeParamsAtom, currentState)
+ set(threadModelParamsAtom, currentState)
}
)
diff --git a/web/hooks/useCreateNewThread.ts b/web/hooks/useCreateNewThread.ts
index 7f81c1e83..325e7621c 100644
--- a/web/hooks/useCreateNewThread.ts
+++ b/web/hooks/useCreateNewThread.ts
@@ -19,7 +19,6 @@ import {
setActiveThreadIdAtom,
threadStatesAtom,
updateThreadAtom,
- setThreadModelRuntimeParamsAtom,
} from '@/helpers/atoms/Thread.atom'
const createNewThreadAtom = atom(null, (get, set, newThread: Thread) => {
@@ -45,10 +44,6 @@ export const useCreateNewThread = () => {
const createNewThread = useSetAtom(createNewThreadAtom)
const setActiveThreadId = useSetAtom(setActiveThreadIdAtom)
const updateThread = useSetAtom(updateThreadAtom)
- const setThreadModelRuntimeParams = useSetAtom(
- setThreadModelRuntimeParamsAtom
- )
-
const { deleteThread } = useDeleteThread()
const requestCreateNewThread = async (
@@ -77,10 +72,7 @@ export const useCreateNewThread = () => {
model: {
id: modelId,
settings: {},
- parameters: {
- stream: true,
- max_tokens: 1024,
- },
+ parameters: {},
engine: undefined,
},
instructions: assistant.instructions,
@@ -94,7 +86,6 @@ export const useCreateNewThread = () => {
created: createdAt,
updated: createdAt,
}
- setThreadModelRuntimeParams(thread.id, assistantInfo.model.parameters)
// add the new thread on top of the thread list to the state
createNewThread(thread)
diff --git a/web/hooks/useDeleteThread.ts b/web/hooks/useDeleteThread.ts
index 168d2dc33..2aac2b207 100644
--- a/web/hooks/useDeleteThread.ts
+++ b/web/hooks/useDeleteThread.ts
@@ -22,6 +22,7 @@ import {
setActiveThreadIdAtom,
deleteThreadStateAtom,
threadStatesAtom,
+ updateThreadStateLastMessageAtom,
} from '@/helpers/atoms/Thread.atom'
export default function useDeleteThread() {
@@ -33,21 +34,23 @@ export default function useDeleteThread() {
const deleteMessages = useSetAtom(deleteChatMessagesAtom)
const cleanMessages = useSetAtom(cleanChatMessagesAtom)
const deleteThreadState = useSetAtom(deleteThreadStateAtom)
-
const threadStates = useAtomValue(threadStatesAtom)
+ const updateThreadLastMessage = useSetAtom(updateThreadStateLastMessageAtom)
const cleanThread = async (threadId: string) => {
if (threadId) {
const thread = threads.filter((c) => c.id === threadId)[0]
cleanMessages(threadId)
- if (thread)
+ if (thread) {
await extensionManager
.get(ExtensionType.Conversational)
?.writeMessages(
threadId,
messages.filter((msg) => msg.role === ChatCompletionRole.System)
)
+ updateThreadLastMessage(threadId, undefined)
+ }
}
}
diff --git a/web/hooks/useRecommendedModel.ts b/web/hooks/useRecommendedModel.ts
index 6dc5771f1..dd474d0b5 100644
--- a/web/hooks/useRecommendedModel.ts
+++ b/web/hooks/useRecommendedModel.ts
@@ -42,6 +42,7 @@ export default function useRecommendedModel() {
const getRecommendedModel = useCallback(async (): Promise<
Model | undefined
> => {
+ const models = await getAndSortDownloadedModels()
if (!activeThread) {
return
}
@@ -49,7 +50,6 @@ export default function useRecommendedModel() {
const finishInit = threadStates[activeThread.id].isFinishInit ?? true
if (finishInit) {
const modelId = activeThread.assistants[0]?.model.id
- const models = await getAndSortDownloadedModels()
const model = models.find((model) => model.id === modelId)
if (model) {
@@ -60,7 +60,6 @@ export default function useRecommendedModel() {
} else {
const modelId = activeThread.assistants[0]?.model.id
if (modelId !== '*') {
- const models = await getAndSortDownloadedModels()
const model = models.find((model) => model.id === modelId)
if (model) {
@@ -78,7 +77,7 @@ export default function useRecommendedModel() {
}
// sort the model, for display purpose
- const models = await getAndSortDownloadedModels()
+
if (models.length === 0) {
// if we have no downloaded models, then can't recommend anything
console.debug("No downloaded models, can't recommend anything")
diff --git a/web/hooks/useSendChatMessage.ts b/web/hooks/useSendChatMessage.ts
index 8913104d3..fb9761d60 100644
--- a/web/hooks/useSendChatMessage.ts
+++ b/web/hooks/useSendChatMessage.ts
@@ -24,6 +24,8 @@ import { currentPromptAtom } from '@/containers/Providers/Jotai'
import { toaster } from '@/containers/Toast'
+import { toRuntimeParams, toSettingParams } from '@/utils/model_param'
+
import { useActiveModel } from './useActiveModel'
import { extensionManager } from '@/extension/ExtensionManager'
@@ -33,7 +35,7 @@ import {
} from '@/helpers/atoms/ChatMessage.atom'
import {
activeThreadAtom,
- getActiveThreadModelRuntimeParamsAtom,
+ getActiveThreadModelParamsAtom,
threadStatesAtom,
updateThreadAtom,
updateThreadInitSuccessAtom,
@@ -56,7 +58,7 @@ export default function useSendChatMessage() {
const modelRef = useRef()
const threadStates = useAtomValue(threadStatesAtom)
const updateThreadInitSuccess = useSetAtom(updateThreadInitSuccessAtom)
- const activeModelParams = useAtomValue(getActiveThreadModelRuntimeParamsAtom)
+ const activeModelParams = useAtomValue(getActiveThreadModelParamsAtom)
useEffect(() => {
modelRef.current = activeModel
@@ -128,17 +130,22 @@ export default function useSendChatMessage() {
}
const sendChatMessage = async () => {
- if (!currentPrompt || currentPrompt.trim().length === 0) {
- return
- }
+ if (!currentPrompt || currentPrompt.trim().length === 0) return
+
if (!activeThread) {
console.error('No active thread')
return
}
const activeThreadState = threadStates[activeThread.id]
+ const runtimeParams = toRuntimeParams(activeModelParams)
+ const settingParams = toSettingParams(activeModelParams)
+
// if the thread is not initialized, we need to initialize it first
- if (!activeThreadState.isFinishInit) {
+ if (
+ !activeThreadState.isFinishInit ||
+ activeThread.assistants[0].model.id !== selectedModel?.id
+ ) {
if (!selectedModel) {
toaster({ title: 'Please select a model' })
return
@@ -147,11 +154,6 @@ export default function useSendChatMessage() {
const assistantName = activeThread.assistants[0].assistant_name ?? ''
const instructions = activeThread.assistants[0].instructions ?? ''
- const modelParams: ModelRuntimeParams = {
- ...selectedModel.parameters,
- ...activeModelParams,
- }
-
const updatedThread: Thread = {
...activeThread,
assistants: [
@@ -161,8 +163,8 @@ export default function useSendChatMessage() {
instructions: instructions,
model: {
id: selectedModel.id,
- settings: selectedModel.settings,
- parameters: modelParams,
+ settings: settingParams,
+ parameters: runtimeParams,
engine: selectedModel.engine,
},
},
@@ -208,13 +210,17 @@ export default function useSendChatMessage() {
const msgId = ulid()
const modelRequest = selectedModel ?? activeThread.assistants[0].model
+ if (runtimeParams.stream == null) {
+ runtimeParams.stream = true
+ }
const messageRequest: MessageRequest = {
id: msgId,
threadId: activeThread.id,
messages,
model: {
...modelRequest,
- ...(activeModelParams ? { parameters: activeModelParams } : {}),
+ settings: settingParams,
+ parameters: runtimeParams,
},
}
const timestamp = Date.now()
diff --git a/web/hooks/useThreads.ts b/web/hooks/useThreads.ts
index a43a43ea0..646969674 100644
--- a/web/hooks/useThreads.ts
+++ b/web/hooks/useThreads.ts
@@ -1,6 +1,5 @@
import {
ExtensionType,
- ModelRuntimeParams,
Thread,
ThreadState,
ConversationalExtension,
@@ -12,7 +11,8 @@ import useSetActiveThread from './useSetActiveThread'
import { extensionManager } from '@/extension/ExtensionManager'
import {
- threadModelRuntimeParamsAtom,
+ ModelParams,
+ threadModelParamsAtom,
threadStatesAtom,
threadsAtom,
} from '@/helpers/atoms/Thread.atom'
@@ -21,7 +21,7 @@ const useThreads = () => {
const [threadStates, setThreadStates] = useAtom(threadStatesAtom)
const [threads, setThreads] = useAtom(threadsAtom)
const [threadModelRuntimeParams, setThreadModelRuntimeParams] = useAtom(
- threadModelRuntimeParamsAtom
+ threadModelParamsAtom
)
const { setActiveThread } = useSetActiveThread()
@@ -29,7 +29,7 @@ const useThreads = () => {
try {
const localThreads = await getLocalThreads()
const localThreadStates: Record = {}
- const threadModelParams: Record = {}
+ const threadModelParams: Record = {}
localThreads.forEach((thread) => {
if (thread.id != null) {
@@ -42,9 +42,12 @@ const useThreads = () => {
isFinishInit: true,
}
- // model params
const modelParams = thread.assistants?.[0]?.model?.parameters
- threadModelParams[thread.id] = modelParams
+ const engineParams = thread.assistants?.[0]?.model?.settings
+ threadModelParams[thread.id] = {
+ ...modelParams,
+ ...engineParams,
+ }
}
})
diff --git a/web/hooks/useUpdateModelParameters.ts b/web/hooks/useUpdateModelParameters.ts
index d6bb2d0db..db7c759fc 100644
--- a/web/hooks/useUpdateModelParameters.ts
+++ b/web/hooks/useUpdateModelParameters.ts
@@ -1,31 +1,34 @@
+/* eslint-disable @typescript-eslint/no-explicit-any */
import {
ConversationalExtension,
ExtensionType,
- ModelRuntimeParams,
Thread,
+ ThreadAssistantInfo,
} from '@janhq/core'
import { useAtomValue, useSetAtom } from 'jotai'
+import { toRuntimeParams, toSettingParams } from '@/utils/model_param'
+
import { extensionManager } from '@/extension'
import {
+ ModelParams,
activeThreadStateAtom,
- setThreadModelRuntimeParamsAtom,
+ getActiveThreadModelParamsAtom,
+ setThreadModelParamsAtom,
threadsAtom,
- updateThreadAtom,
} from '@/helpers/atoms/Thread.atom'
export default function useUpdateModelParameters() {
const threads = useAtomValue(threadsAtom)
- const updateThread = useSetAtom(updateThreadAtom)
- const setThreadModelRuntimeParams = useSetAtom(
- setThreadModelRuntimeParamsAtom
- )
+ const setThreadModelParams = useSetAtom(setThreadModelParamsAtom)
const activeThreadState = useAtomValue(activeThreadStateAtom)
+ const activeModelParams = useAtomValue(getActiveThreadModelParamsAtom)
const updateModelParameter = async (
threadId: string,
- params: ModelRuntimeParams
+ name: string,
+ value: number | boolean | string
) => {
const thread = threads.find((thread) => thread.id === threadId)
if (!thread) {
@@ -37,27 +40,37 @@ export default function useUpdateModelParameters() {
console.error('No active thread')
return
}
+ const updatedModelParams: ModelParams = {
+ ...activeModelParams,
+ [name]: value,
+ }
// update the state
- setThreadModelRuntimeParams(thread.id, params)
+ setThreadModelParams(thread.id, updatedModelParams)
if (!activeThreadState.isFinishInit) {
// if thread is not initialized, we don't need to update thread.json
return
}
- const assistants = thread.assistants.map((assistant) => {
- assistant.model.parameters = params
- return assistant
- })
+ const assistants = thread.assistants.map(
+ (assistant: ThreadAssistantInfo) => {
+ const runtimeParams = toRuntimeParams(updatedModelParams)
+ const settingParams = toSettingParams(updatedModelParams)
+
+ assistant.model.parameters = runtimeParams
+ assistant.model.settings = settingParams
+ return assistant
+ }
+ )
// update thread
const updatedThread: Thread = {
...thread,
assistants,
}
- updateThread(updatedThread)
- extensionManager
+
+ await extensionManager
.get(ExtensionType.Conversational)
?.saveThread(updatedThread)
}
diff --git a/web/screens/Chat/EngineSetting/index.tsx b/web/screens/Chat/EngineSetting/index.tsx
new file mode 100644
index 000000000..ed3aa7953
--- /dev/null
+++ b/web/screens/Chat/EngineSetting/index.tsx
@@ -0,0 +1,31 @@
+import { useAtomValue } from 'jotai'
+
+import { selectedModelAtom } from '@/containers/DropdownListSidebar'
+
+import { getConfigurationsData } from '@/utils/componentSettings'
+import { toSettingParams } from '@/utils/model_param'
+
+import settingComponentBuilder from '../ModelSetting/settingComponentBuilder'
+
+import { getActiveThreadModelParamsAtom } from '@/helpers/atoms/Thread.atom'
+
+const EngineSetting: React.FC = () => {
+ const activeModelParams = useAtomValue(getActiveThreadModelParamsAtom)
+ const selectedModel = useAtomValue(selectedModelAtom)
+
+ if (!selectedModel || !activeModelParams) return null
+
+ const modelSettingParams = toSettingParams(activeModelParams)
+
+ const componentData = getConfigurationsData(modelSettingParams)
+
+ componentData.sort((a, b) => a.title.localeCompare(b.title))
+
+ return (
+
+ )
+}
+
+export default EngineSetting
diff --git a/web/screens/Chat/ModelSetting/index.tsx b/web/screens/Chat/ModelSetting/index.tsx
index dbd4c7892..97e0d4ecd 100644
--- a/web/screens/Chat/ModelSetting/index.tsx
+++ b/web/screens/Chat/ModelSetting/index.tsx
@@ -1,47 +1,33 @@
-import { useForm } from 'react-hook-form'
-
-import { ModelRuntimeParams } from '@janhq/core'
+import React from 'react'
import { useAtomValue } from 'jotai'
-import { presetConfiguration } from './predefinedComponent'
-import settingComponentBuilder, {
- SettingComponentData,
-} from './settingComponentBuilder'
+import { selectedModelAtom } from '@/containers/DropdownListSidebar'
-import { getActiveThreadModelRuntimeParamsAtom } from '@/helpers/atoms/Thread.atom'
+import { getConfigurationsData } from '@/utils/componentSettings'
+import { toRuntimeParams } from '@/utils/model_param'
-export default function ModelSetting() {
- const { register } = useForm()
- const activeModelParams = useAtomValue(getActiveThreadModelRuntimeParamsAtom)
+import settingComponentBuilder from './settingComponentBuilder'
- if (!activeModelParams) {
- return null
- }
+import { getActiveThreadModelParamsAtom } from '@/helpers/atoms/Thread.atom'
- const componentData: SettingComponentData[] = []
- Object.keys(activeModelParams).forEach((key) => {
- const componentSetting = presetConfiguration[key]
+const ModelSetting: React.FC = () => {
+ const activeModelParams = useAtomValue(getActiveThreadModelParamsAtom)
+ const selectedModel = useAtomValue(selectedModelAtom)
- if (componentSetting) {
- if ('value' in componentSetting.controllerData) {
- componentSetting.controllerData.value = Number(
- activeModelParams[key as keyof ModelRuntimeParams]
- )
- } else if ('checked' in componentSetting.controllerData) {
- const checked = activeModelParams[
- key as keyof ModelRuntimeParams
- ] as boolean
+ if (!selectedModel || !activeModelParams) return null
- componentSetting.controllerData.checked = checked
- }
- componentData.push(componentSetting)
- }
- })
+ const modelRuntimeParams = toRuntimeParams(activeModelParams)
+
+ const componentData = getConfigurationsData(modelRuntimeParams)
+
+ componentData.sort((a, b) => a.title.localeCompare(b.title))
return (
)
}
+
+export default React.memo(ModelSetting)
diff --git a/web/screens/Chat/ModelSetting/predefinedComponent.ts b/web/screens/Chat/ModelSetting/predefinedComponent.ts
index d8299ae10..8a102a11f 100644
--- a/web/screens/Chat/ModelSetting/predefinedComponent.ts
+++ b/web/screens/Chat/ModelSetting/predefinedComponent.ts
@@ -1,10 +1,43 @@
import { SettingComponentData } from './settingComponentBuilder'
export const presetConfiguration: Record = {
+ prompt_template: {
+ name: 'prompt_template',
+ title: 'Prompt template',
+ description: 'Prompt template',
+ controllerType: 'input',
+ controllerData: {
+ placeholder: 'Prompt template',
+ value: '',
+ },
+ },
+ stop: {
+ name: 'stop',
+ title: 'Stop',
+ description: 'Stop',
+ controllerType: 'input',
+ controllerData: {
+ placeholder: 'Stop',
+ value: '',
+ },
+ },
+ ctx_len: {
+ name: 'ctx_len',
+ title: 'Context Length',
+ description: 'Context Length',
+ controllerType: 'slider',
+ controllerData: {
+ min: 0,
+ max: 4096,
+ step: 128,
+ value: 1024,
+ },
+ },
max_tokens: {
name: 'max_tokens',
title: 'Max Tokens',
- description: 'Maximum context length the model can handle.',
+ description:
+ 'The maximum number of tokens the model will generate in a single response.',
controllerType: 'slider',
controllerData: {
min: 0,
@@ -56,4 +89,52 @@ export const presetConfiguration: Record = {
value: 0.7,
},
},
+ frequency_penalty: {
+ name: 'frequency_penalty',
+ title: 'Frequency Penalty',
+ description: 'Frequency Penalty',
+ controllerType: 'slider',
+ controllerData: {
+ min: 0,
+ max: 1,
+ step: 0.1,
+ value: 0.7,
+ },
+ },
+ presence_penalty: {
+ name: 'presence_penalty',
+ title: 'Presence Penalty',
+ description: 'Presence Penalty',
+ controllerType: 'slider',
+ controllerData: {
+ min: 0,
+ max: 1,
+ step: 0.1,
+ value: 0.7,
+ },
+ },
+ top_p: {
+ name: 'top_p',
+ title: 'Top P',
+ description: 'Top P',
+ controllerType: 'slider',
+ controllerData: {
+ min: 0,
+ max: 1,
+ step: 0.1,
+ value: 0.95,
+ },
+ },
+ n_parallel: {
+ name: 'n_parallel',
+ title: 'N Parallel',
+ description: 'N Parallel',
+ controllerType: 'slider',
+ controllerData: {
+ min: 1,
+ max: 4,
+ step: 1,
+ value: 1,
+ },
+ },
}
diff --git a/web/screens/Chat/ModelSetting/settingComponentBuilder.tsx b/web/screens/Chat/ModelSetting/settingComponentBuilder.tsx
index 761704241..38240dfa0 100644
--- a/web/screens/Chat/ModelSetting/settingComponentBuilder.tsx
+++ b/web/screens/Chat/ModelSetting/settingComponentBuilder.tsx
@@ -1,17 +1,21 @@
/* eslint-disable no-case-declarations */
-import { FieldValues, UseFormRegister } from 'react-hook-form'
-
import Checkbox from '@/containers/Checkbox'
+import ModelConfigInput from '@/containers/ModelConfigInput'
import Slider from '@/containers/Slider'
-export type ControllerType = 'slider' | 'checkbox'
+export type ControllerType = 'slider' | 'checkbox' | 'input'
export type SettingComponentData = {
name: string
title: string
description: string
controllerType: ControllerType
- controllerData: SliderData | CheckboxData
+ controllerData: SliderData | CheckboxData | InputData
+}
+
+export type InputData = {
+ placeholder: string
+ value: string
}
export type SliderData = {
@@ -25,10 +29,7 @@ type CheckboxData = {
checked: boolean
}
-const settingComponentBuilder = (
- componentData: SettingComponentData[],
- register: UseFormRegister
-) => {
+const settingComponentBuilder = (componentData: SettingComponentData[]) => {
const components = componentData.map((data) => {
switch (data.controllerType) {
case 'slider':
@@ -42,7 +43,18 @@ const settingComponentBuilder = (
step={step}
value={value}
name={data.name}
- register={register}
+ />
+ )
+ case 'input':
+ const { placeholder, value: textValue } =
+ data.controllerData as InputData
+ return (
+
)
case 'checkbox':
@@ -50,7 +62,6 @@ const settingComponentBuilder = (
return (
(true)
-export default function Sidebar() {
+const Sidebar: React.FC = () => {
const showing = useAtomValue(showRightSideBarAtom)
const activeThread = useAtomValue(activeThreadAtom)
const selectedModel = useAtomValue(selectedModelAtom)
const { updateThreadMetadata } = useCreateNewThread()
const threadStates = useAtomValue(threadStatesAtom)
+ const activeModelParams = useAtomValue(getActiveThreadModelParamsAtom)
+ const modelSettingParams = toSettingParams(activeModelParams)
+
const onReviewInFinderClick = async (type: string) => {
if (!activeThread) return
const activeThreadState = threadStates[activeThread.id]
@@ -187,6 +199,17 @@ export default function Sidebar() {
+ {Object.keys(modelSettingParams).length ? (
+