Commit Graph

20 Commits

Author SHA1 Message Date
madcat 2ddf6b7741 deps: add markdown-it-py for TTS text stripping 2026-05-29 19:53:55 +02:00
marauder-actual e2f6bc7ee3 fix: remove marauder/MCP references from all LLM-facing prompts
- BT_SYSTEM_PROMPT: removed MCP/marauder limitation lines
- calibration.py: removed marauder CLI suggestion from generated cart prompts
- Adam's cart on sin: cleaned separately (marauder line removed)
2026-05-29 19:16:14 +02:00
marauder-actual 13bb1c354b fix: strip markdown for TTS + render rich markdown in chat UI
- _md_to_speech(): AST-based markdown stripping via markdown-it-py
- Truncate TTS input at 450 chars on sentence boundary (chatterbox overflow)
- chat.js: render assistant messages as markdown via marked.js + DOMPurify
- Typewriter accumulates raw text, renders markdown progressively
2026-05-29 19:05:04 +02:00
marauder-actual 34295d2f14 use dedicated chat agent for opencode sessions 2026-05-29 18:42:44 +02:00
marauder-actual fa018f380c fix: SSE event parsing — use message.part.delta + correct session.status format 2026-05-29 18:26:31 +02:00
marauder-actual d2638e0650 fix: use vllm provider for qwen3-coder-next AWQ (already loaded) 2026-05-29 18:10:08 +02:00
marauder-actual f4eac499cf feat(auth): add /auth/token?t=<token> for headless login 2026-05-29 18:01:22 +02:00
marauder-actual 7b283d343e feat(transport): async prompt + SSE streaming from opencode events
- POST /session/:id/prompt_async fires the prompt (204 immediate)
- GET /event SSE stream picks up message.part.updated with real text deltas
- Filters events by session ID, computes delta from cumulative text
- Breaks on message completed or session idle
2026-05-29 17:54:26 +02:00
marauder-actual 0ed951d505 fix: use ollama/qwen3-coder-next instead of anthropic, remove stale ANTHROPIC_MODEL ref 2026-05-29 17:51:44 +02:00
marauder-actual 92224e514f fix(transport): rewrite to use opencode session API instead of nonexistent /v1/chat/completions
- POST /session/:id/message (synchronous) replaces broken SSE stream
- Per-user opencode sessions with conversation history
- Word-by-word typewriter streaming to WebSocket
- TTS timeout wrapper prevents connection hangs
2026-05-29 17:49:28 +02:00
marauder-actual 89a5a85c56 fix(ws): wrap TTS in timeout so it doesn't block WebSocket after text streams 2026-05-29 17:41:49 +02:00
marauder-actual df2791a4de feat(persona): samantha uses chatterbox-turbo with paralinguistic tags 2026-05-29 17:26:24 +02:00
marauder-actual c8b554ce76 fix(calibration): fallback voices use madcat-tts cart IDs, not piper model names 2026-05-29 16:59:59 +02:00
marauder-actual ae384fe618 feat: chatterbox TTS via madcat-tts daemon, Web Speech API STT, styled persona picker
- tts.py: replace piper subprocess with HTTP POST to madcat-tts /v1/audio/speech (chatterbox voice cloning)
- chat.js: replace whisper server upload with browser Web Speech API (webkitSpeechRecognition)
- chat.css: style persona picker — appearance:none select, themed with CSS vars, mobile responsive
- main.py: default TTS voice → bt7274-en
2026-05-29 16:43:41 +02:00
marauder-actual f3c35eba72 fix(persona): always call sidecar, even in PREVIEW_MODE 2026-05-29 16:16:32 +02:00
marauder-actual 348918cad9 Merge branch 'feat/p3-transport' 2026-05-29 14:30:48 +02:00
marauder-actual 6dee0e8c6f fix(transport): use cart-specific voice IDs and TTS engine backends in PERSONAS 2026-05-29 14:27:18 +02:00
marauder-actual b0893a3699 feat(transport): swap Anthropic → opencode; add persona switcher
Part 1 — Transport swap:
- Replace anthropic.AsyncAnthropic streaming with httpx SSE client
  calling opencode's OpenAI-compat /v1/chat/completions on sin:4096
- Auth: basic auth opencode:$OPENCODE_PASSWORD
- Env: OPENCODE_URL (default http://sin:4096), OPENCODE_PASSWORD
- Sidecar binding (sin:4098) consulted per message to resolve active
  persona; voice read from binding → cart → env default
- Helper _session_id_for_user: deterministic sha256 slug per email
  so sidecar binding survives WebSocket reconnects
- anthropic dep retained in pyproject.toml (not removed — P4 may use it)

Part 2 — Persona switcher:
- PERSONAS dict: bt7274, friday, samantha (slug → voice/backend/prompt)
- POST /api/persona  — bind persona via sidecar, maps slug → full config
- GET  /api/persona/current — return current binding
- GET  /api/personas — list available personas
- chat.html: persona <select> in topnav with server-rendered active state
- chat.js: onChange → fetch /api/persona, update __personaName +
  status badge + system message in conversation feed

TODO: add CSS polish for .topnav__persona-wrap (inherits base styles for now)
2026-05-29 14:18:47 +02:00
marauder-actual 4594f07ebc feat(calibration): 10-question battery, config-driven voice, WCAG-safe theme
Rework calibration.py into a fixed 10-question battery:
  - 4 critical questions (language, name, persona name, gender)
  - 6 random probes drawn from the 12-item pool (all AI/tech-unrelated)

Theme inference (palette × typography × density × labels):
  - Palette: warmth × contrast × energy → default|rose|morning|evening|
    sage|paper|ink; all choices verified WCAG AA ≥4.5:1.
  - Typography: elaborate+warm → serif-warm; cool+ink → mono;
    high-contrast+cool → serif-formal; energetic+warm → mixed-modern;
    otherwise sans.
  - Density: elaborate cadence → airy; terse → dense; else normal.
  - Labels: serif-warm/mixed-modern → cursive; mono → prefix; else block.

Voice selection:
  - Removed VOICE_POOL (lang × gender heuristic).
  - Added _load_persona_voices(): reads [voices.<lang>].id from a
    cart.toml persona file at PCART_CONFIG_PATH or app/conf/persona.toml.
    Falls back to FALLBACK_VOICES when no file is present.
  - _pick_voice(lang) selects by language key only — no gender heuristic.

cart_store.Cart changes:
  - Added calibration_version: int = 1 (new carts emit 2)
  - Bumped version: 3 → 4
  - Removed unused  import

No changes to app/main.py — calibration wiring was already correct.
2026-05-29 14:00:14 +02:00
marauder-actual 96ba8f4b6e chore: initial commit — chat-saiden web chat baseline 2026-05-29 13:47:34 +02:00