feat: ai model config

This commit is contained in:
scarqin
2024-12-26 16:05:56 +08:00
parent e8730d6d18
commit 3b45a339d3
2 changed files with 143 additions and 127 deletions
@@ -0,0 +1,139 @@
import Icon, { LoadingOutlined } from '@ant-design/icons'
import WithPermission from '@common/components/aoplatform/WithPermission'
import { BasicResponse, RESPONSE_TIPS, STATUS_CODE } from '@common/const/const'
import { useFetch } from '@common/hooks/http'
import { $t } from '@common/locales'
import { App, Button, Card, Divider, Empty, Spin, Tag } from 'antd'
import { FC, memo, useEffect, useState } from 'react'
import { AiSettingListItem } from './AiSettingList'
const CardBox = memo(
({ provider, openModal }: { provider: AiSettingListItem; openModal: (provider: AiSettingListItem) => void }) => {
return (
<Card
title={
<div className="flex w-full items-center justify-between gap-[4px]">
<div className="flex flex-1 overflow-hidden items-center gap-[4px]">
<span
className=" flex items-center h-[22px] ai-setting-svg-container"
dangerouslySetInnerHTML={{ __html: provider.logo }}
></span>
<span className="font-normal truncate">{provider.name}</span>
</div>
<Tag
bordered={false}
color={provider.configured ? 'green' : undefined}
className="h-[22px] px-[4px] text-center"
>
{provider.configured ? $t('已配置') : $t('未配置')}
</Tag>
</div>
}
className="shadow-[0_5px_10px_0_rgba(0,0,0,0.05)] rounded-[10px] overflow-visible h-[156px] m-0 flex flex-col "
classNames={{ header: 'border-b-[0px] p-[20px] px-[24px]', body: 'pt-0 flex-1' }}
>
<div className="flex flex-col justify-between h-full gap-btnbase">
<div className="flex items-center w-full h-[32px] flex-1">
{provider.configured && (
<>
<label className="text-nowrap">{$t('默认')}</label>
<span className="overflow-hidden flex-1 truncate">{provider.defaultLlm}</span>
</>
)}
</div>
<WithPermission access="system.settings.ai_provider.view">
<Button
block
icon={<Icon icon="ic:outline-settings" width={18} height={18} />}
onClick={() => openModal(provider)}
classNames={{ icon: 'h-[18px]' }}
>
{$t('设置')}
</Button>
</WithPermission>
</div>
</Card>
)
}
)
const ModelCardArea = ({ modelList, className }: { modelList: AiSettingListItem[]; className?: string }) => {
return (
<>
{modelList.length > 0 ? (
<div
className={className}
style={{
display: 'grid',
gridTemplateColumns: 'repeat(auto-fill, minmax(300px, 1fr))',
gap: '20px'
}}
>
{modelList.map((provider: AiSettingListItem) => (
<CardBox key={provider.id} provider={provider} />
))}
</div>
) : (
<Empty image={Empty.PRESENTED_IMAGE_SIMPLE} />
)}
</>
)
}
const AIUnconfigure: FC = () => {
const { message } = App.useApp()
const { fetchData } = useFetch()
const [aiSettingList, setAiSettingList] = useState<AiSettingListItem[]>([])
const [loading, setLoading] = useState<boolean>(false)
const getAiSettingList = () => {
setLoading(true)
return fetchData<BasicResponse<{ providers: Omit<AiSettingListItem, 'availableLlms' | 'llmListStatus'>[] }>>(
`ai/providers/unconfigured`,
{ method: 'GET', eoTransformKeys: ['default_llm', 'default_llm_logo'] }
)
.then((response) => {
const { code, data, msg } = response
if (code === STATUS_CODE.SUCCESS) {
setAiSettingList(
data.providers?.map((x: AiSettingListItem) => ({
...x,
name: $t(x.name),
llmListStatus: 'unload',
availableLlms: []
}))
)
} else {
message.error(msg || $t(RESPONSE_TIPS.error))
}
})
.finally(() => setLoading(false))
}
useEffect(() => {
getAiSettingList()
}, [])
return (
<Spin
className="h-full"
wrapperClassName="h-full pr-PAGE_INSIDE_X"
indicator={<LoadingOutlined style={{ fontSize: 24 }} spin />}
spinning={loading}
>
{aiSettingList && aiSettingList.length > 0 ? (
<div>
{aiSettingList.filter((item) => !item.configured).length > 0 && (
<>
<Divider style={{ margin: '20px 0 !important;' }} />
<p className="text-[14px] text-[#666] mb-[4px] mt-[20px] font-bold">{$t('未配置')}</p>
<ModelCardArea modelList={aiSettingList.filter((item) => !item.configured) || []} />
</>
)}
</div>
) : (
<Empty image={Empty.PRESENTED_IMAGE_SIMPLE} />
)}
</Spin>
)
}
export default AIUnconfigure
@@ -1,16 +1,15 @@
import { LoadingOutlined } from '@ant-design/icons'
import InsidePage from '@common/components/aoplatform/InsidePage'
import WithPermission from '@common/components/aoplatform/WithPermission'
import { BasicResponse, RESPONSE_TIPS, STATUS_CODE } from '@common/const/const'
import { useGlobalContext } from '@common/contexts/GlobalStateContext'
import { useFetch } from '@common/hooks/http'
import { $t } from '@common/locales'
import { checkAccess } from '@common/utils/permission'
import { Icon } from '@iconify/react/dist/iconify.js'
import { App, Button, Card, Divider, Empty, Spin, Tag } from 'antd'
import { memo, useEffect, useRef, useState } from 'react'
import { App } from 'antd'
import { useRef } from 'react'
import AIFlowChart from './AIFlowChart'
import AiSettingModalContent, { AiSettingModalContentHandle } from './AiSettingModal'
import AIUnconfigure from './AIUnconfigure'
export type AiSettingListItem = {
name: string
@@ -47,36 +46,9 @@ export type AiProviderConfig = {
const AiSettingList = () => {
const { modal, message } = App.useApp()
const { fetchData } = useFetch()
const [aiSettingList, setAiSettingList] = useState<AiSettingListItem[]>([])
const [loading, setLoading] = useState<boolean>(false)
const modalRef = useRef<AiSettingModalContentHandle>()
const { setAiConfigFlushed, accessData } = useGlobalContext()
const getAiSettingList = () => {
setLoading(true)
return fetchData<BasicResponse<{ providers: Omit<AiSettingListItem, 'availableLlms' | 'llmListStatus'>[] }>>(
`ai/providers/unconfigured`,
{ method: 'GET', eoTransformKeys: ['default_llm', 'default_llm_logo'] }
// eoApiPrefix: 'http://uat.apikit.com:11204/mockApi/aoplatform/api/v1/'
)
.then((response) => {
const { code, data, msg } = response
if (code === STATUS_CODE.SUCCESS) {
setAiSettingList(
data.providers?.map((x: AiSettingListItem) => ({
...x,
name: $t(x.name),
llmListStatus: 'unload',
availableLlms: []
}))
)
} else {
message.error(msg || $t(RESPONSE_TIPS.error))
}
})
.finally(() => setLoading(false))
}
const openModal = async (entity: AiSettingListItem) => {
message.loading($t(RESPONSE_TIPS.loading))
const { code, data, msg } = await fetchData<BasicResponse<{ provider: AiProviderConfig }>>('ai/provider/config', {
@@ -101,7 +73,6 @@ const AiSettingList = () => {
onOk: () => {
return modalRef.current?.save().then((res) => {
if (res === true) setAiConfigFlushed(true)
getAiSettingList()
})
},
width: 600,
@@ -131,81 +102,6 @@ const AiSettingList = () => {
})
}
useEffect(() => {
getAiSettingList()
}, [])
const CardBox = memo(({ provider }: { provider: AiSettingListItem }) => {
return (
<Card
title={
<div className="flex w-full items-center justify-between gap-[4px]">
<div className="flex flex-1 overflow-hidden items-center gap-[4px]">
<span
className=" flex items-center h-[22px] ai-setting-svg-container"
dangerouslySetInnerHTML={{ __html: provider.logo }}
></span>
<span className="font-normal truncate">{provider.name}</span>
</div>
<Tag
bordered={false}
color={provider.configured ? 'green' : undefined}
className="h-[22px] px-[4px] text-center"
>
{provider.configured ? $t('已配置') : $t('未配置')}
</Tag>
</div>
}
className="shadow-[0_5px_10px_0_rgba(0,0,0,0.05)] rounded-[10px] overflow-visible h-[156px] m-0 flex flex-col "
classNames={{ header: 'border-b-[0px] p-[20px] px-[24px]', body: 'pt-0 flex-1' }}
>
<div className="flex flex-col justify-between h-full gap-btnbase">
<div className="flex items-center w-full h-[32px] flex-1">
{provider.configured && (
<>
<label className="text-nowrap">{$t('默认')}</label>
<span className="overflow-hidden flex-1 truncate">{provider.defaultLlm}</span>
</>
)}
</div>
<WithPermission access="system.settings.ai_provider.view">
<Button
block
icon={<Icon icon="ic:outline-settings" width={18} height={18} />}
onClick={() => openModal(provider)}
classNames={{ icon: 'h-[18px]' }}
>
{$t('设置')}
</Button>
</WithPermission>
</div>
</Card>
)
})
const ModelCardArea = ({ modelList, className }: { modelList: AiSettingListItem[]; className?: string }) => {
return (
<>
{modelList.length > 0 ? (
<div
className={className}
style={{
display: 'grid',
gridTemplateColumns: 'repeat(auto-fill, minmax(300px, 1fr))',
gap: '20px'
}}
>
{modelList.map((provider: AiSettingListItem) => (
<CardBox key={provider.id} provider={provider} />
))}
</div>
) : (
<Empty image={Empty.PRESENTED_IMAGE_SIMPLE} />
)}
</>
)
}
return (
<>
<InsidePage
@@ -216,26 +112,7 @@ const AiSettingList = () => {
scrollPage={false}
>
<AIFlowChart />
<Spin
className="h-full"
wrapperClassName="h-full pr-PAGE_INSIDE_X"
indicator={<LoadingOutlined style={{ fontSize: 24 }} spin />}
spinning={loading}
>
{aiSettingList && aiSettingList.length > 0 ? (
<div>
{aiSettingList.filter((item) => !item.configured).length > 0 && (
<>
<Divider style={{ margin: '20px 0 !important;' }} />
<p className="text-[14px] text-[#666] mb-[4px] mt-[20px] font-bold">{$t('未配置')}</p>
<ModelCardArea modelList={aiSettingList.filter((item) => !item.configured) || []} />
</>
)}
</div>
) : (
<Empty image={Empty.PRESENTED_IMAGE_SIMPLE} />
)}
</Spin>
<AIUnconfigure openModal={openModal} />
</InsidePage>
</>
)