Embedding Providers
Configure embedding providers for semantic search — OpenAI, Ollama, and Google — and understand how embeddings power agent memory.
How Embeddings Work
Embeddings convert text into dense numerical vectors that capture semantic meaning. Two pieces of text about the same topic will have vectors that are close together in high-dimensional space, even if they use different words.
In Cogitator, embeddings enable:
- Semantic search over stored documents and past conversations
- Hybrid search combining vector similarity with keyword matching
- Knowledge graph node search by meaning rather than exact name
- Context building that retrieves the most relevant memories for the current query
All embedding providers implement the EmbeddingService interface:
interface EmbeddingService {
embed(text: string): Promise<number[]>;
embedBatch(texts: string[]): Promise<number[][]>;
readonly dimensions: number;
readonly model: string;
}embed('') throws, and embedBatch([]) returns [] without a request. Every provider retries timeouts, network errors and HTTP 408/429/5xx responses up to 3 times with exponential backoff (honouring Retry-After), with a 30-second timeout per attempt; other errors throw with the provider's response text.
Factory Function
Use createEmbeddingService to instantiate a provider from a config object:
import { createEmbeddingService } from '@cogitator-ai/memory';
const embeddings = createEmbeddingService({
provider: 'openai',
apiKey: process.env.OPENAI_API_KEY!,
model: 'text-embedding-3-small',
});
const vector = await embeddings.embed('What is Cogitator?');
console.log(vector.length); // 1536The same config object is what memory.embedding takes in the Cogitator config.
OpenAI
Uses the OpenAI embeddings API (or any OpenAI-compatible endpoint via baseUrl).
import { OpenAIEmbeddingService } from '@cogitator-ai/memory';
const embeddings = new OpenAIEmbeddingService({
apiKey: process.env.OPENAI_API_KEY!,
model: 'text-embedding-3-small',
});
const vector = await embeddings.embed('Agent memory architecture');
console.log(embeddings.dimensions); // 1536
console.log(embeddings.model); // 'text-embedding-3-small'Batch embedding sends all texts in a single API call and returns vectors in input order:
const vectors = await embeddings.embedBatch([
'First document about agents',
'Second document about tools',
'Third document about memory',
]);
// vectors.length === 3, each vector.length === 1536Request shorter vectors from text-embedding-3-* models with dimensions. Gateway ids such as openai/text-embedding-3-small (OpenRouter) count as the same model:
const compact = new OpenAIEmbeddingService({
apiKey: process.env.OPENAI_API_KEY!,
model: 'text-embedding-3-large',
dimensions: 768,
});| Option | Type | Default | Description |
|---|---|---|---|
apiKey | string | — | OpenAI API key |
model | string | text-embedding-3-small | Embedding model name |
baseUrl | string | https://api.openai.com/v1 | API base URL (for proxies and compatible APIs) |
dimensions | number | model default | Output size, see below |
Dimensions by model:
| Model | Dimensions |
|---|---|
text-embedding-3-small | 1536 |
text-embedding-3-large | 3072 |
text-embedding-ada-002 | 1536 |
Gateway ids that contain one of these names, such as openai/text-embedding-3-large, report that model's size. Other models report 1536 unless you pass dimensions.
dimensions is sent to the API only for text-embedding-3 models, which shorten their output to it. For any other model it declares the size the model returns. Either way it is the size the service promises: when a response holds vectors of another size, embed() and embedBatch() throw instead of handing them to a store sized for dimensions.
Ollama
Run embedding models locally with zero API costs. Ollama's /api/embed endpoint supports both single and batch embedding natively.
import { OllamaEmbeddingService } from '@cogitator-ai/memory';
const embeddings = new OllamaEmbeddingService({
model: 'nomic-embed-text',
baseUrl: 'http://localhost:11434',
});
const vector = await embeddings.embed('Local embedding example');
console.log(embeddings.dimensions); // 768Pull a model first:
ollama pull nomic-embed-text| Option | Type | Default | Description |
|---|---|---|---|
model | string | nomic-embed-text | Ollama embedding model |
baseUrl | string | http://localhost:11434 | Ollama server URL |
dimensions | number | from the table below | Reported vector size for models not in the table |
Known models and dimensions:
| Model | Dimensions |
|---|---|
nomic-embed-text | 768 |
nomic-embed-text-v2-moe | 768 |
mxbai-embed-large | 1024 |
all-minilm | 384 |
snowflake-arctic-embed | 1024 |
You can use any Ollama embedding model. For models not in this list the service reports 768 dimensions unless you set dimensions — Ollama itself always returns the model's native size, so set it to match the model.
Uses the Gemini gemini-embedding-001 model via the Google AI API.
import { GoogleEmbeddingService } from '@cogitator-ai/memory';
const embeddings = new GoogleEmbeddingService({
apiKey: process.env.GOOGLE_API_KEY!,
model: 'gemini-embedding-001',
});
const vector = await embeddings.embed('Google embedding example');
console.log(embeddings.dimensions); // 3072Batch embedding uses Google's batchEmbedContents endpoint, split into requests of up to 100 texts that run in parallel:
const vectors = await embeddings.embedBatch(['First passage', 'Second passage']);| Option | Type | Default | Description |
|---|---|---|---|
apiKey | string | — | Google AI API key |
model | string | gemini-embedding-001 | Embedding model name |
dimensions | number | 3072 | Output size; when set, sent as outputDimensionality |
baseUrl | string | https://generativelanguage.googleapis.com/v1beta | API base URL |
Dimensions: gemini-embedding-001 produces 3072-dimensional vectors by default. Pass dimensions (for example 768 or 1536) to request smaller vectors, and make sure your vector column size matches.
Using with Memory Adapters
Embeddings are stored via EmbeddingAdapter (implemented by PostgresAdapter, QdrantAdapter, and InMemoryEmbeddingAdapter).
import { PostgresAdapter, OpenAIEmbeddingService, unwrap } from '@cogitator-ai/memory';
const embeddings = new OpenAIEmbeddingService({
apiKey: process.env.OPENAI_API_KEY!,
});
const db = new PostgresAdapter({
provider: 'postgres',
connectionString: process.env.DATABASE_URL!,
});
db.setVectorDimensions(embeddings.dimensions);
unwrap(await db.connect());
const text = 'Cogitator agents can use tools to interact with external systems.';
const vector = await embeddings.embed(text);
await db.addEmbedding({
sourceId: 'doc-1',
sourceType: 'document',
vector,
content: text,
});
const queryVector = await embeddings.embed('How do agents use tools?');
const results = unwrap(
await db.search({
vector: queryVector,
limit: 5,
threshold: 0.7,
})
);
for (const result of results) {
console.log(`${result.content} (score: ${result.score.toFixed(3)})`);
}Dimension Matching
The vector dimensions of your embedding model must match the adapter configuration:
PostgresAdapterdefaults to 768. CallsetVectorDimensions(n)beforeconnect()if using a different model — the column is created with that size, and the call throws once connected. Anembeddingstable that already exists keeps its size.QdrantAdapterrequiresdimensionsin the constructor. The collection is auto-created with the specified size.InMemoryEmbeddingAdapterhandles any dimension since it uses in-process cosine similarity (vectors of different lengths score 0).PostgresGraphAdaptertakesvectorDimensions(default 768) for node embeddings.
When memory is configured through Cogitator (memory.adapter: 'postgres'), the table is created with the dimensions of memory.embedding (768 without one). memory.qdrant.dimensions must be set to match the embedding model; the runtime logs a warning when it does not.
The adapter indexes vectors with a pgvector HNSW index, which supports up to 2000 dimensions. With larger vectors (e.g. 3072) the adapter logs a warning and searches without the index.
Provider Comparison
| Provider | Dimensions | Cost | Privacy |
|---|---|---|---|
| OpenAI | 1536 / 3072 (configurable) | Pay per token | Data sent to API |
| Ollama | Model-dependent (384–1024) | Free | Fully local |
| 3072 (configurable) | Free tier available | Data sent to API |
Choose Ollama for local-first deployments with zero data egress, OpenAI for widely used hosted embeddings, and Google for a generous free tier.