feat(bot): every free-text message gets the agent's answer; every demo entry is a private copy
CI / ruff · pytest (pull_request) Successful in 2m41s

- Idle chat: all messages go to the agent (greetings, side-effect and substitute questions included); check-in
  signals are still recorded silently, quit intent keeps its rule reaction. Seen live: «какие будут побочки»
  was logged as a complaint («Спасибо, отметил»), «привет» got a canned phrase.
- Agent prompt: warm short replies to small talk; substitutes → only the doctor changes the drug.
- ЕМИАС demo persona button creates a private copy too (starting from consent + questionnaires, subject to
  the screening switch); the web app drops a stored session of the old shared demo account.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-04 22:27:26 +03:00
co-authored by Claude Opus 5.5
parent 535838e941
commit 31585c7f35
6 changed files with 69 additions and 11 deletions
+12
View File
@@ -62,6 +62,7 @@ from app.dialog.flows.meds import (
)
from app.dialog.fsm import Ctx, Step, abandonable, on_enter, state, trigger
from app.dialog.scale_session import register_scale_intro
from app.llm import agent
PREFIX = "daily"
STEPS = ("mood", "med", "sleep", "nightmares", "alcohol", "text")
@@ -429,6 +430,9 @@ async def _idle_text(ctx: Ctx, event: Event) -> Step:
assert ctx.text is not None
facts = await ctx.gateway.extract_checkin(ctx.text)
if facts is None:
smart = await agent.answer_chat(ctx.token, ctx.text)
if smart is not None:
return Step(replies=[menu_reply(ctx, agent.render(smart))])
text = await knowledge_answer(ctx, explicit=False) if looks_like_question(ctx.text) else None
return Step(replies=[menu_reply(ctx, text or await ctx.gateway.phrase("support"))])
fields: dict[str, Any] = {}
@@ -446,6 +450,14 @@ async def _idle_text(ctx: Ctx, event: Event) -> Step:
text = (await side_effect_reply(ctx, codes)).text
if fields:
await save_checkin(ctx, **fields)
if not facts.quit_intent:
# Chat first: whatever the patient writes in idle gets the agent's answer; check-in signals above are
# recorded silently for the doctor. Quit intent keeps its rule reaction (explanation + doctor notified).
smart = await agent.answer_chat(ctx.token, ctx.text)
if smart is not None:
return Step(
replies=[menu_reply(ctx, agent.render(smart))], notify_monitoring=bool(fields or facts.side_effects)
)
if text is None and facts.med_question and not facts.quit_intent:
text = await answer_question(ctx, await current_prescription(ctx))
# A question is answered even if it also mentioned alcohol/side effects (those are recorded above).