mirror of
https://github.com/APIParkLab/APIPark.git
synced 2026-06-04 10:13:53 +08:00
feat: delete model
This commit is contained in:
@@ -1,14 +1,13 @@
|
||||
import { QuestionCircleOutlined } from '@ant-design/icons'
|
||||
import { Codebox } from '@common/components/postcat/api/Codebox'
|
||||
import { BasicResponse, PLACEHOLDER, RESPONSE_TIPS, STATUS_CODE } from '@common/const/const'
|
||||
import { useFetch } from '@common/hooks/http'
|
||||
import { $t } from '@common/locales'
|
||||
import { App, Form, InputNumber, Select, Switch, Tag, Tooltip } from 'antd'
|
||||
import { App, Form, Select, Switch, Tag } from 'antd'
|
||||
import { forwardRef, useEffect, useImperativeHandle, useState } from 'react'
|
||||
import { AiProviderLlmsItems, ModelDetailData } from './types'
|
||||
|
||||
export type AiSettingModalContentProps = {
|
||||
entity: ModelDetailData & { defaultLlm: string }
|
||||
entity?: { id: string | undefined; defaultLlm: string | undefined }
|
||||
readOnly: boolean
|
||||
}
|
||||
|
||||
@@ -23,12 +22,36 @@ const AiSettingModalContent = forwardRef<AiSettingModalContentHandle, AiSettingM
|
||||
const { fetchData } = useFetch()
|
||||
const [llmList, setLlmList] = useState<AiProviderLlmsItems[]>()
|
||||
const [loading, setLoading] = useState<boolean>(false)
|
||||
const [enableState, setEnableState] = useState<boolean>(entity.status === 'enabled')
|
||||
const [enableState, setEnableState] = useState<boolean>(entity?.status === 'enabled' ?? true)
|
||||
const [providers, setProviders] = useState<Array<{ id: string; name: string }>>([])
|
||||
const [selectedProvider, setSelectedProvider] = useState<string>(entity?.id || '')
|
||||
|
||||
const getUnconfiguredProviders = () => {
|
||||
if (entity) return // Skip if editing existing provider
|
||||
|
||||
fetchData<BasicResponse<{ providers: Array<{ id: string; name: string }> }>>('ai/providers/unconfigured', {
|
||||
method: 'GET'
|
||||
}).then((response) => {
|
||||
const { code, data, msg } = response
|
||||
if (code === STATUS_CODE.SUCCESS) {
|
||||
setProviders(data.providers)
|
||||
if (data.providers.length > 0) {
|
||||
setSelectedProvider(data.providers[0].id)
|
||||
form.setFieldValue('provider', data.providers[0].id)
|
||||
}
|
||||
} else {
|
||||
message.error(msg || $t(RESPONSE_TIPS.error))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const getLlmList = () => {
|
||||
if (!selectedProvider) return
|
||||
|
||||
setLoading(true)
|
||||
fetchData<BasicResponse<{ llms: AiProviderLlmsItems[] }>>(`ai/provider/llms`, {
|
||||
method: 'GET',
|
||||
eoParams: { provider: entity.id }
|
||||
eoParams: { provider: selectedProvider }
|
||||
})
|
||||
.then((response) => {
|
||||
const { code, data, msg } = response
|
||||
@@ -42,56 +65,79 @@ const AiSettingModalContent = forwardRef<AiSettingModalContentHandle, AiSettingM
|
||||
setLoading(false)
|
||||
})
|
||||
}
|
||||
const initData = async () => {
|
||||
if (entity?.id) {
|
||||
message.loading($t(RESPONSE_TIPS.loading))
|
||||
const { code, data, msg } = await fetchData<BasicResponse<{ provider: ModelDetailData }>>('ai/provider/config', {
|
||||
method: 'GET',
|
||||
eoParams: { provider: entity.id },
|
||||
eoTransformKeys: ['get_apikey_url', 'default_llm']
|
||||
})
|
||||
message.destroy()
|
||||
if (code !== STATUS_CODE.SUCCESS) {
|
||||
message.error(msg || $t(RESPONSE_TIPS.error))
|
||||
return
|
||||
}
|
||||
const provider = data.provider
|
||||
form.setFieldsValue({
|
||||
defaultLlm: provider.defaultLlm,
|
||||
config: provider.config ? JSON.stringify(JSON.parse(provider.config), null, 2) : '',
|
||||
enable: provider.status === 'enabled'
|
||||
})
|
||||
return
|
||||
}
|
||||
form.setFieldsValue({
|
||||
defaultLlm: entity?.defaultLlm,
|
||||
config: '',
|
||||
enable: true
|
||||
})
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
getUnconfiguredProviders()
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
getLlmList()
|
||||
try {
|
||||
form.setFieldsValue({
|
||||
defaultLlm: entity.defaultLlm,
|
||||
config: entity!.config ? JSON.stringify(JSON.parse(entity!.config), null, 2) : '',
|
||||
priority: entity.priority || 1,
|
||||
enable: entity.status === 'enabled'
|
||||
})
|
||||
} catch (e) {
|
||||
form.setFieldsValue({
|
||||
defaultLlm: entity.defaultLlm,
|
||||
config: '',
|
||||
priority: 1,
|
||||
enable: true
|
||||
})
|
||||
}
|
||||
}, [])
|
||||
}, [selectedProvider])
|
||||
|
||||
useEffect(() => {
|
||||
initData()
|
||||
}, [entity])
|
||||
|
||||
const save: () => Promise<boolean | string> = () => {
|
||||
return new Promise((resolve, reject) => {
|
||||
form
|
||||
.validateFields()
|
||||
.then((value) => {
|
||||
const finalValue = {
|
||||
...value,
|
||||
priority: Math.max(1, value.priority)
|
||||
}
|
||||
return new Promise(async (resolve, reject) => {
|
||||
try {
|
||||
form
|
||||
.validateFields()
|
||||
.then((value) => {
|
||||
const finalValue = {
|
||||
...value,
|
||||
priority: Math.max(1, value.priority)
|
||||
}
|
||||
|
||||
fetchData<BasicResponse<null>>('ai/provider/config', {
|
||||
method: 'PUT',
|
||||
eoParams: { provider: entity?.id },
|
||||
eoBody: finalValue,
|
||||
eoTransformKeys: ['defaultLlm']
|
||||
// eoApiPrefix: 'http://uat.apikit.com:11204/mockApi/aoplatform/api/v1/'
|
||||
})
|
||||
.then((response) => {
|
||||
const { code, msg } = response
|
||||
if (code === STATUS_CODE.SUCCESS) {
|
||||
message.success(msg || $t(RESPONSE_TIPS.success))
|
||||
resolve(true)
|
||||
} else {
|
||||
message.error(msg || $t(RESPONSE_TIPS.error))
|
||||
reject(msg || $t(RESPONSE_TIPS.error))
|
||||
}
|
||||
fetchData<BasicResponse<null>>('ai/provider/config', {
|
||||
method: entity ? 'PUT' : 'POST',
|
||||
eoParams: { provider: selectedProvider },
|
||||
eoBody: finalValue,
|
||||
eoTransformKeys: ['defaultLlm']
|
||||
})
|
||||
.catch((errorInfo) => reject(errorInfo))
|
||||
})
|
||||
.catch((errorInfo) => reject(errorInfo))
|
||||
.then((response) => {
|
||||
const { code, msg } = response
|
||||
if (code === STATUS_CODE.SUCCESS) {
|
||||
message.success(msg || $t(RESPONSE_TIPS.success))
|
||||
resolve(true)
|
||||
} else {
|
||||
message.error(msg || $t(RESPONSE_TIPS.error))
|
||||
reject(msg || $t(RESPONSE_TIPS.error))
|
||||
}
|
||||
})
|
||||
.catch((errorInfo) => reject(errorInfo))
|
||||
})
|
||||
.catch((errorInfo) => reject(errorInfo))
|
||||
} catch (error) {
|
||||
reject(error)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -117,6 +163,15 @@ const AiSettingModalContent = forwardRef<AiSettingModalContentHandle, AiSettingM
|
||||
autoComplete="off"
|
||||
disabled={readOnly}
|
||||
>
|
||||
{!entity && (
|
||||
<Form.Item label={$t('供应商')} name="provider" rules={[{ required: true, message: $t('请选择供应商') }]}>
|
||||
<Select
|
||||
placeholder={$t('请选择供应商')}
|
||||
onChange={(value) => setSelectedProvider(value)}
|
||||
options={providers.map((p) => ({ label: p.name, value: p.id }))}
|
||||
/>
|
||||
</Form.Item>
|
||||
)}
|
||||
<Form.Item<ModelDetailData> label={$t('默认模型')} name="defaultLlm" rules={[{ required: true }]}>
|
||||
<Select
|
||||
className="w-INPUT_NORMAL"
|
||||
@@ -134,34 +189,6 @@ const AiSettingModalContent = forwardRef<AiSettingModalContentHandle, AiSettingM
|
||||
></Select>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item<ModelDetailData>
|
||||
label={
|
||||
<span className="flex items-center">
|
||||
{$t('负载优先级')}
|
||||
<Tooltip
|
||||
title={$t('负载优先级决定在原供应商异常或停用后,优先使用哪一个供应商。优先级数字越小,优先级越高。')}
|
||||
>
|
||||
<QuestionCircleOutlined className="ml-1 text-gray-500" />
|
||||
</Tooltip>
|
||||
</span>
|
||||
}
|
||||
name="priority"
|
||||
rules={[
|
||||
{ required: true },
|
||||
{
|
||||
validator: async (_, value) => {
|
||||
if (value <= 0) {
|
||||
throw new Error($t('优先级必须大于 0'))
|
||||
}
|
||||
return Promise.resolve()
|
||||
}
|
||||
}
|
||||
]}
|
||||
initialValue={1}
|
||||
>
|
||||
<InputNumber className="w-INPUT_NORMAL" min={1} placeholder={$t('请输入优先级')} />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item<ModelDetailData> label={$t('API Key(默认 Key)')} name="config">
|
||||
<Codebox
|
||||
editorTheme="vs-dark"
|
||||
@@ -172,8 +199,7 @@ const AiSettingModalContent = forwardRef<AiSettingModalContentHandle, AiSettingM
|
||||
enableToolbar={false}
|
||||
/>
|
||||
</Form.Item>
|
||||
|
||||
{entity.configured && (
|
||||
{entity?.id && (
|
||||
<Form.Item className="p-4 bg-white rounded-lg" label={$t('LLM 状态管理')}>
|
||||
<div className="flex justify-between items-center">
|
||||
<div>
|
||||
|
||||
@@ -27,11 +27,10 @@ const OnlineModelList: React.FC = () => {
|
||||
|
||||
const handleDelete = async (id: string) => {
|
||||
try {
|
||||
const response = await fetchData<BasicResponse<any>>('ai/resource/key', {
|
||||
const response = await fetchData<BasicResponse<any>>('ai/provider', {
|
||||
method: 'DELETE',
|
||||
eoParams: {
|
||||
id: id,
|
||||
branchID: 0
|
||||
provider: id
|
||||
}
|
||||
// eoApiPrefix: 'http://uat.apikit.com:11204/mockApi/aoplatform/api/v1/'
|
||||
})
|
||||
@@ -61,7 +60,6 @@ const OnlineModelList: React.FC = () => {
|
||||
})
|
||||
|
||||
if (response.code === STATUS_CODE.SUCCESS) {
|
||||
console.log(response)
|
||||
setTotal(response.data.total)
|
||||
return {
|
||||
data: response.data.providers,
|
||||
@@ -128,8 +126,8 @@ const OnlineModelList: React.FC = () => {
|
||||
dataIndex: 'status',
|
||||
ellipsis: true,
|
||||
valueType: 'select',
|
||||
filters: true,
|
||||
onFilter: true,
|
||||
// filters: true,
|
||||
// onFilter: true,
|
||||
valueEnum: statusEnum,
|
||||
render: (dom: React.ReactNode, entity: ModelListData) => statusEnum[entity.status]?.text || entity.status
|
||||
},
|
||||
@@ -160,7 +158,6 @@ const OnlineModelList: React.FC = () => {
|
||||
showPagination={true}
|
||||
searchPlaceholder={$t('请输入名称搜索')}
|
||||
columns={columns}
|
||||
dragSortKey="drag"
|
||||
addNewBtnTitle={$t('添加模型')}
|
||||
onAddNewBtnClick={handleAdd}
|
||||
/>
|
||||
|
||||
@@ -1,13 +1,11 @@
|
||||
import Icon from '@ant-design/icons'
|
||||
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 { App } from 'antd'
|
||||
import { createContext, useContext, useRef } from 'react'
|
||||
import AiSettingModalContent, { AiSettingModalContentHandle } from '../AiSettingModal'
|
||||
import { AiSettingListItem, ModelDetailData } from '../types'
|
||||
import { AiSettingListItem } from '../types'
|
||||
|
||||
interface AiSettingContextType {
|
||||
openConfigModal: (entity?: AiSettingListItem) => Promise<void>
|
||||
@@ -16,30 +14,17 @@ interface AiSettingContextType {
|
||||
const AiSettingContext = createContext<AiSettingContextType | undefined>(undefined)
|
||||
|
||||
export const AiSettingProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => {
|
||||
const { modal, message } = App.useApp()
|
||||
const { fetchData } = useFetch()
|
||||
const { modal } = App.useApp()
|
||||
const { aiConfigFlushed, setAiConfigFlushed, accessData } = useGlobalContext()
|
||||
const modalRef = useRef<AiSettingModalContentHandle>()
|
||||
|
||||
const openConfigModal = async (entity: AiSettingListItem) => {
|
||||
message.loading($t(RESPONSE_TIPS.loading))
|
||||
const { code, data, msg } = await fetchData<BasicResponse<{ provider: ModelDetailData }>>('ai/provider/config', {
|
||||
method: 'GET',
|
||||
eoParams: { provider: entity!.id },
|
||||
eoTransformKeys: ['get_apikey_url']
|
||||
})
|
||||
message.destroy()
|
||||
if (code !== STATUS_CODE.SUCCESS) {
|
||||
message.error(msg || $t(RESPONSE_TIPS.error))
|
||||
return
|
||||
}
|
||||
|
||||
const openConfigModal = async (entity?: AiSettingListItem) => {
|
||||
modal.confirm({
|
||||
title: $t('模型配置'),
|
||||
content: (
|
||||
<AiSettingModalContent
|
||||
ref={modalRef}
|
||||
entity={{ ...data.provider, defaultLlm: entity.defaultLlm }}
|
||||
entity={{ id: entity?.id, defaultLlm: entity?.defaultLlm }}
|
||||
readOnly={!checkAccess('system.devops.ai_provider.edit', accessData)}
|
||||
/>
|
||||
),
|
||||
@@ -58,10 +43,10 @@ export const AiSettingProvider: React.FC<{ children: React.ReactNode }> = ({ chi
|
||||
<a
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
href={data.provider.getApikeyUrl}
|
||||
// href={data.provider.getApikeyUrl}
|
||||
className="flex items-center gap-[8px]"
|
||||
>
|
||||
<span>{$t('从 (0) 获取 API KEY', [data.provider.name])}</span>
|
||||
{/* <span>{$t('从 (0) 获取 API KEY', [data.provider.name])}</span> */}
|
||||
<Icon icon="ic:baseline-open-in-new" width={16} height={16} />
|
||||
</a>
|
||||
<div>
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
export type ModelStatus = 'enabled' | 'abnormal'|'disabled'
|
||||
export type KeyStatus ='normal' | 'abnormal'|'disabled'
|
||||
export type ModelStatus = 'enabled' | 'abnormal' | 'disabled'
|
||||
export type KeyStatus = 'normal' | 'abnormal' | 'disabled'
|
||||
|
||||
export interface KeyData {
|
||||
id: string
|
||||
name: string
|
||||
status: KeyStatus,
|
||||
status: KeyStatus
|
||||
}
|
||||
|
||||
export interface ModelListData {
|
||||
@@ -17,16 +17,14 @@ export interface ModelListData {
|
||||
key_count: number
|
||||
keys: KeyData[]
|
||||
}
|
||||
export interface ModelDetailData extends ModelListData{
|
||||
enable:boolean
|
||||
config: string,
|
||||
priority?: number
|
||||
export interface ModelDetailData extends ModelListData {
|
||||
enable: boolean
|
||||
config: string
|
||||
getApikeyUrl: string
|
||||
status: ModelStatus
|
||||
configured: boolean
|
||||
}
|
||||
|
||||
|
||||
export type AiSettingListItem = {
|
||||
name: string
|
||||
id: string
|
||||
@@ -53,5 +51,3 @@ export type AiProviderDefaultConfig = {
|
||||
defaultLlm: string
|
||||
scopes: string[]
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user