"""temporal_context 文本组装(辰正设计 §8.1)。 纯函数:把 ``TemporalSnapshot`` + ``ModulationFactors`` 渲染为注入对话 prompt 的 中文时态上下文块。降级原则:任何字段缺失都跳过该行,不抛异常。 """ from __future__ import annotations from app.modules.time_sense.modulation import ModulationFactors from app.modules.time_sense.snapshot import TemporalSnapshot def render_temporal_context(snapshot: TemporalSnapshot, factors: ModulationFactors) -> str: # noqa: ARG001 """渲染 §8.1 格式的 temporal_context 文本块。 ``factors`` 为 service 层统一传入的调制因子;v1 仅用 snapshot 字段, 参数预留给 v2(如 festival_joy / late_night_fragility 的影响话术)。 """ lines: list[str] = [] # 第一行:时辰·时段(本地时间、时区、周几) shichen = snapshot.shichen period = snapshot.day_period local_hm = snapshot.local_time[11:16] tz_short = _tz_offset_short(snapshot.local_time) weekday_cn = "一二三四五六日"[snapshot.weekday] lines.append(f"现在是{shichen}·{period}({local_hm},UTC{tz_short}),周{weekday_cn}。") # 第二行:农历 + 距下一节气 lunar_part = "" if snapshot.lunar is not None: lunar_part = ( f"农历{snapshot.lunar.year_ganzhi}年{snapshot.lunar.month}月{snapshot.lunar.day}日," ) term_part = "" if snapshot.solar_term is not None: days_to_next = getattr(snapshot.solar_term, "days_to_next", None) next_name = getattr(snapshot.solar_term, "next_name", None) if days_to_next is not None and next_name: term_part = f"距{next_name}还有 {days_to_next} 天。" if lunar_part or term_part: lines.append(lunar_part + term_part) # 第三行:认识时长 acq = snapshot.acquaintance if acq.natural_language: lines.append(f"已经认识 {acq.natural_language}。") # 第四行:离线/重逢 offline = snapshot.offline if offline.band == "reunion_impact": days = int(offline.minutes // 1440) lines.append(f"用户上次说话是 {days} 天前——久别重逢。") elif offline.band == "long_absence": days = int(offline.minutes // 1440) lines.append(f"用户上次说话是 {days} 天前。") # 第五行:纪念日 active = snapshot.active_milestones next_ms = snapshot.next_milestone if active: names = "、".join(m.name for m in active) lines.append(f"今日命中:{names}。") else: lines.append("今日命中:无纪念日。") if next_ms is not None: lines.append(f"下一个特殊日子:{next_ms.name},还有 {next_ms.days_until} 天。") return "\n".join(lines) def _tz_offset_short(local_time_iso: str) -> str: """从本地 ISO 时间(如 ``2026-07-04T02:00:00+08:00``)提取 UTC 偏移。 返回 ``+8`` / ``-5`` / ``+0`` 形式,用于 ``UTC{tz_short}`` 显示—— 即用户本地时区相对 UTC 的偏移(非家时区差)。 """ # isoformat() 偏移形如 +HH:MM / -HH:MM(末 6 字符);Z 或无偏移按 UTC if local_time_iso.endswith("Z") or len(local_time_iso) < 6: return "+0" tail = local_time_iso[-6:] sign = tail[0] if tail[0] in "+-" else "+" return f"{sign}{int(tail[1:3])}"