schemas.py 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192
  1. #!/usr/bin/env python3
  2. # -*- coding: utf-8 -*-
  3. """
  4. Pydantic schemas for data_modules outputs.
  5. """
  6. from __future__ import annotations
  7. from typing import Any, Dict, List, Optional
  8. from pydantic import BaseModel, Field, ValidationError, ConfigDict
  9. class EntityAppeared(BaseModel):
  10. model_config = ConfigDict(extra="allow")
  11. id: str
  12. type: str
  13. mentions: List[str] = Field(default_factory=list)
  14. confidence: float = 1.0
  15. class EntityNew(BaseModel):
  16. model_config = ConfigDict(extra="allow")
  17. suggested_id: str
  18. name: str
  19. type: str
  20. tier: str = "装饰"
  21. class StateChange(BaseModel):
  22. model_config = ConfigDict(extra="allow")
  23. entity_id: str
  24. field: str
  25. old: Optional[str] = None
  26. new: str
  27. reason: Optional[str] = None
  28. class RelationshipNew(BaseModel):
  29. model_config = ConfigDict(extra="allow", populate_by_name=True)
  30. from_entity: str = Field(alias="from")
  31. to_entity: str = Field(alias="to")
  32. type: str
  33. description: Optional[str] = None
  34. chapter: Optional[int] = None
  35. class UncertainCandidate(BaseModel):
  36. model_config = ConfigDict(extra="allow")
  37. type: str
  38. id: str
  39. class UncertainMention(BaseModel):
  40. model_config = ConfigDict(extra="allow")
  41. mention: str
  42. candidates: List[UncertainCandidate] = Field(default_factory=list)
  43. confidence: float = 0.0
  44. adopted: Optional[str] = None
  45. class TimelineEvent(BaseModel):
  46. model_config = ConfigDict(extra="allow")
  47. event: str
  48. chapter: Optional[int] = None
  49. time_hint: Optional[str] = None
  50. event_type: Optional[str] = None
  51. class WorldRule(BaseModel):
  52. model_config = ConfigDict(extra="allow")
  53. rule: str
  54. scope: Optional[str] = None
  55. domain: Optional[str] = None
  56. field: Optional[str] = None
  57. class OpenLoop(BaseModel):
  58. model_config = ConfigDict(extra="allow")
  59. content: str
  60. status: Optional[str] = None
  61. urgency: Optional[float] = None
  62. planted_chapter: Optional[int] = None
  63. expected_payoff: Optional[str] = None
  64. class ReaderPromise(BaseModel):
  65. model_config = ConfigDict(extra="allow")
  66. content: str
  67. type: Optional[str] = None
  68. target: Optional[str] = None
  69. class MemoryFacts(BaseModel):
  70. model_config = ConfigDict(extra="allow")
  71. timeline_events: List[TimelineEvent] = Field(default_factory=list)
  72. world_rules: List[WorldRule] = Field(default_factory=list)
  73. open_loops: List[OpenLoop] = Field(default_factory=list)
  74. reader_promises: List[ReaderPromise] = Field(default_factory=list)
  75. class DataAgentOutput(BaseModel):
  76. model_config = ConfigDict(extra="allow")
  77. entities_appeared: List[EntityAppeared] = Field(default_factory=list)
  78. entities_new: List[EntityNew] = Field(default_factory=list)
  79. state_changes: List[StateChange] = Field(default_factory=list)
  80. relationships_new: List[RelationshipNew] = Field(default_factory=list)
  81. scenes_chunked: int = 0
  82. uncertain: List[UncertainMention] = Field(default_factory=list)
  83. warnings: List[str] = Field(default_factory=list)
  84. memory_facts: Optional[MemoryFacts] = None
  85. class ErrorSchema(BaseModel):
  86. model_config = ConfigDict(extra="allow")
  87. code: str
  88. message: str
  89. suggestion: Optional[str] = None
  90. details: Optional[Dict[str, Any]] = None
  91. def validate_data_agent_output(payload: Dict[str, Any]) -> DataAgentOutput:
  92. return DataAgentOutput.model_validate(payload)
  93. def format_validation_error(exc: ValidationError) -> Dict[str, Any]:
  94. return {
  95. "code": "SCHEMA_VALIDATION_FAILED",
  96. "message": "数据结构校验失败",
  97. "details": {"errors": exc.errors()},
  98. "suggestion": "请检查 data-agent 输出字段是否完整且类型正确",
  99. }
  100. def normalize_data_agent_output(payload: Dict[str, Any]) -> Dict[str, Any]:
  101. if not isinstance(payload, dict):
  102. return {}
  103. # 操作副本,避免修改调用方原始数据
  104. payload = dict(payload)
  105. def _ensure_list(key: str):
  106. value = payload.get(key)
  107. if value is None:
  108. payload[key] = []
  109. elif isinstance(value, list):
  110. return
  111. else:
  112. payload[key] = [value]
  113. for key in [
  114. "entities_appeared",
  115. "entities_new",
  116. "state_changes",
  117. "relationships_new",
  118. "uncertain",
  119. "warnings",
  120. ]:
  121. _ensure_list(key)
  122. memory_facts = payload.get("memory_facts")
  123. if memory_facts is None:
  124. payload["memory_facts"] = {}
  125. elif not isinstance(memory_facts, dict):
  126. payload["memory_facts"] = {}
  127. else:
  128. memory_facts = dict(memory_facts)
  129. payload["memory_facts"] = memory_facts
  130. for key in ["timeline_events", "world_rules", "open_loops", "reader_promises"]:
  131. value = memory_facts.get(key)
  132. if value is None:
  133. memory_facts[key] = []
  134. elif not isinstance(value, list):
  135. memory_facts[key] = [value]
  136. payload.setdefault("scenes_chunked", 0)
  137. return payload