初始化提交
This commit is contained in:
commit
887c1f449e
963 files changed
+166869
No files matched your search
@@ -0,0 +1,142 @@
|
||||
"""大周天候选生成 + 半自动应用调度测试。"""
|
||||
|
||||
import pytest
|
||||
|
||||
from app.application.big_cycle import drain_pending_big_cycles, maybe_trigger_big_cycle
|
||||
from app.core.config import settings
|
||||
from app.db.session import AsyncSessionLocal
|
||||
from app.models.memory import Memory
|
||||
from app.repositories import big_cycle as big_cycle_repo
|
||||
from app.repositories import evolution as evolution_repo
|
||||
from app.services.characters import get_active_character_model
|
||||
|
||||
_MIXED_RESPONSE = (
|
||||
"["
|
||||
'{"type":"personality","level":"L4","scope":"character_scope",'
|
||||
'"title":"身份演化","proposal":"调整身份表达",'
|
||||
'"delta":{"identity_profile":"新的身份描述"},'
|
||||
'"confidence":0.7,"benefit_score":0.6,"risk_score":0.4,'
|
||||
'"auto_apply_allowed":false,"evidence":[0]},'
|
||||
'{"type":"procedural","level":"L2","scope":"task_scope",'
|
||||
'"title":"确认假设","proposal":"技术问题先确认前提",'
|
||||
'"delta":{"title":"确认假设","content":"先确认前提再作答"},'
|
||||
'"confidence":0.8,"benefit_score":0.7,"risk_score":0.2,'
|
||||
'"auto_apply_allowed":true,"evidence":[0]}'
|
||||
"]"
|
||||
)
|
||||
|
||||
|
||||
async def _seed_memories(character_id: str, count: int) -> None:
|
||||
async with AsyncSessionLocal() as session:
|
||||
for i in range(count):
|
||||
session.add(
|
||||
Memory(
|
||||
character_id=character_id,
|
||||
type="user",
|
||||
title=f"记忆{i}",
|
||||
content=f"内容{i}",
|
||||
)
|
||||
)
|
||||
await session.commit()
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_evolution_chat(monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
async def fake_chat(*_args, **_kwargs):
|
||||
return _MIXED_RESPONSE
|
||||
|
||||
monkeypatch.setattr("app.application.big_cycle._call_model", fake_chat)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_big_cycle_generates_candidates_and_auto_applies(
|
||||
mock_evolution_chat, # noqa: ARG001 - pytest fixture 靠 monkeypatch 副作用生效
|
||||
) -> None:
|
||||
async with AsyncSessionLocal() as session:
|
||||
character = await get_active_character_model(session)
|
||||
await _seed_memories(character.id, 20)
|
||||
|
||||
cycle_id = await maybe_trigger_big_cycle(character.id, trigger="test")
|
||||
assert cycle_id is not None
|
||||
await drain_pending_big_cycles()
|
||||
|
||||
async with AsyncSessionLocal() as session:
|
||||
cycle = await big_cycle_repo.get_cycle(session, cycle_id)
|
||||
candidates, total = await evolution_repo.list_candidates(session, character.id)
|
||||
|
||||
assert cycle is not None
|
||||
assert cycle.status == "completed"
|
||||
assert cycle.evaluation_snapshot_json is not None
|
||||
assert total == 2
|
||||
|
||||
personality = next(c for c in candidates if c.type == "personality")
|
||||
assert personality.status == "pending" # L4 auto_apply_allowed=false
|
||||
assert personality.level == "L4"
|
||||
assert personality.source == "big_cycle"
|
||||
assert personality.cycle_id == cycle_id
|
||||
|
||||
procedural = next(c for c in candidates if c.type == "procedural")
|
||||
assert procedural.status == "applied" # L2 auto_apply_allowed=true
|
||||
assert procedural.resulting_target_id is not None
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_auto_apply_degrades_when_applier_missing(
|
||||
monkeypatch: pytest.MonkeyPatch,
|
||||
) -> None:
|
||||
response = (
|
||||
'[{"type":"tool_strategy","level":"L2","scope":"task_scope",'
|
||||
'"title":"工具策略","proposal":"偏好shell",'
|
||||
'"delta":{},"auto_apply_allowed":true}]'
|
||||
)
|
||||
|
||||
async def fake_chat(*_args, **_kwargs):
|
||||
return response
|
||||
|
||||
monkeypatch.setattr("app.application.big_cycle._call_model", fake_chat)
|
||||
|
||||
async with AsyncSessionLocal() as session:
|
||||
character = await get_active_character_model(session)
|
||||
await _seed_memories(character.id, 20)
|
||||
|
||||
await maybe_trigger_big_cycle(character.id, trigger="test")
|
||||
await drain_pending_big_cycles()
|
||||
|
||||
async with AsyncSessionLocal() as session:
|
||||
candidates, _ = await evolution_repo.list_candidates(session, character.id)
|
||||
assert len(candidates) == 1
|
||||
cand = candidates[0]
|
||||
assert cand.status == "pending" # applier 缺失,降级不自动应用
|
||||
assert cand.auto_apply_allowed is True
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_big_cycle_uses_configured_model_hint(
|
||||
monkeypatch: pytest.MonkeyPatch,
|
||||
) -> None:
|
||||
captured: dict = {}
|
||||
result = type("FakeResponse", (), {"content": "[]"})()
|
||||
|
||||
class _FakeManager:
|
||||
def to_message_list(self, *_args: object, **_kwargs: object) -> list:
|
||||
return []
|
||||
|
||||
async def chat(self, messages: object, model_hint: str | None = None) -> object: # noqa: ARG002
|
||||
captured["model_hint"] = model_hint
|
||||
return result
|
||||
|
||||
def fake_get_provider_manager() -> _FakeManager:
|
||||
return _FakeManager()
|
||||
|
||||
monkeypatch.setattr(
|
||||
"app.application.big_cycle._get_provider_manager", fake_get_provider_manager
|
||||
)
|
||||
|
||||
async with AsyncSessionLocal() as session:
|
||||
character = await get_active_character_model(session)
|
||||
await _seed_memories(character.id, 20)
|
||||
|
||||
await maybe_trigger_big_cycle(character.id, trigger="test")
|
||||
await drain_pending_big_cycles()
|
||||
|
||||
assert captured["model_hint"] == settings.big_cycle_model
|
||||
Reference in new issue
Block a user