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 workGotchas
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.
Related
- Protocols — the
Embedder/LLMClientseams - The knowledge graph — what the providers build
- Ingest & search the graph — using them
- Your first memory-backed agent — the full loop