@molecule/api-ai-embeddings-openai
Provider bond · ai-embeddings · API (Node) · v1.0.5 · Apache-2.0
OpenAI embeddings provider for molecule.dev — text-embedding-3-small/large
npm install @molecule/api-ai-embeddings-openainpm · Source on GitHub · Implements @molecule/api-ai-embeddings
How it works
@molecule/api-ai-embeddings-openai is a provider bond on the API (Node) side: it implements the ai-embeddings core interface (@molecule/api-ai-embeddings) with a concrete vendor or library behind it.
Your code calls the core; you wire this provider once at startup. Swapping vendors later is one line in that wiring, not a rewrite.
// npm install @molecule/api-ai-embeddings-openai --workspace=api
import { setProvider, requireProvider } from '@molecule/api-ai-embeddings'
import { provider } from '@molecule/api-ai-embeddings-openai'
setProvider(provider) // reads OPENAI_API_KEY from the environment
// Batch, don't loop: one call for many texts (default model text-embedding-3-small).
const { embeddings, usage } = await requireProvider().embed({
input: ['How do I reset my password?', 'Billing and invoices'],
})
const queryVector = await requireProvider().embedQuery('forgot my password')Works with: @molecule/api-ai-embeddings, @molecule/api-secrets
Secrets: OPENAI_API_KEY
Live demos
Running apps built with Molecule that install this package — open the app or study the template.
Reference
OpenAI ai-embeddings provider for molecule.dev.
Auto-generated, AI-first package reference for the molecule.dev ecosystem. It is written to be read by coding agents as much as by people, and is generated from this package's source — edit
src/index.tsJSDoc, not this file.
Quick Start
// npm install @molecule/api-ai-embeddings-openai --workspace=api
import { setProvider, requireProvider } from '@molecule/api-ai-embeddings'
import { provider } from '@molecule/api-ai-embeddings-openai'
setProvider(provider) // reads OPENAI_API_KEY from the environment
// Batch, don't loop: one call for many texts (default model text-embedding-3-small).
const { embeddings, usage } = await requireProvider().embed({
input: ['How do I reset my password?', 'Billing and invoices'],
})
const queryVector = await requireProvider().embedQuery('forgot my password')
Type
provider
Installation
npm install @molecule/api-ai-embeddings-openai @molecule/api-ai-embeddings @molecule/api-secrets
API
Interfaces
OpenaiEmbeddingsConfig
Configuration for the OpenAI embeddings provider.
interface OpenaiEmbeddingsConfig {
/** OpenAI API key. Defaults to OPENAI_API_KEY env var. */
apiKey?: string
/** Default embedding model. Defaults to 'text-embedding-3-small'. */
defaultModel?: string
/** Base URL for the OpenAI API. Defaults to 'https://api.openai.com'. */
baseUrl?: string
/** Maximum number of texts per batch request. Defaults to 2048. */
maxBatchSize?: number
/** Default number of output dimensions (for text-embedding-3 models). */
dimensions?: number
}
Functions
createProvider(config)
Creates an OpenAI embeddings provider instance.
function createProvider(config?: OpenaiEmbeddingsConfig): AIEmbeddingsProvider
config— OpenAI-specific configuration (API key, model, base URL, dimensions).
Returns: An AIEmbeddingsProvider backed by the OpenAI Embeddings API.
Constants
provider
The provider implementation.
const provider: AIEmbeddingsProvider
aiEmbeddingsOpenaiSecretDefinitions
Secret definitions required by the OpenAI embeddings bond.
const aiEmbeddingsOpenaiSecretDefinitions: SecretDefinition[]
Core Interface
Implements @molecule/api-ai-embeddings interface.
Bond Wiring
Setup function to register this provider with the core interface:
import { setProvider } from '@molecule/api-ai-embeddings'
import { provider } from '@molecule/api-ai-embeddings-openai'
export function setupAiEmbeddingsOpenai(): void {
setProvider(provider)
}
Injection Notes
Requirements
Peer dependencies:
@molecule/api-ai-embeddings>=1.0.1@molecule/api-secrets^1.0.1
Environment Variables
OPENAI_API_KEY(required) — OpenAI API key- Setup: Create a secret key on the OpenAI platform (API keys page).
- Get it here: https://platform.openai.com/api-keys
- Example:
sk-proj-...
Runtime Dependencies
@molecule/api-ai-embeddings@molecule/api-secrets
Config: OPENAI_API_KEY (SERVER-side only) plus optional defaultModel
(default text-embedding-3-small; also supports text-embedding-3-large
and text-embedding-ada-002), dimensions (text-embedding-3 models only),
maxBatchSize (default 2048 inputs per request — larger arrays are batched
automatically), and a base URL override (OPENAI_BASE_URL env var or
baseUrl, for proxies/gateways).
Wire it with the core's setProvider() (it registers the bond
'ai-embeddings', so bond('ai-embeddings', provider) is equivalent). To
run a second embeddings provider next to the app's default, bond it NAMED —
bond('ai-embeddings', 'search', provider) — and read it with
get('ai-embeddings', 'search') from @molecule/api-bond.
Self-hosted models over the same wire format. Any server that speaks
OpenAI's /v1/embeddings works — e.g. EmbeddingGemma 2 on vLLM
(vllm serve google/embeddinggemma-2 --runner pooling --max-model-len 8192):
createProvider({ baseUrl: 'http://localhost:8000', defaultModel: 'google/embeddinggemma-2', apiKey: 'unused' }). The bond appends
/v1/embeddings itself, so baseUrl must NOT end in /v1. This path sends
text as-is: add the model's task prefixes yourself
(task: search result | query: … for queries, title: none | text: … for
documents) and truncate + re-normalize client-side for 512/256/128 dims — or
use @molecule/api-ai-embeddings-embeddinggemma, which does both in-process.
Unlike the chat AI bonds, a missing OPENAI_API_KEY does NOT fail fast —
the first embed call fails with the upstream 401. Validate the key at boot
if you want an actionable startup error.
E2E Tests
Integration checklist — drive the real flow (no mocks), adapt each item to this app's actual data and features, and check every box off one by one. A box you can't check is an integration bug to fix — not a skip. Embeddings are infrastructure, so PROVE them through the feature they power (semantic search / "related items" / dedup) AND with a direct property check on the vectors:
-
embedQuery(text)returns a non-empty numericnumber[]of the model's fixed dimension, and every vector fromembed/embedDocumentshas that SAME length — no empty arrays, noNaN/nullentries, and the length is identical across calls (a query and a document must be comparable). - The semantic property holds — there is NO built-in similarity helper, so compute cosine similarity yourself (dot product over the two magnitudes): two related texts ("dog"/"puppy", or a query and a matching doc) score HIGH, two unrelated texts ("dog"/"quarterly taxes") score clearly LOWER. If every pair scores alike the vectors are dead — one positive check alone doesn't prove it.
- Ranking works: embed a query plus a handful of documents and sort by cosine similarity — the semantically closest document ranks ABOVE the unrelated ones. This ordering is the whole point; a query that ranks an off-topic doc first is broken.
- The feature built on it works end-to-end from the real UI: semantic search / "related items" / dedup returns results ranked by MEANING, not keyword — a search for a synonym or paraphrase finds the right item even when it shares NO words with the query (the case a plain text/keyword search would miss).
- Embedding is stable: the same text embedded twice yields (near-)identical vectors, so a stored index stays valid — re-embedding an item must not silently drift it out of its own neighborhood.
- Batching is order-preserving:
embedDocuments([a, b, c])(orembed({ input })) returns exactly one vector per input in the SAME order —embeddings[i]is the vector forinput[i], never shuffled, merged, or dropped. - Embedding runs SERVER-SIDE: the call goes through the app's API and the provider key never reaches the browser (no provider request or key in the Network tab). Any endpoint that embeds caller-supplied text is authenticated and rate-limited — an open embed endpoint is an unbounded per-token cost/abuse vector.