v3/docs/adr/ADR-094-xenova-to-huggingface-transformers-migration.md
@xenova/transformers → @huggingface/transformersStatus: Proposed
Date: 2026-05-03
Version: targets v3.7.x
Supersedes: nothing
Related: ADR-093, npm audit CVE chain through protobufjs
@xenova/[email protected] is the legacy ONNX inference package we use for client-side embeddings (pipeline('feature-extraction', ...)). The package was deprecated in favor of @huggingface/transformers (currently 4.2.0) in late 2024. The legacy package has not received updates in over a year and pins old transitive deps that have since picked up critical CVEs:
protobufjs <7.5.5 — CRITICAL — Arbitrary code execution — comes through onnxruntime-web → onnx-proto@xenova/transformers → onnxruntime-web@<1.17 → onnx-proto → protobufjs@<7.5.5@huggingface/[email protected]+ uses modern onnxruntime-web versions that pull in protobufjs >=7.5.5, eliminating this CVE class entirely.
We had previously documented this as a deferred ADR-093 follow-up. The npm overrides we shipped in 3.6.18 cannot resolve this chain because (a) overrides only apply at the install root and (b) the version range required by @xenova/transformers's manifests forbids the safer protobufjs.
Only two call sites import @xenova/transformers in the v3 monorepo:
| File | Import | Usage |
|---|---|---|
v3/@claude-flow/embeddings/src/embedding-service.ts:387 | const { pipeline } = await import('@xenova/transformers') | Used as the ONNX backend for feature-extraction |
v3/@claude-flow/cli/src/memory/memory-initializer.ts:1539 | const transformers = await import('@xenova/transformers').catch(() => null) | Optional ONNX provider for embedding generation |
Both are dynamic imports wrapped in try/catch — the migration risk is bounded.
A direct exports comparison (probed against @xenova/[email protected] and @huggingface/[email protected]):
@xenova/transformers: 501 exports@huggingface/transformers: 935 exports (superset)pipeline, env, AutoTokenizer, AutoModelPipeline calls in both packages:
pipeline('feature-extraction', modelId) → 384/768-dim Float32Array via output.dataMigrate both call sites to a provider-agnostic loader that prefers @huggingface/transformers, falls back to @xenova/transformers for backwards compat with consumers who haven't installed the new package, and reports honest status via embeddings_status.ruvectorStatus (already structured per ADR-093 F5).
// New helper in @claude-flow/embeddings/src/transformers-loader.ts
export async function loadTransformersPipeline(): Promise<{
pipeline: PipelineFn;
source: '@huggingface/transformers' | '@xenova/transformers';
version?: string;
} | null> {
// Prefer the maintained successor
try {
const mod = await import('@huggingface/transformers');
if (typeof mod.pipeline === 'function') {
return { pipeline: mod.pipeline, source: '@huggingface/transformers', version: mod.version };
}
} catch { /* fall through */ }
// Fall back to legacy for backwards compat
try {
const mod = await import('@xenova/transformers');
if (typeof mod.pipeline === 'function') {
return { pipeline: mod.pipeline, source: '@xenova/transformers', version: mod.version };
}
} catch { /* both unavailable */ }
return null;
}
Update both call sites to use the loader:
// embedding-service.ts:387 (was: const { pipeline } = await import('@xenova/transformers'))
const t = await loadTransformersPipeline();
if (!t) throw new Error('No ONNX transformers package available');
this.pipeline = await t.pipeline('feature-extraction', this.modelName);
this.transformersSource = t.source;
@claude-flow/embeddings/package.json:
@xenova/transformers from dependencies to optionalDependencies (keeps install size small for users who don't need ONNX)@huggingface/transformers: "^4.2.0" as a peerDependency (optional) and optionalDependencies (auto-install)@claude-flow/cli/package.json: no direct change (transformers is a transitive of @claude-flow/embeddings).
Xenova/all-MiniLM-L6-v2). Snapshot the first 8 dims for "hello world" and verify byte-identical output between packages.@huggingface/transformers and verify embeddings work; install only @xenova/transformers and verify the fallback works; install neither and verify the graceful "no provider" path.embeddings_* MCP tools.npm audit post-migration: protobufjs <7.5.5 should drop from the prod tree.Positive:
protobufjs. The 14-critical / 4-high audit count from 3.6.17 should drop substantially.Negative:
@xenova/transformers optional rather than removing entirely (consumers can opt for the smaller package).@claude-flow/embeddings consumers to be aware of.Risk:
pipeline('feature-extraction', model) might produce subtly different outputs vs xenova for the same model (e.g. different default normalization). Validation step #1 (byte-identical output check) catches this before merge.@huggingface/transformers package itself currently audits clean against npm advisory database as of 2026-05-03.getQueryEmbedding pattern in hooks-tools.ts:3050, so callers familiar with that codebase will recognize the shape.