Análisis profundo de los hooks | Workflow SDK

VVercel
Computing/SoftwareSmall Business/StartupsInternet Technology

Transcript

00:00:00Hola, muchas gracias por acompañarnos hoy.
00:00:02Soy Praneet, del equipo de Workflow aquí en Vercel.
00:00:05Hola, soy Nate, también del equipo de Workflow.
00:00:08Nate, tú y I hemos estado en el equipo de Workflow desde el principio,
00:00:12y de todas las cosas que hemos lanzado en los últimos seis meses,
00:00:15creo que los hooks y webhooks son una de mis funciones favoritas,
00:00:18y eso es exactamente de lo que vienes a hablar hoy.
00:00:21Los hooks y webhooks también son mi función favorita.
00:00:23Son increíblemente potentes, y mostraré algunas demos para explicar por qué.
00:00:28La primera demo es algo con lo que probablemente todos estemos familiarizados, los Magic Links.
00:00:33Magic Link es un formulario de inicio de sesión. Escribes tu correo, recibes un email,
00:00:40y cuando haces clic en ese enlace, inicias sesión en el servicio.
00:00:44Sí, y si mal no recuerdo, con Vercel, incluso antes de que se llamara Vercel,
00:00:48cuando todavía se llamaba ZEIT, los Magic Links eran la única forma de autenticarse,
00:00:52y básicamente nosotros mismos construimos todo ese sistema en aquel entonces.
00:00:56Así es, y todavía tengo trauma postraumático.
00:01:01Porque sin Workflow, implementar un sistema así es mucho más complicado de lo que parece a simple vista.
00:01:08La lógica termina dispersa en múltiples archivos.
00:01:12Necesitas involucrar una base de datos para rastrear el estado, y se vuelve un caos rápido.
00:01:19Sí, ya estaba pensando en cómo estructuraría esto y qué base de datos usaría,
00:01:24porque parece el tipo de problema común para el que ya he construido cosas antes.
00:01:28Así que sí, me encantaría ver cómo queda.
00:01:30Sí, para demostrar a qué me refiero, esos puntos críticos de los que hablo,
00:01:38comencé implementando una versión "tradicional" sin Workflow de un inicio de sesión con Magic Link.
00:01:43Y hay tres endpoints involucrados.
00:01:47El primero es cuando se envía el formulario de inicio de sesión,
00:01:50y necesita generar una sesión y guardarla en una base de datos, como Redis.
00:01:57Tienes que implementar un TTL; no puedes dejar datos ahí para siempre, deben expirar.
00:02:06Y luego enviar el correo; esto podría fallar, el inicio de sesión no funcionaría y sería frustrante.
00:02:14Exacto, y luego tienes que tener un cron job o un becario limpiando la base de datos.
00:02:19Yo podría haber sido ese becario en aquel momento.
00:02:22Pero luego hay un segundo endpoint, el que ocurre cuando el usuario hace clic en el enlace del correo.
00:02:28Y este básicamente necesita consultar la base de datos y restaurar el estado creado en el primer endpoint.
00:02:36Y ya estamos llegando a un código tipo espagueti.
00:02:38Al intentar imaginar cómo se vería esto, este código me resulta familiar y así es como yo lo estructuraría.
00:02:48Vemos que se complica rápido, aunque sea un concepto muy sencillo.
00:02:54Así que veamos cómo implementarías esta función en Workflow.
00:02:59La implementación del Magic Link usando el SDK de Workflow se ve algo así.
00:03:05Vemos que tenemos nuestra función con la directiva useWorkflow, lo que indica que es nuestra función de Workflow.
00:03:11Y lo primero que hacemos es llamar a la función createWebhook, que viene del paquete de Workflow.
00:03:18También usamos la opción respondWithManual, lo que significa que el Workflow se encargará
00:03:36de enviar la respuesta a la solicitud HTTP que activa este Webhook.
00:03:40¿Y esto es para poder hacer una redirección o algo después de que inicien sesión?
00:03:51Sí, por si hay información en nuestra función de Workflow que necesitemos para saber qué respuesta enviar.
00:03:57Al igual que en el primer endpoint, enviamos el email. Esta es una función useStep.
00:04:03Si algo así falla, el SDK de Workflow tiene reintentos automáticos.
00:04:10El aspecto de la durabilidad ya ofrece una ventaja sobre el enfoque tradicional.
00:04:21Así que sendLoginEmails es un paso, y si el envío falla, reintenta
00:04:26con la misma URL que ya creaste para el Webhook.
00:04:30Y si miramos aquí, hay un patrón muy interesante.
00:04:35Estamos usando promise.race con un sleep de cinco minutos.
00:04:40Esto es posible porque este objeto Webhook implementa una promesa.
00:04:50Para esperar la solicitud de este Webhook, solo haces await Webhook.
00:04:58O en este caso, lo haces con la carrera. Y es genial porque esperaba que
00:05:02esta función de Webhook tuviera un timeout o alguna opción como argumento.
00:05:06Pero me gusta que sea tan limpio; para hacer un timeout, simplemente haces una carrera entre el Webhook y un sleep.
00:05:12Siento que puedo hacer mucho más con esto. Podría poner a competir dos Webhooks distintos entre sí.
00:05:16No hay mucho que puedas hacer cuando tienes solo un par de argumentos en una función.
00:05:21Pero el hecho de que sea una promesa y pueda hacer un promise.race contra un sleep, o quizás otro paso...
00:05:23Me encanta este patrón. Mi mente ya está imaginando todo lo que podría construir con esto.
00:05:28Exacto, y esa es la belleza de las primitivas que ofrece el SDK de Workflow.
00:05:33Todo se expone como una promesa.
00:05:41Así que los patrones estándar de JavaScript como await promise.race simplemente funcionan.
00:05:44Y otra cosa a destacar aquí es que no hay Redis. No hay base de datos.
00:05:50En el ejemplo tradicional, usábamos el TTL de Redis para implementar este tiempo de espera.
00:05:51Y en este caso, estamos usando la primitiva sleep de Workflow.
00:05:59Y tampoco hay becarios que tengan que limpiar una base de datos desordenada después.
00:06:07Esa es la mejor parte.
00:06:12Y puedes ver que el Workflow responde a la solicitud pública redirigiendo a la página de éxito.
00:06:17Luego recupera información sobre tu usuario para devolverla al cliente que inició el login.
00:06:24Y ese es todo nuestro Workflow. Nuestra implementación de Magic Link son 50 líneas de código.
00:06:31Es increíble verlo. ¿Podemos verlo en acción?
00:06:41Aquí está la demo de Magic Link. Introduciré mi correo electrónico.
00:06:47Nuestro Workflow se inició ahí y envió el correo. Y hay un webhook simplemente esperando.
00:06:52De hecho, nuestro Workflow está suspendido ahora mismo. Se consume cero cómputo mientras se espera al humano.
00:06:57¿Y cómo se ve esto en Vercel? ¿Puedo ver una ejecución que esté pendiente?
00:07:02Bien, ya recibimos el correo. Antes de hacer clic, echemos un vistazo a la observabilidad.
00:07:08Sé que estoy saltando de un lado a otro, pero me encanta que estemos viendo esto.
00:07:13Vale, vemos que nuestra ejecución está aquí y comenzó hace 40 segundos.
00:07:17Si miramos, tenemos las funciones de observabilidad estándar que proporciona Workflow.
00:07:25Podemos ver las entradas de nuestra ejecución. Ves mi correo que escribí en el formulario.
00:07:39Y curiosamente, podemos ver que nuestro hook está simplemente esperando.
00:07:46Dijiste que no hay cómputo ejecutándose. Esta es la observabilidad, pero no hay nada esperando activamente.
00:07:50Exacto. El hook está esperando y el sleep está durmiendo, y ninguna de esas cosas implica cómputo real.
00:07:59Pero vemos nuestro hook y, si recuerdas, ambos están compitiendo en un promise.race.
00:08:01Así que uno de ellos debe terminar primero para que el Workflow continúe.
00:08:05Si hago clic en el enlace... vale, vemos que fui redirigido a la página de éxito,
00:08:08que era uno de los pasos en nuestra lógica de Workflow.
00:08:11Y si vuelvo al formulario de inicio de sesión...
00:08:17Y de vuelta en el dashboard, eso también debería estar completado.
00:08:27Correcto. Nuestro Workflow se completó.
00:08:34Y ves que el temporizador se detiene en cuanto el hook gana.
00:08:41Sí, pudimos implementar el magic link con unas 50 líneas de código.
00:08:59Es muy ingenioso. Es genial ver cómo, si tuviéramos que dibujar un diagrama de cómo funciona el magic link,
00:09:07los pasos que tienes en el código son exactamente como lo planearías, y así es el código final.
00:09:10No hubo una base de datos intermedia. No hubo múltiples rutas de API. Se lee con mucha claridad.
00:09:16Correcto. Así que la función "create webhook" es un poco más de alto nivel en ese sentido, ya que proporciona una URL de webhook única generada aleatoriamente.
00:09:23Que se vincula a una ejecución específica de Workflow.
00:09:31En el caso de nuestra ruta de webhook de GitHub o Slack, eso podría vincularse a cualquier número de ejecuciones de Workflow.
00:09:36porque me da una forma totalmente distinta de pensar en ellos.
00:09:49Es solo una URL efímera que puedo crear y sobre la cual puedo suspender la ejecución.
00:09:54De hecho, esto sirve de transición, porque nosotros construimos muchos agentes en Vercel.
00:10:04Hacemos agentes de Slack y de GitHub, y solemos suscribirnos a sus webhooks, ¿verdad?
00:10:07Cada vez que hay un comentario en un PR y queremos activar un agente de Vercel,
00:10:14queremos hacerlo basándonos en un evento de estos webhooks que envía GitHub.
00:10:20¿Podemos usar los webhooks de Workflow para suscribirnos a eventos de GitHub, por ejemplo?
00:10:28Bueno, para un webhook enviado desde Slack o GitHub, normalmente
00:10:31tienes que ir al dashboard manualmente y configurar una URL de callback estática.
00:10:32Cierto. No puedes crear esto como algo único; no puedo darle una URL temporal como hice con el correo.
00:10:35Exacto. La función createWebhook es de un nivel un poco más alto en ese sentido,
00:10:40ya que proporciona una URL de webhook única generada aleatoriamente.
00:10:47Que apunta a una ejecución específica de Workflow.
00:10:52En el caso de nuestra ruta de webhook de GitHub o Slack, eso podría apuntar a cualquier número de ejecuciones.
00:11:05Claro. Tienes que preconfigurar algo, pero tienes múltiples pull requests y todos van al mismo endpoint.
00:11:09Así que para implementar eso con el SDK, bajaremos un nivel y usaremos la primitiva hook de bajo nivel.
00:11:13Y tengo una demo para enseñártelo.
00:11:17Echémosle un vistazo.
00:11:20Vale. Este es el bot de cuentos (Storytime Bot).
00:11:26Fue la primera aplicación que escribí con el SDK de Workflow hace algo más de un año.
00:11:28Funciona así: escribes el comando /storytime y veremos cómo se crea este hilo.
00:11:35Cada hilo está representado por una ejecución individual de Workflow.
00:11:38Cuando expandimos el hilo, vemos que un LLM comenzó la historia por nosotros,
00:11:44y tú, yo o cualquiera en este canal puede continuarla.
00:11:50Y el LLM nos ayudará a llevarla hasta su conclusión final.
00:11:53Vale. Luna tiene una semilla mágica y ¿qué pasa después? Ella planta la semilla.
00:12:04Bien. Vemos algo de actividad ocurriendo aquí.
00:12:23¿Qué pasa después? Algo mágico.
00:12:27Nuestra historia ha terminado, tenemos el relato final y también se generará una pequeña imagen.
00:12:34Pero volveremos a eso luego.
00:12:40Ya tengo curiosidad, porque esperaba una solicitud de webhook pero hubo al menos dos,
00:12:55porque tenías dos mensajes. Tengo muchas ganas de ver cómo se ve esto en el código.
00:13:04Bien. Esta es la función de Workflow para nuestro bot de cuentos.
00:13:10Vemos que recibe el ID del canal, que es el canal del bot.
00:13:14Tiene algunas opciones de configuración que puedes pasar.
00:13:28Pero lo interesante es este array de mensajes que, si conoces el AI SDK, es el formato de datos de la IA.
00:13:35En una aplicación típica de bot de Slack como esta, normalmente guardarías esto
00:13:44en una base de datos y, en cada iteración o evento de webhook, restaurarías el estado.
00:13:58Y eso no es lo que pasa aquí. Es solo un array dentro de tu función.
00:14:04Sí. Me reí antes porque vi la intro y tenías ese comentario que decía: "Mira mamá, sin colas ni KV".
00:14:17Y no hay ningún import para una base de datos. Solo estás importando Workflow.
00:14:29Y volviendo a lo del último mensaje, casi se pasa por alto, pero tienes una variable
00:14:37llamada final story a la que presumiblemente iremos añadiendo mensajes,
00:14:50y la historia final aparecerá como un string, pero no tiene que ir a ninguna base de datos.
00:14:59Es casi como si "let" fuera tu base de datos aquí.
00:15:07Sí, "let es tu base de datos" es un gran término; vamos a acuñar eso.
00:15:13Puede que te lo haya robado a ti, pero...
00:15:20Lo interesante aquí, y de lo que venimos a hablar, es la función hook que estamos creando.
00:15:28A diferencia del ejemplo del magic link, en este caso estamos proporcionando un token,
00:15:35que es un string con información identificativa única para esta ejecución de Workflow.
00:15:44El TS es el ID del hilo. Así que este string es el token que identifica unívocamente la ejecución.
00:15:50Cuando miremos el código de la ruta del webhook, veremos que los datos que envía Slack
00:15:55contienen todo lo necesario para recrear este identificador de forma determinista.
00:16:06Pero siempre han sido buenos para demostraciones y no había podido encontrar una buena forma de usarlos.
00:16:11Aquí me parece que simplemente tienes un bucle.
00:16:17Pero en lugar de iterar sobre un conjunto fijo de elementos o una marca de tiempo, al usar for await y el hook, el bucle encaja exactamente.
00:16:25Entiendo que tienes una API ya conectada a Slack, pero cada vez que recibes un mensaje,
00:16:33básicamente calculas el mismo token en el lado de la reanudación.
00:16:42Así tu Workflow puede esperar este token y tú puedes construir el mismo token
00:16:46desde los datos del mensaje para reanudar la ejecución.
00:16:50Exactamente. El bot de Slack se configuró una vez manualmente en su dashboard,
00:17:01donde tienes que definir estáticamente una URL de callback.
00:17:05Por eso la primitiva hook de nivel inferior funciona mejor aquí, porque recreamos el token dinámicamente.
00:17:12Echemos un vistazo rápido: esta es la ruta del webhook, y en realidad no pasa gran cosa.
00:17:22Lo principal es cómo recreamos el token a partir de los datos que nos pasa Slack.
00:17:33Y luego llamamos a la función resume, y esto reanuda la ejecución única de ese Workflow.
00:17:42Me parece genial. Imagino que con los webhooks normales estáis haciendo algo parecido.
00:17:43¿Es el webhook básicamente un token aleatorio y un endpoint HTTP que resuelve ese mismo token?
00:17:48Sí, bueno, la diferencia con la función webhook es que no necesitas definir esa ruta de API en tu código.
00:17:57El SDK de Workflow ya implementa una ruta por defecto para ti.
00:18:03Pero sí, aparte de eso, es un token aleatorio único para una ejecución específica.
00:18:08Pero en este caso, tenemos nuestro hook con el token y, como mencionaste antes,
00:18:12este hook puede recibir datos varias veces.
00:18:16Esto es distinto al ejemplo del Magic Link, que solo necesitaba activarse una vez.
00:18:23Aquí queremos que el hook se active por cada mensaje único que alguien escriba en el hilo de Slack.
00:18:32Para eso, usamos la sintaxis for await de JavaScript, común con los iteradores asíncronos.
00:18:39Pero en este caso, recibimos múltiples datos del webhook de Slack usando nuestro hook.
00:18:49Qué bueno. Nunca había encontrado un buen caso de uso... me encantan los iteradores asíncronos,
00:18:52incluso di una charla sobre esto hace mucho, pero siempre parecían mejores para demos.
00:18:56Aquí se lee simplemente como un bucle.
00:18:58Pero en lugar de iterar sobre una lista fija o un tiempo, usas for await con el hook.
00:19:05Aquí tienes un bucle que encaja perfectamente.
00:19:10Todo lo que hay dentro del bucle corresponde a un mensaje del usuario.
00:19:14Es una forma agradable de pensarlo: cada mensaje causa una iteración y simplemente se encola.
00:19:16Lo mejor es que, en cada iteración del bucle, mientras esperamos el siguiente mensaje,
00:19:25no se consume absolutamente nada de cómputo.
00:19:34El workflow está suspendido y el siguiente mensaje podría llegar en minutos, días o nunca.
00:19:40Probablemente haya hilos en ese canal de Slack donde podría volver ahora mismo
00:19:42y habría una ejecución esperando desde hace semanas si nadie respondió. Es genial.
00:19:43Y volviendo al array de mensajes de antes, ahora estamos modificando el array.
00:19:54Añadimos el nuevo mensaje y esa es nuestra modificación de "base de datos" en la variable local.
00:19:55Qué guay. Y veo que haces más promise.all para paralelizar pasos intermedios.
00:20:02Se lee muy limpio para cada mensaje de Slack.
00:20:05Me gusta porque es exactamente como lo modelarías si estuvieras en una hackathon.
00:20:06Es como: "aquí escribo lo que ocurre con cada mensaje".
00:20:17Sí, en el modelo promise.all, estas son solo funciones useStep normales ejecutándose en paralelo.
00:20:32Añadir una reacción al mensaje de Slack es para dar feedback inmediato al usuario.
00:20:41Pero al mismo tiempo, queremos iniciar el LLM para que avance la generación de la historia.
00:20:51Me interesaría mucho ver cómo queda la observabilidad cuando podamos,
00:20:56porque imagino esos tramos empezando a la vez y haciéndolo muy obvio.
00:21:00Ya tenemos la observabilidad de Storytime. Ya terminó, así que luego veremos la imagen.
00:21:09Así que, de hecho, esta operación es un Paso.
00:21:12Por lo que puedes distribuir un paquete de NPM.
00:21:15Sandbox básicamente distribuye un paquete NPM que tiene la directiva "use Step" dentro de esta función.
00:21:21Así, cuando lo importas y usas en un flujo de trabajo, Sandbox se convierte en un Paso sin que escribas nada de ese código.
00:21:29Pero eso no quita que puedas seguir usando Sandbox para crear fuera de un flujo de trabajo.
00:21:32¿Qué sucede cuando llamas a esto sin un flujo de trabajo?
00:21:35Si te das cuenta, la directiva es solo una cadena de texto, y si la ejecutas sin el compilador de flujos de trabajo,
00:21:47esa cadena no hace nada. Simplemente funciona.
00:21:49Añadir "use Step" en tus paquetes NPM funciona perfectamente sin el SDK de flujos de trabajo.
00:21:55Y una vez que usas esa función dentro del SDK de flujos de trabajo, obtienes los beneficios de durabilidad de inmediato.
00:22:03Bien, el Sandbox solo realiza algunas tareas típicas.
00:22:07Instala FFmpeg porque no está disponible por defecto.
00:22:11Descarga la URL de un archivo que vamos a especificar.
00:22:14¿Y cada una de estas ejecuciones también son Pasos ahora mismo?
00:22:17Sí, ejecutan un comando individual en el Sandbox y son Pasos. Podremos verlos en la observabilidad.
00:22:29Bien, entiendo. Qué bien. Ya veo que, me estaba adelantando un poco, pero noté que hay un AND en esta ejecución.
00:22:36Pero en este caso, pasaremos esa URL del webhook a nuestro script de Bash que ejecutaremos en el Sandbox.
00:22:43Lo que sucede es que ejecutaremos FFmpeg y convertiremos el archivo al formato que solicitemos en la interfaz.
00:22:53Y cuando termine, el script de Bash ejecutará un cURL contra nuestra URL de retorno del webhook.
00:22:59Y cuando ocurra esa solicitud de cURL, nuestra lógica del flujo de trabajo se reanudará.
00:23:04Entiendo. Eso es genial. Me estaba adelantando un poco, pero noté que hay un "AND" en esta ejecución.
00:23:11Estás escribiendo el script ejecutando esto en segundo plano porque un paso de FFmpeg como este podría tardar mucho.
00:23:17No quieres un Paso que se quede ahí sentado esperando.
00:23:20Exacto. Esta línea de aquí inicia nuestro script de conversión de FFmpeg en segundo plano.
00:23:28Luego, nuestra función de flujo de trabajo se suspende y esperamos a que el webhook se reanude.
00:23:34Veo el "promise race" de nuevo con una espera de una hora. Es un patrón muy interesante.
00:23:40Cierto, y esta vez, el proceso de conversión de FFmpeg podría tardar bastante.
00:23:46Podría ser un archivo multimedia muy grande. Así que especificamos un tiempo de espera de una hora en este caso.
00:23:51Y eso está bien. En un flujo de trabajo, puedes suspenderlo por un tiempo esencialmente indefinido.
00:23:56Y de nuevo, hay cero consumo de cómputo mientras esperamos a que se reanude este webhook.
00:24:01¿Podemos ver esto? ¿Podemos ver esta ejecución? ¿Tenemos una demostración?
00:24:04La tenemos.
00:24:05Es un ejemplo un poco tonto.
00:24:07Sí, reconocí el ejemplo del conejo grande inmediatamente. Es de Blender.
00:24:12Sí, recuerdo ver estos videos cuando aprendía Blender hace mucho tiempo.
00:24:16Oh, vaya, tengo envidia.
00:24:19Ya pegamos la URL de nuestro archivo multimedia. En este caso, simplemente extraeremos la capa de audio.
00:24:26Una vez que hacemos clic en el botón, se inicia un flujo de trabajo y deberíamos poder ir a nuestra observabilidad.
00:24:33Ahí está. Sí, podemos ver la creación de nuestro sandbox.
00:24:37Y eso nos devuelve nuestra instancia de sandbox. Muy genial.
00:24:42Y esto es porque los sandboxes, todo en un flujo de trabajo, tiene que ser serializable.
00:24:46Pero como dijiste, los sandboxes implementan serialización, así que son serializables y aparecen en el flujo de trabajo.
00:24:53Correcto. Sí. El paquete de Vercel sandbox tiene una clase sandbox y esa clase implementa las funciones de serialización del flujo.
00:25:03Así que simplemente funciona en nuestra observabilidad.
00:25:06Y cualquier paquete puede hacer esto, ¿verdad? No es solo el sandbox. Podría implementar lo mismo y tener directivas de pasos.
00:25:17Así es. Podemos ver que nuestro enlace terminó recibiendo la llamada en 20 segundos esta vez.
00:25:25Una conversión un poco más rápida porque es un archivo pequeño, pero podría haber sido cualquier cantidad de tiempo.
00:25:31Vemos que tras crear e inicializar el sandbox, se creó nuestro hook y lo pasamos al sandbox para iniciar el comando FFmpeg.
00:25:43Y cuando eso terminó, recibimos una carga útil de nuestro sandbox.
00:25:48Este es el curl que ocurrió antes en el script de bash. Escribe el comando y usa curl en un sandbox para completar el webhook.
00:25:57Exacto. Nuestro sandbox terminó el trabajo que estaba haciendo, así que devuelve el control a nuestro flujo de trabajo.
00:26:04Como lo veo ahora, con los pasos en un flujo de trabajo, ejecutas un paso, corre un código en segundo plano y luego continúa.
00:26:13Pero tanto el hook como el webhook parecen ser de un nivel más bajo. Puedo crear un token o URL y esperar lo que sea.
00:26:21Podría ser un enlace mágico humano, un correo, un sandbox o cualquier tipo de proceso que deba ocurrir.
00:26:27Y mi flujo de trabajo simplemente se pausa con todo su estado hasta que ocurra ese evento. Parece más básico que el Paso en sí.
00:26:34Sí. Yo lo veo así: webhook y hook son una forma de pasar cargas útiles externas a tu flujo de trabajo.
00:26:42Pienso que un paso es una forma en la que un flujo puede suspenderse y esperar a que termine un proceso para reanudarse.
00:26:50Pero hook y webhook parecen de nivel más bajo porque solo generas un token o URL que podrías enviar a cualquier parte.
00:27:01Podría ser una persona, un correo electrónico o incluso otro flujo de trabajo, por ejemplo.
00:27:05Y cuando eso se completa, tu flujo de trabajo principal básicamente se despierta y reanuda justo donde lo dejó.
00:27:12Así que es de un nivel más bajo que un Paso. Es una forma de suspender tu flujo para cualquier acción externa.
00:27:19Sí. Me gusta pensar que el hook es una forma de suspender tu flujo y esperar una carga útil externa, lo cual es muy potente.
00:27:31Esto es genial. Ya se nos acabó el tiempo hoy, pero con estas demostraciones me has validado por qué el hook es mi función favorita.
00:27:42Fantástico. Me alegra que lo hayas disfrutado.

Key Takeaway

El SDK de Workflow permite construir sistemas complejos como Magic Links o bots de Slack eliminando bases de datos intermedias y colas de mensajes al tratar variables locales y promesas como el estado persistente del sistema.

Highlights

  • La implementación de Magic Links mediante Workflow SDK reduce el código a solo 50 líneas y elimina la necesidad de gestionar bases de datos externas o Redis.

  • El uso de la función createWebhook con la opción respondWithManual permite que el flujo de trabajo controle directamente la respuesta HTTP y ejecute redirecciones personalizadas.

  • Los patrones estándar de JavaScript como Promise.race y for await funcionan de forma nativa con los hooks porque el SDK expone estas primitivas como promesas.

  • El sistema suspende la ejecución y consume cero cómputo mientras espera una respuesta de un webhook, permitiendo pausas de minutos, días o semanas sin coste operativo.

  • La primitiva hook de bajo nivel permite la reanudación determinista de ejecuciones mediante tokens generados a partir de metadatos externos como el ID de un hilo de Slack.

  • La integración con sandboxes permite ejecutar procesos pesados como conversiones de FFmpeg en segundo plano, notificando al flujo principal mediante un cURL al terminar.

Timeline

Simplificación de Magic Links y eliminación de infraestructura

  • El enfoque tradicional de inicio de sesión sin contraseña requiere múltiples endpoints y una base de datos con TTL para gestionar estados temporales.
  • La lógica dispersa en varios archivos y la necesidad de limpiar datos obsoletos genera una complejidad técnica innecesaria en aplicaciones estándar.
  • Los fallos en el envío de correos electrónicos en sistemas convencionales suelen interrumpir el flujo de autenticación de forma crítica.

La arquitectura clásica de Magic Link depende de Redis para rastrear tokens y de tareas cron para el mantenimiento de la base de datos. Este modelo fragmenta la lógica de negocio en tres puntos distintos: el envío del formulario, la persistencia del estado y la validación del enlace. El desorden resultante en el código complica la observabilidad y aumenta el riesgo de fallos en el proceso de inicio de sesión.

Implementación técnica con Workflow SDK y promesas

  • La directiva useWorkflow encapsula toda la lógica de autenticación en una sola función duradera de 50 líneas.
  • Los reintentos automáticos en el paso sendLoginEmails garantizan que el flujo no se rompa si el proveedor de correo falla momentáneamente.
  • El patrón de carrera entre un webhook y un temporizador sleep sustituye eficazmente al TTL de las bases de datos tradicionales.

Al utilizar la primitiva createWebhook, el flujo de trabajo genera una URL única que espera una interacción humana. La belleza del sistema reside en usar promise.race contra un sleep de cinco minutos; si el usuario no hace clic a tiempo, el temporizador gana y el flujo termina limpiamente. No se requiere infraestructura externa porque el estado reside en la propia ejecución del flujo.

Observabilidad y eficiencia en la suspensión de cómputo

  • Las ejecuciones suspendidas no consumen recursos de CPU mientras esperan una entrada externa o una acción humana.
  • El panel de control de Vercel permite visualizar las entradas exactas y el estado de cada paso en tiempo real durante la espera.
  • El flujo de trabajo se reanuda instantáneamente en el punto exacto de suspensión tras recibir la llamada al webhook.

Durante la fase de espera del Magic Link, el flujo entra en un estado de pausa total. La observabilidad muestra que el hook está activo y el temporizador en marcha, pero el entorno de ejecución está liberado. Una vez que el usuario hace clic en el enlace, el sistema recupera el contexto y redirige a la página de éxito, completando el ciclo sin haber mantenido una conexión abierta o un servidor activo.

Gestión de hilos y estados con hooks de bajo nivel

  • Los hooks de bajo nivel utilizan tokens deterministas para vincular eventos externos con ejecuciones de flujo específicas.
  • Las variables locales declaradas con let actúan como una base de datos persistente durante toda la vida del hilo de conversación.
  • El uso de arrays simples para almacenar el historial de mensajes evita la necesidad de servicios de almacenamiento de clave-valor como Redis o KV.

En el caso de un bot de Slack, cada hilo de conversación se gestiona como una ejecución independiente. Al calcular un token basado en el ID del hilo, el webhook de Slack puede enviar múltiples mensajes a la misma ejecución. Esto permite mantener el contexto de una IA dentro de un array de JavaScript local, simplificando drásticamente el desarrollo de agentes que requieren memoria a largo plazo.

Iteradores asíncronos y flujos de larga duración

  • La sintaxis for await permite procesar múltiples cargas útiles de un mismo hook de forma secuencial y limpia.
  • La paralelización de tareas mediante promise.all permite ejecutar respuestas de IA y reacciones de interfaz simultáneamente.
  • Un flujo puede permanecer a la espera de nuevos mensajes durante semanas sin incurrir en costes de servidor.

El uso de iteradores asíncronos transforma la recepción de mensajes en un bucle lógico donde cada iteración representa una nueva interacción del usuario. Dentro de este bucle, se pueden orquestar pasos paralelos: mientras se genera una respuesta con un LLM, se puede enviar una reacción visual a Slack. El flujo simplemente se 'duerme' al final de cada iteración, manteniendo todas las variables locales intactas para el siguiente mensaje.

Integración con procesos externos y sandboxes

  • Los paquetes de NPM pueden incluir directivas useStep para integrarse de forma transparente en flujos de trabajo duraderos.
  • El patrón de suspensión permite delegar tareas pesadas de Bash o FFmpeg a entornos aislados que notifican su finalización vía cURL.
  • Cualquier objeto serializable puede ser persistido como parte del estado del flujo de trabajo, incluyendo instancias de clases específicas.

Para tareas que exceden los límites de tiempo de una función normal, como la edición de vídeo, se utiliza un sandbox. El flujo crea el entorno, inicia un script de Bash y se suspende inmediatamente pasando una URL de retorno. El script realiza la conversión y, al finalizar, ejecuta un cURL que despierta al flujo principal con el resultado. Este mecanismo de 'espera de carga útil externa' es la base más potente de la arquitectura de Workflow.

Community Posts

No posts yet. Be the first to write about this video!

Write about this video