"""AI PMO kernel closure helpers mixin.""" from __future__ import annotations from . import ai_pmo_kernel_common as _common for _name, _value in vars(_common).items(): if not _name.startswith("__"): globals()[_name] = _value class KernelClosureHelpersMixin: @staticmethod def _normalize_dialogue_text(value: Any, *, max_len: int) -> str: text = re.sub(r"\s+", " ", str(value or "")).strip() if not text: return "" return text[:max_len] @staticmethod def _parse_datetime_like(value: Any) -> Optional[datetime]: if isinstance(value, datetime): dt = value else: raw = str(value or "").strip() if not raw: return None try: dt = datetime.fromisoformat(raw.replace("Z", "+00:00")) except Exception: return None if dt.tzinfo is not None: return dt.astimezone(timezone.utc).replace(tzinfo=None) return dt @staticmethod def _normalize_feishu_user_text(value: Any, *, max_len: int) -> str: text = str(value or "") text = re.sub(r"^@_user_\d+\s*", "", text) text = re.sub(r"]*>", " ", text, flags=re.IGNORECASE) text = re.sub(r"\s+", " ", text).strip() if not text: return "" return text[:max_len] @classmethod def _extract_assistant_text_from_log_payload(cls, payload: Any) -> str: if not isinstance(payload, dict): return "" candidates = ( payload.get("followup_reply"), payload.get("direct_reply_text"), payload.get("clarification_message"), payload.get("answer"), payload.get("reason"), payload.get("result_summary"), payload.get("error_message"), payload.get("message"), ) for item in candidates: text = cls._normalize_dialogue_text(item, max_len=420) if not text: continue if any(token in text for token in ("chat_id", "message_type", "sender_name")) and len(text) < 48: continue return text return "" @staticmethod def _merge_closure_turns( base_turns: list[dict[str, str]], extra_turns: list[dict[str, str]], *, prepend: bool = False, ) -> list[dict[str, str]]: if not extra_turns: return list(base_turns) merged = list(extra_turns) + list(base_turns) if prepend else list(base_turns) + list(extra_turns) compact: list[dict[str, str]] = [] for row in merged: role = str(row.get("role") or "").strip().lower() text = str(row.get("text") or "").strip() if role not in {"user", "assistant"} or not text: continue if compact and compact[-1].get("role") == role and compact[-1].get("text") == text: continue compact.append({"role": role, "text": text}) return compact[-8:] @staticmethod def _turns_have_assistant(turns: list[dict[str, str]]) -> bool: return any(str(turn.get("role") or "").strip().lower() == "assistant" and str(turn.get("text") or "").strip() for turn in turns)