candding 0.1.0

Pure candle embeddings for Rust with no ONNX: dense, sparse, multi-vector and rerank models today, image models on the roadmap
Documentation
use super::*;

/// `jinaai/jina-colbert-v2`'s `config.json` byte for byte, extra fields included; it has no `model_type`.
const JINA_COLBERT_V2: &str = r#"{
  "_name_or_path": "jinaai/jina-embeddings-v3-xlm-large-pairs-vol5",
  "architectures": ["HF_ColBERT"],
  "attention_probs_dropout_prob": 0.1,
  "auto_map": {
    "AutoConfig": "jinaai/xlm-roberta-flash-implementation--configuration_xlm_roberta.XLMRobertaFlashConfig",
    "AutoModel": "jinaai/xlm-roberta-flash-implementation--modeling_xlm_roberta.XLMRobertaModel"
  },
  "bos_token_id": 0,
  "classifier_dropout": null,
  "emb_pooler": null,
  "eos_token_id": 2,
  "hidden_act": "gelu",
  "hidden_dropout_prob": 0.1,
  "hidden_size": 1024,
  "initializer_range": 0.02,
  "intermediate_size": 4096,
  "layer_norm_eps": 1e-05,
  "load_trained_adapters": false,
  "lora_adaptations": null,
  "lora_alpha": 1,
  "lora_rank": 4,
  "matryoshka_dimensions": null,
  "max_position_embeddings": 8194,
  "num_attention_heads": 16,
  "num_hidden_layers": 24,
  "output_past": true,
  "pad_token_id": 1,
  "position_embedding_type": "rotary",
  "rotary_emb_base": 10000.0,
  "torch_dtype": "bfloat16",
  "transformers_version": "4.43.2",
  "truncate_dim": null,
  "type_vocab_size": 1,
  "use_cache": true,
  "use_flash_attn": true,
  "vocab_size": 250004
}"#;

#[test]
fn reads_the_checkpoints_own_config_json() {
  let config: XlmRobertaFlashConfig = serde_json::from_str(JINA_COLBERT_V2).unwrap();
  assert_eq!(config.vocab_size, 250_004);
  assert_eq!(config.hidden_size, 1024);
  assert_eq!(config.num_hidden_layers, 24);
  assert_eq!(config.num_attention_heads, 16);
  assert_eq!(config.intermediate_size, 4096);
  assert_eq!(config.type_vocab_size, 1);
  assert_eq!(config.max_position_embeddings, 8194);
  assert_eq!(config.layer_norm_eps, 1e-5);
  assert_eq!(config.position_embedding_type.as_deref(), Some("rotary"));
  assert_eq!(config.pad_token_id, 1);
  assert_eq!(config.rotary_emb_base, 10_000.0);
  assert_eq!(config.hidden_act, Activation::Gelu);
  assert_eq!(config.head_dim(), 64);
  assert!(
    config.lora_adaptations.is_none(),
    "the published file names null"
  );
  assert!(
    !config.load_trained_adapters,
    "the published file names false"
  );
  assert!(
    config.matryoshka_dimensions.is_none(),
    "the published file names null"
  );
  config.ensure_supported().unwrap();
}

/// `jinaai/jina-reranker-v2-base-multilingual`'s `config.json` at `9cfeff2df7d40d1b78e75e5e9cebec92a99813c9`.
const JINA_RERANKER_V2: &str = r#"{
  "_name_or_path": "jinaai/jina-reranker-v2-base-multilingual",
  "architectures": ["XLMRobertaForSequenceClassification"],
  "attention_probs_dropout_prob": 0.1,
  "auto_map": {
    "AutoConfig": "configuration_xlm_roberta.XLMRobertaFlashConfig",
    "AutoModel": "modeling_xlm_roberta.XLMRobertaModel",
    "AutoModelForSequenceClassification": "modeling_xlm_roberta.XLMRobertaForSequenceClassification"
  },
  "bos_token_id": 0,
  "classifier_dropout": null,
  "emb_pooler": null,
  "eos_token_id": 2,
  "hidden_act": "gelu",
  "hidden_dropout_prob": 0.1,
  "hidden_size": 768,
  "num_labels": 1,
  "id2label": {
    "0": "LABEL_0"
  },
  "initializer_range": 0.02,
  "intermediate_size": 3072,
  "label2id": {
    "LABEL_0": 0
  },
  "layer_norm_eps": 1e-5,
  "max_position_embeddings": 1026,
  "num_attention_heads": 12,
  "num_hidden_layers": 12,
  "output_past": true,
  "pad_token_id": 1,
  "position_embedding_type": "absolute",
  "torch_dtype": "bfloat16",
  "transformers_version": "4.40.0",
  "type_vocab_size": 1,
  "use_cache": false,
  "use_flash_attn": true,
  "vocab_size": 250002
}"#;

/// 1026 rows numbered from `pad_token_id + 1 = 2` leave 1024 positions; the reference raises `IndexError` at the 1025th.
#[test]
fn reads_the_rerankers_own_config_json() {
  let config: XlmRobertaFlashConfig = serde_json::from_str(JINA_RERANKER_V2).unwrap();
  assert_eq!(config.vocab_size, 250_002);
  assert_eq!(config.hidden_size, 768);
  assert_eq!(config.num_hidden_layers, 12);
  assert_eq!(config.num_attention_heads, 12);
  assert_eq!(config.intermediate_size, 3072);
  assert_eq!(config.type_vocab_size, 1);
  assert_eq!(config.max_position_embeddings, 1026);
  assert_eq!(config.layer_norm_eps, 1e-5);
  assert_eq!(config.position_embedding_type.as_deref(), Some("absolute"));
  assert_eq!(config.pad_token_id, 1);
  assert_eq!(config.position_offset(), 2);
  assert_eq!(config.sequence_limit(), 1024);
  assert_eq!(config.head_dim(), 64);
  config.ensure_supported().unwrap();
}

#[test]
fn rotary_and_absolute_are_supported_and_every_other_scheme_is_refused() {
  let mut config: XlmRobertaFlashConfig = serde_json::from_str(JINA_COLBERT_V2).unwrap();
  for supported in ["rotary", "absolute"] {
    config.position_embedding_type = Some(supported.to_string());
    config.ensure_supported().unwrap();
  }
  for other in ["alibi", "relative_key"] {
    config.position_embedding_type = Some(other.to_string());
    let err = config.ensure_supported().unwrap_err();
    assert!(
      matches!(err, CanddingError::UnsupportedArchitecture(ref m) if m.contains(other)),
      "{err}"
    );
  }
}

/// A `jinaai/jina-embeddings-v3` sibling with LoRA adapters still says `"rotary"`, so the position check alone would pass it.
#[test]
fn a_declared_lora_adaptation_is_refused_even_with_rotary_positions() {
  let mut config: XlmRobertaFlashConfig = serde_json::from_str(JINA_COLBERT_V2).unwrap();
  assert_eq!(config.position_embedding_type.as_deref(), Some("rotary"));
  config.lora_adaptations = Some(serde_json::json!(["adapter"]));
  let err = config.ensure_supported().unwrap_err();
  assert!(
    matches!(err, CanddingError::UnsupportedArchitecture(_)),
    "{err}"
  );
}

/// Either LoRA field alone means the plain tensor read would skip a step the config says to take.
#[test]
fn load_trained_adapters_true_is_refused_on_its_own() {
  let mut config: XlmRobertaFlashConfig = serde_json::from_str(JINA_COLBERT_V2).unwrap();
  assert!(config.lora_adaptations.is_none());
  config.load_trained_adapters = true;
  let err = config.ensure_supported().unwrap_err();
  assert!(
    matches!(err, CanddingError::UnsupportedArchitecture(_)),
    "{err}"
  );
}

#[test]
fn declared_matryoshka_dimensions_are_refused() {
  let mut config: XlmRobertaFlashConfig = serde_json::from_str(JINA_COLBERT_V2).unwrap();
  config.matryoshka_dimensions = Some(serde_json::json!([64, 128, 256]));
  let err = config.ensure_supported().unwrap_err();
  assert!(
    matches!(err, CanddingError::UnsupportedArchitecture(_)),
    "{err}"
  );
}

/// `position_embedding_type` is read as absent rather than defaulted: the two implementation repositories default it differently.
#[test]
fn the_defaultable_fields_match_the_upstream_config_class_and_the_scheme_has_no_default() {
  let stripped = JINA_COLBERT_V2
    .replace("\"layer_norm_eps\": 1e-05,", "")
    .replace("\"pad_token_id\": 1,", "")
    .replace("\"position_embedding_type\": \"rotary\",", "")
    .replace("\"rotary_emb_base\": 10000.0,", "")
    .replace("\"hidden_act\": \"gelu\",", "");
  assert!(!stripped.contains("position_embedding_type"), "{stripped}");
  let config: XlmRobertaFlashConfig = serde_json::from_str(&stripped).unwrap();
  assert_eq!(config.layer_norm_eps, 1e-5);
  assert_eq!(config.pad_token_id, 1);
  assert_eq!(config.position_embedding_type, None);
  assert_eq!(config.rotary_emb_base, 10_000.0);
  assert_eq!(config.hidden_act, Activation::Gelu);
  config.ensure_supported().unwrap();
}