feat: add key

This commit is contained in:
scarqin
2024-12-26 14:36:24 +08:00
parent ae2e37cedb
commit 977919fdb1
4 changed files with 166 additions and 142 deletions
@@ -1,12 +1,12 @@
import { memo, useCallback, useEffect, useImperativeHandle, useRef, useState } from 'react'
import type { RefObject } from 'react'
import { Box, useTheme } from '@mui/material'
import { Editor, useMonaco } from '@monaco-editor/react'
import { type editor as MonacoEditor } from 'monaco-editor'
import { IconButton } from '../IconButton'
import { message } from 'antd'
import { $t } from '@common/locales'
import { RESPONSE_TIPS } from '@common/const/const'
import { $t } from '@common/locales'
import { Editor, useMonaco } from '@monaco-editor/react'
import { Box, useTheme } from '@mui/material'
import { message } from 'antd'
import { type editor as MonacoEditor } from 'monaco-editor'
import type { RefObject } from 'react'
import { memo, useCallback, useEffect, useImperativeHandle, useRef, useState } from 'react'
import { IconButton } from '../IconButton'
export interface CodeboxApiRef {
insertCode: (value: string) => void
@@ -20,124 +20,118 @@ type AiSettingModalContentField = {
defaultLlm: string
}
const AiSettingModalContent = forwardRef<AiSettingModalContentHandle, AiSettingModalContentProps>(
(props, ref) => {
const [form] = Form.useForm()
const { message } = App.useApp()
const { entity, readOnly } = props
const { fetchData } = useFetch()
const [llmList, setLlmList] = useState<AiProviderLlmsItems[]>()
const [loading, setLoading] = useState<boolean>(false)
const AiSettingModalContent = forwardRef<AiSettingModalContentHandle, AiSettingModalContentProps>((props, ref) => {
const [form] = Form.useForm()
const { message } = App.useApp()
const { entity, readOnly } = props
const { fetchData } = useFetch()
const [llmList, setLlmList] = useState<AiProviderLlmsItems[]>()
const [loading, setLoading] = useState<boolean>(false)
const getLlmList = () => {
setLoading(true)
fetchData<BasicResponse<{ llms: AiProviderLlmsItems[] }>>(`ai/provider/llms`, {
method: 'GET',
eoParams: { provider: entity.id }
const getLlmList = () => {
setLoading(true)
fetchData<BasicResponse<{ llms: AiProviderLlmsItems[] }>>(`ai/provider/llms`, {
method: 'GET',
eoParams: { provider: entity.id }
})
.then((response) => {
const { code, data, msg } = response
if (code === STATUS_CODE.SUCCESS) {
setLlmList(data.llms)
} else {
message.error(msg || $t(RESPONSE_TIPS.error))
}
})
.then(response => {
const { code, data, msg } = response
if (code === STATUS_CODE.SUCCESS) {
setLlmList(data.llms)
} else {
message.error(msg || $t(RESPONSE_TIPS.error))
}
})
.finally(() => {
setLoading(false)
})
}
useEffect(() => {
getLlmList()
try {
form.setFieldsValue({
defaultLlm: entity.defaultLlm,
config: entity!.config ? JSON.stringify(JSON.parse(entity!.config), null, 2) : ''
})
} catch (e) {
form.setFieldsValue({
defaultLlm: entity.defaultLlm,
config: ''
})
}
}, [])
const save: () => Promise<boolean | string> = () => {
return new Promise((resolve, reject) => {
form
.validateFields()
.then(value => {
fetchData<BasicResponse<null>>('ai/provider/config', {
method: 'PUT',
eoParams: { provider: entity?.id },
eoBody: value,
eoTransformKeys: ['defaultLlm']
})
.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))
.finally(() => {
setLoading(false)
})
}
useImperativeHandle(ref, () => ({
save
}))
return (
<Form
layout="vertical"
labelAlign="left"
scrollToFirstError
form={form}
className="flex flex-col mx-auto h-full"
name="aiServiceInsideRouterModalConfig"
autoComplete="off"
>
<Form.Item<AiSettingModalContentField>
label={$t('模型')}
name="defaultLlm"
rules={[{ required: true }]}
>
<Select
className="w-INPUT_NORMAL"
placeholder={$t(PLACEHOLDER.select)}
loading={loading}
options={llmList?.map(x => ({
value: x.id,
label: (
<div className="flex items-center gap-[10px]">
<span>{x.id}</span>
{x?.scopes?.map(s => <Tag>{s?.toLocaleUpperCase()}</Tag>)}
</div>
)
}))}
></Select>
</Form.Item>
<Form.Item<AiSettingModalContentField> label={$t('参数')} name="config">
<Codebox
editorTheme="vs-dark"
readOnly={readOnly}
width="100%"
height="300px"
language="json"
enableToolbar={false}
/>
</Form.Item>
</Form>
)
}
)
useEffect(() => {
getLlmList()
try {
form.setFieldsValue({
defaultLlm: entity.defaultLlm,
config: entity!.config ? JSON.stringify(JSON.parse(entity!.config), null, 2) : ''
})
} catch (e) {
form.setFieldsValue({
defaultLlm: entity.defaultLlm,
config: ''
})
}
}, [])
const save: () => Promise<boolean | string> = () => {
return new Promise((resolve, reject) => {
form
.validateFields()
.then((value) => {
fetchData<BasicResponse<null>>('ai/provider/config', {
method: 'PUT',
eoParams: { provider: entity?.id },
eoBody: value,
eoTransformKeys: ['defaultLlm']
})
.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))
})
}
useImperativeHandle(ref, () => ({
save
}))
return (
<Form
layout="vertical"
labelAlign="left"
scrollToFirstError
form={form}
className="flex flex-col mx-auto h-full"
name="aiServiceInsideRouterModalConfig"
autoComplete="off"
>
<Form.Item<AiSettingModalContentField> label={$t('模型')} name="defaultLlm" rules={[{ required: true }]}>
<Select
className="w-INPUT_NORMAL"
placeholder={$t(PLACEHOLDER.select)}
loading={loading}
options={llmList?.map((x) => ({
value: x.id,
label: (
<div className="flex items-center gap-[10px]">
<span>{x.id}</span>
{x?.scopes?.map((s) => <Tag>{s?.toLocaleUpperCase()}</Tag>)}
</div>
)
}))}
></Select>
</Form.Item>
<Form.Item<AiSettingModalContentField> label={$t('参数')} name="config">
<Codebox
editorTheme="vs-dark"
readOnly={readOnly}
width="100%"
height="300px"
language="json"
enableToolbar={false}
/>
</Form.Item>
</Form>
)
})
export default AiSettingModalContent
@@ -1,9 +1,11 @@
import { Codebox } from '@common/components/postcat/api/Codebox'
import { BasicResponse, RESPONSE_TIPS, STATUS_CODE } from '@common/const/const'
import { useFetch } from '@common/hooks/http'
import { $t } from '@common/locales'
import { AIProvider } from '@core/components/AIProviderSelect'
import { DatePicker, Form, Input, Switch } from 'antd'
import { App, DatePicker, Form, Input, Switch } from 'antd'
import dayjs from 'dayjs'
import React, { useEffect, useState } from 'react'
import React, { forwardRef, useEffect, useImperativeHandle, useState } from 'react'
import { EditAPIKey } from '../types'
interface ApiKeyContentProps {
@@ -11,15 +13,19 @@ interface ApiKeyContentProps {
entity: EditAPIKey
}
const ApiKeyContent: React.FC<ApiKeyContentProps> = ({ provider, entity }) => {
const ApiKeyContent: React.FC<ApiKeyContentProps> = forwardRef(({ provider, entity }, ref) => {
const [form] = Form.useForm()
const [neverExpire, setNeverExpire] = useState(true)
const { fetchData } = useFetch()
const { message } = App.useApp()
useEffect(() => {
try {
const isNeverExpire = entity.expire_time === '0'
setNeverExpire(isNeverExpire)
form.setFieldsValue({
name: entity.name,
expire_time: entity.expire_time === '0' ? 0 : dayjs(entity.expire_time),
expire_time: isNeverExpire ? undefined : dayjs(entity.expire_time),
config: entity.config
})
} catch (e) {
@@ -29,23 +35,43 @@ const ApiKeyContent: React.FC<ApiKeyContentProps> = ({ provider, entity }) => {
config: ''
})
}
// setNeverExpire(entity.expire_time === '0')
}, [])
const handleOk = async () => {
try {
const values = await form.validateFields()
// onSave({
// ...values,
// expire_time: neverExpire ? null : values.expire_time.format('YYYY-MM-DD HH:mm:ss')
// })
const { expire_time, ...restValues } = values
const expireTime = neverExpire ? '0' : expire_time?.format('YYYY-MM-DD HH:mm:ss')
const [response, error] = await fetchData<BasicResponse<null>>('ai/resource/key', {
method: 'POST',
eoParams: { provider: provider?.id },
eoBody: { ...restValues, expire_time: expireTime },
eoTransformKeys: ['config']
})
if (error) {
console.error('API request failed:', error)
message.error($t(RESPONSE_TIPS.error))
throw error
}
const { code, msg } = response
if (code === STATUS_CODE.SUCCESS) {
message.success(msg || $t(RESPONSE_TIPS.success))
return true
} else {
message.error(msg || $t(RESPONSE_TIPS.error))
throw new Error(msg || $t(RESPONSE_TIPS.error))
}
} catch (error) {
console.error('Validation failed:', error)
}
}
useImperativeHandle(ref, () => ({
handleOk
}))
const handleNeverExpireChange = (checked: boolean) => {
console.log(checked)
setNeverExpire(checked)
if (!checked) {
form.setFieldsValue({
@@ -64,18 +90,17 @@ const ApiKeyContent: React.FC<ApiKeyContentProps> = ({ provider, entity }) => {
editorTheme="vs-dark"
readOnly={false}
width="100%"
height="300px"
height="150px"
language="json"
enableToolbar={false}
/>
</Form.Item>
<Form.Item label={$t('过期时间')} name="neverExpire" valuePropName="checked">
<div className="flex items-center">
<Switch onChange={handleNeverExpireChange} />
<Switch onChange={handleNeverExpireChange} checked={neverExpire} />
<span className="ml-2">{neverExpire ? $t('永不过期') : $t('设置过期时间')}</span>
</div>
</Form.Item>
neverExpire:{neverExpire}
{!neverExpire && (
<Form.Item
name="expire_time"
@@ -87,6 +112,6 @@ const ApiKeyContent: React.FC<ApiKeyContentProps> = ({ provider, entity }) => {
)}
</Form>
)
}
})
export default ApiKeyContent
@@ -4,8 +4,10 @@ import InsidePage from '@common/components/aoplatform/InsidePage'
import PageList, { PageProColumns } from '@common/components/aoplatform/PageList'
import TableBtnWithPermission from '@common/components/aoplatform/TableBtnWithPermission'
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 React, { useEffect, useRef, useState } from 'react'
@@ -22,6 +24,8 @@ const KeySettings: React.FC = () => {
const [searchWord, setSearchWord] = useState<string>('')
const [total, setTotal] = useState<number>(0)
const modalRef = useRef<any>()
const { accessData } = useGlobalContext()
useEffect(() => {
pageListRef.current?.reload()
}, [selectedProvider])
@@ -63,9 +67,10 @@ const KeySettings: React.FC = () => {
title: mode === 'add' ? $t(`添加 ${provider?.name} APIKey`) : $t('编辑 APIKey'),
content: <ApiKeyContent ref={modalRef} entity={newEntity} provider={provider} />,
onOk: () => {
return modalRef.current?.save().then((res) => {
// if (res === true) setAiConfigFlushed(true)
// getAiSettingList()
return modalRef.current?.handleOk().then((res) => {
if (res === true) {
pageListRef.current?.reload()
}
})
},
width: 600,
@@ -84,7 +89,7 @@ const KeySettings: React.FC = () => {
</a>
<div>
<CancelBtn />
{/* {checkAccess('system.devops.ai_provider.edit', accessData) ? <OkBtn /> : null} */}
{checkAccess('system.settings.ai_key_resource.manager', accessData) ? <OkBtn /> : null}
</div>
</div>
)