# Embedding Providers

URL: https://cogitator.app/docs/memory/embeddings

> 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:

```typescript
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:

```typescript
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); // 1536
```

The 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`).

```typescript
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:

```typescript
const vectors = await embeddings.embedBatch([
  'First document about agents',
  'Second document about tools',
  'Third document about memory',
]);
// vectors.length === 3, each vector.length === 1536
```

Request shorter vectors from `text-embedding-3-*` models with `dimensions`. Gateway ids such as `openai/text-embedding-3-small` (OpenRouter) count as the same model:

```typescript
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.

```typescript
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); // 768
```

Pull a model first:

```bash
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.

## Google

Uses the Gemini `gemini-embedding-001` model via the Google AI API.

```typescript
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); // 3072
```

Batch embedding uses Google's `batchEmbedContents` endpoint, split into requests of up to 100 texts that run in parallel:

```typescript
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`).

```typescript
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:

* `PostgresAdapter` defaults to 768. Call `setVectorDimensions(n)` before `connect()` if using a different model — the column is created with that size, and the call throws once connected. An `embeddings` table that already exists keeps its size.
* `QdrantAdapter` requires `dimensions` in the constructor. The collection is auto-created with the specified size.
* `InMemoryEmbeddingAdapter` handles any dimension since it uses in-process cosine similarity (vectors of different lengths score 0).
* `PostgresGraphAdapter` takes `vectorDimensions` (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      |
| Google   | 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.
