@molecule/api-ai-embeddings
Core interface · ai-embeddings · API (Node) · v1.1.0 · Apache-2.0
Text embeddings core interface — turn text into vectors for semantic search, clustering, and similarity scoring via a swappable AI provider.
npm install @molecule/api-ai-embeddingsHow it works
@molecule/api-ai-embeddings is the ai-embeddings core interface on the API (Node) side: the API your app calls, with no vendor inside.
Choose the implementation by bonding one of its 5 providers: @molecule/api-ai-embeddings-embeddinggemma, @molecule/api-ai-embeddings-gemini, @molecule/api-ai-embeddings-local, @molecule/api-ai-embeddings-molecule, @molecule/api-ai-embeddings-openai.
import { setProvider, requireProvider } from '@molecule/api-ai-embeddings'
import { createProvider } from '@molecule/api-ai-embeddings-openai'
// Wire at startup. See the bond package for its config/env (e.g. OPENAI_API_KEY).
setProvider(createProvider({ defaultModel: 'text-embedding-3-small' }))
// Use anywhere after startup.
const { embeddings, usage } = await requireProvider().embed({
input: ['How do I reset my password?', 'Billing and invoices'],
})
const queryVector = await requireProvider().embedQuery('forgot my password')Providers (5): @molecule/api-ai-embeddings-embeddinggemma, @molecule/api-ai-embeddings-gemini, @molecule/api-ai-embeddings-local, @molecule/api-ai-embeddings-molecule, @molecule/api-ai-embeddings-openai
Works with: @molecule/api-bond
Reference
AI text-embeddings core interface for molecule.dev.
Defines the AIEmbeddingsProvider contract — turn text into vectors for
semantic search, clustering, deduplication, and similarity scoring
(embed, embedQuery, embedDocuments) — plus the accessor
(setProvider/getProvider/hasProvider/requireProvider). Interface-only:
bond a provider package (e.g. @molecule/api-ai-embeddings-openai, or
@molecule/api-ai-embeddings-local for keyless local inference).
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
import { setProvider, requireProvider } from '@molecule/api-ai-embeddings'
import { createProvider } from '@molecule/api-ai-embeddings-openai'
// Wire at startup. See the bond package for its config/env (e.g. OPENAI_API_KEY).
setProvider(createProvider({ defaultModel: 'text-embedding-3-small' }))
// Use anywhere after startup.
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
core
Installation
npm install @molecule/api-ai-embeddings @molecule/api-bond
API
Interfaces
EmbedContentInput
One input to AIEmbeddingsProvider.embedContent. Every field present in
ONE input is embedded together into ONE vector (e.g. a caption plus its image).
interface EmbedContentInput {
/** Text, optionally with `<|image|>` / `<|audio|>` / `<|video|>` placeholders where the media sits. */
text?: string
/** An image. */
image?: EmbedMediaSource
/** An audio clip. */
audio?: EmbedMediaSource
/** A video clip. */
video?: EmbedMediaSource
}
EmbedContentParams
Parameters for AIEmbeddingsProvider.embedContent.
interface EmbedContentParams {
/** The inputs; the result holds one vector per input, in the same order. */
inputs: EmbedContentInput[]
/** Number of dimensions for the output vectors (if supported by the model). */
dimensions?: number
/** Model to use (provider-specific). */
model?: string
/** What the vectors are for (instruction-prefixed models only). */
task?: EmbeddingTask
/** Query or document side of the task (instruction-prefixed models only). */
inputType?: EmbeddingInputType
}
EmbedParams
Parameters for generating embeddings.
interface EmbedParams {
/** Text or array of texts to embed. */
input: string | string[]
/** Model to use for embedding (provider-specific). */
model?: string
/** Number of dimensions for the output vectors (if supported by model). */
dimensions?: number
/** What the vectors are for (instruction-prefixed models only; others ignore it). */
task?: EmbeddingTask
/** Query or document side of the task (instruction-prefixed models only; others ignore it). */
inputType?: EmbeddingInputType
}
EmbeddingUsage
Token usage information for an embedding request.
interface EmbeddingUsage {
/** Number of prompt tokens consumed. */
promptTokens: number
/** Total tokens consumed. */
totalTokens: number
}
EmbeddingResult
Result of an embedding request.
interface EmbeddingResult {
/** The embedding vectors, one per input text. */
embeddings: number[][]
/** Model that produced the embeddings. */
model: string
/** Token usage information. */
usage: EmbeddingUsage
}
AIEmbeddingsProvider
AIEmbeddings provider interface.
Providers generate vector embeddings from text, enabling semantic search, clustering, and similarity comparisons.
interface AIEmbeddingsProvider {
/** Provider name identifier. */
readonly name: string
/**
* Generate embeddings for one or more text inputs.
*
* @param params - Embedding parameters including input text(s), model, and dimensions.
* @returns Embedding vectors with usage metadata.
*/
embed(params: EmbedParams): Promise<EmbeddingResult>
/**
* Generate a single embedding vector for a query string.
* Convenience method equivalent to `embed({ input: text })` returning the first vector.
*
* @param text - The query text to embed.
* @returns A single embedding vector.
*/
embedQuery(text: string): Promise<number[]>
/**
* Generate embedding vectors for multiple documents in batch.
* Convenience method equivalent to `embed({ input: texts })` returning all vectors.
*
* @param texts - The document texts to embed.
* @returns An array of embedding vectors, one per document.
*/
embedDocuments(texts: string[]): Promise<number[][]>
/**
* The input kinds this provider can embed. Absent means text only. Check it
* before calling `embedContent` with media instead of catching errors.
*/
readonly modalities?: ReadonlyArray<EmbeddingModality>
/**
* Embed text and/or media into the same vector space as `embed`. Optional:
* only multimodal providers implement it — check `modalities` first.
*
* @param params - The inputs (one vector each) plus dimensions/model/task.
* @returns One vector per input, in order, with usage metadata.
*/
embedContent?(params: EmbedContentParams): Promise<EmbeddingResult>
}
AIEmbeddingsConfig
Base configuration for embeddings providers.
interface AIEmbeddingsConfig {
/** API key for the embeddings service. */
apiKey?: string
/** Default model to use. */
defaultModel?: string
/** Base URL override (for proxies or self-hosted endpoints). */
baseUrl?: string
/** Additional provider-specific options. */
[key: string]: unknown
}
Types
EmbeddingTask
What the embeddings are for. Models trained with instruction prefixes (e.g.
EmbeddingGemma 2, gemini-embedding-2) embed differently per task; bonds
whose model has no such notion ignore it.
type EmbeddingTask =
| 'search'
| 'question-answering'
| 'fact-checking'
| 'code-retrieval'
| 'classification'
| 'clustering'
| 'similarity'
EmbeddingInputType
Which side of an asymmetric task an input is on: the short query a user
types, or the document stored in the index. Bonds that use instruction
prefixes map it; others ignore it.
type EmbeddingInputType = 'query' | 'document'
EmbeddingModality
A kind of input a provider can embed.
type EmbeddingModality = 'text' | 'image' | 'audio' | 'video'
EmbedMediaSource
A media input: raw bytes, a URL or file path, or bytes / a URL plus the
MIME type (image/png, audio/wav, video/mp4, …). Hosted APIs need the
MIME type, so prefer the object form when you have it.
type EmbedMediaSource = Uint8Array | string | { data: Uint8Array | string; mimeType: string }
Functions
setProvider(provider)
Registers the AI embeddings provider singleton.
function setProvider(provider: AIEmbeddingsProvider): void
provider— The AI embeddings provider implementation to register.
getProvider()
Returns the bonded AI embeddings provider, or null if none is registered.
function getProvider(): AIEmbeddingsProvider | null
Returns: The active provider, or null.
hasProvider()
Returns whether an AI embeddings provider has been registered.
function hasProvider(): boolean
Returns: true if a provider is bonded.
requireProvider()
Returns the bonded AI embeddings provider, throwing if none is configured.
function requireProvider(): AIEmbeddingsProvider
Returns: The active provider.
Available Providers
| Provider | Package |
|---|---|
| EmbeddingGemma 2 | @molecule/api-ai-embeddings-embeddinggemma |
| Gemini API | @molecule/api-ai-embeddings-gemini |
| Local (Transformers.js, offline) | @molecule/api-ai-embeddings-local |
| Molecule (hosted) | @molecule/api-ai-embeddings-molecule |
| OpenAI | @molecule/api-ai-embeddings-openai |
Injection Notes
Requirements
Peer dependencies:
@molecule/api-bond^1.0.1
Runtime Dependencies
-
@molecule/api-bond -
Wire it at startup with
setProvider(...)— or the equivalentbond('ai-embeddings', provider). This core routes through the shared@molecule/api-bondregistry, so either call registers the same provider andvalidateBonds()reports it as missing when unwired. -
Vectors are only comparable within ONE model + dimension. Never mix embeddings from different models (or
dimensionssettings) in the same collection/index — record which model produced a vector and re-embed the corpus when switching models. -
Batch, don't loop. Use
embed({ input: texts })/embedDocuments(texts)for many texts — N separateembedQuery()calls multiply latency and cost. -
Server-side only, gated. The provider key stays on the API; embedding is billed per token, so auth + rate-limit any endpoint that embeds caller-supplied text.
-
task/inputTypeonly matter to instruction-prefixed models (@molecule/api-ai-embeddings-embeddinggemma,-gemini); other bonds ignore them. On those bondsembedQueryandembedDocumentsalready pick the right side — use them rather thanembed()for search. -
Images, audio and video go through the OPTIONAL
embedContent. Checkprovider.modalities?.includes('image')before calling it — text-only bonds (-openai,-local,-molecule) do not implement it. Eachinputs[i]becomes ONE vector, even when it combines text and media. -
Most apps shouldn't call this directly:
@molecule/api-semantic-searchcomposes this bond with@molecule/api-ai-vector-store(index + query in one call), and@molecule/api-ai-ragbuilds grounded Q&A on top of both.
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.