Skip to content

Commit 6c7fd8c

Browse files
perf: The assistant is optimized for manual data source selection
1 parent fc6a740 commit 6c7fd8c

File tree

13 files changed

+140
-47
lines changed

13 files changed

+140
-47
lines changed

backend/apps/chat/api/chat.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -172,9 +172,9 @@ async def start_chat(session: SessionDep, current_user: CurrentUser, create_chat
172172
module=OperationModules.CHAT,
173173
result_id_expr="id"
174174
))
175-
async def start_chat(session: SessionDep, current_user: CurrentUser):
175+
async def start_chat(session: SessionDep, current_user: CurrentUser, current_assistant: CurrentAssistant, create_chat_obj: CreateChat = CreateChat(origin=2)):
176176
try:
177-
return create_chat(session, current_user, CreateChat(origin=2), False)
177+
return create_chat(session, current_user, create_chat_obj, create_chat_obj and create_chat_obj.datasource, current_assistant)
178178
except Exception as e:
179179
raise HTTPException(
180180
status_code=500,

backend/apps/chat/curd/chat.py

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,9 @@
1111
TypeEnum, OperationEnum, ChatRecordResult
1212
from apps.datasource.crud.recommended_problem import get_datasource_recommended_chart
1313
from apps.datasource.models.datasource import CoreDatasource
14-
from apps.system.crud.assistant import AssistantOutDsFactory
14+
from apps.db.constant import DB
15+
from apps.system.crud.assistant import AssistantOutDs, AssistantOutDsFactory
16+
from apps.system.schemas.system_schema import AssistantOutDsSchema
1517
from common.core.deps import CurrentAssistant, SessionDep, CurrentUser, Trans
1618
from common.utils.utils import extract_nested_json
1719

@@ -447,7 +449,7 @@ def list_generate_chart_logs(session: SessionDep, chart_id: int) -> List[ChatLog
447449

448450

449451
def create_chat(session: SessionDep, current_user: CurrentUser, create_chat_obj: CreateChat,
450-
require_datasource: bool = True) -> ChatInfo:
452+
require_datasource: bool = True, current_assistant: CurrentAssistant = None) -> ChatInfo:
451453
if not create_chat_obj.datasource and require_datasource:
452454
raise Exception("Datasource cannot be None")
453455

@@ -459,10 +461,15 @@ def create_chat(session: SessionDep, current_user: CurrentUser, create_chat_obj:
459461
oid=current_user.oid if current_user.oid is not None else 1,
460462
brief=create_chat_obj.question.strip()[:20],
461463
origin=create_chat_obj.origin if create_chat_obj.origin is not None else 0)
462-
ds: CoreDatasource | None = None
464+
ds: CoreDatasource | AssistantOutDsSchema | None = None
463465
if create_chat_obj.datasource:
464466
chat.datasource = create_chat_obj.datasource
465-
ds = session.get(CoreDatasource, create_chat_obj.datasource)
467+
if current_assistant and current_assistant.type == 1:
468+
out_ds_instance: AssistantOutDs = AssistantOutDsFactory.get_instance(current_assistant)
469+
ds = out_ds_instance.get_ds(chat.datasource)
470+
ds.type_name = DB.get_db(ds.type)
471+
else:
472+
ds = session.get(CoreDatasource, create_chat_obj.datasource)
466473

467474
if not ds:
468475
raise Exception(f"Datasource with id {create_chat_obj.datasource} not found")
@@ -494,7 +501,7 @@ def create_chat(session: SessionDep, current_user: CurrentUser, create_chat_obj:
494501
record.finish = True
495502
record.create_time = datetime.datetime.now()
496503
record.create_by = current_user.id
497-
if ds.recommended_config == 2:
504+
if isinstance(ds, CoreDatasource) and ds.recommended_config == 2:
498505
questions = get_datasource_recommended_chart(session, ds.id)
499506
record.recommended_question = orjson.dumps(questions).decode()
500507
record.recommended_question_answer = orjson.dumps({

backend/apps/system/api/assistant.py

Lines changed: 55 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,16 @@
88
from sqlbot_xpack.file_utils import SQLBotFileUtils
99
from sqlmodel import select
1010

11+
from apps.datasource.models.datasource import CoreDatasource
12+
from apps.db.constant import DB
1113
from apps.swagger.i18n import PLACEHOLDER_PREFIX
12-
from apps.system.crud.assistant import get_assistant_info
14+
from apps.system.crud.assistant import AssistantOutDs, AssistantOutDsFactory, get_assistant_info
1315
from apps.system.crud.assistant_manage import dynamic_upgrade_cors, save
1416
from apps.system.models.system_model import AssistantModel
1517
from apps.system.schemas.auth import CacheName, CacheNamespace
1618
from apps.system.schemas.system_schema import AssistantBase, AssistantDTO, AssistantUiSchema, AssistantValidator
1719
from common.core.config import settings
18-
from common.core.deps import SessionDep, Trans, CurrentUser
20+
from common.core.deps import CurrentAssistant, SessionDep, Trans, CurrentUser
1921
from common.core.security import create_access_token
2022
from common.core.sqlbot_cache import clear_cache
2123
from common.utils.utils import get_origin_from_referer, origin_match_domain
@@ -163,6 +165,56 @@ async def ui(session: SessionDep, data: str = Form(), files: List[UploadFile] =
163165
async def clear_ui_cache(id: int):
164166
pass
165167

168+
@router.get("/ds", include_in_schema=False, response_model=list[dict])
169+
async def ds(session: SessionDep, current_assistant: CurrentAssistant):
170+
if current_assistant.type == 0:
171+
online = current_assistant.online
172+
configuration = current_assistant.configuration
173+
config: dict[any] = json.loads(configuration)
174+
oid: int = int(config['oid'])
175+
stmt = select(CoreDatasource.id, CoreDatasource.name, CoreDatasource.description, CoreDatasource.type, CoreDatasource.type_name, CoreDatasource.num).where(
176+
CoreDatasource.oid == oid)
177+
if not online:
178+
public_list: list[int] = config.get('public_list') or None
179+
if public_list:
180+
stmt = stmt.where(CoreDatasource.id.in_(public_list))
181+
else:
182+
return []
183+
db_ds_list = session.exec(stmt)
184+
return [
185+
{
186+
"id": ds.id,
187+
"name": ds.name,
188+
"description": ds.description,
189+
"type": ds.type,
190+
"type_name": ds.type_name,
191+
"num": ds.num,
192+
}
193+
for ds in db_ds_list]
194+
if current_assistant.type == 1:
195+
out_ds_instance: AssistantOutDs = AssistantOutDsFactory.get_instance(current_assistant)
196+
return [
197+
{
198+
"id": ds.id,
199+
"name": ds.name,
200+
"description": ds.description or ds.comment,
201+
"type": ds.type,
202+
"type_name": get_db_type(ds.type),
203+
"num": len(ds.tables) if ds.tables else 0,
204+
}
205+
for ds in out_ds_instance.ds_list
206+
if get_db_type(ds.type)
207+
]
208+
209+
return None
210+
211+
def get_db_type(type):
212+
try:
213+
db = DB.get_db(type)
214+
return db.db_name
215+
except Exception:
216+
return None
217+
166218

167219
@router.get("", response_model=list[AssistantModel], summary=f"{PLACEHOLDER_PREFIX}assistant_grid_api", description=f"{PLACEHOLDER_PREFIX}assistant_grid_api")
168220
async def query(session: SessionDep, current_user: CurrentUser):
@@ -219,3 +271,4 @@ async def delete(request: Request, session: SessionDep, id: int = Path(descripti
219271
session.delete(db_model)
220272
session.commit()
221273
dynamic_upgrade_cors(request=request, session=session)
274+

frontend/src/api/chat.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -330,8 +330,8 @@ export const chatApi = {
330330
startChat: (data: any): Promise<ChatInfo> => {
331331
return request.post('/chat/start', data)
332332
},
333-
startAssistantChat: (): Promise<ChatInfo> => {
334-
return request.post('/chat/assistant/start')
333+
startAssistantChat: (data?: any): Promise<ChatInfo> => {
334+
return request.post('/chat/assistant/start', Object.assign({ origin: 2 }, data))
335335
},
336336
renameChat: (chat_id: number | undefined, brief: string): Promise<string> => {
337337
return request.post('/chat/rename', { id: chat_id, brief: brief })

frontend/src/i18n/en.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -669,7 +669,8 @@
669669
"data_analysis_now": "I can query data, generate charts, detect data anomalies, predict data, and more! Enable Smart Data Analysis now!",
670670
"window_entrance_icon": "Floating window entrance icon",
671671
"preview_error": "The current page prohibits embedding using Iframe!",
672-
"assistant_app": "Assistant App"
672+
"assistant_app": "Assistant App",
673+
"auto_select_ds": "Automatically select data source"
673674
},
674675
"chat": {
675676
"type": "Chart Type",

frontend/src/i18n/ko-KR.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -669,7 +669,8 @@
669669
"data_analysis_now": "저는 데이터 조회, 차트 생성, 데이터 이상 감지, 데이터 예측 등을 할 수 있습니다. 빨리 스마트 데이터 조회를 시작하세요~",
670670
"window_entrance_icon": "플로팅 윈도우 입구 아이콘",
671671
"preview_error": "현재 페이지는 Iframe 임베딩을 허용하지 않습니다!",
672-
"assistant_app": "어시스턴트 앱"
672+
"assistant_app": "어시스턴트 앱",
673+
"auto_select_ds": "자동 데이터 소스 선택"
673674
},
674675
"chat": {
675676
"type": "차트 유형",

frontend/src/i18n/zh-CN.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -669,7 +669,8 @@
669669
"data_analysis_now": "我可以查询数据、生成图表、检测数据异常、预测数据等赶快开启智能问数吧~",
670670
"window_entrance_icon": "浮窗入口图标",
671671
"preview_error": "当前页面禁止使用 Iframe 嵌入!",
672-
"assistant_app": "小助手应用"
672+
"assistant_app": "小助手应用",
673+
"auto_select_ds": "自动选择数据源"
673674
},
674675
"chat": {
675676
"type": "图表类型",

frontend/src/stores/assistant.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ interface AssistantState {
2222
pageEmbedded?: boolean
2323
history: boolean
2424
hostOrigin: string
25+
autoDs?: boolean
2526
requestPromiseMap: Map<string, PendingRequest>
2627
}
2728

@@ -38,6 +39,7 @@ export const AssistantStore = defineStore('assistant', {
3839
pageEmbedded: false,
3940
history: true,
4041
hostOrigin: '',
42+
autoDs: false,
4143
requestPromiseMap: new Map<string, PendingRequest>(),
4244
}
4345
},
@@ -75,6 +77,9 @@ export const AssistantStore = defineStore('assistant', {
7577
getHostOrigin(): string {
7678
return this.hostOrigin
7779
},
80+
getAutoDs(): boolean {
81+
return !!this.autoDs
82+
},
7883
},
7984
actions: {
8085
refreshCertificate<T>() {
@@ -146,6 +151,9 @@ export const AssistantStore = defineStore('assistant', {
146151
setHostOrigin(origin: string) {
147152
this.hostOrigin = origin
148153
},
154+
setAutoDs(autoDs?: boolean) {
155+
this.autoDs = !!autoDs
156+
},
149157
async setChat() {
150158
if (!this.assistant) {
151159
return null

frontend/src/views/chat/ChatCreator.vue

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,15 @@ import { datasourceApi } from '@/api/datasource.ts'
99
import Card from '@/views/ds/ChatCard.vue'
1010
import AddDrawer from '@/views/ds/AddDrawer.vue'
1111
import { useUserStore } from '@/stores/user'
12-
12+
import { useAssistantStore } from '@/stores/assistant'
13+
import { request } from '@/utils/request'
14+
const assistantStore = useAssistantStore()
1315
const userStore = useUserStore()
1416
1517
const isWsAdmin = computed(() => userStore.isAdmin || userStore.isSpaceAdmin)
18+
const selectAssistantDs = computed(
19+
() => assistantStore.getAssistant && !assistantStore.getEmbedded && !assistantStore.getAutoDs
20+
)
1621
const props = withDefaults(
1722
defineProps<{
1823
hidden?: boolean
@@ -42,8 +47,7 @@ const emits = defineEmits(['onChatCreated'])
4247
4348
function listDs() {
4449
searchLoading.value = true
45-
datasourceApi
46-
.list()
50+
;(selectAssistantDs.value ? request.get('/system/assistant/ds') : datasourceApi.list())
4751
.then((res) => {
4852
datasourceList.value = res
4953
})
@@ -73,6 +77,10 @@ function selectDsInDialog(ds: any) {
7377
7478
function confirmSelectDs() {
7579
if (innerDs.value) {
80+
if (assistantStore.getType == 1) {
81+
createChat(innerDs.value)
82+
return
83+
}
7684
statusLoading.value = true
7785
//check first
7886
datasourceApi
@@ -90,10 +98,15 @@ function confirmSelectDs() {
9098
9199
function createChat(datasource: number) {
92100
loading.value = true
93-
chatApi
94-
.startChat({
95-
datasource: datasource,
96-
})
101+
const param = {
102+
datasource: datasource,
103+
} as any
104+
let method = chatApi.startChat
105+
if (assistantStore.getAssistant) {
106+
param['origin'] = 2
107+
method = chatApi.startAssistantChat
108+
}
109+
method(param)
97110
.then((res) => {
98111
const chat: ChatInfo | undefined = chatApi.toChatInfo(res)
99112
if (chat == undefined) {
@@ -140,11 +153,11 @@ defineExpose({
140153
>
141154
<template #header="{ close }">
142155
<span style="white-space: nowrap">{{ $t('qa.select_datasource') }}</span>
143-
<div class="flex-center" style="width: 100%">
156+
<div class="flex-center" style="width: 100%; margin-right: 32px">
144157
<el-input
145158
v-model="keywords"
146159
clearable
147-
style="width: 320px"
160+
style="width: 320px; max-width: calc(100% - 32px)"
148161
:placeholder="$t('datasource.search')"
149162
>
150163
<template #prefix>

frontend/src/views/chat/index.vue

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,7 @@
147147
</div>
148148

149149
<el-button
150-
v-if="isCompletePage && currentChatId === undefined"
150+
v-if="(isCompletePage || selectAssistantDs) && currentChatId === undefined"
151151
size="large"
152152
type="primary"
153153
class="greeting-btn"
@@ -360,9 +360,12 @@
360360
</div>
361361
</el-scrollbar>
362362
</el-main>
363-
<el-footer v-if="computedMessages.length > 0 || !isCompletePage" class="chat-footer">
363+
<el-footer
364+
v-if="computedMessages.length > 0 || (!isCompletePage && !selectAssistantDs)"
365+
class="chat-footer"
366+
>
364367
<div class="input-wrapper" @click="clickInput">
365-
<div v-if="isCompletePage" class="datasource">
368+
<div v-if="isCompletePage || selectAssistantDs" class="datasource">
366369
<template v-if="currentChat.datasource && currentChat.datasource_name">
367370
{{ t('qa.selected_datasource') }}:
368371
<img
@@ -397,7 +400,7 @@
397400
:disabled="isTyping"
398401
clearable
399402
class="input-area"
400-
:class="!isCompletePage && 'is-assistant'"
403+
:class="!isCompletePage && !selectAssistantDs && 'is-assistant'"
401404
type="textarea"
402405
:autosize="{ minRows: 1, maxRows: 8.583 }"
403406
:placeholder="t('qa.question_placeholder')"
@@ -420,7 +423,11 @@
420423
</el-footer>
421424
</el-container>
422425

423-
<ChatCreator v-if="isCompletePage" ref="chatCreatorRef" @on-chat-created="onChatCreatedQuick" />
426+
<ChatCreator
427+
v-if="isCompletePage || selectAssistantDs"
428+
ref="chatCreatorRef"
429+
@on-chat-created="onChatCreatedQuick"
430+
/>
424431
<ChatCreator ref="hiddenChatCreatorRef" hidden @on-chat-created="onChatCreatedQuick" />
425432
</el-container>
426433
</template>
@@ -482,6 +489,10 @@ const isCompletePage = computed(() => !assistantStore.getAssistant || assistantS
482489
const embeddedHistoryHidden = computed(
483490
() => assistantStore.getAssistant && !assistantStore.getHistory
484491
)
492+
// const autoDs = computed(() => assistantStore.getAssistant && assistantStore.getAutoDs)
493+
const selectAssistantDs = computed(() => {
494+
return assistantStore.getAssistant && !assistantStore.getAutoDs
495+
})
485496
const customName = computed(() => {
486497
if (!isCompletePage.value && props.pageEmbedded) return props.appName
487498
return ''
@@ -634,7 +645,7 @@ const createNewChat = async () => {
634645
return
635646
}
636647
goEmpty()
637-
if (!isCompletePage.value) {
648+
if (!isCompletePage.value && !selectAssistantDs.value) {
638649
currentChat.value = new ChatInfo()
639650
currentChatId.value = undefined
640651
return
@@ -764,6 +775,7 @@ function onChatStop() {
764775
const assistantPrepareSend = async () => {
765776
if (
766777
!isCompletePage.value &&
778+
!selectAssistantDs.value &&
767779
(currentChatId.value == null || typeof currentChatId.value == 'undefined')
768780
) {
769781
const assistantChat = await assistantStore.setChat()

0 commit comments

Comments
 (0)