Hub 加载、路径与缓存
1. 资源路径三元组
buildResourcePaths 把「模型 id 或本地目录」解析为:
requestURL:逻辑路径(repoId/filename);localPath:本地映射(HF id 时拼env.localModelPath);remoteURL:env.remoteHost+remotePathTemplate(含{model}、{revision})。
125:156:packages/transformers/src/utils/hub.js
export function buildResourcePaths(path_or_repo_id, filename, options = {}, cache = null) {
const revision = options.revision ?? 'main';
const requestURL = pathJoin(path_or_repo_id, filename);
const validModelId = isValidHfModelId(path_or_repo_id);
const localPath = validModelId ? pathJoin(env.localModelPath, requestURL) : requestURL;
const remoteURL = pathJoin(
env.remoteHost,
env.remotePathTemplate
.replaceAll('{model}', path_or_repo_id)
.replaceAll('{revision}', encodeURIComponent(revision)),
filename,
);设计原因:
- 同一套代码兼容 完全离线(仅
localPath)与 Hub 在线(remoteURL); revision固定时可把缓存 key 与分支/commit 绑定,避免 main 漂移 导致缓存污染(见proposedCacheKey分支逻辑)。
2. Fetch 与 Node 头
getFetchHeaders:Node 环境为 HF 请求附加 User-Agent 与 Authorization(HF_TOKEN / HF_ACCESS_TOKEN);浏览器刻意不加 Token,避免密钥进前端 bundle。
84:107:packages/transformers/src/utils/hub.js
export function getFetchHeaders(urlOrPath) {
const isNode = typeof process !== 'undefined' && process?.release?.name === 'node';
...
if (isNode) {
...
const isHFURL = isValidUrl(urlOrPath, ['http:', 'https:'], ['huggingface.co', 'hf.co']);
if (isHFURL) {
const token = process.env?.HF_TOKEN ?? process.env?.HF_ACCESS_TOKEN;
if (token) {
headers.set('Authorization', `Bearer ${token}`);
}
}
} else {
// Running in a browser-environment, so we use default headers
// NOTE: We do not allow passing authorization headers in the browser,
// since this would require exposing the token to the client.
}简明表述:这是安全与易用性的权衡 —— 私有模型在浏览器中应通过代理或预置缓存,而不是把 token 写进网页。
3. 缓存层级:getCache
utils/cache.js 按优先级尝试:
env.useCustomCache+env.customCache(需实现 Cache API 风格的match/put);- 实验性
CrossOriginStorage(跨域存储,需环境支持); env.useBrowserCache→caches.open(env.cacheKey)(iframe/隐身模式可能失败,捕获异常并降级为无缓存);env.useFSCache→FileCache(Node 等本地文件系统)。
22:71:packages/transformers/src/utils/cache.js
export async function getCache(file_cache_dir = null) {
...
if (env.useCustomCache) {
...
}
if (!cache && env.experimental_useCrossOriginStorage && CrossOriginStorage.isAvailable()) {
cache = new CrossOriginStorage();
}
if (!cache && env.useBrowserCache) {
...
try {
cache = await caches.open(env.cacheKey);
} catch (e) {
logger.warn('An error occurred while opening the browser cache:', e);
}
}
if (!cache && env.useFSCache) {
...
cache = new FileCache(file_cache_dir ?? env.cacheDir);
}
return cache;依据:不同宿主能力差异极大(Extension、PWA、Node、Deno),可插拔缓存比单一实现更可持续。
4. 与 Pipeline 进度的衔接
pipeline() 在下载前用 get_file_metadata 填充每个文件的 total 字节,再交给 DefaultProgressCallback,实现 聚合进度条(多文件并行下载时仍可按权重累加)。
5. 最佳实践
- 生产环境:对大型模型配合 Service Worker + Cache API 或 自建 CDN,并设置合理
revision; - CI/离线测试:
local_files_only: true+ 本地目录或env.allowRemoteModels = false; - 隐私合规:浏览器端避免依赖需 Token 的私有 Hub URL,改用同域代理。