feat(bot): veteran daily check-in, medication reminders and rule reactions
Daily check-in (mood → dose → sleep/nightmares → alcohol → free text with extract_checkin, buttons win over the LLM, progressive upsert of daily_checkins), medication reminder flow (taken / skip with reason / side effects → doses + daily_checkins), medication question menu via med_qa, med_info rule reactions from the drug instruction excerpt, «записаться раньше» request recorded for lane B, friendly intros for scheduled scales. actions.py: additive med_info handler hook. Refs #37 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
+16
-4
@@ -6,7 +6,7 @@ are ignored here. While a crisis is open only crisis-relevant actions (crisis, s
|
||||
the patient: no medication tips or visit offers in the middle of a crisis.
|
||||
"""
|
||||
|
||||
from collections.abc import Iterable
|
||||
from collections.abc import Awaitable, Callable, Iterable
|
||||
from typing import Any
|
||||
|
||||
from app.core.rules import Action
|
||||
@@ -20,6 +20,16 @@ from app.dialog.scale_session import active_scale, add_pending
|
||||
_MED_INFO_INTENTS = {"delayed_effect": "quit_intent", "alcohol_interaction": "alcohol_info", "nausea": "nausea_info"}
|
||||
_CRISIS_SAFE = {"crisis", "show_safety_plan", "schedule_scale"}
|
||||
|
||||
# Optional `med_info` answerer registered by a flow (`flows/meds.py`, #37): answers from the drug instruction
|
||||
# (med_qa). Returning None falls back to the phrase template below.
|
||||
MedInfoHandler = Callable[[Ctx, Action], Awaitable[Reply | None]]
|
||||
_med_info_handler: MedInfoHandler | None = None
|
||||
|
||||
|
||||
def register_med_info_handler(handler: MedInfoHandler | None) -> None:
|
||||
global _med_info_handler
|
||||
_med_info_handler = handler
|
||||
|
||||
|
||||
def _plan_line(step: Any) -> str:
|
||||
if isinstance(step, dict):
|
||||
@@ -57,9 +67,11 @@ async def execute(ctx: Ctx, actions: Iterable[Action]) -> Step:
|
||||
pending = add_pending(data, scale_id, bool(action.params.get("scheduled", False)))
|
||||
step = step.then(Step(data={"pending_scales": pending}))
|
||||
elif action.type == "med_info":
|
||||
intent = _MED_INFO_INTENTS.get(str(action.params.get("topic")), "default")
|
||||
text = await ctx.gateway.phrase(intent, {})
|
||||
step = step.then(Step(replies=[Reply(text=text, kind="info")]))
|
||||
reply = await _med_info_handler(ctx, action) if _med_info_handler is not None else None
|
||||
if reply is None:
|
||||
intent = _MED_INFO_INTENTS.get(str(action.params.get("topic")), "default")
|
||||
reply = Reply(text=await ctx.gateway.phrase(intent, {}), kind="info")
|
||||
step = step.then(Step(replies=[reply]))
|
||||
elif action.type == "suggest_visit":
|
||||
text = await ctx.gateway.phrase("book_visit_offer", {})
|
||||
button = Button(label=ctx.texts.visit.button, data="menu:visit")
|
||||
|
||||
Reference in New Issue
Block a user