mirror of
https://gh-proxy.org/https://github.com/klinsmaya/SQLBot.git
synced 2026-11-08 01:06:59 +08:00
feat:Recommended questions support user configuration. (#487)
This commit is contained in:
@@ -0,0 +1,38 @@
|
||||
"""empty message
|
||||
|
||||
Revision ID: e408f8766753
|
||||
Revises: cb12c4238120
|
||||
Create Date: 2025-11-24 17:34:04.436927
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
import sqlmodel.sql.sqltypes
|
||||
from sqlalchemy.dialects import postgresql
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = 'e408f8766753'
|
||||
down_revision = 'cb12c4238120'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
op.create_table('ds_recommended_problem',
|
||||
sa.Column('id', sa.BIGINT(),
|
||||
sa.Identity(always=True, start=1, increment=1, minvalue=1, maxvalue=9999999999,
|
||||
cycle=False, cache=1), autoincrement=True, nullable=False),
|
||||
sa.Column('datasource_id', sa.BIGINT(), autoincrement=False, nullable=True),
|
||||
sa.Column('question', sa.TEXT(), autoincrement=False, nullable=True),
|
||||
sa.Column('remark', sa.TEXT(), autoincrement=False, nullable=True),
|
||||
sa.Column('sort', sa.BIGINT(), autoincrement=False, nullable=True),
|
||||
sa.Column('create_time', postgresql.TIMESTAMP(precision=6), autoincrement=False, nullable=True),
|
||||
sa.Column('create_by', sa.BIGINT(), autoincrement=False, nullable=True),
|
||||
sa.PrimaryKeyConstraint('id', name=op.f('ds_recommended_problem_pkey'))
|
||||
)
|
||||
op.add_column('core_datasource', sa.Column('recommended_config', sa.BigInteger(),default=0, nullable=True))
|
||||
|
||||
def downgrade():
|
||||
op.drop_table('ds_recommended_problem')
|
||||
op.drop_column('core_datasource', 'recommended_config')
|
||||
|
||||
+3
-1
@@ -3,7 +3,7 @@ from fastapi import APIRouter
|
||||
from apps.chat.api import chat
|
||||
from apps.dashboard.api import dashboard_api
|
||||
from apps.data_training.api import data_training
|
||||
from apps.datasource.api import datasource, table_relation
|
||||
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.terminology.api import terminology
|
||||
@@ -23,3 +23,5 @@ 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(recommended_problem.router)
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
from fastapi import APIRouter
|
||||
|
||||
from apps.datasource.crud.recommended_problem import get_datasource_recommended
|
||||
from common.core.deps import SessionDep
|
||||
|
||||
router = APIRouter(tags=["recommended_problem"], prefix="/recommended_problem")
|
||||
|
||||
|
||||
@router.get("/get_datasource_recommended/{ds_id}")
|
||||
async def datasource_recommended(session: SessionDep, ds_id: int):
|
||||
return get_datasource_recommended(session, ds_id)
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
from sqlmodel import select
|
||||
|
||||
from common.core.deps import SessionDep
|
||||
from ..models.datasource import DsRecommendedProblem
|
||||
|
||||
|
||||
def get_datasource_recommended(session: SessionDep, ds_id: int):
|
||||
statement = select(DsRecommendedProblem).where(DsRecommendedProblem.datasource_id == ds_id)
|
||||
dsRecommendedProblem = session.exec(statement)
|
||||
return dsRecommendedProblem
|
||||
|
||||
@@ -22,6 +22,7 @@ class CoreDatasource(SQLModel, table=True):
|
||||
oid: int = Field(sa_column=Column(BigInteger()))
|
||||
table_relation: List = Field(sa_column=Column(JSONB, nullable=True))
|
||||
embedding: str = Field(sa_column=Column(Text, nullable=True))
|
||||
recommended_config: int = Field(sa_column=Column(BigInteger()))
|
||||
|
||||
|
||||
class CoreTable(SQLModel, table=True):
|
||||
@@ -34,6 +35,16 @@ class CoreTable(SQLModel, table=True):
|
||||
custom_comment: str = Field(sa_column=Column(Text))
|
||||
embedding: str = Field(sa_column=Column(Text, nullable=True))
|
||||
|
||||
class DsRecommendedProblem(SQLModel, table=True):
|
||||
__tablename__ = "ds_recommended_problem"
|
||||
id: int = Field(sa_column=Column(BigInteger, Identity(always=True), nullable=False, primary_key=True))
|
||||
datasource_id: int = Field(sa_column=Column(BigInteger()))
|
||||
question: str = Field(sa_column=Column(Text))
|
||||
remark: str = Field(sa_column=Column(Text))
|
||||
sort: int = Field(sa_column=Column(BigInteger()))
|
||||
create_time: datetime = Field(sa_column=Column(DateTime(timezone=False), nullable=True))
|
||||
create_by: int = Field(sa_column=Column(BigInteger()))
|
||||
|
||||
|
||||
class CoreField(SQLModel, table=True):
|
||||
__tablename__ = "core_field"
|
||||
@@ -61,6 +72,7 @@ class CreateDatasource(BaseModel):
|
||||
num: str = ''
|
||||
oid: int = 1
|
||||
tables: List[CoreTable] = []
|
||||
recommended_config: int = 1
|
||||
|
||||
|
||||
# edit local saved table and fields
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
import { request } from '@/utils/request'
|
||||
|
||||
export const recommendedApi = {
|
||||
get_recommended_problem: (dsId: any) =>
|
||||
request.get(`/recommended_problem/get_datasource_recommended/${dsId}`),
|
||||
save_recommended_problem: (data: any) => request.post(`/recommended_problem/save`, data),
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1763971949643" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="8822" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M0 0v1024h1024V0H0z m907.636364 907.636364H116.363636V116.363636h791.272728v791.272728z" p-id="8823"></path><path d="M449.163636 572.509091h116.363637v109.381818h-116.363637zM509.672727 351.418182c25.6 2.327273 37.236364 11.636364 39.563637 32.581818 0 13.963636-11.636364 27.927273-34.909091 37.236364-44.218182 20.945455-62.836364 55.854545-58.181818 109.381818H558.545455c-4.654545-20.945455 11.636364-41.890909 46.545454-60.509091 39.563636-23.272727 62.836364-53.527273 62.836364-88.436364-4.654545-72.145455-53.527273-109.381818-151.272728-114.036363-100.072727 4.654545-151.272727 46.545455-158.254545 128h116.363636c-2.327273-27.927273 9.309091-41.890909 34.909091-44.218182z" p-id="8824"></path></svg>
|
||||
|
After Width: | Height: | Size: 1.0 KiB |
@@ -308,6 +308,12 @@
|
||||
}
|
||||
},
|
||||
"datasource": {
|
||||
"recommended_problem_configuration": "Recommended Problem Configuration",
|
||||
"problem_generation_method": "Problem Generation Method",
|
||||
"ai_automatic_generation": "AI Automatic Generation",
|
||||
"user_defined": "User Defined",
|
||||
"question_tips": "Please enter recommended questions",
|
||||
"add_question": "Add Question",
|
||||
"data_source_yet": "No data source yet",
|
||||
"search_by_name": "Search by Name",
|
||||
"search": "Search",
|
||||
|
||||
@@ -308,6 +308,12 @@
|
||||
}
|
||||
},
|
||||
"datasource": {
|
||||
"recommended_problem_configuration": "추천 문제 구성",
|
||||
"problem_generation_method": "문제 생성 방식",
|
||||
"ai_automatic_generation": "AI 자동 생성",
|
||||
"user_defined": "사용자 정의",
|
||||
"question_tips": "추천 문제를 입력해 주세요",
|
||||
"add_question": "문제 추가",
|
||||
"data_source_yet": "데이터 소스가 없습니다",
|
||||
"search_by_name": "이름으로 검색",
|
||||
"search": "검색",
|
||||
|
||||
@@ -308,6 +308,12 @@
|
||||
}
|
||||
},
|
||||
"datasource": {
|
||||
"recommended_problem_configuration": "推荐问题配置",
|
||||
"problem_generation_method": "问题生成方式",
|
||||
"ai_automatic_generation": "AI 自动生成",
|
||||
"user_defined": "用户自定义",
|
||||
"question_tips": "请输入推荐问题",
|
||||
"add_question": "添加问题",
|
||||
"data_source_yet": "暂无数据源",
|
||||
"search_by_name": "通过名称搜索",
|
||||
"search": "搜索",
|
||||
|
||||
@@ -7,6 +7,7 @@ import { computed, ref, unref } from 'vue'
|
||||
import { ClickOutside as vClickOutside } from 'element-plus-secondary'
|
||||
import { dsTypeWithImg } from './js/ds-type'
|
||||
import edit from '@/assets/svg/icon_edit_outlined.svg'
|
||||
import icon_recommended_problem from '@/assets/svg/icon_recommended_problem.svg'
|
||||
import { datasourceApi } from '@/api/datasource.ts'
|
||||
|
||||
const props = withDefaults(
|
||||
@@ -27,7 +28,14 @@ const props = withDefaults(
|
||||
}
|
||||
)
|
||||
|
||||
const emits = defineEmits(['edit', 'del', 'question', 'dataTableDetail', 'showTable'])
|
||||
const emits = defineEmits([
|
||||
'edit',
|
||||
'del',
|
||||
'question',
|
||||
'dataTableDetail',
|
||||
'showTable',
|
||||
'recommendation',
|
||||
])
|
||||
const icon = computed(() => {
|
||||
return (dsTypeWithImg.find((ele) => props.type === ele.type) || {}).img
|
||||
})
|
||||
@@ -35,6 +43,10 @@ const handleEdit = () => {
|
||||
emits('edit')
|
||||
}
|
||||
|
||||
const handleRecommendation = () => {
|
||||
emits('recommendation')
|
||||
}
|
||||
|
||||
const handleDel = () => {
|
||||
emits('del')
|
||||
}
|
||||
@@ -124,6 +136,12 @@ const onClickOutside = () => {
|
||||
</el-icon>
|
||||
{{ $t('datasource.edit') }}
|
||||
</div>
|
||||
<div class="item" style="display: none" @click.stop="handleRecommendation">
|
||||
<el-icon size="16">
|
||||
<icon_recommended_problem></icon_recommended_problem>
|
||||
</el-icon>
|
||||
{{ $t('datasource.recommended_problem_configuration') }}
|
||||
</div>
|
||||
<div class="item" @click.stop="handleDel">
|
||||
<el-icon size="16">
|
||||
<delIcon></delIcon>
|
||||
|
||||
@@ -17,8 +17,11 @@ import { dsTypeWithImg } from './js/ds-type'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { useUserStore } from '@/stores/user'
|
||||
import { chatApi } from '@/api/chat'
|
||||
import RecommendedProblemConfigDialog from '@/views/ds/RecommendedProblemConfigDialog.vue'
|
||||
const userStore = useUserStore()
|
||||
interface Datasource {
|
||||
const recommendedProblemConfigRef = ref()
|
||||
|
||||
export interface Datasource {
|
||||
name: string
|
||||
num: string
|
||||
type_name: string
|
||||
@@ -26,6 +29,7 @@ interface Datasource {
|
||||
img: string
|
||||
description: string
|
||||
id?: string
|
||||
recommended_config?: string
|
||||
}
|
||||
|
||||
const router = useRouter()
|
||||
@@ -77,6 +81,10 @@ const handleEditDatasource = (res: any) => {
|
||||
addDrawerRef.value.handleEditDatasource(res)
|
||||
}
|
||||
|
||||
const handleRecommendation = (res: Datasource) => {
|
||||
recommendedProblemConfigRef.value?.init(res)
|
||||
}
|
||||
|
||||
const handleQuestion = async (id: string) => {
|
||||
try {
|
||||
await chatApi.checkLLMModel()
|
||||
@@ -295,6 +303,7 @@ useEmitt({
|
||||
:description="ele.description"
|
||||
@question="handleQuestion"
|
||||
@edit="handleEditDatasource(ele)"
|
||||
@recommendation="handleRecommendation(ele)"
|
||||
@del="deleteHandler(ele)"
|
||||
@data-table-detail="dataTableDetail(ele)"
|
||||
></Card>
|
||||
@@ -317,7 +326,9 @@ useEmitt({
|
||||
</el-button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<RecommendedProblemConfigDialog
|
||||
ref="recommendedProblemConfigRef"
|
||||
></RecommendedProblemConfigDialog>
|
||||
<AddDrawer ref="addDrawerRef" @search="search"></AddDrawer>
|
||||
</div>
|
||||
<DataTable
|
||||
|
||||
@@ -0,0 +1,130 @@
|
||||
<script setup lang="ts">
|
||||
import { reactive, ref } from 'vue'
|
||||
import { recommendedApi } from '@/api/recommendedApi.ts'
|
||||
|
||||
interface RecommendedProblem {
|
||||
id?: number
|
||||
question: string
|
||||
datasourceId?: any | undefined
|
||||
sort: number
|
||||
}
|
||||
|
||||
const dialogShow = ref(false)
|
||||
|
||||
const state = reactive({
|
||||
dsId: null,
|
||||
recommended: {
|
||||
recommendedConfig: 1,
|
||||
recommendedProblemList: [] as RecommendedProblem[],
|
||||
},
|
||||
})
|
||||
|
||||
const init = (params: any) => {
|
||||
dialogShow.value = true
|
||||
state.recommended.recommendedConfig = params.recommendedConfig
|
||||
state.dsId = params.id
|
||||
recommendedApi.get_recommended_problem(state.dsId).then((res: any) => {
|
||||
state.recommended.recommendedProblemList = res
|
||||
})
|
||||
}
|
||||
|
||||
const addRecommendedProblem = (): void => {
|
||||
state.recommended.recommendedProblemList.push({
|
||||
question: '',
|
||||
datasourceId: state.dsId,
|
||||
} as RecommendedProblem)
|
||||
}
|
||||
|
||||
const closeDialog = () => {
|
||||
dialogShow.value = false
|
||||
state.recommended = {
|
||||
recommendedConfig: 1,
|
||||
recommendedProblemList: [] as RecommendedProblem[],
|
||||
}
|
||||
}
|
||||
const save = () => {
|
||||
recommendedApi.save_recommended_problem(state.recommended.recommendedProblemList)
|
||||
closeDialog()
|
||||
}
|
||||
|
||||
const form = ref<any>({
|
||||
id: null,
|
||||
question: '',
|
||||
sort: null,
|
||||
})
|
||||
|
||||
defineExpose({
|
||||
init,
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<el-dialog
|
||||
v-model="dialogShow"
|
||||
:title="$t('datasource.recommended_problem_configuration')"
|
||||
width="600"
|
||||
modal-class="add-question_dialog"
|
||||
destroy-on-close
|
||||
:close-on-click-modal="false"
|
||||
@before-closed="closeDialog"
|
||||
>
|
||||
<el-form-item :label="$t('datasource.problem_generation_method')" prop="mode">
|
||||
<el-radio-group v-model="form.mode">
|
||||
<el-radio value="1">{{ $t('datasource.ai_automatic_generation') }}</el-radio>
|
||||
<el-radio value="2">{{ $t('datasource.user_defined') }}</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item
|
||||
v-for="(recommendedItem, index) in state.recommended.recommendedProblemList"
|
||||
:key="index"
|
||||
prop="mode"
|
||||
>
|
||||
<el-input
|
||||
v-model="recommendedItem.question"
|
||||
clearable
|
||||
:placeholder="$t('datasource.question_tips')"
|
||||
>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
<div>
|
||||
<el-button text @click="addRecommendedProblem">
|
||||
{{ $t('datasource.add_question') }}</el-button
|
||||
>
|
||||
</div>
|
||||
<div style="display: flex; justify-content: flex-end; margin-top: 20px">
|
||||
<el-button secondary @click="closeDialog">{{ $t('common.cancel') }}</el-button>
|
||||
<el-button type="primary" @click="save">{{ $t('model.add') }}</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<style scoped lang="less">
|
||||
.add-question_dialog {
|
||||
.ed-input-group__append {
|
||||
background-color: #fff;
|
||||
padding: 0 12px;
|
||||
}
|
||||
|
||||
.value-input {
|
||||
.ed-input-group__append {
|
||||
color: #1f2329;
|
||||
position: relative;
|
||||
&:hover {
|
||||
&::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: -1px;
|
||||
top: 0;
|
||||
width: calc(100% - 1px);
|
||||
height: calc(100% - 2px);
|
||||
background: var(--ed-color-primary-1a, #1cba901a);
|
||||
border: 1px solid var(--ed-color-primary);
|
||||
border-bottom-right-radius: 6px;
|
||||
border-top-right-radius: 6px;
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user