mirror of
https://gh-proxy.org/https://github.com/klinsmaya/SQLBot.git
synced 2026-11-08 01:06:59 +08:00
Merge branch 'main' of https://github.com/dataease/SQLBot
This commit is contained in:
@@ -399,7 +399,8 @@ class LLMService:
|
||||
]
|
||||
""" _ds_list = self.session.exec(select(CoreDatasource).options(
|
||||
load_only(CoreDatasource.id, CoreDatasource.name, CoreDatasource.description))).all() """
|
||||
|
||||
if not _ds_list:
|
||||
raise SingleMessageError('No available datasource configuration found')
|
||||
ignore_auto_select = _ds_list and len(_ds_list) == 1
|
||||
# ignore auto select ds
|
||||
|
||||
@@ -1175,7 +1176,7 @@ class LLMService:
|
||||
match_ds = any(item.get("id") == _ds.id for item in _ds_list)
|
||||
if not match_ds:
|
||||
type = self.current_assistant.type
|
||||
msg = f"ds is invalid [please check ds list and public ds list]" if type == 0 else f"ds is invalid [please check ds api]"
|
||||
msg = f"[please check ds list and public ds list]" if type == 0 else f"[please check ds api]"
|
||||
raise SingleMessageError(msg)
|
||||
except Exception as e:
|
||||
raise SingleMessageError(f"ds is invalid [{str(e)}]")
|
||||
|
||||
+58
-44
@@ -102,10 +102,62 @@ def get_session(ds: CoreDatasource | AssistantOutDsSchema):
|
||||
return session
|
||||
|
||||
|
||||
def check_connection(trans: Optional[Trans], ds: CoreDatasource, is_raise: bool = False):
|
||||
db = DB.get_db(ds.type)
|
||||
if db.connect_type == ConnectType.sqlalchemy:
|
||||
conn = get_engine(ds, 10)
|
||||
def check_connection(trans: Optional[Trans], ds: CoreDatasource | AssistantOutDsSchema, is_raise: bool = False):
|
||||
if isinstance(ds, CoreDatasource):
|
||||
db = DB.get_db(ds.type)
|
||||
if db.connect_type == ConnectType.sqlalchemy:
|
||||
conn = get_engine(ds, 10)
|
||||
try:
|
||||
with conn.connect() as connection:
|
||||
SQLBotLogUtil.info("success")
|
||||
return True
|
||||
except Exception as e:
|
||||
SQLBotLogUtil.error(f"Datasource {ds.id} connection failed: {e}")
|
||||
if is_raise:
|
||||
raise HTTPException(status_code=500, detail=trans('i18n_ds_invalid') + f': {e.args}')
|
||||
return False
|
||||
else:
|
||||
conf = DatasourceConf(**json.loads(aes_decrypt(ds.configuration)))
|
||||
if ds.type == 'dm':
|
||||
with dmPython.connect(user=conf.username, password=conf.password, server=conf.host,
|
||||
port=conf.port) as conn, conn.cursor() as cursor:
|
||||
try:
|
||||
cursor.execute('select 1', timeout=10).fetchall()
|
||||
SQLBotLogUtil.info("success")
|
||||
return True
|
||||
except Exception as e:
|
||||
SQLBotLogUtil.error(f"Datasource {ds.id} connection failed: {e}")
|
||||
if is_raise:
|
||||
raise HTTPException(status_code=500, detail=trans('i18n_ds_invalid') + f': {e.args}')
|
||||
return False
|
||||
elif ds.type == 'doris':
|
||||
with pymysql.connect(user=conf.username, passwd=conf.password, host=conf.host,
|
||||
port=conf.port, db=conf.database, connect_timeout=10,
|
||||
read_timeout=10) as conn, conn.cursor() as cursor:
|
||||
try:
|
||||
cursor.execute('select 1')
|
||||
SQLBotLogUtil.info("success")
|
||||
return True
|
||||
except Exception as e:
|
||||
SQLBotLogUtil.error(f"Datasource {ds.id} connection failed: {e}")
|
||||
if is_raise:
|
||||
raise HTTPException(status_code=500, detail=trans('i18n_ds_invalid') + f': {e.args}')
|
||||
return False
|
||||
elif ds.type == 'redshift':
|
||||
with redshift_connector.connect(host=conf.host, port=conf.port, database=conf.database, user=conf.username,
|
||||
password=conf.password,
|
||||
timeout=10) as conn, conn.cursor() as cursor:
|
||||
try:
|
||||
cursor.execute('select 1')
|
||||
SQLBotLogUtil.info("success")
|
||||
return True
|
||||
except Exception as e:
|
||||
SQLBotLogUtil.error(f"Datasource {ds.id} connection failed: {e}")
|
||||
if is_raise:
|
||||
raise HTTPException(status_code=500, detail=trans('i18n_ds_invalid') + f': {e.args}')
|
||||
return False
|
||||
else:
|
||||
conn = get_ds_engine(ds)
|
||||
try:
|
||||
with conn.connect() as connection:
|
||||
SQLBotLogUtil.info("success")
|
||||
@@ -115,46 +167,8 @@ def check_connection(trans: Optional[Trans], ds: CoreDatasource, is_raise: bool
|
||||
if is_raise:
|
||||
raise HTTPException(status_code=500, detail=trans('i18n_ds_invalid') + f': {e.args}')
|
||||
return False
|
||||
else:
|
||||
conf = DatasourceConf(**json.loads(aes_decrypt(ds.configuration)))
|
||||
if ds.type == 'dm':
|
||||
with dmPython.connect(user=conf.username, password=conf.password, server=conf.host,
|
||||
port=conf.port) as conn, conn.cursor() as cursor:
|
||||
try:
|
||||
cursor.execute('select 1', timeout=10).fetchall()
|
||||
SQLBotLogUtil.info("success")
|
||||
return True
|
||||
except Exception as e:
|
||||
SQLBotLogUtil.error(f"Datasource {ds.id} connection failed: {e}")
|
||||
if is_raise:
|
||||
raise HTTPException(status_code=500, detail=trans('i18n_ds_invalid') + f': {e.args}')
|
||||
return False
|
||||
elif ds.type == 'doris':
|
||||
with pymysql.connect(user=conf.username, passwd=conf.password, host=conf.host,
|
||||
port=conf.port, db=conf.database, connect_timeout=10,
|
||||
read_timeout=10) as conn, conn.cursor() as cursor:
|
||||
try:
|
||||
cursor.execute('select 1')
|
||||
SQLBotLogUtil.info("success")
|
||||
return True
|
||||
except Exception as e:
|
||||
SQLBotLogUtil.error(f"Datasource {ds.id} connection failed: {e}")
|
||||
if is_raise:
|
||||
raise HTTPException(status_code=500, detail=trans('i18n_ds_invalid') + f': {e.args}')
|
||||
return False
|
||||
elif ds.type == 'redshift':
|
||||
with redshift_connector.connect(host=conf.host, port=conf.port, database=conf.database, user=conf.username,
|
||||
password=conf.password,
|
||||
timeout=10) as conn, conn.cursor() as cursor:
|
||||
try:
|
||||
cursor.execute('select 1')
|
||||
SQLBotLogUtil.info("success")
|
||||
return True
|
||||
except Exception as e:
|
||||
SQLBotLogUtil.error(f"Datasource {ds.id} connection failed: {e}")
|
||||
if is_raise:
|
||||
raise HTTPException(status_code=500, detail=trans('i18n_ds_invalid') + f': {e.args}')
|
||||
return False
|
||||
|
||||
return False
|
||||
|
||||
|
||||
def get_version(ds: CoreDatasource | AssistantOutDsSchema):
|
||||
|
||||
@@ -44,7 +44,7 @@ def get_assistant_ds(session: Session, llm_service) -> list[dict]:
|
||||
if public_list:
|
||||
stmt = stmt.where(CoreDatasource.id.in_(public_list))
|
||||
else:
|
||||
raise RuntimeError('no public datasource valid')
|
||||
return []
|
||||
""" private_list: list[int] = config.get('private_list') or None
|
||||
if private_list:
|
||||
stmt = stmt.where(~CoreDatasource.id.in_(private_list)) """
|
||||
|
||||
+14
-6
@@ -28,7 +28,7 @@ template:
|
||||
不要编造<m-schema>内没有提供给你的表结构
|
||||
</rule>
|
||||
<rule>
|
||||
生成的SQL必须符合<db-engine>提供数据库引擎的规范
|
||||
生成的SQL必须符合<db-engine>内提供数据库引擎的规范
|
||||
</rule>
|
||||
<rule>
|
||||
若用户提问中提供了参考SQL,你需要判断该SQL是否是查询语句
|
||||
@@ -74,10 +74,14 @@ template:
|
||||
如数据库引擎是 MySQL、Doris,则在表名、字段名、别名外层加反引号;
|
||||
如数据库引擎是 Microsoft SQL Server,则在schema、表名、字段名、别名外层加方括号。
|
||||
<example>
|
||||
以PostgreSQL为例,查询Schema为TEST表TABLE下所有数据,则生成的SQL为:
|
||||
SELECT "id" FROM "TEST"."TABLE"
|
||||
以PostgreSQL为例,查询Schema为TEST表TABLE下前1000条id字段,则生成的SQL为:
|
||||
SELECT "id" FROM "TEST"."TABLE" LIMIT 1000
|
||||
- 注意在表名外双引号的位置,千万不要生成为:
|
||||
SELECT "id" FROM "TEST.TABLE"
|
||||
SELECT "id" FROM "TEST.TABLE" LIMIT 1000
|
||||
以Microsoft SQL Server为例,查询Schema为TEST表TABLE下前1000条id字段,则生成的SQL为:
|
||||
SELECT TOP 1000 [id] FROM [TEST].[TABLE]
|
||||
- 注意在表名外方括号的位置,千万不要生成为:
|
||||
SELECT TOP 1000 [id] FROM [TEST.TABLE]
|
||||
</example>
|
||||
</rule>
|
||||
<rule>
|
||||
@@ -95,9 +99,13 @@ template:
|
||||
</rule>
|
||||
<rule>
|
||||
如果用户没有指定数据条数的限制,输出的查询SQL需要加上1000条的数据条数限制
|
||||
</rule>
|
||||
<rule>
|
||||
如果用户指定的限制大于1000,则按1000处理
|
||||
<example>
|
||||
以PostgreSQL为例,查询Schema为TEST表TABLE下id字段,则生成的SQL为:
|
||||
SELECT "id" FROM "TEST"."TABLE" LIMIT 1000
|
||||
以Microsoft SQL Server为例,查询Schema为TEST表TABLE下id字段,则生成的SQL为:
|
||||
SELECT TOP 1000 [id] FROM [TEST].[TABLE]
|
||||
</example>
|
||||
</rule>
|
||||
<rule>
|
||||
若需关联多表,优先使用<m-schema>中标记为"Primary key"/"ID"/"主键"的字段作为关联条件。
|
||||
|
||||
@@ -157,7 +157,55 @@
|
||||
closeviewport.classList.remove('sqlbot-assistant-viewportnone')
|
||||
}
|
||||
}
|
||||
const drag = (e) => {
|
||||
if (data.float_icon_drag) {
|
||||
chat_button.setAttribute('draggable', 'true')
|
||||
|
||||
let startX = 0
|
||||
let startY = 0
|
||||
const img = new Image()
|
||||
img.src = 'data:image/gif;base64,R0lGODlhAQABAIAAAAUEBAAAACwAAAAAAQABAAACAkQBADs='
|
||||
chat_button.addEventListener('dragstart', (e) => {
|
||||
startX = e.clientX - chat_button.offsetLeft
|
||||
startY = e.clientY - chat_button.offsetTop
|
||||
e.dataTransfer.setDragImage(img, 0, 0)
|
||||
})
|
||||
|
||||
chat_button.addEventListener('drag', (e) => {
|
||||
if (e.clientX && e.clientY) {
|
||||
const left = e.clientX - startX
|
||||
const top = e.clientY - startY
|
||||
|
||||
const maxX = window.innerWidth - chat_button.offsetWidth
|
||||
const maxY = window.innerHeight - chat_button.offsetHeight
|
||||
|
||||
chat_button.style.left = Math.min(Math.max(0, left), maxX) + 'px'
|
||||
chat_button.style.top = Math.min(Math.max(0, top), maxY) + 'px'
|
||||
}
|
||||
})
|
||||
|
||||
let touchStartX = 0
|
||||
let touchStartY = 0
|
||||
|
||||
chat_button.addEventListener('touchstart', (e) => {
|
||||
touchStartX = e.touches[0].clientX - chat_button.offsetLeft
|
||||
touchStartY = e.touches[0].clientY - chat_button.offsetTop
|
||||
e.preventDefault()
|
||||
})
|
||||
|
||||
chat_button.addEventListener('touchmove', (e) => {
|
||||
const left = e.touches[0].clientX - touchStartX
|
||||
const top = e.touches[0].clientY - touchStartY
|
||||
|
||||
const maxX = window.innerWidth - chat_button.offsetWidth
|
||||
const maxY = window.innerHeight - chat_button.offsetHeight
|
||||
|
||||
chat_button.style.left = Math.min(Math.max(0, left), maxX) + 'px'
|
||||
chat_button.style.top = Math.min(Math.max(0, top), maxY) + 'px'
|
||||
|
||||
e.preventDefault()
|
||||
})
|
||||
}
|
||||
/* const drag = (e) => {
|
||||
if (['touchmove', 'touchstart'].includes(e.type)) {
|
||||
chat_button.style.top = e.touches[0].clientY - chat_button_img.clientHeight / 2 + 'px'
|
||||
chat_button.style.left = e.touches[0].clientX - chat_button_img.clientHeight / 2 + 'px'
|
||||
@@ -169,6 +217,7 @@
|
||||
chat_button.style.height = chat_button_img.clientHeight + 'px'
|
||||
}
|
||||
if (data.float_icon_drag) {
|
||||
chat_button.setAttribute('draggable', 'true')
|
||||
chat_button.addEventListener('drag', drag)
|
||||
chat_button.addEventListener('dragover', (e) => {
|
||||
e.preventDefault()
|
||||
@@ -176,7 +225,7 @@
|
||||
chat_button.addEventListener('dragend', drag)
|
||||
chat_button.addEventListener('touchstart', drag)
|
||||
chat_button.addEventListener('touchmove', drag)
|
||||
}
|
||||
} */
|
||||
viewport.onclick = viewport_func
|
||||
closeviewport.onclick = viewport_func
|
||||
}
|
||||
|
||||
+1
-15
@@ -5,20 +5,6 @@ SQLBOT_BASE=/opt
|
||||
SQLBOT_WEB_PORT=8000
|
||||
SQLBOT_MCP_PORT=8001
|
||||
|
||||
# 数据库配置
|
||||
## 是否使用外部数据库
|
||||
SQLBOT_EXTERNAL_DB=false
|
||||
## 数据库地址
|
||||
SQLBOT_DB_HOST=localhost
|
||||
## 数据库端口 (仅使用外部数据库时才生效)
|
||||
SQLBOT_DB_PORT=5432
|
||||
## SQLBot 数据库库名
|
||||
SQLBOT_DB_DB=sqlbot
|
||||
## 数据库用户名
|
||||
SQLBOT_DB_USER=root
|
||||
## 数据库密码,密码如包含特殊字符,请用双引号引起来,例如 SQLBOT_DB_PASSWORD="Test@4&^%*^"
|
||||
SQLBOT_DB_PASSWORD=Password123@pg
|
||||
|
||||
# 其他配置
|
||||
## 普通用户默认密码
|
||||
SQLBOT_DEFAULT_PWD=SQLBot@123456
|
||||
@@ -31,4 +17,4 @@ SQLBOT_LOG_LEVEL="INFO"
|
||||
## 缓存类型
|
||||
SQLBOT_CACHE_TYPE="memory"
|
||||
## MCP 图片存储路径
|
||||
SQLBOT_SERVER_IMAGE_HOST=https://YOUR_SERVER_IP:MCP_PORT/images/
|
||||
SQLBOT_SERVER_IMAGE_HOST=https://YOUR_SERVER_IP:MCP_PORT/images/
|
||||
|
||||
Reference in New Issue
Block a user