Buckets:

rtrm's picture
download
raw
42.6 kB
<meta charset="utf-8" /><meta name="hf:doc:metadata" content="{&quot;title&quot;:&quot;Introduction à la classe <i> Blocks </i>&quot;,&quot;local&quot;:&quot;introduction-à-la-classe-i-blocks-i&quot;,&quot;sections&quot;:[{&quot;title&quot;:&quot;Pourquoi <i> Blocks </i> 🧱 ?&quot;,&quot;local&quot;:&quot;pourquoi-i-blocks-i--&quot;,&quot;sections&quot;:[],&quot;depth&quot;:3},{&quot;title&quot;:&quot;Création d’une démo simple en utilisant <i> Blocks </i>&quot;,&quot;local&quot;:&quot;création-dune-démo-simple-en-utilisant-i-blocks-i&quot;,&quot;sections&quot;:[],&quot;depth&quot;:3},{&quot;title&quot;:&quot;Personnaliser la mise en page de votre démo&quot;,&quot;local&quot;:&quot;personnaliser-la-mise-en-page-de-votre-démo&quot;,&quot;sections&quot;:[],&quot;depth&quot;:3},{&quot;title&quot;:&quot;Exploration des événements et de l’état&quot;,&quot;local&quot;:&quot;exploration-des-événements-et-de-létat&quot;,&quot;sections&quot;:[],&quot;depth&quot;:3},{&quot;title&quot;:&quot;Création de démos multi-étapes&quot;,&quot;local&quot;:&quot;création-de-démos-multi-étapes&quot;,&quot;sections&quot;:[],&quot;depth&quot;:3},{&quot;title&quot;:&quot;Mise à jour des propriétés des composants&quot;,&quot;local&quot;:&quot;mise-à-jour-des-propriétés-des-composants&quot;,&quot;sections&quot;:[],&quot;depth&quot;:3}],&quot;depth&quot;:1}">
<link href="/docs/course/pr_1069/fr/_app/immutable/assets/0.e3b0c442.css" rel="modulepreload">
<link rel="modulepreload" href="/docs/course/pr_1069/fr/_app/immutable/entry/start.cea6db46.js">
<link rel="modulepreload" href="/docs/course/pr_1069/fr/_app/immutable/chunks/scheduler.37c15a92.js">
<link rel="modulepreload" href="/docs/course/pr_1069/fr/_app/immutable/chunks/singletons.2b29b91f.js">
<link rel="modulepreload" href="/docs/course/pr_1069/fr/_app/immutable/chunks/index.18351ede.js">
<link rel="modulepreload" href="/docs/course/pr_1069/fr/_app/immutable/chunks/paths.f6fdf97f.js">
<link rel="modulepreload" href="/docs/course/pr_1069/fr/_app/immutable/entry/app.3f6640b1.js">
<link rel="modulepreload" href="/docs/course/pr_1069/fr/_app/immutable/chunks/index.2bf4358c.js">
<link rel="modulepreload" href="/docs/course/pr_1069/fr/_app/immutable/nodes/0.b777de11.js">
<link rel="modulepreload" href="/docs/course/pr_1069/fr/_app/immutable/chunks/each.e59479a4.js">
<link rel="modulepreload" href="/docs/course/pr_1069/fr/_app/immutable/nodes/76.63e66ca7.js">
<link rel="modulepreload" href="/docs/course/pr_1069/fr/_app/immutable/chunks/Tip.363c041f.js">
<link rel="modulepreload" href="/docs/course/pr_1069/fr/_app/immutable/chunks/CodeBlock.4e987730.js">
<link rel="modulepreload" href="/docs/course/pr_1069/fr/_app/immutable/chunks/CourseFloatingBanner.6add7356.js">
<link rel="modulepreload" href="/docs/course/pr_1069/fr/_app/immutable/chunks/getInferenceSnippets.24b50994.js"><!-- HEAD_svelte-u9bgzb_START --><meta name="hf:doc:metadata" content="{&quot;title&quot;:&quot;Introduction à la classe <i> Blocks </i>&quot;,&quot;local&quot;:&quot;introduction-à-la-classe-i-blocks-i&quot;,&quot;sections&quot;:[{&quot;title&quot;:&quot;Pourquoi <i> Blocks </i> 🧱 ?&quot;,&quot;local&quot;:&quot;pourquoi-i-blocks-i--&quot;,&quot;sections&quot;:[],&quot;depth&quot;:3},{&quot;title&quot;:&quot;Création d’une démo simple en utilisant <i> Blocks </i>&quot;,&quot;local&quot;:&quot;création-dune-démo-simple-en-utilisant-i-blocks-i&quot;,&quot;sections&quot;:[],&quot;depth&quot;:3},{&quot;title&quot;:&quot;Personnaliser la mise en page de votre démo&quot;,&quot;local&quot;:&quot;personnaliser-la-mise-en-page-de-votre-démo&quot;,&quot;sections&quot;:[],&quot;depth&quot;:3},{&quot;title&quot;:&quot;Exploration des événements et de l’état&quot;,&quot;local&quot;:&quot;exploration-des-événements-et-de-létat&quot;,&quot;sections&quot;:[],&quot;depth&quot;:3},{&quot;title&quot;:&quot;Création de démos multi-étapes&quot;,&quot;local&quot;:&quot;création-de-démos-multi-étapes&quot;,&quot;sections&quot;:[],&quot;depth&quot;:3},{&quot;title&quot;:&quot;Mise à jour des propriétés des composants&quot;,&quot;local&quot;:&quot;mise-à-jour-des-propriétés-des-composants&quot;,&quot;sections&quot;:[],&quot;depth&quot;:3}],&quot;depth&quot;:1}"><!-- HEAD_svelte-u9bgzb_END --> <p></p> <h1 class="relative group"><a id="introduction-à-la-classe-i-blocks-i" class="header-link block pr-1.5 text-lg no-hover:hidden with-hover:absolute with-hover:p-1.5 with-hover:opacity-0 with-hover:group-hover:opacity-100 with-hover:right-full" href="#introduction-à-la-classe-i-blocks-i"><span><svg class="" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 256"><path d="M167.594 88.393a8.001 8.001 0 0 1 0 11.314l-67.882 67.882a8 8 0 1 1-11.314-11.315l67.882-67.881a8.003 8.003 0 0 1 11.314 0zm-28.287 84.86l-28.284 28.284a40 40 0 0 1-56.567-56.567l28.284-28.284a8 8 0 0 0-11.315-11.315l-28.284 28.284a56 56 0 0 0 79.196 79.197l28.285-28.285a8 8 0 1 0-11.315-11.314zM212.852 43.14a56.002 56.002 0 0 0-79.196 0l-28.284 28.284a8 8 0 1 0 11.314 11.314l28.284-28.284a40 40 0 0 1 56.568 56.567l-28.285 28.285a8 8 0 0 0 11.315 11.314l28.284-28.284a56.065 56.065 0 0 0 0-79.196z" fill="currentColor"></path></svg></span></a> <span>Introduction à la classe &lt;i> Blocks &lt;/i></span></h1> <div class="flex space-x-1 absolute z-10 right-0 top-0"><a href="https://discuss.huggingface.co/t/chapter-9-questions" target="_blank"><img alt="Ask a Question" class="!m-0" src="https://img.shields.io/badge/Ask%20a%20question-ffcb4c.svg?logo=data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgLTEgMTA0IDEwNiI+PGRlZnM+PHN0eWxlPi5jbHMtMXtmaWxsOiMyMzFmMjA7fS5jbHMtMntmaWxsOiNmZmY5YWU7fS5jbHMtM3tmaWxsOiMwMGFlZWY7fS5jbHMtNHtmaWxsOiMwMGE5NGY7fS5jbHMtNXtmaWxsOiNmMTVkMjI7fS5jbHMtNntmaWxsOiNlMzFiMjM7fTwvc3R5bGU+PC9kZWZzPjx0aXRsZT5EaXNjb3Vyc2VfbG9nbzwvdGl0bGU+PGcgaWQ9IkxheWVyXzIiPjxnIGlkPSJMYXllcl8zIj48cGF0aCBjbGFzcz0iY2xzLTEiIGQ9Ik01MS44NywwQzIzLjcxLDAsMCwyMi44MywwLDUxYzAsLjkxLDAsNTIuODEsMCw1Mi44MWw1MS44Ni0uMDVjMjguMTYsMCw1MS0yMy43MSw1MS01MS44N1M4MCwwLDUxLjg3LDBaIi8+PHBhdGggY2xhc3M9ImNscy0yIiBkPSJNNTIuMzcsMTkuNzRBMzEuNjIsMzEuNjIsMCwwLDAsMjQuNTgsNjYuNDFsLTUuNzIsMTguNEwzOS40LDgwLjE3YTMxLjYxLDMxLjYxLDAsMSwwLDEzLTYwLjQzWiIvPjxwYXRoIGNsYXNzPSJjbHMtMyIgZD0iTTc3LjQ1LDMyLjEyYTMxLjYsMzEuNiwwLDAsMS0zOC4wNSw0OEwxOC44Niw4NC44MmwyMC45MS0yLjQ3QTMxLjYsMzEuNiwwLDAsMCw3Ny40NSwzMi4xMloiLz48cGF0aCBjbGFzcz0iY2xzLTQiIGQ9Ik03MS42MywyNi4yOUEzMS42LDMxLjYsMCwwLDEsMzguOCw3OEwxOC44Niw4NC44MiwzOS40LDgwLjE3QTMxLjYsMzEuNiwwLDAsMCw3MS42MywyNi4yOVoiLz48cGF0aCBjbGFzcz0iY2xzLTUiIGQ9Ik0yNi40Nyw2Ny4xMWEzMS42MSwzMS42MSwwLDAsMSw1MS0zNUEzMS42MSwzMS42MSwwLDAsMCwyNC41OCw2Ni40MWwtNS43MiwxOC40WiIvPjxwYXRoIGNsYXNzPSJjbHMtNiIgZD0iTTI0LjU4LDY2LjQxQTMxLjYxLDMxLjYxLDAsMCwxLDcxLjYzLDI2LjI5YTMxLjYxLDMxLjYxLDAsMCwwLTQ5LDM5LjYzbC0zLjc2LDE4LjlaIi8+PC9nPjwvZz48L3N2Zz4="></a> <div class="relative colab-dropdown "> <button class=" " type="button"> <img alt="Open In Colab" class="!m-0" src="https://colab.research.google.com/assets/colab-badge.svg"> </button> </div> <div class="relative colab-dropdown "> <button class=" " type="button"> <img alt="Open In Studio Lab" class="!m-0" src="https://studiolab.sagemaker.aws/studiolab.svg"> </button> </div></div> <p data-svelte-h="svelte-17xt04g">Dans les sections précédentes, nous avons exploré et créé des démos en utilisant la classe <code>Interface</code>. Dans cette section, nous allons présenter une <strong>nouvelle</strong> API de bas niveau appelée <code>gradio.Blocks</code>.</p> <p data-svelte-h="svelte-32wk3p">Quelle est la différence entre <code>Interface</code> et <code>Blocks</code> ?</p> <ul data-svelte-h="svelte-117el3u"><li><p><code>Interface</code> : une API de haut niveau qui vous permet de créer une démo complète d’apprentissage automatique simplement en fournissant une liste d’entrées et de sorties.</p></li> <li><p>🧱 <code>Blocks</code> : une API de bas niveau qui vous permet d’avoir un contrôle total sur les flux de données et la disposition de votre application. Vous pouvez construire des applications très complexes, en plusieurs étapes, en utilisant <code>Blocks</code>.</p></li></ul> <h3 class="relative group"><a id="pourquoi-i-blocks-i--" class="header-link block pr-1.5 text-lg no-hover:hidden with-hover:absolute with-hover:p-1.5 with-hover:opacity-0 with-hover:group-hover:opacity-100 with-hover:right-full" href="#pourquoi-i-blocks-i--"><span><svg class="" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 256"><path d="M167.594 88.393a8.001 8.001 0 0 1 0 11.314l-67.882 67.882a8 8 0 1 1-11.314-11.315l67.882-67.881a8.003 8.003 0 0 1 11.314 0zm-28.287 84.86l-28.284 28.284a40 40 0 0 1-56.567-56.567l28.284-28.284a8 8 0 0 0-11.315-11.315l-28.284 28.284a56 56 0 0 0 79.196 79.197l28.285-28.285a8 8 0 1 0-11.315-11.314zM212.852 43.14a56.002 56.002 0 0 0-79.196 0l-28.284 28.284a8 8 0 1 0 11.314 11.314l28.284-28.284a40 40 0 0 1 56.568 56.567l-28.285 28.285a8 8 0 0 0 11.315 11.314l28.284-28.284a56.065 56.065 0 0 0 0-79.196z" fill="currentColor"></path></svg></span></a> <span>Pourquoi &lt;i> Blocks &lt;/i> 🧱 ?</span></h3> <p data-svelte-h="svelte-uedh5f">Comme nous l’avons vu dans les sections précédentes, la classe <code>Interface</code> vous permet de créer facilement des démos d’apprentissage automatique à part entière avec seulement quelques lignes de code. L’API <code>Interface</code> est extrêmement facile à utiliser, mais elle n’a pas la flexibilité qu’offre l’API <code>Blocks</code>. Par exemple, vous pourriez vouloir :</p> <ul data-svelte-h="svelte-3t96x9"><li>regrouper des démos connexes sous forme d’onglets multiples dans une application web,</li> <li>modifier la mise en page de votre démo, par exemple pour spécifier l’emplacement des entrées et des sorties,</li> <li>disposer d’interfaces multi-étapes dans lesquelles la sortie d’un modèle devient l’entrée du modèle suivant ou avoir des flux de données plus flexibles en général,</li> <li>modifier les propriétés d’un composant (par exemple, les choix dans une liste déroulante) ou sa visibilité en fonction des entrées de l’utilisateur.</li></ul> <p data-svelte-h="svelte-3mnt39">Nous allons explorer tous ces concepts ci-dessous.</p> <h3 class="relative group"><a id="création-dune-démo-simple-en-utilisant-i-blocks-i" class="header-link block pr-1.5 text-lg no-hover:hidden with-hover:absolute with-hover:p-1.5 with-hover:opacity-0 with-hover:group-hover:opacity-100 with-hover:right-full" href="#création-dune-démo-simple-en-utilisant-i-blocks-i"><span><svg class="" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 256"><path d="M167.594 88.393a8.001 8.001 0 0 1 0 11.314l-67.882 67.882a8 8 0 1 1-11.314-11.315l67.882-67.881a8.003 8.003 0 0 1 11.314 0zm-28.287 84.86l-28.284 28.284a40 40 0 0 1-56.567-56.567l28.284-28.284a8 8 0 0 0-11.315-11.315l-28.284 28.284a56 56 0 0 0 79.196 79.197l28.285-28.285a8 8 0 1 0-11.315-11.314zM212.852 43.14a56.002 56.002 0 0 0-79.196 0l-28.284 28.284a8 8 0 1 0 11.314 11.314l28.284-28.284a40 40 0 0 1 56.568 56.567l-28.285 28.285a8 8 0 0 0 11.315 11.314l28.284-28.284a56.065 56.065 0 0 0 0-79.196z" fill="currentColor"></path></svg></span></a> <span>Création d’une démo simple en utilisant &lt;i> Blocks &lt;/i></span></h3> <p data-svelte-h="svelte-1oy4s0x">Après avoir installé <em>Gradio</em>, exécutez le code ci-dessous sous forme de script Python, de <em>notebook</em> Jupyter ou de <em>notebook</em> Colab.</p> <div class="code-block relative "><div class="absolute top-2.5 right-4"><button class="inline-flex items-center relative text-sm focus:text-green-500 cursor-pointer focus:outline-none transition duration-200 ease-in-out opacity-0 mx-0.5 text-gray-600 " title="code excerpt" type="button"><svg class="" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" fill="currentColor" focusable="false" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 32 32"><path d="M28,10V28H10V10H28m0-2H10a2,2,0,0,0-2,2V28a2,2,0,0,0,2,2H28a2,2,0,0,0,2-2V10a2,2,0,0,0-2-2Z" transform="translate(0)"></path><path d="M4,18H2V4A2,2,0,0,1,4,2H18V4H4Z" transform="translate(0)"></path><rect fill="none" width="32" height="32"></rect></svg> <div class="absolute pointer-events-none transition-opacity bg-black text-white py-1 px-2 leading-tight rounded font-normal shadow left-1/2 top-full transform -translate-x-1/2 translate-y-2 opacity-0"><div class="absolute bottom-full left-1/2 transform -translate-x-1/2 w-0 h-0 border-black border-4 border-t-0" style="border-left-color: transparent; border-right-color: transparent; "></div> Copied</div></button></div> <pre class=""><!-- HTML_TAG_START --><span class="hljs-keyword">import</span> gradio <span class="hljs-keyword">as</span> gr
<span class="hljs-keyword">def</span> <span class="hljs-title function_">flip_text</span>(<span class="hljs-params">x</span>):
<span class="hljs-keyword">return</span> x[::-<span class="hljs-number">1</span>]
demo = gr.Blocks()
<span class="hljs-keyword">with</span> demo:
gr.Markdown(
<span class="hljs-string">&quot;&quot;&quot;
# Flip Text!
Start typing below to see the output.
&quot;&quot;&quot;</span>
)
<span class="hljs-built_in">input</span> = gr.Textbox(placeholder=<span class="hljs-string">&quot;Flip this text&quot;</span>)
output = gr.Textbox()
<span class="hljs-built_in">input</span>.change(fn=flip_text, inputs=<span class="hljs-built_in">input</span>, outputs=output)
demo.launch()<!-- HTML_TAG_END --></pre></div> <iframe src="https://course-demos-flip-text.hf.space" frameborder="0" height="400" title="Gradio app" class="container p-0 flex-grow space-iframe" allow="accelerometer; ambient-light-sensor; autoplay; battery; camera; document-domain; encrypted-media; fullscreen; geolocation; gyroscope; layout-animations; legacy-image-formats; magnetometer; microphone; midi; oversized-images; payment; picture-in-picture; publickey-credentials-get; sync-xhr; usb; vr ; wake-lock; xr-spatial-tracking" sandbox="allow-forms allow-modals allow-popups allow-popups-to-escape-sandbox allow-same-origin allow-scripts allow-downloads"></iframe> <p data-svelte-h="svelte-tty550">Ce simple exemple ci-dessus introduit 4 concepts qui sous-tendent les <em>Blocks</em> :</p> <ol data-svelte-h="svelte-anzn7m"><li>Les <em>Blocks</em> vous permettent de construire des applications web qui combinent Markdown, HTML, boutons et composants interactifs simplement en instanciant des objets en Python dans un contexte <code>with gradio.Blocks</code>.</li></ol> <div class="course-tip bg-gradient-to-br dark:bg-gradient-to-r before:border-green-500 dark:before:border-green-800 from-green-50 dark:from-gray-900 to-white dark:to-gray-950 border border-green-50 text-green-700 dark:text-gray-400">🙋Si vous n&#39;êtes pas familier avec l&#39;instruction `with` en Python, nous vous recommandons de consulter l&#39;excellent <a href="https://realpython.com/python-with-statement/" data-svelte-h="svelte-1el9xdt">tutoriel</a> de Real Python. Revenez ici après l&#39;avoir lu 🤗</div> <p data-svelte-h="svelte-4p86tk">L’ordre dans lequel vous instanciez les composants est important car chaque élément est restitué dans l’application Web dans l’ordre où il a été créé. (Les mises en page plus complexes sont abordées ci-dessous)</p> <ol start="2" data-svelte-h="svelte-111xl6s"><li><p>Vous pouvez définir des fonctions Python ordinaires n’importe où dans votre code et les exécuter avec des entrées utilisateur en utilisant les <code>Blocks</code>. Dans notre exemple, nous avons une fonction simple qui inverse le texte entré mais vous pouvez écrire n’importe quelle fonction Python, du simple calcul au traitement des prédictions d’un modèle d’apprentissage automatique.</p></li> <li><p>Vous pouvez assigner des événements à n’importe quel composant <code>Blocks</code>. Ainsi, votre fonction sera exécutée lorsque le composant sera cliqué, modifié, etc. Lorsque vous assignez un événement, vous passez trois paramètres :</p></li></ol> <ul data-svelte-h="svelte-j637v5"><li><code>fn</code> : la fonction qui doit être appelée,</li> <li><code>inputs</code> : la (liste) des composants d’entrée</li> <li><code>outputs</code> : la (liste) des composants de sortie qui doivent être appelés.<br>
Dans l’exemple ci-dessus, nous exécutons la fonction <code>flip_text()</code> lorsque la valeur de la <code>Textbox</code> nommée input <code>input</code> change. L’événement lit la valeur dans <code>input</code>, la passe comme paramètre de nom à <code>flip_text()</code>, qui renvoie alors une valeur qui est assignée à notre seconde <code>Textbox</code> nommée <code>output</code>.
Pour voir la liste des événements que chaque composant supporte, consultez la <a href="https://www.gradio.app/docs/" rel="nofollow">documentation</a> de <em>Gradio</em>.</li></ul> <ol start="4" data-svelte-h="svelte-1pa5cy3"><li><em>Blocks</em> détermine automatiquement si un composant doit être interactif (accepter les entrées de l’utilisateur) ou non, en fonction des déclencheurs d’événements que vous définissez. Dans notre exemple, la première zone de texte est interactive, puisque sa valeur est utilisée par la fonction <code>flip_text()</code>. La deuxième zone de texte n’est pas interactive, puisque sa valeur n’est jamais utilisée comme entrée. Dans certains cas, vous voudrez peut-être passer outre, ce que vous pouvez faire en passant un booléen au paramètre <code>interactive</code> du composant (par exemple, <code>gr.Textbox(placeholder=&quot;Flip this text&quot;, interactive=True)</code>).</li></ol> <h3 class="relative group"><a id="personnaliser-la-mise-en-page-de-votre-démo" class="header-link block pr-1.5 text-lg no-hover:hidden with-hover:absolute with-hover:p-1.5 with-hover:opacity-0 with-hover:group-hover:opacity-100 with-hover:right-full" href="#personnaliser-la-mise-en-page-de-votre-démo"><span><svg class="" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 256"><path d="M167.594 88.393a8.001 8.001 0 0 1 0 11.314l-67.882 67.882a8 8 0 1 1-11.314-11.315l67.882-67.881a8.003 8.003 0 0 1 11.314 0zm-28.287 84.86l-28.284 28.284a40 40 0 0 1-56.567-56.567l28.284-28.284a8 8 0 0 0-11.315-11.315l-28.284 28.284a56 56 0 0 0 79.196 79.197l28.285-28.285a8 8 0 1 0-11.315-11.314zM212.852 43.14a56.002 56.002 0 0 0-79.196 0l-28.284 28.284a8 8 0 1 0 11.314 11.314l28.284-28.284a40 40 0 0 1 56.568 56.567l-28.285 28.285a8 8 0 0 0 11.315 11.314l28.284-28.284a56.065 56.065 0 0 0 0-79.196z" fill="currentColor"></path></svg></span></a> <span>Personnaliser la mise en page de votre démo</span></h3> <p data-svelte-h="svelte-epv3ib">Comment pouvons-nous utiliser <code>Blocks</code> pour personnaliser la mise en page de notre démo ? Par défaut, <code>Blocks</code> affiche verticalement dans une colonne les composants que vous créez. Vous pouvez changer cela en créant des colonnes supplémentaires <code>avec gradio.Column():</code> ou des lignes <code>avec gradio.Row():</code> et en créant des composants dans ces contextes.</p> <p data-svelte-h="svelte-12j4gcg">Voici ce que vous devez garder à l’esprit : tout composant créé sous une <code>Column</code> (c’est aussi le défaut) sera disposé verticalement. Tout composant créé sous une <code>Row</code> sera disposé horizontalement, comme le <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Flexible_Box_Layout/Basic_Concepts_of_Flexbox" rel="nofollow">modèle flexbox dans le développement web</a>.</p> <p data-svelte-h="svelte-6d4z8v">Enfin, vous pouvez également créer des onglets pour votre démo en utilisant le gestionnaire de contexte <code>with gradio.Tabs()</code>. Dans ce contexte, vous pouvez créer plusieurs onglets en spécifiant des enfants <code>with gradio.TabItem(name_of_tab):</code>. Tout composant créé dans un contexte <code>with gradio.TabItem(name_of_tab):</code> apparaît dans cet onglet.</p> <p data-svelte-h="svelte-ux0g21">Maintenant, ajoutons une fonction <code>flip_image()</code> à notre démo et ajoutons un nouvel onglet qui retourne les images. Vous trouverez ci-dessous un exemple avec 2 onglets et utilisant également une <code>Row</code> :</p> <div class="code-block relative "><div class="absolute top-2.5 right-4"><button class="inline-flex items-center relative text-sm focus:text-green-500 cursor-pointer focus:outline-none transition duration-200 ease-in-out opacity-0 mx-0.5 text-gray-600 " title="code excerpt" type="button"><svg class="" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" fill="currentColor" focusable="false" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 32 32"><path d="M28,10V28H10V10H28m0-2H10a2,2,0,0,0-2,2V28a2,2,0,0,0,2,2H28a2,2,0,0,0,2-2V10a2,2,0,0,0-2-2Z" transform="translate(0)"></path><path d="M4,18H2V4A2,2,0,0,1,4,2H18V4H4Z" transform="translate(0)"></path><rect fill="none" width="32" height="32"></rect></svg> <div class="absolute pointer-events-none transition-opacity bg-black text-white py-1 px-2 leading-tight rounded font-normal shadow left-1/2 top-full transform -translate-x-1/2 translate-y-2 opacity-0"><div class="absolute bottom-full left-1/2 transform -translate-x-1/2 w-0 h-0 border-black border-4 border-t-0" style="border-left-color: transparent; border-right-color: transparent; "></div> Copied</div></button></div> <pre class=""><!-- HTML_TAG_START --><span class="hljs-keyword">import</span> numpy <span class="hljs-keyword">as</span> np
<span class="hljs-keyword">import</span> gradio <span class="hljs-keyword">as</span> gr
demo = gr.Blocks()
<span class="hljs-keyword">def</span> <span class="hljs-title function_">flip_text</span>(<span class="hljs-params">x</span>):
<span class="hljs-keyword">return</span> x[::-<span class="hljs-number">1</span>]
<span class="hljs-keyword">def</span> <span class="hljs-title function_">flip_image</span>(<span class="hljs-params">x</span>):
<span class="hljs-keyword">return</span> np.fliplr(x)
<span class="hljs-keyword">with</span> demo:
gr.Markdown(<span class="hljs-string">&quot;Flip text or image files using this demo.&quot;</span>)
<span class="hljs-keyword">with</span> gr.Tabs():
<span class="hljs-keyword">with</span> gr.TabItem(<span class="hljs-string">&quot;Flip Text&quot;</span>):
<span class="hljs-keyword">with</span> gr.Row():
text_input = gr.Textbox()
text_output = gr.Textbox()
text_button = gr.Button(<span class="hljs-string">&quot;Flip&quot;</span>)
<span class="hljs-keyword">with</span> gr.TabItem(<span class="hljs-string">&quot;Flip Image&quot;</span>):
<span class="hljs-keyword">with</span> gr.Row():
image_input = gr.Image()
image_output = gr.Image()
image_button = gr.Button(<span class="hljs-string">&quot;Flip&quot;</span>)
text_button.click(flip_text, inputs=text_input, outputs=text_output)
image_button.click(flip_image, inputs=image_input, outputs=image_output)
demo.launch()<!-- HTML_TAG_END --></pre></div> <iframe src="https://course-demos-flip-text-image.hf.space" frameborder="0" height="450" title="Gradio app" class="container p-0 flex-grow space-iframe" allow="accelerometer; ambient-light-sensor; autoplay; battery; camera; document-domain; encrypted-media; fullscreen; geolocation; gyroscope; layout-animations; legacy-image-formats; magnetometer; microphone; midi; oversized-images; payment; picture-in-picture; publickey-credentials-get; sync-xhr; usb; vr ; wake-lock; xr-spatial-tracking" sandbox="allow-forms allow-modals allow-popups allow-popups-to-escape-sandbox allow-same-origin allow-scripts allow-downloads"></iframe> <p data-svelte-h="svelte-e5unx2">Vous remarquerez que dans cet exemple, nous avons également créé un composant <code>Button</code> dans chaque onglet et avons assigné un événement de clic à chaque bouton qui est l’élément qui exécute réellement la fonction.</p> <h3 class="relative group"><a id="exploration-des-événements-et-de-létat" class="header-link block pr-1.5 text-lg no-hover:hidden with-hover:absolute with-hover:p-1.5 with-hover:opacity-0 with-hover:group-hover:opacity-100 with-hover:right-full" href="#exploration-des-événements-et-de-létat"><span><svg class="" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 256"><path d="M167.594 88.393a8.001 8.001 0 0 1 0 11.314l-67.882 67.882a8 8 0 1 1-11.314-11.315l67.882-67.881a8.003 8.003 0 0 1 11.314 0zm-28.287 84.86l-28.284 28.284a40 40 0 0 1-56.567-56.567l28.284-28.284a8 8 0 0 0-11.315-11.315l-28.284 28.284a56 56 0 0 0 79.196 79.197l28.285-28.285a8 8 0 1 0-11.315-11.314zM212.852 43.14a56.002 56.002 0 0 0-79.196 0l-28.284 28.284a8 8 0 1 0 11.314 11.314l28.284-28.284a40 40 0 0 1 56.568 56.567l-28.285 28.285a8 8 0 0 0 11.315 11.314l28.284-28.284a56.065 56.065 0 0 0 0-79.196z" fill="currentColor"></path></svg></span></a> <span>Exploration des événements et de l’état</span></h3> <p data-svelte-h="svelte-8wkqn4">De la même manière que vous pouvez contrôler la mise en page, <code>Blocks</code> vous donne un contrôle fin sur les événements qui déclenchent les appels de fonction. Chaque composant et de nombreux layouts ont des événements spécifiques qu’ils supportent.</p> <p data-svelte-h="svelte-qonmri">Par exemple, le composant <code>Textbox</code> a 2 événements : <code>change()</code> (lorsque la valeur contenue dans la zone de texte change), et <code>submit()</code> (lorsqu’un utilisateur appuie sur la touche Entrée alors qu’il est concentré sur la zone de texte). Les composants plus complexes peuvent avoir encore plus d’événements : par exemple, le composant <code>Audio</code> a aussi des événements séparés pour quand le fichier audio est joué, effacé, mis en pause, etc. Consultez la documentation pour connaître les événements pris en charge par chaque composant.</p> <p data-svelte-h="svelte-oeq4km">Vous pouvez attacher un déclencheur d’événement à aucun, un ou plusieurs de ces événements. Vous créez un déclencheur d’événement en appelant le nom de l’événement sur l’instance du composant comme une fonction. Par exemple, <code>textbox.change(...)</code> ou <code>btn.click(...)</code>. La fonction prend trois paramètres, comme indiqué ci-dessus :</p> <ul data-svelte-h="svelte-fsaa4a"><li><code>fn</code> : la fonction à exécuter</li> <li><code>inputs</code> : une (liste de) composante(s) dont les valeurs doivent être fournies comme paramètres d’entrée à la fonction. La valeur de chaque composant est mise en correspondance avec le paramètre de fonction correspondant, dans l’ordre. Ce paramètre peut être <code>None</code> si la fonction ne prend aucun paramètre.</li> <li><code>outputs</code> : un (liste de) composant(s) dont les valeurs doivent être mises à jour en fonction des valeurs retournées par la fonction. Chaque valeur de retour met à jour la valeur du composant correspondant, dans l’ordre. Ce paramètre peut être None si la fonction ne retourne rien.</li></ul> <p data-svelte-h="svelte-13m94gq">Vous pouvez même faire en sorte que le composant d’entrée et de sortie soit le même composant, comme nous le faisons dans cet exemple qui utilise un modèle GPT pour compléter du texte :</p> <div class="code-block relative "><div class="absolute top-2.5 right-4"><button class="inline-flex items-center relative text-sm focus:text-green-500 cursor-pointer focus:outline-none transition duration-200 ease-in-out opacity-0 mx-0.5 text-gray-600 " title="code excerpt" type="button"><svg class="" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" fill="currentColor" focusable="false" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 32 32"><path d="M28,10V28H10V10H28m0-2H10a2,2,0,0,0-2,2V28a2,2,0,0,0,2,2H28a2,2,0,0,0,2-2V10a2,2,0,0,0-2-2Z" transform="translate(0)"></path><path d="M4,18H2V4A2,2,0,0,1,4,2H18V4H4Z" transform="translate(0)"></path><rect fill="none" width="32" height="32"></rect></svg> <div class="absolute pointer-events-none transition-opacity bg-black text-white py-1 px-2 leading-tight rounded font-normal shadow left-1/2 top-full transform -translate-x-1/2 translate-y-2 opacity-0"><div class="absolute bottom-full left-1/2 transform -translate-x-1/2 w-0 h-0 border-black border-4 border-t-0" style="border-left-color: transparent; border-right-color: transparent; "></div> Copied</div></button></div> <pre class=""><!-- HTML_TAG_START --><span class="hljs-keyword">import</span> gradio <span class="hljs-keyword">as</span> gr
api = gr.Interface.load(<span class="hljs-string">&quot;huggingface/EleutherAI/gpt-j-6B&quot;</span>)
<span class="hljs-keyword">def</span> <span class="hljs-title function_">complete_with_gpt</span>(<span class="hljs-params">text</span>):
<span class="hljs-comment"># Utilise les 50 derniers caractères du texte comme contexte.</span>
<span class="hljs-keyword">return</span> text[:-<span class="hljs-number">50</span>] + api(text[-<span class="hljs-number">50</span>:])
<span class="hljs-keyword">with</span> gr.Blocks() <span class="hljs-keyword">as</span> demo:
textbox = gr.Textbox(placeholder=<span class="hljs-string">&quot;Type here and press enter...&quot;</span>, lines=<span class="hljs-number">4</span>)
btn = gr.Button(<span class="hljs-string">&quot;Generate&quot;</span>)
btn.click(complete_with_gpt, textbox, textbox)
demo.launch()<!-- HTML_TAG_END --></pre></div> <iframe src="https://course-demos-blocks-gpt.hf.space" frameborder="0" height="300" title="Gradio app" class="container p-0 flex-grow space-iframe" allow="accelerometer; ambient-light-sensor; autoplay; battery; camera; document-domain; encrypted-media; fullscreen; geolocation; gyroscope; layout-animations; legacy-image-formats; magnetometer; microphone; midi; oversized-images; payment; picture-in-picture; publickey-credentials-get; sync-xhr; usb; vr ; wake-lock; xr-spatial-tracking" sandbox="allow-forms allow-modals allow-popups allow-popups-to-escape-sandbox allow-same-origin allow-scripts allow-downloads"></iframe> <h3 class="relative group"><a id="création-de-démos-multi-étapes" class="header-link block pr-1.5 text-lg no-hover:hidden with-hover:absolute with-hover:p-1.5 with-hover:opacity-0 with-hover:group-hover:opacity-100 with-hover:right-full" href="#création-de-démos-multi-étapes"><span><svg class="" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 256"><path d="M167.594 88.393a8.001 8.001 0 0 1 0 11.314l-67.882 67.882a8 8 0 1 1-11.314-11.315l67.882-67.881a8.003 8.003 0 0 1 11.314 0zm-28.287 84.86l-28.284 28.284a40 40 0 0 1-56.567-56.567l28.284-28.284a8 8 0 0 0-11.315-11.315l-28.284 28.284a56 56 0 0 0 79.196 79.197l28.285-28.285a8 8 0 1 0-11.315-11.314zM212.852 43.14a56.002 56.002 0 0 0-79.196 0l-28.284 28.284a8 8 0 1 0 11.314 11.314l28.284-28.284a40 40 0 0 1 56.568 56.567l-28.285 28.285a8 8 0 0 0 11.315 11.314l28.284-28.284a56.065 56.065 0 0 0 0-79.196z" fill="currentColor"></path></svg></span></a> <span>Création de démos multi-étapes</span></h3> <p data-svelte-h="svelte-1dp00ek">Dans certains cas, vous pouvez vouloir une <em>démo multi-étapes</em>, dans laquelle vous réutilisez la sortie d’une fonction comme entrée de la suivante. C’est très facile à faire avec les <code>Blocks</code>, car vous pouvez utiliser un composant pour l’entrée d’un déclencheur d’événement mais la sortie d’un autre. Regardez le composant texte dans l’exemple ci-dessous, sa valeur est le résultat d’un modèle de conversion de la parole en texte, mais il est également transmis à un modèle d’analyse des sentiments :</p> <div class="code-block relative "><div class="absolute top-2.5 right-4"><button class="inline-flex items-center relative text-sm focus:text-green-500 cursor-pointer focus:outline-none transition duration-200 ease-in-out opacity-0 mx-0.5 text-gray-600 " title="code excerpt" type="button"><svg class="" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" fill="currentColor" focusable="false" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 32 32"><path d="M28,10V28H10V10H28m0-2H10a2,2,0,0,0-2,2V28a2,2,0,0,0,2,2H28a2,2,0,0,0,2-2V10a2,2,0,0,0-2-2Z" transform="translate(0)"></path><path d="M4,18H2V4A2,2,0,0,1,4,2H18V4H4Z" transform="translate(0)"></path><rect fill="none" width="32" height="32"></rect></svg> <div class="absolute pointer-events-none transition-opacity bg-black text-white py-1 px-2 leading-tight rounded font-normal shadow left-1/2 top-full transform -translate-x-1/2 translate-y-2 opacity-0"><div class="absolute bottom-full left-1/2 transform -translate-x-1/2 w-0 h-0 border-black border-4 border-t-0" style="border-left-color: transparent; border-right-color: transparent; "></div> Copied</div></button></div> <pre class=""><!-- HTML_TAG_START --><span class="hljs-keyword">from</span> transformers <span class="hljs-keyword">import</span> pipeline
<span class="hljs-keyword">import</span> gradio <span class="hljs-keyword">as</span> gr
asr = pipeline(<span class="hljs-string">&quot;automatic-speech-recognition&quot;</span>, <span class="hljs-string">&quot;facebook/wav2vec2-base-960h&quot;</span>)
classifier = pipeline(<span class="hljs-string">&quot;text-classification&quot;</span>)
<span class="hljs-keyword">def</span> <span class="hljs-title function_">speech_to_text</span>(<span class="hljs-params">speech</span>):
text = asr(speech)[<span class="hljs-string">&quot;text&quot;</span>]
<span class="hljs-keyword">return</span> text
<span class="hljs-keyword">def</span> <span class="hljs-title function_">text_to_sentiment</span>(<span class="hljs-params">text</span>):
<span class="hljs-keyword">return</span> classifier(text)[<span class="hljs-number">0</span>][<span class="hljs-string">&quot;label&quot;</span>]
demo = gr.Blocks()
<span class="hljs-keyword">with</span> demo:
audio_file = gr.Audio(<span class="hljs-built_in">type</span>=<span class="hljs-string">&quot;filepath&quot;</span>)
text = gr.Textbox()
label = gr.Label()
b1 = gr.Button(<span class="hljs-string">&quot;Recognize Speech&quot;</span>)
b2 = gr.Button(<span class="hljs-string">&quot;Classify Sentiment&quot;</span>)
b1.click(speech_to_text, inputs=audio_file, outputs=text)
b2.click(text_to_sentiment, inputs=text, outputs=label)
demo.launch()<!-- HTML_TAG_END --></pre></div> <iframe src="https://course-demos-blocks-multi-step.hf.space" frameborder="0" height="550" title="Gradio app" class="container p-0 flex-grow space-iframe" allow="accelerometer; ambient-light-sensor; autoplay; battery; camera; document-domain; encrypted-media; fullscreen; geolocation; gyroscope; layout-animations; legacy-image-formats; magnetometer; microphone; midi; oversized-images; payment; picture-in-picture; publickey-credentials-get; sync-xhr; usb; vr ; wake-lock; xr-spatial-tracking" sandbox="allow-forms allow-modals allow-popups allow-popups-to-escape-sandbox allow-same-origin allow-scripts allow-downloads"></iframe> <h3 class="relative group"><a id="mise-à-jour-des-propriétés-des-composants" class="header-link block pr-1.5 text-lg no-hover:hidden with-hover:absolute with-hover:p-1.5 with-hover:opacity-0 with-hover:group-hover:opacity-100 with-hover:right-full" href="#mise-à-jour-des-propriétés-des-composants"><span><svg class="" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 256"><path d="M167.594 88.393a8.001 8.001 0 0 1 0 11.314l-67.882 67.882a8 8 0 1 1-11.314-11.315l67.882-67.881a8.003 8.003 0 0 1 11.314 0zm-28.287 84.86l-28.284 28.284a40 40 0 0 1-56.567-56.567l28.284-28.284a8 8 0 0 0-11.315-11.315l-28.284 28.284a56 56 0 0 0 79.196 79.197l28.285-28.285a8 8 0 1 0-11.315-11.314zM212.852 43.14a56.002 56.002 0 0 0-79.196 0l-28.284 28.284a8 8 0 1 0 11.314 11.314l28.284-28.284a40 40 0 0 1 56.568 56.567l-28.285 28.285a8 8 0 0 0 11.315 11.314l28.284-28.284a56.065 56.065 0 0 0 0-79.196z" fill="currentColor"></path></svg></span></a> <span>Mise à jour des propriétés des composants</span></h3> <p data-svelte-h="svelte-1h2qccv">Jusqu’à présent, nous avons vu comment créer des événements pour mettre à jour la valeur d’un autre composant. Mais que se passe-t-il si vous voulez modifier d’autres propriétés d’un composant, comme la visibilité d’une zone de texte ou les choix dans un groupe de boutons radio ? Vous pouvez le faire en renvoyant la méthode <code>update()</code> d’une classe de composant au lieu d’une valeur de retour normale de votre fonction.</p> <p data-svelte-h="svelte-ci2coz">L’exemple le plus facile à illustrer est le suivant :</p> <div class="code-block relative "><div class="absolute top-2.5 right-4"><button class="inline-flex items-center relative text-sm focus:text-green-500 cursor-pointer focus:outline-none transition duration-200 ease-in-out opacity-0 mx-0.5 text-gray-600 " title="code excerpt" type="button"><svg class="" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" fill="currentColor" focusable="false" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 32 32"><path d="M28,10V28H10V10H28m0-2H10a2,2,0,0,0-2,2V28a2,2,0,0,0,2,2H28a2,2,0,0,0,2-2V10a2,2,0,0,0-2-2Z" transform="translate(0)"></path><path d="M4,18H2V4A2,2,0,0,1,4,2H18V4H4Z" transform="translate(0)"></path><rect fill="none" width="32" height="32"></rect></svg> <div class="absolute pointer-events-none transition-opacity bg-black text-white py-1 px-2 leading-tight rounded font-normal shadow left-1/2 top-full transform -translate-x-1/2 translate-y-2 opacity-0"><div class="absolute bottom-full left-1/2 transform -translate-x-1/2 w-0 h-0 border-black border-4 border-t-0" style="border-left-color: transparent; border-right-color: transparent; "></div> Copied</div></button></div> <pre class=""><!-- HTML_TAG_START --><span class="hljs-keyword">import</span> gradio <span class="hljs-keyword">as</span> gr
<span class="hljs-keyword">def</span> <span class="hljs-title function_">change_textbox</span>(<span class="hljs-params">choice</span>):
<span class="hljs-keyword">if</span> choice == <span class="hljs-string">&quot;short&quot;</span>:
<span class="hljs-keyword">return</span> gr.Textbox.update(lines=<span class="hljs-number">2</span>, visible=<span class="hljs-literal">True</span>)
<span class="hljs-keyword">elif</span> choice == <span class="hljs-string">&quot;long&quot;</span>:
<span class="hljs-keyword">return</span> gr.Textbox.update(lines=<span class="hljs-number">8</span>, visible=<span class="hljs-literal">True</span>)
<span class="hljs-keyword">else</span>:
<span class="hljs-keyword">return</span> gr.Textbox.update(visible=<span class="hljs-literal">False</span>)
<span class="hljs-keyword">with</span> gr.Blocks() <span class="hljs-keyword">as</span> block:
radio = gr.Radio(
[<span class="hljs-string">&quot;short&quot;</span>, <span class="hljs-string">&quot;long&quot;</span>, <span class="hljs-string">&quot;none&quot;</span>], label=<span class="hljs-string">&quot;What kind of essay would you like to write?&quot;</span>
)
text = gr.Textbox(lines=<span class="hljs-number">2</span>, interactive=<span class="hljs-literal">True</span>)
radio.change(fn=change_textbox, inputs=radio, outputs=text)
block.launch()<!-- HTML_TAG_END --></pre></div> <iframe src="https://course-demos-blocks-update-component-properti-833c723.hf.space" frameborder="0" height="300" title="Gradio app" class="container p-0 flex-grow space-iframe" allow="accelerometer; ambient-light-sensor; autoplay; battery; camera; document-domain; encrypted-media; fullscreen; geolocation; gyroscope; layout-animations; legacy-image-formats; magnetometer; microphone; midi; oversized-images; payment; picture-in-picture; publickey-credentials-get; sync-xhr; usb; vr ; wake-lock; xr-spatial-tracking" sandbox="allow-forms allow-modals allow-popups allow-popups-to-escape-sandbox allow-same-origin allow-scripts allow-downloads"></iframe> <p data-svelte-h="svelte-ii925y">Nous venons d’explorer tous les concepts de base des <code>Blocks</code> ! Tout comme avec <code>Interface</code>, vous pouvez créer des démos sympas qui peuvent être partagées en utilisant <code>share=True</code> dans la méthode <code>launch()</code> ou déployées sur <a href="https://huggingface.co/spaces" rel="nofollow"><em>Spaces</em></a>.</p> <a class="!text-gray-400 !no-underline text-sm flex items-center not-prose mt-4" href="https://github.com/huggingface/course/blob/main/chapters/fr/chapter9/7.mdx" target="_blank"><span data-svelte-h="svelte-1kd6by1">&lt;</span> <span data-svelte-h="svelte-x0xyl0">&gt;</span> <span data-svelte-h="svelte-1dajgef"><span class="underline ml-1.5">Update</span> on GitHub</span></a> <p></p>
<script>
{
__sveltekit_1sfisyd = {
assets: "/docs/course/pr_1069/fr",
base: "/docs/course/pr_1069/fr",
env: {}
};
const element = document.currentScript.parentElement;
const data = [null,null];
Promise.all([
import("/docs/course/pr_1069/fr/_app/immutable/entry/start.cea6db46.js"),
import("/docs/course/pr_1069/fr/_app/immutable/entry/app.3f6640b1.js")
]).then(([kit, app]) => {
kit.start(app, element, {
node_ids: [0, 76],
data,
form: null,
error: null
});
});
}
</script>

Xet Storage Details

Size:
42.6 kB
·
Xet hash:
df700395ded37ee7dfb56f9f4e0ff014e4c2a4bb80cff6065ab66d29400bc90b

Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.