From 7f3e43d929325f8dd5031a2dd5f8ea0555208695 Mon Sep 17 00:00:00 2001 From: ningyv <1793599591@qq.com> Date: Tue, 18 Feb 2025 16:00:12 +0800 Subject: [PATCH 1/9] fix: route handling does not work on page reload --- frontend/package.json | 2 +- .../core/src/pages/guide/LocalAiDeploy.tsx | 42 +++++++++---------- 2 files changed, 22 insertions(+), 22 deletions(-) diff --git a/frontend/package.json b/frontend/package.json index 7217f6e4..5998344c 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -43,7 +43,7 @@ "react-dom": "^18.2.0", "react-i18next": "^15.0.1", "react-joyride": "^2.8.2", - "react-router-dom": "^6.20.0", + "react-router-dom": "6.20.0", "swagger-ui-react": "^5.17.14", "tailwindcss": "^3.3.5", "uuid": "^9.0.1", diff --git a/frontend/packages/core/src/pages/guide/LocalAiDeploy.tsx b/frontend/packages/core/src/pages/guide/LocalAiDeploy.tsx index 64d3fc3c..1e5839ef 100644 --- a/frontend/packages/core/src/pages/guide/LocalAiDeploy.tsx +++ b/frontend/packages/core/src/pages/guide/LocalAiDeploy.tsx @@ -135,6 +135,27 @@ const LocalAiDeploy = forwardRef((props: any, ref: any getLocalModelList(value) }} > + + +
{$t('热点模型')} @@ -158,27 +179,6 @@ const LocalAiDeploy = forwardRef((props: any, ref: any : null}
- - - form.setFieldValue('address', e.target.value)} + /> + + + + ) +}) + +export default ConfigureOllamaService diff --git a/frontend/packages/core/src/pages/aiSetting/LocalModelList.tsx b/frontend/packages/core/src/pages/aiSetting/LocalModelList.tsx index 4262e24d..47d266a1 100644 --- a/frontend/packages/core/src/pages/aiSetting/LocalModelList.tsx +++ b/frontend/packages/core/src/pages/aiSetting/LocalModelList.tsx @@ -4,13 +4,15 @@ import TableBtnWithPermission from '@common/components/aoplatform/TableBtnWithPe import { BasicResponse, RESPONSE_TIPS, STATUS_CODE } from '@common/const/const' import { useFetch } from '@common/hooks/http' import { $t } from '@common/locales' -import { App, Divider, Form, Space, Switch, Tag } from 'antd' +import { App, Divider, Form, Space, Switch, Tag, Button } from 'antd' import React, { forwardRef, useEffect, useImperativeHandle, useRef, useState } from 'react' import { ModelListData } from './types' import LocalAiDeploy, { LocalAiDeployHandle } from '../guide/LocalAiDeploy' import { ServiceDeployment } from '../system/serviceDeployment/ServiceDeployment' import { LogsFooter } from '../system/serviceDeployment/ServiceDeployMentFooter' import WithPermission from '@common/components/aoplatform/WithPermission' +import { Icon } from '@iconify/react/dist/iconify.js' +import ConfigureOllamaService, { ConfigureOllamaServiceHandle } from './ConfigureOllamaService' type EditLocalModelModalHandle = { save: () => Promise } @@ -18,20 +20,21 @@ type EditLocalModelModalProps = { enable: boolean modelID?: string } -const EditLocalModelModal = forwardRef((props: EditLocalModelModalProps, ref) => { - const { enable, modelID } = props - const { fetchData } = useFetch() - const { message } = App.useApp() - const [form] = Form.useForm() - const [currentStatus, setCurrentStatus] = useState(enable) +const EditLocalModelModal = forwardRef( + (props: EditLocalModelModalProps, ref) => { + const { enable, modelID } = props + const { fetchData } = useFetch() + const { message } = App.useApp() + const [form] = Form.useForm() + const [currentStatus, setCurrentStatus] = useState(enable) - useEffect(() => { - form.setFieldsValue({ enable }) - }, []) + useEffect(() => { + form.setFieldsValue({ enable }) + }, []) /** - * 保存 - * @returns - */ + * 保存 + * @returns + */ const save: () => Promise = () => { return new Promise((resolve, reject) => { try { @@ -41,22 +44,23 @@ const EditLocalModelModal = forwardRef>('model/local/info', { - method: 'PUT', - eoParams: { model: modelID }, - eoBody: finalValue, - }) - .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)) + + fetchData>('model/local/info', { + method: 'PUT', + eoParams: { model: modelID }, + eoBody: finalValue + }) + .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) { @@ -68,40 +72,41 @@ const EditLocalModelModal = forwardRef -
- -
-
- {$t('当前调用状态:')} - {currentStatus && {$t('正常')}} - {!currentStatus && {$t('停用')}} -
- - { - form.setFieldsValue({ enable: checked }) - setCurrentStatus(checked) - }} - /> - -
-
-
- - ) -}) + return ( + +
+ +
+
+ {$t('当前调用状态:')} + {currentStatus && {$t('正常')}} + {!currentStatus && {$t('停用')}} +
+ + { + form.setFieldsValue({ enable: checked }) + setCurrentStatus(checked) + }} + /> + +
+
+
+
+ ) + } +) const LocalModelList: React.FC = () => { const pageListRef = useRef(null) @@ -109,19 +114,94 @@ const LocalModelList: React.FC = () => { const { fetchData } = useFetch() const [searchWord, setSearchWord] = useState('') const localAiDeployRef = useRef() + const ConfigureOllamaServiceRef = useRef() const EditLocalModelModalRef = useRef() const [stateColumnMap] = useState<{ [k: string]: { text: string; className?: string } }>({ - normal: { text: '正常' }, - deploying: { text: '部署中', className: 'text-[#2196f3] cursor-pointer' }, - error: { text: '模型异常', className: 'text-[#ff4d4f]' }, - disabled: { text: '停用' }, - deploying_error: { text: '部署失败', className: 'text-[#ff4d4f] cursor-pointer' } + normal: { text: $t('正常') }, + deploying: { text: $t('部署中'), className: 'text-[#2196f3] cursor-pointer' }, + error: { text: $t('模型异常'), className: 'text-[#ff4d4f]' }, + disabled: { text: $t('停用') }, + deploying_error: { text: $t('部署失败'), className: 'text-[#ff4d4f] cursor-pointer' } }) + const [ollamaAddress, setOllamaAddress] = useState('') + + useEffect(() => { + getOllamaData() + }, []) + + const configureService = (address?: string) => { + modal.confirm({ + title: $t('配置 Ollama 服务'), + content: ( + + ), + onOk: () => { + return ConfigureOllamaServiceRef.current?.save().then((res) => { + if (res === true) { + getOllamaData() + pageListRef.current?.reload() + } + }) + }, + footer: (_, { OkBtn, CancelBtn }) => { + return ( + + ) + }, + width: 600, + okText: $t('确认'), + cancelText: $t('取消'), + closable: true, + icon: <> + }) + } + + const customEmptyRender = () => { + return ( + <> +
+ +
{$t('模型部署服务未配置')}
+ +
+ + ) + } + + const getOllamaData = async () => { + const response = await fetchData>('model/local/source/ollama', { + method: 'GET' + }) + + if (response.code === STATUS_CODE.SUCCESS) { + setOllamaAddress(response.data?.config?.address || '') + } else { + message.error(response.msg || $t(RESPONSE_TIPS.error)) + } + } + const handleEdit = (record: ModelListData) => { modal.confirm({ title: $t('模型设置'), - content: , + content: ( + + ), onOk: () => { return EditLocalModelModalRef.current?.save().then((res) => { if (res === true) { @@ -243,7 +323,7 @@ const LocalModelList: React.FC = () => { { title: '', key: 'option', - btnNums: 4, + btnNums: 2, fixed: 'right', valueType: 'option', render: (_: React.ReactNode, entity: ModelListData) => [ @@ -283,7 +363,9 @@ const LocalModelList: React.FC = () => { } const modalInstance = modal.confirm({ title: $t('部署过程'), - content: , + content: ( + + ), footer: () => { return }, @@ -326,6 +408,7 @@ const LocalModelList: React.FC = () => { { title: $t('Apis'), dataIndex: 'apiCount', + width: 100, render: (dom: React.ReactNode, record: ModelListData) => ( { { setSearchWord(e.target.value) pageListRef.current?.reload() }} + beforeNewBtn={ + [] + } showPagination={true} searchPlaceholder={$t('请输入名称搜索')} columns={columns} diff --git a/frontend/packages/core/src/pages/guide/AIModelGuide.tsx b/frontend/packages/core/src/pages/guide/AIModelGuide.tsx index 0ca41ad1..5e29dd87 100644 --- a/frontend/packages/core/src/pages/guide/AIModelGuide.tsx +++ b/frontend/packages/core/src/pages/guide/AIModelGuide.tsx @@ -6,16 +6,18 @@ import { $t } from '@common/locales' import { Icon } from '@iconify/react/dist/iconify.js' import { App } from 'antd' import { Card } from 'antd' -import { useRef } from 'react' +import { useEffect, useRef, useState } from 'react' import { useNavigate } from 'react-router-dom' import AiSettingModalContent, { AiSettingModalContentHandle } from '../aiSetting/AiSettingModal' import { checkAccess } from '@common/utils/permission' import LocalAiDeploy, { LocalAiDeployHandle } from './LocalAiDeploy' import useDeployLocalModel from './deployModelUtil' import RestAIDeploy, { RestAIDeployHandle } from './RestAIDeploy' +import { BasicResponse, RESPONSE_TIPS, STATUS_CODE } from '@common/const/const' +import { useFetch } from '@common/hooks/http' export const AIModelGuide = () => { - const { modal } = App.useApp() + const { message, modal } = App.useApp() const entityData = useRef(null) const navigateTo = useNavigate() const { accessData } = useGlobalContext() @@ -23,6 +25,8 @@ export const AIModelGuide = () => { const localAiDeployRef = useRef() const restAiDeployRef = useRef() const { deployLocalModel } = useDeployLocalModel() + const { fetchData } = useFetch() + const [ollamaAddress, setOllamaAddress] = useState('') const dumpServerPage = () => { navigateTo('/service/list') @@ -105,10 +109,31 @@ export const AIModelGuide = () => { }) } + + const getOllamaData = async () => { + const response = await fetchData>('model/local/source/ollama', { + method: 'GET' + }) + + if (response.code === STATUS_CODE.SUCCESS) { + setOllamaAddress(response.data?.config?.address || '') + } else { + message.error(response.msg || $t(RESPONSE_TIPS.error)) + } + } + + useEffect(() => { + getOllamaData() + }, []) + /** * 本地部署 AI 并生成 API */ const localModelCardClick = async () => { + if (!ollamaAddress) { + navigateTo('/aisetting?status=unconfigure') + return + } const modalInstance = modal.confirm({ title: $t('部署本地模型'), content: { @@ -131,6 +156,10 @@ export const AIModelGuide = () => { } const deployDeepSeek = async (e: any) => { e.stopPropagation() + if (!ollamaAddress) { + navigateTo('/aisetting?status=unconfigure') + return + } await deployLocalModel({ modelID: 'deepseek-r1' }) @@ -141,13 +170,13 @@ export const AIModelGuide = () => { { imgSrc: restAPIPic, title: $t('添加 Rest 服务'), - description: $t('支持批量添加现有 API 文档以实现统一的外部访问。'), + description: $t('导入OpenAPI文档,将现有系统的API发布到APIPark。'), click: restCardClick }, { imgSrc: onlineAIPic, title: $t('添加在线 AI API'), - description: $t('快速调用 AI 模型的云服务 API,方便管理提示词和统一计费。'), + description: $t('添加公有云AI模型的 API Key,通过APIPark 统一调用公有云的AI模型。'), click: aiCardClick }, { diff --git a/frontend/packages/core/src/pages/guide/Guide.tsx b/frontend/packages/core/src/pages/guide/Guide.tsx index deeff7fe..53d89a81 100644 --- a/frontend/packages/core/src/pages/guide/Guide.tsx +++ b/frontend/packages/core/src/pages/guide/Guide.tsx @@ -151,12 +151,13 @@ export default function Guide() { description={

+ 🦄 APIPark {$t( - '你能通过 APIPark 快速在企业内部构建 API 开放门户/市场,享受极致的转发性能、API 可观测、服务治理、多租户管理、订阅审核流程等诸多好处。' + '是开源的一站式 AI 网关与 API 门户,可快速接入 OpenAI/DeepSeek 等各类 AI 模型,通过统一请求格式避免模型切换对业务造成影响,提供企业级 API 安全防护(鉴权/限流/敏感词过滤)与实时用量监控,支持团队内 API 共享协作,管理接口订阅授权并保证您的API安全。' )}

- {$t('如果你喜欢我们的产品,欢迎给我们 Star 或提供产品反馈意见。')} + {$t('✨ 欢迎在 Github 为我们 Star 或提供产品反馈意见。')} {$t('点击这里')} @@ -180,6 +181,7 @@ export default function Guide() { Star

+

{$t('⚡您可快速通过以下方式开放API供大家使用:')}

} showBorder={false} diff --git a/frontend/packages/core/src/pages/guide/LocalAiDeploy.tsx b/frontend/packages/core/src/pages/guide/LocalAiDeploy.tsx index 1e5839ef..e131687b 100644 --- a/frontend/packages/core/src/pages/guide/LocalAiDeploy.tsx +++ b/frontend/packages/core/src/pages/guide/LocalAiDeploy.tsx @@ -114,7 +114,7 @@ const LocalAiDeploy = forwardRef((props: any, ref: any name="partitionInsideCert" autoComplete="off" > - + - - -
{$t('热点模型')} @@ -179,6 +158,27 @@ const LocalAiDeploy = forwardRef((props: any, ref: any : null}
+ + + + - OR + {$t('或')} From 9de48bf40039e36070a34d7be1761d020632cc96 Mon Sep 17 00:00:00 2001 From: ningyv <1793599591@qq.com> Date: Tue, 18 Feb 2025 21:55:58 +0800 Subject: [PATCH 3/9] feat: feature/1.5-Data Integration --- .../common/src/components/aoplatform/BasicLayout.tsx | 4 ++-- .../common/src/components/aoplatform/InsidePage.tsx | 4 +++- frontend/packages/common/src/hooks/http.ts | 10 +++++++--- frontend/packages/common/src/locales/scan/en-US.json | 5 +++-- frontend/packages/common/src/locales/scan/ja-JP.json | 3 ++- frontend/packages/common/src/locales/scan/zh-CN.json | 3 ++- frontend/packages/common/src/locales/scan/zh-TW.json | 3 ++- .../src/pages/aiSetting/ConfigureOllamaService.tsx | 5 +++-- .../packages/core/src/pages/guide/AIModelGuide.tsx | 2 +- frontend/packages/core/src/pages/guide/Guide.tsx | 4 +++- 10 files changed, 28 insertions(+), 15 deletions(-) diff --git a/frontend/packages/common/src/components/aoplatform/BasicLayout.tsx b/frontend/packages/common/src/components/aoplatform/BasicLayout.tsx index 19767e95..738a6866 100644 --- a/frontend/packages/common/src/components/aoplatform/BasicLayout.tsx +++ b/frontend/packages/common/src/components/aoplatform/BasicLayout.tsx @@ -280,9 +280,9 @@ function BasicLayout({ project = 'core' }: { project: string }) { collapsedButtonRender={false} >
diff --git a/frontend/packages/common/src/components/aoplatform/InsidePage.tsx b/frontend/packages/common/src/components/aoplatform/InsidePage.tsx index 3260e886..40d55930 100644 --- a/frontend/packages/common/src/components/aoplatform/InsidePage.tsx +++ b/frontend/packages/common/src/components/aoplatform/InsidePage.tsx @@ -22,6 +22,7 @@ class InsidePageProps { headerClassName?: string = '' /** 整个页面滚动 */ scrollPage?: boolean = true + customPadding?: boolean customBtn?: ReactNode } @@ -41,6 +42,7 @@ const InsidePage: FC = ({ contentClassName = '', headerClassName = '', scrollPage = true, + customPadding = false, customBtn }) => { const navigate = useNavigate() @@ -57,7 +59,7 @@ const InsidePage: FC = ({ {!pageTitle && !description && !backUrl && !customBtn ? ( <> ) : ( -
+
{backUrl && (