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 🙏

© 2025 – Pkg Stats / Ryan Hefner

@memvid/sdk

v2.0.124

Published

Single-file AI memory system for Node.js. Store, search, and query documents with built-in RAG.

Readme

@memvid/sdk

Single-file AI memory system for Node.js. Store documents, search with BM25 ranking, and run retrieval-augmented generation (RAG) queries from a portable .mv2 file.

Built on Rust via N-API for native performance. No database setup, no network dependencies, no configuration files.

Install

npm install @memvid/sdk

Quick start

import { use, create } from "@memvid/sdk";

// Create a new memory file
const mv = await use("basic", "notes.mv2", { mode: "auto" });

// Store a document
await mv.put({
  title: "Project kickoff",
  label: "meeting",
  text: "Discussed timeline, assigned tasks to team members.",
  metadata: { date: "2024-01-15" },
});

// Search by keyword
const results = await mv.find("timeline");
console.log(results.hits);

// Ask a question (retrieves relevant context)
const answer = await mv.ask("What was discussed in the kickoff?");
console.log(answer.context);

// Commit and close
await mv.seal();

Core API

Opening a memory

const mv = await use(kind, path, options?)

| Parameter | Type | Description | |-----------|------|-------------| | kind | string | Adapter type: "basic", "langchain", "llamaindex", "openai", "crewai", "vercel-ai", "autogen" | | path | string | Path to .mv2 file | | options.mode | string | "open" (default), "create", or "auto" | | options.enableLex | boolean | Enable lexical index (default: true) | | options.enableVec | boolean | Enable vector index (default: true) | | options.readOnly | boolean | Open in read-only mode (default: false) | | options.lockTimeoutMs | number | Lock acquisition timeout (ms) | | options.force | "stale_only" | Force-open when a stale lock exists | | options.forceWritable | boolean | Force writable open (dangerous) | | options.memvidApiKey | string | Memvid API key for capacity beyond free tier (mv2_*) |

Storing documents

await mv.put({
  title: "Document title",
  label: "category",
  text: "Document content...",
  metadata: { key: "value" },
  uri: "mv2://custom/path",
  tags: ["tag1", "tag2"],
});

Automatic embeddings (optional)

To enable semantic search without supplying pre-computed vectors, set enableEmbedding: true on put():

await mv.put({
  title: "Document title",
  label: "category",
  text: "Document content...",
  enableEmbedding: true,
  // Optional (defaults to "bge-small")
  embeddingModel: "bge-small", // or "openai-small"
});

Notes:

  • Local embedding models use fastembed and cache weights under MEMVID_MODELS_DIR (first run may download).
  • OpenAI embedding models require OPENAI_API_KEY (and optionally OPENAI_BASE_URL).
  • If embeddings cannot be generated, the SDK throws MV015 (it will not silently ingest without embeddings).

Batch ingestion

For bulk imports, putMany processes documents in parallel:

const docs = [
  { title: "Doc 1", text: "First document content..." },
  { title: "Doc 2", text: "Second document content..." },
  // ... thousands more
];

const frameIds = await mv.putMany(docs, { compressionLevel: 3 });
console.log(`Ingested ${frameIds.length} documents`);

To auto-generate embeddings during batch ingest, enable embeddings in the options (requests that already include embedding are left unchanged):

await mv.putMany(docs, {
  enableEmbedding: true,
  embeddingModel: "bge-small", // or "openai-small"
});

If you provide pre-computed embeddings, also attach the embedding identity so the CLI/SDKs can auto-detect the correct query embedding model later:

import { OpenAIEmbeddings } from "@memvid/sdk";

const embedder = new OpenAIEmbeddings({ model: "text-embedding-3-small" });
const vectors = await embedder.embedDocuments(docs.map((d) => d.text));

await mv.putMany(
  docs.map((doc, i) => ({
    ...doc,
    embedding: vectors[i],
    embeddingIdentity: {
      provider: "openai",
      model: embedder.modelName,
      dimension: embedder.dimension,
    },
  }))
);

External embedding providers (bring your own)

For developer-friendly “use any embedding model/API” workflows, pass an embedder:

import { OpenAIEmbeddings } from "@memvid/sdk";

const embedder = new OpenAIEmbeddings({ model: "text-embedding-3-small" });

// Ingest: computes embeddings for requests that omit `embedding` and stores embedding identity metadata.
await mv.putMany(docs, { embedder });

// Query: computes query embeddings when `queryEmbedding` is omitted.
const results = await mv.find("timeline", { mode: "sem", embedder });
const context = await mv.ask("What happened?", { mode: "sem", contextOnly: true, embedder });

Built-in external providers (API keys via env vars):

  • OpenAIEmbeddings (OPENAI_API_KEY)
  • CohereEmbeddings (COHERE_API_KEY)
  • VoyageEmbeddings (VOYAGE_API_KEY)
  • NvidiaEmbeddings (NVIDIA_API_KEY, default base URL https://integrate.api.nvidia.com)

Notes:

  • If embedder is set, putMany({ enableEmbedding: true }) is ignored (embeddings are computed in JS).
  • put() does not accept pre-computed vectors; use putMany([singleDoc], { embedder }) for external embeddings.

Searching

const results = await mv.find(query, options?)

| Option | Type | Description | |--------|------|-------------| | mode | "lex" \| "sem" \| "auto" | Search mode (default: "lex"). "sem" uses vector search. "auto" reranks lexical hits when embeddings are available. | | queryEmbedding | number[] | Pre-computed query embedding (offline-safe). If omitted, the SDK tries to auto-detect the embedding model from the .mv2 and embed the query. | | queryEmbeddingModel | string | Override the query embedding model (e.g. "openai-small", "bge-small"). | | k | number | Number of results (default: 10) | | snippetChars | number | Snippet length (default: 240) | | scope | string | Filter by URI prefix | | adaptive | boolean | Enable adaptive retrieval (semantic-only) | | minRelevancy | number | Minimum relevancy ratio vs top score (default: 0.5, adaptive-only) | | maxK | number | Max candidates to consider (default: 100, adaptive-only) | | adaptiveStrategy | "relative" \| "absolute" \| "cliff" \| "elbow" \| "combined" | Adaptive cutoff strategy (default: "relative", adaptive-only) |

Vector search (pre-computed query embedding)

Use vecSearch() when you already have a query embedding vector (offline-safe):

const queryEmbedding = [/* ... floats ... */];
const results = await mv.vecSearch("my query", queryEmbedding, { k: 10 });
console.log(results.hits);

Retrieval-augmented generation

const response = await mv.ask(question, options?)

| Option | Type | Description | |--------|------|-------------| | k | number | Documents to retrieve (default: 6) | | mode | string | "lex", "sem", or "auto"/"hybrid" | | queryEmbedding | number[] | Pre-computed query embedding for semantic/hybrid ask (offline-safe). If omitted, the SDK tries to auto-detect the embedding model from the .mv2 and embed the query. | | queryEmbeddingModel | string | Override the query embedding model (e.g. "openai-small", "bge-small"). | | adaptive | boolean | Enable adaptive retrieval (semantic/hybrid only) | | minRelevancy | number | Minimum relevancy ratio vs top score (default: 0.5, adaptive-only) | | maxK | number | Max candidates to consider (default: 100, adaptive-only) | | adaptiveStrategy | "relative" \| "absolute" \| "cliff" \| "elbow" \| "combined" | Adaptive cutoff strategy (default: "relative", adaptive-only) |

Best practices: Adaptive retrieval

For best search quality, enable adaptive retrieval with the combined strategy. This dynamically adjusts result counts based on relevance scores rather than returning a fixed k:

// Recommended for find()
const results = await mv.find("your query", {
  mode: "sem", // or "auto"
  adaptive: true,
  adaptiveStrategy: "combined",
});

// Recommended for ask()
const answer = await mv.ask("your question", {
  mode: "auto",
  adaptive: true,
  adaptiveStrategy: "combined",
});

The combined strategy uses both relative thresholds and score cliff detection to filter out low-relevance results, providing higher quality context for RAG applications.

LLM synthesis options

| Option | Type | Description | |--------|------|-------------| | model | string | LLM for synthesis (e.g., "openai:gpt-4o-mini", "nvidia:meta/llama3-8b-instruct") | | modelApiKey | string | API key for the LLM provider | | contextOnly | boolean | Skip synthesis, return context only | | returnSources | boolean | Include source metadata in response |

Timeline queries

const entries = await mv.timeline({
  limit: 50,
  since: 1704067200,  // Unix timestamp
  reverse: true,
});

Statistics

const stats = await mv.stats();
// { frame_count, size_bytes, has_lex_index, has_vec_index, ... }

File operations

// Commit pending changes
await mv.seal();

// Verify file integrity
import { verifyMemvid } from "@memvid/sdk";
const report = await verifyMemvid("notes.mv2", { deep: true });

// Repair indexes
import { doctorMemvid } from "@memvid/sdk";
await doctorMemvid("notes.mv2", { rebuildLexIndex: true, dryRun: true });

// Verify the single-file guarantee (no sidecar files like -wal/-shm)
import { verifySingleFile } from "@memvid/sdk";
await verifySingleFile("notes.mv2");

// Lock visibility helpers
import { lockWho, lockNudge } from "@memvid/sdk";
console.log(await lockWho("notes.mv2")); // { locked: false } or { locked: true, owner: ... }
await lockNudge("notes.mv2"); // sends SIGUSR1 to active writer (Unix)

Framework adapters

Adapters expose framework-native tools when the corresponding dependency is installed.

LangChain

import { use } from "@memvid/sdk";

const mv = await use("langchain", "notes.mv2");
const tools = mv.tools; // StructuredTool instances for put/find/ask

LlamaIndex

const mv = await use("llamaindex", "notes.mv2");
const queryEngine = mv.asQueryEngine();

OpenAI function calling

const mv = await use("openai", "notes.mv2");
const functions = mv.functions; // JSON schemas for tool_calls

Vercel AI SDK

const mv = await use("vercel-ai", "notes.mv2");

Table extraction

Extract structured tables from PDFs:

const result = await mv.putPdfTables("report.pdf", true);
console.log(`Extracted ${result.tables_count} tables`);

const tables = await mv.listTables();
const data = await mv.getTable(tables[0].table_id, "json");

Error handling

Errors include a code prefix for programmatic handling:

| Code | Description | |------|-------------| | MV001 | Storage capacity exceeded | | MV002 | Invalid ticket signature | | MV003 | Ticket replay detected | | MV004 | Lexical index not enabled | | MV005 | Time index missing | | MV006 | Verification failed | | MV007 | File locked by another process | | MV008 | Memvid API key required | | MV009 | Memory already bound | | MV010 | Frame not found | | MV011 | Vector index not enabled | | MV012 | Corrupt file detected | | MV013 | I/O error (e.g., file not found) | | MV014 | Vector dimension mismatch | | MV015 | Embedding runtime unavailable/failed | | MV999 | Unknown/internal error |

import { MemvidError } from "@memvid/sdk";

try {
  await mv.put({ ... });
} catch (err) {
  if (err instanceof MemvidError) {
    if (err.code === "MV001") {
      console.error("Out of storage capacity");
    } else {
      console.error(`Error ${err.code}: ${err.message}`);
    }
  }
}

Environment variables

| Variable | Description | |----------|-------------| | MEMVID_API_KEY | API key for capacity beyond free tier | | MEMVID_API_URL | Control plane URL (for enterprise deployments) | | MEMVID_MODELS_DIR | Path to embedding model cache | | MEMVID_OFFLINE | Set to 1 to disable network-backed embeddings and avoid model downloads | | OPENAI_API_KEY | API key for OpenAI embeddings / LLMs (when used) | | OPENAI_BASE_URL | Override OpenAI API base URL (defaults to https://api.openai.com) |

Building from source

# Install dependencies
pnpm install

# Build TypeScript
pnpm run build

# Build native module (requires Rust toolchain)
cargo build --release
cp target/release/libmemvid_node.dylib index.node  # macOS

Requirements

  • Node.js 18+
  • macOS (Apple Silicon or Intel), Linux (x64), or Windows (x64)

License

Apache-2.0. See LICENSE.