feat(settings): default modal settings

This commit is contained in:
kangfenmao 2024-07-05 13:17:54 +08:00
parent 8398d93b03
commit 7dc0b98f3a
14 changed files with 189 additions and 140 deletions

View File

@ -28,7 +28,7 @@ const Sidebar: FC = () => {
</Menus> </Menus>
</MainMenus> </MainMenus>
<Menus> <Menus>
<StyledLink to="/settings/common"> <StyledLink to="/settings/general">
<Icon className={pathname.startsWith('/settings') ? 'active' : ''}> <Icon className={pathname.startsWith('/settings') ? 'active' : ''}>
<i className="iconfont icon-setting"></i> <i className="iconfont icon-setting"></i>
</Icon> </Icon>

View File

@ -38,46 +38,6 @@ export const SYSTEM_MODELS: Record<string, SystemModel[]> = {
} }
], ],
silicon: [ silicon: [
{
id: 'deepseek-ai/DeepSeek-V2-Chat',
provider: 'silicon',
name: 'DeepSeek-V2-Chat',
group: 'DeepSeek',
temperature: 0.7,
defaultEnabled: true
},
{
id: 'deepseek-ai/DeepSeek-Coder-V2-Instruct',
provider: 'silicon',
name: 'DeepSeek-Coder-V2-Instruct',
group: 'DeepSeek',
temperature: 0.7,
defaultEnabled: true
},
{
id: 'deepseek-ai/deepseek-llm-67b-chat',
provider: 'silicon',
name: 'deepseek-llm-67b-chat',
group: 'DeepSeek',
temperature: 0.7,
defaultEnabled: false
},
{
id: 'google/gemma-2-27b-it',
provider: 'silicon',
name: 'gemma-2-27b-it',
group: 'Gemma',
temperature: 0.7,
defaultEnabled: false
},
{
id: 'google/gemma-2-9b-it',
provider: 'silicon',
name: 'gemma-2-9b-it',
group: 'Gemma',
temperature: 0.7,
defaultEnabled: false
},
{ {
id: 'Qwen/Qwen2-7B-Instruct', id: 'Qwen/Qwen2-7B-Instruct',
provider: 'silicon', provider: 'silicon',
@ -142,6 +102,46 @@ export const SYSTEM_MODELS: Record<string, SystemModel[]> = {
temperature: 0.7, temperature: 0.7,
defaultEnabled: false defaultEnabled: false
}, },
{
id: 'deepseek-ai/DeepSeek-V2-Chat',
provider: 'silicon',
name: 'DeepSeek-V2-Chat',
group: 'DeepSeek',
temperature: 0.7,
defaultEnabled: false
},
{
id: 'deepseek-ai/DeepSeek-Coder-V2-Instruct',
provider: 'silicon',
name: 'DeepSeek-Coder-V2-Instruct',
group: 'DeepSeek',
temperature: 0.7,
defaultEnabled: false
},
{
id: 'deepseek-ai/deepseek-llm-67b-chat',
provider: 'silicon',
name: 'deepseek-llm-67b-chat',
group: 'DeepSeek',
temperature: 0.7,
defaultEnabled: false
},
{
id: 'google/gemma-2-27b-it',
provider: 'silicon',
name: 'gemma-2-27b-it',
group: 'Gemma',
temperature: 0.7,
defaultEnabled: false
},
{
id: 'google/gemma-2-9b-it',
provider: 'silicon',
name: 'gemma-2-9b-it',
group: 'Gemma',
temperature: 0.7,
defaultEnabled: false
},
{ {
id: 'THUDM/glm-4-9b-chat', id: 'THUDM/glm-4-9b-chat',
provider: 'silicon', provider: 'silicon',

View File

@ -9,6 +9,7 @@ import {
removeAssistant, removeAssistant,
updateAssistant updateAssistant
} from '@renderer/store/assistants' } from '@renderer/store/assistants'
import { setDefaultModel as _setDefaultModel, setTopicNamingModel as _setTopicNamingModel } from '@renderer/store/llm'
import { Assistant, Model, Topic } from '@renderer/types' import { Assistant, Model, Topic } from '@renderer/types'
import localforage from 'localforage' import localforage from 'localforage'
@ -19,14 +20,14 @@ export function useAssistants() {
return { return {
assistants, assistants,
addAssistant: (assistant: Assistant) => dispatch(addAssistant(assistant)), addAssistant: (assistant: Assistant) => dispatch(addAssistant(assistant)),
updateAssistant: (assistant: Assistant) => dispatch(updateAssistant(assistant)),
removeAssistant: (id: string) => { removeAssistant: (id: string) => {
dispatch(removeAssistant({ id })) dispatch(removeAssistant({ id }))
const assistant = assistants.find((a) => a.id === id) const assistant = assistants.find((a) => a.id === id)
if (assistant) { if (assistant) {
assistant.topics.forEach((id) => localforage.removeItem(`topic:${id}`)) assistant.topics.forEach((id) => localforage.removeItem(`topic:${id}`))
} }
}, }
updateAssistant: (assistant: Assistant) => dispatch(updateAssistant(assistant))
} }
} }
@ -38,25 +39,22 @@ export function useAssistant(id: string) {
return { return {
assistant, assistant,
model: assistant?.model ?? defaultModel, model: assistant?.model ?? defaultModel,
addTopic: (topic: Topic) => { addTopic: (topic: Topic) => dispatch(_addTopic({ assistantId: assistant.id, topic })),
dispatch(_addTopic({ assistantId: assistant.id, topic })) removeTopic: (topic: Topic) => dispatch(_removeTopic({ assistantId: assistant.id, topic })),
}, updateTopic: (topic: Topic) => dispatch(_updateTopic({ assistantId: assistant.id, topic })),
removeTopic: (topic: Topic) => { removeAllTopics: () => dispatch(_removeAllTopics({ assistantId: assistant.id })),
dispatch(_removeTopic({ assistantId: assistant.id, topic })) setModel: (model: Model) => dispatch(_setModel({ assistantId: assistant.id, model }))
},
updateTopic: (topic: Topic) => {
dispatch(_updateTopic({ assistantId: assistant.id, topic }))
},
removeAllTopics: () => {
dispatch(_removeAllTopics({ assistantId: assistant.id }))
},
setModel: (model: Model) => {
dispatch(_setModel({ assistantId: assistant.id, model }))
}
} }
} }
export function useDefaultModel() { export function useDefaultModel() {
const defaultModel = useAppSelector((state) => state.llm.defaultModel) const { defaultModel, topicNamingModel } = useAppSelector((state) => state.llm)
return { defaultModel } const dispatch = useAppDispatch()
return {
defaultModel,
topicNamingModel,
setDefaultModel: (model: Model) => dispatch(_setDefaultModel({ model })),
setTopicNamingModel: (model: Model) => dispatch(_setTopicNamingModel({ model }))
}
} }

View File

@ -32,5 +32,5 @@ export function useProviderByAssistant(assistant: Assistant) {
} }
export function useSystemProviders() { export function useSystemProviders() {
return useAppSelector((state) => state.llm.providers.filter((p) => p.isSystem)) as unknown as Provider return useAppSelector((state) => state.llm.providers.filter((p) => p.isSystem))
} }

View File

@ -1,11 +1,11 @@
import { FC } from 'react' import { FC } from 'react'
import styled from 'styled-components' import styled from 'styled-components'
const DefaultAssistantSetting: FC = () => { const AssistantSettings: FC = () => {
return <Container>Default Assistant</Container> return <Container>Default Assistant</Container>
} }
const Container = styled.div` const Container = styled.div`
padding: 20px; padding: 20px;
` `
export default DefaultAssistantSetting export default AssistantSettings

View File

@ -1,11 +0,0 @@
import { FC } from 'react'
import styled from 'styled-components'
const DeveloperSetting: FC = () => {
return <Container>Developer</Container>
}
const Container = styled.div`
padding: 20px;
`
export default DeveloperSetting

View File

@ -1,11 +1,11 @@
import { FC } from 'react' import { FC } from 'react'
import styled from 'styled-components' import styled from 'styled-components'
const CommonSettings: FC = () => { const GeneralSettings: FC = () => {
return <Container>Common Settings</Container> return <Container>General Settings</Container>
} }
const Container = styled.div` const Container = styled.div`
padding: 20px; padding: 20px;
` `
export default CommonSettings export default GeneralSettings

View File

@ -0,0 +1,51 @@
import { FC } from 'react'
import { SettingContainer, SettingDivider, SettingTitle } from './components/SettingComponent'
import { Select } from 'antd'
import { useProviders } from '@renderer/hooks/useProvider'
import { useDefaultModel } from '@renderer/hooks/useAssistant'
import { find } from 'lodash'
import { Model } from '@renderer/types'
const ModelSettings: FC = () => {
const { defaultModel, setDefaultModel, setTopicNamingModel } = useDefaultModel()
const providers = useProviders()
const allModels = providers.map((p) => p.models).flat()
return (
<SettingContainer>
<SettingTitle>Default Assistant Model</SettingTitle>
<SettingDivider />
<Select
defaultValue={defaultModel.id}
style={{ width: 200 }}
onChange={(id) => setDefaultModel(find(allModels, { id }) as Model)}
options={providers.map((p) => ({
label: p.name,
title: p.name,
options: p.models.map((m) => ({
label: m.name,
value: m.id
}))
}))}
/>
<div style={{ height: 40 }} />
<SettingTitle>Topic Naming Model</SettingTitle>
<SettingDivider />
<Select
defaultValue={defaultModel.id}
style={{ width: 200 }}
onChange={(id) => setTopicNamingModel(find(allModels, { id }) as Model)}
options={providers.map((p) => ({
label: p.name,
title: p.name,
options: p.models.map((m) => ({
label: m.name,
value: m.id
}))
}))}
/>
</SettingContainer>
)
}
export default ModelSettings

View File

@ -4,8 +4,7 @@ import { FC, useState } from 'react'
import styled from 'styled-components' import styled from 'styled-components'
import ModalProviderSetting from './components/ModalProviderSetting' import ModalProviderSetting from './components/ModalProviderSetting'
// OpenAI Silicon deepseek Groq const ProviderSettings: FC = () => {
const LanguageModelsSettings: FC = () => {
const providers = useSystemProviders() const providers = useSystemProviders()
const [selectedProvider, setSelectedProvider] = useState<Provider>(providers[0]) const [selectedProvider, setSelectedProvider] = useState<Provider>(providers[0])
@ -61,4 +60,4 @@ const ProviderListItem = styled.div`
} }
` `
export default LanguageModelsSettings export default ProviderSettings

View File

@ -2,11 +2,11 @@ import { Navbar, NavbarCenter } from '@renderer/components/app/Navbar'
import { FC } from 'react' import { FC } from 'react'
import { Link, Route, Routes, useLocation } from 'react-router-dom' import { Link, Route, Routes, useLocation } from 'react-router-dom'
import styled from 'styled-components' import styled from 'styled-components'
import CommonSettings from './CommonSettings' import GeneralSettings from './GeneralSettings'
import AboutSettings from './AboutSettings' import AboutSettings from './AboutSettings'
import DefaultAssistantSetting from './DefaultAssistantSetting' import AssistantSettings from './AssistantSettings'
import SystemAssistantSettings from './SystemAssistantSettings' import ModelSettings from './ModelSettings'
import LanguageModelsSettings from './LanguageModelsSettings' import ProviderSettings from './ProviderSettings'
const SettingsPage: FC = () => { const SettingsPage: FC = () => {
const { pathname } = useLocation() const { pathname } = useLocation()
@ -20,17 +20,17 @@ const SettingsPage: FC = () => {
</Navbar> </Navbar>
<ContentContainer> <ContentContainer>
<SettingMenus> <SettingMenus>
<MenuItemLink to="/settings/common"> <MenuItemLink to="/settings/general">
<MenuItem className={isRoute('/settings/common')}>Common Settings</MenuItem> <MenuItem className={isRoute('/settings/general')}>General</MenuItem>
</MenuItemLink> </MenuItemLink>
<MenuItemLink to="/settings/llm"> <MenuItemLink to="/settings/provider">
<MenuItem className={isRoute('/settings/llm')}>Language Model</MenuItem> <MenuItem className={isRoute('/settings/provider')}>Model Provider</MenuItem>
</MenuItemLink> </MenuItemLink>
<MenuItemLink to="/settings/system-assistant"> <MenuItemLink to="/settings/model">
<MenuItem className={isRoute('/settings/system-assistant')}>System Assistant</MenuItem> <MenuItem className={isRoute('/settings/model')}>Model Settings</MenuItem>
</MenuItemLink> </MenuItemLink>
<MenuItemLink to="/settings/default-assistant"> <MenuItemLink to="/settings/assistant">
<MenuItem className={isRoute('/settings/default-assistant')}>Default Assistant</MenuItem> <MenuItem className={isRoute('/settings/assistant')}>Default Assistant</MenuItem>
</MenuItemLink> </MenuItemLink>
<MenuItemLink to="/settings/about"> <MenuItemLink to="/settings/about">
<MenuItem className={isRoute('/settings/about')}>About</MenuItem> <MenuItem className={isRoute('/settings/about')}>About</MenuItem>
@ -38,10 +38,10 @@ const SettingsPage: FC = () => {
</SettingMenus> </SettingMenus>
<SettingContent> <SettingContent>
<Routes> <Routes>
<Route path="common" element={<CommonSettings />} /> <Route path="general" element={<GeneralSettings />} />
<Route path="system-assistant" element={<SystemAssistantSettings />} /> <Route path="provider" element={<ProviderSettings />} />
<Route path="default-assistant" element={<DefaultAssistantSetting />} /> <Route path="model" element={<ModelSettings />} />
<Route path="llm" element={<LanguageModelsSettings />} /> <Route path="assistant" element={<AssistantSettings />} />
<Route path="about" element={<AboutSettings />} /> <Route path="about" element={<AboutSettings />} />
</Routes> </Routes>
</SettingContent> </SettingContent>

View File

@ -1,11 +0,0 @@
import { FC } from 'react'
import styled from 'styled-components'
const SystemAssistantSettings: FC = () => {
return <Container>System Assistant</Container>
}
const Container = styled.div`
padding: 20px;
`
export default SystemAssistantSettings

View File

@ -5,6 +5,7 @@ import { Button, Card, Divider, Input } from 'antd'
import { useProvider } from '@renderer/hooks/useProvider' import { useProvider } from '@renderer/hooks/useProvider'
import ModalListPopup from '@renderer/components/Popups/ModalListPopup' import ModalListPopup from '@renderer/components/Popups/ModalListPopup'
import { groupBy } from 'lodash' import { groupBy } from 'lodash'
import { SettingContainer, SettingSubtitle, SettingTitle } from './SettingComponent'
interface Props { interface Props {
provider: Provider provider: Provider
@ -35,10 +36,10 @@ const ModalProviderSetting: FC<Props> = ({ provider }) => {
} }
return ( return (
<Container> <SettingContainer>
<Title>{provider.name}</Title> <SettingTitle>{provider.name}</SettingTitle>
<Divider style={{ width: '100%', margin: '10px 0' }} /> <Divider style={{ width: '100%', margin: '10px 0' }} />
<SubTitle>API Key</SubTitle> <SettingSubtitle>API Key</SettingSubtitle>
<Input <Input
value={apiKey} value={apiKey}
placeholder="API Key" placeholder="API Key"
@ -46,14 +47,14 @@ const ModalProviderSetting: FC<Props> = ({ provider }) => {
onBlur={onUpdateApiKey} onBlur={onUpdateApiKey}
spellCheck={false} spellCheck={false}
/> />
<SubTitle>API Host</SubTitle> <SettingSubtitle>API Host</SettingSubtitle>
<Input <Input
value={apiHost} value={apiHost}
placeholder="API Host" placeholder="API Host"
onChange={(e) => setApiHost(e.target.value)} onChange={(e) => setApiHost(e.target.value)}
onBlur={onUpdateApiHost} onBlur={onUpdateApiHost}
/> />
<SubTitle>Models</SubTitle> <SettingSubtitle>Models</SettingSubtitle>
{Object.keys(modelGroups).map((group) => ( {Object.keys(modelGroups).map((group) => (
<Card key={group} type="inner" title={group} style={{ marginBottom: '10px' }} size="small"> <Card key={group} type="inner" title={group} style={{ marginBottom: '10px' }} size="small">
{modelGroups[group].map((model) => ( {modelGroups[group].map((model) => (
@ -64,36 +65,10 @@ const ModalProviderSetting: FC<Props> = ({ provider }) => {
<Button type="primary" style={{ width: '100px', marginTop: '10px' }} onClick={onAddModal}> <Button type="primary" style={{ width: '100px', marginTop: '10px' }} onClick={onAddModal}>
Edit Models Edit Models
</Button> </Button>
</Container> </SettingContainer>
) )
} }
const Container = styled.div`
display: flex;
flex-direction: column;
flex: 1;
height: calc(100vh - var(--navbar-height));
padding: 15px;
overflow-y: scroll;
&::-webkit-scrollbar {
display: none;
}
`
const Title = styled.div`
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
`
const SubTitle = styled.div`
font-size: 12px;
color: var(--color-text-3);
margin: 10px 0;
`
const ModelListItem = styled.div` const ModelListItem = styled.div`
display: flex; display: flex;
flex-direction: row; flex-direction: row;

View File

@ -0,0 +1,32 @@
import { Divider } from 'antd'
import styled from 'styled-components'
export const SettingContainer = styled.div`
display: flex;
flex-direction: column;
flex: 1;
height: calc(100vh - var(--navbar-height));
padding: 15px;
overflow-y: scroll;
&::-webkit-scrollbar {
display: none;
}
`
export const SettingTitle = styled.div`
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
`
export const SettingSubtitle = styled.div`
font-size: 12px;
color: var(--color-text-3);
margin: 10px 0;
`
export const SettingDivider = styled(Divider)`
margin: 10px 0;
`

View File

@ -6,10 +6,12 @@ import { uniqBy } from 'lodash'
export interface LlmState { export interface LlmState {
providers: Provider[] providers: Provider[]
defaultModel: Model defaultModel: Model
topicNamingModel: Model
} }
const initialState: LlmState = { const initialState: LlmState = {
defaultModel: SYSTEM_MODELS.openai[0], defaultModel: SYSTEM_MODELS.openai[0],
topicNamingModel: SYSTEM_MODELS.openai[0],
providers: [ providers: [
{ {
id: 'openai', id: 'openai',
@ -78,10 +80,24 @@ const settingsSlice = createSlice({
} }
: p : p
) )
},
setDefaultModel: (state, action: PayloadAction<{ model: Model }>) => {
state.defaultModel = action.payload.model
},
setTopicNamingModel: (state, action: PayloadAction<{ model: Model }>) => {
state.topicNamingModel = action.payload.model
} }
} }
}) })
export const { updateProvider, addProvider, removeProvider, addModel, removeModel } = settingsSlice.actions export const {
updateProvider,
addProvider,
removeProvider,
addModel,
removeModel,
setDefaultModel,
setTopicNamingModel
} = settingsSlice.actions
export default settingsSlice.reducer export default settingsSlice.reducer