mirror of
https://gh-proxy.org/https://github.com/klinsmaya/SQLBot.git
synced 2026-11-08 01:06:59 +08:00
perf: Set default model
This commit is contained in:
@@ -2,7 +2,7 @@ import json
|
||||
from typing import List, Union
|
||||
from apps.system.schemas.ai_model_schema import AiModelConfigItem, AiModelCreator, AiModelEditor, AiModelGridItem
|
||||
from fastapi import APIRouter, Query
|
||||
from sqlmodel import func, select
|
||||
from sqlmodel import func, select, update
|
||||
|
||||
from apps.system.models.system_model import AiModelDetail
|
||||
from common.core.deps import SessionDep
|
||||
@@ -23,7 +23,7 @@ async def query(
|
||||
AiModelDetail.default_model)
|
||||
if keyword is not None:
|
||||
statement = statement.where(AiModelDetail.name.like(f"%{keyword}%"))
|
||||
|
||||
statement = statement.order_by(AiModelDetail.create_time.asc())
|
||||
items = session.exec(statement).all()
|
||||
return items
|
||||
|
||||
@@ -85,6 +85,27 @@ async def delete_model(
|
||||
id: int
|
||||
):
|
||||
item = session.get(AiModelDetail, id)
|
||||
if item.default_model:
|
||||
raise RuntimeError(f"Can not delete [${item.name}], because it is default model!")
|
||||
session.delete(item)
|
||||
session.commit()
|
||||
|
||||
@router.put("/default/{id}")
|
||||
async def set_default(session: SessionDep, id: int):
|
||||
db_model = session.get(AiModelDetail, id)
|
||||
if not db_model:
|
||||
raise ValueError(f"AiModelDetail with id {id} not found")
|
||||
if db_model.default_model:
|
||||
return
|
||||
|
||||
try:
|
||||
session.exec(
|
||||
update(AiModelDetail).values(default_model=False)
|
||||
)
|
||||
db_model.default_model = True
|
||||
session.add(db_model)
|
||||
session.commit()
|
||||
except Exception as e:
|
||||
session.rollback()
|
||||
raise e
|
||||
|
||||
@@ -7,5 +7,5 @@ export const modelApi = {
|
||||
edit: (data: any) => request.put('/system/aimodel', data),
|
||||
delete: (id: number) => request.delete(`/system/aimodel/${id}`),
|
||||
query: (id: number) => request.get(`/system/aimodel/${id}`),
|
||||
status: (data: any) => request.patch('/system/aimodel/status', data),
|
||||
setDefault: (id: number) => request.put(`/system/aimodel/default/${id}`),
|
||||
}
|
||||
|
||||
@@ -9,7 +9,6 @@ import Datasource from '@/views/ds/Datasource.vue'
|
||||
import DashboardEditor from '@/views/dashboard/editor/index.vue'
|
||||
import DashboardPreview from '@//views/dashboard/preview/SQPreviewSingle.vue'
|
||||
import Dashboard from '@/views/dashboard/index.vue'
|
||||
// import Model from '@/views/system/model/index.vue'
|
||||
import Model from '@/views/system/model/Model.vue'
|
||||
// import User from "@/views/system/user/index.vue";
|
||||
import { watchRouter } from './watch'
|
||||
|
||||
@@ -59,14 +59,12 @@ const handleDel = () => {
|
||||
<edit></edit>
|
||||
</el-icon>
|
||||
</el-tooltip>
|
||||
<template v-if="!isDefault">
|
||||
<span class="divide"></span>
|
||||
<el-tooltip :offset="14" effect="dark" :content="$t('dashboard.delete')" placement="top">
|
||||
<el-icon size="16" @click="handleDel">
|
||||
<delIcon></delIcon>
|
||||
</el-icon>
|
||||
</el-tooltip>
|
||||
</template>
|
||||
<span class="divide"></span>
|
||||
<el-tooltip :offset="14" effect="dark" :content="$t('dashboard.delete')" placement="top">
|
||||
<el-icon size="16" @click="handleDel">
|
||||
<delIcon></delIcon>
|
||||
</el-icon>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -69,6 +69,10 @@ const defaultModelListWithSearch = computed(() => {
|
||||
})
|
||||
|
||||
const handleDefaultModelChange = (item: any) => {
|
||||
const current_default_node = modelList.value.find((ele: Model) => ele.default_model)
|
||||
if (current_default_node?.id === item.id) {
|
||||
return
|
||||
}
|
||||
ElMessageBox.confirm(`是否设置 ${item.name} 为系统默认模型?`, {
|
||||
confirmButtonType: 'primary',
|
||||
tip: '系统默认模型被替换后,智能问数的结果将会受到影响,请谨慎操作。',
|
||||
@@ -78,10 +82,10 @@ const handleDefaultModelChange = (item: any) => {
|
||||
autofocus: false,
|
||||
callback: (val: string) => {
|
||||
if (val === 'confirm') {
|
||||
modelList.value.forEach((ele: any) => {
|
||||
ele.default_model = ele.id === item.id
|
||||
modelApi.setDefault(item.id).then(() => {
|
||||
ElMessage.success('设置成功')
|
||||
search()
|
||||
})
|
||||
ElMessage.success('设置成功')
|
||||
}
|
||||
},
|
||||
})
|
||||
|
||||
@@ -1,538 +0,0 @@
|
||||
<template>
|
||||
<div class="sqlbot-table-container professional-container">
|
||||
<div class="sqlbot-tool">
|
||||
<div class="tool-left">
|
||||
<el-input
|
||||
v-model="keyword"
|
||||
class="sqlbot-search"
|
||||
placeholder="Search"
|
||||
@keyup.enter="handleSearch"
|
||||
>
|
||||
<template #prefix>
|
||||
<el-icon class="el-input__icon"><IconSearch /></el-icon>
|
||||
</template>
|
||||
</el-input>
|
||||
</div>
|
||||
<div class="tool-right">
|
||||
<div class="tool-btn secondary-btn" @click="batchEnableHandler">
|
||||
<el-icon><IconEnable /></el-icon>
|
||||
<span>Batch Enable</span>
|
||||
</div>
|
||||
<div class="tool-btn secondary-btn" @click="batchDisableHandler">
|
||||
<el-icon><IconDisable /></el-icon>
|
||||
<span>Batch Disable</span>
|
||||
</div>
|
||||
<div class="tool-btn primary-btn" @click="openDialog">
|
||||
<el-icon><IconOpeAdd /></el-icon>
|
||||
<span>Add Model</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sqlbot-table">
|
||||
<el-table
|
||||
:data="state.tableData"
|
||||
style="width: 100%"
|
||||
row-key="id"
|
||||
@selection-change="handleSelectionChange"
|
||||
>
|
||||
<el-table-column type="selection" width="55" />
|
||||
<el-table-column prop="name" label="Model Name" width="280" />
|
||||
<el-table-column prop="type" label="Model Type" width="280">
|
||||
<template #default="scope">
|
||||
<span>{{ getModelTypeName(scope.row.type) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="api_key" label="API KEY" width="280">
|
||||
<template #default="scope">
|
||||
<div class="user-status-container" :class="[scope.row.api_key ? 'active' : 'disabled']">
|
||||
<span>{{ scope.row.api_key ? 'Configured' : 'Not Configured' }}</span>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="status" label="Status" width="180">
|
||||
<template #default="scope">
|
||||
<div class="user-status-container" :class="[scope.row.status ? 'active' : 'disabled']">
|
||||
<el-icon>
|
||||
<SuccessFilled v-if="scope.row.status" />
|
||||
<CircleCloseFilled v-else />
|
||||
</el-icon>
|
||||
<span>{{ scope.row.status ? 'Enabled' : 'Disabled' }}</span>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="create_time" label="Create time" min-width="180">
|
||||
<template #default="scope">
|
||||
<span>{{ formatTimestamp(scope.row.create_time, 'YYYY-MM-DD HH:mm:ss') }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="id" label="Actions" width="120" fixed="right">
|
||||
<template #default="scope">
|
||||
<div class="table-operate">
|
||||
<div class="opt-btn" @click="editHandler(scope.row.id)">
|
||||
<el-icon><IconOpeEdit /></el-icon>
|
||||
</div>
|
||||
<div class="opt-btn" @click="deleteHandler(scope.row.id)">
|
||||
<el-icon><IconOpeDelete /></el-icon>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<div class="pagination-container">
|
||||
<el-pagination
|
||||
v-model:current-page="state.pageInfo.currentPage"
|
||||
v-model:page-size="state.pageInfo.pageSize"
|
||||
:page-sizes="[10, 20, 30]"
|
||||
:background="true"
|
||||
:pager-count="5"
|
||||
layout="total, sizes, prev, pager, next, jumper"
|
||||
:total="state.pageInfo.total"
|
||||
@size-change="handleSizeChange"
|
||||
@current-change="handleCurrentChange"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<el-dialog
|
||||
v-model="dialogFormVisible"
|
||||
title="Add Ai Model"
|
||||
destroy-on-close
|
||||
width="500"
|
||||
class="sqlbot-dialog"
|
||||
@close="onFormClose"
|
||||
>
|
||||
<el-form
|
||||
ref="termFormRef"
|
||||
:model="state.form"
|
||||
label-width="180px"
|
||||
label-position="top"
|
||||
class="sqlbot-form"
|
||||
size="large"
|
||||
>
|
||||
<el-form-item label="Model Name">
|
||||
<el-input v-model="state.form.name" autocomplete="off" />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="Model Type">
|
||||
<el-select v-model="state.form.type" placeholder="Please select a type">
|
||||
<el-option
|
||||
v-for="item in modelTypeOptions"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
/>
|
||||
<!-- <el-option label="domain1" value="0" />
|
||||
<el-option label="domain2" value="1" />
|
||||
<el-option label="domain3" value="2" /> -->
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="API KEY">
|
||||
<el-input
|
||||
v-model="state.form.api_key"
|
||||
type="password"
|
||||
show-password
|
||||
placeholder="Please input password"
|
||||
/>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="API Endpoint URL">
|
||||
<el-input v-model="state.form.endpoint" placeholder="Please input endpoint" />
|
||||
</el-form-item>
|
||||
|
||||
<!-- <el-form-item label="Maximum Context Window" >
|
||||
<el-input v-model="state.form.max_context_window" type="number" placeholder="Please input max_context_window" />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="Temperature" >
|
||||
<el-slider v-model="state.form.temperature" :format-tooltip="formatTooltip" />
|
||||
</el-form-item>-->
|
||||
|
||||
<el-form-item label="Model Status">
|
||||
<el-checkbox v-model="state.form.status" label="" size="large" />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="Description">
|
||||
<el-input v-model="state.form.description" type="textarea" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button @click="closeForm">Cancel</el-button>
|
||||
<el-button type="primary" @click="saveHandler"> save </el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, reactive, onMounted } from 'vue'
|
||||
import IconEnable from '@/assets/svg/operate/ope-enable.svg'
|
||||
import IconDisable from '@/assets/svg/operate/ope-disable.svg'
|
||||
import IconOpeAdd from '@/assets/svg/operate/ope-add.svg'
|
||||
|
||||
import { SuccessFilled, CircleCloseFilled, Search as IconSearch } from '@element-plus/icons-vue'
|
||||
import IconOpeEdit from '@/assets/svg/operate/ope-edit.svg'
|
||||
import IconOpeDelete from '@/assets/svg/operate/ope-delete.svg'
|
||||
import { modelApi } from '@/api/system'
|
||||
import { formatTimestamp } from '@/utils/date'
|
||||
import { modelTypeOptions, getModelTypeName } from '@/entity/CommonEntity.ts'
|
||||
const keyword = ref('')
|
||||
const dialogFormVisible = ref(false)
|
||||
const termFormRef = ref()
|
||||
const state = reactive({
|
||||
tableData: [],
|
||||
form: {
|
||||
id: '',
|
||||
name: '',
|
||||
type: 0,
|
||||
api_key: '',
|
||||
endpoint: '',
|
||||
max_context_window: 0,
|
||||
temperature: 0,
|
||||
status: false,
|
||||
description: '',
|
||||
},
|
||||
pageInfo: {
|
||||
currentPage: 1,
|
||||
pageSize: 20,
|
||||
total: 0,
|
||||
},
|
||||
selectedIds: [],
|
||||
})
|
||||
onMounted(() => {
|
||||
search()
|
||||
})
|
||||
const handleSearch = (e: any) => {
|
||||
console.log('search', e)
|
||||
}
|
||||
const editHandler = (id: any) => {
|
||||
modelApi.query(id).then((res: any) => {
|
||||
state.form = res
|
||||
state.form.temperature = state.form.temperature * 100
|
||||
dialogFormVisible.value = true
|
||||
})
|
||||
}
|
||||
const deleteHandler = (id: any) => {
|
||||
ElMessageBox.confirm('Are you sure to delete?', 'Warning', {
|
||||
confirmButtonText: 'OK',
|
||||
cancelButtonText: 'Cancel',
|
||||
type: 'warning',
|
||||
})
|
||||
.then(() => {
|
||||
modelApi.delete(id).then(() => {
|
||||
ElMessage({
|
||||
type: 'success',
|
||||
message: 'Delete completed',
|
||||
})
|
||||
search()
|
||||
})
|
||||
})
|
||||
.catch(() => {
|
||||
ElMessage({
|
||||
type: 'info',
|
||||
message: 'Delete canceled',
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
const openDialog = () => {
|
||||
dialogFormVisible.value = true
|
||||
}
|
||||
const resetForm = () => {
|
||||
if (!termFormRef.value) return
|
||||
state.form = {
|
||||
id: '',
|
||||
name: '',
|
||||
type: 0,
|
||||
api_key: '',
|
||||
endpoint: '',
|
||||
max_context_window: 0,
|
||||
temperature: 0,
|
||||
status: false,
|
||||
description: '',
|
||||
}
|
||||
}
|
||||
|
||||
const closeForm = () => {
|
||||
dialogFormVisible.value = false
|
||||
}
|
||||
const onFormClose = () => {
|
||||
resetForm()
|
||||
dialogFormVisible.value = false
|
||||
}
|
||||
|
||||
const search = () => {
|
||||
/* modelApi.pager(state.pageInfo.currentPage, state.pageInfo.pageSize).then((res: any) => {
|
||||
state.tableData = res.items
|
||||
state.pageInfo.total = res.total
|
||||
}) */
|
||||
}
|
||||
const addTerm = () => {
|
||||
const param: any = {
|
||||
...state.form,
|
||||
}
|
||||
delete param.id
|
||||
param.temperature = param.temperature / 100
|
||||
modelApi.add(param).then(() => {
|
||||
dialogFormVisible.value = false
|
||||
search()
|
||||
ElMessage({
|
||||
type: 'success',
|
||||
message: 'Add completed',
|
||||
})
|
||||
})
|
||||
}
|
||||
const editTerm = () => {
|
||||
const param = state.form
|
||||
param.temperature = param.temperature / 100
|
||||
modelApi.edit(state.form).then((res: any) => {
|
||||
console.log('edit term', res)
|
||||
dialogFormVisible.value = false
|
||||
search()
|
||||
ElMessage({
|
||||
type: 'success',
|
||||
message: 'Edit completed',
|
||||
})
|
||||
})
|
||||
}
|
||||
const saveHandler = () => {
|
||||
if (state.form.id) {
|
||||
editTerm()
|
||||
} else {
|
||||
addTerm()
|
||||
}
|
||||
}
|
||||
|
||||
const handleSizeChange = (val: number) => {
|
||||
state.pageInfo.pageSize = val
|
||||
search()
|
||||
}
|
||||
const handleCurrentChange = (val: number) => {
|
||||
state.pageInfo.currentPage = val
|
||||
search()
|
||||
}
|
||||
const handleSelectionChange = (rows: any) => {
|
||||
state.selectedIds = rows.map((item: any) => item.id)
|
||||
}
|
||||
|
||||
const batchDisableHandler = () => {
|
||||
if (state.selectedIds.length === 0) {
|
||||
ElMessage({
|
||||
type: 'warning',
|
||||
message: 'Please select at least one item',
|
||||
})
|
||||
return
|
||||
}
|
||||
const param = {
|
||||
ids: state.selectedIds,
|
||||
status: false,
|
||||
}
|
||||
|
||||
ElMessageBox.confirm('Are you sure to execute this operation?', 'Warning', {
|
||||
confirmButtonText: 'OK',
|
||||
cancelButtonText: 'Cancel',
|
||||
type: 'warning',
|
||||
})
|
||||
.then(() => {
|
||||
modelApi.status(param).then(() => {
|
||||
search()
|
||||
ElMessage({
|
||||
type: 'success',
|
||||
message: 'Batch disable completed',
|
||||
})
|
||||
})
|
||||
})
|
||||
.catch(() => {
|
||||
ElMessage({
|
||||
type: 'info',
|
||||
message: 'Execute canceled',
|
||||
})
|
||||
})
|
||||
}
|
||||
const batchEnableHandler = () => {
|
||||
if (state.selectedIds.length === 0) {
|
||||
ElMessage({
|
||||
type: 'warning',
|
||||
message: 'Please select at least one item',
|
||||
})
|
||||
return
|
||||
}
|
||||
const param = {
|
||||
ids: state.selectedIds,
|
||||
status: true,
|
||||
}
|
||||
modelApi.status(param).then(() => {
|
||||
search()
|
||||
ElMessage({
|
||||
type: 'success',
|
||||
message: 'Batch enable completed',
|
||||
})
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.sqlbot-table-container {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
.sqlbot-tool {
|
||||
height: 42px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
background-color: var(--white);
|
||||
padding: 16px 20px;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
|
||||
margin-bottom: 20px;
|
||||
.tool-left {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
.sqlbot-search {
|
||||
width: 300px;
|
||||
--el-input-inner-height: 40px !important;
|
||||
--el-input-border-radius: 24px;
|
||||
--el-input-bg-color: #f5f7fa;
|
||||
--el-input-border-color: #e5e8ed;
|
||||
}
|
||||
}
|
||||
.tool-right {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
column-gap: 12px;
|
||||
height: 35px;
|
||||
.secondary-btn {
|
||||
background: #f5f5f5;
|
||||
color: #333;
|
||||
border: 1px solid #ddd;
|
||||
}
|
||||
.primary-btn {
|
||||
background: var(--el-color-primary);
|
||||
color: white;
|
||||
}
|
||||
.tool-btn {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 6px 16px;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
outline: none;
|
||||
font-weight: 500;
|
||||
transition: all 0.3s;
|
||||
font-size: 14px;
|
||||
gap: 6px;
|
||||
&:hover {
|
||||
background-color: #f1f3f4;
|
||||
}
|
||||
&.default {
|
||||
color: #2d2e31;
|
||||
font-weight: 500;
|
||||
font-size: 14px;
|
||||
i {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.sqlbot-table {
|
||||
border-radius: 6px;
|
||||
:deep(.ed-table) {
|
||||
--el-table-header-bg-color: #f5f7fa;
|
||||
--el-table-border-color: #ebeef5;
|
||||
--el-table-header-text-color: #606266;
|
||||
|
||||
th {
|
||||
font-weight: 600;
|
||||
height: 48px;
|
||||
}
|
||||
|
||||
td {
|
||||
height: 52px;
|
||||
}
|
||||
/* border-left: 1px solid var(--el-table-border-color);
|
||||
border-right: 1px solid var(--el-table-border-color); */
|
||||
}
|
||||
.table-operate {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
gap: 8px;
|
||||
|
||||
.opt-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: #5f6368;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
background-color: #f1f3f4;
|
||||
border: none;
|
||||
font-size: 18px;
|
||||
&:hover {
|
||||
background-color: #e8f0fe;
|
||||
color: var(--el-color-primary);
|
||||
}
|
||||
}
|
||||
}
|
||||
.pagination-container {
|
||||
display: flex;
|
||||
justify-content: end;
|
||||
align-items: center;
|
||||
margin-top: 20px;
|
||||
background-color: var(--white);
|
||||
padding: 16px 20px;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
}
|
||||
}
|
||||
.sqlbot-form {
|
||||
width: 100%;
|
||||
.ed-form-item {
|
||||
margin-bottom: 16px;
|
||||
text-align: left;
|
||||
label {
|
||||
font-size: 14px;
|
||||
color: #606266;
|
||||
}
|
||||
}
|
||||
}
|
||||
.sqlbot-dialog {
|
||||
width: 500px;
|
||||
|
||||
:deep(.ed-dialog__header) {
|
||||
text-align: left !important;
|
||||
font-size: 16px;
|
||||
background-color: #f5f7fa;
|
||||
border-bottom: 1px solid #ebeef5;
|
||||
color: #606266;
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
.user-status-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
column-gap: 8px;
|
||||
font-size: 14px;
|
||||
color: #606266;
|
||||
padding: 4px 8px;
|
||||
width: fit-content;
|
||||
border-radius: 4px;
|
||||
&.active {
|
||||
background-color: #e6f7ee;
|
||||
color: #389e0d;
|
||||
}
|
||||
&.disabled {
|
||||
background-color: #fff1f0;
|
||||
color: #cf1322;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user