AI-powered personal assistant with real memory and proactive actions.
Lives in Telegram and WhatsApp. Remembers everything. Acts before you ask.
Leer en Espanol · Setup Guide · Releases
Try Evva now — no setup required: t.me/evva_dev_bot
| Brain Persistent semantic memory | Bell Scheduled reminders & proactive messages |
| Note Notes, lists & todos | Person Contact management |
| Calendar Google Calendar integration | Mail Gmail read, search & send |
| Dollar Personal finance & savings goals | Mic Voice note transcription (Groq Whisper) |
| Camera Photo & document analysis (Claude Vision) | Sun Daily proactive briefing |
| Globe Web search (Brave) & news summaries | Cloud Weather for any city |
| Pill Medication & habit tracking | Phone Emergency contacts |
| Language Translation & smart dictation | Music Spotify integration |
| Exchange Live currency conversion | Birthday Proactive birthday reminders |
| Recipe Recipe suggestions from grocery lists | WhatsApp Multi-channel (Telegram + WhatsApp) |
| Plane Flight search (SerpAPI) + bus search (Firecrawl) | Broom Auto-unsubscribe from newsletters |
| Speaker Voice responses (OpenAI TTS) | Puzzle Self-extending runtime skills |
| Pencil Update and delete user data |
| Layer | Technology |
|---|---|
| Framework | NestJS + TypeScript |
| Bot | grammY |
| LLM | Claude Sonnet (Vercel AI SDK) |
| Embeddings | Voyage AI (voyage-3-lite, 512 dims) |
| Database | PostgreSQL + pgvector |
| Queue | BullMQ + Redis |
| Transcription | Groq (Whisper) |
| Vision | Claude Vision |
| Search | Brave Search API |
| Monorepo | pnpm workspaces |
Evva follows a gateway/worker split. The gateway handles all user-facing interactions -- receiving messages, orchestrating the LLM agent loop, and returning responses. Heavy or scheduled work (reminders, fact extraction, daily briefings) is offloaded to an async worker via BullMQ, keeping the bot responsive at all times.
Telegram / WhatsApp
|
v
+-----------------------------------+
| Gateway (NestJS) |
| |
| TelegramModule -- receives messages, sends replies
| ConversationModule -- orchestrates the agent loop
| MemoryModule -- RAG over user facts
| PersonaModule -- dynamic system prompt
| ToolsModule -- web_search, reminders, etc.
| SchedulerModule -- enqueues jobs via BullMQ
| UsersModule -- user and assistant CRUD
+-----------------------------------+
| BullMQ (Redis)
v
+-----------------------------------+
| Worker (NestJS) |
| |
| ScheduledJobProcessor -- executes reminders
| FactExtractionProcessor -- extracts facts from conversations
| DailyBriefingProcessor -- sends daily morning summaries
+-----------------------------------+
|
v
+-----------------------------------+
| PostgreSQL + pgvector |
| |
| users, assistant_config, |
| messages, memory_facts, |
| onboarding_state |
+-----------------------------------+
For detailed step-by-step instructions, see docs/SETUP.md.
Prerequisites: Node.js >= 22, pnpm >= 9, PostgreSQL with pgvector, Redis.
git clone https://github.com/lacasoft/evva-ai.git
cd evva-ai
cp .env.example .env
# Edit .env with your credentials (see .env.example for all variables)
docker compose upPostgreSQL, Redis, migrations, gateway, and worker all start automatically.
Option B -- Manual setup
git clone https://github.com/lacasoft/evva-ai.git
cd evva-ai
pnpm install
cp .env.example .env
# Edit .env with your credentials
pnpm db:migrate
# Terminal 1 -- Gateway (Telegram bot)
pnpm dev:gateway
# Terminal 2 -- Worker (BullMQ processors)
pnpm dev:workerThe bot starts in long polling mode -- no webhook setup required for local development.
evva/
├── apps/
│ ├── gateway/ # Main app -- Telegram bot + API
│ │ └── src/
│ │ ├── telegram/ # Telegram message handler
│ │ ├── conversation/ # Agent loop + onboarding
│ │ ├── memory/ # Fact search and storage
│ │ ├── persona/ # System prompt construction
│ │ ├── tools/ # Tools available to the LLM
│ │ ├── scheduler/ # BullMQ job scheduling
│ │ ├── users/ # User and assistant CRUD
│ │ ├── health/ # Health check endpoints
│ │ └── config/ # Env var validation
│ │
│ └── worker/ # BullMQ worker -- async jobs
│ └── src/
│ ├── processors/ # ScheduledJob + FactExtraction
│ └── handlers/ # TelegramSender
│
├── packages/
│ ├── core/ # Shared types, constants, and utils
│ ├── database/ # PostgreSQL client (pg) and repositories
│ ├── ai/ # LLM (Claude), embeddings (Voyage), prompts
│ └── skills/ # Modular skill plugins (28 skills)
│ └── src/
│ ├── registry.ts # Central skill registry
│ ├── base-skill.ts # SkillDefinition interface
│ ├── rag-helper.ts # Cross-skill RAG fact saving
│ ├── runtime-loader.ts # Runtime skill engine
│ ├── memory/ # Persistent semantic memory
│ ├── notes/ # Notes and lists
│ ├── contacts/ # Contact management
│ ├── data-management/ # Update and delete user data
│ ├── reminders/ # Scheduled reminders
│ ├── finance/ # Cards, transactions, savings
│ ├── finance-security/ # Secret word protection
│ ├── health/ # Medication and habit tracking
│ ├── emergency/ # Emergency contacts
│ ├── calendar/ # Google Calendar
│ ├── gmail/ # Gmail (read, search, send)
│ ├── spotify/ # Spotify integration
│ ├── weather/ # Weather lookup
│ ├── search/ # Web search (Brave)
│ ├── news/ # News summary
│ ├── translator/ # Language translation
│ ├── exchange/ # Currency exchange rates
│ ├── dictation/ # Smart message drafting
│ ├── briefing/ # Daily proactive briefing
│ ├── birthdays/ # Birthday reminders
│ ├── recipes/ # Recipe suggestions
│ ├── voice/ # Voice note transcription
│ ├── vision/ # Photo and document analysis
│ ├── travel/ # Flight and bus search
│ ├── email-cleaner/ # Auto-unsubscribe from newsletters
│ ├── tts/ # Text-to-speech voice responses
│ └── skill-creator/ # Self-extending runtime skills
│
└── docs/ # Setup guides and documentation
Adding a New Skill
Each skill is a self-contained module. To add a new one:
// packages/skills/src/my-skill/index.ts
import { tool } from 'ai';
import { z } from 'zod';
import type { SkillDefinition } from '../base-skill.js';
export const mySkill: SkillDefinition = {
name: 'my-skill',
description: 'What this skill does',
category: 'utility',
forProfiles: ['young', 'adult', 'senior'],
requiredEnv: ['MY_API_KEY'], // optional
buildTools: (ctx) => ({
my_tool: tool({
description: 'Tool description for the LLM',
parameters: z.object({ input: z.string() }),
execute: async ({ input }) => {
return { success: true, result: input };
},
}),
}),
promptInstructions: [
'- my_tool: Description of what this tool does',
],
};Then register it in packages/skills/src/index.ts:
export { mySkill } from './my-skill/index.js';
import { mySkill } from './my-skill/index.js';
skillRegistry.register(mySkill);The skill is automatically available to the LLM. No other files need to change.
- User sends a message in Telegram or WhatsApp.
TelegramServicereceives the message and upserts the user in PostgreSQL.- If the user is new, the onboarding flow starts (choose assistant name).
- For existing users: semantic search retrieves relevant facts (Voyage + pgvector), a dynamic system prompt is constructed, recent history is loaded, and Claude is called with all available tools.
- If the LLM invokes a tool, it is executed and the result is fed back.
- The final response is sent to the user. The message is persisted, and fact extraction is enqueued in BullMQ (async).
- User says "remind me to X on Friday at 8am".
- Claude calls the
create_remindertool. SchedulerServicecreates a BullMQ job with the calculated delay.- At the scheduled time,
ScheduledJobProcessorgenerates a personalized message with Claude and delivers it to the user.
| Tool | Description | Requires |
|---|---|---|
save_fact |
Persist a permanent fact about the user | -- |
create_note |
Create notes or lists | -- |
get_notes |
View active notes and lists | -- |
update_note |
Modify, check items, archive or delete notes | -- |
save_contact |
Store a contact (name, phone, email, relationship) | -- |
search_contacts |
Search contacts by name or relationship | -- |
create_reminder |
Schedule a future reminder | Redis |
configure_daily_briefing |
Enable/disable daily morning summary | -- |
| Tool | Description | Requires |
|---|---|---|
add_credit_card |
Register a credit card with cut-off and payment dates | -- |
get_credit_cards |
View registered cards with balances | -- |
record_transaction |
Log income or expense | -- |
get_finance_summary |
Monthly financial summary by category | -- |
get_recent_transactions |
View recent transactions | -- |
create_savings_goal |
Create a savings goal with target amount | -- |
get_savings_goals |
View savings progress | -- |
| Tool | Description | Requires |
|---|---|---|
connect_google |
Generate Google OAuth link (Calendar + Gmail) | Google OAuth |
list_calendar_events |
List upcoming calendar events | Google OAuth |
create_calendar_event |
Create a new calendar event | Google OAuth |
list_emails |
List recent emails | Google OAuth |
read_email |
Read full email content | Google OAuth |
send_email |
Send an email from user's Gmail | Google OAuth |
| Tool | Description | Requires |
|---|---|---|
add_medication |
Register a medication with schedule | -- |
get_medications |
View active medications | -- |
create_habit |
Create a habit to track daily | -- |
log_habit |
Log progress on a habit | -- |
get_habit_progress |
View today's habit completion | -- |
add_emergency_contact |
Register an emergency contact | -- |
get_emergency_contacts |
View emergency contacts | -- |
| Tool | Description | Requires |
|---|---|---|
web_search |
Up-to-date web search | BRAVE_SEARCH_API_KEY |
summarize_news |
Search and summarize current news | BRAVE_SEARCH_API_KEY |
get_weather |
Current weather for a city | -- |
translate |
Translate text between languages | -- |
calculate_exchange_rate |
Live currency conversion | -- |
draft_message |
Generate formal/informal messages | -- |
save_birthday |
Save a birthday date | -- |
check_upcoming_birthdays |
Find upcoming birthdays | -- |
suggest_recipes |
Suggest recipes from grocery list | -- |
| Tool | Description | Requires |
|---|---|---|
connect_spotify |
Generate Spotify OAuth link | Spotify OAuth |
now_playing |
Show currently playing track | Spotify OAuth |
recent_tracks |
Show recently played tracks | Spotify OAuth |
top_tracks |
Show user's top tracks | Spotify OAuth |
search_music |
Search Spotify catalog | Spotify OAuth |
| Tool | Description | Requires |
|---|---|---|
search_flights |
Search flights between cities | SERPAPI_API_KEY |
search_airport |
Find airport codes by city name | SERPAPI_API_KEY |
get_booking_link |
Get a booking link for a flight | SERPAPI_API_KEY |
search_buses |
Search bus routes between cities | FIRECRAWL_API_KEY |
get_travel_page_info |
Extract details from a travel page | FIRECRAWL_API_KEY |
| Tool | Description | Requires |
|---|---|---|
update_user_data |
Update contacts, emergency, cards, medications, habits | -- |
delete_user_data |
Delete contacts, facts, medications, habits | -- |
set_finance_secret |
Set a secret word for finance protection | -- |
verify_finance_secret |
Verify secret word before financial operations | -- |
create_runtime_skill |
Create a custom skill via conversation | -- |
list_runtime_skills |
List user-created custom skills | -- |
Evva is designed to minimize LLM token consumption per message:
| Optimization | Before | After | Savings |
|---|---|---|---|
| Conversation history window | 12 messages | 6 messages | ~480 tokens |
| maxSteps (tool call rounds) | 3 | 2 | ~5,400 tokens |
| System prompt (behavioral block) | 225 tokens | 80 tokens | ~145 tokens |
| OAuth skill filtering | All 60 tools always loaded | Only connected tools loaded | ~2,000 tokens |
| Provider query | 2 DB calls per message | 1 cached call (Redis 5min TTL) | Latency |
| Skill registry | env check on every call | Cached after first call | CPU |
Estimated total reduction: ~55% (from ~23K to ~10K tokens per message)
Before: [system ~1,400] + [60 tools ~4,200] + [history ~960] x 3 steps = ~27,000 tokens
After: [system ~1,200] + [~25 tools ~1,800] + [history ~480] x 2 steps = ~11,000 tokens
| Variable | Where to get it |
|---|---|
TELEGRAM_BOT_TOKEN |
@BotFather on Telegram |
ANTHROPIC_API_KEY |
console.anthropic.com |
VOYAGE_API_KEY |
dash.voyageai.com |
DATABASE_URL |
postgresql://localhost:5432/evva for local dev |
REDIS_URL |
redis://localhost:6379 for local dev |
| Variable | Purpose |
|---|---|
BRAVE_SEARCH_API_KEY |
Web search tool (brave.com/search/api) |
GROQ_API_KEY |
Voice note transcription via Whisper |
GOOGLE_CLIENT_ID |
Google Calendar and Gmail integration |
GOOGLE_CLIENT_SECRET |
Google Calendar and Gmail integration |
APP_BRAND_NAME |
Custom assistant brand name (default: "Evva") |
LLM_MODEL |
Override the default Claude model |
TELEGRAM_WEBHOOK_URL |
Production only |
TELEGRAM_SECRET_TOKEN |
Production only -- generate with openssl rand -hex 32 |
pnpm build # Build all packages and apps
pnpm build:gateway # Build gateway only
pnpm build:worker # Build worker only
pnpm test # Run all tests
pnpm test:watch # Run tests in watch mode
pnpm test:cov # Run tests with coverage
pnpm lint # Lint all packages
pnpm lint:fix # Lint and auto-fix
pnpm db:migrate # Run pending migrations
pnpm db:migrate:new # Create a new migration file| Command | Action |
|---|---|
/start |
Welcome message and onboarding |
/reset |
Start a new conversation session |
/memory |
Show what the assistant remembers |
/help |
List available commands |
Contributions are welcome! Fork the repo, create a feature branch, and open a pull request. Please ensure all tests pass (pnpm test) and linting is clean (pnpm lint) before submitting. For bugs and feature requests, open an issue.

