MemoFSMemoFS
Adapters

OpenAI Adapter

OpenAI embeddings adapter for MemoFS vector search and semantic recall.

The @memofs/adapter-openai adapter provides vector embedding capabilities for MemoFS using OpenAI's embeddings API (/v1/embeddings).

It implements the core MemoryEmbedder interface, providing automatic batch chunking, dimension truncation, exponential backoff retries with jitter, and robust input/response validation.

Subpath Exports

Export PathTarget EnvironmentDescription
@memofs/adapter-openaiNode.js (>= 22), EdgeRoot entry. Exposes OpenAIEmbedder, createOpenAIEmbedder, OpenAISdkEmbeddingsClient, createOpenAIClient, model utilities, and error classes.
@memofs/adapter-openai/testingTest runnersExposes createFakeOpenAIClient and FakeOpenAIEmbeddingsClient for deterministic unit and integration tests without network calls.

Installation

npm install @memofs/adapter-openai

Requires Node.js >= 22 when running under the Node.js runtime.

Usage

Instantiate the embedder with createOpenAIEmbedder() and pass it to your MemoFS instance:

import { createNodeMemoFs } from "@memofs/core/node-fs";
import { createOpenAIEmbedder } from "@memofs/adapter-openai";

const memo = createNodeMemoFs({
  rootDir: ".",
  embedder: createOpenAIEmbedder({
    apiKey: process.env.OPENAI_API_KEY!,
    model: "text-embedding-3-small",
    dimensions: 1536,
  }),
});
import { MemoFS } from "@memofs/core";
import { createNodeFsMemoryStore } from "@memofs/core/node-fs";
import { createOpenAIEmbedder } from "@memofs/adapter-openai";

const memo = new MemoFS({
  store: createNodeFsMemoryStore({ rootDir: "." }),
  projectId: "openai-app",
  mode: "local",
  embedder: createOpenAIEmbedder({
    apiKey: process.env.OPENAI_API_KEY!,
    model: "text-embedding-3-small",
    dimensions: 1536,
  }),
});

Supported Models & Dimensions

@memofs/adapter-openai provides built-in validation for known OpenAI embedding models and allows custom string model names for forward compatibility:

Model IdentifierDefault DimensionsFlexible Dimensions SupportedTypical Use Case
text-embedding-3-small1536Yes (<= 1536, e.g. 512, 1024)High efficiency, general coding and documentation recall.
text-embedding-3-large3072Yes (<= 3072, e.g. 256, 1024, 1536)Maximum semantic precision for large multi-hop codebases.
text-embedding-ada-0021536No (fixed at 1536)Legacy compatibility.
Custom string (string & {})Model defaultConfigurableCustom or fine-tuned OpenAI proxy deployments.

Configuration API (OpenAIEmbedderConfig)

The createOpenAIEmbedder(config) factory accepts OpenAIEmbedderConfig:

OptionTypeDefaultDescription
apiKeystringOpenAI API key. Mutually exclusive with client. Required unless client is provided.
clientOpenAIEmbeddingsClientPre-configured client instance (e.g. OpenAISdkEmbeddingsClient or test fake).
modelOpenAIEmbeddingModel"text-embedding-3-small"Model identifier to use for embeddings.
dimensionsnumberModel defaultTarget vector dimensions. Validated against model capabilities.
baseUrlstring"https://api.openai.com"Base URL override for custom proxies or Azure OpenAI gateways.
organizationstringOpenAI organization identifier (OpenAI-Organization header).
projectstringOpenAI project identifier (OpenAI-Project header).
fetchOpenAIFetchLikeglobalThis.fetchCustom fetch implementation for edge runtimes or mock interceptors.
timeoutMsnumber30000 (30s)Request timeout in milliseconds.
retryOpenAIRetryOptionsSee belowRetry options for transient network and rate limit errors.
userAgentstringCustom User-Agent header string.
batchSizenumber128Maximum texts per API request (automatically chunked up to OPENAI_MAX_BATCH_SIZE = 2048).
encodingFormat"float""float"Vector encoding format. Base64 is rejected because MemoFS expects numeric arrays.
userstringUnique end-user identifier forwarded to OpenAI for abuse monitoring.
expectedDimensionsnumberStrict expected dimension validation check.
allowEmptyTextbooleanfalseWhether to allow empty strings ("") without throwing validation errors.
allowUnknownModelDimensionsbooleantrueWhether custom models can accept explicit dimensions without failing validation.

Retry Options (OpenAIRetryOptions)

interface OpenAIRetryOptions {
  maxRetries?: number;        // Default: 2
  baseDelayMs?: number;       // Default: 1000ms
  maxDelayMs?: number;        // Default: 30000ms
  jitter?: boolean;           // Default: true
  retryableStatuses?: readonly number[]; // Default: [408, 409, 425, 429, 500, 502, 503, 504]
}

Error Classes

All exceptions thrown by @memofs/adapter-openai inherit from OpenAIEmbedderError and expose a typed .code property:

class OpenAIEmbedderError extends Error {
  readonly code: OpenAIErrorCode;
  readonly cause?: unknown;
}
Error Class.codeCause
OpenAIConfigError"OPENAI_CONFIG_ERROR"Missing API key, conflicting client configuration, or invalid credentials.
OpenAIValidationError"OPENAI_VALIDATION_ERROR"Invalid model name, dimension out of bounds, base64 format requested, or empty text.
OpenAIAPIError"OPENAI_API_ERROR"Upstream API HTTP error response. Exposes .status, .providerCode, .providerType, .providerBody.
OpenAINetworkError"OPENAI_NETWORK_ERROR"Network-level connectivity failure or DNS resolution issue.
OpenAITimeoutError"OPENAI_TIMEOUT_ERROR"Request exceeded configured timeoutMs.
OpenAIResponseError"OPENAI_RESPONSE_ERROR"Malformed JSON response, missing vector data, or index mismatch from API.
OpenAIRetryExhaustedError"OPENAI_RETRY_EXHAUSTED"All retry attempts failed.

Unit Testing with Fake Client

Use the @memofs/adapter-openai/testing subpath to write fast, deterministic unit tests without network requests or API costs:

import { describe, it, expect } from "vitest";
import { OpenAIEmbedder } from "@memofs/adapter-openai";
import { createFakeOpenAIClient } from "@memofs/adapter-openai/testing";

describe("OpenAI embedding pipeline", () => {
  it("generates deterministic embeddings with test fake", async () => {
    const fakeClient = createFakeOpenAIClient({
      dimensions: 1536,
      deterministic: true,
    });

    const embedder = new OpenAIEmbedder({
      client: fakeClient,
      model: "text-embedding-3-small",
    });

    const result = await embedder.embedText("Authentication rules");
    expect(result.embedding).toHaveLength(1536);
    expect(result.model).toBe("text-embedding-3-small");
  });
});

See Also

On this page