fix: topic missing bug and delete assistant crash

This commit is contained in:
kangfenmao 2024-07-21 23:42:54 +08:00
parent 117069e450
commit 2993ab8dc1
6 changed files with 84 additions and 95 deletions

View File

@ -1,6 +1,6 @@
import { QuestionCircleOutlined } from '@ant-design/icons' import { QuestionCircleOutlined } from '@ant-design/icons'
import { DEFAULT_CONEXTCOUNT, DEFAULT_TEMPERATURE } from '@renderer/config/constant' import { DEFAULT_CONEXTCOUNT, DEFAULT_TEMPERATURE } from '@renderer/config/constant'
import { useAssistants } from '@renderer/hooks/useAssistant' import { useAssistant, 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 { debounce } from 'lodash' import { debounce } from 'lodash'
@ -12,10 +12,11 @@ interface Props {
assistant: Assistant assistant: Assistant
} }
const PopoverContent: FC<Props> = ({ assistant }) => { const PopoverContent: FC<Props> = (props) => {
const { assistant } = useAssistant(props.assistant.id)
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 [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 = useCallback( const onUpdateAssistantSettings = useCallback(
@ -64,8 +65,8 @@ const PopoverContent: FC<Props> = ({ assistant }) => {
} }
useEffect(() => { useEffect(() => {
setTemperature(assistant.settings?.temperature ?? DEFAULT_TEMPERATURE) setTemperature(assistant?.settings?.temperature ?? DEFAULT_TEMPERATURE)
setConextCount(assistant.settings?.contextCount ?? DEFAULT_CONEXTCOUNT) setConextCount(assistant?.settings?.contextCount ?? DEFAULT_CONEXTCOUNT)
}, [assistant]) }, [assistant])
return ( return (

View File

@ -5,9 +5,10 @@ import { useAssistants } from '@renderer/hooks/useAssistant'
import { getDefaultTopic } from '@renderer/services/assistant' import { getDefaultTopic } from '@renderer/services/assistant'
import { Assistant } from '@renderer/types' import { Assistant } from '@renderer/types'
import { droppableReorder, uuid } from '@renderer/utils' import { droppableReorder, uuid } from '@renderer/utils'
import { Dropdown, MenuProps } from 'antd' import { Dropdown } from 'antd'
import { ItemType } from 'antd/es/menu/interface'
import { last } from 'lodash' import { last } from 'lodash'
import { FC, useRef } from 'react' import { FC } from 'react'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import styled from 'styled-components' import styled from 'styled-components'
@ -19,7 +20,6 @@ interface Props {
const Assistants: FC<Props> = ({ activeAssistant, setActiveAssistant, onCreateAssistant }) => { const Assistants: FC<Props> = ({ activeAssistant, setActiveAssistant, onCreateAssistant }) => {
const { assistants, removeAssistant, updateAssistant, addAssistant, updateAssistants } = useAssistants() const { assistants, removeAssistant, updateAssistant, addAssistant, updateAssistants } = useAssistants()
const targetAssistant = useRef<Assistant | null>(null)
const { t } = useTranslation() const { t } = useTranslation()
@ -29,26 +29,25 @@ const Assistants: FC<Props> = ({ activeAssistant, setActiveAssistant, onCreateAs
removeAssistant(assistant.id) removeAssistant(assistant.id)
} }
const items: MenuProps['items'] = [ const getMenuItems = (assistant: Assistant) =>
[
{ {
label: t('common.edit'), label: t('common.edit'),
key: 'edit', key: 'edit',
icon: <EditOutlined />, icon: <EditOutlined />,
async onClick() { async onClick() {
if (targetAssistant.current) { const _assistant = await AssistantSettingPopup.show({ assistant })
const _assistant = await AssistantSettingPopup.show({ assistant: targetAssistant.current })
updateAssistant(_assistant) updateAssistant(_assistant)
} }
}
}, },
{ {
label: t('common.duplicate'), label: t('common.duplicate'),
key: 'duplicate', key: 'duplicate',
icon: <CopyOutlined />, icon: <CopyOutlined />,
async onClick() { onClick: async () => {
const assistant: Assistant = { ...activeAssistant, id: uuid(), topics: [getDefaultTopic()] } const _assistant: Assistant = { ...assistant, id: uuid(), topics: [getDefaultTopic()] }
addAssistant(assistant) addAssistant(_assistant)
setActiveAssistant(assistant) setActiveAssistant(_assistant)
} }
}, },
{ type: 'divider' }, { type: 'divider' },
@ -57,11 +56,9 @@ const Assistants: FC<Props> = ({ activeAssistant, setActiveAssistant, onCreateAs
key: 'delete', key: 'delete',
icon: <DeleteOutlined />, icon: <DeleteOutlined />,
danger: true, danger: true,
onClick: () => { onClick: () => onDelete(assistant)
targetAssistant.current && onDelete(targetAssistant.current)
} }
} ] as ItemType[]
]
const onDragEnd = (result: DropResult) => { const onDragEnd = (result: DropResult) => {
if (result.destination) { if (result.destination) {
@ -82,11 +79,7 @@ const Assistants: FC<Props> = ({ activeAssistant, setActiveAssistant, onCreateAs
<Draggable key={`draggable_${assistant.id}_${index}`} draggableId={assistant.id} index={index}> <Draggable key={`draggable_${assistant.id}_${index}`} draggableId={assistant.id} index={index}>
{(provided) => ( {(provided) => (
<div ref={provided.innerRef} {...provided.draggableProps} {...provided.dragHandleProps}> <div ref={provided.innerRef} {...provided.draggableProps} {...provided.dragHandleProps}>
<Dropdown <Dropdown key={assistant.id} menu={{ items: getMenuItems(assistant) }} trigger={['contextMenu']}>
key={assistant.id}
menu={{ items }}
trigger={['contextMenu']}
onOpenChange={() => (targetAssistant.current = assistant)}>
<AssistantItem <AssistantItem
onClick={() => setActiveAssistant(assistant)} onClick={() => setActiveAssistant(assistant)}
className={assistant.id === activeAssistant?.id ? 'active' : ''}> className={assistant.id === activeAssistant?.id ? 'active' : ''}>

View File

@ -65,7 +65,6 @@ const Messages: FC<Props> = ({ assistant, topic }) => {
useEffect(() => { useEffect(() => {
const unsubscribes = [ const unsubscribes = [
EventEmitter.on(EVENT_NAMES.SEND_MESSAGE, async (msg: Message) => { EventEmitter.on(EVENT_NAMES.SEND_MESSAGE, async (msg: Message) => {
console.debug({ assistant, provider, message: msg, topic })
onSendMessage(msg) onSendMessage(msg)
fetchChatCompletion({ assistant, messages: [...messages, msg], topic, onResponse: setLastMessage }) fetchChatCompletion({ assistant, messages: [...messages, msg], topic, onResponse: setLastMessage })
}), }),

View File

@ -4,7 +4,7 @@ import { useShowRightSidebar } from '@renderer/hooks/useStore'
import { fetchMessagesSummary } from '@renderer/services/api' import { fetchMessagesSummary } from '@renderer/services/api'
import { Assistant, Topic } from '@renderer/types' import { Assistant, Topic } from '@renderer/types'
import { Button, Dropdown, MenuProps, Popconfirm } from 'antd' import { Button, Dropdown, MenuProps, Popconfirm } from 'antd'
import { FC, useRef } from 'react' import { FC } from 'react'
import styled from 'styled-components' import styled from 'styled-components'
import { DeleteOutlined, EditOutlined, SignatureOutlined } from '@ant-design/icons' import { DeleteOutlined, EditOutlined, SignatureOutlined } from '@ant-design/icons'
import LocalStorage from '@renderer/services/storage' import LocalStorage from '@renderer/services/storage'
@ -21,22 +21,20 @@ interface Props {
const Topics: FC<Props> = ({ assistant, activeTopic, setActiveTopic }) => { const Topics: FC<Props> = ({ assistant, activeTopic, setActiveTopic }) => {
const { showRightSidebar } = useShowRightSidebar() const { showRightSidebar } = useShowRightSidebar()
const { removeTopic, updateTopic, removeAllTopics, updateTopics } = useAssistant(assistant.id) const { removeTopic, updateTopic, removeAllTopics, updateTopics } = useAssistant(assistant.id)
const currentTopic = useRef<Topic | null>(null)
const { t } = useTranslation() const { t } = useTranslation()
const topicMenuItems: MenuProps['items'] = [ const getTopicMenuItems = (topic: Topic) => {
const menus: MenuProps['items'] = [
{ {
label: t('assistant.topics.auto_rename'), label: t('assistant.topics.auto_rename'),
key: 'auto-rename', key: 'auto-rename',
icon: <SignatureOutlined />, icon: <SignatureOutlined />,
async onClick() { async onClick() {
if (currentTopic.current) { const messages = await LocalStorage.getTopicMessages(topic.id)
const messages = await LocalStorage.getTopicMessages(currentTopic.current.id)
if (messages.length >= 2) { if (messages.length >= 2) {
const summaryText = await fetchMessagesSummary({ messages, assistant }) const summaryText = await fetchMessagesSummary({ messages, assistant })
if (summaryText) { if (summaryText) {
updateTopic({ ...currentTopic.current, name: summaryText }) updateTopic({ ...topic, name: summaryText })
}
} }
} }
} }
@ -49,31 +47,33 @@ const Topics: FC<Props> = ({ assistant, activeTopic, setActiveTopic }) => {
const name = await PromptPopup.show({ const name = await PromptPopup.show({
title: t('assistant.topics.edit.title'), title: t('assistant.topics.edit.title'),
message: t('assistant.topics.edit.placeholder'), message: t('assistant.topics.edit.placeholder'),
defaultValue: currentTopic.current?.name || '' defaultValue: topic?.name || ''
}) })
if (name && currentTopic.current && currentTopic.current?.name !== name) { if (name && topic?.name !== name) {
updateTopic({ ...currentTopic.current, name }) updateTopic({ ...topic, name })
} }
} }
} }
] ]
if (assistant.topics.length > 1) { if (assistant.topics.length > 1) {
topicMenuItems.push({ type: 'divider' }) menus.push({ type: 'divider' })
topicMenuItems.push({ menus.push({
label: t('common.delete'), label: t('common.delete'),
danger: true, danger: true,
key: 'delete', key: 'delete',
icon: <DeleteOutlined />, icon: <DeleteOutlined />,
onClick() { onClick() {
if (assistant.topics.length === 1) return if (assistant.topics.length === 1) return
currentTopic.current && removeTopic(currentTopic.current) removeTopic(topic)
currentTopic.current = null
setActiveTopic(assistant.topics[0]) setActiveTopic(assistant.topics[0])
} }
}) })
} }
return menus
}
const onDragEnd = (result: DropResult) => { const onDragEnd = (result: DropResult) => {
if (result.destination) { if (result.destination) {
const sourceIndex = result.source.index const sourceIndex = result.source.index
@ -108,11 +108,7 @@ const Topics: FC<Props> = ({ assistant, activeTopic, setActiveTopic }) => {
<Draggable key={`draggable_${topic.id}_${index}`} draggableId={topic.id} index={index}> <Draggable key={`draggable_${topic.id}_${index}`} draggableId={topic.id} index={index}>
{(provided) => ( {(provided) => (
<div ref={provided.innerRef} {...provided.draggableProps} {...provided.dragHandleProps}> <div ref={provided.innerRef} {...provided.draggableProps} {...provided.dragHandleProps}>
<Dropdown <Dropdown menu={{ items: getTopicMenuItems(topic) }} trigger={['contextMenu']} key={topic.id}>
menu={{ items: topicMenuItems }}
trigger={['contextMenu']}
key={topic.id}
onOpenChange={(open) => open && (currentTopic.current = topic)}>
<TopicListItem <TopicListItem
className={topic.id === activeTopic?.id ? 'active' : ''} className={topic.id === activeTopic?.id ? 'active' : ''}
onClick={() => setActiveTopic(topic)}> onClick={() => setActiveTopic(topic)}>

View File

@ -47,7 +47,7 @@ export default class ProviderSDK {
model: model.id, model: model.id,
messages: [systemMessage, ...userMessages].filter(Boolean) as MessageParam[], messages: [systemMessage, ...userMessages].filter(Boolean) as MessageParam[],
max_tokens: 4096, max_tokens: 4096,
temperature: assistant.settings?.temperature temperature: assistant?.settings?.temperature
}) })
.on('text', (text) => onChunk({ text: text || '' })) .on('text', (text) => onChunk({ text: text || '' }))
.on('finalMessage', (message) => .on('finalMessage', (message) =>
@ -64,7 +64,7 @@ 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,
temperature: assistant.settings?.temperature temperature: assistant?.settings?.temperature
}) })
for await (const chunk of stream) { for await (const chunk of stream) {
if (window.keyv.get(EVENT_NAMES.CHAT_COMPLETION_PAUSED)) break if (window.keyv.get(EVENT_NAMES.CHAT_COMPLETION_PAUSED)) break

View File

@ -169,10 +169,10 @@ export function getFirstCharacter(str) {
} }
export const getAssistantSettings = (assistant: Assistant): AssistantSettings => { export const getAssistantSettings = (assistant: Assistant): AssistantSettings => {
const contextCount = assistant.settings?.contextCount ?? DEFAULT_CONEXTCOUNT const contextCount = assistant?.settings?.contextCount ?? DEFAULT_CONEXTCOUNT
return { return {
contextCount: contextCount === 20 ? 100000 : contextCount, contextCount: contextCount === 20 ? 100000 : contextCount,
temperature: assistant.settings?.temperature ?? DEFAULT_TEMPERATURE temperature: assistant?.settings?.temperature ?? DEFAULT_TEMPERATURE
} }
} }