refactor(schema): rag 模块全部数据表加 rag_ 前缀(模块跨应用复用防冲突)
- 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 迁移
This commit is contained in:
parent
fb2cf3e703
commit
789960a1cf
54
init/migrate_rag_prefix.sql
Normal file
54
init/migrate_rag_prefix.sql
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
-- rag 模块表名加 rag_ 前缀(模块跨应用复用,避免与业务表冲突)
|
||||||
|
-- 幂等:仅当旧表存在且新表不存在时才 RENAME。
|
||||||
|
-- 用法: mysql -h <host> -u<user> -p<pwd> <db> < 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;
|
||||||
@ -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}}"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -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"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -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}}"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -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"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
66
json/rag_documents.json
Normal file
66
json/rag_documents.json
Normal file
@ -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}}"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
81
json/rag_engine_configs.json
Normal file
81
json/rag_engine_configs.json
Normal file
@ -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"
|
||||||
|
}
|
||||||
|
}
|
||||||
70
json/rag_knowledge_bases.json
Normal file
70
json/rag_knowledge_bases.json
Normal file
@ -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}}"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
40
json/rag_org_storage_limits.json
Normal file
40
json/rag_org_storage_limits.json
Normal file
@ -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"
|
||||||
|
}
|
||||||
|
}
|
||||||
90
json/rag_subscriptions.json
Normal file
90
json/rag_subscriptions.json
Normal file
@ -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"
|
||||||
|
}
|
||||||
|
}
|
||||||
55
json/rag_tags.json
Normal file
55
json/rag_tags.json
Normal file
@ -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}}"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -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"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -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}}"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -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}}"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -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"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -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}}"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -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"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
129
models/rag_document_chunks.json
Normal file
129
models/rag_document_chunks.json
Normal file
@ -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"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
151
models/rag_documents.json
Normal file
151
models/rag_documents.json
Normal file
@ -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"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
124
models/rag_engine_configs.json
Normal file
124
models/rag_engine_configs.json
Normal file
@ -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"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
138
models/rag_entities.json
Normal file
138
models/rag_entities.json
Normal file
@ -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"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
103
models/rag_entity_relations.json
Normal file
103
models/rag_entity_relations.json
Normal file
@ -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"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
124
models/rag_knowledge_bases.json
Normal file
124
models/rag_knowledge_bases.json
Normal file
@ -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"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
87
models/rag_media_tags.json
Normal file
87
models/rag_media_tags.json
Normal file
@ -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"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
39
models/rag_org_storage_limits.json
Normal file
39
models/rag_org_storage_limits.json
Normal file
@ -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"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
134
models/rag_subscriptions.json
Normal file
134
models/rag_subscriptions.json
Normal file
@ -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"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
73
models/rag_tags.json
Normal file
73
models/rag_tags.json
Normal file
@ -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"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
110
models/rag_usage_logs.json
Normal file
110
models/rag_usage_logs.json
Normal file
@ -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"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@ -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"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -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}}"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
88
rag/init.py
88
rag/init.py
@ -27,10 +27,10 @@ async def kb_list_handler(request, params_kw, *args, **kwargs):
|
|||||||
try:
|
try:
|
||||||
userorgid = await env.get_userorgid()
|
userorgid = await env.get_userorgid()
|
||||||
async with get_sor_context(env, 'rag') as sor:
|
async with get_sor_context(env, 'rag') as sor:
|
||||||
recs = await sor.R("knowledge_bases", {})
|
recs = await sor.R("rag_knowledge_bases", {})
|
||||||
cards = []
|
cards = []
|
||||||
for r in recs:
|
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)
|
doc_count = len(doc_recs)
|
||||||
total_size = r.total_size or 0
|
total_size = r.total_size or 0
|
||||||
if total_size >= 1073741824:
|
if total_size >= 1073741824:
|
||||||
@ -64,7 +64,7 @@ async def engines_handler(request, params_kw, *args, **kwargs):
|
|||||||
try:
|
try:
|
||||||
userorgid = await env.get_userorgid()
|
userorgid = await env.get_userorgid()
|
||||||
async with get_sor_context(env, 'rag') as sor:
|
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})
|
recs = await sor.sqlExe(sql, {"org_id": userorgid})
|
||||||
rows = [dict(r) for r in recs]
|
rows = [dict(r) for r in recs]
|
||||||
return json.dumps({"status": "SUCCEEDED", "data": {"rows": rows, "total": len(rows)}}, ensure_ascii=False, default=str)
|
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"""
|
"""Resolve KB IDs: specific or all org KBs"""
|
||||||
async with get_sor_context(env, 'rag') as sor:
|
async with get_sor_context(env, 'rag') as sor:
|
||||||
if kb_id:
|
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]
|
return [r.id for r in recs]
|
||||||
# All org KBs (global + org-specific)
|
# 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})
|
recs = await sor.sqlExe(sql, {"org_id": userorgid})
|
||||||
return [r.id for r in recs]
|
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'
|
emb_engine = 'clip-vith14'
|
||||||
if env is not None and kb_id:
|
if env is not None and kb_id:
|
||||||
async with get_sor_context(env, 'rag') as sor:
|
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:
|
if krecs:
|
||||||
emb_engine = (getattr(krecs[0], 'embedding_engine', '') or 'clip-vith14').strip()
|
emb_engine = (getattr(krecs[0], 'embedding_engine', '') or 'clip-vith14').strip()
|
||||||
if emb_engine == 'bge-m3':
|
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:
|
async with get_sor_context(env, 'rag') as sor:
|
||||||
recs = await sor.sqlExe(
|
recs = await sor.sqlExe(
|
||||||
"SELECT id, file_name, file_type, file_size, status, kb_id, created_at "
|
"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}
|
doc_map = {r.id: dict(r) for r in recs}
|
||||||
|
|
||||||
for h in hits:
|
for h in hits:
|
||||||
@ -325,9 +325,9 @@ async def doc_upload_handler(request, params_kw, *args, **kwargs):
|
|||||||
quota_limit = 104857600
|
quota_limit = 104857600
|
||||||
used = 0
|
used = 0
|
||||||
async with get_sor_context(env, 'rag') as sor:
|
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)
|
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 lim: quota_limit = int(lim[0].limit_bytes)
|
||||||
if used + file_size > quota_limit:
|
if used + file_size > quota_limit:
|
||||||
return json.dumps({"error": "storage_quota_exceeded",
|
return json.dumps({"error": "storage_quota_exceeded",
|
||||||
@ -342,7 +342,7 @@ async def doc_upload_handler(request, params_kw, *args, **kwargs):
|
|||||||
# Create document record
|
# Create document record
|
||||||
async with get_sor_context(env, 'rag') as sor:
|
async with get_sor_context(env, 'rag') as sor:
|
||||||
await sor.sqlExe(
|
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())",
|
"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,
|
{"id": doc_id, "kb_id": kb_id, "file_name": file_name, "file_type": file_type,
|
||||||
"file_size": file_size, "file_path": web_path,
|
"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
|
# Update KB stats
|
||||||
await sor.sqlExe(
|
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})
|
{"size": file_size, "kb_id": kb_id})
|
||||||
|
|
||||||
# Trigger async ingest for text-based files via uapi
|
# 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:
|
async with get_sor_context(env, 'rag') as sor:
|
||||||
chunks_n = ingest_result.get("chunks", 0) if ingest_result else 0
|
chunks_n = ingest_result.get("chunks", 0) if ingest_result else 0
|
||||||
await sor.sqlExe(
|
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})
|
{"chunks": chunks_n, "id": doc_id})
|
||||||
if chunks_n:
|
if chunks_n:
|
||||||
await sor.sqlExe(
|
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})
|
{"n": chunks_n, "kb_id": kb_id})
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
exception(f"uapi ingest failed: {e}")
|
exception(f"uapi ingest failed: {e}")
|
||||||
async with get_sor_context(env, 'rag') as sor:
|
async with get_sor_context(env, 'rag') as sor:
|
||||||
await sor.sqlExe(
|
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({
|
return json.dumps({
|
||||||
"status": "SUCCEEDED",
|
"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:
|
async with get_sor_context(env, 'rag') as sor:
|
||||||
# Get document info
|
# Get document info
|
||||||
recs = await sor.R("documents", {"id": doc_id})
|
recs = await sor.R("rag_documents", {"id": doc_id})
|
||||||
if not recs:
|
if not recs:
|
||||||
return json.dumps({"error": "document not found"})
|
return json.dumps({"error": "document not found"})
|
||||||
doc = recs[0]
|
doc = recs[0]
|
||||||
|
|
||||||
# Get chunks to clean VDB
|
# 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
|
# Delete from VDB
|
||||||
if chunks:
|
if chunks:
|
||||||
@ -424,14 +424,14 @@ async def doc_delete_handler(request, params_kw, *args, **kwargs):
|
|||||||
exception(f"graph delete failed: {e}")
|
exception(f"graph delete failed: {e}")
|
||||||
|
|
||||||
# Delete DB records
|
# Delete DB records
|
||||||
await sor.sqlExe("DELETE FROM document_chunks WHERE doc_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 entities WHERE kb_id=${kb_id}$", {"kb_id": doc.kb_id})
|
await sor.sqlExe("DELETE FROM rag_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 rag_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_documents WHERE id=${id}$", {"id": doc_id})
|
||||||
|
|
||||||
# Update KB stats
|
# Update KB stats
|
||||||
await sor.sqlExe(
|
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})
|
{"size": doc.file_size, "n": len(chunks), "kb_id": doc.kb_id})
|
||||||
|
|
||||||
# Delete file from disk
|
# Delete file from disk
|
||||||
@ -496,7 +496,7 @@ async def _rag_ingest_async(env, text, kb_id, doc_id):
|
|||||||
try:
|
try:
|
||||||
emb_engine = 'clip-vith14'
|
emb_engine = 'clip-vith14'
|
||||||
async with get_sor_context(env, 'rag') as sor:
|
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:
|
if krecs:
|
||||||
emb_engine = (getattr(krecs[0], 'embedding_engine', '') or 'clip-vith14').strip()
|
emb_engine = (getattr(krecs[0], 'embedding_engine', '') or 'clip-vith14').strip()
|
||||||
if emb_engine == 'bge-m3':
|
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:
|
async with get_sor_context(env, 'rag') as sor:
|
||||||
for i, (chunk_text, vid) in enumerate(zip(chunks, vector_ids)):
|
for i, (chunk_text, vid) in enumerate(zip(chunks, vector_ids)):
|
||||||
await sor.sqlExe(
|
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())",
|
"VALUES (${id}$, ${doc_id}$, ${kb_id}$, ${idx}$, ${content}$, ${vid}$, NOW())",
|
||||||
{"id": f"{doc_id}_c{i}", "doc_id": doc_id, "kb_id": kb_id,
|
{"id": f"{doc_id}_c{i}", "doc_id": doc_id, "kb_id": kb_id,
|
||||||
"idx": i, "content": chunk_text[:2000], "vid": vid})
|
"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:
|
async with get_sor_context(env, 'rag') as sor:
|
||||||
dir_id = uuid.uuid4().hex
|
dir_id = uuid.uuid4().hex
|
||||||
await sor.sqlExe(
|
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())",
|
"VALUES (${id}$, '', ${kb_id}$, 0, 'directory', ${name}$, ${parent}$, NOW())",
|
||||||
{"id": dir_id, "kb_id": kb_id, "name": dir_name, "parent": parent_id})
|
{"id": dir_id, "kb_id": kb_id, "name": dir_name, "parent": parent_id})
|
||||||
return json.dumps({"status": "SUCCEEDED", "dir_id": dir_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:
|
if not item_id:
|
||||||
return json.dumps({"error": "item_id required"})
|
return json.dumps({"error": "item_id required"})
|
||||||
async with get_sor_context(env, 'rag') as sor:
|
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"})
|
return json.dumps({"status": "SUCCEEDED"})
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
exception(f"dir_delete: {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:
|
async with get_sor_context(env, 'rag') as sor:
|
||||||
# Get directories (chunk_type='directory')
|
# Get directories (chunk_type='directory')
|
||||||
dirs = await sor.sqlExe(
|
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})
|
{"kb_id": kb_id})
|
||||||
# Get files (documents table)
|
# Get files (documents table)
|
||||||
docs = await sor.sqlExe(
|
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})
|
{"kb_id": kb_id})
|
||||||
items = []
|
items = []
|
||||||
for d in dirs:
|
for d in dirs:
|
||||||
@ -705,14 +705,14 @@ async def tag_create_handler(request, params_kw, *args, **kwargs):
|
|||||||
userorgid = await env.get_userorgid()
|
userorgid = await env.get_userorgid()
|
||||||
async with get_sor_context(env, 'rag') as sor:
|
async with get_sor_context(env, 'rag') as sor:
|
||||||
existing = await sor.sqlExe(
|
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})
|
{"kb_id": kb_id, "name": name, "org_id": userorgid})
|
||||||
if existing:
|
if existing:
|
||||||
return json.dumps({"status": "SUCCEEDED", "tag_id": existing[0].id, "name": name,
|
return json.dumps({"status": "SUCCEEDED", "tag_id": existing[0].id, "name": name,
|
||||||
"color": existing[0].color, "duplicate": True}, ensure_ascii=False)
|
"color": existing[0].color, "duplicate": True}, ensure_ascii=False)
|
||||||
tag_id = uuid.uuid4().hex
|
tag_id = uuid.uuid4().hex
|
||||||
await sor.sqlExe(
|
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())",
|
"VALUES (${id}$, ${kb_id}$, ${name}$, ${color}$, ${org_id}$, NOW())",
|
||||||
{"id": tag_id, "kb_id": kb_id, "name": name, "color": color, "org_id": userorgid})
|
{"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})
|
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"})
|
return json.dumps({"error": "kb_id required"})
|
||||||
userorgid = await env.get_userorgid()
|
userorgid = await env.get_userorgid()
|
||||||
async with get_sor_context(env, 'rag') as sor:
|
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]
|
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)
|
return json.dumps({"status": "SUCCEEDED", "tags": tags}, ensure_ascii=False, default=str)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
@ -746,8 +746,8 @@ async def tag_delete_handler(request, params_kw, *args, **kwargs):
|
|||||||
if not tag_id:
|
if not tag_id:
|
||||||
return json.dumps({"error": "tag_id required"})
|
return json.dumps({"error": "tag_id required"})
|
||||||
async with get_sor_context(env, 'rag') as sor:
|
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 rag_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_tags WHERE id=${id}$", {"id": tag_id})
|
||||||
return json.dumps({"status": "SUCCEEDED", "tag_id": tag_id})
|
return json.dumps({"status": "SUCCEEDED", "tag_id": tag_id})
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
exception(f"tag_delete: {e}, {format_exc()}")
|
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:
|
async with get_sor_context(env, 'rag') as sor:
|
||||||
mt_id = uuid.uuid4().hex
|
mt_id = uuid.uuid4().hex
|
||||||
await sor.sqlExe(
|
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())",
|
"VALUES (${id}$, ${kb_id}$, ${type}$, ${mid}$, ${tid}$, NOW())",
|
||||||
{"id": mt_id, "kb_id": kb_id, "type": media_type, "mid": media_id, "tid": tag_id})
|
{"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})
|
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"})
|
return json.dumps({"error": "media_type, media_id, tag_id required"})
|
||||||
async with get_sor_context(env, 'rag') as sor:
|
async with get_sor_context(env, 'rag') as sor:
|
||||||
await sor.sqlExe(
|
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})
|
{"type": media_type, "mid": media_id, "tid": tag_id})
|
||||||
return json.dumps({"status": "SUCCEEDED"})
|
return json.dumps({"status": "SUCCEEDED"})
|
||||||
except Exception as e:
|
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"})
|
return json.dumps({"error": "media_type and media_id required"})
|
||||||
async with get_sor_context(env, 'rag') as sor:
|
async with get_sor_context(env, 'rag') as sor:
|
||||||
recs = await sor.sqlExe(
|
recs = await sor.sqlExe(
|
||||||
"SELECT t.id, t.name, t.color FROM media_tags mt "
|
"SELECT t.id, t.name, t.color FROM rag_media_tags mt "
|
||||||
"JOIN tags t ON mt.tag_id=t.id "
|
"JOIN rag_tags t ON mt.tag_id=t.id "
|
||||||
"WHERE mt.media_type=${type}$ AND mt.media_id=${mid}$",
|
"WHERE mt.media_type=${type}$ AND mt.media_id=${mid}$",
|
||||||
{"type": media_type, "mid": media_id})
|
{"type": media_type, "mid": media_id})
|
||||||
tags = [{"id": r.id, "name": r.name, "color": r.color} for r in recs]
|
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 = []
|
media_ids_by_tag = []
|
||||||
for tid in tag_ids:
|
for tid in tag_ids:
|
||||||
recs = await sor.sqlExe(
|
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})
|
{"kb_id": kb_id, "tid": tid})
|
||||||
mids = {(r.media_type, r.media_id) for r in recs}
|
mids = {(r.media_type, r.media_id) for r in recs}
|
||||||
media_ids_by_tag.append(mids)
|
media_ids_by_tag.append(mids)
|
||||||
@ -855,19 +855,19 @@ async def tag_search_handler(request, params_kw, *args, **kwargs):
|
|||||||
results = []
|
results = []
|
||||||
if doc_ids:
|
if doc_ids:
|
||||||
docs = await sor.sqlExe(
|
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})
|
{"ids": doc_ids})
|
||||||
for d in docs:
|
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)})
|
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:
|
if face_ids:
|
||||||
faces = await sor.sqlExe(
|
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})
|
{"ids": face_ids})
|
||||||
for f in faces:
|
for f in faces:
|
||||||
results.append({"type": "face", "id": f.id, "name": f.name, "description": f.description, "created_at": str(f.created_at)})
|
results.append({"type": "face", "id": f.id, "name": f.name, "description": f.description, "created_at": str(f.created_at)})
|
||||||
if voice_ids:
|
if voice_ids:
|
||||||
voices = await sor.sqlExe(
|
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})
|
{"ids": voice_ids})
|
||||||
for v in voices:
|
for v in voices:
|
||||||
results.append({"type": "voice", "id": v.id, "name": v.name, "description": v.description, "created_at": str(v.created_at)})
|
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 = []
|
vdb_docs = []
|
||||||
for doc in tag_filtered_docs:
|
for doc in tag_filtered_docs:
|
||||||
chunks = await sor.sqlExe(
|
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"]})
|
{"id": doc["id"]})
|
||||||
for c in chunks:
|
for c in chunks:
|
||||||
vdb_docs.append({"doc_id": doc["id"], "content": c.content})
|
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()]
|
wanted_ids = [t.strip() for t in tag_ids_str.split(",") if t.strip()]
|
||||||
async with get_sor_context(env, 'rag') as sor:
|
async with get_sor_context(env, 'rag') as sor:
|
||||||
recs = await sor.sqlExe(
|
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})
|
{"type": media_type, "mid": media_id})
|
||||||
current = {r.tag_id: r.id for r in recs}
|
current = {r.tag_id: r.id for r in recs}
|
||||||
removed = 0
|
removed = 0
|
||||||
for tid, mt_id in current.items():
|
for tid, mt_id in current.items():
|
||||||
if tid not in wanted_ids:
|
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
|
removed += 1
|
||||||
added = 0
|
added = 0
|
||||||
for tid in wanted_ids:
|
for tid in wanted_ids:
|
||||||
if tid not in current:
|
if tid not in current:
|
||||||
mt_id = uuid.uuid4().hex
|
mt_id = uuid.uuid4().hex
|
||||||
await sor.sqlExe(
|
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())",
|
"VALUES (${id}$, ${kb_id}$, ${type}$, ${mid}$, ${tid}$, NOW())",
|
||||||
{"id": mt_id, "kb_id": kb_id, "type": media_type, "mid": media_id, "tid": tid})
|
{"id": mt_id, "kb_id": kb_id, "type": media_type, "mid": media_id, "tid": tid})
|
||||||
added += 1
|
added += 1
|
||||||
|
|||||||
@ -185,19 +185,19 @@ async def process_upload(env, file_data, kb_id, folder_id, file_name):
|
|||||||
from sqlor.dbpools import get_sor_context
|
from sqlor.dbpools import get_sor_context
|
||||||
async with get_sor_context(env, 'rag') as sor:
|
async with get_sor_context(env, 'rag') as sor:
|
||||||
await sor.sqlExe(
|
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())",
|
"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,
|
{"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,
|
"file_size": file_size, "file_path": "/idfile/files/" + saved_name,
|
||||||
"status": status, "meta": meta, "org_id": userorgid})
|
"status": status, "meta": meta, "org_id": userorgid})
|
||||||
await sor.sqlExe(
|
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})
|
{"size": file_size, "kb_id": kb_id})
|
||||||
if ingest_result and status == 'done':
|
if ingest_result and status == 'done':
|
||||||
chunks_n = ingest_result.get('chunks', 0) if isinstance(ingest_result, dict) else 0
|
chunks_n = ingest_result.get('chunks', 0) if isinstance(ingest_result, dict) else 0
|
||||||
if chunks_n:
|
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 rag_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_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
|
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
|
speakers_n = voice_result.get('speakers', 0) if voice_result and isinstance(voice_result, dict) else 0
|
||||||
|
|||||||
@ -9,7 +9,7 @@ if not doc_id or not tag:
|
|||||||
|
|
||||||
async with get_sor_context(env, 'rag') as sor:
|
async with get_sor_context(env, 'rag') as sor:
|
||||||
# Get existing tags
|
# 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})
|
{"id": doc_id, "kb_id": kb_id})
|
||||||
if not recs:
|
if not recs:
|
||||||
return json.dumps({"status": "error", "error": "document not found"}, ensure_ascii=False)
|
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)
|
tags.append(tag)
|
||||||
meta['tags'] = tags
|
meta['tags'] = tags
|
||||||
await sor.sqlExe(
|
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})
|
{"meta": _json.dumps(meta, ensure_ascii=False), "id": doc_id})
|
||||||
return _json.dumps({"status": "SUCCEEDED", "tags": tags, "added": tag}, ensure_ascii=False)
|
return _json.dumps({"status": "SUCCEEDED", "tags": tags, "added": tag}, ensure_ascii=False)
|
||||||
else:
|
else:
|
||||||
|
|||||||
@ -6,7 +6,7 @@ rows = []
|
|||||||
async with get_sor_context(env, 'rag') as sor:
|
async with get_sor_context(env, 'rag') as sor:
|
||||||
recs = await sor.sqlExe(
|
recs = await sor.sqlExe(
|
||||||
"SELECT id, file_name, file_size, file_type, file_path, metadata, status, chunk_count "
|
"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",
|
"ORDER BY created_at DESC LIMIT 50",
|
||||||
{"kb_id": kb_id})
|
{"kb_id": kb_id})
|
||||||
rows = [dict(r) for r in recs]
|
rows = [dict(r) for r in recs]
|
||||||
|
|||||||
@ -6,10 +6,10 @@ env = request._run_ns
|
|||||||
rows = []
|
rows = []
|
||||||
async with get_sor_context(env, 'rag') as sor:
|
async with get_sor_context(env, 'rag') as sor:
|
||||||
recs = await sor.sqlExe(
|
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})
|
{"kb_id": kb_id})
|
||||||
rows = [dict(r) for r in recs]
|
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'
|
emb_engine = (getattr(krecs[0], 'embedding_engine', '') or 'clip-vith14').strip() if krecs else 'clip-vith14'
|
||||||
if emb_engine == 'bge-m3':
|
if emb_engine == 'bge-m3':
|
||||||
emb_url = 'https://embedding.opencomputing.net:10443/txte/api/embed'
|
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)
|
meta = j2.dumps({"face": face_count, "speakers": speakers}, ensure_ascii=False)
|
||||||
async with get_sor_context(env, 'rag') as sor:
|
async with get_sor_context(env, 'rag') as sor:
|
||||||
await sor.sqlExe(
|
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})
|
{"meta": meta, "id": fid})
|
||||||
results.append(f"🎬 {fn}: faces={face_count} speakers={speakers}")
|
results.append(f"🎬 {fn}: faces={face_count} speakers={speakers}")
|
||||||
continue
|
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:
|
async with get_sor_context(env, 'rag') as sor:
|
||||||
for i, ct in enumerate(chunks):
|
for i, ct in enumerate(chunks):
|
||||||
await sor.sqlExe(
|
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": ""})
|
{"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 rag_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_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")
|
results.append(f"✓ {fn}: {len(text)} chars → {len(chunks)} chunks")
|
||||||
|
|
||||||
|
|||||||
@ -12,7 +12,7 @@ async with db.sqlorContext(dbname) as sor:
|
|||||||
if emb_type not in ("bge-m3", "clip-vith14"):
|
if emb_type not in ("bge-m3", "clip-vith14"):
|
||||||
emb_type = "clip-vith14"
|
emb_type = "clip-vith14"
|
||||||
await sor.sqlExe(
|
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())",
|
"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})
|
{"id": kb_id, "name": name, "desc": desc, "org_id": userorgid, "emb": emb_type})
|
||||||
return {
|
return {
|
||||||
|
|||||||
@ -12,7 +12,7 @@ userorgid = await env.get_userorgid()
|
|||||||
|
|
||||||
async with get_sor_context(env, 'rag') as sor:
|
async with get_sor_context(env, 'rag') as sor:
|
||||||
recs = await sor.sqlExe(
|
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})
|
{"id": doc_id})
|
||||||
if not recs:
|
if not recs:
|
||||||
return json.dumps({"status": "error", "error": "file not found"}, ensure_ascii=False)
|
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
|
file_size = doc.file_size
|
||||||
|
|
||||||
# Delete document + chunks
|
# Delete document + chunks
|
||||||
await sor.sqlExe("DELETE FROM document_chunks WHERE doc_id=${doc_id}$", {"doc_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 documents WHERE id=${id}$", {"id": doc_id})
|
await sor.sqlExe("DELETE FROM rag_documents WHERE id=${id}$", {"id": doc_id})
|
||||||
|
|
||||||
# Update KB stats
|
# Update KB stats
|
||||||
await sor.sqlExe(
|
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})
|
{"size": file_size, "kb_id": kb_id})
|
||||||
|
|
||||||
# Delete physical file via env.realpath
|
# Delete physical file via env.realpath
|
||||||
@ -45,11 +45,11 @@ rows = []
|
|||||||
async with db.sqlorContext(dbname) as sor:
|
async with db.sqlorContext(dbname) as sor:
|
||||||
if folder_id and folder_id != '__root__':
|
if folder_id and folder_id != '__root__':
|
||||||
recs = await sor.sqlExe(
|
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})
|
{"kb_id": kb_id, "folder_id": folder_id})
|
||||||
else:
|
else:
|
||||||
recs = await sor.sqlExe(
|
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})
|
{"kb_id": kb_id})
|
||||||
for r in recs:
|
for r in recs:
|
||||||
rows.append({
|
rows.append({
|
||||||
|
|||||||
@ -14,21 +14,21 @@ deleted = {"documents": 0, "chunks": 0, "tags": 0, "media_tags": 0,
|
|||||||
|
|
||||||
async with get_sor_context(env, 'rag') as sor:
|
async with get_sor_context(env, 'rag') as sor:
|
||||||
kb = await sor.sqlExe(
|
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})
|
{"id": kb_id, "org_id": userorgid})
|
||||||
if not kb:
|
if not kb:
|
||||||
return {"widgettype": "urlwidget",
|
return {"widgettype": "urlwidget",
|
||||||
"options": {"url": entire_url('/rag/knowledge_bases_list/index.ui')}}
|
"options": {"url": entire_url('/rag/knowledge_bases_list/index.ui')}}
|
||||||
|
|
||||||
docs = await sor.sqlExe(
|
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})
|
{"kb_id": kb_id})
|
||||||
doc_ids = [d.id for d in docs]
|
doc_ids = [d.id for d in docs]
|
||||||
|
|
||||||
# 1. VDB cleanup
|
# 1. VDB cleanup
|
||||||
if doc_ids:
|
if doc_ids:
|
||||||
chunks = await sor.sqlExe(
|
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})
|
{"ids": doc_ids})
|
||||||
vector_ids = [c.vector_id for c in chunks]
|
vector_ids = [c.vector_id for c in chunks]
|
||||||
if vector_ids:
|
if vector_ids:
|
||||||
@ -53,40 +53,40 @@ async with get_sor_context(env, 'rag') as sor:
|
|||||||
|
|
||||||
# 3. Entities & relations
|
# 3. Entities & relations
|
||||||
er = await sor.sqlExe(
|
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})
|
{"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
|
deleted["entities"] += er[0].cnt if er else 0
|
||||||
|
|
||||||
ent = await sor.sqlExe(
|
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})
|
{"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
|
deleted["entities"] += ent[0].cnt if ent else 0
|
||||||
|
|
||||||
# 4. Chunks
|
# 4. Chunks
|
||||||
ch = await sor.sqlExe(
|
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})
|
{"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
|
deleted["chunks"] = ch[0].cnt if ch else 0
|
||||||
|
|
||||||
# Media tags
|
# Media tags
|
||||||
mt = await sor.sqlExe("SELECT COUNT(*) AS cnt 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 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
|
deleted["media_tags"] = mt[0].cnt if mt else 0
|
||||||
|
|
||||||
# Tags
|
# Tags
|
||||||
t = await sor.sqlExe("SELECT COUNT(*) AS cnt 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 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
|
deleted["tags"] = t[0].cnt if t else 0
|
||||||
|
|
||||||
# Documents
|
# 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)
|
deleted["documents"] = len(doc_ids)
|
||||||
|
|
||||||
# KB itself
|
# 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
|
# Physical files — paths are like /44/126/174/61/file.mp4
|
||||||
for d in docs:
|
for d in docs:
|
||||||
|
|||||||
@ -2,6 +2,6 @@ ns = params_kw.copy()
|
|||||||
db = DBPools()
|
db = DBPools()
|
||||||
dbname = get_module_dbname('rag')
|
dbname = get_module_dbname('rag')
|
||||||
async with db.sqlorContext(dbname) as sor:
|
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 {"widgettype": "Message", "options": {"user_data": {"id": ns.get("id","")}}}
|
||||||
return {"error": "failed"}
|
return {"error": "failed"}
|
||||||
|
|||||||
@ -14,11 +14,11 @@ try:
|
|||||||
async with db.sqlorContext(dbname) as sor:
|
async with db.sqlorContext(dbname) as sor:
|
||||||
if folder_id:
|
if folder_id:
|
||||||
recs = await sor.sqlExe(
|
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})
|
{"kb_id": kb_id, "folder_id": folder_id})
|
||||||
else:
|
else:
|
||||||
recs = await sor.sqlExe(
|
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})
|
{"kb_id": kb_id})
|
||||||
for r in recs:
|
for r in recs:
|
||||||
rows.append({
|
rows.append({
|
||||||
|
|||||||
@ -7,7 +7,7 @@ try:
|
|||||||
async with db.sqlorContext(dbname) as sor:
|
async with db.sqlorContext(dbname) as sor:
|
||||||
if not id:
|
if not id:
|
||||||
recs = await sor.sqlExe(
|
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})
|
{"kb_id": kb_id})
|
||||||
result = [{"id": "__root__", "parentid": "", "label": "📁 根目录"}]
|
result = [{"id": "__root__", "parentid": "", "label": "📁 根目录"}]
|
||||||
for r in recs:
|
for r in recs:
|
||||||
@ -15,7 +15,7 @@ try:
|
|||||||
return result
|
return result
|
||||||
else:
|
else:
|
||||||
recs = await sor.sqlExe(
|
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})
|
{"kb_id": kb_id, "id": id})
|
||||||
return [dict(r) for r in recs]
|
return [dict(r) for r in recs]
|
||||||
except:
|
except:
|
||||||
|
|||||||
@ -3,7 +3,7 @@ userorgid = await env.get_userorgid()
|
|||||||
cards = []
|
cards = []
|
||||||
async with get_sor_context(env, 'rag') as sor:
|
async with get_sor_context(env, 'rag') as sor:
|
||||||
recs = await sor.sqlExe(
|
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})
|
{"org_id": userorgid})
|
||||||
for r in recs:
|
for r in recs:
|
||||||
doc_count = r.doc_count or 0
|
doc_count = r.doc_count or 0
|
||||||
|
|||||||
@ -3,7 +3,7 @@ env = request._run_ns
|
|||||||
userorgid = await env.get_userorgid()
|
userorgid = await env.get_userorgid()
|
||||||
async with get_sor_context(env, 'rag') as sor:
|
async with get_sor_context(env, 'rag') as sor:
|
||||||
recs = await sor.sqlExe(
|
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})
|
{"org_id": userorgid})
|
||||||
items = [{"value": r.id, "text": r.name} for r in recs]
|
items = [{"value": r.id, "text": r.name} for r in recs]
|
||||||
return json.dumps(items, ensure_ascii=False)
|
return json.dumps(items, ensure_ascii=False)
|
||||||
|
|||||||
@ -12,14 +12,14 @@ try:
|
|||||||
async with db.sqlorContext(dbname) as sor:
|
async with db.sqlorContext(dbname) as sor:
|
||||||
if kind == 'voice':
|
if kind == 'voice':
|
||||||
recs = await sor.sqlExe(
|
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' "
|
"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 '%%.m4a' OR LOWER(file_name) LIKE '%%.aac' OR LOWER(file_name) LIKE '%%.ogg' "
|
||||||
"OR LOWER(file_name) LIKE '%%.flac') ORDER BY created_at DESC",
|
"OR LOWER(file_name) LIKE '%%.flac') ORDER BY created_at DESC",
|
||||||
{"kb_id": kb_id})
|
{"kb_id": kb_id})
|
||||||
else:
|
else:
|
||||||
recs = await sor.sqlExe(
|
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' "
|
"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 '%%.png' OR LOWER(file_name) LIKE '%%.gif' OR LOWER(file_name) LIKE '%%.webp' "
|
||||||
"OR LOWER(file_name) LIKE '%%.bmp') ORDER BY created_at DESC",
|
"OR LOWER(file_name) LIKE '%%.bmp') ORDER BY created_at DESC",
|
||||||
@ -41,8 +41,8 @@ try:
|
|||||||
try:
|
try:
|
||||||
id_list = ','.join(["'" + str(x) + "'" for x in doc_ids])
|
id_list = ','.join(["'" + str(x) + "'" for x in doc_ids])
|
||||||
mt_recs = await sor.sqlExe(
|
mt_recs = await sor.sqlExe(
|
||||||
"SELECT mt.media_id, t.name, t.color FROM media_tags mt " +
|
"SELECT mt.media_id, t.name, t.color FROM rag_media_tags mt " +
|
||||||
"JOIN tags t ON mt.tag_id=t.id " +
|
"JOIN rag_tags t ON mt.tag_id=t.id " +
|
||||||
"WHERE mt.media_type='document' AND mt.media_id IN (" + id_list + ")",
|
"WHERE mt.media_type='document' AND mt.media_id IN (" + id_list + ")",
|
||||||
ns={})
|
ns={})
|
||||||
for mt in mt_recs:
|
for mt in mt_recs:
|
||||||
|
|||||||
@ -7,7 +7,7 @@ async with db.sqlorContext(dbname) as sor:
|
|||||||
parent_id = ns.get("parentid", "")
|
parent_id = ns.get("parentid", "")
|
||||||
kb_id = ns.get("kb_id", "")
|
kb_id = ns.get("kb_id", "")
|
||||||
await sor.sqlExe(
|
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())",
|
"VALUES (${id}$, '', ${kb_id}$, 0, 'directory', ${name}$, ${parentid}$, NOW())",
|
||||||
{"id": dir_id, "kb_id": kb_id, "name": name, "parentid": parent_id})
|
{"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}}}
|
return {"widgettype": "Message", "options": {"user_data": {"id": dir_id, "label": name, "parentid": parent_id}}}
|
||||||
|
|||||||
@ -11,7 +11,7 @@ userorgid = await env.get_userorgid()
|
|||||||
|
|
||||||
async with get_sor_context(env, 'rag') as sor:
|
async with get_sor_context(env, 'rag') as sor:
|
||||||
await sor.sqlExe(
|
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)",
|
"WHERE id=${id}$ AND (org_id=${org_id}$ OR org_id IS NULL)",
|
||||||
{"name": name, "desc": desc, "id": kb_id, "org_id": userorgid})
|
{"name": name, "desc": desc, "id": kb_id, "org_id": userorgid})
|
||||||
|
|
||||||
|
|||||||
@ -11,7 +11,7 @@ env = request._run_ns
|
|||||||
if not name:
|
if not name:
|
||||||
async with get_sor_context(env, 'rag') as sor:
|
async with get_sor_context(env, 'rag') as sor:
|
||||||
recs = await sor.sqlExe(
|
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})
|
{"id": kb_id})
|
||||||
if recs:
|
if recs:
|
||||||
name = recs[0].name or ''
|
name = recs[0].name or ''
|
||||||
|
|||||||
@ -22,18 +22,18 @@ else:
|
|||||||
added = 0; removed = 0
|
added = 0; removed = 0
|
||||||
async with get_sor_context(env, 'rag') as sor:
|
async with get_sor_context(env, 'rag') as sor:
|
||||||
recs = await sor.sqlExe(
|
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})
|
{"mid": doc_id})
|
||||||
current = {r.tag_id: r.id for r in recs}
|
current = {r.tag_id: r.id for r in recs}
|
||||||
for tid, mt_id in list(current.items()):
|
for tid, mt_id in list(current.items()):
|
||||||
if tid not in wanted_ids:
|
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
|
removed += 1
|
||||||
for tid in wanted_ids:
|
for tid in wanted_ids:
|
||||||
if tid not in current:
|
if tid not in current:
|
||||||
mt_id = uuid()
|
mt_id = uuid()
|
||||||
await sor.sqlExe(
|
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())",
|
"VALUES (${id}$, ${kb_id}$, 'document', ${mid}$, ${tid}$, NOW())",
|
||||||
{"id": mt_id, "kb_id": kb_id, "mid": doc_id, "tid": tid})
|
{"id": mt_id, "kb_id": kb_id, "mid": doc_id, "tid": tid})
|
||||||
added += 1
|
added += 1
|
||||||
|
|||||||
@ -51,7 +51,7 @@ if tag_ids_str:
|
|||||||
for i, tid in enumerate(wanted_tags):
|
for i, tid in enumerate(wanted_tags):
|
||||||
placeholders.append("${tid_" + str(i) + "}$")
|
placeholders.append("${tid_" + str(i) + "}$")
|
||||||
nsq["tid_" + str(i)] = tid
|
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) + ") "
|
"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)))
|
"GROUP BY media_id HAVING COUNT(DISTINCT tag_id)=" + str(len(wanted_tags)))
|
||||||
recs = await sor.sqlExe(sql, nsq)
|
recs = await sor.sqlExe(sql, nsq)
|
||||||
@ -79,7 +79,7 @@ kw_rows = []
|
|||||||
emb_engine = 'clip-vith14'
|
emb_engine = 'clip-vith14'
|
||||||
try:
|
try:
|
||||||
async with get_sor_context(env, 'rag') as sor:
|
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:
|
if krecs:
|
||||||
emb_engine = (getattr(krecs[0], 'embedding_engine', '') or 'clip-vith14').strip()
|
emb_engine = (getattr(krecs[0], 'embedding_engine', '') or 'clip-vith14').strip()
|
||||||
except: pass
|
except: pass
|
||||||
@ -126,7 +126,7 @@ if query:
|
|||||||
for i, t in enumerate(tokens):
|
for i, t in enumerate(tokens):
|
||||||
conds.append("content LIKE ${kw_" + str(i) + "}$")
|
conds.append("content LIKE ${kw_" + str(i) + "}$")
|
||||||
nsq["kw_" + str(i)] = "%" + t + "%"
|
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:
|
async with get_sor_context(env, 'rag') as sor:
|
||||||
krecs = await sor.sqlExe(ksql, nsq)
|
krecs = await sor.sqlExe(ksql, nsq)
|
||||||
for r in krecs:
|
for r in krecs:
|
||||||
@ -145,7 +145,7 @@ if query:
|
|||||||
chunk_text = ''
|
chunk_text = ''
|
||||||
async with get_sor_context(env, 'rag') as sor:
|
async with get_sor_context(env, 'rag') as sor:
|
||||||
recs = await sor.sqlExe(
|
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})
|
{"id": rid})
|
||||||
chunk_meta = {}
|
chunk_meta = {}
|
||||||
if recs:
|
if recs:
|
||||||
@ -190,7 +190,7 @@ else:
|
|||||||
placeholders2.append("${did_" + str(i) + "}$")
|
placeholders2.append("${did_" + str(i) + "}$")
|
||||||
nsq2["did_" + str(i)] = did
|
nsq2["did_" + str(i)] = did
|
||||||
# Get documents (exclude face/voice generated derivatives)
|
# 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))
|
"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)
|
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]
|
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):
|
for i, did in enumerate(doc_ids):
|
||||||
chunk_placeholders.append("${cdid_" + str(i) + "}$")
|
chunk_placeholders.append("${cdid_" + str(i) + "}$")
|
||||||
nsq_c["cdid_" + str(i)] = did
|
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))
|
",".join(chunk_placeholders) + ") ORDER BY created_at DESC LIMIT " + str(top_k))
|
||||||
chunk_recs = await sor.sqlExe(chunks_sql, nsq_c)
|
chunk_recs = await sor.sqlExe(chunks_sql, nsq_c)
|
||||||
chunks_by_doc = {}
|
chunks_by_doc = {}
|
||||||
|
|||||||
@ -2,7 +2,7 @@ ns = params_kw.copy()
|
|||||||
env = request._run_ns
|
env = request._run_ns
|
||||||
|
|
||||||
async with get_sor_context(env, 'rag') as sor:
|
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_bytes = int(rec[0].used) if rec else 0
|
||||||
|
|
||||||
used_mb = round(used_bytes / 1048576, 1)
|
used_mb = round(used_bytes / 1048576, 1)
|
||||||
|
|||||||
@ -9,13 +9,13 @@ docs = 0
|
|||||||
async with get_sor_context(env, 'rag') as sor:
|
async with get_sor_context(env, 'rag') as sor:
|
||||||
rec = await sor.sqlExe(
|
rec = await sor.sqlExe(
|
||||||
"SELECT COALESCE(SUM(d.file_size),0) AS used, COUNT(*) AS docs "
|
"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}$",
|
"WHERE k.org_id=${org_id}$",
|
||||||
{"org_id": userorgid})
|
{"org_id": userorgid})
|
||||||
if rec:
|
if rec:
|
||||||
used = int(rec[0].used)
|
used = int(rec[0].used)
|
||||||
docs = int(rec[0].docs)
|
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:
|
if lim:
|
||||||
limit = int(lim[0].limit_bytes)
|
limit = int(lim[0].limit_bytes)
|
||||||
|
|
||||||
|
|||||||
@ -17,14 +17,14 @@ all_tags = [] # all tags for kb: list of (id, name, color)
|
|||||||
try:
|
try:
|
||||||
async with db.sqlorContext(dbname) as sor:
|
async with db.sqlorContext(dbname) as sor:
|
||||||
recs = await sor.sqlExe(
|
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})
|
{"id": doc_id, "kb_id": kb_id})
|
||||||
if recs:
|
if recs:
|
||||||
fname = recs[0].file_name or ''
|
fname = recs[0].file_name or ''
|
||||||
|
|
||||||
# 有哪些标签
|
# 有哪些标签
|
||||||
tag_recs = await sor.sqlExe(
|
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})
|
{"kb_id": kb_id})
|
||||||
for t in tag_recs:
|
for t in tag_recs:
|
||||||
all_tags.append((t.id, t.name, t.color or '#3b82f6'))
|
all_tags.append((t.id, t.name, t.color or '#3b82f6'))
|
||||||
@ -32,7 +32,7 @@ try:
|
|||||||
|
|
||||||
# 当前已关联的
|
# 当前已关联的
|
||||||
mt_recs = await sor.sqlExe(
|
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})
|
{"type": media_type, "mid": doc_id})
|
||||||
cur_tag_ids = [r.tag_id for r in mt_recs]
|
cur_tag_ids = [r.tag_id for r in mt_recs]
|
||||||
except Exception:
|
except Exception:
|
||||||
|
|||||||
@ -3,7 +3,7 @@ env = request._run_ns
|
|||||||
userorgid = await env.get_userorgid()
|
userorgid = await env.get_userorgid()
|
||||||
async with get_sor_context(env, 'rag') as sor:
|
async with get_sor_context(env, 'rag') as sor:
|
||||||
recs = await sor.sqlExe(
|
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})
|
{"org_id": userorgid})
|
||||||
items = [{"value": r.id, "text": r.name, "color": r.color or '#3b82f6'} for r in recs]
|
items = [{"value": r.id, "text": r.name, "color": r.color or '#3b82f6'} for r in recs]
|
||||||
return json.dumps(items, ensure_ascii=False)
|
return json.dumps(items, ensure_ascii=False)
|
||||||
|
|||||||
@ -4,7 +4,7 @@ dbname = get_module_dbname('rag')
|
|||||||
async with db.sqlorContext(dbname) as sor:
|
async with db.sqlorContext(dbname) as sor:
|
||||||
id = ns.get("id", "")
|
id = ns.get("id", "")
|
||||||
name = ns.get("name", "")
|
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})
|
{"name": name, "id": id})
|
||||||
return {"widgettype": "Message", "options": {"user_data": {"id": id, "label": name}}}
|
return {"widgettype": "Message", "options": {"user_data": {"id": id, "label": name}}}
|
||||||
return {"error": "failed"}
|
return {"error": "failed"}
|
||||||
|
|||||||
@ -23,9 +23,9 @@ def fmt_bytes(n):
|
|||||||
quota_limit = 104857600
|
quota_limit = 104857600
|
||||||
used = 0
|
used = 0
|
||||||
async with get_sor_context(env, 'rag') as sor:
|
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)
|
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 lim: quota_limit = int(lim[0].limit_bytes)
|
||||||
if used + file_size > quota_limit:
|
if used + file_size > quota_limit:
|
||||||
return json.dumps({"status": "error", "error": "storage_quota_exceeded",
|
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'
|
emb_engine = 'clip-vith14'
|
||||||
try:
|
try:
|
||||||
async with db.sqlorContext('rag') as sor:
|
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:
|
if krecs:
|
||||||
emb_engine = (getattr(krecs[0], 'embedding_engine', '') or 'clip-vith14').strip()
|
emb_engine = (getattr(krecs[0], 'embedding_engine', '') or 'clip-vith14').strip()
|
||||||
except: pass
|
except: pass
|
||||||
@ -118,7 +118,7 @@ async def ingest_doc(doc_id, kb_id, file_name, ext_l, real_path):
|
|||||||
try:
|
try:
|
||||||
async with db.sqlorContext('rag') as sor:
|
async with db.sqlorContext('rag') as sor:
|
||||||
await sor.sqlExe(
|
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)})
|
{"id": doc_id, "meta": json.dumps({"error": "文本知识库不支持媒体文件,请上传文本类文件(txt/md/pdf/docx等)或改用多媒体知识库"}, ensure_ascii=False)})
|
||||||
except: pass
|
except: pass
|
||||||
return
|
return
|
||||||
@ -195,7 +195,7 @@ async def ingest_doc(doc_id, kb_id, file_name, ext_l, real_path):
|
|||||||
chunk_meta["bboxes"] = frame_bboxes
|
chunk_meta["bboxes"] = frame_bboxes
|
||||||
async with db.sqlorContext('rag') as sor:
|
async with db.sqlorContext('rag') as sor:
|
||||||
await sor.sqlExe(
|
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())",
|
"VALUES (${id}$, ${doc_id}$, ${kb_id}$, 0, ${content}$, ${vid}$, ${meta}$, NOW())",
|
||||||
{"id": doc_id + "_c0", "doc_id": doc_id, "kb_id": kb_id,
|
{"id": doc_id + "_c0", "doc_id": doc_id, "kb_id": kb_id,
|
||||||
"content": file_name, "vid": doc_id + "_c0",
|
"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):
|
for i, chunk_text in enumerate(chunks):
|
||||||
vid = vector_ids[i] if i < len(vector_ids) else ''
|
vid = vector_ids[i] if i < len(vector_ids) else ''
|
||||||
await sor.sqlExe(
|
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())",
|
"VALUES (${id}$, ${doc_id}$, ${kb_id}$, ${idx}$, ${content}$, ${vid}$, NOW())",
|
||||||
{"id": doc_id + "_c" + str(i), "doc_id": doc_id, "kb_id": kb_id,
|
{"id": doc_id + "_c" + str(i), "doc_id": doc_id, "kb_id": kb_id,
|
||||||
"idx": i, "content": chunk_text[:2000], "vid": vid})
|
"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:
|
try:
|
||||||
async with db.sqlorContext('rag') as sor:
|
async with db.sqlorContext('rag') as sor:
|
||||||
await sor.sqlExe(
|
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)})
|
{"id": doc_id, "meta": json.dumps({"error": str(e)[:300]}, ensure_ascii=False)})
|
||||||
except: pass
|
except: pass
|
||||||
return
|
return
|
||||||
@ -296,11 +296,11 @@ async def ingest_doc(doc_id, kb_id, file_name, ext_l, real_path):
|
|||||||
try:
|
try:
|
||||||
async with db.sqlorContext('rag') as sor:
|
async with db.sqlorContext('rag') as sor:
|
||||||
await sor.sqlExe(
|
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})
|
{"id": doc_id, "chunks": chunks_n, "meta": meta_json})
|
||||||
if chunks_n:
|
if chunks_n:
|
||||||
await sor.sqlExe(
|
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})
|
{"n": chunks_n, "kb_id": kb_id})
|
||||||
except: pass
|
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:
|
async with get_sor_context(env, 'rag') as sor:
|
||||||
await sor.sqlExe(
|
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())",
|
"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,
|
{"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})
|
"file_size": file_size, "file_path": web_path, "org_id": userorgid})
|
||||||
await sor.sqlExe(
|
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})
|
{"size": file_size, "kb_id": kb_id})
|
||||||
|
|
||||||
# Fire background ingestion — pass primitives only (no env/request/proxy objects)
|
# Fire background ingestion — pass primitives only (no env/request/proxy objects)
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user