Upload 29 files
Browse files- .gitattributes +2 -0
- modules/agent_builder/__pycache__/routes.cpython-312.pyc +0 -0
- modules/code_executor/__pycache__/routes.cpython-312.pyc +0 -0
- modules/knowledge_base/__pycache__/generator.cpython-312.pyc +0 -0
- modules/knowledge_base/__pycache__/processor.cpython-312.pyc +0 -0
- modules/knowledge_base/__pycache__/reranker.cpython-312.pyc +0 -0
- modules/knowledge_base/__pycache__/retriever.cpython-312.pyc +0 -0
- modules/knowledge_base/__pycache__/routes.cpython-312.pyc +0 -0
- modules/knowledge_base/__pycache__/vector_store.cpython-312.pyc +0 -0
- modules/knowledge_base/processor.py +45 -16
- modules/knowledge_base/routes.py +50 -34
- modules/visualization/__pycache__/routes.cpython-312.pyc +0 -0
- templates/index.html +464 -275
- uploads/496449c167344601ba87c24743701b99.pdf +3 -0
- uploads/eb55dde866f64be4a1fdbc81d3d1df0e.pdf +3 -0
.gitattributes
CHANGED
@@ -33,3 +33,5 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
|
|
33 |
*.zip filter=lfs diff=lfs merge=lfs -text
|
34 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
|
|
|
|
|
33 |
*.zip filter=lfs diff=lfs merge=lfs -text
|
34 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
36 |
+
uploads/496449c167344601ba87c24743701b99.pdf filter=lfs diff=lfs merge=lfs -text
|
37 |
+
uploads/eb55dde866f64be4a1fdbc81d3d1df0e.pdf filter=lfs diff=lfs merge=lfs -text
|
modules/agent_builder/__pycache__/routes.cpython-312.pyc
ADDED
Binary file (18.8 kB). View file
|
|
modules/code_executor/__pycache__/routes.cpython-312.pyc
ADDED
Binary file (11.5 kB). View file
|
|
modules/knowledge_base/__pycache__/generator.cpython-312.pyc
ADDED
Binary file (17.4 kB). View file
|
|
modules/knowledge_base/__pycache__/processor.cpython-312.pyc
ADDED
Binary file (11 kB). View file
|
|
modules/knowledge_base/__pycache__/reranker.cpython-312.pyc
ADDED
Binary file (2.26 kB). View file
|
|
modules/knowledge_base/__pycache__/retriever.cpython-312.pyc
ADDED
Binary file (4.36 kB). View file
|
|
modules/knowledge_base/__pycache__/routes.cpython-312.pyc
ADDED
Binary file (11.6 kB). View file
|
|
modules/knowledge_base/__pycache__/vector_store.cpython-312.pyc
ADDED
Binary file (7.18 kB). View file
|
|
modules/knowledge_base/processor.py
CHANGED
@@ -14,9 +14,12 @@ import io
|
|
14 |
|
15 |
class DocumentLoader:
|
16 |
"""通用文档加载器"""
|
17 |
-
def __init__(self, file_path: str):
|
18 |
self.file_path = file_path
|
19 |
-
|
|
|
|
|
|
|
20 |
self.api_key = os.getenv("API_KEY")
|
21 |
self.api_base = os.getenv("BASE_URL")
|
22 |
|
@@ -74,15 +77,27 @@ class DocumentLoader:
|
|
74 |
|
75 |
def load(self):
|
76 |
try:
|
|
|
|
|
77 |
if self.extension == '.md':
|
78 |
-
|
79 |
-
|
|
|
|
|
|
|
|
|
|
|
80 |
elif self.extension == '.pdf':
|
81 |
loader = PyPDFLoader(self.file_path)
|
82 |
return loader.load()
|
83 |
elif self.extension == '.txt':
|
84 |
-
|
85 |
-
|
|
|
|
|
|
|
|
|
|
|
86 |
elif self.extension in ['.png', '.jpg', '.jpeg', '.gif', '.bmp']:
|
87 |
# 处理图片
|
88 |
description = self.process_image(self.file_path)
|
@@ -92,18 +107,29 @@ class DocumentLoader:
|
|
92 |
page_content=description,
|
93 |
metadata={
|
94 |
'source': self.file_path,
|
|
|
95 |
'img_url': os.path.abspath(self.file_path) # 存储图片的绝对路径
|
96 |
}
|
97 |
)
|
98 |
return [doc]
|
99 |
else:
|
|
|
100 |
raise ValueError(f"不支持的文件格式: {self.extension}")
|
101 |
|
102 |
except UnicodeDecodeError:
|
103 |
-
#
|
|
|
104 |
if self.extension in ['.md', '.txt']:
|
105 |
-
|
106 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
107 |
raise
|
108 |
|
109 |
class DocumentProcessor:
|
@@ -123,12 +149,13 @@ class DocumentProcessor:
|
|
123 |
# 如果是文件,使用文件名(不含扩展名)
|
124 |
return f"rag_{os.path.splitext(os.path.basename(path))[0].lower()}"
|
125 |
|
126 |
-
def process(self, path: str, progress_callback: Optional[Callable] = None) -> List[Dict]:
|
127 |
"""
|
128 |
加载并处理文档,支持目录或单个文件
|
129 |
参数:
|
130 |
path: 文档路径
|
131 |
progress_callback: 进度回调函数,用于报告处理进度
|
|
|
132 |
返回:处理后的文档列表
|
133 |
"""
|
134 |
if os.path.isdir(path):
|
@@ -148,11 +175,12 @@ class DocumentProcessor:
|
|
148 |
processed_files += 1
|
149 |
progress_callback(processed_size, f"处理文件 {processed_files}/{total_files}: {file}")
|
150 |
|
151 |
-
|
|
|
152 |
docs = loader.load()
|
153 |
# 添加文件名到metadata
|
154 |
for doc in docs:
|
155 |
-
doc.metadata['file_name'] =
|
156 |
documents.extend(docs)
|
157 |
except Exception as e:
|
158 |
print(f"警告:加载文件 {file_path} 时出错: {str(e)}")
|
@@ -161,17 +189,18 @@ class DocumentProcessor:
|
|
161 |
try:
|
162 |
if progress_callback:
|
163 |
file_size = os.path.getsize(path)
|
164 |
-
progress_callback(file_size * 0.3, f"加载文件: {os.path.basename(path)}")
|
165 |
|
166 |
-
|
|
|
167 |
documents = loader.load()
|
168 |
|
169 |
# 更新进度
|
170 |
if progress_callback:
|
171 |
progress_callback(file_size * 0.6, f"处理文件内容...")
|
172 |
|
173 |
-
#
|
174 |
-
file_name = os.path.basename(path)
|
175 |
for doc in documents:
|
176 |
doc.metadata['file_name'] = file_name
|
177 |
except Exception as e:
|
|
|
14 |
|
15 |
class DocumentLoader:
|
16 |
"""通用文档加载器"""
|
17 |
+
def __init__(self, file_path: str, original_filename: str = None):
|
18 |
self.file_path = file_path
|
19 |
+
# 使用传入的原始文件名或者从路径提取的文件名
|
20 |
+
self.original_filename = original_filename or os.path.basename(file_path)
|
21 |
+
# 从原始文件名中获取扩展名,确保中文文件名也能正确识别文件类型
|
22 |
+
self.extension = os.path.splitext(self.original_filename)[1].lower()
|
23 |
self.api_key = os.getenv("API_KEY")
|
24 |
self.api_base = os.getenv("BASE_URL")
|
25 |
|
|
|
77 |
|
78 |
def load(self):
|
79 |
try:
|
80 |
+
print(f"正在加载文件: {self.file_path}, 原始文件名: {self.original_filename}, 扩展名: {self.extension}")
|
81 |
+
|
82 |
if self.extension == '.md':
|
83 |
+
try:
|
84 |
+
loader = UnstructuredMarkdownLoader(self.file_path, encoding='utf-8')
|
85 |
+
return loader.load()
|
86 |
+
except UnicodeDecodeError:
|
87 |
+
# 如果UTF-8失败,尝试GBK
|
88 |
+
loader = UnstructuredMarkdownLoader(self.file_path, encoding='gbk')
|
89 |
+
return loader.load()
|
90 |
elif self.extension == '.pdf':
|
91 |
loader = PyPDFLoader(self.file_path)
|
92 |
return loader.load()
|
93 |
elif self.extension == '.txt':
|
94 |
+
try:
|
95 |
+
loader = TextLoader(self.file_path, encoding='utf-8')
|
96 |
+
return loader.load()
|
97 |
+
except UnicodeDecodeError:
|
98 |
+
# 如果UTF-8失败,尝试GBK
|
99 |
+
loader = TextLoader(self.file_path, encoding='gbk')
|
100 |
+
return loader.load()
|
101 |
elif self.extension in ['.png', '.jpg', '.jpeg', '.gif', '.bmp']:
|
102 |
# 处理图片
|
103 |
description = self.process_image(self.file_path)
|
|
|
107 |
page_content=description,
|
108 |
metadata={
|
109 |
'source': self.file_path,
|
110 |
+
'file_name': self.original_filename, # 使用原始文件名
|
111 |
'img_url': os.path.abspath(self.file_path) # 存储图片的绝对路径
|
112 |
}
|
113 |
)
|
114 |
return [doc]
|
115 |
else:
|
116 |
+
print(f"不支持的文件扩展名: {self.extension}")
|
117 |
raise ValueError(f"不支持的文件格式: {self.extension}")
|
118 |
|
119 |
except UnicodeDecodeError:
|
120 |
+
# 如果默认编码处理失败,尝试其他编码
|
121 |
+
print(f"文件编码错误,尝试其他编码: {self.file_path}")
|
122 |
if self.extension in ['.md', '.txt']:
|
123 |
+
try:
|
124 |
+
loader = TextLoader(self.file_path, encoding='gbk')
|
125 |
+
return loader.load()
|
126 |
+
except Exception as e:
|
127 |
+
print(f"尝试GBK编码也失败: {str(e)}")
|
128 |
+
raise
|
129 |
+
except Exception as e:
|
130 |
+
print(f"加载文件 {self.file_path} 时出错: {str(e)}")
|
131 |
+
import traceback
|
132 |
+
traceback.print_exc()
|
133 |
raise
|
134 |
|
135 |
class DocumentProcessor:
|
|
|
149 |
# 如果是文件,使用文件名(不含扩展名)
|
150 |
return f"rag_{os.path.splitext(os.path.basename(path))[0].lower()}"
|
151 |
|
152 |
+
def process(self, path: str, progress_callback: Optional[Callable] = None, original_filename: str = None) -> List[Dict]:
|
153 |
"""
|
154 |
加载并处理文档,支持目录或单个文件
|
155 |
参数:
|
156 |
path: 文档路径
|
157 |
progress_callback: 进度回调函数,用于报告处理进度
|
158 |
+
original_filename: 原始文件名(包括中文)
|
159 |
返回:处理后的文档列表
|
160 |
"""
|
161 |
if os.path.isdir(path):
|
|
|
175 |
processed_files += 1
|
176 |
progress_callback(processed_size, f"处理文件 {processed_files}/{total_files}: {file}")
|
177 |
|
178 |
+
# 为目录中的每个文件,传递原始文件名
|
179 |
+
loader = DocumentLoader(file_path, original_filename=file)
|
180 |
docs = loader.load()
|
181 |
# 添加文件名到metadata
|
182 |
for doc in docs:
|
183 |
+
doc.metadata['file_name'] = file # 使用原始文件名
|
184 |
documents.extend(docs)
|
185 |
except Exception as e:
|
186 |
print(f"警告:加载文件 {file_path} 时出错: {str(e)}")
|
|
|
189 |
try:
|
190 |
if progress_callback:
|
191 |
file_size = os.path.getsize(path)
|
192 |
+
progress_callback(file_size * 0.3, f"加载文件: {original_filename or os.path.basename(path)}")
|
193 |
|
194 |
+
# 为单个文件,传递原始文件名
|
195 |
+
loader = DocumentLoader(path, original_filename=original_filename)
|
196 |
documents = loader.load()
|
197 |
|
198 |
# 更新进度
|
199 |
if progress_callback:
|
200 |
progress_callback(file_size * 0.6, f"处理文件内容...")
|
201 |
|
202 |
+
# 使用原始文件名而不是存储的文件名
|
203 |
+
file_name = original_filename or os.path.basename(path)
|
204 |
for doc in documents:
|
205 |
doc.metadata['file_name'] = file_name
|
206 |
except Exception as e:
|
modules/knowledge_base/routes.py
CHANGED
@@ -60,12 +60,12 @@ def create_knowledge():
|
|
60 |
if not name:
|
61 |
return jsonify({"success": False, "message": "Knowledge base name cannot be empty"}), 400
|
62 |
|
63 |
-
#
|
64 |
indices = retriever.get_all_indices()
|
65 |
if f"rag_{name}" in indices:
|
66 |
return jsonify({"success": False, "message": f"Knowledge base '{name}' already exists"}), 400
|
67 |
|
68 |
-
#
|
69 |
if 'file' not in request.files:
|
70 |
return jsonify({"success": False, "message": "No file uploaded"}), 400
|
71 |
|
@@ -73,53 +73,62 @@ def create_knowledge():
|
|
73 |
if file.filename == '':
|
74 |
return jsonify({"success": False, "message": "No file selected"}), 400
|
75 |
|
76 |
-
#
|
77 |
-
|
78 |
-
|
|
|
|
|
|
|
|
|
79 |
file.save(file_path)
|
80 |
|
81 |
-
#
|
82 |
task_id = f"task_{int(time.time())}_{name}"
|
83 |
|
84 |
-
#
|
85 |
processing_tasks[task_id] = {
|
86 |
"progress": 0,
|
87 |
"status": "Starting document processing...",
|
88 |
"index_name": name,
|
89 |
"file_path": file_path,
|
|
|
90 |
"error": False,
|
91 |
"docCount": 0
|
92 |
}
|
93 |
|
94 |
-
#
|
95 |
def process_in_thread():
|
96 |
try:
|
97 |
-
#
|
98 |
processing_tasks[task_id]["progress"] = 10
|
99 |
processing_tasks[task_id]["status"] = "Loading document..."
|
100 |
|
101 |
-
#
|
102 |
def update_progress(progress, status):
|
103 |
processing_tasks[task_id]["progress"] = min(95, progress)
|
104 |
processing_tasks[task_id]["status"] = status
|
105 |
|
106 |
-
#
|
107 |
-
processed_docs = doc_processor.process(
|
|
|
|
|
|
|
|
|
108 |
|
109 |
-
#
|
110 |
processing_tasks[task_id]["progress"] = 95
|
111 |
processing_tasks[task_id]["status"] = "Creating vector store..."
|
112 |
processing_tasks[task_id]["docCount"] = len(processed_docs)
|
113 |
|
114 |
-
#
|
115 |
vector_store.store(processed_docs, f"rag_{name}")
|
116 |
|
117 |
-
#
|
118 |
processing_tasks[task_id]["progress"] = 100
|
119 |
processing_tasks[task_id]["status"] = "Processing complete"
|
120 |
|
121 |
except Exception as e:
|
122 |
-
#
|
123 |
processing_tasks[task_id]["error"] = True
|
124 |
processing_tasks[task_id]["status"] = f"Processing failed: {str(e)}"
|
125 |
import traceback
|
@@ -158,12 +167,12 @@ def get_progress(task_id):
|
|
158 |
def add_documents(index_id):
|
159 |
"""Add documents to a knowledge base"""
|
160 |
try:
|
161 |
-
#
|
162 |
indices = retriever.get_all_indices()
|
163 |
if index_id not in indices:
|
164 |
return jsonify({"success": False, "message": "Knowledge base does not exist"}), 404
|
165 |
|
166 |
-
#
|
167 |
if 'file' not in request.files:
|
168 |
return jsonify({"success": False, "message": "No file uploaded"}), 400
|
169 |
|
@@ -171,56 +180,63 @@ def add_documents(index_id):
|
|
171 |
if file.filename == '':
|
172 |
return jsonify({"success": False, "message": "No file selected"}), 400
|
173 |
|
174 |
-
#
|
175 |
-
|
176 |
-
|
|
|
|
|
177 |
file.save(file_path)
|
178 |
|
179 |
-
#
|
180 |
kb_name = index_id[4:] if index_id.startswith('rag_') else index_id
|
181 |
|
182 |
-
#
|
183 |
-
task_id = f"task_{int(time.time())}_{kb_name}_{
|
184 |
|
185 |
-
#
|
186 |
processing_tasks[task_id] = {
|
187 |
"progress": 0,
|
188 |
"status": "Starting document processing...",
|
189 |
"index_name": kb_name,
|
190 |
"file_path": file_path,
|
|
|
191 |
"error": False,
|
192 |
"docCount": 0
|
193 |
}
|
194 |
|
195 |
-
#
|
196 |
def process_in_thread():
|
197 |
try:
|
198 |
-
#
|
199 |
processing_tasks[task_id]["progress"] = 10
|
200 |
processing_tasks[task_id]["status"] = "Loading document..."
|
201 |
|
202 |
-
#
|
203 |
def update_progress(progress, status):
|
204 |
processing_tasks[task_id]["progress"] = min(95, progress)
|
205 |
processing_tasks[task_id]["status"] = status
|
206 |
|
207 |
-
#
|
208 |
-
processed_docs = doc_processor.process(
|
|
|
|
|
|
|
|
|
209 |
|
210 |
-
#
|
211 |
processing_tasks[task_id]["progress"] = 95
|
212 |
processing_tasks[task_id]["status"] = "Creating vector store..."
|
213 |
processing_tasks[task_id]["docCount"] = len(processed_docs)
|
214 |
|
215 |
-
#
|
216 |
vector_store.store(processed_docs, index_id)
|
217 |
|
218 |
-
#
|
219 |
processing_tasks[task_id]["progress"] = 100
|
220 |
processing_tasks[task_id]["status"] = "Processing complete"
|
221 |
|
222 |
except Exception as e:
|
223 |
-
#
|
224 |
processing_tasks[task_id]["error"] = True
|
225 |
processing_tasks[task_id]["status"] = f"Processing failed: {str(e)}"
|
226 |
import traceback
|
|
|
60 |
if not name:
|
61 |
return jsonify({"success": False, "message": "Knowledge base name cannot be empty"}), 400
|
62 |
|
63 |
+
# 检查知识库是否已存在
|
64 |
indices = retriever.get_all_indices()
|
65 |
if f"rag_{name}" in indices:
|
66 |
return jsonify({"success": False, "message": f"Knowledge base '{name}' already exists"}), 400
|
67 |
|
68 |
+
# 处理上传文件
|
69 |
if 'file' not in request.files:
|
70 |
return jsonify({"success": False, "message": "No file uploaded"}), 400
|
71 |
|
|
|
73 |
if file.filename == '':
|
74 |
return jsonify({"success": False, "message": "No file selected"}), 400
|
75 |
|
76 |
+
# 保存原始文件名
|
77 |
+
original_filename = file.filename
|
78 |
+
# 从原始文件名中提取扩展名,确保中文文件名也能正确识别文件类型
|
79 |
+
file_ext = os.path.splitext(original_filename)[1].lower()
|
80 |
+
# 使用UUID生成唯一文件名
|
81 |
+
unique_filename = f"{uuid.uuid4().hex}{file_ext}"
|
82 |
+
file_path = os.path.join(UPLOAD_FOLDER, unique_filename)
|
83 |
file.save(file_path)
|
84 |
|
85 |
+
# 创建任务ID
|
86 |
task_id = f"task_{int(time.time())}_{name}"
|
87 |
|
88 |
+
# 初始化任务状态
|
89 |
processing_tasks[task_id] = {
|
90 |
"progress": 0,
|
91 |
"status": "Starting document processing...",
|
92 |
"index_name": name,
|
93 |
"file_path": file_path,
|
94 |
+
"original_filename": original_filename, # 保存原始文件名
|
95 |
"error": False,
|
96 |
"docCount": 0
|
97 |
}
|
98 |
|
99 |
+
# 处理文档的线程函数
|
100 |
def process_in_thread():
|
101 |
try:
|
102 |
+
# 更新任务状态
|
103 |
processing_tasks[task_id]["progress"] = 10
|
104 |
processing_tasks[task_id]["status"] = "Loading document..."
|
105 |
|
106 |
+
# 处理文档进度回调
|
107 |
def update_progress(progress, status):
|
108 |
processing_tasks[task_id]["progress"] = min(95, progress)
|
109 |
processing_tasks[task_id]["status"] = status
|
110 |
|
111 |
+
# 处理文档,传递原始文件名
|
112 |
+
processed_docs = doc_processor.process(
|
113 |
+
file_path,
|
114 |
+
progress_callback=update_progress,
|
115 |
+
original_filename=original_filename # 传递原始文件名
|
116 |
+
)
|
117 |
|
118 |
+
# 更新任务状态
|
119 |
processing_tasks[task_id]["progress"] = 95
|
120 |
processing_tasks[task_id]["status"] = "Creating vector store..."
|
121 |
processing_tasks[task_id]["docCount"] = len(processed_docs)
|
122 |
|
123 |
+
# 存储向量
|
124 |
vector_store.store(processed_docs, f"rag_{name}")
|
125 |
|
126 |
+
# 完成任务
|
127 |
processing_tasks[task_id]["progress"] = 100
|
128 |
processing_tasks[task_id]["status"] = "Processing complete"
|
129 |
|
130 |
except Exception as e:
|
131 |
+
# 记录错误
|
132 |
processing_tasks[task_id]["error"] = True
|
133 |
processing_tasks[task_id]["status"] = f"Processing failed: {str(e)}"
|
134 |
import traceback
|
|
|
167 |
def add_documents(index_id):
|
168 |
"""Add documents to a knowledge base"""
|
169 |
try:
|
170 |
+
# 检查知识库是否存在
|
171 |
indices = retriever.get_all_indices()
|
172 |
if index_id not in indices:
|
173 |
return jsonify({"success": False, "message": "Knowledge base does not exist"}), 404
|
174 |
|
175 |
+
# 处理上传文件
|
176 |
if 'file' not in request.files:
|
177 |
return jsonify({"success": False, "message": "No file uploaded"}), 400
|
178 |
|
|
|
180 |
if file.filename == '':
|
181 |
return jsonify({"success": False, "message": "No file selected"}), 400
|
182 |
|
183 |
+
# 保存原始文件名并使用UUID生成唯一文件名
|
184 |
+
original_filename = file.filename
|
185 |
+
file_ext = os.path.splitext(original_filename)[1].lower()
|
186 |
+
unique_filename = f"{uuid.uuid4().hex}{file_ext}"
|
187 |
+
file_path = os.path.join(UPLOAD_FOLDER, unique_filename)
|
188 |
file.save(file_path)
|
189 |
|
190 |
+
# 提取知识库名称
|
191 |
kb_name = index_id[4:] if index_id.startswith('rag_') else index_id
|
192 |
|
193 |
+
# 创建任务ID
|
194 |
+
task_id = f"task_{int(time.time())}_{kb_name}_{uuid.uuid4().hex[:8]}"
|
195 |
|
196 |
+
# 初始化任务状态
|
197 |
processing_tasks[task_id] = {
|
198 |
"progress": 0,
|
199 |
"status": "Starting document processing...",
|
200 |
"index_name": kb_name,
|
201 |
"file_path": file_path,
|
202 |
+
"original_filename": original_filename, # 保存原始文件名
|
203 |
"error": False,
|
204 |
"docCount": 0
|
205 |
}
|
206 |
|
207 |
+
# 处理文档的线程函数
|
208 |
def process_in_thread():
|
209 |
try:
|
210 |
+
# 更新任务状态
|
211 |
processing_tasks[task_id]["progress"] = 10
|
212 |
processing_tasks[task_id]["status"] = "Loading document..."
|
213 |
|
214 |
+
# 处理文档进度回调
|
215 |
def update_progress(progress, status):
|
216 |
processing_tasks[task_id]["progress"] = min(95, progress)
|
217 |
processing_tasks[task_id]["status"] = status
|
218 |
|
219 |
+
# 处理文档,传递原始文件名
|
220 |
+
processed_docs = doc_processor.process(
|
221 |
+
file_path,
|
222 |
+
progress_callback=update_progress,
|
223 |
+
original_filename=original_filename # 传递原始文件名
|
224 |
+
)
|
225 |
|
226 |
+
# 更新任务状态
|
227 |
processing_tasks[task_id]["progress"] = 95
|
228 |
processing_tasks[task_id]["status"] = "Creating vector store..."
|
229 |
processing_tasks[task_id]["docCount"] = len(processed_docs)
|
230 |
|
231 |
+
# 存储向量
|
232 |
vector_store.store(processed_docs, index_id)
|
233 |
|
234 |
+
# 完成任务
|
235 |
processing_tasks[task_id]["progress"] = 100
|
236 |
processing_tasks[task_id]["status"] = "Processing complete"
|
237 |
|
238 |
except Exception as e:
|
239 |
+
# 记录错误
|
240 |
processing_tasks[task_id]["error"] = True
|
241 |
processing_tasks[task_id]["status"] = f"Processing failed: {str(e)}"
|
242 |
import traceback
|
modules/visualization/__pycache__/routes.cpython-312.pyc
ADDED
Binary file (8.64 kB). View file
|
|
templates/index.html
CHANGED
@@ -4,7 +4,7 @@
|
|
4 |
<head>
|
5 |
<meta charset="UTF-8">
|
6 |
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
7 |
-
|
8 |
<title>教育AI助手开发平台</title>
|
9 |
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css">
|
10 |
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/font/bootstrap-icons.css">
|
@@ -1653,249 +1653,327 @@
|
|
1653 |
}
|
1654 |
|
1655 |
// 加载知识库列表
|
1656 |
-
|
1657 |
-
|
1658 |
-
|
1659 |
-
|
|
|
|
|
|
|
1660 |
|
1661 |
-
|
1662 |
-
|
1663 |
-
|
1664 |
-
|
1665 |
-
|
1666 |
-
|
1667 |
-
|
1668 |
-
|
1669 |
-
|
1670 |
-
|
1671 |
-
|
1672 |
-
|
1673 |
-
|
1674 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1675 |
</div>
|
1676 |
-
<div class="
|
1677 |
-
<
|
1678 |
-
|
1679 |
-
|
|
|
|
|
|
|
|
|
1680 |
</div>
|
1681 |
`;
|
1682 |
-
} else {
|
1683 |
-
knowledgeListElement.innerHTML = '';
|
1684 |
|
1685 |
-
|
1686 |
-
|
1687 |
-
item.className = 'plugin-option';
|
1688 |
-
item.setAttribute('data-id', knowledge.id);
|
1689 |
-
item.setAttribute('onclick', 'toggleKnowledgeSelection(this)');
|
1690 |
-
|
1691 |
-
item.innerHTML = `
|
1692 |
-
<div class="plugin-icon">
|
1693 |
-
<i class="bi bi-journal-text"></i>
|
1694 |
-
</div>
|
1695 |
-
<div class="plugin-content">
|
1696 |
-
<div class="plugin-title">${knowledge.name}</div>
|
1697 |
-
<div class="plugin-description">
|
1698 |
-
<small>${knowledge.fileCount || 0}个文件</small>
|
1699 |
-
</div>
|
1700 |
-
</div>
|
1701 |
-
<div class="checkbox">
|
1702 |
-
<input type="checkbox" class="form-check-input">
|
1703 |
-
</div>
|
1704 |
-
`;
|
1705 |
-
|
1706 |
-
knowledgeListElement.appendChild(item);
|
1707 |
-
});
|
1708 |
-
}
|
1709 |
}
|
1710 |
-
|
1711 |
-
|
1712 |
-
|
1713 |
-
|
1714 |
-
|
1715 |
-
|
1716 |
-
|
1717 |
-
|
1718 |
-
|
1719 |
-
|
1720 |
-
|
1721 |
-
|
1722 |
-
|
|
|
|
|
|
|
|
|
|
|
1723 |
|
1724 |
-
|
1725 |
-
|
1726 |
-
|
1727 |
-
|
1728 |
-
|
1729 |
-
|
1730 |
-
|
1731 |
-
|
1732 |
-
|
1733 |
-
|
1734 |
-
|
1735 |
-
|
1736 |
-
|
1737 |
-
|
1738 |
-
|
1739 |
-
|
1740 |
-
|
1741 |
-
|
1742 |
-
|
1743 |
-
|
1744 |
-
|
1745 |
-
|
1746 |
-
|
1747 |
-
<div class="knowledge-file-name">${file}</div>
|
1748 |
-
</div>
|
1749 |
-
<button class="btn btn-sm btn-icon btn-light" onclick="downloadFile('${knowledge.id}', '${file}')">
|
1750 |
-
<i class="bi bi-download"></i>
|
1751 |
-
</button>
|
1752 |
</div>
|
1753 |
-
|
1754 |
-
});
|
1755 |
-
fileListHtml += '</div>';
|
1756 |
-
}
|
1757 |
-
|
1758 |
-
item.innerHTML = `
|
1759 |
-
<div class="knowledge-header">
|
1760 |
-
<h5 class="knowledge-title">
|
1761 |
-
<i class="bi bi-journal-text"></i> ${knowledge.name}
|
1762 |
-
</h5>
|
1763 |
-
<div>
|
1764 |
-
<button class="btn btn-sm btn-light me-1" onclick="editKnowledgeBase('${knowledge.id}')">
|
1765 |
-
<i class="bi bi-pencil"></i>
|
1766 |
-
</button>
|
1767 |
-
<button class="btn btn-sm btn-light text-danger" onclick="deleteKnowledgeBase('${knowledge.id}')">
|
1768 |
<i class="bi bi-trash"></i>
|
1769 |
</button>
|
1770 |
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1771 |
</div>
|
1772 |
-
|
1773 |
-
|
1774 |
-
|
1775 |
-
|
1776 |
-
|
1777 |
-
|
1778 |
-
<div class="knowledge-meta-item">
|
1779 |
-
<i class="bi bi-calendar3"></i>
|
1780 |
-
<span>创建于 ${new Date(knowledge.created_at * 1000).toLocaleDateString()}</span>
|
1781 |
-
</div>
|
1782 |
</div>
|
1783 |
-
<div class="
|
1784 |
-
<
|
1785 |
-
|
1786 |
-
<input type="file" class="form-control form-control-sm" required>
|
1787 |
-
<button type="submit" class="btn btn-sm btn-outline-primary">添加文件</button>
|
1788 |
-
</div>
|
1789 |
-
</form>
|
1790 |
</div>
|
1791 |
-
|
1792 |
-
|
1793 |
-
|
1794 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
1795 |
|
1796 |
-
|
1797 |
-
|
1798 |
-
|
1799 |
-
|
1800 |
-
|
1801 |
-
|
|
|
|
|
1802 |
});
|
1803 |
-
}
|
1804 |
}
|
1805 |
-
} else {
|
1806 |
-
console.error('加载知识库失败:', result.message);
|
1807 |
}
|
1808 |
-
}
|
1809 |
-
console.error('
|
1810 |
}
|
|
|
|
|
1811 |
}
|
|
|
1812 |
|
1813 |
// 创建知识库
|
1814 |
-
|
1815 |
-
|
1816 |
-
|
1817 |
-
|
1818 |
-
|
1819 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1820 |
|
1821 |
-
|
1822 |
-
|
1823 |
-
|
1824 |
-
|
|
|
1825 |
|
1826 |
-
|
1827 |
-
progressBar.style.display = 'block';
|
1828 |
-
progressBar.querySelector('.progress-bar').style.width = '0%';
|
1829 |
|
1830 |
-
const
|
1831 |
-
formData.append('name', nameInput.value);
|
1832 |
-
formData.append('file', fileInput.files[0]);
|
1833 |
|
1834 |
-
|
1835 |
-
|
1836 |
-
|
1837 |
-
|
1838 |
-
|
1839 |
-
|
1840 |
-
const result = await response.json();
|
1841 |
-
|
1842 |
-
if (result.success) {
|
1843 |
-
// 成功启动处理,轮询进度
|
1844 |
-
pollProgress(result.task_id, progressBar);
|
1845 |
-
} else {
|
1846 |
-
alert('创建知识库失败: ' + result.message);
|
1847 |
-
progressBar.style.display = 'none';
|
1848 |
-
}
|
1849 |
-
} catch (error) {
|
1850 |
-
console.error('创建知识库出错:', error);
|
1851 |
-
alert('创建知识库出错,请查看控制台日志');
|
1852 |
progressBar.style.display = 'none';
|
|
|
|
|
1853 |
}
|
1854 |
-
}
|
1855 |
-
|
1856 |
-
|
1857 |
-
|
1858 |
-
|
1859 |
-
|
1860 |
-
|
1861 |
-
|
1862 |
-
if (!fileInput.files[0]) {
|
1863 |
-
alert('请选择文件');
|
1864 |
-
return;
|
1865 |
}
|
1866 |
|
1867 |
-
|
1868 |
-
|
1869 |
-
submitBtn.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1870 |
|
1871 |
-
|
1872 |
-
formData.append('file', fileInput.files[0]);
|
1873 |
|
1874 |
-
|
1875 |
-
|
1876 |
-
|
1877 |
-
|
1878 |
-
|
1879 |
-
|
1880 |
-
|
1881 |
-
|
1882 |
-
if (result.success) {
|
1883 |
-
alert('文件上传成功,开始处理');
|
1884 |
-
// 轮询处理进度
|
1885 |
-
pollProgressSimple(result.task_id, submitBtn);
|
1886 |
-
} else {
|
1887 |
-
alert('添加文件失败: ' + result.message);
|
1888 |
-
submitBtn.disabled = false;
|
1889 |
-
submitBtn.textContent = '添加文件';
|
1890 |
-
}
|
1891 |
-
} catch (error) {
|
1892 |
-
console.error('添加文件出错:', error);
|
1893 |
-
alert('添加文件出错,请查看控制台日志');
|
1894 |
submitBtn.disabled = false;
|
1895 |
submitBtn.textContent = '添加文件';
|
1896 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1897 |
}
|
1898 |
-
|
1899 |
// 删除知识库
|
1900 |
async function deleteKnowledgeBase(knowledgeId) {
|
1901 |
if (!confirm('确定要删除这个知识库吗?此操作不可恢复。')) {
|
@@ -1920,85 +1998,196 @@
|
|
1920 |
alert('删除知识库出错,请查看控制台日志');
|
1921 |
}
|
1922 |
}
|
1923 |
-
|
1924 |
-
|
1925 |
-
|
1926 |
-
|
1927 |
-
|
1928 |
-
|
1929 |
-
|
1930 |
-
|
1931 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1932 |
|
1933 |
-
|
1934 |
-
|
1935 |
-
|
1936 |
-
|
1937 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1938 |
|
1939 |
-
|
1940 |
-
|
1941 |
-
|
1942 |
-
|
1943 |
-
if (progressData.error) {
|
1944 |
-
alert('处理失败: ' + progressData.status);
|
1945 |
-
} else {
|
1946 |
-
alert(`知识库处理成功! 已处理${progressData.docCount}个文档片段`);
|
1947 |
-
}
|
1948 |
-
|
1949 |
-
// 隐藏进度条
|
1950 |
-
progressElement.style.display = 'none';
|
1951 |
-
|
1952 |
-
// 重置表单
|
1953 |
-
document.getElementById('knowledge-name').value = '';
|
1954 |
-
document.getElementById('knowledge-file').value = '';
|
1955 |
-
|
1956 |
-
// 重新加载知识库列表
|
1957 |
-
loadKnowledgeBases();
|
1958 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1959 |
}
|
1960 |
-
}
|
1961 |
-
|
1962 |
}
|
1963 |
-
|
1964 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1965 |
|
1966 |
-
|
1967 |
-
|
1968 |
-
|
1969 |
-
|
1970 |
-
|
1971 |
-
|
1972 |
-
|
1973 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1974 |
|
1975 |
-
|
1976 |
-
|
|
|
|
|
|
|
|
|
1977 |
|
1978 |
-
|
1979 |
-
|
1980 |
-
|
1981 |
-
|
1982 |
-
if (progressData.error) {
|
1983 |
-
alert('处理失败: ' + progressData.status);
|
1984 |
-
} else {
|
1985 |
-
alert(`处理成功! 已处理${progressData.docCount}个文档片段`);
|
1986 |
-
}
|
1987 |
-
|
1988 |
-
// 恢复按钮
|
1989 |
-
buttonElement.disabled = false;
|
1990 |
-
buttonElement.textContent = originalText;
|
1991 |
-
|
1992 |
-
// 重新加载知识库列表
|
1993 |
-
loadKnowledgeBases();
|
1994 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1995 |
}
|
1996 |
-
}
|
1997 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1998 |
}
|
1999 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2000 |
}
|
2001 |
-
|
2002 |
// 使用AI生成工作流
|
2003 |
async function generateAIWorkflow() {
|
2004 |
const description = document.getElementById('agent-description').value;
|
|
|
4 |
<head>
|
5 |
<meta charset="UTF-8">
|
6 |
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
7 |
+
|
8 |
<title>教育AI助手开发平台</title>
|
9 |
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css">
|
10 |
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/font/bootstrap-icons.css">
|
|
|
1653 |
}
|
1654 |
|
1655 |
// 加载知识库列表
|
1656 |
+
async function loadKnowledgeBases() {
|
1657 |
+
try {
|
1658 |
+
const response = await fetch('/api/knowledge');
|
1659 |
+
const result = await response.json();
|
1660 |
+
|
1661 |
+
if (result.success) {
|
1662 |
+
allKnowledgeBases = result.data || [];
|
1663 |
|
1664 |
+
// 更新仪表盘计数
|
1665 |
+
document.getElementById('knowledge-count').textContent = allKnowledgeBases.length;
|
1666 |
+
|
1667 |
+
// 更新Agent创建页面的知识库列表
|
1668 |
+
const knowledgeListElement = document.getElementById('knowledge-list');
|
1669 |
+
if (knowledgeListElement) {
|
1670 |
+
if (allKnowledgeBases.length === 0) {
|
1671 |
+
knowledgeListElement.innerHTML = `
|
1672 |
+
<div class="alert alert-info">
|
1673 |
+
<i class="bi bi-info-circle me-2"></i>
|
1674 |
+
暂无知识库,请先创建知识库
|
1675 |
+
</div>
|
1676 |
+
<div class="text-center">
|
1677 |
+
<button class="btn btn-primary" onclick="switchSection('knowledge-base')">
|
1678 |
+
创建知识库
|
1679 |
+
</button>
|
1680 |
+
</div>
|
1681 |
+
`;
|
1682 |
+
} else {
|
1683 |
+
knowledgeListElement.innerHTML = '';
|
1684 |
+
|
1685 |
+
allKnowledgeBases.forEach(knowledge => {
|
1686 |
+
const item = document.createElement('div');
|
1687 |
+
item.className = 'plugin-option';
|
1688 |
+
item.setAttribute('data-id', knowledge.id);
|
1689 |
+
item.setAttribute('onclick', 'toggleKnowledgeSelection(this)');
|
1690 |
+
|
1691 |
+
item.innerHTML = `
|
1692 |
+
<div class="plugin-icon">
|
1693 |
+
<i class="bi bi-journal-text"></i>
|
1694 |
</div>
|
1695 |
+
<div class="plugin-content">
|
1696 |
+
<div class="plugin-title">${knowledge.name}</div>
|
1697 |
+
<div class="plugin-description">
|
1698 |
+
<small>${knowledge.fileCount || 0}个文件</small>
|
1699 |
+
</div>
|
1700 |
+
</div>
|
1701 |
+
<div class="checkbox">
|
1702 |
+
<input type="checkbox" class="form-check-input">
|
1703 |
</div>
|
1704 |
`;
|
|
|
|
|
1705 |
|
1706 |
+
knowledgeListElement.appendChild(item);
|
1707 |
+
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1708 |
}
|
1709 |
+
}
|
1710 |
+
|
1711 |
+
// 更新知识库管理页面的列表
|
1712 |
+
const existingKnowledgeList = document.getElementById('existing-knowledge-list');
|
1713 |
+
if (existingKnowledgeList) {
|
1714 |
+
if (allKnowledgeBases.length === 0) {
|
1715 |
+
existingKnowledgeList.innerHTML = `
|
1716 |
+
<div class="alert alert-info">
|
1717 |
+
<i class="bi bi-info-circle me-2"></i>
|
1718 |
+
暂无知识库,请创建第一个知识库
|
1719 |
+
</div>
|
1720 |
+
`;
|
1721 |
+
} else {
|
1722 |
+
existingKnowledgeList.innerHTML = '';
|
1723 |
+
|
1724 |
+
allKnowledgeBases.forEach(knowledge => {
|
1725 |
+
const item = document.createElement('div');
|
1726 |
+
item.className = 'knowledge-item';
|
1727 |
|
1728 |
+
let fileListHtml = '';
|
1729 |
+
if (knowledge.files && knowledge.files.length > 0) {
|
1730 |
+
fileListHtml = '<div class="knowledge-files mt-3">';
|
1731 |
+
knowledge.files.forEach(file => {
|
1732 |
+
// 使用文件名来判断文件类型
|
1733 |
+
const isImage = /\.(jpg|jpeg|png|gif|bmp)$/i.test(file);
|
1734 |
+
const isPdf = /\.pdf$/i.test(file);
|
1735 |
+
const isDoc = /\.(doc|docx)$/i.test(file);
|
1736 |
+
const isTxt = /\.txt$/i.test(file);
|
1737 |
+
const isMd = /\.md$/i.test(file);
|
1738 |
+
|
1739 |
+
let iconClass = 'bi-file-text';
|
1740 |
+
if (isImage) iconClass = 'bi-file-image';
|
1741 |
+
if (isPdf) iconClass = 'bi-file-pdf';
|
1742 |
+
if (isDoc) iconClass = 'bi-file-earmark-word';
|
1743 |
+
if (isTxt) iconClass = 'bi-file-text';
|
1744 |
+
if (isMd) iconClass = 'bi-file-text';
|
1745 |
+
|
1746 |
+
fileListHtml += `
|
1747 |
+
<div class="d-flex align-items-center p-2 border-bottom">
|
1748 |
+
<i class="bi ${iconClass} me-2" style="font-size: 1.2rem; color: var(--secondary-color);"></i>
|
1749 |
+
<div class="flex-grow-1 text-truncate" title="${file}">
|
1750 |
+
${file}
|
|
|
|
|
|
|
|
|
|
|
1751 |
</div>
|
1752 |
+
<button class="btn btn-sm btn-icon btn-light" onclick="deleteFile('${knowledge.id}', '${encodeURIComponent(file)}')">
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1753 |
<i class="bi bi-trash"></i>
|
1754 |
</button>
|
1755 |
</div>
|
1756 |
+
`;
|
1757 |
+
});
|
1758 |
+
fileListHtml += '</div>';
|
1759 |
+
}
|
1760 |
+
|
1761 |
+
item.innerHTML = `
|
1762 |
+
<div class="knowledge-header">
|
1763 |
+
<h5 class="knowledge-title">
|
1764 |
+
<i class="bi bi-journal-text"></i> ${knowledge.name}
|
1765 |
+
</h5>
|
1766 |
+
<div>
|
1767 |
+
<button class="btn btn-sm btn-light me-1" onclick="editKnowledgeBase('${knowledge.id}')">
|
1768 |
+
<i class="bi bi-pencil"></i>
|
1769 |
+
</button>
|
1770 |
+
<button class="btn btn-sm btn-light text-danger" onclick="deleteKnowledgeBase('${knowledge.id}')">
|
1771 |
+
<i class="bi bi-trash"></i>
|
1772 |
+
</button>
|
1773 |
</div>
|
1774 |
+
</div>
|
1775 |
+
${fileListHtml}
|
1776 |
+
<div class="knowledge-meta">
|
1777 |
+
<div class="knowledge-meta-item">
|
1778 |
+
<i class="bi bi-file-text"></i>
|
1779 |
+
<span>${knowledge.fileCount || 0}个文件</span>
|
|
|
|
|
|
|
|
|
1780 |
</div>
|
1781 |
+
<div class="knowledge-meta-item">
|
1782 |
+
<i class="bi bi-calendar3"></i>
|
1783 |
+
<span>创建于 ${new Date(knowledge.created_at * 1000).toLocaleDateString()}</span>
|
|
|
|
|
|
|
|
|
1784 |
</div>
|
1785 |
+
</div>
|
1786 |
+
<div class="mt-3">
|
1787 |
+
<form class="upload-form" data-id="${knowledge.id}">
|
1788 |
+
<div class="input-group">
|
1789 |
+
<input type="file" class="form-control form-control-sm" required>
|
1790 |
+
<button type="submit" class="btn btn-sm btn-outline-primary">添加文件</button>
|
1791 |
+
</div>
|
1792 |
+
</form>
|
1793 |
+
</div>
|
1794 |
+
`;
|
1795 |
|
1796 |
+
existingKnowledgeList.appendChild(item);
|
1797 |
+
});
|
1798 |
+
|
1799 |
+
// 添加上传表单事件监听
|
1800 |
+
document.querySelectorAll('.upload-form').forEach(form => {
|
1801 |
+
form.addEventListener('submit', function(event) {
|
1802 |
+
event.preventDefault();
|
1803 |
+
addFileToKnowledgeBase(form);
|
1804 |
});
|
1805 |
+
});
|
1806 |
}
|
|
|
|
|
1807 |
}
|
1808 |
+
} else {
|
1809 |
+
console.error('加载知识库失败:', result.message);
|
1810 |
}
|
1811 |
+
} catch (error) {
|
1812 |
+
console.error('加载知识库出错:', error);
|
1813 |
}
|
1814 |
+
}
|
1815 |
|
1816 |
// 创建知识库
|
1817 |
+
async function createKnowledgeBase(event) {
|
1818 |
+
event.preventDefault();
|
1819 |
+
|
1820 |
+
const nameInput = document.getElementById('knowledge-name');
|
1821 |
+
const fileInput = document.getElementById('knowledge-file');
|
1822 |
+
const progressBar = document.getElementById('upload-progress');
|
1823 |
+
const submitBtn = event.submitter || document.querySelector('#knowledge-form button[type="submit"]');
|
1824 |
+
|
1825 |
+
if (!nameInput.value || !fileInput.files[0]) {
|
1826 |
+
alert('请填写知识库名称并选择文件');
|
1827 |
+
return;
|
1828 |
+
}
|
1829 |
+
|
1830 |
+
// 获取文件扩展名
|
1831 |
+
const fileName = fileInput.files[0].name;
|
1832 |
+
const fileExt = fileName.substring(fileName.lastIndexOf('.')).toLowerCase();
|
1833 |
+
|
1834 |
+
// 检查文件类型
|
1835 |
+
const supportedExts = ['.txt', '.pdf', '.md', '.jpg', '.jpeg', '.png', '.gif', '.bmp'];
|
1836 |
+
if (!supportedExts.includes(fileExt)) {
|
1837 |
+
alert(`不支持的文件类型: ${fileExt}。\n支持的文件类型: ${supportedExts.join(', ')}`);
|
1838 |
+
return;
|
1839 |
+
}
|
1840 |
+
|
1841 |
+
// 检查文件大小
|
1842 |
+
const fileSize = fileInput.files[0].size;
|
1843 |
+
const fileSizeMB = fileSize / (1024 * 1024);
|
1844 |
+
const maxFileSizeMB = 16; // 最大16MB
|
1845 |
+
|
1846 |
+
if (fileSizeMB > maxFileSizeMB) {
|
1847 |
+
alert(`文件大小(${fileSizeMB.toFixed(2)}MB)超过最大限制(${maxFileSizeMB}MB)`);
|
1848 |
+
return;
|
1849 |
+
}
|
1850 |
+
|
1851 |
+
// 显示进度条并禁用提交按钮
|
1852 |
+
progressBar.style.display = 'block';
|
1853 |
+
progressBar.querySelector('.progress-bar').style.width = '0%';
|
1854 |
+
submitBtn.disabled = true;
|
1855 |
+
submitBtn.innerHTML = '<span class="spinner-border spinner-border-sm" role="status" aria-hidden="true"></span> 上传中...';
|
1856 |
+
|
1857 |
+
const formData = new FormData();
|
1858 |
+
formData.append('name', nameInput.value);
|
1859 |
+
formData.append('file', fileInput.files[0]);
|
1860 |
+
|
1861 |
+
try {
|
1862 |
+
// 使用 fetch 的 timeout 设置
|
1863 |
+
const controller = new AbortController();
|
1864 |
+
const timeoutId = setTimeout(() => controller.abort(), 60000); // 60秒超时
|
1865 |
|
1866 |
+
const response = await fetch('/api/knowledge', {
|
1867 |
+
method: 'POST',
|
1868 |
+
body: formData,
|
1869 |
+
signal: controller.signal
|
1870 |
+
});
|
1871 |
|
1872 |
+
clearTimeout(timeoutId);
|
|
|
|
|
1873 |
|
1874 |
+
const result = await response.json();
|
|
|
|
|
1875 |
|
1876 |
+
if (result.success) {
|
1877 |
+
// 成功启动处理,轮询进度
|
1878 |
+
pollProgress(result.task_id, progressBar, submitBtn);
|
1879 |
+
} else {
|
1880 |
+
alert('创建知识库失败: ' + result.message);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1881 |
progressBar.style.display = 'none';
|
1882 |
+
submitBtn.disabled = false;
|
1883 |
+
submitBtn.textContent = '创建知识库';
|
1884 |
}
|
1885 |
+
} catch (error) {
|
1886 |
+
console.error('创建知识库出错:', error);
|
1887 |
+
|
1888 |
+
// 判断是否是超时错误
|
1889 |
+
let errorMessage = '创建知识库出错,请查看控制台日志';
|
1890 |
+
if (error.name === 'AbortError') {
|
1891 |
+
errorMessage = '上传请求超时,请检查网络连接或尝试上传较小的文件';
|
|
|
|
|
|
|
|
|
1892 |
}
|
1893 |
|
1894 |
+
alert(errorMessage);
|
1895 |
+
progressBar.style.display = 'none';
|
1896 |
+
submitBtn.disabled = false;
|
1897 |
+
submitBtn.textContent = '创建知识库';
|
1898 |
+
}
|
1899 |
+
}
|
1900 |
+
// 添加文件到知识库
|
1901 |
+
async function addFileToKnowledgeBase(form) {
|
1902 |
+
const knowledgeId = form.getAttribute('data-id');
|
1903 |
+
const fileInput = form.querySelector('input[type="file"]');
|
1904 |
+
const submitBtn = form.querySelector('button[type="submit"]');
|
1905 |
+
|
1906 |
+
if (!fileInput.files[0]) {
|
1907 |
+
alert('请选择文件');
|
1908 |
+
return;
|
1909 |
+
}
|
1910 |
+
|
1911 |
+
// 获取文件扩展名
|
1912 |
+
const fileName = fileInput.files[0].name;
|
1913 |
+
const fileExt = fileName.substring(fileName.lastIndexOf('.')).toLowerCase();
|
1914 |
+
|
1915 |
+
// 检查文件类型
|
1916 |
+
const supportedExts = ['.txt', '.pdf', '.md', '.jpg', '.jpeg', '.png', '.gif', '.bmp'];
|
1917 |
+
if (!supportedExts.includes(fileExt)) {
|
1918 |
+
alert(`不支持的文件类型: ${fileExt}。\n支持的文件类型: ${supportedExts.join(', ')}`);
|
1919 |
+
return;
|
1920 |
+
}
|
1921 |
+
|
1922 |
+
// 检查文件大小
|
1923 |
+
const fileSize = fileInput.files[0].size;
|
1924 |
+
const fileSizeMB = fileSize / (1024 * 1024);
|
1925 |
+
const maxFileSizeMB = 16; // 最大16MB
|
1926 |
+
|
1927 |
+
if (fileSizeMB > maxFileSizeMB) {
|
1928 |
+
alert(`文件大小(${fileSizeMB.toFixed(2)}MB)超过最大限制(${maxFileSizeMB}MB)`);
|
1929 |
+
return;
|
1930 |
+
}
|
1931 |
+
|
1932 |
+
// 禁用提交按钮
|
1933 |
+
submitBtn.disabled = true;
|
1934 |
+
submitBtn.innerHTML = '<span class="spinner-border spinner-border-sm" role="status" aria-hidden="true"></span> 上传中...';
|
1935 |
+
|
1936 |
+
const formData = new FormData();
|
1937 |
+
formData.append('file', fileInput.files[0]);
|
1938 |
+
|
1939 |
+
try {
|
1940 |
+
// 使用 fetch 的 timeout 设置
|
1941 |
+
const controller = new AbortController();
|
1942 |
+
const timeoutId = setTimeout(() => controller.abort(), 60000); // 60秒超时
|
1943 |
+
|
1944 |
+
const response = await fetch(`/api/knowledge/${knowledgeId}/documents`, {
|
1945 |
+
method: 'POST',
|
1946 |
+
body: formData,
|
1947 |
+
signal: controller.signal
|
1948 |
+
});
|
1949 |
|
1950 |
+
clearTimeout(timeoutId);
|
|
|
1951 |
|
1952 |
+
const result = await response.json();
|
1953 |
+
|
1954 |
+
if (result.success) {
|
1955 |
+
alert('文件上传成功,开始处理');
|
1956 |
+
// 轮询处理进度
|
1957 |
+
pollProgressSimple(result.task_id, submitBtn);
|
1958 |
+
} else {
|
1959 |
+
alert('添加文件失败: ' + result.message);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1960 |
submitBtn.disabled = false;
|
1961 |
submitBtn.textContent = '添加文件';
|
1962 |
}
|
1963 |
+
} catch (error) {
|
1964 |
+
console.error('添加文件出错:', error);
|
1965 |
+
|
1966 |
+
// 判断是否是超时错误
|
1967 |
+
let errorMessage = '添加文件出错,请查看控制台日志';
|
1968 |
+
if (error.name === 'AbortError') {
|
1969 |
+
errorMessage = '上传请求超时,请检查网络连接或尝试上传较小的文件';
|
1970 |
+
}
|
1971 |
+
|
1972 |
+
alert(errorMessage);
|
1973 |
+
submitBtn.disabled = false;
|
1974 |
+
submitBtn.textContent = '添加文件';
|
1975 |
}
|
1976 |
+
}
|
1977 |
// 删除知识库
|
1978 |
async function deleteKnowledgeBase(knowledgeId) {
|
1979 |
if (!confirm('确定要删除这个知识库吗?此操作不可恢复。')) {
|
|
|
1998 |
alert('删除知识库出错,请查看控制台日志');
|
1999 |
}
|
2000 |
}
|
2001 |
+
// 删除知识库中的单个文件
|
2002 |
+
async function deleteFile(indexId, encodedFileName) {
|
2003 |
+
try {
|
2004 |
+
// 解码文件名以在确认对话框中显示
|
2005 |
+
const fileName = decodeURIComponent(encodedFileName);
|
2006 |
+
|
2007 |
+
if (!confirm(`确定要删除文件 "${fileName}" 吗?此操作不可恢复。`)) {
|
2008 |
+
return;
|
2009 |
+
}
|
2010 |
+
|
2011 |
+
const response = await fetch(`/api/knowledge/${indexId}/documents/${encodedFileName}`, {
|
2012 |
+
method: 'DELETE'
|
2013 |
+
});
|
2014 |
+
|
2015 |
+
const result = await response.json();
|
2016 |
+
|
2017 |
+
if (result.success) {
|
2018 |
+
alert('文件删除成功!');
|
2019 |
+
loadKnowledgeBases(); // 重新加载知识库列表
|
2020 |
+
} else {
|
2021 |
+
alert('删除文件失败: ' + result.message);
|
2022 |
+
}
|
2023 |
+
} catch (error) {
|
2024 |
+
console.error('删除文件出错:', error);
|
2025 |
+
alert('删除文件出错,请查看控制台日志');
|
2026 |
+
}
|
2027 |
+
}
|
2028 |
+
// 轮询进度(带进度条显示)
|
2029 |
+
function pollProgress(taskId, progressElement, submitBtn) {
|
2030 |
+
const progressBar = progressElement.querySelector('.progress-bar');
|
2031 |
+
let retryCount = 0;
|
2032 |
+
const maxRetries = 3;
|
2033 |
+
|
2034 |
+
const interval = setInterval(async () => {
|
2035 |
+
try {
|
2036 |
+
const response = await fetch(`/api/progress/${taskId}`);
|
2037 |
+
|
2038 |
+
if (!response.ok) {
|
2039 |
+
throw new Error(`HTTP错误: ${response.status}`);
|
2040 |
+
}
|
2041 |
+
|
2042 |
+
const data = await response.json();
|
2043 |
+
|
2044 |
+
if (data.success) {
|
2045 |
+
const progressData = data.data;
|
2046 |
|
2047 |
+
// 更新进度条
|
2048 |
+
progressBar.style.width = progressData.progress + '%';
|
2049 |
+
progressBar.setAttribute('aria-valuenow', progressData.progress);
|
2050 |
+
|
2051 |
+
// 更新进度状态
|
2052 |
+
if (submitBtn.tagName === 'BUTTON') {
|
2053 |
+
submitBtn.innerHTML = `<i class="bi bi-arrow-repeat spin me-1"></i> ${progressData.status || '处理中...'}`;
|
2054 |
+
}
|
2055 |
+
|
2056 |
+
// 处理完成或出错时
|
2057 |
+
if (progressData.progress >= 100 || progressData.error) {
|
2058 |
+
clearInterval(interval);
|
2059 |
|
2060 |
+
if (progressData.error) {
|
2061 |
+
alert('处理失败: ' + progressData.status);
|
2062 |
+
} else {
|
2063 |
+
alert(`知识库处理成功! 已处理${progressData.docCount}个文档片段`);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2064 |
}
|
2065 |
+
|
2066 |
+
// 隐藏进度条
|
2067 |
+
progressElement.style.display = 'none';
|
2068 |
+
|
2069 |
+
// 恢复按钮状态
|
2070 |
+
submitBtn.disabled = false;
|
2071 |
+
submitBtn.innerHTML = '创建知识库';
|
2072 |
+
|
2073 |
+
// 重置表单
|
2074 |
+
document.getElementById('knowledge-name').value = '';
|
2075 |
+
document.getElementById('knowledge-file').value = '';
|
2076 |
+
|
2077 |
+
// 重新加载知识库列表
|
2078 |
+
loadKnowledgeBases();
|
2079 |
}
|
2080 |
+
} else {
|
2081 |
+
throw new Error('轮询返回错误状态');
|
2082 |
}
|
2083 |
+
|
2084 |
+
// 重置重试计数
|
2085 |
+
retryCount = 0;
|
2086 |
+
|
2087 |
+
} catch (error) {
|
2088 |
+
console.error('获取进度信息出错:', error);
|
2089 |
+
retryCount++;
|
2090 |
+
|
2091 |
+
if (retryCount >= maxRetries) {
|
2092 |
+
clearInterval(interval);
|
2093 |
+
alert('获取进度信息失败,请检查控制台日志');
|
2094 |
+
|
2095 |
+
// 恢复按钮状态
|
2096 |
+
submitBtn.disabled = false;
|
2097 |
+
submitBtn.innerHTML = '创建知识库';
|
2098 |
+
progressElement.style.display = 'none';
|
2099 |
+
}
|
2100 |
+
}
|
2101 |
+
}, 1000); // 每秒轮询一次
|
2102 |
+
}
|
2103 |
|
2104 |
+
// 简化版轮询进度(不显示进度条)
|
2105 |
+
function pollProgressSimple(taskId, buttonElement) {
|
2106 |
+
const originalText = buttonElement.textContent;
|
2107 |
+
let retryCount = 0;
|
2108 |
+
const maxRetries = 3;
|
2109 |
+
|
2110 |
+
buttonElement.innerHTML = '<i class="bi bi-arrow-repeat spin me-1"></i> 处理中...';
|
2111 |
+
|
2112 |
+
const interval = setInterval(async () => {
|
2113 |
+
try {
|
2114 |
+
const response = await fetch(`/api/progress/${taskId}`);
|
2115 |
+
|
2116 |
+
if (!response.ok) {
|
2117 |
+
throw new Error(`HTTP错误: ${response.status}`);
|
2118 |
+
}
|
2119 |
+
|
2120 |
+
const data = await response.json();
|
2121 |
+
|
2122 |
+
if (data.success) {
|
2123 |
+
const progressData = data.data;
|
2124 |
|
2125 |
+
// 更新按钮文本显示进度
|
2126 |
+
buttonElement.innerHTML = `<i class="bi bi-arrow-repeat spin me-1"></i> ${progressData.status || '处理中...'} (${progressData.progress}%)`;
|
2127 |
+
|
2128 |
+
// 处理完成或出错时
|
2129 |
+
if (progressData.progress >= 100 || progressData.error) {
|
2130 |
+
clearInterval(interval);
|
2131 |
|
2132 |
+
if (progressData.error) {
|
2133 |
+
alert('处理失败: ' + progressData.status);
|
2134 |
+
} else {
|
2135 |
+
alert(`处理成功! 已处理${progressData.docCount}个文档片段`);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2136 |
}
|
2137 |
+
|
2138 |
+
// 恢复按钮
|
2139 |
+
buttonElement.disabled = false;
|
2140 |
+
buttonElement.textContent = originalText;
|
2141 |
+
|
2142 |
+
// 重新加载知识库列表
|
2143 |
+
loadKnowledgeBases();
|
2144 |
}
|
2145 |
+
} else {
|
2146 |
+
throw new Error('轮询返回错误状态');
|
2147 |
+
}
|
2148 |
+
|
2149 |
+
// 重置重试计数
|
2150 |
+
retryCount = 0;
|
2151 |
+
|
2152 |
+
} catch (error) {
|
2153 |
+
console.error('获取进度信息出错:', error);
|
2154 |
+
retryCount++;
|
2155 |
+
|
2156 |
+
if (retryCount >= maxRetries) {
|
2157 |
+
clearInterval(interval);
|
2158 |
+
alert('获取进度信息失败,请检查控制台日志');
|
2159 |
+
|
2160 |
+
// 恢复按钮
|
2161 |
+
buttonElement.disabled = false;
|
2162 |
+
buttonElement.textContent = originalText;
|
2163 |
}
|
2164 |
+
}
|
2165 |
+
}, 1000); // 每秒轮询一次
|
2166 |
+
}
|
2167 |
+
// 根据文件扩展名获取图标类
|
2168 |
+
function getFileIconClass(fileName) {
|
2169 |
+
if (!fileName) return 'bi-file';
|
2170 |
+
|
2171 |
+
const ext = fileName.split('.').pop().toLowerCase();
|
2172 |
+
|
2173 |
+
switch (ext) {
|
2174 |
+
case 'pdf': return 'bi-file-pdf';
|
2175 |
+
case 'doc':
|
2176 |
+
case 'docx': return 'bi-file-earmark-word';
|
2177 |
+
case 'xls':
|
2178 |
+
case 'xlsx': return 'bi-file-earmark-excel';
|
2179 |
+
case 'ppt':
|
2180 |
+
case 'pptx': return 'bi-file-earmark-ppt';
|
2181 |
+
case 'jpg':
|
2182 |
+
case 'jpeg':
|
2183 |
+
case 'png':
|
2184 |
+
case 'gif':
|
2185 |
+
case 'bmp': return 'bi-file-image';
|
2186 |
+
case 'md': return 'bi-markdown';
|
2187 |
+
case 'txt': return 'bi-file-text';
|
2188 |
+
default: return 'bi-file';
|
2189 |
}
|
2190 |
+
}
|
2191 |
// 使用AI生成工作流
|
2192 |
async function generateAIWorkflow() {
|
2193 |
const description = document.getElementById('agent-description').value;
|
uploads/496449c167344601ba87c24743701b99.pdf
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:a046fe2339ff94de24bb636ebdffbb108629131e633e3e6e9090b9550f0afeda
|
3 |
+
size 499224
|
uploads/eb55dde866f64be4a1fdbc81d3d1df0e.pdf
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:a046fe2339ff94de24bb636ebdffbb108629131e633e3e6e9090b9550f0afeda
|
3 |
+
size 499224
|