Transcript
00:00:00Hé, merci beaucoup de nous avoir rejoints aujourd'hui.
00:00:02Je suis Praneet, de l'équipe Workflow ici chez Vercel.
00:00:05Salut, je suis Nate, également de l'équipe Workflow.
00:00:08Nate, toi et moi faisons partie de l'équipe depuis le début,
00:00:12et de tout ce que nous avons lancé ces six derniers mois,
00:00:15je pense que les hooks et webhooks sont mes fonctionnalités préférées,
00:00:18et c'est précisément ce dont tu es venu nous parler aujourd'hui.
00:00:21Les hooks et webhooks sont aussi mes fonctionnalités favorites.
00:00:23Ils sont incroyablement puissants, et je vais vous montrer pourquoi.
00:00:28La première démo concerne un sujet familier : les Magic Links.
00:00:33Le Magic Link est un formulaire de connexion. On tape son e-mail,
00:00:40on reçoit un e-mail et en cliquant sur le lien, on est connecté.
00:00:44Oui, et si je me souviens bien, avec Vercel, même avant ce nom,
00:00:48Il faut impliquer une base de données pour suivre l'état, et ça devient vite le bazar.
00:00:52Ouais, je réfléchissais déjà à la manière dont je structurerais ça et à la base de données que j'utiliserais,
00:00:56car on dirait un problème classique pour lequel j'ai déjà construit des solutions similaires.
00:01:01Alors oui, j'aimerais beaucoup voir à quoi cela ressemble.
00:01:08Oui, donc juste pour démontrer ce dont je parle, les points de friction que j'évoque,
00:01:12j'ai commencé par implémenter une version "traditionnelle" sans workflow d'une connexion par Magic Link.
00:01:19Et il y a donc trois points de terminaison impliqués.
00:01:24Le premier intervient lors de la soumission du formulaire de connexion,
00:01:28et il doit générer une session et la stocker dans une base de données, telle que Redis.
00:01:30Vous devez implémenter un TTL, car vous ne pouvez pas laisser les données traîner indéfiniment, il faut qu'elles expirent.
00:01:38Et ensuite, l'envoi de l'e-mail peut échouer, et là votre connexion ne fonctionne pas, ce qui est très frustrant.
00:01:43Il y a trois points de terminaison impliqués.
00:01:47Le premier intervient lors de l'envoi du formulaire,
00:01:50il génère une session et la stocke en base de données, comme Redis.
00:01:57Il faut gérer un TTL pour ne pas laisser traîner les données.
00:02:06Puis envoyer l'e-mail. Si ça échoue, la connexion échoue, c'est frustrant.
00:02:14Et il faut une tâche cron ou un stagiaire pour nettoyer la base.
00:02:19C'était peut-être moi, le stagiaire, à l'époque.
00:02:22Ensuite, il y a le second point, quand l'utilisateur clique sur le lien.
00:02:28Il faut interroger la base pour restaurer l'état créé au début.
00:02:36Et là, on tombe déjà dans du code spaghetti.
00:02:38En imaginant le résultat, ce code me semble très familier.
00:02:48C'est vite complexe, même pour un concept aussi simple.
00:02:54Voyons maintenant comment implémenter cela avec Workflow.
00:02:59L'implémentation du Magic Link avec le SDK Workflow ressemble à ceci.
00:03:05On voit notre fonction avec la directive "useWorkflow".
00:03:11D'abord, on appelle la fonction "createWebhook" du paquet Workflow.
00:03:18On utilise l'option "respondWithManual" car notre fonction gérera
00:03:36la réponse à la requête HTTP qui déclenche ce Webhook.
00:03:40C'est pour effectuer une redirection après la connexion ?
00:03:51Oui, si nous avons besoin d'infos dans la fonction pour la réponse.
00:03:57Comme pour le premier point, on envoie l'e-mail via "useStep".
00:04:03Si cela échoue, le SDK Workflow gère les tentatives automatiques.
00:04:10L'aspect durabilité apporte déjà un avantage sur l'approche classique.
00:04:21Si l'envoi échoue, on réessaie avec la même URL de Webhook.
00:04:26Et si on regarde ici, c'est un modèle très intéressant.
00:04:30On utilise "promise race" avec un délai de cinq minutes.
00:04:35C'est possible car cet objet Webhook implémente une promesse.
00:04:40Pour attendre la requête du Webhook, il suffit de faire "await Webhook".
00:04:50Ou ici avec une course. Je m'attendais à une option de délai d'attente.
00:04:58Mais c'est plus propre ainsi : on modélise le délai avec une course.
00:05:02On peut imaginer faire courir deux Webhooks l'un contre l'autre.
00:05:06C'est limité quand on n'a que quelques arguments de fonction.
00:05:12Mais là, c'est une promesse utilisable avec "promise.race".
00:05:16J'adore ce modèle, mon esprit fourmille d'idées de création.
00:05:21C'est la beauté des primitives offertes par le SDK Workflow.
00:05:23Tout est exposé sous forme de promesse.
00:05:28Les modèles JavaScript standards fonctionnent simplement.
00:05:33Autre point : il n'y a ni Redis, ni base de données ici.
00:05:41Dans l'exemple classique, on utilisait le TTL de Redis pour le délai.
00:05:44Ici, on utilise la primitive de sommeil (sleep) de Workflow.
00:05:50Et pas de stagiaire pour nettoyer une base de données encombrée.
00:05:51C'est la meilleure partie.
00:05:59Le Workflow répond à la requête par une redirection vers le succès,
00:06:07puis récupère les infos de l'utilisateur pour le client.
00:06:12Et voilà, notre implémentation tient en 50 lignes de code.
00:06:17C'est incroyable. Peut-on voir cela en action ?
00:06:24Voici la démo du Magic Link. Je saisis mon adresse e-mail.
00:06:31Le Workflow est lancé et l'e-mail envoyé. Le webhook attend.
00:06:41En fait, le Workflow est suspendu. Zéro calcul n'est consommé
00:06:47pendant qu'on attend que l'humain clique sur le lien.
00:06:52À quoi cela ressemble-t-il sur Vercel ? On peut voir l'exécution ?
00:06:57On a reçu l'e-mail. Avant de cliquer, regardons l'observabilité.
00:07:02Je saute d'un sujet à l'autre, mais j'adore regarder cela.
00:07:08L'exécution est ici, elle a commencé il y a 40 secondes.
00:07:13On y retrouve les fonctions d'observabilité standards de Workflow.
00:07:17On voit les entrées, comme l'e-mail que j'ai saisi.
00:07:25Et on remarque que notre hook est simplement en attente.
00:07:39Tu disais qu'aucun calcul n'est en cours. Rien n'attend activement ?
00:07:46C'est ça. Le hook attend, le sommeil dort, sans consommer de calcul.
00:07:50On voit que les deux sont en compétition dans un "promise.race".
00:07:59L'un d'eux doit finir pour que le Workflow continue.
00:08:01Je clique sur le lien, et je suis redirigé vers la page de succès,
00:08:05ce qui était l'une des étapes de notre logique de Workflow.
00:08:08Et si je reviens au formulaire de connexion...
00:08:11Sur le tableau de bord, cela devrait aussi être terminé.
00:08:17C'est exact. Le Workflow est bien terminé.
00:08:27Et le minuteur s'arrête dès que le hook a gagné.
00:08:34On a implémenté le Magic Link en environ 50 lignes de code.
00:08:41C'est génial de voir qu'un schéma explicatif de Magic Link
00:08:59correspond exactement aux étapes que l'on retrouve dans le code final.
00:09:07Pas de base de données intermédiaire, pas de routes API multiples.
00:09:10C'était juste le code que tu m'as montré, très lisible.
00:09:16Exactement. La fonction de création de webhook est un peu plus sophistiquée, car elle fournit une URL de webhook unique et générée aléatoirement.
00:09:23Celle-ci correspond alors à une exécution spécifique du Workflow.
00:09:31Dans le cas de notre route de webhook GitHub ou Slack, elle peut correspondre à n'importe quel nombre d'exécutions de Workflow.
00:09:36J'aime aussi l'idée des webhooks ici, ça change ma vision du sujet.
00:09:49C'est juste une URL éphémère que je peux créer et suspendre.
00:09:54Ça fait une bonne transition, car nous créons beaucoup d'agents.
00:10:04On fait des agents Slack ou GitHub, et on s'abonne à leurs webhooks.
00:10:07Dès qu'il y a un commentaire sur une PR, on veut lancer un agent.
00:10:14Peut-on utiliser les webhooks Workflow pour s'y abonner ?
00:10:20Pour un webhook de Slack ou GitHub, il faut généralement
00:10:28configurer manuellement une URL de rappel statique.
00:10:31On ne peut pas créer une URL unique comme pour l'e-mail ici ?
00:10:32Exact. La fonction de création de webhook est de plus haut niveau,
00:10:35elle fournit une URL unique générée aléatoirement.
00:10:40Elle correspond à une exécution spécifique du Workflow.
00:10:47Dans le cas de GitHub ou Slack, une route de webhook
00:10:52peut correspondre à n'importe quel nombre d'exécutions.
00:11:05On configure un point d'entrée unique pour plusieurs Pull Requests.
00:11:09Pour cela, nous allons utiliser la primitive "hook" de plus bas niveau.
00:11:13J'ai justement une démo pour te montrer ça.
00:11:17Regardons cela de plus près.
00:11:20Voici le "storytime bot".
00:11:26C'est la première application écrite avec le SDK il y a un an.
00:11:28On tape la commande "storytime/" et on voit le fil de discussion se créer.
00:11:35Chaque fil est représenté par une exécution individuelle du Workflow.
00:11:38En ouvrant le fil, on voit qu'une IA a commencé l'histoire,
00:11:44et n'importe qui dans le canal peut la continuer.
00:11:50L'IA nous aidera ensuite à arriver à la conclusion finale.
00:11:53Luna a une graine magique, que se passe-t-il ? Elle la plante.
00:12:04On voit de l'activité ici.
00:12:23La suite ? Quelque chose de magique.
00:12:27L'histoire est finie, et une petite image va même être générée.
00:12:34Mais revenons un peu en arrière.
00:12:40Je suis curieux car je m'attendais à une seule requête webhook,
00:12:55mais il y en a eu au moins deux pour les deux messages.
00:13:04J'ai hâte de voir le code de tout cela.
00:13:10Voici la fonction Workflow pour notre bot Storytime.
00:13:14Elle prend l'ID du canal en entrée.
00:13:28Il y a quelques options de configuration.
00:13:35On voit un tableau de messages, format classique pour l'IA.
00:13:44D'ordinaire, pour un bot Slack, on stockerait ça en base de données
00:13:58et on restaurerait l'état à chaque événement de webhook.
00:14:04Ici, ce n'est pas le cas. C'est juste un tableau dans votre fonction.
00:14:17J'ai souri en voyant le commentaire : "Regarde maman, pas de files."
00:14:29Et il n'y a pas d'importation de base de données, juste Workflow.
00:14:37Il y a une variable "final story" où on va ajouter des messages,
00:14:50et l'histoire finale apparaîtra sous forme de chaîne de caractères.
00:14:59Pas besoin de base de données. C'est comme si "let" était la base.
00:15:07"Let est votre base de données", c'est une expression à retenir.
00:15:13Je l'ai peut-être volée, mais bon.
00:15:20Ce qui nous intéresse ici, c'est la fonctionnalité de hook.
00:15:28Contrairement au Magic Link, nous fournissons ici un jeton (token).
00:15:35C'est une chaîne avec des infos uniques à cette exécution.
00:15:44Le "TS" est l'ID du fil, ce jeton identifie le Workflow de façon unique.
00:15:50Le code de la route du webhook montre que la charge utile de Slack
00:15:55contient tout ce qu'il faut pour recréer cet identifiant.
00:16:06Mais ils n'ont toujours servi qu'à des démos et je n'ai pas pu trouver de bon moyen de les utiliser.
00:16:11Ici, j'ai l'impression que c'est juste une boucle.
00:16:17Mais au lieu d'itérer sur un ensemble fixe d'éléments ou un horodatage, vous utilisez "for await" sur le hook, et cette boucle correspond exactement.
00:16:25vous calculez le même jeton côté reprise.
00:16:33Le Workflow attend ce jeton pour reprendre son exécution.
00:16:42Exactement. Le bot Slack a été configuré statiquement
00:16:46avec une URL de rappel fixe dans le tableau de bord.
00:16:50C'est pourquoi la primitive de hook est mieux adaptée ici.
00:17:01Si on regarde la route du webhook, il n'y a pas grand-chose.
00:17:05L'essentiel est la recréation du jeton à partir des données de Slack.
00:17:12On appelle ensuite la fonction de reprise pour ce Workflow.
00:17:22C'est vraiment cool. Et j'imagine que pour les webhooks,
00:17:33le principe est sensiblement le même ?
00:17:42La différence, c'est que vous n'avez pas à définir la route API.
00:17:43Le SDK Workflow implémente une route par défaut pour vous.
00:17:48Mais sinon, c'est bien un jeton généré pour une exécution précise.
00:17:57Ici, ce hook peut recevoir des données plusieurs fois.
00:18:03C'est différent du Magic Link qui n'était déclenché qu'une fois.
00:18:08Ici, on veut que le hook s'active à chaque nouveau message Slack.
00:18:12Pour cela, on utilise la syntaxe "for await" de JavaScript.
00:18:16On reçoit ainsi plusieurs charges utiles via notre hook.
00:18:23J'adore les itérateurs asynchrones, mais je n'en trouvais pas d'usage.
00:18:32Je les utilisais pour des démos, mais rarement en production.
00:18:39Ici, cela se lit simplement comme une boucle.
00:18:49Au lieu de boucler sur des éléments fixes, on utilise "for await".
00:18:52Tout ce qui est dans la boucle correspond à un message utilisateur.
00:18:56C'est une excellente façon de voir les choses : une itération par message.
00:18:58Le plus beau, c'est qu'entre chaque message, pendant l'attente,
00:19:05absolument aucun calcul n'est consommé.
00:19:10Le workflow est suspendu, que l'attente dure des minutes ou des jours.
00:19:14Il y a donc peut-être des fils Slack qui attendent depuis des semaines.
00:19:16C'est vraiment impressionnant.
00:19:25Pour revenir au tableau de messages, on le modifie maintenant.
00:19:34On y ajoute le nouveau message : c'est notre modification de base.
00:19:40On peut aussi utiliser "promise.all" pour paralléliser des étapes.
00:19:42C'est très lisible pour chaque message reçu sur Slack.
00:19:43C'est exactement comme je le modéliserais lors d'un hackathon.
00:19:54Les fonctions "useStep" sont exécutées en parallèle via "promise.all".
00:19:55Ajouter une réaction sur Slack donne un retour immédiat à l'utilisateur.
00:20:02Pendant ce temps, on sollicite l'IA pour faire avancer l'histoire.
00:20:05J'aimerais voir l'observabilité, pour visualiser ces étapes parallèles.
00:20:06L'exécution Storytime est terminée, on peut aller voir.
00:20:17On voit notre hook et les deux événements reçus.
00:20:32Cela correspond aux deux messages que j'ai tapés sur Slack.
00:20:41Vous pouvez donc publier un package NPM.
00:20:51Sandbox consiste essentiellement à publier un package NPM qui contient la directive "use Step" dans cette fonction.
00:20:56Ainsi, lorsque vous l'importez et l'utilisez dans un workflow, Sandbox devient automatiquement une étape sans que vous ayez à écrire ce code.
00:21:00Et on obtient finalement l'image de notre histoire.
00:21:09Donc, en fait, cette opération est un Step.
00:21:12Vous pouvez donc distribuer un package NPM.
00:21:15Sandbox livre juste un package NPM qui contient la directive "use Step" dans cette fonction.
00:21:21Quand vous l'importez dans un workflow, Sandbox devient un Step sans avoir à écrire de code.
00:21:29Ça ne signifie pas qu'on ne peut plus utiliser Sandbox en dehors d'un workflow.
00:21:32Que se passe-t-il si vous l'appelez sans workflow ?
00:21:35La directive n'est qu'une chaîne de caractères et, sans le compilateur de workflow,
00:21:47cette chaîne ne fait rien. Donc ça fonctionne tout simplement.
00:21:49L'ajout de "use Step" dans vos packages NPM fonctionne très bien sans le SDK de workflow.
00:21:55Et une fois utilisée avec le SDK, vous bénéficiez automatiquement de la durabilité.
00:22:03D'accord, donc la Sandbox fait des choses classiques.
00:22:07Elle installe FFmpeg car ce n'est pas disponible par défaut.
00:22:11Elle télécharge l'URL d'un fichier que nous allons spécifier.
00:22:14Et chacune de ces exécutions sont aussi des Steps en ce moment ?
00:22:17Oui, elles exécutent une commande individuelle dans la Sandbox et ce sont des Steps.
00:22:29Puis nous revenons à l'appel de "create-webhook", comme dans la démo du Magic Link.
00:22:36Mais ici, on passe l'URL du webhook à notre script Bash qui s'exécute dans la Sandbox.
00:22:43On va lancer FFmpeg pour convertir le fichier dans le format demandé dans l'interface.
00:22:53Une fois terminé, le script Bash va effectuer un cURL vers notre URL de rappel du webhook.
00:22:59Et quand cette requête cURL se produit, la logique de notre workflow reprend.
00:23:04D'accord, je vois. J'ai remarqué qu'il y avait un "AND" sur cette exécution.
00:23:11Vous lancez le script en arrière-plan car un Step FFmpeg peut être très long.
00:23:17Vous ne voulez pas d'un Step qui reste là à attendre sans rien faire.
00:23:20Exactement. Cette ligne lance notre script de conversion FFmpeg en arrière-plan.
00:23:28Ensuite, notre fonction de workflow se suspend et attend que le webhook reprenne.
00:23:34Et je vois encore le "Promise.race" avec une pause d'une heure. C'est génial comme modèle.
00:23:40Oui, car le processus de conversion FFmpeg peut être long.
00:23:46S'il s'agit d'un très gros fichier média, on spécifie ici un délai d'une heure.
00:23:51C'est l'avantage : dans un workflow, on peut attendre presque indéfiniment.
00:23:56Et encore une fois, aucune ressource de calcul n'est utilisée pendant l'attente du webhook.
00:24:01Est-ce qu'on peut voir ça ? Est-ce qu'on a une démo ?
00:24:04Oui, tout à fait.
00:24:05C'est un exemple un peu simpliste.
00:24:07Ah oui, je reconnais tout de suite l'exemple du gros lapin. Ça vient de Blender.
00:24:12Je me souviens avoir regardé ces vidéos en apprenant Blender il y a longtemps.
00:24:16Oh wow, je suis jaloux.
00:24:19On a collé l'URL du fichier média. Ici, on va juste extraire la piste audio.
00:24:26En cliquant sur le bouton, on lance le workflow et on peut aller voir l'observabilité.
00:24:33Ah, le voilà. On peut voir la création de notre sandbox.
00:24:37Et cela nous renvoie notre instance de sandbox. C'est plutôt cool.
00:24:42C'est parce que dans un workflow, tout doit être sérialisable.
00:24:46Mais comme tu l'as dit, les sandboxes gèrent la sérialisation pour apparaître ici.
00:24:53Exact. Le package NPM Vercel Sandbox a une classe qui implémente ces fonctions.
00:25:03Donc ça fonctionne directement dans notre outil d'observabilité.
00:25:06N'importe quel package peut le faire, non ? Toute classe pourrait implémenter ces symboles.
00:25:17C'est ça. On voit que notre hook a été rappelé en 20 secondes cette fois.
00:25:25La conversion a été rapide car le fichier est petit, mais ça aurait pu être n'importe quand.
00:25:31On voit qu'après l'initialisation de la sandbox, le hook a lancé la commande FFmpeg.
00:25:43Et quand c'est fini, on a reçu les données de notre sandbox.
00:25:48C'est le cURL du script Bash. Il utilise cURL dans la sandbox pour valider le webhook.
00:25:57Exactement. La sandbox a fini son travail et redonne le contrôle au workflow.
00:26:04De la façon dont je vois les choses, un Step exécute du code et continue ensuite.
00:26:13Mais les hooks et webhooks semblent être à un niveau plus bas.
00:26:21Je peux créer un jeton ou une URL et attendre n'importe quoi : un humain, un e-mail...
00:26:27Le workflow se met en pause avec tout son état en attendant cet événement.
00:26:34Oui. Pour moi, le webhook et le hook permettent d'injecter des données externes.
00:26:42Le Step permet de suspendre le workflow en attendant la fin d'un calcul interne.
00:26:50Mais hook et webhook sont plus fondamentaux car on peut envoyer ce jeton n'importe où.
00:27:01Ça peut être une personne, un e-mail ou même un autre workflow.
00:27:05Dès que c'est terminé, le workflow parent se réveille exactement là où il s'était arrêté.
00:27:12C'est donc un moyen de suspendre le workflow pour toute action externe.
00:27:19Oui, c'est une façon de suspendre et d'attendre un flux de données externe, c'est très puissant.
00:27:31C'est génial. On n'a plus de temps, mais ces démos confirment pourquoi hook est ma fonction préférée.
00:27:42Super. Je suis ravi que ça t'ait plu.
Community Posts
No posts yet. Be the first to write about this video!
Write about this video