From bbadc4975986347c37143e3145aee11600b4aad1 Mon Sep 17 00:00:00 2001 From: fit2cloud-chenyw Date: Thu, 29 Jan 2026 10:55:04 +0800 Subject: [PATCH 001/325] perf: Cache clearance error in datasource deletion API --- backend/apps/datasource/crud/datasource.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/apps/datasource/crud/datasource.py b/backend/apps/datasource/crud/datasource.py index 8c919166b..868e979e7 100644 --- a/backend/apps/datasource/crud/datasource.py +++ b/backend/apps/datasource/crud/datasource.py @@ -135,7 +135,7 @@ async def delete_ds(session: SessionDep, id: int): delete_table_by_ds_id(session, id) delete_field_by_ds_id(session, id) if term: - await clear_ws_resource_cache(term.oid) + await clear_ws_ds_cache(term.oid) return { "message": f"Datasource with ID {id} deleted successfully." } From a435415f5da2f5ba8aa912ae4d30f6a2ee52260e Mon Sep 17 00:00:00 2001 From: wangjiahao <1522128093@qq.com> Date: Thu, 29 Jan 2026 11:05:20 +0800 Subject: [PATCH 002/325] fix: Fix the issue where the recommendation is not refreshed after retrieval --- frontend/src/views/chat/QuickQuestion.vue | 2 +- frontend/src/views/chat/RecommendQuestionQuick.vue | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/frontend/src/views/chat/QuickQuestion.vue b/frontend/src/views/chat/QuickQuestion.vue index a4e93d4e6..2f173b6d1 100644 --- a/frontend/src/views/chat/QuickQuestion.vue +++ b/frontend/src/views/chat/QuickQuestion.vue @@ -15,7 +15,7 @@ const getRecommendQuestions = () => { const questions = '[]' const retrieveQuestions = () => { - recommendQuestionRef.value.getRecommendQuestions(10) + recommendQuestionRef.value.getRecommendQuestions(10, true) recentQuestionRef.value.getRecentQuestions() } const quickAsk = (question: string) => { diff --git a/frontend/src/views/chat/RecommendQuestionQuick.vue b/frontend/src/views/chat/RecommendQuestionQuick.vue index f07b11158..8995161d9 100644 --- a/frontend/src/views/chat/RecommendQuestionQuick.vue +++ b/frontend/src/views/chat/RecommendQuestionQuick.vue @@ -48,11 +48,11 @@ function clickQuestion(question: string): void { const stopFlag = ref(false) -async function getRecommendQuestions(articles_number: number) { +async function getRecommendQuestions(articles_number: number, isRetrieve: false) { recommendedApi.get_datasource_recommended_base(props.datasource).then((res) => { if (res.recommended_config === 2) { questions.value = res.questions - } else if (currentChat.value.recommended_generate) { + } else if (currentChat.value.recommended_generate && !isRetrieve) { questions.value = currentChat.value.recommended_question as string } else { getRecommendQuestionsLLM(articles_number) From f33188a5451a7bd7bd41b39a3bb171284039ce16 Mon Sep 17 00:00:00 2001 From: junjun Date: Thu, 29 Jan 2026 13:51:06 +0800 Subject: [PATCH 003/325] fix: check sql only contain read operation #814 --- backend/apps/db/db.py | 33 ++++++++++++++++++++++++++++++++- backend/pyproject.toml | 1 + 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/backend/apps/db/db.py b/backend/apps/db/db.py index fd58d2125..2f3c8b8a1 100644 --- a/backend/apps/db/db.py +++ b/backend/apps/db/db.py @@ -3,8 +3,8 @@ import os import platform import urllib.parse -from decimal import Decimal from datetime import timedelta +from decimal import Decimal from typing import Optional import oracledb @@ -32,6 +32,8 @@ from fastapi import HTTPException from apps.db.es_engine import get_es_connect, get_es_index, get_es_fields, get_es_data_by_http from common.core.config import settings +import sqlglot +from sqlglot import expressions as exp try: if os.path.exists(settings.ORACLE_CLIENT_PATH): @@ -464,6 +466,9 @@ def convert_value(value): def exec_sql(ds: CoreDatasource | AssistantOutDsSchema, sql: str, origin_column=False): while sql.endswith(';'): sql = sql[:-1] + # check execute sql only contain read operations + if not check_sql_read(sql): + raise ValueError(f"SQL can only contain read operations") db = DB.get_db(ds.type) if db.connect_type == ConnectType.sqlalchemy: @@ -569,3 +574,29 @@ def exec_sql(ds: CoreDatasource | AssistantOutDsSchema, sql: str, origin_column= "sql": bytes.decode(base64.b64encode(bytes(sql, 'utf-8')))} except Exception as ex: raise Exception(str(ex)) + + +def check_sql_read(sql: str, dialect=None): + try: + + statements = sqlglot.parse(sql, dialect=dialect) + + if not statements: + raise ValueError("Parse SQL Error") + + write_types = ( + exp.Insert, exp.Update, exp.Delete, + exp.Create, exp.Drop, exp.Alter, + exp.Merge, exp.Command + ) + + for stmt in statements: + if stmt is None: + continue + if isinstance(stmt, write_types): + return False + + return True + + except Exception as e: + raise ValueError(f"Parse SQL Error: {e}") diff --git a/backend/pyproject.toml b/backend/pyproject.toml index 2f4e9d317..a133dc833 100644 --- a/backend/pyproject.toml +++ b/backend/pyproject.toml @@ -52,6 +52,7 @@ dependencies = [ "redshift-connector>=2.1.8", "elasticsearch[requests] (>=7.10,<8.0)", "ldap3>=2.9.1", + "sqlglot>=28.6.0", ] [project.optional-dependencies] From 184e76ebc2b0d33996b3a642aec9a0ea379b2f46 Mon Sep 17 00:00:00 2001 From: junjun Date: Thu, 29 Jan 2026 14:09:12 +0800 Subject: [PATCH 004/325] fix: check sql only contain read operation #814 --- backend/apps/db/db.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/backend/apps/db/db.py b/backend/apps/db/db.py index 2f3c8b8a1..7a9973b32 100644 --- a/backend/apps/db/db.py +++ b/backend/apps/db/db.py @@ -10,6 +10,7 @@ import oracledb import psycopg2 import pymssql +import re from apps.db.db_sql import get_table_sql, get_field_sql, get_version_sql from common.error import ParseSQLResultError @@ -578,6 +579,8 @@ def exec_sql(ds: CoreDatasource | AssistantOutDsSchema, sql: str, origin_column= def check_sql_read(sql: str, dialect=None): try: + ansi_escape = re.compile(r'\x1b\[[0-9;]*m') + sql = ansi_escape.sub('', sql) statements = sqlglot.parse(sql, dialect=dialect) From bf56c3e48655024ee875f559526c104ec19437f3 Mon Sep 17 00:00:00 2001 From: junjun Date: Thu, 29 Jan 2026 14:21:19 +0800 Subject: [PATCH 005/325] fix: check sql only contain read operation #814 --- backend/apps/db/db.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/backend/apps/db/db.py b/backend/apps/db/db.py index 7a9973b32..2002ee622 100644 --- a/backend/apps/db/db.py +++ b/backend/apps/db/db.py @@ -577,10 +577,11 @@ def exec_sql(ds: CoreDatasource | AssistantOutDsSchema, sql: str, origin_column= raise Exception(str(ex)) -def check_sql_read(sql: str, dialect=None): +def check_sql_read(sql: str, ds: CoreDatasource | AssistantOutDsSchema): try: - ansi_escape = re.compile(r'\x1b\[[0-9;]*m') - sql = ansi_escape.sub('', sql) + dialect = None + if ds.type == "mysql" or ds.type == "doris" or ds.type == "starrocks": + dialect = 'mysql' statements = sqlglot.parse(sql, dialect=dialect) From 07af6d0066f9fc389f4d7b980cae4e91bd6545e9 Mon Sep 17 00:00:00 2001 From: junjun Date: Thu, 29 Jan 2026 14:44:58 +0800 Subject: [PATCH 006/325] fix: check sql only contain read operation #814 --- backend/apps/db/db.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/apps/db/db.py b/backend/apps/db/db.py index 2002ee622..239b51018 100644 --- a/backend/apps/db/db.py +++ b/backend/apps/db/db.py @@ -468,7 +468,7 @@ def exec_sql(ds: CoreDatasource | AssistantOutDsSchema, sql: str, origin_column= while sql.endswith(';'): sql = sql[:-1] # check execute sql only contain read operations - if not check_sql_read(sql): + if not check_sql_read(sql, ds): raise ValueError(f"SQL can only contain read operations") db = DB.get_db(ds.type) From ecf9e0b18d6a90b1cdad4932e33e84ad5acc9684 Mon Sep 17 00:00:00 2001 From: dataeaseShu Date: Thu, 29 Jan 2026 16:44:49 +0800 Subject: [PATCH 007/325] fix: Style optimization --- frontend/src/assets/svg/logo_lark.svg | 26 +++++++------------ .../src/views/chat/chat-block/ChartBlock.vue | 3 --- frontend/src/views/chat/index.vue | 2 ++ .../src/views/system/authentication/index.vue | 17 +++++++----- .../views/system/platform/PlatformInfo.vue | 23 +++++++--------- frontend/src/views/system/platform/index.vue | 3 +-- 6 files changed, 32 insertions(+), 42 deletions(-) diff --git a/frontend/src/assets/svg/logo_lark.svg b/frontend/src/assets/svg/logo_lark.svg index 0eaf8df87..fc82390a3 100644 --- a/frontend/src/assets/svg/logo_lark.svg +++ b/frontend/src/assets/svg/logo_lark.svg @@ -1,20 +1,12 @@ - - - - - - - + + + - - - - - - - - - - + + + + + + diff --git a/frontend/src/views/chat/chat-block/ChartBlock.vue b/frontend/src/views/chat/chat-block/ChartBlock.vue index 01f154576..e4e9ae930 100644 --- a/frontend/src/views/chat/chat-block/ChartBlock.vue +++ b/frontend/src/views/chat/chat-block/ChartBlock.vue @@ -640,9 +640,6 @@ watch( padding: 16px; display: flex; flex-direction: column; - position: relative; - z-index: 10; - border: 1px solid rgba(222, 224, 227, 1); border-radius: 12px; diff --git a/frontend/src/views/chat/index.vue b/frontend/src/views/chat/index.vue index 47584c305..3d8b7c821 100644 --- a/frontend/src/views/chat/index.vue +++ b/frontend/src/views/chat/index.vue @@ -1195,6 +1195,8 @@ onMounted(() => { align-items: center; padding-left: 56px; padding-right: 56px; + position: relative; + z-index: 10; &.no-sidebar { padding-left: 96px; diff --git a/frontend/src/views/system/authentication/index.vue b/frontend/src/views/system/authentication/index.vue index 5a52a538d..62d43315d 100644 --- a/frontend/src/views/system/authentication/index.vue +++ b/frontend/src/views/system/authentication/index.vue @@ -183,11 +183,14 @@ init(true) .authentication-card { width: calc(25% - 12px); min-width: 230px; - height: 116px; - padding: 24px; + height: 100px; + padding: 16px; border-radius: 12px; background-color: #fff; border: 1px solid #dee0e3; + &:hover { + box-shadow: 0px 6px 24px 0px #1f232914; + } .inner-card { position: relative; .inner-card-info { @@ -196,6 +199,8 @@ init(true) align-items: center; .card-info-left { width: calc(100% - 40px); + display: flex; + align-items: center; .card-span { font-family: var(--de-custom_font, 'PingFang'); font-size: 16px; @@ -212,12 +217,12 @@ init(true) } .card-status { margin-left: 8px; - padding: 1px 6px; - border-radius: 2px; + padding: 0 4px; + border-radius: 4px; background-color: #f54a4533; color: #d03f3b; - line-height: 22px; - font-size: 14px; + line-height: 20px; + font-size: 12px; font-weight: 400; } } diff --git a/frontend/src/views/system/platform/PlatformInfo.vue b/frontend/src/views/system/platform/PlatformInfo.vue index db7956cb3..5b08a0ce2 100644 --- a/frontend/src/views/system/platform/PlatformInfo.vue +++ b/frontend/src/views/system/platform/PlatformInfo.vue @@ -3,8 +3,8 @@
- - + + {{ cardTitle }}
@@ -182,18 +182,13 @@ const validateHandler = () => { .platform-setting-head-left { display: flex; + align-items: center; .lead-left-icon { display: flex; line-height: 24px; align-items: center; - i { - width: 24px; - height: 24px; - font-size: 20px; - } + span { - margin-left: 4px; - font-family: var(--de-custom_font, 'PingFang'); font-size: 16px; font-style: normal; font-weight: 500; @@ -201,15 +196,15 @@ const validateHandler = () => { } } .lead-left-status { - margin-left: 4px; - height: 24px; + margin-left: 8px; + height: 20px; background: #34c72433; - padding: 0 6px; - font-size: 14px; + padding: 0 4px; + font-size: 12px; border-radius: 2px; overflow: hidden; span { - line-height: 24px; + line-height: 20px; color: #2ca91f; } } diff --git a/frontend/src/views/system/platform/index.vue b/frontend/src/views/system/platform/index.vue index 534551213..f4b534ba1 100644 --- a/frontend/src/views/system/platform/index.vue +++ b/frontend/src/views/system/platform/index.vue @@ -59,9 +59,8 @@ onMounted(() => { flex-direction: column; } .container-sys-platform { - padding: 24px; + padding: 16px; overflow: hidden; - //border-radius: 4px; background: var(--ContentBG, #ffffff); border-radius: 12px; From 8a1a9e01d300daa7b594947feca277e45ab524c2 Mon Sep 17 00:00:00 2001 From: ulleo Date: Thu, 29 Jan 2026 16:53:13 +0800 Subject: [PATCH 008/325] feat: add chat log step display #324 #742 --- .../versions/063_update_chat_log_dll.py | 36 +++++++++++++++++++ backend/apps/chat/curd/chat.py | 20 +++++++++-- backend/apps/chat/models/chat_model.py | 5 +++ backend/apps/chat/task/llm.py | 22 +++++++----- frontend/src/api/chat.ts | 11 ++++-- frontend/src/views/chat/ExecutionDetails.vue | 21 ++++++++--- 6 files changed, 96 insertions(+), 19 deletions(-) create mode 100644 backend/alembic/versions/063_update_chat_log_dll.py diff --git a/backend/alembic/versions/063_update_chat_log_dll.py b/backend/alembic/versions/063_update_chat_log_dll.py new file mode 100644 index 000000000..f4c8e01d1 --- /dev/null +++ b/backend/alembic/versions/063_update_chat_log_dll.py @@ -0,0 +1,36 @@ +"""063_update_chat_log_dll + +Revision ID: c8751179a8de +Revises: c9ab05247503 +Create Date: 2026-01-29 14:41:21.022781 + +""" +from alembic import op +import sqlalchemy as sa +import sqlmodel.sql.sqltypes +from sqlalchemy.dialects import postgresql + +# revision identifiers, used by Alembic. +revision = 'c8751179a8de' +down_revision = 'c9ab05247503' +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.add_column('chat_log', sa.Column('error', sa.Boolean(), nullable=True)) + sql = ''' + UPDATE chat_log SET error = false + ''' + op.execute(sql) + op.alter_column('chat_log', 'error', + existing_type=sa.BOOLEAN(), + nullable=False) + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.drop_column('chat_log', 'error') + # ### end Alembic commands ### diff --git a/backend/apps/chat/curd/chat.py b/backend/apps/chat/curd/chat.py index d87c3e517..799c19f73 100644 --- a/backend/apps/chat/curd/chat.py +++ b/backend/apps/chat/curd/chat.py @@ -515,7 +515,8 @@ def format_record(record: ChatRecordResult): return _dict -def get_chat_log_history(session: SessionDep, chat_record_id: int, current_user: CurrentUser, without_steps: bool = False) -> ChatLogHistory: +def get_chat_log_history(session: SessionDep, chat_record_id: int, current_user: CurrentUser, + without_steps: bool = False) -> ChatLogHistory: """ 获取ChatRecord的详细历史记录 @@ -573,6 +574,7 @@ def get_chat_log_history(session: SessionDep, chat_record_id: int, current_user: # 获取操作类型的枚举名称 operate_name = None + message = None if log.operate: # 如果是OperationEnum枚举实例 if isinstance(log.operate, OperationEnum): @@ -590,6 +592,14 @@ def get_chat_log_history(session: SessionDep, chat_record_id: int, current_user: else: operate_name = str(log.operate) + if log.messages is not None: + message = log.messages + if not log.operate == OperationEnum.CHOOSE_TABLE: + try: + message = orjson.loads(log.messages) + except Exception: + pass + # 创建ChatLogHistoryItem history_item = ChatLogHistoryItem( start_time=log.start_time, @@ -597,7 +607,9 @@ def get_chat_log_history(session: SessionDep, chat_record_id: int, current_user: duration=duration, total_tokens=log_tokens, operate=operate_name, - local_operation=log.local_operation + local_operation=log.local_operation, + error=log.error, + message=message, ) steps.append(history_item) @@ -622,6 +634,7 @@ def get_chat_log_history(session: SessionDep, chat_record_id: int, current_user: return chat_log_history + def get_chat_brief_generate(session: SessionDep, chat_id: int): chat = get_chat(session=session, chat_id=chat_id) if chat is not None and chat.brief_generate is not None: @@ -1055,7 +1068,8 @@ def save_error_message(session: SessionDep, record_id: int, message: str) -> Cha # log error finish stmt = update(ChatLog).where(and_(ChatLog.pid == record.id, ChatLog.finish_time.is_(None))).values( - finish_time=record.finish_time + finish_time=record.finish_time, + error=True ) session.execute(stmt) session.commit() diff --git a/backend/apps/chat/models/chat_model.py b/backend/apps/chat/models/chat_model.py index defed2e39..546e24d74 100644 --- a/backend/apps/chat/models/chat_model.py +++ b/backend/apps/chat/models/chat_model.py @@ -47,6 +47,7 @@ class OperationEnum(Enum): EXECUTE_SQL = '12' GENERATE_PICTURE = '13' + class ChatFinishStep(Enum): GENERATE_SQL = 1 QUERY_DATA = 2 @@ -77,6 +78,7 @@ class ChatLog(SQLModel, table=True): finish_time: datetime = Field(sa_column=Column(DateTime(timezone=False), nullable=True)) token_usage: Optional[dict | None | int] = Field(sa_column=Column(JSONB)) local_operation: bool = Field(default=False) + error: bool = Field(default=False) class Chat(SQLModel, table=True): @@ -195,6 +197,9 @@ class ChatLogHistoryItem(BaseModel): total_tokens: Optional[int] = None # token总消耗 operate: Optional[str] = None local_operation: Optional[bool] = False + message: Optional[str | dict | list[dict]] = None + error: Optional[bool] = False + class ChatLogHistory(BaseModel): start_time: Optional[datetime] = None diff --git a/backend/apps/chat/task/llm.py b/backend/apps/chat/task/llm.py index 7f6ab1317..7eae4bec8 100644 --- a/backend/apps/chat/task/llm.py +++ b/backend/apps/chat/task/llm.py @@ -220,7 +220,6 @@ def init_messages(self, session: Session): self.sql_message.append(SystemMessage( content=self.chat_question.sql_sys_question(self.ds.type, self.enable_sql_row_limit))) if last_sql_messages is not None and len(last_sql_messages) > 0: - # 获取最后3轮对话 last_rounds = get_last_conversation_rounds(last_sql_messages, rounds=count_limit) for _msg_dict in last_rounds: @@ -234,20 +233,27 @@ def init_messages(self, session: Session): last_chart_messages: List[dict[str, Any]] = self.generate_chart_logs[-1].messages if len( self.generate_chart_logs) > 0 else [] + if self.chat_question.regenerate_record_id: + # filter record before regenerate_record_id + _temp_log = next( + filter(lambda obj: obj.pid == self.chat_question.regenerate_record_id, self.generate_chart_logs), None) + last_chart_messages: List[dict[str, Any]] = _temp_log.messages if _temp_log else [] + + count_chart_limit = self.base_message_round_count_limit self.chart_message = [] # add sys prompt self.chart_message.append(SystemMessage(content=self.chat_question.chart_sys_question())) - if last_chart_messages is not None and len(last_chart_messages) > 0: - # limit count - for last_chart_message in last_chart_messages: + last_rounds = get_last_conversation_rounds(last_chart_messages, rounds=count_chart_limit) + + for _msg_dict in last_rounds: _msg: BaseMessage - if last_chart_message.get('type') == 'human': - _msg = HumanMessage(content=last_chart_message.get('content')) + if _msg_dict.get('type') == 'human': + _msg = HumanMessage(content=_msg_dict.get('content')) self.chart_message.append(_msg) - elif last_chart_message.get('type') == 'ai': - _msg = AIMessage(content=last_chart_message.get('content')) + elif _msg_dict.get('type') == 'ai': + _msg = AIMessage(content=_msg_dict.get('content')) self.chart_message.append(_msg) def init_record(self, session: Session) -> ChatRecord: diff --git a/frontend/src/api/chat.ts b/frontend/src/api/chat.ts index 2b1d85f5e..71c8b25f5 100644 --- a/frontend/src/api/chat.ts +++ b/frontend/src/api/chat.ts @@ -301,6 +301,7 @@ export class ChatLogHistoryItem { total_tokens?: number | undefined operate?: string | undefined local_operation?: boolean | undefined + error?: boolean | undefined constructor() constructor( @@ -309,7 +310,8 @@ export class ChatLogHistoryItem { duration: number | undefined, total_tokens: number | undefined, operate: string | undefined, - local_operation: boolean | undefined + local_operation: boolean | undefined, + error: boolean | undefined ) constructor( start_time?: Date | string, @@ -317,7 +319,8 @@ export class ChatLogHistoryItem { duration?: number | undefined, total_tokens?: number | undefined, operate?: string | undefined, - local_operation?: boolean | undefined + local_operation?: boolean | undefined, + error?: boolean | undefined ) { this.start_time = getDate(start_time) this.finish_time = getDate(finish_time) @@ -325,6 +328,7 @@ export class ChatLogHistoryItem { this.total_tokens = total_tokens this.operate = t('chat.log.' + operate) this.local_operation = !!local_operation + this.error = !!error } } @@ -368,7 +372,8 @@ const toChatLogHistoryItem = (data?: any): any | undefined => { data.duration, data.total_tokens, data.operate, - data.local_operation + data.local_operation, + data.error ) } diff --git a/frontend/src/views/chat/ExecutionDetails.vue b/frontend/src/views/chat/ExecutionDetails.vue index 455d0d379..9e9eec68f 100644 --- a/frontend/src/views/chat/ExecutionDetails.vue +++ b/frontend/src/views/chat/ExecutionDetails.vue @@ -51,13 +51,22 @@ defineExpose({
{{ t('parameter.execution_details') }}
-
-
{{ ele.operate }}
+
+
+ {{ ele.operate }} +
+
+ {{ ele.total_tokens }} tokens +
{{ ele.duration }}s
- - + +
@@ -94,7 +103,6 @@ defineExpose({ .name { float: left; color: #646a73; - font-family: PingFang SC; font-weight: 400; font-size: 14px; line-height: 22px; @@ -132,6 +140,9 @@ defineExpose({ font-weight: 500; font-size: 14px; line-height: 22px; + display: flex; + flex-direction: row; + gap: 12px; } .time { From dc4b3fda4a789c8e492b16f93df56e9894031138 Mon Sep 17 00:00:00 2001 From: dataeaseShu Date: Thu, 29 Jan 2026 17:07:24 +0800 Subject: [PATCH 009/325] fix(Advanced Application): In the Assistant's floating window mode at normal size, the execution consumption window is not fully displayed. --- frontend/src/views/chat/ExecutionDetails.vue | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/frontend/src/views/chat/ExecutionDetails.vue b/frontend/src/views/chat/ExecutionDetails.vue index 9e9eec68f..9695f8b6a 100644 --- a/frontend/src/views/chat/ExecutionDetails.vue +++ b/frontend/src/views/chat/ExecutionDetails.vue @@ -10,8 +10,10 @@ import { useI18n } from 'vue-i18n' const { t } = useI18n() const logHistory = ref({}) const dialogFormVisible = ref(false) +const drawerSize = ref('600px') function getLogList(recordId: any) { + drawerSize.value = window.innerWidth < 500 ? '460px' : '600px' chatApi.get_chart_log_history(recordId).then((res) => { logHistory.value = chatApi.toChatLogHistory(res) as ChatLogHistory dialogFormVisible.value = true @@ -29,7 +31,7 @@ defineExpose({ :title="t('parameter.execution_details')" destroy-on-close modal-class="execution-details" - size="600px" + :size="drawerSize" >
{{ t('parameter.overview') }}
@@ -89,7 +91,7 @@ defineExpose({ justify-content: space-between; margin-bottom: 24px; .item { - width: 268px; + width: calc(50% - 8px); height: 86px; border-radius: 12px; border: 1px solid #dee0e3; @@ -106,7 +108,7 @@ defineExpose({ font-weight: 400; font-size: 14px; line-height: 22px; - width: 180px; + width: 64%; } .value { @@ -122,7 +124,7 @@ defineExpose({ .list { .list-item { - width: 552px; + width: 100%; height: 54px; border-radius: 12px; border: 1px solid #dee0e3; From 733227dfe1b0063952f30431ef43c88ef4193d56 Mon Sep 17 00:00:00 2001 From: ulleo Date: Thu, 29 Jan 2026 17:09:12 +0800 Subject: [PATCH 010/325] feat: add chat log step display #324 #742 --- backend/apps/chat/models/chat_model.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/apps/chat/models/chat_model.py b/backend/apps/chat/models/chat_model.py index 546e24d74..c78646fc7 100644 --- a/backend/apps/chat/models/chat_model.py +++ b/backend/apps/chat/models/chat_model.py @@ -197,7 +197,7 @@ class ChatLogHistoryItem(BaseModel): total_tokens: Optional[int] = None # token总消耗 operate: Optional[str] = None local_operation: Optional[bool] = False - message: Optional[str | dict | list[dict]] = None + message: Optional[str | dict | list] = None error: Optional[bool] = False From 041a0a3a9ee27bfc3bf0aed7b0dedd666d66298a Mon Sep 17 00:00:00 2001 From: ulleo Date: Fri, 30 Jan 2026 16:24:15 +0800 Subject: [PATCH 011/325] feat: improve template --- backend/templates/sql_examples/Oracle.yaml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/backend/templates/sql_examples/Oracle.yaml b/backend/templates/sql_examples/Oracle.yaml index 44e7ebedc..60d36d477 100644 --- a/backend/templates/sql_examples/Oracle.yaml +++ b/backend/templates/sql_examples/Oracle.yaml @@ -3,13 +3,13 @@ template: 1. 分析用户问题,确定查询需求 2. 根据表结构生成基础SQL - 3. 强制检查:SQL是否包含GROUP BY/聚合函数? - 4. 如果是GROUP BY查询:必须使用外层查询结构包裹 - 5. 强制检查:应用数据量限制规则 - 6. 应用其他规则(引号、别名等) - 7. 最终验证:GROUP BY查询的ROWNUM位置是否正确? - 8. 强制检查:检查语法是否正确? - 9. 确定图表类型 + 3. 强制检查:验证SQL中使用的表名和字段名是否在中定义 + 4. 强制检查:应用数据量限制规则(默认限制或用户指定数量) + 5. 应用其他规则(引号、别名、格式化等) + 6. 最终验证:GROUP BY查询的ROWNUM位置是否正确? + 7. 强制检查:验证SQL语法是否符合规范 + 8. 确定图表类型(根据规则选择table/column/bar/line/pie) + 9. 确定对话标题 10. 返回JSON结果 From 9decb9189bca36fdee38048cefc739352f70656b Mon Sep 17 00:00:00 2001 From: ulleo Date: Fri, 30 Jan 2026 17:05:38 +0800 Subject: [PATCH 012/325] perf: Assistant uses workspace-level Q&A configuration --- .../apps/data_training/curd/data_training.py | 30 +++++---------- frontend/src/views/system/training/index.vue | 38 ++++++------------- 2 files changed, 22 insertions(+), 46 deletions(-) diff --git a/backend/apps/data_training/curd/data_training.py b/backend/apps/data_training/curd/data_training.py index 175f6c812..07cee6bfe 100644 --- a/backend/apps/data_training/curd/data_training.py +++ b/backend/apps/data_training/curd/data_training.py @@ -162,10 +162,7 @@ def create_training(session: SessionDep, info: DataTrainingInfo, oid: int, trans # 检查数据源和高级应用不能同时为空 if info.datasource is None and info.advanced_application is None: - if oid == 1: - raise Exception(trans("i18n_data_training.datasource_assistant_cannot_be_none")) - else: - raise Exception(trans("i18n_data_training.datasource_cannot_be_none")) + raise Exception(trans("i18n_data_training.datasource_assistant_cannot_be_none")) # 检查重复记录 stmt = select(DataTraining.id).where( @@ -221,10 +218,7 @@ def update_training(session: SessionDep, info: DataTrainingInfo, oid: int, trans raise Exception(trans("i18n_data_training.description_cannot_be_empty")) if info.datasource is None and info.advanced_application is None: - if oid == 1: - raise Exception(trans("i18n_data_training.datasource_assistant_cannot_be_none")) - else: - raise Exception(trans("i18n_data_training.datasource_cannot_be_none")) + raise Exception(trans("i18n_data_training.datasource_assistant_cannot_be_none")) count = session.query(DataTraining).filter( DataTraining.id == info.id @@ -310,11 +304,11 @@ def batch_create_training(session: SessionDep, info_list: List[DataTrainingInfo] datasource_name_to_id[ds.name.strip()] = ds.id assistant_name_to_id = {} - if oid == 1: - assistant_stmt = select(AssistantModel.id, AssistantModel.name).where(AssistantModel.type == 1) - assistant_result = session.execute(assistant_stmt).all() - for assistant in assistant_result: - assistant_name_to_id[assistant.name.strip()] = assistant.id + + assistant_stmt = select(AssistantModel.id, AssistantModel.name).where(and_(AssistantModel.type == 1, AssistantModel.oid == oid)) + assistant_result = session.execute(assistant_stmt).all() + for assistant in assistant_result: + assistant_name_to_id[assistant.name.strip()] = assistant.id # 验证和转换数据 valid_records = [] @@ -338,7 +332,7 @@ def batch_create_training(session: SessionDep, info_list: List[DataTrainingInfo] # 高级应用验证和转换 advanced_application_id = None - if oid == 1 and info.advanced_application_name and info.advanced_application_name.strip(): + if info.advanced_application_name and info.advanced_application_name.strip(): if info.advanced_application_name.strip() in assistant_name_to_id: advanced_application_id = assistant_name_to_id[info.advanced_application_name.strip()] else: @@ -346,12 +340,8 @@ def batch_create_training(session: SessionDep, info_list: List[DataTrainingInfo] trans("i18n_data_training.advanced_application_not_found").format(info.advanced_application_name)) # 检查数据源和高级应用不能同时为空 - if oid == 1: - if not datasource_id and not advanced_application_id: - error_messages.append(trans("i18n_data_training.datasource_assistant_cannot_be_none")) - else: - if not datasource_id: - error_messages.append(trans("i18n_data_training.datasource_cannot_be_none")) + if not datasource_id and not advanced_application_id: + error_messages.append(trans("i18n_data_training.datasource_assistant_cannot_be_none")) if error_messages: failed_records.append({ diff --git a/frontend/src/views/system/training/index.vue b/frontend/src/views/system/training/index.vue index ac2f781a2..013722751 100644 --- a/frontend/src/views/system/training/index.vue +++ b/frontend/src/views/system/training/index.vue @@ -11,7 +11,6 @@ import IconOpeDelete from '@/assets/svg/icon_delete.svg' import icon_searchOutline_outlined from '@/assets/svg/icon_search-outline_outlined.svg' import EmptyBackground from '@/views/dashboard/common/EmptyBackground.vue' import { useClipboard } from '@vueuse/core' -import { useUserStore } from '@/stores/user' import { useI18n } from 'vue-i18n' import { cloneDeep } from 'lodash-es' import { getAdvancedApplicationList } from '@/api/embedded.ts' @@ -26,7 +25,6 @@ interface Form { advanced_application_name: string | null description: string | null } -const userStore = useUserStore() const { t } = useI18n() const multipleSelectionAll = ref([]) const keywords = ref('') @@ -42,10 +40,6 @@ onMounted(() => { search() }) -const isDefaultOrg = computed(() => { - return userStore.oid === '1' -}) - const dialogFormVisible = ref(false) const multipleTableRef = ref() const isIndeterminate = ref(true) @@ -261,14 +255,13 @@ const rules = computed(() => { }, ], } - if (!isDefaultOrg.value) { - _list.datasource = [ - { - required: true, - message: t('datasource.Please_select') + t('common.empty') + t('ds.title'), - }, - ] - } + // _list.datasource = [ + // { + // required: true, + // message: t('datasource.Please_select') + t('common.empty') + t('ds.title'), + // }, + // ] + return _list }) @@ -276,11 +269,9 @@ const list = () => { datasourceApi.list().then((res: any) => { options.value = res || [] }) - if (isDefaultOrg.value) { - getAdvancedApplicationList().then((res: any) => { - adv_options.value = res || [] - }) - } + getAdvancedApplicationList().then((res: any) => { + adv_options.value = res || [] + }) } const saveHandler = () => { @@ -426,7 +417,6 @@ const onRowFormClose = () => { { - + { {{ pageForm.datasource_name }}
- +
{{ pageForm.advanced_application_name }}
From e2af93e781dcf3450c7e5e833a4fec0b5c1267c3 Mon Sep 17 00:00:00 2001 From: dataeaseShu Date: Fri, 30 Jan 2026 18:16:22 +0800 Subject: [PATCH 013/325] fix(Smart Questioning): The scrollbar in the chat has been optimized to allow for dragging. --- frontend/src/views/chat/ChatTokenTime.vue | 14 ++++++++++++++ frontend/src/views/chat/index.vue | 4 ++-- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/frontend/src/views/chat/ChatTokenTime.vue b/frontend/src/views/chat/ChatTokenTime.vue index e1e2ca7ab..dcdfb3754 100644 --- a/frontend/src/views/chat/ChatTokenTime.vue +++ b/frontend/src/views/chat/ChatTokenTime.vue @@ -49,6 +49,20 @@ function getLogList() { margin-left: auto; display: flex; align-items: center; + position: relative; + &:hover { + &::after { + content: ''; + background: #1f23291a; + border-radius: 6px; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + width: 84px; + height: 26px; + position: absolute; + } + } } } diff --git a/frontend/src/views/chat/index.vue b/frontend/src/views/chat/index.vue index 3d8b7c821..3c791d6ee 100644 --- a/frontend/src/views/chat/index.vue +++ b/frontend/src/views/chat/index.vue @@ -1175,6 +1175,8 @@ onMounted(() => { border-radius: 0 12px 12px 0; .no-horizontal.ed-scrollbar { + position: relative; + z-index: 10; .ed-scrollbar__bar.is-horizontal { display: none; } @@ -1195,8 +1197,6 @@ onMounted(() => { align-items: center; padding-left: 56px; padding-right: 56px; - position: relative; - z-index: 10; &.no-sidebar { padding-left: 96px; From e8508681a34befdf35163c8b6c11e23b3ad40f01 Mon Sep 17 00:00:00 2001 From: junjun Date: Mon, 2 Feb 2026 10:05:03 +0800 Subject: [PATCH 014/325] fix: check sql only contain read operation --- backend/apps/db/db.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/backend/apps/db/db.py b/backend/apps/db/db.py index 239b51018..1d0477c02 100644 --- a/backend/apps/db/db.py +++ b/backend/apps/db/db.py @@ -582,6 +582,8 @@ def check_sql_read(sql: str, ds: CoreDatasource | AssistantOutDsSchema): dialect = None if ds.type == "mysql" or ds.type == "doris" or ds.type == "starrocks": dialect = 'mysql' + elif ds.type == "sqlServer": + dialect = 'tsql' statements = sqlglot.parse(sql, dialect=dialect) From 16617a03366cef1ae4ac92274b9c9fdc3f7cd6d9 Mon Sep 17 00:00:00 2001 From: dataeaseShu Date: Mon, 2 Feb 2026 10:18:15 +0800 Subject: [PATCH 015/325] fix(Basic Application): The history window embedded in the floating window can still display its frame even after it is hidden. --- frontend/src/views/chat/chat-block/ChartBlock.vue | 5 +++-- frontend/src/views/embedded/index.vue | 1 - 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/frontend/src/views/chat/chat-block/ChartBlock.vue b/frontend/src/views/chat/chat-block/ChartBlock.vue index e4e9ae930..57981a46b 100644 --- a/frontend/src/views/chat/chat-block/ChartBlock.vue +++ b/frontend/src/views/chat/chat-block/ChartBlock.vue @@ -561,6 +561,7 @@ watch( .chart-fullscreen-dialog-body { padding: 0; + height: 100%; } .chart-sql-drawer-body { @@ -647,6 +648,7 @@ watch( border: unset; border-radius: unset; padding: 0; + height: 100%; .header-bar { border-bottom: 1px solid rgba(31, 35, 41, 0.15); @@ -657,8 +659,7 @@ watch( .chart-block { margin: unset; padding: 16px; - - height: calc(100vh - 56px); + height: calc(100% - 56px); } } diff --git a/frontend/src/views/embedded/index.vue b/frontend/src/views/embedded/index.vue index 0d2d2e01a..4a6994062 100644 --- a/frontend/src/views/embedded/index.vue +++ b/frontend/src/views/embedded/index.vue @@ -1,7 +1,6 @@ {{ $t('user.filter') }} + + + +
+
+
+ +
{{ $t(ele.name) }}
+
+
+
+
-
+ + + + + + + {{ $t('qa.start_sqlbot') }} +
@@ -149,13 +133,10 @@ const pageLogo = computed(() => { } .center { width: 100%; - position: absolute; - top: 50%; - left: 50%; - transform: translate(-50%, -50%); display: flex; align-items: center; flex-direction: column; + margin-top: 180px; .i-am { font-weight: 600; @@ -182,37 +163,37 @@ const pageLogo = computed(() => { } .content { width: calc(100% - 32px); + margin-top: 8px; + margin-left: 16px; - .textarea-send { + .greeting-btn { width: 100%; - position: relative; - .ed-textarea { - width: 100%; - } + height: 88px; + border-radius: 16px; + border-style: dashed; - :deep(.ed-textarea__inner) { - padding: 12px 12px 52px 12px; - font-weight: 400; - font-size: 16px; - line-height: 24px; - border-radius: 16px; + .inner-icon { + display: flex; + flex-direction: row; + align-items: center; - &::placeholder { - color: #8f959e; - } + margin-right: 6px; } - img { - cursor: pointer; - position: absolute; - right: 12px; - bottom: 12px; - } - } + font-size: 16px; + line-height: 24px; + font-weight: 500; - position: absolute; - bottom: 16px; - left: 16px; + --ed-button-text-color: var(--ed-color-primary, rgba(28, 186, 144, 1)); + --ed-button-hover-text-color: var(--ed-color-primary, rgba(28, 186, 144, 1)); + --ed-button-active-text-color: var(--ed-color-primary, rgba(28, 186, 144, 1)); + --ed-button-bg-color: rgba(248, 249, 250, 1); + --ed-button-hover-bg-color: var(--ed-color-primary-1a, #1cba901a); + --ed-button-border-color: rgba(217, 220, 223, 1); + --ed-button-hover-border-color: var(--ed-color-primary, rgba(28, 186, 144, 1)); + --ed-button-active-bg-color: var(--ed-color-primary-33, #1cba9033); + --ed-button-active-border-color: var(--ed-color-primary, rgba(28, 186, 144, 1)); + } } .drawer-assistant { diff --git a/frontend/src/views/system/user/User.vue b/frontend/src/views/system/user/User.vue index 799c85a4c..42e44651d 100644 --- a/frontend/src/views/system/user/User.vue +++ b/frontend/src/views/system/user/User.vue @@ -24,7 +24,21 @@ {{ $t('user.filter') }} - + + + + {{ t('sync.sync_users') }} + + + +