test_decode_service.py 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
  1. from __future__ import annotations
  2. from uuid import UUID, uuid4
  3. from core.models import Post
  4. from decode_content import gates
  5. from decode_content.contracts import load_decode_contracts
  6. from decode_content.models import ContractSnapshot, DecodeJob, DecodeResult, KnowledgeParticle, PayloadDraft, ReadResult, ScopeResult
  7. from decode_content.scoping import NORMALIZE
  8. from decode_content.service import DecodeService, slugify
  9. class FakeRepo:
  10. def __init__(self):
  11. self.jobs = []
  12. self.results = []
  13. self.particles = []
  14. self.scopes = []
  15. self.payloads = []
  16. self.snapshots = []
  17. def create_decode_job(self, *, item_id: UUID, status: str = "pending", metadata=None):
  18. job = DecodeJob(id=uuid4(), item_id=item_id, status=status, metadata=metadata or {})
  19. self.jobs.append(job)
  20. return job
  21. def save_decode_result(self, **kwargs):
  22. result = DecodeResult(id=uuid4(), **kwargs)
  23. self.results.append(result)
  24. return result
  25. def save_knowledge_particle(self, **kwargs):
  26. particle = KnowledgeParticle(id=uuid4(), **kwargs)
  27. self.particles.append(particle)
  28. return particle
  29. def save_scope_result(self, **kwargs):
  30. scope = ScopeResult(id=uuid4(), **kwargs)
  31. self.scopes.append(scope)
  32. return scope
  33. def save_payload_draft(self, **kwargs):
  34. payload = PayloadDraft(id=uuid4(), **kwargs)
  35. self.payloads.append(payload)
  36. return payload
  37. def save_contract_snapshot(self, **kwargs):
  38. snapshot = ContractSnapshot(id=uuid4(), **kwargs)
  39. self.snapshots.append(snapshot)
  40. return snapshot
  41. def test_slugify_formal_helper():
  42. assert slugify("分镜脚本") == "分镜脚本"
  43. assert slugify("A B!c") == "a-b-c"
  44. assert slugify("") == "run"
  45. def test_decode_service_short_circuits_empty_read():
  46. item_id = uuid4()
  47. repo = FakeRepo()
  48. service = DecodeService(repository=repo, reader=lambda post: ReadResult(is_empty=True))
  49. out = service.decode_post(item_id=item_id, post=Post(id="xhs_1", platform="xiaohongshu", url="u", content_id="1"))
  50. assert out.status == "skipped"
  51. assert repo.results[0].status == "skipped"
  52. assert repo.particles == []
  53. assert repo.snapshots
  54. def test_decode_service_full_flow_with_fake_llm():
  55. item_id = uuid4()
  56. repo = FakeRepo()
  57. contract = load_decode_contracts()
  58. def fake_chat(system, user, timeout=None):
  59. if system == gates.GATE_ADMIT:
  60. return {"in_scope": True}
  61. if system == gates.GATE_REFUTE:
  62. return {"out_of_scope": False}
  63. if system == gates.GATE_HOW_ADMIT:
  64. return {"is_real_how": True}
  65. if system == gates.GATE_HOW_REFUTE:
  66. return {"is_fake": False}
  67. if system == gates.GATE_WHY_REFUTE:
  68. return {"not_why": False}
  69. if system == NORMALIZE:
  70. return {"映射": {}}
  71. if system == contract.phase1:
  72. return {
  73. "knowledges": [
  74. {
  75. "id": "k1",
  76. "type": "how",
  77. "role": "主",
  78. "title": "脚本搭建流程",
  79. "purpose": "搭出脚本",
  80. "业务阶段": ["脚本"],
  81. "steps": [
  82. {
  83. "id": "s1",
  84. "input": "选题",
  85. "directive": "先定受众",
  86. "output": "受众画像",
  87. "创作阶段": "定向",
  88. }
  89. ],
  90. }
  91. ]
  92. }
  93. if system == contract.phase2:
  94. return {
  95. "scopes": [
  96. {
  97. "knowledge_id": "k1",
  98. "step_id": "s1",
  99. "items": [{"scope_type": "intent", "value": "吸引注意"}],
  100. }
  101. ]
  102. }
  103. raise AssertionError(system[:80])
  104. service = DecodeService(
  105. repository=repo,
  106. contract=contract,
  107. chat_json_fn=fake_chat,
  108. reader=lambda post: ReadResult(text="这是一条脚本创作知识"),
  109. )
  110. out = service.decode_post(
  111. item_id=item_id,
  112. post=Post(id="xhs_1", platform="xiaohongshu", url="u", content_id="1", title="脚本教程"),
  113. )
  114. assert out.status == "decoded"
  115. assert repo.results[0].status == "decoded"
  116. assert repo.particles[0].particle_type == "how"
  117. assert repo.scopes[0].scope_type == "intent"
  118. assert repo.payloads[0].payload["dim_attributes"] == ["how工序"]
  119. assert repo.snapshots[0].contract_name == "创作知识提取-skill"