介绍 swift-huggingface:完整的 Hugging Face Swift 客户端

Hugging Face:Blog(RSS)·2025-12-05 08:00·268天前
AI 导读

swift-huggingface 是一个全新的 Swift 客户端,旨在彻底解决旧库下载模型缓慢、不可靠且不支持断点续传的问题。它提供完整的 Hub API 覆盖,核心改进包括具备进度跟踪和断点续传的可靠下载、与 Python 生态共享缓存以避免重复下载,以及通过灵活的 TokenProvider 模式简化身份验证。该库现已独立发布,并将很快集成到 swift-transformers 中取代原有实现,未来还将支持 Xet 存储后端以实现更快的下载。

Hugging Face:Blog(RSS)
精选
76AI 编辑部评分,满分 100

介绍 swift-huggingface:完整的 Hugging Face Swift 客户端

2025-12-05 08:00· 268天前
AI 导读

swift-huggingface 是一个全新的 Swift 客户端,旨在彻底解决旧库下载模型缓慢、不可靠且不支持断点续传的问题。它提供完整的 Hub API 覆盖,核心改进包括具备进度跟踪和断点续传的可靠下载、与 Python 生态共享缓存以避免重复下载,以及通过灵活的 TokenProvider 模式简化身份验证。该库现已独立发布,并将很快集成到 swift-transformers 中取代原有实现,未来还将支持 Xet 存储后端以实现更快的下载。

推荐理由

Swift 开发者可无缝集成 Hugging Face 模型,下载更可靠且与 Python 共享缓存。

正文 · AI 翻译

今天,我们宣布推出 swift-huggingface,这是一个全新的 Swift 包,为 Hugging Face Hub 提供了完整的客户端。你现在就可以将其作为独立包使用,它很快将集成到 swift-transformers 中,取代其当前的 HubApi 实现。

问题所在

今年早些时候发布 swift-transformers 1.0 时,我们收到了社区的强烈反馈:

  • 下载速度慢且不可靠。大型模型文件(通常有数 GB)会在中途下载失败,且无法断点续传。开发者不得不手动下载模型并将其打包到应用中——这完全违背了动态模型加载的初衷。
  • 与 Python 生态没有共享缓存。Python 的 transformers 库将模型存储在 ~/.cache/huggingface/hub 中。Swift 应用则下载到另一个不同结构的位置。如果你已经用 Python CLI 下载过某个模型,为 Swift 应用还得再下载一次。
  • 身份认证令人困惑。token 应该从哪里获取?环境变量?文件?钥匙串?答案是“视情况而定”,而现有的实现并没有把选项说清楚。

介绍 swift-huggingface

swift-huggingface 是一次从零开始的重写,专注于可靠性和开发者体验。它提供了:

  • 完整的 Hub API 覆盖——模型、数据集、空间、收藏集、讨论等
  • 稳健的文件操作——进度追踪、断点续传支持以及完善的错误处理
  • 与 Python 兼容的缓存——在 Swift 和 Python 客户端之间共享已下载的模型
  • 灵活的身份认证——TokenProvider 模式让凭据来源清晰明确
  • OAuth 支持——为需要对用户进行身份认证的面向用户应用提供一流支持
  • Xet 存储后端支持(即将推出!)——基于块的去重技术,实现显著更快的下载速度

让我们来看一些示例。


使用 TokenProvider 实现灵活的身份认证

最大的改进之一在于身份认证的工作方式。TokenProvider 模式让凭据的来源变得清晰明确:

import HuggingFace

// For development: auto-detect from environment and standard locations
// Checks HF_TOKEN, HUGGING_FACE_HUB_TOKEN, ~/.cache/huggingface/token, etc.
let client = HubClient.default

// For CI/CD: explicit token
let client = HubClient(tokenProvider: .static("hf_xxx"))

// For production apps: read from Keychain
let client = HubClient(tokenProvider: .keychain(service: "com.myapp", account: "hf_token"))

自动检测遵循与 Python huggingface_hub 库相同的约定:

  1. HF_TOKEN 环境变量
  2. HUGGING_FACE_HUB_TOKEN 环境变量
  3. `HF_TOKEN_PATH` 环境变量(指向 token 文件的路径)
  4. `$HF_HOME/token` 文件
  5. `~/.cache/huggingface/token`(标准 HF CLI 位置)
  6. `~/.huggingface/token`(备用位置)

这意味着,如果你已经通过 `hf auth login` 登录过,swift-huggingface 会自动找到并使用那个 token。

面向用户应用的 OAuth

正在构建一个让用户通过 Hugging Face 账户登录的应用?swift-huggingface 包含了完整的 OAuth 2.0 实现:

import HuggingFace

// Create authentication manager
let authManager = try HuggingFaceAuthenticationManager(
    clientID: "your_client_id",
    redirectURL: URL(string: "yourapp://oauth/callback")!,
    scope: [.openid, .profile, .email],
    keychainService: "com.yourapp.huggingface",
    keychainAccount: "user_token"
)

// Sign in user (presents system browser)
try await authManager.signIn()

// Use with Hub client
let client = HubClient(tokenProvider: .oauth(manager: authManager))

// Tokens are automatically refreshed when needed
let userInfo = try await client.whoami()
print("Signed in as: \(userInfo.name)")

OAuth 管理器负责在 Keychain 中存储 token、自动刷新以及安全登出。无需再手动管理 token。

可靠的下载

现在,下载大型模型变得简单直接,并带有完善的进度追踪和断点续传支持:

// Download with progress tracking
let progress = Progress(totalUnitCount: 0)

Task {
    for await _ in progress.publisher(for: \.fractionCompleted).values {
        print("Download: \(Int(progress.fractionCompleted * 100))%")
    }
}

let fileURL = try await client.downloadFile(
    at: "model.safetensors",
    from: "microsoft/phi-2",
    to: destinationURL,
    progress: progress
)

如果下载中断,你可以从中断处继续:

// Resume from where you left off
let fileURL = try await client.resumeDownloadFile(
    resumeData: savedResumeData,
    to: destinationURL,
    progress: progress
)

对于下载整个模型仓库,`downloadSnapshot` 可以处理一切:

let modelDir = try await client.downloadSnapshot(
    of: "mlx-community/Llama-3.2-1B-Instruct-4bit",
    to: cacheDirectory,
    matching: ["*.safetensors", "*.json"],  // Only download what you need
    progressHandler: { progress in
        print("Downloaded \(progress.completedUnitCount) of \(progress.totalUnitCount) files")
    }
)

快照函数会追踪每个文件的元数据,因此后续调用只会下载发生变化的文件。

与 Python 共享缓存

还记得我们提到的第二个问题吗?"与 Python 生态没有共享缓存。" 现在这个问题已经解决了。

swift-huggingface 实现了一个与 Python 兼容的缓存结构,允许 Swift 和 Python 客户端之间无缝共享:

~/.cache/huggingface/hub/
├── models--deepseek-ai--DeepSeek-V3.2/
│   ├── blobs/
│   │   └── <etag>           # actual file content
│   ├── refs/
│   │   └── main             # contains commit hash
│   └── snapshots/
│       └── <commit_hash>/
│           └── config.json  # symlink → ../../blobs/<etag>

这意味着:

  • 一次下载,随处可用。如果你已经通过 hf CLI 或 Python 库下载过某个模型,swift-huggingface 会自动找到它。
  • 内容寻址存储。文件根据其 ETag 存储在 `blobs/` 目录中。如果两个版本共享同一个文件,它只会被存储一次。
  • 符号链接提高效率。快照目录包含指向 blobs 的符号链接,在保持清晰文件结构的同时,最大限度地减少磁盘占用。

缓存位置遵循与 Python 相同的环境变量约定:

  1. `HF_HUB_CACHE` 环境变量
  2. `HF_HOME` 环境变量 + `/hub`
  3. `~/.cache/huggingface/hub`(默认)

你也可以直接使用缓存:

let cache = HubCache.default

// Check if a file is already cached
if let cachedPath = cache.cachedFilePath(
    repo: "deepseek-ai/DeepSeek-V3.2",
    kind: .model,
    revision: "main",
    filename: "config.json"
) {
    let data = try Data(contentsOf: cachedPath)
    // Use cached file without any network request
}

为防止多个进程同时访问同一缓存时出现竞态条件,swift-huggingface 使用了文件锁(`flock(2)`)。

前后对比

以下是使用旧的 HubApi 下载模型快照时的代码:

// Before: HubApi in swift-transformers
let hub = HubApi()
let repo = Hub.Repo(id: "mlx-community/Llama-3.2-1B-Instruct-4bit")

// No progress tracking, no resume, errors swallowed
let modelDir = try await hub.snapshot(
    from: repo,
    matching: ["*.safetensors", "*.json"]
) { progress in
    // Progress object exists but wasn't always accurate
    print(progress.fractionCompleted)
}

以下是使用 swift-huggingface 执行相同操作的代码:

// After: swift-huggingface
let client = HubClient.default

let modelDir = try await client.downloadSnapshot(
    of: "mlx-community/Llama-3.2-1B-Instruct-4bit",
    to: cacheDirectory,
    matching: ["*.safetensors", "*.json"],
    progressHandler: { progress in
        // Accurate progress per file
        print("\(progress.completedUnitCount)/\(progress.totalUnitCount) files")
    }
)

API 类似,但实现方式完全不同——它基于 URLSession 下载任务构建,具备完善的代理处理、断点续传数据支持和元数据追踪功能。

不止于下载

但等等,还有更多功能!swift-huggingface 包含一个完整的 Hub 客户端:

// List trending models
let models = try await client.listModels(
    filter: "library:mlx",
    sort: "trending",
    limit: 10
)

// Get model details
let model = try await client.getModel("mlx-community/Llama-3.2-1B-Instruct-4bit")
print("Downloads: \(model.downloads ?? 0)")
print("Likes: \(model.likes ?? 0)")

// Work with collections
let collections = try await client.listCollections(owner: "huggingface", sort: "trending")

// Manage discussions
let discussions = try await client.listDiscussions(kind: .model, "username/my-model")

这还不是全部!swift-huggingface 拥有与 Hugging Face 推理提供商交互所需的一切功能,让你的应用能够即时访问数百个机器学习模型,并由世界一流的推理提供商提供支持:

import HuggingFace

// Create a client (uses auto-detected credentials from environment)
let client = InferenceClient.default

// Generate images from a text prompt
let response = try await client.textToImage(
    model: "black-forest-labs/FLUX.1-schnell",
    prompt: "A serene Japanese garden with cherry blossoms",
    provider: .hfInference,
    width: 1024,
    height: 1024,
    numImages: 1,
    guidanceScale: 7.5,
    numInferenceSteps: 50,
    seed: 42
)

// Save the generated image
try response.image.write(to: URL(fileURLWithPath: "generated.png"))

请查看 README 以获取所有支持功能的完整列表。

下一步计划

我们正积极在两个方面推进工作:

与 swift-transformers 集成。我们有一个正在进行的拉取请求,旨在用 swift-huggingface 替换 HubApi。这将为所有使用 swift-transformers、mlx-swift-lm 以及更广泛生态系统的用户带来可靠的下载体验。如果你维护基于 Swift 的库或应用,并希望获得采用 swift-huggingface 的帮助,请联系我们——我们很乐意提供支持。

通过 Xet 实现更快的下载。我们正在增加对 Xet 存储后端的支持,该后端能够实现基于块的去重,并显著加快大型模型的下载速度。更多详情即将发布。

立即试用

将 swift-huggingface 添加到你的项目中:

dependencies: [
    .package(url: "https://github.com/huggingface/swift-huggingface.git", from: "0.4.0")
]

我们期待你的反馈。如果你曾在 Swift 中为模型下载感到困扰,请尝试一下并告诉我们使用体验。你的体验报告将帮助我们确定下一步的改进优先级。

资源

  • GitHub 上的 swift-huggingface
  • swift-transformers
  • mlx-swift-examples
  • AnyLanguageModel

感谢 swift-transformers 社区提供的反馈,这些反馈塑造了这个项目;也感谢所有提交问题并分享经验的人。这是为你们而做的。❤️

来源:Hugging Face:Blog(RSS)· huggingface.co