From 789960a1cf1f74f2deb124fd5459804b1d0b6c69 Mon Sep 17 00:00:00 2001 From: ymq Date: Tue, 25 Aug 2026 14:24:37 +0800 Subject: [PATCH] =?UTF-8?q?refactor(schema):=20rag=20=E6=A8=A1=E5=9D=97?= =?UTF-8?q?=E5=85=A8=E9=83=A8=E6=95=B0=E6=8D=AE=E8=A1=A8=E5=8A=A0=20rag=5F?= =?UTF-8?q?=20=E5=89=8D=E7=BC=80=EF=BC=88=E6=A8=A1=E5=9D=97=E8=B7=A8?= =?UTF-8?q?=E5=BA=94=E7=94=A8=E5=A4=8D=E7=94=A8=E9=98=B2=E5=86=B2=E7=AA=81?= =?UTF-8?q?=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - models/ 重建为正确表定义格式(summary+fields+indexes),覆盖全部 11 个表 修正原 models/ 误放 CRUD 格式副本(缺 summary)导致 json2ddl 100% 失败被静默忽略 - json/ 6 个 CRUD 定义 tblname 加前缀 + 文件改名 - 129 处 SQL/sor 表名加 rag_ 前缀(URL 路径 knowledge_bases_list 保持不变) - 新增 init/migrate_rag_prefix.sql 幂等 RENAME TABLE 迁移 --- init/migrate_rag_prefix.sql | 54 +++++++ json/documents.json | 25 --- json/engine_configs.json | 26 --- json/knowledge_bases.json | 26 --- json/org_storage_limits.json | 17 -- json/rag_documents.json | 66 ++++++++ json/rag_engine_configs.json | 81 ++++++++++ json/rag_knowledge_bases.json | 70 ++++++++ json/rag_org_storage_limits.json | 40 +++++ json/rag_subscriptions.json | 90 +++++++++++ json/rag_tags.json | 55 +++++++ json/subscriptions.json | 28 ---- json/tags.json | 22 --- models/documents.json | 25 --- models/engine_configs.json | 26 --- models/knowledge_bases.json | 26 --- models/org_storage_limits.json | 17 -- models/rag_document_chunks.json | 129 +++++++++++++++ models/rag_documents.json | 151 ++++++++++++++++++ models/rag_engine_configs.json | 124 ++++++++++++++ models/rag_entities.json | 138 ++++++++++++++++ models/rag_entity_relations.json | 103 ++++++++++++ models/rag_knowledge_bases.json | 124 ++++++++++++++ models/rag_media_tags.json | 87 ++++++++++ models/rag_org_storage_limits.json | 39 +++++ models/rag_subscriptions.json | 134 ++++++++++++++++ models/rag_tags.json | 73 +++++++++ models/rag_usage_logs.json | 110 +++++++++++++ models/subscriptions.json | 28 ---- models/tags.json | 22 --- rag/init.py | 88 +++++----- rag/pipeline.py | 8 +- wwwroot/knowledge_bases_list/add_tag.dspy | 4 +- wwwroot/knowledge_bases_list/analysis.dspy | 2 +- .../knowledge_bases_list/batch_ingest.dspy | 12 +- wwwroot/knowledge_bases_list/create_kb.dspy | 2 +- wwwroot/knowledge_bases_list/delete_file.dspy | 12 +- wwwroot/knowledge_bases_list/delete_kb.dspy | 30 ++-- .../delete_tree_item.dspy | 2 +- wwwroot/knowledge_bases_list/file_list.dspy | 4 +- .../knowledge_bases_list/get_tree_data.dspy | 4 +- wwwroot/knowledge_bases_list/kb_list.dspy | 2 +- wwwroot/knowledge_bases_list/kb_options.dspy | 2 +- wwwroot/knowledge_bases_list/media_cards.dspy | 8 +- .../knowledge_bases_list/new_tree_item.dspy | 2 +- wwwroot/knowledge_bases_list/rename_kb.dspy | 2 +- .../knowledge_bases_list/rename_kb_form.dspy | 2 +- wwwroot/knowledge_bases_list/save_tags.dspy | 6 +- .../knowledge_bases_list/search_result.dspy | 12 +- .../knowledge_bases_list/storage_card.dspy | 2 +- .../knowledge_bases_list/storage_stats.dspy | 4 +- wwwroot/knowledge_bases_list/tag_form.dspy | 6 +- wwwroot/knowledge_bases_list/tag_options.dspy | 2 +- .../update_tree_item.dspy | 2 +- wwwroot/knowledge_bases_list/upload_file.dspy | 22 +-- 55 files changed, 1789 insertions(+), 409 deletions(-) create mode 100644 init/migrate_rag_prefix.sql delete mode 100644 json/documents.json delete mode 100644 json/engine_configs.json delete mode 100644 json/knowledge_bases.json delete mode 100644 json/org_storage_limits.json create mode 100644 json/rag_documents.json create mode 100644 json/rag_engine_configs.json create mode 100644 json/rag_knowledge_bases.json create mode 100644 json/rag_org_storage_limits.json create mode 100644 json/rag_subscriptions.json create mode 100644 json/rag_tags.json delete mode 100644 json/subscriptions.json delete mode 100644 json/tags.json delete mode 100644 models/documents.json delete mode 100644 models/engine_configs.json delete mode 100644 models/knowledge_bases.json delete mode 100644 models/org_storage_limits.json create mode 100644 models/rag_document_chunks.json create mode 100644 models/rag_documents.json create mode 100644 models/rag_engine_configs.json create mode 100644 models/rag_entities.json create mode 100644 models/rag_entity_relations.json create mode 100644 models/rag_knowledge_bases.json create mode 100644 models/rag_media_tags.json create mode 100644 models/rag_org_storage_limits.json create mode 100644 models/rag_subscriptions.json create mode 100644 models/rag_tags.json create mode 100644 models/rag_usage_logs.json delete mode 100644 models/subscriptions.json delete mode 100644 models/tags.json diff --git a/init/migrate_rag_prefix.sql b/init/migrate_rag_prefix.sql new file mode 100644 index 0000000..d1f5971 --- /dev/null +++ b/init/migrate_rag_prefix.sql @@ -0,0 +1,54 @@ +-- rag 模块表名加 rag_ 前缀(模块跨应用复用,避免与业务表冲突) +-- 幂等:仅当旧表存在且新表不存在时才 RENAME。 +-- 用法: mysql -h -u -p < migrate_rag_prefix.sql + +DELIMITER $$ +DROP PROCEDURE IF EXISTS rag_rename_tables$$ +CREATE PROCEDURE rag_rename_tables() +BEGIN + DECLARE done INT DEFAULT 0; + DECLARE oldname VARCHAR(64); + DECLARE cur CURSOR FOR + SELECT t FROM ( + SELECT 'knowledge_bases' AS t UNION ALL + SELECT 'documents' UNION ALL + SELECT 'document_chunks' UNION ALL + SELECT 'tags' UNION ALL + SELECT 'media_tags' UNION ALL + SELECT 'entities' UNION ALL + SELECT 'entity_relations' UNION ALL + SELECT 'engine_configs' UNION ALL + SELECT 'subscriptions' UNION ALL + SELECT 'org_storage_limits' UNION ALL + SELECT 'usage_logs' + ) x; + DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1; + + OPEN cur; + read_loop: LOOP + FETCH cur INTO oldname; + IF done THEN LEAVE read_loop; END IF; + + SET @has_old = (SELECT COUNT(*) FROM information_schema.TABLES + WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = oldname); + SET @has_new = (SELECT COUNT(*) FROM information_schema.TABLES + WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = CONCAT('rag_', oldname)); + + IF @has_old = 1 AND @has_new = 0 THEN + SET @sql = CONCAT('RENAME TABLE `', oldname, '` TO `rag_', oldname, '`'); + PREPARE st FROM @sql; + EXECUTE st; + DEALLOCATE PREPARE st; + SELECT CONCAT('RENAMED: ', oldname, ' -> rag_', oldname) AS result; + ELSEIF @has_new = 1 THEN + SELECT CONCAT('SKIP (already migrated): rag_', oldname) AS result; + ELSE + SELECT CONCAT('SKIP (no such table): ', oldname) AS result; + END IF; + END LOOP; + CLOSE cur; +END$$ +DELIMITER ; + +CALL rag_rename_tables(); +DROP PROCEDURE rag_rename_tables; diff --git a/json/documents.json b/json/documents.json deleted file mode 100644 index 937f7d1..0000000 --- a/json/documents.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "tblname": "documents", - "params": { - "title": "文档管理", - "browserfields": [ - {"field": "file_name", "title": "文件名", "width": "25%"}, - {"field": "file_type", "title": "类型", "width": "10%"}, - {"field": "file_size", "title": "大小", "width": "10%"}, - {"field": "chunk_count", "title": "分片数", "width": "8%"}, - {"field": "status", "title": "状态", "width": "10%"}, - {"field": "kb_id", "title": "知识库", "width": "17%"}, - {"field": "created_at", "title": "上传时间", "width": "20%"} - ], - "editfields": [ - {"field": "file_name", "uitype": "Text", "required": true}, - {"field": "kb_id", "uitype": "Text", "required": true}, - {"field": "file_type", "uitype": "Text"} - ], - "searchfields": ["file_name"], - "sort": "created_at desc", - "data_filter": { - "org_id": "{{userorgid}}" - } - } -} diff --git a/json/engine_configs.json b/json/engine_configs.json deleted file mode 100644 index c338fb2..0000000 --- a/json/engine_configs.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "tblname": "engine_configs", - "params": { - "title": "引擎配置", - "browserfields": [ - {"field": "engine_type", "title": "引擎类型", "width": "15%"}, - {"field": "engine_name", "title": "名称", "width": "15%"}, - {"field": "endpoint_url", "title": "服务地址", "width": "25%"}, - {"field": "model_name", "title": "模型", "width": "15%"}, - {"field": "is_default", "title": "默认", "width": "8%"}, - {"field": "status", "title": "状态", "width": "10%"}, - {"field": "org_id", "title": "机构", "width": "12%"} - ], - "editfields": [ - {"field": "engine_type", "uitype": "Text", "required": true}, - {"field": "engine_name", "uitype": "Text", "required": true}, - {"field": "endpoint_url", "uitype": "Text"}, - {"field": "api_key", "uitype": "Text"}, - {"field": "model_name", "uitype": "Text"}, - {"field": "is_default", "uitype": "Text"}, - {"field": "config_json", "uitype": "Text"} - ], - "searchfields": ["engine_type", "engine_name", "endpoint_url"], - "sort": "engine_type, priority desc" - } -} diff --git a/json/knowledge_bases.json b/json/knowledge_bases.json deleted file mode 100644 index 99482d5..0000000 --- a/json/knowledge_bases.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "tblname": "knowledge_bases", - "params": { - "title": "知识库管理", - "browserfields": [ - {"field": "name", "title": "名称", "width": "20%"}, - {"field": "description", "title": "描述", "width": "30%"}, - {"field": "doc_count", "title": "文档数", "width": "10%"}, - {"field": "total_size", "title": "大小", "width": "10%"}, - {"field": "embedding_engine", "title": "向量引擎", "width": "15%"}, - {"field": "created_at", "title": "创建时间", "width": "15%"} - ], - "editfields": [ - {"field": "name", "uitype": "Text", "required": true}, - {"field": "description", "uitype": "Text"}, - {"field": "embedding_engine", "uitype": "Text", "default": "clip-vith14"}, - {"field": "vdb_collection", "uitype": "Text"}, - {"field": "graph_name", "uitype": "Text"} - ], - "searchfields": ["name", "description"], - "sort": "created_at desc", - "data_filter": { - "org_id": "{{userorgid}}" - } - } -} diff --git a/json/org_storage_limits.json b/json/org_storage_limits.json deleted file mode 100644 index f40d581..0000000 --- a/json/org_storage_limits.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "tblname": "org_storage_limits", - "params": { - "title": "机构存储限额", - "browserfields": [ - {"field": "org_id", "title": "机构ID", "width": "35%"}, - {"field": "limit_bytes", "title": "限额(字节)", "width": "35%"}, - {"field": "updated_at", "title": "更新时间", "width": "30%"} - ], - "editfields": [ - {"field": "org_id", "uitype": "Text", "required": true}, - {"field": "limit_bytes", "uitype": "Text", "required": true, "default": "104857600"} - ], - "searchfields": ["org_id"], - "sort": "updated_at desc" - } -} diff --git a/json/rag_documents.json b/json/rag_documents.json new file mode 100644 index 0000000..5d539c1 --- /dev/null +++ b/json/rag_documents.json @@ -0,0 +1,66 @@ +{ + "tblname": "rag_documents", + "params": { + "title": "文档管理", + "browserfields": [ + { + "field": "file_name", + "title": "文件名", + "width": "25%" + }, + { + "field": "file_type", + "title": "类型", + "width": "10%" + }, + { + "field": "file_size", + "title": "大小", + "width": "10%" + }, + { + "field": "chunk_count", + "title": "分片数", + "width": "8%" + }, + { + "field": "status", + "title": "状态", + "width": "10%" + }, + { + "field": "kb_id", + "title": "知识库", + "width": "17%" + }, + { + "field": "created_at", + "title": "上传时间", + "width": "20%" + } + ], + "editfields": [ + { + "field": "file_name", + "uitype": "Text", + "required": true + }, + { + "field": "kb_id", + "uitype": "Text", + "required": true + }, + { + "field": "file_type", + "uitype": "Text" + } + ], + "searchfields": [ + "file_name" + ], + "sort": "created_at desc", + "data_filter": { + "org_id": "{{userorgid}}" + } + } +} diff --git a/json/rag_engine_configs.json b/json/rag_engine_configs.json new file mode 100644 index 0000000..477174c --- /dev/null +++ b/json/rag_engine_configs.json @@ -0,0 +1,81 @@ +{ + "tblname": "rag_engine_configs", + "params": { + "title": "引擎配置", + "browserfields": [ + { + "field": "engine_type", + "title": "引擎类型", + "width": "15%" + }, + { + "field": "engine_name", + "title": "名称", + "width": "15%" + }, + { + "field": "endpoint_url", + "title": "服务地址", + "width": "25%" + }, + { + "field": "model_name", + "title": "模型", + "width": "15%" + }, + { + "field": "is_default", + "title": "默认", + "width": "8%" + }, + { + "field": "status", + "title": "状态", + "width": "10%" + }, + { + "field": "org_id", + "title": "机构", + "width": "12%" + } + ], + "editfields": [ + { + "field": "engine_type", + "uitype": "Text", + "required": true + }, + { + "field": "engine_name", + "uitype": "Text", + "required": true + }, + { + "field": "endpoint_url", + "uitype": "Text" + }, + { + "field": "api_key", + "uitype": "Text" + }, + { + "field": "model_name", + "uitype": "Text" + }, + { + "field": "is_default", + "uitype": "Text" + }, + { + "field": "config_json", + "uitype": "Text" + } + ], + "searchfields": [ + "engine_type", + "engine_name", + "endpoint_url" + ], + "sort": "engine_type, priority desc" + } +} diff --git a/json/rag_knowledge_bases.json b/json/rag_knowledge_bases.json new file mode 100644 index 0000000..bd273e2 --- /dev/null +++ b/json/rag_knowledge_bases.json @@ -0,0 +1,70 @@ +{ + "tblname": "rag_knowledge_bases", + "params": { + "title": "知识库管理", + "browserfields": [ + { + "field": "name", + "title": "名称", + "width": "20%" + }, + { + "field": "description", + "title": "描述", + "width": "30%" + }, + { + "field": "doc_count", + "title": "文档数", + "width": "10%" + }, + { + "field": "total_size", + "title": "大小", + "width": "10%" + }, + { + "field": "embedding_engine", + "title": "向量引擎", + "width": "15%" + }, + { + "field": "created_at", + "title": "创建时间", + "width": "15%" + } + ], + "editfields": [ + { + "field": "name", + "uitype": "Text", + "required": true + }, + { + "field": "description", + "uitype": "Text" + }, + { + "field": "embedding_engine", + "uitype": "Text", + "default": "clip-vith14" + }, + { + "field": "vdb_collection", + "uitype": "Text" + }, + { + "field": "graph_name", + "uitype": "Text" + } + ], + "searchfields": [ + "name", + "description" + ], + "sort": "created_at desc", + "data_filter": { + "org_id": "{{userorgid}}" + } + } +} diff --git a/json/rag_org_storage_limits.json b/json/rag_org_storage_limits.json new file mode 100644 index 0000000..16dc8e5 --- /dev/null +++ b/json/rag_org_storage_limits.json @@ -0,0 +1,40 @@ +{ + "tblname": "rag_org_storage_limits", + "params": { + "title": "机构存储限额", + "browserfields": [ + { + "field": "org_id", + "title": "机构ID", + "width": "35%" + }, + { + "field": "limit_bytes", + "title": "限额(字节)", + "width": "35%" + }, + { + "field": "updated_at", + "title": "更新时间", + "width": "30%" + } + ], + "editfields": [ + { + "field": "org_id", + "uitype": "Text", + "required": true + }, + { + "field": "limit_bytes", + "uitype": "Text", + "required": true, + "default": "104857600" + } + ], + "searchfields": [ + "org_id" + ], + "sort": "updated_at desc" + } +} diff --git a/json/rag_subscriptions.json b/json/rag_subscriptions.json new file mode 100644 index 0000000..288b9f2 --- /dev/null +++ b/json/rag_subscriptions.json @@ -0,0 +1,90 @@ +{ + "tblname": "rag_subscriptions", + "params": { + "title": "订阅管理", + "browserfields": [ + { + "field": "org_id", + "title": "机构", + "width": "15%" + }, + { + "field": "plan_name", + "title": "套餐", + "width": "15%" + }, + { + "field": "disk_quota_bytes", + "title": "磁盘配额", + "width": "12%" + }, + { + "field": "disk_used_bytes", + "title": "已用磁盘", + "width": "12%" + }, + { + "field": "doc_used", + "title": "文档数", + "width": "10%" + }, + { + "field": "start_date", + "title": "开始", + "width": "12%" + }, + { + "field": "end_date", + "title": "结束", + "width": "12%" + }, + { + "field": "status", + "title": "状态", + "width": "12%" + } + ], + "editfields": [ + { + "field": "org_id", + "uitype": "Text", + "required": true + }, + { + "field": "plan_name", + "uitype": "Text", + "required": true + }, + { + "field": "disk_quota_bytes", + "uitype": "Text", + "default": "1073741824" + }, + { + "field": "doc_quota", + "uitype": "Text" + }, + { + "field": "kb_quota", + "uitype": "Text" + }, + { + "field": "api_call_quota", + "uitype": "Text" + }, + { + "field": "start_date", + "uitype": "Text" + }, + { + "field": "end_date", + "uitype": "Text" + } + ], + "searchfields": [ + "org_id", + "plan_name" + ], + "sort": "created_at desc" + } +} diff --git a/json/rag_tags.json b/json/rag_tags.json new file mode 100644 index 0000000..9dfdb50 --- /dev/null +++ b/json/rag_tags.json @@ -0,0 +1,55 @@ +{ + "tblname": "rag_tags", + "params": { + "title": "标签管理", + "browserfields": [ + { + "field": "name", + "title": "标签名", + "width": "25%" + }, + { + "field": "color", + "title": "颜色", + "width": "15%" + }, + { + "field": "kb_id", + "title": "知识库", + "width": "30%" + }, + { + "field": "created_at", + "title": "创建时间", + "width": "30%" + } + ], + "editfields": [ + { + "field": "name", + "uitype": "Text", + "required": true, + "label": "标签名" + }, + { + "field": "kb_id", + "uitype": "Text", + "required": true, + "label": "知识库ID" + }, + { + "field": "color", + "uitype": "Text", + "default": "#3b82f6", + "label": "颜色(#hex)" + } + ], + "searchfields": [ + "name" + ], + "sort": "created_at desc", + "data_filter": { + "org_id": "{{userorgid}}" + } + } +} diff --git a/json/subscriptions.json b/json/subscriptions.json deleted file mode 100644 index 53f7ae5..0000000 --- a/json/subscriptions.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "tblname": "subscriptions", - "params": { - "title": "订阅管理", - "browserfields": [ - {"field": "org_id", "title": "机构", "width": "15%"}, - {"field": "plan_name", "title": "套餐", "width": "15%"}, - {"field": "disk_quota_bytes", "title": "磁盘配额", "width": "12%"}, - {"field": "disk_used_bytes", "title": "已用磁盘", "width": "12%"}, - {"field": "doc_used", "title": "文档数", "width": "10%"}, - {"field": "start_date", "title": "开始", "width": "12%"}, - {"field": "end_date", "title": "结束", "width": "12%"}, - {"field": "status", "title": "状态", "width": "12%"} - ], - "editfields": [ - {"field": "org_id", "uitype": "Text", "required": true}, - {"field": "plan_name", "uitype": "Text", "required": true}, - {"field": "disk_quota_bytes", "uitype": "Text", "default": "1073741824"}, - {"field": "doc_quota", "uitype": "Text"}, - {"field": "kb_quota", "uitype": "Text"}, - {"field": "api_call_quota", "uitype": "Text"}, - {"field": "start_date", "uitype": "Text"}, - {"field": "end_date", "uitype": "Text"} - ], - "searchfields": ["org_id", "plan_name"], - "sort": "created_at desc" - } -} diff --git a/json/tags.json b/json/tags.json deleted file mode 100644 index acdf5dd..0000000 --- a/json/tags.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "tblname": "tags", - "params": { - "title": "标签管理", - "browserfields": [ - {"field": "name", "title": "标签名", "width": "25%"}, - {"field": "color", "title": "颜色", "width": "15%"}, - {"field": "kb_id", "title": "知识库", "width": "30%"}, - {"field": "created_at", "title": "创建时间", "width": "30%"} - ], - "editfields": [ - {"field": "name", "uitype": "Text", "required": true, "label": "标签名"}, - {"field": "kb_id", "uitype": "Text", "required": true, "label": "知识库ID"}, - {"field": "color", "uitype": "Text", "default": "#3b82f6", "label": "颜色(#hex)"} - ], - "searchfields": ["name"], - "sort": "created_at desc", - "data_filter": { - "org_id": "{{userorgid}}" - } - } -} diff --git a/models/documents.json b/models/documents.json deleted file mode 100644 index 937f7d1..0000000 --- a/models/documents.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "tblname": "documents", - "params": { - "title": "文档管理", - "browserfields": [ - {"field": "file_name", "title": "文件名", "width": "25%"}, - {"field": "file_type", "title": "类型", "width": "10%"}, - {"field": "file_size", "title": "大小", "width": "10%"}, - {"field": "chunk_count", "title": "分片数", "width": "8%"}, - {"field": "status", "title": "状态", "width": "10%"}, - {"field": "kb_id", "title": "知识库", "width": "17%"}, - {"field": "created_at", "title": "上传时间", "width": "20%"} - ], - "editfields": [ - {"field": "file_name", "uitype": "Text", "required": true}, - {"field": "kb_id", "uitype": "Text", "required": true}, - {"field": "file_type", "uitype": "Text"} - ], - "searchfields": ["file_name"], - "sort": "created_at desc", - "data_filter": { - "org_id": "{{userorgid}}" - } - } -} diff --git a/models/engine_configs.json b/models/engine_configs.json deleted file mode 100644 index c338fb2..0000000 --- a/models/engine_configs.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "tblname": "engine_configs", - "params": { - "title": "引擎配置", - "browserfields": [ - {"field": "engine_type", "title": "引擎类型", "width": "15%"}, - {"field": "engine_name", "title": "名称", "width": "15%"}, - {"field": "endpoint_url", "title": "服务地址", "width": "25%"}, - {"field": "model_name", "title": "模型", "width": "15%"}, - {"field": "is_default", "title": "默认", "width": "8%"}, - {"field": "status", "title": "状态", "width": "10%"}, - {"field": "org_id", "title": "机构", "width": "12%"} - ], - "editfields": [ - {"field": "engine_type", "uitype": "Text", "required": true}, - {"field": "engine_name", "uitype": "Text", "required": true}, - {"field": "endpoint_url", "uitype": "Text"}, - {"field": "api_key", "uitype": "Text"}, - {"field": "model_name", "uitype": "Text"}, - {"field": "is_default", "uitype": "Text"}, - {"field": "config_json", "uitype": "Text"} - ], - "searchfields": ["engine_type", "engine_name", "endpoint_url"], - "sort": "engine_type, priority desc" - } -} diff --git a/models/knowledge_bases.json b/models/knowledge_bases.json deleted file mode 100644 index 99482d5..0000000 --- a/models/knowledge_bases.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "tblname": "knowledge_bases", - "params": { - "title": "知识库管理", - "browserfields": [ - {"field": "name", "title": "名称", "width": "20%"}, - {"field": "description", "title": "描述", "width": "30%"}, - {"field": "doc_count", "title": "文档数", "width": "10%"}, - {"field": "total_size", "title": "大小", "width": "10%"}, - {"field": "embedding_engine", "title": "向量引擎", "width": "15%"}, - {"field": "created_at", "title": "创建时间", "width": "15%"} - ], - "editfields": [ - {"field": "name", "uitype": "Text", "required": true}, - {"field": "description", "uitype": "Text"}, - {"field": "embedding_engine", "uitype": "Text", "default": "clip-vith14"}, - {"field": "vdb_collection", "uitype": "Text"}, - {"field": "graph_name", "uitype": "Text"} - ], - "searchfields": ["name", "description"], - "sort": "created_at desc", - "data_filter": { - "org_id": "{{userorgid}}" - } - } -} diff --git a/models/org_storage_limits.json b/models/org_storage_limits.json deleted file mode 100644 index f40d581..0000000 --- a/models/org_storage_limits.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "tblname": "org_storage_limits", - "params": { - "title": "机构存储限额", - "browserfields": [ - {"field": "org_id", "title": "机构ID", "width": "35%"}, - {"field": "limit_bytes", "title": "限额(字节)", "width": "35%"}, - {"field": "updated_at", "title": "更新时间", "width": "30%"} - ], - "editfields": [ - {"field": "org_id", "uitype": "Text", "required": true}, - {"field": "limit_bytes", "uitype": "Text", "required": true, "default": "104857600"} - ], - "searchfields": ["org_id"], - "sort": "updated_at desc" - } -} diff --git a/models/rag_document_chunks.json b/models/rag_document_chunks.json new file mode 100644 index 0000000..6d6bdec --- /dev/null +++ b/models/rag_document_chunks.json @@ -0,0 +1,129 @@ +{ + "summary": [ + { + "name": "rag_document_chunks", + "title": "文档分片", + "primary": [ + "id" + ] + } + ], + "fields": [ + { + "name": "id", + "title": "ID", + "type": "str", + "length": 32, + "nullable": "no" + }, + { + "name": "doc_id", + "title": "文档ID", + "type": "str", + "length": 32, + "nullable": "yes" + }, + { + "name": "kb_id", + "title": "知识库ID", + "type": "str", + "length": 32, + "nullable": "yes" + }, + { + "name": "chunk_index", + "title": "分片序号", + "type": "int", + "nullable": "yes" + }, + { + "name": "chunk_type", + "title": "分片类型", + "type": "str", + "length": 32, + "nullable": "yes" + }, + { + "name": "content", + "title": "文本内容", + "type": "text", + "nullable": "yes" + }, + { + "name": "description", + "title": "描述", + "type": "text", + "nullable": "yes" + }, + { + "name": "vector_id", + "title": "向量库ID", + "type": "str", + "length": 128, + "nullable": "yes" + }, + { + "name": "start_offset", + "title": "起始偏移", + "type": "int", + "nullable": "yes" + }, + { + "name": "end_offset", + "title": "结束偏移", + "type": "int", + "nullable": "yes" + }, + { + "name": "time_start", + "title": "时间起始(秒)", + "type": "double", + "length": 10, + "dec": 3, + "nullable": "yes" + }, + { + "name": "time_end", + "title": "时间结束(秒)", + "type": "double", + "length": 10, + "dec": 3, + "nullable": "yes" + }, + { + "name": "metadata", + "title": "元数据JSON", + "type": "text", + "nullable": "yes" + }, + { + "name": "created_at", + "title": "创建时间", + "type": "datetime", + "nullable": "yes" + } + ], + "indexes": [ + { + "name": "idx_chunk_doc", + "idxtype": "index", + "idxfields": [ + "doc_id" + ] + }, + { + "name": "idx_chunk_kb", + "idxtype": "index", + "idxfields": [ + "kb_id" + ] + }, + { + "name": "idx_chunk_vector", + "idxtype": "index", + "idxfields": [ + "vector_id" + ] + } + ] +} diff --git a/models/rag_documents.json b/models/rag_documents.json new file mode 100644 index 0000000..f1c3774 --- /dev/null +++ b/models/rag_documents.json @@ -0,0 +1,151 @@ +{ + "summary": [ + { + "name": "rag_documents", + "title": "文档", + "primary": [ + "id" + ] + } + ], + "fields": [ + { + "name": "id", + "title": "ID", + "type": "str", + "length": 32, + "nullable": "no" + }, + { + "name": "kb_id", + "title": "知识库ID", + "type": "str", + "length": 32, + "nullable": "yes" + }, + { + "name": "folder_id", + "title": "目录ID", + "type": "str", + "length": 32, + "nullable": "yes", + "default": "" + }, + { + "name": "file_name", + "title": "文件名", + "type": "str", + "length": 255, + "nullable": "yes" + }, + { + "name": "file_type", + "title": "文件类型", + "type": "str", + "length": 32, + "nullable": "yes" + }, + { + "name": "file_size", + "title": "文件大小", + "type": "int", + "nullable": "yes" + }, + { + "name": "file_path", + "title": "文件路径", + "type": "str", + "length": 500, + "nullable": "yes" + }, + { + "name": "mime_type", + "title": "MIME类型", + "type": "str", + "length": 128, + "nullable": "yes" + }, + { + "name": "chunk_count", + "title": "分片数", + "type": "int", + "nullable": "yes" + }, + { + "name": "embed_dim", + "title": "向量维度", + "type": "int", + "nullable": "yes", + "default": "1024" + }, + { + "name": "metadata", + "title": "元数据JSON", + "type": "text", + "nullable": "yes" + }, + { + "name": "status", + "title": "状态", + "type": "str", + "length": 16, + "nullable": "yes", + "default": "pending" + }, + { + "name": "error_msg", + "title": "错误信息", + "type": "text", + "nullable": "yes" + }, + { + "name": "org_id", + "title": "所属机构", + "type": "str", + "length": 32, + "nullable": "yes" + }, + { + "name": "created_by", + "title": "创建人", + "type": "str", + "length": 32, + "nullable": "yes" + }, + { + "name": "created_at", + "title": "创建时间", + "type": "datetime", + "nullable": "yes" + }, + { + "name": "updated_at", + "title": "更新时间", + "type": "datetime", + "nullable": "yes" + } + ], + "indexes": [ + { + "name": "idx_doc_kb", + "idxtype": "index", + "idxfields": [ + "kb_id" + ] + }, + { + "name": "idx_doc_org", + "idxtype": "index", + "idxfields": [ + "org_id" + ] + }, + { + "name": "idx_doc_status", + "idxtype": "index", + "idxfields": [ + "status" + ] + } + ] +} diff --git a/models/rag_engine_configs.json b/models/rag_engine_configs.json new file mode 100644 index 0000000..ecbdb9a --- /dev/null +++ b/models/rag_engine_configs.json @@ -0,0 +1,124 @@ +{ + "summary": [ + { + "name": "rag_engine_configs", + "title": "引擎配置", + "primary": [ + "id" + ] + } + ], + "fields": [ + { + "name": "id", + "title": "ID", + "type": "str", + "length": 32, + "nullable": "no" + }, + { + "name": "org_id", + "title": "机构ID", + "type": "str", + "length": 32, + "nullable": "yes" + }, + { + "name": "engine_type", + "title": "引擎类型", + "type": "str", + "length": 32, + "nullable": "yes" + }, + { + "name": "engine_name", + "title": "引擎名称", + "type": "str", + "length": 64, + "nullable": "yes" + }, + { + "name": "endpoint_url", + "title": "服务地址", + "type": "str", + "length": 255, + "nullable": "yes" + }, + { + "name": "api_key", + "title": "API密钥", + "type": "str", + "length": 255, + "nullable": "yes" + }, + { + "name": "model_name", + "title": "模型名称", + "type": "str", + "length": 128, + "nullable": "yes" + }, + { + "name": "config_json", + "title": "扩展配置JSON", + "type": "text", + "nullable": "yes" + }, + { + "name": "is_default", + "title": "是否默认", + "type": "int", + "nullable": "yes" + }, + { + "name": "priority", + "title": "优先级", + "type": "int", + "nullable": "yes" + }, + { + "name": "status", + "title": "状态", + "type": "str", + "length": 16, + "nullable": "yes", + "default": "active" + }, + { + "name": "health_check_at", + "title": "最近健康检查", + "type": "datetime", + "nullable": "yes" + }, + { + "name": "created_at", + "title": "创建时间", + "type": "datetime", + "nullable": "yes" + }, + { + "name": "updated_at", + "title": "更新时间", + "type": "datetime", + "nullable": "yes" + } + ], + "indexes": [ + { + "name": "idx_eng_type", + "idxtype": "index", + "idxfields": [ + "engine_type", + "is_default" + ] + }, + { + "name": "idx_eng_org", + "idxtype": "index", + "idxfields": [ + "org_id", + "engine_type" + ] + } + ] +} diff --git a/models/rag_entities.json b/models/rag_entities.json new file mode 100644 index 0000000..3f49996 --- /dev/null +++ b/models/rag_entities.json @@ -0,0 +1,138 @@ +{ + "summary": [ + { + "name": "rag_entities", + "title": "实体", + "primary": [ + "id" + ] + } + ], + "fields": [ + { + "name": "id", + "title": "ID", + "type": "str", + "length": 32, + "nullable": "no" + }, + { + "name": "kb_id", + "title": "知识库ID", + "type": "str", + "length": 32, + "nullable": "yes" + }, + { + "name": "name", + "title": "实体名", + "type": "str", + "length": 255, + "nullable": "yes" + }, + { + "name": "entity_type", + "title": "实体类型", + "type": "str", + "length": 64, + "nullable": "yes" + }, + { + "name": "description", + "title": "描述", + "type": "text", + "nullable": "yes" + }, + { + "name": "graph_node_id", + "title": "图节点ID", + "type": "str", + "length": 128, + "nullable": "yes" + }, + { + "name": "source_doc_ids", + "title": "来源文档ID列表", + "type": "text", + "nullable": "yes" + }, + { + "name": "face_embedding_id", + "title": "人脸特征ID", + "type": "str", + "length": 128, + "nullable": "yes" + }, + { + "name": "voice_embedding_id", + "title": "声纹特征ID", + "type": "str", + "length": 128, + "nullable": "yes" + }, + { + "name": "metadata", + "title": "元数据JSON", + "type": "text", + "nullable": "yes" + }, + { + "name": "org_id", + "title": "所属机构", + "type": "str", + "length": 32, + "nullable": "yes" + }, + { + "name": "created_at", + "title": "创建时间", + "type": "datetime", + "nullable": "yes" + }, + { + "name": "updated_at", + "title": "更新时间", + "type": "datetime", + "nullable": "yes" + } + ], + "indexes": [ + { + "name": "idx_entity_kb", + "idxtype": "index", + "idxfields": [ + "kb_id" + ] + }, + { + "name": "idx_entity_name", + "idxtype": "index", + "idxfields": [ + "kb_id", + "name" + ] + }, + { + "name": "idx_entity_type", + "idxtype": "index", + "idxfields": [ + "kb_id", + "entity_type" + ] + }, + { + "name": "idx_entity_face", + "idxtype": "index", + "idxfields": [ + "face_embedding_id" + ] + }, + { + "name": "idx_entity_voice", + "idxtype": "index", + "idxfields": [ + "voice_embedding_id" + ] + } + ] +} diff --git a/models/rag_entity_relations.json b/models/rag_entity_relations.json new file mode 100644 index 0000000..d1aea6b --- /dev/null +++ b/models/rag_entity_relations.json @@ -0,0 +1,103 @@ +{ + "summary": [ + { + "name": "rag_entity_relations", + "title": "实体关系", + "primary": [ + "id" + ] + } + ], + "fields": [ + { + "name": "id", + "title": "ID", + "type": "str", + "length": 32, + "nullable": "no" + }, + { + "name": "kb_id", + "title": "知识库ID", + "type": "str", + "length": 32, + "nullable": "yes" + }, + { + "name": "subject_id", + "title": "主体实体ID", + "type": "str", + "length": 32, + "nullable": "yes" + }, + { + "name": "object_id", + "title": "客体实体ID", + "type": "str", + "length": 32, + "nullable": "yes" + }, + { + "name": "relation_type", + "title": "关系类型", + "type": "str", + "length": 64, + "nullable": "yes" + }, + { + "name": "description", + "title": "关系描述", + "type": "text", + "nullable": "yes" + }, + { + "name": "graph_edge_id", + "title": "图边ID", + "type": "str", + "length": 128, + "nullable": "yes" + }, + { + "name": "source_chunk_ids", + "title": "来源分片ID列表", + "type": "text", + "nullable": "yes" + }, + { + "name": "org_id", + "title": "所属机构", + "type": "str", + "length": 32, + "nullable": "yes" + }, + { + "name": "created_at", + "title": "创建时间", + "type": "datetime", + "nullable": "yes" + } + ], + "indexes": [ + { + "name": "idx_rel_kb", + "idxtype": "index", + "idxfields": [ + "kb_id" + ] + }, + { + "name": "idx_rel_subject", + "idxtype": "index", + "idxfields": [ + "subject_id" + ] + }, + { + "name": "idx_rel_object", + "idxtype": "index", + "idxfields": [ + "object_id" + ] + } + ] +} diff --git a/models/rag_knowledge_bases.json b/models/rag_knowledge_bases.json new file mode 100644 index 0000000..45171b3 --- /dev/null +++ b/models/rag_knowledge_bases.json @@ -0,0 +1,124 @@ +{ + "summary": [ + { + "name": "rag_knowledge_bases", + "title": "知识库", + "primary": [ + "id" + ] + } + ], + "fields": [ + { + "name": "id", + "title": "ID", + "type": "str", + "length": 32, + "nullable": "no" + }, + { + "name": "name", + "title": "名称", + "type": "str", + "length": 100, + "nullable": "yes" + }, + { + "name": "description", + "title": "描述", + "type": "text", + "nullable": "yes" + }, + { + "name": "org_id", + "title": "所属机构", + "type": "str", + "length": 32, + "nullable": "yes" + }, + { + "name": "embedding_engine", + "title": "向量化引擎", + "type": "str", + "length": 64, + "nullable": "yes", + "default": "clip-vith14" + }, + { + "name": "vdb_collection", + "title": "向量库集合名", + "type": "str", + "length": 128, + "nullable": "yes" + }, + { + "name": "graph_name", + "title": "图名称", + "type": "str", + "length": 128, + "nullable": "yes" + }, + { + "name": "doc_count", + "title": "文档数", + "type": "int", + "nullable": "yes" + }, + { + "name": "chunk_count", + "title": "分片数", + "type": "int", + "nullable": "yes" + }, + { + "name": "total_size", + "title": "总大小(字节)", + "type": "int", + "nullable": "yes" + }, + { + "name": "status", + "title": "状态", + "type": "str", + "length": 16, + "nullable": "yes", + "default": "active" + }, + { + "name": "created_by", + "title": "创建人", + "type": "str", + "length": 32, + "nullable": "yes" + }, + { + "name": "created_at", + "title": "创建时间", + "type": "datetime", + "nullable": "yes" + }, + { + "name": "updated_at", + "title": "更新时间", + "type": "datetime", + "nullable": "yes" + } + ], + "indexes": [ + { + "name": "idx_kb_name", + "idxtype": "unique", + "idxfields": [ + "org_id", + "name" + ] + }, + { + "name": "idx_kb_org", + "idxtype": "index", + "idxfields": [ + "org_id" + ] + } + ] +} diff --git a/models/rag_media_tags.json b/models/rag_media_tags.json new file mode 100644 index 0000000..f53721f --- /dev/null +++ b/models/rag_media_tags.json @@ -0,0 +1,87 @@ +{ + "summary": [ + { + "name": "rag_media_tags", + "title": "媒体标签关联", + "primary": [ + "id" + ] + } + ], + "fields": [ + { + "name": "id", + "title": "ID", + "type": "str", + "length": 32, + "nullable": "no" + }, + { + "name": "kb_id", + "title": "知识库ID", + "type": "str", + "length": 32, + "nullable": "no" + }, + { + "name": "media_type", + "title": "媒体类型", + "type": "str", + "length": 32, + "nullable": "no" + }, + { + "name": "media_id", + "title": "媒体ID", + "type": "str", + "length": 32, + "nullable": "no" + }, + { + "name": "tag_id", + "title": "标签ID", + "type": "str", + "length": 32, + "nullable": "no" + }, + { + "name": "created_at", + "title": "创建时间", + "type": "datetime", + "nullable": "yes" + } + ], + "indexes": [ + { + "name": "idx_mt_unique", + "idxtype": "unique", + "idxfields": [ + "media_type", + "media_id", + "tag_id" + ] + }, + { + "name": "idx_mt_media", + "idxtype": "index", + "idxfields": [ + "media_type", + "media_id" + ] + }, + { + "name": "idx_mt_tag", + "idxtype": "index", + "idxfields": [ + "tag_id" + ] + }, + { + "name": "idx_mt_kb", + "idxtype": "index", + "idxfields": [ + "kb_id" + ] + } + ] +} diff --git a/models/rag_org_storage_limits.json b/models/rag_org_storage_limits.json new file mode 100644 index 0000000..41a6afc --- /dev/null +++ b/models/rag_org_storage_limits.json @@ -0,0 +1,39 @@ +{ + "summary": [ + { + "name": "rag_org_storage_limits", + "title": "机构存储限额", + "primary": [ + "org_id" + ] + } + ], + "fields": [ + { + "name": "org_id", + "title": "机构ID", + "type": "str", + "length": 32, + "nullable": "no" + }, + { + "name": "limit_bytes", + "title": "限额(字节)", + "type": "bigint", + "nullable": "no", + "default": "104857600" + }, + { + "name": "created_at", + "title": "创建时间", + "type": "datetime", + "nullable": "yes" + }, + { + "name": "updated_at", + "title": "更新时间", + "type": "datetime", + "nullable": "yes" + } + ] +} diff --git a/models/rag_subscriptions.json b/models/rag_subscriptions.json new file mode 100644 index 0000000..d32cdf3 --- /dev/null +++ b/models/rag_subscriptions.json @@ -0,0 +1,134 @@ +{ + "summary": [ + { + "name": "rag_subscriptions", + "title": "租户订阅", + "primary": [ + "id" + ] + } + ], + "fields": [ + { + "name": "id", + "title": "ID", + "type": "str", + "length": 32, + "nullable": "no" + }, + { + "name": "org_id", + "title": "机构ID", + "type": "str", + "length": 32, + "nullable": "yes" + }, + { + "name": "plan_name", + "title": "套餐名称", + "type": "str", + "length": 64, + "nullable": "yes" + }, + { + "name": "disk_quota_bytes", + "title": "磁盘配额(字节)", + "type": "int", + "nullable": "yes", + "default": "1073741824" + }, + { + "name": "disk_used_bytes", + "title": "已用磁盘(字节)", + "type": "int", + "nullable": "yes" + }, + { + "name": "doc_quota", + "title": "文档数配额", + "type": "int", + "nullable": "yes", + "default": "1000" + }, + { + "name": "doc_used", + "title": "已用文档数", + "type": "int", + "nullable": "yes" + }, + { + "name": "kb_quota", + "title": "知识库数配额", + "type": "int", + "nullable": "yes", + "default": "10" + }, + { + "name": "kb_used", + "title": "已用知识库数", + "type": "int", + "nullable": "yes" + }, + { + "name": "api_call_quota", + "title": "API调用配额(月)", + "type": "int", + "nullable": "yes", + "default": "10000" + }, + { + "name": "api_call_used", + "title": "已用API调用(月)", + "type": "int", + "nullable": "yes" + }, + { + "name": "start_date", + "title": "开始日期", + "type": "date", + "nullable": "yes" + }, + { + "name": "end_date", + "title": "结束日期", + "type": "date", + "nullable": "yes" + }, + { + "name": "status", + "title": "状态", + "type": "str", + "length": 16, + "nullable": "yes", + "default": "active" + }, + { + "name": "created_at", + "title": "创建时间", + "type": "datetime", + "nullable": "yes" + }, + { + "name": "updated_at", + "title": "更新时间", + "type": "datetime", + "nullable": "yes" + } + ], + "indexes": [ + { + "name": "idx_sub_org", + "idxtype": "unique", + "idxfields": [ + "org_id" + ] + }, + { + "name": "idx_sub_status", + "idxtype": "index", + "idxfields": [ + "status" + ] + } + ] +} diff --git a/models/rag_tags.json b/models/rag_tags.json new file mode 100644 index 0000000..7412f5a --- /dev/null +++ b/models/rag_tags.json @@ -0,0 +1,73 @@ +{ + "summary": [ + { + "name": "rag_tags", + "title": "标签", + "primary": [ + "id" + ] + } + ], + "fields": [ + { + "name": "id", + "title": "ID", + "type": "str", + "length": 32, + "nullable": "no" + }, + { + "name": "kb_id", + "title": "知识库ID", + "type": "str", + "length": 32, + "nullable": "no" + }, + { + "name": "name", + "title": "标签名", + "type": "str", + "length": 64, + "nullable": "no" + }, + { + "name": "color", + "title": "颜色", + "type": "str", + "length": 16, + "nullable": "no", + "default": "#3b82f6" + }, + { + "name": "org_id", + "title": "所属机构", + "type": "str", + "length": 32, + "nullable": "no", + "default": "" + }, + { + "name": "created_at", + "title": "创建时间", + "type": "datetime", + "nullable": "yes" + } + ], + "indexes": [ + { + "name": "idx_tag_name", + "idxtype": "unique", + "idxfields": [ + "kb_id", + "name" + ] + }, + { + "name": "idx_tag_kb", + "idxtype": "index", + "idxfields": [ + "kb_id" + ] + } + ] +} diff --git a/models/rag_usage_logs.json b/models/rag_usage_logs.json new file mode 100644 index 0000000..d1093b9 --- /dev/null +++ b/models/rag_usage_logs.json @@ -0,0 +1,110 @@ +{ + "summary": [ + { + "name": "rag_usage_logs", + "title": "用量日志", + "primary": [ + "id" + ] + } + ], + "fields": [ + { + "name": "id", + "title": "ID", + "type": "str", + "length": 32, + "nullable": "no" + }, + { + "name": "org_id", + "title": "机构ID", + "type": "str", + "length": 32, + "nullable": "yes" + }, + { + "name": "kb_id", + "title": "知识库ID", + "type": "str", + "length": 32, + "nullable": "yes" + }, + { + "name": "operation", + "title": "操作类型", + "type": "str", + "length": 32, + "nullable": "yes" + }, + { + "name": "bytes_delta", + "title": "磁盘变化(字节)", + "type": "int", + "nullable": "yes" + }, + { + "name": "bytes_total", + "title": "累计磁盘", + "type": "int", + "nullable": "yes" + }, + { + "name": "api_calls", + "title": "API调用数", + "type": "int", + "nullable": "yes", + "default": "1" + }, + { + "name": "engine_type", + "title": "引擎类型", + "type": "str", + "length": 32, + "nullable": "yes" + }, + { + "name": "tokens_used", + "title": "Token消耗", + "type": "int", + "nullable": "yes" + }, + { + "name": "cost_estimate", + "title": "预估费用", + "type": "double", + "length": 20, + "dec": 4, + "nullable": "yes" + }, + { + "name": "detail", + "title": "详情JSON", + "type": "text", + "nullable": "yes" + }, + { + "name": "created_at", + "title": "创建时间", + "type": "datetime", + "nullable": "yes" + } + ], + "indexes": [ + { + "name": "idx_usage_org", + "idxtype": "index", + "idxfields": [ + "org_id", + "created_at" + ] + }, + { + "name": "idx_usage_kb", + "idxtype": "index", + "idxfields": [ + "kb_id" + ] + } + ] +} diff --git a/models/subscriptions.json b/models/subscriptions.json deleted file mode 100644 index 53f7ae5..0000000 --- a/models/subscriptions.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "tblname": "subscriptions", - "params": { - "title": "订阅管理", - "browserfields": [ - {"field": "org_id", "title": "机构", "width": "15%"}, - {"field": "plan_name", "title": "套餐", "width": "15%"}, - {"field": "disk_quota_bytes", "title": "磁盘配额", "width": "12%"}, - {"field": "disk_used_bytes", "title": "已用磁盘", "width": "12%"}, - {"field": "doc_used", "title": "文档数", "width": "10%"}, - {"field": "start_date", "title": "开始", "width": "12%"}, - {"field": "end_date", "title": "结束", "width": "12%"}, - {"field": "status", "title": "状态", "width": "12%"} - ], - "editfields": [ - {"field": "org_id", "uitype": "Text", "required": true}, - {"field": "plan_name", "uitype": "Text", "required": true}, - {"field": "disk_quota_bytes", "uitype": "Text", "default": "1073741824"}, - {"field": "doc_quota", "uitype": "Text"}, - {"field": "kb_quota", "uitype": "Text"}, - {"field": "api_call_quota", "uitype": "Text"}, - {"field": "start_date", "uitype": "Text"}, - {"field": "end_date", "uitype": "Text"} - ], - "searchfields": ["org_id", "plan_name"], - "sort": "created_at desc" - } -} diff --git a/models/tags.json b/models/tags.json deleted file mode 100644 index acdf5dd..0000000 --- a/models/tags.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "tblname": "tags", - "params": { - "title": "标签管理", - "browserfields": [ - {"field": "name", "title": "标签名", "width": "25%"}, - {"field": "color", "title": "颜色", "width": "15%"}, - {"field": "kb_id", "title": "知识库", "width": "30%"}, - {"field": "created_at", "title": "创建时间", "width": "30%"} - ], - "editfields": [ - {"field": "name", "uitype": "Text", "required": true, "label": "标签名"}, - {"field": "kb_id", "uitype": "Text", "required": true, "label": "知识库ID"}, - {"field": "color", "uitype": "Text", "default": "#3b82f6", "label": "颜色(#hex)"} - ], - "searchfields": ["name"], - "sort": "created_at desc", - "data_filter": { - "org_id": "{{userorgid}}" - } - } -} diff --git a/rag/init.py b/rag/init.py index 4456b25..3ea8aa7 100644 --- a/rag/init.py +++ b/rag/init.py @@ -27,10 +27,10 @@ async def kb_list_handler(request, params_kw, *args, **kwargs): try: userorgid = await env.get_userorgid() async with get_sor_context(env, 'rag') as sor: - recs = await sor.R("knowledge_bases", {}) + recs = await sor.R("rag_knowledge_bases", {}) cards = [] for r in recs: - doc_recs = await sor.R("documents", {"kb_id": r.id}) + doc_recs = await sor.R("rag_documents", {"kb_id": r.id}) doc_count = len(doc_recs) total_size = r.total_size or 0 if total_size >= 1073741824: @@ -64,7 +64,7 @@ async def engines_handler(request, params_kw, *args, **kwargs): try: userorgid = await env.get_userorgid() async with get_sor_context(env, 'rag') as sor: - sql = "SELECT * FROM engine_configs WHERE status='active' AND (org_id IS NULL OR org_id=${org_id}$) ORDER BY engine_type, priority DESC" + sql = "SELECT * FROM rag_engine_configs WHERE status='active' AND (org_id IS NULL OR org_id=${org_id}$) ORDER BY engine_type, priority DESC" recs = await sor.sqlExe(sql, {"org_id": userorgid}) rows = [dict(r) for r in recs] return json.dumps({"status": "SUCCEEDED", "data": {"rows": rows, "total": len(rows)}}, ensure_ascii=False, default=str) @@ -173,10 +173,10 @@ async def _resolve_search_kbs(env, userorgid, kb_id): """Resolve KB IDs: specific or all org KBs""" async with get_sor_context(env, 'rag') as sor: if kb_id: - recs = await sor.R("knowledge_bases", {"id": kb_id}) + recs = await sor.R("rag_knowledge_bases", {"id": kb_id}) return [r.id for r in recs] # All org KBs (global + org-specific) - sql = "SELECT id FROM knowledge_bases WHERE org_id IS NULL OR org_id=${org_id}$" + sql = "SELECT id FROM rag_knowledge_bases WHERE org_id IS NULL OR org_id=${org_id}$" recs = await sor.sqlExe(sql, {"org_id": userorgid}) return [r.id for r in recs] @@ -219,7 +219,7 @@ async def _build_search_vector(query, file_data, file_name, env=None, kb_id=''): emb_engine = 'clip-vith14' if env is not None and kb_id: async with get_sor_context(env, 'rag') as sor: - krecs = await sor.sqlExe("SELECT embedding_engine FROM knowledge_bases WHERE id=${kb_id}$", {"kb_id": kb_id}) + krecs = await sor.sqlExe("SELECT embedding_engine FROM rag_knowledge_bases WHERE id=${kb_id}$", {"kb_id": kb_id}) if krecs: emb_engine = (getattr(krecs[0], 'embedding_engine', '') or 'clip-vith14').strip() if emb_engine == 'bge-m3': @@ -287,7 +287,7 @@ async def _enrich_search_results(env, hits): async with get_sor_context(env, 'rag') as sor: recs = await sor.sqlExe( "SELECT id, file_name, file_type, file_size, status, kb_id, created_at " - "FROM documents WHERE id IN (" + ",".join(repr(d) for d in doc_ids) + ")", {}) + "FROM rag_documents WHERE id IN (" + ",".join(repr(d) for d in doc_ids) + ")", {}) doc_map = {r.id: dict(r) for r in recs} for h in hits: @@ -325,9 +325,9 @@ async def doc_upload_handler(request, params_kw, *args, **kwargs): quota_limit = 104857600 used = 0 async with get_sor_context(env, 'rag') as sor: - rec = await sor.sqlExe("SELECT COALESCE(SUM(file_size),0) AS used FROM documents WHERE org_id=${org_id}$", {"org_id": userorgid}) + rec = await sor.sqlExe("SELECT COALESCE(SUM(file_size),0) AS used FROM rag_documents WHERE org_id=${org_id}$", {"org_id": userorgid}) if rec: used = int(rec[0].used) - lim = await sor.sqlExe("SELECT limit_bytes FROM org_storage_limits WHERE org_id=${org_id}$", {"org_id": userorgid}) + lim = await sor.sqlExe("SELECT limit_bytes FROM rag_org_storage_limits WHERE org_id=${org_id}$", {"org_id": userorgid}) if lim: quota_limit = int(lim[0].limit_bytes) if used + file_size > quota_limit: return json.dumps({"error": "storage_quota_exceeded", @@ -342,7 +342,7 @@ async def doc_upload_handler(request, params_kw, *args, **kwargs): # Create document record async with get_sor_context(env, 'rag') as sor: await sor.sqlExe( - "INSERT INTO documents (id, kb_id, file_name, file_type, file_size, file_path, mime_type, status, org_id, created_at, updated_at) " + "INSERT INTO rag_documents (id, kb_id, file_name, file_type, file_size, file_path, mime_type, status, org_id, created_at, updated_at) " "VALUES (${id}$, ${kb_id}$, ${file_name}$, ${file_type}$, ${file_size}$, ${file_path}$, ${mime_type}$, 'pending', ${org_id}$, NOW(), NOW())", {"id": doc_id, "kb_id": kb_id, "file_name": file_name, "file_type": file_type, "file_size": file_size, "file_path": web_path, @@ -350,7 +350,7 @@ async def doc_upload_handler(request, params_kw, *args, **kwargs): # Update KB stats await sor.sqlExe( - "UPDATE knowledge_bases SET doc_count=doc_count+1, total_size=total_size+${size}$ WHERE id=${kb_id}$", + "UPDATE rag_knowledge_bases SET doc_count=doc_count+1, total_size=total_size+${size}$ WHERE id=${kb_id}$", {"size": file_size, "kb_id": kb_id}) # Trigger async ingest for text-based files via uapi @@ -363,17 +363,17 @@ async def doc_upload_handler(request, params_kw, *args, **kwargs): async with get_sor_context(env, 'rag') as sor: chunks_n = ingest_result.get("chunks", 0) if ingest_result else 0 await sor.sqlExe( - "UPDATE documents SET status='done', chunk_count=${chunks}$ WHERE id=${id}$", + "UPDATE rag_documents SET status='done', chunk_count=${chunks}$ WHERE id=${id}$", {"chunks": chunks_n, "id": doc_id}) if chunks_n: await sor.sqlExe( - "UPDATE knowledge_bases SET chunk_count=chunk_count+${n}$ WHERE id=${kb_id}$", + "UPDATE rag_knowledge_bases SET chunk_count=chunk_count+${n}$ WHERE id=${kb_id}$", {"n": chunks_n, "kb_id": kb_id}) except Exception as e: exception(f"uapi ingest failed: {e}") async with get_sor_context(env, 'rag') as sor: await sor.sqlExe( - "UPDATE documents SET status='error' WHERE id=${id}$", {"id": doc_id}) + "UPDATE rag_documents SET status='error' WHERE id=${id}$", {"id": doc_id}) return json.dumps({ "status": "SUCCEEDED", @@ -399,13 +399,13 @@ async def doc_delete_handler(request, params_kw, *args, **kwargs): async with get_sor_context(env, 'rag') as sor: # Get document info - recs = await sor.R("documents", {"id": doc_id}) + recs = await sor.R("rag_documents", {"id": doc_id}) if not recs: return json.dumps({"error": "document not found"}) doc = recs[0] # Get chunks to clean VDB - chunks = await sor.R("document_chunks", {"doc_id": doc_id}) + chunks = await sor.R("rag_document_chunks", {"doc_id": doc_id}) # Delete from VDB if chunks: @@ -424,14 +424,14 @@ async def doc_delete_handler(request, params_kw, *args, **kwargs): exception(f"graph delete failed: {e}") # Delete DB records - await sor.sqlExe("DELETE FROM document_chunks WHERE doc_id=${id}$", {"id": doc_id}) - await sor.sqlExe("DELETE FROM entities WHERE kb_id=${kb_id}$", {"kb_id": doc.kb_id}) - await sor.sqlExe("DELETE FROM entity_relations WHERE kb_id=${kb_id}$", {"kb_id": doc.kb_id}) - await sor.sqlExe("DELETE FROM documents WHERE id=${id}$", {"id": doc_id}) + await sor.sqlExe("DELETE FROM rag_document_chunks WHERE doc_id=${id}$", {"id": doc_id}) + await sor.sqlExe("DELETE FROM rag_entities WHERE kb_id=${kb_id}$", {"kb_id": doc.kb_id}) + await sor.sqlExe("DELETE FROM rag_entity_relations WHERE kb_id=${kb_id}$", {"kb_id": doc.kb_id}) + await sor.sqlExe("DELETE FROM rag_documents WHERE id=${id}$", {"id": doc_id}) # Update KB stats await sor.sqlExe( - "UPDATE knowledge_bases SET doc_count=GREATEST(doc_count-1,0), total_size=GREATEST(total_size-${size}$,0), chunk_count=GREATEST(chunk_count-${n}$,0) WHERE id=${kb_id}$", + "UPDATE rag_knowledge_bases SET doc_count=GREATEST(doc_count-1,0), total_size=GREATEST(total_size-${size}$,0), chunk_count=GREATEST(chunk_count-${n}$,0) WHERE id=${kb_id}$", {"size": doc.file_size, "n": len(chunks), "kb_id": doc.kb_id}) # Delete file from disk @@ -496,7 +496,7 @@ async def _rag_ingest_async(env, text, kb_id, doc_id): try: emb_engine = 'clip-vith14' async with get_sor_context(env, 'rag') as sor: - krecs = await sor.sqlExe("SELECT embedding_engine FROM knowledge_bases WHERE id=${kb_id}$", {"kb_id": kb_id}) + krecs = await sor.sqlExe("SELECT embedding_engine FROM rag_knowledge_bases WHERE id=${kb_id}$", {"kb_id": kb_id}) if krecs: emb_engine = (getattr(krecs[0], 'embedding_engine', '') or 'clip-vith14').strip() if emb_engine == 'bge-m3': @@ -552,7 +552,7 @@ async def _rag_ingest_async(env, text, kb_id, doc_id): async with get_sor_context(env, 'rag') as sor: for i, (chunk_text, vid) in enumerate(zip(chunks, vector_ids)): await sor.sqlExe( - "INSERT INTO document_chunks (id, doc_id, kb_id, chunk_index, content, vector_id, metadata, created_at) " + "INSERT INTO rag_document_chunks (id, doc_id, kb_id, chunk_index, content, vector_id, metadata, created_at) " "VALUES (${id}$, ${doc_id}$, ${kb_id}$, ${idx}$, ${content}$, ${vid}$, NOW())", {"id": f"{doc_id}_c{i}", "doc_id": doc_id, "kb_id": kb_id, "idx": i, "content": chunk_text[:2000], "vid": vid}) @@ -642,7 +642,7 @@ async def dir_create_handler(request, params_kw, *args, **kwargs): async with get_sor_context(env, 'rag') as sor: dir_id = uuid.uuid4().hex await sor.sqlExe( - "INSERT INTO document_chunks (id, doc_id, kb_id, chunk_index, chunk_type, content, description, created_at) " + "INSERT INTO rag_document_chunks (id, doc_id, kb_id, chunk_index, chunk_type, content, description, created_at) " "VALUES (${id}$, '', ${kb_id}$, 0, 'directory', ${name}$, ${parent}$, NOW())", {"id": dir_id, "kb_id": kb_id, "name": dir_name, "parent": parent_id}) return json.dumps({"status": "SUCCEEDED", "dir_id": dir_id}) @@ -659,7 +659,7 @@ async def dir_delete_handler(request, params_kw, *args, **kwargs): if not item_id: return json.dumps({"error": "item_id required"}) async with get_sor_context(env, 'rag') as sor: - await sor.sqlExe("DELETE FROM document_chunks WHERE id=${id}$ OR doc_id=${id}$", {"id": item_id}) + await sor.sqlExe("DELETE FROM rag_document_chunks WHERE id=${id}$ OR doc_id=${id}$", {"id": item_id}) return json.dumps({"status": "SUCCEEDED"}) except Exception as e: exception(f"dir_delete: {e}") @@ -676,11 +676,11 @@ async def dir_list_handler(request, params_kw, *args, **kwargs): async with get_sor_context(env, 'rag') as sor: # Get directories (chunk_type='directory') dirs = await sor.sqlExe( - "SELECT id, content as label, description as parent_id FROM document_chunks WHERE kb_id=${kb_id}$ AND chunk_type='directory'", + "SELECT id, content as label, description as parent_id FROM rag_document_chunks WHERE kb_id=${kb_id}$ AND chunk_type='directory'", {"kb_id": kb_id}) # Get files (documents table) docs = await sor.sqlExe( - "SELECT id, file_name as label, '' as parent_id FROM documents WHERE kb_id=${kb_id}$", + "SELECT id, file_name as label, '' as parent_id FROM rag_documents WHERE kb_id=${kb_id}$", {"kb_id": kb_id}) items = [] for d in dirs: @@ -705,14 +705,14 @@ async def tag_create_handler(request, params_kw, *args, **kwargs): userorgid = await env.get_userorgid() async with get_sor_context(env, 'rag') as sor: existing = await sor.sqlExe( - "SELECT id, color FROM tags WHERE kb_id=${kb_id}$ AND name=${name}$ AND org_id=${org_id}$", + "SELECT id, color FROM rag_tags WHERE kb_id=${kb_id}$ AND name=${name}$ AND org_id=${org_id}$", {"kb_id": kb_id, "name": name, "org_id": userorgid}) if existing: return json.dumps({"status": "SUCCEEDED", "tag_id": existing[0].id, "name": name, "color": existing[0].color, "duplicate": True}, ensure_ascii=False) tag_id = uuid.uuid4().hex await sor.sqlExe( - "INSERT INTO tags (id, kb_id, name, color, org_id, created_at) " + "INSERT INTO rag_tags (id, kb_id, name, color, org_id, created_at) " "VALUES (${id}$, ${kb_id}$, ${name}$, ${color}$, ${org_id}$, NOW())", {"id": tag_id, "kb_id": kb_id, "name": name, "color": color, "org_id": userorgid}) return json.dumps({"status": "SUCCEEDED", "tag_id": tag_id, "name": name, "color": color}) @@ -730,7 +730,7 @@ async def tag_list_handler(request, params_kw, *args, **kwargs): return json.dumps({"error": "kb_id required"}) userorgid = await env.get_userorgid() async with get_sor_context(env, 'rag') as sor: - recs = await sor.R("tags", {"kb_id": kb_id, "org_id": userorgid}) + recs = await sor.R("rag_tags", {"kb_id": kb_id, "org_id": userorgid}) tags = [{"id": r.id, "name": r.name, "color": r.color, "created_at": str(r.created_at)} for r in recs] return json.dumps({"status": "SUCCEEDED", "tags": tags}, ensure_ascii=False, default=str) except Exception as e: @@ -746,8 +746,8 @@ async def tag_delete_handler(request, params_kw, *args, **kwargs): if not tag_id: return json.dumps({"error": "tag_id required"}) async with get_sor_context(env, 'rag') as sor: - await sor.sqlExe("DELETE FROM media_tags WHERE tag_id=${id}$", {"id": tag_id}) - await sor.sqlExe("DELETE FROM tags WHERE id=${id}$", {"id": tag_id}) + await sor.sqlExe("DELETE FROM rag_media_tags WHERE tag_id=${id}$", {"id": tag_id}) + await sor.sqlExe("DELETE FROM rag_tags WHERE id=${id}$", {"id": tag_id}) return json.dumps({"status": "SUCCEEDED", "tag_id": tag_id}) except Exception as e: exception(f"tag_delete: {e}, {format_exc()}") @@ -769,7 +769,7 @@ async def tag_assign_handler(request, params_kw, *args, **kwargs): async with get_sor_context(env, 'rag') as sor: mt_id = uuid.uuid4().hex await sor.sqlExe( - "INSERT INTO media_tags (id, kb_id, media_type, media_id, tag_id, created_at) " + "INSERT INTO rag_media_tags (id, kb_id, media_type, media_id, tag_id, created_at) " "VALUES (${id}$, ${kb_id}$, ${type}$, ${mid}$, ${tid}$, NOW())", {"id": mt_id, "kb_id": kb_id, "type": media_type, "mid": media_id, "tid": tag_id}) return json.dumps({"status": "SUCCEEDED", "media_tag_id": mt_id}) @@ -789,7 +789,7 @@ async def tag_unassign_handler(request, params_kw, *args, **kwargs): return json.dumps({"error": "media_type, media_id, tag_id required"}) async with get_sor_context(env, 'rag') as sor: await sor.sqlExe( - "DELETE FROM media_tags WHERE media_type=${type}$ AND media_id=${mid}$ AND tag_id=${tid}$", + "DELETE FROM rag_media_tags WHERE media_type=${type}$ AND media_id=${mid}$ AND tag_id=${tid}$", {"type": media_type, "mid": media_id, "tid": tag_id}) return json.dumps({"status": "SUCCEEDED"}) except Exception as e: @@ -807,8 +807,8 @@ async def tag_media_tags_handler(request, params_kw, *args, **kwargs): return json.dumps({"error": "media_type and media_id required"}) async with get_sor_context(env, 'rag') as sor: recs = await sor.sqlExe( - "SELECT t.id, t.name, t.color FROM media_tags mt " - "JOIN tags t ON mt.tag_id=t.id " + "SELECT t.id, t.name, t.color FROM rag_media_tags mt " + "JOIN rag_tags t ON mt.tag_id=t.id " "WHERE mt.media_type=${type}$ AND mt.media_id=${mid}$", {"type": media_type, "mid": media_id}) tags = [{"id": r.id, "name": r.name, "color": r.color} for r in recs] @@ -835,7 +835,7 @@ async def tag_search_handler(request, params_kw, *args, **kwargs): media_ids_by_tag = [] for tid in tag_ids: recs = await sor.sqlExe( - "SELECT media_type, media_id FROM media_tags WHERE kb_id=${kb_id}$ AND tag_id=${tid}$", + "SELECT media_type, media_id FROM rag_media_tags WHERE kb_id=${kb_id}$ AND tag_id=${tid}$", {"kb_id": kb_id, "tid": tid}) mids = {(r.media_type, r.media_id) for r in recs} media_ids_by_tag.append(mids) @@ -855,19 +855,19 @@ async def tag_search_handler(request, params_kw, *args, **kwargs): results = [] if doc_ids: docs = await sor.sqlExe( - "SELECT id, file_name, file_type, file_size, status, created_at FROM documents WHERE id IN (${ids}$)", + "SELECT id, file_name, file_type, file_size, status, created_at FROM rag_documents WHERE id IN (${ids}$)", {"ids": doc_ids}) for d in docs: results.append({"type": "document", "id": d.id, "name": d.file_name, "file_type": d.file_type, "size": d.file_size, "status": d.status, "created_at": str(d.created_at)}) if face_ids: faces = await sor.sqlExe( - "SELECT id, name, description, face_embedding_id, created_at FROM entities WHERE id IN (${ids}$) AND entity_type='person'", + "SELECT id, name, description, face_embedding_id, created_at FROM rag_entities WHERE id IN (${ids}$) AND entity_type='person'", {"ids": face_ids}) for f in faces: results.append({"type": "face", "id": f.id, "name": f.name, "description": f.description, "created_at": str(f.created_at)}) if voice_ids: voices = await sor.sqlExe( - "SELECT id, name, description, voice_embedding_id, created_at FROM entities WHERE id IN (${ids}$) AND entity_type='voice'", + "SELECT id, name, description, voice_embedding_id, created_at FROM rag_entities WHERE id IN (${ids}$) AND entity_type='voice'", {"ids": voice_ids}) for v in voices: results.append({"type": "voice", "id": v.id, "name": v.name, "description": v.description, "created_at": str(v.created_at)}) @@ -877,7 +877,7 @@ async def tag_search_handler(request, params_kw, *args, **kwargs): vdb_docs = [] for doc in tag_filtered_docs: chunks = await sor.sqlExe( - "SELECT content FROM document_chunks WHERE doc_id=${id}$ LIMIT 3", + "SELECT content FROM rag_document_chunks WHERE doc_id=${id}$ LIMIT 3", {"id": doc["id"]}) for c in chunks: vdb_docs.append({"doc_id": doc["id"], "content": c.content}) @@ -911,20 +911,20 @@ async def tag_sync_handler(request, params_kw, *args, **kwargs): wanted_ids = [t.strip() for t in tag_ids_str.split(",") if t.strip()] async with get_sor_context(env, 'rag') as sor: recs = await sor.sqlExe( - "SELECT id, tag_id FROM media_tags WHERE media_type=${type}$ AND media_id=${mid}$", + "SELECT id, tag_id FROM rag_media_tags WHERE media_type=${type}$ AND media_id=${mid}$", {"type": media_type, "mid": media_id}) current = {r.tag_id: r.id for r in recs} removed = 0 for tid, mt_id in current.items(): if tid not in wanted_ids: - await sor.sqlExe("DELETE FROM media_tags WHERE id=${id}$", {"id": mt_id}) + await sor.sqlExe("DELETE FROM rag_media_tags WHERE id=${id}$", {"id": mt_id}) removed += 1 added = 0 for tid in wanted_ids: if tid not in current: mt_id = uuid.uuid4().hex await sor.sqlExe( - "INSERT INTO media_tags (id, kb_id, media_type, media_id, tag_id, created_at) " + "INSERT INTO rag_media_tags (id, kb_id, media_type, media_id, tag_id, created_at) " "VALUES (${id}$, ${kb_id}$, ${type}$, ${mid}$, ${tid}$, NOW())", {"id": mt_id, "kb_id": kb_id, "type": media_type, "mid": media_id, "tid": tid}) added += 1 diff --git a/rag/pipeline.py b/rag/pipeline.py index 48cb8e8..ecf8ae3 100644 --- a/rag/pipeline.py +++ b/rag/pipeline.py @@ -185,19 +185,19 @@ async def process_upload(env, file_data, kb_id, folder_id, file_name): from sqlor.dbpools import get_sor_context async with get_sor_context(env, 'rag') as sor: await sor.sqlExe( - "INSERT INTO documents (id, kb_id, folder_id, file_name, file_type, file_size, file_path, mime_type, status, metadata, org_id, created_at, updated_at) " + "INSERT INTO rag_documents (id, kb_id, folder_id, file_name, file_type, file_size, file_path, mime_type, status, metadata, org_id, created_at, updated_at) " "VALUES (" + "${id}$, ${kb_id}$, ${folder_id}$, ${file_name}$, 'other', ${file_size}$, ${file_path}$, 'application/octet-stream', ${status}$, ${meta}$, ${org_id}$, NOW(), NOW())", {"id": doc_id, "kb_id": kb_id, "folder_id": folder_id, "file_name": file_name, "file_size": file_size, "file_path": "/idfile/files/" + saved_name, "status": status, "meta": meta, "org_id": userorgid}) await sor.sqlExe( - "UPDATE knowledge_bases SET doc_count=doc_count+1, total_size=total_size+" + "${size}$ WHERE id=${kb_id}$", + "UPDATE rag_knowledge_bases SET doc_count=doc_count+1, total_size=total_size+" + "${size}$ WHERE id=${kb_id}$", {"size": file_size, "kb_id": kb_id}) if ingest_result and status == 'done': chunks_n = ingest_result.get('chunks', 0) if isinstance(ingest_result, dict) else 0 if chunks_n: - await sor.sqlExe("UPDATE documents SET chunk_count=${chunks}$ WHERE id=${id}$", {"chunks": chunks_n, "id": doc_id}) - await sor.sqlExe("UPDATE knowledge_bases SET chunk_count=chunk_count+${n}$ WHERE id=${kb_id}$", {"n": chunks_n, "kb_id": kb_id}) + await sor.sqlExe("UPDATE rag_documents SET chunk_count=${chunks}$ WHERE id=${id}$", {"chunks": chunks_n, "id": doc_id}) + await sor.sqlExe("UPDATE rag_knowledge_bases SET chunk_count=chunk_count+${n}$ WHERE id=${kb_id}$", {"n": chunks_n, "kb_id": kb_id}) faces_n = face_result.get('faces', 0) if face_result and isinstance(face_result, dict) else 0 speakers_n = voice_result.get('speakers', 0) if voice_result and isinstance(voice_result, dict) else 0 diff --git a/wwwroot/knowledge_bases_list/add_tag.dspy b/wwwroot/knowledge_bases_list/add_tag.dspy index 318a100..7507beb 100644 --- a/wwwroot/knowledge_bases_list/add_tag.dspy +++ b/wwwroot/knowledge_bases_list/add_tag.dspy @@ -9,7 +9,7 @@ if not doc_id or not tag: async with get_sor_context(env, 'rag') as sor: # Get existing tags - recs = await sor.sqlExe("SELECT metadata FROM documents WHERE id=${id}$ AND kb_id=${kb_id}$", + recs = await sor.sqlExe("SELECT metadata FROM rag_documents WHERE id=${id}$ AND kb_id=${kb_id}$", {"id": doc_id, "kb_id": kb_id}) if not recs: return json.dumps({"status": "error", "error": "document not found"}, ensure_ascii=False) @@ -29,7 +29,7 @@ async with get_sor_context(env, 'rag') as sor: tags.append(tag) meta['tags'] = tags await sor.sqlExe( - "UPDATE documents SET metadata=${meta}$ WHERE id=${id}$", + "UPDATE rag_documents SET metadata=${meta}$ WHERE id=${id}$", {"meta": _json.dumps(meta, ensure_ascii=False), "id": doc_id}) return _json.dumps({"status": "SUCCEEDED", "tags": tags, "added": tag}, ensure_ascii=False) else: diff --git a/wwwroot/knowledge_bases_list/analysis.dspy b/wwwroot/knowledge_bases_list/analysis.dspy index 68cc833..ab08544 100644 --- a/wwwroot/knowledge_bases_list/analysis.dspy +++ b/wwwroot/knowledge_bases_list/analysis.dspy @@ -6,7 +6,7 @@ rows = [] async with get_sor_context(env, 'rag') as sor: recs = await sor.sqlExe( "SELECT id, file_name, file_size, file_type, file_path, metadata, status, chunk_count " - "FROM documents WHERE kb_id=${kb_id}$ AND metadata IS NOT NULL AND metadata != '' " + "FROM rag_documents WHERE kb_id=${kb_id}$ AND metadata IS NOT NULL AND metadata != '' " "ORDER BY created_at DESC LIMIT 50", {"kb_id": kb_id}) rows = [dict(r) for r in recs] diff --git a/wwwroot/knowledge_bases_list/batch_ingest.dspy b/wwwroot/knowledge_bases_list/batch_ingest.dspy index 9602c63..6376483 100644 --- a/wwwroot/knowledge_bases_list/batch_ingest.dspy +++ b/wwwroot/knowledge_bases_list/batch_ingest.dspy @@ -6,10 +6,10 @@ env = request._run_ns rows = [] async with get_sor_context(env, 'rag') as sor: recs = await sor.sqlExe( - "SELECT id, file_name, file_size, file_path FROM documents WHERE kb_id=${kb_id}$ AND (chunk_count IS NULL OR chunk_count=0 OR status='pending') LIMIT 20", + "SELECT id, file_name, file_size, file_path FROM rag_documents WHERE kb_id=${kb_id}$ AND (chunk_count IS NULL OR chunk_count=0 OR status='pending') LIMIT 20", {"kb_id": kb_id}) rows = [dict(r) for r in recs] - krecs = await sor.sqlExe("SELECT embedding_engine FROM knowledge_bases WHERE id=${kb_id}$", {"kb_id": kb_id}) + krecs = await sor.sqlExe("SELECT embedding_engine FROM rag_knowledge_bases WHERE id=${kb_id}$", {"kb_id": kb_id}) emb_engine = (getattr(krecs[0], 'embedding_engine', '') or 'clip-vith14').strip() if krecs else 'clip-vith14' if emb_engine == 'bge-m3': emb_url = 'https://embedding.opencomputing.net:10443/txte/api/embed' @@ -156,7 +156,7 @@ async with aiohttp.ClientSession(timeout=aiohttp.ClientTimeout(total=60)) as ses meta = j2.dumps({"face": face_count, "speakers": speakers}, ensure_ascii=False) async with get_sor_context(env, 'rag') as sor: await sor.sqlExe( - "UPDATE documents SET status='done',chunk_count=0,metadata=${meta}$ WHERE id=${id}$", + "UPDATE rag_documents SET status='done',chunk_count=0,metadata=${meta}$ WHERE id=${id}$", {"meta": meta, "id": fid}) results.append(f"🎬 {fn}: faces={face_count} speakers={speakers}") continue @@ -201,10 +201,10 @@ async with aiohttp.ClientSession(timeout=aiohttp.ClientTimeout(total=60)) as ses async with get_sor_context(env, 'rag') as sor: for i, ct in enumerate(chunks): await sor.sqlExe( - "INSERT INTO document_chunks (id,doc_id,kb_id,chunk_index,content,vector_id,created_at) VALUES (${id}$,${doc_id}$,${kb_id}$,${idx}$,${content}$,${vid}$,NOW())", + "INSERT INTO rag_document_chunks (id,doc_id,kb_id,chunk_index,content,vector_id,created_at) VALUES (${id}$,${doc_id}$,${kb_id}$,${idx}$,${content}$,${vid}$,NOW())", {"id": f"{fid}_c{i}", "doc_id": fid, "kb_id": kb_id, "idx": i, "content": ct[:2000], "vid": ""}) - await sor.sqlExe("UPDATE documents SET status='done',chunk_count=${n}$ WHERE id=${id}$", {"n": len(chunks), "id": fid}) - await sor.sqlExe("UPDATE knowledge_bases SET chunk_count=chunk_count+${n}$ WHERE id=${kb_id}$", {"n": len(chunks), "kb_id": kb_id}) + await sor.sqlExe("UPDATE rag_documents SET status='done',chunk_count=${n}$ WHERE id=${id}$", {"n": len(chunks), "id": fid}) + await sor.sqlExe("UPDATE rag_knowledge_bases SET chunk_count=chunk_count+${n}$ WHERE id=${kb_id}$", {"n": len(chunks), "kb_id": kb_id}) results.append(f"✓ {fn}: {len(text)} chars → {len(chunks)} chunks") diff --git a/wwwroot/knowledge_bases_list/create_kb.dspy b/wwwroot/knowledge_bases_list/create_kb.dspy index 448545a..ebf4ca9 100644 --- a/wwwroot/knowledge_bases_list/create_kb.dspy +++ b/wwwroot/knowledge_bases_list/create_kb.dspy @@ -12,7 +12,7 @@ async with db.sqlorContext(dbname) as sor: if emb_type not in ("bge-m3", "clip-vith14"): emb_type = "clip-vith14" await sor.sqlExe( - "INSERT INTO knowledge_bases (id, name, description, org_id, embedding_engine, vdb_collection, doc_count, total_size, chunk_count, status, created_at) " + "INSERT INTO rag_knowledge_bases (id, name, description, org_id, embedding_engine, vdb_collection, doc_count, total_size, chunk_count, status, created_at) " "VALUES (${id}$, ${name}$, ${desc}$, ${org_id}$, ${emb}$, 'rag_collection', 0, 0, 0, 'active', NOW())", {"id": kb_id, "name": name, "desc": desc, "org_id": userorgid, "emb": emb_type}) return { diff --git a/wwwroot/knowledge_bases_list/delete_file.dspy b/wwwroot/knowledge_bases_list/delete_file.dspy index b08df22..7f23db9 100644 --- a/wwwroot/knowledge_bases_list/delete_file.dspy +++ b/wwwroot/knowledge_bases_list/delete_file.dspy @@ -12,7 +12,7 @@ userorgid = await env.get_userorgid() async with get_sor_context(env, 'rag') as sor: recs = await sor.sqlExe( - "SELECT file_path, file_size, kb_id FROM documents WHERE id=${id}$", + "SELECT file_path, file_size, kb_id FROM rag_documents WHERE id=${id}$", {"id": doc_id}) if not recs: return json.dumps({"status": "error", "error": "file not found"}, ensure_ascii=False) @@ -21,12 +21,12 @@ async with get_sor_context(env, 'rag') as sor: file_size = doc.file_size # Delete document + chunks - await sor.sqlExe("DELETE FROM document_chunks WHERE doc_id=${doc_id}$", {"doc_id": doc_id}) - await sor.sqlExe("DELETE FROM documents WHERE id=${id}$", {"id": doc_id}) + await sor.sqlExe("DELETE FROM rag_document_chunks WHERE doc_id=${doc_id}$", {"doc_id": doc_id}) + await sor.sqlExe("DELETE FROM rag_documents WHERE id=${id}$", {"id": doc_id}) # Update KB stats await sor.sqlExe( - "UPDATE knowledge_bases SET doc_count=GREATEST(doc_count-1, 0), total_size=GREATEST(total_size-${size}$, 0) WHERE id=${kb_id}$", + "UPDATE rag_knowledge_bases SET doc_count=GREATEST(doc_count-1, 0), total_size=GREATEST(total_size-${size}$, 0) WHERE id=${kb_id}$", {"size": file_size, "kb_id": kb_id}) # Delete physical file via env.realpath @@ -45,11 +45,11 @@ rows = [] async with db.sqlorContext(dbname) as sor: if folder_id and folder_id != '__root__': recs = await sor.sqlExe( - "SELECT id, file_name, file_type, file_size, file_path, status, chunk_count, created_at FROM documents WHERE kb_id=${kb_id}$ AND folder_id=${folder_id}$ ORDER BY created_at DESC", + "SELECT id, file_name, file_type, file_size, file_path, status, chunk_count, created_at FROM rag_documents WHERE kb_id=${kb_id}$ AND folder_id=${folder_id}$ ORDER BY created_at DESC", {"kb_id": kb_id, "folder_id": folder_id}) else: recs = await sor.sqlExe( - "SELECT id, file_name, file_type, file_size, file_path, status, chunk_count, created_at FROM documents WHERE kb_id=${kb_id}$ AND (folder_id IS NULL OR folder_id='') ORDER BY created_at DESC", + "SELECT id, file_name, file_type, file_size, file_path, status, chunk_count, created_at FROM rag_documents WHERE kb_id=${kb_id}$ AND (folder_id IS NULL OR folder_id='') ORDER BY created_at DESC", {"kb_id": kb_id}) for r in recs: rows.append({ diff --git a/wwwroot/knowledge_bases_list/delete_kb.dspy b/wwwroot/knowledge_bases_list/delete_kb.dspy index 07ddfcc..d1a5c3e 100644 --- a/wwwroot/knowledge_bases_list/delete_kb.dspy +++ b/wwwroot/knowledge_bases_list/delete_kb.dspy @@ -14,21 +14,21 @@ deleted = {"documents": 0, "chunks": 0, "tags": 0, "media_tags": 0, async with get_sor_context(env, 'rag') as sor: kb = await sor.sqlExe( - "SELECT id, name FROM knowledge_bases WHERE id=${id}$ AND (org_id=${org_id}$ OR org_id IS NULL)", + "SELECT id, name FROM rag_knowledge_bases WHERE id=${id}$ AND (org_id=${org_id}$ OR org_id IS NULL)", {"id": kb_id, "org_id": userorgid}) if not kb: return {"widgettype": "urlwidget", "options": {"url": entire_url('/rag/knowledge_bases_list/index.ui')}} docs = await sor.sqlExe( - "SELECT id, file_path FROM documents WHERE kb_id=${kb_id}$", + "SELECT id, file_path FROM rag_documents WHERE kb_id=${kb_id}$", {"kb_id": kb_id}) doc_ids = [d.id for d in docs] # 1. VDB cleanup if doc_ids: chunks = await sor.sqlExe( - "SELECT vector_id FROM document_chunks WHERE doc_id IN (${ids}$) AND vector_id IS NOT NULL AND vector_id != ''", + "SELECT vector_id FROM rag_document_chunks WHERE doc_id IN (${ids}$) AND vector_id IS NOT NULL AND vector_id != ''", {"ids": doc_ids}) vector_ids = [c.vector_id for c in chunks] if vector_ids: @@ -53,40 +53,40 @@ async with get_sor_context(env, 'rag') as sor: # 3. Entities & relations er = await sor.sqlExe( - "SELECT COUNT(*) AS cnt FROM entity_relations WHERE kb_id=${kb_id}$", + "SELECT COUNT(*) AS cnt FROM rag_entity_relations WHERE kb_id=${kb_id}$", {"kb_id": kb_id}) - await sor.sqlExe("DELETE FROM entity_relations WHERE kb_id=${kb_id}$", {"kb_id": kb_id}) + await sor.sqlExe("DELETE FROM rag_entity_relations WHERE kb_id=${kb_id}$", {"kb_id": kb_id}) deleted["entities"] += er[0].cnt if er else 0 ent = await sor.sqlExe( - "SELECT COUNT(*) AS cnt FROM entities WHERE kb_id=${kb_id}$", + "SELECT COUNT(*) AS cnt FROM rag_entities WHERE kb_id=${kb_id}$", {"kb_id": kb_id}) - await sor.sqlExe("DELETE FROM entities WHERE kb_id=${kb_id}$", {"kb_id": kb_id}) + await sor.sqlExe("DELETE FROM rag_entities WHERE kb_id=${kb_id}$", {"kb_id": kb_id}) deleted["entities"] += ent[0].cnt if ent else 0 # 4. Chunks ch = await sor.sqlExe( - "SELECT COUNT(*) AS cnt FROM document_chunks WHERE doc_id IN (${ids}$)", + "SELECT COUNT(*) AS cnt FROM rag_document_chunks WHERE doc_id IN (${ids}$)", {"ids": doc_ids}) - await sor.sqlExe("DELETE FROM document_chunks WHERE doc_id IN (${ids}$)", {"ids": doc_ids}) + await sor.sqlExe("DELETE FROM rag_document_chunks WHERE doc_id IN (${ids}$)", {"ids": doc_ids}) deleted["chunks"] = ch[0].cnt if ch else 0 # Media tags - mt = await sor.sqlExe("SELECT COUNT(*) AS cnt FROM media_tags WHERE kb_id=${kb_id}$", {"kb_id": kb_id}) - await sor.sqlExe("DELETE FROM media_tags WHERE kb_id=${kb_id}$", {"kb_id": kb_id}) + mt = await sor.sqlExe("SELECT COUNT(*) AS cnt FROM rag_media_tags WHERE kb_id=${kb_id}$", {"kb_id": kb_id}) + await sor.sqlExe("DELETE FROM rag_media_tags WHERE kb_id=${kb_id}$", {"kb_id": kb_id}) deleted["media_tags"] = mt[0].cnt if mt else 0 # Tags - t = await sor.sqlExe("SELECT COUNT(*) AS cnt FROM tags WHERE kb_id=${kb_id}$", {"kb_id": kb_id}) - await sor.sqlExe("DELETE FROM tags WHERE kb_id=${kb_id}$", {"kb_id": kb_id}) + t = await sor.sqlExe("SELECT COUNT(*) AS cnt FROM rag_tags WHERE kb_id=${kb_id}$", {"kb_id": kb_id}) + await sor.sqlExe("DELETE FROM rag_tags WHERE kb_id=${kb_id}$", {"kb_id": kb_id}) deleted["tags"] = t[0].cnt if t else 0 # Documents - await sor.sqlExe("DELETE FROM documents WHERE kb_id=${kb_id}$", {"kb_id": kb_id}) + await sor.sqlExe("DELETE FROM rag_documents WHERE kb_id=${kb_id}$", {"kb_id": kb_id}) deleted["documents"] = len(doc_ids) # KB itself - await sor.sqlExe("DELETE FROM knowledge_bases WHERE id=${id}$", {"id": kb_id}) + await sor.sqlExe("DELETE FROM rag_knowledge_bases WHERE id=${id}$", {"id": kb_id}) # Physical files — paths are like /44/126/174/61/file.mp4 for d in docs: diff --git a/wwwroot/knowledge_bases_list/delete_tree_item.dspy b/wwwroot/knowledge_bases_list/delete_tree_item.dspy index 6319d8f..42ce8b8 100644 --- a/wwwroot/knowledge_bases_list/delete_tree_item.dspy +++ b/wwwroot/knowledge_bases_list/delete_tree_item.dspy @@ -2,6 +2,6 @@ ns = params_kw.copy() db = DBPools() dbname = get_module_dbname('rag') async with db.sqlorContext(dbname) as sor: - await sor.sqlExe("DELETE FROM document_chunks WHERE id=${id}$", {"id": ns.get("id","")}) + await sor.sqlExe("DELETE FROM rag_document_chunks WHERE id=${id}$", {"id": ns.get("id","")}) return {"widgettype": "Message", "options": {"user_data": {"id": ns.get("id","")}}} return {"error": "failed"} diff --git a/wwwroot/knowledge_bases_list/file_list.dspy b/wwwroot/knowledge_bases_list/file_list.dspy index 71a2970..574affc 100644 --- a/wwwroot/knowledge_bases_list/file_list.dspy +++ b/wwwroot/knowledge_bases_list/file_list.dspy @@ -14,11 +14,11 @@ try: async with db.sqlorContext(dbname) as sor: if folder_id: recs = await sor.sqlExe( - "SELECT id, file_name, file_type, file_size, file_path, status, chunk_count, created_at FROM documents WHERE kb_id=${kb_id}$ AND folder_id=${folder_id}$ ORDER BY created_at DESC", + "SELECT id, file_name, file_type, file_size, file_path, status, chunk_count, created_at FROM rag_documents WHERE kb_id=${kb_id}$ AND folder_id=${folder_id}$ ORDER BY created_at DESC", {"kb_id": kb_id, "folder_id": folder_id}) else: recs = await sor.sqlExe( - "SELECT id, file_name, file_type, file_size, file_path, status, chunk_count, created_at FROM documents WHERE kb_id=${kb_id}$ AND (folder_id IS NULL OR folder_id IN ('','__root__')) ORDER BY created_at DESC", + "SELECT id, file_name, file_type, file_size, file_path, status, chunk_count, created_at FROM rag_documents WHERE kb_id=${kb_id}$ AND (folder_id IS NULL OR folder_id IN ('','__root__')) ORDER BY created_at DESC", {"kb_id": kb_id}) for r in recs: rows.append({ diff --git a/wwwroot/knowledge_bases_list/get_tree_data.dspy b/wwwroot/knowledge_bases_list/get_tree_data.dspy index 95d027e..f335d70 100644 --- a/wwwroot/knowledge_bases_list/get_tree_data.dspy +++ b/wwwroot/knowledge_bases_list/get_tree_data.dspy @@ -7,7 +7,7 @@ try: async with db.sqlorContext(dbname) as sor: if not id: recs = await sor.sqlExe( - "SELECT id, '' as parentid, content as label FROM document_chunks WHERE kb_id=${kb_id}$ AND chunk_type='directory' AND (description IS NULL OR description='') ORDER BY content", + "SELECT id, '' as parentid, content as label FROM rag_document_chunks WHERE kb_id=${kb_id}$ AND chunk_type='directory' AND (description IS NULL OR description='') ORDER BY content", {"kb_id": kb_id}) result = [{"id": "__root__", "parentid": "", "label": "📁 根目录"}] for r in recs: @@ -15,7 +15,7 @@ try: return result else: recs = await sor.sqlExe( - "SELECT id, description as parentid, content as label FROM document_chunks WHERE kb_id=${kb_id}$ AND chunk_type='directory' AND description=${id}$ ORDER BY content", + "SELECT id, description as parentid, content as label FROM rag_document_chunks WHERE kb_id=${kb_id}$ AND chunk_type='directory' AND description=${id}$ ORDER BY content", {"kb_id": kb_id, "id": id}) return [dict(r) for r in recs] except: diff --git a/wwwroot/knowledge_bases_list/kb_list.dspy b/wwwroot/knowledge_bases_list/kb_list.dspy index 1ad0f67..e2f1b87 100644 --- a/wwwroot/knowledge_bases_list/kb_list.dspy +++ b/wwwroot/knowledge_bases_list/kb_list.dspy @@ -3,7 +3,7 @@ userorgid = await env.get_userorgid() cards = [] async with get_sor_context(env, 'rag') as sor: recs = await sor.sqlExe( - "SELECT * FROM knowledge_bases WHERE org_id=${org_id}$ ORDER BY created_at DESC", + "SELECT * FROM rag_knowledge_bases WHERE org_id=${org_id}$ ORDER BY created_at DESC", {"org_id": userorgid}) for r in recs: doc_count = r.doc_count or 0 diff --git a/wwwroot/knowledge_bases_list/kb_options.dspy b/wwwroot/knowledge_bases_list/kb_options.dspy index dff45b7..a1b01c6 100644 --- a/wwwroot/knowledge_bases_list/kb_options.dspy +++ b/wwwroot/knowledge_bases_list/kb_options.dspy @@ -3,7 +3,7 @@ env = request._run_ns userorgid = await env.get_userorgid() async with get_sor_context(env, 'rag') as sor: recs = await sor.sqlExe( - "SELECT id, name FROM knowledge_bases WHERE org_id=${org_id}$ ORDER BY created_at DESC", + "SELECT id, name FROM rag_knowledge_bases WHERE org_id=${org_id}$ ORDER BY created_at DESC", {"org_id": userorgid}) items = [{"value": r.id, "text": r.name} for r in recs] return json.dumps(items, ensure_ascii=False) diff --git a/wwwroot/knowledge_bases_list/media_cards.dspy b/wwwroot/knowledge_bases_list/media_cards.dspy index 51f3989..780de30 100644 --- a/wwwroot/knowledge_bases_list/media_cards.dspy +++ b/wwwroot/knowledge_bases_list/media_cards.dspy @@ -12,14 +12,14 @@ try: async with db.sqlorContext(dbname) as sor: if kind == 'voice': recs = await sor.sqlExe( - "SELECT id, file_name, file_path, metadata, created_at FROM documents " + "SELECT id, file_name, file_path, metadata, created_at FROM rag_documents " "WHERE kb_id=${kb_id}$ AND (metadata LIKE '%%voiceprint_status%%done%%' OR LOWER(file_name) LIKE '%%.mp3' OR LOWER(file_name) LIKE '%%.wav' " "OR LOWER(file_name) LIKE '%%.m4a' OR LOWER(file_name) LIKE '%%.aac' OR LOWER(file_name) LIKE '%%.ogg' " "OR LOWER(file_name) LIKE '%%.flac') ORDER BY created_at DESC", {"kb_id": kb_id}) else: recs = await sor.sqlExe( - "SELECT id, file_name, file_path, metadata, created_at FROM documents " + "SELECT id, file_name, file_path, metadata, created_at FROM rag_documents " "WHERE kb_id=${kb_id}$ AND (LOWER(file_name) LIKE '%%.jpg' OR LOWER(file_name) LIKE '%%.jpeg' " "OR LOWER(file_name) LIKE '%%.png' OR LOWER(file_name) LIKE '%%.gif' OR LOWER(file_name) LIKE '%%.webp' " "OR LOWER(file_name) LIKE '%%.bmp') ORDER BY created_at DESC", @@ -41,8 +41,8 @@ try: try: id_list = ','.join(["'" + str(x) + "'" for x in doc_ids]) mt_recs = await sor.sqlExe( - "SELECT mt.media_id, t.name, t.color FROM media_tags mt " + - "JOIN tags t ON mt.tag_id=t.id " + + "SELECT mt.media_id, t.name, t.color FROM rag_media_tags mt " + + "JOIN rag_tags t ON mt.tag_id=t.id " + "WHERE mt.media_type='document' AND mt.media_id IN (" + id_list + ")", ns={}) for mt in mt_recs: diff --git a/wwwroot/knowledge_bases_list/new_tree_item.dspy b/wwwroot/knowledge_bases_list/new_tree_item.dspy index e97627a..06e2ffe 100644 --- a/wwwroot/knowledge_bases_list/new_tree_item.dspy +++ b/wwwroot/knowledge_bases_list/new_tree_item.dspy @@ -7,7 +7,7 @@ async with db.sqlorContext(dbname) as sor: parent_id = ns.get("parentid", "") kb_id = ns.get("kb_id", "") await sor.sqlExe( - "INSERT INTO document_chunks (id, doc_id, kb_id, chunk_index, chunk_type, content, description, created_at) " + "INSERT INTO rag_document_chunks (id, doc_id, kb_id, chunk_index, chunk_type, content, description, created_at) " "VALUES (${id}$, '', ${kb_id}$, 0, 'directory', ${name}$, ${parentid}$, NOW())", {"id": dir_id, "kb_id": kb_id, "name": name, "parentid": parent_id}) return {"widgettype": "Message", "options": {"user_data": {"id": dir_id, "label": name, "parentid": parent_id}}} diff --git a/wwwroot/knowledge_bases_list/rename_kb.dspy b/wwwroot/knowledge_bases_list/rename_kb.dspy index 700f58c..6276265 100644 --- a/wwwroot/knowledge_bases_list/rename_kb.dspy +++ b/wwwroot/knowledge_bases_list/rename_kb.dspy @@ -11,7 +11,7 @@ userorgid = await env.get_userorgid() async with get_sor_context(env, 'rag') as sor: await sor.sqlExe( - "UPDATE knowledge_bases SET name=${name}$, description=${desc}$, updated_at=NOW() " + "UPDATE rag_knowledge_bases SET name=${name}$, description=${desc}$, updated_at=NOW() " "WHERE id=${id}$ AND (org_id=${org_id}$ OR org_id IS NULL)", {"name": name, "desc": desc, "id": kb_id, "org_id": userorgid}) diff --git a/wwwroot/knowledge_bases_list/rename_kb_form.dspy b/wwwroot/knowledge_bases_list/rename_kb_form.dspy index 50e1a3f..ab51539 100644 --- a/wwwroot/knowledge_bases_list/rename_kb_form.dspy +++ b/wwwroot/knowledge_bases_list/rename_kb_form.dspy @@ -11,7 +11,7 @@ env = request._run_ns if not name: async with get_sor_context(env, 'rag') as sor: recs = await sor.sqlExe( - "SELECT name, description FROM knowledge_bases WHERE id=${id}$", + "SELECT name, description FROM rag_knowledge_bases WHERE id=${id}$", {"id": kb_id}) if recs: name = recs[0].name or '' diff --git a/wwwroot/knowledge_bases_list/save_tags.dspy b/wwwroot/knowledge_bases_list/save_tags.dspy index bc62b39..b0dad12 100644 --- a/wwwroot/knowledge_bases_list/save_tags.dspy +++ b/wwwroot/knowledge_bases_list/save_tags.dspy @@ -22,18 +22,18 @@ else: added = 0; removed = 0 async with get_sor_context(env, 'rag') as sor: recs = await sor.sqlExe( - "SELECT id, tag_id FROM media_tags WHERE media_type='document' AND media_id=${mid}$", + "SELECT id, tag_id FROM rag_media_tags WHERE media_type='document' AND media_id=${mid}$", {"mid": doc_id}) current = {r.tag_id: r.id for r in recs} for tid, mt_id in list(current.items()): if tid not in wanted_ids: - await sor.sqlExe("DELETE FROM media_tags WHERE id=${id}$", {"id": mt_id}) + await sor.sqlExe("DELETE FROM rag_media_tags WHERE id=${id}$", {"id": mt_id}) removed += 1 for tid in wanted_ids: if tid not in current: mt_id = uuid() await sor.sqlExe( - "INSERT INTO media_tags (id, kb_id, media_type, media_id, tag_id, created_at) " + "INSERT INTO rag_media_tags (id, kb_id, media_type, media_id, tag_id, created_at) " "VALUES (${id}$, ${kb_id}$, 'document', ${mid}$, ${tid}$, NOW())", {"id": mt_id, "kb_id": kb_id, "mid": doc_id, "tid": tid}) added += 1 diff --git a/wwwroot/knowledge_bases_list/search_result.dspy b/wwwroot/knowledge_bases_list/search_result.dspy index f82a1a4..7f5262f 100644 --- a/wwwroot/knowledge_bases_list/search_result.dspy +++ b/wwwroot/knowledge_bases_list/search_result.dspy @@ -51,7 +51,7 @@ if tag_ids_str: for i, tid in enumerate(wanted_tags): placeholders.append("${tid_" + str(i) + "}$") nsq["tid_" + str(i)] = tid - sql = ("SELECT media_id FROM media_tags " + sql = ("SELECT media_id FROM rag_media_tags " "WHERE " + kb_filter + "media_type='document' AND tag_id IN (" + ",".join(placeholders) + ") " "GROUP BY media_id HAVING COUNT(DISTINCT tag_id)=" + str(len(wanted_tags))) recs = await sor.sqlExe(sql, nsq) @@ -79,7 +79,7 @@ kw_rows = [] emb_engine = 'clip-vith14' try: async with get_sor_context(env, 'rag') as sor: - krecs = await sor.sqlExe("SELECT embedding_engine FROM knowledge_bases WHERE id=${kb_id}$", {"kb_id": kb_id}) + krecs = await sor.sqlExe("SELECT embedding_engine FROM rag_knowledge_bases WHERE id=${kb_id}$", {"kb_id": kb_id}) if krecs: emb_engine = (getattr(krecs[0], 'embedding_engine', '') or 'clip-vith14').strip() except: pass @@ -126,7 +126,7 @@ if query: for i, t in enumerate(tokens): conds.append("content LIKE ${kw_" + str(i) + "}$") nsq["kw_" + str(i)] = "%" + t + "%" - ksql = "SELECT id, doc_id, content FROM document_chunks WHERE kb_id=${kb_id}$ AND (" + " OR ".join(conds) + ") LIMIT 20" + ksql = "SELECT id, doc_id, content FROM rag_document_chunks WHERE kb_id=${kb_id}$ AND (" + " OR ".join(conds) + ") LIMIT 20" async with get_sor_context(env, 'rag') as sor: krecs = await sor.sqlExe(ksql, nsq) for r in krecs: @@ -145,7 +145,7 @@ if query: chunk_text = '' async with get_sor_context(env, 'rag') as sor: recs = await sor.sqlExe( - "SELECT content, doc_id, metadata FROM document_chunks WHERE id=${id}$", + "SELECT content, doc_id, metadata FROM rag_document_chunks WHERE id=${id}$", {"id": rid}) chunk_meta = {} if recs: @@ -190,7 +190,7 @@ else: placeholders2.append("${did_" + str(i) + "}$") nsq2["did_" + str(i)] = did # Get documents (exclude face/voice generated derivatives) - sql2 = ("SELECT id, file_name, kb_id, file_path FROM documents WHERE " + kb_cond + + sql2 = ("SELECT id, file_name, kb_id, file_path FROM rag_documents WHERE " + kb_cond + "id IN (" + ",".join(placeholders2) + ") AND (metadata IS NULL OR metadata NOT LIKE '%%\"face\"%%') ORDER BY created_at DESC LIMIT " + str(top_k * 2)) docs = await sor.sqlExe(sql2, nsq2) doc_list = [(r.id, r.file_name or '', r.kb_id or '', r.file_path or '') for r in docs] @@ -203,7 +203,7 @@ else: for i, did in enumerate(doc_ids): chunk_placeholders.append("${cdid_" + str(i) + "}$") nsq_c["cdid_" + str(i)] = did - chunks_sql = ("SELECT id, doc_id, content FROM document_chunks WHERE doc_id IN (" + + chunks_sql = ("SELECT id, doc_id, content FROM rag_document_chunks WHERE doc_id IN (" + ",".join(chunk_placeholders) + ") ORDER BY created_at DESC LIMIT " + str(top_k)) chunk_recs = await sor.sqlExe(chunks_sql, nsq_c) chunks_by_doc = {} diff --git a/wwwroot/knowledge_bases_list/storage_card.dspy b/wwwroot/knowledge_bases_list/storage_card.dspy index f409fe9..a8bf1b6 100644 --- a/wwwroot/knowledge_bases_list/storage_card.dspy +++ b/wwwroot/knowledge_bases_list/storage_card.dspy @@ -2,7 +2,7 @@ ns = params_kw.copy() env = request._run_ns async with get_sor_context(env, 'rag') as sor: - rec = await sor.sqlExe("SELECT COALESCE(SUM(total_size),0) used FROM knowledge_bases", {}) + rec = await sor.sqlExe("SELECT COALESCE(SUM(total_size),0) used FROM rag_knowledge_bases", {}) used_bytes = int(rec[0].used) if rec else 0 used_mb = round(used_bytes / 1048576, 1) diff --git a/wwwroot/knowledge_bases_list/storage_stats.dspy b/wwwroot/knowledge_bases_list/storage_stats.dspy index 7721eb2..d5015e0 100644 --- a/wwwroot/knowledge_bases_list/storage_stats.dspy +++ b/wwwroot/knowledge_bases_list/storage_stats.dspy @@ -9,13 +9,13 @@ docs = 0 async with get_sor_context(env, 'rag') as sor: rec = await sor.sqlExe( "SELECT COALESCE(SUM(d.file_size),0) AS used, COUNT(*) AS docs " - "FROM documents d JOIN knowledge_bases k ON d.kb_id=k.id " + "FROM rag_documents d JOIN rag_knowledge_bases k ON d.kb_id=k.id " "WHERE k.org_id=${org_id}$", {"org_id": userorgid}) if rec: used = int(rec[0].used) docs = int(rec[0].docs) - lim = await sor.sqlExe("SELECT limit_bytes FROM org_storage_limits WHERE org_id=${org_id}$", {"org_id": userorgid}) + lim = await sor.sqlExe("SELECT limit_bytes FROM rag_org_storage_limits WHERE org_id=${org_id}$", {"org_id": userorgid}) if lim: limit = int(lim[0].limit_bytes) diff --git a/wwwroot/knowledge_bases_list/tag_form.dspy b/wwwroot/knowledge_bases_list/tag_form.dspy index 99a4272..a9e74c0 100644 --- a/wwwroot/knowledge_bases_list/tag_form.dspy +++ b/wwwroot/knowledge_bases_list/tag_form.dspy @@ -17,14 +17,14 @@ all_tags = [] # all tags for kb: list of (id, name, color) try: async with db.sqlorContext(dbname) as sor: recs = await sor.sqlExe( - "SELECT file_name FROM documents WHERE id=${id}$ AND kb_id=${kb_id}$", + "SELECT file_name FROM rag_documents WHERE id=${id}$ AND kb_id=${kb_id}$", {"id": doc_id, "kb_id": kb_id}) if recs: fname = recs[0].file_name or '' # 有哪些标签 tag_recs = await sor.sqlExe( - "SELECT id, name, color FROM tags WHERE kb_id=${kb_id}$ ORDER BY created_at", + "SELECT id, name, color FROM rag_tags WHERE kb_id=${kb_id}$ ORDER BY created_at", {"kb_id": kb_id}) for t in tag_recs: all_tags.append((t.id, t.name, t.color or '#3b82f6')) @@ -32,7 +32,7 @@ try: # 当前已关联的 mt_recs = await sor.sqlExe( - "SELECT tag_id FROM media_tags WHERE media_type=${type}$ AND media_id=${mid}$", + "SELECT tag_id FROM rag_media_tags WHERE media_type=${type}$ AND media_id=${mid}$", {"type": media_type, "mid": doc_id}) cur_tag_ids = [r.tag_id for r in mt_recs] except Exception: diff --git a/wwwroot/knowledge_bases_list/tag_options.dspy b/wwwroot/knowledge_bases_list/tag_options.dspy index a99e46f..27056fc 100644 --- a/wwwroot/knowledge_bases_list/tag_options.dspy +++ b/wwwroot/knowledge_bases_list/tag_options.dspy @@ -3,7 +3,7 @@ env = request._run_ns userorgid = await env.get_userorgid() async with get_sor_context(env, 'rag') as sor: recs = await sor.sqlExe( - "SELECT id, name, color FROM tags WHERE org_id=${org_id}$ ORDER BY created_at", + "SELECT id, name, color FROM rag_tags WHERE org_id=${org_id}$ ORDER BY created_at", {"org_id": userorgid}) items = [{"value": r.id, "text": r.name, "color": r.color or '#3b82f6'} for r in recs] return json.dumps(items, ensure_ascii=False) diff --git a/wwwroot/knowledge_bases_list/update_tree_item.dspy b/wwwroot/knowledge_bases_list/update_tree_item.dspy index 7eff651..f05a928 100644 --- a/wwwroot/knowledge_bases_list/update_tree_item.dspy +++ b/wwwroot/knowledge_bases_list/update_tree_item.dspy @@ -4,7 +4,7 @@ dbname = get_module_dbname('rag') async with db.sqlorContext(dbname) as sor: id = ns.get("id", "") name = ns.get("name", "") - await sor.sqlExe("UPDATE document_chunks SET content=${name}$ WHERE id=${id}$", + await sor.sqlExe("UPDATE rag_document_chunks SET content=${name}$ WHERE id=${id}$", {"name": name, "id": id}) return {"widgettype": "Message", "options": {"user_data": {"id": id, "label": name}}} return {"error": "failed"} diff --git a/wwwroot/knowledge_bases_list/upload_file.dspy b/wwwroot/knowledge_bases_list/upload_file.dspy index 3e30fe4..907ddd3 100644 --- a/wwwroot/knowledge_bases_list/upload_file.dspy +++ b/wwwroot/knowledge_bases_list/upload_file.dspy @@ -23,9 +23,9 @@ def fmt_bytes(n): quota_limit = 104857600 used = 0 async with get_sor_context(env, 'rag') as sor: - rec = await sor.sqlExe("SELECT COALESCE(SUM(file_size),0) AS used FROM documents WHERE org_id=${org_id}$", {"org_id": userorgid}) + rec = await sor.sqlExe("SELECT COALESCE(SUM(file_size),0) AS used FROM rag_documents WHERE org_id=${org_id}$", {"org_id": userorgid}) if rec: used = int(rec[0].used) - lim = await sor.sqlExe("SELECT limit_bytes FROM org_storage_limits WHERE org_id=${org_id}$", {"org_id": userorgid}) + lim = await sor.sqlExe("SELECT limit_bytes FROM rag_org_storage_limits WHERE org_id=${org_id}$", {"org_id": userorgid}) if lim: quota_limit = int(lim[0].limit_bytes) if used + file_size > quota_limit: return json.dumps({"status": "error", "error": "storage_quota_exceeded", @@ -52,7 +52,7 @@ async def ingest_doc(doc_id, kb_id, file_name, ext_l, real_path): emb_engine = 'clip-vith14' try: async with db.sqlorContext('rag') as sor: - krecs = await sor.sqlExe("SELECT embedding_engine FROM knowledge_bases WHERE id=${kb_id}$", {"kb_id": kb_id}) + krecs = await sor.sqlExe("SELECT embedding_engine FROM rag_knowledge_bases WHERE id=${kb_id}$", {"kb_id": kb_id}) if krecs: emb_engine = (getattr(krecs[0], 'embedding_engine', '') or 'clip-vith14').strip() except: pass @@ -118,7 +118,7 @@ async def ingest_doc(doc_id, kb_id, file_name, ext_l, real_path): try: async with db.sqlorContext('rag') as sor: await sor.sqlExe( - "UPDATE documents SET status='failed', metadata=${meta}$, updated_at=NOW() WHERE id=${id}$", + "UPDATE rag_documents SET status='failed', metadata=${meta}$, updated_at=NOW() WHERE id=${id}$", {"id": doc_id, "meta": json.dumps({"error": "文本知识库不支持媒体文件,请上传文本类文件(txt/md/pdf/docx等)或改用多媒体知识库"}, ensure_ascii=False)}) except: pass return @@ -195,7 +195,7 @@ async def ingest_doc(doc_id, kb_id, file_name, ext_l, real_path): chunk_meta["bboxes"] = frame_bboxes async with db.sqlorContext('rag') as sor: await sor.sqlExe( - "INSERT INTO document_chunks (id, doc_id, kb_id, chunk_index, content, vector_id, metadata, created_at) " + "INSERT INTO rag_document_chunks (id, doc_id, kb_id, chunk_index, content, vector_id, metadata, created_at) " "VALUES (${id}$, ${doc_id}$, ${kb_id}$, 0, ${content}$, ${vid}$, ${meta}$, NOW())", {"id": doc_id + "_c0", "doc_id": doc_id, "kb_id": kb_id, "content": file_name, "vid": doc_id + "_c0", @@ -277,7 +277,7 @@ async def ingest_doc(doc_id, kb_id, file_name, ext_l, real_path): for i, chunk_text in enumerate(chunks): vid = vector_ids[i] if i < len(vector_ids) else '' await sor.sqlExe( - "INSERT INTO document_chunks (id, doc_id, kb_id, chunk_index, content, vector_id, created_at) " + "INSERT INTO rag_document_chunks (id, doc_id, kb_id, chunk_index, content, vector_id, created_at) " "VALUES (${id}$, ${doc_id}$, ${kb_id}$, ${idx}$, ${content}$, ${vid}$, NOW())", {"id": doc_id + "_c" + str(i), "doc_id": doc_id, "kb_id": kb_id, "idx": i, "content": chunk_text[:2000], "vid": vid}) @@ -286,7 +286,7 @@ async def ingest_doc(doc_id, kb_id, file_name, ext_l, real_path): try: async with db.sqlorContext('rag') as sor: await sor.sqlExe( - "UPDATE documents SET status='failed', metadata=${meta}$, updated_at=NOW() WHERE id=${id}$", + "UPDATE rag_documents SET status='failed', metadata=${meta}$, updated_at=NOW() WHERE id=${id}$", {"id": doc_id, "meta": json.dumps({"error": str(e)[:300]}, ensure_ascii=False)}) except: pass return @@ -296,11 +296,11 @@ async def ingest_doc(doc_id, kb_id, file_name, ext_l, real_path): try: async with db.sqlorContext('rag') as sor: await sor.sqlExe( - "UPDATE documents SET status='done', chunk_count=${chunks}$, metadata=${meta}$, updated_at=NOW() WHERE id=${id}$", + "UPDATE rag_documents SET status='done', chunk_count=${chunks}$, metadata=${meta}$, updated_at=NOW() WHERE id=${id}$", {"id": doc_id, "chunks": chunks_n, "meta": meta_json}) if chunks_n: await sor.sqlExe( - "UPDATE knowledge_bases SET chunk_count=chunk_count+${n}$ WHERE id=${kb_id}$", + "UPDATE rag_knowledge_bases SET chunk_count=chunk_count+${n}$ WHERE id=${kb_id}$", {"n": chunks_n, "kb_id": kb_id}) except: pass @@ -310,12 +310,12 @@ async def ingest_doc(doc_id, kb_id, file_name, ext_l, real_path): # ============================================================ async with get_sor_context(env, 'rag') as sor: await sor.sqlExe( - "INSERT INTO documents (id, kb_id, folder_id, file_name, file_type, file_size, file_path, mime_type, status, chunk_count, metadata, org_id, created_at, updated_at) " + "INSERT INTO rag_documents (id, kb_id, folder_id, file_name, file_type, file_size, file_path, mime_type, status, chunk_count, metadata, org_id, created_at, updated_at) " "VALUES (${id}$, ${kb_id}$, ${folder_id}$, ${file_name}$, 'other', ${file_size}$, ${file_path}$, 'application/octet-stream', 'pending', 0, '{}', ${org_id}$, NOW(), NOW())", {"id": doc_id, "kb_id": kb_id, "folder_id": folder_id, "file_name": file_name, "file_size": file_size, "file_path": web_path, "org_id": userorgid}) await sor.sqlExe( - "UPDATE knowledge_bases SET doc_count=doc_count+1, total_size=total_size+${size}$ WHERE id=${kb_id}$", + "UPDATE rag_knowledge_bases SET doc_count=doc_count+1, total_size=total_size+${size}$ WHERE id=${kb_id}$", {"size": file_size, "kb_id": kb_id}) # Fire background ingestion — pass primitives only (no env/request/proxy objects)