Files

154 lines
5.5 KiB
Python

from __future__ import annotations
from copy import deepcopy
from dataclasses import dataclass
from typing import Any
from .fsm import FSMError, Transition, assistant_turn, component
from .fsm_enrichment import (
add_another_transition,
advance_or_finish,
begin_module,
current_module,
mark_completed,
)
from .models import ComposerMode, Stage
from .services import ExtractedExperience
@dataclass(slots=True)
class RewriteConfirmationTransition:
stage: Stage
profile: dict[str, Any]
turn: dict[str, Any]
lifecycle: str = "submitted"
create_draft: bool = False
resume_content: dict[str, Any] | None = None
refresh_resume: bool = False
def prepare_rewrite_confirmation(
profile: dict[str, Any],
extraction: ExtractedExperience,
proposal: dict[str, Any],
*,
module: str | None = None,
record_type: str | None = None,
) -> tuple[dict[str, Any], dict[str, Any]]:
updated = deepcopy(profile)
pending = extraction.to_dict()
if module is not None:
pending["module"] = module
if record_type is not None:
pending["record_type"] = record_type
pending["description"] = extraction.raw_text
optimized = str(proposal.get("optimized_description") or "").strip()
if optimized and optimized != extraction.raw_text.strip():
pending_proposal = {
"optimized_description": optimized,
"changes": proposal.get("changes") or [],
"source": proposal.get("source", "ai_expanded"),
}
for key in ("generation_source", "fallback_reason"):
if proposal.get(key):
pending_proposal[key] = proposal[key]
pending["pending_proposal"] = pending_proposal
updated["pending_experience"] = pending
summary = _confirmation_summary(extraction)
turn = assistant_turn(
"我已把这段事实整理成正式简历语言,请确认后再写入简历。",
[
component(
"ExperienceConfirmCard",
title="确认 AI 改写",
description="只在内容准确时加入简历;需要调整可返回继续描述。",
value=summary,
ai_proposal=pending.get("pending_proposal"),
confirmation_kind="rewrite",
)
],
mode=ComposerMode.UI_ONLY,
)
return updated, turn
def process_rewrite_confirmation(
profile: dict[str, Any], action: str, payload: dict[str, Any] | None = None
) -> Transition | RewriteConfirmationTransition:
updated = deepcopy(profile)
normalized = action.strip().lower()
payload = payload or {}
pending = updated.get("pending_experience")
module = pending.get("module") if isinstance(pending, dict) else None
if normalized in {"edit", "revise", "edit_anchor"}:
updated.pop("pending_experience", None)
spec = _module_spec(updated, module)
if spec is not None:
return begin_module(updated, spec)
return RewriteConfirmationTransition(
Stage.RESUME_ENRICHING,
updated,
assistant_turn(
"这版暂不写入。请补充或纠正事实,我会重新整理。",
[],
mode=ComposerMode.CHAT,
),
)
if normalized not in {"confirm", "confirm_anchor", "confirm_rewrite"}:
raise FSMError("invalid_action", "Confirm or revise the proposed rewrite")
experience = updated.pop("pending_experience", None)
if not isinstance(experience, dict):
raise FSMError("rewrite_not_pending", "No proposed rewrite is waiting for confirmation")
proposal = experience.pop("pending_proposal", None)
if isinstance(proposal, dict) and payload.get("use_optimized") is True:
experience["description"] = str(proposal.get("optimized_description") or "").strip()
experience["provenance"] = proposal.get("source", "ai_expanded")
record_type = experience.get("record_type")
if record_type:
record = {**experience, "confirmed": True, "rewrite_confirmed": True}
updated.setdefault("records", {}).setdefault(record_type, []).append(record)
else:
updated.setdefault("experiences", []).append(experience)
updated["ai_rewrites_confirmed"] = True
spec = _module_spec(updated, module)
if spec is not None:
if spec.multi:
transition = add_another_transition(updated, spec)
else:
mark_completed(updated, spec.name)
transition = advance_or_finish(updated)
transition.refresh_resume = True
return transition
return RewriteConfirmationTransition(
Stage.CONTENT_READY,
updated,
assistant_turn(
"已确认并写入简历。",
[
component(
"ContentReadyCard",
formal_content_ready=True,
actions=["continue_enriching", "finish_enrichment"],
)
],
mode=ComposerMode.HYBRID,
),
lifecycle="confirmed",
refresh_resume=True,
)
def _module_spec(profile: dict[str, Any], module: Any):
if not module:
return None
spec = current_module(profile)
return spec if spec is not None and spec.name == module else None
def _confirmation_summary(extraction: ExtractedExperience) -> dict[str, Any]:
return {
"title": extraction.title,
"organization": extraction.organization,
"role": extraction.role,
"description": extraction.raw_text,
}