Skip to main content

Core Principle: Enrichment, Not Replacement

The embedding annotates the observation - it doesn’t replace .data. In memory1, .data IS the embedding and you need parent_id + project_to() to get back to the source image. We avoid this entirely.

Observation Types

EmbeddedObservation is a subclass - passes anywhere Observation is accepted (LSP). Users who don’t care about types just use Observation. Users who want precision annotate with EmbeddedObservation. derive() on Observation promotes to EmbeddedObservation if embedding= is passed. derive() on EmbeddedObservation returns EmbeddedObservation, preserving the embedding unless explicitly replaced.

Embed Transformer

Embed is Transformer[T, T] - same data type in and out. It populates .embedding on each observation:
Stream[Image] stays Stream[Image] after embedding - T is about .data, not the observation subclass. .search(query_vec, k) lives on Stream itself. Returns a new Stream filtered to top-k by cosine similarity:

Backend Handles Storage Strategy

The Backend composite decides how to route storage based on what it sees:
  • append(image, ts=now, embedding=vec) β†’ backend routes: blob via BlobStore, vector via VectorStore, metadata via ObservationStore
  • append(image, ts=now) β†’ blob + metadata only (no embedding)
  • ListObservationStore: stores metadata in-memory, brute-force cosine via MemoryVectorStore
  • SqliteObservationStore: metadata in SQLite, vec0 side table for fast ANN search via SqliteVectorStore
  • Future backends (Postgres/pgvector, Qdrant, etc.) do their thing
Search is pushed down to the VectorStore. Stream just passes .search() calls through.

Projection / Lineage

Usually not needed. Since .data IS the original data, search results give you the image directly. When a downstream transform replaces .data (e.g., Image β†’ Detection), use temporal join to get back to the source:

Multi-Modal

Same embedding space = same stream. CLIP maps images and text to the same 512-d space:
Different embedding spaces = different streams. Can’t mix CLIP and sentence-transformer vectors.

Chaining - Embedding as Cheap Pre-Filter

Text Search (FTS) - Separate Concern

FTS is keyword-based, not embedding-based. Complementary, not competing: