chore: initial commit for Pretor v0.1.0-alpha

正式发布 Pretor 平台的首个 alpha 版本。本项目旨在构建一个基于分布式架构的多智能体协同工作流水线。

核心功能实现:
1. 建立基于 BaseIndividual 的动态插件加载机制。
2. 实现三类核心 worker_individual 子个体。
3. 集成 Ray 框架支持分布式集群调度。
4. 基于 PostgreSQL 的全量持久化存储方案。
5. 提供完整的 FastAPI 后端与 React 前端交互界面。
This commit is contained in:
2026-04-29 10:09:07 +08:00
commit d84212f780
163 changed files with 19251 additions and 0 deletions
+145
View File
@@ -0,0 +1,145 @@
import pytest
from unittest.mock import MagicMock, AsyncMock, patch
import json
import sys
import builtins
real_import = builtins.__import__
def mock_import(name, globals=None, locals=None, fromlist=(), level=0):
if name == 'sqlmodel':
mock_sqlmodel = MagicMock()
class DummySQLModel:
def __init_subclass__(cls, **kwargs):
pass
mock_sqlmodel.SQLModel = DummySQLModel
mock_sqlmodel.Field = MagicMock(return_value=None)
mock_sqlmodel.select = MagicMock()
return mock_sqlmodel
return real_import(name, globals, locals, fromlist, level)
builtins.__import__ = mock_import
for mod in list(sys.modules.keys()):
if 'pretor.core.database.module.memory' in mod or 'sqlmodel' in mod:
del sys.modules[mod]
from pretor.core.database.module.memory import MemoryRAG
builtins.__import__ = real_import
@pytest.fixture(autouse=True)
def mock_dependencies():
with patch("pretor.core.database.module.memory.WorkflowRecord") as mock_workflow_record:
with patch("pretor.core.database.module.memory.MemoryRecord") as mock_memory_record:
with patch("pretor.core.database.module.memory.select") as mock_select:
yield mock_workflow_record, mock_memory_record, mock_select
@pytest.fixture
def mock_session_maker():
maker = MagicMock()
session = AsyncMock()
session.add = MagicMock()
maker.return_value.__aenter__.return_value = session
maker.__aenter__.return_value = session
maker.__aexit__ = AsyncMock()
return maker, session
@pytest.mark.asyncio
async def test_save_workflow(mock_session_maker, mock_dependencies):
mock_workflow_record, _, _ = mock_dependencies
maker, session = mock_session_maker
rag = MemoryRAG(maker)
mock_record = MagicMock()
mock_workflow_record.return_value = mock_record
workflow_data = {"key": "value"}
record = await rag.save_workflow("wf_123", workflow_data)
mock_workflow_record.assert_called_once_with(
workflow_id="wf_123",
workflow_data_json=json.dumps(workflow_data)
)
session.add.assert_called_once_with(mock_record)
session.commit.assert_called_once()
session.refresh.assert_called_once_with(mock_record)
assert record == mock_record
@pytest.mark.asyncio
async def test_get_workflow_success(mock_session_maker, mock_dependencies):
_, _, mock_select = mock_dependencies
maker, session = mock_session_maker
rag = MemoryRAG(maker)
mock_statement = MagicMock()
mock_select.return_value.where.return_value = mock_statement
mock_record = MagicMock()
mock_record.workflow_data_json = '{"key": "value"}'
mock_exec_result = MagicMock()
mock_exec_result.scalar_one_or_none.return_value = mock_record
session.execute = AsyncMock(return_value=mock_exec_result)
data = await rag.get_workflow("wf_123")
session.execute.assert_called_once_with(mock_statement)
assert data == {"key": "value"}
@pytest.mark.asyncio
async def test_get_workflow_not_found(mock_session_maker, mock_dependencies):
_, _, mock_select = mock_dependencies
maker, session = mock_session_maker
rag = MemoryRAG(maker)
mock_exec_result = MagicMock()
mock_exec_result.scalar_one_or_none.return_value = None
session.execute = AsyncMock(return_value=mock_exec_result)
data = await rag.get_workflow("wf_123")
assert data is None
@pytest.mark.asyncio
async def test_add_memory(mock_session_maker, mock_dependencies):
_, mock_memory_record, _ = mock_dependencies
maker, session = mock_session_maker
rag = MemoryRAG(maker)
mock_record = MagicMock()
mock_memory_record.return_value = mock_record
record = await rag.add_memory("text", [0.1, 0.2])
mock_memory_record.assert_called_once_with(memory_text="text", embedding=[0.1, 0.2])
session.add.assert_called_once_with(mock_record)
session.commit.assert_called_once()
session.refresh.assert_called_once_with(mock_record)
assert record == mock_record
@pytest.mark.asyncio
async def test_retrieve_memory(mock_session_maker, mock_dependencies):
_, _, mock_select = mock_dependencies
maker, session = mock_session_maker
rag = MemoryRAG(maker)
mock_statement = MagicMock()
mock_select.return_value.limit.return_value = mock_statement
mock_exec_result = MagicMock()
mock_exec_result.all.return_value = ["res1", "res2"]
session.execute = AsyncMock(return_value=mock_exec_result)
results = await rag.retrieve_memory([0.1, 0.2], 5)
session.execute.assert_called_once_with(mock_statement)
assert results == ["res1", "res2"]
+180
View File
@@ -0,0 +1,180 @@
import pytest
from unittest.mock import MagicMock, AsyncMock, patch
@pytest.fixture(autouse=True)
def mock_dependencies():
with patch("pretor.core.database.module.user.User") as mock_user_cls:
mock_user_cls.user_name = MagicMock()
with patch("pretor.core.database.module.user.select") as mock_select:
yield mock_user_cls, mock_select
@pytest.fixture
def mock_session_maker():
maker = MagicMock()
session = AsyncMock()
session.add = MagicMock()
session.delete = MagicMock()
maker.return_value.__aenter__.return_value = session
maker.__aenter__.return_value = session
maker.__aexit__ = AsyncMock()
return maker, session
@pytest.mark.asyncio
async def test_add_user(mock_session_maker, mock_dependencies):
mock_user_cls, _ = mock_dependencies
from pretor.core.database.module.user import AuthDatabase
maker, session = mock_session_maker
db = AuthDatabase(maker)
mock_user = MagicMock()
mock_user.user_name = "testuser"
mock_user.hashed_password = "hashedpw"
mock_user_cls.return_value = mock_user
mock_exec_result = MagicMock()
mock_exec_result.first.return_value = None
session.execute = AsyncMock(return_value=mock_exec_result)
user = await db.add_user("testuser", "hashedpw")
assert user.user_name == "testuser"
assert user.hashed_password == "hashedpw"
session.add.assert_called_once_with(mock_user)
session.commit.assert_called_once()
session.refresh.assert_called_once_with(mock_user)
@pytest.mark.asyncio
async def test_change_password_success(mock_session_maker, mock_dependencies):
mock_user_cls, mock_select = mock_dependencies
from pretor.core.database.module.user import AuthDatabase
maker, session = mock_session_maker
db = AuthDatabase(maker)
mock_statement = MagicMock()
mock_select.return_value.where.return_value = mock_statement
from pretor.utils.access import Accessor
mock_user = MagicMock()
mock_user.hashed_password = Accessor.hash_password("old_password")
mock_exec_result = MagicMock()
mock_exec_result.scalar_one_or_none.return_value = mock_user
session.execute = AsyncMock(return_value=mock_exec_result)
user = await db.change_password("testuser", "old_password", "new_password")
session.execute.assert_called_once_with(mock_statement)
assert user.hashed_password == "new_password"
session.add.assert_called_once_with(mock_user)
session.commit.assert_called_once()
session.refresh.assert_called_once_with(mock_user)
@pytest.mark.asyncio
async def test_change_password_user_not_exist(mock_session_maker, mock_dependencies):
mock_user_cls, mock_select = mock_dependencies
from pretor.core.database.module.user import AuthDatabase
maker, session = mock_session_maker
db = AuthDatabase(maker)
mock_exec_result = MagicMock()
mock_exec_result.scalar_one_or_none.return_value = None
session.execute = AsyncMock(return_value=mock_exec_result)
result = await db.change_password("testuser", "old_password", "new_password")
assert result is None
@pytest.mark.asyncio
async def test_change_password_wrong_password(mock_session_maker, mock_dependencies):
mock_user_cls, mock_select = mock_dependencies
from pretor.core.database.module.user import AuthDatabase
maker, session = mock_session_maker
db = AuthDatabase(maker)
from pretor.utils.access import Accessor
mock_user = MagicMock()
mock_user.hashed_password = Accessor.hash_password("actual_password")
mock_exec_result = MagicMock()
mock_exec_result.scalar_one_or_none.return_value = mock_user
session.execute = AsyncMock(return_value=mock_exec_result)
from pretor.utils.error import UserPasswordError
with pytest.raises(UserPasswordError):
await db.change_password("testuser", "old_password", "new_password")
@pytest.mark.asyncio
async def test_delete_user_success(mock_session_maker, mock_dependencies):
mock_user_cls, mock_select = mock_dependencies
from pretor.core.database.module.user import AuthDatabase
maker, session = mock_session_maker
db = AuthDatabase(maker)
mock_statement = MagicMock()
mock_select.return_value.where.return_value = mock_statement
mock_user = MagicMock()
mock_exec_result = MagicMock()
mock_exec_result.scalar_one_or_none.return_value = mock_user
session.execute = AsyncMock(return_value=mock_exec_result)
await db.delete_user("testuser")
session.execute.assert_called_once_with(mock_statement)
session.delete.assert_called_once_with(mock_user)
session.commit.assert_called_once()
@pytest.mark.asyncio
async def test_delete_user_not_exist(mock_session_maker, mock_dependencies):
mock_user_cls, mock_select = mock_dependencies
from pretor.core.database.module.user import AuthDatabase
maker, session = mock_session_maker
db = AuthDatabase(maker)
mock_exec_result = MagicMock()
mock_exec_result.scalar_one_or_none.return_value = None
session.execute = AsyncMock(return_value=mock_exec_result)
result = await db.delete_user("testuser")
assert result is None
@pytest.mark.asyncio
async def test_login_user_success(mock_session_maker, mock_dependencies):
mock_user_cls, mock_select = mock_dependencies
from pretor.core.database.module.user import AuthDatabase
maker, session = mock_session_maker
db = AuthDatabase(maker)
mock_statement = MagicMock()
mock_select.return_value.where.return_value = mock_statement
mock_user = MagicMock()
mock_exec_result = MagicMock()
mock_exec_result.scalar_one_or_none.return_value = mock_user
session.execute = AsyncMock(return_value=mock_exec_result)
user = await db.login_user("testuser")
session.execute.assert_called_once_with(mock_statement)
assert user == mock_user
@pytest.mark.asyncio
async def test_login_user_not_exist(mock_session_maker, mock_dependencies):
mock_user_cls, mock_select = mock_dependencies
from pretor.core.database.module.user import AuthDatabase
maker, session = mock_session_maker
db = AuthDatabase(maker)
mock_exec_result = MagicMock()
mock_exec_result.scalar_one_or_none.return_value = None
session.execute = AsyncMock(return_value=mock_exec_result)
result = await db.login_user("testuser")
assert result is None