j)rD}=;sSz5!kV)`gX*1rsiVa%W
z{7_LSt@fLgbHj4j(a-np$WZpe4YjO}?fr?;dacx7
zgj#nNhruumYFHlY44d!DG(*>x(*`zE1(n!dEvvfTAaTOuS~0Lu)atNPc(+UQIKN@f
zcW@q_Qx&;%YUJ3>cY;qz0E~gZAz72*^Tj^bx_xyP!HpTrS{568VsBjk-sV|Do`HH$
zc;A#Qo$9G8pt6kk=*NYJgJh;}n@lW_QUJ?g$A%VWx~<5cpEgrGV=#?o{T_X7+(D6m
z|BEE)*b3ileSTM*G@2E`{>rnXQ%!l7Z%>aMRdgEi6z!Kyuh@vHHdb4Ze(kc1UW*{f
zF1g`RuKAhMx4uWDy}e%3hjLnfI&G~7;(rYq?)GR%k_M>n=0|8@O3H5IV?)A`DIxd%
zCgUHUPY^_psr@GM{IfCY*-q7tZ48qI3GKpGG!h234sDPs?`gH=Fe5gg%rH0ed;
zrGJG+1vt0|^^^Qj1uO-I4tO&d*5GPj(B&uoAk~!%=chu+HAik8;1)K>U5DH~f*#0Y
zhde&Y<0BZte?(R-svdK&I(G3N0Wkj$C@TS2U>BA$_n|iwpk)S&_uy#jVsp>h=i)!-
CQDfZz
literal 0
HcmV?d00001
diff --git a/frontend/src/views/ds/js/ds-type.ts b/frontend/src/views/ds/js/ds-type.ts
index beeb7f435..f99a03a44 100644
--- a/frontend/src/views/ds/js/ds-type.ts
+++ b/frontend/src/views/ds/js/ds-type.ts
@@ -8,6 +8,7 @@ import dm from '@/assets/datasource/icon_dm.png'
import doris from '@/assets/datasource/icon_doris.png'
import redshift from '@/assets/datasource/icon_redshift.png'
import es from '@/assets/datasource/icon_es.png'
+import kingbase from '@/assets/datasource/icon_kingbase.png'
import { i18n } from '@/i18n'
const t = i18n.global.t
@@ -22,6 +23,7 @@ export const dsType = [
{ label: 'Apache Doris', value: 'doris' },
{ label: 'AWS Redshift', value: 'redshift' },
{ label: 'Elasticsearch', value: 'es' },
+ { label: 'Kingbase', value: 'kingbase' },
]
export const dsTypeWithImg = [
@@ -35,6 +37,7 @@ export const dsTypeWithImg = [
{ name: 'Apache Doris', type: 'doris', img: doris },
{ name: 'AWS Redshift', type: 'redshift', img: redshift },
{ name: 'Elasticsearch', type: 'es', img: es },
+ { name: 'Kingbase', type: 'kingbase', img: kingbase },
]
-export const haveSchema = ['sqlServer', 'pg', 'oracle', 'dm', 'redshift']
+export const haveSchema = ['sqlServer', 'pg', 'oracle', 'dm', 'redshift', 'kingbase']
From 3c35a3a8e403eb93298d5cf0b77308f3cf48b314 Mon Sep 17 00:00:00 2001
From: dataeaseShu
Date: Sun, 28 Sep 2025 15:14:57 +0800
Subject: [PATCH 0330/1125] feat(prompt): add prompt
---
frontend/src/api/prompt.ts | 11 +
frontend/src/components/layout/Menu.vue | 1 +
frontend/src/i18n/en.json | 16 +
frontend/src/i18n/zh-CN.json | 16 +
frontend/src/router/index.ts | 7 +
frontend/src/views/system/prompt/index.vue | 864 +++++++++++++++++++++
6 files changed, 915 insertions(+)
create mode 100644 frontend/src/api/prompt.ts
create mode 100644 frontend/src/views/system/prompt/index.vue
diff --git a/frontend/src/api/prompt.ts b/frontend/src/api/prompt.ts
new file mode 100644
index 000000000..01d73488d
--- /dev/null
+++ b/frontend/src/api/prompt.ts
@@ -0,0 +1,11 @@
+import { request } from '@/utils/request'
+
+export const promptApi = {
+ getList: (pageNum: any, pageSize: any, type: any, params: any) =>
+ request.get(`/system/custom_prompt/${type}/page/${pageNum}/${pageSize}`, {
+ params,
+ }),
+ updateEmbedded: (data: any) => request.put(`/system/custom_prompt`, data),
+ deleteEmbedded: (params: any) => request.delete('/system/custom_prompt', { data: params }),
+ getOne: (id: any) => request.get(`/system/custom_prompt/${id}`),
+}
diff --git a/frontend/src/components/layout/Menu.vue b/frontend/src/components/layout/Menu.vue
index 24143e951..0cd0425e8 100644
--- a/frontend/src/components/layout/Menu.vue
+++ b/frontend/src/components/layout/Menu.vue
@@ -51,6 +51,7 @@ const routerList = computed(() => {
!route.path.includes('member') &&
!route.path.includes('professional') &&
!route.path.includes('training') &&
+ !route.path.includes('prompt') &&
!route.path.includes('permission') &&
!route.path.includes('preview') &&
route.path !== '/login' &&
diff --git a/frontend/src/i18n/en.json b/frontend/src/i18n/en.json
index 7c6a2df91..5c6847a3d 100644
--- a/frontend/src/i18n/en.json
+++ b/frontend/src/i18n/en.json
@@ -6,6 +6,22 @@
"Dashboard": "Dashboard",
"AI Model Configuration": "AI Model Configuration"
},
+ "prompt": {
+ "no_prompt_words": "No prompt words",
+ "customize_prompt_words": "Customize prompt words",
+ "ask_sql": "Ask SQL",
+ "data_analysis": "Data Analysis",
+ "data_prediction": "Data Prediction",
+ "add_prompt_word": "Add prompt word",
+ "prompt_word_name": "Prompt word name",
+ "prompt_word_content": "Prompt word content",
+ "training_data_details": "Training data details",
+ "selected_prompt_words": "Do you want to delete the {msg} selected prompt words?",
+ "replaced_with": "Please enter a prompt word. Example: When returning names, only the last name will be displayed, and the first name will be replaced with *.",
+ "loss_exercise_caution": "Entering high-risk operations such as deleting a database or table or modifying the table structure may result in permanent data loss. Exercise caution!",
+ "edit_prompt_word": "Edit prompt word",
+ "prompt_word_name_de": "Do you want to delete the prompt word: {msg}?"
+ },
"training": {
"effective_data_sources": "Effective data sources",
"all_data_sources": "All data sources",
diff --git a/frontend/src/i18n/zh-CN.json b/frontend/src/i18n/zh-CN.json
index dc77bf278..8511a3f3d 100644
--- a/frontend/src/i18n/zh-CN.json
+++ b/frontend/src/i18n/zh-CN.json
@@ -6,6 +6,22 @@
"Dashboard": "仪表盘",
"AI Model Configuration": "模型配置"
},
+ "prompt": {
+ "no_prompt_words": "暂无提示词",
+ "customize_prompt_words": "自定义提示词",
+ "ask_sql": "问数 SQL",
+ "data_analysis": "数据分析",
+ "data_prediction": "数据预测",
+ "add_prompt_word": "添加提示词",
+ "prompt_word_name": "提示词名称",
+ "prompt_word_content": "提示词内容",
+ "training_data_details": "训练数据详情",
+ "selected_prompt_words": "是否删除选中的 {msg} 条提示词?",
+ "replaced_with": "请输入提示词,示例:返回姓名时,只显示姓氏,名字用*代替",
+ "loss_exercise_caution": "若输入删除数据库、数据表或修改表结构等高风险的操作指令,可能导致数据永久丢失,请务必谨慎!",
+ "edit_prompt_word": "编辑提示词",
+ "prompt_word_name_de": "是否删除提示词:{msg}?"
+ },
"training": {
"effective_data_sources": "生效数据源",
"all_data_sources": "所有数据源",
diff --git a/frontend/src/router/index.ts b/frontend/src/router/index.ts
index 00fad1666..84e298596 100644
--- a/frontend/src/router/index.ts
+++ b/frontend/src/router/index.ts
@@ -17,6 +17,7 @@ import EmbeddedPage from '@/views/embedded/page.vue'
import Member from '@/views/system/member/index.vue'
import Professional from '@/views/system/professional/index.vue'
import Training from '@/views/system/training/index.vue'
+import Prompt from '@/views/system/prompt/index.vue'
import Appearance from '@/views/system/appearance/index.vue'
import Permission from '@/views/system/permission/index.vue'
import User from '@/views/system/user/User.vue'
@@ -120,6 +121,12 @@ export const routes = [
component: Training,
meta: { title: t('training.data_training') },
},
+ {
+ path: '/set/prompt',
+ name: 'prompt',
+ component: Prompt,
+ meta: { title: t('prompt.customize_prompt_words') },
+ },
],
},
{
diff --git a/frontend/src/views/system/prompt/index.vue b/frontend/src/views/system/prompt/index.vue
new file mode 100644
index 000000000..1e3d39ce2
--- /dev/null
+++ b/frontend/src/views/system/prompt/index.vue
@@ -0,0 +1,864 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ t('training.all_data_sources') }}
+
+
+
+
+ {{ formatTimestamp(scope.row.create_time, 'YYYY-MM-DD HH:mm:ss') }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ $t('prompt.add_prompt_word') }}
+
+
+
+
+
+
+
+
+
+
+
+ {{ $t('datasource.select_all') }}
+
+
+
+
+
+ {{
+ $t('user.selected_2_items', { msg: multipleSelectionAll.length })
+ }}
+
+
+ {{ $t('common.cancel') }}
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ t('prompt.loss_exercise_caution') }}
+
+
+
+
+
+ {{ $t('training.all_data_sources') }}
+ {{ $t('training.partial_data_sources') }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ pageForm.name }}
+
+
+
+
+ {{ pageForm.prompt }}
+
+
+
+
+
+
+
+
+
+
+
+ {{ pageForm.datasource_name.join() }}
+
+
+
+
+
+
+
+
From 1b25652ba49099b3c441886b83eee460a6fc655b Mon Sep 17 00:00:00 2001
From: dataeaseShu
Date: Sun, 28 Sep 2025 15:45:46 +0800
Subject: [PATCH 0331/1125] feat(prompt): add prompt
---
frontend/src/views/system/prompt/index.vue | 2 ++
1 file changed, 2 insertions(+)
diff --git a/frontend/src/views/system/prompt/index.vue b/frontend/src/views/system/prompt/index.vue
index 1e3d39ce2..041110ae5 100644
--- a/frontend/src/views/system/prompt/index.vue
+++ b/frontend/src/views/system/prompt/index.vue
@@ -322,6 +322,8 @@ const handleChange = () => {
const typeChange = (val: any) => {
currentType.value = val
+ pageInfo.currentPage = 0
+ search()
}
From 75bd13a54f104e9d53b1c882f9dfe80acde46f95 Mon Sep 17 00:00:00 2001
From: ulleo
Date: Sun, 28 Sep 2025 19:18:38 +0800
Subject: [PATCH 0332/1125] feat(X-Pack): add custom prompt #213
---
backend/alembic/env.py | 1 +
.../alembic/versions/046_add_custom_prompt.py | 39 ++++++++++++
backend/apps/chat/models/chat_model.py | 10 ++-
backend/apps/chat/task/llm.py | 18 ++++++
backend/apps/terminology/curd/terminology.py | 4 +-
backend/locales/en.json | 4 ++
backend/locales/zh-CN.json | 4 ++
backend/pyproject.toml | 2 +-
backend/template.yaml | 61 ++++++++++++++-----
frontend/src/views/system/prompt/index.vue | 2 +-
10 files changed, 123 insertions(+), 22 deletions(-)
create mode 100644 backend/alembic/versions/046_add_custom_prompt.py
diff --git a/backend/alembic/env.py b/backend/alembic/env.py
index 79dfbdc68..16ef1c3e9 100755
--- a/backend/alembic/env.py
+++ b/backend/alembic/env.py
@@ -26,6 +26,7 @@
# from apps.settings.models.setting_models import SQLModel
# from apps.chat.models.chat_model import SQLModel
from apps.terminology.models.terminology_model import SQLModel
+#from apps.custom_prompt.models.custom_prompt_model import SQLModel
# from apps.data_training.models.data_training_model import SQLModel
# from apps.dashboard.models.dashboard_model import SQLModel
from common.core.config import settings # noqa
diff --git a/backend/alembic/versions/046_add_custom_prompt.py b/backend/alembic/versions/046_add_custom_prompt.py
new file mode 100644
index 000000000..692c0f3f5
--- /dev/null
+++ b/backend/alembic/versions/046_add_custom_prompt.py
@@ -0,0 +1,39 @@
+"""046_add_custom_prompt
+
+Revision ID: 8855aea2dd61
+Revises: 45e7e52bf2b8
+Create Date: 2025-09-28 13:57:01.509249
+
+"""
+from alembic import op
+import sqlalchemy as sa
+import sqlmodel.sql.sqltypes
+from sqlalchemy.dialects import postgresql
+
+# revision identifiers, used by Alembic.
+revision = '8855aea2dd61'
+down_revision = '45e7e52bf2b8'
+branch_labels = None
+depends_on = None
+
+
+def upgrade():
+ # ### commands auto generated by Alembic - please adjust! ###
+ op.create_table('custom_prompt',
+ sa.Column('id', sa.BigInteger(), sa.Identity(always=True), nullable=False),
+ sa.Column('oid', sa.BigInteger(), nullable=True),
+ sa.Column('type', sa.Enum('GENERATE_SQL', 'ANALYSIS', 'PREDICT_DATA', name='customprompttypeenum', native_enum=False, length=20), nullable=True),
+ sa.Column('create_time', sa.DateTime(), nullable=True),
+ sa.Column('name', sqlmodel.sql.sqltypes.AutoString(length=255), nullable=True),
+ sa.Column('prompt', sa.Text(), nullable=True),
+ sa.Column('specific_ds', sa.Boolean(), nullable=True),
+ sa.Column('datasource_ids', postgresql.JSONB(astext_type=sa.Text()), nullable=True),
+ sa.PrimaryKeyConstraint('id')
+ )
+ # ### end Alembic commands ###
+
+
+def downgrade():
+ # ### commands auto generated by Alembic - please adjust! ###
+ op.drop_table('custom_prompt')
+ # ### end Alembic commands ###
diff --git a/backend/apps/chat/models/chat_model.py b/backend/apps/chat/models/chat_model.py
index 6cc30b839..d24e19e15 100644
--- a/backend/apps/chat/models/chat_model.py
+++ b/backend/apps/chat/models/chat_model.py
@@ -40,11 +40,13 @@ class OperationEnum(Enum):
CHOOSE_DATASOURCE = '6'
GENERATE_DYNAMIC_SQL = '7'
+
class ChatFinishStep(Enum):
GENERATE_SQL = 1
QUERY_DATA = 2
GENERATE_CHART = 3
+
# TODO choose table / check connection / generate description
class ChatLog(SQLModel, table=True):
@@ -177,12 +179,13 @@ class AiModelQuestion(BaseModel):
sub_query: Optional[list[dict]] = None
terminologies: str = ""
data_training: str = ""
+ custom_prompt: str = ""
error_msg: str = ""
def sql_sys_question(self):
return get_sql_template()['system'].format(engine=self.engine, schema=self.db_schema, question=self.question,
lang=self.lang, terminologies=self.terminologies,
- data_training=self.data_training)
+ data_training=self.data_training, custom_prompt=self.custom_prompt)
def sql_user_question(self, current_time: str):
return get_sql_template()['user'].format(engine=self.engine, schema=self.db_schema, question=self.question,
@@ -196,13 +199,14 @@ def chart_user_question(self, chart_type: Optional[str] = None):
chart_type=chart_type)
def analysis_sys_question(self):
- return get_analysis_template()['system'].format(lang=self.lang, terminologies=self.terminologies)
+ return get_analysis_template()['system'].format(lang=self.lang, terminologies=self.terminologies,
+ custom_prompt=self.custom_prompt)
def analysis_user_question(self):
return get_analysis_template()['user'].format(fields=self.fields, data=self.data)
def predict_sys_question(self):
- return get_predict_template()['system'].format(lang=self.lang)
+ return get_predict_template()['system'].format(lang=self.lang, custom_prompt=self.custom_prompt)
def predict_user_question(self):
return get_predict_template()['user'].format(fields=self.fields, data=self.data)
diff --git a/backend/apps/chat/task/llm.py b/backend/apps/chat/task/llm.py
index a30a741dd..9dce46a4f 100644
--- a/backend/apps/chat/task/llm.py
+++ b/backend/apps/chat/task/llm.py
@@ -30,6 +30,9 @@
get_last_execute_sql_error
from apps.chat.models.chat_model import ChatQuestion, ChatRecord, Chat, RenameChat, ChatLog, OperationEnum, \
ChatFinishStep
+from sqlbot_xpack.license.license_manage import SQLBotLicenseUtil
+from sqlbot_xpack.custom_prompt.curd.custom_prompt import find_custom_prompts
+from sqlbot_xpack.custom_prompt.models.custom_prompt_model import CustomPromptTypeEnum
from apps.data_training.curd.data_training import get_training_template
from apps.datasource.crud.datasource import get_table_schema
from apps.datasource.crud.permission import get_row_permission_filters, is_normal_user
@@ -244,6 +247,9 @@ def generate_analysis(self):
ds_id = self.ds.id if isinstance(self.ds, CoreDatasource) else None
self.chat_question.terminologies = get_terminology_template(self.session, self.chat_question.question,
self.current_user.oid, ds_id)
+ if SQLBotLicenseUtil.valid():
+ self.chat_question.custom_prompt = find_custom_prompts(self.session, CustomPromptTypeEnum.ANALYSIS,
+ self.current_user.oid, ds_id)
analysis_msg.append(SystemMessage(content=self.chat_question.analysis_sys_question()))
analysis_msg.append(HumanMessage(content=self.chat_question.analysis_user_question()))
@@ -288,6 +294,12 @@ def generate_predict(self):
self.chat_question.fields = orjson.dumps(fields).decode()
data = get_chat_chart_data(self.session, self.record.id)
self.chat_question.data = orjson.dumps(data.get('data')).decode()
+
+ if SQLBotLicenseUtil.valid():
+ ds_id = self.ds.id if isinstance(self.ds, CoreDatasource) else None
+ self.chat_question.custom_prompt = find_custom_prompts(self.session, CustomPromptTypeEnum.PREDICT_DATA,
+ self.current_user.oid, ds_id)
+
predict_msg: List[Union[BaseMessage, dict[str, Any]]] = []
predict_msg.append(SystemMessage(content=self.chat_question.predict_sys_question()))
predict_msg.append(HumanMessage(content=self.chat_question.predict_user_question()))
@@ -509,6 +521,9 @@ def select_datasource(self):
ds_id)
self.chat_question.data_training = get_training_template(self.session, self.chat_question.question, ds_id,
oid)
+ if SQLBotLicenseUtil.valid():
+ self.chat_question.custom_prompt = find_custom_prompts(self.session, CustomPromptTypeEnum.GENERATE_SQL,
+ oid, ds_id)
self.init_messages()
@@ -902,6 +917,9 @@ def run_task(self, in_chat: bool = True, stream: bool = True,
oid, ds_id)
self.chat_question.data_training = get_training_template(self.session, self.chat_question.question,
ds_id, oid)
+ if SQLBotLicenseUtil.valid():
+ self.chat_question.custom_prompt = find_custom_prompts(self.session, CustomPromptTypeEnum.GENERATE_SQL,
+ oid, ds_id)
self.init_messages()
diff --git a/backend/apps/terminology/curd/terminology.py b/backend/apps/terminology/curd/terminology.py
index 355f483e7..baa0df0aa 100644
--- a/backend/apps/terminology/curd/terminology.py
+++ b/backend/apps/terminology/curd/terminology.py
@@ -5,9 +5,7 @@
from xml.dom.minidom import parseString
import dicttoxml
-from sqlalchemy import BigInteger
-from sqlalchemy import and_, or_, select, func, delete, update, union
-from sqlalchemy import text
+from sqlalchemy import and_, or_, select, func, delete, update, union, text, BigInteger
from sqlalchemy.orm import aliased
from sqlalchemy.orm.session import Session
diff --git a/backend/locales/en.json b/backend/locales/en.json
index df427789a..79c4c6f40 100644
--- a/backend/locales/en.json
+++ b/backend/locales/en.json
@@ -47,6 +47,10 @@
"datasource_cannot_be_none": "Datasource cannot be none",
"data_training_not_exists": "Example does not exists",
"exists_in_db": "Question exists"
+ },
+ "i18n_custom_prompt": {
+ "exists_in_db": "Prompt name exists",
+ "not_exists": "Prompt does not exists"
},
"i18n_excel_export": {
"data_is_empty": "The form data is empty, cannot export data"
diff --git a/backend/locales/zh-CN.json b/backend/locales/zh-CN.json
index 63961877e..963ea8382 100644
--- a/backend/locales/zh-CN.json
+++ b/backend/locales/zh-CN.json
@@ -48,6 +48,10 @@
"data_training_not_exists": "该示例不存在",
"exists_in_db": "该问题已存在"
},
+ "i18n_custom_prompt": {
+ "exists_in_db": "模版名称已存在",
+ "not_exists": "该模版不存在"
+ },
"i18n_excel_export": {
"data_is_empty": "表单数据为空,无法导出数据"
}
diff --git a/backend/pyproject.toml b/backend/pyproject.toml
index 8ccdd5851..1bd7fab40 100644
--- a/backend/pyproject.toml
+++ b/backend/pyproject.toml
@@ -39,7 +39,7 @@ dependencies = [
"pyyaml (>=6.0.2,<7.0.0)",
"fastapi-mcp (>=0.3.4,<0.4.0)",
"tabulate>=0.9.0",
- "sqlbot-xpack>=0.0.3.31,<1.0.0",
+ "sqlbot-xpack>=0.0.3.36,<1.0.0",
"fastapi-cache2>=0.2.2",
"sqlparse>=0.5.3",
"redis>=6.2.0",
diff --git a/backend/template.yaml b/backend/template.yaml
index a9b8d7e4b..b28bcf172 100644
--- a/backend/template.yaml
+++ b/backend/template.yaml
@@ -14,8 +14,9 @@ template:
内有等信息;
其中,:提供数据库引擎及版本信息;
:以 M-Schema 格式提供数据库表结构信息;
- :提供一组术语,块内每一个就是术语,其中同一个内的多个代表术语的多种叫法,也就是术语与它的同义词,