refactor: restructure to core/ + ui/ with multi-threaded backend

This commit is contained in:
root
2026-04-12 18:15:55 +08:00
parent c13d3f671c
commit 83906a6985
64 changed files with 5269 additions and 571 deletions

View File

@ -3,7 +3,7 @@
import pytest
import tempfile
import os
from graph_memory_tui.core.embedded_db import EmbeddedGraphDB
from core import EmbeddedGraphDB
@pytest.fixture

View File

@ -1,50 +1,32 @@
"""核心逻辑导入测试"""
import pytest
def test_import_neo4j_graph():
"""测试 Neo4jGraph 类导入"""
from graph_memory_tui.core.imports import Neo4jGraph
assert Neo4jGraph is not None
assert hasattr(Neo4jGraph, 'recall')
assert hasattr(Neo4jGraph, 'commit')
assert hasattr(Neo4jGraph, 'purge')
def test_import_backend_server():
from core import BackendServer
assert BackendServer is not None
def test_import_graph_memory_client():
"""测试 GraphMemoryClient 类导入"""
from graph_memory_tui.core.imports import GraphMemoryClient
def test_import_backend_client():
from core import BackendClient
assert BackendClient is not None
def test_import_embedded_db():
from core import EmbeddedGraphDB
assert EmbeddedGraphDB is not None
def test_import_graph_client():
from core.graph_client import GraphMemoryClient
assert GraphMemoryClient is not None
assert hasattr(GraphMemoryClient, 'send_message')
def test_import_tools():
"""测试 TOOLS 定义导入"""
from graph_memory_tui.core.imports import TOOLS
assert TOOLS is not None
assert isinstance(TOOLS, list)
assert len(TOOLS) > 0
assert any(t['function']['name'] == 'memory_recall' for t in TOOLS)
def test_import_tool_limiter():
from core.tool_limiter import ToolLimiter
assert ToolLimiter is not None
def test_import_execute_tool():
"""测试 execute_tool 函数导入"""
from graph_memory_tui.core.imports import execute_tool
def test_import_tool_executor():
from core.tool_executor import execute_tool
assert execute_tool is not None
assert callable(execute_tool)
def test_import_config_vars():
"""测试配置变量导入"""
from graph_memory_tui.core.imports import (
DEEPSEEK_API_KEY,
DEEPSEEK_BASE_URL,
MODEL_NAME,
NEO4J_URI,
NEO4J_USER,
NEO4J_PASSWORD,
)
assert DEEPSEEK_BASE_URL is not None
assert MODEL_NAME is not None
assert NEO4J_URI is not None
assert callable(execute_tool)

View File

@ -1,138 +0,0 @@
"""记忆工具测试"""
import pytest
from graph_memory_tui.core.tools.memory_tools import (
MEMORY_TOOLS,
PERSONA_TOOLS,
WORKING_MEMORY_TOOLS,
TOOLS
)
def test_memory_tools_exist():
"""测试记忆工具存在"""
assert len(MEMORY_TOOLS) >= 6
def test_persona_tools_exist():
"""测试人设工具存在"""
assert len(PERSONA_TOOLS) >= 2
def test_working_memory_tools_exist():
"""测试工作记忆工具存在"""
assert len(WORKING_MEMORY_TOOLS) >= 4
def test_all_tools_combined():
"""测试工具合并"""
assert len(TOOLS) == len(MEMORY_TOOLS) + len(PERSONA_TOOLS) + len(WORKING_MEMORY_TOOLS)
def test_memory_recall_tool():
"""测试 memory_recall 工具定义"""
recall = next((t for t in TOOLS if t['function']['name'] == 'memory_recall'), None)
assert recall is not None
params = recall['function']['parameters']['properties']
assert 'query_intent' in params
assert 'seed_entities' in params
assert 'depth' in params
def test_memory_commit_tool():
"""测试 memory_commit 工具定义"""
commit = next((t for t in TOOLS if t['function']['name'] == 'memory_commit'), None)
assert commit is not None
params = commit['function']['parameters']['properties']
assert 'triplets' in params
def test_memory_purge_tool():
"""测试 memory_purge 工具定义"""
purge = next((t for t in TOOLS if t['function']['name'] == 'memory_purge'), None)
assert purge is not None
params = purge['function']['parameters']['properties']
assert 'criteria' in params
assert 'mode' in params
def test_memory_introspect_tool():
"""测试 memory_introspect 工具定义"""
introspect = next((t for t in TOOLS if t['function']['name'] == 'memory_introspect'), None)
assert introspect is not None
def test_persona_update_tool():
"""测试 persona_update 工具定义"""
update = next((t for t in TOOLS if t['function']['name'] == 'persona_update'), None)
assert update is not None
params = update['function']['parameters']['properties']
assert 'attributes' in params
def test_persona_clear_tool():
"""测试 persona_clear 工具定义"""
clear = next((t for t in TOOLS if t['function']['name'] == 'persona_clear'), None)
assert clear is not None
def test_task_create_tool():
"""测试 task_create 工具定义"""
create = next((t for t in TOOLS if t['function']['name'] == 'task_create'), None)
assert create is not None
params = create['function']['parameters']['properties']
assert 'task_id' in params
assert 'description' in params
def test_task_set_state_tool():
"""测试 task_set_state 工具定义"""
set_state = next((t for t in TOOLS if t['function']['name'] == 'task_set_state'), None)
assert set_state is not None
params = set_state['function']['parameters']['properties']
assert 'task_id' in params
assert 'state' in params
def test_task_delete_tool():
"""测试 task_delete 工具定义"""
delete = next((t for t in TOOLS if t['function']['name'] == 'task_delete'), None)
assert delete is not None
def test_task_link_info_tool():
"""测试 task_link_info 工具定义"""
link = next((t for t in TOOLS if t['function']['name'] == 'task_link_info'), None)
assert link is not None
params = link['function']['parameters']['properties']
assert 'task_id' in params
assert 'info_node_names' in params
def test_tool_has_required_fields():
"""测试工具都有必需字段"""
for tool in TOOLS:
assert 'type' in tool
assert tool['type'] == 'function'
assert 'function' in tool
assert 'name' in tool['function']
assert 'description' in tool['function']
assert 'parameters' in tool['function']
def test_tool_state_enum():
"""测试 task_set_state 的状态枚举"""
set_state = next((t for t in TOOLS if t['function']['name'] == 'task_set_state'), None)
state_enum = set_state['function']['parameters']['properties']['state']['enum']
assert '进行中' in state_enum
assert '已完成' in state_enum
assert '已暂停' in state_enum
assert '已取消' in state_enum

View File

@ -0,0 +1,57 @@
import pytest
import os
import tempfile
os.environ["DEEPSEEK_API_KEY"] = "fake-test-key"
class TestCoreImport:
def test_import_backend_server(self):
from core import BackendServer
assert BackendServer is not None
def test_import_backend_client(self):
from core import BackendClient
assert BackendClient is not None
def test_import_embedded_db(self):
from core import EmbeddedGraphDB
assert EmbeddedGraphDB is not None
class TestBackendServer:
def test_create_server(self):
from core import BackendServer
server = BackendServer(db_path=":memory:", use_embedded_db=True)
assert server is not None
assert server._running is False
def test_start_server(self):
from core import BackendServer
with tempfile.NamedTemporaryFile(suffix=".db", delete=False) as f:
db_path = f.name
try:
server = BackendServer(db_path=db_path, use_embedded_db=True)
server.start(api_key="", base_url="https://api.test.com")
assert server._running is True
assert server._graph is not None
server.shutdown()
finally:
if os.path.exists(db_path):
os.unlink(db_path)
def test_shutdown(self):
from core import BackendServer
with tempfile.NamedTemporaryFile(suffix=".db", delete=False) as f:
db_path = f.name
server = BackendServer(db_path=db_path, use_embedded_db=True)
server.start(api_key="", base_url="https://api.test.com")
assert server._running is True
server.shutdown()
assert server._running is False

View File

@ -1,148 +0,0 @@
"""工具限制器测试"""
import pytest
from graph_memory_tui.core.tools.tool_limiter import (
ToolLimiter,
ToolLimits,
ToolCallCount
)
@pytest.fixture
def limiter():
"""创建限制器实例"""
return ToolLimiter()
def test_classify_persona_tools(limiter):
"""测试人设工具分类"""
category, operation = limiter._classify_tool('persona_update', {})
assert category == 'persona'
assert operation == 'update'
category, operation = limiter._classify_tool('persona_clear', {})
assert category == 'persona'
assert operation == 'update'
def test_classify_task_tools(limiter):
"""测试任务工具分类"""
category, operation = limiter._classify_tool('task_create', {})
assert category == 'task'
assert operation == 'update'
category, operation = limiter._classify_tool('task_set_state', {})
assert category == 'task'
assert operation == 'update'
category, operation = limiter._classify_tool('task_delete', {})
assert category == 'task'
assert operation == 'update'
category, operation = limiter._classify_tool('task_link_info', {})
assert category == 'task'
assert operation == 'update'
def test_classify_memory_recall(limiter):
"""测试 memory_recall 分类"""
category, operation = limiter._classify_tool('memory_recall', {'query_intent': 'Python'})
assert category == 'memory'
assert operation == 'query'
def test_classify_memory_recall_persona_query(limiter):
"""测试 memory_recall 查询人设图"""
category, operation = limiter._classify_tool(
'memory_recall',
{'query_intent': 'AI,人设,角色'}
)
assert category == 'persona'
assert operation == 'query'
def test_classify_memory_recall_task_query(limiter):
"""测试 memory_recall 查询工作记忆链"""
category, operation = limiter._classify_tool(
'memory_recall',
{'query_intent': 'TaskNode,工作记忆'}
)
assert category == 'task'
assert operation == 'query'
def test_can_call_allowed(limiter):
"""测试允许调用"""
allowed, reason = limiter.can_call('memory_recall', {'query_intent': 'test'})
assert allowed is True
def test_can_call_limit_reached(limiter):
"""测试达到限制"""
for _ in range(20):
limiter.record_call('memory_recall', {'query_intent': 'test'})
allowed, reason = limiter.can_call('memory_recall', {'query_intent': 'test'})
assert allowed is False
assert '上限' in reason
def test_record_call(limiter):
"""测试记录调用"""
initial_count = limiter.counts.memory_query
limiter.record_call('memory_recall', {'query_intent': 'test'})
assert limiter.counts.memory_query == initial_count + 1
def test_reset(limiter):
"""测试重置计数"""
limiter.record_call('memory_recall', {'query_intent': 'test'})
limiter.record_call('memory_recall', {'query_intent': 'test'})
limiter.reset()
assert limiter.counts.memory_query == 0
assert limiter.counts.memory_update == 0
def test_get_summary(limiter):
"""测试获取统计摘要"""
limiter.record_call('memory_recall', {'query_intent': 'test'})
summary = limiter.get_summary()
assert isinstance(summary, str)
assert '一般记忆' in summary
assert '查询1' in summary
def test_custom_limits():
"""测试自定义限制"""
limits = ToolLimits(
memory_query_max=5,
memory_update_max=3
)
limiter = ToolLimiter(limits)
assert limiter.limits.memory_query_max == 5
assert limiter.limits.memory_update_max == 3
def test_persona_query_limit(limiter):
"""测试人设图查询限制"""
for _ in range(1):
limiter.record_call('memory_recall', {'query_intent': '人设'})
allowed, _ = limiter.can_call('memory_recall', {'query_intent': '人设'})
assert allowed is False
def test_task_query_limit(limiter):
"""测试工作记忆链查询限制"""
for _ in range(4):
limiter.record_call('memory_recall', {'query_intent': 'TaskNode'})
allowed, _ = limiter.can_call('memory_recall', {'query_intent': 'TaskNode'})
assert allowed is False