Files
cherry-studio/src/renderer/src/pages/home/Inputbar/MentionModelsButton.tsx
T
2025-04-13 21:03:19 +08:00

115 lines
3.8 KiB
TypeScript

import { PlusOutlined } from '@ant-design/icons'
import ModelTagsWithLabel from '@renderer/components/ModelTagsWithLabel'
import { useQuickPanel } from '@renderer/components/QuickPanel'
import { QuickPanelListItem } from '@renderer/components/QuickPanel/types'
import { getModelLogo, isEmbeddingModel, isRerankModel } from '@renderer/config/models'
import db from '@renderer/databases'
import { useProviders } from '@renderer/hooks/useProvider'
import { getModelUniqId } from '@renderer/services/ModelService'
import { Model } from '@renderer/types'
import { Avatar, Tooltip } from 'antd'
import { first, sortBy } from 'lodash'
import { AtSign } from 'lucide-react'
import { FC, useCallback, useEffect, useImperativeHandle, useMemo, useState } from 'react'
import { useTranslation } from 'react-i18next'
import { useNavigate } from 'react-router'
export interface MentionModelsButtonRef {
openQuickPanel: () => void
}
interface Props {
ref?: React.RefObject<MentionModelsButtonRef | null>
mentionModels: Model[]
onMentionModel: (model: Model) => void
ToolbarButton: any
}
const MentionModelsButton: FC<Props> = ({ ref, mentionModels, onMentionModel, ToolbarButton }) => {
const { providers } = useProviders()
const [pinnedModels, setPinnedModels] = useState<string[]>([])
const { t } = useTranslation()
const navigate = useNavigate()
const quickPanel = useQuickPanel()
const modelItems = useMemo(() => {
// Get all models from providers
const allModels = providers
.filter((p) => p.models && p.models.length > 0)
.flatMap((p) =>
p.models
.filter((m) => !isEmbeddingModel(m))
.filter((m) => !isRerankModel(m))
.map((m) => ({
model: m,
provider: p,
isPinned: pinnedModels.includes(getModelUniqId(m))
}))
)
// Sort by pinned status and name
const newList: QuickPanelListItem[] = sortBy(allModels, ['isPinned', 'model.name'])
.reverse()
.map((item) => ({
label: `${item.provider.isSystem ? t(`provider.${item.provider.id}`) : item.provider.name} | ${item.model.name}`,
description: <ModelTagsWithLabel model={item.model} showLabel={false} size={10} style={{ opacity: 0.8 }} />,
icon: (
<Avatar src={getModelLogo(item.model.id)} size={20}>
{first(item.model.name)}
</Avatar>
),
action: () => onMentionModel(item.model),
isSelected: mentionModels.some((selected) => getModelUniqId(selected) === getModelUniqId(item.model))
}))
newList.push({
label: t('settings.models.add.add_model') + '...',
icon: <PlusOutlined />,
action: () => navigate('/settings/provider'),
isSelected: false
})
return newList
}, [providers, t, pinnedModels, mentionModels, onMentionModel, navigate])
const openQuickPanel = useCallback(() => {
quickPanel.open({
title: t('agents.edit.model.select.title'),
list: modelItems,
symbol: '@',
multiple: true,
afterAction({ item }) {
item.isSelected = !item.isSelected
}
})
}, [modelItems, quickPanel, t])
const handleOpenQuickPanel = useCallback(() => {
if (quickPanel.isVisible && quickPanel.symbol === '@') {
quickPanel.close()
} else {
openQuickPanel()
}
}, [openQuickPanel, quickPanel])
useEffect(() => {
const loadPinnedModels = async () => {
const setting = await db.settings.get('pinned:models')
setPinnedModels(setting?.value || [])
}
loadPinnedModels()
}, [])
useImperativeHandle(ref, () => ({
openQuickPanel
}))
return (
<Tooltip placement="top" title={t('agents.edit.model.select.title')} arrow>
<ToolbarButton type="text" onClick={handleOpenQuickPanel}>
<AtSign size={18} />
</ToolbarButton>
</Tooltip>
)
}
export default MentionModelsButton