En el dinámico panorama de la tecnología, donde la innovación da forma continuamente a los límites de lo posible, la inteligencia artificial (IA) nunca deja de cautivar nuestra imaginación.
La IA se refiere a la simulación de procesos de inteligencia humana por parte de sistemas informáticos. Estos procesos incluyen tareas como el aprendizaje, el razonamiento, la resolución de problemas, la percepción, la comprensión del lenguaje y la toma de decisiones.
En la actualidad, personas y empresas han desarrollado y entrenado varios modelos de IA para realizar determinadas tareas mejor que los humanos en tiempo real. Entre las innumerables aplicaciones de la IA, un área especialmente interesante es la generación de imágenes con IA.
Lo que Estás Construyendo
Esta guía explica cómo construir una aplicación React que se integre perfectamente con la API OpenAI DALL-E a través de un backend Node.js y genere imágenes fascinantes basadas en indicaciones textuales.
Requisitos Previos
Para seguir este proyecto, debes tener:
- Conocimientos fundamentales de HTML, CSS y JavaScript
- Conocimientos básicos de React y Node.js
- Node.js y npm (Node Package Manager) o yarn instalados en tu ordenador
¿Qué es la API OpenAI DALL-E?
La API OpenAI es una plataforma basada en la nube que permite a los desarrolladores acceder a los modelos de IA preentrenados de OpenAI, como DALL-E y GPT-3 (utilizamos este modelo para construir un clon de ChatGPT con el código de este repositorio Git). Permite a los desarrolladores añadir funciones de IA como el resumen, la traducción, la generación de imágenes y la modificación a sus programas sin necesidad de desarrollar y entrenar sus modelos.
Para utilizar la API de OpenAI, crea una cuenta utilizando tu cuenta de Google o tu correo electrónico en el sitio web de OpenAI y obtén una clave API. Para generar una clave API, haz clic en Personal en la esquina superior derecha del sitio web y, a continuación, selecciona Ver claves API.
Haz clic en el botón Crear nueva clave secreta, y guarda la clave en algún sitio. La utilizarás en esta aplicación para interactuar con la API DALL-E de OpenAI.
Configurar el Entorno de Desarrollo
Puedes crear una aplicación React desde cero y desarrollar tu propia interfaz, o puedes coger nuestra plantilla de inicio Git siguiendo estos pasos:
- Visita el repositorio GitHub de este proyecto.
- Selecciona Usar esta plantilla > Crear un nuevo repositorio para copiar el código de inicio en un repositorio dentro de tu cuenta de GitHub (marca la casilla para incluir todas las ramas).
- Extrae el repositorio a tu ordenador local y cambia a la rama starter-files utilizando el comando :
git checkout starter-files
.
- Instala las dependencias necesarias ejecutando el comando
npm install
.
Una vez completada la instalación, puedes lanzar el proyecto en tu ordenador local con npm run start
. Esto hace que el proyecto esté disponible en http://localhost:3000/.
Comprender los Archivos del Proyecto
En este proyecto, hemos añadido todas las dependencias necesarias para tu aplicación React. Aquí tienes un resumen de lo que se ha instalado:
- file-server: Esta biblioteca de utilidades simplifica el proceso de descarga de las imágenes generadas. Está vinculada al botón de descarga, lo que garantiza una experiencia de usuario fluida.
- uuid: Esta biblioteca asigna una identificación única a cada imagen. Esto evita cualquier posibilidad de que las imágenes compartan el mismo nombre de archivo por defecto, manteniendo el orden y la claridad.
- react-icons: Integrada en el proyecto, esta biblioteca incorpora iconos sin esfuerzo, mejorando el atractivo visual de tu aplicación.
En el core de tu aplicación React se encuentra la carpeta src. Aquí es donde se aloja el código JavaScript esencial para Webpack. Vamos a entender los archivos y carpetas de la carpeta src:
- assets: En este directorio encontrarás las imágenes y el cargador gif que se utilizan en todo el proyecto.
- data: Esta carpeta contiene un archivo index.js que exporta un array de 30 prompts. Estos prompts pueden utilizarse para generar imágenes diversas y aleatorias. Siéntete libre de editarlo.
- index.css: Aquí es donde se almacenan los estilos utilizados en este proyecto.
Comprender la Carpeta Utils
Dentro de esta carpeta, el archivo index.js define dos funciones reutilizables. La primera función aleatoriza la selección de prompts que describen varias imágenes que se pueden generar.
import { randomPrompts } from '../data';
export const getRandomPrompt = () => {
const randomIndex = Math.floor(Math.random() * randomPrompts.length);
const randomPrompt = randomPrompts[randomIndex];
return randomPrompt;
}
La segunda función gestiona la descarga de las imágenes generadas aprovechando la dependencia file-saver. Ambas funciones se han creado para ofrecer modularidad y eficacia, y pueden importarse cómodamente en componentes cuando sea necesario.
import FileSaver from 'file-saver';
import { v4 as uuidv4 } from 'uuid';
export async function downloadImage(photo) {
const _id = uuidv4();
FileSaver.saveAs(photo, `download-${_id}.jpg`);
}
En el código anterior, la dependencia uuid da a cada archivo de imagen generado un ID único, para que no tengan el mismo nombre de archivo.
Comprender los Componentes
Son pequeños bloques de código separados para que tu código sea fácil de mantener y comprender. Para este proyecto, se crearon tres componentes: Header.jsx, Footer.jsx y Form.jsx. El componente principal es el componente Form, donde se recibe la entrada y se pasa al archivo App.jsx con la función generateImage
añadida como evento onClick
al botón Generar imagen.
En el componente Form, se crea un estado para almacenar y actualizar la solicitud. Además, una función te permite hacer clic en un icono aleatorio para generar los prompts aleatorios. Esto es posible gracias a la función handleRandomPrompt
, que utiliza la función getRandomPrompt
que ya has configurado. Cuando haces clic en el icono, se obtiene un prompt aleatorio y se actualiza el estado con él:
const handleRandomPrompt = () => {
const randomPrompt = getRandomPrompt();
setPrompt(randomPrompt)
}
Comprender el Archivo App.jsx
Aquí es donde reside la mayor parte del código. Aquí se reúnen todos los componentes. También hay un área designada para mostrar la imagen generada. Si aún no se ha generado ninguna imagen, se muestra una imagen de marcador de posición (Imagen de vista previa).
Dentro de este archivo, se gestionan dos estados:
isGenerating
: Mantiene un registro de si se está generando actualmente una imagen. Por defecto, se establece en falso.generatedImage
: Este estado almacena información sobre la imagen que se ha generado.
Además, se importa la función de utilidad downloadImage
, que te permite activar la descarga de la imagen generada cuando pulsas el botón Descargar:
<button
className="btn"
onClick={() => downloadImage(generatedImage.photo)}
>
Ahora que ya conoces los archivos de inicio y has configurado tu proyecto. Empecemos a manejar la lógica de esta aplicación.
Generar Imágenes con la API DALL-E de OpenAI
Para aprovechar las capacidades de la API DALL-E de OpenAI, utilizarás Node.js para establecer un servidor. Dentro de este servidor, crearás una ruta POST. Esta ruta se encargará de recibir el texto del prompt enviado desde tu aplicación React y de utilizarlo para generar una imagen.
Para empezar, instala las dependencias necesarias en el directorio de tu proyecto ejecutando el siguiente comando:
npm i express cors openai
Además, instala las siguientes dependencias como dependencias dev. Estas herramientas te ayudarán a configurar tu servidor Node.js:
npm i -D dotenv nodemon
Las dependencias instaladas se explican a continuación:
- express: Esta biblioteca ayuda a crear un servidor en Node.js.
- cors: CORS facilita la comunicación segura entre diferentes dominios.
- openai: Esta dependencia te permite acceder a la API DALL-E de OpenAI.
- dotenv: dotenv ayuda a gestionar las variables de entorno.
- nodemon: nodemon es una herramienta de desarrollo que supervisa los cambios en tus archivos y reinicia automáticamente el servidor.
Una vez realizada la instalación, crea un archivo server.js en el root de tu proyecto. Aquí es donde se almacenará todo el código de tu servidor.
En el archivo server.js, importa las bibliotecas que acabas de instalar y crea una instancia de ellas:
// Import the necessary libraries
const express = require('express');
const cors = require('cors');
require('dotenv').config();
const OpenAI = require('openai');
// Create an instance of the Express application
const app = express();
// Enable Cross-Origin Resource Sharing (CORS)
app.use(cors());
// Configure Express to parse JSON data and set a data limit
app.use(express.json({ limit: '50mb' }));
// Create an instance of the OpenAI class and provide your API key
const openai = new OpenAI({
apiKey: process.env.OPENAI_API_KEY,
});
// Define a function to start the server
const startServer = async () => {
app.listen(8080, () => console.log('Server started on port 8080'));
};
// Call the startServer function to begin listening on the specified port
startServer();
En el código anterior, importa las bibliotecas necesarias. A continuación, establece una instancia de la aplicación Express utilizando const app = express();
. Después, habilita CORS. A continuación, se configura Express para procesar los datos JSON entrantes, especificando un límite de tamaño de datos de 50mb
.
A continuación, se crea una instancia de la clase OpenAI utilizando tu clave API OpenAI. Crea un archivo .env en el root de tu proyecto y añade tu clave API utilizando la variable OPENAI_API_KEY
. Por último, define una función asíncrona startServer
y llámala para poner en marcha el servidor.
Ahora ya tienes configurado tu archivo server.js. Vamos a crear una ruta POST que puedas utilizar en tu aplicación React para interactuar con este servidor:
app.post('/api', async (req, res) => {
try {
const { prompt } = req.body;
const response = await openai.images.generate({
prompt,
n: 1,
size: '1024x1024',
response_format: 'b64_json',
});
const image = response.data[0].b64_json;
res.status(200).json({ photo: image });
} catch (error) {
console.error(error);
}
});
En este código, la ruta se establece en /api
, y está diseñada para gestionar las peticiones POST entrantes. Dentro de la función de devolución de llamada de la ruta, recibes los datos enviados desde tu aplicación React utilizando req.body
– concretamente el valor prompt
.
A continuación, se invoca el método images.generate
de la biblioteca OpenAI. Este método toma el prompt proporcionado y genera una imagen como respuesta. Parámetros como n
determinan el número de imágenes a generar (aquí, sólo una), size
especifica las dimensiones de la imagen, y response_format
indica el formato en el que debe proporcionarse la respuesta (b64_json
en este caso).
Tras generar la imagen, extraes los datos de la imagen de la respuesta y los almacenas en la variable image
. A continuación, envías una respuesta JSON a la aplicación React con los datos de la imagen generada, estableciendo el estado HTTP en 200
(indicando éxito) utilizando res.status(200).json({ photo: image })
.
Si se produce algún error durante este proceso, se ejecuta el código dentro del bloque catch
, registrando el error en la consola para su depuración.
¡Ahora el servidor está listo! Vamos a especificar el comando que se utilizará para ejecutar nuestro servidor en el archivo package.json objeto scripts
:
"scripts": {
"dev:frontend": "react-scripts start",
"dev:backend": "nodemon server.js",
"build": "react-scripts build",
},
Ahora, cuando ejecutes npm run dev:backend
, tu servidor se iniciará en http://localhost:8080/, mientras que si ejecutas npm run dev:frontend
, tu aplicación React se iniciará en http://localhost:3000/. Asegúrate de que ambas se ejecutan en terminales diferentes.
Hacer Peticiones HTTP desde React al Servidor Node.js
En el archivo App.jsx, crearás una función generateImage
que se activa cuando se pulsa el botón Generar Imagen en el componente Form.jsx. Esta función acepta dos parámetros: prompt
y setPrompt
del componente Form.jsx.
En la función generateImage
, haz una petición HTTP POST al servidor Node.js:
const generateImage = async (prompt, setPrompt) => {
if (prompt) {
try {
setIsGenerating(true);
const response = await fetch(
'http://localhost:8080/api',
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
prompt,
}),
}
);
const data = await response.json();
setGeneratedImage({
photo: `data:image/jpeg;base64,${data.photo}`,
altText: prompt,
});
} catch (err) {
alert(err);
} finally {
setPrompt('');
setIsGenerating(false);
}
} else {
alert('Please provide proper prompt');
}
};
En el código anterior, comprueba si el parámetro prompt
tiene un valor y, a continuación, establece el estado isGenerating
en true
, ya que la operación se está iniciando. Esto hará que el cargador aparezca en la pantalla porque en el archivo App.jsx, tenemos este código controlando la visualización del cargador:
{isGenerating && (
<div> className="loader-comp">
<img src={Loader} alt="" className='loader-img' />
</div>
)}
A continuación, utiliza el método fetch()
para hacer una petición POST al servidor utilizando http://localhost:8080/api — esta es la razón por la que instalamos CORS ya que estamos interactuando con una API en otra URL. Utilizamos la solicitud como cuerpo del mensaje. A continuación, extrae la respuesta devuelta por el servidor Node.js y ponla en el estado generatedImage
.
Una vez que el estado generatedImage
tenga un valor, se mostrará la imagen:
{generatedImage.photo ? (
<img
src={generatedImage.photo}
alt={generatedImage.altText}
className="imgg ai-img"
/>
) : (
<img
src={preview}
alt="preview"
className="imgg preview-img"
/>
)}
Este es el aspecto que tendrá tu archivo App.jsx completo:
import { Form, Footer, Header } from './components';
import preview from './assets/preview.png';
import Loader from './assets/loader-3.gif'
import { downloadImage } from './utils';
import { useState } from 'react';
const App = () => {
const [isGenerating, setIsGenerating] = useState(false);
const [generatedImage, setGeneratedImage] = useState({
photo: null,
altText: null,
});
const generateImage = async (prompt, setPrompt) => {
if (prompt) {
try {
setIsGenerating(true);
const response = await fetch(
'http://localhost:8080/api',
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
prompt,
}),
}
);
const data = await response.json();
setGeneratedImage({
photo: `data:image/jpeg;base64,${data.photo}`,
altText: prompt,
});
} catch (err) {
alert(err);
} finally {
setPrompt('');
setIsGenerating(false);
}
} else {
alert('Please provide proper prompt');
}
};
return (
<div className='container'>
<Header />
<main className="flex-container">
<Form generateImage={generateImage} prompt={prompt} />
<div className="image-container">
{generatedImage.photo ? (
<img
src={generatedImage.photo}
alt={generatedImage.altText}
className="imgg ai-img"
/>
) : (
<img
src={preview}
alt="preview"
className="imgg preview-img"
/>
)}
{isGenerating && (
<div className="loader-comp">
<img src={Loader} alt="" className='loader-img' />
</div>
)}
<button
className="btn"
onClick={() => downloadImage(generatedImage.photo)}
>
Download
</button>
</div>
</main>
<Footer />
</div>
);
};
export default App;
Despliega tu Aplicación Full-Stack en Kinsta
Hasta ahora, has construido con éxito una aplicación React que interactúa con Node.js, lo que la convierte en una aplicación full-stack. Ahora vamos a desplegar esta aplicación en Kinsta.
En primer lugar, configura el servidor para que sirva los archivos estáticos generados durante el proceso de construcción de la aplicación React. Esto se consigue importando el módulo path
y utilizándolo para servir los archivos estáticos:
const path = require('path');
app.use(express.static(path.resolve(__dirname, './build')));
Cuando ejecutes el comando npm run build && npm run dev:backend
, tu aplicación React full stack se cargará en http://localhost:8080/. Esto se debe a que la aplicación React se compila en archivos estáticos dentro de la carpeta build. A continuación, estos archivos se incorporan a tu servidor Node.js como directorio estático. En consecuencia, cuando ejecutes tu servidor Node, la aplicación estará accesible.
Antes de desplegar tu código en el proveedor Git que hayas elegido (Bitbucket, GitHub o GitLab), recuerda modificar la URL de solicitud HTTP en tu archivo App.jsx. Cambia http://localhost:8080/api
por /api
, ya que se añadirá un prefijo a la URL.
Por último, en tu archivo package.json, añade un comando script para el servidor Node.js que se utilizaría para el despliegue:
"scripts": {
// …
"start": "node server.js",
},
A continuación, envía tu código a tu proveedor Git preferido y despliega tu repositorio en Kinsta siguiendo estos pasos:
- Accede a tu cuenta Kinsta en el panel MyKinsta.
- Selecciona Aplicación en la barra lateral izquierda y haz clic en el botón Añadir Aplicación.
- En el modal que aparece, elige el repositorio que quieres desplegar. Si tienes varias ramas, puedes seleccionar la rama deseada y dar un nombre a tu aplicación.
- Selecciona una de las ubicaciones de centros de datos disponibles.
- Añade el
OPENAI_API_KEY
como variable de entorno. Kinsta creará un Dockerfile automáticamente por ti. - Por último, en el campo de comando de inicio, añade
npm run build && npm run start
. Kinsta instalará las dependencias de tu aplicación desde package.json, y luego construirá y desplegará tu aplicación.
Resumen
En esta guía, has aprendido a aprovechar la potencia de la API DALL-E de OpenAI para la generación de imágenes. También has aprendido a trabajar con React y Node.js para construir una aplicación básica full-stack.
Las posibilidades son infinitas con la IA, ya que cada día se introducen nuevos modelos, y puedes crear proyectos increíbles que se pueden desplegar en el Alojamiento de Aplicaciones de Kinsta.
¿Qué modelo te gustaría explorar y sobre qué proyecto te gustaría que escribiéramos próximamente? Compártelo en los comentarios.
Deja una respuesta