planning.py 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221
  1. from __future__ import annotations
  2. from typing import Any
  3. from .common import (
  4. EventLoader,
  5. context_input_fields,
  6. decision_context,
  7. decision_event_refs,
  8. event_notices,
  9. event_unit,
  10. find_raw_round,
  11. find_round,
  12. load_events,
  13. )
  14. from .schema import field, payload
  15. def project_objective(
  16. bundle: dict[str, Any], view: dict[str, Any], load_event: EventLoader
  17. ) -> dict[str, Any]:
  18. record = bundle.get("record") or {}
  19. node = view.get("objective") or {}
  20. context = decision_context(node)
  21. events = load_events(load_event, decision_event_refs(context))
  22. inputs = context_input_fields(context, "objective", events)
  23. output = field(
  24. "objective:current-value",
  25. "完整创作方向",
  26. record.get("script_direction"),
  27. source_kind="database",
  28. source_label="script_build_record",
  29. source_ref="run:objective",
  30. field_path="script_direction",
  31. relation="persisted-output",
  32. )
  33. outputs = [output]
  34. if context.get("decisionItems"):
  35. outputs.append(field(
  36. "objective:goals",
  37. "创作目标",
  38. context.get("decisionItems"),
  39. source_kind="calculation",
  40. source_label="创作方向结构化解析",
  41. source_ref="run:objective",
  42. field_path="decision.decisionItems",
  43. relation="persisted-output",
  44. ))
  45. if context.get("constraints"):
  46. outputs.append(field(
  47. "objective:dimensions",
  48. "评估维度与约束",
  49. context.get("constraints"),
  50. source_kind="calculation",
  51. source_label="创作方向结构化解析",
  52. source_ref="run:objective",
  53. field_path="decision.constraints",
  54. relation="persisted-output",
  55. ))
  56. notices = event_notices(events)
  57. if inputs and any(item["relation"] == "available-upstream" for item in inputs):
  58. notices.append({"level": "info", "message": "可确认是上游信息,但无法确认是否被本次决策采用。"})
  59. return payload(
  60. "objective",
  61. "business-record",
  62. inputs=inputs,
  63. units=[event_unit(item) for item in events],
  64. outputs=outputs,
  65. runtime_summary=(f"找到 {len(events)} 个保存/修订事件。" if events else "只能确认当前保存的业务值,未找到完整形成过程。"),
  66. notices=notices,
  67. completeness=context.get("completeness") if context.get("completeness") in {"complete", "partial", "missing"} else None,
  68. business_modules=[
  69. {"id": "direction", "title": "完整创作方向原文", "sourceIds": [output["id"]]},
  70. {"id": "inputs", "title": "形成前读取", "sourceIds": [item["id"] for item in inputs]},
  71. {"id": "structure", "title": "目标与评估维度", "sourceIds": [item["id"] for item in outputs[1:]]},
  72. {"id": "revisions", "title": "修订记录", "sourceIds": [output["id"]]},
  73. ],
  74. card_fields=[
  75. {"key": "headline", "label": "核心目标", "sourceIds": [output["id"]], "transform": "使用结构化解析的 headline"},
  76. {"key": "targetCount", "label": "目标数", "sourceIds": ["objective:goals"], "transform": "列表计数"},
  77. {"key": "dimensionCount", "label": "评估维度数", "sourceIds": ["objective:dimensions"], "transform": "列表计数"},
  78. ],
  79. )
  80. def project_round_goal(
  81. detail_ref: str,
  82. round_index: int,
  83. bundle: dict[str, Any],
  84. view: dict[str, Any],
  85. load_event: EventLoader,
  86. ) -> dict[str, Any]:
  87. raw = find_raw_round(bundle, round_index) or {}
  88. round_ = find_round(view, round_index) or {}
  89. node = round_.get("goal") or {}
  90. context = decision_context(node)
  91. refs = decision_event_refs(context)
  92. refs.extend(
  93. f"event:{item.get('id')}"
  94. for item in bundle.get("events") or []
  95. if item.get("event_name") == "begin_round"
  96. )
  97. events = load_events(load_event, refs)
  98. inputs = context_input_fields(context, f"round:{round_index}:goal", events)
  99. begin_round_fields = [
  100. field(
  101. f"round:{round_index}:goal:begin:{event.get('id')}",
  102. "进入本轮时提交的目标",
  103. ((event.get("input") or {}).get("content") or {}).get("goal")
  104. if isinstance((event.get("input") or {}).get("content"), dict)
  105. else (event.get("input") or {}).get("content"),
  106. source_kind="runtime-event",
  107. source_label="begin_round 运行事件",
  108. source_ref=f"event:{event.get('id')}",
  109. field_path="input.content.goal",
  110. relation="run-output",
  111. )
  112. for event in events
  113. if event.get("event_name") == "begin_round"
  114. and (
  115. (
  116. isinstance((event.get("output") or {}).get("content"), dict)
  117. and int(((event.get("output") or {}).get("content") or {}).get("round_index") or 0) == round_index
  118. )
  119. or (
  120. isinstance((event.get("input") or {}).get("content"), dict)
  121. and ((event.get("input") or {}).get("content") or {}).get("goal") == raw.get("goal")
  122. )
  123. )
  124. ]
  125. outputs = [field(
  126. f"round:{round_index}:goal:value",
  127. "Goal 原文",
  128. raw.get("goal"),
  129. source_kind="database",
  130. source_label="script_build_round",
  131. source_ref=detail_ref,
  132. field_path="goal",
  133. relation="persisted-output",
  134. )]
  135. return payload(
  136. "round-goal",
  137. "business-record",
  138. inputs=[*inputs, *begin_round_fields],
  139. units=[event_unit(item) for item in events],
  140. outputs=outputs,
  141. runtime_summary=(f"通过 {len(events)} 个 begin_round/读取事件形成本轮目标。" if events else "本轮目标有业务记录,但形成过程未完整保存。"),
  142. notices=event_notices(events) + ([{"level": "info", "message": "可确认是上游信息,但无法确认是否被本次决策采用。"}] if any(item["relation"] == "available-upstream" for item in inputs) else []),
  143. completeness=context.get("completeness") if context.get("completeness") in {"complete", "partial", "missing"} else None,
  144. business_modules=[
  145. {"id": "goal", "title": "Goal 原文", "sourceIds": [outputs[0]["id"]]},
  146. {"id": "why", "title": "进入本轮时提交的目标", "sourceIds": [item["id"] for item in begin_round_fields]},
  147. {"id": "dependencies", "title": "本轮形成前可见的上游", "sourceIds": [item["id"] for item in inputs]},
  148. ],
  149. card_fields=[
  150. {"key": "headline", "label": "本轮目标", "sourceIds": [outputs[0]["id"]], "transform": "使用结构化目标首项"},
  151. {"key": "basis", "label": "形成前依据", "sourceIds": [item["id"] for item in inputs[:1]], "transform": "取第一个明确上游依据"},
  152. ],
  153. )
  154. def project_implementation_plan(
  155. detail_ref: str,
  156. round_index: int,
  157. bundle: dict[str, Any],
  158. view: dict[str, Any],
  159. load_event: EventLoader,
  160. ) -> dict[str, Any]:
  161. raw = find_raw_round(bundle, round_index) or {}
  162. round_ = find_round(view, round_index) or {}
  163. node = ((round_.get("plan") or {}).get("node") or {})
  164. context = decision_context(node)
  165. refs = decision_event_refs(context)
  166. refs.extend(
  167. f"event:{item.get('id')}"
  168. for item in bundle.get("events") or []
  169. if item.get("event_name") == "record_multipath_plan"
  170. and int(item.get("round_index") or 0) == round_index
  171. )
  172. events = load_events(load_event, refs)
  173. inputs = context_input_fields(context, f"round:{round_index}:plan", events)
  174. outputs = [
  175. field(f"round:{round_index}:plan:mode", "规划方式", raw.get("race_or_divide"), source_kind="database", source_label="script_build_round", source_ref=detail_ref, field_path="race_or_divide", relation="persisted-output"),
  176. field(f"round:{round_index}:plan:routes", "实现路线", raw.get("multipath_plan") or [], source_kind="database", source_label="script_build_round", source_ref=detail_ref, field_path="multipath_plan", relation="persisted-output"),
  177. field(f"round:{round_index}:plan:reason", "规划理由", raw.get("plan_note"), source_kind="database", source_label="script_build_round", source_ref=detail_ref, field_path="plan_note", relation="persisted-output"),
  178. ]
  179. revision_fields = [
  180. field(
  181. f"round:{round_index}:plan:revision:{event.get('id')}",
  182. f"规划保存 Event {event.get('id')}",
  183. (event.get("input") or {}).get("content"),
  184. source_kind="runtime-event",
  185. source_label="record_multipath_plan 运行事件",
  186. source_ref=f"event:{event.get('id')}",
  187. field_path="input.content",
  188. relation="run-output",
  189. )
  190. for event in events
  191. if event.get("event_name") == "record_multipath_plan"
  192. ]
  193. return payload(
  194. "implementation-plan",
  195. "business-record",
  196. inputs=inputs,
  197. units=[event_unit(item) for item in events],
  198. outputs=[*outputs, *revision_fields],
  199. runtime_summary=(f"找到 {len(events)} 个规划保存/修订事件。" if events else "展示数据库当前保存的实现规划。"),
  200. notices=event_notices(events),
  201. completeness=context.get("completeness") if context.get("completeness") in {"complete", "partial", "missing"} else None,
  202. business_modules=[
  203. {"id": "mode", "title": "规划模式与理由", "sourceIds": [outputs[0]["id"], outputs[2]["id"]]},
  204. {"id": "routes", "title": "逐条实现路线", "sourceIds": [outputs[1]["id"]]},
  205. {"id": "basis", "title": "规划依据", "sourceIds": [item["id"] for item in inputs]},
  206. {"id": "revisions", "title": "规划保存与修订记录", "sourceIds": [item["id"] for item in revision_fields]},
  207. ],
  208. card_fields=[
  209. {"key": "mode", "label": "模式", "sourceIds": [outputs[0]["id"]], "transform": "规范化为单路/赛马/分工"},
  210. {"key": "routeCount", "label": "路线数", "sourceIds": [outputs[1]["id"]], "transform": "列表计数"},
  211. {"key": "routeScope", "label": "路线作用范围", "sourceIds": [outputs[1]["id"]], "transform": "取每路结构化 target"},
  212. ],
  213. )