This graph shows how many times the word ______ has been mentioned throughout the history of the program.
Hoy vamos a hacer un producto, porque joder, es que hay mucha gente que hace este producto, de verdad.
De hecho, mira, tengo por aquí, tengo por aquí el enlace, sí, sí, sí.
Mirad, a ver, es que os digo que es un producto porque mirad, si buscáis chat PDF, chat PDF, chat PDF, chat with any PDF, ask your PDF,
hay gente que se ha hecho rica con estas cosas, que se ha hecho rica.
Claro, es verdad que esta gente, la clave es que tiene un buen dominio, pero tampoco es tan complicado esto, ¿no?
Y lo vamos a intentar hacer hoy y de paso vamos a intentar utilizar algunos servicios, algunas cositas, ¿vale?
Y aquí lo tenemos, chat with PDF, ask your PDF, hay algunos que están mejor, algunos que están peor,
hay otros que aquí te permite PDF, TXT, PowerPoint, sabes que hay diferentes formatos.
Aquí vamos a hacerlo con solo PDF, más adelante, si nos da tiempo, pues me gustaría que lo podamos hacer con más cosas.
También demostraremos la parte de la inteligencia artificial, porque he visto que ha salido,
es una pena que no me ha dado tiempo ni de echarle un vistazo,
pero la gente de Cloudflare ha sacado un servicio muy chulo, muy, muy, muy chulo.
A ver si, a ver si lo encuentro, amigos, a ver si lo encuentro, Product News.
Es que lo ha sacado, lo sacó ayer, es que de inteligencia artificial, Cloudflare, no me falles, Cloudflare,
Cloudflare, Langchain, es que lo sacó ayer mismo, lo sacó ayer mismo, ayer mismo.
Mira, Langchain Support for Worker's Inteligencia Artificial Vectorize and D1, 31 de enero.
Y me parece muy interesante esto, es que no sé cómo explicaros cómo de increíble es esta noticia
de las posibilidades que tiene esto.
Pero si hubiéramos tenido más tiempo, si lo hubieran anunciado con más tiempo,
seguramente lo hubiéramos podido haber hecho hoy mismo.
Pero es que esto abre una de posibilidades y a muy buen precio, a un precio espectacular.
De hecho, ellos mismos han hecho un ejemplo también, como un template, de hablar con un PDF.
O sea, es que es increíblemente interesante, porque además lo podéis guardar,
podéis utilizar la inteligencia artificial en sus servidores, con sus embeddings,
con sus bases de datos, con el hosting de Cloudflare.
O sea, y todo tan fácil, que es que tiene muy, muy buena pinta.
Esto, amigos, ya os digo yo, que esta noticia, que a lo mejor dices,
ah, es que yo no tengo mucha idea, no te preocupes.
Pero lo que te voy a decir es que esta noticia,
muchos productos que se van a forrar de aquí a unos cuantos meses,
van a utilizar esto por detrás.
Así de claro te lo digo, ¿vale?
Así de claro, es espectacular.
Y de hecho, muchos de estos productos utilizan por detrás esta tecnología o muy similares.
Seguramente más adelante haremos un directo sobre esto,
porque ya veréis que esto tiene muy buena pinta, ¿eh?
Muy, muy, muy buena pinta.
Y ya cuando lo hagamos ya os explicaré al detalle,
pero solo os dejo por ahí el hype, porque es impresionante.
Pues vamos a hacer esto.
De hecho, una persona de la comunidad el otro día me escribió
y me comentó que él tenía un proyecto también muy similar, ¿eh?
Que se llama PDFizado.
Entonces, también le podéis echar un vistazo, que tiene muy buena pinta, gratis.
Tenéis hasta 8 megas de límite, respuesta estándar, soporte estándar,
podéis subir hasta 6 PDFs, obviamente de pago.
Pero lo interesante es que de código abierto,
entonces tenéis todo el código abierto para echarle un vistazo.
Le podéis echar un vistazo al código a ver cómo lo hizo, a ver qué hizo y tal.
Yo lo he estado mirando un poco por encima y tiene muy buena pinta.
Hoy no nos va a dar tiempo a hacer todas las cosas que hizo él, obviamente.
Pero está bastante bien, ¿vale?
Nosotros, para ayudarnos un poco, y ya que Cloudinary estuvo patrocinando el Adven.js,
¿vale?
Ya sabéis que el Adven.js, los retos de programación entre los patrocinadores,
teníamos a la gente de Cloudinary, ¿vale?
Y Cloudinary, una cosa que se puede hacer, que es bastante interesante,
es que no solo podéis subir imágenes y vídeos, sino que también podéis subir PDFs.
Entonces, aprovechando, vamos a subir el PDF,
y cuando subamos el PDF vamos a hacer dos cosas.
Uno, extraer imágenes, y segundo, extraer el texto.
Entonces, extraeremos el texto del PDF,
y ese texto es el que le pasaremos a la inteligencia artificial para preguntarle cosas.
Y así, pues, nos quitará trabajo.
Y además podemos generar imágenes para mostrarle al usuario cómo ha quedado el PDF y todo esto.
Así que, esto también lo vamos a hacer hoy, que me parece muy interesante.
De hecho, os he dejado el comando de Cloudinary, por si lo queréis.
En este caso, sí, Cloudinary sí que me patrocina, ¿vale?
Sí que me patrocina, porque lo digo, porque entonces ahora, no, tienes que...
Pues sí me patrocina, Cloudinary sí que me patrocina.
Patrocinó el Adven.js, y por eso, pues, lo estamos utilizando hoy, ¿vale?
Si queréis ponerlo de Ad, lo podéis poner.
Pero bueno, también es un servicio que ya sabéis que a mí me gusta mucho,
y que por eso lo hacemos.
De hecho, de hecho, es verdad, mira, es que me estoy acordando ahora de una cosa.
Tenemos una cosa de Cloudinary, tenemos una cosa de Cloudinary.
Sí, sí, sí.
El caso es que le comenté que iba a hacer esto hoy...
Ah, amigos, mira, os voy a poner una cosa.
Sí, el caso es que le comenté que íbamos a hacer esto hoy de subir el PDF,
y el tema es que Cloudinary ya sabéis que podéis empezar gratis,
pero va por créditos, ¿vale?
Hostia, yo no sé cuál es el que tengo.
A ver, un momento, no sé si es...
Yo creo que es GitHub, yo creo que es GitHub.
El tema, que me han dicho que podéis conseguir crédito gratis si iniciáis con un formulario.
Muy fácil, con un formulario.
Vale, ya veo que sí que es esta la cuenta.
Vale, con este enlace, y que además me ha dicho,
oye, pues hacemos un sorteo y vamos a estar sorteando 50 dólares,
si no me equivoco, ¿cuánto eran?
Eran tres cupones de 50 dólares para todo el mundo que pida los créditos.
Entonces, si tenéis una cuenta de Cloudinary, pues pedid...
Mira, ponéis aquí vuestro correo, vuestro CloudName, que el CloudName, si no me equivoco, es...
Lo tenéis aquí, ¿ves? CloudName, el mío es Midudeb.
Pues le pasáis el CloudName, ponéis aquí vuestro correo y el CloudName,
y os dan créditos gratis, y además entráis a un sorteo de 50 dólares.
Y hay tres en total disponibles, ¿vale?
Así que nada, ahí tenéis el enlace, lo iremos pasando ahí en el chat para lo que tengáis.
Y así, pues, sorteáis y podéis participar en el sorteo, que está bastante interesante.
Y así tenéis créditos extra para cualquier cosa, para vuestro portfolio, por ejemplo.
A ver, la capa gratuita es bastante generosa, ¿eh? Bastante generosa.
Porque yo, por ejemplo, lo estoy utilizando desde hace un montón de tiempo,
pero hace un montón de tiempo, y no he pagado nada nunca.
A ver, Dashboard...
A ver...
Mira, uno...
Bueno, también es verdad que es que se acaba de reiniciar,
pero siempre me quedo en el 70% y tal, y lo utilizo en mi blog para optimizar las imágenes.
Y la verdad es que nunca, nunca he tenido que pagar absolutamente nada.
Va muy bien, muy bien.
Pues eso vamos a hacer, ¿vale?
Subiremos el PDF ahí, inteligencia artificial y tal.
Creo que va a estar divertido.
Sí, la verdad es que la herramienta de Cloudinary me parece increíble.
¿Para qué es Cloudinary?
Es una plataforma para subir imágenes y vídeos y también documentos.
Y lo interesante que tiene Cloudinary, ¿vale?
Es que puedes hacer transformaciones, ¿ves?
Puedes decir, una vez que subes la imagen,
también puedes subir, o sea, puedes quitarle el fondo con inteligencia artificial.
Puedes eliminar incluso objetos, ¿ves?
Y esto lo haces o con la API o con la URL, lo cual está muy chulo.
Tienes temas de inteligencia artificial, optimización de imágenes, por ejemplo.
Optimización de imágenes.
Tú le pones en la URL, pues, estos campos, ¿vale?
Este sería la imagen original y le dices, vale, quiero que optimices la imagen y no sé qué.
Y de 18 megas te lo dejan 135.
Y además está chulo.
Una cosa que está muy bien es que te pone, o sea, siempre, si tú utilizas la imagen de Cloudinary,
utiliza el formato de la imagen que mejor funcione con el navegador.
Mira, por ejemplo, si esta imagen está en Cloudinary, si vamos al network, ¿vale?
Vamos al network, ¿veis aquí que pone jpeg?
Claro, no sé si es porque me habrá puesto jpeg, a ver, o me lo ha cambiado y me ha puesto otro.
¿Veis? Mira, aunque tú estás pidiendo un jpeg, a mí me está llegando un aviv.
Pero si esta imagen la recuperas, no sé si Safari tendrá aviv.
Yo creo que no.
¿Ves? Si esta imagen tú la intentas pedir con Safari, seguramente...
¿Ves que aquí hay otro que pone JP2?
O sea, la ha optimizado con otro formato.
Es que automáticamente, de forma inteligente, pues lo que está haciendo es transformar la imagen al formato que soporta el navegador.
Eso está muy chulo.
Porque así no te tienes que preocupar tú de tener que servir diferentes imágenes y lo que hace automáticamente.
Bueno, pues eso es lo que vamos a hacer.
La verdad, un producto lo vamos a hacer de código abierto para que podáis participar y todo esto.
Pero está muy chulo porque además de que vamos a aprender un montón, lo que está genial es que creo que para el portfolio puede estar bastante chulo.
Lo vamos a hacer con Astro y con Svelte.
Pero no os preocupéis porque es que de las dos cosas es bastante fácil.
Lo vamos a hacer con Svelte porque me apetece.
Porque me apetece.
Siempre lo hacemos con otras cosas.
Vamos a utilizar Svelte que siempre utilizamos y hace mucho, mucho, mucho tiempo que no utilizamos Svelte.
Y es que es tan fácil Svelte.
Y es tan fácil.
Y de la parte de la inteligencia artificial vamos a intentar utilizar...
Vamos a utilizar primero OpenAI.
Pero luego te voy a...
Mira, hostia.
Os voy a enseñar una cosa hoy muy chula.
Es que de verdad que vale la pena.
Porque no sé si os sabéis esto de Oyama.
Oyama, RAM, Yama 2.
Esto de que podéis tener una inteligencia artificial local en vuestro ordenador.
Entonces, mira, Oyama, RAM, Yama 2.
Por ejemplo, podéis preguntar, ¿qué es JavaScript?
Pues vamos a intentar utilizar Oyama también de forma local a ver qué resultados nos da.
Que es bastante interesante.
Y entonces con esto, ¿veis?
Esta inteligencia artificial también podemos utilizarla la API.
De hecho, esto lo podéis desplegar a un servidor y tendríais una inteligencia artificial gratuita, entre comillas.
¿Entendéis?
Está bastante chula.
Y como han sacado una biblioteca ahora de Node, vamos a ver cómo lo podríamos hacer.
¿Vale?
Así que nada.
Ahora vamos a utilizar esto.
Vamos a Create Astro, Latest.
Y vamos a por ellas, ¿eh?
Midu, ¿por qué no usas Linux?
Porque no me da la gana.
Porque me da la gana utilizar Macos.
Pero es como la gente que utiliza Windows.
¿Por qué utiliza Windows?
Porque le da la gana también.
Así que esa es la razón, básicamente.
No sé, me gusta Macos.
Y no me gusta tanto Linux.
Es así de sencillo.
Pero respeto y entiendo a la gente que utiliza Linux, ¿eh?
No hay ningún tipo de problema.
De hecho, yo utilicé Linux durante muchos años, ¿eh?
Durante mucho, mucho tiempo.
Cuando en los inicios de Ubuntu, que te enviaban el CD a casa, yo lo utilicé.
Y también he utilizado Windows.
De hecho, los he utilizado todos.
Nunca he tenido ningún problema con ninguno.
Pero en los últimos 8 o 10 años he utilizado Macos.
Bueno, porque me gusta el hardware y también me gusta el software.
¿Y de distro favorita?
Pues lo era Ubuntu.
Me gustaba mucho Ubuntu.
Pero llegó un momento que Ubuntu, no sé, como que me terminó...
Me terminó un poco...
No sé si de aburrir muchos problemas con las tarjetas gráficas, especialmente.
Pensad que yo era bastante gamer, ¿sabes?
Entonces me daba un poquito de la villa el tema de tener Windows y Ubuntu.
Era un poco rollo.
Y entonces ahí es cuando me pasé a Windows, la verdad.
Pero igualmente creo que ha mejorado mucho Linux.
Y a mí la que me gusta mucho ahora es Elementario S.
Sé que no es la típica versión de Linux, la más, no sé, purista.
Pero se la instalé a mi padre porque tiene un ordenador antiguo y tal.
Sé que se parece un poco a Macos, pero me gusta mucho, la verdad.
Me gusta un montón porque me gusta mucho el diseño, la simplicidad...
Me encanta, me encanta.
O sea, me gustó un montón.
Me pareció uno de esos Linux que dices, ostras, qué maravilla.
Qué maravilla que está tan bien trabajado.
Que sí, que es una copia Macos en muchos aspectos.
Pero me gustó y mi padre está contentísimo.
O sea que nada.
Pero bueno, que al final de sabores de Linux hay tantos que no me voy a meter con ninguno.
Vale, voy a utilizar PNPM, que no sé por qué he utilizado NPM antes.
¿Tu papá con Linux?
Sí, sí, sí.
Bueno, es que a él le da igual.
Es lo que quiere es un botón para abrir el navegador y ya está.
Eso es lo que él quiere.
Con eso ya va sobrado.
Va sobrado.
Vale, pues venga, vamos a abrir el chat with GPT.
Bueno, con PDF.
Y lo que sí que vamos a utilizar en este, porque es que si no, no me da la vida, es Tailwind.
¿Vale?
Porque claro, es que si no, si tengo que hacerlo también con CSS, mucha gente me decía CSS, CSS.
Bueno, vamos a hacer como un layout inicial, ¿vale?
Para lo que quiero hacer hoy.
Vamos a irnos, mira, aquí en layout mismo.
En layout.
Bueno, en layout no.
En el index.astro.
Ah, os he dicho que os iba a enseñar unas cosas chulas de Visual Studio Code.
Os voy a enseñar unos truquillos de Visual Studio Code muy chulos.
Como por ejemplo, que se puede manejar ahora con la voz.
Mira, por ejemplo, imagínate.
Ok, Code.
Me he ignorado.
Inline Chat.
A ver, ¿por qué me estás ignorando?
Si antes me la...
A ver, Speech.
A ver si es que tengo que instalar.
Speech.
¿Es este?
Cambiar la versión de lanzamiento.
¿Por qué?
Ah, A-Code.
Vale.
A-Code.
Tampoco.
Hostia, me ha dejado fatal.
Me ha dejado fatal.
Me ha dejado fatal, ¿eh?
Se ha ido a tomar por saco.
¿Ves?
Que pone escuchando A-Code.
No sé por qué ha dejado de funcionar.
No sé si puede ser que esté en beta o a lo mejor puede ser porque estoy streameando
y a lo mejor se le va la olla.
Bueno, es una pena porque os juro que es increíble.
Tú estás diciendo ahí A-Code y entonces de repente aparece aquí como una cosa
y lo puedes configurar.
¿Ves?
Es que puedes configurar que te aparezca el chat en línea, en contexto, en no sé qué.
Y entonces te aparece esto.
Te aparece esto de aquí.
Y aquí le puedes decir Remove all the content from the tag main.
O sea, eso sí que funciona.
¿Vale?
Y veis, está eliminando.
Y entonces pues nada, le das y ya está.
Es raro porque antes sí que me ha funcionado.
Y ahora sí que te entiende bastante, bastante bien, ¿eh?
Ves, aceptar.
Bueno, y ahora se supone que me ha eliminado lo del main.
O sea, que le puedes decir...
¿Será que entienda mi inglés?
Sí, se lo tienes que decir en inglés, ¿eh?
Porque en español no entiendo una mierda.
Pero está bastante bien.
O sea, está bastante bien.
Es una pena que no sé por qué no funciona.
Ya os juro que me ha funcionado antes, ¿eh?
Os lo juro que me funcionaba, ¿eh?
Lo funcionaba.
Para hacerlo funcionar necesitáis Visual Studio Code Speech y también tener GitHub Pilot.
Si tenéis los dos, os funciona.
¿Eso reconoce cualquier voz?
Sí, sí.
Bueno, a ver, lo que pasa es que tiene que ser del micrófono, no que esté aquí, ¿eh?
En fin, bueno, pues nada.
Luego os enseñaré más cosas porque hay más trucos, hay más trucos.
Es que la verdad, en mi poesía funcionaba bien.
Bueno, este es mi PC, ¿vale?
O sea, que no estoy utilizando un PC diferente ni nada.
Vale, venga, vamos por partes.
Vamos a llamarle aquí chat with PDF.
Voy a utilizar uno de estos backgrounds de UI Bailik.
Nunca me acuerdo cómo se escribe.
Ah, mira, Ibelik.
Ahí está, Ibelik.
Ya está, ya está.
Uy, Ibelik.
Vale, este, este.
Pues este hombre tiene como unos fondos.
Vamos a utilizar uno de los top fondos.
Backgrounds.
Por cierto, os quería comentar una cosa.
Bueno, es que tengo muchas cosas que comentaros, ¿eh?
No sé cuál utilizar.
A ver, este.
Este se ve bonito.
Vamos a utilizar este.
Pues os iba a comentar una cosa porque...
A ver cómo queda esto.
Vale, levantamos el servidor.
PNPM RANDEP.
Y empezamos a darle cañita aquí.
¿Vale?
Os iba a comentar una cosa.
Os interesaría...
Tenéis que poner muchos sí en el chat si os interesa, ¿vale?
Pero os interesaría, por curiosidad,
si hacemos un plugin de Tailwind desde cero.
Todos juntos.
Un plugin de Tailwind desde cero.
Es que el Belik este, Belik, tiene unas...
Sí, sí, sí, sí.
Podríamos hacer un plugin de Tailwind desde cero.
Es que tiene aquí una cosa muy interesante, que son unas animaciones de Tailwind.
A ver, tú, tú, tú, tú.
Animation.
¿Ves?
Estas animaciones de Tailwind.
Y he pensado...
Buah, ¿sabes lo que estaría chulo?
Que hiciésemos un plugin para hacerlo desde cero,
donde pudieses utilizar todas y cada una de estas animaciones.
¿Sabes?
Fácilmente.
Que pongas las clases...
Sé que ya existe alguna, pero ¿qué es lo que pasa?
Que he visto, y algunos plugins que hay de animaciones,
o no me gusta mucho la página web, que es como horrible, ¿vale?
O le faltan animaciones.
Y es que esta tiene un montón de animaciones.
Esta tiene un montón, pero un montón de animaciones.
Entonces, me gustaría hacer un plugin añadiendo este.
¿Sabes?
Añadiendo todas estas animaciones, porque están bastante chulas.
Así que no sé si os interesa, porque veis,
si no, habría que hacer esto y copiarlo a mano.
Y yo creo que con un plugin quedaría increíble.
Pues si queréis, pues igual lo hacemos la semana que viene,
si os parece.
Y hacemos un plugin de Tailwind desde cero.
Para ver cómo se hace, lo hacemos de código abierto,
y además lo podéis mejorar con vuestras ideas y todo esto.
Vale.
Vamos a poner aquí grid, place content center,
que solo ocupe el screen.
Esto, que sea el screen.
Y vamos a poner un h1, chat with your pdf.
¿Vale?
Aquí lo hacemos un poquito grande, con el opacity un poquito así.
Ay, ay, ay, ay, que se me escapa.
Se me escapa.
¿Vale?
Le ponemos un poquito de separación, algo así.
Y aquí es donde vamos a poner ya la aplicación que vamos a hacer con Svelte en un momentito.
De hecho, vamos a crear ya aquí app.svelte.
¿Sabéis una cosa que he pensado de Astro que sería increíble?
Que detectase automáticamente cuando creas un archivo con un formato,
o sea, .svelte, .jsx, .lo que sea,
y te instalase automáticamente la integración.
Eso sería increíble.
Sería chulísimo.
En este caso vamos a utilizar Svelte, que es muy sencillo.
Lo vas a ver.
Si sabes React, Angular o lo que sea, es que va a ser un paseo.
Va a ser un paseo.
En este caso, para añadir soporte a Svelte en Astro,
pues nada, pnpxastro.at.svelte.
Y esto nos va a hacer la instalación que necesitamos.
De hecho, voy a eliminar el package lock para que,
a ver si me hace la instalación, ¿ves?
Otra vez.
Me está intentando hacer la instalación con npm.
Es por el package lock.
A ver, lo ejecuto otra vez y ahora me intentará hacer la instalación correctamente.
Y si no sé nada de eso, no paseo, da igual.
Si sabes HTML, CSS y tal, no te va a costar nada.
Es que Svelte es muy, muy fácil.
Es que si cometes un error a escribir, te instala de un algo que no necesitas.
Es un poco doble filo.
Nada, tienes razón.
He dicho que molaría mucho, ¿eh?
¿Por qué usas Astro?
Lo he explicado tantas veces, J. Barceló.
Que es que no podría explicarte una vez más.
¿Por qué?
Pero básicamente para mí es lo mejor que le ha pasado al desarrollo web en muchísimo tiempo.
Y porque es que te simplifica mucho el trabajo.
Mucho, mucho, mucho, mucho.
Mucho.
Ya en esto nos ha creado un entorno de desarrollo en muy poco tiempo
que sin tener que configurar casi que nada,
ya nos funciona Tailwind y Svelte.
O sea, es que a mí me parece maravilloso, sinceramente.
Vale, para Svelte, que lo teníamos aquí, vamos a crear aquí app.
Y ahora añadimos esto y esto lo copiamos aquí y lo importamos.
Vamos a importar la aplicación from components barra app.svelte.
Y está chulo porque además pueden mezclar Svelte con React, con Solid, con lo que te dé la gana.
Vale, pues vamos a ver.
Vale, ya tenemos aquí ChatWidget PDF y aquí tendríamos la aplicación.
Yo para no perder mucho tiempo con los estilos, porque si no vamos hasta aquí todo el día,
voy a utilizar Flowbyte Svelte, que es una librería de componentes de UI que está basada en Flowbyte,
que lo tenéis también para React, para Angular, para Vue, para un montón de cosas.
Me encanta.
A mí Flowbyte me encanta.
Incluso para HTML, porque es de copiar y pegar.
Está basado en Tailwind, eso sí.
Pero ya os digo que, mira, lo tenéis para React, Next.js, Vue, Naxx, Svelte, Angular, Astro, Remix, Meteor, Gatsby.
O sea, lo que os dé la gana.
Lo que os dé la gana.
Mira, hasta Blazor.
Ahí lo tenéis, hasta para Blazor.
Y en Svelte, pues también está.
Lo único que tenéis que hacer es ejecutar este comando de aquí, que vamos a ponerlo por acá,
para instalar las dependencias.
Y una vez que tengáis las dependencias, hay que cambiar unas cositas de la configuración,
que de hecho lo ponen por aquí, ¿vale?
Hay que instalar Tailwind, que ya lo tenemos.
Y veis, aquí esta configuración es la que habría que añadir.
Hay que tener cuidado, porque si miramos la configuración de Tailwind,
claro, aquí no tiene el archivo Astro.
Entonces la liaríamos parda.
Así que voy a copiar solo lo que nos interesa, ¿vale?
Que sería básicamente esta parte de aquí.
Entonces, aquí voy a copiar esta parte.
Y con esto ya estaría.
Voy a hacer esto para que se vea un poquito mejor.
Pero básicamente lo que se añade es que también compile los archivos del NodeModules de Flowbyte.
Y las extensiones que tiene aquí.
Utiliza el plugin para añadir un poquito de JavaScript en alguna cosa.
Esto del Dark Mode no lo vamos a utilizar, o sea que es indiferente, pero bueno, lo vamos a dejar.
Y luego aquí tendríamos el tema de los colores primarios.
Y ya está.
Así que con esto ya podríamos ir a los componentes, a la alerta y por ejemplo utilizar este en nuestro componente de Svelte.
Nos vamos para aquí y aquí ya utilizando este script podemos utilizar esta alerta, ¿vale?
Vamos a poner esta alerta y ya está.
Fijaos que Svelte, si no lo has visto nunca, es muy fácil porque parece HTML.
Aquí pondría la parte de los scripts, aquí tienes lo que se renderiza y la parte de los estilos lo pondríamos en style.
Pero como vamos a utilizar Tailwind, pues no hace falta.
Y luego vas a ver un poquito de sintaxis, pero es que es muy sencillo Svelte.
Es lo bueno, es que es muy sencillo, ¿vale?
Pues vale, mira, ya tenemos ahí la alerta, ya tenemos nuestro primer componente.
Así que nada, continuamos.
Muy View.
Sí, se parece mucho a los single file components de View, lo cual está súper bien, ¿eh?
Está muy, muy, muy, muy bien.
ShadCN o Flowbyte, ¿cuál te gusta más?
Ah, a mí me gusta más.
O sea, yo soy consciente que ShadCN tiene más popularidad y seguramente tiene más éxito.
Ojo, Mr. LOL, ¿vas a lanzar este chat con...?
Ah, no.
Dice, Midu, cuéntanos qué vienes a hacer a Colombia.
Danos un anticipo.
No os puedo dar un anticipo porque no puedo confirmar dónde voy, porque obviamente quieren guardar la sorpresa, ¿vale?
Pero en cuanto pueda, os lo diré, no os lo preocupéis.
Lo único que os puedo confirmar es que este año voy a viajar a Colombia al 99%.
Y Mr. LOL dice, ¿va a lanzar este chat como producto open source?
Como open source.
No lo vamos a hacer como producto porque en dos horas sería muy difícil.
Me debería dar todavía más tiempo para que sea un producto en condiciones, ¿vale?
¿Por qué no usas RIAG?
Venga, otra pregunta.
¿Esto qué es?
Que cada vez que no uso algo me vais a preguntar, ¿por qué no usas tal?
Y cuando lo uso es, ¿por qué lo uso?
¡Ay, Dios!
Pues, ¿por qué no uso RIAG?
Porque siempre uso RIAG.
Porque siempre uso RIAG, joder.
Entonces, ¿por un día que no usas RIAG, por qué me vais a preguntar eso?
¿Por qué no usas gafas también?
A ver, las gafas.
Ah, me las he dejado.
Pues nada, no uso gafas por eso.
En fin.
Venga, vamos a darle cañita antes de que me sigáis preguntando por qué no uso X.
Creamos la app.
Ya tenemos aquí la app.
Ya estamos renderizándola.
Lo que voy a hacer, vamos a hacer como un formulario en diferentes pasos, ¿vale?
Que tenga más de un paso.
El primer paso que sea arrastrar el archivo.
Es verdad que vamos a ver drag and drop también.
Es que estaba chulo, ¿eh?
Tiene cositas interesantes.
Entonces, voy a crear una cosa muy chula, muy interesante de Svelte, que serían las stores.
Tienes el concepto de store, sería como un estado global, ¿vale?
Store.ts.
Y lo bueno del estado global que tienes en Svelte es que lo puedes leer desde cualquier sitio y lo tienes en un archivo, lo exportas y ya está.
Es así de fácil.
De hecho, ¿ves?
Este ejemplo es así.
Con este export const count writable al cero, esto lo exportas y ya lo puedes importar y ya tienes el estado global en todos los sitios.
Y le cambias el valor y entonces se renderiza automáticamente los componentes.
Nosotros lo vamos a utilizar como para tener el estado de nuestra aplicación.
Primero lo vamos a tener con el estado inicial, luego va a estar el estado de carga cuando va a estar cargando el PDF y subiéndolo, luego vamos a estar en el modo chat, ¿vale?
Y luego si hay un error, pues le vamos a poner esto.
Esto de los números es lo importante es que sean todos diferentes, ¿vale?
No tiene ningún significado así que sea importante.
Vamos a tener aquí, ¿vale?
El app status inicial que va a ser el init y lo que vamos a hacer con esto es que dependiendo de qué estado está,
O sea, si estamos en inicial, pues mostramos un componente.
Si estamos en loading, mostramos otro, ¿vale?
Esa es un poco la idea.
Por ejemplo, si vamos aquí y vamos a importar el app status que acabo de crear,
no sé por qué no me lo pilla, porque lo estoy exportando, ¿verdad?
App status.
A veces se le va a esto y me da un poco de rabia que no me pille las cosas automáticamente.
Pero app status y app status, ¿vale?
Puedes pillar también el estado.
Entonces aquí, pues ya vamos a ver cómo funcionan en Svelte los renderizados condicionales de toda la vida.
Por ejemplo, vamos a poner si el app status, ya te explicó el dólar, ¿vale?
Es igual app status.init.
Entonces, aquí te cerramos el if y esto es la página inicial.
Inicial, ¿vale?
Entonces así todo el rato, en lugar de cerrar aquí podríamos poner el shift.
Si entonces, si en lugar el app status aquí es que está cargando, ¿vale?
Pues aquí vamos a poner loading.
Oye, Gijacopilot, ayúdame.
Vale, muy bien.
Muy bien, Gijacopilot.
Entonces, en el error vamos a poner esto, ¿vale?
Vamos a poner el alert este que hemos puesto por aquí.
Vamos a poner algo ha pasado.
Algo malo ha pasado.
Hay un error en la aplicación.
Bueno, luego ya lo arreglaremos, ¿eh?
Pero es básicamente para que tengamos...
Y este también.
Vamos a poner aquí este.
¿Vale?
Para que básicamente si...
Estado o acción no reconocida.
En el caso de que alguien intente entrar por lo que sea, ¿eh?
Este estado de la aplicación no está reconocido.
Que sería como el estado básico, ¿no?
Entonces, aquí tendríamos la página inicial, el loading y este sería, más que success, este sería el chat mode.
Chat mode.
Esto vamos a poner aquí chat mode.
Y aquí chat mode.
¿Vale?
Vale.
Entonces, lo único que te tengo que explicar de esto, que te puede volar un poco la cabeza, es este dólar.
¿Ves este dólar?
Esto básicamente es porque en Svelte, y es la única magia así rara que vas a ver aquí, porque el if se entiende muy fácil,
es que tienes que ponerle un dólar a las stores cuando quieres observar su valor.
¿Por qué?
Porque en realidad este app status no tiene directamente el valor, sino que es un observable.
Y tienes como que suscribirte para ver cuando el valor cambia.
Y esto lo hace automáticamente por ti.
¿Vale?
Y ya está.
Esa es la única magia.
Por lo tanto, si quieres acceder al valor de una store, pues le darías así y ya lo tendrías.
¿Vale?
Así que con esto ya lo tendríamos.
Lo que voy a hacer también aquí, pues vamos a tener el steploading.svelte, vamos a tener aquí también el stepupload.svelte,
que es el que vamos a hacer ahora, y el stepchat.svelte.
Entonces, vamos a importar aquí stepupload, que este es el primero que cargaríamos,
que es el que vamos a hacer el drag and drop, el steploading, que este mientras se está cargando,
y este va a ser el stepchat, ¿vale?
Cuando tengamos el chat ya preparado para que funcione.
Y con esto ya tendríamos la página inicial, solo que ahora nos falta esto, ¿vale?
Que es lo que debería aparecer en nuestra página.
Nos falta esto.
A ver, ¿qué tal? ¿Cómo lo lleváis?
¿Todo bien?
¿Todo bien?
¿Qué clase de magia negra es esa?
Bueno, sí, es un poco de magia negra, pero es azúcar sintáctico realmente,
porque si no, lo que tendríais que hacer es muy complicado, porque tendríais que...
Mira, para que os hagáis una idea, ¿por qué se hace lo del dólar?
Porque si no hicieses lo del dólar, mira, en el upload, si no hicieses lo del dólar,
tendrías que hacer algo así.
A appstatus, tendrías que hacer appstatus.subscribe, ¿ves?
Tendrías que suscribirte al valor para recuperar el valor y esto sería un rollazo, esto sería
un rollazo.
Entonces, por eso lo hacen, ¿vale?
Por eso lo hacen, ¿vale?
Vale, perfecto.
Venga, vamos con más cositas.
Vamos con más cositas.
Se tendría que suscribir directamente y manejar la de suscripción.
Claro, y es un rollo, Mario.
Es mucho más fácil así, ¿eh?
Es mucho más fácil el hecho de, en lugar de tener que suscribirte así manualmente,
y además de suscribirte al observable, pues ya te meten este dólar aquí delante,
justamente para que haces esa magia automáticamente.
Y entonces aquí ya tienes el valor.
Lo cual, pues yo creo que tiene bastante sentido, sinceramente, si me preguntan a mí.
Muy bien, venga.
Vamos con el upload.
Mirad, yo voy a utilizar, porque hay muchas cosillas que podríamos utilizar aquí,
pero yo voy a utilizar el Svelte Dropson.
Es una biblioteca que está disponible, este, este, File Dropson.
Es una biblioteca que tiene disponible en React, en Vue, para Vanilla, para cualquiera.
Y os sirve para que arrastréis un archivo y automáticamente es este,
Drag and Dropson File, no sé qué.
Que arrastréis un archivo y automáticamente lo tengáis aquí.
¿Ves?
Yo tengo aquí, por aquí tenía un archivo, un PDF, a ver, sí, pum, que lo arrastras y ya está.
Lo que pasa es que aquí no están actualizando absolutamente nada.
No sé si es porque no se ve, a ver, no, creo que es porque no se ve.
Si no, ahora, a ver, me lo voy a copiar, me lo copio, me voy a copiar el archivo por aquí,
un momento, que no se vean cosas raras.
Es que como he tenido antes la reunión esta, no quiero que se vea algún archivo
que no debería verse para no liar la parda.
A ver, me voy a copiar esto aquí, que es todo confidencial.
Vale, ¿veis?
Así se sube.
Pues lo interesante es que hoy también vamos a ver cómo se sube un archivo a una API.
O sea, que vamos a ver cómo arrastrar el archivo y cómo se subiría a una API.
Vale, pues vamos a utilizar este componente y vais a ver que este,
este componente es bastante sencillito de utilizar.
Está bastante bien porque en un momento, mira, de hecho, es que nos vamos a copiar eso.
Nos vamos a copiar eso porque pnpmi, vale, vamos a instalarlo con esto.
File drop zone y nos vamos a copiar todo esto a ver cómo queda.
Y así pues ya, vale, ahora no sé dónde me ha dejado el tututu, aquí.
Nos vamos a copiar aquí en el upload todo esto.
Y esta parte, vale, esta parte es la del script.
Vamos a poner por aquí porque vamos a editar el app status y todo esto.
Yo creo que lo vamos a editar, al menos una parte.
Vamos a dejar por aquí.
El alert este no lo necesitamos.
Por ahora dejamos esto así, ¿vale?
Y entonces con esto ya lo que tendríamos es básicamente el drop zone.
Vamos a ver si...
Voy a quitar esto que me estaba molestando un poco.
Vamos a ver cómo queda.
Vamos a ver cómo queda.
Ajá, aquí.
Vale, pues ahora.
Mira, fíjate, ahí está.
Ahí está.
Bueno, entonces ya tendríamos aquí lo del drop.
Vale, que ves que aparece ahí el archivo y no hace nada.
Bueno, eso lo vamos a arreglar porque obviamente hay unas cuantas cosas que arreglar.
Lo primero que vamos a arreglar de esto es tener en cuenta aquí el handle file select.
Esto lo que hace es que cuando arrastras, aquí tienes los archivos, los que ha aceptado y los que ha rechazado.
Pero aquí tendríamos que configurar el drop zone un poquito.
Por ejemplo, aquí le podemos cambiar lo que hay dentro.
Le podríamos poner aquí arrastra y suelta aquí tu PDF.
Y con esto, ¿vale?
Arrastra y suelta aquí tu PDF.
Aparte de esto, hay que quitar que pueda utilizar más de un archivo.
Creo que aquí hay, por ejemplo, multiple.
¿Ves?
Multiple, pues esto tiene que ser false para evitar que se arrastre más de uno.
Le tenemos que decir cuál es el tipo de archivo que acepta.
En este caso le vamos a poner el PDF, ¿vale?
¿Y qué más?
Y otra cosa que podríamos hacer es estilarlo un poco.
No me acuerdo si hay un container, un class o algo.
Container class, ¿vale?
Entonces, bueno, a ver.
Yo creo que no lo está estilando, ¿no?
Porque border, dash...
A ver, claro, es que como...
Como JSON...
No, es que yo creo que esto no lo está estilando.
No sé si es que es el de dentro.
Bueno, da igual.
No es importante.
O sea, me da igual.
Los estilos no importan.
Bueno, pues arrastramos.
Tenemos aquí los archivos.
Los archivos aceptados.
Aunque esto lo vamos a quitar ahora.
Y aquí tendríamos los accepted files, ¿vale?
Que esto sería el archivo que nosotros arrastramos aquí.
Ahora, en la consola, deberíamos tener la información del archivo.
¿Veis?
Aquí tenemos la información del archivo.
Que nos dice, pues, el archivo, cuándo se modificó, el nombre, el tamaño, el tipo, la ruta, no sé qué.
Pues este archivo es el que vamos a subir a la API.
Vamos a tener que crear una API y subir este archivo.
Y de ahí lo subiremos a Cloudinary.
Entonces, para subir el archivo.
Lo primero es que...
A ver, files accepted is rejected.
Por ahora esto yo creo que lo podemos ignorar, básicamente.
Vamos a poner accepted files.length.
Vale.
Lo primero es que si tenemos archivos aceptados, vamos a cambiar el estado de la store, ¿no?
Porque nuestra aplicación va a pasar a estar en carga.
Entonces, aquí podemos exportar métodos, también, setAppStatusLoading, por ejemplo.
Y esto directamente que haga que el appStatus cambie al estado de carga.
Este método nos lo traemos para aquí.
Y así, cuando vayamos a subir el archivo, pues cambiamos el estado a que estamos cargando.
Y no hace falta que nos traigamos, ¿sabes?
Llamando directamente esto, ya cambia el estado.
De hecho, vamos a ver que esto debería funcionar ya, ¿eh?
Esto ya debería funcionar.
Vale, no sale nada porque no soy muy listo yo y el step loading este lo he dejado vacío.
Pero vamos a irnos a Flowbyte Svelte y vamos a buscar un spinner en un momento que básicamente nos ayude a hacer que eso quede mejor.
A ver, Svelte, Spinner.
Este está perfecto.
Este está perfecto.
Así que hacemos Deep, el Spinner.
Tenemos que importar el Spinner, obviamente.
Import, Spinner.
¡Madre mía!
Estoy acostumbrado a Astro.
Estoy acostumbrado a Astro.
Y he utilizado ahí la... como si fuese Astro.
Svelte...
Hostia, ¿cómo es el...?
¿Cómo se tiene que importar?
Flowbyte Svelte.
Flowbyte Svelte, eso.
Flowbyte Svelte, ¿vale?
Y ya con esto tenemos el Spinner.
Vamos a poner aquí también Flex, Justify Center, Item Center...
Para poner básicamente aquí también un cargando, yo que sé.
Bueno, estamos subiendo, subiendo el archivo y extrayendo el texto, ¿vale?
Entonces ya con esto al menos deberíamos ver...
Vale, ya se ve esto.
Vale, lo arreglamos un poquito porque esto tendría que ser así.
Y ya está, ¿vale?
Y esto ya es para nota, para nota, ¿vale?
Venga, algo así.
Vale, entonces ya tendríamos el loading.
Perfecto, venga.
Necesitamos la API.
Aquí está lo interesante, ¿no?
Cómo subir un archivo.
La verdad es que esto es una cosa que no sé por qué muchas veces nos cuesta mucho.
El cómo subir un archivo a una API.
A la gente se vuelve loca con esto.
Y lo importante que tienes que saber es que hay muchas formas de hacerlo.
Pero hay unas que no te van a fallar nunca, ¿vale?
Una de las formas de hacerlo es básicamente con el FormData.
Y con el FormData es que muy poca...
Si tienes un archivo del objeto file, que es lo que tenemos justamente al arrastrar, o con un input, esto yo diría que es siempre lo mejor.
Crear un FormData.
Tú creas un FormData.
FormData.
¿Vale?
Esto es algo...
No es de Svel ni nada.
Esto es JavaScript.
JavaScript.
Esto es JavaScript, ¿vale?
JavaScript nativo.
Vale.
Y ahora que tienes el FormData, le pones un append y le pones el file.
Y aquí le vamos a poner el archivo que justamente hemos puesto, ¿vale?
Ya está.
Ya está.
Ya tienes el archivo.
Ya te lo dice hasta ahí Jacob Pailo.
Ya dice, bueno, pues ya que tengo el archivo, hago todo esto.
Bueno, pues no lo voy a hacer así.
No me da la gana.
Vamos a hacerlo de otra forma.
Vamos a llamar a la API que vamos a crear ahora.
Eso.
API Upload.
FormData.
Y aquí...
Esto lo vamos a guardar en el response.
Y esto tenemos que hacer que sea asíncrono para poder utilizar el awaitai.
Y aquí diremos, oye, si la respuesta no está bien, ¿vale?
Entonces aquí hacemos el app status.
Vamos a cambiar en la store.
Store.
Y aquí, pues otro.
Export con set app status error.
¿Vale?
App status punto set error.
Muy bien.
Lo hacemos aquí.
Y así podremos cambiar también el estado aquí del status error.
Pues ponemos aquí.
Ha habido un error.
Y hacemos un return para que no se vaya ejecutando.
Y si todo ha ido bien, pues aquí tendríamos el resultado.
Esperamos.
Miramos el JSON.
Esto es lo típico de un fetch.
Tendríamos aquí el JSON.
¿Vale?
No sé.
¿Se ve bien la letra o está muy chiquitita?
Si está muy chiquitita, la hago más grande.
Un formdata no falla.
Totalmente.
Es que un formdata no falla nunca.
Nunca.
Y es, yo creo que de lo más fácil.
¿Se ve bien?
Vale.
Gracias, amigos.
Esto, claro, aquí dependerá el resultado.
Todavía no sé qué va a tener resultado.
Pero seguro que aquí deberíamos cambiar, en lugar de loading, al chat mode.
¿Vale?
Y aquí le pasaríamos el resultado.
Ya luego vemos lo que tenemos, ¿eh?
Pero por ahora vamos a tener aquí una export con setStatus, chat mode, appStatus.set, chat mode.
Esto, ¿por qué lo hago así?
Esto lo hago así, básicamente, para no tener que exportar.
Aunque aquí le he puesto a exportar.
Pero básicamente para no tener que pasarle por parámetro fuera de la store, tener que pasarle por parámetro esto.
Porque me parece innecesario y ya está, ¿vale?
Y aquí, como no sé todavía lo que llega, pues ya luego veremos, ¿vale?
Por ahora voy a poner un any y luego ya nos preocupamos.
Por ahora, al menos, ya tenemos lo del upload.
Ya tendríamos la parte del cliente.
Y ahora necesitamos hacer la parte del servidor, ¿vale?
Y la parte del servidor, a ver, en principio no debería tener mucha historia.
No sé si poner aquí un error.
O sea, por ejemplo, esto.
Esto voy a hacer que eso luego aparezca.
Si accepted files.
Aquí había puesto files.accepted.
Vale, si files.accepted es .length es igual a cero, entonces mostramos el drop zone.
Es que no tiene mucho sentido mostrarlo.
Que va a cambiar igualmente, ¿eh?
Pero bueno, básicamente para asegurarnos.
Y también tendríamos que tener alguna forma de si hay un error o lo que sea, ¿vale?
Vale, por ahora yo lo que voy a hacer es que la API, la vamos a hacer aquí en las páginas, en pages.
Tendríamos que tener una API, una carpeta API, que no me funciona ahora, ¿vale?
API y vamos a poner upload.ts.
Entonces, el upload.ts, ¿cómo se hace en endpoints?
Esto es una cosa de astro.
¿Cómo se hace en endpoints?
Básicamente, tenemos que exportar una constante con el verbo que queremos utilizar.
En este caso, es el verbo post.
En el caso de que querías post.
En el caso de que sea get, pues tendrías que poner get.
Y aquí vamos a utilizar el API root para que lo tengamos totalmente contipado, ¿vale?
Esto no está bien.
No sé por qué siempre Gijacopay lo pone esto, pero esto no es así.
Esto es...
Esto es así.
¿Así? Sí, eso es así.
¿Vale?
Entonces, aquí tendríamos que devolver una respuesta que puede ser Hello World.
Y ya tendríamos nuestra API creada.
Así de fácil.
Ya tenemos una API creada.
Aquí lo que podemos hacer para simular por ahora esto, ¿vale?
Sería...
A ver, para que veáis cómo funcionaría lo de formdata.
Aquí el formdata lo recuperamos así de fácil.
Y esto, esto es JavaScript, ¿vale?
Esto funciona en todos los sitios.
En Node, en Dino, en Next.js, en todos los sitios.
Debería funcionar esto, ¿vale?
De la request, tú puedes recuperar el formdata, ¿vale?
Y con esto ya lo tendríamos.
Con esto hemos recuperado los datos del formulario, pero todavía no tenemos el archivo.
Para recuperar el archivo, hacemos lo contrario que hemos hecho antes.
Del formdata, quitamos y recuperamos el file.
Aquí podéis forzar si queréis lo del file.
Bueno, no sé si será necesario.
Pero ahora lo miraremos.
Y voy a hacer un console.log solo para que veáis cómo funciona.
Y para que esto...
Simulate delay.
Para que se vea el loading un rato y que luego cambie la página.
Vamos a simular aquí un delay de 3 segundos.
¿Vale?
Y a ver cómo...
A ver este console.log.
A ver si está funcionando la API y todo esto.
Vamos poco a poco.
¿Vale?
Arrastramos.
Algo malo ha pasado.
No puede pasear el contenido como formdata.
Formdata.
Me ha dejado fatal.
Me ha dejado fatal porque yo aquí diciendo...
Yo aquí diciendo todo el rato.
No, que el formdata es la hostia.
Que es súper bueno.
Que no sé qué.
Y va...
Y lo primero que me hace es en toda la cara.
Vale.
A ver.
Yo creo que aquí puede ser que esté mal esto.
Que el formdata aquí...
Con el método post...
Claro.
Puede ser...
No.
Pues está bien, ¿no?
Si no tenemos que hacer nada.
Formdata.
Accepted file.
Cero.
Esto está bien.
No sé.
¿Dónde me he equivocado?
El content type.
No.
El content type está bien.
O sea, el content type por defecto debería ser este.
El post.
Formdata.
O sea, esto está bien.
El formdate nos ha fallado.
No, el date no.
Formdata.
Formdata.
Upload.
Formdata.
Request.
A ver si hay que lo...
No tienes que convertir...
No.
No hay que convertirlo.
El course.
No.
El course.
Qué ratas.
No, no, no.
El course no.
El course no.
No, no, no.
A ver.
Esto no tiene sentido.
Vamos a restar esto otra vez.
Vale.
Request.
Formdata.
Como no pass content as formdata.
Esto no puede ser.
Estoy seguro que esto funcionaba.
El div no está bien centrado.
Parece.
Creo que debería ser el content type.
La otra vez me pasó que tenía application json.
Ya, pero en este caso...
Ay, no.
No, no, no.
Iba a decir.
Es el return, no sé qué, pero no es.
Clona la request.
¿Creéis que hay que clonar la request?
Hostia.
Me está ahí dejando patidifuso, ¿eh?
No creo, ¿eh?
Pero bueno.
No sé.
Puede ser.
Yo creo que no.
Yo creo que no hay que hacer un clon de la request.
No.
No, no, no, no.
Hostia.
Me ha dejado, me ha dejado loco esto, ¿eh?
Formdata.
A ver, entiendo que el problema está aquí, en la request.
En la request.
Joder.
Menos mal que os he dicho.
Esto no falla nunca porque no sé qué.
Formdata.
Appen.
Esto está bien.
Vamos a ver un momento en el network qué es lo que le está pasando esto.
Vamos a ver.
Bueno, está guay tener errores para ver en tiempo real.
International error.
File binary.
Pues está bien.
To turn from data name file file.
A ver, y le estoy pasando uno, ¿no?
Sí, o sea, esto está bien.
Y aquí estoy haciendo el get, ¿vale?
Y aquí estoy haciendo la pen.
Pues que está bien, no tiene mucha historia.
Que eso me está escapando.
Se me tiene que estar escapando una tontería.
A ver, os voy a leer.
Imprime accepted files.
Tienen razón.
Voy a imprimir el accepted files este.
A ver si es que no.
No, pero es que lo hemos imprimido.
Antes estaba bien, ¿no?
La request viene en el body.
No, pero es que en el body.
Es que no hace falta hacerlo en el body.
Es que, a ver.
No es body.formdata.
¿Ves que no existe?
Pero aquí sí que existe el formdata.
Que directamente esto te vuelve con el await.
Y esto debería funcionar.
Es que no entiendo por qué no funciona.
Vamos a ver una cosa.
Porque el hecho de tenerlo aquí en la terminal.
Vamos a poner esto por aquí.
Vamos a poner esto por acá.
Y a ver si aquí, a lo mejor hay algo de la consola de la terminal que me está dejando patidifuso.
Vale.
Vamos a ver otra vez el error.
Vale.
Sí, aquí tiene.
No puede parsear como formdata.
Dice...
Ah, coño de la madre.
Ya sé el error.
Ya sé cuál es el problema.
Ya sé cuál es el problema, amigos.
Ya sé cuál es el problema.
Pero me encanta.
Me encanta.
¿Ves?
Conchale.
Coño la madre, me.
El tema es que estamos en modo estático en Astro.
Yo creo que...
Bueno, quiero pensar que ese es el problema.
Aquí nos falta el output que sea server, creo, ¿no?
Claro.
Porque si no se pone de estilo servidor, no puedes tener endpoints.
Creo que es eso.
Creo que es eso.
Vamos a verlo.
Creo que es eso, ¿eh?
Creo que es eso.
¿Ya decía yo?
O sea, es que estaba convencido.
Porque mira qué fácil, ¿ves?
Ahora está aquí.
Ahora está aquí.
Es que, ¿veis qué fácil?
Porque dices, es que no tiene mucho...
Son dos líneas en el cliente, dos líneas en el servidor.
La culpa no era mi código.
Mi culpa...
Era culpa de Astro.
Era culpa de Astro.
No era ni de Svelte ni de nada.
Era básicamente esto, ¿no?
Era esto está bien y esto está bien.
¿Veis ahora que está aquí todo el archivo?
Toda la información del archivo.
¿Cuál era el problema?
El problema era que Astro lo tenía en modo estático.
Y en modo estático no puedes crear 10 points.
Y por eso no estaba reconociendo las cabeceras y tal.
¿Veis?
Aquí tengo ya el archivo en el servidor.
Es que ya decía yo.
Es que estaba convencido al mil por cien.
Es que estaba convencido.
No sé.
Me estaba volviendo loco ya.
Bueno, estas cosas pasan, ¿eh?
El error de Astro en el browser debería haber sido más claro.
Estoy de acuerdo, Gonci.
Estoy totalmente de acuerdo contigo.
La verdad es que era muy raro.
Era muy raro.
¿Cuál era el problema?
El problema es que Astro, digamos, que tiene dos modos.
Tiene más modos, ¿vale?
Pero tiene dos modos.
El output lo puedes tener en static, en server o en híbrido.
Nuestro caso lo voy a poner en server, aunque podríamos trabajar en híbrido.
Pero bueno, lo vamos a poner en server.
Porque queremos tener en points.
Y eso significa que necesitamos un servidor detrás.
En estático lo que haríamos es generar HTML y ya está.
Y no podríamos tener en points.
No tendrías en tu.
¿Vale?
Así que por eso era el error.
Porque no estaba entendiendo.
Lo que pasa es que el error no era muy claro.
No estaba muy bien.
No estaba muy bien, sinceramente.
Bueno, ya tenemos el archivo.
Bueno, pues yo creo que si tenemos el archivo, lo que ya podríamos hacer es...
Bueno, ya tenemos el archivo.
Entonces, lo que podemos hacer es subirlo a Cloudinary.
Para subirlo a Cloudinary...
Necesitamos una cuenta de Cloudinary.
Cloudinary, os he dicho antes que era una plataforma de imágenes y vídeos.
Y además podéis subir también archivos PDF.
Pero, ojo, cuidado con esto.
Porque para subir archivos PDF, lo estuve probando la semana pasada.
Porque íbamos a hacer la semana pasada esto.
Y me di cuenta que no podía.
Y digo, ¿what?
Y resulta que en las preferencias hay una cosa por algún sitio.
A ver si lo encuentro.
Que creo que es aquí en Upload o en Security.
Creo que es en Security.
Puede ser.
Puede ser Security.
Que hay aquí, aquí abajo.
¿Vale?
Asegurados que tenéis el PDF and SIP Files Delivery.
¿Vale?
Que lo tengáis esto activado.
Porque si no...
Porque si no, no funciona.
Solo para que lo sepáis.
No sé si hay algún sitio más.
Pero en ese seguro.
Porque si no, no funciona como esperáis.
¿Vale?
Tenéis que tener esa parte activada.
Para que lo tengáis ahí en cuenta.
Y luego, hay diferentes add-ons que se pueden activar.
Que son con inteligencia artificial.
Por ejemplo, para eliminar fondos y tal.
Y uno que tenéis que activar, si queréis utilizar lo de hoy.
Como lo estamos haciendo.
Es este de OCR Text Detection and Extraction.
Que esto lo que hace es que le puedes pasar una imagen, un PDF o lo que quieras.
Y le extrae el texto.
Nosotros lo vamos a hacer con el PDF.
¿Vale?
Tiene 50 al mes gratis.
Aunque, claro, pagando ya tenéis muchas más.
Pero estoy seguro que a lo mejor si le decís a Colby.
Oye, dame créditos de esto, porfa.
Que estoy probando una cosa.
Seguramente os dará créditos.
Pero bueno, para que lo sepáis.
Que tiene una capa gratuita.
Pero que son solo 50.
Y que a lo mejor es poco.
Hay otras alternativas.
Yo lo voy a hacer con esto para simplificar.
Pero seguramente la próxima vez lo vamos a hacer con LangChain.
Porque además así lo haremos más rápido.
Que aunque tengamos el PDF.
Pues lo podamos tener con el texto.
Que lo detecte y lo extraiga.
Y lo tendremos nosotros con un servicio.
Lo que pasa es que a nosotros nos podría salir.
O sea, tenemos que pagar el servidor para que haga la extracción.
Ya está.
Ese es el problemilla.
Pero bueno.
El tema.
Vamos a subirlo a Cloudinary.
Para subirlo a Cloudinary.
Primero instalamos Cloudinary.
¿Vale?
Que es el SDK oficial de Cloudinary.
Y lo importamos por aquí.
Vamos a importar del V2 como Cloudinary.
V2 porque el SDK tiene más de una versión.
Y nosotros vamos a utilizar la versión 2.
¿Vale?
Y esto lo vamos a traer de Cloudinary.
Cloudinary.
Por ahora con esto ya tendríamos suficiente.
¿Vale?
Ahora tenemos que crear como Cloudinary config.
Y aquí le tenemos que pasar tres cositas.
El CloudName.
De hecho lo tenéis aquí.
Lo tenéis aquí.
Cuando entréis.
Aquí tenéis toda la información que necesitáis.
Si entra esa Cloudinary en la consola.
Lo que pasa es que con cuidado.
Que no se os cuele en vuestro código.
De hecho ahora tendré cuidado.
Y lo arreglaré.
Y lo pondré en la variable de entorno.
Pero aquí.
¿Veis?
Aquí tenéis la información que es.
Bueno.
Es que de hecho podéis copiar esto.
Y ya lo tendríais.
Pero ¿ves el API Secret ahí que aparece así?
Bueno.
Pues eso.
Ahora no lo voy a pegar.
Porque si no.
Se va a ver todo.
Pero.
Hay que hacer esa configuración.
Así que.
Voy a hacer una cosa.
Vais a ver mi cara.
Mi hermosa cara.
¿Vale?
Un momentito.
Para pegar esto.
Y así copiarme el API Secret.
Que si no.
La liamos para la.
¿Vale?
¿Y el Secret?
No me va.
Pero este Secret no me.
Porque este Secret.
A ver.
Es que no me está copiando el Secret.
Me está poniendo.
Secret.
Pues eso quiero yo el Secret.
Ahora.
Vale.
Vale.
Vale.
Vale.
Tigo.
Hostia.
Que no me está copiando el Secret.
Vale.
Vale.
Entonces.
Esto lo pongo por aquí.
Quitamos los asteriscos aquí.
Esto fuera.
Esto fuera.
Esto lo ponemos en la variable.
En una.
En un archivo.
.env.
¿Vale?
Creas el archivo.env.
Mírame a los ojos.
Mírame a los ojos.
Entonces.
Archivo.env.
Le voy a llamar.
Cloudinary Secret.
Voy a copiar el token.
¿Vale?
Y ahora.
Aquí vamos a utilizar el import.
.meta.env.
.cloudinary secret.
¿Ok?
Y con esto.
Si no me he equivocado.
Y a ver.
Me voy a asegurar que he cerrado todos los archivos.
Y ya os enseño la pantalla.
Bueno.
Pues ya lo tendríamos.
¿Vale?
¿Dónde he dejado el?
Aquí.
Vale.
Pues la API Key.
Bueno.
La API Key si queréis.
La podéis dejar ahí.
Porque esta.
Es pública.
No pasa nada.
Aunque si lo tenéis en variables de entorno.
Yo creo que mejor.
Pero bueno.
Solo que lo sepáis.
Y la API Secret.
La ha puesto la variable de entorno.
Cloudinary secret.
¿Ok?
Y con esto tendríamos ya la configuración de Cloudinary.
Ahora.
Aquí hay un tema.
Hay un tema.
¿Vale?
Para subir el archivo a Cloudinary.
Me lo estuve mirando.
Porque claro.
Es la primera vez que he utilizado Cloudinary para subir un PDF.
Y hay un método que se llama Cloudinary Upload Stream.
Que es este.
Y esto.
Claro.
Hay que hacer alguna cosita.
Porque tienes que transformar el archivo en binario en un array buffer para poder subirlo.
¿No?
Y aquí pues podéis ver un poquito toda la información.
Tenéis como máximo de cuánto puede ser lo que se puede subir la imagen y todo esto.
Hay unos límites.
Pero bueno.
El límite va bastante sobrado.
Y aquí te explica un poco cómo es el tema.
¿Vale?
Y hay tres formas de subir.
Con Unsign Upload, Upload Stream y Upload Large.
En este caso vamos a utilizar el Upload Upload Stream.
Que es como para subir un stream de datos.
Y esto tiene sentido cuando son archivos muy grandes para no paralizar nada.
Y veis.
Aquí tenemos todo esto.
Todo esto así.
Que yo lo voy a hacer un poco diferente.
Pero solo para que entendáis la idea de lo que queremos hacer.
Queremos ir leyendo el archivo y irlo subiendo.
Porque los archivos pueden ser muy grandes.
Y así no paralizamos nada.
Entonces, yo voy a crear un método que le voy a llamar Upload Stream.
Donde le vamos a pasar el buffer.
El buffer básicamente, para que nos hagamos una idea, un buffer que es.
El tema es que tratar los archivos con texto, como el texto que nosotros entendemos,
es muy complicado y muy lento para las máquinas.
Ellos lo que quieren es que le pasen los bits.
Con los bits se entienden mucho mejor.
Entonces, un Array Buffer básicamente es un espacio de memoria donde tiene los bits
y tiene la información del archivo que queremos subir.
Entonces, no tiene sentido que lo convirtamos a un string y lo manipulemos.
Y no sé qué.
Porque lo que quiere directamente es un buffer.
Un buffer de datos.
Y lo tenemos ya en memoria porque así lo manipula mucho más rápido.
¿Vale?
Lo digo porque hay veces que aquí la gente se vuelve un poco loca.
Pero así es como funcionan muchas veces cosas de Node.
Cuando trabajamos con imágenes y tal.
Y el formato que se utiliza es el del entero de 8 bits.
¿Vale?
Sin signo.
Que por eso tiene una U aquí.
Es sin signo.
Pero bueno.
Tampoco hace falta mucho que volváis muy locos.
Simplemente con lo que os he dicho ya lo tendréis bien.
¿Vale?
Y le vamos a decir a qué carpeta lo tiene que subir.
Así que creamos este método por aquí.
Lo vamos a envolver en una promesa.
Esto es un poco la idea.
La misma idea que habéis visto en la documentación.
De hecho esto lo he aprendido yo de la documentación.
¿Vale?
Y aquí ya he estado haciendo exactamente lo mismo.
El stream de datos utilizando Cloudinary.
Llamando al uploader.
A upload stream.
Aquí le pasamos las opciones.
Si hay un resultado pues nada.
Hacemos el resolve.
Y si hay un error pues hacemos el reject.
¿Vale?
Lo vamos a simplificar aquí el if.
Para no utilizar el shift.
¿Vale?
Esto.
A ver aquí.
Hay una cosa que no me gusta mucho muchas veces de GeekHack o Pilot.
Y es que se vuelve un poco tonto cuando tiene que cerrar cosas y tal.
De hecho todo esto lo vamos a quitar.
Yo lo que voy a hacer es aquí llamar al end y pasarle el buffer.
¿Vale?
Le vamos a pasar directamente aquí el buffer.
Y esto lo cerramos aquí.
Esto aquí.
Y con esto ya tendríamos el fichero.
Lo que queremos.
Este stream en realidad.
Este stream no estamos haciendo nada con él.
O sea no hace falta.
Directamente el stream no lo necesitamos.
Con esto ya tendríamos.
Lo importante es que la promesa nos resuelva el resultado o nos dé un error.
¿Vale?
Y ya podríamos llamar el upload stream este donde lo necesitemos.
¿Dónde lo necesitamos?
Pues aquí.
Aquí lo que vamos a hacer primero.
Lo que os decía.
Primero tenemos que convertir los archivos en un array buffer.
El archivo.
Ah claro.
Porque me dice que es nul.
Claro.
Tiene sentido.
Si file es nul.
Vamos a dar un error.
Return.
New response.
File not found.
Y dice que esto.
¿Cómo que nos da esto?
Ah.
¿Ves?
Es que claro.
Array buffer no existe en el tipo string.
Y es porque el form data.
Este file.
Claro.
Me dice form data entry value o nul.
Claro.
Esto.
Vamos a poner que esto sea file.
Ya está.
Y que no nos dé problemas.
Y así sí que va a encontrar el array buffer.
¿Vale?
Y ahora ya pues lo transformamos en el tipo de datos este que necesitamos.
Y ya lo tendríamos.
Ya le podemos pasar esto al upload stream.
Le pasamos este buffer.
Y aquí las opciones.
Que el folder que vamos a utilizar sea el de PDF.
Esto devolvía una promesa.
Así que hacemos aquí una wait con un result.
Y vamos a ver si esto sube los archivos o no sube los archivos.
¿Vale?
Vamos a ver si sube los archivos.
Vamos a ver si esto ha ido todo bien.
Print.
¿Vale?
Está subiendo el archivo por ahora.
No sé si está subiendo el archivo.
Vale.
Ha dado un error.
Inexpected token.
Vale.
Eso tiene buena pinta.
Eso significa que nos ha subido el archivo.
De hecho aquí tenemos la URL.
Nos ha subido el archivo.
¿Vale?
Aquí tenemos el archivo PDF.
Con todo esto.
Nos ha subido el archivo a Cloudinary.
Lo tenemos aquí.
La URL.
Este response que veis aquí.
Es este console.log que vemos aquí.
O sea que ya tenemos el archivo subido.
Lo que pasa es que esta respuesta que yo he devuelto aquí a lo bestia.
Esto en realidad tendríamos que devolver un json.stringify.
Y aquí pues tendríamos que tener.
Bueno, pues mira.
Ya que estamos vamos a recuperar información interesante.
Por ejemplo.
Vamos a sacar la ID.
El asset.
Asset ID.
Lo vamos a renombrar ID.
Vamos a cambiar también asset ID.
La URL.
¿Vale?
Y también URL.
Ah.
Y nos dice las páginas.
Pues vamos a devolver también las páginas.
Que hemos.
¿Vale?
No existe en el tal.
Ah, no.
Vale.
Claro.
Es que esto.
Podemos decirle que el upload stream.
Tu, tu, tu.
Claro.
Esto debería devolver.
No sé dónde poner.
No sé dónde ponerlo.
Pero podríamos decirle en algún sitio.
No sé si aquí.
Yo creo que aquí debería funcionar.
O sea.
Podemos decirle que esta promesa devuelve upload response.
Y esto ahora sí lo está tipando correctamente.
Y aquí.
Vale.
Ahora esto no se está funcionando bien.
Vale.
Entonces la promesa está.
Esto no lo necesitamos.
Y aquí ya podemos pasar la información que estamos recuperando.
La URL y las páginas.
¿Vale?
Vale.
Y aquí.
Que aquí había puesto el result.
Aquí deberíamos tener la.
La.
Result.
Tendríamos la ID.
Bueno.
Podemos pasar aquí.
¿No?
ID, URL y páginas.
ID, URL y páginas.
¿Vale?
Y en el setup status.
En la store.
Aquí tendríamos la ID.
Que es.
Según esto.
Un string.
La.
El.
URL.
Que es un string.
Y el pages.
Que es un number.
Vale.
Espérate.
Que esto siempre.
Siempre la lío.
Con estas cosas.
Esto.
ID, URL y pages.
¿Vale?
Básicamente para tener los tipos.
Y ya está.
¿Vale?
Vamos a necesitar aquí más información.
¿No?
Vamos a tener que poner aquí.
App status.
Status.
Info.
Writable.
Vamos a poner.
ID.
URL.
Pages.
Cero.
Y vamos a cambiar esta información aquí.
App status.
Punto set.
Y le vamos a poner esto.
No se puede asignar.
Pages.
Cero.
A ver.
¿Por qué?
Al tipo.
Ah.
Perdón.
Que estoy seteando el que no es.
Ahora.
Vale.
Básicamente así.
Le pasamos esa información.
Y la podremos recuperar en el siguiente.
¿Vale?
Entonces.
Con esto.
En principio.
Lo que es la parte.
De subir el archivo.
Y todo esto.
Lo deberíamos tener.
Así que.
Si arrastramos aquí.
¿Vale?
¿Vale?
Setup status chat mode.
Is not defined.
Setup.
Chat mode.
Is not defined.
Esto es porque no he importado aquí esto.
¿Vale?
¿Vale?
¿Vale?
A ver.
Probamos otra vez.
Me voy a gastar todos los créditos hoy.
¿Vale?
¿Vale?
Está bien.
Está vacío porque nuestro componente está vacío.
¿Vale?
Pero ya estamos avanzando.
Ya estamos.
Estamos.
Metiendo.
En Cloudinary.
Y ahora vamos a ver la URL y tal.
¿No?
¿Por qué hay dos URLs?
Secure URL y URL.
Bueno.
En este caso.
Creo que la Secure URL es exactamente la misma.
Si no me equivoco.
¿Ves?
Que es exactamente la misma.
Pero puede ser que puedas subir imágenes.
Tú imagínate que quieres subir una imagen que no es pública.
Entonces, a veces.
Ah, bueno.
Perdón.
No, no, no.
La Secure URL.
La única diferencia en esta es de HTTP.
No, no.
Es que, como he visto lo de Access Mode, pensaba que cuando era privada, a lo mejor la Secure URL es que venía con algún token o algo.
Pero no.
La única diferencia es de HTTP.
Pues, ¿sabes qué?
Que bien visto.
Porque así vamos a utilizar aquí el Secure URL, que tiene más sentido, ¿no?
Para no utilizar el de HTTP.
Vale.
Pues, ahora que tenemos esto, vamos a hacer lo de la parte del Step Chat.
¿Vale?
Step Chat.
Vale.
En el Step Chat lo que vamos a querer es, básicamente, primero, vamos a poner el input, el label y el spinner del, ¿cómo se llama?
Flow Byte Svelte, ¿vale?
Vamos a importar el app status info, que es la información que tenemos, la URL y tal.
Vamos a recuperar la URL, las páginas y también vamos a recuperar la ID.
Era la ID, ¿no?
Status info.
Y esto, acordaos que hay que hacerlo así, para poder recuperar el valor.
Y queremos el valor.
¿Vale?
Muy bien.
Entonces, aquí vamos a buscar ahora un sitio donde podamos poner un formulario.
Vamos a buscar aquí en el Flow Byte Svelte un input para nuestro chat.
¿Vale?
Vamos a buscar un input, un input.
Formularios, forms.
¿Vale?
A ver.
No sé si poner...
A ver.
Mira, este.
Yorimen, no sé qué, no sé cuánto.
A ver, tampoco hace falta que sea muy...
Yo que sé.
Este ya está bien.
Este ya está bien.
Este ya está bien.
Solo que me sobra es este input que hemos puesto aquí.
Esto fuera.
Vale.
Y aquí vamos a poner pregunta.
Deja aquí tu pregunta.
¿Vale?
Playholder.
De qué trata este documento, por ejemplo.
¿Vale?
La idea esta no hace falta.
Es un poco raro.
A ver, aquí podemos poner question input.
Y aquí question input.
Y aquí el type.
No vamos a poner nada.
Pero vamos a poner que esto es required.
Esto por aquí.
Esto por acá.
Esto tampoco hace falta.
Creo yo.
¿Vale?
On submit.
Handle submit.
Esto es muy parecido a lo que...
Cómo lo harías en todos los sitios.
¿Vale?
Y esto.
Input así.
Y esto así.
Esto así.
Y aquí hacemos el handle submit.
Que es al que llamaremos a la API para preguntar la inteligencia artificial.
De qué es lo que me tiene que decir y todo esto.
Pero antes vamos a hacer una cosa.
Vamos...
Bueno, voy a poner un loading aquí.
Por ahora voy a poner false.
Esto lo utilizaremos después.
Voy a enseñar imágenes del PDF.
Porque esto es una cosa que está bastante chula.
Mira.
Vamos a enseñar un máximo de cuatro imágenes del PDF.
¿Vale?
Entonces vamos a poner...
En lugar de cargar todo el PDF que puede ocupar mucho.
Vamos a enseñar imágenes pequeñitas.
Como máximo vamos a poner cuatro imágenes.
¿Vale?
Images.
Vamos a poner...
Images to...
O num.
Num of images to show.
¿Vale?
Y vamos a mostrar cuatro imágenes.
Vamos a mostrar cuatro imágenes.
Array from.
Porque esto es una funcionalidad que está bastante chula de Cloudinary.
Cuando subís un PDF...
Una cosa que podéis hacer.
Num of images to show.
¿Vale?
Esto sería hasta aquí.
Y ahora podemos mapear con el segundo parámetro.
Esto es una cosa de los arrays de...
De...
JavaScript.
¿Vale?
Aquí podríamos con la URL...
Fijaos.
Yo tengo esta URL.
¿Vale?
Esta URL.
Vale.
Yo tengo esta URL.
Pues esta URL, si no me equivoco, podemos recuperar la imagen.
¿Vale?
Lo que podemos hacer es decirle...
Eh...
No me acuerdo.
Pero lo vamos a buscar.
Cloudinary.
Con ver PDF tu JPG.
Vale.
Esto es lo que quiero.
Básicamente podemos cambiar...
Aquí.
¿Veis?
Podemos recuperar esta parte.
Y le podemos poner ahí unos...
Esta parte.
Y además...
Después del upload ponemos esta parte.
Barra.
Y debería...
No.
Ah.
Porque hay que cambiar el formato.
JPG.
¿Vale?
Ahora sí.
Ahora sí.
¡Ting!
Claro.
Se le puede cambiar un poco la calidad y todo eso.
Que la calidad es un poco...
Pero podéis poner...
¿Veis aquí que pone PG2?
Es que podéis poner el número de la página.
Esto está muy chulo.
El hecho de subir un PDF y que puedas recuperar para mostrar las imágenes.
Esto está bastante chulo.
Entonces ahí puedes poner el número de la página y te aparece aquí.
Aquí vamos a darle un poquito más de alto para que quede un poquito mejor.
Y vamos a ponerle 400.
Vamos a ponerle el doble de 270.
¿Verdad?
Vale.
540.
Nada.
Yo también tener que hacer eso.
¿Vale?
Para darle un poquito más de calidad.
Y con esto lo que vamos a hacer aquí...
Esta parte...
Esta parte...
Es la que vamos a utilizar.
Esta es la URL que tenemos.
¿Vale?
Entonces vamos a hacer magia.
Nos vamos a run.js.
Esto es JavaScript.
Estas son las cosillas que me gustan de JavaScript.
Y la parte de scripting de JavaScript.
Que es como de lo más bonito.
¿No?
Teníamos la URL.
Que es esta de aquí.
Con el PDF.
Vale.
Pues URL.
Joder.
Deja autocomplete.
Replace.
Lo vamos a simplificar.
Vamos a poner aquí que el upload barra...
Vamos a poner los doble de upload.
Lo vamos a cambiar por barra upload barra...
Lo voy a hacer fácil, ¿eh?
Tampoco me va a complicar mucho la vida.
Y aquí iríamos con el índice.
O el número de página.
Page.
Entonces, pongamos que quiero la página 1.
¿Vale?
URL.
Y también tenemos que reemplazar el .pdf por el .jpg.
Y se supone que este debería ser...
Vale.
La he liado.
La he liado porque...
¿Por qué la he liado?
¿Por qué la he liado?
La he liado...
¿Por qué la he liado?
¿Porque se me ha olvidado una barra?
No.
¿Porque se me ha olvidado...
Se me ha olvidado esto.
Esta barra.
¿Puede ser?
Sí.
Vale.
Vale.
Pues ya lo tendríamos.
Esta es la lógica que queremos.
Esta es la lógica que queremos, amigos.
Ya aquí tendríamos las imágenes, la URL, la reemplazamos.
Y para calcular el número de la página, el page será I más 1.
Y esto lo devolvemos.
Y estas serían las imágenes que queremos mostrarle al usuario para que sepa a qué le va a preguntar.
¿Vale?
Entonces, vamos a poner if loading, no sé qué, no sé cuánto.
No.
Each images as image.
¿Vale?
PDF page.
¿Vale?
Esto ponemos h auto.
Vamos a poner esto en un div.
Porque vamos a utilizar aquí un grid.
Grid de 4 columnas.
Ok.
4 columnas.
Y con un gap de 2.
Mostramos las imágenes.
Las imágenes, no sé si ponerle un poquito de rounded.
Ay, lo he puesto esto dos veces, ¿no?
Lo he puesto dos veces.
Lo que estoy pensativo, porque estoy ahí súper atento.
Ha puesto class dos veces.
Arreglado.
Ya está.
Tipo de secure URL.
Tipo secure URL.
¿Dónde ha sido el typo de secure URL?
Ah, vale.
Gracias.
Secure URL.
A ver, no la liemos.
Secure URL.
Vale, gracias, porque con esta lo habrá liado, ¿eh?
Ya está.
Gracias.
Gracias, gracias.
Muy bien.
Pues ahora ya deberíamos ser capaces de subir la imagen.
O sea, subir el PDF y recuperar la imagen.
Subiendo el archivo y extrayendo el texto.
Vale.
Bien.
Bien, sí.
Ya tenemos ahí las imágenes.
Y al menos ahí sabremos con qué estamos hablando.
Voy a ponerle, añadirle un poquito de margen aquí.
Porque este formulario, pues debería tener ahí un poquito de margen.
Y estoy pensando aquí el Asper Ratio.
Claro, ¿cómo sería el Asper Ratio de esto?
Más que nada, porque si le ponemos el Asper Ratio, así no dará el salto.
Entonces, vamos a ver, Asper 400 por 540.
Bueno, pues Asper 400 por 500, 540.
Y con esto, al menos no debería dar el salto.
¿Vale?
Vale.
Entonces, ya tendríamos todo esto.
Aquí con el Handle Submit lo que podemos hacer.
Vale.
Me parece bien.
Me parece bien.
El Question lo vamos a recuperar también.
Esto sería lo que vamos a hacer.
O sea, vamos a tener que crear aquí una Ask.ts.
Que ahora lo haremos.
Es un poquito similar a este que hemos hecho aquí.
Este Export Const y todo esto.
Así que me voy a copiar esto.
Sin ningún tipo de miedo.
Happy Route.
Y Happy Route.
Bueno, vamos a pillar solo el tipo.
¿Vale?
Y aquí por ahora vamos a poner return new response.
JSON.stringify.
Y response.
Ponemos esto y ya está.
Por ahora lo dejamos así.
Ahora luego volvemos a esto.
Pero al menos ya tendríamos la API.
Esto es lo que debería hacer.
¡Hostia!
Estoy pensando.
Estoy pensando cosas con esto.
Estoy pensando cosas.
Vale.
Estoy pensando.
Es que claro.
Esto si es el chat.
Lo que tendría sentido en lugar de hacer un fetch.
Sería hacer el streaming de los datos.
Pero bueno.
Lo podemos hacer primero normal.
Y luego hacemos el streaming de los datos.
Que es bastante interesante.
El hecho de cómo hacer como un WebSocket.
O un server.
Son server site events.
Y lo podemos hacer.
Porque además tengo por ahí un fichero.
Con una biblioteca.
Que me lo simplifica bastante.
Pero bueno.
Tendríamos que recuperar.
Primero.
Cuando haces un submit.
Tienes que hacer el preview default.
Para evitar que se refresque toda la pantalla.
¿Vale?
Lo segundo.
Bueno.
Esto da igual.
Vamos a cambiarlo a loading directamente.
También lo que tendríamos que hacer.
Es recuperar la pregunta.
¿Vale?
Pues del event.target.
¿Cómo le llamo aquí?
¿Cuestion o?
Question input.
Hostia.
Question input.
Bueno.
Vamos a ponerlo así.
Question.
Question punto value.
¿Vale?
Ahí tendríamos la pregunta.
Y esta pregunta.
Es la que le vamos a pasar a la API.
¿Vale?
Entonces ya teníamos la question.
Aquí tendríamos la respuesta.
Y otra vez un poco lo mismo.
¿No?
Si la respuesta no está bien.
Podríamos hacer algo.
Y si no.
Aquí tendríamos la respuesta.
Y podríamos mostrar la respuesta justo debajo.
¿Vale?
Entonces si tenemos.
No.
Si está cargando.
Bueno.
También.
Si está cargando.
Hace esto.
Y si tenemos la respuesta.
Vamos a poner aquí la answer.
Let answer.
Con una cadena de texto vacía.
¿Vale?
Si tenemos una.
If answer.
Pues le decimos.
Tenemos aquí la respuesta.
Y no sé qué.
No sé cuánto.
¿Vale?
Vale.
Vale.
Aquí tendríamos esto.
Loading.
La cuestión.
La respuesta.
Y este answer.
Vamos a poner.
API answer.
Es la que le tenemos que aplicar.
Esto está muy chulo de Svelte.
Porque tú haces directamente.
Una variable.
Y cuando cambias la variable.
Se refresca.
O sea.
Es un estado automáticamente.
Es lo mejor que.
O sea.
Para mí es de lo mejor que tiene Svelte.
Lo fácil que debes pensar en estados.
Porque básicamente detecta que estás utilizando esta variable dentro de tu componente.
Y por lo tanto cada vez que la cambias.
Pues dice.
Ah vale.
Pues era un estado.
Y te lo cambia y te lo refresca.
Eso está muy chulo.
Sin necesidad de importar ningún hook.
Ni hacer absolutamente nada.
Esa es una de las cosas que me gustan mucho.
Y lo que no me gusta justamente.
De lo que están haciendo con el tema de.
De.
¿Cómo se llama esto?
De las runas.
Que.
A mí me parece que lo están complicando un poco.
Pone el loading a false.
Cierto.
Cuando terminamos aquí.
Tanto aquí.
Loading.
False.
Como aquí.
Tendríamos que poner loading a false.
Bien visto.
Muy bien.
Muchas gracias.
Vale.
¿Y es reactivo entre componentes?
Totalmente.
Totalmente.
No entiendo nada.
Joder Eduardo.
Si no entiendes nada.
Pues entonces tienes que estudiar mucho.
Porque a ver.
Un fetch a una API.
¿Cómo no la vas a entender?
No entiendo.
La verdad es que.
Si decís que no entendéis nada.
Nada.
Pues yo creo que.
Deberíais estar estudiando.
En lugar de ver este stream.
Amigos.
No pasa nada.
Pero como no vais a entender.
Un fetch de datos.
Un fetching.
O sea.
Es que a ver.
A no ser que acabas de empezar.
A tu programación.
¿Sabes?
Si acabas de empezar.
Lo puedo entender.
Pero un fetch con un post.
Hombre.
Amigo.
Esto.
Si esto no es nada.
A no ser que sea tu primer día.
Si es tu primer día.
Lo entiendo.
Y te abrazo.
Pero.
Pero claro.
Normal que no entiendas nada.
Es normal.
Pero aunque no entiendas Svelte.
Es que.
Fíjate que.
No tiene nada Svelte.
Es todo JavaScript.
Todo.
Todo.
A ver.
Yo entiendo lo que pasa.
Pero no entiendo como JavaScript es capaz de algunas cosas.
Bueno.
Eso es más normal.
Pero una cosa es no entender nada.
Y otra cosa es que te escape alguna cosa.
Otra cosa es que te escape alguna cosa.
El resok es igual que un catch.
No es igual que un catch.
De hecho.
Eso es un error bastante común.
Que la gente.
De forma errónea.
Hace el try catch.
Try.
El try catch del fetch.
Y eso no está bien del todo.
Porque.
El fetch.
El catch que se hace del fetch.
Este catch.
En realidad.
Solo va a ocurrir.
Si.
Si no hay conexión.
O se.
O se produce un error.
Muy bestia.
No es la petición.
¿Sabes?
Si la petición te devuelve un 400.
¿Vale?
Por ejemplo.
Un 404.
Un 500.
Y tal.
Va a entrar aquí.
Va a entrar a este res.ok.
O sea.
Va a ser un punto den.
Se va a resolver la promesa.
O sea.
Que tener cuidado.
Por esto.
Tener cuidado.
¿Ok?
Mido.
Mándame un abrazo.
Te mando un abrazo.
Amigo.
Un abrazo para ti.
Justo aprendí eso esta semana.
Bueno.
Eso es bastante importante.
Hay gente incluso con.
Gente con.
Con años de experiencia.
Que son signos.
Que meten ese error.
Igual te falta un try catch.
Para que no explote.
¿No?
Si.
Me falta un try catch.
O sea.
Tendría que poner un try catch.
Para que no explotase.
Pero bueno.
En este caso.
No lo estoy metiendo.
Básicamente para entender.
Pero.
O sea.
Para hacerlo ya está.
Use funnily para lo adding false.
Bueno.
Podríamos.
Es que.
Es que no quiero mezclar las dos cosas.
Pero bueno.
Venga.
Vamos a hacer aquí.
Vamos a meterlas aquí.
Venga.
Try catch.
Vamos a meter esto aquí.
Esto lo vamos a meter aquí.
¿Vale?
Porque esto.
Tendría que ser fuera del catch.
¿Vale?
Aquí sí que vamos a hacer el catch.
Y aquí vamos a meter el finally.
¿Vale?
Y ya tendríamos ahí correctamente esto.
Esto sería aquí en este caso.
Y aquí.
Pues nada.
Vamos a meter console error.
Tendríamos aquí.
Y tal.
A ver.
Que aquí tendríamos que poner a lo mejor el status error este.
Meter este status.
App status.
Para meterle en el error.
Y ya está.
¿Puedo usarlo para pasar los PDF a JSON?
Claro que sí.
Sin ningún problema.
Totalmente.
¿Vale?
Pues ya está.
Ya teníamos el try catch finally.
El finally va a entrar independientemente si ha pasado por aquí o por aquí.
O sea que se va a ejecutar siempre de las dos formas.
Lo cual.
Bueno.
Hay gente que no le gusta mucho porque era una mala práctica en su día.
A mí la verdad es que me parece que está bien.
Y también el finally.
También el finally funciona con las promesas.
O sea.
Puedes hacer un promise.
¿Vale?
Promise.
Punto then y punto catch punto finally.
¿Vale?
También puedes hacer un finally.
Que es bastante interesante.
Lo digo porque no es solo un try catch.
También se puede hacer con las promesas.
Vale.
Pues venga.
Vamos a seguir con esto porque ahora lo que nos faltaría es el hecho de...
Claro.
No nos va a dar tiempo a subirlo a una base de datos.
Porque eso sería la clave.
Subirlo a una base de datos o subirlo a un S3.
Pero vamos a hacer una cosa.
Cuando subimos el PDF.
Al menos para tenerlo.
Porque es que si no, no le vamos a dar cañita a esto.
Vamos a hacer una cosa.
Cuando tenemos esto aquí.
Ya os he dicho que había un formato.
Como una extensión de Cloudinary para sacar el texto.
Entonces para activarlo y utilizarlo.
Creo que está por aquí.
Ah mira aquí estaba.
Este era el addon.
¿Vale?
Este es el addon que tenéis que activar aquí en Cloudinary.
En Cloudinary.
A ver si aquí me sale un enlace.
Esto lo que hace es que detecta los textos de imágenes, PDFs y todo esto.
Y podéis hacer cosas muy chulas como por ejemplo.
Pues emborronar.
¿Vale?
Podéis emborronar el texto.
Podéis recuperarlo.
Extraerlo.
Y de hecho veis que hay aquí uno que pone OCR.
Add OCR.
Bueno.
Para que esto funcione.
Tenéis que activar desde la consola.
En los addons.
Tenéis que activarlo.
¿Vale?
Porque si no.
No funciona.
A ver si soy capaz de entrar a mis addons.
Siempre que voy a la página.
Siempre tengo que iniciar sesión.
Sí.
No puedo ir directamente a los settings.
Porque no me sale la consola.
Sino que me sale ya directamente a la página.
O sea.
A lo mejor es que tenía que darle aquí.
Pero.
A ver.
¿Dónde están los addons?
Addons.
Ah.
No los encuentro.
No encuentro los addons.
Programable.
Media flows.
Los hemos visto antes.
Antes los hemos visto.
Y ahora no los encuentro.
A ver.
¿Alguien sabe dónde estaba?
Sí.
Sí.
Estaba.
Pero ahora no los encuentro.
No sé dónde los he metido.
En settings.
Puede ser.
En settings.
Ti, ti, ti, ti, ti.
Account.
My profile.
Aquí.
Addons.
Igual es que no lo he visto antes.
Y ya está.
Bueno.
Tenéis que activar los que vais a utilizar.
No hace falta los activéis todos.
En este caso vamos a utilizar este.
El OCR.
Text detection and extraction.
Así que nada.
Vamos a utilizar este.
Y lo tenéis que tener activado para utilizarlo.
Si no.
Pues no funciona.
Y lo único que tenemos que hacer es esto.
¿Ves?
Hay que pasarle como una opción ahí.
Para cuando hagamos el upload stream.
El upload stream.
Aquí.
En estas opciones.
Le tenemos que pasar.
Si realmente queremos hacerlo del OCR y todo esto.
¿No?
Entonces vamos a añadir aquí OCR.
Vamos a decir string.
Que sea opcional.
Y en el upload stream.
Aquí.
Le vamos a pasar.
El.
No.
OCR.
Advance OCR.
¿Vale?
Y esto lo que va a hacer es extraer el texto.
Como no quiero.
Porque claro.
Esto se va gastando.
Y tarda ahí y tal.
Como no quiero estar ahí todo el rato haciendo esto.
Yo ya sé dónde viene el archivo.
De hecho.
Aquí está el objeto que te devuelve.
¿Ves?
Info OCR y tal.
Así que para extraer este objeto.
Yo ya sé que aquí me viene el info.
Y por lo tanto tenemos el data de info.
OCR.
¿Vale?
Punto.
Advance OCR.
¿Vale?
Advance OCR.
Punto.
Otra vez.
Data.
Y aquí tendríamos todos los datos con todo el texto que ha detectado y tal.
¿Vale?
Ahora.
Si nos fijamos un poco.
Pues vemos que tiene text annotations y tal.
Y aquí estaría description.
Que es donde tiene toda la información que nos interesa.
Pues lo que vamos a hacer es sacar todo el texto.
Y para hacer eso lo vamos a mapear.
Vamos a hacer el data map.
Y aquí sacamos todos los bloques de código.
¿Vale?
Blocks.
Que los bloques.
Text annotations.
Vamos a decir.
Para simplificarlo.
¿Vale?
Text annotations.
Es.
No.
Description tiene.
Ah.
Pues mira.
Sí.
Perfecto.
Bueno.
Pues description string.
Tal.
Vale.
Pues se me lo ha detectado.
A ver.
Ahora.
No.
Espérate.
Espérate.
Que aquí ha metido tanta cosa que.
No.
Se esperaba una declaración.
Una instrucción.
Esto es aquí.
Mira.
¿Sabes qué?
Es que creo que es porque he metido mucha cosa ahí.
Blocks es.
A ver.
Text map.
A ver.
Blocks.
Aquí están los blocks.
Vale.
Muy bien.
Perfecto.
Entonces.
Aquí.
Los blocks.
Venga.
Los blocks son.
Y vamos a decirle aquí.
Que tiene los type.
Text annotations.
¿Vale?
Text annotations.
Y que esto a su vez.
Es un.
Tu tu tu.
Tiene description.
Que es un string.
No sé si es un string.
No sé si tal.
Creo que sí.
Description.
A ver.
Es un string.
¿No?
Vale.
Creo que lo he tipado bien.
Si no he tipado bien.
Pues nada.
Ahora lo veremos.
¿Vale?
Sacamos las annotations.
De los bloques.
Sacamos los text annotations.
Y si no.
Hacemos que es un objeto vacío.
Porque todos tienen un objeto.
A ver.
Que debería tenerlo.
Pero bueno.
Por si acaso.
Luego.
Vamos a sacar.
De las annotations.
Annotations.
Tu tu tu.
Vamos a sacar el primer.
First annotation.
¿Vale?
First annotations.
Tiene un tipo any.
Joder.
De forma implícita.
Cago en la leche.
No se puede indexar el tipo.
La prueba de cero no existe en el tipo string.
Ah.
Porque claro.
Es que esto.
Esto.
En realidad es un array.
Claro.
Es un array.
Por eso tenemos que sacar el primero.
¿Vale?
Y luego sacamos el contenido.
First annotation.
O first.
Sacamos la descripción.
Y si no.
Pues una cadena de texto.
Y el contenido.
Le hacemos un trim.
Básicamente.
Habría.
Habría que limpiarlo mejor.
Porque tenía saltos de línea y tal.
Pero con esto.
Ya deberíamos tener el texto.
Vamos a filtrar aquí.
Las cadenas de texto vacías.
Por si.
Lo que sea.
Nos mete alguna cadena de texto vacía.
Y.
Y aquí.
Lo vamos a meter todo.
Con un salto de línea.
¿Vale?
Aquí deberíamos tener el texto.
Claro.
Esto sería la clave.
La clave aquí.
Sería.
Meter.
Esta info.
En una base de datos.
O mejor.
Mejor todavía.
En un vector.
Y meter.
Y hacer.
Los embeddings.
Pero.
No.
No nos da tiempo.
No nos da.
No nos da tiempo.
Pero lo vamos a dejar.
Para otro día.
Porque esto.
Me parece súper interesante.
Y súper potente.
Y hoy.
A lo mejor.
Para simplificarlo.
Lo que vamos a hacer.
Al menos.
Para que podamos hablar.
Con nuestra API.
Es.
Vamos a sacar.
Y vamos a crear.
Que esto también lo he visto.
Que se hace.
No así.
Porque así no tiene mucho sentido.
Porque un vector de ataque.
Bastante importante.
Pero sí de subirlo a un S3.
Esto sí que lo he visto.
Entonces.
Lo que vamos a hacer.
Lo que vamos a hacer.
Es meter aquí.
Un directorio.
En nuestra propia carpeta.
Outputdir.
Donde vamos a guardar el texto.
Y así podremos acceder.
A través de la ID.
A ese texto.
Como mínimo.
Lo deberíamos subir a un S3.
Que sea privado.
Que no sea público.
Para que nosotros solo.
Seamos los que podamos.
Acceder a esta información.
Aunque lo ideal.
Sería tener una base de datos.
Con vectores.
Para.
Porque está pensado más.
Para la.
Para la.
La inteligencia artificial.
Y que.
Podamos hacer embeddings.
Porque así de alguna forma.
Ya le estamos como preentrenando.
Con la información.
Solo del PDF.
Lo haremos con Lankchain.
En la próxima vez.
Y lo haremos con los servicios de Cloudflare.
Porque me parece muy interesante.
Y por ahora lo hacemos así.
Y al menos.
Pues nada.
Vamos iterando.
Y lo mejoraremos la próxima vez.
Entonces.
Vamos a meter esto por aquí.
En el public.
Aquí vamos a crear una carpeta.
Text.
Donde iremos guardando.
Estos archivos.
¿Vale?
De los PDFs.
Con el texto.
Y esto lo mejoraremos.
La.
La próxima vez.
¿Vale?
Vale.
En el write file aquí.
Pues nada.
Simplemente vamos a poner.
Que en el output dir.
Utilizando la id.
Guardamos el txt.
El texto.
Y no sé si necesita.
UTF8.
Yo creo que no.
Pero bueno.
Vamos a dejar así.
No hace falta aquí.
Voy a poner un todo.
Todo.
No hace falta esperarlo.
Porque total.
Mientras lo espera.
Y mientras el usuario lo utiliza.
Pues tenemos ahí un tiempecito.
¿Vale?
Lo importante es que esto funcione.
Vamos a subirlo.
Y mientras se sube y eso.
Os voy a enseñar un truquillo de Visual Studio Code mientras.
¿Vale?
Que está bastante interesante.
Me gustaría que lo hicieras.
Para aprender los vectores en base de datos.
Lo haremos.
Lo haremos.
Y con Lankchain.
Porque es que está muy interesante todo eso.
Está muy muy interesante.
Node.
FS.
Promises.
¿Qué ha pasado?
¿Lo he puesto mal?
Ah, no.
No he puesto lo de Node 2 puntos.
Bueno.
Ya está puesto.
Ya está puesto.
Vale.
A ver que esto realmente funcione.
Que claro.
Ahora tendríamos que ver que nos crea el public text.
Si no lo he puesto mal y no da ningún error.
En algún evento.
Claro.
Pensad que ahora tarda más porque estás trayendo el texto también.
Entonces es normal que esté tardando un poquito más.
Pues amigos.
Os voy a enseñar mientras un truco que me ha volado la cabeza.
Os voy a enseñar un truquillo que me ha volado la cabeza.
De Visual Studio Code.
Una cosa nueva que han sacado.
A ver.
Demo.
Visual Studio Code.
CD.
Demo.
Visual Studio Code.
Mirad.
Esto es una cosa nueva de Visual Studio Code que está muy chula.
Y es que básicamente.
¿Veis?
Aquí tengo un HTML.
¿Vale?
HTML.
HTML.
HTML.
Maravilloso.
HTML.
Maravilloso.
A ver.
Mientras si esto.
A ver.
Petar no ha petado.
Ah.
Bueno.
Tiene buena pinta esto.
Tiene buena pinta.
O sea que.
A ver si nos ha creado la carpeta.
¿Nos ha creado la carpeta o qué?
Aquí.
Mira.
Aquí lo tiene.
Aquí tenemos todo el texto.
Claro.
Esto se puede transformar en unos vectores que al final con esto.
Se lo puedes pasar a un embedding para crear, para que tu modelo lo utilice y tenga esta información y le preguntes solo de esto.
Y esto es óptimo para inteligencias artificiales justamente para no tener que pasárselo cada vez que le preguntas.
De hecho, tiene un artículo muy chulo OpenAI sobre esto.
Bueno.
Entonces, esto ha funcionado correctamente.
Os enseño el truquito, ¿vale?
De esto.
Y os digo.
Mirad.
Aquí lo que tenemos es HTML y todo esto.
Vale.
Voy a abrir el Live Preview.
¿Vale?
HTML maravilloso y tal.
Imaginad que vais a la Wikipedia.
Vais a la Wikipedia.
Vais aquí.
Mira.
Y dices.
Ah, pues me quiero copiar todo esto.
Me voy a copiar.
Me gusta.
O bueno, yo qué sé.
Es que esta.
No sé.
Mira.
Esta parte.
Esta parte.
Mira.
Te copias esto.
Control C.
Te vas a tu editor.
Y dices.
Vale.
Quiero copiarme este HTML.
Claro.
Lo copias así.
Y dices.
Menuda decepción.
Me ha copiado mal el HTML.
¿Qué mierda es esta?
¿No?
Bueno.
Espérate.
Porque ahora han metido una cosa muy chula.
Que si pones mayúscula comando P.
Y le das a pegar como.
Y le das a insertar HTML.
Te copia el HTML.
Y además con los estilos.
Porque fijaos.
Los estilos.
Con los anchors.
Y todo.
Es verdad que los estilos.
Lo meten en línea.
Que no es lo ideal.
Pero queráis o no.
Ya te ha puesto ahí.
Pues.
Toda la semántica.
Con todo el HTML.
Los estilos se ven correctamente.
Y que muchas veces.
En lugar de necesitar.
Tener que abrir las herramientas.
Y tal.
Pues oye.
Pues ya lo tienes.
De hecho.
De hecho.
Yo creo que funciona hasta con imágenes.
Fijaos.
Yo creo que funciona hasta con imágenes.
A ver.
Si.
Pego aquí.
Pegar como.
O sea.
Está súper chulo.
Está súper chulo.
Fijaos que me ha pegado hasta la imagen.
O sea.
Me ha pegado el HTML con la imagen y todo.
Y hombre.
A ver.
Es que me ha pegado hasta los estilos.
Está muy.
Muy chulo.
Muy chulo.
Cualquier cosa de eso se os ocurra.
Yo creo que incluso.
A ver.
Si vamos a.
Claro.
Que los SESLAN.
Estoy pensando.
Estoy pensando en.
Algún sitio que tengamos un vídeo.
Un vídeo de YouTube.
A ver.
Por ejemplo.
Si voy a los premios SESLAN.
A ver.
Si esto funciona.
Me cago encima.
Si esto funciona.
Me cago encima.
Si.
Yo creo que no funcionará.
Yo creo que no.
Porque.
A ver.
Si hago esto.
Y lo intento pegar.
O sea.
Está chulísimo o no, tío.
Está chulísimo o no.
O sea.
Estoy alucinando.
Está muy bien.
Porque te permite copiar.
El HTML directamente.
A ver.
Y si intento copiar toda la página.
Toda la página.
Que a ver.
Esta no es tampoco la idea.
Pero.
Bueno.
A ver.
Fijaos.
A ver.
Claro.
Le faltan cosillas.
Le faltan cosillas.
No tiene JavaScript.
No tiene Responsive.
No tiene Responsive.
Claro.
Pero.
Que.
La ha copiado todo.
La ha copiado de una.
No sé.
O sea.
Está.
Está muy interesante.
Esta novedad de.
De Visual Studio Code.
La verdad.
Está bastante.
Bastante interesante.
Bastante interesante.
Trucazo.
De Visual Studio Code.
Trucazo.
Me parece que está muy interesante.
Bueno.
Tampoco es que el Live Preview.
Sea una maravilla.
Hostia.
Pues a mí me parece una maravilla.
Live Preview.
Me parece increíble el Live Preview.
Me encanta.
De hecho.
A ver.
Está bien para lo que.
Está bien.
No es que puedas tener ahí.
Un entorno de desarrollo increíble.
Pero para HTML.
Y cosas así pequeñitas.
Estaba.
Está muy.
Muy.
Muy bien.
Vale.
Pues.
Solo nos queda lo de chatear.
Vamos a ver.
Vamos a chatear con la cosa esta.
Vamos a instalar OpenAI.
En un momento.
Install OpenAI.
Ya os digo que esto lo vamos a mejorar.
Lo vamos a mejorar porque lo vamos a hacer con nuestra propia API, digamos.
Y utilizando.
Intentando utilizar cosas nuestras para mejorar esto.
Mirad.
Me voy a copiar el.
Tengo por aquí un archivo.
Ah, bueno.
Da igual.
Lo voy a hacer.
Lo voy a hacer de otra forma.
Lo iba a hacer con streaming de datos.
Porque creo que te haya sentido.
Pero.
Pero por ahora lo hacemos normal.
Y así quedará un poquito mejor.
¿Vale?
Para que lo entendáis todos al principio.
Y luego si queréis.
Lo hacemos como.
Como se debería hacer con el streaming de datos.
Vale.
La API key.
Import meta.
Open.
OpenAI.
API key.
Vale.
Voy a sacar una API key de OpenAI.
Pero.
Ahora lo voy a hacer con OpenAI.
Para que veáis cómo funciona.
Pero luego.
Muy fácilmente.
Lo vamos a cambiar.
Para hacerlo de forma local.
¿Vale?
Con.
Totalmente gratis.
O sea.
Lo hago con la API key.
Espérate.
Que no voy a dejar que veáis la API key.
Que os conozco.
Que me la robáis.
Y me quitáis todo el dinero del mundo.
¿Vale?
API key.
Vamos a poner PDF.
Ok.
Y la pongo en una variable de entorno.
Verifica que eres humano.
La madre.
Mira.
¡Qué fuerte me parece!
¡Qué fuerte me parece!
¡Ay!
¡Qué fuerte me parece!
Utiliza las flechas para girar el objeto en la dirección.
La madre que los parió.
¡Qué difícil!
¡Joder!
Una plancha y todo.
¿Vale?
Así.
¡Joder!
Macho.
¡Uf!
Creo que lo he pasado.
Creo que lo he pasado.
Creo que lo he pasado.
¡Madre mía!
¡Qué complicado!
Cada vez lo hacen más difícil.
Vale.
Entonces.
Estoy poniendo la API key.
Y ahora.
Y ahora os comento.
Esto.
Por aquí.
API key.
Cerramos aquí.
Vale.
Ahora os comento cómo lo vamos a hacer gratis también.
Para que lo podáis hacer en local también.
¿Ok?
Y así no tengáis ningún tipo de problema.
Por ahora lo hacemos primero así.
Entonces.
Vamos a tener que sacar de la API.
La URL.
Lo podemos sacar.
Vamos a hacerlo con un get.
Luego os explicaré por qué.
Porque luego si hacemos los server site events.
Creo que estará bien.
Mira.
Voy a utilizar lo que justamente hoy he explicado en Twitter.
de que no instaléis ningún tipo de dependencia ni nada para trabajar con query params.
Que ahora mismo es que está tirado.
Es tan fácil como hacer esto.
¿Sabes?
URL.searchparams.get y ya está.
Esto creando con el new URL.
Le pasas una URL y ya puedes acceder a los query params súper fácil.
Y esto es un tema de JavaScript también.
Muy fácil.
Si no tenemos una idea, le devolvemos una respuesta que sea un error.
Como diciéndole.
Oye.
Que nos falta esto.
Y si le falta la pregunta.
También hacemos que pete la API.
¿Vale?
Y le decimos.
Falta una pregunta.
Vale.
Ahora tenemos que llamar.
Pues básicamente.
A nuestra API.
Lo ideal sería.
Lo que os decía.
Tenerlo de la base de datos y eso.
Pero bueno.
Vamos a leerlo.
A manija.
Con lo que tenemos aquí en el texto.
Lo leemos a manija.
Esto lo arreglaremos.
Y lo vamos a hacer con el tema este de Cloudflare.
Porque me parece que tiene muy muy buena pinta.
OpenAI.apkey.chat.completions.create.
Y aquí le tenemos que decir qué modelo queremos utilizar.
Vamos a necesitar utilizar.
Vamos a utilizar este.
Que es de 16.000 tokens.
Que creo que habrá suficiente.
Y aquí ahora le decimos el mensaje que queremos utilizar aquí con OpenAI.
¿Vale?
Entonces le vamos a tener que decir.
No.
Speaker.
Speaker.
Que bueno.
El sistema.
Y le vamos a explicar aquí qué es lo que tiene que hacer.
Entonces le decimos.
A ver.
Tengo por aquí.
T-t-t-t-t-t-t-t-t-t-t.
Tengo por aquí en algún sitio.
A ver.
Por aquí.
Vale.
Tengo por aquí una cosa ya preparada para explicarle lo que tiene que hacer.
Eres un investigador español experimentado.
Experto en interpretar y responder preguntas.
Basadas en las fuentes proporcionadas.
Utilizando una respuesta concisa para una pregunta que no sé qué.
No sé cuánto.
Bueno.
Pues ya está.
Ay.
No es message.
Es messages.
¿Vale?
Lo que vamos a hacer es pasarle entre etiquetas el contenido.
Pero ya os digo que esto lo cambiaremos.
Porque creo que tiene más sentido hacerlo de la otra forma con embeddings.
Y que saldría mejor.
¿Vale?
Le decimos el usuario.
Y al usuario, pues aquí le pasamos por un lado en el contenido.
Vamos a poner el contexto, que sería el texto.
Y luego la cuestión, que es lo que le estamos pasando del usuario.
¿Vale?
Y aquí ya tendríamos...
Bueno, no me acuerdo cómo es la respuesta.
Ahora lo veremos.
A ver.
Response.
Porque creo que esto como tiene tipado...
A ver.
Response.choices.foreach.
Ah, pues será a cero.
Porque solo puede haber una.
FinishReasonMessage.content.
Vale.
Ya está.
Esta sería.
Lo malo es que va a tardar un poco.
¿Y por qué?
Porque no estamos haciendo streaming.
Esto es lo que os comentaba del streaming.
Que mola más.
Cuando haces streaming de la respuesta, como en chatGPT, que va escribiendo mientras le va recibiendo.
Porque de esta forma queda mucho mejor.
Y va mucho, mucho, mucho más rápido.
¿Vale?
Pero bueno.
Al menos para ver que si esto está funcionando.
Para pasarle la idea.
Hemos dicho que esto es un get.
Aquí en el chat.
¿Vale?
Esto lo tenemos que poner aquí.
Y hemos dicho que aquí const new.
Bueno, const API URL.
New URL barra API barra ask.
¿Vale?
API.
Le ponemos la idea.
La cuestión.
Y aquí podemos utilizar la API URL punto href.
Y ya con esto tendríamos.
La cuestión la sacamos de aquí.
La idea la sacamos de la store.
Y con esto ya lo tendríamos.
Ah, bueno.
Esto no lo necesitamos.
Porque ya hemos dicho que iba a ser con get.
¿Vale?
Y ya está.
Para no tener que estar subiendo todo el rato el archivo.
Porque si no va a ser un poco rollo.
Voy a hacer una cosa.
Voy a hacer.
Esto es una cosa que está muy chula de tener un store.
O sea, de tener un estado.
Porque lo que podemos hacer aquí es forzar.
De que en lugar de ir al init.
Vamos directamente al chat mode.
Y en lugar de esperar a que esto nos lo pasen.
Lo bueno es que le puedo decir.
Vale.
La idea es esta.
Y así podemos simular ya.
Que realmente.
Ya como si ya hubiéramos subido el archivo.
En lugar de estar subiendo el archivo todo el rato.
Esta es la típica cosa.
Que tiene un montón de sentido para mejorar.
Para evitar este tipo de cosas.
De hacer manualmente todo el rato lo mismo.
¿Sabes?
El utilizar el estado.
Cambiarlo y forzarlo.
Para simular que estás en un punto ya.
Vale.
Aquí me ha dado un error.
Cannot prime server URLs before server listen is called.
Bueno.
Yo creo que eso.
Uy.
Uy.
Casi.
Casi se ve.
Casi.
¿Qué es esto de tracking?
Tracking.
¿Qué es esto?
Enable tracking para ver el uso no sé qué, no sé cuánto.
Ah, amigo.
Interesante.
Uy.
Por un momento pensaba que se iba a colar ahí la API.
Vale.
Vale.
Pues.
¿Vale?
¿Veis?
Ya.
Refresco directamente.
Y ya estoy en este paso.
Y así no me tengo que preocupar de pasar por todos los pasos y todo esto.
Entonces.
Vamos al chat.
Es ver chat.
Ta, ta, ta.
Y aquí he visto que se me ha escapado esto.
¿Vale?
Y aquí vamos a ver.
¿De qué trata el documento?
Vamos a ver si falla.
Ha fallado algo, ¿eh?
Ah, vale.
Porque no se puede construir una URL así.
La madre que lo parió.
Pensaba que me iba a dejar construir una URL así, pero no.
No cuela.
No cuela.
Bueno, a ver.
No pasa nada.
Se podría crear un URL search params.
Search params.
Y aquí hacer esto.
Hubiera estado bien, pero no.
Search params.
Y aquí puedes hacer barra API barra ask.
Y si no me equivoco.
A ver.
¿Ves?
A veces no me acuerdo de las cosas.
Y tengo que probarlas.
Porque si no.
Pongamos que tengo la ID 1.
Y que tengo la cuestión.
Hola.
Esto, si no me equivoco mal.
Search params punto.
Tu string.
Vale.
No le deja el...
Vale.
Pues ya está.
Entonces, esto lo ponemos aquí.
Esto aquí.
Y así ya tenemos los query params.
¿Que por qué no lo hago a mano?
Bueno, porque me gusta utilizar los métodos.
Porque me parece que es menos hacky.
¿Vale?
Y ya estaría.
Vale.
Entonces, ahora sí que esto se supone...
¿De qué trata el documento?
Esperando respuesta.
Vale.
Apetado.
Hostia, incorrecta.
Incorrecta la API key.
No sé qué, no sé cuánto.
Vale, vale, vale.
Esperen, esperen que la hemos liado con la API key.
Esperen que la hemos liado con la API key.
Vale.
No la hemos liado con la API key, sino que es el problema que siempre tengo.
Que siempre tengo de que...
De que me está intentando utilizar una API key que no es.
Eso es porque tengo una API key a nivel del sistema.
Y entonces siempre me da este problemilla.
Pero ahora ya la he arreglado.
¿Vale?
Vamos a ver ahora.
¿De qué trata el documento?
¿Vale?
Pero, claro.
¿Ves que tarda mucho?
Tarda mucho porque no estamos haciendo streaming.
¡Hostia!
¡Ay!
Claro.
Espérate.
Espérate.
¿Por qué?
No he puesto en el chat cuando recupero esto.
Aquí.
Este, claro.
He puesto aquí answer, pero esto no sé lo que está devolviendo esto.
Vamos a poner aquí un console.log.
Un momento.
Console.log.
Response.
Y ahora veremos esto.
A ver, yo entiendo que la response directamente sería el string.
Lo voy a poner para que directamente me lo diga.
¿Vale?
Y a ver.
Y además voy a arreglar esto del esperando respuesta.
Este loading que me está poniendo un poquito de...
Me da un poco...
Ah.
Mira, ahora sí.
Response.
El documento trata sobre el lenguaje JavaScript.
¿Vale?
Vale.
Pues al menos ya tendríamos ahí, ¿no?
El documento trata sobre el lenguaje JavaScript os útiles para aprender.
Object, object.
La madre que me parió.
La madre que me parió.
A ver.
¿Es porque esto...?
Ah.
Porque es que esto devuelve el response como un objeto.
Vale, vale.
Vale.
Madre mía.
Bueno.
Vamos a hacer una cosa para que esto quede bien.
Porque si no queda un poco raro.
¿Veis que tarda mucho?
Tarda mucho realmente.
¿Ves?
Lo ideal aquí es que vaya escribiendo.
O sea, que se vaya viendo cómo va escribiendo.
Y para eso, el documento trata sobre el lenguaje JavaScript y su uso.
¿Qué editores recomienda?
Entonces, para arreglar esto, lo ideal...
Ah, mira.
Tendría que quitar la respuesta.
Algunos editores de código que se recomiendan son...
¿Ves?
Ya le podemos estar haciendo preguntas que sea básicamente del PDF.
Ahora, lo malo es que deberíamos mejorar un poquito la experiencia.
¿Sabes?
Lo malo es que tiene un poquito de trabajo.
Porque si nosotros aquí activamos el stream, esto hace que ahora no podamos devolver la respuesta exactamente igual.
Por suerte, tengo aquí una utilidad, ¿vale?
Que ya la tengo preparada de tantas veces que he hecho esto.
Que se llama SSE.
Esto es una utilidad que básicamente lo que te permite es hacer una respuesta, un server-side event.
O sea, puedes ir devolviendo respuestas desde el servidor al cliente.
Esto lo hemos hecho un montón de veces porque esto lo que hace un stream de datos.
Pero bueno, que no es muy importante lo que hace por dentro.
Lo hemos explicado muchas veces en stream.
Lo hemos explicado aquí en directo unas cuantas veces.
Pero básicamente es que en lugar de hacer una vez la respuesta, no paras de enviar la respuesta una y otra vez.
Vas enviando la respuesta todo el rato.
Entonces, para utilizar esto, lo que vamos a hacer aquí es importar el SSE, ¿vale?
Que ya veo que no me lo está pillando.
Response SS, ¿vale?
Y lo pillamos de Utils SS, ¿vale?
Y este response SSE, vamos a quitar esto, esto de aquí, esto lo quitamos.
Y lo que vamos a hacer es responder directamente con esto.
Ahora, aquí dentro habría que pasarle por un lado la request y aquí dentro es que tenemos que ir enviando los chunks.
Pero claro, para poder tener los chunks vamos a mover esto aquí dentro, ¿vale?
Aquí tendríamos la respuesta.
Y ahora que tenemos la respuesta, lo que tenemos que hacer es iterar cada vez que tengamos una respuesta.
Esto lo quitamos, ¿vale?
Y hacemos un for await.
Cada vez que tenemos una parte de la respuesta, pues lo que hacemos es send event y enviamos part.choices 0.delta.content.
Ya está.
Ya está.
Hasta ahí.
Tenemos que transformar esto en una sync, ¿vale?
Y ya lo tendríamos.
Y esto lo que va a hacer es ir respondiendo.
Esta sería la parte del servidor.
O sea, ya tendríamos la parte del servidor.
Tendríamos que enviar aquí que el evento ha terminado, que podemos poner end.
No sé si ponerlo...
No sé, da igual.
Le podéis llamar aquí lo que queráis.
Lo importante es que en el cliente detectemos esto y lo hagamos también, ¿vale?
Entonces, ahora lo que está haciendo la respuesta es enviarla a trozos, ¿vale?
Estamos streameando los datos.
Es como streamear un vídeo, pues lo mismo, pero con los datos.
Y lo que vamos a hacer aquí en el step chat, en lugar de hacer un fetch, aquí lo que tenemos que hacer es un event source.
O sea, tenemos que ir a escuchar.
Tenemos que crear aquí un event source.
Event source.
New event source.
Y utilizamos exactamente la misma URL que habíamos hecho antes.
La misma.
Pues la hacemos aquí, ¿vale?
Entonces, vamos a estar escuchando los eventos de este sitio, ¿vale?
Ahora, en lugar de tener aquí la respuesta y todo esto, esto ya no lo tenemos.
No lo tenemos así.
Lo que tenemos es que cada vez que el event source le llegue un mensaje, aquí tendríamos el evento.
Y aquí, pues, podemos ir cambiando.
Pues, vamos a quitar el loading, obviamente, porque ya está escribiendo.
Vamos a recuperar los datos de entrada.
Lo hacemos con el JSON pass event.data, ¿vale?
Aquí tendríamos los datos.
Si el incoming data es igual a que termina, que lo tendríamos aquí, lo que hemos puesto aquí.
Si termina, pues le vamos a decir que cierre el event source.
Close.
Y hacemos un return.
Y si no, lo que hacemos es que el mensaje lo vaya incrementando, ¿vale?
El message, que es el estado que tenemos en el chat, este answer, answer, que he puesto message, el answer, vamos añadiendo el mensaje que va llegando al answer.
Y entonces deberíamos ver cómo va escribiendo el chat, ¿vale?
Y creo que con esto debería estar.
A ver, ¿de qué trata el documento?
¿Veis?
Mientras va recibiendo la respuesta, la va escribiendo.
Lo cual está muy chulo porque así, hostia, se ha petado.
Está mal el estilo ese.
Pero bueno, lo importante es que ahora, a ver, ¿para qué sirve JavaScript según el documento?
Bueno, ¿para qué sirve JavaScript?
Vamos a decirle.
¿Para qué sirve JavaScript?
A ver si es capaz de detectar.
Ah, mira, deberíamos, deberíamos, cuando le doy, aquí deberíamos borrar la respuesta también.
El answer lo vamos a dejar vacío, ¿no?
¿Para qué sirve JavaScript?
Fue creado para dar vida a las páginas web.
Claro, ¿por qué pone eso?
Eso lo pone porque si vamos aquí y buscamos para qué sirve JavaScript.
¿Qué es JavaScript?
JavaScript fue creado para dar vida a las páginas web.
JavaScript fue creado para dar vida a las páginas web.
¿Veis?
O sea, que está utilizando realmente, que lo está haciendo bien.
Está utilizando realmente el contenido de aquí.
¿Qué puede hacer JavaScript en el navegador?
¿Qué no puede hacer?
Yo qué sé.
A ver, le puedo preguntar algo muy específico.
Claro, si le pregunto, ¿cuántas calorías tiene una naranja?
Me debería decir.
No lo sé.
El texto proporcionado no contiene información sobre las calorías de una naranja.
O sea, solo nos va a contestar justamente sobre nuestro documento.
Lo cual está bastante bien.
Todavía lo podemos mejorar bastante.
Lo podemos mejorar bastante.
Las cosas como son.
Os dejo aquí chatea con un PDF.
¿Vale?
Creamos el repositorio.
Os recuerdo, Cloudinary, que es un servicio que está muy chulo para subir imágenes, vídeos y ya veis que también hasta PDFs, que si queréis más coins, ¿vale?
Si tenéis, porque ya sabéis que esto va como con unos créditos, recordad que podéis entrar aquí a la URL esta de MidudevCredits, que he ido compartiendo todo el rato en el chat.
Y que lo que podéis hacer, mira, aquí tenéis una demo buenísima de lo que podéis hacer.
Podéis pedir créditos extra gratis y además entráis al sorteo de tres cupones de 50 dólares, que yo creo que lo anunciaremos seguramente ya mañana.
O sea, que aprovechad, no esperéis, entrad con vuestro correo, dejáis vuestro nombre de Cloudinary, vuestro Cloud Name y así pues entráis al sorteo.
Y además os ganáis unos créditos, que está bastante buenísimo.
Pues eso, amigos, espero que os haya gustado. A mí me parece súper interesante, así que, no sé, ya podéis chatear con PDF, pero creo que todavía podemos hacer mejores cosas.
Por ejemplo, utilizar una inteligencia artificial local, utilizar Langchain, Vectores, Embeddings, para mejorar todavía esto.
Funciona bien, pero si el PDF es muy grande seguramente no saldría a cuenta.
Pero bueno, como para empezar el proyecto, yo creo que ya está bien.
Además hemos utilizado Svelte, que muchas veces me decís, ¿qué Svelte? Pues ahí tienes Svelte.