npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@molecule/api-ai-embeddings-gemini

v1.0.2

Published

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)

Readme

@molecule/api-ai-embeddings-gemini

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.ts JSDoc, 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 {@link 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

Runtime Dependencies

  • @molecule/api-ai-embeddings

  • @molecule/api-secrets

  • Use embedQuery for what the user types and embedDocuments for what you index. gemini-embedding-2 rejects task_type; the task goes in the text (task: search result | query: … vs title: none | text: …), which this bond adds. embed() treats input as DOCUMENTS unless you pass inputType: 'query'. Text already starting with task: / title: is left alone; applyPrefixes: false turns prefixing off.

  • This is NOT EmbeddingGemma 2. gemini-embedding-2 is 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 across dimensions settings. 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 embedContent input = 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.promptTokens is Google's promptTokenCount. Auth + rate-limit any endpoint that embeds caller input.

  • dimensions must be an integer 128–3072; vectors below 3072 come back re-normalized by Google.

  • A missing key throws the tagged config.notConfigured error naming GOOGLE_AI_API_KEY (the same key as @molecule/api-ai-google). API failures throw GeminiEmbeddingsError with Google's HTTP status and code (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 batchEmbedContents in chunks of maxBatchSize (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 numeric number[] of the model's fixed dimension, and every vector from embed/embedDocuments has that SAME length — no empty arrays, no NaN/null entries, 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]) (or embed({ input })) returns exactly one vector per input in the SAME order — embeddings[i] is the vector for input[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.