This commit is contained in:
minecraft1024a
2025-11-07 20:21:36 +08:00
parent c253d60d6a
commit 80b040da2f

View File

@@ -5,22 +5,21 @@
""" """
import asyncio import asyncio
import orjson
import logging import logging
# 添加项目根目录到 Python 路径
import sys
from datetime import datetime from datetime import datetime
from pathlib import Path from pathlib import Path
from typing import Any, Dict, List, Optional from typing import Optional
from flask import Flask, jsonify, render_template, request from flask import Flask, jsonify, render_template, request
from flask_cors import CORS from flask_cors import CORS
# 添加项目根目录到 Python 路径
import sys
project_root = Path(__file__).parent.parent.parent project_root = Path(__file__).parent.parent.parent
sys.path.insert(0, str(project_root)) sys.path.insert(0, str(project_root))
from src.memory_graph.manager import MemoryManager from src.memory_graph.manager import MemoryManager
from src.memory_graph.models import EdgeType, MemoryType, NodeType
logging.basicConfig(level=logging.INFO) logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@@ -48,13 +47,13 @@ def init_memory_manager():
raise raise
@app.route('/') @app.route("/")
def index(): def index():
"""主页面""" """主页面"""
return render_template('visualizer.html') return render_template("visualizer.html")
@app.route('/api/graph/full') @app.route("/api/graph/full")
def get_full_graph(): def get_full_graph():
""" """
获取完整记忆图数据 获取完整记忆图数据
@@ -79,28 +78,30 @@ def get_full_graph():
for memory in all_memories: for memory in all_memories:
# 添加记忆信息 # 添加记忆信息
memory_info.append({ memory_info.append(
'id': memory.id, {
'type': memory.memory_type.value, "id": memory.id,
'importance': memory.importance, "type": memory.memory_type.value,
'activation': memory.activation, "importance": memory.importance,
'status': memory.status.value, "activation": memory.activation,
'created_at': memory.created_at.isoformat(), "status": memory.status.value,
'text': memory.to_text(), "created_at": memory.created_at.isoformat(),
'access_count': memory.access_count, "text": memory.to_text(),
}) "access_count": memory.access_count,
}
)
# 处理节点 # 处理节点
for node in memory.nodes: for node in memory.nodes:
if node.id not in nodes_dict: if node.id not in nodes_dict:
nodes_dict[node.id] = { nodes_dict[node.id] = {
'id': node.id, "id": node.id,
'label': node.content, "label": node.content,
'type': node.node_type.value, "type": node.node_type.value,
'group': node.node_type.name, # 用于颜色分组 "group": node.node_type.name, # 用于颜色分组
'title': f"{node.node_type.value}: {node.content}", "title": f"{node.node_type.value}: {node.content}",
'metadata': node.metadata, "metadata": node.metadata,
'created_at': node.created_at.isoformat(), "created_at": node.created_at.isoformat(),
} }
# 处理边 - 使用字典自动去重 # 处理边 - 使用字典自动去重
@@ -114,43 +115,42 @@ def get_full_graph():
counter += 1 counter += 1
edges_dict[edge_id] = { edges_dict[edge_id] = {
'id': edge_id, "id": edge_id,
'from': edge.source_id, "from": edge.source_id,
'to': edge.target_id, "to": edge.target_id,
'label': edge.relation, "label": edge.relation,
'type': edge.edge_type.value, "type": edge.edge_type.value,
'importance': edge.importance, "importance": edge.importance,
'title': f"{edge.edge_type.value}: {edge.relation}", "title": f"{edge.edge_type.value}: {edge.relation}",
'arrows': 'to', "arrows": "to",
'memory_id': memory.id, "memory_id": memory.id,
} }
nodes_list = list(nodes_dict.values()) nodes_list = list(nodes_dict.values())
edges_list = list(edges_dict.values()) edges_list = list(edges_dict.values())
return jsonify({ return jsonify(
'success': True, {
'data': { "success": True,
'nodes': nodes_list, "data": {
'edges': edges_list, "nodes": nodes_list,
'memories': memory_info, "edges": edges_list,
'stats': { "memories": memory_info,
'total_nodes': len(nodes_list), "stats": {
'total_edges': len(edges_list), "total_nodes": len(nodes_list),
'total_memories': len(all_memories), "total_edges": len(edges_list),
} "total_memories": len(all_memories),
},
},
} }
}) )
except Exception as e: except Exception as e:
logger.error(f"获取图数据失败: {e}", exc_info=True) logger.error(f"获取图数据失败: {e}", exc_info=True)
return jsonify({ return jsonify({"success": False, "error": str(e)}), 500
'success': False,
'error': str(e)
}), 500
@app.route('/api/memory/<memory_id>') @app.route("/api/memory/<memory_id>")
def get_memory_detail(memory_id: str): def get_memory_detail(memory_id: str):
""" """
获取特定记忆的详细信息 获取特定记忆的详细信息
@@ -165,25 +165,16 @@ def get_memory_detail(memory_id: str):
memory = memory_manager.graph_store.get_memory_by_id(memory_id) memory = memory_manager.graph_store.get_memory_by_id(memory_id)
if memory is None: if memory is None:
return jsonify({ return jsonify({"success": False, "error": "记忆不存在"}), 404
'success': False,
'error': '记忆不存在'
}), 404
return jsonify({ return jsonify({"success": True, "data": memory.to_dict()})
'success': True,
'data': memory.to_dict()
})
except Exception as e: except Exception as e:
logger.error(f"获取记忆详情失败: {e}", exc_info=True) logger.error(f"获取记忆详情失败: {e}", exc_info=True)
return jsonify({ return jsonify({"success": False, "error": str(e)}), 500
'success': False,
'error': str(e)
}), 500
@app.route('/api/search') @app.route("/api/search")
def search_memories(): def search_memories():
""" """
搜索记忆 搜索记忆
@@ -197,49 +188,45 @@ def search_memories():
if memory_manager is None: if memory_manager is None:
init_memory_manager() init_memory_manager()
query = request.args.get('q', '') query = request.args.get("q", "")
memory_type = request.args.get('type', None) memory_type = request.args.get("type", None)
limit = int(request.args.get('limit', 50)) limit = int(request.args.get("limit", 50))
loop = asyncio.new_event_loop() loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop) asyncio.set_event_loop(loop)
# 执行搜索 # 执行搜索
results = loop.run_until_complete( results = loop.run_until_complete(memory_manager.search_memories(query=query, top_k=limit))
memory_manager.search_memories(
query=query,
top_k=limit
)
)
# 构建返回数据 # 构建返回数据
memories = [] memories = []
for memory in results: for memory in results:
memories.append({ memories.append(
'id': memory.id, {
'text': memory.to_text(), "id": memory.id,
'type': memory.memory_type.value, "text": memory.to_text(),
'importance': memory.importance, "type": memory.memory_type.value,
'created_at': memory.created_at.isoformat(), "importance": memory.importance,
}) "created_at": memory.created_at.isoformat(),
}
)
return jsonify({ return jsonify(
'success': True, {
'data': { "success": True,
'results': memories, "data": {
'count': len(memories), "results": memories,
"count": len(memories),
},
} }
}) )
except Exception as e: except Exception as e:
logger.error(f"搜索失败: {e}", exc_info=True) logger.error(f"搜索失败: {e}", exc_info=True)
return jsonify({ return jsonify({"success": False, "error": str(e)}), 500
'success': False,
'error': str(e)
}), 500
@app.route('/api/stats') @app.route("/api/stats")
def get_statistics(): def get_statistics():
""" """
获取记忆图统计信息 获取记忆图统计信息
@@ -259,36 +246,30 @@ def get_statistics():
all_edges += len(memory.edges) all_edges += len(memory.edges)
stats = { stats = {
'total_memories': len(all_memories), "total_memories": len(all_memories),
'total_nodes': len(all_nodes), "total_nodes": len(all_nodes),
'total_edges': all_edges, "total_edges": all_edges,
'node_types': {}, "node_types": {},
'memory_types': {}, "memory_types": {},
} }
# 统计节点类型分布 # 统计节点类型分布
for memory in all_memories: for memory in all_memories:
mem_type = memory.memory_type.value mem_type = memory.memory_type.value
stats['memory_types'][mem_type] = stats['memory_types'].get(mem_type, 0) + 1 stats["memory_types"][mem_type] = stats["memory_types"].get(mem_type, 0) + 1
for node in memory.nodes: for node in memory.nodes:
node_type = node.node_type.value node_type = node.node_type.value
stats['node_types'][node_type] = stats['node_types'].get(node_type, 0) + 1 stats["node_types"][node_type] = stats["node_types"].get(node_type, 0) + 1
return jsonify({ return jsonify({"success": True, "data": stats})
'success': True,
'data': stats
})
except Exception as e: except Exception as e:
logger.error(f"获取统计信息失败: {e}", exc_info=True) logger.error(f"获取统计信息失败: {e}", exc_info=True)
return jsonify({ return jsonify({"success": False, "error": str(e)}), 500
'success': False,
'error': str(e)
}), 500
@app.route('/api/files') @app.route("/api/files")
def list_files(): def list_files():
""" """
列出所有可用的数据文件 列出所有可用的数据文件
@@ -296,50 +277,48 @@ def list_files():
""" """
try: try:
from pathlib import Path from pathlib import Path
data_dir = Path("data/memory_graph") data_dir = Path("data/memory_graph")
files = [] files = []
if data_dir.exists(): if data_dir.exists():
for f in data_dir.glob("*.json"): for f in data_dir.glob("*.json"):
stat = f.stat() stat = f.stat()
files.append({ files.append(
'path': str(f), {
'name': f.name, "path": str(f),
'size': stat.st_size, "name": f.name,
'size_kb': round(stat.st_size / 1024, 2), "size": stat.st_size,
'modified': datetime.fromtimestamp(stat.st_mtime).isoformat(), "size_kb": round(stat.st_size / 1024, 2),
'modified_readable': datetime.fromtimestamp(stat.st_mtime).strftime('%Y-%m-%d %H:%M:%S'), "modified": datetime.fromtimestamp(stat.st_mtime).isoformat(),
'is_current': True # 完整版始终使用内存数据 "modified_readable": datetime.fromtimestamp(stat.st_mtime).strftime("%Y-%m-%d %H:%M:%S"),
}) "is_current": True, # 完整版始终使用内存数据
}
)
return jsonify({ return jsonify(
'success': True, {
'files': files, "success": True,
'count': len(files), "files": files,
'current_file': 'memory_manager (实时数据)', "count": len(files),
'note': '完整版服务器使用实时内存数据,如需切换文件请使用独立版服务器' "current_file": "memory_manager (实时数据)",
}) "note": "完整版服务器使用实时内存数据,如需切换文件请使用独立版服务器",
}
)
except Exception as e: except Exception as e:
logger.error(f"获取文件列表失败: {e}", exc_info=True) logger.error(f"获取文件列表失败: {e}", exc_info=True)
return jsonify({ return jsonify({"success": False, "error": str(e)}), 500
'success': False,
'error': str(e)
}), 500
@app.route('/api/reload') @app.route("/api/reload")
def reload_data(): def reload_data():
""" """
重新加载数据 重新加载数据
""" """
return jsonify({ return jsonify({"success": True, "message": "完整版服务器使用实时数据,无需重新加载", "note": "数据始终是最新的"})
'success': True,
'message': '完整版服务器使用实时数据,无需重新加载',
'note': '数据始终是最新的'
})
def run_server(host: str = '127.0.0.1', port: int = 5000, debug: bool = False): def run_server(host: str = "127.0.0.1", port: int = 5000, debug: bool = False):
""" """
启动可视化服务器 启动可视化服务器
@@ -352,5 +331,5 @@ def run_server(host: str = '127.0.0.1', port: int = 5000, debug: bool = False):
app.run(host=host, port=port, debug=debug) app.run(host=host, port=port, debug=debug)
if __name__ == '__main__': if __name__ == "__main__":
run_server(debug=True) run_server(debug=True)