An emotion healing web app that helps users process negative emotions through ritual-based interactions, powered by AI. Users express their worries, receive AI-driven emotion analysis, and undergo therapeutic rituals (Fire/Water/Earth) grounded in CBT, AEDP, and ACT methodologies.
Layer
Stack
Frontend
Bun, React 19, Vite, TypeScript, Tailwind CSS, Framer Motion, Zustand, i18next
Backend
Python 3.12+, uv, FastAPI, Google ADK, SQLAlchemy, SQLite, Alembic, Pydantic, Giskard, pydantic-evals
AI/ML
Gemini 3 Flash (reasoning), Gemini 2.5 Flash Lite (routing/ASR/search), Gemini 2.5 Flash Image (generation)
Tooling
just, Docker Compose, Biome, Ruff, ty, Bandit, GitHub Actions
Deployment
Render.com, Logfire (tracing)
Multi-modal input - Text and voice (Gemini ASR with multilingual support)
AI emotion detection - Classifies emotions (Anger, Anxiety, Grief, Shame, Depression, Frustration) and recommends a matching ritual
Three therapeutic rituals
Fire (CBT) - Cognitive restructuring and challenging negative thoughts
Water (AEDP) - Emotional acceptance, validation, and flow
Earth (ACT) - Grounding, present-moment awareness, and values
Crisis detection - Query routing agent identifies high-risk content and provides safety resources
AI-generated ritual images - Feedback loop with up to 3 retry attempts on safety blocks
Emotion Museum - Gallery of completed rituals as flip-card memories
3D particle orbs - Canvas-based particle system that morphs to emotion image colors
Internationalization - English and Chinese support
Animated ritual sequences - Fire/water/earth video animations with audio effects and BGM
sequenceDiagram
actor User
participant Frontend
participant Backend
participant Gemini as Gemini AI
User->>Frontend: Write worry (text or voice π€)
Frontend->>Backend: POST /analyze
Backend->>Gemini: Query Routing Agent (guardrail + risk classification)
alt Invalid content
Gemini-->>Backend: blocked=True
Backend-->>Frontend: 400 CONTENT_BLOCKED
else High risk (self-harm/crisis)
Gemini-->>Backend: is_high_risk=True
Note over Backend: Skip emotion analysis, emotion="Crisis"
Backend->>Gemini: Image Prompt Agent + Image Generation (parallel)
Gemini-->>Backend: image_path
Backend-->>Frontend: {record_id, "Crisis", "fire", image_path}
else Normal worry
Gemini-->>Backend: is_high_risk=False
Backend->>Gemini: Emotion Agent + Image Pipeline (parallel)
Gemini-->>Backend: emotion_label, recommended_ritual, image_path
Backend-->>Frontend: {record_id, emotion_label, recommended_ritual, image_path}
end
User->>Frontend: Select ritual (π₯/π/π§)
Frontend->>Backend: POST /ritual
alt High risk record
Backend->>Gemini: Crisis Support Agent
Gemini-->>Backend: 3 safety suggestions + emergency summary
else Normal record
Backend->>Gemini: Ritual Agent (Fire CBT / Earth ACT / Water AEDP)
Note over Backend,Gemini: May call Web Search Agent for timely worries
Gemini-->>Backend: 3 perspectives + summary
end
Backend-->>Frontend: {perspectives, summary}
User->>Frontend: Select perspective
Frontend->>Frontend: Play totem animation π₯
Frontend->>Backend: POST /complete
Frontend-->>User: Show totem β¨ + summary
Loading
graph TB
subgraph GitHub
Repo[Repository]
end
subgraph Render[Render.com Serverless Host]
subgraph FE[Frontend]
React[React + Vite + TypeScript]
Tailwind[Tailwind CSS]
Framer[Framer Motion]
Zustand[Zustand Store]
I18n[i18next]
end
subgraph BE[Backend]
FastAPI[FastAPI + Python 3.12]
Logfire[Logfire Observability]
subgraph AgentLayer[AI Agents - Google ADK]
RoutingAgent[π‘οΈ Query Routing Agent<br/>Guardrail + Risk Classification]
EmotionAgent[π Emotion Analysis Agent]
RitualAgents[π₯ Fire CBT / π Earth ACT / π§ Water AEDP]
CrisisAgent[π¨ Crisis Support Agent]
SearchAgent[π Web Search Agent<br/>google_search tool]
ImagePromptAgent[πΌοΈ Image Prompt Agent<br/>π§ Thinking Mode]
end
subgraph Services[Services]
ImageGen[Image Generation<br/>google-genai direct]
ASR[π€ ASR Service<br/>Speech-to-Text]
end
SQLAlchemy[SQLAlchemy + Alembic]
end
subgraph Storage
VOL[(Volume /var/data)]
DB[(db.sqlite)]
IMG[images/]
end
end
subgraph External[Gemini API Models]
GeminiMain[gemini-3-flash-preview<br/>π§ Thinking Mode<br/>Main / Crisis / Image Prompt]
GeminiLite[gemini-2.5-flash-lite<br/>Routing / Guardrail / Search / ASR]
GeminiImage[gemini-2.5-flash-image<br/>π¨ Native Image Generation]
end
subgraph Testing[AI Governance]
Evals[π§ͺ LLM Evaluation Testing<br/><br/>π’ Giskard<br/>Safety Scans<br/>Harmfulness / Sycophancy / Hallucination / Prompt Injection<br/><br/>π pydantic-evals<br/>Quality Checks<br/>LLMJudge Rubrics / Perspective Count / Word Limits / Ritual Matching]
end
Repo -->|git push| Render
React --> FastAPI
FastAPI --> RoutingAgent
RoutingAgent -->|valid| EmotionAgent
RoutingAgent -->|high risk| CrisisAgent
EmotionAgent --> RitualAgents
RitualAgents -->|tool call| SearchAgent
CrisisAgent -->|tool call| SearchAgent
SQLAlchemy -->|history context| RitualAgents
SQLAlchemy -->|history context| CrisisAgent
ImagePromptAgent --> ImageGen
RoutingAgent -->|Google ADK| GeminiLite
EmotionAgent -->|Google ADK| GeminiMain
RitualAgents -->|Google ADK| GeminiMain
CrisisAgent -->|Google ADK| GeminiMain
SearchAgent -->|grounding| GeminiLite
ImagePromptAgent -->|Google ADK| GeminiMain
ImageGen --> GeminiImage
ASR --> GeminiLite
Logfire -.->|trace| AgentLayer
Evals -.->|test| AgentLayer
FastAPI --> SQLAlchemy
SQLAlchemy --> VOL
VOL --> DB
VOL --> IMG
Loading
Agent
Model
Purpose
Query Router
gemini-2.5-flash-lite
Content validation and risk detection
Emotion Detector
gemini-3-flash-preview
Emotion classification and ritual recommendation
Fire Ritual
gemini-3-flash-preview
CBT therapeutic perspectives
Water Ritual
gemini-3-flash-preview
AEDP therapeutic perspectives
Earth Ritual
gemini-3-flash-preview
ACT therapeutic perspectives
Crisis Support
gemini-3-flash-preview
High-risk content safety suggestions
Image Prompt
gemini-3-flash-preview
Ritual image prompt generation
Web Search
gemini-2.5-flash-lite
Fact-checking user claims
ASR
gemini-2.5-flash-lite
Audio transcription
Image Gen
gemini-2.5-flash-image
Ritual image generation
Method
Path
Purpose
GET
/health
Health check
POST
/api/analyze
Emotion detection and ritual recommendation
POST
/api/ritual
Generate therapeutic perspectives
POST
/api/complete
Mark ritual as completed
GET
/api/records
List completed rituals
POST
/api/transcribe
Audio-to-text transcription
GET
/api/images/{device_id}/{filename}
Serve ritual images
All /api/* endpoints require an X-Device-Id header (UUID).
Bun (v1.0+)
uv (Python package manager)
just (command runner)
Docker (optional, for containerized dev)
# Clone the repo
git clone https://github.com/your-org/emoFuneral.git
cd emoFuneral
# Copy environment files
cp frontend/.env.example frontend/.env
cp backend/.env.example backend/.env
# Edit backend/.env to set GOOGLE_API_KEY
# Install dependencies
cd frontend && bun install && cd ..
cd backend && uv sync && cd ..
# Start development servers
just dev-frontend # in one terminal
just dev-backend # in another terminal
Or use Docker:
just # List all commands
# Frontend
just dev-frontend # Start Vite dev server
just lint-frontend # Run Biome lint
just format-frontend # Format with Biome
just test-frontend # Run Bun test
just typecheck-frontend # TypeScript check
# Backend
just dev-backend # Start FastAPI server
just lint-backend # Run Ruff lint
just format-backend # Format with Ruff
just test-backend # Run pytest
just typecheck-backend # Run ty type check
just security-backend # Run Bandit security scan
just migrate # Run database migrations
# All
just lint # Lint frontend + backend
just format # Format all
just fix # Auto-fix formatting + linting
just test # Test all
just ci # Run all CI checks (lint, typecheck, security, env-check, test)
# LLM Evaluation (real API calls, NOT in CI)
just eval-giskard-safety # Giskard safety-critical tests (~3-5 min)
just eval-giskard # All Giskard eval tests
just eval-pydantic # pydantic-evals tests (LLMJudge + deterministic evaluators)
just eval # All evaluation tests (Giskard + pydantic-evals)
just eval-giskard-scan # Full Giskard vulnerability scan (~30+ min, HTML reports)
# Docker
just up # Start containers
just down # Stop containers
just up-build # Rebuild and start
emoFuneral/
βββ frontend/ # React + Vite frontend
β βββ src/
β βββ api/ # API client with case conversion
β βββ components/ # React components
β β βββ Ritual/ # Ritual flow steps (input β analyze β select β animate β complete)
β β βββ Museum/ # Emotion museum carousel and totem cards
β β βββ common/ # Shared UI (EmotionOrb, GlassButton, VoiceInput, etc.)
β βββ hooks/ # Custom hooks (animation, audio, viewport, speech)
β βββ stores/ # Zustand state management
β βββ i18n/ # Translations (en, zh)
β βββ config/ # Animation timings, sizes, API config
β βββ mocks/ # MSW handlers for development
β βββ types/ # TypeScript type definitions
βββ backend/ # FastAPI backend
β βββ app/
β βββ routers/ # API endpoint handlers
β βββ services/ # AI agents and business logic
β β βββ query_routing_agent/ # Content validation + risk detection
β β βββ ritual_recommend_agent/ # Emotion detection + ritual recommendation
β β βββ ritual_agents/ # Fire (CBT), Water (AEDP), Earth (ACT), Crisis
β β βββ image_prompt_agent/ # Ritual image prompt generation
β β βββ web_search_agent/ # Fact-checking via Google Search
β β βββ image_gen/ # Image generation with feedback loop
β β βββ asr/ # Audio transcription
β βββ tests/
β β βββ evaluation/ # LLM eval tests (71 tests, --run-eval)
β β β βββ conftest.py # Real API key + Giskard/pydantic-evals config
β β β βββ datasets.py # Hand-crafted test inputs (shared)
β β β βββ giskard_evals/ # Giskard scan + direct assertion tests
β β β βββ pydantic_evals/ # pydantic-evals (LLMJudge + deterministic evaluators)
β β βββ ... # Unit tests (154 tests)
β βββ db/ # SQLAlchemy models and session management
β βββ schemas/ # Pydantic request/response models
β βββ middleware/ # Device ID validation
β βββ fallbacks/ # Static fallback content when AI fails
β βββ core/ # Configuration and settings
βββ docs/ # Documentation
β βββ prd.md # Product requirements
β βββ tech-spec.md # Technical specification
β βββ architecture.md # System architecture diagrams
βββ .github/workflows/ # CI: backend, frontend, env, docs checks
βββ justfile # Development commands
βββ docker-compose.yml # Local containerized dev
βββ render.yaml # Render.com deployment blueprint
See .env.example files in frontend/ and backend/ directories.
Backend (backend/.env.example)
Variable
Default
Description
GOOGLE_API_KEY
(required)
Google Gemini API key
GEMINI_MODEL
gemini-3-flash-preview
Primary reasoning model
GEMINI_GUARDRAIL_MODEL
gemini-2.5-flash-lite
Guardrail content classifier
GEMINI_ROUTING_MODEL
gemini-2.5-flash-lite
Query routing / risk classifier
GEMINI_CRISIS_MODEL
gemini-3-flash-preview
Crisis support agent
GEMINI_IMAGE_MODEL
gemini-2.5-flash-image
Image generation
GEMINI_ASR_MODEL
gemini-2.5-flash-lite
Audio transcription
GEMINI_SEARCH_MODEL
gemini-2.5-flash-lite
Web search agent
GEMINI_PROMPT_MODEL
gemini-3-flash-preview
Image prompt generation
GEMINI_THINKING_LEVEL
LOW
Extended thinking (MINIMAL/LOW/MEDIUM/HIGH)
GEMINI_IMAGE_BLOCK_LEVEL
BLOCK_ONLY_HIGH
Image safety threshold
GEMINI_MAX_OUTPUT_TOKENS
4096
Max output tokens for agents
GEMINI_SEARCH_MAX_TOKENS
350
Web search output limit
WORRY_HISTORY_LIMIT
5
Max past worries in ritual context
PERSPECTIVE_MAX_WORDS
20
Max words per perspective/summary
IMAGE_PROMPT_MAX_ATTEMPTS
3
Image generation feedback loop retries
AGENT_MAX_RETRIES
2
Agent call retries
AGENT_RETRY_DELAY
0.5
Initial retry delay (seconds, exponential backoff)
CONTENT_MAX_LENGTH
500
Max user input characters
GUARDRAIL_MAX_TOKENS
50
Guardrail judge output limit
DB_POOL_RECYCLE
1800
DB connection recycle interval (seconds)
LOGFIRE_TOKEN
(optional)
Logfire tracing token
RATE_LIMIT_PER_MINUTE
100
API rate limit
DATA_DIR
/var/data
Data/image storage directory
GISKARD_EVAL_MODEL
gemini/gemini-3-flash-preview
Giskard scan evaluator LLM (litellm provider/model)
GISKARD_EMBEDDING_MODEL
gemini/gemini-embedding-001
Giskard scan embedding model (litellm provider/model)
PYDANTIC_EVAL_MODEL
google-gla:gemini-3-flash-preview
pydantic-evals LLMJudge model (pydantic-ai provider:model)
CORS_ORIGINS_STR
http://localhost:5173
Comma-separated allowed origins
Frontend (frontend/.env.example)
Variable
Default
Description
VITE_API_URL
http://localhost:8000
Backend API URL
VITE_API_TIMEOUT
60000
API request timeout (ms)
VITE_RITUAL_CONTENT_MAX_CHARS
500
Max input characters
VITE_BGM_VOLUME
0.2
Background music volume (0-1)
VITE_EFFECT_VOLUME
0.5
Ritual sound effect volume (0-1)
VITE_ENABLE_MOCKS
false
Force MSW mocks (true/false)