A chat and voice mental health companion that supports your well-being through reflection, guided exercises, and a memory that grows with you.
Important
Not a therapist. Not a diagnostic tool. Not an emergency service. OpenCouch is a supportive companion for self-reflection and wellness exercises. It is not a substitute for professional mental health care or medical advice.
Warning
Invasive Changes In Progress: OpenCouch is currently going through significant architecture and product changes. The web UI is broken for now while the app shell catches up with the backend refactor. For local dogfooding, use scripts/cli_dogfood.sh to start the text agent and scripts/voice_agent.sh to start the LiveKit voice agent. Expect breaking changes, moving APIs, and documentation that may temporarily lag behind the code while the system is being simplified and stabilized.
- 📖 Overview
- ✨ Key Features
- Screenshots
- 🚀 Quick Start
- 🧠 Architecture
- 📁 Project Structure
- 🧪 Development & Validation
- 📝 Changelog
- 🤝 Contributing
- 🗺️ Roadmap
OpenCouch is a chat and voice companion for day-to-day emotional support, self-reflection, and practical coping. It combines modern conversational AI with structured therapeutic patterns, so users can move between open-ended conversation, guided exercises, and longer-term reflection without starting over each time.
Unlike chatting with ChatGPT, Gemini, or Claude on the web, OpenCouch is not a blank general-purpose assistant. It is built around mental-health-adjacent product needs: safety-aware routing, continuity across sessions, structured memory, and concrete coping workflows. General AI chat can be helpful in the moment, but OpenCouch is designed to support ongoing daily use—remembering what has mattered, guiding users through exercises like grounding or thought work, and keeping the experience focused on emotional support rather than generic task completion.
Under the hood, the text runtime is a LangGraph graph behind a FastAPI server, with Postgres-first durable persistence and a legacy SQLite fallback. Memory is split into three CoALA-inspired layers: semantic facts, episodic arcs, and procedural rules. Before the assistant responds, each turn goes through safety routing, and local evals plus Opik traces help catch regressions in core routing behavior.
Voice support is experimental and LiveKit-native. The browser joins a LiveKit room, a LiveKit Agents worker owns the speech loop, and OpenAI Realtime handles the speech-to-speech model interaction. The voice worker now lives under agent/voice/ and uses OpenCouch plain services for crisis classification, turn policy, memory context, exercise consent, tools, and transcript finalization.
The project is still pre-beta; a closed beta is planned.
- Persistent Memory: Retains context across sessions using semantic facts, episodic arcs, and procedural rules.
- Safety First: Built-in safety routing evaluates every turn before responding, backed by a durable crisis-audit log.
- Guided Exercises: 13 multi-turn, state-tracked exercises including grounding, breathing, thought work, and values reflection.
- Voice Support: Browser voice sessions via LiveKit and OpenAI Realtime, with configurable voices, transcription hints, and interruption handling.
- Optional Telegram Gateway: Direct message interface with allow-listing, markdown rendering, and session rotation.
- Tracing & Regression Checks: Backend tests, live-provider checks, and Opik traces for regression tracking.
![]() |
![]() |
![]() |
|||
![]() |
![]() |
||||
- Docker Desktop running for the Compose stack.
uvandpnpmfor manual backend/web development.- Provider keys only for real model runs. Deterministic CLI and many local checks can run without external API keys.
OpenCouch loads local environment files from the repo root and apps/backend (.env, then .env.local). Deterministic mode does not need external API keys. Real model runs need at least one configured provider.
View Environment Setup Details
For local persistence, the recommended path is the Dockerized Postgres service from compose.yml. Backend services default to that stack configuration when OPENCOUCH_PERSISTENCE_BACKEND is unset inside Compose.
# Text model provider. Defaults to openai when unset.
LLM_PROVIDER=openai
OPENAI_API_KEY=...
# Alternative text provider.
# LLM_PROVIDER=gemini
# GEMINI_API_KEY=...
# GOOGLE_API_KEY=...
# Local persistence backend for memory, checkpoints, audit, feedback,
# active-session state, and LiveKit voice finalization status.
# The Docker Compose stack defaults to these values automatically.
OPENCOUCH_PERSISTENCE_BACKEND=postgres
OPENCOUCH_MEMORY_DATABASE_URL=postgresql://opencouch:opencouch@postgres:5432/opencouchVoice needs extra configuration for browser or LiveKit console sessions:
# Web voice via LiveKit + OpenAI Realtime model.
LIVEKIT_URL=wss://your-project.livekit.cloud
LIVEKIT_API_KEY=...
LIVEKIT_API_SECRET=...
OPENAI_API_KEY=...Optional: Telegram Gateway Environment
# Telegram gateway.
OPENCOUCH_TELEGRAM_BOT_TOKEN=123456:abc...
OPENCOUCH_TELEGRAM_ALLOW_FROM=123456789
OPENCOUCH_TELEGRAM_OWNER_ID=alice
OPENCOUCH_TELEGRAM_RESPONSE_MODEL_TIER=fastKeep real .env files local and out of version control.
The most reliable dogfood paths are scripts/cli_dogfood.sh for text and scripts/voice_agent.sh for local voice. Compose starts the browser stack: Postgres, backend API, LiveKit voice worker, and web.
View commands for Compose, CLI, voice, web, Telegram, and docs
# Start the full stack with logs attached.
docker compose -f compose.yml up
# Start in the background.
docker compose -f compose.yml up -d
# Rebuild images, then start.
docker compose -f compose.yml up --build
# Rebuild only the web container after frontend edits.
docker compose -f compose.yml up --build web
# Follow API + voice logs after background start.
docker compose -f compose.yml logs -f api voice-agent
# Stop the stack.
docker compose -f compose.yml downLocal URLs: web at localhost:3000, API at localhost:8080, health at localhost:8080/api/health, and Postgres at postgresql://opencouch:opencouch@localhost:5432/opencouch.
Compose reads .env, .env.local, apps/backend/.env, and apps/backend/.env.local. Browser voice needs LIVEKIT_URL, LIVEKIT_API_KEY, LIVEKIT_API_SECRET, and OPENAI_API_KEY. Inside Compose, API and voice use the in-network Postgres URL automatically.
# Preferred persistent dogfood command.
./scripts/cli_dogfood.sh --mode auto --memory-mode persistent --user-id dogfood --response-model-tier quality
# Raw backend CLI commands.
cd apps/backend
uv run python -m opencouch_cli --mode deterministic --memory-mode guest --thread-id scratch
uv run python -m opencouch_cli --mode auto --memory-mode persistent --user-id alice --thread-id s1
uv run python -m opencouch_cli --voicescripts/cli_dogfood.sh starts Dockerized Postgres first and forwards flags to opencouch_cli.
# Local microphone voice session.
./scripts/voice_agent.sh --user-id dogfood console
# Same voice runtime, but typed input/output for quick smoke checks.
./scripts/voice_agent.sh --user-id dogfood console --text
# Incognito voice session; no durable memory writes.
./scripts/voice_agent.sh --memory-mode incognito console
# Worker mode for browser/LiveKit room sessions.
# This waits for a LiveKit room participant and does not listen to
# your terminal microphone.
./scripts/voice_agent.sh --user-id dogfood startscripts/voice_agent.sh starts Postgres by default and forwards flags to agent.voice.agent. If console starts but cannot hear you, check macOS microphone permission for your terminal app under System Settings → Privacy & Security → Microphone.
Use this when you want each process in its own terminal. The manual stack uses port 8000 for the API because the web client defaults to http://localhost:8000/api.
# Terminal 1: API server.
cd apps/backend && uv run uvicorn main:app --port 8000 --reload
# Terminal 2: frontend, from the repo root.
pnpm install && pnpm --dir apps/web dev
# Optional terminal 3: LiveKit voice worker.
./scripts/voice_agent.sh startcd apps/backend
OPENCOUCH_TELEGRAM_BOT_TOKEN="123456:abc..." \
OPENCOUCH_TELEGRAM_ALLOW_FROM="123456789" \
OPENCOUCH_TELEGRAM_OWNER_ID="alice" \
OPENCOUCH_TELEGRAM_RESPONSE_MODEL_TIER="fast" \
uv run python -m channels.gateway telegramThe official documentation is live at whanyu1212.github.io/OpenCouch. Run the docs site locally only when editing documentation:
cd apps/docs
pnpm install && npx docusaurus start --port 3001See apps/backend/README.md for backend-specific commands.
Before response generation, each turn runs through safety routing. Memory writes happen in two phases: per-turn extraction, then a runtime-coordinated session-end commit for episodic summaries and held candidates.
- CLI: Local text and voice harness for development and testing.
- Web chat: Next.js text UI backed by FastAPI REST and WebSocket streaming routes.
- Web voice: LiveKit browser sessions with a LiveKit Agents worker and OpenAI Realtime model.
- Optional Telegram: Direct-message gateway with allow-listing, markdown rendering,
/end, and session rotation. - Backend API: FastAPI route layer used by the web UI and other clients.
flowchart TD
%% Define Node Styles (Tinted for Light/Dark Mode)
classDef inputNode fill:#64748B1A,stroke:#64748B,stroke-width:2px
classDef gateNode fill:#EF44441A,stroke:#EF4444,stroke-width:2px
classDef safeNode fill:#10B9811A,stroke:#10B981,stroke-width:2px
classDef riskNode fill:#F59E0B1A,stroke:#F59E0B,stroke-width:2px
classDef sysNode fill:#3B82F61A,stroke:#3B82F6,stroke-width:2px
classDef dbNode fill:#64748B1A,stroke:#64748B,stroke-width:2px
subgraph SURF ["Runtime Surfaces"]
CLI["CLI"]:::inputNode
WEB["Next.js web chat"]:::inputNode
VOICE["LiveKit voice<br/>separate Agents runtime"]:::inputNode
TG["Optional Telegram DM gateway<br/>thread rotation"]:::inputNode
API["FastAPI REST/WebSocket"]:::inputNode
end
IN(["Text user message"]):::inputNode
VIN(["Voice turn / transcript"]):::inputNode
subgraph GATE ["Safety Gate"]
CG{"crisis_gate<br/>LLM-only classifier"}:::gateNode
end
subgraph SAFE ["Therapeutic Branch"]
direction TB
TDISP{"turn_dispatch<br/>LLM route + active-flow lifecycle"}:::safeNode
MC[["memory_control<br/>natural-language memory ops"]]:::safeNode
GA[["grounded_answer<br/>search-grounded answer"]]:::safeNode
LM["load_memory<br/>awaits recall / prefetch"]:::safeNode
TDISP ==>|memory control| MC
TDISP ==>|lookup| GA
TDISP ==>|support| LM
end
subgraph THERAPY ["Therapeutic Subgraph"]
direction TB
TD{"therapeutic_dispatch<br/>LLM route plan + continuity"}:::safeNode
TR[["therapeutic_response<br/>shared response node"]]:::safeNode
GE[["guided_exercise_response<br/>LangGraph adapter"]]:::safeNode
ER[["ExerciseRunner service<br/>selection • step state • deltas"]]:::safeNode
TD ==>|response style| TR
TD ==>|guided exercise| GE
GE -.-> ER
end
subgraph RISK ["Crisis Branch"]
RL[["crisis_resource_lookup<br/>location-aware resources"]]:::riskNode
CR[["crisis_response<br/>PFA overlay • local hotlines"]]:::riskNode
CL[/"crisis_log"/]:::riskNode
RL ==> CR
CR ==> CL
end
FT{{"finalize_turn<br/>checkpoint reply • set route"}}:::sysNode
subgraph POST ["Runtime Memory Work (outside LangGraph)"]
direction LR
MP["memory prefetch<br/>turn-start speculation"]:::sysNode
MX["TurnExtractionCoordinator<br/>background after graph END<br/>drain before next turn/session end"]:::sysNode
EF["semantic extraction + write policy<br/>commit_now • session_end • repeat/drop"]:::sysNode
EP["procedural extraction + write policy<br/>commit_now • session_end • drop"]:::sysNode
SB[("session buffer<br/>held semantic/procedural candidates")]:::sysNode
MP -.-> LM
MX -.-> EF
MX -.-> EP
EF -.->|hold| SB
EP -.->|hold| SB
end
subgraph SESSION ["Session-End Commit (ActiveSessionManager, outside the LangGraph workflow)"]
direction TB
SE(["session_end trigger<br/>/end • timeout • shutdown • voice disconnect"]):::sysNode
SS(["summarize_session<br/>episodic arc"]):::sysNode
CM(["commit_session_memory<br/>promote held semantic • procedural"]):::sysNode
SE ==> SS
SS ==> CM
end
DB[("Postgres + pgvector<br/>threads • memory • active sessions • crisis log • feedback • voice status")]:::dbNode
%% Logic Flows
CLI ==> IN
WEB ==> API
API ==> IN
TG ==> IN
VOICE ==> VIN
VIN -.->|startup/mid-session memory| DB
VIN -.->|disconnect transcript finalization| SE
IN ==> CG
IN -.->|runtime context| MP
CG ==>|Safe| TDISP
CG -.->|Risk| RL
MC ==> FT
GA ==> FT
LM ==> TD
TR ==> FT
GE ==> FT
CL -.-> FT
FT -.->|runtime schedules| MX
EF -.->|immediate writes| DB
EP -.->|immediate writes| DB
MX -.->|persist active-session buffer| DB
SB -.->|held candidates| CM
CM -.->|promoted / reconciled writes| DB
SS -.->|episodic arc| DB
%% Subgraph Styling (Removes default gray background)
style SURF fill:none,stroke:#64748B,stroke-width:1px,stroke-dasharray: 5 5,rx:5,ry:5
style GATE fill:none,stroke:#EF4444,stroke-width:1px,stroke-dasharray: 5 5,rx:5,ry:5
style SAFE fill:none,stroke:#10B981,stroke-width:1px,stroke-dasharray: 5 5,rx:5,ry:5
style THERAPY fill:none,stroke:#10B981,stroke-width:1px,stroke-dasharray: 5 5,rx:5,ry:5
style RISK fill:none,stroke:#F59E0B,stroke-width:1px,stroke-dasharray: 5 5,rx:5,ry:5
style POST fill:none,stroke:#3B82F6,stroke-width:1px,stroke-dasharray: 5 5,rx:5,ry:5
style SESSION fill:none,stroke:#3B82F6,stroke-width:1px,stroke-dasharray: 5 5,rx:5,ry:5
This repository is a monorepo managed with uv and pnpm.
View Repository Tree
OpenCouch/
├── apps/
│ ├── backend/ # Python backend (FastAPI, LangGraph)
│ │ ├── agent/ # Conversation graph, nodes, state, runtime context
│ │ │ ├── nodes/ # Individual graph nodes
│ │ │ ├── memory/ # Memory retrieval, deduplication, embeddings
│ │ │ ├── therapeutic/ # Therapeutic subgraph, exercises, prompt logic
│ │ │ └── voice/ # LiveKit voice worker, agents, tasks, tools
│ │ ├── llm/ # LLM adapters (Gemini, OpenAI, etc.)
│ │ ├── opencouch_cli/ # Interactive terminal CLI
│ │ ├── channels/ # Telegram gateway and channel adapters
│ │ ├── api/ # FastAPI REST + WebSocket routes
│ │ └── tests/ # 1100+ pytest unit/integration tests
│ ├── web/ # Next.js chat application
│ └── docs/ # Docusaurus documentation site
Backend:
cd apps/backend && uv sync --group dev
# Run the test suite before opening a PR.
uv run pytest tests/unit tests/integrationWeb:
pnpm install
pnpm --dir apps/web lint
pnpm --dir apps/web buildRepository hooks:
uv run pre-commit run --all-filesFor local development trace review, add Opik credentials to .env before running the CLI or API:
OPIK_API_KEY=...
OPIK_WORKSPACE=...
OPIK_PROJECT_NAME=opencouch-devLangSmith / LangChain tracing can be enabled as a secondary backend:
LANGSMITH_TRACING=true
LANGSMITH_ENDPOINT=https://api.smith.langchain.com
LANGSMITH_API_KEY=...
LANGSMITH_PROJECT=opencouch-dev
LANGCHAIN_TRACING_V2=true
LANGCHAIN_ENDPOINT=https://api.smith.langchain.com
LANGCHAIN_API_KEY=...
LANGCHAIN_PROJECT=opencouch-devSee CHANGELOG.md for project history and release notes.
We welcome contributions. Run the relevant checks in Development & Validation before submitting a Pull Request.
Branch Conventions:
feature/*for new capabilitiesfix/*for bug fixesrefactor/*for architectural changesdocs/*for documentation updates
(Note: All PRs should target the develop branch.)
OpenCouch is pre-beta and currently focused on stabilizing the core chat, memory, safety, and voice experience before expanding to more platforms.
| Horizon | Area | Focus |
|---|---|---|
| ✅ Shipped | Core product | Web chat, threading, persistent/incognito sessions, memory inspection, and session feedback |
| ✅ Shipped | Voice | LiveKit-backed browser voice sessions with OpenAI Realtime, safety routing, transcript handling, and memory integration |
| ✅ Shipped | Guided support | 13 state-tracked coping exercises for grounding, breathing, thought work, values reflection, and related flows |
| ✅ Shipped | Runtime & API | FastAPI REST/WebSocket backend, Postgres-backed persistence, LangGraph checkpoints, crisis audit, and feedback storage |
| 🧪 Dogfood | Messaging | Telegram direct-message gateway with allow-listing, Markdown rendering, /end, and session rotation |
| 🔜 Next | Product stabilization | Closed beta readiness, onboarding polish, reliability improvements, clearer session lifecycle, and feedback-driven UX fixes |
| 🔜 Next | Memory quality | Background fact consolidation, dormant/obsolete memory handling, better review controls, and undo support |
| 🔜 Next | Safety & evaluation | Broader eval coverage, clinician-informed review of safety behavior, and stronger regression monitoring |
| 🧭 Later | Mobile | Native iOS app once the web and backend voice paths are stable |
| 🧭 Later | More channels | WhatsApp and Discord adapters after the core messaging abstraction is stable |
| 🧭 Later | Graph memory | Graphiti + Neo4j exploration for entity-relationship reasoning |
| 🧭 Later | Acoustic safety | Paralinguistic crisis signals such as prosody, flatness, or distress markers |
| 🛑 Blocked | Clinical review | Expert clinician audit of knowledge files, prompts, guided exercises, and safety logic |




