generated from kgod/ai-review-template
自内部仓库剥离深度优化与 RAG 知识库后的交付版本: - Builder 对话式简历生成(FSM + 意图路由 LLM 兜底增强) - 条目级轻度优化:事实覆盖门禁 + STAR/bullet 修复链,功能/简介/成果与技术栈同级保护 - 简历导入:DOCX/PDF 解析、结构归一、手机号脱敏 - PostgreSQL 运行时 + Alembic 迁移链 Co-Authored-By: Claude <noreply@anthropic.com>
126 lines
5.1 KiB
Python
126 lines
5.1 KiB
Python
"""记录类模块收集器:类型选择、经历卡片提交、条目确认、卡内调整。"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from typing import Any
|
|
|
|
from .enrichment_modules import PICKER_OPTIONS, ModuleSpec
|
|
from .fsm import ANCHOR_FIELDS, FIELD_LABELS, FSMError, Transition, assistant_turn, component
|
|
from .fsm_enrichment import (
|
|
add_another_transition,
|
|
advance_or_finish,
|
|
mark_completed,
|
|
progress_block,
|
|
)
|
|
from .models import ComposerMode, Stage
|
|
from .record_card import record_card
|
|
from .validators import record_entry_errors
|
|
|
|
|
|
def collect_choice(profile: dict[str, Any], spec: ModuleSpec, payload: dict[str, Any]) -> Transition:
|
|
value = str(payload.get("value") or "")
|
|
labels = dict(PICKER_OPTIONS[spec.name])
|
|
if value not in labels:
|
|
raise FSMError("invalid_record_type", "请选择列出的经历类型", status_code=422)
|
|
profile["enrichment"]["module_draft"] = {"record_type": value}
|
|
return Transition(
|
|
Stage.RESUME_ENRICHING,
|
|
profile,
|
|
assistant_turn(
|
|
f"好,{labels[value]}。请在卡片中填写这段经历。",
|
|
[progress_block(profile, spec), record_card(profile, spec)],
|
|
mode=ComposerMode.UI_ONLY,
|
|
),
|
|
)
|
|
|
|
|
|
def collect_record_fields(profile: dict[str, Any], spec: ModuleSpec, payload: dict[str, Any]) -> Transition:
|
|
draft = profile["enrichment"]["module_draft"]
|
|
existing = draft.get("entry") if isinstance(draft.get("entry"), dict) else {}
|
|
record_type = (
|
|
draft.get("record_type") or existing.get("record_type") or spec.record_type or profile.get("anchor_type")
|
|
)
|
|
description = str(payload.get("description") or "").strip()
|
|
if spec.kind == "anchor_note":
|
|
if not description:
|
|
raise FSMError("invalid_record", "请填写经历描述", status_code=422, missing_fields=["description"])
|
|
entry = {"record_type": record_type, "module": spec.name, "description": description}
|
|
else:
|
|
entry = {
|
|
"record_type": record_type,
|
|
"module": spec.name,
|
|
**{field: str(payload.get(field) or "").strip() for field in ANCHOR_FIELDS.get(record_type, [])},
|
|
}
|
|
if description:
|
|
entry["description"] = description
|
|
errors = record_entry_errors(entry, ANCHOR_FIELDS.get(record_type, []))
|
|
if errors:
|
|
raise FSMError("invalid_record", "核心字段缺失或格式有误", 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={k: v for k, v in entry.items() if k not in {"record_type", "module"}},
|
|
labels=FIELD_LABELS,
|
|
),
|
|
],
|
|
mode=ComposerMode.UI_ONLY,
|
|
),
|
|
)
|
|
transition.polish_description = True
|
|
return transition
|
|
|
|
|
|
def confirm_module_entry(
|
|
profile: dict[str, Any], spec: ModuleSpec, payload: dict[str, Any]
|
|
) -> Transition:
|
|
entry = profile["enrichment"]["module_draft"].pop("entry", None)
|
|
if not isinstance(entry, dict):
|
|
raise FSMError("invalid_state", "没有待确认的条目", status_code=409)
|
|
proposal = entry.pop("pending_proposal", None)
|
|
if isinstance(proposal, dict) and payload.get("use_optimized") is True:
|
|
entry["description"] = str(proposal.get("optimized_description") or "").strip()
|
|
entry["provenance"] = proposal.get("source", "ai_expanded")
|
|
entry["confirmed"] = True
|
|
entry["rewrite_confirmed"] = True
|
|
if spec.kind == "anchor_note":
|
|
anchor = profile.setdefault("anchor", {})
|
|
for key in ("description", "highlights", "metrics", "provenance"):
|
|
if entry.get(key):
|
|
anchor[key] = entry[key]
|
|
mark_completed(profile, spec.name)
|
|
return advance_or_finish(profile, refresh=True)
|
|
profile["records"][entry.get("record_type") or spec.record_type].append(entry)
|
|
if spec.multi:
|
|
return add_another_transition(profile, spec, refresh=True)
|
|
mark_completed(profile, spec.name)
|
|
return advance_or_finish(profile, refresh=True)
|
|
|
|
|
|
def edit_module_entry(profile: dict[str, Any], spec: ModuleSpec) -> Transition:
|
|
entry = profile["enrichment"]["module_draft"].get("entry")
|
|
if not isinstance(entry, dict):
|
|
raise FSMError("invalid_state", "没有可调整的条目", status_code=409)
|
|
if spec.kind == "record_form":
|
|
card = component("CompetitionFields", module=spec.name, title=spec.prompt, value=entry)
|
|
else:
|
|
card = record_card(profile, spec, value=entry)
|
|
return Transition(
|
|
Stage.RESUME_ENRICHING,
|
|
profile,
|
|
assistant_turn(
|
|
"请直接在卡片中修改这段经历。",
|
|
[progress_block(profile, spec), card],
|
|
mode=ComposerMode.UI_ONLY,
|
|
),
|
|
)
|