fix: assistant settings bugs

This commit is contained in:
kangfenmao 2024-07-21 19:56:02 +08:00
parent 4169a2ef35
commit c5965dc696
14 changed files with 124 additions and 211 deletions

View File

@ -1,3 +1,2 @@
export const DEFAULT_TEMPERATURE = 0.7 export const DEFAULT_TEMPERATURE = 0.7
export const DEFAULT_MAXTOKENS = 800
export const DEFAULT_CONEXTCOUNT = 5 export const DEFAULT_CONEXTCOUNT = 5

View File

@ -66,11 +66,10 @@ const resources = {
'input.send': 'Send', 'input.send': 'Send',
'input.pause': 'Pause', 'input.pause': 'Pause',
'input.settings': 'Settings', 'input.settings': 'Settings',
'input.estimated_tokens': 'Estimated tokens: ',
'settings.temperature': 'Temperature', 'settings.temperature': 'Temperature',
'settings.temperature.tip': 'settings.temperature.tip':
'Lower values make the model more creative and unpredictable, while higher values make it more deterministic and precise.', 'Lower values make the model more creative and unpredictable, while higher values make it more deterministic and precise.',
'settings.max_tokens': 'Max Tokens',
'settings.max_tokens.tip': 'The maximum number of tokens to generate in the completion.',
'settings.conext_count': 'Context', 'settings.conext_count': 'Context',
'settings.conext_count.tip': 'The number of previous messages to keep in the context.', 'settings.conext_count.tip': 'The number of previous messages to keep in the context.',
'settings.reset': 'Reset', 'settings.reset': 'Reset',
@ -200,12 +199,10 @@ const resources = {
'input.send': '发送', 'input.send': '发送',
'input.pause': '暂停', 'input.pause': '暂停',
'input.settings': '设置', 'input.settings': '设置',
'input.estimated_tokens': '预估消耗',
'settings.temperature': '模型温度', 'settings.temperature': '模型温度',
'settings.temperature.tip': 'settings.temperature.tip':
'模型生成文本的随机程度。值越大,回复内容越赋有多样性、创造性、随机性;设为 0 根据事实回答。日常聊天建议设置为 0.7', '模型生成文本的随机程度。值越大,回复内容越赋有多样性、创造性、随机性;设为 0 根据事实回答。日常聊天建议设置为 0.7',
'settings.max_tokens': '最大回复',
'settings.max_tokens.tip':
'最大回复内容多少,数值越大,生成的文本越长。普通聊天建议 500-800短文生成建议 800-2000代码生成建议 2000-3600长文生成建议切换模型到 4000 左右',
'settings.conext_count': '上下文数', 'settings.conext_count': '上下文数',
'settings.conext_count.tip': 'settings.conext_count.tip':
'要保留在上下文中的消息数量,数值越大,上下文越长,消耗的 token 越多。普通聊天建议 5-10代码生成建议 5-10', '要保留在上下文中的消息数量,数值越大,上下文越长,消耗的 token 越多。普通聊天建议 5-10代码生成建议 5-10',
@ -233,7 +230,7 @@ const resources = {
}, },
settings: { settings: {
title: '设置', title: '设置',
general: '常规', general: '常规设置',
provider: '模型提供商', provider: '模型提供商',
model: '模型设置', model: '模型设置',
assistant: '默认助手', assistant: '默认助手',

View File

@ -1,9 +1,10 @@
import { QuestionCircleOutlined } from '@ant-design/icons' import { QuestionCircleOutlined } from '@ant-design/icons'
import { DEFAULT_CONEXTCOUNT, DEFAULT_MAXTOKENS, DEFAULT_TEMPERATURE } from '@renderer/config/constant' import { DEFAULT_CONEXTCOUNT, DEFAULT_TEMPERATURE } from '@renderer/config/constant'
import { useAssistants } from '@renderer/hooks/useAssistant' import { useAssistants } from '@renderer/hooks/useAssistant'
import { Assistant } from '@renderer/types' import { Assistant } from '@renderer/types'
import { Button, Col, InputNumber, Popover, Row, Slider, Tooltip } from 'antd' import { Button, Col, InputNumber, Popover, Row, Slider, Tooltip } from 'antd'
import { FC, PropsWithChildren, useState } from 'react' import { debounce } from 'lodash'
import { FC, PropsWithChildren, useCallback, useEffect, useState } from 'react'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import styled from 'styled-components' import styled from 'styled-components'
@ -14,29 +15,26 @@ interface Props {
const PopoverContent: FC<Props> = ({ assistant }) => { const PopoverContent: FC<Props> = ({ assistant }) => {
const { updateAssistant } = useAssistants() const { updateAssistant } = useAssistants()
const [temperature, setTemperature] = useState(assistant.settings?.temperature ?? DEFAULT_TEMPERATURE) const [temperature, setTemperature] = useState(assistant.settings?.temperature ?? DEFAULT_TEMPERATURE)
const [maxTokens, setMaxTokens] = useState(assistant.settings?.maxTokens ?? DEFAULT_MAXTOKENS)
const [contextCount, setConextCount] = useState(assistant.settings?.contextCount ?? DEFAULT_CONEXTCOUNT) const [contextCount, setConextCount] = useState(assistant.settings?.contextCount ?? DEFAULT_CONEXTCOUNT)
const { t } = useTranslation() const { t } = useTranslation()
const onUpdateAssistantSettings = ({ const onUpdateAssistantSettings = useCallback(
_temperature, debounce(
_maxTokens, ({ _temperature, _contextCount }: { _temperature?: number; _contextCount?: number }) => {
_contextCount updateAssistant({
}: { ...assistant,
_temperature?: number settings: {
_maxTokens?: number ...assistant.settings,
_contextCount?: number temperature: _temperature ?? temperature,
}) => { contextCount: _contextCount ?? contextCount
updateAssistant({ }
...assistant, })
settings: { },
...assistant.settings, 1000,
temperature: _temperature ?? temperature, { leading: false, trailing: true }
maxTokens: _maxTokens ?? maxTokens, ),
contextCount: _contextCount ?? contextCount []
} )
})
}
const onTemperatureChange = (value) => { const onTemperatureChange = (value) => {
if (!isNaN(value as number)) { if (!isNaN(value as number)) {
@ -45,13 +43,6 @@ const PopoverContent: FC<Props> = ({ assistant }) => {
} }
} }
const onMaxTokensChange = (value) => {
if (!isNaN(value as number)) {
setMaxTokens(value)
onUpdateAssistantSettings({ _maxTokens: value })
}
}
const onConextCountChange = (value) => { const onConextCountChange = (value) => {
if (!isNaN(value as number)) { if (!isNaN(value as number)) {
setConextCount(value) setConextCount(value)
@ -61,24 +52,27 @@ const PopoverContent: FC<Props> = ({ assistant }) => {
const onReset = () => { const onReset = () => {
setTemperature(DEFAULT_TEMPERATURE) setTemperature(DEFAULT_TEMPERATURE)
setMaxTokens(DEFAULT_MAXTOKENS)
setConextCount(DEFAULT_CONEXTCOUNT) setConextCount(DEFAULT_CONEXTCOUNT)
updateAssistant({ updateAssistant({
...assistant, ...assistant,
settings: { settings: {
...assistant.settings, ...assistant.settings,
temperature: DEFAULT_TEMPERATURE, temperature: DEFAULT_TEMPERATURE,
maxTokens: DEFAULT_MAXTOKENS,
contextCount: DEFAULT_CONEXTCOUNT contextCount: DEFAULT_CONEXTCOUNT
} }
}) })
} }
useEffect(() => {
setTemperature(assistant.settings?.temperature ?? DEFAULT_TEMPERATURE)
setConextCount(assistant.settings?.contextCount ?? DEFAULT_CONEXTCOUNT)
}, [assistant])
return ( return (
<Container> <Container>
<Row align="middle" style={{ marginBottom: 10 }} gutter={20}> <Row align="middle" style={{ marginBottom: 10 }} gutter={20}>
<Col span={5}> <Col span={6}>
<Row align="middle"> <Row align="middle" justify="end">
<Label>{t('assistant.settings.temperature')}</Label> <Label>{t('assistant.settings.temperature')}</Label>
<Tooltip title={t('assistant.settings.temperature.tip')}> <Tooltip title={t('assistant.settings.temperature.tip')}>
<QuestionIcon /> <QuestionIcon />
@ -95,11 +89,11 @@ const PopoverContent: FC<Props> = ({ assistant }) => {
step={0.1} step={0.1}
/> />
</Col> </Col>
<Col span={4}> <Col span={3}>
<InputNumber <InputNumber
min={0} min={0}
max={1.2} max={1.2}
style={{ width: 70, marginLeft: 5, textAlign: 'center' }} style={{ width: 50, marginLeft: 5, textAlign: 'center' }}
step={0.1} step={0.1}
value={temperature} value={temperature}
onChange={onTemperatureChange} onChange={onTemperatureChange}
@ -108,8 +102,8 @@ const PopoverContent: FC<Props> = ({ assistant }) => {
</Col> </Col>
</Row> </Row>
<Row align="middle" style={{ marginBottom: 10 }} gutter={20}> <Row align="middle" style={{ marginBottom: 10 }} gutter={20}>
<Col span={5}> <Col span={6}>
<Row align="middle"> <Row align="middle" justify="end">
<Label>{t('assistant.settings.conext_count')}</Label> <Label>{t('assistant.settings.conext_count')}</Label>
<Tooltip title={t('assistant.settings.conext_count.tip')}> <Tooltip title={t('assistant.settings.conext_count.tip')}>
<QuestionIcon /> <QuestionIcon />
@ -126,11 +120,11 @@ const PopoverContent: FC<Props> = ({ assistant }) => {
step={1} step={1}
/> />
</Col> </Col>
<Col span={4}> <Col span={3}>
<InputNumber <InputNumber
min={0} min={0}
max={20} max={20}
style={{ width: 70, marginLeft: 5, textAlign: 'center' }} style={{ width: 50, marginLeft: 5, textAlign: 'center' }}
step={1} step={1}
value={contextCount} value={contextCount}
onChange={onConextCountChange} onChange={onConextCountChange}
@ -138,41 +132,8 @@ const PopoverContent: FC<Props> = ({ assistant }) => {
/> />
</Col> </Col>
</Row> </Row>
<Row align="middle" gutter={20}> <Row justify="center">
<Col span={5}> <Button onClick={onReset}>{t('assistant.settings.reset')}</Button>
<Row align="middle">
<Label>{t('assistant.settings.max_tokens')}</Label>
<Tooltip title={t('assistant.settings.max_tokens.tip')}>
<QuestionIcon />
</Tooltip>
</Row>
</Col>
<Col span={14}>
<Slider
min={0}
max={5000}
onChange={onMaxTokensChange}
value={typeof maxTokens === 'number' ? maxTokens : 0}
marks={{ 0: '0', 800: '800', 2000: '2000', 3600: '3600', 5000: t('assistant.settings.max') }}
step={64}
/>
</Col>
<Col span={4}>
<InputNumber
min={0}
max={5000}
style={{ width: 70, marginLeft: 5, textAlign: 'center' }}
step={64}
value={maxTokens}
onChange={onMaxTokensChange}
controls={false}
/>
</Col>
</Row>
<Row justify="center" style={{ marginTop: 10 }}>
<Button onClick={onReset} style={{ marginRight: 10 }}>
{t('assistant.settings.reset')}
</Button>
</Row> </Row>
</Container> </Container>
) )
@ -199,7 +160,7 @@ const Container = styled.div`
display: flex; display: flex;
flex-direction: column; flex-direction: column;
margin-bottom: 8px; margin-bottom: 8px;
width: 500px; width: 420px;
padding: 5px; padding: 5px;
` `

View File

@ -24,11 +24,9 @@ const Assistants: FC<Props> = ({ activeAssistant, setActiveAssistant, onCreateAs
const { t } = useTranslation() const { t } = useTranslation()
const onDelete = (assistant: Assistant) => { const onDelete = (assistant: Assistant) => {
const _assistant = last(assistants.filter((a) => a.id !== assistant.id))
_assistant ? setActiveAssistant(_assistant) : onCreateAssistant()
removeAssistant(assistant.id) removeAssistant(assistant.id)
setTimeout(() => {
const _assistant = last(assistants.filter((a) => a.id !== assistant.id))
_assistant ? setActiveAssistant(_assistant) : onCreateAssistant()
}, 0)
} }
const items: MenuProps['items'] = [ const items: MenuProps['items'] = [

View File

@ -1,5 +1,5 @@
import { Assistant, Message } from '@renderer/types' import { Assistant } from '@renderer/types'
import { FC, useRef } from 'react' import { FC } from 'react'
import styled from 'styled-components' import styled from 'styled-components'
import Inputbar from './Inputbar' import Inputbar from './Inputbar'
import Messages from './Messages' import Messages from './Messages'
@ -15,17 +15,12 @@ interface Props {
const Chat: FC<Props> = (props) => { const Chat: FC<Props> = (props) => {
const { assistant } = useAssistant(props.assistant.id) const { assistant } = useAssistant(props.assistant.id)
const { activeTopic, setActiveTopic } = useActiveTopic(assistant) const { activeTopic, setActiveTopic } = useActiveTopic(assistant)
const messagesRef = useRef<Message[]>([])
if (!assistant) {
return null
}
return ( return (
<Container id="chat"> <Container id="chat">
<Flex vertical flex={1} justify="space-between"> <Flex vertical flex={1} justify="space-between">
<Messages assistant={assistant} topic={activeTopic} messagesRef={messagesRef} /> <Messages assistant={assistant} topic={activeTopic} />
<Inputbar assistant={assistant} setActiveTopic={setActiveTopic} messagesRef={messagesRef} /> <Inputbar assistant={assistant} setActiveTopic={setActiveTopic} />
</Flex> </Flex>
<Topics assistant={assistant} activeTopic={activeTopic} setActiveTopic={setActiveTopic} /> <Topics assistant={assistant} activeTopic={activeTopic} setActiveTopic={setActiveTopic} />
</Container> </Container>

View File

@ -1,7 +1,7 @@
import { EVENT_NAMES, EventEmitter } from '@renderer/services/event' import { EVENT_NAMES, EventEmitter } from '@renderer/services/event'
import { Assistant, Message, Topic } from '@renderer/types' import { Assistant, Message, Topic } from '@renderer/types'
import { estimateTokenCount, uuid } from '@renderer/utils' import { estimateInputTokenCount, uuid } from '@renderer/utils'
import { FC, MutableRefObject, useCallback, useEffect, useRef, useState } from 'react' import { FC, useCallback, useEffect, useMemo, useRef, useState } from 'react'
import styled from 'styled-components' 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'
@ -9,15 +9,15 @@ import { useShowRightSidebar } from '@renderer/hooks/useStore'
import { useAssistant } from '@renderer/hooks/useAssistant' import { useAssistant } from '@renderer/hooks/useAssistant'
import { import {
ClearOutlined, ClearOutlined,
ControlOutlined,
FullscreenExitOutlined, FullscreenExitOutlined,
FullscreenOutlined, FullscreenOutlined,
HistoryOutlined, HistoryOutlined,
PauseCircleOutlined, PauseCircleOutlined,
PlusCircleOutlined, PlusCircleOutlined
SettingOutlined
} from '@ant-design/icons' } from '@ant-design/icons'
import TextArea, { TextAreaRef } from 'antd/es/input/TextArea' import TextArea, { TextAreaRef } from 'antd/es/input/TextArea'
import { isEmpty } from 'lodash' import { debounce, isEmpty } from 'lodash'
import SendMessageSetting from './SendMessageSetting' import SendMessageSetting from './SendMessageSetting'
import { useSettings } from '@renderer/hooks/useSettings' import { useSettings } from '@renderer/hooks/useSettings'
import dayjs from 'dayjs' import dayjs from 'dayjs'
@ -30,15 +30,15 @@ import AssistantSettings from './AssistantSettings'
interface Props { interface Props {
assistant: Assistant assistant: Assistant
setActiveTopic: (topic: Topic) => void setActiveTopic: (topic: Topic) => void
messagesRef: MutableRefObject<Message[]>
} }
const Inputbar: FC<Props> = ({ assistant, setActiveTopic, messagesRef }) => { const Inputbar: FC<Props> = ({ assistant, setActiveTopic }) => {
const [text, setText] = useState('') const [text, setText] = useState('')
const { setShowRightSidebar } = useShowRightSidebar() const { setShowRightSidebar } = useShowRightSidebar()
const { addTopic } = useAssistant(assistant.id) const { addTopic } = useAssistant(assistant.id)
const { sendMessageShortcut } = useSettings() const { sendMessageShortcut } = useSettings()
const [expended, setExpend] = useState(false) const [expended, setExpend] = useState(false)
const [estimateTokenCount, setEstimateTokenCount] = useState(0)
const generating = useAppSelector((state) => state.runtime.generating) const generating = useAppSelector((state) => state.runtime.generating)
const inputRef = useRef<TextAreaRef>(null) const inputRef = useRef<TextAreaRef>(null)
@ -68,6 +68,8 @@ const Inputbar: FC<Props> = ({ assistant, setActiveTopic, messagesRef }) => {
setText('') setText('')
} }
const inputTokenCount = useMemo(() => estimateInputTokenCount(text), [text])
const handleKeyDown = (event: React.KeyboardEvent<HTMLTextAreaElement>) => { const handleKeyDown = (event: React.KeyboardEvent<HTMLTextAreaElement>) => {
if (sendMessageShortcut === 'Enter' && event.key === 'Enter') { if (sendMessageShortcut === 'Enter' && event.key === 'Enter') {
if (event.shiftKey) { if (event.shiftKey) {
@ -96,8 +98,6 @@ const Inputbar: FC<Props> = ({ assistant, setActiveTopic, messagesRef }) => {
store.dispatch(setGenerating(false)) store.dispatch(setGenerating(false))
} }
const textCount = text.length === 0 ? '' : (text: string) => estimateTokenCount(text, assistant, messagesRef.current)
// Command or Ctrl + N create new topic // Command or Ctrl + N create new topic
useEffect(() => { useEffect(() => {
const onKeydown = (e) => { const onKeydown = (e) => {
@ -113,11 +113,13 @@ const Inputbar: FC<Props> = ({ assistant, setActiveTopic, messagesRef }) => {
}, [addNewTopic, generating]) }, [addNewTopic, generating])
useEffect(() => { useEffect(() => {
const _setEstimateTokenCount = debounce(setEstimateTokenCount, 100, { leading: false, trailing: true })
const unsubscribes = [ const unsubscribes = [
EventEmitter.on(EVENT_NAMES.EDIT_MESSAGE, (message: Message) => { EventEmitter.on(EVENT_NAMES.EDIT_MESSAGE, (message: Message) => {
setText(message.content) setText(message.content)
inputRef.current?.focus() inputRef.current?.focus()
}) }),
EventEmitter.on(EVENT_NAMES.ESTIMATED_TOKEN_COUNT, _setEstimateTokenCount)
] ]
return () => unsubscribes.forEach((unsub) => unsub()) return () => unsubscribes.forEach((unsub) => unsub())
}, []) }, [])
@ -155,7 +157,7 @@ const Inputbar: FC<Props> = ({ assistant, setActiveTopic, messagesRef }) => {
</Tooltip> </Tooltip>
<AssistantSettings assistant={assistant}> <AssistantSettings assistant={assistant}>
<ToolbarButton type="text"> <ToolbarButton type="text">
<SettingOutlined /> <ControlOutlined />
</ToolbarButton> </ToolbarButton>
</AssistantSettings> </AssistantSettings>
<Tooltip placement="top" title={expended ? t('assistant.input.collapse') : t('assistant.input.expand')} arrow> <Tooltip placement="top" title={expended ? t('assistant.input.collapse') : t('assistant.input.expand')} arrow>
@ -188,19 +190,12 @@ const Inputbar: FC<Props> = ({ assistant, setActiveTopic, messagesRef }) => {
contextMenu="true" contextMenu="true"
variant="borderless" variant="borderless"
showCount showCount
count={{ strategy: textCount }}
ref={inputRef} ref={inputRef}
styles={{ styles={{ textarea: { paddingLeft: 0 } }}
textarea: { paddingLeft: 0 },
count: {
position: 'absolute',
right: 5,
bottom: 5,
fontSize: 11,
display: text.length === 0 ? 'none' : 'block'
}
}}
/> />
<TextCount>
{t('assistant.input.estimated_tokens')}: {`${inputTokenCount}/${estimateTokenCount}`}
</TextCount>
</Container> </Container>
) )
} }
@ -213,6 +208,7 @@ const Container = styled.div`
border-top: 0.5px solid var(--color-border); border-top: 0.5px solid var(--color-border);
padding: 5px 15px; padding: 5px 15px;
transition: all 0.3s ease; transition: all 0.3s ease;
position: relative;
` `
const Textarea = styled(TextArea)` const Textarea = styled(TextArea)`
@ -256,4 +252,12 @@ const ToolbarButton = styled(Button)`
} }
` `
const TextCount = styled.div`
position: absolute;
right: 8px;
bottom: 8px;
font-size: 11px;
color: var(--color-text-3);
`
export default Inputbar export default Inputbar

View File

@ -104,8 +104,8 @@ const MessageItem: FC<Props> = ({ message, index, showMenu, onDeleteMessage }) =
<MessageMetadata>{message.modelId}</MessageMetadata> <MessageMetadata>{message.modelId}</MessageMetadata>
{message.usage && ( {message.usage && (
<> <>
<MessageMetadata style={{ textTransform: 'uppercase' }}> <MessageMetadata>
tokens used: {message.usage.total_tokens} (IN:{message.usage.prompt_tokens}/OUT: Tokens: {message.usage.total_tokens} (IN:{message.usage.prompt_tokens}/OUT:
{message.usage.completion_tokens}) {message.usage.completion_tokens})
</MessageMetadata> </MessageMetadata>
</> </>

View File

@ -1,13 +1,13 @@
import { EVENT_NAMES, EventEmitter } from '@renderer/services/event' import { EVENT_NAMES, EventEmitter } from '@renderer/services/event'
import { Assistant, Message, Topic } from '@renderer/types' import { Assistant, Message, Topic } from '@renderer/types'
import localforage from 'localforage' import localforage from 'localforage'
import { FC, MutableRefObject, useCallback, useEffect, useRef, useState } from 'react' import { FC, useCallback, useEffect, useRef, useState } from 'react'
import styled from 'styled-components' import styled from 'styled-components'
import MessageItem from './Message' import MessageItem from './Message'
import { reverse } from 'lodash' import { reverse } from 'lodash'
import { fetchChatCompletion, fetchMessagesSummary } from '@renderer/services/api' import { fetchChatCompletion, fetchMessagesSummary } from '@renderer/services/api'
import { useAssistant } from '@renderer/hooks/useAssistant' import { useAssistant } from '@renderer/hooks/useAssistant'
import { runAsyncFunction } from '@renderer/utils' import { estimateHistoryTokenCount, runAsyncFunction } from '@renderer/utils'
import LocalStorage from '@renderer/services/storage' import LocalStorage from '@renderer/services/storage'
import { useProviderByAssistant } from '@renderer/hooks/useProvider' import { useProviderByAssistant } from '@renderer/hooks/useProvider'
import { t } from 'i18next' import { t } from 'i18next'
@ -15,10 +15,9 @@ import { t } from 'i18next'
interface Props { interface Props {
assistant: Assistant assistant: Assistant
topic: Topic topic: Topic
messagesRef: MutableRefObject<Message[]>
} }
const Messages: FC<Props> = ({ assistant, topic, messagesRef }) => { const Messages: FC<Props> = ({ assistant, topic }) => {
const [messages, setMessages] = useState<Message[]>([]) const [messages, setMessages] = useState<Message[]>([])
const [lastMessage, setLastMessage] = useState<Message | null>(null) const [lastMessage, setLastMessage] = useState<Message | null>(null)
const { updateTopic } = useAssistant(assistant.id) const { updateTopic } = useAssistant(assistant.id)
@ -97,8 +96,11 @@ const Messages: FC<Props> = ({ assistant, topic, messagesRef }) => {
useEffect(() => { useEffect(() => {
containerRef.current?.scrollTo({ top: 100000, behavior: 'auto' }) containerRef.current?.scrollTo({ top: 100000, behavior: 'auto' })
messagesRef.current = messages }, [messages])
}, [messages, messagesRef])
useEffect(() => {
EventEmitter.emit(EVENT_NAMES.ESTIMATED_TOKEN_COUNT, estimateHistoryTokenCount(assistant, messages))
}, [assistant, messages])
return ( return (
<Container id="messages" key={assistant.id} ref={containerRef}> <Container id="messages" key={assistant.id} ref={containerRef}>

View File

@ -82,12 +82,8 @@ const Topics: FC<Props> = ({ assistant, activeTopic, setActiveTopic }) => {
} }
} }
if (!showRightSidebar) {
return null
}
return ( return (
<Container className={showRightSidebar ? '' : 'collapsed'}> <Container style={{ display: showRightSidebar ? 'block' : 'none' }}>
<TopicTitle> <TopicTitle>
<span> <span>
{t('assistant.topics.title')} ({assistant.topics.length}) {t('assistant.topics.title')} ({assistant.topics.length})

View File

@ -1,40 +1,38 @@
import { QuestionCircleOutlined } from '@ant-design/icons' import { QuestionCircleOutlined } from '@ant-design/icons'
import { DEFAULT_CONEXTCOUNT, DEFAULT_MAXTOKENS, DEFAULT_TEMPERATURE } from '@renderer/config/constant' import { DEFAULT_CONEXTCOUNT, DEFAULT_TEMPERATURE } from '@renderer/config/constant'
import { useDefaultAssistant } from '@renderer/hooks/useAssistant' import { useDefaultAssistant } from '@renderer/hooks/useAssistant'
import { Button, Col, Input, InputNumber, Row, Slider, Tooltip } from 'antd' import { Button, Col, Input, InputNumber, Row, Slider, Tooltip } from 'antd'
import TextArea from 'antd/es/input/TextArea' import TextArea from 'antd/es/input/TextArea'
import { FC, useState } from 'react' import { FC, useCallback, useState } from 'react'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import styled from 'styled-components' import styled from 'styled-components'
import { SettingContainer, SettingDivider, SettingSubtitle, SettingTitle } from './components' import { SettingContainer, SettingDivider, SettingSubtitle, SettingTitle } from './components'
import { debounce } from 'lodash'
const AssistantSettings: FC = () => { const AssistantSettings: FC = () => {
const { defaultAssistant, updateDefaultAssistant } = useDefaultAssistant() const { defaultAssistant, updateDefaultAssistant } = useDefaultAssistant()
const [temperature, setTemperature] = useState(defaultAssistant.settings?.temperature || DEFAULT_TEMPERATURE) const [temperature, setTemperature] = useState(defaultAssistant.settings?.temperature ?? DEFAULT_TEMPERATURE)
const [maxTokens, setMaxTokens] = useState(defaultAssistant.settings?.maxTokens || DEFAULT_MAXTOKENS) const [contextCount, setConextCount] = useState(defaultAssistant.settings?.contextCount ?? DEFAULT_CONEXTCOUNT)
const [contextCount, setConextCount] = useState(defaultAssistant.settings?.contextCount || DEFAULT_CONEXTCOUNT)
const { t } = useTranslation() const { t } = useTranslation()
const onUpdateAssistantSettings = ({ const onUpdateAssistantSettings = useCallback(
_temperature, debounce(
_maxTokens, ({ _temperature, _contextCount }: { _temperature?: number; _contextCount?: number }) => {
_contextCount updateDefaultAssistant({
}: { ...defaultAssistant,
_temperature?: number settings: {
_maxTokens?: number ...defaultAssistant.settings,
_contextCount?: number temperature: _temperature ?? temperature,
}) => { contextCount: _contextCount ?? contextCount
updateDefaultAssistant({ }
...defaultAssistant, })
settings: { },
...defaultAssistant.settings, 1000,
temperature: _temperature || temperature, { leading: false, trailing: true }
maxTokens: _maxTokens || maxTokens, ),
contextCount: _contextCount || contextCount []
} )
})
}
const onTemperatureChange = (value) => { const onTemperatureChange = (value) => {
if (!isNaN(value as number)) { if (!isNaN(value as number)) {
@ -43,13 +41,6 @@ const AssistantSettings: FC = () => {
} }
} }
const onMaxTokensChange = (value) => {
if (!isNaN(value as number)) {
setMaxTokens(value)
onUpdateAssistantSettings({ _maxTokens: value })
}
}
const onConextCountChange = (value) => { const onConextCountChange = (value) => {
if (!isNaN(value as number)) { if (!isNaN(value as number)) {
setConextCount(value) setConextCount(value)
@ -59,14 +50,12 @@ const AssistantSettings: FC = () => {
const onReset = () => { const onReset = () => {
setTemperature(DEFAULT_TEMPERATURE) setTemperature(DEFAULT_TEMPERATURE)
setMaxTokens(DEFAULT_MAXTOKENS)
setConextCount(DEFAULT_CONEXTCOUNT) setConextCount(DEFAULT_CONEXTCOUNT)
updateDefaultAssistant({ updateDefaultAssistant({
...defaultAssistant, ...defaultAssistant,
settings: { settings: {
...defaultAssistant.settings, ...defaultAssistant.settings,
temperature: DEFAULT_TEMPERATURE, temperature: DEFAULT_TEMPERATURE,
maxTokens: DEFAULT_MAXTOKENS,
contextCount: DEFAULT_CONEXTCOUNT contextCount: DEFAULT_CONEXTCOUNT
} }
}) })
@ -147,35 +136,7 @@ const AssistantSettings: FC = () => {
/> />
</Col> </Col>
</Row> </Row>
<Row align="middle"> <Button onClick={onReset} style={{ width: 100 }}>
<Label>{t('assistant.settings.max_tokens')}</Label>
<Tooltip title={t('assistant.settings.max_tokens.tip')}>
<QuestionIcon />
</Tooltip>
</Row>
<Row align="middle" gutter={20}>
<Col span={22}>
<Slider
min={0}
max={5000}
onChange={onMaxTokensChange}
value={typeof maxTokens === 'number' ? maxTokens : 0}
marks={{ 0: '0', 800: '800', 2000: '2000', 3600: '3600', 5000: t('assistant.settings.max') }}
step={64}
/>
</Col>
<Col span={2}>
<InputNumber
min={0}
max={5000}
step={100}
value={maxTokens}
onChange={onMaxTokensChange}
style={{ width: '100%' }}
/>
</Col>
</Row>
<Button onClick={onReset} style={{ width: 100, marginTop: 20 }}>
{t('assistant.settings.reset')} {t('assistant.settings.reset')}
</Button> </Button>
</SettingContainer> </SettingContainer>

View File

@ -32,11 +32,11 @@ export default class ProviderSDK {
) { ) {
const defaultModel = getDefaultModel() const defaultModel = getDefaultModel()
const model = assistant.model || defaultModel const model = assistant.model || defaultModel
const { contextCount, maxTokens } = getAssistantSettings(assistant) const { contextCount } = getAssistantSettings(assistant)
const systemMessage = assistant.prompt ? { role: 'system', content: assistant.prompt } : undefined const systemMessage = assistant.prompt ? { role: 'system', content: assistant.prompt } : undefined
const userMessages = takeRight(messages, contextCount).map((message) => ({ const userMessages = takeRight(messages, contextCount + 1).map((message) => ({
role: message.role, role: message.role,
content: message.content content: message.content
})) }))
@ -46,7 +46,7 @@ export default class ProviderSDK {
.stream({ .stream({
model: model.id, model: model.id,
messages: [systemMessage, ...userMessages].filter(Boolean) as MessageParam[], messages: [systemMessage, ...userMessages].filter(Boolean) as MessageParam[],
max_tokens: assistant.settings?.maxTokens || maxTokens, max_tokens: 4096,
temperature: assistant.settings?.temperature temperature: assistant.settings?.temperature
}) })
.on('text', (text) => onChunk({ text: text || '' })) .on('text', (text) => onChunk({ text: text || '' }))
@ -64,7 +64,6 @@ export default class ProviderSDK {
model: model.id, model: model.id,
messages: [systemMessage, ...userMessages].filter(Boolean) as ChatCompletionMessageParam[], messages: [systemMessage, ...userMessages].filter(Boolean) as ChatCompletionMessageParam[],
stream: true, stream: true,
max_tokens: assistant.settings?.maxTokens,
temperature: assistant.settings?.temperature temperature: assistant.settings?.temperature
}) })
for await (const chunk of stream) { for await (const chunk of stream) {

View File

@ -10,5 +10,6 @@ export const EVENT_NAMES = {
ADD_ASSISTANT: 'ADD_ASSISTANT', ADD_ASSISTANT: 'ADD_ASSISTANT',
EDIT_MESSAGE: 'EDIT_MESSAGE', EDIT_MESSAGE: 'EDIT_MESSAGE',
REGENERATE_MESSAGE: 'REGENERATE_MESSAGE', REGENERATE_MESSAGE: 'REGENERATE_MESSAGE',
CHAT_COMPLETION_PAUSED: 'CHAT_COMPLETION_PAUSED' CHAT_COMPLETION_PAUSED: 'CHAT_COMPLETION_PAUSED',
ESTIMATED_TOKEN_COUNT: 'ESTIMATED_TOKEN_COUNT'
} }

View File

@ -13,7 +13,6 @@ export type Assistant = {
export type AssistantSettings = { export type AssistantSettings = {
contextCount: number contextCount: number
temperature: number temperature: number
maxTokens: number
} }
export type Message = { export type Message = {

View File

@ -2,8 +2,8 @@ import { v4 as uuidv4 } from 'uuid'
import imageCompression from 'browser-image-compression' import imageCompression from 'browser-image-compression'
import { Assistant, AssistantSettings, Message, Model } from '@renderer/types' import { Assistant, AssistantSettings, Message, Model } from '@renderer/types'
import { GPTTokens } from 'gpt-tokens' import { GPTTokens } from 'gpt-tokens'
import { DEFAULT_CONEXTCOUNT, DEFAULT_MAXTOKENS, DEFAULT_TEMPERATURE } from '@renderer/config/constant' import { DEFAULT_CONEXTCOUNT, DEFAULT_TEMPERATURE } from '@renderer/config/constant'
import { take } from 'lodash' import { takeRight } from 'lodash'
export const runAsyncFunction = async (fn: () => void) => { export const runAsyncFunction = async (fn: () => void) => {
await fn() await fn()
@ -169,31 +169,32 @@ export function getFirstCharacter(str) {
} }
export const getAssistantSettings = (assistant: Assistant): AssistantSettings => { export const getAssistantSettings = (assistant: Assistant): AssistantSettings => {
const contextCount = assistant.settings?.contextCount ?? DEFAULT_CONEXTCOUNT
return { return {
contextCount: assistant.settings?.contextCount ?? DEFAULT_CONEXTCOUNT, contextCount: contextCount === 20 ? 100000 : contextCount,
temperature: assistant.settings?.temperature ?? DEFAULT_TEMPERATURE, temperature: assistant.settings?.temperature ?? DEFAULT_TEMPERATURE
maxTokens: assistant.settings?.maxTokens ?? DEFAULT_MAXTOKENS
} }
} }
export function estimateTokenCount(text: string, assistant: Assistant, msgs: Message[]) { export function estimateInputTokenCount(text: string) {
const { contextCount } = getAssistantSettings(assistant)
console.debug('contextCount', contextCount)
const input = new GPTTokens({ const input = new GPTTokens({
model: 'gpt-4o', model: 'gpt-4o',
messages: [{ role: 'user', content: text }] messages: [{ role: 'user', content: text }]
}) })
return input.usedTokens - 7
}
export function estimateHistoryTokenCount(assistant: Assistant, msgs: Message[]) {
const { contextCount } = getAssistantSettings(assistant)
const all = new GPTTokens({ const all = new GPTTokens({
model: 'gpt-4o', model: 'gpt-4o',
messages: [ messages: [
{ role: 'system', content: assistant.prompt }, { role: 'system', content: assistant.prompt },
{ role: 'user', content: text }, ...takeRight(msgs, contextCount).map((message) => ({ role: message.role, content: message.content }))
...take(msgs, contextCount).map((message) => ({ role: message.role, content: message.content }))
] ]
}) })
return `Token ${input.usedTokens - 7} / ${all.usedTokens}` as unknown as number return all.usedTokens - 7
} }