Embeddings
Generate embeddings for text and perform semantic search.
Embeddings convert text into numerical vectors that capture semantic meaning. Use them for similarity search, clustering, and RAG applications.
See it in action
Try the Knowledge Base block for a working demo of these APIs.
embed()
Generate an embedding for a single value:
import { embed } from '@localmode/core';
import { transformers } from '@localmode/transformers';
const model = transformers.embedding('Xenova/bge-small-en-v1.5');
const { embedding, usage, response } = await embed({
model,
value: 'Hello, world!',
});
console.log('Dimensions:', embedding.length); // 384
console.log('Tokens:', usage.tokens); // 4
console.log('Model:', response.modelId); // 'Xenova/bge-small-en-v1.5'const controller = new AbortController();
setTimeout(() => controller.abort(), 5000); // Cancel after 5s
const { embedding } = await embed({
model,
value: 'Hello, world!',
abortSignal: controller.signal,
});EmbedOptions
Prop
Type
EmbedResult
Prop
Type
embedMany()
Generate embeddings for multiple values efficiently:
import { embedMany } from '@localmode/core';
const { embeddings, usage } = await embedMany({
model,
values: ['Hello', 'World', 'AI', 'Machine Learning'],
});
console.log('Count:', embeddings.length); // 4
console.log('Total tokens:', usage.tokens); // ~8import { streamEmbedMany } from '@localmode/core';
const stream = streamEmbedMany({
model,
values: largeArrayOfTexts,
onBatch: (progress) => {
console.log(`Processed ${progress.index + progress.count}/${progress.total}`);
},
});
for await (const { embedding } of stream) {
// Process each embedding as it arrives
}For progress tracking, use streamEmbedMany() with the onBatch callback. embedMany() does not support progress callbacks.
const controller = new AbortController();
// Cancel after 5 seconds
setTimeout(() => controller.abort(), 5000);
try {
const { embeddings } = await embedMany({
model,
values: largeArray,
abortSignal: controller.signal,
});
} catch (error) {
if (error.name === 'AbortError') {
console.log('Operation cancelled');
}
}EmbedManyOptions
Prop
Type
streamEmbedMany()
Stream embeddings as they're generated:
import { streamEmbedMany } from '@localmode/core';
const stream = streamEmbedMany({
model,
values: texts,
});
for await (const { index, embedding } of stream) {
console.log(`Embedding ${index}:`, embedding.length);
}semanticSearch()
Search for semantically similar documents:
import { semanticSearch, createVectorDB } from '@localmode/core';
const db = await createVectorDB({ name: 'docs', dimensions: 384 });
// Add documents to the database first (e.g. via ingest())...
const { results } = await semanticSearch({
db,
model,
query: 'What is machine learning?',
k: 5,
});
results.forEach((result) => {
console.log(`Score: ${result.score.toFixed(3)}`);
console.log(`Text: ${result.text}`);
});semanticSearch() returns { results, usage } — results is an array of { id, score, text?, metadata? } items, and usage reports embeddingTokens, embedDurationMs, and searchDurationMs.
Result Text Extraction
Each result's text is extracted from its stored metadata by checking these fields in priority order and returning the first string value found:
textcontentbody_text— theTEXT_METADATA_FIELDconstant, the keyingest()/ingestChunks()write chunk text under__textpageContent
If none match, text is undefined (the result is still returned). Because the RAG ingest() pipeline stores chunk text under TEXT_METADATA_FIELD ('_text'), an ingest → search round-trip populates results[].text out of the box — and metadata you stored yourself under text always wins over the ingest-written key:
import { TEXT_METADATA_FIELD } from '@localmode/core';
const { results } = await semanticSearch({ db, model, query, k: 5 });
results[0].text; // chunk text stored by ingest()
results[0].metadata?.[TEXT_METADATA_FIELD]; // the same text, via the raw keyWith Filters
const { results } = await semanticSearch({
db,
model,
query: 'AI applications',
k: 5,
filter: {
category: { $eq: 'technology' },
year: { $gte: 2023 },
},
});Options
interface SemanticSearchOptions {
db: VectorDB;
model: EmbeddingModel | string;
query: string;
k?: number;
filter?: Record<string, unknown>;
threshold?: number;
abortSignal?: AbortSignal;
}Distance Functions
Compare vectors directly:
import { cosineSimilarity, euclideanDistance, dotProduct } from '@localmode/core';
const similarity = cosineSimilarity(embedding1, embedding2);
console.log('Similarity:', similarity); // 0.0 to 1.0
const distance = euclideanDistance(embedding1, embedding2);
console.log('Distance:', distance);
const dot = dotProduct(embedding1, embedding2);
console.log('Dot product:', dot);Middleware
Wrap embedding models with middleware for caching, logging, etc.:
import { wrapEmbeddingModel, cachingMiddleware, loggingMiddleware } from '@localmode/core';
const baseModel = transformers.embedding('Xenova/bge-small-en-v1.5');
const model = wrapEmbeddingModel(baseModel, [
cachingMiddleware({ maxSize: 1000 }),
loggingMiddleware({ logger: console.log }),
]);
// Now all embed calls will be cached and logged
const { embedding } = await embed({ model, value: 'Hello' });See Middleware for more details.
Implementing Custom Models
Create your own embedding model by implementing the EmbeddingModel interface:
import type { EmbeddingModel, DoEmbedOptions } from '@localmode/core';
class MyCustomEmbedder implements EmbeddingModel {
readonly modelId = 'custom:my-embedder';
readonly provider = 'custom';
readonly dimensions = 768;
readonly maxEmbeddingsPerCall = 100;
readonly supportsParallelCalls = true;
async doEmbed(options: DoEmbedOptions) {
const { values } = options;
// Your embedding logic here
const embeddings = values.map(() => new Float32Array(768));
return {
embeddings,
usage: { tokens: values.length * 10 },
response: { modelId: this.modelId, timestamp: new Date() },
};
}
}
// Use with core functions
const model = new MyCustomEmbedder();
const { embedding } = await embed({ model, value: 'Hello' });Best Practices
Performance Tips
- Batch embeddings - Use
embedMany()instead of multipleembed()calls - Use caching - Add
cachingMiddleware()for repeated queries - Choose the right model - Smaller models (MiniLM-L6) are faster, larger ones more accurate
- Preload models - Load models during app initialization
For recommended models, provider-specific configuration, and practical recipes, see the Transformers Embeddings guide.
Next Steps
Vector Database
Store and search embeddings efficiently.
RAG
Build retrieval-augmented generation pipelines.
React Hooks
useEmbed, useEmbedMany, and useSemanticSearch hooks.
Multimodal Embeddings
Embed images and text in the same vector space with CLIP.
Drift Detection
Detect model changes and re-embed documents automatically.
Blocks
| App | Description | Links |
|---|---|---|
| Knowledge Base (Semantic Search) | Embed documents and search by meaning | Live block · Source |
| Photo Search (Cross-Modal Search) | Embed text and images for cross-modal retrieval | Live block · Source |
| Photo Search (Product Search) | Embed product catalog for semantic product discovery | Live block · Source |
| Photo Search (Smart Gallery) | Embed images for intelligent photo organization | Live block · Source |
| Privacy (PII Redactor) | Embed text with differential privacy noise | Live block · Source |
| Knowledge Base (PDF Search) | Embed PDF chunks for document Q&A | Live block · Source |
| Knowledge Base (LangChain RAG) | Embed documents in a LangChain RAG pipeline | Live block · Source |
| Photo Search (Duplicate Finder) | Embed image features to detect near-duplicates | Live block · Source |