Skip to content

AI Generation & Media Pipeline

This section explains how DreamStream turns a dream entry into analysis + visuals in the background, how results appear “live” in the UI, and how DreamStream stores images/audio securely.

The promise to the user (plain language)

When you log a dream, DreamStream doesn’t force you to wait.

  • The dream saves immediately.
  • AI results (analysis and visuals) appear as they finish.
  • If something fails, you see a clear message and can retry.

What DreamStream generates

Automatically generated (default after saving a dream)

DreamStream queues these background tasks for most newly saved dreams:

Output What it is (user-facing) Where it appears
Analysis Supportive interpretation, key dream signs/tags, summary hook, suggested actions, optional archetype Dream Detail → Insights
Dream image A single “hero” visualization of the dream Dream Detail → Visuals
Comic A stylized multi-panel visual representation Dream Detail → Visuals

Dream Signs (tags) inside analysis

  • The analysis model returns two types of tags:
  • Dream Profile patterns: 2–3 broad pattern tags chosen from people, tension, feelings, places, body, pressure (analytics only).
  • Dream Signs: 3–5 concrete, user-visible signs (symbols, people, places, feelings, actions).
  • Signs are normalized into consistent, human-readable labels for browsing (e.g., Family, Home, Flying).
  • Users can edit Dream Signs later in Dream Detail. Legacy/internal tags are filtered from the Dream Signs UI list.
  • Analytics (Dream Radar, Deep Insights) uses the stable Dream Profile patterns above. Stress remains a separate numeric metric.

On-demand (user-triggered)

These are typically generated only when the user chooses to:

Output What it is (user-facing) Where it appears
Insight Lens A secondary “lens” visual variant / decoder-style image Dream Detail → Visuals (Lens)
Healing visual A “safer” / comforting re-visualization, often paired with nightmare rewrite work Dream Detail → Healing

Insight Lens (Dream Decoder)

The Insight Lens is a unique visualization that reveals hidden meanings behind dream elements.

What users see

  • A before/after slider comparing:
  • The original dream image
  • An annotated "insight" version with symbolic overlays
  • Text annotations explaining what key elements represent

Example annotations

Visual element Insight annotation
Locked door "Fear of the unknown"
Dark water "Suppressed emotions"
Flying "Desire for freedom"

Generation behavior

  • User taps "Generate Insight Lens" in Visuals section
  • A background job creates the annotated image
  • Result appears via realtime update

Refinement Chat (Image Iteration)

Users can iterate on their dream images through conversational AI.

What users see

  • A chat modal opens when tapping "Refine Image"
  • Users type instructions like:
  • "Make the sky more purple"
  • "Add more stars"
  • "Make it look more dreamlike"

How it works

  1. User sends a refinement request
  2. AI generates a new image based on the instruction + original context
  3. New version is saved
  4. User can continue refining or save the version

Version history

  • Each refinement creates a new version
  • Users can browse previous versions
  • Revert to earlier versions if desired

How the background pipeline works (conceptual)

DreamStream uses a background “job” system. Think of a job as:

  • A single piece of AI work (e.g., “generate image”).
  • With a clear status (pending → processing → completed/failed).

Job statuses (user-visible meaning)

Status What it means to the user
pending The work is queued and will start soon
processing The work is actively running
completed The result is ready and will appear automatically
failed The work did not succeed (user can retry)

End-to-end lifecycle: from dream → results

flowchart TD
  A[User logs a dream] --> B[Dream saved]
  B --> C["Background jobs created<br/>(analysis + image + comic)"]
  C --> D[Job processor claims work]
  D --> E[AI generates result]
  E --> F[Media stored securely]
  E --> G["Dream record updated<br/>(status + result fields)"]
  G --> H[App receives realtime update]
  H --> I[Dream UI updates automatically]

Live updating in the app (no manual refresh)

DreamStream subscribes to background job updates.

  • If a job changes to processing, the app updates the dream’s UI state (spinners / “generating…”).
  • If a job becomes completed, the app fetches the latest dream record and displays the final content.
  • If a job fails, the app shows a toast message (e.g., "Image generation failed. Tap the dream to retry.").

Why this matters (product)

  • Users can keep scrolling their journal while AI runs.
  • Results “pop in” without pulling to refresh.
  • Users immediately learn if something failed.

Zero-refresh experience

Users never need to manually refresh to see AI results. The app subscribes to realtime updates and displays content automatically as it completes.

Failure + retry behavior (important for Support)

What users see

  • A specific output can fail without breaking the whole dream.
  • During automatic retry, the dream typically stays in a loading/spinner state (auto-retry is happening in the background).
  • If all automatic retries fail, the UI shows a "Retry" button.
  • After ~90 seconds of an active generation state (pending/processing/retrying), Dream Detail shows a prominent timeout state (e.g. "Something seems wrong") with a retry button.

90-second timeout vs stuck job detection

The 90-second timeout UI is a convenience so users aren't stuck watching spinners. It does NOT cancel the in-flight attempt—if the backend succeeds later, the result still appears. The backend stuck-job detection (10-30 minutes) is a separate self-healing mechanism that runs automatically.

Status fields (internal)

There are two related status fields:

  • generation_jobs.status tracks the job lifecycle in the queue.
  • dreams.<type>_status tracks the dream output status the UI reads (image/comic/analysis/insight/healing).

generation_jobs.status

Status What it means
pending Queued, waiting to be picked up
processing Actively generating
completed Finished successfully
failed All retries exhausted, user can manually retry

dreams.<type>_status

Status What it means
idle Nothing queued (or draft dream)
pending Queued, waiting to start
processing Actively generating
retrying A prior attempt failed; the job was automatically re-queued
completed Finished successfully
failed All retries exhausted (user can manually retry)

Behind the scenes

  • Jobs have a retry counter (retry_count) and a maximum retries limit (max_retries, default: 3).
  • If a job fails but still has retries remaining, it is automatically re-queued (generation_jobs.status returns to pending, retry_count increments) and the corresponding dream status becomes retrying.
  • Backend processor scheduler (pickup mechanism): a Cloudflare Worker cron (dreamstream-job-processor-scheduler) calls the process-generation-jobs Edge Function every 2 minutes using x-job-secret.
  • This is the only pickup mechanism. The client never invokes the job processor directly.
  • Operational requirement: process-generation-jobs must be deployed with verify_jwt=false so the Worker can call it; the function itself enforces x-job-secret.
  • Comic delegation (internal helper): comic jobs are delegated to a separate generate-comic Edge Function.
  • generate-comic is also deployed with verify_jwt=false and enforces x-job-secret (it should never be callable by clients).
  • Backend stuck-job reaper cron (safety mechanism): independently, a backend cron runs the stuck-job reaper every 2 minutes to recover jobs that were claimed but never finished (e.g. crashed worker / timeout).
  • The reaper only affects jobs stuck in processing (it re-queues them as pending or marks them failed after retries) and does not start pending jobs.

Scheduler health is P0

Job pickup is server-only. If the Cloudflare cron is disabled/misconfigured or process-generation-jobs is deployed with verify_jwt=true, then pending jobs will not be processed automatically.

Common symptoms: - Many generation_jobs.status='pending' rows that never move to processing - Edge logs show POST 401 to process-generation-jobs (JWT gate) or show no traffic at all (cron down)

Runbook checks: - Confirm Cloudflare Worker cron is running (dreamstream-job-processor-scheduler) - Confirm process-generation-jobs.verify_jwt=false - Confirm Edge logs show periodic POST 200 to process-generation-jobs

  • If retries are exhausted (typically after 3 attempts), the job becomes failed and stays visible as failed.

Stuck job detection (self-healing)

Jobs that get stuck "processing" for too long are automatically recovered:

  • A backend cron runs the stuck-job reaper every 2 minutes to recover jobs that were claimed but never finished (e.g. crashed worker / timeout).
  • The reaper only affects jobs stuck in processing and does not start pending jobs.
Job type Timeout What happens
Analysis, Image 5 minutes Auto-retry or fail
Insight, Healing 5 minutes Auto-retry or fail
Comic 5 minutes Auto-retry or fail
Deep Insights 5 minutes Auto-retry or fail
Video 30 minutes Auto-retry or fail

Reaper thresholds vs. expected runtime

These timeouts are stuck-processing detection thresholds (safety net), not the expected runtime. Note that because the reaper cron runs every ~2 minutes, a “5 minute” threshold is typically enforced at ~6 minutes (next cron tick).

When a stuck job is detected:

  • If retries remain → job is re-queued (pending), and dreams.<type>_status becomes retrying
  • If retries exhausted → job becomes failed, and dreams.<type>_status becomes failed (UI shows retry button)

Auto-retry limits

Jobs automatically retry up to their max_retries limit. If all retries fail, users must manually tap retry in the Dream Detail view.

“Recovery” if the app closes mid-generation

DreamStream attempts to recover partially-generated dreams:

  • On load, it scans for dreams still marked as processing / pending.
  • If any essential output is missing (analysis/image/comic), it can re-queue the missing work.

This prevents “stuck dreams” after network drops or app restarts.

Deep Insights (multi-dream pattern report)

Deep Insights is a separate user-level background workflow, built to synthesize long-range patterns without slowing the app.

How it’s queued

  • The UI enqueues/refreshes a single deep-insights job via the DB RPC public.enqueue_deep_insights_job() (no direct client writes to generation_jobs).
  • The server job processor picks it up on the next scheduler tick (typically within ~2 minutes).

What gets analyzed

  • Pulls the 50 most recent real dreams (samples never reach the database).
  • Requires 3+ real dreams; otherwise the job fails fast and the UI stays gated.
  • Uses Known People from the profile as grounding context when available.
  • Lucidity triggers are only requested if the user has lucid dreams in the dataset.

Output shape (what the AI is asked to return)

Deep Insights is instructed to output a structured report including:

  • Mind State summary + dominant archetype
  • Mind State evidence (2-3 dream IDs)
  • Recurring elements (only if they appear in 2+ dreams, max 6)
  • Dream Evolution narrative + evidence
  • Emotional Trend (short, evidence-backed)
  • Waking Life Correlations (short, evidence-backed)
  • Action Plan components (takeaways, prompts, weekly plan, practices, triggers)
  • Lucid triggers (only for lucid dreamers)
  • Evidence references that point back to dream IDs from the 50‑dream window

The UI hides any sections that come back empty, so the report always feels tailored rather than padded.

Trust and evidence

Deep Insights must show its work. For major claims (Mind State, Dream Evolution, Emotional Trend, Waking Life Connections), the model includes small evidence arrays of dream IDs. If evidence is weak, those arrays should be empty rather than guessed.

Refresh logic & caching

The report is stored on the user profile and reused until stale. It regenerates when:

  • There are 3+ new dreams since the last report, OR
  • The report is older than 7 days and at least one new dream was logged, OR
  • The report format was upgraded (new fields were added)

User-visible behavior

  • If a fresh report exists, it opens instantly.
  • If a report is generating, users can close the modal and receive “Deep Insights Ready” when it finishes (push notifications must be available).

Media storage model (privacy-first)

DreamStream stores generated media (and some user-uploaded media) in private storage.

Core rules

  • Each user’s files are stored under a user-specific folder prefix.
  • Media is served through an authenticated proxy so random URLs can’t be shared publicly.
  • The app uses signed URLs that can be refreshed automatically.

What file types DreamStream handles

Category Examples
Images avatar, digital twin, dream visuals, comics
Audio draft recordings (quick capture)

Upload flow (user uploads and some AI outputs)

sequenceDiagram
  participant A as App
  participant F as Upload Helper
  participant R2 as Media Storage
  participant P as Media Proxy

  A->>F: Ask for a pre-signed upload URL
  A->>R2: Upload file directly (PUT)
  A->>P: Build a signed viewing URL
  A-->>A: Store only the file path in the database

Viewing flow (how images load securely)

sequenceDiagram
  participant UI as App UI
  participant P as Media Proxy
  participant R2 as Media Storage

  UI->>P: GET /<path>?token=<session>
  P->>P: Verify token + confirm path belongs to user
  P->>R2: Fetch file
  P-->>UI: Stream file bytes

Automatic refresh of signed URLs

Signed URLs may expire over time. DreamStream’s image components:

  • Can generate a fresh signed URL using the user’s current session.
  • Can retry automatically if an image fails to load.

This reduces “broken image” experiences.

Deletion & cleanup

DreamStream deletes media in two main cases:

  • The user deletes a dream (clean up associated image/comic variants).
  • The user replaces a profile media item (e.g., new Digital Twin), cleaning up the old file.

There is also server-side cleanup for old background jobs:

  • Completed jobs can be removed after a time window.
  • Failed jobs can be removed after a longer time window.
  • Stuck processing jobs are automatically reset to pending (with retry increment) or marked failed if retries are exhausted. Dream statuses are synced accordingly.

Notifications tied to AI completion

DreamStream uses two notification styles:

  • Local notifications (device scheduled)
  • Example: “Don’t lose your dream” morning recall reminder.
  • Push notifications (server-triggered)
  • Example: “Dream Ready” when generation finishes.

Push notification events (as implemented)

Currently sent

Event Trigger Purpose
Dream Ready When all queued jobs for a dream are finished Bring the user back to review what's ready
Deep Insights Ready When the Deep Insights report finishes generating Invite the user to review long-term patterns
Streak reminder If the user hasn't logged a dream recently Soft encouragement, no heavy gamification

Defined in code but may not be active

None.

User notification preferences are respected where applicable (for example, Dream Ready can be skipped if the user disabled the relevant preference in their profile).

Troubleshooting signals (Support-facing)

  • “My dream is stuck on generating.”
  • Likely: job stalled, network drop, or background worker delay.
  • User action: open the dream and tap retry for the failed/stuck item.

  • “Images stopped loading.”

  • Likely: signed URL expired or session changed.
  • The app auto-refreshes signed URLs; sign-out/sign-in can fix persistent session issues.

  • “I didn’t get a notification.”

  • Check: user disabled notifications, system permission denied, or push not supported on web.
Implementation map (for accuracy checks)
  • Job queue helpers (client): src/lib/jobQueue.ts
  • Realtime job subscription + UI updates: App.tsx
  • Dream Detail generation/retry buttons: components/DreamDetail.tsx
  • Deep insights background report: components/DeepInsightsModal.tsx
  • Job processor (server): supabase/functions/process-generation-jobs/index.ts
  • Comic generator (server helper): supabase/functions/generate-comic/index.ts
  • AI proxy (server): supabase/functions/gemini-proxy/index.ts
  • Upload helper (server): supabase/functions/r2-upload-helper/index.ts
  • Media proxy (Cloudflare Worker): workers/media-proxy/src/index.ts
  • Notification cron: supabase/functions/send-streak-reminders/index.ts
  • Signed URL + upload utilities (client): src/lib/storage.ts
  • Auto-refresh image loader: components/LazyImage.tsx

← Home · Native Mobile →