opus-mt-en-fr machine translation (MarianMT)
Translation model for transformers.js (fp32). Runs on WASM or WebGPU —
404 MB downloaded once from models.skillsafe.ai, then cached for every SkillSafe app that
uses it. Inference happens on your device; nothing you enter is uploaded to load it, and it never costs a credit.
Weights from Hugging Face · skillsafe-ai/opus-mt-en-fr, pinned at 4f304696f52f — also loadable straight from Hugging Face outside SkillSafe (how).
Details
opus-mt-en-fr-fp32@287262064f304696f52fb0d9228acbb00f125666f455a20aFiles
Each file is served at an immutable URL; the canonical form is the SHA-256 itself. Tokenizer and config JSON are not here by design — they ship in your app bundle.
| Path | Format | Size | SHA-256 | |
|---|---|---|---|---|
onnx/decoder_model_merged.onnx | onnx | 214 MB | d87fdd0e4f16…0bad57 | |
onnx/encoder_model.onnx | onnx | 189 MB | ffa8429c73be…95b0d9 |
Signature
Graph inputs and outputs read from the ONNX bytes at vetting — the tensor names your session.run() call feeds and reads. Symbolic dimensions are shown by name.
onnx/decoder_model_merged.onnx
Inputs
encoder_attention_maskint64 [batch_size, encoder_sequence_length]input_idsint64 [batch_size, decoder_sequence_length]encoder_hidden_statesfloat32 [batch_size, encoder_sequence_length, 512]past_key_values.0.decoder.keyfloat32 [batch_size, 8, past_decoder_sequence_length, 64]past_key_values.0.decoder.valuefloat32 [batch_size, 8, past_decoder_sequence_length, 64]past_key_values.0.encoder.keyfloat32 [batch_size, 8, encoder_sequence_length_out, 64]past_key_values.0.encoder.valuefloat32 [batch_size, 8, encoder_sequence_length_out, 64]past_key_values.1.decoder.keyfloat32 [batch_size, 8, past_decoder_sequence_length, 64]past_key_values.1.decoder.valuefloat32 [batch_size, 8, past_decoder_sequence_length, 64]past_key_values.1.encoder.keyfloat32 [batch_size, 8, encoder_sequence_length_out, 64]past_key_values.1.encoder.valuefloat32 [batch_size, 8, encoder_sequence_length_out, 64]past_key_values.2.decoder.keyfloat32 [batch_size, 8, past_decoder_sequence_length, 64]past_key_values.2.decoder.valuefloat32 [batch_size, 8, past_decoder_sequence_length, 64]past_key_values.2.encoder.keyfloat32 [batch_size, 8, encoder_sequence_length_out, 64]past_key_values.2.encoder.valuefloat32 [batch_size, 8, encoder_sequence_length_out, 64]past_key_values.3.decoder.keyfloat32 [batch_size, 8, past_decoder_sequence_length, 64]past_key_values.3.decoder.valuefloat32 [batch_size, 8, past_decoder_sequence_length, 64]past_key_values.3.encoder.keyfloat32 [batch_size, 8, encoder_sequence_length_out, 64]past_key_values.3.encoder.valuefloat32 [batch_size, 8, encoder_sequence_length_out, 64]past_key_values.4.decoder.keyfloat32 [batch_size, 8, past_decoder_sequence_length, 64]past_key_values.4.decoder.valuefloat32 [batch_size, 8, past_decoder_sequence_length, 64]past_key_values.4.encoder.keyfloat32 [batch_size, 8, encoder_sequence_length_out, 64]past_key_values.4.encoder.valuefloat32 [batch_size, 8, encoder_sequence_length_out, 64]past_key_values.5.decoder.keyfloat32 [batch_size, 8, past_decoder_sequence_length, 64]past_key_values.5.decoder.valuefloat32 [batch_size, 8, past_decoder_sequence_length, 64]past_key_values.5.encoder.keyfloat32 [batch_size, 8, encoder_sequence_length_out, 64]past_key_values.5.encoder.valuefloat32 [batch_size, 8, encoder_sequence_length_out, 64]use_cache_branchbool [1]
Outputs
logitsfloat32 [batch_size, decoder_sequence_length, 59514]present.0.decoder.keyfloat32 [batch_size, 8, past_decoder_sequence_length + 1, 64]present.0.decoder.valuefloat32 [batch_size, 8, past_decoder_sequence_length + 1, 64]present.0.encoder.keyfloat32 [batch_size, 8, encoder_sequence_length_out, 64]present.0.encoder.valuefloat32 [batch_size, 8, encoder_sequence_length_out, 64]present.1.decoder.keyfloat32 [batch_size, 8, past_decoder_sequence_length + 1, 64]present.1.decoder.valuefloat32 [batch_size, 8, past_decoder_sequence_length + 1, 64]present.1.encoder.keyfloat32 [batch_size, 8, encoder_sequence_length_out, 64]present.1.encoder.valuefloat32 [batch_size, 8, encoder_sequence_length_out, 64]present.2.decoder.keyfloat32 [batch_size, 8, past_decoder_sequence_length + 1, 64]present.2.decoder.valuefloat32 [batch_size, 8, past_decoder_sequence_length + 1, 64]present.2.encoder.keyfloat32 [batch_size, 8, encoder_sequence_length_out, 64]present.2.encoder.valuefloat32 [batch_size, 8, encoder_sequence_length_out, 64]present.3.decoder.keyfloat32 [batch_size, 8, past_decoder_sequence_length + 1, 64]present.3.decoder.valuefloat32 [batch_size, 8, past_decoder_sequence_length + 1, 64]present.3.encoder.keyfloat32 [batch_size, 8, encoder_sequence_length_out, 64]present.3.encoder.valuefloat32 [batch_size, 8, encoder_sequence_length_out, 64]present.4.decoder.keyfloat32 [batch_size, 8, past_decoder_sequence_length + 1, 64]present.4.decoder.valuefloat32 [batch_size, 8, past_decoder_sequence_length + 1, 64]present.4.encoder.keyfloat32 [batch_size, 8, encoder_sequence_length_out, 64]present.4.encoder.valuefloat32 [batch_size, 8, encoder_sequence_length_out, 64]present.5.decoder.keyfloat32 [batch_size, 8, past_decoder_sequence_length + 1, 64]present.5.decoder.valuefloat32 [batch_size, 8, past_decoder_sequence_length + 1, 64]present.5.encoder.keyfloat32 [batch_size, 8, encoder_sequence_length_out, 64]present.5.encoder.valuefloat32 [batch_size, 8, encoder_sequence_length_out, 64]
onnx/encoder_model.onnx
Inputs
input_idsint64 [batch_size, encoder_sequence_length]attention_maskint64 [batch_size, encoder_sequence_length]
Outputs
last_hidden_statefloat32 [batch_size, encoder_sequence_length, 512]
Use it in an app declaration · SDK loader · transformers.js · URLs · Hugging Face — generated from this entry
Add to the body of POST /v1/apps/{slug}/releases (or a release session). An unknown or withdrawn model is refused with a 400 naming it; the app page then shows "downloads 404 MB · runs on your device" and /models.txt carries the attribution.
{
"models": [
{
"id": "opus-mt-en-fr-fp32",
"revision": "28726206"
}
]
} Streams with progress, verifies the SHA-256 against the catalogue, keeps a durable copy in the app's Cache API and reports whether the bytes came from cache or the network.
<script src="/sdk.js"></script> <!-- vendored from https://skillsafe.ai/apps-sdk/v1.js -->
const ss = SkillSafe.init({ slug: "your-app" });
const caps = await ss.models.capabilities(); // { webgpu, wasm, wasmSimd, storage }
const files = await ss.models.loadAll("opus-mt-en-fr-fp32@28726206", {
onProgress: (p) => (bar.style.width = Math.round(p.ratio * 100) + "%"),
}); // { path: ArrayBuffer } — SHA-256 verified, cached
const decoder_model_merged = files["onnx/decoder_model_merged.onnx"];
const encoder_model = files["onnx/encoder_model.onnx"];
// Later visits: await ss.models.status("opus-mt-en-fr-fp32@28726206") → { cached: [...], missing: [] }
// Background prefetch on a landing page: ss.models.warm("opus-mt-en-fr-fp32@28726206") The runtime itself ships in your bundle as a vetted {path, sha256} reference — only the weights come from the registry. Pass the bytes in; do not re-fetch by URL.
import { env, pipeline } from "./transformers.min.js";
env.allowLocalModels = true;
env.localModelPath = "/models/"; // ship config.json + tokenizer.json at /models/opus-mt-en-fr-fp32/
env.allowRemoteModels = true; // only the weights fall through to the registry
env.remoteHost = "https://models.skillsafe.ai/";
env.remotePathTemplate = "{model}@{revision}/"; // → https://models.skillsafe.ai/opus-mt-en-fr-fp32@28726206/onnx/decoder_model_merged.onnx
const pipe = await pipeline("translation", "opus-mt-en-fr-fp32", {
revision: "28726206",
dtype: "fp32",
device: "webgpu",
}); Immutable, credential-free, Access-Control-Allow-Origin: *, range requests honoured. The host refuses requests carrying a foreign Origin; a plain curl is fine.
https://models.skillsafe.ai/opus-mt-en-fr-fp32@28726206/onnx/decoder_model_merged.onnx
https://models.skillsafe.ai/opus-mt-en-fr-fp32@28726206/onnx/encoder_model.onnx curl -sSL -o decoder_model_merged.onnx "https://models.skillsafe.ai/opus-mt-en-fr-fp32@28726206/onnx/decoder_model_merged.onnx"
shasum -a 256 decoder_model_merged.onnx # d87fdd0e4f16e9ea2b58148624bf58ed64cec7c2c0e8d87bb82f4142b80bad57 SkillSafe's copy on Hugging Face — the same bytes (the SHA-256 on each file page matches the hash above), plus manifest.json with the upstream commit, recipe and toolchain, the parity reference tensors and a model card. Use it directly for your own site or notebook. A SkillSafe app must load from the registry: its CSP allows models.skillsafe.ai only, and that copy is what the SDK verifies, the browser shares across apps and the app page discloses. SkillSafe keeps hosting its own copies and the API either way.
// Outside SkillSafe apps only: app hosts allow connect-src models.skillsafe.ai, not huggingface.co.
import { pipeline } from "@huggingface/transformers";
const pipe = await pipeline("translation", "skillsafe-ai/opus-mt-en-fr", { revision: "4f304696f52fb0d9228acbb00f125666f455a20a", device: "webgpu" }); https://huggingface.co/skillsafe-ai/opus-mt-en-fr/resolve/4f304696f52fb0d9228acbb00f125666f455a20a/onnx/decoder_model_merged.onnx
https://huggingface.co/skillsafe-ai/opus-mt-en-fr/resolve/4f304696f52fb0d9228acbb00f125666f455a20a/onnx/encoder_model.onnx Evaluation
onnx.checker + onnxruntime CPU smoke run with zero-filled inputs at the declared shapes; per-file SHA-256 pinned to the source — imported as published upstream, then checked. Evaluated Sep 22, 2026.
Runs under onnxruntime
Zero-filled inputs at the declared shapes, CPU execution provider on the converter host; the check is that the graph loads, runs, and emits the declared output shapes.
onnx/decoder_model_merged.onnx: encoder_attention_mask[1,8], input_ids[1,4], encoder_hidden_states[1,8,512], past_key_values.0.decoder.key[1,8,1,64], past_key_values.0.decoder.value[1,8,1,64], past_key_values.0.encoder.key[1,8,8,64], past_key_values.0.encoder.value[1,8,8,64], past_key_values.1.decoder.key[1,8,1,64], past_key_values.1.decoder.value[1,8,1,64], past_key_values.1.encoder.key[1,8,8,64], past_key_values.1.encoder.value[1,8,8,64], past_key_values.2.decoder.key[1,8,1,64], past_key_values.2.decoder.value[1,8,1,64], past_key_values.2.encoder.key[1,8,8,64], past_key_values.2.encoder.value[1,8,8,64], past_key_values.3.decoder.key[1,8,1,64], past_key_values.3.decoder.value[1,8,1,64], past_key_values.3.encoder.key[1,8,8,64], past_key_values.3.encoder.value[1,8,8,64], past_key_values.4.decoder.key[1,8,1,64], past_key_values.4.decoder.value[1,8,1,64], past_key_values.4.encoder.key[1,8,8,64], past_key_values.4.encoder.value[1,8,8,64], past_key_values.5.decoder.key[1,8,1,64], past_key_values.5.decoder.value[1,8,1,64], past_key_values.5.encoder.key[1,8,8,64], past_key_values.5.encoder.value[1,8,8,64], use_cache_branch[1] → logits[1,4,59514], present.0.decoder.key[1,8,4,64], present.0.decoder.value[1,8,4,64], present.0.encoder.key[1,8,8,64], present.0.encoder.value[1,8,8,64], present.1.decoder.key[1,8,4,64], present.1.decoder.value[1,8,4,64], present.1.encoder.key[1,8,8,64], present.1.encoder.value[1,8,8,64], present.2.decoder.key[1,8,4,64], present.2.decoder.value[1,8,4,64], present.2.encoder.key[1,8,8,64], present.2.encoder.value[1,8,8,64], present.3.decoder.key[1,8,4,64], present.3.decoder.value[1,8,4,64], present.3.encoder.key[1,8,8,64], present.3.encoder.value[1,8,8,64], present.4.decoder.key[1,8,4,64], present.4.decoder.value[1,8,4,64], present.4.encoder.key[1,8,8,64], present.4.encoder.value[1,8,8,64], present.5.decoder.key[1,8,4,64], present.5.decoder.value[1,8,4,64], present.5.encoder.key[1,8,8,64], present.5.encoder.value[1,8,8,64] 3.8 msonnx/decoder_model_merged_quantized.onnx: encoder_attention_mask[1,8], input_ids[1,4], encoder_hidden_states[1,8,512], past_key_values.0.decoder.key[1,8,1,64], past_key_values.0.decoder.value[1,8,1,64], past_key_values.0.encoder.key[1,8,8,64], past_key_values.0.encoder.value[1,8,8,64], past_key_values.1.decoder.key[1,8,1,64], past_key_values.1.decoder.value[1,8,1,64], past_key_values.1.encoder.key[1,8,8,64], past_key_values.1.encoder.value[1,8,8,64], past_key_values.2.decoder.key[1,8,1,64], past_key_values.2.decoder.value[1,8,1,64], past_key_values.2.encoder.key[1,8,8,64], past_key_values.2.encoder.value[1,8,8,64], past_key_values.3.decoder.key[1,8,1,64], past_key_values.3.decoder.value[1,8,1,64], past_key_values.3.encoder.key[1,8,8,64], past_key_values.3.encoder.value[1,8,8,64], past_key_values.4.decoder.key[1,8,1,64], past_key_values.4.decoder.value[1,8,1,64], past_key_values.4.encoder.key[1,8,8,64], past_key_values.4.encoder.value[1,8,8,64], past_key_values.5.decoder.key[1,8,1,64], past_key_values.5.decoder.value[1,8,1,64], past_key_values.5.encoder.key[1,8,8,64], past_key_values.5.encoder.value[1,8,8,64], use_cache_branch[1] → logits[1,4,59514], present.0.decoder.key[1,8,4,64], present.0.decoder.value[1,8,4,64], present.0.encoder.key[1,8,8,64], present.0.encoder.value[1,8,8,64], present.1.decoder.key[1,8,4,64], present.1.decoder.value[1,8,4,64], present.1.encoder.key[1,8,8,64], present.1.encoder.value[1,8,8,64], present.2.decoder.key[1,8,4,64], present.2.decoder.value[1,8,4,64], present.2.encoder.key[1,8,8,64], present.2.encoder.value[1,8,8,64], present.3.decoder.key[1,8,4,64], present.3.decoder.value[1,8,4,64], present.3.encoder.key[1,8,8,64], present.3.encoder.value[1,8,8,64], present.4.decoder.key[1,8,4,64], present.4.decoder.value[1,8,4,64], present.4.encoder.key[1,8,8,64], present.4.encoder.value[1,8,8,64], present.5.decoder.key[1,8,4,64], present.5.decoder.value[1,8,4,64], present.5.encoder.key[1,8,8,64], present.5.encoder.value[1,8,8,64] 5.9 msonnx/encoder_model.onnx: input_ids[1,8], attention_mask[1,8] → last_hidden_state[1,8,512] 1.4 ms
Toolchain: python 3.12.13 · platform Darwin 25.6.0 arm64 · torch 2.10.0 · onnx 1.23.0 · onnxruntime 1.30.0. Recipe models/recipes/opus-mt-en-fr.yaml (002d21bcda3c). Full manifest.json
Licence & attribution
Apache-2.0 · licence text · notice
opus-mt-en-fr: Helsinki-NLP (Jörg Tiedemann, University of Helsinki), Apache-2.0, trained on OPUS data (https://huggingface.co/Helsinki-NLP/opus-mt-en-fr); ONNX export by Xenova (https://huggingface.co/Xenova/opus-mt-en-fr).
Apps that declare this model get this text in their generated /models.txt, so a licence that requires a notice always carries one.
Every file here was approved by exact SHA-256 after a structural audit of the graph, fetched from a content-pinned source, and is served credential-free at an immutable URL. The SDK re-verifies the hash on your device before it caches or returns anything. Missing a variant? Request it — or read how the registry works.