diff --git a/backend/apps/chat/task/llm.py b/backend/apps/chat/task/llm.py index 6765356b..b99275d8 100644 --- a/backend/apps/chat/task/llm.py +++ b/backend/apps/chat/task/llm.py @@ -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)}]") diff --git a/backend/apps/db/db.py b/backend/apps/db/db.py index 8a9b47d8..6549740b 100644 --- a/backend/apps/db/db.py +++ b/backend/apps/db/db.py @@ -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): diff --git a/backend/apps/system/crud/assistant.py b/backend/apps/system/crud/assistant.py index 1a2a8dd2..8e8f5745 100644 --- a/backend/apps/system/crud/assistant.py +++ b/backend/apps/system/crud/assistant.py @@ -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)) """ diff --git a/backend/template.yaml b/backend/template.yaml index 9ac84f5f..5d16992c 100644 --- a/backend/template.yaml +++ b/backend/template.yaml @@ -28,7 +28,7 @@ template: 不要编造内没有提供给你的表结构 - 生成的SQL必须符合提供数据库引擎的规范 + 生成的SQL必须符合内提供数据库引擎的规范 若用户提问中提供了参考SQL,你需要判断该SQL是否是查询语句 @@ -74,10 +74,14 @@ template: 如数据库引擎是 MySQL、Doris,则在表名、字段名、别名外层加反引号; 如数据库引擎是 Microsoft SQL Server,则在schema、表名、字段名、别名外层加方括号。 - 以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] @@ -95,9 +99,13 @@ template: 如果用户没有指定数据条数的限制,输出的查询SQL需要加上1000条的数据条数限制 - - 如果用户指定的限制大于1000,则按1000处理 + + 以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] + 若需关联多表,优先使用中标记为"Primary key"/"ID"/"主键"的字段作为关联条件。 diff --git a/frontend/public/assistant.js b/frontend/public/assistant.js index 794289a4..382d9954 100644 --- a/frontend/public/assistant.js +++ b/frontend/public/assistant.js @@ -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 } diff --git a/installer/install.conf b/installer/install.conf index 96c7e05b..53211023 100644 --- a/installer/install.conf +++ b/installer/install.conf @@ -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/ \ No newline at end of file +SQLBOT_SERVER_IMAGE_HOST=https://YOUR_SERVER_IP:MCP_PORT/images/