feat: Add parameter configuration functionality
This commit is contained in:
committed by
fit2cloud-chenyw
parent
9deca7e6b6
commit
e47e0e8ab8
@@ -5,7 +5,7 @@ from apps.dashboard.api import dashboard_api
|
||||
from apps.data_training.api import data_training
|
||||
from apps.datasource.api import datasource, table_relation, recommended_problem
|
||||
from apps.mcp import mcp
|
||||
from apps.system.api import login, user, aimodel, workspace, assistant
|
||||
from apps.system.api import login, user, aimodel, workspace, assistant, parameter
|
||||
from apps.terminology.api import terminology
|
||||
from apps.settings.api import base
|
||||
|
||||
@@ -23,5 +23,6 @@ api_router.include_router(chat.router)
|
||||
api_router.include_router(dashboard_api.router)
|
||||
api_router.include_router(mcp.router)
|
||||
api_router.include_router(table_relation.router)
|
||||
api_router.include_router(parameter.router)
|
||||
|
||||
api_router.include_router(recommended_problem.router)
|
||||
|
||||
17
backend/apps/system/api/parameter.py
Normal file
17
backend/apps/system/api/parameter.py
Normal file
@@ -0,0 +1,17 @@
|
||||
|
||||
from fastapi import APIRouter, Request
|
||||
from sqlbot_xpack.config.model import SysArgModel
|
||||
|
||||
|
||||
from apps.system.crud.parameter_manage import get_parameter_args, save_parameter_args
|
||||
from common.core.deps import SessionDep
|
||||
|
||||
router = APIRouter(tags=["system/parameter"], prefix="/system/parameter")
|
||||
|
||||
@router.get("")
|
||||
async def get_args(session: SessionDep) -> list[SysArgModel]:
|
||||
return await get_parameter_args(session)
|
||||
|
||||
@router.post("", )
|
||||
async def save_args(session: SessionDep, request: Request):
|
||||
return await save_parameter_args(session = session, request = request)
|
||||
41
backend/apps/system/crud/parameter_manage.py
Normal file
41
backend/apps/system/crud/parameter_manage.py
Normal file
@@ -0,0 +1,41 @@
|
||||
from fastapi import Request
|
||||
from sqlbot_xpack.config.arg_manage import get_group_args, save_group_args
|
||||
from sqlbot_xpack.config.model import SysArgModel
|
||||
import json
|
||||
from common.core.deps import SessionDep
|
||||
from sqlbot_xpack.file_utils import SQLBotFileUtils
|
||||
|
||||
async def get_parameter_args(session: SessionDep) -> list[SysArgModel]:
|
||||
group_args = await get_group_args(session=session)
|
||||
return [x for x in group_args if not x.pkey.startswith('appearance.')]
|
||||
|
||||
async def save_parameter_args(session: SessionDep, request: Request):
|
||||
allow_file_mapping = {
|
||||
""" "test_logo": { "types": [".jpg", ".jpeg", ".png", ".svg"], "size": 5 * 1024 * 1024 } """
|
||||
}
|
||||
form_data = await request.form()
|
||||
files = form_data.getlist("files")
|
||||
json_text = form_data.get("data")
|
||||
sys_args = [
|
||||
SysArgModel(**{**item, "pkey": f"{item['pkey']}"})
|
||||
for item in json.loads(json_text)
|
||||
if "pkey" in item
|
||||
]
|
||||
if not sys_args:
|
||||
return
|
||||
file_mapping = None
|
||||
if files:
|
||||
file_mapping = {}
|
||||
for file in files:
|
||||
origin_file_name = file.filename
|
||||
file_name, flag_name = SQLBotFileUtils.split_filename_and_flag(origin_file_name)
|
||||
file.filename = file_name
|
||||
allow_limit_obj = allow_file_mapping.get(flag_name)
|
||||
if allow_limit_obj:
|
||||
SQLBotFileUtils.check_file(file=file, file_types=allow_limit_obj.get("types"), limit_file_size=allow_limit_obj.get("size"))
|
||||
else:
|
||||
raise Exception(f'The file [{file_name}] is not allowed to be uploaded!')
|
||||
file_id = await SQLBotFileUtils.upload(file)
|
||||
file_mapping[f"{flag_name}"] = file_id
|
||||
|
||||
await save_group_args(session=session, sys_args=sys_args, file_mapping=file_mapping)
|
||||
@@ -14,13 +14,15 @@
|
||||
"rows_of_data": "Limit 1000 Rows of Data",
|
||||
"third_party_platform_settings": "Third-Party Platform Settings",
|
||||
"by_third_party_platform": "Automatic User Creation by Third-Party Platform",
|
||||
"platform_user_organization": "Third-Party Platform User Organization",
|
||||
"platform_user_organization": "Third-Party Platform Workspace",
|
||||
"platform_user_roles": "Third-Party Platform User Roles",
|
||||
"excessive_data_volume": "Disabling the 1000-row data limit may cause system lag due to excessive data volume.",
|
||||
"prompt": "Prompt",
|
||||
"disabling_successfully": "Disabling Successfully",
|
||||
"closed_by_default": "In the Question Count window, control whether the model thinking process is expanded or closed by default.",
|
||||
"and_platform_integration": "Scope includes authentication settings and platform integration."
|
||||
"and_platform_integration": "Scope includes authentication settings and platform integration.",
|
||||
"login_settings": "Login Settings",
|
||||
"default_login": "Default Login Method"
|
||||
},
|
||||
"prompt": {
|
||||
"default_password": "Default password:{msg}",
|
||||
@@ -816,4 +818,4 @@
|
||||
"modelType": {
|
||||
"llm": "Large Language Model"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,13 +14,15 @@
|
||||
"rows_of_data": "데이터 1,000행 제한",
|
||||
"third_party_platform_settings": "타사 플랫폼 설정",
|
||||
"by_third_party_platform": "타사 플랫폼에 의한 자동 사용자 생성",
|
||||
"platform_user_organization": "타사 플랫폼 사용자 구성",
|
||||
"platform_user_organization": "제3자 플랫폼 작업 공간",
|
||||
"platform_user_roles": "타사 플랫폼 사용자 역할",
|
||||
"excessive_data_volume": "1,000행 데이터 제한을 비활성화하면 과도한 데이터 양으로 인해 시스템 지연이 발생할 수 있습니다.",
|
||||
"prompt": "프롬프트",
|
||||
"disabling_successfully": "비활성화 완료",
|
||||
"closed_by_default": "질문 수 창에서 모델 사고 프로세스를 기본적으로 확장할지 또는 닫을지 여부를 제어합니다.",
|
||||
"and_platform_integration": "범위에는 인증 설정 및 플랫폼 통합이 포함됩니다."
|
||||
"and_platform_integration": "범위에는 인증 설정 및 플랫폼 통합이 포함됩니다.",
|
||||
"login_settings": "로그인 설정",
|
||||
"default_login": "기본 로그인 방식"
|
||||
},
|
||||
"prompt": {
|
||||
"default_password:": "기본 비밀번호:{msg}",
|
||||
@@ -816,4 +818,4 @@
|
||||
"modelType": {
|
||||
"llm": "대형 언어 모델"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,13 +14,15 @@
|
||||
"rows_of_data": "限制 1000 行数据",
|
||||
"third_party_platform_settings": "第三方平台设置",
|
||||
"by_third_party_platform": "第三方自动创建用户",
|
||||
"platform_user_organization": "第三方平台用户组织",
|
||||
"platform_user_organization": "第三方平台工作空间",
|
||||
"platform_user_roles": "第三方平台用户角色",
|
||||
"excessive_data_volume": "关闭1000行的数据限制后,数据量过大,可能会造成系统卡顿",
|
||||
"prompt": "提示",
|
||||
"disabling_successfully": "关闭成功",
|
||||
"closed_by_default": "在问数窗口中,控制模型思考过程默认展开或者关闭",
|
||||
"and_platform_integration": "作用域包括认证设置和平台对接"
|
||||
"and_platform_integration": "作用域包括认证设置和平台对接",
|
||||
"login_settings": "登录设置",
|
||||
"default_login": "默认登录方式"
|
||||
},
|
||||
"prompt": {
|
||||
"default_password": "默认密码:{msg}",
|
||||
@@ -817,4 +819,4 @@
|
||||
"modelType": {
|
||||
"llm": "大语言模型"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -95,6 +95,7 @@ export const routes = [
|
||||
},
|
||||
{
|
||||
path: '/set',
|
||||
name: 'set',
|
||||
component: LayoutDsl,
|
||||
redirect: '/set/member',
|
||||
meta: { title: t('workspace.set'), iconActive: 'set', iconDeActive: 'noSet' },
|
||||
@@ -145,6 +146,7 @@ export const routes = [
|
||||
},
|
||||
{
|
||||
path: '/system',
|
||||
name: 'system',
|
||||
component: LayoutDsl,
|
||||
redirect: '/system/user',
|
||||
meta: { hidden: true },
|
||||
|
||||
@@ -263,3 +263,22 @@ export const getSQLBotAddr = (portEnd?: boolean) => {
|
||||
}
|
||||
return addr.substring(0, addr.length - 1)
|
||||
}
|
||||
|
||||
export const formatArg = (text: string) => {
|
||||
if (!text) {
|
||||
return false
|
||||
}
|
||||
const mappingArray = ['true', 'false', '1', '0']
|
||||
const match = mappingArray.some((item: string) => {
|
||||
return item === text.toLocaleLowerCase()
|
||||
})
|
||||
if (!match) {
|
||||
return text
|
||||
}
|
||||
try {
|
||||
return JSON.parse(text)
|
||||
} catch (e: any) {
|
||||
console.warn(e)
|
||||
return text
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,23 +1,63 @@
|
||||
<script lang="ts" setup>
|
||||
import { ref, shallowRef } from 'vue'
|
||||
import { onMounted, provide, reactive, unref } from 'vue'
|
||||
import icon_info_outlined_1 from '@/assets/svg/icon_info_outlined_1.svg'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
||||
import PlatformParam from './xpack/PlatformParam.vue'
|
||||
import { request } from '@/utils/request'
|
||||
import { formatArg } from '@/utils/utils'
|
||||
const { t } = useI18n()
|
||||
|
||||
const chatSetting = ref({
|
||||
modelThinkingProcess: false,
|
||||
rows_of_data: false,
|
||||
const state = reactive({
|
||||
parameterForm: reactive<any>({
|
||||
'chat.enable_model_thinking': false,
|
||||
'chat.rows_of_data': false,
|
||||
}),
|
||||
})
|
||||
|
||||
const platform = ref({
|
||||
organization: false,
|
||||
modelThinkingProcess: false,
|
||||
roles: [],
|
||||
provide('parameterForm', state.parameterForm)
|
||||
const loadData = () => {
|
||||
request.get('/system/parameter').then((res: any) => {
|
||||
if (res) {
|
||||
res.forEach((item: any) => {
|
||||
if (
|
||||
item.pkey?.startsWith('chat') ||
|
||||
item.pkey?.startsWith('login') ||
|
||||
item.pkey?.startsWith('platform')
|
||||
) {
|
||||
state.parameterForm[item.pkey] = formatArg(item.pval)
|
||||
}
|
||||
})
|
||||
console.log(state.parameterForm)
|
||||
}
|
||||
})
|
||||
}
|
||||
const buildParam = () => {
|
||||
const changedItemArray = Object.keys(state.parameterForm).map((key: string) => {
|
||||
return {
|
||||
pkey: key,
|
||||
pval: Object.prototype.hasOwnProperty.call(state.parameterForm, 'key')
|
||||
? state.parameterForm[key].toString()
|
||||
: state.parameterForm[key],
|
||||
}
|
||||
})
|
||||
const formData = new FormData()
|
||||
formData.append('data', JSON.stringify(unref(changedItemArray)))
|
||||
return formData
|
||||
}
|
||||
const saveHandler = () => {
|
||||
const param = buildParam()
|
||||
request
|
||||
.post('/system/parameter', param, {
|
||||
headers: {
|
||||
'Content-Type': 'multipart/form-data',
|
||||
},
|
||||
})
|
||||
.then(() => {
|
||||
ElMessage.success(t('common.save_success'))
|
||||
})
|
||||
}
|
||||
onMounted(() => {
|
||||
loadData()
|
||||
})
|
||||
|
||||
const organizations = shallowRef<any[]>([])
|
||||
const roles = shallowRef<any[]>([])
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -41,7 +81,7 @@ const roles = shallowRef<any[]>([])
|
||||
</el-tooltip>
|
||||
</div>
|
||||
<div class="value">
|
||||
<el-switch v-model="chatSetting.modelThinkingProcess" />
|
||||
<el-switch v-model="state.parameterForm['chat.enable_model_thinking']" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -59,77 +99,15 @@ const roles = shallowRef<any[]>([])
|
||||
</el-tooltip>
|
||||
</div>
|
||||
<div class="value">
|
||||
<el-switch v-model="chatSetting.rows_of_data" />
|
||||
<el-switch v-model="state.parameterForm['chat.rows_of_data']" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-title">
|
||||
{{ t('parameter.third_party_platform_settings') }}
|
||||
</div>
|
||||
<div class="card-item" style="width: 100%">
|
||||
<div class="label">
|
||||
{{ t('parameter.by_third_party_platform') }}
|
||||
</div>
|
||||
<div class="value">
|
||||
<el-switch v-model="platform.modelThinkingProcess" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-item">
|
||||
<div class="label">
|
||||
{{ t('parameter.platform_user_organization') }}
|
||||
<span class="require"></span>
|
||||
<el-tooltip
|
||||
effect="dark"
|
||||
:content="t('parameter.and_platform_integration')"
|
||||
placement="top"
|
||||
>
|
||||
<el-icon size="16">
|
||||
<icon_info_outlined_1></icon_info_outlined_1>
|
||||
</el-icon>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
<div class="value">
|
||||
<el-select filterable v-model="platform.organization">
|
||||
<el-option
|
||||
v-for="item in organizations"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
/>
|
||||
</el-select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-item" style="margin-left: 16px">
|
||||
<div class="label">
|
||||
{{ t('parameter.platform_user_roles') }}
|
||||
<span class="require"></span>
|
||||
<el-tooltip
|
||||
effect="dark"
|
||||
:content="t('parameter.and_platform_integration')"
|
||||
placement="top"
|
||||
>
|
||||
<el-icon size="16">
|
||||
<icon_info_outlined_1></icon_info_outlined_1>
|
||||
</el-icon>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
<div class="value">
|
||||
<el-select multiple filterable v-model="platform.roles">
|
||||
<el-option
|
||||
v-for="item in roles"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
/>
|
||||
</el-select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<platform-param />
|
||||
</div>
|
||||
<div class="save" style="margin-top: 16px">
|
||||
<el-button type="primary">{{ t('common.save') }}</el-button>
|
||||
<el-button type="primary" @click="saveHandler">{{ t('common.save') }}</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
210
frontend/src/views/system/parameter/xpack/PlatformParam.vue
Normal file
210
frontend/src/views/system/parameter/xpack/PlatformParam.vue
Normal file
@@ -0,0 +1,210 @@
|
||||
<template>
|
||||
<div v-if="xpackValid" class="card">
|
||||
<div class="card-title">
|
||||
{{ t('parameter.third_party_platform_settings') }}
|
||||
</div>
|
||||
<div class="card-item" style="width: 100%">
|
||||
<div class="label">
|
||||
{{ t('parameter.by_third_party_platform') }}
|
||||
</div>
|
||||
<div class="value">
|
||||
<el-switch v-model="formData['platform.auto_create']" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-item">
|
||||
<div class="label">
|
||||
{{ t('parameter.platform_user_organization') }}
|
||||
<span class="require"></span>
|
||||
<el-tooltip
|
||||
effect="dark"
|
||||
:content="t('parameter.and_platform_integration')"
|
||||
placement="top"
|
||||
>
|
||||
<el-icon size="16">
|
||||
<icon_info_outlined_1></icon_info_outlined_1>
|
||||
</el-icon>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
<div class="value">
|
||||
<el-select v-model="formData['platform.oid']" filterable>
|
||||
<el-option
|
||||
v-for="item in organizations"
|
||||
:key="item.id"
|
||||
:label="item.name"
|
||||
:value="item.id"
|
||||
/>
|
||||
</el-select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-item" style="margin-left: 16px">
|
||||
<div class="label">
|
||||
{{ t('workspace.member_type') }}
|
||||
<span class="require"></span>
|
||||
<el-tooltip
|
||||
effect="dark"
|
||||
:content="t('parameter.and_platform_integration')"
|
||||
placement="top"
|
||||
>
|
||||
<el-icon size="16">
|
||||
<icon_info_outlined_1></icon_info_outlined_1>
|
||||
</el-icon>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
<div class="value">
|
||||
<el-select v-model="formData['platform.rid']" filterable>
|
||||
<el-option
|
||||
v-for="item in roles"
|
||||
:key="item.value"
|
||||
:label="item.name"
|
||||
:value="item.value"
|
||||
/>
|
||||
</el-select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="anyPlatformEnable" class="card">
|
||||
<div class="card-title">
|
||||
{{ t('parameter.login_settings') }}
|
||||
</div>
|
||||
<div class="card-item" style="width: 100%">
|
||||
<div class="label">
|
||||
{{ t('parameter.default_login') }}
|
||||
</div>
|
||||
<div class="value">
|
||||
<el-radio-group v-model="formData['login.default_login']">
|
||||
<el-radio v-for="item in loginTypeOptions" :key="item.value" :label="item.value">{{
|
||||
item.label
|
||||
}}</el-radio>
|
||||
</el-radio-group>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { inject, onMounted, reactive, ref, shallowRef } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { workspaceList } from '@/api/workspace'
|
||||
import { request } from '@/utils/request'
|
||||
import icon_info_outlined_1 from '@/assets/svg/icon_info_outlined_1.svg'
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
const anyPlatformEnable = ref(false)
|
||||
const defaultForm = reactive<Record<string, any>>({
|
||||
'platform.auto_create': false,
|
||||
'platform.oid': 1,
|
||||
'platform.rid': 1,
|
||||
'login.default_login': 0,
|
||||
})
|
||||
|
||||
const loginTypeOptions = shallowRef<any[]>([{ value: 0, label: t('login.account_login') }])
|
||||
|
||||
const formData = inject<Record<string, any>>('parameterForm', {})
|
||||
|
||||
const xpackValid = ref(false)
|
||||
|
||||
const organizations = shallowRef<any[]>([])
|
||||
const roles = [
|
||||
{
|
||||
name: t('workspace.administrator'),
|
||||
value: 1,
|
||||
},
|
||||
{
|
||||
name: t('workspace.ordinary_member'),
|
||||
value: 0,
|
||||
},
|
||||
]
|
||||
const platformMapping = {
|
||||
cas: { value: 1, label: 'CAS' },
|
||||
oidc: { value: 2, label: 'OIDC' },
|
||||
ldap: { value: 3, label: 'LDAP' },
|
||||
oauth2: { value: 4, label: 'Oauth2' },
|
||||
saml2: { value: 5, label: 'Saml2' },
|
||||
} as any
|
||||
const setDefaultForm = () => {
|
||||
for (const key in defaultForm) {
|
||||
if (formData[key] === undefined) {
|
||||
formData[key] = defaultForm[key]
|
||||
}
|
||||
}
|
||||
}
|
||||
const queryCategoryStatus = () => {
|
||||
const url = `/system/authentication/platform/status`
|
||||
return request.get(url)
|
||||
}
|
||||
onMounted(async () => {
|
||||
// eslint-disable-next-line no-undef
|
||||
const obj = LicenseGenerator.getLicense()
|
||||
if (obj?.status !== 'valid') {
|
||||
xpackValid.value = false
|
||||
return
|
||||
}
|
||||
const wsRes: any = await workspaceList()
|
||||
organizations.value = wsRes
|
||||
const platformStatusRes: any = await queryCategoryStatus()
|
||||
platformStatusRes.forEach((item: any) => {
|
||||
if (item.enable) {
|
||||
loginTypeOptions.value.push(platformMapping[item.name])
|
||||
anyPlatformEnable.value = true
|
||||
}
|
||||
})
|
||||
if (
|
||||
!formData['login.default_login'] ||
|
||||
!loginTypeOptions.value.some(
|
||||
(option: any) => parseInt(formData['login.default_login']) === option.value
|
||||
)
|
||||
) {
|
||||
formData['login.default_login'] = 0
|
||||
}
|
||||
formData['login.default_login'] = parseInt(formData['login.default_login'])
|
||||
setDefaultForm()
|
||||
xpackValid.value = true
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.card {
|
||||
width: 100%;
|
||||
border-radius: 12px;
|
||||
padding: 16px;
|
||||
border: 1px solid #dee0e3;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
margin-top: 16px;
|
||||
.card-title {
|
||||
font-weight: 500;
|
||||
font-style: Medium;
|
||||
font-size: 16px;
|
||||
line-height: 24px;
|
||||
width: 100%;
|
||||
}
|
||||
.card-item {
|
||||
margin-top: 16px;
|
||||
width: calc(50% - 8px);
|
||||
.label {
|
||||
font-weight: 400;
|
||||
font-size: 14px;
|
||||
line-height: 22px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.ed-icon {
|
||||
margin-left: 4px;
|
||||
}
|
||||
|
||||
.require::after {
|
||||
content: '*';
|
||||
color: var(--ed-color-danger);
|
||||
margin-left: 4px;
|
||||
}
|
||||
}
|
||||
|
||||
.value {
|
||||
margin-top: 8px;
|
||||
line-height: 20px;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user