feat: quick regenerate with new model

This commit is contained in:
kangfenmao 2024-08-04 14:04:11 +08:00
parent cbd9f60cfc
commit de41199f7e
4 changed files with 80 additions and 47 deletions

View File

@ -1,6 +1,5 @@
import { TranslationOutlined } from '@ant-design/icons'
import Logo from '@renderer/assets/images/logo.png'
import { isWindows } from '@renderer/config/constant'
import useAvatar from '@renderer/hooks/useAvatar'
import { FC } from 'react'
import { Link, useLocation } from 'react-router-dom'
@ -13,7 +12,7 @@ const Sidebar: FC = () => {
const isRoute = (path: string): string => (pathname === path ? 'active' : '')
return (
<Container style={isWindows ? { paddingTop: 0 } : {}}>
<Container>
<StyledLink to="/">
<AvatarImg src={avatar || Logo} draggable={false} />
</StyledLink>

View File

@ -14,7 +14,7 @@ import useAvatar from '@renderer/hooks/useAvatar'
import { useSettings } from '@renderer/hooks/useSettings'
import { useRuntime } from '@renderer/hooks/useStore'
import { EVENT_NAMES, EventEmitter } from '@renderer/services/event'
import { Message } from '@renderer/types'
import { Message, Model } from '@renderer/types'
import { firstLetter, removeLeadingEmoji } from '@renderer/utils'
import { Avatar, Dropdown, Popconfirm, Tooltip } from 'antd'
import dayjs from 'dayjs'
@ -24,6 +24,7 @@ import { useTranslation } from 'react-i18next'
import styled from 'styled-components'
import Markdown from './markdown/Markdown'
import SelectModelDropdown from './SelectModelDropdown'
interface Props {
message: Message
@ -36,7 +37,7 @@ interface Props {
const MessageItem: FC<Props> = ({ message, index, showMenu, onDeleteMessage }) => {
const avatar = useAvatar()
const { t } = useTranslation()
const { assistant } = useAssistant(message.assistantId)
const { assistant, model, setModel } = useAssistant(message.assistantId)
const { userName, showMessageDivider, messageFont } = useSettings()
const { generating } = useRuntime()
const [copied, setCopied] = useState(false)
@ -55,10 +56,14 @@ const MessageItem: FC<Props> = ({ message, index, showMenu, onDeleteMessage }) =
const onEdit = useCallback(() => EventEmitter.emit(EVENT_NAMES.EDIT_MESSAGE, message), [message])
const onRegenerate = useCallback(() => {
onDeleteMessage?.(message)
setTimeout(() => EventEmitter.emit(EVENT_NAMES.REGENERATE_MESSAGE), 100)
}, [message, onDeleteMessage])
const onRegenerate = useCallback(
(model: Model) => {
setModel(model)
onDeleteMessage?.(message)
setTimeout(() => EventEmitter.emit(EVENT_NAMES.REGENERATE_MESSAGE), 100)
},
[message, onDeleteMessage, setModel]
)
const getUserName = useCallback(() => {
if (message.id === 'assistant') return assistant?.name
@ -144,11 +149,13 @@ const MessageItem: FC<Props> = ({ message, index, showMenu, onDeleteMessage }) =
</Tooltip>
</Popconfirm>
{canRegenerate && (
<Tooltip title={t('common.regenerate')} mouseEnterDelay={0.8}>
<ActionButton onClick={onRegenerate}>
<SyncOutlined />
</ActionButton>
</Tooltip>
<SelectModelDropdown model={model} onSelect={onRegenerate} placement="topRight">
<Tooltip title={t('common.regenerate')} mouseEnterDelay={0.8}>
<ActionButton>
<SyncOutlined />
</ActionButton>
</Tooltip>
</SelectModelDropdown>
)}
{!isUserMessage && (
<Dropdown menu={{ items: dropdownItems }} trigger={['click']} placement="topRight" arrow>

View File

@ -1,19 +1,18 @@
import { CodeSandboxOutlined } from '@ant-design/icons'
import { NavbarCenter } from '@renderer/components/app/Navbar'
import { isMac } from '@renderer/config/constant'
import { getModelLogo } from '@renderer/config/provider'
import { useAssistant } from '@renderer/hooks/useAssistant'
import { useProviders } from '@renderer/hooks/useProvider'
import { useShowAssistants } from '@renderer/hooks/useStore'
import { Assistant } from '@renderer/types'
import { removeLeadingEmoji } from '@renderer/utils'
import { Avatar, Button, Dropdown, MenuProps } from 'antd'
import { first, upperFirst } from 'lodash'
import { Button } from 'antd'
import { upperFirst } from 'lodash'
import { FC } from 'react'
import { useTranslation } from 'react-i18next'
import styled from 'styled-components'
import { NewButton } from '../HomePage'
import SelectModelDropdown from './SelectModelDropdown'
interface Props {
activeAssistant: Assistant
@ -22,29 +21,9 @@ interface Props {
const NavigationCenter: FC<Props> = ({ activeAssistant }) => {
const { assistant } = useAssistant(activeAssistant.id)
const { model, setModel } = useAssistant(activeAssistant.id)
const { providers } = useProviders()
const { t } = useTranslation()
const { showAssistants, toggleShowAssistants } = useShowAssistants()
const items: MenuProps['items'] = providers
.filter((p) => p.models.length > 0)
.map((p) => ({
key: p.id,
label: p.isSystem ? t(`provider.${p.id}`) : p.name,
type: 'group',
children: p.models.map((m) => ({
key: m?.id,
label: upperFirst(m?.name),
style: m?.id === model?.id ? { color: 'var(--color-primary)' } : undefined,
icon: (
<Avatar src={getModelLogo(m?.id || '')} size={24}>
{first(m?.name)}
</Avatar>
),
onClick: () => m && setModel(m)
}))
}))
return (
<NavbarCenter style={{ paddingLeft: isMac ? 16 : 8 }}>
{!showAssistants && (
@ -53,27 +32,20 @@ const NavigationCenter: FC<Props> = ({ activeAssistant }) => {
</NewButton>
)}
<AssistantName>{removeLeadingEmoji(assistant?.name) || t('assistant.default.name')}</AssistantName>
<DropdownMenu
menu={{ items, style: { maxHeight: '80vh', overflow: 'auto' } }}
trigger={['click']}
overlayClassName="chat-nav-dropdown">
<SelectModelDropdown model={model} onSelect={setModel}>
<DropdownButton size="small" type="primary" ghost>
<CodeSandboxOutlined />
<ModelName>{model ? upperFirst(model.name) : t('button.select_model')}</ModelName>
</DropdownButton>
</DropdownMenu>
</SelectModelDropdown>
</NavbarCenter>
)
}
const DropdownMenu = styled(Dropdown)`
-webkit-app-region: none;
margin-left: 10px;
`
const AssistantName = styled.span`
font-weight: bold;
margin-left: 5px;
margin-right: 10px;
`
const DropdownButton = styled(Button)`

View File

@ -0,0 +1,55 @@
import { getModelLogo } from '@renderer/config/provider'
import { useProviders } from '@renderer/hooks/useProvider'
import { Model } from '@renderer/types'
import { Avatar, Dropdown, DropdownProps, MenuProps } from 'antd'
import { first, upperFirst } from 'lodash'
import { FC, PropsWithChildren } from 'react'
import { useTranslation } from 'react-i18next'
import styled from 'styled-components'
interface Props extends DropdownProps {
model: Model
onSelect: (model: Model) => void
}
const SelectModelDropdown: FC<Props & PropsWithChildren> = ({ children, model, onSelect, ...props }) => {
const { t } = useTranslation()
const { providers } = useProviders()
const items: MenuProps['items'] = providers
.filter((p) => p.models.length > 0)
.map((p) => ({
key: p.id,
label: p.isSystem ? t(`provider.${p.id}`) : p.name,
type: 'group',
children: p.models.map((m) => ({
key: m?.id,
label: upperFirst(m?.name),
style: m?.id === model?.id ? { color: 'var(--color-primary)' } : undefined,
icon: (
<Avatar src={getModelLogo(m?.id || '')} size={24}>
{first(m?.name)}
</Avatar>
),
onClick: () => m && onSelect(m)
}))
}))
return (
<DropdownMenu
menu={{ items, style: { maxHeight: '80vh', overflow: 'auto' } }}
trigger={['click']}
arrow
placement="bottomCenter"
overlayClassName="chat-nav-dropdown"
{...props}>
{children}
</DropdownMenu>
)
}
const DropdownMenu = styled(Dropdown)`
-webkit-app-region: none;
`
export default SelectModelDropdown