A medida que crece el uso de chatbots y asistentes virtuales, muchas empresas y desarrolladores buscan formas de crear sus propios chatbots con IA. ChatGPT es uno de esos chatbots, creado por OpenAI, que es capaz de entablar conversaciones similares a las humanas y responder a una amplia gama de preguntas.

Qué Vas a Construir

En este tutorial, aprenderás a construir una aplicación clon de ChatGPT utilizando React y la API de OpenAI. Si quieres probar suerte en un proyecto divertido y atractivo durante el fin de semana, esta es una gran oportunidad para sumergirte en React y OpenAI.

También aprenderás a desplegar directamente desde tu repositorio de GitHub a la plataforma de Alojamiento de Aplicaciones de Kinsta, que proporciona un dominio .kinsta.app gratuito para que tu proyecto se ponga en marcha rápidamente. Y con la prueba gratuita de Kinsta y el nivel Hobby, puedes empezar fácilmente sin ningún coste.

Aquí tienes una demostración en directo de la aplicación clon de ChatGPT.

Aplicación clon de ChatGPT
Aplicación clon de ChatGPT

Si quieres inspeccionar este proyecto más de cerca, puedes acceder a su repositorio de GitHub.

Alternativamente, usando esta plantilla de proyecto de inicio, puedes seleccionar Usar esta plantilla > Crear un nuevo repositorio — esto copiará el código de inicio en un nuevo repositorio. Este proyecto de inicio viene con elementos fundamentales como estilos, enlace Font Awesome CDN, paquete OpenAi, y la estructura básica para ayudarte a empezar.

Requisitos/Prerrequisitos

Este tutorial está diseñado para ser una experiencia «follow-along» (para seguir) . Por lo tanto, se recomienda que tengas lo siguiente para poder programar juntos con facilidad:

¿Qué Es la API OpenAI?

La API de OpenAI es una plataforma basada en la nube que permite a los desarrolladores acceder a los modelos lingüísticos de OpenAI, como el GPT-3, a través de una API. Permite a los desarrolladores añadir a sus aplicaciones funciones de procesamiento del lenguaje natural, como completado de texto, análisis de sentimientos, resumen y traducción, sin necesidad de desarrollar y entrenar sus modelos.

Para utilizar la API de OpenAI, los desarrolladores deben crear una cuenta en el sitio web de OpenAI y obtener una clave API. La clave de la API se utiliza para autenticar las solicitudes de la API y hacer un seguimiento de su uso.

Una vez obtenida la clave de la API, los desarrolladores pueden utilizarla para enviar texto al modelo lingüístico y recibir respuestas.

¿Por Qué React?

React es una popular biblioteca JavaScript para construir interfaces de usuario. Según la encuesta a desarrolladores de Stack Overflow de 2022, es la segunda tecnología web más utilizada, con el 42,62% de la cuota de mercado.

React permite a los desarrolladores crear componentes declarativos que representan diferentes partes de la interfaz de usuario. Estos componentes se definen mediante una sintaxis llamada JSX, que es una combinación de JavaScript y HTML.

Gracias a su gran ecosistema de bibliotecas y kits de componentes, los desarrolladores pueden trabajar fácilmente con APIs como la API OpenAI e integrarlas, para construir complejas interfaces de chat, y eso es lo que lo convierte en una excelente opción para construir una aplicación clonada de ChatGPT.

Cómo Configurar Tu Entorno de Desarrollo React

La mejor forma de instalar React o crear un proyecto React es instalarlo con create-react-app. Un requisito previo es tener Node.js instalado en tu máquina. Para confirmar que tienes Node instalado, ejecuta el siguiente comando en tu terminal.

node -v

Si aparece una versión, es que existe. Para utilizar npx, tendrás que asegurarte de que tu versión de Node no es inferior a v14.0.0, y tu versión de NPM no es inferior a v5.6; de lo contrario, podrías tener que actualizarla ejecutando npm update -g. Una vez que te hayas familiarizado con npm, ya puedes configurar un proyecto React ejecutando el siguiente comando:

npx create-react-app chatgpt-clone

Nota: «chatgpt-clone» es el nombre de la aplicación que estamos creando, pero puedes cambiarlo por cualquier nombre de tu elección.

El proceso de instalación puede tardar unos minutos. Una vez finalizado con éxito, puedes navegar hasta el directorio e instalar el paquete OpenAI de Node.js, que proporciona un cómodo acceso a la API de OpenAI desde Node.js utilizando el comando que se indica a continuación:

npm install openai

Ahora puedes ejecutar npm start para ver tu aplicación en directo en localhost:3000.

Cuando se crea un proyecto React utilizando el comando create-react-app, se crea automáticamente una estructura de carpetas. La carpeta principal que te concierne es la carpeta src, que es donde tiene lugar el desarrollo. Esta carpeta contiene muchos archivos por defecto, pero sólo deberían preocuparte los archivos App.js, index.js e index.css.

  1. App.js: El archivo App.js es el componente principal de una aplicación React. Normalmente representa el componente de nivel superior que renderiza todos los demás componentes de la aplicación.
  2. index.js: Este archivo es el punto de entrada de tu aplicación React. Es el primer archivo que se carga cuando se abre la aplicación y es responsable de renderizar el componente App.js en el navegador.
  3. index.css: Este archivo es responsable de definir el estilo general y el diseño de tu aplicación React.

Cómo Construir un Clon de ChatGPT con React y la API OpenAI

La aplicación clon de ChatGPT constará de dos componentes para que la aplicación sea más fácil de entender y mantener. Estos dos componentes son:

  1. Sección Formulario: Este componente incluye un campo de área de texto y un botón para que los usuarios interactúen con el chatbot.
  2. Sección de Respuestas: Las preguntas y sus correspondientes respuestas se almacenarán en un array y se mostrarán en esta sección. Recorrerás el array cronológicamente, mostrando primero la más reciente.

Configuración de la Aplicación Clon ChatGPT

En este tutorial, vamos a empezar por construir primero la interfaz de la aplicación y luego podrás implementar funcionalidades para que tu aplicación interactúe con la API de OpenAI. Empieza creando los dos componentes que utilizarás en este tutorial. Para una correcta organización, crearás una carpeta llamada components en la carpeta src donde se almacenarán todos los componentes.

El Componente Sección de Formulario

Este es un formulario simple que consiste en un textarea y un submit button.


// components/FormSection.jsx
const FormSection = () => {

    return (
        <div className="form-section">
            <textarea
                rows="5"
                className="form-control"
                placeholder="Ask me anything..."
            ></textarea>
            <button className="btn">
                Generate Response 🤖
            </button>
        </div>
    )
}

export default FormSection;

Este es el aspecto que se espera que tenga el formulario cuando lo importes a tu archivo App.js:

Componente de sección de formulario para el clon de ChatGPT
Componente de la sección Formulario

El Componente de la Sección de Respuestas

Esta sección es donde se mostrarán todas las preguntas y respuestas. Este es el aspecto que tendrá esta sección cuando también la importes a tu archivo App.js.

Componente de la sección de respuestas del clon ChatGPT
Componente de la sección de respuestas

Obtendrás estas preguntas y respuestas de un array y un bucle para que tu código sea más fácil de leer y mantener.

// components/AnswerSection.jsx

const AnswerSection = () => {
    return (
        <>
            <hr className="hr-line" />
            <div className="answer-container">
                <div className="answer-section">
                    <p className="question">Who is the founder of OpenAi?</p>
                    <p className="answer">OpenAI was founded in December 2015 by Elon Musk, Sam Altman, Greg Brockman, Ilya Sutskever, Wojciech Zaremba, and John Schulman.</p>
                    <div className="copy-icon">
                        <i className="fa-solid fa-copy"></i>
                    </div>
                </div>
            </div>
        </>
    )
}

export default AnswerSection;

La Página de Inicio

Ya tienes ambos componentes creados, pero no aparecerá nada cuando ejecutes tu aplicación porque necesitas importarlos a tu archivo App.js. Para esta aplicación, no implementarás ninguna forma de enrutamiento, lo que significa que el archivo App.js servirá como componente/página de inicio de tu aplicación.

Puedes añadir algo de contenido, como el título y la descripción de tu aplicación, antes de importar los componentes.

// App.js

import FormSection from './components/FormSection';
import AnswerSection from './components/AnswerSection';

const App = () => {
    return (
        <div>
            <div className="header-section">
                <h1>ChatGPT CLONE 🤖</h1>
                <p>
                    I am an automated question and answer system, designed to assist you
                    in finding relevant information. You are welcome to ask me any queries
                    you may have, and I will do my utmost to offer you a reliable
                    response. Kindly keep in mind that I am a machine and operate solely
                    based on programmed algorithms.
                </p>
            </div>

            <FormSection />
            <AnswerSection />
        </div>
    );
};

export default App;

En el código anterior, los dos componentes se importan y se añaden a la aplicación. Cuando ejecutes tu aplicación, éste es el aspecto que tendrá:

Aplicación clon completa de ChatGPT
Aplicación clon completa de ChatGPT

Añadir Funcionalidad e Integrar la API OpenAI

Ya tienes la interfaz de usuario de tu aplicación. El siguiente paso es hacer que la aplicación sea funcional para que pueda interactuar con la API OpenAI y obtener respuestas. En primer lugar, necesitas obtener el valor de tu formulario cuando se envíe, ya que se utilizará para consultar la API OpenAI.

Obtener Datos del Formulario

En React, la mejor forma de almacenar y actualizar datos es utilizar estados. En los componentes funcionales, el hook useState() se utiliza para trabajar con estados. Puedes crear un estado, asignarle el valor de tu formulario y actualizarlo cada vez que cambie su valor. Empecemos importando el hook useState() en el componente FormSection.jsx y, a continuación, creando un estado para almacenar y actualizar newQuestions.

// components/FormSection.jsx

import { useState } from 'react';

const FormSection = ({ generateResponse }) => {
    const [newQuestion, setNewQuestion] = useState('');

    return (
        // Form to submit a new question
    )
}

export default FormSection;

A continuación, puedes asignar el valor del campo textarea al estado y crear un evento onChange() para actualizar el estado cada vez que cambie el valor de la entrada:

<textarea
    rows="5"
    className="form-control"
    placeholder="Ask me anything..."
    value={newQuestion}
    onChange={(e) => setNewQuestion(e.target.value)}
></textarea>

Por último, puedes crear un evento onClick(), para cargar una función cada vez que se pulse el botón de envío. Este método se creará en el archivo App.js y se pasará como props al componente FormSection.jsx con los valores newQuestion y setNewQuestion como argumentos.

<button className="btn" onClick={() => generateResponse(newQuestion, setNewQuestion)}>
    Generate Response 🤖
</button>

Ahora has creado un estado para almacenar y actualizar el valor del formulario, has añadido un método que se pasa como props desde el archivo App.js y has gestionado el evento clic. Este es el aspecto que tendrá el código final:

// components/FormSection.jsx

import { useState } from 'react';

const FormSection = ({ generateResponse }) => {
    const [newQuestion, setNewQuestion] = useState('');

    return (
        <div className="form-section">
            <textarea
                rows="5"
                className="form-control"
                placeholder="Ask me anything..."
                value={newQuestion}
                onChange={(e) => setNewQuestion(e.target.value)}
            ></textarea>
            <button className="btn" onClick={() => generateResponse(newQuestion, setNewQuestion)}>
                Generate Response 🤖
            </button>
        </div>
    )
}

export default FormSection;

El siguiente paso será crear un método en el archivo App.js para manejar todo el proceso de interacción con la API de OpenAI.

Interactuar con la API OpenAI

Para interactuar con la API de OpenAI y obtener claves de API en una aplicación React, debes crear una cuenta de API de OpenAI. Puedes registrarte para obtener una cuenta en el sitio web de OpenAI utilizando tu cuenta de google o tu correo electrónico. Para generar una clave API, haz clic en Personal en la esquina superior derecha del sitio web; aparecerán algunas opciones; haz clic en View API keys.

Acceder a las claves API de OpenAI.
Acceder a las claves API de OpenAI.

Haz clic en el botón Create new secret key, copia la clave en algún lugar ya que la vas a utiliza en esta aplicación para interactuar con OpenAI. Ahora puedes proceder a inicializar OpenAI importando el paquete openai (que ya has instalado) junto con el método de configuración. A continuación, crea una configuración con tu clave generada y utilízala para inicializar OpenAI.

// src/App.js

import { Configuration, OpenAIApi } from 'openai';

import FormSection from './components/FormSection';
import AnswerSection from './components/AnswerSection';

const App = () => {
    const configuration = new Configuration({
        apiKey: process.env.REACT_APP_OPENAI_API_KEY,
    });

    const openai = new OpenAIApi(configuration);

    return (
        // Render FormSection and AnswerSection
    );
};

export default App;

En el código anterior, la clave de la API de OpenAI se almacena como una variable de entorno en el archivo . env. Puedes crear un archivo . env en la carpeta root de tu aplicación y almacenar la clave en la variable REACT_APP_OPENAI_API_KEY.

// .env
REACT_APP_OPENAI_API_KEY = sk-xxxxxxxxxx…

Ahora puedes proceder a crear el método generateResponse en el archivo App.js, y pasar los dos parámetros esperados del formulario que ya has creado para gestionar la solicitud y obtener la respuesta de la API.

// src/App.js

import FormSection from './components/FormSection';
import AnswerSection from './components/AnswerSection';

const App = () => {
    const generateResponse = (newQuestion, setNewQuestion) => {
        // Set up OpenAI API and handle response
    };

    return (
        // Render FormSection and AnswerSection
    );
};

export default App;

Ahora puedes enviar una solicitud a la API OpenAI. La API OpenAI puede realizar muchas operaciones, como preguntas y respuestas (P&R), corrección gramatical, traducción y muchas más. Para cada una de estas operaciones, las opciones son diferentes. Por ejemplo, el valor del motor para preguntas y respuestas es text-davinci-00, mientras que para la traducción SQL es code-davinci-002. No dudes en consultar la documentación de ejemplos de OpenAI para ver los distintos ejemplos y sus opciones.

Para este tutorial, vamos a trabajar sólo con el Q&A, así es como se ve la opción:

{
  model: "text-davinci-003",
  prompt: "Who is Obama?",
  temperature: 0,
  max_tokens: 100,
  top_p: 1,
  frequency_penalty: 0.0,
  presence_penalty: 0.0,
  stop: [""],
}

Nota: He cambiado el valor del prompt.

El prompt es la pregunta que se envía desde el formulario. Esto significa que tendrás que recibirla de la entrada del formulario que estás pasando al método generateResponse como parámetro. Para ello, definirás las opciones y luego utilizarás el operador spread para crear una opción completa que incluya la pregunta:

// src/App.js

import { Configuration, OpenAIApi } from 'openai';
import FormSection from './components/FormSection';
import AnswerSection from './components/AnswerSection';

const App = () => {
    const configuration = new Configuration({
        apiKey: process.env.REACT_APP_OPENAI_API_KEY,
    });

    const openai = new OpenAIApi(configuration);

    const generateResponse = async (newQuestion, setNewQuestion) => {
        let options = {
            model: 'text-davinci-003',
            temperature: 0,
            max_tokens: 100,
            top_p: 1,
            frequency_penalty: 0.0,
            presence_penalty: 0.0,
            stop: ['/'],
        };

        let completeOptions = {
            ...options,
            prompt: newQuestion,
        };

    };

    return (
         // Render FormSection and AnswerSection
    );
};

export default App;

Llegados a este punto, lo que queda es enviar una petición a través del método createCompletion a OpenAI para obtener una respuesta.

// src/App.js

import { Configuration, OpenAIApi } from 'openai';
import FormSection from './components/FormSection';
import AnswerSection from './components/AnswerSection';

import { useState } from 'react';

const App = () => {
    const configuration = new Configuration({
        apiKey: process.env.REACT_APP_OPENAI_API_KEY,
    });

    const openai = new OpenAIApi(configuration);

    const [storedValues, setStoredValues] = useState([]);

    const generateResponse = async (newQuestion, setNewQuestion) => {
        let options = {
            model: 'text-davinci-003',
            temperature: 0,
            max_tokens: 100,
            top_p: 1,
            frequency_penalty: 0.0,
            presence_penalty: 0.0,
            stop: ['/'],
        };

        let completeOptions = {
            ...options,
            prompt: newQuestion,
        };

        const response = await openai.createCompletion(completeOptions);

        console.log(response.data.choices[0].text);
    };

    return (
        // Render FormSection and AnswerSection
    );
};

export default App;

En el código anterior, el texto de la respuesta se mostrará en tu consola. Siéntete libre de probar tu aplicación haciendo cualquier pregunta. El paso final sería crear un estado que contenga la matriz de preguntas y respuestas y luego enviar este array como prop al componente AnswerSection. Este es el aspecto que tendrá el código final de App.js:

// src/App.js
import { Configuration, OpenAIApi } from 'openai';

import FormSection from './components/FormSection';
import AnswerSection from './components/AnswerSection';

import { useState } from 'react';

const App = () => {
    const configuration = new Configuration({
        apiKey: process.env.REACT_APP_OPENAI_API_KEY,
    });

    const openai = new OpenAIApi(configuration);

    const [storedValues, setStoredValues] = useState([]);

    const generateResponse = async (newQuestion, setNewQuestion) => {
        let options = {
            model: 'text-davinci-003',
            temperature: 0,
            max_tokens: 100,
            top_p: 1,
            frequency_penalty: 0.0,
            presence_penalty: 0.0,
            stop: ['/'],
        };

        let completeOptions = {
            ...options,
            prompt: newQuestion,
        };

        const response = await openai.createCompletion(completeOptions);

        if (response.data.choices) {
            setStoredValues([
                {
                    question: newQuestion,
                    answer: response.data.choices[0].text,
                },
                ...storedValues,
            ]);
            setNewQuestion('');
        }
    };

    return (
        <div>
            <div className="header-section">
                <h1>ChatGPT CLONE 🤖</h1>
                    <p>
                        I am an automated question and answer system, designed to assist you
                        in finding relevant information. You are welcome to ask me any
                        queries you may have, and I will do my utmost to offer you a
                        reliable response. Kindly keep in mind that I am a machine and
                        operate solely based on programmed algorithms.
                    </p>
            </div>

            <FormSection generateResponse={generateResponse} />

            <AnswerSection storedValues={storedValues} />
        </div>
    );
};

export default App;

Ahora puedes editar el componente AnswerSection, para que reciba el valor props de App.js y utilizar el método JavaScript Map() para buscar en la matriz storedValues:

// components/AnswerSection.jsx

const AnswerSection = ({ storedValues }) => {
    return (
        <>
            <hr className="hr-line" />
            <div className="answer-container">
                {storedValues.map((value, index) => {
                    return (
                        <div className="answer-section" key={index}>
                            <p className="question">{value.question}</p>
                            <p className="answer">{value.answer}</p>
                            <div className="copy-icon">
                                <i className="fa-solid fa-copy"></i>
                            </div>
                        </div>
                    );
                })}
            </div>
        </>
    )
}

export default AnswerSection;

Cuando ejecutes tu aplicación y la pruebes haciendo preguntas, la respuesta se mostrará a continuación. Pero te darás cuenta de que el botón de copiar no funciona. Tendrás que añadir un evento onClick() al botón, para que active un método que se encargue de la funcionalidad. Puedes utilizar el método navigator.clipboard.writeText() para manejar la funcionalidad. Este es el aspecto que tendrá ahora el componente AnswerSection:

// components/AnswerSection.jsx

const AnswerSection = ({ storedValues }) => {
    const copyText = (text) => {
        navigator.clipboard.writeText(text);
    };

    return (
        <>
            <hr className="hr-line" />
            <div className="answer-container">
                {storedValues.map((value, index) => {
                    return (
                        <div className="answer-section" key={index}>
                            <p className="question">{value.question}</p>
                            <p className="answer">{value.answer}</p>
                            <div
                                className="copy-icon"
                                onClick={() => copyText(value.answer)}
                            >
                                <i className="fa-solid fa-copy"></i>
                            </div>
                        </div>
                    );
                })}
            </div>
        </>
    )
}

export default AnswerSection;

Cuando ejecutes tu aplicación, tu aplicación clon de ChatGPT funcionará perfectamente. Ahora puedes desplegar tu aplicación para acceder a ella online y compartirla con tus amigos.

Cómo Desplegar tu Aplicación React en Kinsta

No basta con crear esta aplicación y dejarla en tus ordenadores locales. Querrás compartirla online, para que otros puedan acceder a ella. Veamos cómo hacerlo utilizando GitHub y Kinsta.

Envía Tu Código a GitHub

Para enviar tu código a GitHub, puedes utilizar comandos Git, que es una forma fiable y eficaz de gestionar los cambios de código, colaborar en proyectos y mantener el historial de versiones.

El primer paso para enviar tu código será crear un nuevo repositorio accediendo a tu cuenta de GitHub, haciendo clic en el botón + de la esquina superior derecha de la pantalla y seleccionando New repository en el menú desplegable.

Crear un nuevo repositorio en GitHub
Crear un nuevo repositorio en GitHub

Dale un nombre a tu repositorio, añade una descripción (opcional) y elige si quieres que sea público o privado. Haz clic en Create repository para crearlo.

Una vez creado tu repositorio, asegúrate de obtener la URL del repositorio de la página principal de tu repositorio, que necesitarás para enviar tu código a GitHub.

Accede a la URL de tu repositorio
Accede a la URL de tu repositorio

Abre tu terminal o símbolo del sistema y navega hasta el directorio que contiene tu proyecto. Ejecuta los siguientes comandos uno a uno para enviar tu código a tu repositorio de GitHub:

git init
git add .
git commit -m "my first commit"
git remote add origin [repository URL]
git push -u origin master

git init inicializa un repositorio Git local, git add . añade todos los archivos del directorio actual y sus subdirectorios al nuevo repositorio Git. git commit -m "mi primer commit" envía los cambios al repositorio con un breve mensaje. git remote add origin [repository URL] establece la URL del repositorio como repositorio remoto y git push -u origin master envía el código al repositorio remoto (origen) en la rama maestra.

Despliega tu Aplicación ChatGPT Clone React en Kinsta

Para desplegar tu repositorio en Kinsta, sigue estos pasos:

  1. Inicia sesión o crea tu cuenta Kinsta en el panel MyKinsta.
  2. Haz clic en Aplicaciones en la barra lateral izquierda y luego en Añadir servicio.
  3. Selecciona Aplicación en el menú desplegable para desplegar una aplicación React en Kinsta.
  4. Selecciona el repositorio que deseas desplegar en el modal que aparece. Si tienes varias ramas, puedes elegir la que deseas desplegar y asignar un nombre a la aplicación. Selecciona una ubicación de centro de datos entre las 25 disponibles, y Kinsta detectará automáticamente un comando de inicio.
  5. Por último, no es seguro enviar claves de API a alojamientos públicos como GitHub, se añadió como una variable de entorno localmente. Cuando se aloja, también se puede añadir como una variable de entorno utilizando el mismo nombre de variable y la clave como su valor.
Desplegando el clon de ChatGPT en Kinsta.
Desplegando el clon de ChatGPT en Kinsta.

Tu aplicación comenzará a desplegarse y, en unos minutos, se proporcionará un enlace para acceder a la versión desplegada de tu aplicación. En este caso, se trata de https://chatgpt-clone-g9q10.kinsta.app/

Nota: Puedes activar el despliegue automático, para que Kinsta vuelva a desplegar tu aplicación cada vez que cambies tu código base y lo envíes a GitHub.

Resumen

La API de OpenAI puede utilizarse para crear una amplia gama de aplicaciones potenciales, desde la atención al cliente y los asistentes personales hasta la traducción de idiomas y la creación de contenidos.

En este tutorial, has aprendido a construir una aplicación clon de ChatGPT con React y OpenAI. Puedes integrar esta aplicación/función en otras aplicaciones para proporcionar experiencias conversacionales similares a las humanas a los usuarios.

Hay más cosas que se pueden hacer con la API de OpenAI y cómo mejorar esta aplicación clon. Por ejemplo, puedes implementar el almacenamiento local para que las preguntas y respuestas anteriores no desaparezcan incluso después de actualizar el navegador.

Con la prueba gratuita de Kinsta y el Nivel Hobby, puedes empezar fácilmente sin ningún coste en nuestro Alojamiento de Aplicaciones. Así que, ¿por qué no lo pruebas y ves lo que puedes crear?

Comparte tu proyecto y tus experiencias en los comentarios.

Joel Olawanle Kinsta

Joel is a Frontend developer working at Kinsta as a Technical Editor. He is a passionate teacher with love for open source and has written over 200 technical articles majorly around JavaScript and it's frameworks.