LocalMode
Core

Import / Export

Migrate vector data between cloud vector databases and LocalMode. Parse Pinecone, ChromaDB, CSV, and JSONL formats. Export to interoperable formats.

Import / Export

Migrate vector data from cloud vector databases (Pinecone, ChromaDB) to LocalMode's in-browser VectorDB. Parse, preview, import, and export vector data in multiple formats — all offline.

See it in action

Try the Knowledge Base block for a working demo of these APIs — its Data tab runs Pinecone/CSV/JSONL import and export against a live in-browser corpus.

The import/export module is separate from the existing db.import() / db.export() methods, which handle LocalMode's own internal JSON format. The new functions handle external cloud database formats.

Supported Formats

FormatShapeUse Case
Pinecone JSON{ vectors: [{ id, values, metadata }] }Pinecone dashboard exports
ChromaDB JSON{ ids, embeddings, metadatas, documents }ChromaDB collection exports
CSVHeader row with vector column (JSON array)Spreadsheet-compatible transfers
JSONLOne { id, vector, ... } per lineStreaming-friendly, large datasets

Quick Start

import { importFrom, createVectorDB } from '@localmode/core';
import { transformers } from '@localmode/transformers';

// 1. Create a VectorDB
const db = await createVectorDB({ name: 'migrated', dimensions: 384 });

// 2. Import from Pinecone export
const stats = await importFrom({
  db,
  content: pineconeExportJson,
  format: 'pinecone',
  model: transformers.embedding('Xenova/bge-small-en-v1.5'), // re-embed text-only records
  onProgress: (p) => console.log(`${p.phase}: ${p.completed}/${p.total}`),
});

console.log(`Imported ${stats.imported}, skipped ${stats.skipped}`);

Preview Before Import

Use parseExternalFormat() to inspect data before committing to an import:

import { parseExternalFormat } from '@localmode/core';

const result = parseExternalFormat(fileContent);
// result.format       → 'pinecone' (auto-detected)
// result.totalRecords → 1000
// result.recordsWithVectors → 800
// result.recordsWithTextOnly → 200
// result.dimensions   → 384

// Show preview to the user before importing

importFrom()

The main orchestrator function. Parses external data, validates dimensions, optionally re-embeds text-only records, and batches into db.addMany().

Options

OptionTypeDefaultDescription
dbVectorDBrequiredTarget VectorDB instance
contentstringrequiredRaw content string to parse
formatExternalFormatauto-detectSource format override
modelEmbeddingModelEmbedding model for re-embedding text-only records
batchSizenumber100Records per addMany() call
skipDimensionCheckbooleanfalseSkip dimension validation
onProgress(progress) => voidProgress callback
abortSignalAbortSignalCancellation signal

ImportStats Result

FieldTypeDescription
importednumberRecords successfully imported
skippednumberRecords skipped (no vector and no model)
reEmbeddednumberText-only records re-embedded
totalParsednumberTotal records parsed from source
formatExternalFormatDetected or specified format
dimensionsnumberVector dimensions
durationMsnumberTotal operation time

Export

exportToCSV()

import { exportToCSV } from '@localmode/core';

const csv = exportToCSV(records, {
  delimiter: ',',
  includeVectors: true,
  includeText: true,
});

exportToJSONL()

import { exportToJSONL } from '@localmode/core';

const jsonl = exportToJSONL(records, {
  includeVectors: true,
  vectorFieldName: 'embedding', // custom field name
});

Format Conversion

Convert between formats without a VectorDB:

import { convertFormat } from '@localmode/core';

// Pinecone JSON → CSV
const csv = convertFormat(pineconeJson, { to: 'csv' });

// ChromaDB JSON → JSONL
const jsonl = convertFormat(chromaJson, { from: 'chroma', to: 'jsonl' });

// CSV → Pinecone format
const pinecone = convertFormat(csvData, { to: 'pinecone' });

Re-embedding Workflow

When importing from a cloud database, some records may have text but no vectors (because the original embeddings were generated server-side). Pass an EmbeddingModel to importFrom() to re-embed these records locally:

import { importFrom } from '@localmode/core';
import { transformers } from '@localmode/transformers';

const stats = await importFrom({
  db,
  content: chromaExport,
  model: transformers.embedding('Xenova/bge-small-en-v1.5'),
});

// stats.reEmbedded → number of text-only records that were re-embedded

Re-embedded vectors will differ from the original cloud-generated vectors because different models produce different embeddings. This is expected — the local model becomes the new source of truth.

Error Handling

import { importFrom, ParseError, DimensionMismatchOnImportError } from '@localmode/core';

try {
  await importFrom({ db, content });
} catch (error) {
  if (error instanceof ParseError) {
    console.log(error.hint);           // actionable fix suggestion
    console.log(error.context?.format); // format being parsed
    console.log(error.context?.line);   // line number (JSONL/CSV)
  }
  if (error instanceof DimensionMismatchOnImportError) {
    console.log(error.expected); // target DB dimensions
    console.log(error.actual);   // imported vector dimensions
    console.log(error.recordId); // first mismatched record
  }
}

React Hook

import { useImportExport } from '@localmode/react';

function ImportPanel() {
  const {
    importData, parsePreview, exportCSV, exportJSONL,
    isImporting, isParsing, progress, stats, parseResult, error,
    cancel, reset,
  } = useImportExport({
    db: myVectorDB,
    model: embeddingModel,
  });

  return (
    <div>
      <button onClick={() => parsePreview({ content: fileText })}>Preview</button>
      <button onClick={() => importData({ content: fileText })}>Import</button>
      <button onClick={exportCSV}>Export CSV</button>
      {isImporting && <p>Phase: {progress?.phase}</p>}
      {stats && <p>Imported: {stats.imported}</p>}
    </div>
  );
}

Recipe: Moving from Pinecone to LocalMode

Export from Pinecone

In the Pinecone console, export your index as JSON. The file will have the format:

{ "vectors": [{ "id": "...", "values": [...], "metadata": {...} }] }

Preview the export

const result = parseExternalFormat(pineconeJson);
console.log(`${result.totalRecords} records, ${result.dimensions}d vectors`);

Create a matching VectorDB

const db = await createVectorDB({
  name: 'my-app',
  dimensions: result.dimensions!,
});

Import with progress

const stats = await importFrom({
  db,
  content: pineconeJson,
  format: 'pinecone',
  onProgress: (p) => updateProgressBar(p.overallCompleted / p.overallTotal),
});

Delete your Pinecone account

Your data now lives entirely in the browser. No servers, no API keys, no monthly bill.

Blocks

AppDescriptionLinks
Knowledge Base (Semantic Search)Export and import vector indexesLive block · Source
Knowledge Base (Data Migrator)Migrate vectors between Pinecone, ChromaDB, CSV, JSONLLive block · Source

On this page