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

xuanwo pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/opendal.git


The following commit(s) were added to refs/heads/main by this push:
     new 8b45b121e feat(services/huggingface): Add repo_type=space support 
(#6833)
8b45b121e is described below

commit 8b45b121e15d70cac77470f4a6b9a084a58577d2
Author: Aryan Bagade <[email protected]>
AuthorDate: Sat Nov 29 00:29:36 2025 -0800

    feat(services/huggingface): Add repo_type=space support (#6833)
    
    Enable HuggingFace Spaces support by extending the repo_type enum and URL 
templates to allow reading/listing Space repositories like models and datasets.
    
    Part of #6830
---
 core/src/services/huggingface/backend.rs | 19 +++++---
 core/src/services/huggingface/core.rs    | 82 ++++++++++++++++++++++++++++++++
 2 files changed, 95 insertions(+), 6 deletions(-)

diff --git a/core/src/services/huggingface/backend.rs 
b/core/src/services/huggingface/backend.rs
index 49fa1618e..7bfe9e068 100644
--- a/core/src/services/huggingface/backend.rs
+++ b/core/src/services/huggingface/backend.rs
@@ -45,8 +45,8 @@ impl HuggingfaceBuilder {
     /// - model
     /// - dataset
     /// - datasets (alias for dataset)
+    /// - space
     ///
-    /// Currently, only models and datasets are supported.
     /// [Reference](https://huggingface.co/docs/hub/repositories)
     pub fn repo_type(mut self, repo_type: &str) -> Self {
         if !repo_type.is_empty() {
@@ -130,10 +130,7 @@ impl Builder for HuggingfaceBuilder {
         let repo_type = match self.config.repo_type.as_deref() {
             Some("model") => Ok(RepoType::Model),
             Some("dataset") | Some("datasets") => Ok(RepoType::Dataset),
-            Some("space") => Err(Error::new(
-                ErrorKind::ConfigInvalid,
-                "repo type \"space\" is unsupported",
-            )),
+            Some("space") => Ok(RepoType::Space),
             Some(repo_type) => Err(Error::new(
                 ErrorKind::ConfigInvalid,
                 format!("unknown repo_type: {repo_type}").as_str(),
@@ -284,12 +281,13 @@ impl Access for HuggingfaceBackend {
     }
 }
 
-/// Repository type of Huggingface. Currently, we only support `model` and 
`dataset`.
+/// Repository type of Huggingface. Supports `model`, `dataset`, and `space`.
 /// [Reference](https://huggingface.co/docs/hub/repositories)
 #[derive(Debug, Clone, Copy)]
 pub enum RepoType {
     Model,
     Dataset,
+    Space,
 }
 
 #[cfg(test)]
@@ -304,4 +302,13 @@ mod tests {
             .build()
             .expect("builder should accept datasets alias");
     }
+
+    #[test]
+    fn build_accepts_space_repo_type() {
+        HuggingfaceBuilder::default()
+            .repo_id("org/space")
+            .repo_type("space")
+            .build()
+            .expect("builder should accept space repo type");
+    }
 }
diff --git a/core/src/services/huggingface/core.rs 
b/core/src/services/huggingface/core.rs
index 12f036075..156e7b1d0 100644
--- a/core/src/services/huggingface/core.rs
+++ b/core/src/services/huggingface/core.rs
@@ -75,6 +75,12 @@ impl HuggingfaceCore {
                 &self.repo_id,
                 percent_encode_revision(&self.revision)
             ),
+            RepoType::Space => format!(
+                "{}/api/spaces/{}/paths-info/{}",
+                &self.endpoint,
+                &self.repo_id,
+                percent_encode_revision(&self.revision)
+            ),
         };
 
         let mut req = Request::post(&url);
@@ -121,6 +127,13 @@ impl HuggingfaceCore {
                 percent_encode_revision(&self.revision),
                 percent_encode_path(&p)
             ),
+            RepoType::Space => format!(
+                "{}/api/spaces/{}/tree/{}/{}?expand=True",
+                &self.endpoint,
+                &self.repo_id,
+                percent_encode_revision(&self.revision),
+                percent_encode_path(&p)
+            ),
         };
 
         if recursive {
@@ -183,6 +196,13 @@ impl HuggingfaceCore {
                 percent_encode_revision(&self.revision),
                 percent_encode_path(&p)
             ),
+            RepoType::Space => format!(
+                "{}/spaces/{}/resolve/{}/{}",
+                &self.endpoint,
+                &self.repo_id,
+                percent_encode_revision(&self.revision),
+                percent_encode_path(&p)
+            ),
         };
 
         let mut req = Request::get(&url);
@@ -509,6 +529,68 @@ mod tests {
         Ok(())
     }
 
+    #[tokio::test]
+    async fn test_hf_path_info_url_space() -> Result<()> {
+        let (core, mock_client) = create_test_core(
+            RepoType::Space,
+            "test-user/test-space",
+            "main",
+            "https://huggingface.co";,
+        );
+
+        core.hf_path_info("app.py").await?;
+
+        let url = mock_client.get_captured_url();
+        assert_eq!(
+            url,
+            
"https://huggingface.co/api/spaces/test-user/test-space/paths-info/main";
+        );
+
+        Ok(())
+    }
+
+    #[tokio::test]
+    async fn test_hf_list_url_space() -> Result<()> {
+        let (core, mock_client) = create_test_core(
+            RepoType::Space,
+            "org/space",
+            "main",
+            "https://huggingface.co";,
+        );
+
+        core.hf_list("static", false, None).await?;
+
+        let url = mock_client.get_captured_url();
+        assert_eq!(
+            url,
+            
"https://huggingface.co/api/spaces/org/space/tree/main/static?expand=True";
+        );
+
+        Ok(())
+    }
+
+    #[tokio::test]
+    async fn test_hf_resolve_url_space() -> Result<()> {
+        let (core, mock_client) = create_test_core(
+            RepoType::Space,
+            "user/space",
+            "main",
+            "https://huggingface.co";,
+        );
+
+        let args = OpRead::default();
+        core.hf_resolve("README.md", BytesRange::default(), &args)
+            .await?;
+
+        let url = mock_client.get_captured_url();
+        assert_eq!(
+            url,
+            "https://huggingface.co/spaces/user/space/resolve/main/README.md";
+        );
+
+        Ok(())
+    }
+
     #[tokio::test]
     async fn test_hf_resolve_with_range() -> Result<()> {
         let (core, mock_client) = create_test_core(

Reply via email to