初始化提交
This commit is contained in:
commit
887c1f449e
963 files changed
+166869
No files matched your search
@@ -0,0 +1,164 @@
|
||||
"""大周天抽取候选审核服务(记忆候选 + 世界实体候选)。
|
||||
|
||||
记忆候选 approve 派生为 ``memories``(source=big_cycle);世界实体候选 approve
|
||||
→ confirmed 进入世界模型。审核仅 pending 可操作(见 [大周天记忆/世界抽取落地设计]
|
||||
../docs/domain/core-domains.md §5)。
|
||||
"""
|
||||
|
||||
from app.core.errors import AppError
|
||||
from app.core.time import utc_now
|
||||
from app.db.session import AsyncSessionLocal
|
||||
from app.models.memory import Memory
|
||||
from app.repositories import candidates as candidates_repo
|
||||
from app.repositories import world as world_repo
|
||||
from app.schemas.candidates import MemoryCandidateResponse
|
||||
from app.schemas.common import Page
|
||||
from app.schemas.world import WorldEntityResponse
|
||||
from app.services.audit import record_audit_event
|
||||
from app.services.serializers import (
|
||||
character_world_entity_to_response,
|
||||
memory_candidate_to_response,
|
||||
)
|
||||
|
||||
|
||||
async def list_memory_proposals(
|
||||
*,
|
||||
character_id: str,
|
||||
source: str | None = None,
|
||||
status: str | None = None,
|
||||
page: int = 1,
|
||||
page_size: int = 20,
|
||||
) -> Page[MemoryCandidateResponse]:
|
||||
async with AsyncSessionLocal() as session:
|
||||
all_items = await candidates_repo.list_memory_candidates(
|
||||
session, character_id, source=source, status=status
|
||||
)
|
||||
start = max(page - 1, 0) * page_size
|
||||
items = all_items[start : start + page_size]
|
||||
return Page(
|
||||
items=[memory_candidate_to_response(i) for i in items],
|
||||
page=page,
|
||||
page_size=page_size,
|
||||
total=len(all_items),
|
||||
)
|
||||
|
||||
|
||||
async def approve_memory_proposal(
|
||||
candidate_id: str,
|
||||
) -> MemoryCandidateResponse:
|
||||
async with AsyncSessionLocal() as session:
|
||||
candidate = await candidates_repo.get_memory_candidate(session, candidate_id)
|
||||
if candidate is None:
|
||||
raise AppError("not_found", "记忆候选不存在。")
|
||||
if candidate.status != "pending":
|
||||
raise AppError("conflict", f"候选当前状态为 {candidate.status},不可审核。")
|
||||
session.add(
|
||||
Memory(
|
||||
character_id=candidate.character_id,
|
||||
type=candidate.type,
|
||||
title=candidate.title,
|
||||
content=candidate.content,
|
||||
source="big_cycle",
|
||||
confidence=candidate.confidence,
|
||||
importance=candidate.importance,
|
||||
sensitivity=candidate.sensitivity,
|
||||
evidence_ids_json=candidate.evidence_ids_json,
|
||||
status="active",
|
||||
)
|
||||
)
|
||||
candidate.status = "applied"
|
||||
candidate.reviewed_at = utc_now()
|
||||
await record_audit_event(
|
||||
session,
|
||||
action="extraction.memory.applied",
|
||||
actor="user",
|
||||
character_id=candidate.character_id,
|
||||
input_summary=candidate.title[:200],
|
||||
resource_type="memory_candidate",
|
||||
resource_id=candidate.id,
|
||||
)
|
||||
await session.commit()
|
||||
await session.refresh(candidate)
|
||||
return memory_candidate_to_response(candidate)
|
||||
|
||||
|
||||
async def reject_memory_proposal(
|
||||
candidate_id: str,
|
||||
) -> MemoryCandidateResponse:
|
||||
return await _reject_memory(candidate_id)
|
||||
|
||||
|
||||
async def list_world_entities_pending(
|
||||
*,
|
||||
character_id: str,
|
||||
status: str | None = None,
|
||||
page: int = 1,
|
||||
page_size: int = 20,
|
||||
) -> Page[WorldEntityResponse]:
|
||||
async with AsyncSessionLocal() as session:
|
||||
items, total = await world_repo.list_entities(
|
||||
session, character_id, status=status, page=page, page_size=page_size
|
||||
)
|
||||
return Page(
|
||||
items=[character_world_entity_to_response(i) for i in items],
|
||||
page=page,
|
||||
page_size=page_size,
|
||||
total=total,
|
||||
)
|
||||
|
||||
|
||||
async def approve_world_entity(entity_id: str) -> WorldEntityResponse:
|
||||
return await _review_world_entity(entity_id, "confirmed", decision="auto")
|
||||
|
||||
|
||||
async def reject_world_entity(entity_id: str) -> WorldEntityResponse:
|
||||
return await _review_world_entity(entity_id, "rejected", decision="denied")
|
||||
|
||||
|
||||
async def _reject_memory(candidate_id: str) -> MemoryCandidateResponse:
|
||||
async with AsyncSessionLocal() as session:
|
||||
candidate = await candidates_repo.get_memory_candidate(session, candidate_id)
|
||||
if candidate is None:
|
||||
raise AppError("not_found", "记忆候选不存在。")
|
||||
if candidate.status != "pending":
|
||||
raise AppError("conflict", f"候选当前状态为 {candidate.status},不可审核。")
|
||||
candidate.status = "rejected"
|
||||
candidate.reviewed_at = utc_now()
|
||||
await record_audit_event(
|
||||
session,
|
||||
action="extraction.memory.rejected",
|
||||
actor="user",
|
||||
decision="denied",
|
||||
character_id=candidate.character_id,
|
||||
input_summary=candidate.title[:200],
|
||||
resource_type="memory_candidate",
|
||||
resource_id=candidate.id,
|
||||
)
|
||||
await session.commit()
|
||||
await session.refresh(candidate)
|
||||
return memory_candidate_to_response(candidate)
|
||||
|
||||
|
||||
async def _review_world_entity(
|
||||
entity_id: str, target_status: str, *, decision: str
|
||||
) -> WorldEntityResponse:
|
||||
async with AsyncSessionLocal() as session:
|
||||
entity = await world_repo.get_entity(session, entity_id)
|
||||
if entity is None:
|
||||
raise AppError("not_found", "世界实体不存在。")
|
||||
if entity.status != "pending":
|
||||
raise AppError("conflict", f"实体当前状态为 {entity.status},不可审核。")
|
||||
entity.status = target_status
|
||||
await record_audit_event(
|
||||
session,
|
||||
action=f"extraction.world.{target_status}",
|
||||
actor="user",
|
||||
decision=decision,
|
||||
character_id=entity.character_id,
|
||||
input_summary=entity.name[:200],
|
||||
resource_type="character_world_entity",
|
||||
resource_id=entity.id,
|
||||
)
|
||||
await session.commit()
|
||||
await session.refresh(entity)
|
||||
return character_world_entity_to_response(entity)
|
||||
Reference in new issue
Block a user