UNPKG

@huggingface/transformers

Version:

State-of-the-art Machine Learning for the web. Run 🤗 Transformers directly in your browser, with no need for a server!

536 lines (504 loc) • 20.4 kB
/** * @file Helper module for using model configs. For more information, see the corresponding * [Python documentation](https://huggingface.co/docs/transformers/main/en/model_doc/auto#transformers.AutoConfig). * * **Example:** Load an `AutoConfig`. * * ```javascript * import { AutoConfig } from '@huggingface/transformers'; * const config = await AutoConfig.from_pretrained('bert-base-uncased'); * console.log(config); * // PretrainedConfig { * // "model_type": "bert", * // "is_encoder_decoder": false, * // "architectures": [ * // "BertForMaskedLM" * // ], * // "vocab_size": 30522 * // "num_attention_heads": 12, * // "num_hidden_layers": 12, * // "hidden_size": 768, * // "max_position_embeddings": 512, * // ... * // } * ``` * * @module configs */ import { pick } from './utils/core.js'; import { getModelJSON } from './utils/hub.js'; /** * @typedef {import('./utils/hub.js').PretrainedOptions} PretrainedOptions */ /** * @typedef {import('./utils/core.js').ProgressCallback} ProgressCallback */ /** * @typedef {import('./utils/core.js').ProgressInfo} ProgressInfo */ /** * Loads a config from the specified path. * @param {string} pretrained_model_name_or_path The path to the config directory. * @param {PretrainedOptions} options Additional options for loading the config. * @returns {Promise<Object>} A promise that resolves with information about the loaded config. */ async function loadConfig(pretrained_model_name_or_path, options) { return await getModelJSON(pretrained_model_name_or_path, 'config.json', true, options); } /** * * @param {PretrainedConfig} config * @returns {Object} The normalized configuration. */ function getNormalizedConfig(config) { const mapping = {}; let init_normalized_config = {}; switch (config.model_type) { // Sub-configs case 'llava': case 'paligemma': case 'gemma3': case 'florence2': case 'llava_onevision': case 'idefics3': case 'granite_speech': case 'ultravox': case 'voxtral': case 'voxtral_realtime': case 'smolvlm': case 'gemma3n': case 'gemma4': case 'lfm2_vl': case 'chatterbox': case 'lighton_ocr': case 'glm_ocr': case 'mistral3': case 'qwen2_5_vl': case 'qwen3_vl': case 'qwen3_vl_moe': // @ts-expect-error TS2339 init_normalized_config = getNormalizedConfig(config.text_config); break; case 'moondream1': // @ts-expect-error TS2339 init_normalized_config = getNormalizedConfig(config.phi_config); break; case 'musicgen': // @ts-expect-error TS2339 init_normalized_config = getNormalizedConfig(config.decoder); break; case 'multi_modality': // @ts-expect-error TS2339 init_normalized_config = getNormalizedConfig(config.language_config); break; // Decoder-only models case 'gpt2': case 'gptj': case 'jais': case 'codegen': case 'gpt_bigcode': mapping['num_heads'] = 'n_head'; mapping['num_layers'] = 'n_layer'; mapping['hidden_size'] = 'n_embd'; break; case 'gpt_neox': case 'stablelm': case 'opt': case 'falcon': case 'modernbert-decoder': mapping['num_heads'] = 'num_attention_heads'; mapping['num_layers'] = 'num_hidden_layers'; mapping['hidden_size'] = 'hidden_size'; break; case 'gpt_oss': case 'llama': case 'llama4_text': case 'nanochat': case 'apertus': case 'arcee': case 'afmoe': case 'lfm2': case 'lfm2_moe': case 'smollm3': case 'olmo': case 'olmo2': case 'olmo3': case 'mobilellm': case 'granite': case 'granitemoehybrid': case 'cohere': case 'cohere2': case 'mistral': case 'voxtral_realtime_text': case 'voxtral_realtime_encoder': case 'starcoder2': case 'qwen2': case 'qwen2_moe': case 'qwen2_vl': case 'qwen2_vl_text': case 'qwen2_5_vl_text': case 'qwen3_moe': case 'qwen3_vl_text': case 'qwen3_vl_moe_text': case 'phi': case 'phi3': case 'phi3_v': case 'llava_qwen2': mapping['num_heads'] = 'num_key_value_heads'; mapping['num_layers'] = 'num_hidden_layers'; mapping['hidden_size'] = 'hidden_size'; mapping['num_attention_heads'] = 'num_attention_heads'; mapping['dim_kv'] = 'head_dim'; break; case 'qwen3': case 'solar_open': case 'glm_ocr_text': case 'gemma': case 'gemma2': case 'vaultgemma': case 'gemma3_text': case 'gemma3n_text': case 'gemma4_text': case 'glm': case 'helium': case 'ernie4_5': case 'hunyuan_v1_dense': case 'falcon_h1': case 'nemotron_h': case 'ministral': case 'ministral3': mapping['num_heads'] = 'num_key_value_heads'; mapping['num_layers'] = 'num_hidden_layers'; mapping['dim_kv'] = 'head_dim'; break; case 'openelm': mapping['num_heads'] = 'num_kv_heads'; mapping['num_layers'] = 'num_transformer_layers'; mapping['dim_kv'] = 'head_dim'; break; case 'gpt_neo': case 'donut-swin': mapping['num_heads'] = 'num_heads'; mapping['num_layers'] = 'num_layers'; mapping['hidden_size'] = 'hidden_size'; break; case 'bloom': mapping['num_heads'] = 'n_head'; mapping['num_layers'] = 'n_layer'; mapping['hidden_size'] = 'hidden_size'; break; case 'mpt': mapping['num_heads'] = 'n_heads'; mapping['num_layers'] = 'n_layers'; mapping['hidden_size'] = 'd_model'; break; case 'exaone': mapping['num_heads'] = 'num_key_value_heads'; mapping['num_layers'] = 'num_layers'; mapping['dim_kv'] = 'head_dim'; mapping['num_attention_heads'] = 'num_attention_heads'; break; case 'youtu': case 'deepseek_v3': case 'glm_moe_dsa': case 'mistral4': mapping['num_heads'] = 'num_key_value_heads'; mapping['num_layers'] = 'num_hidden_layers'; mapping['dim_kv'] = 'qk_head_dim'; mapping['num_attention_heads'] = 'num_attention_heads'; break; // Encoder-decoder models case 't5': case 'mt5': case 'longt5': mapping['num_decoder_layers'] = 'num_decoder_layers'; mapping['num_decoder_heads'] = 'num_heads'; mapping['decoder_dim_kv'] = 'd_kv'; mapping['num_encoder_layers'] = 'num_layers'; mapping['num_encoder_heads'] = 'num_heads'; mapping['encoder_dim_kv'] = 'd_kv'; break; case 'bart': case 'mbart': case 'marian': case 'whisper': case 'lite-whisper': case 'm2m_100': case 'blenderbot': case 'blenderbot-small': case 'florence2_language': mapping['num_decoder_layers'] = 'decoder_layers'; mapping['num_decoder_heads'] = 'decoder_attention_heads'; mapping['decoder_hidden_size'] = 'd_model'; mapping['num_encoder_layers'] = 'encoder_layers'; mapping['num_encoder_heads'] = 'encoder_attention_heads'; mapping['encoder_hidden_size'] = 'd_model'; break; case 'speecht5': mapping['num_decoder_layers'] = 'decoder_layers'; mapping['num_decoder_heads'] = 'decoder_attention_heads'; mapping['decoder_hidden_size'] = 'hidden_size'; mapping['num_encoder_layers'] = 'encoder_layers'; mapping['num_encoder_heads'] = 'encoder_attention_heads'; mapping['encoder_hidden_size'] = 'hidden_size'; break; case 'trocr': mapping['num_encoder_layers'] = mapping['num_decoder_layers'] = 'decoder_layers'; mapping['num_encoder_heads'] = mapping['num_decoder_heads'] = 'decoder_attention_heads'; mapping['encoder_hidden_size'] = mapping['decoder_hidden_size'] = 'd_model'; break; case 'musicgen_decoder': mapping['num_encoder_layers'] = mapping['num_decoder_layers'] = 'num_hidden_layers'; mapping['num_encoder_heads'] = mapping['num_decoder_heads'] = 'num_attention_heads'; mapping['encoder_hidden_size'] = mapping['decoder_hidden_size'] = 'hidden_size'; break; case 'moonshine': mapping['num_decoder_layers'] = 'decoder_num_hidden_layers'; mapping['num_decoder_heads'] = 'decoder_num_key_value_heads'; mapping['num_encoder_layers'] = 'encoder_num_hidden_layers'; mapping['num_encoder_heads'] = 'encoder_num_key_value_heads'; mapping['encoder_hidden_size'] = mapping['decoder_hidden_size'] = 'hidden_size'; break; case 'cohere_asr': mapping['num_decoder_layers'] = 'num_hidden_layers'; mapping['num_decoder_heads'] = 'num_key_value_heads'; mapping['decoder_hidden_size'] = 'hidden_size'; mapping['decoder_dim_kv'] = 'head_dim'; const { num_hidden_layers: num_encoder_layers, num_attention_heads: num_encoder_heads, hidden_size: encoder_hidden_size, } = /** @type {any} */ (config).encoder_config; init_normalized_config = { num_encoder_layers, num_encoder_heads, encoder_hidden_size, // @ts-expect-error TS2339 encoder_dim_kv: config.head_dim, }; break; case 'vision-encoder-decoder': // @ts-expect-error TS2339 const decoderConfig = getNormalizedConfig(config.decoder); const add_encoder_pkv = 'num_decoder_layers' in decoderConfig; const result = pick(config, ['model_type', 'is_encoder_decoder']); if (add_encoder_pkv) { // Decoder is part of an encoder-decoder model result.num_decoder_layers = decoderConfig.num_decoder_layers; result.num_decoder_heads = decoderConfig.num_decoder_heads; result.decoder_hidden_size = decoderConfig.decoder_hidden_size; result.num_encoder_layers = decoderConfig.num_encoder_layers; result.num_encoder_heads = decoderConfig.num_encoder_heads; result.encoder_hidden_size = decoderConfig.encoder_hidden_size; } else { // Decoder is a decoder-only model result.num_layers = decoderConfig.num_layers; result.num_heads = decoderConfig.num_heads; result.hidden_size = decoderConfig.hidden_size; } return result; } // NOTE: If `num_attention_heads` is not set, it is assumed to be equal to `num_heads` const normalized_config = { ...init_normalized_config, ...pick(config, ['model_type', 'multi_query', 'is_encoder_decoder']), }; for (const key in mapping) { normalized_config[key] = config[mapping[key]]; } return normalized_config; } /** * @param {PretrainedConfig} config * @param {{ prefix?: string, session_name?: string }} [options] * @returns {Set<string>} */ export function getCacheNames(config, options) { if (!(config instanceof PretrainedConfig)) { config = new PretrainedConfig(config); } const pkv_prefix = options?.prefix ?? 'past_key_values'; const conv_prefix = pkv_prefix === 'present' ? 'present' : 'past'; /** @type {Set<string>} */ const names = new Set(); if (['lfm2', 'lfm2_moe'].includes(config.model_type)) { const { layer_types } = /** @type {any} */ (config); for (let i = 0; i < layer_types.length; ++i) { if (layer_types[i] === 'full_attention') { names.add(`${pkv_prefix}.${i}.key`); names.add(`${pkv_prefix}.${i}.value`); } else if (layer_types[i] === 'conv') { names.add(`${conv_prefix}_conv.${i}`); } else { throw new Error(`Unsupported layer type: ${layer_types[i]}`); } } return names; } else if (['granitemoehybrid', 'falcon_h1', 'nemotron_h'].includes(config.model_type)) { const c = /** @type {any} */ (config); const layer_types = c.layer_types ?? c.layers_block_type; const num_layers = c.num_hidden_layers ?? layer_types?.length; for (let i = 0; i < num_layers; ++i) { if (!layer_types || layer_types[i] === 'mamba') { names.add(`${conv_prefix}_conv.${i}`); names.add(`${conv_prefix}_ssm.${i}`); } if (!layer_types || layer_types[i] === 'attention') { names.add(`${pkv_prefix}.${i}.key`); names.add(`${pkv_prefix}.${i}.value`); } } return names; } else if (['qwen3_next', 'qwen3_5_text', 'qwen3_5_moe_text', 'olmo_hybrid'].includes(config.model_type)) { const { layer_types } = /** @type {any} */ (config); for (let i = 0; i < layer_types.length; ++i) { if (layer_types[i] === 'full_attention') { names.add(`${pkv_prefix}.${i}.key`); names.add(`${pkv_prefix}.${i}.value`); } else if (layer_types[i] === 'linear_attention') { if (config.model_type === 'olmo_hybrid') { names.add(`${conv_prefix}_conv.${i}.key`); names.add(`${conv_prefix}_conv.${i}.value`); names.add(`${conv_prefix}_conv.${i}.query`); } else { names.add(`${conv_prefix}_conv.${i}`); } names.add(`${conv_prefix}_recurrent.${i}`); } else { throw new Error(`Unsupported layer type: ${layer_types[i]}`); } } return names; } else if (['gemma4', 'gemma4_text'].includes(config.model_type)) { const c = /** @type {any} */ ( config.model_type === 'gemma4' ? /** @type {any} */ (config).text_config : config ); const num_hidden_layers = c.num_hidden_layers; const num_kv_shared_layers = c.num_kv_shared_layers ?? 0; const num_kv_layers = num_hidden_layers - num_kv_shared_layers; for (let i = 0; i < num_kv_layers; ++i) { names.add(`${pkv_prefix}.${i}.key`); names.add(`${pkv_prefix}.${i}.value`); } return names; } else if (['lfm2_vl', 'qwen3_5', 'qwen3_5_moe', 'voxtral_realtime'].includes(config.model_type)) { let subConfig; if (config.model_type === 'voxtral_realtime' && options?.session_name === 'audio_encoder') { subConfig = /** @type {any} */ (config).audio_config; } else { subConfig = /** @type {any} */ (config).text_config; } return getCacheNames(subConfig, options); } return getKeyValueNames(config, { prefix: pkv_prefix }); } /** * @param {PretrainedConfig} config * @param {{ prefix?: string }} [options] * @returns {Set<string>} */ function getKeyValueNames(config, { prefix = 'past_key_values' } = {}) { /** @type {Set<string>} */ const names = new Set(); const normalized_config = config.normalized_config; if ( normalized_config.is_encoder_decoder && 'num_encoder_heads' in normalized_config && 'num_decoder_heads' in normalized_config ) { for (let i = 0; i < normalized_config.num_decoder_layers; ++i) { names.add(`${prefix}.${i}.encoder.key`); names.add(`${prefix}.${i}.encoder.value`); names.add(`${prefix}.${i}.decoder.key`); names.add(`${prefix}.${i}.decoder.value`); } } else if (normalized_config.multi_query) { // e.g., for `gpt_bigcode` for (let i = 0; i < normalized_config.num_layers; ++i) { names.add(`${prefix}.${i}.key_value`); } } else { for (let i = 0; i < normalized_config.num_layers; ++i) { names.add(`${prefix}.${i}.key`); names.add(`${prefix}.${i}.value`); } } return names; } /** * Base class for all configuration classes. For more information, see the corresponding * [Python documentation](https://huggingface.co/docs/transformers/main/en/main_classes/configuration#transformers.PretrainedConfig). */ export class PretrainedConfig { // NOTE: Typo in original /** @type {string|null} */ model_type = null; /** @type {boolean} */ is_encoder_decoder = false; /** @type {number} */ max_position_embeddings; /** @type {TransformersJSConfig} */ 'transformers.js_config'; /** * Create a new PreTrainedTokenizer instance. * @param {Object} configJSON The JSON of the config. */ constructor(configJSON) { Object.assign(this, configJSON); this.normalized_config = getNormalizedConfig(this); } /** * Loads a pre-trained config from the given `pretrained_model_name_or_path`. * * @param {string} pretrained_model_name_or_path The path to the pre-trained config. * @param {PretrainedOptions} options Additional options for loading the config. * @throws {Error} Throws an error if the config.json is not found in the `pretrained_model_name_or_path`. * * @returns {Promise<PretrainedConfig>} A new instance of the `PretrainedConfig` class. */ static async from_pretrained( pretrained_model_name_or_path, { progress_callback = null, config = null, cache_dir = null, local_files_only = false, revision = 'main' } = {}, ) { if (config && !(config instanceof PretrainedConfig)) { config = new PretrainedConfig(config); } const data = config ?? (await loadConfig(pretrained_model_name_or_path, { progress_callback, config, cache_dir, local_files_only, revision, })); return new this(data); } } /** * Helper class which is used to instantiate pretrained configs with the `from_pretrained` function. * * @example * const config = await AutoConfig.from_pretrained('Xenova/bert-base-uncased'); */ export class AutoConfig { /** @type {typeof PretrainedConfig.from_pretrained} */ static async from_pretrained(...args) { return PretrainedConfig.from_pretrained(...args); } } /** * Transformers.js-specific configuration, possibly present in config.json under the key `transformers.js_config`. * @typedef {Object} TransformersJSConfig * @property {Record<import('./utils/devices.js').DeviceType, DeviceConfig>} [device_config] Device-specific configurations. * @property {Record<string, number>} [free_dimension_overrides] Override the free dimensions of the model. * See https://onnxruntime.ai/docs/tutorials/web/env-flags-and-session-options.html#freedimensionoverrides * for more information. * @property {import('./utils/devices.js').DeviceType} [device] The default device to use for the model. * @property {import('./utils/dtypes.js').DataType|Record<string, import('./utils/dtypes.js').DataType>} [dtype] The default data type to use for the model. * @property {import('./utils/hub.js').ExternalData|Record<string, import('./utils/hub.js').ExternalData>} [use_external_data_format=false] Whether to load the model using the external data format (used for models >= 2GB in size). */ /** * Device-specific configuration options. * @typedef {Omit<TransformersJSConfig, "device" | "device_config">} DeviceConfig */