You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@opendal.apache.org by xu...@apache.org on 2026/09/11 16:58:37 UTC

(opendal) branch xuanwo/hf-resolve-cache-opt-in created (now 88bb3a66e)

This is an automated email from the ASF dual-hosted git repository.

Xuanwo pushed a change to branch xuanwo/hf-resolve-cache-opt-in
in repository https://gitbox.apache.org/repos/asf/opendal.git


      at 88bb3a66e fix(services/hf): make resolve cache opt-in

This branch includes the following new commits:

     new 88bb3a66e fix(services/hf): make resolve cache opt-in

The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.



(opendal) 01/01: fix(services/hf): make resolve cache opt-in

Posted by xu...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

Xuanwo pushed a commit to branch xuanwo/hf-resolve-cache-opt-in
in repository https://gitbox.apache.org/repos/asf/opendal.git

commit 88bb3a66e528a0958dc15b41b844d7384f86cb7a
Author: Xuanwo <gi...@xuanwo.io>
AuthorDate: Sat Sep 12 00:58:27 2026 +0800

    fix(services/hf): make resolve cache opt-in
---
 .../ServiceConfig/HuggingfaceServiceConfig.cs      | 16 +++---
 .../java/org/apache/opendal/ServiceConfig.java     | 20 ++++----
 bindings/python/python/opendal/config.py           |  4 +-
 core/services/hf/src/backend.rs                    | 52 +++++++++++++++-----
 core/services/hf/src/config.rs                     | 57 ++++++++++++++++++----
 core/services/hf/src/core.rs                       | 10 ++--
 core/services/hf/src/docs.md                       | 27 +++++-----
 core/services/hf/src/reader.rs                     | 42 +++++++++++++---
 website/data/services.json                         | 14 +++---
 9 files changed, 167 insertions(+), 75 deletions(-)

diff --git a/bindings/dotnet/OpenDAL/ServiceConfig/HuggingfaceServiceConfig.cs b/bindings/dotnet/OpenDAL/ServiceConfig/HuggingfaceServiceConfig.cs
index 685efe912..3d210d519 100644
--- a/bindings/dotnet/OpenDAL/ServiceConfig/HuggingfaceServiceConfig.cs
+++ b/bindings/dotnet/OpenDAL/ServiceConfig/HuggingfaceServiceConfig.cs
@@ -33,13 +33,13 @@ namespace OpenDAL.ServiceConfig
         /// </summary>
         public string? DownloadMode { get; init; }
         /// <summary>
-        /// Endpoint of the Hugging Face Hub. The default is `https://huggingface.co`.
+        /// Enable caching of resolved HTTP download addresses and XET file metadata. Defaults to `false`. Set to `true` to share resolve results across readers on the same backend. Changed files may remain invisible while cached results are reused. See [`HfBuilder::enable_resolve_cache`] for freshness semantics.
         /// </summary>
-        public string? Endpoint { get; init; }
+        public bool? EnableResolveCache { get; init; }
         /// <summary>
-        /// Resolve every range through the Hugging Face Hub. Defaults to `false`. Set to `true` to resolve every range through the Hub. See [`HfBuilder::force_resolve`] for cache and freshness semantics.
+        /// Endpoint of the Hugging Face Hub. The default is `https://huggingface.co`.
         /// </summary>
-        public bool? ForceResolve { get; init; }
+        public string? Endpoint { get; init; }
         /// <summary>
         /// Repo id of this backend. This is required.
         /// </summary>
@@ -70,13 +70,13 @@ namespace OpenDAL.ServiceConfig
             {
                 map["download_mode"] = Utilities.ToOptionString(DownloadMode);
             }
-            if (Endpoint is not null)
+            if (EnableResolveCache is not null)
             {
-                map["endpoint"] = Utilities.ToOptionString(Endpoint);
+                map["enable_resolve_cache"] = Utilities.ToOptionString(EnableResolveCache);
             }
-            if (ForceResolve is not null)
+            if (Endpoint is not null)
             {
-                map["force_resolve"] = Utilities.ToOptionString(ForceResolve);
+                map["endpoint"] = Utilities.ToOptionString(Endpoint);
             }
             if (RepoId is not null)
             {
diff --git a/bindings/java/src/main/java/org/apache/opendal/ServiceConfig.java b/bindings/java/src/main/java/org/apache/opendal/ServiceConfig.java
index 70287125d..7d1048204 100644
--- a/bindings/java/src/main/java/org/apache/opendal/ServiceConfig.java
+++ b/bindings/java/src/main/java/org/apache/opendal/ServiceConfig.java
@@ -1681,17 +1681,19 @@ public interface ServiceConfig {
          * <p>See <a href="https://huggingface.co/docs/huggingface_hub/package_reference/environment_variables#hfhubdisablexet">https://huggingface.co/docs/huggingface_hub/package_reference/environment_variables#hfhubdisablexet</a>.</p>
          */
         public final String downloadMode;
+        /**
+         * <p>Enable caching of resolved HTTP download addresses and XET file metadata.</p>
+         * <p>Defaults to <code>false</code>. Set to <code>true</code> to share resolve results across readers
+         * on the same backend. Changed files may remain invisible while cached
+         * results are reused. See [<code>HfBuilder::enable_resolve_cache</code>] for freshness
+         * semantics.</p>
+         */
+        public final Boolean enableResolveCache;
         /**
          * <p>Endpoint of the Hugging Face Hub.</p>
          * <p>The default is <code>https://huggingface.co</code>.</p>
          */
         public final String endpoint;
-        /**
-         * <p>Resolve every range through the Hugging Face Hub. Defaults to <code>false</code>.</p>
-         * <p>Set to <code>true</code> to resolve every range through the Hub. See
-         * [<code>HfBuilder::force_resolve</code>] for cache and freshness semantics.</p>
-         */
-        public final Boolean forceResolve;
         /**
          * <p>Repo id of this backend.</p>
          * <p>This is required.</p>
@@ -1728,12 +1730,12 @@ public interface ServiceConfig {
             if (downloadMode != null) {
                 map.put("download_mode", downloadMode);
             }
+            if (enableResolveCache != null) {
+                map.put("enable_resolve_cache", String.valueOf(enableResolveCache));
+            }
             if (endpoint != null) {
                 map.put("endpoint", endpoint);
             }
-            if (forceResolve != null) {
-                map.put("force_resolve", String.valueOf(forceResolve));
-            }
             if (repoId != null) {
                 map.put("repo_id", repoId);
             }
diff --git a/bindings/python/python/opendal/config.py b/bindings/python/python/opendal/config.py
index a8eb04f0e..924be85b5 100644
--- a/bindings/python/python/opendal/config.py
+++ b/bindings/python/python/opendal/config.py
@@ -410,10 +410,10 @@ class HfConfig(TypedDict):
     """The service scheme; fixed to `"hf"`."""
     download_mode: NotRequired[str]
     """Download mode. Either `xet` (default) or `http`.  When unset, the mode is resolved from the `HF_HUB_DISABLE_XET` environment variable: a non-empty value forces `http`, otherwise it defaults to `xet`. An explicit value here takes precedence.  See <https://huggingface.co/docs/huggingface_hub/package_reference/environment_variables#hfhubdisablexet>."""
+    enable_resolve_cache: NotRequired[bool]
+    """Enable caching of resolved HTTP download addresses and XET file metadata.  Defaults to `false`. Set to `true` to share resolve results across readers on the same backend. Changed files may remain invisible while cached results are reused. See [`HfBuilder::enable_resolve_cache`] for freshness semantics."""
     endpoint: NotRequired[str]
     """Endpoint of the Hugging Face Hub.  The default is `https://huggingface.co`."""
-    force_resolve: NotRequired[bool]
-    """Resolve every range through the Hugging Face Hub. Defaults to `false`.  Set to `true` to resolve every range through the Hub. See [`HfBuilder::force_resolve`] for cache and freshness semantics."""
     repo_id: NotRequired[str]
     """Repo id of this backend.  This is required."""
     repo_type: NotRequired[str]
diff --git a/core/services/hf/src/backend.rs b/core/services/hf/src/backend.rs
index 78de27333..5f6a857e0 100644
--- a/core/services/hf/src/backend.rs
+++ b/core/services/hf/src/backend.rs
@@ -133,20 +133,19 @@ impl HfBuilder {
         self
     }
 
-    /// Resolve every range through the Hugging Face Hub.
+    /// Enable caching of resolved HTTP download addresses and XET file metadata.
     ///
-    /// Defaults to `false`: readers share resolved HTTP download addresses and
-    /// XET file metadata on the same backend. HTTP addresses refresh near expiry.
-    /// Set to `true` to resolve every range through the Hub in either mode.
-    /// This bypasses cached HTTP addresses and XET file metadata.
+    /// Defaults to `false`: every range resolves through the Hub in either
+    /// download mode. Set to `true` to share resolve results across readers on
+    /// the same backend. HTTP addresses refresh near expiry.
     ///
-    /// Enable this when paths can be overwritten, deleted and reused, or moved
-    /// to different content by another client or a floating repository revision.
-    /// With reuse enabled, an issued download URL can remain usable until expiry
-    /// even after Hub permissions change. Separate authorization identities must
-    /// use separately constructed backends.
-    pub fn force_resolve(mut self, enabled: bool) -> Self {
-        self.config.force_resolve = enabled;
+    /// Enable this only when previously written files are not modified. Changed
+    /// files can remain invisible while cached results are reused, including
+    /// changes from other clients or a floating repository revision. Issued
+    /// download URLs can remain usable until expiry after Hub permissions change.
+    /// Separate authorization identities must use separately constructed backends.
+    pub fn enable_resolve_cache(mut self, enabled: bool) -> Self {
+        self.config.enable_resolve_cache = enabled;
         self
     }
 
@@ -284,7 +283,7 @@ impl Builder for HfBuilder {
         debug!("backend repo uri: {:?}", repo.uri(&root, ""));
 
         let mut core = HfCore::build(info, capability, repo, root, token, endpoint, download_mode)?;
-        core.force_resolve = self.config.force_resolve;
+        core.enable_resolve_cache = self.config.enable_resolve_cache;
         Ok(HfBackend {
             core: Arc::new(core),
         })
@@ -642,6 +641,33 @@ mod tests {
         assert_eq!(mode, HfDownloadMode::Xet);
     }
 
+    #[tokio::test]
+    async fn build_resolve_cache_requires_opt_in() -> Result<()> {
+        use super::super::core::test_utils::MockHttpTransport;
+
+        for enabled in [None, Some(false), Some(true)] {
+            let mut builder = HfBuilder::default()
+                .repo_type("model")
+                .repo_id("org/repo")
+                .download_mode("xet");
+            if let Some(enabled) = enabled {
+                builder = builder.enable_resolve_cache(enabled);
+            }
+            let transport = MockHttpTransport::new();
+            let ctx = OperationContext::new()
+                .with_http_transport(HttpTransporter::new(transport.clone()));
+            let op = Operator::new(builder)?.with_context(ctx);
+            for reads in 1..=2 {
+                assert_eq!(op.read("plain.txt").await?.to_vec(), b"hello");
+                assert_eq!(
+                    transport.request_count(),
+                    reads + usize::from(enabled == Some(true))
+                );
+            }
+        }
+        Ok(())
+    }
+
     #[test]
     fn build_accepts_datasets_alias() {
         HfBuilder::default()
diff --git a/core/services/hf/src/config.rs b/core/services/hf/src/config.rs
index 44b13cbcd..9e77389df 100644
--- a/core/services/hf/src/config.rs
+++ b/core/services/hf/src/config.rs
@@ -60,11 +60,13 @@ pub struct HfConfig {
     ///
     /// See <https://huggingface.co/docs/huggingface_hub/package_reference/environment_variables#hfhubdisablexet>.
     pub download_mode: Option<HfDownloadMode>,
-    /// Resolve every range through the Hugging Face Hub. Defaults to `false`.
+    /// Enable caching of resolved HTTP download addresses and XET file metadata.
     ///
-    /// Set to `true` to resolve every range through the Hub. See
-    /// [`HfBuilder::force_resolve`] for cache and freshness semantics.
-    pub force_resolve: bool,
+    /// Defaults to `false`. Set to `true` to share resolve results across readers
+    /// on the same backend. Changed files may remain invisible while cached
+    /// results are reused. See [`HfBuilder::enable_resolve_cache`] for freshness
+    /// semantics.
+    pub enable_resolve_cache: bool,
 }
 
 impl Debug for HfConfig {
@@ -78,7 +80,7 @@ impl Debug for HfConfig {
             .field("revision", &self.revision)
             .field("root", &self.root)
             .field("download_mode", &self.download_mode)
-            .field("force_resolve", &self.force_resolve)
+            .field("enable_resolve_cache", &self.enable_resolve_cache)
             .finish_non_exhaustive()
     }
 }
@@ -112,14 +114,14 @@ impl opendal_core::Configurator for HfConfig {
             .map(|s| HfDownloadMode::parse(s))
             .transpose()?;
 
-        let force_resolve = opts
-            .get("force_resolve")
+        let enable_resolve_cache = opts
+            .get("enable_resolve_cache")
             .map(|value| value.parse::<bool>())
             .transpose()
             .map_err(|err| {
                 opendal_core::Error::new(
                     opendal_core::ErrorKind::ConfigInvalid,
-                    "force_resolve must be true or false",
+                    "enable_resolve_cache must be true or false",
                 )
                 .set_source(err)
             })?
@@ -136,7 +138,7 @@ impl opendal_core::Configurator for HfConfig {
                 token: opts.get("token").cloned(),
                 endpoint: opts.get("endpoint").cloned(),
                 download_mode,
-                force_resolve,
+                enable_resolve_cache,
             })
         } else {
             // Bare scheme from via_iter, all config is in options.
@@ -158,7 +160,7 @@ impl opendal_core::Configurator for HfConfig {
                 token: opts.get("token").cloned(),
                 endpoint: opts.get("endpoint").cloned(),
                 download_mode,
-                force_resolve,
+                enable_resolve_cache,
             })
         }
     }
@@ -243,4 +245,39 @@ mod tests {
         let cfg = HfConfig::from_uri(&uri).unwrap();
         assert_eq!(cfg.download_mode.unwrap_or_default(), HfDownloadMode::Xet);
     }
+
+    #[test]
+    fn from_uri_resolve_cache_options() {
+        for scheme in ["hf://datasets/user/repo", "huggingface"] {
+            for (value, expected) in [(None, false), (Some("false"), false), (Some("true"), true)] {
+                let mut options = vec![("repo_type", "dataset"), ("repo_id", "user/repo")];
+                if let Some(value) = value {
+                    options.push(("enable_resolve_cache", value));
+                }
+                let uri = OperatorUri::new(
+                    scheme,
+                    options
+                        .into_iter()
+                        .map(|(key, value)| (key.to_string(), value.to_string())),
+                )
+                .unwrap();
+                assert_eq!(
+                    HfConfig::from_uri(&uri).unwrap().enable_resolve_cache,
+                    expected
+                );
+            }
+
+            let uri = OperatorUri::new(
+                scheme,
+                [("enable_resolve_cache".to_string(), "invalid".to_string())],
+            )
+            .unwrap();
+            let err = HfConfig::from_uri(&uri).unwrap_err();
+            assert_eq!(err.kind(), opendal_core::ErrorKind::ConfigInvalid);
+            assert!(
+                err.to_string()
+                    .contains("enable_resolve_cache must be true or false")
+            );
+        }
+    }
 }
diff --git a/core/services/hf/src/core.rs b/core/services/hf/src/core.rs
index 0dc37f654..8a505cf8e 100644
--- a/core/services/hf/src/core.rs
+++ b/core/services/hf/src/core.rs
@@ -427,7 +427,7 @@ pub struct HfCore {
     pub endpoint: String,
     pub xet_session: XetSession,
     pub download_mode: HfDownloadMode,
-    pub force_resolve: bool,
+    pub enable_resolve_cache: bool,
     /// Shared file resolutions, keyed by paths relative to this core's root.
     resolved_files: Arc<ResolvedFiles>,
     /// Cached CAS read token, shared by every `XetDownloadStreamGroup` this
@@ -470,7 +470,7 @@ impl HfCore {
             endpoint,
             xet_session,
             download_mode,
-            force_resolve: false,
+            enable_resolve_cache: false,
             resolved_files: Arc::default(),
             xet_read_token: Arc::new(Mutex::new(None)),
             xet_write_token: Arc::new(Mutex::new(None)),
@@ -777,10 +777,10 @@ impl HfCore {
         path: &str,
         range: BytesRange,
     ) -> Result<HfReadResponse> {
-        let entry = if self.force_resolve {
-            None
-        } else {
+        let entry = if self.enable_resolve_cache {
             self.resolved_files.entry(path)
+        } else {
+            None
         };
         let Some(entry) = entry else {
             let resp = self.resolve(ctx, path, range, self.download_mode).await?;
diff --git a/core/services/hf/src/docs.md b/core/services/hf/src/docs.md
index 9cc121794..d6c6e3d43 100644
--- a/core/services/hf/src/docs.md
+++ b/core/services/hf/src/docs.md
@@ -32,26 +32,25 @@ Use [`crate::HfConfig`] for serializable configuration and this builder's
 methods for direct construction. The field and method documentation defines
 accepted values, defaults, and environment interaction.
 
-### Reading files that can change
+### Caching resolve results
 
-OpenDAL assumes that previously written files are not modified. Each backend
-shares resolved HTTP download addresses and XET file metadata across readers and
-batches. If files can change, enable
-[`force_resolve`](crate::Hf::force_resolve) to resolve every
-range through the Hub:
+By default, every range resolves through the Hugging Face Hub in both HTTP and
+XET download modes. Enable
+[`enable_resolve_cache`](crate::Hf::enable_resolve_cache) to share resolved HTTP
+download addresses and XET file metadata across readers and batches on the same
+backend:
 
 ```rust,no_run
 let builder = opendal_service_hf::Hf::default()
-    .repo_type("bucket")
-    .repo_id("username/my-bucket")
-    .force_resolve(true);
+    .repo_type("dataset")
+    .repo_id("username/my-dataset")
+    .enable_resolve_cache(true);
 ```
 
-The option controls read freshness, not permission to write. It also applies to
-paths changed by other clients or a floating repository revision. With the
-option disabled, changed files can remain invisible while a resolved address or
-XET file metadata is reused. Issued signed URLs can also remain usable after Hub
-permissions change.
+Enable the cache only when previously written files are not modified. Changed
+files can remain invisible while a resolved address or XET file metadata is
+reused, including changes from other clients or a floating repository revision.
+Issued signed URLs can also remain usable after Hub permissions change.
 
 The shared cache retains bounded in-memory metadata. HTTP addresses refresh
 30 seconds before their signed expiry, on the next read, or once after a cached
diff --git a/core/services/hf/src/reader.rs b/core/services/hf/src/reader.rs
index 7b0379f80..306c43dc9 100644
--- a/core/services/hf/src/reader.rs
+++ b/core/services/hf/src/reader.rs
@@ -185,6 +185,29 @@ mod tests {
         assert_eq!(xet_range(BytesRange::new(4, None)), Some(4..u64::MAX));
     }
 
+    #[tokio::test]
+    async fn test_default_reads_resolve_each_range_across_readers() -> Result<()> {
+        for mode in [HfDownloadMode::Http, HfDownloadMode::Xet] {
+            let (mut core, ctx, mock_client) = create_test_core(
+                HfRepoType::Model,
+                "test-user/test-repo",
+                "main",
+                "https://huggingface.co",
+            );
+            core.download_mode = mode;
+            let core = Arc::new(core);
+            let first = hf_reader_from_arc(core.clone(), ctx.clone(), "plain.txt");
+            let second = hf_reader_from_arc(core, ctx, "plain.txt");
+
+            for (index, reader) in [&first, &first, &second].into_iter().enumerate() {
+                let (_, mut stream) = reader.open(BytesRange::new(index as u64, Some(1))).await?;
+                assert!(!stream.read().await?.is_empty());
+                assert_eq!(mock_client.request_count(), index + 1);
+            }
+        }
+        Ok(())
+    }
+
     #[tokio::test]
     async fn test_http_read_uses_resolve_url() -> Result<()> {
         let (core, ctx, mock_client) = create_test_core(
@@ -237,12 +260,13 @@ mod tests {
     /// classification already cached).
     #[tokio::test]
     async fn test_non_xet_reads_cache_classification_but_still_fetch_each_range() -> Result<()> {
-        let (core, ctx, mock_client) = create_test_core(
+        let (mut core, ctx, mock_client) = create_test_core(
             HfRepoType::Model,
             "test-user/test-repo",
             "main",
             "https://huggingface.co",
         );
+        core.enable_resolve_cache = true;
         let reader = hf_reader(core, ctx, "plain.txt");
 
         let (_, mut s1) = reader.open(BytesRange::new(0, Some(1))).await?;
@@ -261,12 +285,13 @@ mod tests {
     /// stuck erroring for the reader's whole lifetime.
     #[tokio::test]
     async fn test_classification_retries_after_resolve_failure() -> Result<()> {
-        let (core, ctx, mock_client) = create_test_core(
+        let (mut core, ctx, mock_client) = create_test_core(
             HfRepoType::Model,
             "test-user/test-repo",
             "main",
             "https://huggingface.co",
         );
+        core.enable_resolve_cache = true;
         let reader = hf_reader(core, ctx, "plain.txt");
         mock_client.fail_next_requests(1);
 
@@ -295,12 +320,13 @@ mod tests {
     /// racing to build its own group.
     #[tokio::test]
     async fn test_concurrent_cold_opens_on_xet_file_share_one_group() -> Result<()> {
-        let (core, ctx, mock_client) = create_test_core(
+        let (mut core, ctx, mock_client) = create_test_core(
             HfRepoType::Model,
             "test-user/test-repo",
             "main",
             "https://huggingface.co",
         );
+        core.enable_resolve_cache = true;
         mock_client.set_xet_backed(&"00".repeat(32), 4);
         mock_client.set_xet_token_expires_at(u64::MAX);
         let reader = hf_reader(core, ctx, "xet-file.bin");
@@ -330,12 +356,13 @@ mod tests {
     /// (248,987 B measured before this).
     #[tokio::test]
     async fn test_xet_probe_uses_fixed_single_byte_range() -> Result<()> {
-        let (core, ctx, mock_client) = create_test_core(
+        let (mut core, ctx, mock_client) = create_test_core(
             HfRepoType::Model,
             "test-user/test-repo",
             "main",
             "https://huggingface.co",
         );
+        core.enable_resolve_cache = true;
         mock_client.set_xet_backed(&"00".repeat(32), 64);
         mock_client.set_xet_token_expires_at(u64::MAX);
         let reader = hf_reader(core, ctx, "xet-file.bin");
@@ -385,12 +412,13 @@ mod tests {
     /// it isn't XET-backed. Each still fetches its own range afterward.
     #[tokio::test]
     async fn test_concurrent_cold_opens_share_one_classifying_resolve() -> Result<()> {
-        let (core, ctx, mock_client) = create_test_core(
+        let (mut core, ctx, mock_client) = create_test_core(
             HfRepoType::Model,
             "test-user/test-repo",
             "main",
             "https://huggingface.co",
         );
+        core.enable_resolve_cache = true;
         let reader = hf_reader(core, ctx, "plain.txt");
 
         let (r1, r2, r3) = futures::join!(
@@ -533,8 +561,8 @@ mod tests {
 
     /// Exercises the group-reuse path for real: two ranges fetched through
     /// the same `Reader` (mirroring `object_store::get_ranges`) must both
-    /// return correct bytes even though only the first call resolves the
-    /// XET hash and builds the CAS download group.
+    /// return correct bytes while each call resolves the XET hash and the
+    /// reader reuses its CAS download group.
     #[tokio::test]
     #[ignore = "requires network access"]
     async fn test_read_xet_multiple_ranges_on_one_reader() {
diff --git a/website/data/services.json b/website/data/services.json
index fb4d2ab93..fa6fa3827 100644
--- a/website/data/services.json
+++ b/website/data/services.json
@@ -2310,11 +2310,11 @@
           "comments": "Download mode. Either `xet` (default) or `http`.\n\nWhen unset, the mode is resolved from the `HF_HUB_DISABLE_XET`\nenvironment variable: a non-empty value forces `http`, otherwise it\ndefaults to `xet`. An explicit value here takes precedence.\n\nSee <https://huggingface.co/docs/huggingface_hub/package_reference/environment_variables#hfhubdisablexet>."
         },
         {
-          "name": "force_resolve",
+          "name": "enable_resolve_cache",
           "type": "bool",
           "required": false,
           "group": "General",
-          "comments": "Resolve every range through the Hugging Face Hub. Defaults to `false`.\n\nSet to `true` to resolve every range through the Hub. See\n[`HfBuilder::force_resolve`] for cache and freshness semantics."
+          "comments": "Enable caching of resolved HTTP download addresses and XET file metadata.\n\nDefaults to `false`. Set to `true` to share resolve results across readers\non the same backend. Changed files may remain invisible while cached\nresults are reused. See [`HfBuilder::enable_resolve_cache`] for freshness\nsemantics."
         }
       ],
       "examples": [
@@ -2322,31 +2322,31 @@
           "binding": "rust",
           "language": "rust",
           "minimal": "use opendal::Operator;\n\nlet operator = Operator::via_iter(\"hf\", [\n])?;",
-          "full": "use opendal::Operator;\n\nlet operator = Operator::via_iter(\"hf\", [\n    // Repo type of this backend. Required.\n    // (\"repo_type\".to_string(), \"...\".to_string()),\n\n    // Repo id of this backend.\n    //\n    // This is required.\n    // (\"repo_id\".to_string(), \"...\".to_string()),\n\n    // Revision of this backend.\n    //\n    // Default is main.\n    // (\"revision\".to_string(), \"...\".to_string()),\n\n    // Root of this backend. Can be \"/path/to [...]
+          "full": "use opendal::Operator;\n\nlet operator = Operator::via_iter(\"hf\", [\n    // Repo type of this backend. Required.\n    // (\"repo_type\".to_string(), \"...\".to_string()),\n\n    // Repo id of this backend.\n    //\n    // This is required.\n    // (\"repo_id\".to_string(), \"...\".to_string()),\n\n    // Revision of this backend.\n    //\n    // Default is main.\n    // (\"revision\".to_string(), \"...\".to_string()),\n\n    // Root of this backend. Can be \"/path/to [...]
         },
         {
           "binding": "java",
           "language": "java",
           "minimal": "import java.util.HashMap;\nimport java.util.Map;\nimport org.apache.opendal.Operator;\n\nMap<String, String> config = new HashMap<>();\nOperator operator = Operator.of(\"hf\", config);",
-          "full": "import java.util.HashMap;\nimport java.util.Map;\nimport org.apache.opendal.Operator;\n\nMap<String, String> config = new HashMap<>();\n// Repo type of this backend. Required.\n// config.put(\"repo_type\", \"...\");\n\n// Repo id of this backend.\n//\n// This is required.\n// config.put(\"repo_id\", \"...\");\n\n// Revision of this backend.\n//\n// Default is main.\n// config.put(\"revision\", \"...\");\n\n// Root of this backend. Can be \"/path/to/dir\".\n//\n// Defau [...]
+          "full": "import java.util.HashMap;\nimport java.util.Map;\nimport org.apache.opendal.Operator;\n\nMap<String, String> config = new HashMap<>();\n// Repo type of this backend. Required.\n// config.put(\"repo_type\", \"...\");\n\n// Repo id of this backend.\n//\n// This is required.\n// config.put(\"repo_id\", \"...\");\n\n// Revision of this backend.\n//\n// Default is main.\n// config.put(\"revision\", \"...\");\n\n// Root of this backend. Can be \"/path/to/dir\".\n//\n// Defau [...]
         },
         {
           "binding": "python",
           "language": "python",
           "minimal": "import opendal\n\noperator = opendal.Operator(\n    \"hf\",\n)",
-          "full": "import opendal\n\noperator = opendal.Operator(\n    \"hf\",\n    # Repo type of this backend. Required.\n    # repo_type=\"...\",\n\n    # Repo id of this backend.\n    #\n    # This is required.\n    # repo_id=\"...\",\n\n    # Revision of this backend.\n    #\n    # Default is main.\n    # revision=\"...\",\n\n    # Root of this backend. Can be \"/path/to/dir\".\n    #\n    # Default is \"/\".\n    # root=\"...\",\n\n    # Token of this backend.\n    #\n    # This is [...]
+          "full": "import opendal\n\noperator = opendal.Operator(\n    \"hf\",\n    # Repo type of this backend. Required.\n    # repo_type=\"...\",\n\n    # Repo id of this backend.\n    #\n    # This is required.\n    # repo_id=\"...\",\n\n    # Revision of this backend.\n    #\n    # Default is main.\n    # revision=\"...\",\n\n    # Root of this backend. Can be \"/path/to/dir\".\n    #\n    # Default is \"/\".\n    # root=\"...\",\n\n    # Token of this backend.\n    #\n    # This is [...]
         },
         {
           "binding": "nodejs",
           "language": "javascript",
           "minimal": "import { Operator } from \"opendal\";\n\nconst operator = new Operator(\"hf\", {\n});",
-          "full": "import { Operator } from \"opendal\";\n\nconst operator = new Operator(\"hf\", {\n  // Repo type of this backend. Required.\n  // repo_type: \"...\",\n\n  // Repo id of this backend.\n  //\n  // This is required.\n  // repo_id: \"...\",\n\n  // Revision of this backend.\n  //\n  // Default is main.\n  // revision: \"...\",\n\n  // Root of this backend. Can be \"/path/to/dir\".\n  //\n  // Default is \"/\".\n  // root: \"...\",\n\n  // Token of this backend.\n  //\n  // [...]
+          "full": "import { Operator } from \"opendal\";\n\nconst operator = new Operator(\"hf\", {\n  // Repo type of this backend. Required.\n  // repo_type: \"...\",\n\n  // Repo id of this backend.\n  //\n  // This is required.\n  // repo_id: \"...\",\n\n  // Revision of this backend.\n  //\n  // Default is main.\n  // revision: \"...\",\n\n  // Root of this backend. Can be \"/path/to/dir\".\n  //\n  // Default is \"/\".\n  // root: \"...\",\n\n  // Token of this backend.\n  //\n  // [...]
         },
         {
           "binding": "go",
           "language": "go",
           "minimal": "import (\n\t\"github.com/apache/opendal-go-services/hf\"\n\topendal \"github.com/apache/opendal/bindings/go\"\n)\n\noperator, err := opendal.NewOperator(hf.Scheme, opendal.OperatorOptions{\n})",
-          "full": "import (\n\t\"github.com/apache/opendal-go-services/hf\"\n\topendal \"github.com/apache/opendal/bindings/go\"\n)\n\noperator, err := opendal.NewOperator(hf.Scheme, opendal.OperatorOptions{\n\t// Repo type of this backend. Required.\n\t// \"repo_type\": \"...\",\n\n\t// Repo id of this backend.\n\t//\n\t// This is required.\n\t// \"repo_id\": \"...\",\n\n\t// Revision of this backend.\n\t//\n\t// Default is main.\n\t// \"revision\": \"...\",\n\n\t// Root of this backend [...]
+          "full": "import (\n\t\"github.com/apache/opendal-go-services/hf\"\n\topendal \"github.com/apache/opendal/bindings/go\"\n)\n\noperator, err := opendal.NewOperator(hf.Scheme, opendal.OperatorOptions{\n\t// Repo type of this backend. Required.\n\t// \"repo_type\": \"...\",\n\n\t// Repo id of this backend.\n\t//\n\t// This is required.\n\t// \"repo_id\": \"...\",\n\n\t// Revision of this backend.\n\t//\n\t// Default is main.\n\t// \"revision\": \"...\",\n\n\t// Root of this backend [...]
         }
       ]
     },