pgmem
Reference

Protocols

Embedder, LLMClient, and other provider seams.

pgmem is model- and driver-agnostic: it depends on narrow, structural Protocols, not on any concrete SDK. You bring implementations and the PgMem composition root wires them together. Anything that matches the shape works — no base class to inherit.

Provider seams (you bring these)

Embedder

class Embedder(Protocol):
    dimension: int
    async def embed(self, texts: Sequence[str]) -> list[list[float]]: ...

Turns text into dense vectors for the semantic graph. dimension must be 1536 (the schema's vector(N) width). Required for Tier 4 ingestion, search, and community building.

LLMClient

class LLMClient(Protocol):
    async def generate(self, prompt: Prompt, *, response_model: type[T]) -> T: ...

Structured generation for pgmem's internal work (entity/edge extraction, dedup, contradiction resolution, compaction summaries, saga briefs). It receives a Prompt (a list of messages) and the Pydantic model to fill, and must use the provider's structured-output feature to return a validated instance. This powers pgmem's internal calls — generating the user-facing reply is your own call against compile_context().messages.

CrossEncoder (optional)

class CrossEncoder(Protocol):
    async def rank(self, query: str, passages: Sequence[str]) -> list[float]: ...

Reranks passages against a query. Optional — wiring one selects the cross-encoder search recipe.

Internal seams

Tokenizer

class Tokenizer(Protocol):
    def count(self, text: str) -> int: ...

Counts tokens for the context compiler's budget. Defaults to tiktoken's o200k_base; configure via TokenizerConfig or inject your own.

Executor

class Executor(Protocol):
    async def execute(self, query, /, *args) -> Any: ...
    async def fetch(self, query, /, *args) -> Any: ...
    async def fetchrow(self, query, /, *args) -> Any: ...
    async def fetchval(self, query, /, *args) -> Any: ...
    async def executemany(self, query, args, /) -> Any: ...

The database surface the storage layer needs. Satisfied structurally by pgmem's Driver, an asyncpg pool, and a transaction Connection alike — you don't implement this; it's why repositories run standalone or inside a transaction without binding to a concrete driver.

Wiring

mem = await PgMem.create(
    dsn,
    embedder=embedder,        # Embedder
    llm=llm,                  # LLMClient
    cross_encoder=reranker,   # CrossEncoder | None
    tokenizer=tokenizer,      # Tokenizer | None (defaults to tiktoken)
)

The session/turn/memory/context surface needs no providers; embedder + llm are required only for the semantic graph and compaction.

On this page