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,61 +47,63 @@ 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():
""" """
获取完整记忆图数据 获取完整记忆图数据
返回所有节点和边,格式化为前端可用的结构 返回所有节点和边,格式化为前端可用的结构
""" """
try: try:
if memory_manager is None: if memory_manager is None:
init_memory_manager() init_memory_manager()
# 获取所有记忆 # 获取所有记忆
loop = asyncio.new_event_loop() loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop) asyncio.set_event_loop(loop)
# 获取所有记忆 # 获取所有记忆
all_memories = memory_manager.graph_store.get_all_memories() all_memories = memory_manager.graph_store.get_all_memories()
# 构建节点和边数据 # 构建节点和边数据
nodes_dict = {} # {node_id: node_data} nodes_dict = {} # {node_id: node_data}
edges_dict = {} # {edge_id: edge_data} - 使用字典去重 edges_dict = {} # {edge_id: edge_data} - 使用字典去重
memory_info = [] memory_info = []
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(),
} }
# 处理边 - 使用字典自动去重 # 处理边 - 使用字典自动去重
for edge in memory.edges: for edge in memory.edges:
edge_id = edge.id edge_id = edge.id
@@ -112,82 +113,72 @@ def get_full_graph():
while edge_id in edges_dict: while edge_id in edges_dict:
edge_id = f"{original_edge_id}_{counter}" edge_id = f"{original_edge_id}_{counter}"
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):
""" """
获取特定记忆的详细信息 获取特定记忆的详细信息
Args: Args:
memory_id: 记忆ID memory_id: 记忆ID
""" """
try: try:
if memory_manager is None: if memory_manager is None:
init_memory_manager() init_memory_manager()
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': '记忆不存在' return jsonify({"success": True, "data": memory.to_dict()})
}), 404
return jsonify({
'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():
""" """
搜索记忆 搜索记忆
Query参数: Query参数:
- q: 搜索关键词 - q: 搜索关键词
- type: 记忆类型过滤 - type: 记忆类型过滤
@@ -196,50 +187,46 @@ def search_memories():
try: try:
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({ )
'success': True,
'data': { return jsonify(
'results': memories, {
'count': len(memories), "success": True,
"data": {
"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():
""" """
获取记忆图统计信息 获取记忆图统计信息
@@ -247,48 +234,42 @@ def get_statistics():
try: try:
if memory_manager is None: if memory_manager is None:
init_memory_manager() init_memory_manager()
# 获取统计信息 # 获取统计信息
all_memories = memory_manager.graph_store.get_all_memories() all_memories = memory_manager.graph_store.get_all_memories()
all_nodes = set() all_nodes = set()
all_edges = 0 all_edges = 0
for memory in all_memories: for memory in all_memories:
for node in memory.nodes: for node in memory.nodes:
all_nodes.add(node.id) all_nodes.add(node.id)
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,53 +277,51 @@ 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({ )
'success': True,
'files': files, return jsonify(
'count': len(files), {
'current_file': 'memory_manager (实时数据)', "success": True,
'note': '完整版服务器使用实时内存数据,如需切换文件请使用独立版服务器' "files": files,
}) "count": len(files),
"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):
""" """
启动可视化服务器 启动可视化服务器
Args: Args:
host: 服务器地址 host: 服务器地址
port: 端口号 port: 端口号
@@ -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)