Crea tu propio chat de cocina con Next.js, Project IDX y Firebase: una guía paso a paso
En este artículo, les mostraré cómo construir un chat interactivo especializado en cocina utilizando Next.js, el potente framework de React. Aprovecharemos las herramientas de Google, como Project IDX para el desarrollo y Firebase para el alojamiento, y conectaremos nuestro chat a una API personalizada creada con MakerSuite. ¡Prepárense para llevar sus conversaciones culinarias al siguiente nivel!
Paso 1: Configuración Inicial en Project IDX
- Crear Workspace:
- Accede a Project IDX y crea un nuevo workspace.
- Selecciona “Web” como plataforma y elige “Next.js” como framework.
- Nombra tu workspace (por ejemplo, “chat-cocina-nextjs”) y sigue los pasos de configuración para tu proyecto en nextt.js, de acuerdo a tus preferencias.
2. Copiar la API de MakerSuite:
- Genera tu API de chat en MakerSuite y copia la URL base.
- En Project IDX, crea un archivo
.env.local
en la raíz de tu proyecto. - Agrega la siguiente línea, reemplazando con tu URL real:
NEXT_PUBLIC_API_BASE_URL=TU_URL_BASE_DE_API_AQUÍ
Paso 2: Estructura del Chat de Cocina en Next.js
- Crear Componentes:
Crea los componentes necesarios para tu aplicación:
CookingChat.js
: Componente para el manejo del chatLoading.js
: Componente para el spinner o loading de nuestro chat. (Este componente es opcional).
"use client";
import Head from "next/head";
export default function CookingChat() {
return (
<>
<Head>
<title>Cooking Chat</title>
</Head>
<div className="bg-gray-100">
<form>
<label
htmlFor="message"
className="block text-gray-700 text-sm font-bold mb-2"
>
Your Message
</label>
<textarea
id="message"
rows="3"
className="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline"
placeholder="I am AI model, expert cooking assistant. Ask me anything about cooking!"
></textarea>
<button
className="mt-3 bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded focus:outline-none focus:shadow-outline"
type="submit"
>
Send
</button>
</form>
</div>
</>
);
}
Nota: Adecua tu proyecto de acuerdo a tus necesidades, adiciona autenticación si deseas u otros componentes. Puedes seguir las recomendaciones de la página oficial de Next.js para darle estructura a tus archivos https://nextjs.org/docs/getting-started/project-structure
Paso 3: Consumir la API de MakerSuite
Para usar exitosamente la API de Gemini, debemos hacer la instalación de la librería en la terminal de IDX:
npm install @google/generative-ai
- Dentro de tu componente CookingChat, importa los módulos necesarios e inicializa la conexión con Gemini y crea algunas constantes para definir el modelo y algunos parámetros de seguridad
import {
GoogleGenerativeAI,
HarmBlockThreshold,
HarmCategory,
} from "@google/generative-ai";
// safety settings for the model
const safetySettings = [
{
category: HarmCategory.HARM_CATEGORY_HARASSMENT,
threshold: HarmBlockThreshold.BLOCK_MEDIUM_AND_ABOVE,
},
{
category: HarmCategory.HARM_CATEGORY_HATE_SPEECH,
threshold: HarmBlockThreshold.BLOCK_MEDIUM_AND_ABOVE,
},
{
category: HarmCategory.HARM_CATEGORY_SEXUALLY_EXPLICIT,
threshold: HarmBlockThreshold.BLOCK_MEDIUM_AND_ABOVE,
},
{
category: HarmCategory.HARM_CATEGORY_DANGEROUS_CONTENT,
threshold: HarmBlockThreshold.BLOCK_MEDIUM_AND_ABOVE,
},
];
// generation config for the model
const generationConfig = {
temperature: 1,
topP: 0.95,
topK: 64,
maxOutputTokens: 8192,
responseMimeType: "text/plain",
};
const MODEL_NAME = "gemini-1.5-flash";
const API_KEY = process.env.NEXT_PUBLIC_API_KEY;
Añadiremos el código necesario para manejar los eventos que controlaran el botón y el campo de texto. Además de la lógica para manejar la funcionalidad del chat que instanciará la API de Gemini.
export default function CookingChat() {
const [loading, setLoading] = useState(false);
const [chatHistory, setChatHistory] = useState([{ parts: "", role: "" }]);
const [message, setMessage] = useState(
" You are an expert AI cooking model, I want to prepare a dish, for example a pizza, can you help me with the recipe?"
);
const genAI = new GoogleGenerativeAI(API_KEY);
const chat = model.startChat({
history: [],
});
const addMessageToHistory = (role, message) => {
setChatHistory((prev) => [...prev, { parts: message, role }]);
};
const fetchData = async () => {
setLoading(true);
addMessageToHistory("user", message);
const result = await chat.sendMessage(message);
const response = result.response
.text()
.replace(/\*\*/g, "<b>")
.replace(/\*/g, "<li>")
.replace(/\_/g, "<i>")
.replace(/\n/g, "<br>");
addMessageToHistory("model", response);
setMessage("");
setLoading(false);
};
const handleSubmit = async (event) => {
event.preventDefault();
fetchData();
};
const handleSetMessage = (event) => {
setMessage(event.target.value);
};
return (
<>
<Head>
<title>Cooking Chat</title>
</Head>
<div className="bg-gray-100">
<div className="container mx-auto p-4">
<div className="bg-white rounded-lg p-4">
<div className="mb-4">
<h2 className="text-xl font-semibold">Chat</h2>
{loading && <Loading />}
<div className="border-t-2 border-gray-200 mt-2 pt-2">
{chatHistory.map(({ parts, role }, index) => (
<div className="flex items-start mb-4 text-sm" key={index}>
{parts.length > 0 ? (
<div className="flex-1 ml-3 pt-1">
<p className="text-gray-600">
{role === "user" ? (
<span>User</span>
) : (
<span>Model:</span>
)}
</p>
<div dangerouslySetInnerHTML={{ __html: parts }} />
</div>
) : null}
</div>
))}
</div>
</div>
<div className="mt-4">
<form onSubmit={handleSubmit}>
<label
htmlFor="message"
className="block text-gray-700 text-sm font-bold mb-2"
>
Your Message
</label>
<textarea
id="message"
rows="3"
className="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline"
placeholder="I am AI model, expert cooking assistant. Ask me anything about cooking!"
value={message}
onChange={handleSetMessage}
></textarea>
<button
className="mt-3 bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded focus:outline-none focus:shadow-outline"
type="submit"
>
Send
</button>
</form>
</div>
</div>
</div>
</div>
</>
);
}
Puedes ver el repositorio del proyecto aquí:
Paso 4: Diseño y Maquetación
- Utiliza componentes de interfaz de usuario (por ejemplo, de Material UI o Chakra UI) para crear el diseño del chat, para este chat usé Tailwind.
- Personaliza los estilos según la temática de cocina.
Paso 5: Hostear en Firebase Hosting (Directamente desde Project IDX)
- Conectar a Firebase:
- En Project IDX, ve a la sección “Firebase” en la barra lateral.
- Haz clic en “Connect to Firebase” y sigue las instrucciones para vincular tu proyecto de Firebase.
Al dar clic en autenticar, te preguntará si deseas compartir información de reporte de información.
Luego en la terminal se debe seguir las siguientes instrucciones:
Permite que el CLI se vincule a tu cuenta de Gmail y una vez lo hagas se mostrará los siguientes pasos:
Una vez aceptes el primer paso, confirmará el session ID.
Y luego te genera un código de autorización.
Al copiar el código en la terminal quedarás exitosamente logueado.
Una vez logueado, se procede a inicializar el Firebase Hosting.
Podemos seleccionar un proyecto creado previamente en la consola de Firebase o crear uno nuevo.
Si seleccionamos crear uno nuevo aparecerá la siguiente información:
Podemos crear el proyecto en la consola de Firebase y seguimos los pasos hasta que nuestro proyecto esté creado.
Podemos seleccionar un proyecto existente desde la terminal de IDX.
Nos pregunta si deseamos usar el código existente.
Seleccionamos la región, y se inicializará Firebase.
2. Desplegar
- Una vez conectado, haz clic en el botón “Deploy” en la sección de Firebase de Project IDX.
- Project IDX se encargará de construir y desplegar tu aplicación en Firebase Hosting automáticamente.
Al hacer el deploy a producción en la consola de Firebase podemos ver los dominios generados, donde podremos revisar nuestra aplicación.
Te comparto los slides de una charla relacionada con este artículo. Charla que compartí en el Google i/o extended en Cali.