Views
No views yet
jinaai/jina-embeddings-v5-omni-nano for in-browser inference via transformers.js v4 with WebGPU.jina-embeddings-v5-omni-nano is a ~1.04 B-parameter multimodal embedding model that maps text, images, audio, and video into a single shared 768-dimensional L2-normalized space, enabling cross-modal retrieval without reindexing.retrieval task adapter merged into the static weights.| Tower | Backbone | Role |
|---|---|---|
| Text | EuroBERT-210m (loaded as a bidirectional Llama) | Text encoder |
| Vision | Qwen3-VL vision tower + spatial merger | Image and video frames |
| Audio | Whisper-large-v3 encoder + Qwen2.5-Omni audio adapter | Audio (16 kHz mono) |
.onnx schema + a large .onnx.data or .onnx_data external-weight sidecar (HF Hub-friendly layout, no 2 GB protobuf limits).| Modality | fp32 | fp16 | q4f16 | Verified parity vs fp32 (fp16) |
|---|---|---|---|---|
| Text | 849 MB | 424 MB | 263 MB | cos = 1.000000 |
| Vision | 1247 MB | 622 MB | 460 MB | cos = 0.999998 |
| Audio | 3400 MB | 1700 MB | 1465 MB | (fp16 doesn't load on CPU EP; verify on WebGPU) |
q4f16 — it's the smallest and runs natively on shader-int4 hardware. Use fp16 if you need higher numerical fidelity or your GPU lacks int4 paths.sentence_embedding of shape [batch, 768], already L2-normalized — cosine similarity reduces to a dot product.text_model*.onnx): inputs input_ids and attention_mask, both [batch, seq] with seq fully dynamic. Apply the asymmetric retrieval prefix convention before tokenizing: Query: … for queries, Document: … for corpus items.vision_model*.onnx): the graph is traced at a fixed image-patch layout. image_grid_thw is folded as a constant (it drives torch.linspace inside Qwen3-VL's fast_pos_embed_interpolate, which dynamo cannot symbolicate). Resize every image to 224×224 before passing through LlavaEuroBertProcessor — that yields the exact shapes the graph expects:input_ids [batch, 271] int64
attention_mask [batch, 271] int64
pixel_values [1024, 1536] float32image_grid_thw is NOT an ONNX input (it's a constant). Don't pass it.audio_model*.onnx): traced with a 5-second 16 kHz mono clip. The graph expects exactly:input_ids [batch, 125] int64 (audio_token_id placeholders)
attention_mask [batch, 125] int64
input_features [1, 128, 3000] float32 (Whisper log-mel, 30s padded)
feature_attention_mask [1, 3000] int64 (frame-level, 500 ones for 5s of real audio)1import { AutoTokenizer } from "@huggingface/transformers";
2import * as ort from "onnxruntime-web";
3
4const REPO = "shreyask/jina-embeddings-v5-omni-nano-ONNX";
5
6const tok = await AutoTokenizer.from_pretrained(REPO);
7const sess = await ort.InferenceSession.create(
8 `https://huggingface.co/${REPO}/resolve/main/onnx/text_model_q4f16.onnx`,
9 { executionProviders: ["webgpu", "wasm"] },
10);
11
12const { input_ids, attention_mask } = await tok(
13 "Query: a saxophone solo",
14 { return_tensors: "ort" },
15);
16const { sentence_embedding } = await sess.run({ input_ids, attention_mask });
17// Float32Array of length 768, already L2-normalized.1import { AutoProcessor } from "@huggingface/transformers";
2import * as ort from "onnxruntime-web";
3
4const proc = await AutoProcessor.from_pretrained(REPO);
5const sess = await ort.InferenceSession.create(
6 `https://huggingface.co/${REPO}/resolve/main/onnx/vision_model_q4f16.onnx`,
7 { executionProviders: ["webgpu", "wasm"] },
8);
9
10// Resize to 224x224 before passing in.
11const inputs = await proc.apply_chat_template(
12 [{ role: "user", content: [{ type: "image", image: imageBlob }] }],
13 { add_generation_prompt: false, tokenize: true, return_dict: true, return_tensors: "ort" },
14);
15const { sentence_embedding } = await sess.run({
16 input_ids: inputs.input_ids,
17 attention_mask: inputs.attention_mask,
18 pixel_values: inputs.pixel_values,
19});LlavaEuroBertProcessor — use Whisper's feature extractor directly and stamp audio_token_id placeholders into input_ids. See the reference implementation for the exact mel-spec → placeholder count plumbing.const score = textVec.reduce((s, v, i) => s + v * imageVec[i], 0);torch 2.11, transformers 5.8, onnx 1.21, onnxruntime 1.26torch.onnx.export(..., dynamo=True). The LlamaModel-based encoder exports cleanly through the dynamo pathtorch.onnx.export(..., dynamo=False) (legacy TorchScript tracer). Dynamo refuses to specialize Qwen3-VL's data-dependent torch.linspace, and the GQA-aware SDPA is monkey-patched with a manual MatMul+Softmax for the trace's durationmerge_and_unload(safe_merge=True) so the retrieval task adapter is baked inonnxruntime.transformers.float16.convert_float_to_float16 (the onnxconverter_common path mishandles dynamo's _to_copy nodes)onnxruntime.quantization.matmul_nbits_quantizer.MatMulNBitsQuantizer (bits=4, block_size=32, accuracy_level=4)Cast(to=int64) inserted before every Slice index input (366 inserts), and Unsqueeze(0)/Squeeze(0) wrapped around the rank-2-input AveragePool (1 site)sales@jina.ai.1@misc{jina-embeddings-v5-omni-nano,
2 author = {Jina AI},
3 title = {jina-embeddings-v5-omni-nano},
4 year = {2025},
5 url = {https://huggingface.co/jinaai/jina-embeddings-v5-omni-nano}
6}