Commit Graph
4 Commits
Author SHA1 Message Date
Claude 72189d2b7b fix(chat): use native async LLM call to prevent session reset on message submit
The previous implementation used asyncio.to_thread(llm.complete) to avoid
blocking the event loop, but Chainlit's contextvars context is not reliably
propagated across thread boundaries, causing the session to reset and clear
the message history on each user submission.

Changes:
- Add llm.acomplete() using litellm.acompletion() (native coroutine)
- Replace asyncio.to_thread() in on_message with await llm.acomplete()
- Store the welcome message in state.messages so it is replayed on reconnect
- Persist state to cl.user_session immediately after appending the user's
  message (before the LLM call) so reconnect detection has the latest history
- Add pytest-asyncio dev dependency and asyncio_mode = "auto" config
- Add 6 async tests for acomplete() in tests/test_llm.py

https://claude.ai/code/session_01SUWzMzFvSfWiHXA2p6rPg9
2026-02-22 12:38:20 +00:00
Claude 981948c106 fix(chat): restore message history on reconnect; fix event loop blocking
Two related issues caused the screen to clear after each answer:

1. Blocking event loop: the synchronous llm.stream_complete() for-loop
   was running directly in the async on_message handler, blocking the
   event loop for the full LLM response duration. This caused the
   WebSocket to time out and Chainlit to reconnect after each message.

   Fix: replace stream_complete() with asyncio.to_thread(llm.complete)
   so the network-bound LLM call runs in a thread pool and the event
   loop (and WebSocket) stay alive throughout.

2. Reconnect resets history: on_chat_start always created a fresh empty
   state and sent the welcome message, even on WebSocket reconnections
   where cl.user_session still held the existing conversation.

   Fix: if cl.user_session["state"] is already present, replay the
   stored message history into the new thread instead of starting fresh.

https://claude.ai/code/session_01SUWzMzFvSfWiHXA2p6rPg9
2026-02-22 08:30:52 +00:00
Claude 808be0185f fix(chat): show only reply text to user, not raw JSON
The LLM returns a structured JSON object. Previously, raw tokens were
streamed directly to the user via msg.stream_token(), causing the full
JSON blob to appear in the chat.

Fix: collect all chunks silently, parse the JSON, then send only the
reply field with cl.Message(content=reply_text).send(). The JSON fields
(updates, next_step, registration_complete, language, intent) are still
processed in the background — parents never see them.

https://claude.ai/code/session_01SUWzMzFvSfWiHXA2p6rPg9
2026-02-22 08:16:12 +00:00
Claude 9fdbe341be feat(chat): implement web chat interface with accessibility
Core implementation:
- chat_app.py: Chainlit entry point with @cl.on_chat_start,
  @cl.on_message (streaming via llm.stream_complete), @cl.on_chat_end
  Reuses Config, KnowledgeBase, ConversationStore, AdminNotifier from src/
  Handles registration completion, post-completion updates, new-child flow

- src/llm.py: add stream_complete() generator (litellm stream=True)
  alongside existing complete(); tests added in tests/test_llm.py

- src/agent/response_parser.py: extract parse_llm_response(),
  apply_updates(), fallback_message() from EmailAgent into shared module
  EmailAgent now delegates to these functions (no logic change)

Chainlit configuration:
- chainlit.toml: telemetry off, German default, custom CSS + JS paths
- chainlit.md: German welcome page with playgroup info

Accessibility (WCAG 2.1 AA):
- public/custom.css: contrast overrides (≥4.5:1), prefers-reduced-motion
  (static "…" replaces animated dots), skip link styles, 100dvh fix
- public/accessibility.js: MutationObserver injects aria-live="polite"
  on message list, focus management after agent replies, skip link element

Other:
- .gitignore: add .chainlit/ (Chainlit runtime, auto-generated)
- openspec/config.yaml: populate context field with tech stack
- openspec/changes/implement-web-chat/tasks.md: mark completed tasks

95 tests pass.

https://claude.ai/code/session_01SUWzMzFvSfWiHXA2p6rPg9
2026-02-22 07:42:29 +00:00