Merge remote-tracking branch 'origin/feature/1.5-cx' into feature/1.5-local-model

This commit is contained in:
Liujian
2025-02-16 21:38:41 +08:00
11 changed files with 158 additions and 146 deletions
@@ -25,10 +25,11 @@ interface AIProviderResponse {
interface AIProviderSelectProps {
value?: string
onChange?: (value: string, provider: AIProvider) => void
style?: React.CSSProperties
style?: React.CSSProperties,
source?: 'ai_api' | 'ai_keys'
}
const AIProviderSelect: React.FC<AIProviderSelectProps> = ({ value, onChange, style = { width: 200 } }) => {
const AIProviderSelect: React.FC<AIProviderSelectProps> = ({ value, onChange, source = 'ai', style = { width: 200 } }) => {
const { t } = useTranslation()
const [providers, setProviders] = useState<AIProvider[]>([])
const [loading, setLoading] = useState(false)
@@ -40,7 +41,7 @@ const AIProviderSelect: React.FC<AIProviderSelectProps> = ({ value, onChange, st
if (isMounted) setLoading(true)
try {
const endpoint = 'simple/ai/providers/configured'
const response = await fetchData<AIProviderResponse>(endpoint, { method: 'GET', eoParams: { all: true} })
const response = await fetchData<AIProviderResponse>(endpoint, { method: 'GET', ...(source === 'ai_api' ? { eoParams: { all: true } } : {}) })
const { code, data, msg } = response
if (code === STATUS_CODE.SUCCESS) {
const providers = data.providers.map((val) => ({
+3
View File
@@ -752,6 +752,9 @@ p{
.custom-steps .ant-steps-item-content {
margin-top: 0 !important;
}
.custom-steps .ant-steps-item-content .ant-steps-item-description {
width: 138px !important;
}
.ant-modal-body .pr-PAGE_INSIDE_X{
@@ -255,6 +255,7 @@ const ApiSettings: React.FC = () => {
<div className="flex gap-2 items-center">
<AIProviderSelect
value={selectedProvider}
source="ai_api"
onChange={(value, option) => {
setSelectedProvider(value)
setProvider(option)
@@ -151,7 +151,7 @@ const AiServiceInsideRouterCreate = () => {
type: aiModel?.type
}) as AiProviderDefaultConfig & { config: string }
)
aiModel?.type !== 'local' && getDefaultModelConfig(aiModel?.provider)
aiModel?.type !== 'local' && getDefaultModelConfig(aiModel?.provider, false)
} else {
message.error(msg || $t(RESPONSE_TIPS.error))
}
@@ -160,7 +160,7 @@ const AiServiceInsideRouterCreate = () => {
.finally(() => setLoading(false))
}
const getDefaultModelConfig = (provider?: string) => {
const getDefaultModelConfig = (provider?: string, resetDefaultLlm = true) => {
fetchData<BasicResponse<{ llms: AiProviderLlmsItems[]; provider: AiProviderDefaultConfig }>>('ai/provider/llms', {
method: 'GET',
eoParams: { provider: provider ?? aiServiceInfo?.provider?.id },
@@ -170,19 +170,21 @@ const AiServiceInsideRouterCreate = () => {
const { code, data, msg } = response
if (code === STATUS_CODE.SUCCESS) {
setLlmList(data.llms)
setDefaultLlm((prev) => {
const llmSetting = data.llms?.find(
(x: AiProviderLlmsItems) => x.id === (prev?.id ?? data.provider.defaultLlm)
)
return {
...prev,
defaultLlm: data.provider.defaultLlm,
provider: data.provider.id,
name: data.provider.name,
config: llmSetting?.config || '',
...(llmSetting ?? {})
} as AiProviderDefaultConfig & { config: string }
})
if (resetDefaultLlm) {
setDefaultLlm((prev) => {
const llmSetting = data.llms?.find(
(x: AiProviderLlmsItems) => x.id === (prev?.id ?? data.provider.defaultLlm)
)
return {
...prev,
defaultLlm: data.provider.defaultLlm,
provider: data.provider.id,
name: data.provider.name,
config: llmSetting?.config || '',
...(llmSetting ?? {})
} as AiProviderDefaultConfig & { config: string }
})
}
} else {
message.error(msg || $t(RESPONSE_TIPS.error))
}
@@ -97,7 +97,7 @@ const AiServiceRouterModelConfig = forwardRef<AiServiceRouterModelConfigHandle,
setModelType(entity.type as 'online' | 'local')
if (entity.type === 'online') {
getProviderList()
getLlmList(entity.provider)
getLlmList(entity.provider, false)
} else {
getLocalLlmList()
}
@@ -129,7 +129,7 @@ const AiServiceRouterModelConfig = forwardRef<AiServiceRouterModelConfigHandle,
})
}
const getLlmList = (provider: string) => {
const getLlmList = (provider: string, setDefaultValue = true) => {
fetchData<BasicResponse<{ llms: AiProviderLlmsItems[]; provider: AiProviderDefaultConfig }>>('ai/provider/llms', {
method: 'GET',
eoParams: { provider },
@@ -139,10 +139,12 @@ const AiServiceRouterModelConfig = forwardRef<AiServiceRouterModelConfigHandle,
const { code, data, msg } = response
if (code === STATUS_CODE.SUCCESS) {
setLlmList(data.llms)
form.setFieldsValue({
id: data.provider.defaultLlm,
config: data.llms.find((x) => x.id === data.provider.defaultLlm)?.config
})
if (setDefaultValue && data.llms.length) {
form.setFieldsValue({
id: data.provider.defaultLlm,
config: data.llms.find((x) => x.id === data.provider.defaultLlm)?.config
})
}
} else {
message.error(msg || $t(RESPONSE_TIPS.error))
}
@@ -114,7 +114,7 @@ const LocalModelList: React.FC = () => {
normal: { text: '正常' },
deploying: { text: '部署中', className: 'text-[#2196f3] cursor-pointer' },
error: { text: '模型异常', className: 'text-[#ff4d4f]' },
disabled: { text: '停用', className: 'text-[#999]' },
disabled: { text: '停用' },
deploying_error: { text: '部署失败', className: 'text-[#ff4d4f] cursor-pointer' }
})
@@ -318,7 +318,7 @@ const LocalModelList: React.FC = () => {
render: (dom: React.ReactNode, record: ModelListData) => (
<span className="[&>.key-link]:text-[#2196f3] cursor-pointer">
<a
href={`/aiApis?modelId=${record?.id}`}
href={`/aiApis?modelId=${record?.provider}`}
target="_blank"
className="key-link"
style={{
@@ -156,6 +156,7 @@ const OnlineModelList: React.FC = () => {
},
{
title: $t('默认模型'),
ellipsis: true,
dataIndex: 'defaultLlm'
},
{
@@ -13,6 +13,7 @@ export interface ModelListData {
name: string
logo: string
defaultLlm: string | undefined
provider?: string
modelMode?: string
status: ModelStatus
state?: ModelDeployStatus
@@ -57,9 +57,12 @@ const LocalAiDeploy = forwardRef<LocalAiDeployHandle, any>((props: any, ref: any
* @returns
*/
const deployPopularModel = async (id: string) => {
await deployLocalModel({
const response = await deployLocalModel({
modelID: id
})
if (response.code !== STATUS_CODE.SUCCESS) {
return
}
onClose?.()
}
@@ -84,10 +87,13 @@ const LocalAiDeploy = forwardRef<LocalAiDeployHandle, any>((props: any, ref: any
form
.validateFields()
.then(async (value) => {
await deployLocalModel({
const response = await deployLocalModel({
modelID: value.model,
team: value.team
})
if (response.code !== STATUS_CODE.SUCCESS) {
return
}
resolve(true)
})
.catch((errorInfo) => reject(errorInfo))
@@ -23,6 +23,7 @@ const useDeployLocalModel = () => {
const { code, msg } = response
if (code === STATUS_CODE.SUCCESS) {
message.success(msg || $t(RESPONSE_TIPS.success))
return response
} else {
message.error(msg || $t(RESPONSE_TIPS.error))
}
@@ -14,7 +14,6 @@ import AddLoadBalancingModel from './AddModel'
const LoadBalancingPage = () => {
const pageListRef = useRef<ActionType>(null)
const [searchWord, setSearchWord] = useState<string>('')
const [columns, setColumns] = useState<PageProColumns<LoadBalancingItems>[]>([])
const { modal, message } = App.useApp()
const [apiKeys, setApiKeys] = useState<LoadBalancingItems[]>([])
const addModelRef = useRef<LoadBalancingHandle>()
@@ -150,125 +149,120 @@ const LoadBalancingPage = () => {
})
}
/**
/**
*
*/
const setTableColumns = () => {
setColumns([
{
title: '',
dataIndex: 'drag',
width: '40px'
},
{
title: $t('优先级'),
dataIndex: 'priority',
width: 80,
ellipsis: true,
key: 'priority'
},
{
title: $t('模型'),
dataIndex: ['provider', 'name'],
ellipsis: true,
width: 100,
key: 'provider',
render: (dom: React.ReactNode, record: LoadBalancingItems) => (
<span>
{record.provider?.name} / {record.model?.name}
</span>
)
},
{
title: $t('类型'),
dataIndex: 'type',
width: 100,
ellipsis: true,
key: 'type',
render: (dom: React.ReactNode, record: LoadBalancingItems) => (
<span>{record.type === 'online' ? $t('线上模型') : $t('本地模型')}</span>
)
},
{
title: $t('状态'),
dataIndex: 'state',
width: 120,
ellipsis: true,
key: 'state',
render: (dom: React.ReactNode, record: LoadBalancingItems) => (
<span>{statusEnum[record.state]?.text || '-'}</span>
)
},
{
title: $t('Apis'),
dataIndex: 'apiCount',
ellipsis: true,
width: 80,
key: 'apiCount',
render: (dom: React.ReactNode, record: LoadBalancingItems) => (
<span className="[&>.key-link]:text-[#2196f3] cursor-pointer">
<a
href={`/aiApis?modelId=${record.model?.id}`}
target="_blank"
className="key-link"
style={{
fontWeight: 500,
cursor: 'pointer',
pointerEvents: 'all',
textDecoration: 'none'
}}
>
{record.apiCount || '0'}
</a>
</span>
)
},
{
title: $t('Keys'),
dataIndex: 'keyCount',
ellipsis: true,
width: 80,
key: 'keyCount',
render: (dom: React.ReactNode, record: LoadBalancingItems) => (
<span className="[&>.key-link]:text-[#2196f3] cursor-pointer">
<a
href={`/keysetting?modelId=${record.model?.id}`}
target="_blank"
className="key-link"
style={{
fontWeight: 500,
cursor: 'pointer',
pointerEvents: 'all',
textDecoration: 'none'
}}
>
{record.keyCount || '0'}
</a>
</span>
)
},
{
title: '',
key: 'option',
btnNums: 1,
width: 80,
fixed: 'right',
valueType: 'option',
render: (_: React.ReactNode, entity: any) => [
<TableBtnWithPermission
access="system.settings.ai_balance.delete"
key="delete"
btnType="delete"
onClick={() => handleDelete(entity.id as string)}
btnTitle={$t('删除')}
/>
]
}
])
}
useEffect(() => {
setTableColumns()
}, [])
const columns: PageProColumns<LoadBalancingItems>[] = [
{
title: '',
dataIndex: 'drag',
width: '40px'
},
{
title: $t('优先级'),
dataIndex: 'priority',
width: 80,
ellipsis: true,
key: 'priority'
},
{
title: $t('模型'),
dataIndex: ['provider', 'name'],
ellipsis: true,
key: 'provider',
render: (dom: React.ReactNode, record: LoadBalancingItems) => (
<span>
{record.provider?.name} / {record.model?.name}
</span>
)
},
{
title: $t('类型'),
dataIndex: 'type',
width: 120,
ellipsis: true,
key: 'type',
render: (dom: React.ReactNode, record: LoadBalancingItems) => (
<span>{record.type === 'online' ? $t('线上模型') : $t('本地模型')}</span>
)
},
{
title: $t('状态'),
dataIndex: 'state',
width: 80,
ellipsis: true,
key: 'state',
render: (dom: React.ReactNode, record: LoadBalancingItems) => (
<span>{statusEnum[record.state]?.text || '-'}</span>
)
},
{
title: $t('Apis'),
dataIndex: 'apiCount',
ellipsis: true,
width: 80,
key: 'apiCount',
render: (dom: React.ReactNode, record: LoadBalancingItems) => (
<span className="[&>.key-link]:text-[#2196f3] cursor-pointer">
<a
href={`/aiApis?modelId=${record.provider?.id}`}
target="_blank"
className="key-link"
style={{
fontWeight: 500,
cursor: 'pointer',
pointerEvents: 'all',
textDecoration: 'none'
}}
>
{record.apiCount || '0'}
</a>
</span>
)
},
{
title: $t('Keys'),
dataIndex: 'keyCount',
ellipsis: true,
width: 80,
key: 'keyCount',
render: (dom: React.ReactNode, record: LoadBalancingItems) => (
<span className="[&>.key-link]:text-[#2196f3] cursor-pointer">
<a
href={`/keysetting?modelId=${record.provider?.id}`}
target="_blank"
className="key-link"
style={{
fontWeight: 500,
cursor: 'pointer',
pointerEvents: 'all',
textDecoration: 'none'
}}
>
{record.keyCount || '0'}
</a>
</span>
)
},
{
title: '',
key: 'option',
btnNums: 1,
width: 50,
fixed: 'right',
valueType: 'option',
render: (_: React.ReactNode, entity: any) => [
<TableBtnWithPermission
access="system.settings.ai_balance.delete"
key="delete"
btnType="delete"
onClick={() => handleDelete(entity.id as string)}
btnTitle={$t('删除')}
/>
]
}
]
return (
<>