From 8336aa2b7d5c2d709de15bc814424f7cdfc018ef Mon Sep 17 00:00:00 2001 From: HuangHai <10402852@qq.com> Date: Sat, 15 Mar 2025 09:45:58 +0800 Subject: [PATCH] 'commit' --- AI/Config.py | 5 +- AI/Text2Sql/Util/VannaUtil.py | 61 ++++++------------ .../__pycache__/VannaUtil.cpython-310.pyc | Bin 7615 -> 7061 bytes AI/Text2Sql/__pycache__/app.cpython-310.pyc | Bin 3082 -> 3072 bytes AI/Text2Sql/app.py | 12 +--- .../1cf515f7-ffde-4e97-98cf-67005aecad80.xlsx | Bin 0 -> 6473 bytes .../3ebca71e-e8f2-434f-8941-b15cc5a98591.xlsx | Bin 0 -> 6474 bytes AI/__pycache__/Config.cpython-310.pyc | Bin 1152 -> 1245 bytes 8 files changed, 27 insertions(+), 51 deletions(-) create mode 100644 AI/Text2Sql/static/1cf515f7-ffde-4e97-98cf-67005aecad80.xlsx create mode 100644 AI/Text2Sql/static/3ebca71e-e8f2-434f-8941-b15cc5a98591.xlsx diff --git a/AI/Config.py b/AI/Config.py index 2760a541..bff3493d 100644 --- a/AI/Config.py +++ b/AI/Config.py @@ -30,4 +30,7 @@ NEO4J_AUTH = ("neo4j", "DsideaL4r5t6y7u") # Dify DIFY_API_KEY = "app-jdd8mRNx3IJqVTYhRhmRfxtl" -DIFY_URL='http://10.10.14.207/v1' \ No newline at end of file +DIFY_URL='http://10.10.14.207/v1' + +# Vanna Postgresql +VANNA_POSTGRESQL_URI = "postgresql://postgres:DsideaL147258369@10.10.14.71:5432/szjz_db" diff --git a/AI/Text2Sql/Util/VannaUtil.py b/AI/Text2Sql/Util/VannaUtil.py index 431f1c9f..cfd860a3 100644 --- a/AI/Text2Sql/Util/VannaUtil.py +++ b/AI/Text2Sql/Util/VannaUtil.py @@ -1,88 +1,69 @@ import re from typing import List, Dict, Any import requests -import sqlite3 +import psycopg2 from vanna.base import VannaBase from Config import * class VannaUtil(VannaBase): - def __init__(self, db_type='sqlite', db_uri=None): + def __init__(self, db_uri=None): super().__init__() self.api_key = MODEL_API_KEY self.base_url = MODEL_GENERATION_TEXT_URL # 阿里云专用API地址 self.model = QWEN_MODEL_NAME # 根据实际模型名称调整 self.training_data = [] self.chat_history = [] - self.db_type = db_type - self.db_uri = db_uri or 'Db/vanna.db' # 默认使用 SQLite + self.db_uri = db_uri or VANNA_POSTGRESQL_URI # 默认 PostgreSQL 连接字符串 self._init_db() def _init_db(self): - """初始化数据库连接""" - if self.db_type == 'sqlite': - self.conn = sqlite3.connect(self.db_uri) - self._create_tables() - elif self.db_type == 'postgres': - import psycopg2 - self.conn = psycopg2.connect(self.db_uri) - self._create_tables() - else: - raise ValueError(f"Unsupported database type: {self.db_type}") + """初始化 PostgreSQL 数据库连接""" + self.conn = psycopg2.connect(self.db_uri) + self._create_tables() def _create_tables(self): """创建训练数据表""" cursor = self.conn.cursor() - if self.db_type == 'sqlite': - cursor.execute(''' - CREATE TABLE IF NOT EXISTS training_data ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - type TEXT, - question TEXT, - sql TEXT, - content TEXT - ) - ''') - elif self.db_type == 'postgres': - cursor.execute(''' - CREATE TABLE IF NOT EXISTS training_data ( - id SERIAL PRIMARY KEY, - type TEXT, - question TEXT, - sql TEXT, - content TEXT - ) - ''') + cursor.execute(''' + CREATE TABLE IF NOT EXISTS training_data ( + id SERIAL PRIMARY KEY, + type TEXT, + question TEXT, + sql TEXT, + content TEXT + ) + ''') self.conn.commit() def add_ddl(self, ddl: str, **kwargs) -> None: """添加 DDL""" cursor = self.conn.cursor() - cursor.execute('INSERT INTO training_data (type, content) VALUES (?, ?)', ('ddl', ddl)) + cursor.execute('INSERT INTO training_data (type, content) VALUES (%s, %s)', ('ddl', ddl)) self.conn.commit() def add_documentation(self, doc: str, **kwargs) -> None: """添加文档""" cursor = self.conn.cursor() - cursor.execute('INSERT INTO training_data (type, content) VALUES (?, ?)', ('documentation', doc)) + cursor.execute('INSERT INTO training_data (type, content) VALUES (%s, %s)', ('documentation', doc)) self.conn.commit() def add_question_sql(self, question: str, sql: str, **kwargs) -> None: """添加问答对""" cursor = self.conn.cursor() - cursor.execute('INSERT INTO training_data (type, question, sql) VALUES (?, ?, ?)', ('qa', question, sql)) + cursor.execute('INSERT INTO training_data (type, question, sql) VALUES (%s, %s, %s)', ('qa', question, sql)) self.conn.commit() def get_related_ddl(self, question: str, **kwargs) -> str: """获取相关 DDL""" cursor = self.conn.cursor() - cursor.execute('SELECT content FROM training_data WHERE type = ?', ('ddl',)) + cursor.execute('SELECT content FROM training_data WHERE type = %s', ('ddl',)) return "\n".join(row[0] for row in cursor.fetchall()) def get_related_documentation(self, question: str, **kwargs) -> str: """获取相关文档""" cursor = self.conn.cursor() - cursor.execute('SELECT content FROM training_data WHERE type = ?', ('documentation',)) + cursor.execute('SELECT content FROM training_data WHERE type = %s', ('documentation',)) return "\n".join(row[0] for row in cursor.fetchall()) def get_training_data(self, **kwargs) -> List[Dict[str, Any]]: @@ -95,7 +76,7 @@ class VannaUtil(VannaBase): def remove_training_data(self, id: str, **kwargs) -> bool: """删除训练数据""" cursor = self.conn.cursor() - cursor.execute('DELETE FROM training_data WHERE id = ?', (id,)) + cursor.execute('DELETE FROM training_data WHERE id = %s', (id,)) self.conn.commit() return cursor.rowcount > 0 diff --git a/AI/Text2Sql/Util/__pycache__/VannaUtil.cpython-310.pyc b/AI/Text2Sql/Util/__pycache__/VannaUtil.cpython-310.pyc index 0641d26d6362dba8c461bff796a007f9ed9224a9..79f579773103eacbd67e36998f167c8a337f7728 100644 GIT binary patch delta 2642 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 G5T9QQ1aC_K delta 531 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 S0{|GvzbJD4{z3Hn?tcJ#G+G(} literal 0 HcmV?d00001 diff --git a/AI/Text2Sql/static/3ebca71e-e8f2-434f-8941-b15cc5a98591.xlsx b/AI/Text2Sql/static/3ebca71e-e8f2-434f-8941-b15cc5a98591.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..8811a3e75e460e134b167499ce09b1f6971670a2 GIT binary patch literal 6474 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 U&tz2=V@9>fku0h#EQ}n?0F7=j`v3p{ delta 87 zcmcc1*}%!0&&$ij00f4|E~g7m