| title | Storing Conversations & Memories |
|---|---|
| icon | database |
| description | This document outlines how Omi stores conversation data and extracts memories from conversations. |
Omi uses a dual-collection architecture for storing user data:
**Primary storage** for recorded interactions - transcripts, audio, structured summaries **Secondary storage** for extracted facts/learnings FROM conversationsThis separation allows for efficient retrieval of both full conversation context and quick access to key facts about the user.
flowchart TD
subgraph Recording["📱 Recording"]
R[User Recording] --> AS[Audio Stream]
AS --> T[Transcription<br/>Parakeet / Modulate]
end
T --> POST[POST /v1/conversations]
POST --> PC[process_conversation]
PC --> GS[_get_structured<br/>title, overview,<br/>action_items, events]
PC --> EM[_extract_memories<br/>facts → memories]
PC --> SAI[_save_action_items<br/>standalone collection]
GS --> UC[upsert_conversation]
EM --> SM[save_memories]
UC --> FS[(Firestore:<br/>conversations/)]
SM --> FSM[(Firestore:<br/>memories/)]
UC --> Pine[(Pinecone:<br/>vectors)]
users/
├── {uid}/
│ ├── conversations/ # PRIMARY - Recorded interactions
│ │ └── {conversation_id}/
│ │ ├── id
│ │ ├── created_at
│ │ ├── started_at
│ │ ├── finished_at
│ │ ├── source
│ │ ├── language
│ │ ├── status
│ │ ├── structured
│ │ ├── transcript_segments
│ │ ├── geolocation
│ │ ├── photos/ (subcollection)
│ │ ├── audio_files
│ │ ├── apps_results
│ │ ├── discarded
│ │ ├── visibility
│ │ ├── is_locked
│ │ └── data_protection_level
│ │
│ ├── memories/ # SECONDARY - Extracted facts
│ │ └── {memory_id}/
│ │ ├── id
│ │ ├── uid
│ │ ├── conversation_id
│ │ ├── content
│ │ ├── category
│ │ ├── tags
│ │ ├── visibility
│ │ ├── created_at
│ │ ├── updated_at
│ │ ├── reviewed
│ │ ├── user_review
│ │ ├── scoring
│ │ └── data_protection_level
│ │
│ └── action_items/ # Standalone action items
│ └── {action_item_id}/
│ ├── description
│ ├── completed
│ ├── conversation_id
│ ├── created_at
│ ├── due_at
│ └── completed_at
The app sends a POST request to `/v1/conversations` with transcript data `process_conversation()` in `utils/conversations/process_conversation.py` handles the logic LLM extracts title, overview, action items, and events from the transcript `upsert_conversation()` in `database/conversations.py` saves to Firestore Conversation is embedded and stored in Pinecone for semantic search
| Field | Type | Description |
|---|---|---|
id |
string | Unique conversation identifier |
created_at |
datetime | When the conversation record was created |
started_at |
datetime | When the actual conversation started |
finished_at |
datetime | When the conversation ended |
source |
enum | Source device (omi, phone, desktop, openglass, etc.) |
language |
string | Language code of the conversation |
status |
enum | Processing status: in_progress, processing, completed, failed |
structured |
object | Extracted structured information (see below) |
transcript_segments |
array | List of transcript segments |
geolocation |
object | Location data (latitude, longitude, address) |
photos |
array | Photos captured during conversation |
audio_files |
array | Audio file references |
apps_results |
array | Results from summarization apps |
external_data |
object | Data from external integrations |
discarded |
boolean | Whether conversation was marked as low-quality |
visibility |
enum | private, shared, or public |
is_locked |
boolean | Whether conversation is locked from editing |
data_protection_level |
string | standard or enhanced (encrypted) |
The structured field contains LLM-extracted information:
| Field | Type | Description |
|---|---|---|
title |
string | Short descriptive title for the conversation |
overview |
string | Summary of key points discussed |
emoji |
string | Emoji representing the conversation |
category |
enum | Category (personal, work, health, etc.) |
action_items |
array | Tasks or to-dos mentioned |
events |
array | Calendar events to be created |
Each segment in transcript_segments includes:
| Field | Type | Description |
|---|---|---|
text |
string | Transcribed text content |
speaker |
string | Speaker label (e.g., "SPEAKER_00") |
start |
float | Start time in seconds |
end |
float | End time in seconds |
is_user |
boolean | Whether spoken by the device owner |
person_id |
string | ID of identified person (if matched) |
Action items are stored both inline (in structured.action_items) and in a standalone collection:
| Field | Type | Description |
|---|---|---|
description |
string | The action item text |
completed |
boolean | Whether the item is done |
created_at |
datetime | When extracted |
due_at |
datetime | Optional due date |
completed_at |
datetime | When marked complete |
conversation_id |
string | Source conversation |
Calendar events extracted from conversations:
| Field | Type | Description |
|---|---|---|
title |
string | Event title |
description |
string | Event description |
start |
datetime | Start date/time |
duration |
integer | Duration in minutes |
created |
boolean | Whether added to calendar |
Memories are facts about the user extracted from conversations. They represent learnings, preferences, habits, and other personal information.
During process_conversation(), the system:
| Field | Type | Description |
|---|---|---|
id |
string | Unique memory identifier |
uid |
string | User ID |
conversation_id |
string | Source conversation (links back) |
content |
string | The actual fact/learning (max ~15 words) |
category |
enum | interesting, system, or manual |
tags |
array | Categorization tags |
visibility |
string | private or public |
created_at |
datetime | When memory was created |
updated_at |
datetime | Last modification time |
reviewed |
boolean | Whether user has reviewed |
user_review |
boolean | User's approval (true/false/null) |
edited |
boolean | Whether user edited the content |
scoring |
string | Ranking score for retrieval |
manually_added |
boolean | Whether user created manually |
is_locked |
boolean | Prevent automatic deletion |
app_id |
string | Source app (if from integration) |
data_protection_level |
string | Encryption level |
The system follows these guidelines when extracting memories:
- Maximum ~15 words per memory
- Must pass the "shareability test" - would this be worth telling someone?
- Maximum 2
interesting+ 2systemmemories per conversation - No duplicate or near-duplicate facts
- Skip mundane details (eating, sleeping, commuting)
Both conversations and memories support encryption for sensitive data.
### Standard Protection LevelNo encryption, stored as plaintext. This is the default for most users.
- Fastest read/write performance
- Data visible in Firestore console
- Suitable for general use
AES encryption for sensitive fields. Provides additional security for sensitive conversations.
**Encrypted Fields:**
- **Conversations**: `transcript_segments` (the actual transcript text)
- **Memories**: `content` (the memory text)
<Warning>
Enhanced encryption adds processing overhead to read/write operations.
</Warning>
# Conversations: database/conversations.py
def _prepare_conversation_for_write(conversation_data, data_protection_level):
if data_protection_level == 'enhanced':
# Encrypt transcript_segments before storage
...
def _prepare_conversation_for_read(conversation_data, data_protection_level):
if data_protection_level == 'enhanced':
# Decrypt transcript_segments after retrieval
...Conversations are also stored as vector embeddings in Pinecone for semantic search.
| Data | Embedded? | Stored in Metadata? |
|---|---|---|
| Title | Yes | No |
| Overview | Yes | No |
| Action Items | Yes | No |
| Full Transcript | No (too large) | No |
| People Mentioned | No | Yes |
| Topics | No | Yes |
| Entities | No | Yes |
| created_at | No | Yes |
Vectors are created in a background thread after conversation processing:
# utils/conversations/process_conversation.py
threading.Thread(
target=save_structured_vector,
args=(uid, conversation)
).start()The save_structured_vector() function:
- Generates embedding from
conversation.structured(title + overview + action_items + events) - Extracts metadata via LLM (people, topics, entities, dates)
- Upserts to Pinecone with metadata filters
| Component | File Path |
|---|---|
| Conversation Model | backend/models/conversation.py |
| Memory Model | backend/models/memories.py |
| Process Conversation | backend/utils/conversations/process_conversation.py |
| Database - Conversations | backend/database/conversations.py |
| Database - Memories | backend/database/memories.py |
| Router - Conversations | backend/routers/conversations.py |
| Router - Memories | backend/routers/memories.py |
| Vector Database | backend/database/vector_db.py |
| Method | Endpoint | Description |
|---|---|---|
| POST | /v1/conversations |
Process and store a new conversation |
| GET | /v1/conversations |
List user's conversations |
| GET | /v1/conversations/{id} |
Get specific conversation |
| PATCH | /v1/conversations/{id}/title |
Update conversation title |
| DELETE | /v1/conversations/{id} |
Delete a conversation |
| Method | Endpoint | Description |
|---|---|---|
| POST | /v3/memories |
Create a manual memory |
| GET | /v3/memories |
List user's memories |
| PATCH | /v3/memories/{id} |
Edit a memory |
| DELETE | /v3/memories/{id} |
Delete a memory |
| PATCH | /v3/memories/{id}/visibility |
Change memory visibility |
How conversations are retrieved for chat using LangGraph WebSocket-based real-time speech-to-text General backend architecture overview Environment setup and configuration