pgmem
Guides

Connect a model provider

Wire an LLM and an embedder into pgmem.

pgmem needs two providers for its graph and compaction work: an LLM (for extraction, compaction summaries, and saga briefs) and an embedder (for the semantic graph). It ships LLM adapters for OpenAI, Anthropic, and Ollama; you bring an embedder by implementing a one-method protocol. Neither is needed for the plain session layer — only for the knowledge graph and compaction.

The provider you pass to PgMem powers pgmem's internal work. Generating the user-facing reply is always your own model call against compile_context().messages — the same provider or a different one.

Prerequisites

  • pgmem installed.
  • An API key (OpenAI / Anthropic) or a running Ollama server.

Steps

Install the provider extra

Each LLM adapter imports its SDK lazily, so install only the one you use:

uv add "pgmem[openai]"
uv add "pgmem[anthropic]"
uv add "pgmem[ollama]"

Build the LLM client

Each adapter has a connect(...) classmethod. API keys default to the SDK's env resolution (OPENAI_API_KEY, ANTHROPIC_API_KEY), so you usually pass just the model:

from pgmem.llm import OpenAILLM

# base_url is optional — point it at any OpenAI-compatible endpoint
# (vLLM, Groq, Together, …).
llm = OpenAILLM.connect(model="gpt-4o-mini")
from pgmem.llm import AnthropicLLM

llm = AnthropicLLM.connect(model="claude-sonnet-4-6")
from pgmem.llm import OllamaLLM

llm = OllamaLLM.connect("http://localhost:11434", model="qwen2.5")

The adapter must return structured output (it fills a Pydantic model), so pick a model that supports it. Bring another provider by subclassing BaseLLMClient or implementing the LLMClient protocol.

Bring an embedder

pgmem ships no built-in embedder — implement the Embedder protocol. It's one async method plus a dimension that must be 1536 (the schema's fixed vector(1536) width). OpenAI's text-embedding-3-small is 1536-dim, so it drops straight in:

from openai import AsyncOpenAI
from collections.abc import Sequence

class OpenAIEmbedder:
    dimension = 1536

    def __init__(self, model: str = "text-embedding-3-small"):
        self._client = AsyncOpenAI()
        self._model = model

    async def embed(self, texts: Sequence[str]) -> list[list[float]]:
        resp = await self._client.embeddings.create(model=self._model, input=list(texts))
        return [d.embedding for d in resp.data]

embedder = OpenAIEmbedder()

Pass them to PgMem

from pgmem import PgMem

mem = await PgMem.create(dsn, embedder=embedder, llm=llm)

Verify

Ingest one episode and confirm the graph was built — this exercises both providers (the LLM extracts, the embedder vectorizes):

await mem.add_episode(content="Acme signed a 2-year enterprise contract.", group_id="acme")
results = await mem.search("enterprise contract", group_ids=["acme"])
print([n.name for n in results.nodes])   # non-empty → both providers work

Gotchas

The embedder's dimension must be 1536. A mismatch fails at PgMem.create. If your model emits a different size, use one that produces 1536 dims (or a model that supports dimension reduction, like text-embedding-3-large capped to 1536).

  • Providers are injected, never in PgMemConfig — keep API keys in your secret manager and construct the adapters yourself, so config stays pure, loggable data.
  • These providers do real LLM/embedding work — keep add_episode, compaction, and promotion off the response path.

On this page