feat: add models
This commit is contained in:
parent
be71f659ac
commit
392dfcee13
131
src/renderer/src/components/Popups/ModalListPopup.tsx
Normal file
131
src/renderer/src/components/Popups/ModalListPopup.tsx
Normal file
@ -0,0 +1,131 @@
|
|||||||
|
import { Button, Modal } from 'antd'
|
||||||
|
import { useState } from 'react'
|
||||||
|
import { TopView } from '../TopView'
|
||||||
|
import { Model, Provider } from '@renderer/types'
|
||||||
|
import { groupBy } from 'lodash'
|
||||||
|
import styled from 'styled-components'
|
||||||
|
import { MinusOutlined, PlusOutlined } from '@ant-design/icons'
|
||||||
|
import { useProvider } from '@renderer/hooks/useProvider'
|
||||||
|
import { SYSTEM_MODELS } from '@renderer/config/models'
|
||||||
|
|
||||||
|
interface ShowParams {
|
||||||
|
provider: Provider
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Props extends ShowParams {
|
||||||
|
resolve: (data: any) => void
|
||||||
|
}
|
||||||
|
|
||||||
|
const PopupContainer: React.FC<Props> = ({ provider: _provider, resolve }) => {
|
||||||
|
const [open, setOpen] = useState(true)
|
||||||
|
const { provider, addModel, removeModel } = useProvider(_provider.id)
|
||||||
|
|
||||||
|
const systemModels = SYSTEM_MODELS[_provider.id]
|
||||||
|
const systemModelGroups = groupBy(systemModels, 'group')
|
||||||
|
|
||||||
|
const onOk = () => {
|
||||||
|
setOpen(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
const onCancel = () => {
|
||||||
|
setOpen(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
const onClose = () => {
|
||||||
|
resolve({})
|
||||||
|
}
|
||||||
|
|
||||||
|
const onAddModel = (model: Model) => {
|
||||||
|
addModel(model)
|
||||||
|
}
|
||||||
|
|
||||||
|
const onRemoveModel = (model: Model) => {
|
||||||
|
removeModel(model)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal
|
||||||
|
title={String(provider.name + ' Models').toUpperCase()}
|
||||||
|
open={open}
|
||||||
|
onOk={onOk}
|
||||||
|
onCancel={onCancel}
|
||||||
|
afterClose={onClose}
|
||||||
|
footer={null}
|
||||||
|
width="600px"
|
||||||
|
styles={{
|
||||||
|
content: { padding: 0 },
|
||||||
|
header: { padding: 22, paddingBottom: 15 }
|
||||||
|
}}>
|
||||||
|
<ListContainer>
|
||||||
|
{Object.keys(systemModelGroups).map((group) => (
|
||||||
|
<div key={group}>
|
||||||
|
<ListHeader key={group}>{group}</ListHeader>
|
||||||
|
{systemModelGroups[group].map((model) => {
|
||||||
|
const hasModel = provider.models.find((m) => m.id === model.id)
|
||||||
|
return (
|
||||||
|
<ListItem key={model.id}>
|
||||||
|
<ListItemName>{model.id}</ListItemName>
|
||||||
|
{hasModel ? (
|
||||||
|
<Button type="default" onClick={() => onRemoveModel(model)} icon={<MinusOutlined />} />
|
||||||
|
) : (
|
||||||
|
<Button type="primary" onClick={() => onAddModel(model)} icon={<PlusOutlined />} />
|
||||||
|
)}
|
||||||
|
</ListItem>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</ListContainer>
|
||||||
|
</Modal>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const ListContainer = styled.div`
|
||||||
|
max-height: 70vh;
|
||||||
|
overflow-y: scroll;
|
||||||
|
padding-bottom: 20px;
|
||||||
|
`
|
||||||
|
|
||||||
|
const ListHeader = styled.div`
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
background-color: var(--color-background-soft);
|
||||||
|
padding: 8px 22px;
|
||||||
|
color: #ffffff50;
|
||||||
|
`
|
||||||
|
|
||||||
|
const ListItem = styled.div`
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: 10px 22px;
|
||||||
|
`
|
||||||
|
|
||||||
|
const ListItemName = styled.div`
|
||||||
|
color: #fff;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 600;
|
||||||
|
`
|
||||||
|
|
||||||
|
export default class ModalListPopup {
|
||||||
|
static topviewId = 0
|
||||||
|
static hide() {
|
||||||
|
TopView.hide(this.topviewId)
|
||||||
|
}
|
||||||
|
static show(props: ShowParams) {
|
||||||
|
return new Promise<any>((resolve) => {
|
||||||
|
this.topviewId = TopView.show(
|
||||||
|
<PopupContainer
|
||||||
|
{...props}
|
||||||
|
resolve={(v) => {
|
||||||
|
resolve(v)
|
||||||
|
this.hide()
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
54
src/renderer/src/components/Popups/TemplatePopup.tsx
Normal file
54
src/renderer/src/components/Popups/TemplatePopup.tsx
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
import { Modal } from 'antd'
|
||||||
|
import { useState } from 'react'
|
||||||
|
import { TopView } from '../TopView'
|
||||||
|
import { Box } from '../Layout'
|
||||||
|
|
||||||
|
interface ShowParams {
|
||||||
|
title: string
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Props extends ShowParams {
|
||||||
|
resolve: (data: any) => void
|
||||||
|
}
|
||||||
|
|
||||||
|
const PopupContainer: React.FC<Props> = ({ title, resolve }) => {
|
||||||
|
const [open, setOpen] = useState(true)
|
||||||
|
|
||||||
|
const onOk = () => {
|
||||||
|
setOpen(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
const onCancel = () => {
|
||||||
|
setOpen(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
const onClose = () => {
|
||||||
|
resolve({})
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal title={title} open={open} onOk={onOk} onCancel={onCancel} afterClose={onClose}>
|
||||||
|
<Box mb={8}>Name</Box>
|
||||||
|
</Modal>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class TemplatePopup {
|
||||||
|
static topviewId = 0
|
||||||
|
static hide() {
|
||||||
|
TopView.hide(this.topviewId)
|
||||||
|
}
|
||||||
|
static show(props: ShowParams) {
|
||||||
|
return new Promise<any>((resolve) => {
|
||||||
|
this.topviewId = TopView.show(
|
||||||
|
<PopupContainer
|
||||||
|
{...props}
|
||||||
|
resolve={(v) => {
|
||||||
|
resolve(v)
|
||||||
|
this.hide()
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -28,7 +28,7 @@ const Sidebar: FC = () => {
|
|||||||
</Menus>
|
</Menus>
|
||||||
</MainMenus>
|
</MainMenus>
|
||||||
<Menus>
|
<Menus>
|
||||||
<StyledLink to="/settings/general">
|
<StyledLink to="/settings/common">
|
||||||
<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>
|
||||||
|
|||||||
144
src/renderer/src/config/assistant.ts
Normal file
144
src/renderer/src/config/assistant.ts
Normal file
@ -0,0 +1,144 @@
|
|||||||
|
import { SystemAssistant } from '@renderer/types'
|
||||||
|
|
||||||
|
export const SYSTEM_ASSISTANTS: SystemAssistant[] = [
|
||||||
|
// Software Engineer
|
||||||
|
{
|
||||||
|
id: '43CEDACF-C9EB-431B-848C-4D08EC26EB90',
|
||||||
|
name: '软件工程师',
|
||||||
|
description: '你是一个高级软件工程师,你需要帮我解答各种技术难题',
|
||||||
|
prompt:
|
||||||
|
'你是一个高级软件工程师,你需要帮我解答各种技术难题、设计技术方案以及编写代码。你编写的代码必须可以正常运行,而且没有任何 Bug 和其他问题。',
|
||||||
|
group: 'Software Engineer'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '6B1D8E9F-9B7F-4E2B-8FBB-0F5B6F7B0D2A',
|
||||||
|
name: '前端工程师',
|
||||||
|
description: '你是一个高级前端工程师,你需要帮我解答各种技术难题',
|
||||||
|
prompt:
|
||||||
|
'你擅长使用 TypeScript, JavaScript, HMLT, CSS 等编程语言。同时你还会使用 Node.js 及各种包来解决开发中遇到的问题。你还会使用 React, Vue 等前端框架。对于我的问题希望你能给出具体的代码示例,最好能够封装成一个函数方便我复制运行测试。',
|
||||||
|
group: 'Software Engineer'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '6B1D8E9F-9B7F-4E2B-8FBB-0F5B6F7B0D2B',
|
||||||
|
name: '后端工程师',
|
||||||
|
description: '你是一个高级后端工程师,你需要帮我解答各种技术难题',
|
||||||
|
prompt: '你是一个高级后端工程师,你需要帮我解答各种技术难题',
|
||||||
|
group: 'Software Engineer'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '6B1D8E9F-9B7F-4E2B-8FBB-0F5B6F7B0D2C',
|
||||||
|
name: '全栈工程师',
|
||||||
|
description: '你是一个高级全栈工程师,你需要帮我解答各种技术难题',
|
||||||
|
prompt: '你是一个高级全栈工程师,你需要帮我解答各种技术难题',
|
||||||
|
group: 'Software Engineer'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '6B1D8E9F-9B7F-4E2B-8FBB-0F5B6F7B0D2D',
|
||||||
|
name: '测试工程师',
|
||||||
|
description: '你是一个高级测试工程师,你需要帮我解答各种技术难题',
|
||||||
|
prompt: '你是一个高级测试工程师,你需要帮我解答各种技术难题',
|
||||||
|
group: 'Software Engineer'
|
||||||
|
},
|
||||||
|
// Programming Languages Assistants
|
||||||
|
{
|
||||||
|
id: '6B1D8E9F-9B7F-4E2B-8FBB-0F5B6F7B0D2E',
|
||||||
|
name: 'Python',
|
||||||
|
description: '你是一个高级Python工程师,你需要帮我解答各种技术难题',
|
||||||
|
prompt: '你是一个高级Python工程师,你需要帮我解答各种技术难题',
|
||||||
|
group: 'Programming Languages'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '6B1D8E9F-9B7F-4E2B-8FBB-0F5B6F7B0D2F',
|
||||||
|
name: 'Java',
|
||||||
|
description: '你是一个高级Java工程师,你需要帮我解答各种技术难题',
|
||||||
|
prompt: '你是一个高级Java工程师,你需要帮我解答各种技术难题',
|
||||||
|
group: 'Programming Languages'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '6B1D8E9F-9B7F-4E2B-8FBB-0F5B6F7B0D30',
|
||||||
|
name: 'C#',
|
||||||
|
description: '你是一个高级C#工程师,你需要帮我解答各种技术难题',
|
||||||
|
prompt: '你是一个高级C#工程师,你需要帮我解答各种技术难题',
|
||||||
|
group: 'Programming Languages'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '6B1D8E9F-9B7F-4E2B-8FBB-0F5B6F7B0D31',
|
||||||
|
name: 'C++',
|
||||||
|
description: '你是一个高级C++工程师,你需要帮我解答各种技术难题',
|
||||||
|
prompt: '你是一个高级C++工程师,你需要帮我解答各种技术难题',
|
||||||
|
group: 'Programming Languages'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '6B1D8E9F-9B7F-4E2B-8FBB-0F5B6F7B0D32',
|
||||||
|
name: 'C',
|
||||||
|
description: '你是一个高级C工程师,你需要帮我解答各种技术难题',
|
||||||
|
prompt: '你是一个高级C工程师,你需要帮我解答各种技术难题',
|
||||||
|
group: 'Programming Languages'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '6B1D8E9F-9B7F-4E2B-8FBB-0F5B6F7B0D33',
|
||||||
|
name: 'Go',
|
||||||
|
description: '你是一个高级Go工程师,你需要帮我解答各种技术难题',
|
||||||
|
prompt: '你是一个高级Go工程师,你需要帮我解答各种技术难题',
|
||||||
|
group: 'Programming Languages'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '6B1D8E9F-9B7F-4E2B-8FBB-0F5B6F7B0D34',
|
||||||
|
name: 'Rust',
|
||||||
|
description: '你是一个高级Rust工程师,你需要帮我解答各种技术难题',
|
||||||
|
prompt: '你是一个高级Rust工程师,你需要帮我解答各种技术难题',
|
||||||
|
group: 'Programming Languages'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '6B1D8E9F-9B7F-4E2B-8FBB-0F5B6F7B0D35',
|
||||||
|
name: 'PHP',
|
||||||
|
description: '你是一个高级PHP工程师,你需要帮我解答各种技术难题',
|
||||||
|
prompt: '你是一个高级PHP工程师,你需要帮我解答各种技术难题',
|
||||||
|
group: 'Programming Languages'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '6B1D8E9F-9B7F-4E2B-8FBB-0F5B6F7B0D36',
|
||||||
|
name: 'Ruby',
|
||||||
|
description: '你是一个高级Ruby工程师,你需要帮我解答各种技术难题',
|
||||||
|
prompt: '你是一个高级Ruby工程师,你需要帮我解答各种技术难题',
|
||||||
|
group: 'Programming Languages'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '6B1D8E9F-9B7F-4E2B-8FBB-0F5B6F7B0D37',
|
||||||
|
name: 'Swift',
|
||||||
|
description: '你是一个高级Swift工程师,你需要帮我解答各种技术难题',
|
||||||
|
prompt: '你是一个高级Swift工程师,你需要帮我解答各种技术难题',
|
||||||
|
group: 'Programming Languages'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '6B1D8E9F-9B7F-4E2B-8FBB-0F5B6F7B0D38',
|
||||||
|
name: 'Kotlin',
|
||||||
|
description: '你是一个高级Kotlin工程师,你需要帮我解答各种技术难题',
|
||||||
|
prompt: '你是一个高级Kotlin工程师,你需要帮我解答各种技术难题',
|
||||||
|
group: 'Programming Languages'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '6B1D8E9F-9B7F-4E2B-8FBB-0F5B6F7B0D39',
|
||||||
|
name: 'Dart',
|
||||||
|
description: '你是一个高级Dart工程师,你需要帮我解答各种技术难题',
|
||||||
|
prompt: '你是一个高级Dart工程师,你需要帮我解答各种技术难题',
|
||||||
|
group: 'Programming Languages'
|
||||||
|
},
|
||||||
|
// Translation
|
||||||
|
{
|
||||||
|
id: '6B1D8E9F-9B7F-4E2B-8FBB-0F5B6F7B0D40',
|
||||||
|
name: '翻译成中文',
|
||||||
|
description: '你是一个好用的翻译助手, 可以把任何语言翻译成中文',
|
||||||
|
prompt:
|
||||||
|
'你是一个好用的翻译助手。请将我的英文翻译成中文,将所有非中文的翻译成中文。我发给你所有的话都是需要翻译的内容,你只需要回答翻译结果。翻译结果请符合中文的语言习惯。',
|
||||||
|
group: 'Translation'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '6B1D8E9F-9B7F-4E2B-8FBB-0F5B6F7B0D41',
|
||||||
|
name: '翻译成英文',
|
||||||
|
description: '你是一个好用的翻译助手, 可以把任何语言翻译成英文',
|
||||||
|
prompt:
|
||||||
|
'你是一个好用的翻译助手。请将我的中文翻译成英文,将所有非中文的翻译成英文。我发给你所有的话都是需要翻译的内容,你只需要回答翻译结果。翻译结果请符合英文的语言习惯。',
|
||||||
|
group: 'Translation'
|
||||||
|
}
|
||||||
|
]
|
||||||
222
src/renderer/src/config/models.ts
Normal file
222
src/renderer/src/config/models.ts
Normal file
@ -0,0 +1,222 @@
|
|||||||
|
import { Model } from '@renderer/types'
|
||||||
|
|
||||||
|
export const SYSTEM_MODELS: Record<string, Model[]> = {
|
||||||
|
openai: [
|
||||||
|
{
|
||||||
|
id: 'gpt-3.5-turbo',
|
||||||
|
name: 'gpt-3.5-turbo',
|
||||||
|
group: 'GPT 3.5',
|
||||||
|
temperature: 0.7
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'gpt-3.5-turbo-0301',
|
||||||
|
name: 'gpt-3.5-turbo',
|
||||||
|
group: 'GPT 3.5',
|
||||||
|
temperature: 0.3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'gpt-4',
|
||||||
|
name: 'gpt-4',
|
||||||
|
group: 'GPT 4',
|
||||||
|
temperature: 0.7
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'gpt-4-0314',
|
||||||
|
name: 'gpt-4',
|
||||||
|
group: 'GPT 4',
|
||||||
|
temperature: 0.3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'gpt-4-32k',
|
||||||
|
name: 'gpt-4-32k',
|
||||||
|
group: 'GPT 4',
|
||||||
|
temperature: 0.7
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'gpt-4-32k-0314',
|
||||||
|
name: 'gpt-4-32k',
|
||||||
|
group: 'GPT 4',
|
||||||
|
temperature: 0.3
|
||||||
|
}
|
||||||
|
],
|
||||||
|
silicon: [
|
||||||
|
{
|
||||||
|
id: 'deepseek-ai/DeepSeek-V2-Chat',
|
||||||
|
name: 'DeepSeek-V2-Chat',
|
||||||
|
group: 'DeepSeek',
|
||||||
|
temperature: 0.7
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'deepseek-ai/DeepSeek-Coder-V2-Instruct',
|
||||||
|
name: 'DeepSeek-Coder-V2-Instruct',
|
||||||
|
group: 'DeepSeek',
|
||||||
|
temperature: 0.7
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'deepseek-ai/deepseek-llm-67b-chat',
|
||||||
|
name: 'deepseek-llm-67b-chat',
|
||||||
|
group: 'DeepSeek',
|
||||||
|
temperature: 0.7
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'google/gemma-2-27b-it',
|
||||||
|
name: 'gemma-2-27b-it',
|
||||||
|
group: 'Gemma',
|
||||||
|
temperature: 0.7
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'google/gemma-2-9b-it',
|
||||||
|
name: 'gemma-2-9b-it',
|
||||||
|
group: 'Gemma',
|
||||||
|
temperature: 0.7
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'Qwen/Qwen2-7B-Instruct',
|
||||||
|
name: 'Qwen2-7B-Instruct',
|
||||||
|
group: 'Qwen2',
|
||||||
|
temperature: 0.7
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'Qwen/Qwen2-1.5B-Instruct',
|
||||||
|
name: 'Qwen2-1.5B-Instruct',
|
||||||
|
group: 'Qwen2',
|
||||||
|
temperature: 0.7
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'Qwen/Qwen1.5-7B-Chat',
|
||||||
|
name: 'Qwen1.5-7B-Chat',
|
||||||
|
group: 'Qwen1.5',
|
||||||
|
temperature: 0.7
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'Qwen/Qwen2-72B-Instruct',
|
||||||
|
name: 'Qwen2-72B-Instruct',
|
||||||
|
group: 'Qwen2',
|
||||||
|
temperature: 0.7
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'Qwen/Qwen2-57B-A14B-Instruct',
|
||||||
|
name: 'Qwen2-57B-A14B-Instruct',
|
||||||
|
group: 'Qwen2',
|
||||||
|
temperature: 0.7
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'Qwen/Qwen1.5-110B-Chat',
|
||||||
|
name: 'Qwen1.5-110B-Chat',
|
||||||
|
group: 'Qwen1.5',
|
||||||
|
temperature: 0.7
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'Qwen/Qwen1.5-32B-Chat',
|
||||||
|
name: 'Qwen1.5-32B-Chat',
|
||||||
|
group: 'Qwen1.5',
|
||||||
|
temperature: 0.7
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'Qwen/Qwen1.5-14B-Chat',
|
||||||
|
name: 'Qwen1.5-14B-Chat',
|
||||||
|
group: 'Qwen1.5',
|
||||||
|
temperature: 0.7
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'THUDM/glm-4-9b-chat',
|
||||||
|
name: 'glm-4-9b-chat',
|
||||||
|
group: 'GLM',
|
||||||
|
temperature: 0.7
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'THUDM/chatglm3-6b',
|
||||||
|
name: 'chatglm3-6b',
|
||||||
|
group: 'GLM',
|
||||||
|
temperature: 0.7
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '01-ai/Yi-1.5-9B-Chat-16K',
|
||||||
|
name: 'Yi-1.5-9B-Chat-16K',
|
||||||
|
group: 'Yi',
|
||||||
|
temperature: 0.7
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '01-ai/Yi-1.5-6B-Chat',
|
||||||
|
name: 'Yi-1.5-6B-Chat',
|
||||||
|
group: 'Yi',
|
||||||
|
temperature: 0.7
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '01-ai/Yi-1.5-34B-Chat-16K',
|
||||||
|
name: 'Yi-1.5-34B-Chat-16K',
|
||||||
|
group: 'Yi',
|
||||||
|
temperature: 0.7
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'OpenAI/GPT-4o',
|
||||||
|
name: 'GPT-4o',
|
||||||
|
group: 'OpenAI',
|
||||||
|
temperature: 0.7
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'OpenAI/GPT-3.5 Turbo',
|
||||||
|
name: 'GPT-3.5 Turbo',
|
||||||
|
group: 'OpenAI',
|
||||||
|
temperature: 0.7
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'Anthropic/claude-3-5-sonnet',
|
||||||
|
name: 'claude-3-5-sonnet',
|
||||||
|
group: 'Claude',
|
||||||
|
temperature: 0.7
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'meta-llama/Meta-Llama-3-8B-Instruct',
|
||||||
|
name: 'Meta-Llama-3-8B-Instruct',
|
||||||
|
group: 'Meta Llama',
|
||||||
|
temperature: 0.7
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'meta-llama/Meta-Llama-3-70B-Instruct',
|
||||||
|
name: 'Meta-Llama-3-70B-Instruct',
|
||||||
|
group: 'Meta Llama',
|
||||||
|
temperature: 0.7
|
||||||
|
}
|
||||||
|
],
|
||||||
|
deepseek: [
|
||||||
|
{
|
||||||
|
id: 'deepseek-chat',
|
||||||
|
name: 'deepseek-chat',
|
||||||
|
group: 'Deepseek Chat',
|
||||||
|
temperature: 0.7
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'deepseek-coder',
|
||||||
|
name: 'deepseek-coder',
|
||||||
|
group: 'Deepseek Coder',
|
||||||
|
temperature: 1.0
|
||||||
|
}
|
||||||
|
],
|
||||||
|
groq: [
|
||||||
|
{
|
||||||
|
id: 'llama3-8b-8192',
|
||||||
|
name: 'LLaMA3 8b',
|
||||||
|
group: 'Llama3',
|
||||||
|
temperature: 0.7
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'llama3-70b-8192',
|
||||||
|
name: 'LLaMA3 70b',
|
||||||
|
group: 'Llama3',
|
||||||
|
temperature: 0.7
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'mixtral-8x7b-32768',
|
||||||
|
name: 'Mixtral 8x7b',
|
||||||
|
group: 'Mixtral',
|
||||||
|
temperature: 0.7
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'gemma-7b-it',
|
||||||
|
name: 'Gemma 7b',
|
||||||
|
group: 'Gemma',
|
||||||
|
temperature: 0.7
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@ -11,7 +11,7 @@ import {
|
|||||||
import { Assistant, Topic } from '@renderer/types'
|
import { Assistant, Topic } from '@renderer/types'
|
||||||
import localforage from 'localforage'
|
import localforage from 'localforage'
|
||||||
|
|
||||||
export default function useAssistants() {
|
export function useAssistants() {
|
||||||
const { assistants } = useAppSelector((state) => state.assistants)
|
const { assistants } = useAppSelector((state) => state.assistants)
|
||||||
const dispatch = useAppDispatch()
|
const dispatch = useAppDispatch()
|
||||||
|
|
||||||
32
src/renderer/src/hooks/useProvider.ts
Normal file
32
src/renderer/src/hooks/useProvider.ts
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
import { useAppDispatch, useAppSelector } from '@renderer/store'
|
||||||
|
import {
|
||||||
|
addModel as _addModel,
|
||||||
|
removeModel as _removeModel,
|
||||||
|
updateProvider as _updateProvider
|
||||||
|
} from '@renderer/store/llm'
|
||||||
|
import { Model, Provider } from '@renderer/types'
|
||||||
|
|
||||||
|
export function useProviders() {
|
||||||
|
return useAppSelector((state) => state.llm.providers)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useProvider(id: string) {
|
||||||
|
const provider = useAppSelector((state) => state.llm.providers.find((p) => p.id === id) as Provider)
|
||||||
|
const dispatch = useAppDispatch()
|
||||||
|
|
||||||
|
return {
|
||||||
|
provider,
|
||||||
|
models: provider.models,
|
||||||
|
updateProvider: (provider: Provider) => dispatch(_updateProvider(provider)),
|
||||||
|
addModel: (model: Model) => dispatch(_addModel({ providerId: id, model })),
|
||||||
|
removeModel: (model: Model) => dispatch(_removeModel({ providerId: id, model }))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useDefaultProvider() {
|
||||||
|
return useAppSelector((state) => state.llm.providers.find((p) => p.isDefault))
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useSystemProviders() {
|
||||||
|
return useAppSelector((state) => state.llm.providers.filter((p) => p.isSystem))
|
||||||
|
}
|
||||||
@ -1,13 +1,81 @@
|
|||||||
import { Navbar, NavbarCenter } from '@renderer/components/app/Navbar'
|
import { Navbar, NavbarCenter } from '@renderer/components/app/Navbar'
|
||||||
|
import { SYSTEM_ASSISTANTS } from '@renderer/config/assistant'
|
||||||
|
import { Button, Col, message, Row, Tooltip, Typography } from 'antd'
|
||||||
|
import { find, groupBy } from 'lodash'
|
||||||
import { FC } from 'react'
|
import { FC } from 'react'
|
||||||
import styled from 'styled-components'
|
import styled from 'styled-components'
|
||||||
|
import { CheckOutlined, PlusOutlined } from '@ant-design/icons'
|
||||||
|
import { SystemAssistant } from '@renderer/types'
|
||||||
|
import { getDefaultAssistant } from '@renderer/services/assistant'
|
||||||
|
import { useAssistants } from '@renderer/hooks/useAssistant'
|
||||||
|
|
||||||
|
const { Title } = Typography
|
||||||
|
|
||||||
const AppsPage: FC = () => {
|
const AppsPage: FC = () => {
|
||||||
|
const { assistants, addAssistant } = useAssistants()
|
||||||
|
const assistantGroups = groupBy(SYSTEM_ASSISTANTS, 'group')
|
||||||
|
const [messageApi, contextHolder] = message.useMessage()
|
||||||
|
|
||||||
|
const onAddAssistant = (assistant: SystemAssistant) => {
|
||||||
|
addAssistant({
|
||||||
|
...getDefaultAssistant(),
|
||||||
|
...assistant
|
||||||
|
})
|
||||||
|
messageApi.destroy()
|
||||||
|
messageApi.open({
|
||||||
|
type: 'success',
|
||||||
|
content: 'Assistant added successfully',
|
||||||
|
style: {
|
||||||
|
marginTop: '5vh'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Container>
|
<Container>
|
||||||
|
{contextHolder}
|
||||||
<Navbar>
|
<Navbar>
|
||||||
<NavbarCenter>Assistant Market</NavbarCenter>
|
<NavbarCenter>Assistant Market</NavbarCenter>
|
||||||
</Navbar>
|
</Navbar>
|
||||||
|
<ContentContainer>
|
||||||
|
{Object.keys(assistantGroups).map((group) => (
|
||||||
|
<div key={group}>
|
||||||
|
<Title level={3} key={group} style={{ marginBottom: 16 }}>
|
||||||
|
{group}
|
||||||
|
</Title>
|
||||||
|
<Row gutter={16}>
|
||||||
|
{assistantGroups[group].map((assistant, index) => {
|
||||||
|
const added = find(assistants, { id: assistant.id })
|
||||||
|
return (
|
||||||
|
<Col span={6} key={group + index} style={{ marginBottom: 16 }}>
|
||||||
|
<AssistantCard>
|
||||||
|
<AssistantHeader>
|
||||||
|
<Title level={5} style={{ marginBottom: 0, color: '#00b96b' }}>
|
||||||
|
{assistant.name}
|
||||||
|
</Title>
|
||||||
|
{added && <Button type="primary" shape="circle" size="small" icon={<CheckOutlined />} />}
|
||||||
|
{!added && (
|
||||||
|
<Tooltip placement="top" title=" Add to assistant list " arrow>
|
||||||
|
<Button
|
||||||
|
type="default"
|
||||||
|
shape="circle"
|
||||||
|
size="small"
|
||||||
|
icon={<PlusOutlined />}
|
||||||
|
onClick={() => onAddAssistant(assistant)}
|
||||||
|
/>
|
||||||
|
</Tooltip>
|
||||||
|
)}
|
||||||
|
</AssistantHeader>
|
||||||
|
<AssistantCardDescription>{assistant.description}</AssistantCardDescription>
|
||||||
|
<AssistantCardPrompt>{assistant.prompt}</AssistantCardPrompt>
|
||||||
|
</AssistantCard>
|
||||||
|
</Col>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</Row>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</ContentContainer>
|
||||||
</Container>
|
</Container>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -15,6 +83,46 @@ const AppsPage: FC = () => {
|
|||||||
const Container = styled.div`
|
const Container = styled.div`
|
||||||
display: flex;
|
display: flex;
|
||||||
flex: 1;
|
flex: 1;
|
||||||
|
flex-direction: column;
|
||||||
|
height: 100%;
|
||||||
|
`
|
||||||
|
|
||||||
|
const ContentContainer = styled.div`
|
||||||
|
display: flex;
|
||||||
|
flex: 1;
|
||||||
|
flex-direction: column;
|
||||||
|
height: calc(100vh - var(--navbar-height));
|
||||||
|
padding: 20px;
|
||||||
|
overflow-y: scroll;
|
||||||
|
`
|
||||||
|
|
||||||
|
const AssistantCard = styled.div`
|
||||||
|
margin-bottom: 16px;
|
||||||
|
background-color: #141414;
|
||||||
|
border-radius: 10px;
|
||||||
|
padding: 20px;
|
||||||
|
`
|
||||||
|
|
||||||
|
const AssistantHeader = styled.div`
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
`
|
||||||
|
|
||||||
|
const AssistantCardDescription = styled.div`
|
||||||
|
font-size: 12px;
|
||||||
|
color: #888;
|
||||||
|
margin-top: 10px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
line-height: 1.5;
|
||||||
|
`
|
||||||
|
|
||||||
|
const AssistantCardPrompt = styled.div`
|
||||||
|
color: white;
|
||||||
|
margin-top: 10px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
line-height: 1.5;
|
||||||
`
|
`
|
||||||
|
|
||||||
export default AppsPage
|
export default AppsPage
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import { Navbar, NavbarCenter, NavbarLeft, NavbarRight } from '@renderer/components/app/Navbar'
|
import { Navbar, NavbarCenter, NavbarLeft, NavbarRight } from '@renderer/components/app/Navbar'
|
||||||
import useAssistants from '@renderer/hooks/useAssistants'
|
import { useAssistants } from '@renderer/hooks/useAssistant'
|
||||||
import { FC, useState } from 'react'
|
import { FC, useState } from 'react'
|
||||||
import styled from 'styled-components'
|
import styled from 'styled-components'
|
||||||
import Chat from './components/Chat/Chat'
|
import Chat from './components/Chat/Chat'
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import { FC, useRef } from 'react'
|
import { FC, useRef } from 'react'
|
||||||
import styled from 'styled-components'
|
import styled from 'styled-components'
|
||||||
import useAssistants from '@renderer/hooks/useAssistants'
|
import { useAssistants } from '@renderer/hooks/useAssistant'
|
||||||
import { Assistant } from '@renderer/types'
|
import { Assistant } from '@renderer/types'
|
||||||
import { Dropdown, MenuProps } from 'antd'
|
import { Dropdown, MenuProps } from 'antd'
|
||||||
import { last } from 'lodash'
|
import { last } from 'lodash'
|
||||||
|
|||||||
@ -5,7 +5,7 @@ import Inputbar from './Inputbar'
|
|||||||
import Conversations from './Conversations'
|
import Conversations from './Conversations'
|
||||||
import { Flex } from 'antd'
|
import { Flex } from 'antd'
|
||||||
import TopicList from './TopicList'
|
import TopicList from './TopicList'
|
||||||
import { useAssistant } from '@renderer/hooks/useAssistants'
|
import { useAssistant } from '@renderer/hooks/useAssistant'
|
||||||
import { useActiveTopic } from '@renderer/hooks/useTopic'
|
import { useActiveTopic } from '@renderer/hooks/useTopic'
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
|
|||||||
@ -7,7 +7,7 @@ import MessageItem from './Message'
|
|||||||
import { reverse } from 'lodash'
|
import { reverse } from 'lodash'
|
||||||
import hljs from 'highlight.js'
|
import hljs from 'highlight.js'
|
||||||
import { fetchChatCompletion, fetchConversationSummary } from '@renderer/services/api'
|
import { fetchChatCompletion, fetchConversationSummary } from '@renderer/services/api'
|
||||||
import { useAssistant } from '@renderer/hooks/useAssistants'
|
import { useAssistant } from '@renderer/hooks/useAssistant'
|
||||||
import { DEFAULT_TOPIC_NAME } from '@renderer/config/constant'
|
import { DEFAULT_TOPIC_NAME } from '@renderer/config/constant'
|
||||||
import { runAsyncFunction } from '@renderer/utils'
|
import { runAsyncFunction } from '@renderer/utils'
|
||||||
import LocalStorage from '@renderer/services/storage'
|
import LocalStorage from '@renderer/services/storage'
|
||||||
|
|||||||
@ -6,7 +6,7 @@ import styled from 'styled-components'
|
|||||||
import { MoreOutlined } from '@ant-design/icons'
|
import { MoreOutlined } from '@ant-design/icons'
|
||||||
import { Button, Popconfirm, Tooltip } from 'antd'
|
import { Button, Popconfirm, Tooltip } from 'antd'
|
||||||
import { useShowRightSidebar } from '@renderer/hooks/useStore'
|
import { useShowRightSidebar } from '@renderer/hooks/useStore'
|
||||||
import { useAssistant } from '@renderer/hooks/useAssistants'
|
import { useAssistant } from '@renderer/hooks/useAssistant'
|
||||||
import { ClearOutlined, HistoryOutlined, PlusCircleOutlined } from '@ant-design/icons'
|
import { ClearOutlined, HistoryOutlined, PlusCircleOutlined } from '@ant-design/icons'
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import PromptPopup from '@renderer/components/Popups/PromptPopup'
|
import PromptPopup from '@renderer/components/Popups/PromptPopup'
|
||||||
import { useAssistant } from '@renderer/hooks/useAssistants'
|
import { useAssistant } from '@renderer/hooks/useAssistant'
|
||||||
import { useShowRightSidebar } from '@renderer/hooks/useStore'
|
import { useShowRightSidebar } from '@renderer/hooks/useStore'
|
||||||
import { fetchConversationSummary } from '@renderer/services/api'
|
import { fetchConversationSummary } from '@renderer/services/api'
|
||||||
import { Assistant, Topic } from '@renderer/types'
|
import { Assistant, Topic } from '@renderer/types'
|
||||||
|
|||||||
@ -5,6 +5,8 @@ const AboutSettings: FC = () => {
|
|||||||
return <Container>About</Container>
|
return <Container>About</Container>
|
||||||
}
|
}
|
||||||
|
|
||||||
const Container = styled.div``
|
const Container = styled.div`
|
||||||
|
padding: 20px;
|
||||||
|
`
|
||||||
|
|
||||||
export default AboutSettings
|
export default AboutSettings
|
||||||
|
|||||||
@ -5,6 +5,7 @@ const CommonSettings: FC = () => {
|
|||||||
return <Container>Common Settings</Container>
|
return <Container>Common Settings</Container>
|
||||||
}
|
}
|
||||||
|
|
||||||
const Container = styled.div``
|
const Container = styled.div`
|
||||||
|
padding: 20px;
|
||||||
|
`
|
||||||
export default CommonSettings
|
export default CommonSettings
|
||||||
|
|||||||
@ -5,6 +5,7 @@ const DefaultAssistantSetting: FC = () => {
|
|||||||
return <Container>Default Assistant</Container>
|
return <Container>Default Assistant</Container>
|
||||||
}
|
}
|
||||||
|
|
||||||
const Container = styled.div``
|
const Container = styled.div`
|
||||||
|
padding: 20px;
|
||||||
|
`
|
||||||
export default DefaultAssistantSetting
|
export default DefaultAssistantSetting
|
||||||
|
|||||||
@ -5,6 +5,7 @@ const DeveloperSetting: FC = () => {
|
|||||||
return <Container>Developer</Container>
|
return <Container>Developer</Container>
|
||||||
}
|
}
|
||||||
|
|
||||||
const Container = styled.div``
|
const Container = styled.div`
|
||||||
|
padding: 20px;
|
||||||
|
`
|
||||||
export default DeveloperSetting
|
export default DeveloperSetting
|
||||||
|
|||||||
@ -1,36 +1,64 @@
|
|||||||
import { Collapse } from 'antd'
|
import { useSystemProviders } from '@renderer/hooks/useProvider'
|
||||||
import { FC } from 'react'
|
import { Provider } from '@renderer/types'
|
||||||
|
import { FC, useState } from 'react'
|
||||||
import styled from 'styled-components'
|
import styled from 'styled-components'
|
||||||
|
import ModalProviderSetting from './components/ModalProviderSetting'
|
||||||
|
|
||||||
|
// OpenAI Silicon deepseek Groq
|
||||||
const LanguageModelsSettings: FC = () => {
|
const LanguageModelsSettings: FC = () => {
|
||||||
|
const providers = useSystemProviders()
|
||||||
|
const [selectedProvider, setSelectedProvider] = useState<Provider>(providers[0])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Container>
|
<Container>
|
||||||
<Collapse style={{ width: '100%', marginBottom: 10 }}>
|
<ProviderListContainer>
|
||||||
<Collapse.Panel header="OpenAI" key="openai">
|
{providers.map((provider) => (
|
||||||
<p>OpenAI</p>
|
<ProviderListItem
|
||||||
</Collapse.Panel>
|
key={JSON.stringify(provider)}
|
||||||
</Collapse>
|
className={provider.id === selectedProvider?.id ? 'active' : ''}
|
||||||
<Collapse style={{ width: '100%', marginBottom: 10 }}>
|
onClick={() => setSelectedProvider(provider)}>
|
||||||
<Collapse.Panel header="Silicon" key="silicon">
|
{provider.name}
|
||||||
<p>Silicon</p>
|
</ProviderListItem>
|
||||||
</Collapse.Panel>
|
))}
|
||||||
</Collapse>
|
</ProviderListContainer>
|
||||||
<Collapse style={{ width: '100%', marginBottom: 10 }}>
|
<ModalProviderSetting provider={selectedProvider} />
|
||||||
<Collapse.Panel header="deepseek" key="deepseek">
|
|
||||||
<p>deepseek</p>
|
|
||||||
</Collapse.Panel>
|
|
||||||
</Collapse>
|
|
||||||
<Collapse style={{ width: '100%', marginBottom: 10 }}>
|
|
||||||
<Collapse.Panel header="Groq" key="groq">
|
|
||||||
<p>Groq</p>
|
|
||||||
</Collapse.Panel>
|
|
||||||
</Collapse>
|
|
||||||
</Container>
|
</Container>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const Container = styled.div`
|
const Container = styled.div`
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
`
|
||||||
|
|
||||||
|
const ProviderListContainer = styled.div`
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
width: var(--assistants-width);
|
||||||
|
height: 100%;
|
||||||
|
border-right: 0.5px solid var(--color-border);
|
||||||
|
padding: 10px;
|
||||||
|
`
|
||||||
|
|
||||||
|
const ProviderListItem = styled.div`
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
padding: 6px 10px;
|
||||||
|
margin-bottom: 5px;
|
||||||
|
width: 100%;
|
||||||
|
cursor: pointer;
|
||||||
|
border-radius: 5px;
|
||||||
|
font-size: 14px;
|
||||||
|
transition: all 0.2s ease-in-out;
|
||||||
|
&:hover {
|
||||||
|
background: #135200;
|
||||||
|
}
|
||||||
|
&.active {
|
||||||
|
background: #135200;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
`
|
`
|
||||||
|
|
||||||
export default LanguageModelsSettings
|
export default LanguageModelsSettings
|
||||||
|
|||||||
@ -96,8 +96,7 @@ const SettingContent = styled.div`
|
|||||||
display: flex;
|
display: flex;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
flex: 1;
|
flex: 1;
|
||||||
border-right: 1px solid var(--color-border);
|
border-right: 0.5px solid var(--color-border);
|
||||||
padding: 20px;
|
|
||||||
`
|
`
|
||||||
|
|
||||||
export default SettingsPage
|
export default SettingsPage
|
||||||
|
|||||||
@ -5,6 +5,7 @@ const SystemAssistantSettings: FC = () => {
|
|||||||
return <Container>System Assistant</Container>
|
return <Container>System Assistant</Container>
|
||||||
}
|
}
|
||||||
|
|
||||||
const Container = styled.div``
|
const Container = styled.div`
|
||||||
|
padding: 20px;
|
||||||
|
`
|
||||||
export default SystemAssistantSettings
|
export default SystemAssistantSettings
|
||||||
|
|||||||
@ -0,0 +1,112 @@
|
|||||||
|
import { Provider } from '@renderer/types'
|
||||||
|
import { FC, useEffect, useState } from 'react'
|
||||||
|
import styled from 'styled-components'
|
||||||
|
import { Button, Card, Divider, Input } from 'antd'
|
||||||
|
import { useProvider } from '@renderer/hooks/useProvider'
|
||||||
|
import ModalListPopup from '@renderer/components/Popups/ModalListPopup'
|
||||||
|
import { groupBy } from 'lodash'
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
provider: Provider
|
||||||
|
}
|
||||||
|
|
||||||
|
const ModalProviderSetting: FC<Props> = ({ provider }) => {
|
||||||
|
const [apiKey, setApiKey] = useState(provider.apiKey)
|
||||||
|
const [apiHost, setApiHost] = useState(provider.apiHost)
|
||||||
|
const [apiPath, setApiPath] = useState(provider.apiPath)
|
||||||
|
const { updateProvider, models } = useProvider(provider.id)
|
||||||
|
|
||||||
|
const modelGroups = groupBy(models, 'group')
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setApiKey(provider.apiKey)
|
||||||
|
setApiHost(provider.apiHost)
|
||||||
|
setApiPath(provider.apiPath)
|
||||||
|
}, [provider])
|
||||||
|
|
||||||
|
const onUpdateApiKey = () => {
|
||||||
|
updateProvider({ ...provider, apiKey })
|
||||||
|
}
|
||||||
|
|
||||||
|
const onUpdateApiHost = () => {
|
||||||
|
updateProvider({ ...provider, apiHost })
|
||||||
|
}
|
||||||
|
|
||||||
|
const onUpdateApiPath = () => {
|
||||||
|
updateProvider({ ...provider, apiHost })
|
||||||
|
}
|
||||||
|
|
||||||
|
const onAddModal = () => {
|
||||||
|
ModalListPopup.show({ provider })
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Container>
|
||||||
|
<Title>{provider.name}</Title>
|
||||||
|
<Divider style={{ width: '100%', margin: '10px 0' }} />
|
||||||
|
<SubTitle>API Key</SubTitle>
|
||||||
|
<Input value={apiKey} placeholder="API Key" onChange={(e) => setApiKey(e.target.value)} onBlur={onUpdateApiKey} />
|
||||||
|
<SubTitle>API Host</SubTitle>
|
||||||
|
<Input
|
||||||
|
value={apiHost}
|
||||||
|
placeholder="API Host"
|
||||||
|
onChange={(e) => setApiHost(e.target.value)}
|
||||||
|
onBlur={onUpdateApiHost}
|
||||||
|
/>
|
||||||
|
<SubTitle>API Path</SubTitle>
|
||||||
|
<Input
|
||||||
|
value={apiPath}
|
||||||
|
placeholder="API Path"
|
||||||
|
onChange={(e) => setApiPath(e.target.value)}
|
||||||
|
onBlur={onUpdateApiPath}
|
||||||
|
/>
|
||||||
|
<SubTitle>Models</SubTitle>
|
||||||
|
{Object.keys(modelGroups).map((group) => (
|
||||||
|
<Card key={group} type="inner" title={group} style={{ marginBottom: '10px' }} size="small">
|
||||||
|
{modelGroups[group].map((model) => (
|
||||||
|
<ModelListItem key={model.id}>{model.id}</ModelListItem>
|
||||||
|
))}
|
||||||
|
</Card>
|
||||||
|
))}
|
||||||
|
<Button type="primary" style={{ width: '100px', marginTop: '10px' }} onClick={onAddModal}>
|
||||||
|
Edit Models
|
||||||
|
</Button>
|
||||||
|
</Container>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
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`
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: 5px 0;
|
||||||
|
`
|
||||||
|
|
||||||
|
export default ModalProviderSetting
|
||||||
@ -6,5 +6,6 @@ export const EVENT_NAMES = {
|
|||||||
SEND_MESSAGE: 'SEND_MESSAGE',
|
SEND_MESSAGE: 'SEND_MESSAGE',
|
||||||
AI_CHAT_COMPLETION: 'AI_CHAT_COMPLETION',
|
AI_CHAT_COMPLETION: 'AI_CHAT_COMPLETION',
|
||||||
AI_AUTO_RENAME: 'AI_AUTO_RENAME',
|
AI_AUTO_RENAME: 'AI_AUTO_RENAME',
|
||||||
CLEAR_CONVERSATION: 'CLEAR_CONVERSATION'
|
CLEAR_CONVERSATION: 'CLEAR_CONVERSATION',
|
||||||
|
ADD_ASSISTANT: 'ADD_ASSISTANT'
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,10 +4,12 @@ import { FLUSH, PAUSE, PERSIST, persistReducer, persistStore, PURGE, REGISTER, R
|
|||||||
import storage from 'redux-persist/lib/storage'
|
import storage from 'redux-persist/lib/storage'
|
||||||
import assistants from './assistants'
|
import assistants from './assistants'
|
||||||
import settings from './settings'
|
import settings from './settings'
|
||||||
|
import llm from './llm'
|
||||||
|
|
||||||
const rootReducer = combineReducers({
|
const rootReducer = combineReducers({
|
||||||
assistants,
|
assistants,
|
||||||
settings
|
settings,
|
||||||
|
llm
|
||||||
})
|
})
|
||||||
|
|
||||||
const store = configureStore({
|
const store = configureStore({
|
||||||
|
|||||||
@ -1,31 +1,88 @@
|
|||||||
import { createSlice } from '@reduxjs/toolkit'
|
import { createSlice, PayloadAction } from '@reduxjs/toolkit'
|
||||||
|
import { Model, Provider } from '@renderer/types'
|
||||||
type Provider = {
|
import { uniqBy } from 'lodash'
|
||||||
id: string
|
|
||||||
name: string
|
|
||||||
apiKey: string
|
|
||||||
apiUrl: string
|
|
||||||
url: string
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface LlmState {
|
export interface LlmState {
|
||||||
providers: Provider[]
|
providers: Provider[]
|
||||||
}
|
}
|
||||||
|
|
||||||
const initialState: LlmState = {
|
const initialState: LlmState = {
|
||||||
providers: []
|
providers: [
|
||||||
|
{
|
||||||
|
id: 'openai',
|
||||||
|
name: 'OpenAI',
|
||||||
|
apiKey: '',
|
||||||
|
apiHost: 'https://api.openai.com',
|
||||||
|
apiPath: '/v1/chat/completions',
|
||||||
|
isSystem: true,
|
||||||
|
models: []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'silicon',
|
||||||
|
name: 'Silicon',
|
||||||
|
apiKey: '',
|
||||||
|
apiHost: 'https://api.siliconflow.cn',
|
||||||
|
apiPath: '/v1/chat/completions',
|
||||||
|
isSystem: true,
|
||||||
|
models: []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'deepseek',
|
||||||
|
name: 'deepseek',
|
||||||
|
apiKey: '',
|
||||||
|
apiHost: 'https://api.deepseek.com',
|
||||||
|
apiPath: '/v1/chat/completions',
|
||||||
|
isSystem: true,
|
||||||
|
models: []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'groq',
|
||||||
|
name: 'Groq',
|
||||||
|
apiKey: '',
|
||||||
|
apiHost: 'https://api.groq.com',
|
||||||
|
apiPath: '/v1/chat/completions',
|
||||||
|
isSystem: true,
|
||||||
|
models: []
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
const settingsSlice = createSlice({
|
const settingsSlice = createSlice({
|
||||||
name: 'settings',
|
name: 'settings',
|
||||||
initialState,
|
initialState,
|
||||||
reducers: {
|
reducers: {
|
||||||
updateProvider: () => {
|
updateProvider: (state, action: PayloadAction<Provider>) => {
|
||||||
//
|
state.providers = state.providers.map((p) => (p.id === action.payload.id ? { ...p, ...action.payload } : p))
|
||||||
|
},
|
||||||
|
addProvider: (state, action: PayloadAction<Provider>) => {
|
||||||
|
state.providers.push(action.payload)
|
||||||
|
},
|
||||||
|
removeProvider: (state, action: PayloadAction<{ id: string }>) => {
|
||||||
|
state.providers = state.providers.filter((p) => p.id !== action.payload.id && !p.isSystem)
|
||||||
|
},
|
||||||
|
addModel: (state, action: PayloadAction<{ providerId: string; model: Model }>) => {
|
||||||
|
state.providers = state.providers.map((p) =>
|
||||||
|
p.id === action.payload.providerId
|
||||||
|
? {
|
||||||
|
...p,
|
||||||
|
models: uniqBy(p.models.concat(action.payload.model), 'id')
|
||||||
|
}
|
||||||
|
: p
|
||||||
|
)
|
||||||
|
},
|
||||||
|
removeModel: (state, action: PayloadAction<{ providerId: string; model: Model }>) => {
|
||||||
|
state.providers = state.providers.map((p) =>
|
||||||
|
p.id === action.payload.providerId
|
||||||
|
? {
|
||||||
|
...p,
|
||||||
|
models: p.models.filter((m) => m.id !== action.payload.model.id)
|
||||||
|
}
|
||||||
|
: p
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
export const { updateProvider } = settingsSlice.actions
|
export const { updateProvider, addProvider, removeProvider, addModel, removeModel } = settingsSlice.actions
|
||||||
|
|
||||||
export default settingsSlice.reducer
|
export default settingsSlice.reducer
|
||||||
|
|||||||
@ -27,3 +27,29 @@ export type User = {
|
|||||||
avatar: string
|
avatar: string
|
||||||
email: string
|
email: string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type Provider = {
|
||||||
|
id: string
|
||||||
|
name: string
|
||||||
|
apiKey: string
|
||||||
|
apiHost: string
|
||||||
|
apiPath: string
|
||||||
|
models: Model[]
|
||||||
|
isSystem?: boolean
|
||||||
|
isDefault?: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
export type Model = {
|
||||||
|
id: string
|
||||||
|
name: string
|
||||||
|
group: string
|
||||||
|
temperature: number
|
||||||
|
}
|
||||||
|
|
||||||
|
export type SystemAssistant = {
|
||||||
|
id: string
|
||||||
|
name: string
|
||||||
|
description: string
|
||||||
|
prompt: string
|
||||||
|
group: string
|
||||||
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user