Add Memory Title Generation to Enrichment Pipeline
System Intent
This plan adds automatic title generation for memories to improve user comprehension and memory discoverability. During enrichment, the system will use an LLM to generate concise, descriptive titles from memory transcripts. The backend will persist titles in the memory ORM, and the frontend will display titles with graceful fallback to default behavior if titles are unavailable.
Mermaid Diagram
graph LR
A[Enrichment Pipeline] -->|Transcript| B[LLM Title Generator]
B -->|Generated Title| C[Memory ORM]
C -->|Store Title Field| D[Database]
C -->|Query with Title| E[Frontend]
E -->|Display Title or Fallback| F[User Interface]
D -->|Retrieve Title| C
style A fill:#e1f5ff
style B fill:#fff3e0
style C fill:#f3e5f5
style D fill:#e8f5e9
style E fill:#fce4ec
style F fill:#f1f8e9 Black-Box Input/Output Contracts
System-Level Contract
Input
- Enriched Memory Object — Memory with populated transcript field
- Type: Memory object (ORM entity)
- Required fields:
id,transcript,created_at - Optional fields: existing
title(null or string)
Output (Success)
- Memory with Title — Memory object persisted with generated title
- Type: Memory object
- Fields: all input fields +
title(non-null string, 50–150 characters) - Behavior: Title summarizes key content from transcript for quick recognition
Output (Failure)
- Memory without Title — Memory persists without generated title
- Type: Memory object
- Fields: all input fields with
title= null - Behavior: Frontend falls back to default display (e.g., timestamp or "Untitled Memory")
- Trigger: LLM call timeout, API error, rate limit exceeded
Contracts by Flow
Flow: Generate Title from Transcript
| Component | Input | Output | Validation | Updated |
|---|---|---|---|---|
| LLM Service | Transcript text (string, max 4000 chars) | Title (string, 50–150 chars) | Title length within bounds; no empty strings | Y |
| Enrichment Processor | Memory object with transcript | Memory object with title field populated | Title field is non-null; ORM save succeeds | Y |
| Error Handling | LLM timeout or API error | Memory with title=null, error logged | Error logged; memory persists regardless | Y |
Flow: Persist Title to ORM
| Component | Input | Output | Validation | Updated |
|---|---|---|---|---|
| Memory ORM | Title value (string or null) | Database row with title field | Foreign key integrity; title field accepts null | Y |
| Database Schema | Title column (nullable VARCHAR) | Persisted title row | Column type allows null; index on title for search | Y |
Flow: Display Title in Frontend
| Component | Input | Output | Validation | Updated |
|---|---|---|---|---|
| Frontend API | Query memory with title field | Title (or null) | API returns title field; null is valid | Y |
| UI Component | Memory object with title (or null) | Rendered title or fallback text | If title exists, show it; else show default label | Y |
Acceptance Criteria and Test Flows
Test Flow 1: Generate and Persist Title (Happy Path)
Inputs: - Memory object with transcript = "I went to the park today. The weather was sunny. I saw my friends."
Pass Criteria: 1. LLM generates a title (e.g., "Park Visit with Friends") 2. Title length is between 50 and 150 characters 3. Title is persisted to database in memory.title field 4. Memory object returned by ORM includes title field populated 5. No errors logged
Fail Criteria: - Title is empty or exceeds length bounds - Database save fails - ORM does not return title field in response
Test Flow 2: Handle LLM Timeout (Error Path)
Inputs: - Memory object with transcript - LLM service timeout occurs (simulated)
Pass Criteria: 1. Title generation attempt fails with timeout error 2. Error is logged with memory ID and timestamp 3. Memory is persisted to database with title=null 4. Memory object returned includes title=null 5. Enrichment pipeline continues (does not halt)
Fail Criteria: - Memory is not persisted when LLM fails - Error is not logged - Enrichment pipeline halts
Test Flow 3: Frontend Displays Title (Happy Path)
Inputs: - API query returns memory object with title = "Park Visit with Friends"
Pass Criteria: 1. Frontend receives title in API response 2. UI component renders title in memory card 3. Title is clickable/searchable if applicable 4. No console errors
Fail Criteria: - Title is not rendered - UI breaks if title is missing
Test Flow 4: Frontend Fallback when Title is Null (Edge Case)
Inputs: - API query returns memory object with title=null
Pass Criteria: 1. Frontend receives title=null 2. UI component displays fallback text (e.g., "Untitled Memory" or timestamp) 3. Memory is still usable (clickable, selectable) 4. No console errors
Fail Criteria: - UI breaks or shows "null"/"undefined" - Memory becomes unusable
Recommended Additional Tests
- Batch Title Generation — Verify enrichment handles multiple memories in a single run without rate-limit errors
- Title Uniqueness — Verify two different transcripts produce different titles (no copy-paste errors from LLM)
- Search Integration — Verify titles are indexed and searchable (if applicable to feature scope)
- Concurrent Updates — Verify database handles concurrent title updates without race conditions
- Empty Transcript — Verify graceful fallback when transcript is empty or very short
Pseudocode
Enrichment Processor: generate_title_for_memory
function enrich_memory_with_title(memory):
if memory.transcript is empty or too short:
memory.title = null
return memory
try:
# Call LLM to generate title
title = llm.generate_title(
transcript=memory.transcript,
max_length=150,
min_length=50,
context="User memory from wearable AI system"
)
# Validate title
if title is None or len(title) < 50 or len(title) > 150:
log_error(f"Title validation failed for memory {memory.id}: {title}")
memory.title = null
else:
memory.title = title
except TimeoutError:
log_error(f"LLM timeout for memory {memory.id}")
memory.title = null
except RateLimitError:
log_error(f"LLM rate limit for memory {memory.id}")
memory.title = null
except Exception as e:
log_error(f"Unexpected error generating title for memory {memory.id}: {e}")
memory.title = null
finally:
# Always persist memory, with or without title
memory.save()
return memory
Frontend: MemoryCard Component
function MemoryCard(memory):
title_display = memory.title if memory.title else "Untitled Memory"
timestamp_display = format_time(memory.created_at)
render:
<div class="memory-card">
<h3 class="memory-title">{title_display}</h3>
<p class="memory-time">{timestamp_display}</p>
<p class="memory-preview">{memory.transcript[:100]}...</p>
</div>
Status
Draft — Awaiting approval of system intent, diagram, contracts, and acceptance criteria before implementation.
Files to Create/Modify
src/backend/enrichment/title_generator.py— LLM title generation servicesrc/backend/enrichment/enrichment_processor.py— Update to call title generatorsrc/backend/orm/models.py— Add nullabletitlefield to Memory modelsrc/backend/db/migrations/— Add title column to memories tablesrc/frontend/components/MemoryCard.tsx— Update to display title with fallbacktests/backend/test_enrichment_titles.py— Unit and integration teststests/frontend/MemoryCard.test.tsx— Frontend component tests