refactor: 将 A_Memorix 重构为主线长期记忆子系统并重建管理界面

- 将 A_Memorix 从旧 submodule / 插件形态迁入主线源码,主体落到 src/A_memorix
- 调整主程序接入方式,使 A_Memorix 作为源码内长期记忆子系统运行
- 回收父项目插件体系中针对 A_Memorix 的特判,减少对 plugin 通用层的侵入
- 将长期记忆配置、运行时、自检、导入、调优等能力收口到 memory 路由与主线服务层
- 重做长期记忆控制台与图谱页面,按 MaiBot 现有 dashboard 风格接入
- 补充实体关系图与证据视图双视图能力,支持查看节点、关系、段落及其证据链路
- 新增长期记忆配置编辑器与 memory-api,支持主线内配置管理
- 补齐删除管理能力:删除预览、混合删除、来源批量删除、删除操作恢复
- 优化删除预览与删除操作详情的前端展示,支持分页、检索,并以实体名/关系内容/段落摘要替代单纯 hash 展示
- 修复图谱与控制台相关前端问题,包括证据视图切换、查询触发时机、删除弹层空值保护等
- 新增或更新 A_Memorix 相关测试、WebUI 路由测试、前端 vitest 测试与辅助验证脚本
- 移除旧 plugins/A_memorix、.gitmodules 及相关历史维护文档
This commit is contained in:
A-Dawn
2026-04-03 08:08:24 +08:00
parent bf5eb45709
commit 15d436b3a1
136 changed files with 52533 additions and 629 deletions

View File

@@ -6,14 +6,16 @@ import uuid
from pathlib import Path
from typing import Any, Optional
from fastapi import APIRouter, Body, Depends, File, Form, Query, UploadFile
import tomlkit
from fastapi import APIRouter, Body, Depends, File, Form, HTTPException, Query, UploadFile
from pydantic import BaseModel, Field
from src.A_memorix.host_service import a_memorix_host_service
from src.services.memory_service import MemorySearchResult, memory_service
from src.webui.dependencies import require_auth
router = APIRouter(prefix="/api/webui/memory", tags=["memory"], dependencies=[Depends(require_auth)])
router = APIRouter(prefix="/memory", tags=["memory"], dependencies=[Depends(require_auth)])
compat_router = APIRouter(prefix="/api", tags=["memory-compat"], dependencies=[Depends(require_auth)])
STAGING_ROOT = Path(__file__).resolve().parents[3] / "data" / "memory_upload_staging"
@@ -82,6 +84,14 @@ class AutoSaveRequest(BaseModel):
enabled: bool
class MemoryConfigUpdateRequest(BaseModel):
config: dict[str, Any] = Field(default_factory=dict)
class MemoryRawConfigUpdateRequest(BaseModel):
config: str = ""
class TuningApplyProfileRequest(BaseModel):
profile: dict[str, Any] = Field(default_factory=dict)
reason: str = "manual"
@@ -158,6 +168,44 @@ async def _graph_get(limit: int) -> dict:
return await memory_service.graph_admin(action="get_graph", limit=limit)
async def _graph_get_node_detail(
node_id: str,
*,
relation_limit: int,
paragraph_limit: int,
evidence_node_limit: int,
) -> dict:
payload = await memory_service.graph_admin(
action="node_detail",
node_id=node_id,
relation_limit=relation_limit,
paragraph_limit=paragraph_limit,
evidence_node_limit=evidence_node_limit,
)
if not bool(payload.get("success", False)):
raise HTTPException(status_code=404, detail=str(payload.get("error", "未找到节点详情")))
return payload
async def _graph_get_edge_detail(
source: str,
target: str,
*,
paragraph_limit: int,
evidence_node_limit: int,
) -> dict:
payload = await memory_service.graph_admin(
action="edge_detail",
source=source,
target=target,
paragraph_limit=paragraph_limit,
evidence_node_limit=evidence_node_limit,
)
if not bool(payload.get("success", False)):
raise HTTPException(status_code=404, detail=str(payload.get("error", "未找到边详情")))
return payload
async def _graph_create_node(payload: NodeRequest) -> dict:
return await memory_service.graph_admin(action="create_node", name=payload.name)
@@ -325,6 +373,42 @@ async def _runtime_auto_save(enabled: bool | None = None) -> dict:
return await memory_service.runtime_admin(action="set_auto_save", enabled=enabled)
async def _memory_config_schema() -> dict:
return {
"success": True,
"schema": a_memorix_host_service.get_config_schema(),
"path": str(a_memorix_host_service.get_config_path()),
}
async def _memory_config_get() -> dict:
return {
"success": True,
"config": a_memorix_host_service.get_config(),
"path": str(a_memorix_host_service.get_config_path()),
}
async def _memory_config_get_raw() -> dict:
return {
"success": True,
"config": a_memorix_host_service.get_raw_config(),
"path": str(a_memorix_host_service.get_config_path()),
}
async def _memory_config_update(payload: MemoryConfigUpdateRequest) -> dict:
return await a_memorix_host_service.update_config(payload.config)
async def _memory_config_update_raw(payload: MemoryRawConfigUpdateRequest) -> dict:
try:
tomlkit.loads(payload.config)
except Exception as exc:
raise HTTPException(status_code=400, detail=f"TOML 格式错误: {exc}") from exc
return await a_memorix_host_service.update_raw_config(payload.config)
async def _maintenance_recycle_bin(limit: int) -> dict:
return await memory_service.get_recycle_bin(limit=limit)
@@ -565,6 +649,36 @@ async def get_memory_graph(limit: int = Query(200, ge=1, le=5000)):
return await _graph_get(limit)
@router.get("/graph/node-detail")
async def get_memory_graph_node_detail(
node_id: str = Query(..., min_length=1),
relation_limit: int = Query(20, ge=1, le=100),
paragraph_limit: int = Query(20, ge=1, le=100),
evidence_node_limit: int = Query(80, ge=12, le=200),
):
return await _graph_get_node_detail(
node_id,
relation_limit=relation_limit,
paragraph_limit=paragraph_limit,
evidence_node_limit=evidence_node_limit,
)
@router.get("/graph/edge-detail")
async def get_memory_graph_edge_detail(
source: str = Query(..., min_length=1),
target: str = Query(..., min_length=1),
paragraph_limit: int = Query(20, ge=1, le=100),
evidence_node_limit: int = Query(80, ge=12, le=200),
):
return await _graph_get_edge_detail(
source,
target,
paragraph_limit=paragraph_limit,
evidence_node_limit=evidence_node_limit,
)
@router.post("/graph/node")
async def create_memory_node(payload: NodeRequest):
return await _graph_create_node(payload)
@@ -703,6 +817,31 @@ async def save_memory_runtime():
return await _runtime_save()
@router.get("/config/schema")
async def get_memory_config_schema():
return await _memory_config_schema()
@router.get("/config")
async def get_memory_config():
return await _memory_config_get()
@router.put("/config")
async def update_memory_config(payload: MemoryConfigUpdateRequest):
return await _memory_config_update(payload)
@router.get("/config/raw")
async def get_memory_config_raw():
return await _memory_config_get_raw()
@router.put("/config/raw")
async def update_memory_config_raw(payload: MemoryRawConfigUpdateRequest):
return await _memory_config_update_raw(payload)
@router.get("/runtime/config")
async def get_memory_runtime_config():
return await _runtime_config()