xberg 1.2.5 → 1.2.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,5 +1,5 @@
1
1
  // This file is auto-generated by alef. DO NOT EDIT.
2
- // alef:hash:88d2135df85c592f02269aceb1caa207be8b22378d55c8bd717ffaa3f5359d86
2
+ // alef:hash:f96e7fa37ad41175262a0e7e7e6cc03473ea5e795d7cf76e5118687cbeff2554
3
3
  // Re-generate with: alef generate
4
4
  #![allow(dead_code, unused_imports, unused_variables)]
5
5
  #![allow(
@@ -6006,8 +6006,12 @@ impl DbfMetadata {
6006
6006
  #[magnus::wrap(class = "Xberg::DeepseekOcrBackendOptions")]
6007
6007
  pub struct DeepseekOcrBackendOptions {
6008
6008
  model_path: Option<String>,
6009
+ model_id: Option<String>,
6010
+ hf_revision: Option<String>,
6011
+ cache_dir: Option<String>,
6009
6012
  device: Option<CandleDevicePreference>,
6010
6013
  version: Option<u32>,
6014
+ dtype: Option<CandleDeepseekOcrDtype>,
6011
6015
  }
6012
6016
 
6013
6017
  #[cfg(feature = "candle-ocr")]
@@ -6074,6 +6078,33 @@ impl DeepseekOcrBackendOptions {
6074
6078
  })?),
6075
6079
  None => None,
6076
6080
  },
6081
+ model_id: match kwargs.get(ruby.to_symbol("model_id")).filter(|v| !v.is_nil()) {
6082
+ Some(v) => Some(String::try_convert(v).map_err(|e| {
6083
+ magnus::Error::new(
6084
+ unsafe { magnus::Ruby::get_unchecked() }.exception_type_error(),
6085
+ format!("invalid value for `model_id`: {}", e),
6086
+ )
6087
+ })?),
6088
+ None => None,
6089
+ },
6090
+ hf_revision: match kwargs.get(ruby.to_symbol("hf_revision")).filter(|v| !v.is_nil()) {
6091
+ Some(v) => Some(String::try_convert(v).map_err(|e| {
6092
+ magnus::Error::new(
6093
+ unsafe { magnus::Ruby::get_unchecked() }.exception_type_error(),
6094
+ format!("invalid value for `hf_revision`: {}", e),
6095
+ )
6096
+ })?),
6097
+ None => None,
6098
+ },
6099
+ cache_dir: match kwargs.get(ruby.to_symbol("cache_dir")).filter(|v| !v.is_nil()) {
6100
+ Some(v) => Some(String::try_convert(v).map_err(|e| {
6101
+ magnus::Error::new(
6102
+ unsafe { magnus::Ruby::get_unchecked() }.exception_type_error(),
6103
+ format!("invalid value for `cache_dir`: {}", e),
6104
+ )
6105
+ })?),
6106
+ None => None,
6107
+ },
6077
6108
  device: match kwargs.get(ruby.to_symbol("device")).filter(|v| !v.is_nil()) {
6078
6109
  Some(v) => Some(CandleDevicePreference::try_convert(v).map_err(|e| {
6079
6110
  magnus::Error::new(
@@ -6092,6 +6123,15 @@ impl DeepseekOcrBackendOptions {
6092
6123
  })?),
6093
6124
  None => None,
6094
6125
  },
6126
+ dtype: match kwargs.get(ruby.to_symbol("dtype")).filter(|v| !v.is_nil()) {
6127
+ Some(v) => Some(CandleDeepseekOcrDtype::try_convert(v).map_err(|e| {
6128
+ magnus::Error::new(
6129
+ unsafe { magnus::Ruby::get_unchecked() }.exception_type_error(),
6130
+ format!("invalid value for `dtype`: {}", e),
6131
+ )
6132
+ })?),
6133
+ None => None,
6134
+ },
6095
6135
  })
6096
6136
  }
6097
6137
 
@@ -6099,6 +6139,18 @@ impl DeepseekOcrBackendOptions {
6099
6139
  self.model_path.clone()
6100
6140
  }
6101
6141
 
6142
+ fn model_id(&self) -> Option<String> {
6143
+ self.model_id.clone()
6144
+ }
6145
+
6146
+ fn hf_revision(&self) -> Option<String> {
6147
+ self.hf_revision.clone()
6148
+ }
6149
+
6150
+ fn cache_dir(&self) -> Option<String> {
6151
+ self.cache_dir.clone()
6152
+ }
6153
+
6102
6154
  fn device(&self) -> Option<CandleDevicePreference> {
6103
6155
  self.device.clone()
6104
6156
  }
@@ -6106,6 +6158,10 @@ impl DeepseekOcrBackendOptions {
6106
6158
  fn version(&self) -> Option<u32> {
6107
6159
  self.version
6108
6160
  }
6161
+
6162
+ fn dtype(&self) -> Option<CandleDeepseekOcrDtype> {
6163
+ self.dtype.clone()
6164
+ }
6109
6165
  }
6110
6166
 
6111
6167
  #[cfg(feature = "api")]
@@ -12691,6 +12747,202 @@ impl ExtractionConfig {
12691
12747
  core_self.needs_image_data()
12692
12748
  }
12693
12749
 
12750
+ fn runs_ocr_on_embedded_images(&self) -> bool {
12751
+ #[allow(clippy::needless_update)]
12752
+ let core_self = xberg::ExtractionConfig {
12753
+ mime_detection_policy: self.mime_detection_policy.clone().into(),
12754
+
12755
+ use_cache: self.use_cache,
12756
+
12757
+ enable_quality_processing: self.enable_quality_processing,
12758
+
12759
+ ocr: self.ocr.clone().map(Into::into),
12760
+
12761
+ force_ocr: self.force_ocr,
12762
+
12763
+ ocr_strategy: self.ocr_strategy.clone().into(),
12764
+
12765
+ force_ocr_pages: self.force_ocr_pages.clone(),
12766
+
12767
+ disable_ocr: self.disable_ocr,
12768
+
12769
+ chunking: self.chunking.clone().map(Into::into),
12770
+
12771
+ content_filter: self.content_filter.clone().map(Into::into),
12772
+
12773
+ images: self.images.clone().map(Into::into),
12774
+
12775
+ token_reduction: self.token_reduction.clone().map(Into::into),
12776
+
12777
+ language_detection: self.language_detection.clone().map(Into::into),
12778
+
12779
+ pages: self.pages.clone().map(Into::into),
12780
+
12781
+ postprocessor: self.postprocessor.clone().map(Into::into),
12782
+
12783
+ extraction_timeout_secs: self.extraction_timeout_secs,
12784
+
12785
+ max_concurrent_extractions: self.max_concurrent_extractions,
12786
+
12787
+ result_format: self.result_format.clone().into(),
12788
+
12789
+ security_limits: self.security_limits.clone().map(Into::into),
12790
+
12791
+ max_embedded_file_bytes: self.max_embedded_file_bytes,
12792
+
12793
+ output_format: self.output_format.clone().into(),
12794
+
12795
+ escape_markdown: self.escape_markdown,
12796
+
12797
+ table_anchors: self.table_anchors,
12798
+
12799
+ jupyter_cell_rendering: self.jupyter_cell_rendering.clone().into(),
12800
+
12801
+ apply_notebook_cell_tags: self.apply_notebook_cell_tags,
12802
+
12803
+ use_layout_for_markdown: self.use_layout_for_markdown,
12804
+
12805
+ include_document_structure: self.include_document_structure,
12806
+
12807
+ acceleration: self.acceleration.clone().map(Into::into),
12808
+
12809
+ cache_namespace: self.cache_namespace.clone(),
12810
+
12811
+ cache_ttl_secs: self.cache_ttl_secs,
12812
+
12813
+ email: self.email.clone().map(Into::into),
12814
+
12815
+ csv: self.csv.clone().map(Into::into),
12816
+
12817
+ geojson: self.geojson.clone().map(Into::into),
12818
+
12819
+ concurrency: self.concurrency.clone().map(Into::into),
12820
+
12821
+ url: self.url.clone().into(),
12822
+
12823
+ max_archive_depth: self.max_archive_depth,
12824
+
12825
+ structured_extraction: self.structured_extraction.clone().map(Into::into),
12826
+
12827
+ ner: self.ner.clone().map(Into::into),
12828
+
12829
+ redaction: self.redaction.clone().map(Into::into),
12830
+
12831
+ summarization: self.summarization.clone().map(Into::into),
12832
+
12833
+ translation: self.translation.clone().map(Into::into),
12834
+
12835
+ page_classification: self.page_classification.clone().map(Into::into),
12836
+
12837
+ chunk_classification: self.chunk_classification.clone().map(Into::into),
12838
+
12839
+ captioning: self.captioning.clone().map(Into::into),
12840
+
12841
+ qr_codes: self.qr_codes,
12842
+
12843
+ ..Default::default()
12844
+ };
12845
+ core_self.runs_ocr_on_embedded_images()
12846
+ }
12847
+
12848
+ fn wants_own_bytes_in_result(&self) -> bool {
12849
+ #[allow(clippy::needless_update)]
12850
+ let core_self = xberg::ExtractionConfig {
12851
+ mime_detection_policy: self.mime_detection_policy.clone().into(),
12852
+
12853
+ use_cache: self.use_cache,
12854
+
12855
+ enable_quality_processing: self.enable_quality_processing,
12856
+
12857
+ ocr: self.ocr.clone().map(Into::into),
12858
+
12859
+ force_ocr: self.force_ocr,
12860
+
12861
+ ocr_strategy: self.ocr_strategy.clone().into(),
12862
+
12863
+ force_ocr_pages: self.force_ocr_pages.clone(),
12864
+
12865
+ disable_ocr: self.disable_ocr,
12866
+
12867
+ chunking: self.chunking.clone().map(Into::into),
12868
+
12869
+ content_filter: self.content_filter.clone().map(Into::into),
12870
+
12871
+ images: self.images.clone().map(Into::into),
12872
+
12873
+ token_reduction: self.token_reduction.clone().map(Into::into),
12874
+
12875
+ language_detection: self.language_detection.clone().map(Into::into),
12876
+
12877
+ pages: self.pages.clone().map(Into::into),
12878
+
12879
+ postprocessor: self.postprocessor.clone().map(Into::into),
12880
+
12881
+ extraction_timeout_secs: self.extraction_timeout_secs,
12882
+
12883
+ max_concurrent_extractions: self.max_concurrent_extractions,
12884
+
12885
+ result_format: self.result_format.clone().into(),
12886
+
12887
+ security_limits: self.security_limits.clone().map(Into::into),
12888
+
12889
+ max_embedded_file_bytes: self.max_embedded_file_bytes,
12890
+
12891
+ output_format: self.output_format.clone().into(),
12892
+
12893
+ escape_markdown: self.escape_markdown,
12894
+
12895
+ table_anchors: self.table_anchors,
12896
+
12897
+ jupyter_cell_rendering: self.jupyter_cell_rendering.clone().into(),
12898
+
12899
+ apply_notebook_cell_tags: self.apply_notebook_cell_tags,
12900
+
12901
+ use_layout_for_markdown: self.use_layout_for_markdown,
12902
+
12903
+ include_document_structure: self.include_document_structure,
12904
+
12905
+ acceleration: self.acceleration.clone().map(Into::into),
12906
+
12907
+ cache_namespace: self.cache_namespace.clone(),
12908
+
12909
+ cache_ttl_secs: self.cache_ttl_secs,
12910
+
12911
+ email: self.email.clone().map(Into::into),
12912
+
12913
+ csv: self.csv.clone().map(Into::into),
12914
+
12915
+ geojson: self.geojson.clone().map(Into::into),
12916
+
12917
+ concurrency: self.concurrency.clone().map(Into::into),
12918
+
12919
+ url: self.url.clone().into(),
12920
+
12921
+ max_archive_depth: self.max_archive_depth,
12922
+
12923
+ structured_extraction: self.structured_extraction.clone().map(Into::into),
12924
+
12925
+ ner: self.ner.clone().map(Into::into),
12926
+
12927
+ redaction: self.redaction.clone().map(Into::into),
12928
+
12929
+ summarization: self.summarization.clone().map(Into::into),
12930
+
12931
+ translation: self.translation.clone().map(Into::into),
12932
+
12933
+ page_classification: self.page_classification.clone().map(Into::into),
12934
+
12935
+ chunk_classification: self.chunk_classification.clone().map(Into::into),
12936
+
12937
+ captioning: self.captioning.clone().map(Into::into),
12938
+
12939
+ qr_codes: self.qr_codes,
12940
+
12941
+ ..Default::default()
12942
+ };
12943
+ core_self.wants_own_bytes_in_result()
12944
+ }
12945
+
12694
12946
  fn needs_image_processing(&self) -> bool {
12695
12947
  #[allow(clippy::needless_update)]
12696
12948
  let core_self = xberg::ExtractionConfig {
@@ -22794,6 +23046,8 @@ pub struct OcrQualityThresholds {
22794
23046
  min_undecodable_ratio: f64,
22795
23047
  enable_provenance_ocr_routing: bool,
22796
23048
  min_provenance_fallback_ratio: f64,
23049
+ enable_plausibility_ocr_routing: bool,
23050
+ min_reliable_language_chunk_ratio: f64,
22797
23051
  }
22798
23052
 
22799
23053
  unsafe impl IntoValueFromNative for OcrQualityThresholds {}
@@ -23065,6 +23319,24 @@ impl OcrQualityThresholds {
23065
23319
  })?,
23066
23320
  None => 0.5,
23067
23321
  },
23322
+ enable_plausibility_ocr_routing: match kwargs.get(ruby.to_symbol("enable_plausibility_ocr_routing")) {
23323
+ Some(v) => bool::try_convert(v).map_err(|e| {
23324
+ magnus::Error::new(
23325
+ unsafe { magnus::Ruby::get_unchecked() }.exception_type_error(),
23326
+ format!("invalid value for `enable_plausibility_ocr_routing`: {}", e),
23327
+ )
23328
+ })?,
23329
+ None => true,
23330
+ },
23331
+ min_reliable_language_chunk_ratio: match kwargs.get(ruby.to_symbol("min_reliable_language_chunk_ratio")) {
23332
+ Some(v) => f64::try_convert(v).map_err(|e| {
23333
+ magnus::Error::new(
23334
+ unsafe { magnus::Ruby::get_unchecked() }.exception_type_error(),
23335
+ format!("invalid value for `min_reliable_language_chunk_ratio`: {}", e),
23336
+ )
23337
+ })?,
23338
+ None => 0.1,
23339
+ },
23068
23340
  })
23069
23341
  }
23070
23342
 
@@ -23163,6 +23435,14 @@ impl OcrQualityThresholds {
23163
23435
  fn min_provenance_fallback_ratio(&self) -> f64 {
23164
23436
  self.min_provenance_fallback_ratio
23165
23437
  }
23438
+
23439
+ fn enable_plausibility_ocr_routing(&self) -> bool {
23440
+ self.enable_plausibility_ocr_routing
23441
+ }
23442
+
23443
+ fn min_reliable_language_chunk_ratio(&self) -> f64 {
23444
+ self.min_reliable_language_chunk_ratio
23445
+ }
23166
23446
  }
23167
23447
 
23168
23448
  #[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
@@ -26652,6 +26932,8 @@ pub struct PdfMetadata {
26652
26932
  page_count: Option<u32>,
26653
26933
  scanned_confidence: Option<f32>,
26654
26934
  scanned_pages: Option<Vec<u32>>,
26935
+ fabricated_text_pages: Option<Vec<u32>>,
26936
+ implausible_text_pages: Option<Vec<u32>>,
26655
26937
  layout_gated_pages: Option<Vec<u32>>,
26656
26938
  layout_gate_reasons: Option<Vec<String>>,
26657
26939
  }
@@ -26774,6 +27056,30 @@ impl PdfMetadata {
26774
27056
  })?),
26775
27057
  None => None,
26776
27058
  },
27059
+ fabricated_text_pages: match kwargs
27060
+ .get(ruby.to_symbol("fabricated_text_pages"))
27061
+ .filter(|v| !v.is_nil())
27062
+ {
27063
+ Some(v) => Some(<Vec<u32>>::try_convert(v).map_err(|e| {
27064
+ magnus::Error::new(
27065
+ unsafe { magnus::Ruby::get_unchecked() }.exception_type_error(),
27066
+ format!("invalid value for `fabricated_text_pages`: {}", e),
27067
+ )
27068
+ })?),
27069
+ None => None,
27070
+ },
27071
+ implausible_text_pages: match kwargs
27072
+ .get(ruby.to_symbol("implausible_text_pages"))
27073
+ .filter(|v| !v.is_nil())
27074
+ {
27075
+ Some(v) => Some(<Vec<u32>>::try_convert(v).map_err(|e| {
27076
+ magnus::Error::new(
27077
+ unsafe { magnus::Ruby::get_unchecked() }.exception_type_error(),
27078
+ format!("invalid value for `implausible_text_pages`: {}", e),
27079
+ )
27080
+ })?),
27081
+ None => None,
27082
+ },
26777
27083
  layout_gated_pages: match kwargs.get(ruby.to_symbol("layout_gated_pages")).filter(|v| !v.is_nil()) {
26778
27084
  Some(v) => Some(<Vec<u32>>::try_convert(v).map_err(|e| {
26779
27085
  magnus::Error::new(
@@ -26830,6 +27136,14 @@ impl PdfMetadata {
26830
27136
  self.scanned_pages.clone()
26831
27137
  }
26832
27138
 
27139
+ fn fabricated_text_pages(&self) -> Option<Vec<u32>> {
27140
+ self.fabricated_text_pages.clone()
27141
+ }
27142
+
27143
+ fn implausible_text_pages(&self) -> Option<Vec<u32>> {
27144
+ self.implausible_text_pages.clone()
27145
+ }
27146
+
26833
27147
  fn layout_gated_pages(&self) -> Option<Vec<u32>> {
26834
27148
  self.layout_gated_pages.clone()
26835
27149
  }
@@ -35309,7 +35623,8 @@ impl magnus::IntoValue for AnnotationKind {
35309
35623
 
35310
35624
  impl magnus::TryConvert for AnnotationKind {
35311
35625
  fn try_convert(val: magnus::Value) -> Result<Self, magnus::Error> {
35312
- // For data enums with fields (e.g., PageAction), try to deserialize from JSON first.
35626
+ // Generated Data variants carry native DTOs, not JSON-compatible display strings.
35627
+ let class_name: Option<String> = val.class().funcall("name", ())?; // For data enums with fields (e.g., PageAction), try to deserialize from JSON first.
35313
35628
  // For unit enums or when passed as a string, fall back to string-based conversion.
35314
35629
  let json_str: String = if let Ok(s) = <String as magnus::TryConvert>::try_convert(val) {
35315
35630
  s
@@ -35341,9 +35656,10 @@ impl magnus::TryConvert for AnnotationKind {
35341
35656
 
35342
35657
  unsafe impl IntoValueFromNative for AnnotationKind {}
35343
35658
  unsafe impl TryConvertOwned for AnnotationKind {}
35659
+
35344
35660
  impl AnnotationKind {}
35345
35661
 
35346
- #[derive(Clone, Copy, PartialEq, Eq, Debug, serde::Serialize, serde::Deserialize)]
35662
+ #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, serde::Serialize, serde::Deserialize)]
35347
35663
  #[serde(rename_all = "snake_case")]
35348
35664
  pub enum AssetCategory {
35349
35665
  Document,
@@ -35384,7 +35700,14 @@ impl magnus::IntoValue for AssetCategory {
35384
35700
 
35385
35701
  impl magnus::TryConvert for AssetCategory {
35386
35702
  fn try_convert(val: magnus::Value) -> Result<Self, magnus::Error> {
35387
- let s: String = magnus::TryConvert::try_convert(val)?;
35703
+ // IntoValue emits Symbols, including when this enum is a Hash key.
35704
+ // Accept that exact representation as well as String inputs; do not
35705
+ // coerce arbitrary objects through to_s.
35706
+ let s: String = if let Some(symbol) = magnus::Symbol::from_value(val) {
35707
+ symbol.name()?.into_owned()
35708
+ } else {
35709
+ magnus::TryConvert::try_convert(val)?
35710
+ };
35388
35711
  // Accept the real serde wire value, the legacy always-snake_case symbol Magnus used to
35389
35712
  // emit unconditionally, and the verbatim PascalCase Rust variant name, so fixtures and
35390
35713
  // existing consumer code written in any of those styles keep working.
@@ -35441,7 +35764,8 @@ impl magnus::IntoValue for AuthConfig {
35441
35764
 
35442
35765
  impl magnus::TryConvert for AuthConfig {
35443
35766
  fn try_convert(val: magnus::Value) -> Result<Self, magnus::Error> {
35444
- // For data enums with fields (e.g., PageAction), try to deserialize from JSON first.
35767
+ // Generated Data variants carry native DTOs, not JSON-compatible display strings.
35768
+ let class_name: Option<String> = val.class().funcall("name", ())?; // For data enums with fields (e.g., PageAction), try to deserialize from JSON first.
35445
35769
  // For unit enums or when passed as a string, fall back to string-based conversion.
35446
35770
  let json_str: String = if let Ok(s) = <String as magnus::TryConvert>::try_convert(val) {
35447
35771
  s
@@ -35473,9 +35797,10 @@ impl magnus::TryConvert for AuthConfig {
35473
35797
 
35474
35798
  unsafe impl IntoValueFromNative for AuthConfig {}
35475
35799
  unsafe impl TryConvertOwned for AuthConfig {}
35800
+
35476
35801
  impl AuthConfig {}
35477
35802
 
35478
- #[derive(Clone, Copy, PartialEq, Eq, Debug, serde::Serialize, serde::Deserialize)]
35803
+ #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, serde::Serialize, serde::Deserialize)]
35479
35804
  #[serde(rename_all = "snake_case")]
35480
35805
  pub enum BlockType {
35481
35806
  Paragraph,
@@ -35528,7 +35853,14 @@ impl magnus::IntoValue for BlockType {
35528
35853
 
35529
35854
  impl magnus::TryConvert for BlockType {
35530
35855
  fn try_convert(val: magnus::Value) -> Result<Self, magnus::Error> {
35531
- let s: String = magnus::TryConvert::try_convert(val)?;
35856
+ // IntoValue emits Symbols, including when this enum is a Hash key.
35857
+ // Accept that exact representation as well as String inputs; do not
35858
+ // coerce arbitrary objects through to_s.
35859
+ let s: String = if let Some(symbol) = magnus::Symbol::from_value(val) {
35860
+ symbol.name()?.into_owned()
35861
+ } else {
35862
+ magnus::TryConvert::try_convert(val)?
35863
+ };
35532
35864
  // Accept the real serde wire value, the legacy always-snake_case symbol Magnus used to
35533
35865
  // emit unconditionally, and the verbatim PascalCase Rust variant name, so fixtures and
35534
35866
  // existing consumer code written in any of those styles keep working.
@@ -35560,7 +35892,7 @@ impl magnus::TryConvert for BlockType {
35560
35892
  unsafe impl IntoValueFromNative for BlockType {}
35561
35893
  unsafe impl TryConvertOwned for BlockType {}
35562
35894
 
35563
- #[derive(Clone, Copy, PartialEq, Eq, Debug, serde::Serialize, serde::Deserialize)]
35895
+ #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, serde::Serialize, serde::Deserialize)]
35564
35896
  #[serde(rename_all = "snake_case")]
35565
35897
  pub enum BoundaryReason {
35566
35898
  Start,
@@ -35591,7 +35923,14 @@ impl magnus::IntoValue for BoundaryReason {
35591
35923
 
35592
35924
  impl magnus::TryConvert for BoundaryReason {
35593
35925
  fn try_convert(val: magnus::Value) -> Result<Self, magnus::Error> {
35594
- let s: String = magnus::TryConvert::try_convert(val)?;
35926
+ // IntoValue emits Symbols, including when this enum is a Hash key.
35927
+ // Accept that exact representation as well as String inputs; do not
35928
+ // coerce arbitrary objects through to_s.
35929
+ let s: String = if let Some(symbol) = magnus::Symbol::from_value(val) {
35930
+ symbol.name()?.into_owned()
35931
+ } else {
35932
+ magnus::TryConvert::try_convert(val)?
35933
+ };
35595
35934
  // Accept the real serde wire value, the legacy always-snake_case symbol Magnus used to
35596
35935
  // emit unconditionally, and the verbatim PascalCase Rust variant name, so fixtures and
35597
35936
  // existing consumer code written in any of those styles keep working.
@@ -35612,7 +35951,7 @@ impl magnus::TryConvert for BoundaryReason {
35612
35951
  unsafe impl IntoValueFromNative for BoundaryReason {}
35613
35952
  unsafe impl TryConvertOwned for BoundaryReason {}
35614
35953
 
35615
- #[derive(Clone, Copy, PartialEq, Eq, Debug, serde::Serialize, serde::Deserialize)]
35954
+ #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, serde::Serialize, serde::Deserialize)]
35616
35955
  #[serde(rename_all = "snake_case")]
35617
35956
  pub enum BrowserBackend {
35618
35957
  Chromiumoxide,
@@ -35637,7 +35976,14 @@ impl magnus::IntoValue for BrowserBackend {
35637
35976
 
35638
35977
  impl magnus::TryConvert for BrowserBackend {
35639
35978
  fn try_convert(val: magnus::Value) -> Result<Self, magnus::Error> {
35640
- let s: String = magnus::TryConvert::try_convert(val)?;
35979
+ // IntoValue emits Symbols, including when this enum is a Hash key.
35980
+ // Accept that exact representation as well as String inputs; do not
35981
+ // coerce arbitrary objects through to_s.
35982
+ let s: String = if let Some(symbol) = magnus::Symbol::from_value(val) {
35983
+ symbol.name()?.into_owned()
35984
+ } else {
35985
+ magnus::TryConvert::try_convert(val)?
35986
+ };
35641
35987
  // Accept the real serde wire value, the legacy always-snake_case symbol Magnus used to
35642
35988
  // emit unconditionally, and the verbatim PascalCase Rust variant name, so fixtures and
35643
35989
  // existing consumer code written in any of those styles keep working.
@@ -35655,7 +36001,7 @@ impl magnus::TryConvert for BrowserBackend {
35655
36001
  unsafe impl IntoValueFromNative for BrowserBackend {}
35656
36002
  unsafe impl TryConvertOwned for BrowserBackend {}
35657
36003
 
35658
- #[derive(Clone, Copy, PartialEq, Eq, Debug, serde::Serialize, serde::Deserialize)]
36004
+ #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, serde::Serialize, serde::Deserialize)]
35659
36005
  #[serde(rename_all = "snake_case")]
35660
36006
  pub enum BrowserMode {
35661
36007
  Auto,
@@ -35684,7 +36030,14 @@ impl magnus::IntoValue for BrowserMode {
35684
36030
 
35685
36031
  impl magnus::TryConvert for BrowserMode {
35686
36032
  fn try_convert(val: magnus::Value) -> Result<Self, magnus::Error> {
35687
- let s: String = magnus::TryConvert::try_convert(val)?;
36033
+ // IntoValue emits Symbols, including when this enum is a Hash key.
36034
+ // Accept that exact representation as well as String inputs; do not
36035
+ // coerce arbitrary objects through to_s.
36036
+ let s: String = if let Some(symbol) = magnus::Symbol::from_value(val) {
36037
+ symbol.name()?.into_owned()
36038
+ } else {
36039
+ magnus::TryConvert::try_convert(val)?
36040
+ };
35688
36041
  // Accept the real serde wire value, the legacy always-snake_case symbol Magnus used to
35689
36042
  // emit unconditionally, and the verbatim PascalCase Rust variant name, so fixtures and
35690
36043
  // existing consumer code written in any of those styles keep working.
@@ -35704,7 +36057,7 @@ impl magnus::TryConvert for BrowserMode {
35704
36057
  unsafe impl IntoValueFromNative for BrowserMode {}
35705
36058
  unsafe impl TryConvertOwned for BrowserMode {}
35706
36059
 
35707
- #[derive(Clone, Copy, PartialEq, Eq, Debug, serde::Serialize, serde::Deserialize)]
36060
+ #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, serde::Serialize, serde::Deserialize)]
35708
36061
  #[serde(rename_all = "snake_case")]
35709
36062
  pub enum BrowserWait {
35710
36063
  NetworkIdle,
@@ -35731,7 +36084,14 @@ impl magnus::IntoValue for BrowserWait {
35731
36084
 
35732
36085
  impl magnus::TryConvert for BrowserWait {
35733
36086
  fn try_convert(val: magnus::Value) -> Result<Self, magnus::Error> {
35734
- let s: String = magnus::TryConvert::try_convert(val)?;
36087
+ // IntoValue emits Symbols, including when this enum is a Hash key.
36088
+ // Accept that exact representation as well as String inputs; do not
36089
+ // coerce arbitrary objects through to_s.
36090
+ let s: String = if let Some(symbol) = magnus::Symbol::from_value(val) {
36091
+ symbol.name()?.into_owned()
36092
+ } else {
36093
+ magnus::TryConvert::try_convert(val)?
36094
+ };
35735
36095
  // Accept the real serde wire value, the legacy always-snake_case symbol Magnus used to
35736
36096
  // emit unconditionally, and the verbatim PascalCase Rust variant name, so fixtures and
35737
36097
  // existing consumer code written in any of those styles keep working.
@@ -35750,7 +36110,7 @@ impl magnus::TryConvert for BrowserWait {
35750
36110
  unsafe impl IntoValueFromNative for BrowserWait {}
35751
36111
  unsafe impl TryConvertOwned for BrowserWait {}
35752
36112
 
35753
- #[derive(Clone, Copy, PartialEq, Eq, Debug, serde::Serialize, serde::Deserialize)]
36113
+ #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, serde::Serialize, serde::Deserialize)]
35754
36114
  #[serde(rename_all = "snake_case")]
35755
36115
  pub enum CallMode {
35756
36116
  TextOnly,
@@ -35777,7 +36137,14 @@ impl magnus::IntoValue for CallMode {
35777
36137
 
35778
36138
  impl magnus::TryConvert for CallMode {
35779
36139
  fn try_convert(val: magnus::Value) -> Result<Self, magnus::Error> {
35780
- let s: String = magnus::TryConvert::try_convert(val)?;
36140
+ // IntoValue emits Symbols, including when this enum is a Hash key.
36141
+ // Accept that exact representation as well as String inputs; do not
36142
+ // coerce arbitrary objects through to_s.
36143
+ let s: String = if let Some(symbol) = magnus::Symbol::from_value(val) {
36144
+ symbol.name()?.into_owned()
36145
+ } else {
36146
+ magnus::TryConvert::try_convert(val)?
36147
+ };
35781
36148
  // Accept the real serde wire value, the legacy always-snake_case symbol Magnus used to
35782
36149
  // emit unconditionally, and the verbatim PascalCase Rust variant name, so fixtures and
35783
36150
  // existing consumer code written in any of those styles keep working.
@@ -35796,7 +36163,63 @@ impl magnus::TryConvert for CallMode {
35796
36163
  unsafe impl IntoValueFromNative for CallMode {}
35797
36164
  unsafe impl TryConvertOwned for CallMode {}
35798
36165
 
35799
- #[derive(Clone, Copy, PartialEq, Eq, Debug, serde::Serialize, serde::Deserialize)]
36166
+ #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, serde::Serialize, serde::Deserialize)]
36167
+ #[serde(rename_all = "lowercase")]
36168
+ pub enum CandleDeepseekOcrDtype {
36169
+ Auto,
36170
+ F32,
36171
+ F16,
36172
+ Bf16,
36173
+ }
36174
+
36175
+ impl Default for CandleDeepseekOcrDtype {
36176
+ fn default() -> Self {
36177
+ Self::Auto
36178
+ }
36179
+ }
36180
+
36181
+ impl magnus::IntoValue for CandleDeepseekOcrDtype {
36182
+ fn into_value_with(self, handle: &Ruby) -> magnus::Value {
36183
+ let sym = match self {
36184
+ CandleDeepseekOcrDtype::Auto => "auto",
36185
+ CandleDeepseekOcrDtype::F32 => "f32",
36186
+ CandleDeepseekOcrDtype::F16 => "f16",
36187
+ CandleDeepseekOcrDtype::Bf16 => "bf16",
36188
+ };
36189
+ handle.to_symbol(sym).into_value_with(handle)
36190
+ }
36191
+ }
36192
+
36193
+ impl magnus::TryConvert for CandleDeepseekOcrDtype {
36194
+ fn try_convert(val: magnus::Value) -> Result<Self, magnus::Error> {
36195
+ // IntoValue emits Symbols, including when this enum is a Hash key.
36196
+ // Accept that exact representation as well as String inputs; do not
36197
+ // coerce arbitrary objects through to_s.
36198
+ let s: String = if let Some(symbol) = magnus::Symbol::from_value(val) {
36199
+ symbol.name()?.into_owned()
36200
+ } else {
36201
+ magnus::TryConvert::try_convert(val)?
36202
+ };
36203
+ // Accept the real serde wire value, the legacy always-snake_case symbol Magnus used to
36204
+ // emit unconditionally, and the verbatim PascalCase Rust variant name, so fixtures and
36205
+ // existing consumer code written in any of those styles keep working.
36206
+ match s.as_str() {
36207
+ "auto" | "Auto" => Ok(CandleDeepseekOcrDtype::Auto),
36208
+ "f32" | "F32" => Ok(CandleDeepseekOcrDtype::F32),
36209
+ "f16" | "F16" => Ok(CandleDeepseekOcrDtype::F16),
36210
+ "bf16" | "Bf16" => Ok(CandleDeepseekOcrDtype::Bf16),
36211
+ other => Err(magnus::Error::new(
36212
+ unsafe { Ruby::get_unchecked() }.exception_arg_error(),
36213
+ format!("invalid CandleDeepseekOcrDtype value: {other}"),
36214
+ )),
36215
+ }
36216
+ }
36217
+ }
36218
+
36219
+ unsafe impl IntoValueFromNative for CandleDeepseekOcrDtype {}
36220
+ unsafe impl TryConvertOwned for CandleDeepseekOcrDtype {}
36221
+
36222
+ #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, serde::Serialize, serde::Deserialize)]
35800
36223
  #[serde(rename_all = "lowercase")]
35801
36224
  pub enum CandleDevicePreference {
35802
36225
  Auto,
@@ -35825,7 +36248,14 @@ impl magnus::IntoValue for CandleDevicePreference {
35825
36248
 
35826
36249
  impl magnus::TryConvert for CandleDevicePreference {
35827
36250
  fn try_convert(val: magnus::Value) -> Result<Self, magnus::Error> {
35828
- let s: String = magnus::TryConvert::try_convert(val)?;
36251
+ // IntoValue emits Symbols, including when this enum is a Hash key.
36252
+ // Accept that exact representation as well as String inputs; do not
36253
+ // coerce arbitrary objects through to_s.
36254
+ let s: String = if let Some(symbol) = magnus::Symbol::from_value(val) {
36255
+ symbol.name()?.into_owned()
36256
+ } else {
36257
+ magnus::TryConvert::try_convert(val)?
36258
+ };
35829
36259
  // Accept the real serde wire value, the legacy always-snake_case symbol Magnus used to
35830
36260
  // emit unconditionally, and the verbatim PascalCase Rust variant name, so fixtures and
35831
36261
  // existing consumer code written in any of those styles keep working.
@@ -35845,7 +36275,7 @@ impl magnus::TryConvert for CandleDevicePreference {
35845
36275
  unsafe impl IntoValueFromNative for CandleDevicePreference {}
35846
36276
  unsafe impl TryConvertOwned for CandleDevicePreference {}
35847
36277
 
35848
- #[derive(Clone, Copy, PartialEq, Eq, Debug, serde::Serialize, serde::Deserialize)]
36278
+ #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, serde::Serialize, serde::Deserialize)]
35849
36279
  #[serde(rename_all = "kebab-case")]
35850
36280
  pub enum CandleTrocrVariant {
35851
36281
  BasePrinted,
@@ -35874,7 +36304,14 @@ impl magnus::IntoValue for CandleTrocrVariant {
35874
36304
 
35875
36305
  impl magnus::TryConvert for CandleTrocrVariant {
35876
36306
  fn try_convert(val: magnus::Value) -> Result<Self, magnus::Error> {
35877
- let s: String = magnus::TryConvert::try_convert(val)?;
36307
+ // IntoValue emits Symbols, including when this enum is a Hash key.
36308
+ // Accept that exact representation as well as String inputs; do not
36309
+ // coerce arbitrary objects through to_s.
36310
+ let s: String = if let Some(symbol) = magnus::Symbol::from_value(val) {
36311
+ symbol.name()?.into_owned()
36312
+ } else {
36313
+ magnus::TryConvert::try_convert(val)?
36314
+ };
35878
36315
  // Accept the real serde wire value, the legacy always-snake_case symbol Magnus used to
35879
36316
  // emit unconditionally, and the verbatim PascalCase Rust variant name, so fixtures and
35880
36317
  // existing consumer code written in any of those styles keep working.
@@ -35919,7 +36356,8 @@ impl magnus::IntoValue for ChunkSizing {
35919
36356
 
35920
36357
  impl magnus::TryConvert for ChunkSizing {
35921
36358
  fn try_convert(val: magnus::Value) -> Result<Self, magnus::Error> {
35922
- // For data enums with fields (e.g., PageAction), try to deserialize from JSON first.
36359
+ // Generated Data variants carry native DTOs, not JSON-compatible display strings.
36360
+ let class_name: Option<String> = val.class().funcall("name", ())?; // For data enums with fields (e.g., PageAction), try to deserialize from JSON first.
35923
36361
  // For unit enums or when passed as a string, fall back to string-based conversion.
35924
36362
  let json_str: String = if let Ok(s) = <String as magnus::TryConvert>::try_convert(val) {
35925
36363
  s
@@ -35951,9 +36389,10 @@ impl magnus::TryConvert for ChunkSizing {
35951
36389
 
35952
36390
  unsafe impl IntoValueFromNative for ChunkSizing {}
35953
36391
  unsafe impl TryConvertOwned for ChunkSizing {}
36392
+
35954
36393
  impl ChunkSizing {}
35955
36394
 
35956
- #[derive(Clone, Copy, PartialEq, Eq, Debug, serde::Serialize, serde::Deserialize)]
36395
+ #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, serde::Serialize, serde::Deserialize)]
35957
36396
  #[serde(rename_all = "snake_case")]
35958
36397
  pub enum ChunkType {
35959
36398
  Heading,
@@ -36006,7 +36445,14 @@ impl magnus::IntoValue for ChunkType {
36006
36445
 
36007
36446
  impl magnus::TryConvert for ChunkType {
36008
36447
  fn try_convert(val: magnus::Value) -> Result<Self, magnus::Error> {
36009
- let s: String = magnus::TryConvert::try_convert(val)?;
36448
+ // IntoValue emits Symbols, including when this enum is a Hash key.
36449
+ // Accept that exact representation as well as String inputs; do not
36450
+ // coerce arbitrary objects through to_s.
36451
+ let s: String = if let Some(symbol) = magnus::Symbol::from_value(val) {
36452
+ symbol.name()?.into_owned()
36453
+ } else {
36454
+ magnus::TryConvert::try_convert(val)?
36455
+ };
36010
36456
  // Accept the real serde wire value, the legacy always-snake_case symbol Magnus used to
36011
36457
  // emit unconditionally, and the verbatim PascalCase Rust variant name, so fixtures and
36012
36458
  // existing consumer code written in any of those styles keep working.
@@ -36038,7 +36484,7 @@ impl magnus::TryConvert for ChunkType {
36038
36484
  unsafe impl IntoValueFromNative for ChunkType {}
36039
36485
  unsafe impl TryConvertOwned for ChunkType {}
36040
36486
 
36041
- #[derive(Clone, Copy, PartialEq, Eq, Debug, serde::Serialize, serde::Deserialize)]
36487
+ #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, serde::Serialize, serde::Deserialize)]
36042
36488
  #[serde(rename_all = "lowercase")]
36043
36489
  pub enum ChunkerType {
36044
36490
  Text,
@@ -36067,7 +36513,14 @@ impl magnus::IntoValue for ChunkerType {
36067
36513
 
36068
36514
  impl magnus::TryConvert for ChunkerType {
36069
36515
  fn try_convert(val: magnus::Value) -> Result<Self, magnus::Error> {
36070
- let s: String = magnus::TryConvert::try_convert(val)?;
36516
+ // IntoValue emits Symbols, including when this enum is a Hash key.
36517
+ // Accept that exact representation as well as String inputs; do not
36518
+ // coerce arbitrary objects through to_s.
36519
+ let s: String = if let Some(symbol) = magnus::Symbol::from_value(val) {
36520
+ symbol.name()?.into_owned()
36521
+ } else {
36522
+ magnus::TryConvert::try_convert(val)?
36523
+ };
36071
36524
  // Accept the real serde wire value, the legacy always-snake_case symbol Magnus used to
36072
36525
  // emit unconditionally, and the verbatim PascalCase Rust variant name, so fixtures and
36073
36526
  // existing consumer code written in any of those styles keep working.
@@ -36116,7 +36569,8 @@ impl magnus::IntoValue for ChunkingReason {
36116
36569
 
36117
36570
  impl magnus::TryConvert for ChunkingReason {
36118
36571
  fn try_convert(val: magnus::Value) -> Result<Self, magnus::Error> {
36119
- // For data enums with fields (e.g., PageAction), try to deserialize from JSON first.
36572
+ // Generated Data variants carry native DTOs, not JSON-compatible display strings.
36573
+ let class_name: Option<String> = val.class().funcall("name", ())?; // For data enums with fields (e.g., PageAction), try to deserialize from JSON first.
36120
36574
  // For unit enums or when passed as a string, fall back to string-based conversion.
36121
36575
  let json_str: String = if let Ok(s) = <String as magnus::TryConvert>::try_convert(val) {
36122
36576
  s
@@ -36148,9 +36602,10 @@ impl magnus::TryConvert for ChunkingReason {
36148
36602
 
36149
36603
  unsafe impl IntoValueFromNative for ChunkingReason {}
36150
36604
  unsafe impl TryConvertOwned for ChunkingReason {}
36605
+
36151
36606
  impl ChunkingReason {}
36152
36607
 
36153
- #[derive(Clone, Copy, PartialEq, Eq, Debug, serde::Serialize, serde::Deserialize)]
36608
+ #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, serde::Serialize, serde::Deserialize)]
36154
36609
  pub enum CodeBlockStyle {
36155
36610
  Indented,
36156
36611
  Backticks,
@@ -36176,7 +36631,14 @@ impl magnus::IntoValue for CodeBlockStyle {
36176
36631
 
36177
36632
  impl magnus::TryConvert for CodeBlockStyle {
36178
36633
  fn try_convert(val: magnus::Value) -> Result<Self, magnus::Error> {
36179
- let s: String = magnus::TryConvert::try_convert(val)?;
36634
+ // IntoValue emits Symbols, including when this enum is a Hash key.
36635
+ // Accept that exact representation as well as String inputs; do not
36636
+ // coerce arbitrary objects through to_s.
36637
+ let s: String = if let Some(symbol) = magnus::Symbol::from_value(val) {
36638
+ symbol.name()?.into_owned()
36639
+ } else {
36640
+ magnus::TryConvert::try_convert(val)?
36641
+ };
36180
36642
  // Accept the real serde wire value, the legacy always-snake_case symbol Magnus used to
36181
36643
  // emit unconditionally, and the verbatim PascalCase Rust variant name, so fixtures and
36182
36644
  // existing consumer code written in any of those styles keep working.
@@ -36195,7 +36657,7 @@ impl magnus::TryConvert for CodeBlockStyle {
36195
36657
  unsafe impl IntoValueFromNative for CodeBlockStyle {}
36196
36658
  unsafe impl TryConvertOwned for CodeBlockStyle {}
36197
36659
 
36198
- #[derive(Clone, Copy, PartialEq, Eq, Debug, serde::Serialize, serde::Deserialize)]
36660
+ #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, serde::Serialize, serde::Deserialize)]
36199
36661
  #[serde(rename_all = "snake_case")]
36200
36662
  pub enum CodeContentMode {
36201
36663
  Chunks,
@@ -36222,7 +36684,14 @@ impl magnus::IntoValue for CodeContentMode {
36222
36684
 
36223
36685
  impl magnus::TryConvert for CodeContentMode {
36224
36686
  fn try_convert(val: magnus::Value) -> Result<Self, magnus::Error> {
36225
- let s: String = magnus::TryConvert::try_convert(val)?;
36687
+ // IntoValue emits Symbols, including when this enum is a Hash key.
36688
+ // Accept that exact representation as well as String inputs; do not
36689
+ // coerce arbitrary objects through to_s.
36690
+ let s: String = if let Some(symbol) = magnus::Symbol::from_value(val) {
36691
+ symbol.name()?.into_owned()
36692
+ } else {
36693
+ magnus::TryConvert::try_convert(val)?
36694
+ };
36226
36695
  // Accept the real serde wire value, the legacy always-snake_case symbol Magnus used to
36227
36696
  // emit unconditionally, and the verbatim PascalCase Rust variant name, so fixtures and
36228
36697
  // existing consumer code written in any of those styles keep working.
@@ -36241,7 +36710,7 @@ impl magnus::TryConvert for CodeContentMode {
36241
36710
  unsafe impl IntoValueFromNative for CodeContentMode {}
36242
36711
  unsafe impl TryConvertOwned for CodeContentMode {}
36243
36712
 
36244
- #[derive(Clone, Copy, PartialEq, Eq, Debug, serde::Serialize, serde::Deserialize)]
36713
+ #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, serde::Serialize, serde::Deserialize)]
36245
36714
  #[serde(rename_all = "snake_case")]
36246
36715
  pub enum CodeDataNodeKind {
36247
36716
  KeyValue,
@@ -36268,7 +36737,14 @@ impl magnus::IntoValue for CodeDataNodeKind {
36268
36737
 
36269
36738
  impl magnus::TryConvert for CodeDataNodeKind {
36270
36739
  fn try_convert(val: magnus::Value) -> Result<Self, magnus::Error> {
36271
- let s: String = magnus::TryConvert::try_convert(val)?;
36740
+ // IntoValue emits Symbols, including when this enum is a Hash key.
36741
+ // Accept that exact representation as well as String inputs; do not
36742
+ // coerce arbitrary objects through to_s.
36743
+ let s: String = if let Some(symbol) = magnus::Symbol::from_value(val) {
36744
+ symbol.name()?.into_owned()
36745
+ } else {
36746
+ magnus::TryConvert::try_convert(val)?
36747
+ };
36272
36748
  // Accept the real serde wire value, the legacy always-snake_case symbol Magnus used to
36273
36749
  // emit unconditionally, and the verbatim PascalCase Rust variant name, so fixtures and
36274
36750
  // existing consumer code written in any of those styles keep working.
@@ -36349,7 +36825,7 @@ impl ConfidenceSemantics {
36349
36825
  }
36350
36826
  }
36351
36827
 
36352
- #[derive(Clone, Copy, PartialEq, Eq, Debug, serde::Serialize, serde::Deserialize)]
36828
+ #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, serde::Serialize, serde::Deserialize)]
36353
36829
  #[serde(rename_all = "snake_case")]
36354
36830
  pub enum ContentFilterKind {
36355
36831
  Bm25,
@@ -36372,7 +36848,14 @@ impl magnus::IntoValue for ContentFilterKind {
36372
36848
 
36373
36849
  impl magnus::TryConvert for ContentFilterKind {
36374
36850
  fn try_convert(val: magnus::Value) -> Result<Self, magnus::Error> {
36375
- let s: String = magnus::TryConvert::try_convert(val)?;
36851
+ // IntoValue emits Symbols, including when this enum is a Hash key.
36852
+ // Accept that exact representation as well as String inputs; do not
36853
+ // coerce arbitrary objects through to_s.
36854
+ let s: String = if let Some(symbol) = magnus::Symbol::from_value(val) {
36855
+ symbol.name()?.into_owned()
36856
+ } else {
36857
+ magnus::TryConvert::try_convert(val)?
36858
+ };
36376
36859
  // Accept the real serde wire value, the legacy always-snake_case symbol Magnus used to
36377
36860
  // emit unconditionally, and the verbatim PascalCase Rust variant name, so fixtures and
36378
36861
  // existing consumer code written in any of those styles keep working.
@@ -36389,7 +36872,7 @@ impl magnus::TryConvert for ContentFilterKind {
36389
36872
  unsafe impl IntoValueFromNative for ContentFilterKind {}
36390
36873
  unsafe impl TryConvertOwned for ContentFilterKind {}
36391
36874
 
36392
- #[derive(Clone, Copy, PartialEq, Eq, Debug, serde::Serialize, serde::Deserialize)]
36875
+ #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, serde::Serialize, serde::Deserialize)]
36393
36876
  #[serde(rename_all = "snake_case")]
36394
36877
  pub enum ContentLayer {
36395
36878
  Body,
@@ -36418,7 +36901,14 @@ impl magnus::IntoValue for ContentLayer {
36418
36901
 
36419
36902
  impl magnus::TryConvert for ContentLayer {
36420
36903
  fn try_convert(val: magnus::Value) -> Result<Self, magnus::Error> {
36421
- let s: String = magnus::TryConvert::try_convert(val)?;
36904
+ // IntoValue emits Symbols, including when this enum is a Hash key.
36905
+ // Accept that exact representation as well as String inputs; do not
36906
+ // coerce arbitrary objects through to_s.
36907
+ let s: String = if let Some(symbol) = magnus::Symbol::from_value(val) {
36908
+ symbol.name()?.into_owned()
36909
+ } else {
36910
+ magnus::TryConvert::try_convert(val)?
36911
+ };
36422
36912
  // Accept the real serde wire value, the legacy always-snake_case symbol Magnus used to
36423
36913
  // emit unconditionally, and the verbatim PascalCase Rust variant name, so fixtures and
36424
36914
  // existing consumer code written in any of those styles keep working.
@@ -36438,7 +36928,7 @@ impl magnus::TryConvert for ContentLayer {
36438
36928
  unsafe impl IntoValueFromNative for ContentLayer {}
36439
36929
  unsafe impl TryConvertOwned for ContentLayer {}
36440
36930
 
36441
- #[derive(Clone, Copy, PartialEq, Eq, Debug, serde::Serialize, serde::Deserialize)]
36931
+ #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, serde::Serialize, serde::Deserialize)]
36442
36932
  #[serde(rename_all = "snake_case")]
36443
36933
  pub enum CrawlStrategyKind {
36444
36934
  Bfs,
@@ -36467,7 +36957,14 @@ impl magnus::IntoValue for CrawlStrategyKind {
36467
36957
 
36468
36958
  impl magnus::TryConvert for CrawlStrategyKind {
36469
36959
  fn try_convert(val: magnus::Value) -> Result<Self, magnus::Error> {
36470
- let s: String = magnus::TryConvert::try_convert(val)?;
36960
+ // IntoValue emits Symbols, including when this enum is a Hash key.
36961
+ // Accept that exact representation as well as String inputs; do not
36962
+ // coerce arbitrary objects through to_s.
36963
+ let s: String = if let Some(symbol) = magnus::Symbol::from_value(val) {
36964
+ symbol.name()?.into_owned()
36965
+ } else {
36966
+ magnus::TryConvert::try_convert(val)?
36967
+ };
36471
36968
  // Accept the real serde wire value, the legacy always-snake_case symbol Magnus used to
36472
36969
  // emit unconditionally, and the verbatim PascalCase Rust variant name, so fixtures and
36473
36970
  // existing consumer code written in any of those styles keep working.
@@ -36512,17 +37009,6 @@ pub enum CredentialProviderConfig {
36512
37009
  },
36513
37010
  }
36514
37011
 
36515
- impl Default for CredentialProviderConfig {
36516
- fn default() -> Self {
36517
- Self::AzureAd {
36518
- tenant_id: Default::default(),
36519
- client_id: Default::default(),
36520
- client_secret: Default::default(),
36521
- scope: Default::default(),
36522
- }
36523
- }
36524
- }
36525
-
36526
37012
  impl magnus::IntoValue for CredentialProviderConfig {
36527
37013
  fn into_value_with(self, handle: &Ruby) -> magnus::Value {
36528
37014
  match serde_json::to_value(&self) {
@@ -36534,7 +37020,8 @@ impl magnus::IntoValue for CredentialProviderConfig {
36534
37020
 
36535
37021
  impl magnus::TryConvert for CredentialProviderConfig {
36536
37022
  fn try_convert(val: magnus::Value) -> Result<Self, magnus::Error> {
36537
- // For data enums with fields (e.g., PageAction), try to deserialize from JSON first.
37023
+ // Generated Data variants carry native DTOs, not JSON-compatible display strings.
37024
+ let class_name: Option<String> = val.class().funcall("name", ())?; // For data enums with fields (e.g., PageAction), try to deserialize from JSON first.
36538
37025
  // For unit enums or when passed as a string, fall back to string-based conversion.
36539
37026
  let json_str: String = if let Ok(s) = <String as magnus::TryConvert>::try_convert(val) {
36540
37027
  s
@@ -36566,6 +37053,7 @@ impl magnus::TryConvert for CredentialProviderConfig {
36566
37053
 
36567
37054
  unsafe impl IntoValueFromNative for CredentialProviderConfig {}
36568
37055
  unsafe impl TryConvertOwned for CredentialProviderConfig {}
37056
+
36569
37057
  impl CredentialProviderConfig {}
36570
37058
 
36571
37059
  #[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
@@ -36594,7 +37082,8 @@ impl magnus::IntoValue for DiffLine {
36594
37082
 
36595
37083
  impl magnus::TryConvert for DiffLine {
36596
37084
  fn try_convert(val: magnus::Value) -> Result<Self, magnus::Error> {
36597
- // For data enums with fields (e.g., PageAction), try to deserialize from JSON first.
37085
+ // Generated Data variants carry native DTOs, not JSON-compatible display strings.
37086
+ let class_name: Option<String> = val.class().funcall("name", ())?; // For data enums with fields (e.g., PageAction), try to deserialize from JSON first.
36598
37087
  // For unit enums or when passed as a string, fall back to string-based conversion.
36599
37088
  let json_str: String = if let Ok(s) = <String as magnus::TryConvert>::try_convert(val) {
36600
37089
  s
@@ -36626,6 +37115,7 @@ impl magnus::TryConvert for DiffLine {
36626
37115
 
36627
37116
  unsafe impl IntoValueFromNative for DiffLine {}
36628
37117
  unsafe impl TryConvertOwned for DiffLine {}
37118
+
36629
37119
  impl DiffLine {
36630
37120
  pub fn context(&self) -> Option<&String> {
36631
37121
  match self {
@@ -36647,7 +37137,7 @@ impl DiffLine {
36647
37137
  }
36648
37138
  }
36649
37139
 
36650
- #[derive(Clone, Copy, PartialEq, Eq, Debug, serde::Serialize, serde::Deserialize)]
37140
+ #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, serde::Serialize, serde::Deserialize)]
36651
37141
  #[serde(rename_all = "snake_case")]
36652
37142
  pub enum DocumentContentEncoding {
36653
37143
  Base64,
@@ -36670,7 +37160,14 @@ impl magnus::IntoValue for DocumentContentEncoding {
36670
37160
 
36671
37161
  impl magnus::TryConvert for DocumentContentEncoding {
36672
37162
  fn try_convert(val: magnus::Value) -> Result<Self, magnus::Error> {
36673
- let s: String = magnus::TryConvert::try_convert(val)?;
37163
+ // IntoValue emits Symbols, including when this enum is a Hash key.
37164
+ // Accept that exact representation as well as String inputs; do not
37165
+ // coerce arbitrary objects through to_s.
37166
+ let s: String = if let Some(symbol) = magnus::Symbol::from_value(val) {
37167
+ symbol.name()?.into_owned()
37168
+ } else {
37169
+ magnus::TryConvert::try_convert(val)?
37170
+ };
36674
37171
  // Accept the real serde wire value, the legacy always-snake_case symbol Magnus used to
36675
37172
  // emit unconditionally, and the verbatim PascalCase Rust variant name, so fixtures and
36676
37173
  // existing consumer code written in any of those styles keep working.
@@ -36687,7 +37184,7 @@ impl magnus::TryConvert for DocumentContentEncoding {
36687
37184
  unsafe impl IntoValueFromNative for DocumentContentEncoding {}
36688
37185
  unsafe impl TryConvertOwned for DocumentContentEncoding {}
36689
37186
 
36690
- #[derive(Clone, Copy, PartialEq, Eq, Debug, serde::Serialize, serde::Deserialize)]
37187
+ #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, serde::Serialize, serde::Deserialize)]
36691
37188
  #[serde(rename_all = "snake_case")]
36692
37189
  pub enum ElementType {
36693
37190
  Title,
@@ -36732,7 +37229,14 @@ impl magnus::IntoValue for ElementType {
36732
37229
 
36733
37230
  impl magnus::TryConvert for ElementType {
36734
37231
  fn try_convert(val: magnus::Value) -> Result<Self, magnus::Error> {
36735
- let s: String = magnus::TryConvert::try_convert(val)?;
37232
+ // IntoValue emits Symbols, including when this enum is a Hash key.
37233
+ // Accept that exact representation as well as String inputs; do not
37234
+ // coerce arbitrary objects through to_s.
37235
+ let s: String = if let Some(symbol) = magnus::Symbol::from_value(val) {
37236
+ symbol.name()?.into_owned()
37237
+ } else {
37238
+ magnus::TryConvert::try_convert(val)?
37239
+ };
36736
37240
  // Accept the real serde wire value, the legacy always-snake_case symbol Magnus used to
36737
37241
  // emit unconditionally, and the verbatim PascalCase Rust variant name, so fixtures and
36738
37242
  // existing consumer code written in any of those styles keep working.
@@ -36766,7 +37270,7 @@ unsafe impl TryConvertOwned for ElementType {}
36766
37270
  pub enum EmbeddingModelType {
36767
37271
  Preset { name: String },
36768
37272
  Custom { model_id: String, dimensions: usize },
36769
- Llm { llm: LlmConfig },
37273
+ Llm { llm: Box<LlmConfig> },
36770
37274
  Plugin { name: String },
36771
37275
  }
36772
37276
 
@@ -36789,7 +37293,8 @@ impl magnus::IntoValue for EmbeddingModelType {
36789
37293
 
36790
37294
  impl magnus::TryConvert for EmbeddingModelType {
36791
37295
  fn try_convert(val: magnus::Value) -> Result<Self, magnus::Error> {
36792
- // For data enums with fields (e.g., PageAction), try to deserialize from JSON first.
37296
+ // Generated Data variants carry native DTOs, not JSON-compatible display strings.
37297
+ let class_name: Option<String> = val.class().funcall("name", ())?; // For data enums with fields (e.g., PageAction), try to deserialize from JSON first.
36793
37298
  // For unit enums or when passed as a string, fall back to string-based conversion.
36794
37299
  let json_str: String = if let Ok(s) = <String as magnus::TryConvert>::try_convert(val) {
36795
37300
  s
@@ -36821,9 +37326,10 @@ impl magnus::TryConvert for EmbeddingModelType {
36821
37326
 
36822
37327
  unsafe impl IntoValueFromNative for EmbeddingModelType {}
36823
37328
  unsafe impl TryConvertOwned for EmbeddingModelType {}
37329
+
36824
37330
  impl EmbeddingModelType {}
36825
37331
 
36826
- #[derive(Clone, Copy, PartialEq, Eq, Debug, serde::Serialize, serde::Deserialize)]
37332
+ #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, serde::Serialize, serde::Deserialize)]
36827
37333
  #[serde(rename_all = "snake_case")]
36828
37334
  pub enum EmbeddingsEmbeddingBackend {
36829
37335
  Onnx,
@@ -36848,7 +37354,14 @@ impl magnus::IntoValue for EmbeddingsEmbeddingBackend {
36848
37354
 
36849
37355
  impl magnus::TryConvert for EmbeddingsEmbeddingBackend {
36850
37356
  fn try_convert(val: magnus::Value) -> Result<Self, magnus::Error> {
36851
- let s: String = magnus::TryConvert::try_convert(val)?;
37357
+ // IntoValue emits Symbols, including when this enum is a Hash key.
37358
+ // Accept that exact representation as well as String inputs; do not
37359
+ // coerce arbitrary objects through to_s.
37360
+ let s: String = if let Some(symbol) = magnus::Symbol::from_value(val) {
37361
+ symbol.name()?.into_owned()
37362
+ } else {
37363
+ magnus::TryConvert::try_convert(val)?
37364
+ };
36852
37365
  // Accept the real serde wire value, the legacy always-snake_case symbol Magnus used to
36853
37366
  // emit unconditionally, and the verbatim PascalCase Rust variant name, so fixtures and
36854
37367
  // existing consumer code written in any of those styles keep working.
@@ -36931,7 +37444,7 @@ impl magnus::TryConvert for EntityCategory {
36931
37444
  unsafe impl IntoValueFromNative for EntityCategory {}
36932
37445
  unsafe impl TryConvertOwned for EntityCategory {}
36933
37446
 
36934
- #[derive(Clone, Copy, PartialEq, Eq, Debug, serde::Serialize, serde::Deserialize)]
37447
+ #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, serde::Serialize, serde::Deserialize)]
36935
37448
  #[serde(rename_all = "lowercase")]
36936
37449
  pub enum ExecutionProviderType {
36937
37450
  Auto,
@@ -36962,7 +37475,14 @@ impl magnus::IntoValue for ExecutionProviderType {
36962
37475
 
36963
37476
  impl magnus::TryConvert for ExecutionProviderType {
36964
37477
  fn try_convert(val: magnus::Value) -> Result<Self, magnus::Error> {
36965
- let s: String = magnus::TryConvert::try_convert(val)?;
37478
+ // IntoValue emits Symbols, including when this enum is a Hash key.
37479
+ // Accept that exact representation as well as String inputs; do not
37480
+ // coerce arbitrary objects through to_s.
37481
+ let s: String = if let Some(symbol) = magnus::Symbol::from_value(val) {
37482
+ symbol.name()?.into_owned()
37483
+ } else {
37484
+ magnus::TryConvert::try_convert(val)?
37485
+ };
36966
37486
  // Accept the real serde wire value, the legacy always-snake_case symbol Magnus used to
36967
37487
  // emit unconditionally, and the verbatim PascalCase Rust variant name, so fixtures and
36968
37488
  // existing consumer code written in any of those styles keep working.
@@ -36983,7 +37503,7 @@ impl magnus::TryConvert for ExecutionProviderType {
36983
37503
  unsafe impl IntoValueFromNative for ExecutionProviderType {}
36984
37504
  unsafe impl TryConvertOwned for ExecutionProviderType {}
36985
37505
 
36986
- #[derive(Clone, Copy, PartialEq, Eq, Debug, serde::Serialize, serde::Deserialize)]
37506
+ #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, serde::Serialize, serde::Deserialize)]
36987
37507
  #[serde(rename_all = "snake_case")]
36988
37508
  pub enum ExtractInputKind {
36989
37509
  Bytes,
@@ -37008,7 +37528,14 @@ impl magnus::IntoValue for ExtractInputKind {
37008
37528
 
37009
37529
  impl magnus::TryConvert for ExtractInputKind {
37010
37530
  fn try_convert(val: magnus::Value) -> Result<Self, magnus::Error> {
37011
- let s: String = magnus::TryConvert::try_convert(val)?;
37531
+ // IntoValue emits Symbols, including when this enum is a Hash key.
37532
+ // Accept that exact representation as well as String inputs; do not
37533
+ // coerce arbitrary objects through to_s.
37534
+ let s: String = if let Some(symbol) = magnus::Symbol::from_value(val) {
37535
+ symbol.name()?.into_owned()
37536
+ } else {
37537
+ magnus::TryConvert::try_convert(val)?
37538
+ };
37012
37539
  // Accept the real serde wire value, the legacy always-snake_case symbol Magnus used to
37013
37540
  // emit unconditionally, and the verbatim PascalCase Rust variant name, so fixtures and
37014
37541
  // existing consumer code written in any of those styles keep working.
@@ -37026,7 +37553,7 @@ impl magnus::TryConvert for ExtractInputKind {
37026
37553
  unsafe impl IntoValueFromNative for ExtractInputKind {}
37027
37554
  unsafe impl TryConvertOwned for ExtractInputKind {}
37028
37555
 
37029
- #[derive(Clone, Copy, PartialEq, Eq, Debug, serde::Serialize, serde::Deserialize)]
37556
+ #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, serde::Serialize, serde::Deserialize)]
37030
37557
  #[serde(rename_all = "snake_case")]
37031
37558
  pub enum ExtractionMethod {
37032
37559
  Native,
@@ -37053,7 +37580,14 @@ impl magnus::IntoValue for ExtractionMethod {
37053
37580
 
37054
37581
  impl magnus::TryConvert for ExtractionMethod {
37055
37582
  fn try_convert(val: magnus::Value) -> Result<Self, magnus::Error> {
37056
- let s: String = magnus::TryConvert::try_convert(val)?;
37583
+ // IntoValue emits Symbols, including when this enum is a Hash key.
37584
+ // Accept that exact representation as well as String inputs; do not
37585
+ // coerce arbitrary objects through to_s.
37586
+ let s: String = if let Some(symbol) = magnus::Symbol::from_value(val) {
37587
+ symbol.name()?.into_owned()
37588
+ } else {
37589
+ magnus::TryConvert::try_convert(val)?
37590
+ };
37057
37591
  // Accept the real serde wire value, the legacy always-snake_case symbol Magnus used to
37058
37592
  // emit unconditionally, and the verbatim PascalCase Rust variant name, so fixtures and
37059
37593
  // existing consumer code written in any of those styles keep working.
@@ -37072,7 +37606,7 @@ impl magnus::TryConvert for ExtractionMethod {
37072
37606
  unsafe impl IntoValueFromNative for ExtractionMethod {}
37073
37607
  unsafe impl TryConvertOwned for ExtractionMethod {}
37074
37608
 
37075
- #[derive(Clone, Copy, PartialEq, Eq, Debug, serde::Serialize, serde::Deserialize)]
37609
+ #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, serde::Serialize, serde::Deserialize)]
37076
37610
  #[serde(rename_all = "snake_case")]
37077
37611
  pub enum FormFieldType {
37078
37612
  Text,
@@ -37107,7 +37641,14 @@ impl magnus::IntoValue for FormFieldType {
37107
37641
 
37108
37642
  impl magnus::TryConvert for FormFieldType {
37109
37643
  fn try_convert(val: magnus::Value) -> Result<Self, magnus::Error> {
37110
- let s: String = magnus::TryConvert::try_convert(val)?;
37644
+ // IntoValue emits Symbols, including when this enum is a Hash key.
37645
+ // Accept that exact representation as well as String inputs; do not
37646
+ // coerce arbitrary objects through to_s.
37647
+ let s: String = if let Some(symbol) = magnus::Symbol::from_value(val) {
37648
+ symbol.name()?.into_owned()
37649
+ } else {
37650
+ magnus::TryConvert::try_convert(val)?
37651
+ };
37111
37652
  // Accept the real serde wire value, the legacy always-snake_case symbol Magnus used to
37112
37653
  // emit unconditionally, and the verbatim PascalCase Rust variant name, so fixtures and
37113
37654
  // existing consumer code written in any of those styles keep working.
@@ -37140,7 +37681,7 @@ pub enum FormatMetadata {
37140
37681
  },
37141
37682
  Docx {
37142
37683
  #[serde(flatten)]
37143
- _0: DocxMetadata,
37684
+ _0: Box<DocxMetadata>,
37144
37685
  },
37145
37686
  Excel {
37146
37687
  #[serde(flatten)]
@@ -37172,7 +37713,7 @@ pub enum FormatMetadata {
37172
37713
  },
37173
37714
  Html {
37174
37715
  #[serde(flatten)]
37175
- _0: HtmlMetadata,
37716
+ _0: Box<HtmlMetadata>,
37176
37717
  },
37177
37718
  Ocr {
37178
37719
  #[serde(flatten)]
@@ -37237,7 +37778,92 @@ impl magnus::IntoValue for FormatMetadata {
37237
37778
 
37238
37779
  impl magnus::TryConvert for FormatMetadata {
37239
37780
  fn try_convert(val: magnus::Value) -> Result<Self, magnus::Error> {
37240
- // For data enums with fields (e.g., PageAction), try to deserialize from JSON first.
37781
+ // Generated Data variants carry native DTOs, not JSON-compatible display strings.
37782
+ let class_name: Option<String> = val.class().funcall("name", ())?;
37783
+ if class_name.as_deref() == Some("Xberg::FormatMetadataPdf") {
37784
+ let payload: PdfMetadata = val.funcall("value", ())?;
37785
+ return Ok(Self::Pdf { _0: payload });
37786
+ }
37787
+ if class_name.as_deref() == Some("Xberg::FormatMetadataDocx") {
37788
+ let payload: DocxMetadata = val.funcall("value", ())?;
37789
+ return Ok(Self::Docx { _0: Box::new(payload) });
37790
+ }
37791
+ if class_name.as_deref() == Some("Xberg::FormatMetadataExcel") {
37792
+ let payload: ExcelMetadata = val.funcall("value", ())?;
37793
+ return Ok(Self::Excel { _0: payload });
37794
+ }
37795
+ if class_name.as_deref() == Some("Xberg::FormatMetadataEmail") {
37796
+ let payload: EmailMetadata = val.funcall("value", ())?;
37797
+ return Ok(Self::Email { _0: payload });
37798
+ }
37799
+ if class_name.as_deref() == Some("Xberg::FormatMetadataPptx") {
37800
+ let payload: PptxMetadata = val.funcall("value", ())?;
37801
+ return Ok(Self::Pptx { _0: payload });
37802
+ }
37803
+ if class_name.as_deref() == Some("Xberg::FormatMetadataArchive") {
37804
+ let payload: ArchiveMetadata = val.funcall("value", ())?;
37805
+ return Ok(Self::Archive { _0: payload });
37806
+ }
37807
+ if class_name.as_deref() == Some("Xberg::FormatMetadataImage") {
37808
+ let payload: ImageMetadata = val.funcall("value", ())?;
37809
+ return Ok(Self::Image { _0: payload });
37810
+ }
37811
+ if class_name.as_deref() == Some("Xberg::FormatMetadataXml") {
37812
+ let payload: XmlMetadata = val.funcall("value", ())?;
37813
+ return Ok(Self::Xml { _0: payload });
37814
+ }
37815
+ if class_name.as_deref() == Some("Xberg::FormatMetadataText") {
37816
+ let payload: TextMetadata = val.funcall("value", ())?;
37817
+ return Ok(Self::Text { _0: payload });
37818
+ }
37819
+ if class_name.as_deref() == Some("Xberg::FormatMetadataHtml") {
37820
+ let payload: HtmlMetadata = val.funcall("value", ())?;
37821
+ return Ok(Self::Html { _0: Box::new(payload) });
37822
+ }
37823
+ if class_name.as_deref() == Some("Xberg::FormatMetadataOcr") {
37824
+ let payload: OcrMetadata = val.funcall("value", ())?;
37825
+ return Ok(Self::Ocr { _0: payload });
37826
+ }
37827
+ if class_name.as_deref() == Some("Xberg::FormatMetadataCsv") {
37828
+ let payload: CsvMetadata = val.funcall("value", ())?;
37829
+ return Ok(Self::Csv { _0: payload });
37830
+ }
37831
+ if class_name.as_deref() == Some("Xberg::FormatMetadataBibtex") {
37832
+ let payload: BibtexMetadata = val.funcall("value", ())?;
37833
+ return Ok(Self::Bibtex { _0: payload });
37834
+ }
37835
+ if class_name.as_deref() == Some("Xberg::FormatMetadataCitation") {
37836
+ let payload: CitationMetadata = val.funcall("value", ())?;
37837
+ return Ok(Self::Citation { _0: payload });
37838
+ }
37839
+ if class_name.as_deref() == Some("Xberg::FormatMetadataFictionBook") {
37840
+ let payload: FictionBookMetadata = val.funcall("value", ())?;
37841
+ return Ok(Self::FictionBook { _0: payload });
37842
+ }
37843
+ if class_name.as_deref() == Some("Xberg::FormatMetadataDbf") {
37844
+ let payload: DbfMetadata = val.funcall("value", ())?;
37845
+ return Ok(Self::Dbf { _0: payload });
37846
+ }
37847
+ if class_name.as_deref() == Some("Xberg::FormatMetadataJats") {
37848
+ let payload: JatsMetadata = val.funcall("value", ())?;
37849
+ return Ok(Self::Jats { _0: payload });
37850
+ }
37851
+ if class_name.as_deref() == Some("Xberg::FormatMetadataEpub") {
37852
+ let payload: EpubMetadata = val.funcall("value", ())?;
37853
+ return Ok(Self::Epub { _0: payload });
37854
+ }
37855
+ if class_name.as_deref() == Some("Xberg::FormatMetadataPst") {
37856
+ let payload: PstMetadata = val.funcall("value", ())?;
37857
+ return Ok(Self::Pst { _0: payload });
37858
+ }
37859
+ if class_name.as_deref() == Some("Xberg::FormatMetadataAudio") {
37860
+ let payload: AudioMetadata = val.funcall("value", ())?;
37861
+ return Ok(Self::Audio { _0: payload });
37862
+ }
37863
+ if class_name.as_deref() == Some("Xberg::FormatMetadataCode") {
37864
+ let payload: CodeMetadata = val.funcall("value", ())?;
37865
+ return Ok(Self::Code { _0: payload });
37866
+ } // For data enums with fields (e.g., PageAction), try to deserialize from JSON first.
37241
37867
  // For unit enums or when passed as a string, fall back to string-based conversion.
37242
37868
  let json_str: String = if let Ok(s) = <String as magnus::TryConvert>::try_convert(val) {
37243
37869
  s
@@ -37269,6 +37895,7 @@ impl magnus::TryConvert for FormatMetadata {
37269
37895
 
37270
37896
  unsafe impl IntoValueFromNative for FormatMetadata {}
37271
37897
  unsafe impl TryConvertOwned for FormatMetadata {}
37898
+
37272
37899
  impl FormatMetadata {
37273
37900
  pub fn pdf(&self) -> Option<&PdfMetadata> {
37274
37901
  match self {
@@ -37278,7 +37905,7 @@ impl FormatMetadata {
37278
37905
  }
37279
37906
  pub fn docx(&self) -> Option<&DocxMetadata> {
37280
37907
  match self {
37281
- Self::Docx { _0 } => Some(_0),
37908
+ Self::Docx { _0 } => Some(&**_0),
37282
37909
  _ => None,
37283
37910
  }
37284
37911
  }
@@ -37326,7 +37953,7 @@ impl FormatMetadata {
37326
37953
  }
37327
37954
  pub fn html(&self) -> Option<&HtmlMetadata> {
37328
37955
  match self {
37329
- Self::Html { _0 } => Some(_0),
37956
+ Self::Html { _0 } => Some(&**_0),
37330
37957
  _ => None,
37331
37958
  }
37332
37959
  }
@@ -37398,7 +38025,7 @@ impl FormatMetadata {
37398
38025
  }
37399
38026
  }
37400
38027
 
37401
- #[derive(Clone, Copy, PartialEq, Eq, Debug, serde::Serialize, serde::Deserialize)]
38028
+ #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, serde::Serialize, serde::Deserialize)]
37402
38029
  #[serde(rename_all = "snake_case")]
37403
38030
  pub enum FormulaModel {
37404
38031
  LatexOcr,
@@ -37421,7 +38048,14 @@ impl magnus::IntoValue for FormulaModel {
37421
38048
 
37422
38049
  impl magnus::TryConvert for FormulaModel {
37423
38050
  fn try_convert(val: magnus::Value) -> Result<Self, magnus::Error> {
37424
- let s: String = magnus::TryConvert::try_convert(val)?;
38051
+ // IntoValue emits Symbols, including when this enum is a Hash key.
38052
+ // Accept that exact representation as well as String inputs; do not
38053
+ // coerce arbitrary objects through to_s.
38054
+ let s: String = if let Some(symbol) = magnus::Symbol::from_value(val) {
38055
+ symbol.name()?.into_owned()
38056
+ } else {
38057
+ magnus::TryConvert::try_convert(val)?
38058
+ };
37425
38059
  // Accept the real serde wire value, the legacy always-snake_case symbol Magnus used to
37426
38060
  // emit unconditionally, and the verbatim PascalCase Rust variant name, so fixtures and
37427
38061
  // existing consumer code written in any of those styles keep working.
@@ -37438,7 +38072,7 @@ impl magnus::TryConvert for FormulaModel {
37438
38072
  unsafe impl IntoValueFromNative for FormulaModel {}
37439
38073
  unsafe impl TryConvertOwned for FormulaModel {}
37440
38074
 
37441
- #[derive(Clone, Copy, PartialEq, Eq, Debug, serde::Serialize, serde::Deserialize)]
38075
+ #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, serde::Serialize, serde::Deserialize)]
37442
38076
  #[serde(rename_all = "snake_case")]
37443
38077
  pub enum GlmOcrLayoutMode {
37444
38078
  WholePage,
@@ -37463,7 +38097,14 @@ impl magnus::IntoValue for GlmOcrLayoutMode {
37463
38097
 
37464
38098
  impl magnus::TryConvert for GlmOcrLayoutMode {
37465
38099
  fn try_convert(val: magnus::Value) -> Result<Self, magnus::Error> {
37466
- let s: String = magnus::TryConvert::try_convert(val)?;
38100
+ // IntoValue emits Symbols, including when this enum is a Hash key.
38101
+ // Accept that exact representation as well as String inputs; do not
38102
+ // coerce arbitrary objects through to_s.
38103
+ let s: String = if let Some(symbol) = magnus::Symbol::from_value(val) {
38104
+ symbol.name()?.into_owned()
38105
+ } else {
38106
+ magnus::TryConvert::try_convert(val)?
38107
+ };
37467
38108
  // Accept the real serde wire value, the legacy always-snake_case symbol Magnus used to
37468
38109
  // emit unconditionally, and the verbatim PascalCase Rust variant name, so fixtures and
37469
38110
  // existing consumer code written in any of those styles keep working.
@@ -37481,7 +38122,7 @@ impl magnus::TryConvert for GlmOcrLayoutMode {
37481
38122
  unsafe impl IntoValueFromNative for GlmOcrLayoutMode {}
37482
38123
  unsafe impl TryConvertOwned for GlmOcrLayoutMode {}
37483
38124
 
37484
- #[derive(Clone, Copy, PartialEq, Eq, Debug, serde::Serialize, serde::Deserialize)]
38125
+ #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, serde::Serialize, serde::Deserialize)]
37485
38126
  #[serde(rename_all = "lowercase")]
37486
38127
  pub enum GlmOcrTaskKind {
37487
38128
  Ocr,
@@ -37512,7 +38153,14 @@ impl magnus::IntoValue for GlmOcrTaskKind {
37512
38153
 
37513
38154
  impl magnus::TryConvert for GlmOcrTaskKind {
37514
38155
  fn try_convert(val: magnus::Value) -> Result<Self, magnus::Error> {
37515
- let s: String = magnus::TryConvert::try_convert(val)?;
38156
+ // IntoValue emits Symbols, including when this enum is a Hash key.
38157
+ // Accept that exact representation as well as String inputs; do not
38158
+ // coerce arbitrary objects through to_s.
38159
+ let s: String = if let Some(symbol) = magnus::Symbol::from_value(val) {
38160
+ symbol.name()?.into_owned()
38161
+ } else {
38162
+ magnus::TryConvert::try_convert(val)?
38163
+ };
37516
38164
  // Accept the real serde wire value, the legacy always-snake_case symbol Magnus used to
37517
38165
  // emit unconditionally, and the verbatim PascalCase Rust variant name, so fixtures and
37518
38166
  // existing consumer code written in any of those styles keep working.
@@ -37533,7 +38181,7 @@ impl magnus::TryConvert for GlmOcrTaskKind {
37533
38181
  unsafe impl IntoValueFromNative for GlmOcrTaskKind {}
37534
38182
  unsafe impl TryConvertOwned for GlmOcrTaskKind {}
37535
38183
 
37536
- #[derive(Clone, Copy, PartialEq, Eq, Debug, serde::Serialize, serde::Deserialize)]
38184
+ #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, serde::Serialize, serde::Deserialize)]
37537
38185
  pub enum HeadingStyle {
37538
38186
  Underlined,
37539
38187
  Atx,
@@ -37559,7 +38207,14 @@ impl magnus::IntoValue for HeadingStyle {
37559
38207
 
37560
38208
  impl magnus::TryConvert for HeadingStyle {
37561
38209
  fn try_convert(val: magnus::Value) -> Result<Self, magnus::Error> {
37562
- let s: String = magnus::TryConvert::try_convert(val)?;
38210
+ // IntoValue emits Symbols, including when this enum is a Hash key.
38211
+ // Accept that exact representation as well as String inputs; do not
38212
+ // coerce arbitrary objects through to_s.
38213
+ let s: String = if let Some(symbol) = magnus::Symbol::from_value(val) {
38214
+ symbol.name()?.into_owned()
38215
+ } else {
38216
+ magnus::TryConvert::try_convert(val)?
38217
+ };
37563
38218
  // Accept the real serde wire value, the legacy always-snake_case symbol Magnus used to
37564
38219
  // emit unconditionally, and the verbatim PascalCase Rust variant name, so fixtures and
37565
38220
  // existing consumer code written in any of those styles keep working.
@@ -37578,7 +38233,7 @@ impl magnus::TryConvert for HeadingStyle {
37578
38233
  unsafe impl IntoValueFromNative for HeadingStyle {}
37579
38234
  unsafe impl TryConvertOwned for HeadingStyle {}
37580
38235
 
37581
- #[derive(Clone, Copy, PartialEq, Eq, Debug, serde::Serialize, serde::Deserialize)]
38236
+ #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, serde::Serialize, serde::Deserialize)]
37582
38237
  pub enum HighlightStyle {
37583
38238
  DoubleEqual,
37584
38239
  Html,
@@ -37606,7 +38261,14 @@ impl magnus::IntoValue for HighlightStyle {
37606
38261
 
37607
38262
  impl magnus::TryConvert for HighlightStyle {
37608
38263
  fn try_convert(val: magnus::Value) -> Result<Self, magnus::Error> {
37609
- let s: String = magnus::TryConvert::try_convert(val)?;
38264
+ // IntoValue emits Symbols, including when this enum is a Hash key.
38265
+ // Accept that exact representation as well as String inputs; do not
38266
+ // coerce arbitrary objects through to_s.
38267
+ let s: String = if let Some(symbol) = magnus::Symbol::from_value(val) {
38268
+ symbol.name()?.into_owned()
38269
+ } else {
38270
+ magnus::TryConvert::try_convert(val)?
38271
+ };
37610
38272
  // Accept the real serde wire value, the legacy always-snake_case symbol Magnus used to
37611
38273
  // emit unconditionally, and the verbatim PascalCase Rust variant name, so fixtures and
37612
38274
  // existing consumer code written in any of those styles keep working.
@@ -37635,14 +38297,6 @@ pub enum HostMatcher {
37635
38297
  Cidr { value: String },
37636
38298
  }
37637
38299
 
37638
- impl Default for HostMatcher {
37639
- fn default() -> Self {
37640
- Self::Exact {
37641
- value: Default::default(),
37642
- }
37643
- }
37644
- }
37645
-
37646
38300
  impl magnus::IntoValue for HostMatcher {
37647
38301
  fn into_value_with(self, handle: &Ruby) -> magnus::Value {
37648
38302
  match serde_json::to_value(&self) {
@@ -37654,7 +38308,8 @@ impl magnus::IntoValue for HostMatcher {
37654
38308
 
37655
38309
  impl magnus::TryConvert for HostMatcher {
37656
38310
  fn try_convert(val: magnus::Value) -> Result<Self, magnus::Error> {
37657
- // For data enums with fields (e.g., PageAction), try to deserialize from JSON first.
38311
+ // Generated Data variants carry native DTOs, not JSON-compatible display strings.
38312
+ let class_name: Option<String> = val.class().funcall("name", ())?; // For data enums with fields (e.g., PageAction), try to deserialize from JSON first.
37658
38313
  // For unit enums or when passed as a string, fall back to string-based conversion.
37659
38314
  let json_str: String = if let Ok(s) = <String as magnus::TryConvert>::try_convert(val) {
37660
38315
  s
@@ -37686,9 +38341,10 @@ impl magnus::TryConvert for HostMatcher {
37686
38341
 
37687
38342
  unsafe impl IntoValueFromNative for HostMatcher {}
37688
38343
  unsafe impl TryConvertOwned for HostMatcher {}
38344
+
37689
38345
  impl HostMatcher {}
37690
38346
 
37691
- #[derive(Clone, Copy, PartialEq, Eq, Debug, serde::Serialize, serde::Deserialize)]
38347
+ #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, serde::Serialize, serde::Deserialize)]
37692
38348
  #[serde(rename_all = "lowercase")]
37693
38349
  pub enum HtmlTheme {
37694
38350
  Default,
@@ -37719,7 +38375,14 @@ impl magnus::IntoValue for HtmlTheme {
37719
38375
 
37720
38376
  impl magnus::TryConvert for HtmlTheme {
37721
38377
  fn try_convert(val: magnus::Value) -> Result<Self, magnus::Error> {
37722
- let s: String = magnus::TryConvert::try_convert(val)?;
38378
+ // IntoValue emits Symbols, including when this enum is a Hash key.
38379
+ // Accept that exact representation as well as String inputs; do not
38380
+ // coerce arbitrary objects through to_s.
38381
+ let s: String = if let Some(symbol) = magnus::Symbol::from_value(val) {
38382
+ symbol.name()?.into_owned()
38383
+ } else {
38384
+ magnus::TryConvert::try_convert(val)?
38385
+ };
37723
38386
  // Accept the real serde wire value, the legacy always-snake_case symbol Magnus used to
37724
38387
  // emit unconditionally, and the verbatim PascalCase Rust variant name, so fixtures and
37725
38388
  // existing consumer code written in any of those styles keep working.
@@ -37740,7 +38403,7 @@ impl magnus::TryConvert for HtmlTheme {
37740
38403
  unsafe impl IntoValueFromNative for HtmlTheme {}
37741
38404
  unsafe impl TryConvertOwned for HtmlTheme {}
37742
38405
 
37743
- #[derive(Clone, Copy, PartialEq, Eq, Debug, serde::Serialize, serde::Deserialize)]
38406
+ #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, serde::Serialize, serde::Deserialize)]
37744
38407
  pub enum HtmlToMarkdownRsOutputFormat {
37745
38408
  Markdown,
37746
38409
  Djot,
@@ -37766,7 +38429,14 @@ impl magnus::IntoValue for HtmlToMarkdownRsOutputFormat {
37766
38429
 
37767
38430
  impl magnus::TryConvert for HtmlToMarkdownRsOutputFormat {
37768
38431
  fn try_convert(val: magnus::Value) -> Result<Self, magnus::Error> {
37769
- let s: String = magnus::TryConvert::try_convert(val)?;
38432
+ // IntoValue emits Symbols, including when this enum is a Hash key.
38433
+ // Accept that exact representation as well as String inputs; do not
38434
+ // coerce arbitrary objects through to_s.
38435
+ let s: String = if let Some(symbol) = magnus::Symbol::from_value(val) {
38436
+ symbol.name()?.into_owned()
38437
+ } else {
38438
+ magnus::TryConvert::try_convert(val)?
38439
+ };
37770
38440
  // Accept the real serde wire value, the legacy always-snake_case symbol Magnus used to
37771
38441
  // emit unconditionally, and the verbatim PascalCase Rust variant name, so fixtures and
37772
38442
  // existing consumer code written in any of those styles keep working.
@@ -37785,7 +38455,7 @@ impl magnus::TryConvert for HtmlToMarkdownRsOutputFormat {
37785
38455
  unsafe impl IntoValueFromNative for HtmlToMarkdownRsOutputFormat {}
37786
38456
  unsafe impl TryConvertOwned for HtmlToMarkdownRsOutputFormat {}
37787
38457
 
37788
- #[derive(Clone, Copy, PartialEq, Eq, Debug, serde::Serialize, serde::Deserialize)]
38458
+ #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, serde::Serialize, serde::Deserialize)]
37789
38459
  #[serde(rename_all = "snake_case")]
37790
38460
  pub enum ImageKind {
37791
38461
  Photograph,
@@ -37830,7 +38500,14 @@ impl magnus::IntoValue for ImageKind {
37830
38500
 
37831
38501
  impl magnus::TryConvert for ImageKind {
37832
38502
  fn try_convert(val: magnus::Value) -> Result<Self, magnus::Error> {
37833
- let s: String = magnus::TryConvert::try_convert(val)?;
38503
+ // IntoValue emits Symbols, including when this enum is a Hash key.
38504
+ // Accept that exact representation as well as String inputs; do not
38505
+ // coerce arbitrary objects through to_s.
38506
+ let s: String = if let Some(symbol) = magnus::Symbol::from_value(val) {
38507
+ symbol.name()?.into_owned()
38508
+ } else {
38509
+ magnus::TryConvert::try_convert(val)?
38510
+ };
37834
38511
  // Accept the real serde wire value, the legacy always-snake_case symbol Magnus used to
37835
38512
  // emit unconditionally, and the verbatim PascalCase Rust variant name, so fixtures and
37836
38513
  // existing consumer code written in any of those styles keep working.
@@ -37887,7 +38564,8 @@ impl magnus::IntoValue for ImageOutputFormat {
37887
38564
 
37888
38565
  impl magnus::TryConvert for ImageOutputFormat {
37889
38566
  fn try_convert(val: magnus::Value) -> Result<Self, magnus::Error> {
37890
- // For data enums with fields (e.g., PageAction), try to deserialize from JSON first.
38567
+ // Generated Data variants carry native DTOs, not JSON-compatible display strings.
38568
+ let class_name: Option<String> = val.class().funcall("name", ())?; // For data enums with fields (e.g., PageAction), try to deserialize from JSON first.
37891
38569
  // For unit enums or when passed as a string, fall back to string-based conversion.
37892
38570
  let json_str: String = if let Ok(s) = <String as magnus::TryConvert>::try_convert(val) {
37893
38571
  s
@@ -37919,9 +38597,10 @@ impl magnus::TryConvert for ImageOutputFormat {
37919
38597
 
37920
38598
  unsafe impl IntoValueFromNative for ImageOutputFormat {}
37921
38599
  unsafe impl TryConvertOwned for ImageOutputFormat {}
38600
+
37922
38601
  impl ImageOutputFormat {}
37923
38602
 
37924
- #[derive(Clone, Copy, PartialEq, Eq, Debug, serde::Serialize, serde::Deserialize)]
38603
+ #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, serde::Serialize, serde::Deserialize)]
37925
38604
  #[serde(rename_all = "lowercase")]
37926
38605
  pub enum ImageType {
37927
38606
  #[serde(rename = "data-uri")]
@@ -37952,7 +38631,14 @@ impl magnus::IntoValue for ImageType {
37952
38631
 
37953
38632
  impl magnus::TryConvert for ImageType {
37954
38633
  fn try_convert(val: magnus::Value) -> Result<Self, magnus::Error> {
37955
- let s: String = magnus::TryConvert::try_convert(val)?;
38634
+ // IntoValue emits Symbols, including when this enum is a Hash key.
38635
+ // Accept that exact representation as well as String inputs; do not
38636
+ // coerce arbitrary objects through to_s.
38637
+ let s: String = if let Some(symbol) = magnus::Symbol::from_value(val) {
38638
+ symbol.name()?.into_owned()
38639
+ } else {
38640
+ magnus::TryConvert::try_convert(val)?
38641
+ };
37956
38642
  // Accept the real serde wire value, the legacy always-snake_case symbol Magnus used to
37957
38643
  // emit unconditionally, and the verbatim PascalCase Rust variant name, so fixtures and
37958
38644
  // existing consumer code written in any of those styles keep working.
@@ -37972,7 +38658,7 @@ impl magnus::TryConvert for ImageType {
37972
38658
  unsafe impl IntoValueFromNative for ImageType {}
37973
38659
  unsafe impl TryConvertOwned for ImageType {}
37974
38660
 
37975
- #[derive(Clone, Copy, PartialEq, Eq, Debug, serde::Serialize, serde::Deserialize)]
38661
+ #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, serde::Serialize, serde::Deserialize)]
37976
38662
  #[serde(rename_all = "snake_case")]
37977
38663
  pub enum InlineType {
37978
38664
  Text,
@@ -38025,7 +38711,14 @@ impl magnus::IntoValue for InlineType {
38025
38711
 
38026
38712
  impl magnus::TryConvert for InlineType {
38027
38713
  fn try_convert(val: magnus::Value) -> Result<Self, magnus::Error> {
38028
- let s: String = magnus::TryConvert::try_convert(val)?;
38714
+ // IntoValue emits Symbols, including when this enum is a Hash key.
38715
+ // Accept that exact representation as well as String inputs; do not
38716
+ // coerce arbitrary objects through to_s.
38717
+ let s: String = if let Some(symbol) = magnus::Symbol::from_value(val) {
38718
+ symbol.name()?.into_owned()
38719
+ } else {
38720
+ magnus::TryConvert::try_convert(val)?
38721
+ };
38029
38722
  // Accept the real serde wire value, the legacy always-snake_case symbol Magnus used to
38030
38723
  // emit unconditionally, and the verbatim PascalCase Rust variant name, so fixtures and
38031
38724
  // existing consumer code written in any of those styles keep working.
@@ -38057,7 +38750,7 @@ impl magnus::TryConvert for InlineType {
38057
38750
  unsafe impl IntoValueFromNative for InlineType {}
38058
38751
  unsafe impl TryConvertOwned for InlineType {}
38059
38752
 
38060
- #[derive(Clone, Copy, PartialEq, Eq, Debug, serde::Serialize, serde::Deserialize)]
38753
+ #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, serde::Serialize, serde::Deserialize)]
38061
38754
  #[serde(rename_all = "lowercase")]
38062
38755
  pub enum JupyterCellRendering {
38063
38756
  Source,
@@ -38084,7 +38777,14 @@ impl magnus::IntoValue for JupyterCellRendering {
38084
38777
 
38085
38778
  impl magnus::TryConvert for JupyterCellRendering {
38086
38779
  fn try_convert(val: magnus::Value) -> Result<Self, magnus::Error> {
38087
- let s: String = magnus::TryConvert::try_convert(val)?;
38780
+ // IntoValue emits Symbols, including when this enum is a Hash key.
38781
+ // Accept that exact representation as well as String inputs; do not
38782
+ // coerce arbitrary objects through to_s.
38783
+ let s: String = if let Some(symbol) = magnus::Symbol::from_value(val) {
38784
+ symbol.name()?.into_owned()
38785
+ } else {
38786
+ magnus::TryConvert::try_convert(val)?
38787
+ };
38088
38788
  // Accept the real serde wire value, the legacy always-snake_case symbol Magnus used to
38089
38789
  // emit unconditionally, and the verbatim PascalCase Rust variant name, so fixtures and
38090
38790
  // existing consumer code written in any of those styles keep working.
@@ -38103,7 +38803,7 @@ impl magnus::TryConvert for JupyterCellRendering {
38103
38803
  unsafe impl IntoValueFromNative for JupyterCellRendering {}
38104
38804
  unsafe impl TryConvertOwned for JupyterCellRendering {}
38105
38805
 
38106
- #[derive(Clone, Copy, PartialEq, Eq, Debug, serde::Serialize, serde::Deserialize)]
38806
+ #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, serde::Serialize, serde::Deserialize)]
38107
38807
  #[serde(rename_all = "lowercase")]
38108
38808
  pub enum KeywordAlgorithm {
38109
38809
  Yake,
@@ -38128,7 +38828,14 @@ impl magnus::IntoValue for KeywordAlgorithm {
38128
38828
 
38129
38829
  impl magnus::TryConvert for KeywordAlgorithm {
38130
38830
  fn try_convert(val: magnus::Value) -> Result<Self, magnus::Error> {
38131
- let s: String = magnus::TryConvert::try_convert(val)?;
38831
+ // IntoValue emits Symbols, including when this enum is a Hash key.
38832
+ // Accept that exact representation as well as String inputs; do not
38833
+ // coerce arbitrary objects through to_s.
38834
+ let s: String = if let Some(symbol) = magnus::Symbol::from_value(val) {
38835
+ symbol.name()?.into_owned()
38836
+ } else {
38837
+ magnus::TryConvert::try_convert(val)?
38838
+ };
38132
38839
  // Accept the real serde wire value, the legacy always-snake_case symbol Magnus used to
38133
38840
  // emit unconditionally, and the verbatim PascalCase Rust variant name, so fixtures and
38134
38841
  // existing consumer code written in any of those styles keep working.
@@ -38183,7 +38890,8 @@ impl magnus::IntoValue for LateInteractionModelType {
38183
38890
 
38184
38891
  impl magnus::TryConvert for LateInteractionModelType {
38185
38892
  fn try_convert(val: magnus::Value) -> Result<Self, magnus::Error> {
38186
- // For data enums with fields (e.g., PageAction), try to deserialize from JSON first.
38893
+ // Generated Data variants carry native DTOs, not JSON-compatible display strings.
38894
+ let class_name: Option<String> = val.class().funcall("name", ())?; // For data enums with fields (e.g., PageAction), try to deserialize from JSON first.
38187
38895
  // For unit enums or when passed as a string, fall back to string-based conversion.
38188
38896
  let json_str: String = if let Ok(s) = <String as magnus::TryConvert>::try_convert(val) {
38189
38897
  s
@@ -38215,9 +38923,10 @@ impl magnus::TryConvert for LateInteractionModelType {
38215
38923
 
38216
38924
  unsafe impl IntoValueFromNative for LateInteractionModelType {}
38217
38925
  unsafe impl TryConvertOwned for LateInteractionModelType {}
38926
+
38218
38927
  impl LateInteractionModelType {}
38219
38928
 
38220
- #[derive(Clone, Copy, PartialEq, Eq, Debug, serde::Serialize, serde::Deserialize)]
38929
+ #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, serde::Serialize, serde::Deserialize)]
38221
38930
  #[serde(rename_all = "snake_case")]
38222
38931
  pub enum LayoutClass {
38223
38932
  Caption,
@@ -38274,7 +38983,14 @@ impl magnus::IntoValue for LayoutClass {
38274
38983
 
38275
38984
  impl magnus::TryConvert for LayoutClass {
38276
38985
  fn try_convert(val: magnus::Value) -> Result<Self, magnus::Error> {
38277
- let s: String = magnus::TryConvert::try_convert(val)?;
38986
+ // IntoValue emits Symbols, including when this enum is a Hash key.
38987
+ // Accept that exact representation as well as String inputs; do not
38988
+ // coerce arbitrary objects through to_s.
38989
+ let s: String = if let Some(symbol) = magnus::Symbol::from_value(val) {
38990
+ symbol.name()?.into_owned()
38991
+ } else {
38992
+ magnus::TryConvert::try_convert(val)?
38993
+ };
38278
38994
  // Accept the real serde wire value, the legacy always-snake_case symbol Magnus used to
38279
38995
  // emit unconditionally, and the verbatim PascalCase Rust variant name, so fixtures and
38280
38996
  // existing consumer code written in any of those styles keep working.
@@ -38308,7 +39024,7 @@ impl magnus::TryConvert for LayoutClass {
38308
39024
  unsafe impl IntoValueFromNative for LayoutClass {}
38309
39025
  unsafe impl TryConvertOwned for LayoutClass {}
38310
39026
 
38311
- #[derive(Clone, Copy, PartialEq, Eq, Debug, serde::Serialize, serde::Deserialize)]
39027
+ #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, serde::Serialize, serde::Deserialize)]
38312
39028
  #[serde(rename_all = "snake_case")]
38313
39029
  pub enum LayoutStrategy {
38314
39030
  Always,
@@ -38333,7 +39049,14 @@ impl magnus::IntoValue for LayoutStrategy {
38333
39049
 
38334
39050
  impl magnus::TryConvert for LayoutStrategy {
38335
39051
  fn try_convert(val: magnus::Value) -> Result<Self, magnus::Error> {
38336
- let s: String = magnus::TryConvert::try_convert(val)?;
39052
+ // IntoValue emits Symbols, including when this enum is a Hash key.
39053
+ // Accept that exact representation as well as String inputs; do not
39054
+ // coerce arbitrary objects through to_s.
39055
+ let s: String = if let Some(symbol) = magnus::Symbol::from_value(val) {
39056
+ symbol.name()?.into_owned()
39057
+ } else {
39058
+ magnus::TryConvert::try_convert(val)?
39059
+ };
38337
39060
  // Accept the real serde wire value, the legacy always-snake_case symbol Magnus used to
38338
39061
  // emit unconditionally, and the verbatim PascalCase Rust variant name, so fixtures and
38339
39062
  // existing consumer code written in any of those styles keep working.
@@ -38351,7 +39074,7 @@ impl magnus::TryConvert for LayoutStrategy {
38351
39074
  unsafe impl IntoValueFromNative for LayoutStrategy {}
38352
39075
  unsafe impl TryConvertOwned for LayoutStrategy {}
38353
39076
 
38354
- #[derive(Clone, Copy, PartialEq, Eq, Debug, serde::Serialize, serde::Deserialize)]
39077
+ #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, serde::Serialize, serde::Deserialize)]
38355
39078
  pub enum LinkStyle {
38356
39079
  Inline,
38357
39080
  Reference,
@@ -38375,7 +39098,14 @@ impl magnus::IntoValue for LinkStyle {
38375
39098
 
38376
39099
  impl magnus::TryConvert for LinkStyle {
38377
39100
  fn try_convert(val: magnus::Value) -> Result<Self, magnus::Error> {
38378
- let s: String = magnus::TryConvert::try_convert(val)?;
39101
+ // IntoValue emits Symbols, including when this enum is a Hash key.
39102
+ // Accept that exact representation as well as String inputs; do not
39103
+ // coerce arbitrary objects through to_s.
39104
+ let s: String = if let Some(symbol) = magnus::Symbol::from_value(val) {
39105
+ symbol.name()?.into_owned()
39106
+ } else {
39107
+ magnus::TryConvert::try_convert(val)?
39108
+ };
38379
39109
  // Accept the real serde wire value, the legacy always-snake_case symbol Magnus used to
38380
39110
  // emit unconditionally, and the verbatim PascalCase Rust variant name, so fixtures and
38381
39111
  // existing consumer code written in any of those styles keep working.
@@ -38393,7 +39123,7 @@ impl magnus::TryConvert for LinkStyle {
38393
39123
  unsafe impl IntoValueFromNative for LinkStyle {}
38394
39124
  unsafe impl TryConvertOwned for LinkStyle {}
38395
39125
 
38396
- #[derive(Clone, Copy, PartialEq, Eq, Debug, serde::Serialize, serde::Deserialize)]
39126
+ #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, serde::Serialize, serde::Deserialize)]
38397
39127
  #[serde(rename_all = "lowercase")]
38398
39128
  pub enum LinkType {
38399
39129
  Anchor,
@@ -38426,7 +39156,14 @@ impl magnus::IntoValue for LinkType {
38426
39156
 
38427
39157
  impl magnus::TryConvert for LinkType {
38428
39158
  fn try_convert(val: magnus::Value) -> Result<Self, magnus::Error> {
38429
- let s: String = magnus::TryConvert::try_convert(val)?;
39159
+ // IntoValue emits Symbols, including when this enum is a Hash key.
39160
+ // Accept that exact representation as well as String inputs; do not
39161
+ // coerce arbitrary objects through to_s.
39162
+ let s: String = if let Some(symbol) = magnus::Symbol::from_value(val) {
39163
+ symbol.name()?.into_owned()
39164
+ } else {
39165
+ magnus::TryConvert::try_convert(val)?
39166
+ };
38430
39167
  // Accept the real serde wire value, the legacy always-snake_case symbol Magnus used to
38431
39168
  // emit unconditionally, and the verbatim PascalCase Rust variant name, so fixtures and
38432
39169
  // existing consumer code written in any of those styles keep working.
@@ -38448,7 +39185,7 @@ impl magnus::TryConvert for LinkType {
38448
39185
  unsafe impl IntoValueFromNative for LinkType {}
38449
39186
  unsafe impl TryConvertOwned for LinkType {}
38450
39187
 
38451
- #[derive(Clone, Copy, PartialEq, Eq, Debug, serde::Serialize, serde::Deserialize)]
39188
+ #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, serde::Serialize, serde::Deserialize)]
38452
39189
  pub enum ListIndentType {
38453
39190
  Spaces,
38454
39191
  Tabs,
@@ -38472,7 +39209,14 @@ impl magnus::IntoValue for ListIndentType {
38472
39209
 
38473
39210
  impl magnus::TryConvert for ListIndentType {
38474
39211
  fn try_convert(val: magnus::Value) -> Result<Self, magnus::Error> {
38475
- let s: String = magnus::TryConvert::try_convert(val)?;
39212
+ // IntoValue emits Symbols, including when this enum is a Hash key.
39213
+ // Accept that exact representation as well as String inputs; do not
39214
+ // coerce arbitrary objects through to_s.
39215
+ let s: String = if let Some(symbol) = magnus::Symbol::from_value(val) {
39216
+ symbol.name()?.into_owned()
39217
+ } else {
39218
+ magnus::TryConvert::try_convert(val)?
39219
+ };
38476
39220
  // Accept the real serde wire value, the legacy always-snake_case symbol Magnus used to
38477
39221
  // emit unconditionally, and the verbatim PascalCase Rust variant name, so fixtures and
38478
39222
  // existing consumer code written in any of those styles keep working.
@@ -38490,7 +39234,7 @@ impl magnus::TryConvert for ListIndentType {
38490
39234
  unsafe impl IntoValueFromNative for ListIndentType {}
38491
39235
  unsafe impl TryConvertOwned for ListIndentType {}
38492
39236
 
38493
- #[derive(Clone, Copy, PartialEq, Eq, Debug, serde::Serialize, serde::Deserialize)]
39237
+ #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, serde::Serialize, serde::Deserialize)]
38494
39238
  pub enum ListType {
38495
39239
  Bullet,
38496
39240
  Numbered,
@@ -38518,7 +39262,14 @@ impl magnus::IntoValue for ListType {
38518
39262
 
38519
39263
  impl magnus::TryConvert for ListType {
38520
39264
  fn try_convert(val: magnus::Value) -> Result<Self, magnus::Error> {
38521
- let s: String = magnus::TryConvert::try_convert(val)?;
39265
+ // IntoValue emits Symbols, including when this enum is a Hash key.
39266
+ // Accept that exact representation as well as String inputs; do not
39267
+ // coerce arbitrary objects through to_s.
39268
+ let s: String = if let Some(symbol) = magnus::Symbol::from_value(val) {
39269
+ symbol.name()?.into_owned()
39270
+ } else {
39271
+ magnus::TryConvert::try_convert(val)?
39272
+ };
38522
39273
  // Accept the real serde wire value, the legacy always-snake_case symbol Magnus used to
38523
39274
  // emit unconditionally, and the verbatim PascalCase Rust variant name, so fixtures and
38524
39275
  // existing consumer code written in any of those styles keep working.
@@ -38538,7 +39289,7 @@ impl magnus::TryConvert for ListType {
38538
39289
  unsafe impl IntoValueFromNative for ListType {}
38539
39290
  unsafe impl TryConvertOwned for ListType {}
38540
39291
 
38541
- #[derive(Clone, Copy, PartialEq, Eq, Debug, serde::Serialize, serde::Deserialize)]
39292
+ #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, serde::Serialize, serde::Deserialize)]
38542
39293
  #[serde(rename_all = "snake_case")]
38543
39294
  pub enum MergeMode {
38544
39295
  ObjectMerge,
@@ -38565,7 +39316,14 @@ impl magnus::IntoValue for MergeMode {
38565
39316
 
38566
39317
  impl magnus::TryConvert for MergeMode {
38567
39318
  fn try_convert(val: magnus::Value) -> Result<Self, magnus::Error> {
38568
- let s: String = magnus::TryConvert::try_convert(val)?;
39319
+ // IntoValue emits Symbols, including when this enum is a Hash key.
39320
+ // Accept that exact representation as well as String inputs; do not
39321
+ // coerce arbitrary objects through to_s.
39322
+ let s: String = if let Some(symbol) = magnus::Symbol::from_value(val) {
39323
+ symbol.name()?.into_owned()
39324
+ } else {
39325
+ magnus::TryConvert::try_convert(val)?
39326
+ };
38569
39327
  // Accept the real serde wire value, the legacy always-snake_case symbol Magnus used to
38570
39328
  // emit unconditionally, and the verbatim PascalCase Rust variant name, so fixtures and
38571
39329
  // existing consumer code written in any of those styles keep working.
@@ -38584,7 +39342,7 @@ impl magnus::TryConvert for MergeMode {
38584
39342
  unsafe impl IntoValueFromNative for MergeMode {}
38585
39343
  unsafe impl TryConvertOwned for MergeMode {}
38586
39344
 
38587
- #[derive(Clone, Copy, PartialEq, Eq, Debug, serde::Serialize, serde::Deserialize)]
39345
+ #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, serde::Serialize, serde::Deserialize)]
38588
39346
  #[serde(rename_all = "snake_case")]
38589
39347
  pub enum MimeDetectionPolicy {
38590
39348
  PreferContent,
@@ -38611,7 +39369,14 @@ impl magnus::IntoValue for MimeDetectionPolicy {
38611
39369
 
38612
39370
  impl magnus::TryConvert for MimeDetectionPolicy {
38613
39371
  fn try_convert(val: magnus::Value) -> Result<Self, magnus::Error> {
38614
- let s: String = magnus::TryConvert::try_convert(val)?;
39372
+ // IntoValue emits Symbols, including when this enum is a Hash key.
39373
+ // Accept that exact representation as well as String inputs; do not
39374
+ // coerce arbitrary objects through to_s.
39375
+ let s: String = if let Some(symbol) = magnus::Symbol::from_value(val) {
39376
+ symbol.name()?.into_owned()
39377
+ } else {
39378
+ magnus::TryConvert::try_convert(val)?
39379
+ };
38615
39380
  // Accept the real serde wire value, the legacy always-snake_case symbol Magnus used to
38616
39381
  // emit unconditionally, and the verbatim PascalCase Rust variant name, so fixtures and
38617
39382
  // existing consumer code written in any of those styles keep working.
@@ -38630,7 +39395,7 @@ impl magnus::TryConvert for MimeDetectionPolicy {
38630
39395
  unsafe impl IntoValueFromNative for MimeDetectionPolicy {}
38631
39396
  unsafe impl TryConvertOwned for MimeDetectionPolicy {}
38632
39397
 
38633
- #[derive(Clone, Copy, PartialEq, Eq, Debug, serde::Serialize, serde::Deserialize)]
39398
+ #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, serde::Serialize, serde::Deserialize)]
38634
39399
  #[serde(rename_all = "snake_case")]
38635
39400
  pub enum NerBackendKind {
38636
39401
  Onnx,
@@ -38655,7 +39420,14 @@ impl magnus::IntoValue for NerBackendKind {
38655
39420
 
38656
39421
  impl magnus::TryConvert for NerBackendKind {
38657
39422
  fn try_convert(val: magnus::Value) -> Result<Self, magnus::Error> {
38658
- let s: String = magnus::TryConvert::try_convert(val)?;
39423
+ // IntoValue emits Symbols, including when this enum is a Hash key.
39424
+ // Accept that exact representation as well as String inputs; do not
39425
+ // coerce arbitrary objects through to_s.
39426
+ let s: String = if let Some(symbol) = magnus::Symbol::from_value(val) {
39427
+ symbol.name()?.into_owned()
39428
+ } else {
39429
+ magnus::TryConvert::try_convert(val)?
39430
+ };
38659
39431
  // Accept the real serde wire value, the legacy always-snake_case symbol Magnus used to
38660
39432
  // emit unconditionally, and the verbatim PascalCase Rust variant name, so fixtures and
38661
39433
  // existing consumer code written in any of those styles keep working.
@@ -38673,7 +39445,7 @@ impl magnus::TryConvert for NerBackendKind {
38673
39445
  unsafe impl IntoValueFromNative for NerBackendKind {}
38674
39446
  unsafe impl TryConvertOwned for NerBackendKind {}
38675
39447
 
38676
- #[derive(Clone, Copy, PartialEq, Eq, Debug, serde::Serialize, serde::Deserialize)]
39448
+ #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, serde::Serialize, serde::Deserialize)]
38677
39449
  pub enum NewlineStyle {
38678
39450
  Spaces,
38679
39451
  Backslash,
@@ -38697,7 +39469,14 @@ impl magnus::IntoValue for NewlineStyle {
38697
39469
 
38698
39470
  impl magnus::TryConvert for NewlineStyle {
38699
39471
  fn try_convert(val: magnus::Value) -> Result<Self, magnus::Error> {
38700
- let s: String = magnus::TryConvert::try_convert(val)?;
39472
+ // IntoValue emits Symbols, including when this enum is a Hash key.
39473
+ // Accept that exact representation as well as String inputs; do not
39474
+ // coerce arbitrary objects through to_s.
39475
+ let s: String = if let Some(symbol) = magnus::Symbol::from_value(val) {
39476
+ symbol.name()?.into_owned()
39477
+ } else {
39478
+ magnus::TryConvert::try_convert(val)?
39479
+ };
38701
39480
  // Accept the real serde wire value, the legacy always-snake_case symbol Magnus used to
38702
39481
  // emit unconditionally, and the verbatim PascalCase Rust variant name, so fixtures and
38703
39482
  // existing consumer code written in any of those styles keep working.
@@ -38754,7 +39533,8 @@ impl magnus::IntoValue for NoChunkingReason {
38754
39533
 
38755
39534
  impl magnus::TryConvert for NoChunkingReason {
38756
39535
  fn try_convert(val: magnus::Value) -> Result<Self, magnus::Error> {
38757
- // For data enums with fields (e.g., PageAction), try to deserialize from JSON first.
39536
+ // Generated Data variants carry native DTOs, not JSON-compatible display strings.
39537
+ let class_name: Option<String> = val.class().funcall("name", ())?; // For data enums with fields (e.g., PageAction), try to deserialize from JSON first.
38758
39538
  // For unit enums or when passed as a string, fall back to string-based conversion.
38759
39539
  let json_str: String = if let Ok(s) = <String as magnus::TryConvert>::try_convert(val) {
38760
39540
  s
@@ -38786,6 +39566,7 @@ impl magnus::TryConvert for NoChunkingReason {
38786
39566
 
38787
39567
  unsafe impl IntoValueFromNative for NoChunkingReason {}
38788
39568
  unsafe impl TryConvertOwned for NoChunkingReason {}
39569
+
38789
39570
  impl NoChunkingReason {}
38790
39571
 
38791
39572
  #[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
@@ -38881,7 +39662,8 @@ impl magnus::IntoValue for NodeContent {
38881
39662
 
38882
39663
  impl magnus::TryConvert for NodeContent {
38883
39664
  fn try_convert(val: magnus::Value) -> Result<Self, magnus::Error> {
38884
- // For data enums with fields (e.g., PageAction), try to deserialize from JSON first.
39665
+ // Generated Data variants carry native DTOs, not JSON-compatible display strings.
39666
+ let class_name: Option<String> = val.class().funcall("name", ())?; // For data enums with fields (e.g., PageAction), try to deserialize from JSON first.
38885
39667
  // For unit enums or when passed as a string, fall back to string-based conversion.
38886
39668
  let json_str: String = if let Ok(s) = <String as magnus::TryConvert>::try_convert(val) {
38887
39669
  s
@@ -38913,9 +39695,10 @@ impl magnus::TryConvert for NodeContent {
38913
39695
 
38914
39696
  unsafe impl IntoValueFromNative for NodeContent {}
38915
39697
  unsafe impl TryConvertOwned for NodeContent {}
39698
+
38916
39699
  impl NodeContent {}
38917
39700
 
38918
- #[derive(Clone, Copy, PartialEq, Eq, Debug, serde::Serialize, serde::Deserialize)]
39701
+ #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, serde::Serialize, serde::Deserialize)]
38919
39702
  pub enum OcrBackendType {
38920
39703
  Tesseract,
38921
39704
  PaddleOCR,
@@ -38943,7 +39726,14 @@ impl magnus::IntoValue for OcrBackendType {
38943
39726
 
38944
39727
  impl magnus::TryConvert for OcrBackendType {
38945
39728
  fn try_convert(val: magnus::Value) -> Result<Self, magnus::Error> {
38946
- let s: String = magnus::TryConvert::try_convert(val)?;
39729
+ // IntoValue emits Symbols, including when this enum is a Hash key.
39730
+ // Accept that exact representation as well as String inputs; do not
39731
+ // coerce arbitrary objects through to_s.
39732
+ let s: String = if let Some(symbol) = magnus::Symbol::from_value(val) {
39733
+ symbol.name()?.into_owned()
39734
+ } else {
39735
+ magnus::TryConvert::try_convert(val)?
39736
+ };
38947
39737
  // Accept the real serde wire value, the legacy always-snake_case symbol Magnus used to
38948
39738
  // emit unconditionally, and the verbatim PascalCase Rust variant name, so fixtures and
38949
39739
  // existing consumer code written in any of those styles keep working.
@@ -39000,7 +39790,8 @@ impl magnus::IntoValue for OcrBoundingGeometry {
39000
39790
 
39001
39791
  impl magnus::TryConvert for OcrBoundingGeometry {
39002
39792
  fn try_convert(val: magnus::Value) -> Result<Self, magnus::Error> {
39003
- // For data enums with fields (e.g., PageAction), try to deserialize from JSON first.
39793
+ // Generated Data variants carry native DTOs, not JSON-compatible display strings.
39794
+ let class_name: Option<String> = val.class().funcall("name", ())?; // For data enums with fields (e.g., PageAction), try to deserialize from JSON first.
39004
39795
  // For unit enums or when passed as a string, fall back to string-based conversion.
39005
39796
  let json_str: String = if let Ok(s) = <String as magnus::TryConvert>::try_convert(val) {
39006
39797
  s
@@ -39032,9 +39823,10 @@ impl magnus::TryConvert for OcrBoundingGeometry {
39032
39823
 
39033
39824
  unsafe impl IntoValueFromNative for OcrBoundingGeometry {}
39034
39825
  unsafe impl TryConvertOwned for OcrBoundingGeometry {}
39826
+
39035
39827
  impl OcrBoundingGeometry {}
39036
39828
 
39037
- #[derive(Clone, Copy, PartialEq, Eq, Debug, serde::Serialize, serde::Deserialize)]
39829
+ #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, serde::Serialize, serde::Deserialize)]
39038
39830
  #[serde(rename_all = "snake_case")]
39039
39831
  pub enum OcrElementLevel {
39040
39832
  Word,
@@ -39063,7 +39855,14 @@ impl magnus::IntoValue for OcrElementLevel {
39063
39855
 
39064
39856
  impl magnus::TryConvert for OcrElementLevel {
39065
39857
  fn try_convert(val: magnus::Value) -> Result<Self, magnus::Error> {
39066
- let s: String = magnus::TryConvert::try_convert(val)?;
39858
+ // IntoValue emits Symbols, including when this enum is a Hash key.
39859
+ // Accept that exact representation as well as String inputs; do not
39860
+ // coerce arbitrary objects through to_s.
39861
+ let s: String = if let Some(symbol) = magnus::Symbol::from_value(val) {
39862
+ symbol.name()?.into_owned()
39863
+ } else {
39864
+ magnus::TryConvert::try_convert(val)?
39865
+ };
39067
39866
  // Accept the real serde wire value, the legacy always-snake_case symbol Magnus used to
39068
39867
  // emit unconditionally, and the verbatim PascalCase Rust variant name, so fixtures and
39069
39868
  // existing consumer code written in any of those styles keep working.
@@ -39108,7 +39907,8 @@ impl magnus::IntoValue for OcrStrategy {
39108
39907
 
39109
39908
  impl magnus::TryConvert for OcrStrategy {
39110
39909
  fn try_convert(val: magnus::Value) -> Result<Self, magnus::Error> {
39111
- // For data enums with fields (e.g., PageAction), try to deserialize from JSON first.
39910
+ // Generated Data variants carry native DTOs, not JSON-compatible display strings.
39911
+ let class_name: Option<String> = val.class().funcall("name", ())?; // For data enums with fields (e.g., PageAction), try to deserialize from JSON first.
39112
39912
  // For unit enums or when passed as a string, fall back to string-based conversion.
39113
39913
  let json_str: String = if let Ok(s) = <String as magnus::TryConvert>::try_convert(val) {
39114
39914
  s
@@ -39140,6 +39940,7 @@ impl magnus::TryConvert for OcrStrategy {
39140
39940
 
39141
39941
  unsafe impl IntoValueFromNative for OcrStrategy {}
39142
39942
  unsafe impl TryConvertOwned for OcrStrategy {}
39943
+
39143
39944
  impl OcrStrategy {}
39144
39945
 
39145
39946
  #[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
@@ -39203,7 +40004,7 @@ impl magnus::TryConvert for OutputFormat {
39203
40004
  unsafe impl IntoValueFromNative for OutputFormat {}
39204
40005
  unsafe impl TryConvertOwned for OutputFormat {}
39205
40006
 
39206
- #[derive(Clone, Copy, PartialEq, Eq, Debug, serde::Serialize, serde::Deserialize)]
40007
+ #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, serde::Serialize, serde::Deserialize)]
39207
40008
  pub enum PSMMode {
39208
40009
  OsdOnly,
39209
40010
  AutoOsd,
@@ -39245,7 +40046,14 @@ impl magnus::IntoValue for PSMMode {
39245
40046
 
39246
40047
  impl magnus::TryConvert for PSMMode {
39247
40048
  fn try_convert(val: magnus::Value) -> Result<Self, magnus::Error> {
39248
- let s: String = magnus::TryConvert::try_convert(val)?;
40049
+ // IntoValue emits Symbols, including when this enum is a Hash key.
40050
+ // Accept that exact representation as well as String inputs; do not
40051
+ // coerce arbitrary objects through to_s.
40052
+ let s: String = if let Some(symbol) = magnus::Symbol::from_value(val) {
40053
+ symbol.name()?.into_owned()
40054
+ } else {
40055
+ magnus::TryConvert::try_convert(val)?
40056
+ };
39249
40057
  // Accept the real serde wire value, the legacy always-snake_case symbol Magnus used to
39250
40058
  // emit unconditionally, and the verbatim PascalCase Rust variant name, so fixtures and
39251
40059
  // existing consumer code written in any of those styles keep working.
@@ -39272,7 +40080,7 @@ impl magnus::TryConvert for PSMMode {
39272
40080
  unsafe impl IntoValueFromNative for PSMMode {}
39273
40081
  unsafe impl TryConvertOwned for PSMMode {}
39274
40082
 
39275
- #[derive(Clone, Copy, PartialEq, Eq, Debug, serde::Serialize, serde::Deserialize)]
40083
+ #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, serde::Serialize, serde::Deserialize)]
39276
40084
  #[serde(rename_all = "snake_case")]
39277
40085
  pub enum PaddleInferenceBackend {
39278
40086
  Ort,
@@ -39297,7 +40105,14 @@ impl magnus::IntoValue for PaddleInferenceBackend {
39297
40105
 
39298
40106
  impl magnus::TryConvert for PaddleInferenceBackend {
39299
40107
  fn try_convert(val: magnus::Value) -> Result<Self, magnus::Error> {
39300
- let s: String = magnus::TryConvert::try_convert(val)?;
40108
+ // IntoValue emits Symbols, including when this enum is a Hash key.
40109
+ // Accept that exact representation as well as String inputs; do not
40110
+ // coerce arbitrary objects through to_s.
40111
+ let s: String = if let Some(symbol) = magnus::Symbol::from_value(val) {
40112
+ symbol.name()?.into_owned()
40113
+ } else {
40114
+ magnus::TryConvert::try_convert(val)?
40115
+ };
39301
40116
  // Accept the real serde wire value, the legacy always-snake_case symbol Magnus used to
39302
40117
  // emit unconditionally, and the verbatim PascalCase Rust variant name, so fixtures and
39303
40118
  // existing consumer code written in any of those styles keep working.
@@ -39315,7 +40130,7 @@ impl magnus::TryConvert for PaddleInferenceBackend {
39315
40130
  unsafe impl IntoValueFromNative for PaddleInferenceBackend {}
39316
40131
  unsafe impl TryConvertOwned for PaddleInferenceBackend {}
39317
40132
 
39318
- #[derive(Clone, Copy, PartialEq, Eq, Debug, serde::Serialize, serde::Deserialize)]
40133
+ #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, serde::Serialize, serde::Deserialize)]
39319
40134
  pub enum PaddleLanguage {
39320
40135
  English,
39321
40136
  Chinese,
@@ -39367,7 +40182,14 @@ impl magnus::IntoValue for PaddleLanguage {
39367
40182
 
39368
40183
  impl magnus::TryConvert for PaddleLanguage {
39369
40184
  fn try_convert(val: magnus::Value) -> Result<Self, magnus::Error> {
39370
- let s: String = magnus::TryConvert::try_convert(val)?;
40185
+ // IntoValue emits Symbols, including when this enum is a Hash key.
40186
+ // Accept that exact representation as well as String inputs; do not
40187
+ // coerce arbitrary objects through to_s.
40188
+ let s: String = if let Some(symbol) = magnus::Symbol::from_value(val) {
40189
+ symbol.name()?.into_owned()
40190
+ } else {
40191
+ magnus::TryConvert::try_convert(val)?
40192
+ };
39371
40193
  // Accept the real serde wire value, the legacy always-snake_case symbol Magnus used to
39372
40194
  // emit unconditionally, and the verbatim PascalCase Rust variant name, so fixtures and
39373
40195
  // existing consumer code written in any of those styles keep working.
@@ -39399,7 +40221,7 @@ impl magnus::TryConvert for PaddleLanguage {
39399
40221
  unsafe impl IntoValueFromNative for PaddleLanguage {}
39400
40222
  unsafe impl TryConvertOwned for PaddleLanguage {}
39401
40223
 
39402
- #[derive(Clone, Copy, PartialEq, Eq, Debug, serde::Serialize, serde::Deserialize)]
40224
+ #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, serde::Serialize, serde::Deserialize)]
39403
40225
  #[serde(rename_all = "lowercase")]
39404
40226
  pub enum PaddleOcrVlTaskKind {
39405
40227
  Ocr,
@@ -39428,7 +40250,14 @@ impl magnus::IntoValue for PaddleOcrVlTaskKind {
39428
40250
 
39429
40251
  impl magnus::TryConvert for PaddleOcrVlTaskKind {
39430
40252
  fn try_convert(val: magnus::Value) -> Result<Self, magnus::Error> {
39431
- let s: String = magnus::TryConvert::try_convert(val)?;
40253
+ // IntoValue emits Symbols, including when this enum is a Hash key.
40254
+ // Accept that exact representation as well as String inputs; do not
40255
+ // coerce arbitrary objects through to_s.
40256
+ let s: String = if let Some(symbol) = magnus::Symbol::from_value(val) {
40257
+ symbol.name()?.into_owned()
40258
+ } else {
40259
+ magnus::TryConvert::try_convert(val)?
40260
+ };
39432
40261
  // Accept the real serde wire value, the legacy always-snake_case symbol Magnus used to
39433
40262
  // emit unconditionally, and the verbatim PascalCase Rust variant name, so fixtures and
39434
40263
  // existing consumer code written in any of those styles keep working.
@@ -39448,7 +40277,7 @@ impl magnus::TryConvert for PaddleOcrVlTaskKind {
39448
40277
  unsafe impl IntoValueFromNative for PaddleOcrVlTaskKind {}
39449
40278
  unsafe impl TryConvertOwned for PaddleOcrVlTaskKind {}
39450
40279
 
39451
- #[derive(Clone, Copy, PartialEq, Eq, Debug, serde::Serialize, serde::Deserialize)]
40280
+ #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, serde::Serialize, serde::Deserialize)]
39452
40281
  pub enum PageOrientationHandling {
39453
40282
  SelfCorrecting,
39454
40283
  RecognisesRotatedText,
@@ -39474,7 +40303,14 @@ impl magnus::IntoValue for PageOrientationHandling {
39474
40303
 
39475
40304
  impl magnus::TryConvert for PageOrientationHandling {
39476
40305
  fn try_convert(val: magnus::Value) -> Result<Self, magnus::Error> {
39477
- let s: String = magnus::TryConvert::try_convert(val)?;
40306
+ // IntoValue emits Symbols, including when this enum is a Hash key.
40307
+ // Accept that exact representation as well as String inputs; do not
40308
+ // coerce arbitrary objects through to_s.
40309
+ let s: String = if let Some(symbol) = magnus::Symbol::from_value(val) {
40310
+ symbol.name()?.into_owned()
40311
+ } else {
40312
+ magnus::TryConvert::try_convert(val)?
40313
+ };
39478
40314
  // Accept the real serde wire value, the legacy always-snake_case symbol Magnus used to
39479
40315
  // emit unconditionally, and the verbatim PascalCase Rust variant name, so fixtures and
39480
40316
  // existing consumer code written in any of those styles keep working.
@@ -39493,7 +40329,7 @@ impl magnus::TryConvert for PageOrientationHandling {
39493
40329
  unsafe impl IntoValueFromNative for PageOrientationHandling {}
39494
40330
  unsafe impl TryConvertOwned for PageOrientationHandling {}
39495
40331
 
39496
- #[derive(Clone, Copy, PartialEq, Eq, Debug, serde::Serialize, serde::Deserialize)]
40332
+ #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, serde::Serialize, serde::Deserialize)]
39497
40333
  #[serde(rename_all = "snake_case")]
39498
40334
  pub enum PageUnitType {
39499
40335
  Page,
@@ -39520,7 +40356,14 @@ impl magnus::IntoValue for PageUnitType {
39520
40356
 
39521
40357
  impl magnus::TryConvert for PageUnitType {
39522
40358
  fn try_convert(val: magnus::Value) -> Result<Self, magnus::Error> {
39523
- let s: String = magnus::TryConvert::try_convert(val)?;
40359
+ // IntoValue emits Symbols, including when this enum is a Hash key.
40360
+ // Accept that exact representation as well as String inputs; do not
40361
+ // coerce arbitrary objects through to_s.
40362
+ let s: String = if let Some(symbol) = magnus::Symbol::from_value(val) {
40363
+ symbol.name()?.into_owned()
40364
+ } else {
40365
+ magnus::TryConvert::try_convert(val)?
40366
+ };
39524
40367
  // Accept the real serde wire value, the legacy always-snake_case symbol Magnus used to
39525
40368
  // emit unconditionally, and the verbatim PascalCase Rust variant name, so fixtures and
39526
40369
  // existing consumer code written in any of those styles keep working.
@@ -39539,7 +40382,7 @@ impl magnus::TryConvert for PageUnitType {
39539
40382
  unsafe impl IntoValueFromNative for PageUnitType {}
39540
40383
  unsafe impl TryConvertOwned for PageUnitType {}
39541
40384
 
39542
- #[derive(Clone, Copy, PartialEq, Eq, Debug, serde::Serialize, serde::Deserialize)]
40385
+ #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, serde::Serialize, serde::Deserialize)]
39543
40386
  #[serde(rename_all = "snake_case")]
39544
40387
  pub enum PdfAnnotationType {
39545
40388
  Text,
@@ -39596,7 +40439,14 @@ impl magnus::IntoValue for PdfAnnotationType {
39596
40439
 
39597
40440
  impl magnus::TryConvert for PdfAnnotationType {
39598
40441
  fn try_convert(val: magnus::Value) -> Result<Self, magnus::Error> {
39599
- let s: String = magnus::TryConvert::try_convert(val)?;
40442
+ // IntoValue emits Symbols, including when this enum is a Hash key.
40443
+ // Accept that exact representation as well as String inputs; do not
40444
+ // coerce arbitrary objects through to_s.
40445
+ let s: String = if let Some(symbol) = magnus::Symbol::from_value(val) {
40446
+ symbol.name()?.into_owned()
40447
+ } else {
40448
+ magnus::TryConvert::try_convert(val)?
40449
+ };
39600
40450
  // Accept the real serde wire value, the legacy always-snake_case symbol Magnus used to
39601
40451
  // emit unconditionally, and the verbatim PascalCase Rust variant name, so fixtures and
39602
40452
  // existing consumer code written in any of those styles keep working.
@@ -39630,7 +40480,7 @@ impl magnus::TryConvert for PdfAnnotationType {
39630
40480
  unsafe impl IntoValueFromNative for PdfAnnotationType {}
39631
40481
  unsafe impl TryConvertOwned for PdfAnnotationType {}
39632
40482
 
39633
- #[derive(Clone, Copy, PartialEq, Eq, Debug, serde::Serialize, serde::Deserialize)]
40483
+ #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, serde::Serialize, serde::Deserialize)]
39634
40484
  #[serde(rename_all = "snake_case")]
39635
40485
  pub enum PdfBackend {
39636
40486
  Native,
@@ -39655,7 +40505,14 @@ impl magnus::IntoValue for PdfBackend {
39655
40505
 
39656
40506
  impl magnus::TryConvert for PdfBackend {
39657
40507
  fn try_convert(val: magnus::Value) -> Result<Self, magnus::Error> {
39658
- let s: String = magnus::TryConvert::try_convert(val)?;
40508
+ // IntoValue emits Symbols, including when this enum is a Hash key.
40509
+ // Accept that exact representation as well as String inputs; do not
40510
+ // coerce arbitrary objects through to_s.
40511
+ let s: String = if let Some(symbol) = magnus::Symbol::from_value(val) {
40512
+ symbol.name()?.into_owned()
40513
+ } else {
40514
+ magnus::TryConvert::try_convert(val)?
40515
+ };
39659
40516
  // Accept the real serde wire value, the legacy always-snake_case symbol Magnus used to
39660
40517
  // emit unconditionally, and the verbatim PascalCase Rust variant name, so fixtures and
39661
40518
  // existing consumer code written in any of those styles keep working.
@@ -39740,7 +40597,7 @@ impl magnus::TryConvert for PiiCategory {
39740
40597
  unsafe impl IntoValueFromNative for PiiCategory {}
39741
40598
  unsafe impl TryConvertOwned for PiiCategory {}
39742
40599
 
39743
- #[derive(Clone, Copy, PartialEq, Eq, Debug, serde::Serialize, serde::Deserialize)]
40600
+ #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, serde::Serialize, serde::Deserialize)]
39744
40601
  pub enum PreprocessingPreset {
39745
40602
  Minimal,
39746
40603
  Standard,
@@ -39766,7 +40623,14 @@ impl magnus::IntoValue for PreprocessingPreset {
39766
40623
 
39767
40624
  impl magnus::TryConvert for PreprocessingPreset {
39768
40625
  fn try_convert(val: magnus::Value) -> Result<Self, magnus::Error> {
39769
- let s: String = magnus::TryConvert::try_convert(val)?;
40626
+ // IntoValue emits Symbols, including when this enum is a Hash key.
40627
+ // Accept that exact representation as well as String inputs; do not
40628
+ // coerce arbitrary objects through to_s.
40629
+ let s: String = if let Some(symbol) = magnus::Symbol::from_value(val) {
40630
+ symbol.name()?.into_owned()
40631
+ } else {
40632
+ magnus::TryConvert::try_convert(val)?
40633
+ };
39770
40634
  // Accept the real serde wire value, the legacy always-snake_case symbol Magnus used to
39771
40635
  // emit unconditionally, and the verbatim PascalCase Rust variant name, so fixtures and
39772
40636
  // existing consumer code written in any of those styles keep working.
@@ -39785,7 +40649,7 @@ impl magnus::TryConvert for PreprocessingPreset {
39785
40649
  unsafe impl IntoValueFromNative for PreprocessingPreset {}
39786
40650
  unsafe impl TryConvertOwned for PreprocessingPreset {}
39787
40651
 
39788
- #[derive(Clone, Copy, PartialEq, Eq, Debug, serde::Serialize, serde::Deserialize)]
40652
+ #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, serde::Serialize, serde::Deserialize)]
39789
40653
  #[serde(rename_all = "snake_case")]
39790
40654
  pub enum PresetCategory {
39791
40655
  Finance,
@@ -39820,7 +40684,14 @@ impl magnus::IntoValue for PresetCategory {
39820
40684
 
39821
40685
  impl magnus::TryConvert for PresetCategory {
39822
40686
  fn try_convert(val: magnus::Value) -> Result<Self, magnus::Error> {
39823
- let s: String = magnus::TryConvert::try_convert(val)?;
40687
+ // IntoValue emits Symbols, including when this enum is a Hash key.
40688
+ // Accept that exact representation as well as String inputs; do not
40689
+ // coerce arbitrary objects through to_s.
40690
+ let s: String = if let Some(symbol) = magnus::Symbol::from_value(val) {
40691
+ symbol.name()?.into_owned()
40692
+ } else {
40693
+ magnus::TryConvert::try_convert(val)?
40694
+ };
39824
40695
  // Accept the real serde wire value, the legacy always-snake_case symbol Magnus used to
39825
40696
  // emit unconditionally, and the verbatim PascalCase Rust variant name, so fixtures and
39826
40697
  // existing consumer code written in any of those styles keep working.
@@ -39843,7 +40714,7 @@ impl magnus::TryConvert for PresetCategory {
39843
40714
  unsafe impl IntoValueFromNative for PresetCategory {}
39844
40715
  unsafe impl TryConvertOwned for PresetCategory {}
39845
40716
 
39846
- #[derive(Clone, Copy, PartialEq, Eq, Debug, serde::Serialize, serde::Deserialize)]
40717
+ #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, serde::Serialize, serde::Deserialize)]
39847
40718
  #[serde(rename_all = "lowercase")]
39848
40719
  pub enum ProbeStatus {
39849
40720
  Pass,
@@ -39872,7 +40743,14 @@ impl magnus::IntoValue for ProbeStatus {
39872
40743
 
39873
40744
  impl magnus::TryConvert for ProbeStatus {
39874
40745
  fn try_convert(val: magnus::Value) -> Result<Self, magnus::Error> {
39875
- let s: String = magnus::TryConvert::try_convert(val)?;
40746
+ // IntoValue emits Symbols, including when this enum is a Hash key.
40747
+ // Accept that exact representation as well as String inputs; do not
40748
+ // coerce arbitrary objects through to_s.
40749
+ let s: String = if let Some(symbol) = magnus::Symbol::from_value(val) {
40750
+ symbol.name()?.into_owned()
40751
+ } else {
40752
+ magnus::TryConvert::try_convert(val)?
40753
+ };
39876
40754
  // Accept the real serde wire value, the legacy always-snake_case symbol Magnus used to
39877
40755
  // emit unconditionally, and the verbatim PascalCase Rust variant name, so fixtures and
39878
40756
  // existing consumer code written in any of those styles keep working.
@@ -39892,7 +40770,7 @@ impl magnus::TryConvert for ProbeStatus {
39892
40770
  unsafe impl IntoValueFromNative for ProbeStatus {}
39893
40771
  unsafe impl TryConvertOwned for ProbeStatus {}
39894
40772
 
39895
- #[derive(Clone, Copy, PartialEq, Eq, Debug, serde::Serialize, serde::Deserialize)]
40773
+ #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, serde::Serialize, serde::Deserialize)]
39896
40774
  pub enum ProcessingStage {
39897
40775
  Early,
39898
40776
  Middle,
@@ -39918,7 +40796,14 @@ impl magnus::IntoValue for ProcessingStage {
39918
40796
 
39919
40797
  impl magnus::TryConvert for ProcessingStage {
39920
40798
  fn try_convert(val: magnus::Value) -> Result<Self, magnus::Error> {
39921
- let s: String = magnus::TryConvert::try_convert(val)?;
40799
+ // IntoValue emits Symbols, including when this enum is a Hash key.
40800
+ // Accept that exact representation as well as String inputs; do not
40801
+ // coerce arbitrary objects through to_s.
40802
+ let s: String = if let Some(symbol) = magnus::Symbol::from_value(val) {
40803
+ symbol.name()?.into_owned()
40804
+ } else {
40805
+ magnus::TryConvert::try_convert(val)?
40806
+ };
39922
40807
  // Accept the real serde wire value, the legacy always-snake_case symbol Magnus used to
39923
40808
  // emit unconditionally, and the verbatim PascalCase Rust variant name, so fixtures and
39924
40809
  // existing consumer code written in any of those styles keep working.
@@ -39937,7 +40822,7 @@ impl magnus::TryConvert for ProcessingStage {
39937
40822
  unsafe impl IntoValueFromNative for ProcessingStage {}
39938
40823
  unsafe impl TryConvertOwned for ProcessingStage {}
39939
40824
 
39940
- #[derive(Clone, Copy, PartialEq, Eq, Debug, serde::Serialize, serde::Deserialize)]
40825
+ #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, serde::Serialize, serde::Deserialize)]
39941
40826
  #[serde(rename_all = "snake_case")]
39942
40827
  pub enum RedactionStrategy {
39943
40828
  Mask,
@@ -39966,7 +40851,14 @@ impl magnus::IntoValue for RedactionStrategy {
39966
40851
 
39967
40852
  impl magnus::TryConvert for RedactionStrategy {
39968
40853
  fn try_convert(val: magnus::Value) -> Result<Self, magnus::Error> {
39969
- let s: String = magnus::TryConvert::try_convert(val)?;
40854
+ // IntoValue emits Symbols, including when this enum is a Hash key.
40855
+ // Accept that exact representation as well as String inputs; do not
40856
+ // coerce arbitrary objects through to_s.
40857
+ let s: String = if let Some(symbol) = magnus::Symbol::from_value(val) {
40858
+ symbol.name()?.into_owned()
40859
+ } else {
40860
+ magnus::TryConvert::try_convert(val)?
40861
+ };
39970
40862
  // Accept the real serde wire value, the legacy always-snake_case symbol Magnus used to
39971
40863
  // emit unconditionally, and the verbatim PascalCase Rust variant name, so fixtures and
39972
40864
  // existing consumer code written in any of those styles keep working.
@@ -39986,7 +40878,7 @@ impl magnus::TryConvert for RedactionStrategy {
39986
40878
  unsafe impl IntoValueFromNative for RedactionStrategy {}
39987
40879
  unsafe impl TryConvertOwned for RedactionStrategy {}
39988
40880
 
39989
- #[derive(Clone, Copy, PartialEq, Eq, Debug, serde::Serialize, serde::Deserialize)]
40881
+ #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, serde::Serialize, serde::Deserialize)]
39990
40882
  pub enum ReductionLevel {
39991
40883
  Off,
39992
40884
  Light,
@@ -40016,7 +40908,14 @@ impl magnus::IntoValue for ReductionLevel {
40016
40908
 
40017
40909
  impl magnus::TryConvert for ReductionLevel {
40018
40910
  fn try_convert(val: magnus::Value) -> Result<Self, magnus::Error> {
40019
- let s: String = magnus::TryConvert::try_convert(val)?;
40911
+ // IntoValue emits Symbols, including when this enum is a Hash key.
40912
+ // Accept that exact representation as well as String inputs; do not
40913
+ // coerce arbitrary objects through to_s.
40914
+ let s: String = if let Some(symbol) = magnus::Symbol::from_value(val) {
40915
+ symbol.name()?.into_owned()
40916
+ } else {
40917
+ magnus::TryConvert::try_convert(val)?
40918
+ };
40020
40919
  // Accept the real serde wire value, the legacy always-snake_case symbol Magnus used to
40021
40920
  // emit unconditionally, and the verbatim PascalCase Rust variant name, so fixtures and
40022
40921
  // existing consumer code written in any of those styles keep working.
@@ -40037,7 +40936,7 @@ impl magnus::TryConvert for ReductionLevel {
40037
40936
  unsafe impl IntoValueFromNative for ReductionLevel {}
40038
40937
  unsafe impl TryConvertOwned for ReductionLevel {}
40039
40938
 
40040
- #[derive(Clone, Copy, PartialEq, Eq, Debug, serde::Serialize, serde::Deserialize)]
40939
+ #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, serde::Serialize, serde::Deserialize)]
40041
40940
  pub enum RegionKind {
40042
40941
  Figure,
40043
40942
  DenseTable,
@@ -40065,7 +40964,14 @@ impl magnus::IntoValue for RegionKind {
40065
40964
 
40066
40965
  impl magnus::TryConvert for RegionKind {
40067
40966
  fn try_convert(val: magnus::Value) -> Result<Self, magnus::Error> {
40068
- let s: String = magnus::TryConvert::try_convert(val)?;
40967
+ // IntoValue emits Symbols, including when this enum is a Hash key.
40968
+ // Accept that exact representation as well as String inputs; do not
40969
+ // coerce arbitrary objects through to_s.
40970
+ let s: String = if let Some(symbol) = magnus::Symbol::from_value(val) {
40971
+ symbol.name()?.into_owned()
40972
+ } else {
40973
+ magnus::TryConvert::try_convert(val)?
40974
+ };
40069
40975
  // Accept the real serde wire value, the legacy always-snake_case symbol Magnus used to
40070
40976
  // emit unconditionally, and the verbatim PascalCase Rust variant name, so fixtures and
40071
40977
  // existing consumer code written in any of those styles keep working.
@@ -40085,7 +40991,7 @@ impl magnus::TryConvert for RegionKind {
40085
40991
  unsafe impl IntoValueFromNative for RegionKind {}
40086
40992
  unsafe impl TryConvertOwned for RegionKind {}
40087
40993
 
40088
- #[derive(Clone, Copy, PartialEq, Eq, Debug, serde::Serialize, serde::Deserialize)]
40994
+ #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, serde::Serialize, serde::Deserialize)]
40089
40995
  #[serde(rename_all = "snake_case")]
40090
40996
  pub enum RelationshipKind {
40091
40997
  FootnoteReference,
@@ -40120,7 +41026,14 @@ impl magnus::IntoValue for RelationshipKind {
40120
41026
 
40121
41027
  impl magnus::TryConvert for RelationshipKind {
40122
41028
  fn try_convert(val: magnus::Value) -> Result<Self, magnus::Error> {
40123
- let s: String = magnus::TryConvert::try_convert(val)?;
41029
+ // IntoValue emits Symbols, including when this enum is a Hash key.
41030
+ // Accept that exact representation as well as String inputs; do not
41031
+ // coerce arbitrary objects through to_s.
41032
+ let s: String = if let Some(symbol) = magnus::Symbol::from_value(val) {
41033
+ symbol.name()?.into_owned()
41034
+ } else {
41035
+ magnus::TryConvert::try_convert(val)?
41036
+ };
40124
41037
  // Accept the real serde wire value, the legacy always-snake_case symbol Magnus used to
40125
41038
  // emit unconditionally, and the verbatim PascalCase Rust variant name, so fixtures and
40126
41039
  // existing consumer code written in any of those styles keep working.
@@ -40143,7 +41056,7 @@ impl magnus::TryConvert for RelationshipKind {
40143
41056
  unsafe impl IntoValueFromNative for RelationshipKind {}
40144
41057
  unsafe impl TryConvertOwned for RelationshipKind {}
40145
41058
 
40146
- #[derive(Clone, Copy, PartialEq, Eq, Debug, serde::Serialize, serde::Deserialize)]
41059
+ #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, serde::Serialize, serde::Deserialize)]
40147
41060
  #[serde(rename_all = "snake_case")]
40148
41061
  pub enum RerankerHead {
40149
41062
  CrossEncoder,
@@ -40168,7 +41081,14 @@ impl magnus::IntoValue for RerankerHead {
40168
41081
 
40169
41082
  impl magnus::TryConvert for RerankerHead {
40170
41083
  fn try_convert(val: magnus::Value) -> Result<Self, magnus::Error> {
40171
- let s: String = magnus::TryConvert::try_convert(val)?;
41084
+ // IntoValue emits Symbols, including when this enum is a Hash key.
41085
+ // Accept that exact representation as well as String inputs; do not
41086
+ // coerce arbitrary objects through to_s.
41087
+ let s: String = if let Some(symbol) = magnus::Symbol::from_value(val) {
41088
+ symbol.name()?.into_owned()
41089
+ } else {
41090
+ magnus::TryConvert::try_convert(val)?
41091
+ };
40172
41092
  // Accept the real serde wire value, the legacy always-snake_case symbol Magnus used to
40173
41093
  // emit unconditionally, and the verbatim PascalCase Rust variant name, so fixtures and
40174
41094
  // existing consumer code written in any of those styles keep working.
@@ -40201,7 +41121,7 @@ pub enum RerankerModelType {
40201
41121
  head: RerankerHead,
40202
41122
  },
40203
41123
  Llm {
40204
- llm: LlmConfig,
41124
+ llm: Box<LlmConfig>,
40205
41125
  },
40206
41126
  Plugin {
40207
41127
  name: String,
@@ -40227,7 +41147,8 @@ impl magnus::IntoValue for RerankerModelType {
40227
41147
 
40228
41148
  impl magnus::TryConvert for RerankerModelType {
40229
41149
  fn try_convert(val: magnus::Value) -> Result<Self, magnus::Error> {
40230
- // For data enums with fields (e.g., PageAction), try to deserialize from JSON first.
41150
+ // Generated Data variants carry native DTOs, not JSON-compatible display strings.
41151
+ let class_name: Option<String> = val.class().funcall("name", ())?; // For data enums with fields (e.g., PageAction), try to deserialize from JSON first.
40231
41152
  // For unit enums or when passed as a string, fall back to string-based conversion.
40232
41153
  let json_str: String = if let Ok(s) = <String as magnus::TryConvert>::try_convert(val) {
40233
41154
  s
@@ -40259,9 +41180,10 @@ impl magnus::TryConvert for RerankerModelType {
40259
41180
 
40260
41181
  unsafe impl IntoValueFromNative for RerankerModelType {}
40261
41182
  unsafe impl TryConvertOwned for RerankerModelType {}
41183
+
40262
41184
  impl RerankerModelType {}
40263
41185
 
40264
- #[derive(Clone, Copy, PartialEq, Eq, Debug, serde::Serialize, serde::Deserialize)]
41186
+ #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, serde::Serialize, serde::Deserialize)]
40265
41187
  #[serde(rename_all = "snake_case")]
40266
41188
  pub enum ResultFormat {
40267
41189
  Unified,
@@ -40286,7 +41208,14 @@ impl magnus::IntoValue for ResultFormat {
40286
41208
 
40287
41209
  impl magnus::TryConvert for ResultFormat {
40288
41210
  fn try_convert(val: magnus::Value) -> Result<Self, magnus::Error> {
40289
- let s: String = magnus::TryConvert::try_convert(val)?;
41211
+ // IntoValue emits Symbols, including when this enum is a Hash key.
41212
+ // Accept that exact representation as well as String inputs; do not
41213
+ // coerce arbitrary objects through to_s.
41214
+ let s: String = if let Some(symbol) = magnus::Symbol::from_value(val) {
41215
+ symbol.name()?.into_owned()
41216
+ } else {
41217
+ magnus::TryConvert::try_convert(val)?
41218
+ };
40290
41219
  // Accept the real serde wire value, the legacy always-snake_case symbol Magnus used to
40291
41220
  // emit unconditionally, and the verbatim PascalCase Rust variant name, so fixtures and
40292
41221
  // existing consumer code written in any of those styles keep working.
@@ -40334,7 +41263,8 @@ impl magnus::IntoValue for RevisionAnchor {
40334
41263
 
40335
41264
  impl magnus::TryConvert for RevisionAnchor {
40336
41265
  fn try_convert(val: magnus::Value) -> Result<Self, magnus::Error> {
40337
- // For data enums with fields (e.g., PageAction), try to deserialize from JSON first.
41266
+ // Generated Data variants carry native DTOs, not JSON-compatible display strings.
41267
+ let class_name: Option<String> = val.class().funcall("name", ())?; // For data enums with fields (e.g., PageAction), try to deserialize from JSON first.
40338
41268
  // For unit enums or when passed as a string, fall back to string-based conversion.
40339
41269
  let json_str: String = if let Ok(s) = <String as magnus::TryConvert>::try_convert(val) {
40340
41270
  s
@@ -40366,9 +41296,10 @@ impl magnus::TryConvert for RevisionAnchor {
40366
41296
 
40367
41297
  unsafe impl IntoValueFromNative for RevisionAnchor {}
40368
41298
  unsafe impl TryConvertOwned for RevisionAnchor {}
41299
+
40369
41300
  impl RevisionAnchor {}
40370
41301
 
40371
- #[derive(Clone, Copy, PartialEq, Eq, Debug, serde::Serialize, serde::Deserialize)]
41302
+ #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, serde::Serialize, serde::Deserialize)]
40372
41303
  #[serde(rename_all = "snake_case")]
40373
41304
  pub enum RevisionKind {
40374
41305
  Insertion,
@@ -40397,7 +41328,14 @@ impl magnus::IntoValue for RevisionKind {
40397
41328
 
40398
41329
  impl magnus::TryConvert for RevisionKind {
40399
41330
  fn try_convert(val: magnus::Value) -> Result<Self, magnus::Error> {
40400
- let s: String = magnus::TryConvert::try_convert(val)?;
41331
+ // IntoValue emits Symbols, including when this enum is a Hash key.
41332
+ // Accept that exact representation as well as String inputs; do not
41333
+ // coerce arbitrary objects through to_s.
41334
+ let s: String = if let Some(symbol) = magnus::Symbol::from_value(val) {
41335
+ symbol.name()?.into_owned()
41336
+ } else {
41337
+ magnus::TryConvert::try_convert(val)?
41338
+ };
40401
41339
  // Accept the real serde wire value, the legacy always-snake_case symbol Magnus used to
40402
41340
  // emit unconditionally, and the verbatim PascalCase Rust variant name, so fixtures and
40403
41341
  // existing consumer code written in any of those styles keep working.
@@ -40417,7 +41355,7 @@ impl magnus::TryConvert for RevisionKind {
40417
41355
  unsafe impl IntoValueFromNative for RevisionKind {}
40418
41356
  unsafe impl TryConvertOwned for RevisionKind {}
40419
41357
 
40420
- #[derive(Clone, Copy, PartialEq, Eq, Debug, serde::Serialize, serde::Deserialize)]
41358
+ #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, serde::Serialize, serde::Deserialize)]
40421
41359
  #[serde(rename_all = "snake_case")]
40422
41360
  pub enum SchemaCompliance {
40423
41361
  AllValid,
@@ -40444,7 +41382,14 @@ impl magnus::IntoValue for SchemaCompliance {
40444
41382
 
40445
41383
  impl magnus::TryConvert for SchemaCompliance {
40446
41384
  fn try_convert(val: magnus::Value) -> Result<Self, magnus::Error> {
40447
- let s: String = magnus::TryConvert::try_convert(val)?;
41385
+ // IntoValue emits Symbols, including when this enum is a Hash key.
41386
+ // Accept that exact representation as well as String inputs; do not
41387
+ // coerce arbitrary objects through to_s.
41388
+ let s: String = if let Some(symbol) = magnus::Symbol::from_value(val) {
41389
+ symbol.name()?.into_owned()
41390
+ } else {
41391
+ magnus::TryConvert::try_convert(val)?
41392
+ };
40448
41393
  // Accept the real serde wire value, the legacy always-snake_case symbol Magnus used to
40449
41394
  // emit unconditionally, and the verbatim PascalCase Rust variant name, so fixtures and
40450
41395
  // existing consumer code written in any of those styles keep working.
@@ -40500,7 +41445,8 @@ impl magnus::IntoValue for SparseEmbeddingModelType {
40500
41445
 
40501
41446
  impl magnus::TryConvert for SparseEmbeddingModelType {
40502
41447
  fn try_convert(val: magnus::Value) -> Result<Self, magnus::Error> {
40503
- // For data enums with fields (e.g., PageAction), try to deserialize from JSON first.
41448
+ // Generated Data variants carry native DTOs, not JSON-compatible display strings.
41449
+ let class_name: Option<String> = val.class().funcall("name", ())?; // For data enums with fields (e.g., PageAction), try to deserialize from JSON first.
40504
41450
  // For unit enums or when passed as a string, fall back to string-based conversion.
40505
41451
  let json_str: String = if let Ok(s) = <String as magnus::TryConvert>::try_convert(val) {
40506
41452
  s
@@ -40532,9 +41478,10 @@ impl magnus::TryConvert for SparseEmbeddingModelType {
40532
41478
 
40533
41479
  unsafe impl IntoValueFromNative for SparseEmbeddingModelType {}
40534
41480
  unsafe impl TryConvertOwned for SparseEmbeddingModelType {}
41481
+
40535
41482
  impl SparseEmbeddingModelType {}
40536
41483
 
40537
- #[derive(Clone, Copy, PartialEq, Eq, Debug, serde::Serialize, serde::Deserialize)]
41484
+ #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, serde::Serialize, serde::Deserialize)]
40538
41485
  #[serde(rename_all = "lowercase")]
40539
41486
  pub enum StructuredDataType {
40540
41487
  #[serde(rename = "json-ld")]
@@ -40563,7 +41510,14 @@ impl magnus::IntoValue for StructuredDataType {
40563
41510
 
40564
41511
  impl magnus::TryConvert for StructuredDataType {
40565
41512
  fn try_convert(val: magnus::Value) -> Result<Self, magnus::Error> {
40566
- let s: String = magnus::TryConvert::try_convert(val)?;
41513
+ // IntoValue emits Symbols, including when this enum is a Hash key.
41514
+ // Accept that exact representation as well as String inputs; do not
41515
+ // coerce arbitrary objects through to_s.
41516
+ let s: String = if let Some(symbol) = magnus::Symbol::from_value(val) {
41517
+ symbol.name()?.into_owned()
41518
+ } else {
41519
+ magnus::TryConvert::try_convert(val)?
41520
+ };
40567
41521
  // Accept the real serde wire value, the legacy always-snake_case symbol Magnus used to
40568
41522
  // emit unconditionally, and the verbatim PascalCase Rust variant name, so fixtures and
40569
41523
  // existing consumer code written in any of those styles keep working.
@@ -40582,7 +41536,7 @@ impl magnus::TryConvert for StructuredDataType {
40582
41536
  unsafe impl IntoValueFromNative for StructuredDataType {}
40583
41537
  unsafe impl TryConvertOwned for StructuredDataType {}
40584
41538
 
40585
- #[derive(Clone, Copy, PartialEq, Eq, Debug, serde::Serialize, serde::Deserialize)]
41539
+ #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, serde::Serialize, serde::Deserialize)]
40586
41540
  #[serde(rename_all = "snake_case")]
40587
41541
  pub enum SummaryStrategy {
40588
41542
  Extractive,
@@ -40607,7 +41561,14 @@ impl magnus::IntoValue for SummaryStrategy {
40607
41561
 
40608
41562
  impl magnus::TryConvert for SummaryStrategy {
40609
41563
  fn try_convert(val: magnus::Value) -> Result<Self, magnus::Error> {
40610
- let s: String = magnus::TryConvert::try_convert(val)?;
41564
+ // IntoValue emits Symbols, including when this enum is a Hash key.
41565
+ // Accept that exact representation as well as String inputs; do not
41566
+ // coerce arbitrary objects through to_s.
41567
+ let s: String = if let Some(symbol) = magnus::Symbol::from_value(val) {
41568
+ symbol.name()?.into_owned()
41569
+ } else {
41570
+ magnus::TryConvert::try_convert(val)?
41571
+ };
40611
41572
  // Accept the real serde wire value, the legacy always-snake_case symbol Magnus used to
40612
41573
  // emit unconditionally, and the verbatim PascalCase Rust variant name, so fixtures and
40613
41574
  // existing consumer code written in any of those styles keep working.
@@ -40625,7 +41586,7 @@ impl magnus::TryConvert for SummaryStrategy {
40625
41586
  unsafe impl IntoValueFromNative for SummaryStrategy {}
40626
41587
  unsafe impl TryConvertOwned for SummaryStrategy {}
40627
41588
 
40628
- #[derive(Clone, Copy, PartialEq, Eq, Debug, serde::Serialize, serde::Deserialize)]
41589
+ #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, serde::Serialize, serde::Deserialize)]
40629
41590
  #[serde(rename_all = "snake_case")]
40630
41591
  pub enum TableChunkingMode {
40631
41592
  Split,
@@ -40650,7 +41611,14 @@ impl magnus::IntoValue for TableChunkingMode {
40650
41611
 
40651
41612
  impl magnus::TryConvert for TableChunkingMode {
40652
41613
  fn try_convert(val: magnus::Value) -> Result<Self, magnus::Error> {
40653
- let s: String = magnus::TryConvert::try_convert(val)?;
41614
+ // IntoValue emits Symbols, including when this enum is a Hash key.
41615
+ // Accept that exact representation as well as String inputs; do not
41616
+ // coerce arbitrary objects through to_s.
41617
+ let s: String = if let Some(symbol) = magnus::Symbol::from_value(val) {
41618
+ symbol.name()?.into_owned()
41619
+ } else {
41620
+ magnus::TryConvert::try_convert(val)?
41621
+ };
40654
41622
  // Accept the real serde wire value, the legacy always-snake_case symbol Magnus used to
40655
41623
  // emit unconditionally, and the verbatim PascalCase Rust variant name, so fixtures and
40656
41624
  // existing consumer code written in any of those styles keep working.
@@ -40668,7 +41636,7 @@ impl magnus::TryConvert for TableChunkingMode {
40668
41636
  unsafe impl IntoValueFromNative for TableChunkingMode {}
40669
41637
  unsafe impl TryConvertOwned for TableChunkingMode {}
40670
41638
 
40671
- #[derive(Clone, Copy, PartialEq, Eq, Debug, serde::Serialize, serde::Deserialize)]
41639
+ #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, serde::Serialize, serde::Deserialize)]
40672
41640
  #[serde(rename_all = "snake_case")]
40673
41641
  pub enum TableModel {
40674
41642
  Tatr,
@@ -40701,7 +41669,14 @@ impl magnus::IntoValue for TableModel {
40701
41669
 
40702
41670
  impl magnus::TryConvert for TableModel {
40703
41671
  fn try_convert(val: magnus::Value) -> Result<Self, magnus::Error> {
40704
- let s: String = magnus::TryConvert::try_convert(val)?;
41672
+ // IntoValue emits Symbols, including when this enum is a Hash key.
41673
+ // Accept that exact representation as well as String inputs; do not
41674
+ // coerce arbitrary objects through to_s.
41675
+ let s: String = if let Some(symbol) = magnus::Symbol::from_value(val) {
41676
+ symbol.name()?.into_owned()
41677
+ } else {
41678
+ magnus::TryConvert::try_convert(val)?
41679
+ };
40705
41680
  // Accept the real serde wire value, the legacy always-snake_case symbol Magnus used to
40706
41681
  // emit unconditionally, and the verbatim PascalCase Rust variant name, so fixtures and
40707
41682
  // existing consumer code written in any of those styles keep working.
@@ -40723,7 +41698,7 @@ impl magnus::TryConvert for TableModel {
40723
41698
  unsafe impl IntoValueFromNative for TableModel {}
40724
41699
  unsafe impl TryConvertOwned for TableModel {}
40725
41700
 
40726
- #[derive(Clone, Copy, PartialEq, Eq, Debug, serde::Serialize, serde::Deserialize)]
41701
+ #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, serde::Serialize, serde::Deserialize)]
40727
41702
  #[serde(rename_all = "snake_case")]
40728
41703
  pub enum TableOverlapPreference {
40729
41704
  Content,
@@ -40750,7 +41725,14 @@ impl magnus::IntoValue for TableOverlapPreference {
40750
41725
 
40751
41726
  impl magnus::TryConvert for TableOverlapPreference {
40752
41727
  fn try_convert(val: magnus::Value) -> Result<Self, magnus::Error> {
40753
- let s: String = magnus::TryConvert::try_convert(val)?;
41728
+ // IntoValue emits Symbols, including when this enum is a Hash key.
41729
+ // Accept that exact representation as well as String inputs; do not
41730
+ // coerce arbitrary objects through to_s.
41731
+ let s: String = if let Some(symbol) = magnus::Symbol::from_value(val) {
41732
+ symbol.name()?.into_owned()
41733
+ } else {
41734
+ magnus::TryConvert::try_convert(val)?
41735
+ };
40754
41736
  // Accept the real serde wire value, the legacy always-snake_case symbol Magnus used to
40755
41737
  // emit unconditionally, and the verbatim PascalCase Rust variant name, so fixtures and
40756
41738
  // existing consumer code written in any of those styles keep working.
@@ -40769,7 +41751,7 @@ impl magnus::TryConvert for TableOverlapPreference {
40769
41751
  unsafe impl IntoValueFromNative for TableOverlapPreference {}
40770
41752
  unsafe impl TryConvertOwned for TableOverlapPreference {}
40771
41753
 
40772
- #[derive(Clone, Copy, PartialEq, Eq, Debug, serde::Serialize, serde::Deserialize)]
41754
+ #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, serde::Serialize, serde::Deserialize)]
40773
41755
  #[serde(rename_all = "lowercase")]
40774
41756
  pub enum TextDirection {
40775
41757
  #[serde(rename = "ltr")]
@@ -40799,7 +41781,14 @@ impl magnus::IntoValue for TextDirection {
40799
41781
 
40800
41782
  impl magnus::TryConvert for TextDirection {
40801
41783
  fn try_convert(val: magnus::Value) -> Result<Self, magnus::Error> {
40802
- let s: String = magnus::TryConvert::try_convert(val)?;
41784
+ // IntoValue emits Symbols, including when this enum is a Hash key.
41785
+ // Accept that exact representation as well as String inputs; do not
41786
+ // coerce arbitrary objects through to_s.
41787
+ let s: String = if let Some(symbol) = magnus::Symbol::from_value(val) {
41788
+ symbol.name()?.into_owned()
41789
+ } else {
41790
+ magnus::TryConvert::try_convert(val)?
41791
+ };
40803
41792
  // Accept the real serde wire value, the legacy always-snake_case symbol Magnus used to
40804
41793
  // emit unconditionally, and the verbatim PascalCase Rust variant name, so fixtures and
40805
41794
  // existing consumer code written in any of those styles keep working.
@@ -40818,7 +41807,7 @@ impl magnus::TryConvert for TextDirection {
40818
41807
  unsafe impl IntoValueFromNative for TextDirection {}
40819
41808
  unsafe impl TryConvertOwned for TextDirection {}
40820
41809
 
40821
- #[derive(Clone, Copy, PartialEq, Eq, Debug, serde::Serialize, serde::Deserialize)]
41810
+ #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, serde::Serialize, serde::Deserialize)]
40822
41811
  #[serde(rename_all = "snake_case")]
40823
41812
  pub enum TierStrategy {
40824
41813
  Auto,
@@ -40843,7 +41832,14 @@ impl magnus::IntoValue for TierStrategy {
40843
41832
 
40844
41833
  impl magnus::TryConvert for TierStrategy {
40845
41834
  fn try_convert(val: magnus::Value) -> Result<Self, magnus::Error> {
40846
- let s: String = magnus::TryConvert::try_convert(val)?;
41835
+ // IntoValue emits Symbols, including when this enum is a Hash key.
41836
+ // Accept that exact representation as well as String inputs; do not
41837
+ // coerce arbitrary objects through to_s.
41838
+ let s: String = if let Some(symbol) = magnus::Symbol::from_value(val) {
41839
+ symbol.name()?.into_owned()
41840
+ } else {
41841
+ magnus::TryConvert::try_convert(val)?
41842
+ };
40847
41843
  // Accept the real serde wire value, the legacy always-snake_case symbol Magnus used to
40848
41844
  // emit unconditionally, and the verbatim PascalCase Rust variant name, so fixtures and
40849
41845
  // existing consumer code written in any of those styles keep working.
@@ -40861,7 +41857,7 @@ impl magnus::TryConvert for TierStrategy {
40861
41857
  unsafe impl IntoValueFromNative for TierStrategy {}
40862
41858
  unsafe impl TryConvertOwned for TierStrategy {}
40863
41859
 
40864
- #[derive(Clone, Copy, PartialEq, Eq, Debug, serde::Serialize, serde::Deserialize)]
41860
+ #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, serde::Serialize, serde::Deserialize)]
40865
41861
  #[serde(rename_all = "snake_case")]
40866
41862
  pub enum UriKind {
40867
41863
  Hyperlink,
@@ -40894,7 +41890,14 @@ impl magnus::IntoValue for UriKind {
40894
41890
 
40895
41891
  impl magnus::TryConvert for UriKind {
40896
41892
  fn try_convert(val: magnus::Value) -> Result<Self, magnus::Error> {
40897
- let s: String = magnus::TryConvert::try_convert(val)?;
41893
+ // IntoValue emits Symbols, including when this enum is a Hash key.
41894
+ // Accept that exact representation as well as String inputs; do not
41895
+ // coerce arbitrary objects through to_s.
41896
+ let s: String = if let Some(symbol) = magnus::Symbol::from_value(val) {
41897
+ symbol.name()?.into_owned()
41898
+ } else {
41899
+ magnus::TryConvert::try_convert(val)?
41900
+ };
40898
41901
  // Accept the real serde wire value, the legacy always-snake_case symbol Magnus used to
40899
41902
  // emit unconditionally, and the verbatim PascalCase Rust variant name, so fixtures and
40900
41903
  // existing consumer code written in any of those styles keep working.
@@ -40916,7 +41919,7 @@ impl magnus::TryConvert for UriKind {
40916
41919
  unsafe impl IntoValueFromNative for UriKind {}
40917
41920
  unsafe impl TryConvertOwned for UriKind {}
40918
41921
 
40919
- #[derive(Clone, Copy, PartialEq, Eq, Debug, serde::Serialize, serde::Deserialize)]
41922
+ #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, serde::Serialize, serde::Deserialize)]
40920
41923
  pub enum UrlEscapeStyle {
40921
41924
  Angle,
40922
41925
  Percent,
@@ -40940,7 +41943,14 @@ impl magnus::IntoValue for UrlEscapeStyle {
40940
41943
 
40941
41944
  impl magnus::TryConvert for UrlEscapeStyle {
40942
41945
  fn try_convert(val: magnus::Value) -> Result<Self, magnus::Error> {
40943
- let s: String = magnus::TryConvert::try_convert(val)?;
41946
+ // IntoValue emits Symbols, including when this enum is a Hash key.
41947
+ // Accept that exact representation as well as String inputs; do not
41948
+ // coerce arbitrary objects through to_s.
41949
+ let s: String = if let Some(symbol) = magnus::Symbol::from_value(val) {
41950
+ symbol.name()?.into_owned()
41951
+ } else {
41952
+ magnus::TryConvert::try_convert(val)?
41953
+ };
40944
41954
  // Accept the real serde wire value, the legacy always-snake_case symbol Magnus used to
40945
41955
  // emit unconditionally, and the verbatim PascalCase Rust variant name, so fixtures and
40946
41956
  // existing consumer code written in any of those styles keep working.
@@ -40958,7 +41968,7 @@ impl magnus::TryConvert for UrlEscapeStyle {
40958
41968
  unsafe impl IntoValueFromNative for UrlEscapeStyle {}
40959
41969
  unsafe impl TryConvertOwned for UrlEscapeStyle {}
40960
41970
 
40961
- #[derive(Clone, Copy, PartialEq, Eq, Debug, serde::Serialize, serde::Deserialize)]
41971
+ #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, serde::Serialize, serde::Deserialize)]
40962
41972
  #[serde(rename_all = "snake_case")]
40963
41973
  pub enum UrlExtractionMode {
40964
41974
  Auto,
@@ -40985,7 +41995,14 @@ impl magnus::IntoValue for UrlExtractionMode {
40985
41995
 
40986
41996
  impl magnus::TryConvert for UrlExtractionMode {
40987
41997
  fn try_convert(val: magnus::Value) -> Result<Self, magnus::Error> {
40988
- let s: String = magnus::TryConvert::try_convert(val)?;
41998
+ // IntoValue emits Symbols, including when this enum is a Hash key.
41999
+ // Accept that exact representation as well as String inputs; do not
42000
+ // coerce arbitrary objects through to_s.
42001
+ let s: String = if let Some(symbol) = magnus::Symbol::from_value(val) {
42002
+ symbol.name()?.into_owned()
42003
+ } else {
42004
+ magnus::TryConvert::try_convert(val)?
42005
+ };
40989
42006
  // Accept the real serde wire value, the legacy always-snake_case symbol Magnus used to
40990
42007
  // emit unconditionally, and the verbatim PascalCase Rust variant name, so fixtures and
40991
42008
  // existing consumer code written in any of those styles keep working.
@@ -41030,7 +42047,8 @@ impl magnus::IntoValue for VlmFallbackPolicy {
41030
42047
 
41031
42048
  impl magnus::TryConvert for VlmFallbackPolicy {
41032
42049
  fn try_convert(val: magnus::Value) -> Result<Self, magnus::Error> {
41033
- // For data enums with fields (e.g., PageAction), try to deserialize from JSON first.
42050
+ // Generated Data variants carry native DTOs, not JSON-compatible display strings.
42051
+ let class_name: Option<String> = val.class().funcall("name", ())?; // For data enums with fields (e.g., PageAction), try to deserialize from JSON first.
41034
42052
  // For unit enums or when passed as a string, fall back to string-based conversion.
41035
42053
  let json_str: String = if let Ok(s) = <String as magnus::TryConvert>::try_convert(val) {
41036
42054
  s
@@ -41062,9 +42080,10 @@ impl magnus::TryConvert for VlmFallbackPolicy {
41062
42080
 
41063
42081
  unsafe impl IntoValueFromNative for VlmFallbackPolicy {}
41064
42082
  unsafe impl TryConvertOwned for VlmFallbackPolicy {}
42083
+
41065
42084
  impl VlmFallbackPolicy {}
41066
42085
 
41067
- #[derive(Clone, Copy, PartialEq, Eq, Debug, serde::Serialize, serde::Deserialize)]
42086
+ #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, serde::Serialize, serde::Deserialize)]
41068
42087
  #[serde(rename_all = "snake_case")]
41069
42088
  pub enum WhisperModel {
41070
42089
  Tiny,
@@ -41095,7 +42114,14 @@ impl magnus::IntoValue for WhisperModel {
41095
42114
 
41096
42115
  impl magnus::TryConvert for WhisperModel {
41097
42116
  fn try_convert(val: magnus::Value) -> Result<Self, magnus::Error> {
41098
- let s: String = magnus::TryConvert::try_convert(val)?;
42117
+ // IntoValue emits Symbols, including when this enum is a Hash key.
42118
+ // Accept that exact representation as well as String inputs; do not
42119
+ // coerce arbitrary objects through to_s.
42120
+ let s: String = if let Some(symbol) = magnus::Symbol::from_value(val) {
42121
+ symbol.name()?.into_owned()
42122
+ } else {
42123
+ magnus::TryConvert::try_convert(val)?
42124
+ };
41099
42125
  // Accept the real serde wire value, the legacy always-snake_case symbol Magnus used to
41100
42126
  // emit unconditionally, and the verbatim PascalCase Rust variant name, so fixtures and
41101
42127
  // existing consumer code written in any of those styles keep working.
@@ -41116,7 +42142,7 @@ impl magnus::TryConvert for WhisperModel {
41116
42142
  unsafe impl IntoValueFromNative for WhisperModel {}
41117
42143
  unsafe impl TryConvertOwned for WhisperModel {}
41118
42144
 
41119
- #[derive(Clone, Copy, PartialEq, Eq, Debug, serde::Serialize, serde::Deserialize)]
42145
+ #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, serde::Serialize, serde::Deserialize)]
41120
42146
  pub enum WhitespaceMode {
41121
42147
  Normalized,
41122
42148
  Strict,
@@ -41140,7 +42166,14 @@ impl magnus::IntoValue for WhitespaceMode {
41140
42166
 
41141
42167
  impl magnus::TryConvert for WhitespaceMode {
41142
42168
  fn try_convert(val: magnus::Value) -> Result<Self, magnus::Error> {
41143
- let s: String = magnus::TryConvert::try_convert(val)?;
42169
+ // IntoValue emits Symbols, including when this enum is a Hash key.
42170
+ // Accept that exact representation as well as String inputs; do not
42171
+ // coerce arbitrary objects through to_s.
42172
+ let s: String = if let Some(symbol) = magnus::Symbol::from_value(val) {
42173
+ symbol.name()?.into_owned()
42174
+ } else {
42175
+ magnus::TryConvert::try_convert(val)?
42176
+ };
41144
42177
  // Accept the real serde wire value, the legacy always-snake_case symbol Magnus used to
41145
42178
  // emit unconditionally, and the verbatim PascalCase Rust variant name, so fixtures and
41146
42179
  // existing consumer code written in any of those styles keep working.
@@ -41631,7 +42664,7 @@ impl RbOcrBackendBridgeRuntimeDispatcher {
41631
42664
  let (sender, receiver) = std::sync::mpsc::sync_channel(Self::QUEUE_CAPACITY);
41632
42665
  let inner = magnus::value::Opaque::from(rb_obj);
41633
42666
  let thread = ruby.thread_create_from_fn(move |ruby| {
41634
- while let Ok(job) = Self::recv_without_gvl(&receiver) {
42667
+ while let Ok(job) = Self::recv_without_gvl(ruby, &receiver) {
41635
42668
  let value = inner.get_inner_with(ruby);
41636
42669
  let _ = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
41637
42670
  job(ruby, value);
@@ -41664,33 +42697,47 @@ impl RbOcrBackendBridgeRuntimeDispatcher {
41664
42697
  }
41665
42698
 
41666
42699
  fn recv_without_gvl(
42700
+ ruby: &magnus::Ruby,
41667
42701
  receiver: &std::sync::mpsc::Receiver<RbOcrBackendBridgeRuntimeJob>,
41668
42702
  ) -> Result<RbOcrBackendBridgeRuntimeJob, std::sync::mpsc::RecvError> {
41669
42703
  struct ReceiveState<'a> {
41670
42704
  receiver: &'a std::sync::mpsc::Receiver<RbOcrBackendBridgeRuntimeJob>,
41671
- result: Option<Result<RbOcrBackendBridgeRuntimeJob, std::sync::mpsc::RecvError>>,
42705
+ result: Option<Result<RbOcrBackendBridgeRuntimeJob, std::sync::mpsc::RecvTimeoutError>>,
41672
42706
  }
41673
42707
 
41674
42708
  unsafe extern "C" fn receive(data: *mut std::ffi::c_void) -> *mut std::ffi::c_void {
41675
42709
  // SAFETY: the state pointer remains valid until Ruby returns from the
41676
42710
  // without-GVL callback on this same Ruby-created thread.
41677
42711
  let state = unsafe { &mut *(data as *mut ReceiveState<'_>) };
41678
- state.result = Some(state.receiver.recv());
42712
+ state.result = Some(state.receiver.recv_timeout(std::time::Duration::from_millis(50)));
41679
42713
  std::ptr::null_mut()
41680
42714
  }
41681
42715
 
41682
- let mut state = ReceiveState { receiver, result: None };
41683
- // SAFETY: this runs on the dedicated Ruby-created dispatcher thread. The
41684
- // callback touches only Rust channel state and the sender disconnect wakes it.
41685
- unsafe {
41686
- rb_sys::rb_thread_call_without_gvl(
41687
- Some(receive),
41688
- &mut state as *mut ReceiveState<'_> as *mut std::ffi::c_void,
41689
- None,
41690
- std::ptr::null_mut(),
41691
- );
42716
+ loop {
42717
+ let mut state = ReceiveState { receiver, result: None };
42718
+ // SAFETY: the callback only touches Rust channel state, and returns
42719
+ // within 50ms even if a registered sender survives interpreter exit.
42720
+ // The gvl2 API defers interrupts: process them through Magnus below
42721
+ // so a Ruby non-local exit cannot skip Rust stack destructors here.
42722
+ unsafe {
42723
+ rb_sys::rb_thread_call_without_gvl2(
42724
+ Some(receive),
42725
+ &mut state as *mut ReceiveState<'_> as *mut std::ffi::c_void,
42726
+ None,
42727
+ std::ptr::null_mut(),
42728
+ );
42729
+ }
42730
+ if ruby.thread_check_ints().is_err() {
42731
+ return Err(std::sync::mpsc::RecvError);
42732
+ }
42733
+ match state.result {
42734
+ Some(Ok(job)) => return Ok(job),
42735
+ Some(Err(std::sync::mpsc::RecvTimeoutError::Disconnected)) => {
42736
+ return Err(std::sync::mpsc::RecvError);
42737
+ }
42738
+ Some(Err(std::sync::mpsc::RecvTimeoutError::Timeout)) | None => continue,
42739
+ }
41692
42740
  }
41693
- state.result.unwrap_or_else(|| Err(std::sync::mpsc::RecvError))
41694
42741
  }
41695
42742
  }
41696
42743
 
@@ -42914,7 +43961,7 @@ impl RbPostProcessorBridgeRuntimeDispatcher {
42914
43961
  let (sender, receiver) = std::sync::mpsc::sync_channel(Self::QUEUE_CAPACITY);
42915
43962
  let inner = magnus::value::Opaque::from(rb_obj);
42916
43963
  let thread = ruby.thread_create_from_fn(move |ruby| {
42917
- while let Ok(job) = Self::recv_without_gvl(&receiver) {
43964
+ while let Ok(job) = Self::recv_without_gvl(ruby, &receiver) {
42918
43965
  let value = inner.get_inner_with(ruby);
42919
43966
  let _ = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
42920
43967
  job(ruby, value);
@@ -42947,33 +43994,47 @@ impl RbPostProcessorBridgeRuntimeDispatcher {
42947
43994
  }
42948
43995
 
42949
43996
  fn recv_without_gvl(
43997
+ ruby: &magnus::Ruby,
42950
43998
  receiver: &std::sync::mpsc::Receiver<RbPostProcessorBridgeRuntimeJob>,
42951
43999
  ) -> Result<RbPostProcessorBridgeRuntimeJob, std::sync::mpsc::RecvError> {
42952
44000
  struct ReceiveState<'a> {
42953
44001
  receiver: &'a std::sync::mpsc::Receiver<RbPostProcessorBridgeRuntimeJob>,
42954
- result: Option<Result<RbPostProcessorBridgeRuntimeJob, std::sync::mpsc::RecvError>>,
44002
+ result: Option<Result<RbPostProcessorBridgeRuntimeJob, std::sync::mpsc::RecvTimeoutError>>,
42955
44003
  }
42956
44004
 
42957
44005
  unsafe extern "C" fn receive(data: *mut std::ffi::c_void) -> *mut std::ffi::c_void {
42958
44006
  // SAFETY: the state pointer remains valid until Ruby returns from the
42959
44007
  // without-GVL callback on this same Ruby-created thread.
42960
44008
  let state = unsafe { &mut *(data as *mut ReceiveState<'_>) };
42961
- state.result = Some(state.receiver.recv());
44009
+ state.result = Some(state.receiver.recv_timeout(std::time::Duration::from_millis(50)));
42962
44010
  std::ptr::null_mut()
42963
44011
  }
42964
44012
 
42965
- let mut state = ReceiveState { receiver, result: None };
42966
- // SAFETY: this runs on the dedicated Ruby-created dispatcher thread. The
42967
- // callback touches only Rust channel state and the sender disconnect wakes it.
42968
- unsafe {
42969
- rb_sys::rb_thread_call_without_gvl(
42970
- Some(receive),
42971
- &mut state as *mut ReceiveState<'_> as *mut std::ffi::c_void,
42972
- None,
42973
- std::ptr::null_mut(),
42974
- );
44013
+ loop {
44014
+ let mut state = ReceiveState { receiver, result: None };
44015
+ // SAFETY: the callback only touches Rust channel state, and returns
44016
+ // within 50ms even if a registered sender survives interpreter exit.
44017
+ // The gvl2 API defers interrupts: process them through Magnus below
44018
+ // so a Ruby non-local exit cannot skip Rust stack destructors here.
44019
+ unsafe {
44020
+ rb_sys::rb_thread_call_without_gvl2(
44021
+ Some(receive),
44022
+ &mut state as *mut ReceiveState<'_> as *mut std::ffi::c_void,
44023
+ None,
44024
+ std::ptr::null_mut(),
44025
+ );
44026
+ }
44027
+ if ruby.thread_check_ints().is_err() {
44028
+ return Err(std::sync::mpsc::RecvError);
44029
+ }
44030
+ match state.result {
44031
+ Some(Ok(job)) => return Ok(job),
44032
+ Some(Err(std::sync::mpsc::RecvTimeoutError::Disconnected)) => {
44033
+ return Err(std::sync::mpsc::RecvError);
44034
+ }
44035
+ Some(Err(std::sync::mpsc::RecvTimeoutError::Timeout)) | None => continue,
44036
+ }
42975
44037
  }
42976
- state.result.unwrap_or_else(|| Err(std::sync::mpsc::RecvError))
42977
44038
  }
42978
44039
  }
42979
44040
 
@@ -43510,7 +44571,7 @@ impl RbValidatorBridgeRuntimeDispatcher {
43510
44571
  let (sender, receiver) = std::sync::mpsc::sync_channel(Self::QUEUE_CAPACITY);
43511
44572
  let inner = magnus::value::Opaque::from(rb_obj);
43512
44573
  let thread = ruby.thread_create_from_fn(move |ruby| {
43513
- while let Ok(job) = Self::recv_without_gvl(&receiver) {
44574
+ while let Ok(job) = Self::recv_without_gvl(ruby, &receiver) {
43514
44575
  let value = inner.get_inner_with(ruby);
43515
44576
  let _ = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
43516
44577
  job(ruby, value);
@@ -43543,33 +44604,47 @@ impl RbValidatorBridgeRuntimeDispatcher {
43543
44604
  }
43544
44605
 
43545
44606
  fn recv_without_gvl(
44607
+ ruby: &magnus::Ruby,
43546
44608
  receiver: &std::sync::mpsc::Receiver<RbValidatorBridgeRuntimeJob>,
43547
44609
  ) -> Result<RbValidatorBridgeRuntimeJob, std::sync::mpsc::RecvError> {
43548
44610
  struct ReceiveState<'a> {
43549
44611
  receiver: &'a std::sync::mpsc::Receiver<RbValidatorBridgeRuntimeJob>,
43550
- result: Option<Result<RbValidatorBridgeRuntimeJob, std::sync::mpsc::RecvError>>,
44612
+ result: Option<Result<RbValidatorBridgeRuntimeJob, std::sync::mpsc::RecvTimeoutError>>,
43551
44613
  }
43552
44614
 
43553
44615
  unsafe extern "C" fn receive(data: *mut std::ffi::c_void) -> *mut std::ffi::c_void {
43554
44616
  // SAFETY: the state pointer remains valid until Ruby returns from the
43555
44617
  // without-GVL callback on this same Ruby-created thread.
43556
44618
  let state = unsafe { &mut *(data as *mut ReceiveState<'_>) };
43557
- state.result = Some(state.receiver.recv());
44619
+ state.result = Some(state.receiver.recv_timeout(std::time::Duration::from_millis(50)));
43558
44620
  std::ptr::null_mut()
43559
44621
  }
43560
44622
 
43561
- let mut state = ReceiveState { receiver, result: None };
43562
- // SAFETY: this runs on the dedicated Ruby-created dispatcher thread. The
43563
- // callback touches only Rust channel state and the sender disconnect wakes it.
43564
- unsafe {
43565
- rb_sys::rb_thread_call_without_gvl(
43566
- Some(receive),
43567
- &mut state as *mut ReceiveState<'_> as *mut std::ffi::c_void,
43568
- None,
43569
- std::ptr::null_mut(),
43570
- );
44623
+ loop {
44624
+ let mut state = ReceiveState { receiver, result: None };
44625
+ // SAFETY: the callback only touches Rust channel state, and returns
44626
+ // within 50ms even if a registered sender survives interpreter exit.
44627
+ // The gvl2 API defers interrupts: process them through Magnus below
44628
+ // so a Ruby non-local exit cannot skip Rust stack destructors here.
44629
+ unsafe {
44630
+ rb_sys::rb_thread_call_without_gvl2(
44631
+ Some(receive),
44632
+ &mut state as *mut ReceiveState<'_> as *mut std::ffi::c_void,
44633
+ None,
44634
+ std::ptr::null_mut(),
44635
+ );
44636
+ }
44637
+ if ruby.thread_check_ints().is_err() {
44638
+ return Err(std::sync::mpsc::RecvError);
44639
+ }
44640
+ match state.result {
44641
+ Some(Ok(job)) => return Ok(job),
44642
+ Some(Err(std::sync::mpsc::RecvTimeoutError::Disconnected)) => {
44643
+ return Err(std::sync::mpsc::RecvError);
44644
+ }
44645
+ Some(Err(std::sync::mpsc::RecvTimeoutError::Timeout)) | None => continue,
44646
+ }
43571
44647
  }
43572
- state.result.unwrap_or_else(|| Err(std::sync::mpsc::RecvError))
43573
44648
  }
43574
44649
  }
43575
44650
 
@@ -43967,7 +45042,7 @@ impl RbDocumentExtractorBridgeRuntimeDispatcher {
43967
45042
  let (sender, receiver) = std::sync::mpsc::sync_channel(Self::QUEUE_CAPACITY);
43968
45043
  let inner = magnus::value::Opaque::from(rb_obj);
43969
45044
  let thread = ruby.thread_create_from_fn(move |ruby| {
43970
- while let Ok(job) = Self::recv_without_gvl(&receiver) {
45045
+ while let Ok(job) = Self::recv_without_gvl(ruby, &receiver) {
43971
45046
  let value = inner.get_inner_with(ruby);
43972
45047
  let _ = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
43973
45048
  job(ruby, value);
@@ -44000,33 +45075,47 @@ impl RbDocumentExtractorBridgeRuntimeDispatcher {
44000
45075
  }
44001
45076
 
44002
45077
  fn recv_without_gvl(
45078
+ ruby: &magnus::Ruby,
44003
45079
  receiver: &std::sync::mpsc::Receiver<RbDocumentExtractorBridgeRuntimeJob>,
44004
45080
  ) -> Result<RbDocumentExtractorBridgeRuntimeJob, std::sync::mpsc::RecvError> {
44005
45081
  struct ReceiveState<'a> {
44006
45082
  receiver: &'a std::sync::mpsc::Receiver<RbDocumentExtractorBridgeRuntimeJob>,
44007
- result: Option<Result<RbDocumentExtractorBridgeRuntimeJob, std::sync::mpsc::RecvError>>,
45083
+ result: Option<Result<RbDocumentExtractorBridgeRuntimeJob, std::sync::mpsc::RecvTimeoutError>>,
44008
45084
  }
44009
45085
 
44010
45086
  unsafe extern "C" fn receive(data: *mut std::ffi::c_void) -> *mut std::ffi::c_void {
44011
45087
  // SAFETY: the state pointer remains valid until Ruby returns from the
44012
45088
  // without-GVL callback on this same Ruby-created thread.
44013
45089
  let state = unsafe { &mut *(data as *mut ReceiveState<'_>) };
44014
- state.result = Some(state.receiver.recv());
45090
+ state.result = Some(state.receiver.recv_timeout(std::time::Duration::from_millis(50)));
44015
45091
  std::ptr::null_mut()
44016
45092
  }
44017
45093
 
44018
- let mut state = ReceiveState { receiver, result: None };
44019
- // SAFETY: this runs on the dedicated Ruby-created dispatcher thread. The
44020
- // callback touches only Rust channel state and the sender disconnect wakes it.
44021
- unsafe {
44022
- rb_sys::rb_thread_call_without_gvl(
44023
- Some(receive),
44024
- &mut state as *mut ReceiveState<'_> as *mut std::ffi::c_void,
44025
- None,
44026
- std::ptr::null_mut(),
44027
- );
45094
+ loop {
45095
+ let mut state = ReceiveState { receiver, result: None };
45096
+ // SAFETY: the callback only touches Rust channel state, and returns
45097
+ // within 50ms even if a registered sender survives interpreter exit.
45098
+ // The gvl2 API defers interrupts: process them through Magnus below
45099
+ // so a Ruby non-local exit cannot skip Rust stack destructors here.
45100
+ unsafe {
45101
+ rb_sys::rb_thread_call_without_gvl2(
45102
+ Some(receive),
45103
+ &mut state as *mut ReceiveState<'_> as *mut std::ffi::c_void,
45104
+ None,
45105
+ std::ptr::null_mut(),
45106
+ );
45107
+ }
45108
+ if ruby.thread_check_ints().is_err() {
45109
+ return Err(std::sync::mpsc::RecvError);
45110
+ }
45111
+ match state.result {
45112
+ Some(Ok(job)) => return Ok(job),
45113
+ Some(Err(std::sync::mpsc::RecvTimeoutError::Disconnected)) => {
45114
+ return Err(std::sync::mpsc::RecvError);
45115
+ }
45116
+ Some(Err(std::sync::mpsc::RecvTimeoutError::Timeout)) | None => continue,
45117
+ }
44028
45118
  }
44029
- state.result.unwrap_or_else(|| Err(std::sync::mpsc::RecvError))
44030
45119
  }
44031
45120
  }
44032
45121
 
@@ -44469,7 +45558,7 @@ impl RbEmbeddingBackendBridgeRuntimeDispatcher {
44469
45558
  let (sender, receiver) = std::sync::mpsc::sync_channel(Self::QUEUE_CAPACITY);
44470
45559
  let inner = magnus::value::Opaque::from(rb_obj);
44471
45560
  let thread = ruby.thread_create_from_fn(move |ruby| {
44472
- while let Ok(job) = Self::recv_without_gvl(&receiver) {
45561
+ while let Ok(job) = Self::recv_without_gvl(ruby, &receiver) {
44473
45562
  let value = inner.get_inner_with(ruby);
44474
45563
  let _ = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
44475
45564
  job(ruby, value);
@@ -44502,33 +45591,47 @@ impl RbEmbeddingBackendBridgeRuntimeDispatcher {
44502
45591
  }
44503
45592
 
44504
45593
  fn recv_without_gvl(
45594
+ ruby: &magnus::Ruby,
44505
45595
  receiver: &std::sync::mpsc::Receiver<RbEmbeddingBackendBridgeRuntimeJob>,
44506
45596
  ) -> Result<RbEmbeddingBackendBridgeRuntimeJob, std::sync::mpsc::RecvError> {
44507
45597
  struct ReceiveState<'a> {
44508
45598
  receiver: &'a std::sync::mpsc::Receiver<RbEmbeddingBackendBridgeRuntimeJob>,
44509
- result: Option<Result<RbEmbeddingBackendBridgeRuntimeJob, std::sync::mpsc::RecvError>>,
45599
+ result: Option<Result<RbEmbeddingBackendBridgeRuntimeJob, std::sync::mpsc::RecvTimeoutError>>,
44510
45600
  }
44511
45601
 
44512
45602
  unsafe extern "C" fn receive(data: *mut std::ffi::c_void) -> *mut std::ffi::c_void {
44513
45603
  // SAFETY: the state pointer remains valid until Ruby returns from the
44514
45604
  // without-GVL callback on this same Ruby-created thread.
44515
45605
  let state = unsafe { &mut *(data as *mut ReceiveState<'_>) };
44516
- state.result = Some(state.receiver.recv());
45606
+ state.result = Some(state.receiver.recv_timeout(std::time::Duration::from_millis(50)));
44517
45607
  std::ptr::null_mut()
44518
45608
  }
44519
45609
 
44520
- let mut state = ReceiveState { receiver, result: None };
44521
- // SAFETY: this runs on the dedicated Ruby-created dispatcher thread. The
44522
- // callback touches only Rust channel state and the sender disconnect wakes it.
44523
- unsafe {
44524
- rb_sys::rb_thread_call_without_gvl(
44525
- Some(receive),
44526
- &mut state as *mut ReceiveState<'_> as *mut std::ffi::c_void,
44527
- None,
44528
- std::ptr::null_mut(),
44529
- );
45610
+ loop {
45611
+ let mut state = ReceiveState { receiver, result: None };
45612
+ // SAFETY: the callback only touches Rust channel state, and returns
45613
+ // within 50ms even if a registered sender survives interpreter exit.
45614
+ // The gvl2 API defers interrupts: process them through Magnus below
45615
+ // so a Ruby non-local exit cannot skip Rust stack destructors here.
45616
+ unsafe {
45617
+ rb_sys::rb_thread_call_without_gvl2(
45618
+ Some(receive),
45619
+ &mut state as *mut ReceiveState<'_> as *mut std::ffi::c_void,
45620
+ None,
45621
+ std::ptr::null_mut(),
45622
+ );
45623
+ }
45624
+ if ruby.thread_check_ints().is_err() {
45625
+ return Err(std::sync::mpsc::RecvError);
45626
+ }
45627
+ match state.result {
45628
+ Some(Ok(job)) => return Ok(job),
45629
+ Some(Err(std::sync::mpsc::RecvTimeoutError::Disconnected)) => {
45630
+ return Err(std::sync::mpsc::RecvError);
45631
+ }
45632
+ Some(Err(std::sync::mpsc::RecvTimeoutError::Timeout)) | None => continue,
45633
+ }
44530
45634
  }
44531
- state.result.unwrap_or_else(|| Err(std::sync::mpsc::RecvError))
44532
45635
  }
44533
45636
  }
44534
45637
 
@@ -44796,7 +45899,7 @@ impl RbRendererBridgeRuntimeDispatcher {
44796
45899
  let (sender, receiver) = std::sync::mpsc::sync_channel(Self::QUEUE_CAPACITY);
44797
45900
  let inner = magnus::value::Opaque::from(rb_obj);
44798
45901
  let thread = ruby.thread_create_from_fn(move |ruby| {
44799
- while let Ok(job) = Self::recv_without_gvl(&receiver) {
45902
+ while let Ok(job) = Self::recv_without_gvl(ruby, &receiver) {
44800
45903
  let value = inner.get_inner_with(ruby);
44801
45904
  let _ = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
44802
45905
  job(ruby, value);
@@ -44829,33 +45932,47 @@ impl RbRendererBridgeRuntimeDispatcher {
44829
45932
  }
44830
45933
 
44831
45934
  fn recv_without_gvl(
45935
+ ruby: &magnus::Ruby,
44832
45936
  receiver: &std::sync::mpsc::Receiver<RbRendererBridgeRuntimeJob>,
44833
45937
  ) -> Result<RbRendererBridgeRuntimeJob, std::sync::mpsc::RecvError> {
44834
45938
  struct ReceiveState<'a> {
44835
45939
  receiver: &'a std::sync::mpsc::Receiver<RbRendererBridgeRuntimeJob>,
44836
- result: Option<Result<RbRendererBridgeRuntimeJob, std::sync::mpsc::RecvError>>,
45940
+ result: Option<Result<RbRendererBridgeRuntimeJob, std::sync::mpsc::RecvTimeoutError>>,
44837
45941
  }
44838
45942
 
44839
45943
  unsafe extern "C" fn receive(data: *mut std::ffi::c_void) -> *mut std::ffi::c_void {
44840
45944
  // SAFETY: the state pointer remains valid until Ruby returns from the
44841
45945
  // without-GVL callback on this same Ruby-created thread.
44842
45946
  let state = unsafe { &mut *(data as *mut ReceiveState<'_>) };
44843
- state.result = Some(state.receiver.recv());
45947
+ state.result = Some(state.receiver.recv_timeout(std::time::Duration::from_millis(50)));
44844
45948
  std::ptr::null_mut()
44845
45949
  }
44846
45950
 
44847
- let mut state = ReceiveState { receiver, result: None };
44848
- // SAFETY: this runs on the dedicated Ruby-created dispatcher thread. The
44849
- // callback touches only Rust channel state and the sender disconnect wakes it.
44850
- unsafe {
44851
- rb_sys::rb_thread_call_without_gvl(
44852
- Some(receive),
44853
- &mut state as *mut ReceiveState<'_> as *mut std::ffi::c_void,
44854
- None,
44855
- std::ptr::null_mut(),
44856
- );
45951
+ loop {
45952
+ let mut state = ReceiveState { receiver, result: None };
45953
+ // SAFETY: the callback only touches Rust channel state, and returns
45954
+ // within 50ms even if a registered sender survives interpreter exit.
45955
+ // The gvl2 API defers interrupts: process them through Magnus below
45956
+ // so a Ruby non-local exit cannot skip Rust stack destructors here.
45957
+ unsafe {
45958
+ rb_sys::rb_thread_call_without_gvl2(
45959
+ Some(receive),
45960
+ &mut state as *mut ReceiveState<'_> as *mut std::ffi::c_void,
45961
+ None,
45962
+ std::ptr::null_mut(),
45963
+ );
45964
+ }
45965
+ if ruby.thread_check_ints().is_err() {
45966
+ return Err(std::sync::mpsc::RecvError);
45967
+ }
45968
+ match state.result {
45969
+ Some(Ok(job)) => return Ok(job),
45970
+ Some(Err(std::sync::mpsc::RecvTimeoutError::Disconnected)) => {
45971
+ return Err(std::sync::mpsc::RecvError);
45972
+ }
45973
+ Some(Err(std::sync::mpsc::RecvTimeoutError::Timeout)) | None => continue,
45974
+ }
44857
45975
  }
44858
- state.result.unwrap_or_else(|| Err(std::sync::mpsc::RecvError))
44859
45976
  }
44860
45977
  }
44861
45978
 
@@ -45099,7 +46216,7 @@ impl RbRerankerBackendBridgeRuntimeDispatcher {
45099
46216
  let (sender, receiver) = std::sync::mpsc::sync_channel(Self::QUEUE_CAPACITY);
45100
46217
  let inner = magnus::value::Opaque::from(rb_obj);
45101
46218
  let thread = ruby.thread_create_from_fn(move |ruby| {
45102
- while let Ok(job) = Self::recv_without_gvl(&receiver) {
46219
+ while let Ok(job) = Self::recv_without_gvl(ruby, &receiver) {
45103
46220
  let value = inner.get_inner_with(ruby);
45104
46221
  let _ = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
45105
46222
  job(ruby, value);
@@ -45132,33 +46249,47 @@ impl RbRerankerBackendBridgeRuntimeDispatcher {
45132
46249
  }
45133
46250
 
45134
46251
  fn recv_without_gvl(
46252
+ ruby: &magnus::Ruby,
45135
46253
  receiver: &std::sync::mpsc::Receiver<RbRerankerBackendBridgeRuntimeJob>,
45136
46254
  ) -> Result<RbRerankerBackendBridgeRuntimeJob, std::sync::mpsc::RecvError> {
45137
46255
  struct ReceiveState<'a> {
45138
46256
  receiver: &'a std::sync::mpsc::Receiver<RbRerankerBackendBridgeRuntimeJob>,
45139
- result: Option<Result<RbRerankerBackendBridgeRuntimeJob, std::sync::mpsc::RecvError>>,
46257
+ result: Option<Result<RbRerankerBackendBridgeRuntimeJob, std::sync::mpsc::RecvTimeoutError>>,
45140
46258
  }
45141
46259
 
45142
46260
  unsafe extern "C" fn receive(data: *mut std::ffi::c_void) -> *mut std::ffi::c_void {
45143
46261
  // SAFETY: the state pointer remains valid until Ruby returns from the
45144
46262
  // without-GVL callback on this same Ruby-created thread.
45145
46263
  let state = unsafe { &mut *(data as *mut ReceiveState<'_>) };
45146
- state.result = Some(state.receiver.recv());
46264
+ state.result = Some(state.receiver.recv_timeout(std::time::Duration::from_millis(50)));
45147
46265
  std::ptr::null_mut()
45148
46266
  }
45149
46267
 
45150
- let mut state = ReceiveState { receiver, result: None };
45151
- // SAFETY: this runs on the dedicated Ruby-created dispatcher thread. The
45152
- // callback touches only Rust channel state and the sender disconnect wakes it.
45153
- unsafe {
45154
- rb_sys::rb_thread_call_without_gvl(
45155
- Some(receive),
45156
- &mut state as *mut ReceiveState<'_> as *mut std::ffi::c_void,
45157
- None,
45158
- std::ptr::null_mut(),
45159
- );
46268
+ loop {
46269
+ let mut state = ReceiveState { receiver, result: None };
46270
+ // SAFETY: the callback only touches Rust channel state, and returns
46271
+ // within 50ms even if a registered sender survives interpreter exit.
46272
+ // The gvl2 API defers interrupts: process them through Magnus below
46273
+ // so a Ruby non-local exit cannot skip Rust stack destructors here.
46274
+ unsafe {
46275
+ rb_sys::rb_thread_call_without_gvl2(
46276
+ Some(receive),
46277
+ &mut state as *mut ReceiveState<'_> as *mut std::ffi::c_void,
46278
+ None,
46279
+ std::ptr::null_mut(),
46280
+ );
46281
+ }
46282
+ if ruby.thread_check_ints().is_err() {
46283
+ return Err(std::sync::mpsc::RecvError);
46284
+ }
46285
+ match state.result {
46286
+ Some(Ok(job)) => return Ok(job),
46287
+ Some(Err(std::sync::mpsc::RecvTimeoutError::Disconnected)) => {
46288
+ return Err(std::sync::mpsc::RecvError);
46289
+ }
46290
+ Some(Err(std::sync::mpsc::RecvTimeoutError::Timeout)) | None => continue,
46291
+ }
45160
46292
  }
45161
- state.result.unwrap_or_else(|| Err(std::sync::mpsc::RecvError))
45162
46293
  }
45163
46294
  }
45164
46295
 
@@ -45394,7 +46525,7 @@ impl RbTokenizerBackendBridgeRuntimeDispatcher {
45394
46525
  let (sender, receiver) = std::sync::mpsc::sync_channel(Self::QUEUE_CAPACITY);
45395
46526
  let inner = magnus::value::Opaque::from(rb_obj);
45396
46527
  let thread = ruby.thread_create_from_fn(move |ruby| {
45397
- while let Ok(job) = Self::recv_without_gvl(&receiver) {
46528
+ while let Ok(job) = Self::recv_without_gvl(ruby, &receiver) {
45398
46529
  let value = inner.get_inner_with(ruby);
45399
46530
  let _ = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
45400
46531
  job(ruby, value);
@@ -45427,33 +46558,47 @@ impl RbTokenizerBackendBridgeRuntimeDispatcher {
45427
46558
  }
45428
46559
 
45429
46560
  fn recv_without_gvl(
46561
+ ruby: &magnus::Ruby,
45430
46562
  receiver: &std::sync::mpsc::Receiver<RbTokenizerBackendBridgeRuntimeJob>,
45431
46563
  ) -> Result<RbTokenizerBackendBridgeRuntimeJob, std::sync::mpsc::RecvError> {
45432
46564
  struct ReceiveState<'a> {
45433
46565
  receiver: &'a std::sync::mpsc::Receiver<RbTokenizerBackendBridgeRuntimeJob>,
45434
- result: Option<Result<RbTokenizerBackendBridgeRuntimeJob, std::sync::mpsc::RecvError>>,
46566
+ result: Option<Result<RbTokenizerBackendBridgeRuntimeJob, std::sync::mpsc::RecvTimeoutError>>,
45435
46567
  }
45436
46568
 
45437
46569
  unsafe extern "C" fn receive(data: *mut std::ffi::c_void) -> *mut std::ffi::c_void {
45438
46570
  // SAFETY: the state pointer remains valid until Ruby returns from the
45439
46571
  // without-GVL callback on this same Ruby-created thread.
45440
46572
  let state = unsafe { &mut *(data as *mut ReceiveState<'_>) };
45441
- state.result = Some(state.receiver.recv());
46573
+ state.result = Some(state.receiver.recv_timeout(std::time::Duration::from_millis(50)));
45442
46574
  std::ptr::null_mut()
45443
46575
  }
45444
46576
 
45445
- let mut state = ReceiveState { receiver, result: None };
45446
- // SAFETY: this runs on the dedicated Ruby-created dispatcher thread. The
45447
- // callback touches only Rust channel state and the sender disconnect wakes it.
45448
- unsafe {
45449
- rb_sys::rb_thread_call_without_gvl(
45450
- Some(receive),
45451
- &mut state as *mut ReceiveState<'_> as *mut std::ffi::c_void,
45452
- None,
45453
- std::ptr::null_mut(),
45454
- );
46577
+ loop {
46578
+ let mut state = ReceiveState { receiver, result: None };
46579
+ // SAFETY: the callback only touches Rust channel state, and returns
46580
+ // within 50ms even if a registered sender survives interpreter exit.
46581
+ // The gvl2 API defers interrupts: process them through Magnus below
46582
+ // so a Ruby non-local exit cannot skip Rust stack destructors here.
46583
+ unsafe {
46584
+ rb_sys::rb_thread_call_without_gvl2(
46585
+ Some(receive),
46586
+ &mut state as *mut ReceiveState<'_> as *mut std::ffi::c_void,
46587
+ None,
46588
+ std::ptr::null_mut(),
46589
+ );
46590
+ }
46591
+ if ruby.thread_check_ints().is_err() {
46592
+ return Err(std::sync::mpsc::RecvError);
46593
+ }
46594
+ match state.result {
46595
+ Some(Ok(job)) => return Ok(job),
46596
+ Some(Err(std::sync::mpsc::RecvTimeoutError::Disconnected)) => {
46597
+ return Err(std::sync::mpsc::RecvError);
46598
+ }
46599
+ Some(Err(std::sync::mpsc::RecvTimeoutError::Timeout)) | None => continue,
46600
+ }
45455
46601
  }
45456
- state.result.unwrap_or_else(|| Err(std::sync::mpsc::RecvError))
45457
46602
  }
45458
46603
  }
45459
46604
 
@@ -46808,8 +47953,12 @@ impl From<xberg::candle_ocr::DeepseekOcrBackendOptions> for DeepseekOcrBackendOp
46808
47953
  fn from(val: xberg::candle_ocr::DeepseekOcrBackendOptions) -> Self {
46809
47954
  Self {
46810
47955
  model_path: val.model_path.map(|v| v.to_string()),
47956
+ model_id: val.model_id.map(|v| v.to_string()),
47957
+ hf_revision: val.hf_revision.map(|v| v.to_string()),
47958
+ cache_dir: val.cache_dir.map(|v| v.to_string()),
46811
47959
  device: val.device.map(Into::into),
46812
47960
  version: val.version,
47961
+ dtype: val.dtype.map(Into::into),
46813
47962
  }
46814
47963
  }
46815
47964
  }
@@ -50016,6 +51165,8 @@ impl From<OcrQualityThresholds> for xberg::OcrQualityThresholds {
50016
51165
  min_undecodable_ratio: val.min_undecodable_ratio,
50017
51166
  enable_provenance_ocr_routing: val.enable_provenance_ocr_routing,
50018
51167
  min_provenance_fallback_ratio: val.min_provenance_fallback_ratio,
51168
+ enable_plausibility_ocr_routing: val.enable_plausibility_ocr_routing,
51169
+ min_reliable_language_chunk_ratio: val.min_reliable_language_chunk_ratio,
50019
51170
  ..Default::default()
50020
51171
  }
50021
51172
  }
@@ -50048,6 +51199,8 @@ impl From<xberg::OcrQualityThresholds> for OcrQualityThresholds {
50048
51199
  min_undecodable_ratio: val.min_undecodable_ratio,
50049
51200
  enable_provenance_ocr_routing: val.enable_provenance_ocr_routing,
50050
51201
  min_provenance_fallback_ratio: val.min_provenance_fallback_ratio,
51202
+ enable_plausibility_ocr_routing: val.enable_plausibility_ocr_routing,
51203
+ min_reliable_language_chunk_ratio: val.min_reliable_language_chunk_ratio,
50051
51204
  }
50052
51205
  }
50053
51206
  }
@@ -50614,6 +51767,8 @@ impl From<PdfMetadata> for xberg::pdf::metadata::PdfMetadata {
50614
51767
  page_count: val.page_count,
50615
51768
  scanned_confidence: val.scanned_confidence,
50616
51769
  scanned_pages: val.scanned_pages.map(|v| v.into_iter().collect()),
51770
+ fabricated_text_pages: val.fabricated_text_pages.map(|v| v.into_iter().collect()),
51771
+ implausible_text_pages: val.implausible_text_pages.map(|v| v.into_iter().collect()),
50617
51772
  layout_gated_pages: val.layout_gated_pages.map(|v| v.into_iter().collect()),
50618
51773
  layout_gate_reasons: val.layout_gate_reasons.map(|v| v.into_iter().collect()),
50619
51774
  ..Default::default()
@@ -50634,6 +51789,8 @@ impl From<xberg::pdf::metadata::PdfMetadata> for PdfMetadata {
50634
51789
  page_count: val.page_count,
50635
51790
  scanned_confidence: val.scanned_confidence,
50636
51791
  scanned_pages: val.scanned_pages.map(|v| v.into_iter().collect()),
51792
+ fabricated_text_pages: val.fabricated_text_pages.map(|v| v.into_iter().collect()),
51793
+ implausible_text_pages: val.implausible_text_pages.map(|v| v.into_iter().collect()),
50637
51794
  layout_gated_pages: val.layout_gated_pages.map(|v| v.into_iter().collect()),
50638
51795
  layout_gate_reasons: val.layout_gate_reasons.map(|v| v.into_iter().collect()),
50639
51796
  }
@@ -52422,6 +53579,18 @@ impl From<xberg::CallMode> for CallMode {
52422
53579
  }
52423
53580
  }
52424
53581
 
53582
+ #[cfg(feature = "candle-ocr")]
53583
+ impl From<xberg::candle_ocr::CandleDeepseekOcrDtype> for CandleDeepseekOcrDtype {
53584
+ fn from(val: xberg::candle_ocr::CandleDeepseekOcrDtype) -> Self {
53585
+ match val {
53586
+ xberg::candle_ocr::CandleDeepseekOcrDtype::Auto => Self::Auto,
53587
+ xberg::candle_ocr::CandleDeepseekOcrDtype::F32 => Self::F32,
53588
+ xberg::candle_ocr::CandleDeepseekOcrDtype::F16 => Self::F16,
53589
+ xberg::candle_ocr::CandleDeepseekOcrDtype::Bf16 => Self::Bf16,
53590
+ }
53591
+ }
53592
+ }
53593
+
52425
53594
  #[cfg(feature = "candle-ocr")]
52426
53595
  impl From<xberg::candle_ocr::CandleDevicePreference> for CandleDevicePreference {
52427
53596
  fn from(val: xberg::candle_ocr::CandleDevicePreference) -> Self {
@@ -52624,6 +53793,16 @@ impl From<xberg::CodeDataNodeKind> for CodeDataNodeKind {
52624
53793
  }
52625
53794
  }
52626
53795
 
53796
+ impl From<ConfidenceSemantics> for xberg::ConfidenceSemantics {
53797
+ fn from(val: ConfidenceSemantics) -> Self {
53798
+ match val {
53799
+ ConfidenceSemantics::Legibility { scale_max } => Self::Legibility { scale_max },
53800
+ ConfidenceSemantics::Uncalibrated => Self::Uncalibrated,
53801
+ ConfidenceSemantics::None => Self::None,
53802
+ }
53803
+ }
53804
+ }
53805
+
52627
53806
  impl From<xberg::ConfidenceSemantics> for ConfidenceSemantics {
52628
53807
  fn from(val: xberg::ConfidenceSemantics) -> Self {
52629
53808
  match val {
@@ -52850,7 +54029,7 @@ impl From<EmbeddingModelType> for xberg::EmbeddingModelType {
52850
54029
  EmbeddingModelType::Preset { name } => Self::Preset { name },
52851
54030
  EmbeddingModelType::Custom { model_id, dimensions } => Self::Custom { model_id, dimensions },
52852
54031
  EmbeddingModelType::Llm { llm } => Self::Llm {
52853
- llm: Box::new(llm.into()),
54032
+ llm: Box::new((*llm).into()),
52854
54033
  },
52855
54034
  EmbeddingModelType::Plugin { name } => Self::Plugin { name },
52856
54035
  }
@@ -52865,7 +54044,9 @@ impl From<xberg::EmbeddingModelType> for EmbeddingModelType {
52865
54044
  model_id: model_id.to_string(),
52866
54045
  dimensions,
52867
54046
  },
52868
- xberg::EmbeddingModelType::Llm { llm } => Self::Llm { llm: (*llm).into() },
54047
+ xberg::EmbeddingModelType::Llm { llm } => Self::Llm {
54048
+ llm: Box::new((*llm).into()),
54049
+ },
52869
54050
  xberg::EmbeddingModelType::Plugin { name } => Self::Plugin { name: name.to_string() },
52870
54051
  }
52871
54052
  }
@@ -53013,7 +54194,7 @@ impl From<FormatMetadata> for xberg::FormatMetadata {
53013
54194
  #[cfg(feature = "pdf")]
53014
54195
  FormatMetadata::Pdf { _0 } => Self::Pdf(_0.into()),
53015
54196
  #[cfg(feature = "office")]
53016
- FormatMetadata::Docx { _0 } => Self::Docx(Box::new(_0.into())),
54197
+ FormatMetadata::Docx { _0 } => Self::Docx(Box::new((*_0).into())),
53017
54198
  FormatMetadata::Excel { _0 } => Self::Excel(_0.into()),
53018
54199
  FormatMetadata::Email { _0 } => Self::Email(_0.into()),
53019
54200
  FormatMetadata::Pptx { _0 } => Self::Pptx(_0.into()),
@@ -53021,7 +54202,7 @@ impl From<FormatMetadata> for xberg::FormatMetadata {
53021
54202
  FormatMetadata::Image { _0 } => Self::Image(_0.into()),
53022
54203
  FormatMetadata::Xml { _0 } => Self::Xml(_0.into()),
53023
54204
  FormatMetadata::Text { _0 } => Self::Text(_0.into()),
53024
- FormatMetadata::Html { _0 } => Self::Html(Box::new(_0.into())),
54205
+ FormatMetadata::Html { _0 } => Self::Html(Box::new((*_0).into())),
53025
54206
  FormatMetadata::Ocr { _0 } => Self::Ocr(_0.into()),
53026
54207
  FormatMetadata::Csv { _0 } => Self::Csv(_0.into()),
53027
54208
  #[cfg(feature = "office")]
@@ -53051,7 +54232,9 @@ impl From<xberg::FormatMetadata> for FormatMetadata {
53051
54232
  #[cfg(feature = "pdf")]
53052
54233
  xberg::FormatMetadata::Pdf(_0) => Self::Pdf { _0: _0.into() },
53053
54234
  #[cfg(feature = "office")]
53054
- xberg::FormatMetadata::Docx(_0) => Self::Docx { _0: (*_0).into() },
54235
+ xberg::FormatMetadata::Docx(_0) => Self::Docx {
54236
+ _0: Box::new((*_0).into()),
54237
+ },
53055
54238
  xberg::FormatMetadata::Excel(_0) => Self::Excel { _0: _0.into() },
53056
54239
  xberg::FormatMetadata::Email(_0) => Self::Email { _0: _0.into() },
53057
54240
  xberg::FormatMetadata::Pptx(_0) => Self::Pptx { _0: _0.into() },
@@ -53059,7 +54242,9 @@ impl From<xberg::FormatMetadata> for FormatMetadata {
53059
54242
  xberg::FormatMetadata::Image(_0) => Self::Image { _0: _0.into() },
53060
54243
  xberg::FormatMetadata::Xml(_0) => Self::Xml { _0: _0.into() },
53061
54244
  xberg::FormatMetadata::Text(_0) => Self::Text { _0: _0.into() },
53062
- xberg::FormatMetadata::Html(_0) => Self::Html { _0: (*_0).into() },
54245
+ xberg::FormatMetadata::Html(_0) => Self::Html {
54246
+ _0: Box::new((*_0).into()),
54247
+ },
53063
54248
  xberg::FormatMetadata::Ocr(_0) => Self::Ocr { _0: _0.into() },
53064
54249
  xberg::FormatMetadata::Csv(_0) => Self::Csv { _0: _0.into() },
53065
54250
  #[cfg(feature = "office")]
@@ -53792,6 +54977,17 @@ impl From<xberg::NodeContent> for NodeContent {
53792
54977
  }
53793
54978
  }
53794
54979
 
54980
+ impl From<OcrBackendType> for xberg::OcrBackendType {
54981
+ fn from(val: OcrBackendType) -> Self {
54982
+ match val {
54983
+ OcrBackendType::Tesseract => Self::Tesseract,
54984
+ OcrBackendType::PaddleOCR => Self::PaddleOCR,
54985
+ OcrBackendType::Candle => Self::Candle,
54986
+ OcrBackendType::Custom => Self::Custom,
54987
+ }
54988
+ }
54989
+ }
54990
+
53795
54991
  impl From<xberg::OcrBackendType> for OcrBackendType {
53796
54992
  fn from(val: xberg::OcrBackendType) -> Self {
53797
54993
  match val {
@@ -53988,6 +55184,16 @@ impl From<xberg::candle_ocr::PaddleOcrVlTaskKind> for PaddleOcrVlTaskKind {
53988
55184
  }
53989
55185
  }
53990
55186
 
55187
+ impl From<PageOrientationHandling> for xberg::PageOrientationHandling {
55188
+ fn from(val: PageOrientationHandling) -> Self {
55189
+ match val {
55190
+ PageOrientationHandling::SelfCorrecting => Self::SelfCorrecting,
55191
+ PageOrientationHandling::RecognisesRotatedText => Self::RecognisesRotatedText,
55192
+ PageOrientationHandling::RequiresUpright => Self::RequiresUpright,
55193
+ }
55194
+ }
55195
+ }
55196
+
53991
55197
  impl From<xberg::PageOrientationHandling> for PageOrientationHandling {
53992
55198
  fn from(val: xberg::PageOrientationHandling) -> Self {
53993
55199
  match val {
@@ -54200,6 +55406,16 @@ impl From<xberg::ProbeStatus> for ProbeStatus {
54200
55406
  }
54201
55407
  }
54202
55408
 
55409
+ impl From<ProcessingStage> for xberg::ProcessingStage {
55410
+ fn from(val: ProcessingStage) -> Self {
55411
+ match val {
55412
+ ProcessingStage::Early => Self::Early,
55413
+ ProcessingStage::Middle => Self::Middle,
55414
+ ProcessingStage::Late => Self::Late,
55415
+ }
55416
+ }
55417
+ }
55418
+
54203
55419
  impl From<xberg::ProcessingStage> for ProcessingStage {
54204
55420
  fn from(val: xberg::ProcessingStage) -> Self {
54205
55421
  match val {
@@ -54333,7 +55549,7 @@ impl From<RerankerModelType> for xberg::RerankerModelType {
54333
55549
  head: head.into(),
54334
55550
  },
54335
55551
  RerankerModelType::Llm { llm } => Self::Llm {
54336
- llm: Box::new(llm.into()),
55552
+ llm: Box::new((*llm).into()),
54337
55553
  },
54338
55554
  RerankerModelType::Plugin { name } => Self::Plugin { name },
54339
55555
  }
@@ -54357,7 +55573,9 @@ impl From<xberg::RerankerModelType> for RerankerModelType {
54357
55573
  max_length,
54358
55574
  head: head.into(),
54359
55575
  },
54360
- xberg::RerankerModelType::Llm { llm } => Self::Llm { llm: (*llm).into() },
55576
+ xberg::RerankerModelType::Llm { llm } => Self::Llm {
55577
+ llm: Box::new((*llm).into()),
55578
+ },
54361
55579
  xberg::RerankerModelType::Plugin { name } => Self::Plugin { name: name.to_string() },
54362
55580
  }
54363
55581
  }
@@ -55594,12 +56812,24 @@ fn ruby_init(ruby: &Ruby) -> Result<(), Error> {
55594
56812
  #[cfg(feature = "candle-ocr")]
55595
56813
  class.define_method("model_path", method!(DeepseekOcrBackendOptions::model_path, 0))?;
55596
56814
 
56815
+ #[cfg(feature = "candle-ocr")]
56816
+ class.define_method("model_id", method!(DeepseekOcrBackendOptions::model_id, 0))?;
56817
+
56818
+ #[cfg(feature = "candle-ocr")]
56819
+ class.define_method("hf_revision", method!(DeepseekOcrBackendOptions::hf_revision, 0))?;
56820
+
56821
+ #[cfg(feature = "candle-ocr")]
56822
+ class.define_method("cache_dir", method!(DeepseekOcrBackendOptions::cache_dir, 0))?;
56823
+
55597
56824
  #[cfg(feature = "candle-ocr")]
55598
56825
  class.define_method("device", method!(DeepseekOcrBackendOptions::device, 0))?;
55599
56826
 
55600
56827
  #[cfg(feature = "candle-ocr")]
55601
56828
  class.define_method("version", method!(DeepseekOcrBackendOptions::version, 0))?;
55602
56829
 
56830
+ #[cfg(feature = "candle-ocr")]
56831
+ class.define_method("dtype", method!(DeepseekOcrBackendOptions::dtype, 0))?;
56832
+
55603
56833
  let class = module.define_class("DetectResponse", ruby.class_object())?;
55604
56834
 
55605
56835
  #[cfg(feature = "api")]
@@ -56485,6 +57715,16 @@ fn ruby_init(ruby: &Ruby) -> Result<(), Error> {
56485
57715
 
56486
57716
  class.define_method("needs_image_data", method!(ExtractionConfig::needs_image_data, 0))?;
56487
57717
 
57718
+ class.define_method(
57719
+ "runs_ocr_on_embedded_images",
57720
+ method!(ExtractionConfig::runs_ocr_on_embedded_images, 0),
57721
+ )?;
57722
+
57723
+ class.define_method(
57724
+ "wants_own_bytes_in_result",
57725
+ method!(ExtractionConfig::wants_own_bytes_in_result, 0),
57726
+ )?;
57727
+
56488
57728
  class.define_method(
56489
57729
  "needs_image_processing",
56490
57730
  method!(ExtractionConfig::needs_image_processing, 0),
@@ -57966,6 +59206,16 @@ fn ruby_init(ruby: &Ruby) -> Result<(), Error> {
57966
59206
  method!(OcrQualityThresholds::min_provenance_fallback_ratio, 0),
57967
59207
  )?;
57968
59208
 
59209
+ class.define_method(
59210
+ "enable_plausibility_ocr_routing",
59211
+ method!(OcrQualityThresholds::enable_plausibility_ocr_routing, 0),
59212
+ )?;
59213
+
59214
+ class.define_method(
59215
+ "min_reliable_language_chunk_ratio",
59216
+ method!(OcrQualityThresholds::min_reliable_language_chunk_ratio, 0),
59217
+ )?;
59218
+
57969
59219
  let class = module.define_class("OcrRotation", ruby.class_object())?;
57970
59220
 
57971
59221
  class.define_singleton_method("new", function!(OcrRotation::new, -1))?;
@@ -58450,6 +59700,15 @@ fn ruby_init(ruby: &Ruby) -> Result<(), Error> {
58450
59700
  #[cfg(feature = "pdf")]
58451
59701
  class.define_method("scanned_pages", method!(PdfMetadata::scanned_pages, 0))?;
58452
59702
 
59703
+ #[cfg(feature = "pdf")]
59704
+ class.define_method("fabricated_text_pages", method!(PdfMetadata::fabricated_text_pages, 0))?;
59705
+
59706
+ #[cfg(feature = "pdf")]
59707
+ class.define_method(
59708
+ "implausible_text_pages",
59709
+ method!(PdfMetadata::implausible_text_pages, 0),
59710
+ )?;
59711
+
58453
59712
  #[cfg(feature = "pdf")]
58454
59713
  class.define_method("layout_gated_pages", method!(PdfMetadata::layout_gated_pages, 0))?;
58455
59714