From ed609eba6cbfd566948ac2fcd9f152a15c77f48f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9C=B1=E6=BD=AE?= Date: Sat, 1 Nov 2025 00:23:22 +0800 Subject: [PATCH] add file tree --- fastapi_app.py | 268 ++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 208 insertions(+), 60 deletions(-) diff --git a/fastapi_app.py b/fastapi_app.py index 75b0422..b15dda0 100644 --- a/fastapi_app.py +++ b/fastapi_app.py @@ -193,7 +193,6 @@ async def generate_stream_response(agent, messages, request) -> AsyncGenerator[s # Models are now imported from utils module - @app.post("/api/v1/files/process/async") async def process_files_async_endpoint(request: QueueTaskRequest, authorization: Optional[str] = Header(None)): """ @@ -596,65 +595,6 @@ async def health_check(): return {"message": "Database Assistant API is running"} -@app.get("/system/status") -async def system_status(): - """获取系统状态信息""" - # 获取助手缓存统计 - cache_stats = agent_manager.get_cache_stats() - - return { - "status": "running", - "storage_type": "File-Loaded Agent Manager", - "max_cached_agents": max_cached_agents, - "agent_cache": { - "total_cached_agents": cache_stats["total_cached_agents"], - "max_cached_agents": cache_stats["max_cached_agents"], - "cached_agents": cache_stats["agents"] - } - } - - -@app.post("/system/cleanup-cache") -async def cleanup_cache(): - """清理助手缓存""" - try: - # 清理助手实例缓存 - cleared_count = agent_manager.clear_cache() - - return { - "message": "缓存清理成功", - "cleared_agent_instances": cleared_count - } - except Exception as e: - raise HTTPException(status_code=500, detail=f"缓存清理失败: {str(e)}") - - -@app.post("/system/cleanup-agent-cache") -async def cleanup_agent_cache(): - """仅清理助手实例缓存""" - try: - cleared_count = agent_manager.clear_cache() - return { - "message": "助手实例缓存清理成功", - "cleared_agent_instances": cleared_count - } - except Exception as e: - raise HTTPException(status_code=500, detail=f"助手实例缓存清理失败: {str(e)}") - - -@app.get("/system/cached-projects") -async def get_cached_projects(): - """获取所有缓存的项目信息""" - try: - cache_stats = agent_manager.get_cache_stats() - - return { - "cache_stats": cache_stats - } - except Exception as e: - raise HTTPException(status_code=500, detail=f"获取缓存项目信息失败: {str(e)}") - - @app.post("/system/remove-project-cache") async def remove_project_cache(dataset_id: str): """移除特定项目的缓存""" @@ -764,6 +704,214 @@ async def reset_files_processing(dataset_id: str): raise HTTPException(status_code=500, detail=f"重置文件处理状态失败: {str(e)}") +def build_directory_tree(path: str, relative_path: str = "") -> dict: + """构建目录树结构""" + import os + + if not os.path.exists(path): + return {} + + tree = { + "name": os.path.basename(path) or "projects", + "path": relative_path, + "type": "directory", + "children": [], + "size": 0, + "modified_time": os.path.getmtime(path) + } + + try: + entries = os.listdir(path) + entries.sort() + + for entry in entries: + entry_path = os.path.join(path, entry) + entry_relative_path = os.path.join(relative_path, entry) if relative_path else entry + + if os.path.isdir(entry_path): + tree["children"].append(build_directory_tree(entry_path, entry_relative_path)) + else: + try: + file_size = os.path.getsize(entry_path) + file_modified = os.path.getmtime(entry_path) + tree["children"].append({ + "name": entry, + "path": entry_relative_path, + "type": "file", + "size": file_size, + "modified_time": file_modified + }) + tree["size"] += file_size + except (OSError, IOError): + tree["children"].append({ + "name": entry, + "path": entry_relative_path, + "type": "file", + "size": 0, + "modified_time": 0 + }) + except (OSError, IOError) as e: + print(f"Error reading directory {path}: {e}") + + return tree + + +@app.get("/api/v1/projects/tree") +async def get_projects_tree( + include_files: bool = True, + max_depth: int = 10, + filter_type: Optional[str] = None +): + """ + 获取projects文件夹的目录树结构 + + Args: + include_files: 是否包含文件,false时只显示目录 + max_depth: 最大深度限制 + filter_type: 过滤类型 ('data', 'robot', 'uploads') + + Returns: + dict: 包含目录树结构的响应 + """ + try: + projects_dir = "projects" + + if not os.path.exists(projects_dir): + return { + "success": False, + "message": "projects目录不存在", + "tree": {} + } + + tree = build_directory_tree(projects_dir) + + # 根据filter_type过滤 + if filter_type and filter_type in ['data', 'robot', 'uploads']: + filtered_children = [] + for child in tree.get("children", []): + if child["name"] == filter_type: + filtered_children.append(child) + tree["children"] = filtered_children + + # 如果不包含文件,移除所有文件节点 + if not include_files: + tree = filter_directories_only(tree) + + # 计算统计信息 + stats = calculate_tree_stats(tree) + + return { + "success": True, + "message": "目录树获取成功", + "tree": tree, + "stats": stats, + "filters": { + "include_files": include_files, + "max_depth": max_depth, + "filter_type": filter_type + } + } + + except Exception as e: + print(f"Error getting projects tree: {str(e)}") + raise HTTPException(status_code=500, detail=f"获取目录树失败: {str(e)}") + + +def filter_directories_only(tree: dict) -> dict: + """过滤掉文件,只保留目录""" + if tree["type"] != "directory": + return tree + + filtered_children = [] + for child in tree.get("children", []): + if child["type"] == "directory": + filtered_children.append(filter_directories_only(child)) + + tree["children"] = filtered_children + return tree + + +def calculate_tree_stats(tree: dict) -> dict: + """计算目录树统计信息""" + stats = { + "total_directories": 0, + "total_files": 0, + "total_size": 0 + } + + def traverse(node): + if node["type"] == "directory": + stats["total_directories"] += 1 + for child in node.get("children", []): + traverse(child) + else: + stats["total_files"] += 1 + stats["total_size"] += node.get("size", 0) + + traverse(tree) + return stats + + +@app.get("/api/v1/projects/subtree/{sub_path:path}") +async def get_projects_subtree( + sub_path: str, + include_files: bool = True, + max_depth: int = 5 +): + """ + 获取projects子目录的树结构 + + Args: + sub_path: 子目录路径,如 'data/1624be71-5432-40bf-9758-f4aecffd4e9c' + include_files: 是否包含文件 + max_depth: 最大深度 + + Returns: + dict: 包含子目录树结构的响应 + """ + try: + full_path = os.path.join("projects", sub_path) + + if not os.path.exists(full_path): + return { + "success": False, + "message": f"路径不存在: {sub_path}", + "tree": {} + } + + if not os.path.isdir(full_path): + return { + "success": False, + "message": f"路径不是目录: {sub_path}", + "tree": {} + } + + tree = build_directory_tree(full_path, sub_path) + + # 如果不包含文件,移除所有文件节点 + if not include_files: + tree = filter_directories_only(tree) + + # 计算统计信息 + stats = calculate_tree_stats(tree) + + return { + "success": True, + "message": "子目录树获取成功", + "sub_path": sub_path, + "tree": tree, + "stats": stats, + "filters": { + "include_files": include_files, + "max_depth": max_depth + } + } + + except Exception as e: + print(f"Error getting projects subtree: {str(e)}") + raise HTTPException(status_code=500, detail=f"获取子目录树失败: {str(e)}") + + if __name__ == "__main__":