from __future__ import annotations from .shared import * from .helpers_inbox_filters import ( _blocking_reason, _compact_text, _extract_gate_projection_from_work_item, _is_low_value_closure_payload, _next_step_text, _normalize_approval_requirement, _normalize_module_list, _priority_from_review, _safe_int, _stage_text, ) async def _build_closure_payload_overrides( *, db: AsyncSession, tenant_id: int, rows: list[tuple[WorkItem, IngestEvent, Optional[Project]]], ) -> dict[int, dict[str, Any]]: if not rows: return {} service = AIPMOKernelService(db, tenant_id=tenant_id) overrides: dict[int, dict[str, Any]] = {} for row, ingest, _project in rows: payload = ingest.payload if isinstance(ingest.payload, dict) else {} if not isinstance(payload, dict): continue has_closure = bool( str(payload.get("closure_summary") or "").strip() or str(payload.get("closure_dialogue") or "").strip() or (isinstance(payload.get("closure_modules"), list) and payload.get("closure_modules")) ) if has_closure and not _is_low_value_closure_payload(payload): continue message_text = str(payload.get("message_text") or "").strip() if not message_text: continue source_system = str(row.source_system or "").strip() or "pmflow_manual" captured_at_seed: Optional[str] = str(payload.get("captured_at") or "").strip() or None if not captured_at_seed and isinstance(row.created_at, datetime): captured_at_seed = row.created_at.isoformat() closure_payload = await service._build_message_closure_payload( source_system=source_system, channel_ref=str(payload.get("channel_ref") or "").strip() or None, sender=str(payload.get("sender") or "").strip(), source_sender_open_id=str(payload.get("source_sender_open_id") or "").strip() or None, message_text=message_text, source_message_id=str(row.source_message_id or "").strip() or None, owner_user_id=_safe_int(row.owner_user_id), project_id=_safe_int(row.project_id), captured_at=captured_at_seed, ) if not any( ( str(closure_payload.get("closure_summary") or "").strip(), str(closure_payload.get("closure_dialogue") or "").strip(), isinstance(closure_payload.get("closure_modules"), list) and closure_payload.get("closure_modules"), ) ): continue overrides[int(row.id)] = { **payload, "closure_summary": closure_payload.get("closure_summary"), "closure_dialogue": closure_payload.get("closure_dialogue"), "closure_modules": closure_payload.get("closure_modules"), "closure_turns": closure_payload.get("closure_turns"), } return overrides def _serialize_work_item_summary( *, row: WorkItem, ingest: IngestEvent, project: Optional[Project], latest_decision_projection: Optional[dict[str, Any]] = None, payload_override: Optional[dict[str, Any]] = None, ) -> dict[str, Any]: payload = payload_override if isinstance(payload_override, dict) else ( ingest.payload if isinstance(ingest.payload, dict) else {} ) message_text = str(payload.get("message_text") or "").strip() closure_dialogue = str(payload.get("closure_dialogue") or "").strip() or None closure_summary = str(payload.get("closure_summary") or "").strip() or None closure_modules = _normalize_module_list(payload.get("closure_modules")) closure_turns = payload.get("closure_turns") if isinstance(payload.get("closure_turns"), list) else [] source_captured_at = str(payload.get("captured_at") or "").strip() or None source_sender = str(payload.get("sender") or "").strip() or None source_channel_ref = str(payload.get("channel_ref") or "").strip() or None draft_content = row.draft_content if isinstance(row.draft_content, dict) else {} draft_title = _compact_text(str(draft_content.get("title") or "").strip(), 64) draft_summary = _compact_text(str(draft_content.get("summary") or "").strip(), 180) summary_seed = closure_summary or message_text or _next_step_text(row) or row.intent or "AI PMO 工作项" summary = _compact_text(summary_seed, 180) compact_message = re.sub(r"\s+", "", message_text).lower() if message_text else "" prefer_closure_title = bool(compact_message and GENERIC_SOURCE_TEXT_RE.match(compact_message)) title_seed = ( (closure_summary or message_text) if prefer_closure_title else (message_text or closure_summary or f"AI PMO · {row.intent or 'work_item'}") ) title = _compact_text(title_seed, 64) if str(row.state or "").strip() == "draft_pending_confirm": if draft_title: title = draft_title if draft_summary: summary = draft_summary priority, priority_rank = _priority_from_review(row.review_summary) project_name = None if project is not None: project_name = str(project.title or project.name or "").strip() or None project_id = _safe_int(row.project_id) decision_projection = latest_decision_projection if isinstance(latest_decision_projection, dict) else {} latest_policy_gate_code = str(decision_projection.get("latest_policy_gate_code") or "").strip() or None approval_requirement = _normalize_approval_requirement(decision_projection.get("approval_requirement")) if latest_policy_gate_code is None or approval_requirement is None: fallback_projection = _extract_gate_projection_from_work_item(row) if latest_policy_gate_code is None: latest_policy_gate_code = str(fallback_projection.get("latest_policy_gate_code") or "").strip() or None if approval_requirement is None: approval_requirement = _normalize_approval_requirement(fallback_projection.get("approval_requirement")) return { "id": int(row.id), "source_system": row.source_system, "source_message_id": row.source_message_id, "source_message_text": _compact_text(message_text, 600) or None, "source_conversation_closure": _compact_text(closure_dialogue, 2000) or None, "source_captured_at": source_captured_at, "source_sender": source_sender, "source_channel_ref": source_channel_ref, "closure_summary": closure_summary, "implementation_modules": closure_modules, "source_closure_turns": closure_turns[:4], "state": row.state, "intent": row.intent, "decision_level": row.decision_level, "project_id": project_id, "project_name": project_name, "signal_id": _safe_int(row.signal_id), "reply_template_key": row.reply_template_key, "title": title or f"工作项#{int(row.id)}", "summary": summary, "ai_plan": _compact_text(((row.action_bundle or {}) if isinstance(row.action_bundle, dict) else {}).get("summary") or "", 180) or None, "blocking_reason": _blocking_reason(row), "next_step_text": _next_step_text(row), "stage_text": _stage_text(str(row.state or "")), "priority": priority, "priority_rank": priority_rank, "latest_policy_gate_code": latest_policy_gate_code, "approval_requirement": approval_requirement, "target_url": f"/projects/{project_id}/board" if project_id is not None else None, "review_summary": row.review_summary, "execution_result": row.execution_result, "draft_content": draft_content or None, "draft_expires_at": row.draft_expires_at.isoformat() if row.draft_expires_at else None, "draft_version": int(row.draft_version or 0), "confirm_message_id": str(row.confirm_message_id or "").strip() or None, "created_at": row.created_at.isoformat() if row.created_at else None, "updated_at": row.updated_at.isoformat() if row.updated_at else None, }