Buckets:
| import{s as lt,n as st,o as nt}from"../chunks/scheduler.e4ff9b64.js";import{S as ft,i as ot,e as f,s,c as a,h as rt,a as o,d as i,b as n,f as tt,g as p,j as r,k as it,l as at,m as l,n as u,t as d,o as c,p as m}from"../chunks/index.09f1bca0.js";import{C as pt,H as g,E as ut}from"../chunks/MermaidChart.svelte_svelte_type_style_lang.300ddef9.js";function dt(ke){let h,W,Q,X,$,Z,_,ee,b,Ae=`🧨 Diffusers 提供<strong>最先进</strong>的预训练扩散模型支持多模态任务。 | |
| 其目标是成为推理和训练通用的<strong>模块化工具箱</strong>。`,te,v,Ue="我们致力于构建一个经得起时间考验的库,因此对API设计极为重视。",ie,T,Ie='简而言之,Diffusers 被设计为 PyTorch 的自然延伸。因此,我们的多数设计决策都基于 <a href="https://pytorch.org/docs/stable/community/design.html#pytorch-design-philosophy" rel="nofollow">PyTorch 设计原则</a>。以下是核心原则:',le,x,se,w,ze='<li>尽管 Diffusers 包含众多性能优化特性(参见<a href="https://huggingface.co/docs/diffusers/optimization/fp16" rel="nofollow">内存与速度优化</a>),模型默认总是以最高精度和最低优化级别加载。因此除非用户指定,扩散流程(pipeline)默认在CPU上以float32精度初始化。这确保了跨平台和加速器的可用性,意味着运行本库无需复杂安装。</li> <li>Diffusers 追求<strong>轻量化</strong>,仅有少量必需依赖,但提供诸多可选依赖以提升性能(如<code>accelerate</code>、<code>safetensors</code>、<code>onnx</code>等)。我们竭力保持库的轻量级特性,使其能轻松作为其他包的依赖项。</li> <li>Diffusers 偏好简单、自解释的代码而非浓缩的”魔法”代码。这意味着lambda函数等简写语法和高级PyTorch操作符通常不被采用。</li>',ne,M,fe,L,Se="正如PyTorch所言:<strong>显式优于隐式</strong>,<strong>简洁优于复杂</strong>。这一哲学体现在库的多个方面:",oe,C,je='<li>我们遵循PyTorch的API设计,例如使用<a href="https://huggingface.co/docs/diffusers/main/en/api/diffusion_pipeline#diffusers.DiffusionPipeline.to" rel="nofollow"><code>DiffusionPipeline.to</code></a>让用户自主管理设备。</li> <li>明确的错误提示优于静默纠正错误输入。Diffusers 旨在教育用户,而非单纯降低使用难度。</li> <li>暴露复杂的模型与调度器(scheduler)交互逻辑而非内部魔法处理。调度器/采样器与扩散模型分离且相互依赖最小化,迫使用户编写展开的去噪循环。但这种分离便于调试,并赋予用户更多控制权来调整去噪过程或切换模型/调度器。</li> <li>扩散流程中独立训练的组件(如文本编码器、UNet、变分自编码器)各有专属模型类。这要求用户处理组件间交互,且序列化格式将组件分存不同文件。但此举便于调试和定制,得益于组件分离,DreamBooth或Textual Inversion训练变得极为简单。</li>',re,P,ae,y,Be='库的大部分沿用了<a href="https://github.com/huggingface/transformers" rel="nofollow">Transformers库</a>的重要设计原则:宁要重复代码,勿要仓促抽象。这一原则与<a href="https://en.wikipedia.org/wiki/Don%27t_repeat_yourself" rel="nofollow">DRY原则</a>形成鲜明对比。',pe,H,Ne="简言之,正如Transformers对建模文件的做法,Diffusers对流程(pipeline)和调度器(scheduler)保持极低抽象度与高度自包含代码。函数、长代码块甚至类可能在多文件中重复,初看像是糟糕的松散设计。但该设计已被Transformers证明极其成功,对社区驱动的开源机器学习库意义重大:",ue,D,qe="<li>机器学习领域发展迅猛,范式、模型架构和算法快速迭代,难以定义长效代码抽象。</li> <li>ML从业者常需快速修改现有代码进行研究,因此偏好自包含代码而非多重抽象。</li> <li>开源库依赖社区贡献,必须构建易于参与的代码库。抽象度越高、依赖越复杂、可读性越差,贡献难度越大。过度抽象的库会吓退贡献者。若贡献不会破坏核心功能,不仅吸引新贡献者,也更便于并行审查和修改。</li>",de,E,Ge='Hugging Face称此设计为<strong>单文件政策</strong>——即某个类的几乎所有代码都应写在单一自包含文件中。更多哲学探讨可参阅<a href="https://huggingface.co/blog/transformers-design-philosophy" rel="nofollow">此博文</a>。',ce,k,Re='Diffusers对流程和调度器完全遵循该哲学,但对diffusion模型仅部分适用。原因在于多数扩散流程(如<a href="https://huggingface.co/docs/diffusers/api/pipelines/ddpm" rel="nofollow">DDPM</a>、<a href="https://huggingface.co/docs/diffusers/api/pipelines/stable_diffusion/overview#stable-diffusion-pipelines" rel="nofollow">Stable Diffusion</a>、<a href="https://huggingface.co/docs/diffusers/api/pipelines/unclip" rel="nofollow">unCLIP (DALL·E 2)</a>和<a href="https://imagen.research.google/" rel="nofollow">Imagen</a>)都基于相同扩散模型——<a href="https://huggingface.co/docs/diffusers/api/models/unet2d-cond" rel="nofollow">UNet</a>。',me,A,Fe='现在您应已理解🧨 Diffusers的设计理念🤗。我们力求在全库贯彻这些原则,但仍存在少数例外或欠佳设计。如有反馈,我们❤️欢迎在<a href="https://github.com/huggingface/diffusers/issues/new?assignees=&labels=&template=feedback.md&title=" rel="nofollow">GitHub提交</a>。',he,U,ge,I,Oe='现在深入探讨设计细节。Diffusers主要包含三类:<a href="https://github.com/huggingface/diffusers/tree/main/src/diffusers/pipelines" rel="nofollow">流程(pipeline)</a>、<a href="https://github.com/huggingface/diffusers/tree/main/src/diffusers/models" rel="nofollow">模型</a>和<a href="https://github.com/huggingface/diffusers/tree/main/src/diffusers/schedulers" rel="nofollow">调度器(scheduler)</a>。以下是各类的具体设计决策。',$e,z,_e,S,Ye='流程设计追求易用性(因此不完全遵循<a href="#%E7%AE%80%E6%B4%81%E4%BC%98%E4%BA%8E%E7%AE%80%E6%98%93"><em>简洁优于简易</em></a>),不要求功能完备,应视为使用<a href="#%E6%A8%A1%E5%9E%8B">模型</a>和<a href="#%E8%B0%83%E5%BA%A6%E5%99%A8schedulers">调度器</a>进行推理的示例。',be,j,Je="遵循原则:",ve,B,Ke='<li>采用单文件政策。所有流程位于src/diffusers/pipelines下的独立目录。一个流程文件夹对应一篇扩散论文/项目/发布。如<a href="https://github.com/huggingface/diffusers/tree/main/src/diffusers/pipelines/stable_diffusion" rel="nofollow"><code>src/diffusers/pipelines/stable-diffusion</code></a>可包含多个流程文件。若流程功能相似,可使用<a href="https://github.com/huggingface/diffusers/blob/125d783076e5bd9785beb05367a2d2566843a271/src/diffusers/pipelines/stable_diffusion/pipeline_stable_diffusion_img2img.py#L251" rel="nofollow"># Copied from机制</a>。</li> <li>所有流程继承<code>DiffusionPipeline</code>。</li> <li>每个流程由不同模型和调度器组件构成,这些组件记录于<a href="https://huggingface.co/stable-diffusion-v1-5/stable-diffusion-v1-5/blob/main/model_index.json" rel="nofollow"><code>model_index.json</code>文件</a>,可通过同名属性访问,并可用<a href="https://huggingface.co/docs/diffusers/main/en/api/diffusion_pipeline#diffusers.DiffusionPipeline.components" rel="nofollow"><code>DiffusionPipeline.components</code></a>在流程间共享。</li> <li>所有流程应能通过<a href="https://huggingface.co/docs/diffusers/main/en/api/diffusion_pipeline#diffusers.DiffusionPipeline.from_pretrained" rel="nofollow"><code>DiffusionPipeline.from_pretrained</code></a>加载。</li> <li>流程<strong>仅</strong>用于推理。</li> <li>流程代码应具备高可读性、自解释性和易修改性。</li> <li>流程应设计为可相互构建,便于集成到高层API。</li> <li>流程<strong>非</strong>功能完备的用户界面。完整UI推荐<a href="https://github.com/invoke-ai/InvokeAI" rel="nofollow">InvokeAI</a>、<a href="https://github.com/abhishekkrthakur/diffuzers" rel="nofollow">Diffuzers</a>或<a href="https://github.com/Sanster/lama-cleaner" rel="nofollow">lama-cleaner</a>。</li> <li>每个流程应通过唯一的<code>__call__</code>方法运行,且参数命名应跨流程统一。</li> <li>流程应以其解决的任务命名。</li> <li>几乎所有新diffusion流程都应在新文件夹/文件中实现。</li>',Te,N,xe,q,Qe='模型设计为可配置的工具箱,是<a href="https://pytorch.org/docs/stable/generated/torch.nn.Module.html" rel="nofollow">PyTorch Module类</a>的自然延伸,仅部分遵循<strong>单文件政策</strong>。',we,G,Ve="遵循原则:",Me,R,We='<li>模型对应<strong>特定架构类型</strong>。如<code>UNet2DConditionModel</code>类适用于所有需要2D图像输入且受上下文调节的UNet变体。</li> <li>所有模型位于<a href="https://github.com/huggingface/diffusers/tree/main/src/diffusers/models" rel="nofollow"><code>src/diffusers/models</code></a>,每种架构应有独立文件,如<a href="https://github.com/huggingface/diffusers/blob/main/src/diffusers/models/unets/unet_2d_condition.py" rel="nofollow"><code>unets/unet_2d_condition.py</code></a>、<a href="https://github.com/huggingface/diffusers/blob/main/src/diffusers/models/transformers/transformer_2d.py" rel="nofollow"><code>transformers/transformer_2d.py</code></a>等。</li> <li>模型<strong>不</strong>采用单文件政策,应使用小型建模模块如<a href="https://github.com/huggingface/diffusers/blob/main/src/diffusers/models/attention.py" rel="nofollow"><code>attention.py</code></a>、<a href="https://github.com/huggingface/diffusers/blob/main/src/diffusers/models/resnet.py" rel="nofollow"><code>resnet.py</code></a>、<a href="https://github.com/huggingface/diffusers/blob/main/src/diffusers/models/embeddings.py" rel="nofollow"><code>embeddings.py</code></a>等。<strong>注意</strong>:这与Transformers的建模文件截然不同,表明模型未完全遵循单文件政策。</li> <li>模型意图暴露复杂度(类似PyTorch的<code>Module</code>类),并提供明确错误提示。</li> <li>所有模型继承<code>ModelMixin</code>和<code>ConfigMixin</code>。</li> <li>当不涉及重大代码变更、保持向后兼容性且显著提升内存/计算效率时,可对模型进行性能优化。</li> <li>模型默认应具备最高精度和最低性能设置。</li> <li>若新模型检查点可归类为现有架构,应适配现有架构而非新建文件。仅当架构根本性不同时才创建新文件。</li> <li>模型设计应便于未来扩展。可通过限制公开函数参数、配置参数和”预见”变更实现。例如:优先采用可扩展的<code>string</code>类型参数而非布尔型<code>is_..._type</code>参数。对现有架构的修改应保持最小化。</li> <li>模型设计需在代码可读性与多检查点支持间权衡。多数情况下应适配现有类,但某些例外(如<a href="https://github.com/huggingface/diffusers/blob/main/src/diffusers/models/unets/unet_2d_blocks.py" rel="nofollow">UNet块</a>和<a href="https://github.com/huggingface/diffusers/blob/main/src/diffusers/models/attention_processor.py" rel="nofollow">注意力处理器</a>)需新建类以保证长期可读性。</li>',Le,F,Ce,O,Xe="调度器负责引导推理去噪过程及定义训练噪声计划。它们设计为独立的可加载配置类,严格遵循<strong>单文件政策</strong>。",Pe,Y,Ze="遵循原则:",ye,J,et='<li>所有调度器位于<a href="https://github.com/huggingface/diffusers/tree/main/src/diffusers/schedulers" rel="nofollow"><code>src/diffusers/schedulers</code></a>。</li> <li>调度器<strong>禁止</strong>从大型工具文件导入,必须保持高度自包含。</li> <li>一个调度器Python文件对应一种算法(如论文定义的算法)。</li> <li>若调度器功能相似,可使用<code># Copied from</code>机制。</li> <li>所有调度器继承<code>SchedulerMixin</code>和<code>ConfigMixin</code>。</li> <li>调度器可通过<a href="https://huggingface.co/docs/diffusers/main/en/api/configuration#diffusers.ConfigMixin.from_config" rel="nofollow"><code>ConfigMixin.from_config</code></a>轻松切换(详见<a href="../using-diffusers/schedulers">此处</a>)。</li> <li>每个调度器必须包含<code>set_num_inference_steps</code>和<code>step</code>函数。在每次去噪过程前(即调用<code>step(...)</code>前)必须调用<code>set_num_inference_steps(...)</code>。</li> <li>每个调度器通过<code>timesteps</code>属性暴露需要”循环”的时间步,这是模型将被调用的时间步数组。</li> <li><code>step(...)</code>函数接收模型预测输出和”当前”样本(x_t),返回”前一个”略去噪的样本(x_t-1)。</li> <li>鉴于扩散调度器的复杂性,<code>step</code>函数不暴露全部细节,可视为”黑盒”。</li> <li>几乎所有新调度器都应在新文件中实现。</li>',He,K,De,V,Ee;return $=new pt({props:{containerStyle:"float: right; margin-left: 10px; display: inline-flex; position: relative; z-index: 10;"}}),_=new g({props:{title:"设计哲学",local:"设计哲学",headingTag:"h1"}}),x=new g({props:{title:"可用性优先于性能",local:"可用性优先于性能",headingTag:"h2"}}),M=new g({props:{title:"简洁优于简易",local:"简洁优于简易",headingTag:"h2"}}),P=new g({props:{title:"可定制与贡献友好优于抽象",local:"可定制与贡献友好优于抽象",headingTag:"h2"}}),U=new g({props:{title:"设计哲学细节",local:"设计哲学细节",headingTag:"h2"}}),z=new g({props:{title:"流程(Pipelines)",local:"流程pipelines",headingTag:"h3"}}),N=new g({props:{title:"模型",local:"模型",headingTag:"h3"}}),F=new g({props:{title:"调度器(Schedulers)",local:"调度器schedulers",headingTag:"h3"}}),K=new ut({props:{source:"https://github.com/huggingface/diffusers/blob/main/docs/source/zh/conceptual/philosophy.md"}}),{c(){h=f("meta"),W=s(),Q=f("p"),X=s(),a($.$$.fragment),Z=s(),a(_.$$.fragment),ee=s(),b=f("p"),b.innerHTML=Ae,te=s(),v=f("p"),v.textContent=Ue,ie=s(),T=f("p"),T.innerHTML=Ie,le=s(),a(x.$$.fragment),se=s(),w=f("ul"),w.innerHTML=ze,ne=s(),a(M.$$.fragment),fe=s(),L=f("p"),L.innerHTML=Se,oe=s(),C=f("ul"),C.innerHTML=je,re=s(),a(P.$$.fragment),ae=s(),y=f("p"),y.innerHTML=Be,pe=s(),H=f("p"),H.textContent=Ne,ue=s(),D=f("ul"),D.innerHTML=qe,de=s(),E=f("p"),E.innerHTML=Ge,ce=s(),k=f("p"),k.innerHTML=Re,me=s(),A=f("p"),A.innerHTML=Fe,he=s(),a(U.$$.fragment),ge=s(),I=f("p"),I.innerHTML=Oe,$e=s(),a(z.$$.fragment),_e=s(),S=f("p"),S.innerHTML=Ye,be=s(),j=f("p"),j.textContent=Je,ve=s(),B=f("ul"),B.innerHTML=Ke,Te=s(),a(N.$$.fragment),xe=s(),q=f("p"),q.innerHTML=Qe,we=s(),G=f("p"),G.textContent=Ve,Me=s(),R=f("ul"),R.innerHTML=We,Le=s(),a(F.$$.fragment),Ce=s(),O=f("p"),O.innerHTML=Xe,Pe=s(),Y=f("p"),Y.textContent=Ze,ye=s(),J=f("ul"),J.innerHTML=et,He=s(),a(K.$$.fragment),De=s(),V=f("p"),this.h()},l(e){const t=rt("svelte-u9bgzb",document.head);h=o(t,"META",{name:!0,content:!0}),t.forEach(i),W=n(e),Q=o(e,"P",{}),tt(Q).forEach(i),X=n(e),p($.$$.fragment,e),Z=n(e),p(_.$$.fragment,e),ee=n(e),b=o(e,"P",{"data-svelte-h":!0}),r(b)!=="svelte-2ee83w"&&(b.innerHTML=Ae),te=n(e),v=o(e,"P",{"data-svelte-h":!0}),r(v)!=="svelte-ez3bid"&&(v.textContent=Ue),ie=n(e),T=o(e,"P",{"data-svelte-h":!0}),r(T)!=="svelte-bzsbip"&&(T.innerHTML=Ie),le=n(e),p(x.$$.fragment,e),se=n(e),w=o(e,"UL",{"data-svelte-h":!0}),r(w)!=="svelte-131cj8o"&&(w.innerHTML=ze),ne=n(e),p(M.$$.fragment,e),fe=n(e),L=o(e,"P",{"data-svelte-h":!0}),r(L)!=="svelte-856wcm"&&(L.innerHTML=Se),oe=n(e),C=o(e,"UL",{"data-svelte-h":!0}),r(C)!=="svelte-16a0e83"&&(C.innerHTML=je),re=n(e),p(P.$$.fragment,e),ae=n(e),y=o(e,"P",{"data-svelte-h":!0}),r(y)!=="svelte-1p7f16p"&&(y.innerHTML=Be),pe=n(e),H=o(e,"P",{"data-svelte-h":!0}),r(H)!=="svelte-46p1u"&&(H.textContent=Ne),ue=n(e),D=o(e,"UL",{"data-svelte-h":!0}),r(D)!=="svelte-7h7hv0"&&(D.innerHTML=qe),de=n(e),E=o(e,"P",{"data-svelte-h":!0}),r(E)!=="svelte-1flexkj"&&(E.innerHTML=Ge),ce=n(e),k=o(e,"P",{"data-svelte-h":!0}),r(k)!=="svelte-593bid"&&(k.innerHTML=Re),me=n(e),A=o(e,"P",{"data-svelte-h":!0}),r(A)!=="svelte-9tx02f"&&(A.innerHTML=Fe),he=n(e),p(U.$$.fragment,e),ge=n(e),I=o(e,"P",{"data-svelte-h":!0}),r(I)!=="svelte-108eoeg"&&(I.innerHTML=Oe),$e=n(e),p(z.$$.fragment,e),_e=n(e),S=o(e,"P",{"data-svelte-h":!0}),r(S)!=="svelte-1l2p2pp"&&(S.innerHTML=Ye),be=n(e),j=o(e,"P",{"data-svelte-h":!0}),r(j)!=="svelte-hkd8r"&&(j.textContent=Je),ve=n(e),B=o(e,"UL",{"data-svelte-h":!0}),r(B)!=="svelte-csdnjg"&&(B.innerHTML=Ke),Te=n(e),p(N.$$.fragment,e),xe=n(e),q=o(e,"P",{"data-svelte-h":!0}),r(q)!=="svelte-1cgqpug"&&(q.innerHTML=Qe),we=n(e),G=o(e,"P",{"data-svelte-h":!0}),r(G)!=="svelte-hkd8r"&&(G.textContent=Ve),Me=n(e),R=o(e,"UL",{"data-svelte-h":!0}),r(R)!=="svelte-18rj72v"&&(R.innerHTML=We),Le=n(e),p(F.$$.fragment,e),Ce=n(e),O=o(e,"P",{"data-svelte-h":!0}),r(O)!=="svelte-195pti2"&&(O.innerHTML=Xe),Pe=n(e),Y=o(e,"P",{"data-svelte-h":!0}),r(Y)!=="svelte-hkd8r"&&(Y.textContent=Ze),ye=n(e),J=o(e,"UL",{"data-svelte-h":!0}),r(J)!=="svelte-17yyxkz"&&(J.innerHTML=et),He=n(e),p(K.$$.fragment,e),De=n(e),V=o(e,"P",{}),tt(V).forEach(i),this.h()},h(){it(h,"name","hf:doc:metadata"),it(h,"content",ct)},m(e,t){at(document.head,h),l(e,W,t),l(e,Q,t),l(e,X,t),u($,e,t),l(e,Z,t),u(_,e,t),l(e,ee,t),l(e,b,t),l(e,te,t),l(e,v,t),l(e,ie,t),l(e,T,t),l(e,le,t),u(x,e,t),l(e,se,t),l(e,w,t),l(e,ne,t),u(M,e,t),l(e,fe,t),l(e,L,t),l(e,oe,t),l(e,C,t),l(e,re,t),u(P,e,t),l(e,ae,t),l(e,y,t),l(e,pe,t),l(e,H,t),l(e,ue,t),l(e,D,t),l(e,de,t),l(e,E,t),l(e,ce,t),l(e,k,t),l(e,me,t),l(e,A,t),l(e,he,t),u(U,e,t),l(e,ge,t),l(e,I,t),l(e,$e,t),u(z,e,t),l(e,_e,t),l(e,S,t),l(e,be,t),l(e,j,t),l(e,ve,t),l(e,B,t),l(e,Te,t),u(N,e,t),l(e,xe,t),l(e,q,t),l(e,we,t),l(e,G,t),l(e,Me,t),l(e,R,t),l(e,Le,t),u(F,e,t),l(e,Ce,t),l(e,O,t),l(e,Pe,t),l(e,Y,t),l(e,ye,t),l(e,J,t),l(e,He,t),u(K,e,t),l(e,De,t),l(e,V,t),Ee=!0},p:st,i(e){Ee||(d($.$$.fragment,e),d(_.$$.fragment,e),d(x.$$.fragment,e),d(M.$$.fragment,e),d(P.$$.fragment,e),d(U.$$.fragment,e),d(z.$$.fragment,e),d(N.$$.fragment,e),d(F.$$.fragment,e),d(K.$$.fragment,e),Ee=!0)},o(e){c($.$$.fragment,e),c(_.$$.fragment,e),c(x.$$.fragment,e),c(M.$$.fragment,e),c(P.$$.fragment,e),c(U.$$.fragment,e),c(z.$$.fragment,e),c(N.$$.fragment,e),c(F.$$.fragment,e),c(K.$$.fragment,e),Ee=!1},d(e){e&&(i(W),i(Q),i(X),i(Z),i(ee),i(b),i(te),i(v),i(ie),i(T),i(le),i(se),i(w),i(ne),i(fe),i(L),i(oe),i(C),i(re),i(ae),i(y),i(pe),i(H),i(ue),i(D),i(de),i(E),i(ce),i(k),i(me),i(A),i(he),i(ge),i(I),i($e),i(_e),i(S),i(be),i(j),i(ve),i(B),i(Te),i(xe),i(q),i(we),i(G),i(Me),i(R),i(Le),i(Ce),i(O),i(Pe),i(Y),i(ye),i(J),i(He),i(De),i(V)),i(h),m($,e),m(_,e),m(x,e),m(M,e),m(P,e),m(U,e),m(z,e),m(N,e),m(F,e),m(K,e)}}}const ct='{"title":"设计哲学","local":"设计哲学","sections":[{"title":"可用性优先于性能","local":"可用性优先于性能","sections":[],"depth":2},{"title":"简洁优于简易","local":"简洁优于简易","sections":[],"depth":2},{"title":"可定制与贡献友好优于抽象","local":"可定制与贡献友好优于抽象","sections":[],"depth":2},{"title":"设计哲学细节","local":"设计哲学细节","sections":[{"title":"流程(Pipelines)","local":"流程pipelines","sections":[],"depth":3},{"title":"模型","local":"模型","sections":[],"depth":3},{"title":"调度器(Schedulers)","local":"调度器schedulers","sections":[],"depth":3}],"depth":2}],"depth":1}';function mt(ke){return nt(()=>{new URLSearchParams(window.location.search).get("fw")}),[]}class _t extends ft{constructor(h){super(),ot(this,h,mt,dt,lt,{})}}export{_t as component}; | |
Xet Storage Details
- Size:
- 19.9 kB
- Xet hash:
- bafc44d3d2d3abb7ee6cbdb2189d98a32db86d1cd5d8ab1c0b3c7062d0ba6c92
·
Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.