From 66d0653dc6cdb31887937fb413fe2f33ed9cf0d4 Mon Sep 17 00:00:00 2001 From: ningyv <1793599591@qq.com> Date: Tue, 18 Feb 2025 19:51:28 +0800 Subject: [PATCH] feat: feature/1.5-Data Integration --- .../src/components/aoplatform/PageList.tsx | 14 +- .../src/contexts/GlobalStateContext.tsx | 4 +- .../common/src/locales/scan/en-US.json | 21 +- .../common/src/locales/scan/ja-JP.json | 21 +- .../common/src/locales/scan/zh-CN.json | 21 +- .../common/src/locales/scan/zh-TW.json | 21 +- frontend/packages/core/src/index.css | 7 + .../aiSetting/ConfigureOllamaService.tsx | 85 +++++++ .../src/pages/aiSetting/LocalModelList.tsx | 230 ++++++++++++------ .../core/src/pages/guide/AIModelGuide.tsx | 37 ++- .../packages/core/src/pages/guide/Guide.tsx | 6 +- .../core/src/pages/guide/LocalAiDeploy.tsx | 44 ++-- .../core/src/pages/keySettings/index.tsx | 4 +- .../core/src/pages/loadBalancing/index.tsx | 4 +- .../core/src/pages/system/SystemConfig.tsx | 2 +- .../market/src/pages/serviceHub/integrate.tsx | 4 +- 16 files changed, 411 insertions(+), 114 deletions(-) create mode 100644 frontend/packages/core/src/pages/aiSetting/ConfigureOllamaService.tsx diff --git a/frontend/packages/common/src/components/aoplatform/PageList.tsx b/frontend/packages/common/src/components/aoplatform/PageList.tsx index 4575904a..761366cb 100644 --- a/frontend/packages/common/src/components/aoplatform/PageList.tsx +++ b/frontend/packages/common/src/components/aoplatform/PageList.tsx @@ -41,6 +41,7 @@ interface PageListProps extends ProTableProps, RefAttributes) => void afterNewBtn?: React.ReactNode[] + beforeNewBtn?: React.ReactNode[] dragSortKey?: string onDragSortEnd?: (beforeIndex: number, afterIndex: number, newDataSource: T[]) => void | Promise tableTitle?: string @@ -56,7 +57,8 @@ interface PageListProps extends ProTableProps, RefAttributes void + manualReloadTable?: () => void, + customEmptyRender?: () => React.ReactNode } const PageList = >( @@ -80,6 +82,7 @@ const PageList = >( onSearchWordChange, manualReloadTable, afterNewBtn, + beforeNewBtn, dragSortKey, onDragSortEnd, tableTitle, @@ -94,7 +97,8 @@ const PageList = >( tableTitleClass, delayLoading = true, besidesTableHeight, - noScroll + noScroll, + customEmptyRender } = props const parentRef = useRef(null) const [tableHeight, setTableHeight] = useState(minVirtualHeight || window.innerHeight) @@ -190,6 +194,7 @@ const PageList = >( const headerTitle = () => { return ( <> + {beforeNewBtn ? (beforeNewBtn as React.ReactNode[]) : undefined} {tableTitle ? ( {tableTitle} ) : addNewBtnTitle ? ( @@ -345,6 +350,11 @@ const PageList = >( ) onChange?.(pagination, filters, sorter, extra) }} + locale={{ + emptyText: customEmptyRender ? customEmptyRender?.() : undefined + }} + style={customEmptyRender ? { height: '100%' } : undefined} + bodyStyle={customEmptyRender ? { height: '100%' } : undefined} rowKey={primaryKey} dataSource={dataSource} search={false} diff --git a/frontend/packages/common/src/contexts/GlobalStateContext.tsx b/frontend/packages/common/src/contexts/GlobalStateContext.tsx index a1cfcd29..d230cc6d 100644 --- a/frontend/packages/common/src/contexts/GlobalStateContext.tsx +++ b/frontend/packages/common/src/contexts/GlobalStateContext.tsx @@ -148,7 +148,7 @@ const mockData = [ access: 'system.settings.ai_provider.view' }, { - name: 'APIKey 资源池', + name: 'API Key 负载', key: 'aiKeys', path: '/keysetting', icon: 'ic:baseline-key', @@ -162,7 +162,7 @@ const mockData = [ access: 'system.settings.ai_api.view' }, { - name: '负载均衡', + name: '模型灾备', key: 'loadBalancing', path: '/loadBalancing', icon: 'ph:network-x', diff --git a/frontend/packages/common/src/locales/scan/en-US.json b/frontend/packages/common/src/locales/scan/en-US.json index 5834a6fc..1640f887 100644 --- a/frontend/packages/common/src/locales/scan/en-US.json +++ b/frontend/packages/common/src/locales/scan/en-US.json @@ -850,5 +850,24 @@ "Kbe98ba9e": "Private Service", "K24540de": "Stop", "Kd85b3f64": "Continue Waiting", - "K1400a1fc": "As a prefix for all APIs within the service, such as host/{service_name}/{api_path}. This has a significant impact, so modify with caution" + "K1400a1fc": "As a prefix for all APIs within the service, such as host/{service_name}/{api_path}. This has a significant impact, so modify with caution", + "Kb8185132": "OR", + "K83829a3b": "Model Exception", + "Kb92fb02b": "Deployment Failed", + "Kcf8e3b18": "Tags", + "K9ae48909": "API Key Balancing", + "K437724fc": "Supports creating multiple API Keys for intelligent load balancing under a single API model provider", + "K7d17707e": "Model Fallback", + "Kc007db4a": "Only .png, .jpg, .jpeg, .svg image files are supported", + "Kacf10c44": "Ollama Endpoint", + "K8d4f5b44": "Input example: https://www.apipark.com", + "K481442d3": "Configure Ollama Service", + "Kf9b341e3": "How to deploy Ollama?", + "K8632bef2": "Model deployment service not configured", + "Kbbd8ce81": "Configure Service", + "K39a8d392": "Import OpenAPI documents to publish existing system APIs to APIPark.", + "Ka742e079": "Add API Key for public cloud AI models to call public cloud AI models via APIPark.", + "K8097d6be": "is an open-source AI Gateway and API Portal that unifies access to OpenAI, DeepSeek, and other AI models. With enterprise-grade security features and real-time monitoring, it helps teams safely manage and share their AI APIs through a unified gateway.", + "Kf1ce5b3": "✨ We'd love your support on Github! Leave us a star or share your feedback.", + "K3af90490": "⚡ You can quickly open the API for everyone to use via the following methods:" } diff --git a/frontend/packages/common/src/locales/scan/ja-JP.json b/frontend/packages/common/src/locales/scan/ja-JP.json index b7f43805..863d794a 100644 --- a/frontend/packages/common/src/locales/scan/ja-JP.json +++ b/frontend/packages/common/src/locales/scan/ja-JP.json @@ -872,5 +872,24 @@ "Kbe98ba9e": "プライベートサービス", "K24540de": "停止", "Kd85b3f64": "引き続き待機", - "K1400a1fc": "サービス内のすべてのAPIのプレフィックスとして使用されます。例えば host/{service_name}/{api_path} のように、大きな影響を与えるため、慎重に変更してください。" + "K1400a1fc": "サービス内のすべてのAPIのプレフィックスとして使用されます。例えば host/{service_name}/{api_path} のように、大きな影響を与えるため、慎重に変更してください。", + "Kb8185132": "または", + "K83829a3b": "モデル異常", + "Kb92fb02b": "デプロイ失敗", + "Kcf8e3b18": "タグ", + "K9ae48909": "APIキーのペイロード", + "K437724fc": "1つのAPIモデルプロバイダーで複数のAPIキーを作成し、インテリジェント負荷分散をサポート", + "K7d17707e": "モデルフォールバック", + "Kc007db4a": "画像ファイルは .png, .jpg, .jpeg, .svg 形式のみサポートされています", + "Kacf10c44": "Ollama エンドポイント", + "K8d4f5b44": "入力例:https://www.apipark.com", + "K481442d3": "Ollama サービスの設定", + "Kf9b341e3": "Ollamaのデプロイ方法は?", + "K8632bef2": "モデルデプロイサービスが設定されていません", + "Kbbd8ce81": "サービスの設定", + "K39a8d392": "OpenAPIドキュメントをインポートし、既存のシステムのAPIをAPIParkに公開します。", + "Ka742e079": "パブリッククラウドAIモデルのAPIキーを追加し、APIParkを介してパブリッククラウドのAIモデルを統一的に呼び出します。", + "K8097d6be": "OpenAIやDeepSeekなどのさまざまなAIモデルに迅速にアクセスできるオープンソースのワンストップAIゲートウェイおよびAPIポータルです。統一されたリクエスト形式を使用して、モデルの切り替えによるビジネスへの影響を回避し、企業レベルのAPIセキュリティ(認証/レート制限/センシティブワードフィルタリング)とリアルタイムの使用量監視を提供します。チーム内でのAPI共有やコラボレーションをサポートし、インターフェースのサブスクリプション認証を管理してAPIのセキュリティを確保します。", + "Kf1ce5b3": "✨ Githubでスターを付けていただくか、製品フィードバックをお寄せください。", + "K3af90490": "⚡ 以下の方法で、APIをすぐに公開して皆さんに利用してもらえます:" } diff --git a/frontend/packages/common/src/locales/scan/zh-CN.json b/frontend/packages/common/src/locales/scan/zh-CN.json index 7a6129dc..c90b2e5e 100644 --- a/frontend/packages/common/src/locales/scan/zh-CN.json +++ b/frontend/packages/common/src/locales/scan/zh-CN.json @@ -803,5 +803,24 @@ "Kbe98ba9e": "私有服务", "K24540de": "停止", "Kd85b3f64": "继续等待", - "K1400a1fc": "作为服务内所有API的前缀,比如host/{service_name}/{api_path},影响较大,谨慎修改" + "K1400a1fc": "作为服务内所有API的前缀,比如host/{service_name}/{api_path},影响较大,谨慎修改", + "Kb8185132": "或", + "K83829a3b": "模型异常", + "Kb92fb02b": "部署失败", + "Kcf8e3b18": "Tags", + "K9ae48909": "API Key 负载", + "K437724fc": "支持单个 API 模型供应商下创建多个 API Key 进行智能负载均衡", + "K7d17707e": "模型灾备", + "Kc007db4a": "仅支持 .png .jpg .jpeg .svg 格式的图片文件", + "Kacf10c44": "Ollama 端点", + "K8d4f5b44": "输入例如:https://www.apipark.com", + "K481442d3": "配置 Ollama 服务", + "Kf9b341e3": "如何部署 Ollama?", + "K8632bef2": "模型部署服务未配置", + "Kbbd8ce81": "配置服务", + "K39a8d392": "导入OpenAPI文档,将现有系统的API发布到APIPark。", + "Ka742e079": "添加公有云AI模型的 API Key,通过APIPark 统一调用公有云的AI模型。", + "K8097d6be": "是开源的一站式 AI 网关与 API 门户,可快速接入 OpenAI/DeepSeek 等各类 AI 模型,通过统一请求格式避免模型切换对业务造成影响,提供企业级 API 安全防护(鉴权/限流/敏感词过滤)与实时用量监控,支持团队内 API 共享协作,管理接口订阅授权并保证您的API安全。", + "Kf1ce5b3": "✨ 欢迎在 Github 为我们 Star 或提供产品反馈意见。", + "K3af90490": "⚡您可快速通过以下方式开放API供大家使用:" } diff --git a/frontend/packages/common/src/locales/scan/zh-TW.json b/frontend/packages/common/src/locales/scan/zh-TW.json index fb6af1cd..db202013 100644 --- a/frontend/packages/common/src/locales/scan/zh-TW.json +++ b/frontend/packages/common/src/locales/scan/zh-TW.json @@ -872,5 +872,24 @@ "Kbe98ba9e": "私有服務", "K24540de": "停止", "Kd85b3f64": "繼續等待", - "K1400a1fc": "作為服務內所有 API 的前綴,例如 host/{service_name}/{api_path},這會產生較大的影響,請謹慎修改" + "K1400a1fc": "作為服務內所有 API 的前綴,例如 host/{service_name}/{api_path},這會產生較大的影響,請謹慎修改", + "Kb8185132": "或", + "K83829a3b": "模型異常", + "Kb92fb02b": "部署失敗", + "Kcf8e3b18": "標籤", + "K9ae48909": "API Key 負載", + "K437724fc": "支持在單個 API 模型供應商下創建多個 API Key 進行智能負載均衡", + "K7d17707e": "模型災備", + "Kc007db4a": "僅支持 .png, .jpg, .jpeg, .svg 格式的圖片文件", + "Kacf10c44": "Ollama 端點", + "K8d4f5b44": "輸入範例:https://www.apipark.com", + "K481442d3": "配置 Ollama 服務", + "Kf9b341e3": "如何部署 Ollama?", + "K8632bef2": "模型部署服務未配置", + "Kbbd8ce81": "配置服務", + "K39a8d392": "導入 OpenAPI 文件,將現有系統的 API 發佈到 APIPark。", + "Ka742e079": "添加公有雲 AI 模型的 API Key,通過 APIPark 統一調用公有雲的 AI 模型。", + "K8097d6be": "是一個開源的一站式 AI 閘道和 API 入口網站,可快速接入 OpenAI/DeepSeek 等各類 AI 模型,通過統一的請求格式避免模型切換對業務造成影響,提供企業級 API 安全防護(鑑權/限流/敏感詞過濾)與實時用量監控,支持團隊內 API 共享協作,管理介面訂閱授權並保證您的 API 安全。", + "Kf1ce5b3": "✨ 歡迎在 Github 為我們 Star 或提供產品反饋意見。", + "K3af90490": "⚡ 您可以快速通過以下方式開放 API 供大家使用:" } diff --git a/frontend/packages/core/src/index.css b/frontend/packages/core/src/index.css index c912b574..d0f2228c 100644 --- a/frontend/packages/core/src/index.css +++ b/frontend/packages/core/src/index.css @@ -996,6 +996,13 @@ p{ min-width:unset !important; } +.local-model-list .ant-pro-table .ant-table-body { + overflow: hidden !important; +} +.local-model-list .ant-pro-table td { + border-bottom: none !important; +} + .table-border { .ant-table:not(.ant-table-bordered){ border:1px solid var(--border-color) !important; diff --git a/frontend/packages/core/src/pages/aiSetting/ConfigureOllamaService.tsx b/frontend/packages/core/src/pages/aiSetting/ConfigureOllamaService.tsx new file mode 100644 index 00000000..8ca3d2c0 --- /dev/null +++ b/frontend/packages/core/src/pages/aiSetting/ConfigureOllamaService.tsx @@ -0,0 +1,85 @@ +import { forwardRef, useEffect, useImperativeHandle } from 'react' +import { App, Divider, Form, Space, Switch, Tag, Input } from 'antd' +import { BasicResponse, RESPONSE_TIPS, STATUS_CODE } from '@common/const/const' +import { $t } from '@common/locales' +import WithPermission from '@common/components/aoplatform/WithPermission' +import { useFetch } from '@common/hooks/http' + +export type ConfigureOllamaServiceHandle = { + save: () => Promise +} + +const ConfigureOllamaService = forwardRef((props, ref) => { + const { address = '' } = props + const [form] = Form.useForm() + const { fetchData } = useFetch() + const { message } = App.useApp() + + useEffect(() => { + form.setFieldsValue({ address }) + }, []) + + /** + * 保存 + * @returns + */ + const save: () => Promise = () => { + return new Promise((resolve, reject) => { + try { + form + .validateFields() + .then((value) => { + fetchData>('model/local/source/ollama', { + method: 'PUT', + eoParams: { address: value.address } + }) + .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) + } + }) + } + useImperativeHandle(ref, () => ({ + save + })) + return ( + +
+ + 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('或')}