"""Builder profile-state helpers: drafts, gap tracking, and text utilities.""" from __future__ import annotations from copy import deepcopy import re from typing import Any def ensure_builder_state(profile: dict[str, Any]) -> dict[str, Any]: state = profile.setdefault("builder", {}) state.setdefault("active_section", None) state.setdefault("identity_draft", {}) state.setdefault("pending_entry", None) state.setdefault("editing_entry_id", None) state.setdefault("editing_base_entry", None) state.setdefault("selection_candidates", []) state.setdefault("gap_state", {"asked": [], "skipped": [], "rounds": 0}) state.setdefault("revision_mode", False) state.setdefault("last_confirmed_entry", None) state.setdefault("pending_skill_candidates", []) state.setdefault("last_stream_phases", []) state.setdefault("imported", False) return state def _gap_state(state: dict[str, Any]) -> dict[str, Any]: raw = state.setdefault("gap_state", {"asked": [], "skipped": [], "rounds": 0}) raw["asked"] = [str(value) for value in raw.get("asked") or []] raw["skipped"] = [str(value) for value in raw.get("skipped") or []] raw["rounds"] = int(raw.get("rounds") or 0) return raw def _reset_gap_state(state: dict[str, Any]) -> None: state["gap_state"] = {"asked": [], "skipped": [], "rounds": 0} def _set_stream_phases(profile: dict[str, Any], *phases: str) -> None: ensure_builder_state(profile)["last_stream_phases"] = list(dict.fromkeys(phases)) def _clear_draft(state: dict[str, Any]) -> None: state["revision_mode"] = False state["active_section"] = None state["identity_draft"] = {} state["pending_entry"] = None state["editing_entry_id"] = None state["editing_base_entry"] = None state["selection_candidates"] = [] _reset_gap_state(state) def _public_entry(entry: dict[str, Any]) -> dict[str, Any]: return {key: deepcopy(value) for key, value in entry.items() if not key.startswith("_")} def _dedupe_strings(values: list[str]) -> list[str]: return list(dict.fromkeys(values)) def _highlights(text: str) -> list[str]: return [part.strip() for part in re.split(r"[。;;\n]+", text) if part.strip()][:5] def _merge_fact_text(original: str, detail: str, *, skip_no_information: bool = False) -> str: if not detail.strip(): return original if not original or original == detail: return detail or original if skip_no_information: return original return f"{original}\n{detail}" def _completed_sections(resume_content: dict[str, Any]) -> set[str]: return {str(section.get("kind") or "") for section in resume_content.get("sections") or [] if isinstance(section, dict) and any(isinstance(entry, dict) for entry in section.get("items") or [])}