import json
from dataclasses import dataclass
from enum import Enum
class UserFeedbackActionCategory(str, Enum):
"""用户反馈动作的大类。"""
SYNONYM_REWRITE = "synonym_rewrite"
SUPPLEMENTARY_SEARCH = "supplementary_search"
NEW_TASK = "new_task"
SECTION_CHANGE = "section_change"
SYNC = "sync"
FINISH = "finish"
class SynonymRewriteActionSubcategory(str, Enum):
"""同义改写小类动作。"""
EXPAND = "expand"
SHORTEN = "shorten"
POLISH = "polish"
class SupplementarySearchActionSubcategory(str, Enum):
"""补充搜索小类动作。"""
SUPPLEMENTARY_SEARCH = "supplementary_search"
class NewTaskActionSubcategory(str, Enum):
"""新增任务小类动作。"""
NEW_TASK = "new_task"
class FinishActionSubcategory(str, Enum):
"""完成任务小类动作。"""
FINISH = "finish"
class SyncActionSubcategory(str, Enum):
"""整篇报告同步小类动作。"""
SYNC = "sync"
ResolvedActionSubcategory = (
SynonymRewriteActionSubcategory
| SupplementarySearchActionSubcategory
| NewTaskActionSubcategory
| SyncActionSubcategory
| FinishActionSubcategory
)
@dataclass(frozen=True)
class UserInputActionMapping:
"""将前端 action 字符串映射为统一动作大类定义。"""
action_category: UserFeedbackActionCategory
action_subcategory: ResolvedActionSubcategory
@dataclass(frozen=True)
class ResolvedUserAction:
"""由 ``feedback`` 解析得到的规范化动作(大类 + 小类)。"""
action_category: UserFeedbackActionCategory
action_subcategory: ResolvedActionSubcategory
@dataclass(frozen=True)
class UserFeedbackRewriteStreamResult:
"""成功改写后发送给前端的结构化结果。"""
original_text: str
original_start_offset: int
original_end_offset: int
rewritten_text: str
rewritten_start_offset: int
rewritten_end_offset: int
action_category: UserFeedbackActionCategory
action_subcategory: ResolvedActionSubcategory
USER_INPUT_ACTION_MAP: dict[str, UserInputActionMapping] = {
"expand": UserInputActionMapping(
action_category=UserFeedbackActionCategory.SYNONYM_REWRITE,
action_subcategory=SynonymRewriteActionSubcategory.EXPAND,
),
"shorten": UserInputActionMapping(
action_category=UserFeedbackActionCategory.SYNONYM_REWRITE,
action_subcategory=SynonymRewriteActionSubcategory.SHORTEN,
),
"polish": UserInputActionMapping(
action_category=UserFeedbackActionCategory.SYNONYM_REWRITE,
action_subcategory=SynonymRewriteActionSubcategory.POLISH,
),
"supplementary_search": UserInputActionMapping(
action_category=UserFeedbackActionCategory.SUPPLEMENTARY_SEARCH,
action_subcategory=SupplementarySearchActionSubcategory.SUPPLEMENTARY_SEARCH,
),
"new_task": UserInputActionMapping(
action_category=UserFeedbackActionCategory.NEW_TASK,
action_subcategory=NewTaskActionSubcategory.NEW_TASK,
),
"sync": UserInputActionMapping(
action_category=UserFeedbackActionCategory.SYNC,
action_subcategory=SyncActionSubcategory.SYNC,
),
"finish": UserInputActionMapping(
action_category=UserFeedbackActionCategory.FINISH,
action_subcategory=FinishActionSubcategory.FINISH,
),
}
SYNONYM_REWRITE_ACTIONS: frozenset[str] = frozenset(
action
for action, mapping in USER_INPUT_ACTION_MAP.items()
if mapping.action_category == UserFeedbackActionCategory.SYNONYM_REWRITE
)
def resolve_user_input_action(action: str) -> UserInputActionMapping:
"""根据前端 action 获取统一映射定义。
Args:
action: 前端传入的动作字符串。
Returns:
UserInputActionMapping: 统一的动作映射定义。
Raises:
KeyError: 当 action 不在映射表中时抛出。
"""
return USER_INPUT_ACTION_MAP[action]
def resolve_feedback_action(feedback: dict) -> ResolvedUserAction:
"""从 ``feedback["action"]`` 解析出 ``ResolvedUserAction``。
Args:
feedback: 已解析的用户反馈字典,须包含合法字符串键 ``action``。
Returns:
与 ``USER_INPUT_ACTION_MAP`` 一致的大类与小类枚举组合。
Raises:
KeyError: ``action`` 不在映射表中时,由 ``resolve_user_input_action`` 抛出。
"""
mapping = resolve_user_input_action(feedback["action"])
return ResolvedUserAction(
action_category=mapping.action_category,
action_subcategory=mapping.action_subcategory,
)
def _is_report_feedback_payload(message: str) -> bool:
"""若为报告改写约定的 JSON(顶层含 USER_INPUT_ACTION_MAP 中的 action),则视为报告交互负载。"""
if not message or not message.strip().startswith("{"):
return False
try:
data = json.loads(message)
except json.JSONDecodeError:
return False
if not isinstance(data, dict):
return False
action = data.get("action")
return isinstance(action, str) and action in USER_INPUT_ACTION_MAP