diff --git a/frontend/packages/common/src/hooks/http.ts b/frontend/packages/common/src/hooks/http.ts index 92eb99a0..06a23f64 100644 --- a/frontend/packages/common/src/hooks/http.ts +++ b/frontend/packages/common/src/hooks/http.ts @@ -136,6 +136,7 @@ type EoRequest = RequestInit & { eoBody?: { [k: string]: unknown } | Array | string isStream?: boolean handleStream?: (line: any) => void + callback?: (cancel: () => void) => void } type EoHeaders = Headers | { [k: string]: string } @@ -145,6 +146,14 @@ export function useFetch() { const pluginEventHub = usePluginEventHub() function fetchData(url: string, options: EoRequest) { + const controller = new AbortController() + const signal = controller.signal + + // 如果提供了callback,则传递取消请求的函数 + if (options.callback) { + options.callback(() => controller.abort()) + } + // 合并传入的headers与默认headers const headers = { ...(options.body ? {} : DEFAULT_HEADERS), ...options.headers } @@ -163,7 +172,8 @@ export function useFetch() { headers: { ...headers // Authorization: 'Bearer your-token', // 示例:添加统一的Token认证 - } + }, + signal // 将signal传递给fetch请求 } return fetch(`${options?.eoApiPrefix === undefined ? '/api/v1/' : options.eoApiPrefix}${url}`, finalOptions) diff --git a/frontend/packages/core/src/const/ai-service/type.ts b/frontend/packages/core/src/const/ai-service/type.ts index fc788ae5..27062c72 100644 --- a/frontend/packages/core/src/const/ai-service/type.ts +++ b/frontend/packages/core/src/const/ai-service/type.ts @@ -19,6 +19,7 @@ export type AiServiceConfigFieldType = { serviceType?:'public'|'inner'; catalogue?:string | string[]; approvalType?:string; + providerType?:string }; export type AiServiceSubServiceTableListItem = { diff --git a/frontend/packages/core/src/pages/aiService/AiServiceInsidePage.tsx b/frontend/packages/core/src/pages/aiService/AiServiceInsidePage.tsx index 16178602..c438539f 100644 --- a/frontend/packages/core/src/pages/aiService/AiServiceInsidePage.tsx +++ b/frontend/packages/core/src/pages/aiService/AiServiceInsidePage.tsx @@ -31,7 +31,8 @@ const AiServiceInsidePage: FC = () => { const getAiServiceInfo = () => { fetchData>('service/info', { method: 'GET', - eoParams: { team: teamId, service: serviceId } + eoParams: { team: teamId, service: serviceId }, + eoTransformKeys: ['provider_type'] }).then((response) => { const { code, data, msg } = response if (code === STATUS_CODE.SUCCESS) { diff --git a/frontend/packages/core/src/pages/aiService/api/AiServiceInsideRouterCreate.tsx b/frontend/packages/core/src/pages/aiService/api/AiServiceInsideRouterCreate.tsx index f3e0de23..7a063f9b 100644 --- a/frontend/packages/core/src/pages/aiService/api/AiServiceInsideRouterCreate.tsx +++ b/frontend/packages/core/src/pages/aiService/api/AiServiceInsideRouterCreate.tsx @@ -15,12 +15,12 @@ import { AI_SERVICE_VARIABLES_TABLE_COLUMNS } from '@core/const/ai-service/const import { VariableItems } from '@core/const/ai-service/type.ts' import { API_PATH_MATCH_RULES } from '@core/const/system/const' import { useAiServiceContext } from '@core/contexts/AiServiceContext.tsx' -import { AiProviderDefaultConfig, AiProviderLlmsItems } from '@core/pages/aiSetting/AiSettingList' import { Icon } from '@iconify/react/dist/iconify.js' import { App, Button, Form, Input, InputNumber, Row, Space, Spin, Switch, Tag } from 'antd' import { MutableRefObject, useEffect, useMemo, useRef, useState } from 'react' import { useNavigate, useParams } from 'react-router-dom' import AiServiceRouterModelConfig, { AiServiceRouterModelConfigHandle } from './AiServiceInsideRouterModelConfig' +import { AiProviderDefaultConfig, AiProviderLlmsItems } from '@core/pages/aiSetting/types' type AiServiceRouterField = { name: string @@ -151,7 +151,13 @@ const AiServiceInsideRouterCreate = () => { type: aiModel?.type }) as AiProviderDefaultConfig & { config: string } ) - aiModel?.type !== 'local' && getDefaultModelConfig(aiModel?.provider, false) + getDefaultModelConfig({ + provider: aiModel?.provider, + id: aiModel?.id, + replaceDefaultLlm: false, + setIcon: true, + type: aiModel?.type + }) } else { message.error(msg || $t(RESPONSE_TIPS.error)) } @@ -160,36 +166,109 @@ const AiServiceInsideRouterCreate = () => { .finally(() => setLoading(false)) } - const getDefaultModelConfig = (provider?: string, resetDefaultLlm = true) => { - fetchData>('ai/provider/llms', { - method: 'GET', - eoParams: { provider: provider ?? aiServiceInfo?.provider?.id }, - eoTransformKeys: ['default_llm'] - }) - .then((response) => { - const { code, data, msg } = response - if (code === STATUS_CODE.SUCCESS) { - setLlmList(data.llms) - 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)) - } + const getDefaultModelConfig = ({ + provider, + id, + replaceDefaultLlm = true, + setIcon = true, + type + }: { + provider?: string + id?: string + replaceDefaultLlm?: boolean + setIcon?: boolean + type?: string + } = {}) => { + // 如果编辑状态下 是本地 或者,新增状态下是本地 + if (type === 'local' || (!type && aiServiceInfo?.providerType === 'local')) { + fetchData>('simple/ai/models/local/configured', { + method: 'GET', + eoTransformKeys: ['default_config'] }) - .catch((errorInfo) => console.error(errorInfo)) + .then((response) => { + const { code, data, msg } = response + if (code === STATUS_CODE.SUCCESS) { + setLlmList(data.models) + const localId = id || aiServiceInfo?.id + + if (replaceDefaultLlm) { + setDefaultLlm((prev) => { + const llmSetting = data.models?.find( + (x: AiProviderLlmsItems) => x.id === (prev?.id ?? localId) + ) + return { + ...prev, + defaultLlm: localId, + provider: localId, + name: aiServiceInfo?.name, + config: llmSetting?.defaultConfig || '', + type: 'local', + ...(llmSetting ?? {}) + } as AiProviderDefaultConfig & { config: string } + }) + } + if (setIcon) { + setDefaultLlm((prev) => { + const llmSetting = data.models?.find( + (x: AiProviderLlmsItems) => x.id === (prev?.id ?? localId) + ) + return { + ...prev, + logo: llmSetting?.logo, + scopes: llmSetting?.scopes + } as AiProviderDefaultConfig & { config: string } + }) + } + } else { + message.error(msg || $t(RESPONSE_TIPS.error)) + } + }) + .catch((errorInfo) => console.error(errorInfo)) + } else { + fetchData>('ai/provider/llms', { + method: 'GET', + eoParams: { provider: provider ?? aiServiceInfo?.provider?.id }, + eoTransformKeys: ['default_llm'] + }) + .then((response) => { + const { code, data, msg } = response + if (code === STATUS_CODE.SUCCESS) { + setLlmList(data.llms) + if (replaceDefaultLlm) { + 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 || '', + type: 'online', + ...(llmSetting ?? {}) + } as AiProviderDefaultConfig & { config: string } + }) + } + if (setIcon) { + setDefaultLlm((prev) => { + const llmSetting = data.llms?.find( + (x: AiProviderLlmsItems) => x.id === (prev?.id ?? data.provider.defaultLlm) + ) + return { + ...prev, + logo: llmSetting?.logo, + scopes: llmSetting?.scopes + } as AiProviderDefaultConfig & { config: string } + }) + } + } else { + message.error(msg || $t(RESPONSE_TIPS.error)) + } + }) + .catch((errorInfo) => console.error(errorInfo)) + } + } useEffect(() => { @@ -241,6 +320,13 @@ const AiServiceInsideRouterCreate = () => { const handlerSubmit: () => Promise | undefined = () => { return drawerAddFormRef.current?.save()?.then((res: { id: string; config: string, type: string, provider: string }) => { + getDefaultModelConfig({ + provider: res.provider, + id: res.id, + type: res.type, + replaceDefaultLlm: false, + setIcon: true + }) setDefaultLlm( (prev) => ({ diff --git a/frontend/packages/core/src/pages/aiSetting/LocalModelList.tsx b/frontend/packages/core/src/pages/aiSetting/LocalModelList.tsx index 1ad6c662..4262e24d 100644 --- a/frontend/packages/core/src/pages/aiSetting/LocalModelList.tsx +++ b/frontend/packages/core/src/pages/aiSetting/LocalModelList.tsx @@ -273,12 +273,23 @@ const LocalModelList: React.FC = () => { reload && pageListRef.current?.reload() modalInstance.destroy() } + const updateFooter = () => { + record.state = 'error' + modalInstance.update({}) + } + let cancelCb: () => void = () => {} + const cancel = (cancel: () => void) => { + cancelCb = cancel + } const modalInstance = modal.confirm({ title: $t('部署过程'), - content: , + content: , footer: () => { return }, + afterClose: () => { + cancelCb() + }, width: 600, okText: $t('确认'), cancelText: $t('取消'), diff --git a/frontend/packages/core/src/pages/system/SystemList.tsx b/frontend/packages/core/src/pages/system/SystemList.tsx index e11f10b9..4e0f5f12 100644 --- a/frontend/packages/core/src/pages/system/SystemList.tsx +++ b/frontend/packages/core/src/pages/system/SystemList.tsx @@ -141,12 +141,23 @@ const SystemList: FC = () => { modalInstance.destroy() reload && manualReloadTable() } + const updateFooter = () => { + record.state = 'error' + modalInstance.update({}) + } + let cancelCb: () => void = () => {} + const cancel = (cancel: () => void) => { + cancelCb = cancel + } const modalInstance = modal.confirm({ title: $t('部署过程'), - content: , + content: , footer: () => { return }, + afterClose: () => { + cancelCb() + }, width: 600, okText: $t('确认'), cancelText: $t('取消'), diff --git a/frontend/packages/core/src/pages/system/serviceDeployment/ServiceDeployment.tsx b/frontend/packages/core/src/pages/system/serviceDeployment/ServiceDeployment.tsx index ce1bd96c..6a2dd88f 100644 --- a/frontend/packages/core/src/pages/system/serviceDeployment/ServiceDeployment.tsx +++ b/frontend/packages/core/src/pages/system/serviceDeployment/ServiceDeployment.tsx @@ -8,8 +8,8 @@ import { $t } from '@common/locales/index.ts' import { useFetch } from '@common/hooks/http' import { BasicResponse, RESPONSE_TIPS, STATUS_CODE } from '@common/const/const' -export const ServiceDeployment = (props: { record: SystemTableListItem, closeModal?: () => void }) => { - const { record, closeModal } = props +export const ServiceDeployment = (props: { record: SystemTableListItem, closeModal?: () => void, updateFooter?: () => void, cancelCb?: (cancel: () => void) => void }) => { + const { record, closeModal, updateFooter, cancelCb } = props const { message } = App.useApp() const getIcon = (status: string) => { switch (status) { @@ -126,6 +126,9 @@ export const ServiceDeployment = (props: { record: SystemTableListItem, closeMod method: 'POST', eoBody: { model: record.id, team: record.team?.id }, isStream: true, + callback: (cancel: () => void) => { + cancelCb?.(cancel) + }, handleStream: (chunk) => { const parsedChunk = JSON.parse(chunk) // 下载中 @@ -144,6 +147,7 @@ export const ServiceDeployment = (props: { record: SystemTableListItem, closeMod closeModal?.() }, 200) } else if (parsedChunk?.data?.state.includes('error')) { + updateFooter?.() setStepItem((prevItems) => prevItems.map((item, index) => { return { ...item, status: index === step.current ? 'error' : item.status }