@molecule/api-ai-embeddings-gemini
Provider bond · ai-embeddings · API (Node) · v1.0.1 · Apache-2.0
Gemini API ai-embeddings provider for molecule.dev — hosted gemini-embedding-2: text, images, audio and video in one vector space (REST via fetch, zero dependencies)
npm install @molecule/api-ai-embeddings-gemininpm · Source on GitHub · Implements @molecule/api-ai-embeddings
How it works
@molecule/api-ai-embeddings-gemini 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.
import { readFile } from 'node:fs/promises'
import { requireProvider, setProvider } from '@molecule/api-ai-embeddings'
import { createProvider } from '@molecule/api-ai-embeddings-gemini'
// Reads GOOGLE_AI_API_KEY (or GEMINI_API_KEY) per request.
setProvider(createProvider({ dimensions: 768 }))
const embeddings = requireProvider()
const queryVector = await embeddings.embedQuery('a turtle swimming in the ocean')
// One input = one vector; media need their MIME type.
const {
embeddings: [clipVector],
} = await embeddings.embedContent!({
inputs: [{ video: { data: await readFile('dive.mp4'), mimeType: 'video/mp4' } }],
})
console.log(queryVector.length, clipVector?.length) // 768 768Works with: @molecule/api-ai-embeddings, @molecule/api-secrets
Secrets: GOOGLE_AI_API_KEY
Reference
Gemini API ai-embeddings provider for molecule.dev.
Hosted gemini-embedding-2: text, images, audio and video in one vector space
(128–3072 dimensions, default 3072), over Google's REST API with the runtime's
fetch — no SDK. Task prefixes are applied for you.
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 { readFile } from 'node:fs/promises'
import { requireProvider, setProvider } from '@molecule/api-ai-embeddings'
import { createProvider } from '@molecule/api-ai-embeddings-gemini'
// Reads GOOGLE_AI_API_KEY (or GEMINI_API_KEY) per request.
setProvider(createProvider({ dimensions: 768 }))
const embeddings = requireProvider()
const queryVector = await embeddings.embedQuery('a turtle swimming in the ocean')
// One input = one vector; media need their MIME type.
const {
embeddings: [clipVector],
} = await embeddings.embedContent!({
inputs: [{ video: { data: await readFile('dive.mp4'), mimeType: 'video/mp4' } }],
})
console.log(queryVector.length, clipVector?.length) // 768 768
Type
provider
Installation
npm install @molecule/api-ai-embeddings-gemini @molecule/api-ai-embeddings @molecule/api-secrets
API
Interfaces
GeminiEmbeddingsConfig
Configuration for the Gemini API embeddings provider.
interface GeminiEmbeddingsConfig {
/** API key. Defaults to the `GOOGLE_AI_API_KEY` env var, then `GEMINI_API_KEY`. */
apiKey?: string
/** Default model. Defaults to `gemini-embedding-2`. */
defaultModel?: string
/**
* API base URL including the version. Defaults to `GOOGLE_AI_BASE_URL` or
* `https://generativelanguage.googleapis.com/v1beta`.
*/
baseUrl?: string
/** Default output size, 128–3072 (`output_dimensionality`). Omitted = the model default (3072). */
dimensions?: number
/** Default task for prefixes. Defaults to `search`. */
task?: EmbeddingTask
/**
* Prepend the task prefixes Google recommends (`task: search result | query: …`,
* `title: none | text: …`). Defaults to `true`; text that already starts with
* `task: ` or `title: ` is never prefixed twice.
*/
applyPrefixes?: boolean
/** Inputs per `batchEmbedContents` request. Defaults to 100; larger lists are split. */
maxBatchSize?: number
}
Classes
GeminiEmbeddingsError
A Gemini API request failed. Carries the vendor's HTTP status and its error
code (e.g. INVALID_ARGUMENT, RESOURCE_EXHAUSTED). Deliberately NOT tagged
with statusCode/errorKey, so API middleware answers with its generic 500
instead of echoing Google's status to the caller.
class GeminiEmbeddingsError extends Error {
/** HTTP status Google answered with. */
readonly status: number
/** Google's error status string, when it sent one. */
readonly code: string | undefined
/**
* Creates the error.
*
* @param message - Human-readable detail from Google.
* @param status - HTTP status.
* @param code - Google's error status string.
*/
constructor(message: string, status: number, code?: string) {
super(`Gemini Embeddings API error (HTTP ${status}${code ? ` ${code}` : ''}): ${message}`)
this.name = 'GeminiEmbeddingsError'
this.status = status
this.code = code
}
}
Functions
applyTaskPrefix(text, task, inputType)
Prefix one text for the given task and side.
Asymmetric tasks: a query becomes task: <task> | query: <text> and a document
title: none | text: <text>. Symmetric tasks (classification, clustering,
similarity) use the query form for every input.
function applyTaskPrefix(text: string, task: EmbeddingTask, inputType: EmbeddingInputType): string
text— The raw text.task— What the vectors are for.inputType— Query or document side.
Returns: The prefixed text (unchanged if it already carries a prefix).
isEmbeddingTask(value)
Whether a string is a supported task name (for env-var parsing).
function isEmbeddingTask(value: string): boolean
value— Candidate task name.
Returns: true if it is an EmbeddingTask.
createProvider(config)
Creates a Gemini API embeddings provider.
function createProvider(config?: GeminiEmbeddingsConfig): AIEmbeddingsProvider
config— Optional configuration (key, model, base URL, dimensions, task).
Returns: An AIEmbeddingsProvider backed by gemini-embedding-2.
Constants
GEMINI_EMBEDDINGS_DEFAULT_BASE_URL
Default API base URL (version included).
const GEMINI_EMBEDDINGS_DEFAULT_BASE_URL: 'https://generativelanguage.googleapis.com/v1beta'
GEMINI_EMBEDDINGS_DEFAULT_MODEL
Default model: Google's hosted multimodal embedding model.
const GEMINI_EMBEDDINGS_DEFAULT_MODEL: 'gemini-embedding-2'
provider
The provider implementation (reads GOOGLE_AI_API_KEY per request). Bond it
with the ai-embeddings core's setProvider.
const provider: AIEmbeddingsProvider
aiEmbeddingsGeminiSecretDefinitions
Secret definitions required by the Gemini API embeddings bond.
const aiEmbeddingsGeminiSecretDefinitions: 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-gemini'
export function setupAiEmbeddingsGemini(): void {
setProvider(provider)
}
Injection Notes
Requirements
Peer dependencies:
@molecule/api-ai-embeddings>=1.1.0@molecule/api-secrets^1.0.1
Environment Variables
GOOGLE_AI_API_KEY(required) — Google AI (Gemini) API key- Setup: Create a Gemini API key in Google AI Studio.
- Get it here: https://aistudio.google.com/apikey
- Example:
AIza...
Runtime Dependencies
-
@molecule/api-ai-embeddings -
@molecule/api-secrets -
Use
embedQueryfor what the user types andembedDocumentsfor what you index.gemini-embedding-2rejectstask_type; the task goes in the text (task: search result | query: …vstitle: none | text: …), which this bond adds.embed()treats input as DOCUMENTS unless you passinputType: 'query'. Text already starting withtask:/title:is left alone;applyPrefixes: falseturns prefixing off. -
This is NOT EmbeddingGemma 2.
gemini-embedding-2is a different, proprietary model with its own vector space — not comparable with@molecule/api-ai-embeddings-embeddinggemma,gemini-embedding-001, OpenAI or bge vectors, nor acrossdimensionssettings. Switching re-embeds the index. -
Media need a MIME type: pass
{ data: bytes, mimeType }(sent inline, base64) or{ data: fileUri, mimeType }for a Files API upload. Bare bytes or a bare URL throw. Supported: PNG/JPEG images (max 6 per request), MP3/WAV audio (max 180 s), MP4/MOV video (max 120 s, ≤32 frames sampled, its audio track ignored). One input's 8,192 tokens are shared by all its parts; Google silently truncates anything past that. -
One
embedContentinput = one vector, even with several parts (Gemini aggregates them).<|image|>-style placeholders are removed from the text — media are sent as their own parts. -
Billed per token — text, image, audio and video are priced differently, and video is the most expensive.
usage.promptTokensis Google'spromptTokenCount. Auth + rate-limit any endpoint that embeds caller input. -
dimensionsmust be an integer 128–3072; vectors below 3072 come back re-normalized by Google. -
A missing key throws the tagged
config.notConfigurederror namingGOOGLE_AI_API_KEY(the same key as@molecule/api-ai-google). API failures throwGeminiEmbeddingsErrorwith Google's HTTPstatusandcode(INVALID_ARGUMENT,RESOURCE_EXHAUSTED, …); 429/500/503 are retried 3 times first.GOOGLE_AI_BASE_URL(version included, e.g.…/v1beta) points it at a gateway, which may be keyless. -
Lists are sent through
batchEmbedContentsin chunks ofmaxBatchSize(default 100) — one request per text, never one merged vector.
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.