Files
resume-agent/backend/app/enrichment_collectors.py
T

155 lines
5.7 KiB
Python

"""RESUME_ENRICHING 模块组件事件分发与结构化收集器(竞赛/标签/联系方式)。
记录类模块(RecordFields/ChoiceChips/条目确认/卡内调整)见 enrichment_record_collectors.py。
"""
from __future__ import annotations
from typing import Any
from .enrichment_custom import (
CUSTOM_MODULES,
custom_card_picker_transition,
finish_custom_enrichment,
)
from .enrichment_modules import ENRICHMENT_MODULES, TAG_SEQUENCE, ModuleSpec
from .enrichment_record_collectors import (
collect_choice,
collect_record_fields,
confirm_module_entry,
edit_module_entry,
)
from .fsm import FSMError, Transition, assistant_turn, component
from .fsm_enrichment import (
advance_or_finish,
begin_module,
current_module,
defer_enrichment,
ensure_enrichment_state,
mark_completed,
progress_block,
skip_module,
)
from .models import ComposerMode, Stage
from .validators import competition_entry_errors, normalize_tags
def process_module_event(
profile: dict[str, Any],
component_data: dict[str, Any],
action: str,
payload: dict[str, Any],
) -> Transition:
ensure_enrichment_state(profile)
name = component_data.get("component_name")
if action in {"defer", "finish_enrichment"}:
return defer_enrichment(profile)
if name == "CustomCardPicker":
return _handle_custom_card_picker(profile, payload)
spec = current_module(profile)
if spec is None:
raise FSMError("no_active_module", "当前没有进行中的完善模块", status_code=409)
if component_data.get("module") != spec.name:
raise FSMError("stale_module", "该组件不属于当前模块", status_code=409)
if action in {"skip", "skip_module"}:
return skip_module(profile, spec)
if action in {"edit", "edit_anchor"}:
return edit_module_entry(profile, spec)
if name == "ChoiceChips":
return collect_choice(profile, spec, payload)
if name == "RecordFields":
return collect_record_fields(profile, spec, payload)
if name == "CompetitionFields":
return _collect_competition(profile, spec, payload)
if name == "TagsInput":
return _collect_tags(profile, component_data, spec, payload)
if name == "ExperienceConfirmCard":
return confirm_module_entry(profile, spec, payload)
if name == "AddAnother":
return _handle_add_another(profile, spec, payload)
raise FSMError("invalid_component", f"Unsupported module component: {name}", status_code=422)
def _collect_competition(profile: dict[str, Any], spec: ModuleSpec, payload: dict[str, Any]) -> Transition:
entry = {
"record_type": "competition",
"module": spec.name,
"name": str(payload.get("name") or "").strip(),
"award": str(payload.get("award") or "").strip(),
"date": str(payload.get("date") or "").strip(),
"description": str(payload.get("description") or "").strip() or None,
}
errors = competition_entry_errors(entry)
if errors:
raise FSMError(
"invalid_competition", "竞赛信息不完整或格式有误", status_code=422, missing_fields=errors
)
profile["enrichment"]["module_draft"] = {"entry": entry}
transition = Transition(
Stage.RESUME_ENRICHING,
profile,
assistant_turn(
"请确认这条竞赛记录。",
[
progress_block(profile, spec),
component(
"ExperienceConfirmCard",
module=spec.name,
confirmation_kind="module_entry",
title="确认竞赛记录",
value={key: entry[key] for key in ("name", "award", "date", "description")},
labels={"name": "竞赛名称", "award": "获奖名称", "date": "获奖时间", "description": "经历描述"},
),
],
mode=ComposerMode.UI_ONLY,
),
)
transition.polish_description = True
return transition
def _handle_add_another(profile: dict[str, Any], spec: ModuleSpec, payload: dict[str, Any]) -> Transition:
value = str(payload.get("value") or "")
if value == "again":
profile["enrichment"]["module_draft"] = {}
return begin_module(profile, spec)
if value == "next":
mark_completed(profile, spec.name)
if profile["enrichment"].get("custom_mode"):
return custom_card_picker_transition(profile, refresh=True)
return advance_or_finish(profile)
raise FSMError("invalid_action", "请选择再添加一段或进入下一项", status_code=422)
def _handle_custom_card_picker(
profile: dict[str, Any], payload: dict[str, Any]
) -> Transition:
value = str(payload.get("value") or payload.get("card_type") or "")
if value == "finish":
return finish_custom_enrichment(profile)
module_name = CUSTOM_MODULES.get(value)
if module_name is None:
raise FSMError("invalid_card_type", "请选择列出的简历卡片", status_code=422)
enrichment = profile["enrichment"]
enrichment["current"] = module_name
enrichment["module_draft"] = {}
enrichment["custom_mode"] = True
return begin_module(profile, ENRICHMENT_MODULES[module_name])
def _collect_tags(
profile: dict[str, Any],
component_data: dict[str, Any],
spec: ModuleSpec,
payload: dict[str, Any],
) -> Transition:
expected = component_data.get("field")
field = str(payload.get("field") or "")
if field != expected or field not in TAG_SEQUENCE:
raise FSMError("invalid_field", "提交字段与当前组件不符", status_code=422)
profile["tags"][field] = normalize_tags(payload.get("value") or payload.get("values") or [])
mark_completed(profile, spec.name)
return advance_or_finish(profile, refresh=True)