Files
resume-agent/backend/app/builder_conversation/state.py
T

78 lines
2.8 KiB
Python

"""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 [])}