Add AI task composition for routines (bot + web client)

Users can now describe a goal and have AI auto-generate 4-7 ADHD-friendly
steps, which they can review and modify before saving.

- ai/ai_config.json: Add step_generator prompt and ai_compose examples
  to command_parser so bot recognises vague task descriptions
- api/routes/ai.py: New POST /api/ai/generate-steps endpoint — calls
  LLM via ai_parser, validates and sanitises returned steps
- api/main.py: Register new ai_routes module
- bot/commands/routines.py: Add ai_compose action — generates steps,
  shows numbered list with durations, uses existing yes/no confirm flow
- synculous-client/src/lib/api.ts: Add api.ai.generateSteps(goal)
- synculous-client/src/app/dashboard/routines/new/page.tsx: Add
  Generate with AI panel with collapsible textarea, loading spinner,
  and inline error; generated steps slot into existing editable list

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-19 13:56:54 -06:00
parent 9fb56edf74
commit 95ebae6766
6 changed files with 260 additions and 17 deletions

View File

@@ -93,6 +93,56 @@ async def handle_routine(message, session, parsed):
await _create_routine_with_steps(message, token, name, description, steps)
elif action == "ai_compose":
goal = parsed.get("goal")
name = parsed.get("name", "my routine")
if not goal:
await message.channel.send(
"What's the goal for this routine? Tell me what you want to accomplish."
)
return
async with message.channel.typing():
resp, status = api_request(
"post", "/api/ai/generate-steps", token, {"goal": goal}
)
if status != 200:
await message.channel.send(
f"Couldn't generate steps: {resp.get('error', 'unknown error')}\n"
f"Try: \"create {name} routine with step1, step2, step3\""
)
return
steps = resp.get("steps", [])
if not steps:
await message.channel.send("The AI didn't return any steps. Try describing your goal differently.")
return
if "pending_confirmations" not in session:
session["pending_confirmations"] = {}
confirmation_id = f"routine_create_{name}"
session["pending_confirmations"][confirmation_id] = {
"action": "create_with_steps",
"interaction_type": "routine",
"name": name,
"description": f"AI-generated routine for: {goal}",
"steps": [s["name"] for s in steps],
"needs_confirmation": False,
}
total_min = sum(s.get("duration_minutes", 5) for s in steps)
steps_list = "\n".join(
[f"{i+1}. {s['name']} ({s.get('duration_minutes', 5)} min)" for i, s in enumerate(steps)]
)
await message.channel.send(
f"Here's what I suggest for **{name}** (~{total_min} min total):\n\n"
f"{steps_list}\n\n"
f"Reply **yes** to create this routine, or **no** to cancel."
)
elif action == "add_steps":
routine_name = parsed.get("routine_name")
steps = parsed.get("steps", [])