feat: add model fetch and search
This commit is contained in:
parent
fcd3036065
commit
6899775e4e
@ -1,13 +1,16 @@
|
|||||||
import { Avatar, Button, Empty, Modal } from 'antd'
|
import { LoadingOutlined, MinusOutlined, PlusOutlined } from '@ant-design/icons'
|
||||||
import { useState } from 'react'
|
|
||||||
import { TopView } from '../../../components/TopView'
|
|
||||||
import { Model, Provider } from '@renderer/types'
|
|
||||||
import { groupBy, isEmpty, uniqBy } 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'
|
import { SYSTEM_MODELS } from '@renderer/config/models'
|
||||||
|
import { useProvider } from '@renderer/hooks/useProvider'
|
||||||
|
import { fetchModels } from '@renderer/services/api'
|
||||||
import { getModelLogo } from '@renderer/services/provider'
|
import { getModelLogo } from '@renderer/services/provider'
|
||||||
|
import { Model, Provider } from '@renderer/types'
|
||||||
|
import { getDefaultGroupName, runAsyncFunction } from '@renderer/utils'
|
||||||
|
import { Avatar, Button, Empty, Flex, Modal } from 'antd'
|
||||||
|
import Search from 'antd/es/input/Search'
|
||||||
|
import { groupBy, isEmpty, uniqBy } from 'lodash'
|
||||||
|
import { useEffect, useState } from 'react'
|
||||||
|
import styled from 'styled-components'
|
||||||
|
import { TopView } from '../../../components/TopView'
|
||||||
|
|
||||||
interface ShowParams {
|
interface ShowParams {
|
||||||
provider: Provider
|
provider: Provider
|
||||||
@ -20,10 +23,18 @@ interface Props extends ShowParams {
|
|||||||
const PopupContainer: React.FC<Props> = ({ provider: _provider, resolve }) => {
|
const PopupContainer: React.FC<Props> = ({ provider: _provider, resolve }) => {
|
||||||
const [open, setOpen] = useState(true)
|
const [open, setOpen] = useState(true)
|
||||||
const { provider, models, addModel, removeModel } = useProvider(_provider.id)
|
const { provider, models, addModel, removeModel } = useProvider(_provider.id)
|
||||||
|
const [listModels, setListModels] = useState<Model[]>([])
|
||||||
|
const [loading, setLoading] = useState(false)
|
||||||
|
const [searchText, setSearchText] = useState('')
|
||||||
|
|
||||||
const systemModels = SYSTEM_MODELS[_provider.id] || []
|
const systemModels = SYSTEM_MODELS[_provider.id] || []
|
||||||
const allModels = uniqBy([...systemModels, ...models], 'id')
|
const allModels = uniqBy([...systemModels, ...listModels, ...models], 'id')
|
||||||
const systemModelGroups = groupBy(allModels, 'group')
|
|
||||||
|
const list = searchText
|
||||||
|
? allModels.filter((model) => model.id.toLocaleLowerCase().includes(searchText.toLocaleLowerCase()))
|
||||||
|
: allModels
|
||||||
|
|
||||||
|
const modelGroups = groupBy(list, 'group')
|
||||||
|
|
||||||
const onOk = () => {
|
const onOk = () => {
|
||||||
setOpen(false)
|
setOpen(false)
|
||||||
@ -45,9 +56,40 @@ const PopupContainer: React.FC<Props> = ({ provider: _provider, resolve }) => {
|
|||||||
removeModel(model)
|
removeModel(model)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
runAsyncFunction(async () => {
|
||||||
|
try {
|
||||||
|
setLoading(true)
|
||||||
|
const models = await fetchModels(_provider)
|
||||||
|
setListModels(
|
||||||
|
models.map((model) => ({
|
||||||
|
id: model.id,
|
||||||
|
// @ts-ignore name
|
||||||
|
name: model.name || model.id,
|
||||||
|
provider: _provider.id,
|
||||||
|
group: getDefaultGroupName(model.id)
|
||||||
|
}))
|
||||||
|
)
|
||||||
|
setLoading(false)
|
||||||
|
} catch (error) {
|
||||||
|
setLoading(false)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
const ModalHeader = () => {
|
||||||
|
return (
|
||||||
|
<Flex>
|
||||||
|
<ModelHeaderTitle>{provider.name} Models</ModelHeaderTitle>
|
||||||
|
{loading && <LoadingOutlined size={20} />}
|
||||||
|
</Flex>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Modal
|
<Modal
|
||||||
title={String(provider.name + ' Models').toUpperCase()}
|
title={<ModalHeader />}
|
||||||
open={open}
|
open={open}
|
||||||
onOk={onOk}
|
onOk={onOk}
|
||||||
onCancel={onCancel}
|
onCancel={onCancel}
|
||||||
@ -58,11 +100,14 @@ const PopupContainer: React.FC<Props> = ({ provider: _provider, resolve }) => {
|
|||||||
content: { padding: 0 },
|
content: { padding: 0 },
|
||||||
header: { padding: 22, paddingBottom: 15 }
|
header: { padding: 22, paddingBottom: 15 }
|
||||||
}}>
|
}}>
|
||||||
|
<SearchContainer>
|
||||||
|
<Search placeholder="Search model id or name" allowClear onSearch={setSearchText} />
|
||||||
|
</SearchContainer>
|
||||||
<ListContainer>
|
<ListContainer>
|
||||||
{Object.keys(systemModelGroups).map((group) => (
|
{Object.keys(modelGroups).map((group) => (
|
||||||
<div key={group}>
|
<div key={group}>
|
||||||
<ListHeader key={group}>{group}</ListHeader>
|
<ListHeader key={group}>{group}</ListHeader>
|
||||||
{systemModelGroups[group].map((model) => {
|
{modelGroups[group].map((model) => {
|
||||||
const hasModel = provider.models.find((m) => m.id === model.id)
|
const hasModel = provider.models.find((m) => m.id === model.id)
|
||||||
return (
|
return (
|
||||||
<ListItem key={model.id}>
|
<ListItem key={model.id}>
|
||||||
@ -82,12 +127,21 @@ const PopupContainer: React.FC<Props> = ({ provider: _provider, resolve }) => {
|
|||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
{isEmpty(allModels) && <Empty image={Empty.PRESENTED_IMAGE_SIMPLE} description="No models" />}
|
{isEmpty(list) && <Empty image={Empty.PRESENTED_IMAGE_SIMPLE} description="No models" />}
|
||||||
</ListContainer>
|
</ListContainer>
|
||||||
</Modal>
|
</Modal>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const SearchContainer = styled.div`
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: 0 22px;
|
||||||
|
padding-bottom: 20px;
|
||||||
|
`
|
||||||
|
|
||||||
const ListContainer = styled.div`
|
const ListContainer = styled.div`
|
||||||
max-height: 70vh;
|
max-height: 70vh;
|
||||||
overflow-y: scroll;
|
overflow-y: scroll;
|
||||||
@ -128,6 +182,13 @@ const ListItemName = styled.div`
|
|||||||
margin-left: 6px;
|
margin-left: 6px;
|
||||||
`
|
`
|
||||||
|
|
||||||
|
const ModelHeaderTitle = styled.div`
|
||||||
|
color: #fff;
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: 600;
|
||||||
|
margin-right: 10px;
|
||||||
|
`
|
||||||
|
|
||||||
export default class ModelListPopup {
|
export default class ModelListPopup {
|
||||||
static topviewId = 0
|
static topviewId = 0
|
||||||
static hide() {
|
static hide() {
|
||||||
|
|||||||
@ -100,3 +100,13 @@ export async function fetchMessagesSummary({ messages, assistant }: FetchMessage
|
|||||||
|
|
||||||
return response.choices[0].message?.content
|
return response.choices[0].message?.content
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function fetchModels(provider: Provider) {
|
||||||
|
try {
|
||||||
|
const openaiProvider = getOpenAiProvider(provider)
|
||||||
|
const response = await openaiProvider.models.list()
|
||||||
|
return response.data
|
||||||
|
} catch (error) {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user