mirror of
https://github.com/APIParkLab/APIPark.git
synced 2026-06-14 20:41:15 +08:00
feat: ai model
This commit is contained in:
@@ -1,4 +1,3 @@
|
||||
import Icon from '@ant-design/icons'
|
||||
import { ActionType } from '@ant-design/pro-components'
|
||||
import InsidePage from '@common/components/aoplatform/InsidePage'
|
||||
import PageList, { PageProColumns } from '@common/components/aoplatform/PageList'
|
||||
@@ -7,14 +6,12 @@ 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 AIProviderSelect, { AIProvider } from '@core/components/AIProviderSelect'
|
||||
import { App, Divider, Space, Typography } from 'antd'
|
||||
import dayjs from 'dayjs'
|
||||
import React, { useEffect, useRef, useState } from 'react'
|
||||
import { useSearchParams } from 'react-router-dom'
|
||||
import ApiKeyContent from './components/ApiKeyContent'
|
||||
import { APIKey, EditAPIKey } from './types'
|
||||
import { APIKey } from './types'
|
||||
|
||||
const KeySettings: React.FC = () => {
|
||||
const pageListRef = useRef<ActionType>(null)
|
||||
@@ -33,80 +30,9 @@ const KeySettings: React.FC = () => {
|
||||
pageListRef.current?.reload()
|
||||
}, [selectedProvider])
|
||||
|
||||
const handleEdit = (record: APIKey) => {
|
||||
openModal(record)
|
||||
}
|
||||
const handleEdit = (record: APIKey) => {}
|
||||
|
||||
const handleAdd = () => {
|
||||
openModal()
|
||||
}
|
||||
|
||||
const openModal = async (entity?: EditAPIKey) => {
|
||||
if (!provider) return
|
||||
const mode = entity ? 'edit' : 'add'
|
||||
if (mode === 'edit') {
|
||||
message.loading($t(RESPONSE_TIPS.loading))
|
||||
const { code, data, msg } = await fetchData<BasicResponse<{ info: EditAPIKey }>>('ai/resource/key', {
|
||||
method: 'GET',
|
||||
eoParams: { provider: selectedProvider, id: entity!.id }
|
||||
// eoApiPrefix: 'http://uat.apikit.com:11204/mockApi/aoplatform/api/v1/'
|
||||
})
|
||||
message.destroy()
|
||||
if (code !== STATUS_CODE.SUCCESS) {
|
||||
message.error(msg || $t(RESPONSE_TIPS.error))
|
||||
return
|
||||
}
|
||||
entity = data?.info
|
||||
} else {
|
||||
entity = {
|
||||
name: `key${total}`,
|
||||
config: provider.default_config,
|
||||
expire_time: 0
|
||||
} as EditAPIKey
|
||||
}
|
||||
const newEntity = entity as EditAPIKey
|
||||
|
||||
modal.confirm({
|
||||
title: mode === 'add' ? $t(`添加 ${provider?.name} APIKey`) : $t('编辑 APIKey'),
|
||||
content: <ApiKeyContent ref={modalRef} entity={newEntity} provider={provider} />,
|
||||
onOk: () => {
|
||||
return new Promise((resolve, reject) => {
|
||||
modalRef.current?.handleOk().then((res: boolean) => {
|
||||
if (res === true) {
|
||||
pageListRef.current?.reload()
|
||||
resolve(res)
|
||||
return
|
||||
}
|
||||
reject()
|
||||
})
|
||||
})
|
||||
},
|
||||
width: 600,
|
||||
okText: $t('确认'),
|
||||
footer: (_, { OkBtn, CancelBtn }) => {
|
||||
return (
|
||||
<div className="flex justify-between items-center">
|
||||
<a
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
href={provider.getApikeyUrl}
|
||||
className="flex items-center gap-[8px]"
|
||||
>
|
||||
<span>{$t('从 (0) 获取 API KEY', [provider.name])}</span>
|
||||
<Icon icon="ic:baseline-open-in-new" width={16} height={16} />
|
||||
</a>
|
||||
<div>
|
||||
<CancelBtn />
|
||||
{checkAccess('system.settings.ai_key_resource.manager', accessData) ? <OkBtn /> : null}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
},
|
||||
cancelText: $t('取消'),
|
||||
closable: true,
|
||||
icon: <></>
|
||||
})
|
||||
}
|
||||
const handleAdd = () => {}
|
||||
|
||||
const handleDelete = async (id: string) => {
|
||||
try {
|
||||
|
||||
@@ -20,8 +20,9 @@ import { KeyStatusNode } from './components/KeyStatusNode'
|
||||
import { ModelCardNode } from './components/ModelCardNode'
|
||||
import { ServiceCardNode } from './components/NodeComponents'
|
||||
import { LAYOUT } from './constants'
|
||||
import { useAiSetting } from './contexts/AiSettingContext'
|
||||
import './styles.css'
|
||||
import { AiSettingListItem, ModelData } from './types'
|
||||
import { ModelData } from './types'
|
||||
|
||||
export type ApiResponse = BasicResponse<{
|
||||
backup: {
|
||||
@@ -61,17 +62,16 @@ const edgeTypes: EdgeTypes = {
|
||||
custom: CustomEdge
|
||||
}
|
||||
|
||||
const AIFlowChart = ({ openModal }: { openModal: (entity: AiSettingListItem) => Promise<void> }) => {
|
||||
const AIFlowChart = () => {
|
||||
const [modelData, setModelData] = useState<ModelData[]>([])
|
||||
const [nodes, setNodes, onNodesChange] = useNodesState<Node>([])
|
||||
const [edges, setEdges, onEdgesChange] = useEdgesState<Edge>([])
|
||||
const { fetchData } = useFetch()
|
||||
const { openConfigModal } = useAiSetting()
|
||||
|
||||
useEffect(() => {
|
||||
// Mock API call - replace with actual API call
|
||||
fetchData<ApiResponse>('ai/providers/configured', {
|
||||
method: 'GET'
|
||||
// eoApiPrefix: 'http://uat.apikit.com:11204/mockApi/aoplatform/api/v1/'
|
||||
}).then((response) => {
|
||||
const mockApiResponse: ApiResponse = response as ApiResponse
|
||||
setModelData(mockApiResponse.data.providers)
|
||||
@@ -105,7 +105,7 @@ const AIFlowChart = ({ openModal }: { openModal: (entity: AiSettingListItem) =>
|
||||
defaultLlm: model.default_llm,
|
||||
logo: model.logo,
|
||||
id: model.id,
|
||||
openModal
|
||||
openModal: openConfigModal
|
||||
}
|
||||
})),
|
||||
...modelData.map((model) => ({
|
||||
|
||||
@@ -4,7 +4,8 @@ import { BasicResponse, RESPONSE_TIPS, STATUS_CODE } from '@common/const/const'
|
||||
import { useFetch } from '@common/hooks/http'
|
||||
import { $t } from '@common/locales'
|
||||
import { App, Button, Card, Empty, Spin, Tag } from 'antd'
|
||||
import { FC, memo, useEffect, useState } from 'react'
|
||||
import { memo, useEffect, useState } from 'react'
|
||||
import { useAiSetting } from './contexts/AiSettingContext'
|
||||
import { AiSettingListItem } from './types'
|
||||
|
||||
const CardBox = memo(
|
||||
@@ -92,26 +93,23 @@ const ModelCardArea = ({
|
||||
</>
|
||||
)
|
||||
}
|
||||
interface AIUnConfigureProps {
|
||||
openModal: (entity: AiSettingListItem) => Promise<void>
|
||||
}
|
||||
|
||||
const AIUnConfigure: FC<AIUnConfigureProps> = ({ openModal }) => {
|
||||
const { message } = App.useApp()
|
||||
const AIUnConfigure = () => {
|
||||
const [modelData, setModelData] = useState<AiSettingListItem[]>([])
|
||||
const { fetchData } = useFetch()
|
||||
const [aiSettingList, setAiSettingList] = useState<AiSettingListItem[]>([])
|
||||
const { openConfigModal } = useAiSetting()
|
||||
const [loading, setLoading] = useState<boolean>(false)
|
||||
|
||||
const getAiSettingList = () => {
|
||||
useEffect(() => {
|
||||
setLoading(true)
|
||||
return fetchData<BasicResponse<{ providers: Omit<AiSettingListItem, 'availableLlms' | 'llmListStatus'>[] }>>(
|
||||
fetchData<BasicResponse<{ providers: Omit<AiSettingListItem, 'availableLlms' | 'llmListStatus'>[] }>>(
|
||||
`ai/providers/unconfigured`,
|
||||
{ method: 'GET', eoTransformKeys: ['default_llm', 'default_llm_logo'] }
|
||||
)
|
||||
.then((response) => {
|
||||
const { code, data, msg } = response
|
||||
if (code === STATUS_CODE.SUCCESS) {
|
||||
setAiSettingList(
|
||||
setModelData(
|
||||
data.providers?.map((x: AiSettingListItem) => ({
|
||||
...x,
|
||||
name: $t(x.name),
|
||||
@@ -120,14 +118,11 @@ const AIUnConfigure: FC<AIUnConfigureProps> = ({ openModal }) => {
|
||||
}))
|
||||
)
|
||||
} else {
|
||||
const { message } = App.useApp()
|
||||
message.error(msg || $t(RESPONSE_TIPS.error))
|
||||
}
|
||||
})
|
||||
.finally(() => setLoading(false))
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
getAiSettingList()
|
||||
}, [])
|
||||
|
||||
return (
|
||||
@@ -135,13 +130,16 @@ const AIUnConfigure: FC<AIUnConfigureProps> = ({ openModal }) => {
|
||||
className="h-full"
|
||||
wrapperClassName="h-full pr-PAGE_INSIDE_X"
|
||||
indicator={<LoadingOutlined style={{ fontSize: 24 }} spin />}
|
||||
spinning={loading}
|
||||
spinning={modelData.length === 0}
|
||||
>
|
||||
{aiSettingList && aiSettingList.length > 0 ? (
|
||||
{modelData && modelData.length > 0 ? (
|
||||
<div>
|
||||
{aiSettingList.filter((item) => !item.configured).length > 0 && (
|
||||
{modelData.filter((item) => !item.configured).length > 0 && (
|
||||
<>
|
||||
<ModelCardArea openModal={openModal} modelList={aiSettingList.filter((item) => !item.configured) || []} />
|
||||
<ModelCardArea
|
||||
openModal={openConfigModal}
|
||||
modelList={modelData.filter((item) => !item.configured) || []}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
@@ -151,5 +149,4 @@ const AIUnConfigure: FC<AIUnConfigureProps> = ({ openModal }) => {
|
||||
</Spin>
|
||||
)
|
||||
}
|
||||
|
||||
export default AIUnConfigure
|
||||
|
||||
@@ -1,105 +1,50 @@
|
||||
import InsidePage from '@common/components/aoplatform/InsidePage'
|
||||
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 { Icon } from '@iconify/react/dist/iconify.js'
|
||||
import { App, Tabs } from 'antd'
|
||||
import { useRef } from 'react'
|
||||
import { Tabs } from 'antd'
|
||||
import AIFlowChart from './AIFlowChart'
|
||||
import AiSettingModalContent, { AiSettingModalContentHandle } from './AiSettingModal'
|
||||
import AIUnConfigure from './AIUnconfigure'
|
||||
import { AiProviderConfig, AiSettingListItem } from './types'
|
||||
|
||||
const AiSettingList = () => {
|
||||
const { modal, message } = App.useApp()
|
||||
const { fetchData } = useFetch()
|
||||
const modalRef = useRef<AiSettingModalContentHandle>()
|
||||
const { setAiConfigFlushed, accessData } = useGlobalContext()
|
||||
|
||||
const openModal = async (entity: AiSettingListItem) => {
|
||||
console.log(entity)
|
||||
message.loading($t(RESPONSE_TIPS.loading))
|
||||
const { code, data, msg } = await fetchData<BasicResponse<{ provider: AiProviderConfig }>>('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
|
||||
}
|
||||
modal.confirm({
|
||||
title: $t('模型配置'),
|
||||
content: (
|
||||
<AiSettingModalContent
|
||||
ref={modalRef}
|
||||
entity={{ ...data.provider, defaultLlm: entity.defaultLlm }}
|
||||
readOnly={!checkAccess('system.devops.ai_provider.edit', accessData)}
|
||||
/>
|
||||
),
|
||||
onOk: () => {
|
||||
return modalRef.current?.save().then((res) => {
|
||||
if (res === true) setAiConfigFlushed(true)
|
||||
})
|
||||
},
|
||||
width: 600,
|
||||
okText: $t('确认'),
|
||||
footer: (_, { OkBtn, CancelBtn }) => {
|
||||
return (
|
||||
<div className="flex justify-between items-center">
|
||||
<a
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
href={data.provider.getApikeyUrl}
|
||||
className="flex items-center gap-[8px]"
|
||||
>
|
||||
<span>{$t('从 (0) 获取 API KEY', [data.provider.name])}</span>
|
||||
<Icon icon="ic:baseline-open-in-new" width={16} height={16} />
|
||||
</a>
|
||||
<div>
|
||||
<CancelBtn />
|
||||
{checkAccess('system.devops.ai_provider.edit', accessData) ? <OkBtn /> : null}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
},
|
||||
cancelText: $t('取消'),
|
||||
closable: true,
|
||||
icon: <></>
|
||||
})
|
||||
}
|
||||
import { AiSettingProvider } from './contexts/AiSettingContext'
|
||||
|
||||
const AiSettingContent = () => {
|
||||
return (
|
||||
<>
|
||||
<InsidePage
|
||||
className="overflow-y-auto pb-PAGE_INSIDE_B"
|
||||
pageTitle={$t('AI 模型')}
|
||||
description={$t('配置好 AI 模型后,你可以使用对应的大模型来创建 AI 服务')}
|
||||
showBorder={false}
|
||||
scrollPage={false}
|
||||
>
|
||||
<div className="flex flex-col h-full">
|
||||
<Tabs
|
||||
className="flex-shrink-0"
|
||||
items={[
|
||||
{
|
||||
key: 'flow',
|
||||
label: $t('已设置'),
|
||||
children: <AIFlowChart openModal={openModal} />
|
||||
},
|
||||
{
|
||||
key: 'config',
|
||||
label: $t('未设置'),
|
||||
children: <div className="overflow-auto flex-grow">{<AIUnConfigure openModal={openModal} />}</div>
|
||||
}
|
||||
]}
|
||||
/>
|
||||
</div>
|
||||
</InsidePage>
|
||||
</>
|
||||
<InsidePage
|
||||
className="overflow-y-auto pb-PAGE_INSIDE_B"
|
||||
pageTitle={$t('AI 模型')}
|
||||
description={$t('配置好 AI 模型后,你可以使用对应的大模型来创建 AI 服务')}
|
||||
showBorder={false}
|
||||
scrollPage={false}
|
||||
>
|
||||
<div className="flex flex-col h-full">
|
||||
<Tabs
|
||||
className="flex-shrink-0"
|
||||
items={[
|
||||
{
|
||||
key: 'flow',
|
||||
label: $t('已设置'),
|
||||
children: <AIFlowChart />
|
||||
},
|
||||
{
|
||||
key: 'config',
|
||||
label: $t('未设置'),
|
||||
children: (
|
||||
<div className="overflow-auto flex-grow">
|
||||
<AIUnConfigure />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
]}
|
||||
/>
|
||||
</div>
|
||||
</InsidePage>
|
||||
)
|
||||
}
|
||||
|
||||
const AiSettingList = () => {
|
||||
return (
|
||||
<AiSettingProvider>
|
||||
<AiSettingContent />
|
||||
</AiSettingProvider>
|
||||
)
|
||||
}
|
||||
|
||||
export default AiSettingList
|
||||
|
||||
@@ -2,7 +2,7 @@ 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, Select, Tag } from 'antd'
|
||||
import { App, Form, InputNumber, Select, Tag } from 'antd'
|
||||
import { forwardRef, useEffect, useImperativeHandle, useState } from 'react'
|
||||
import { AiProviderConfig, AiProviderLlmsItems } from './AiSettingList'
|
||||
|
||||
@@ -18,6 +18,7 @@ export type AiSettingModalContentHandle = {
|
||||
type AiSettingModalContentField = {
|
||||
config: string
|
||||
defaultLlm: string
|
||||
priority: number
|
||||
}
|
||||
|
||||
const AiSettingModalContent = forwardRef<AiSettingModalContentHandle, AiSettingModalContentProps>((props, ref) => {
|
||||
@@ -52,12 +53,14 @@ const AiSettingModalContent = forwardRef<AiSettingModalContentHandle, AiSettingM
|
||||
try {
|
||||
form.setFieldsValue({
|
||||
defaultLlm: entity.defaultLlm,
|
||||
config: entity!.config ? JSON.stringify(JSON.parse(entity!.config), null, 2) : ''
|
||||
config: entity!.config ? JSON.stringify(JSON.parse(entity!.config), null, 2) : '',
|
||||
priority: entity.priority || 1
|
||||
})
|
||||
} catch (e) {
|
||||
form.setFieldsValue({
|
||||
defaultLlm: entity.defaultLlm,
|
||||
config: ''
|
||||
config: '',
|
||||
priority: 1
|
||||
})
|
||||
}
|
||||
}, [])
|
||||
@@ -67,10 +70,15 @@ const AiSettingModalContent = forwardRef<AiSettingModalContentHandle, AiSettingM
|
||||
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: value,
|
||||
eoBody: finalValue,
|
||||
eoTransformKeys: ['defaultLlm']
|
||||
})
|
||||
.then((response) => {
|
||||
@@ -103,7 +111,7 @@ const AiSettingModalContent = forwardRef<AiSettingModalContentHandle, AiSettingM
|
||||
name="aiServiceInsideRouterModalConfig"
|
||||
autoComplete="off"
|
||||
>
|
||||
<Form.Item<AiSettingModalContentField> label={$t('模型')} name="defaultLlm" rules={[{ required: true }]}>
|
||||
<Form.Item<AiSettingModalContentField> label={$t('默认模型')} name="defaultLlm" rules={[{ required: true }]}>
|
||||
<Select
|
||||
className="w-INPUT_NORMAL"
|
||||
placeholder={$t(PLACEHOLDER.select)}
|
||||
@@ -120,6 +128,25 @@ const AiSettingModalContent = forwardRef<AiSettingModalContentHandle, AiSettingM
|
||||
></Select>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item<AiSettingModalContentField>
|
||||
label={$t('优先级')}
|
||||
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<AiSettingModalContentField> label={$t('参数')} name="config">
|
||||
<Codebox
|
||||
editorTheme="vs-dark"
|
||||
|
||||
@@ -0,0 +1,89 @@
|
||||
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 { AiProviderConfig, AiSettingListItem } from '../types'
|
||||
|
||||
interface AiSettingContextType {
|
||||
openConfigModal: (entity: AiSettingListItem) => Promise<void>
|
||||
}
|
||||
|
||||
const AiSettingContext = createContext<AiSettingContextType | undefined>(undefined)
|
||||
|
||||
export const AiSettingProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => {
|
||||
const { modal, message } = App.useApp()
|
||||
const { fetchData } = useFetch()
|
||||
const { 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: AiProviderConfig }>>('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
|
||||
}
|
||||
|
||||
modal.confirm({
|
||||
title: $t('模型配置'),
|
||||
content: (
|
||||
<AiSettingModalContent
|
||||
ref={modalRef}
|
||||
entity={{ ...data.provider, defaultLlm: entity.defaultLlm }}
|
||||
readOnly={!checkAccess('system.devops.ai_provider.edit', accessData)}
|
||||
/>
|
||||
),
|
||||
onOk: () => {
|
||||
return modalRef.current?.save().then((res) => {
|
||||
if (res === true) {
|
||||
setAiConfigFlushed(true)
|
||||
}
|
||||
})
|
||||
},
|
||||
width: 600,
|
||||
okText: $t('确认'),
|
||||
footer: (_, { OkBtn, CancelBtn }) => {
|
||||
return (
|
||||
<div className="flex justify-between items-center">
|
||||
<a
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
href={data.provider.getApikeyUrl}
|
||||
className="flex items-center gap-[8px]"
|
||||
>
|
||||
<span>{$t('从 (0) 获取 API KEY', [data.provider.name])}</span>
|
||||
<Icon icon="ic:baseline-open-in-new" width={16} height={16} />
|
||||
</a>
|
||||
<div>
|
||||
<CancelBtn />
|
||||
{checkAccess('system.devops.ai_provider.edit', accessData) ? <OkBtn /> : null}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
},
|
||||
cancelText: $t('取消'),
|
||||
closable: true,
|
||||
icon: <></>
|
||||
})
|
||||
}
|
||||
|
||||
return <AiSettingContext.Provider value={{ openConfigModal }}>{children}</AiSettingContext.Provider>
|
||||
}
|
||||
|
||||
export const useAiSetting = () => {
|
||||
const context = useContext(AiSettingContext)
|
||||
if (!context) {
|
||||
throw new Error('useAiSetting must be used within an AiSettingProvider')
|
||||
}
|
||||
return context
|
||||
}
|
||||
@@ -27,6 +27,7 @@ export type AiSettingListItem = {
|
||||
defaultLlmLogo: string
|
||||
enable: boolean
|
||||
configured: boolean
|
||||
priority?: number
|
||||
}
|
||||
|
||||
export type AiProviderLlmsItems = {
|
||||
@@ -48,6 +49,7 @@ export type AiProviderDefaultConfig = {
|
||||
export type AiProviderConfig = {
|
||||
id: string
|
||||
name: string
|
||||
config: string
|
||||
config?: string
|
||||
getApikeyUrl: string
|
||||
priority?: number
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user