La carrera por la IA continúa. Alternativas como Ollama nos ayudan a interactuar con modelos de IA en nuestras máquinas. Sin embargo, los equipos de Chrome y Google están avanzando un paso más al habilitar Chrome con Gemini Nano ejecutándose en nuestros navegadores.

Nota: esta API es experimental y funciona en Chrome Canary.

El equipo de Chrome está trabajando para tener un pequeño LLM en nuestro navegador para realizar tareas comunes y simples sin necesidad de una API externa como OpenAI o ejecutar Ollama para construir e interactuar con un LLM. La API nos ayuda con tareas como resumir, clasificar y reformular. Podemos leer sobre la API, pero un escenario es la mejor manera de aprender y verla en acción.

Escenario

Trabajamos en una empresa que quiere crear una prueba de concepto (POC) para interactuar con tareas comunes en un modelo LLM. Nuestro objetivo es tener un chat donde los usuarios puedan hacer preguntas o elegir de una lista de preguntas comunes.

¿Cómo podemos hacerlo?

¡Empecemos!

Habilitar Gemini Nano en Chrome Canary

Primero, descarga e instala Chrome Canary https://www.google.com/chrome/canary/. Después, habilita la API Prompt para Gemini Nano.

A continuación, activa Gemini Nano en el navegador con las siguientes banderas.

  • chrome://flags/#prompt-api-for-gemini-nano
  • chrome://flags/#optimization-guide-on-device-model

Después de activar estas opciones, recuerda reiniciar Chrome.

Gemini Nano tarda en descargarse. Para confirmar, abre Chrome Canary y ve a chrome://components. Verifica si Optimization Guide On Device Model tiene la versión 2024.6.5.2205.

Nota: Gracias al feedback de @Bezael Pérez, asegúrate de tener al menos 2GB de espacio libre en tu máquina.

Después de eso, ¡estamos listos para empezar a codificar! ¡Vamos!

Configurar el Proyecto

Primero, configura tu aplicación Angular con el comando ng new gemini-nano-angular.

ng new gemini-nano-angular
cd gemini-nano-angular
npm install

Kendo UI proporciona un comando de esquemas para registrar fácilmente su UI Conversacional Angular en nuestro proyecto.

Estoy usando Kendo UI es una prueba gratuita de 30 días. Soporte técnico gratuito y capacitación durante tu prueba y no se requiere tarjeta de crédito.
ng add @progress/kendo-angular-conversational-ui
ℹ Using package manager: npm
✔ Found compatible package version: @progress/kendo-angular-conversational-ui@16.3.0.
✔ Package information loaded.

The package @progress/kendo-angular-conversational-ui@16.3.0 will be installed and executed.
Would you like to proceed? Yes
✔ Packages successfully installed.
UPDATE package.json (1663 bytes)
UPDATE angular.json (2893 bytes)
✔ Packages installed successfully.
UPDATE src/main.ts (295 bytes)
UPDATE tsconfig.app.json (455 bytes)
UPDATE tsconfig.spec.json (461 bytes)
UPDATE angular.json (2973 bytes)

Perfecto, tenemos todo configurado, empecemos a crear nuestra interfaz de chat con Kendo AI Prompt.

Usando AI Prompt

Queremos construir una interfaz limpia para interactuar con el LLM, por lo que elijo usar AI Prompt. AI Prompt es un componente de Kendo UI que nos permite crear una interfaz elegante para interactuar con el LLM. También incluye acciones comunes como copiar, reintentar o calificar la respuesta, y más.

Recomiendo encarecidamente revisar la documentación oficial con excelentes demostraciones.

El kendo-aiprompt viene con un conjunto de componentes hijos y propiedades. Primero, abre app.component.html, elimina el marcado HTML predeterminado y agrega dos componentes: kendo-aiprompt-prompt-view y kendo-aiprompt-output-view.

Recuerda agregar el AIPromptModule en la sección de importaciones.

Personalizamos el título de kendo-aiprompt-view con la propiedad buttonText configurada en "☺️ Haz tu pregunta".

 <h2>Free Local Gemini</h2>
<kendo-aiprompt>
  <kendo-aiprompt-prompt-view
    [buttonText]="'☺️ Ask your question'"
 />
  <kendo-aiprompt-output-view/>
</kendo-aiprompt>

Guarda los cambios y ejecuta ng serve -o y ¡tada! 🎉 tenemos nuestra interfaz de chatbot limpia lista.

Es hora de personalizar el kendo-aiprompt para permitir al usuario cambiar el activeView, establecer sugerencias predeterminadas y, por supuesto, establecer promptOutputs desde el LLM y manejar el promptRequest. ¡Vamos a hacerlo!

Personalizar AIPrompt

Abre app.component.ts. Aquí, necesitamos realizar las siguientes acciones. Primero, agregamos las propiedades. Estas propiedades se vincularán al componente <kendo-aiprompt> para personalizar y reaccionar a los cambios.

  • view: para cambiar entre vistas.
  • promptOutputs: para obtener la salida del LLM.
  • suggestions: una lista de sugerencias de texto predeterminadas.

Por último, crea un método vacío onPromptRequest para obtener el texto de kendo-aiprompt. El código final se ve así:

export class AppComponent {

  public view: number = 0;
  public promptOutputs: Array<PromptOutput> = [];

  public suggestions: Array<string> = [
    'Tell me a short joke',
    'Tell me about Dominican Republic'
  ]


  public onPromptRequest(ev: PromptRequestEvent): void {
      console.log(ev)
  }
}

Luego, ve a app.component.html, y vincula kendo-aiprompt con las propiedades y el método. El código final se ve así:

<h2>Free Local Gemini</h2>
<kendo-aiprompt
  [(activeView)]="view"
  [promptSuggestions]="suggestions"
  [promptOutputs]="promptOutputs"
  (promptRequest)="onPromptRequest($event)"
>
  <kendo-aiprompt-prompt-view
    [buttonText]="' ☺️ Ask your question'"
 />

  <kendo-aiprompt-output-view/>
</kendo-aiprompt>

Tenemos el componente listo, pero ¿qué hay del LLM y Gemini? Bueno, necesitamos crear un servicio para interactuar con la nueva API window.ai.

Usando la API window.ai

Primero, crea un servicio usando el CLI de Angular ng g s services/gemini-nano y abre el archivo gemini-nano.service.ts.

Luego, declara la variable privada system_prompt. Agrega información extra al prompt para el LLM.

import {Injectable} from '@angular/core';

@Injectable({providedIn: 'root'})
export class GeminiNanoService {

  #system_prompt = 'answer in plain text without markdown style'

}

A continuación, crea el método generateText con un string como parámetro. Este método devolverá una promesa de la API window.ai. Envuelve el cuerpo del método con un bloque try-catch para manejar casos donde el usuario no esté usando Chrome Canary.

La API window.ai proporciona un conjunto de métodos, pero en este ejemplo, usaré createTextSession para habilitar una sesión LLM. Devuelve una instancia para interactuar con el LLM.

Crea una nueva variable textSession y almacena el resultado del método window.ai.createSession.

try {
     const textSession = await window.ai.createTextSession();

   }
   catch (err){
     return 'Ups! please use chrome canary and enable IA features'
   }

Oops, no puedo acceder a window.ai.createTextSession. Recibí el error: Property 'ai' does not exist on type 'Window & typeof globalThis'.

El error: Property does not exist on type Window & typeof globalThis

Al trabajar con el tipo Window, la definición está en lib.dom. Sin embargo, como ai no está definido allí, encontrarás el error TS2339: Property ai does not exist on type Window & typeof globalThis.

Pero, ¿cómo se soluciona esto? Solo sigue estos pasos:

  1. Crea un archivo index.d.ts en el directorio src/types.
  2. Exporta y declara la interfaz globalWindow, luego agrega las propiedades necesarias. En mi caso, declaro solo los métodos necesarios para este ejemplo.

¿Cuáles son los métodos de window.ai? Abre las herramientas de desarrollador en Chrome Canary y escribe window.ai. Devolverá una lista de métodos.

Definimos una interfaz para cada método y declaración de tipo. Para ahorrarte tiempo, aquí está la declaración completa de tipos de la API AI.

export {};

declare global {
  interface AISession {
    promptStreaming: (prompt: string) => AsyncIterableIterator<string>;
    prompt: (prompt: string) => Promise<string>;
    destroy: () => void;
  }
  interface AI {
    canCreateGenericSession: () => Promise<string>;
    canCreateTextSession: () => Promise<string>;
    createGenericSession: () => Promise<AISession>;
    createTextSession: () => Promise<AISession>;
    defaultGenericSessionOptions: () => object;
    defaultTextSessionOptions: () => object;
  }
  interface Window {
    ai: AI
  }
}

Finalmente, agrega una referencia al archivo en el archivo tsconfig.json.

  "typeRoots": [
      "src/types"
    ]

Ok, continuemos trabajando con la API window.ai.

El objeto textSession proporciona el método prompt para interactuar. Antes de enviar la pregunta del usuario, la combinamos con el prompt del sistema para mejorar nuestra consulta. Luego, enviamos el prompt combinado al método session.prompt y devolvemos la promesa.

El código final se ve así:

import {Injectable} from '@angular/core';

@Injectable({providedIn: 'root'})
export class GeminiNanoService {

  #system_prompt = 'answer in plain text without markdown style'

 async generateText(question: string): Promise<string> {
   try {
     const textSession = await window.ai.createTextSession();
      const prompt_question = `${question} ${this.#system_prompt}`;
     return await textSession.prompt(prompt_question);
   }
   catch (err){
     return 'Ups! please use chrome canary and enable IA features'
   }

  }
}

Tenemos nuestro servicio listo, así que es hora de conectarlo con kendo-ai prompt para hacerlo interactivo.

Interactuar con LLM

Estamos en el paso final, por lo que solo necesitamos hacer pequeños cambios en app.component.ts, primero inyecta el GeminiNanoService.

Luego, hacemos un cambio en onPromptRequest, llamando al método generateText del servicio, pasamos el prompt y esperamos el retorno de la promesa, en el método then, necesitamos obtener la respuesta y crear un objeto PromptOut y añadirlo al principio del array promptOutputs y cambiar a la vista uno.

El código final se ve así:

import {Component, inject} from '@angular/core';
import { RouterOutlet } from '@angular/router';
import {GeminiNanoService} from "./services/gemini-nano.service";
import {
  AIPromptModule, PromptOutput,
  PromptRequestEvent
} from "@progress/kendo-angular-conversational-ui";

@Component({
  selector: 'app-root',
  standalone: true,
  imports: [RouterOutlet, AIPromptModule],
  templateUrl: './app.component.html',
  styleUrl: './app.component.css'
})
export class AppComponent {

  public view: number = 0;
  public promptOutputs: Array<PromptOutput> = [];

  public suggestions: Array<string> = [
    'Tell me a short joke',
    'Tell me about dominican republic'
  ]
    private nanoService = inject(GeminiNanoService)

  public onPromptRequest(ev: PromptRequestEvent): void {
    this.nanoService.generateText(ev.prompt).then((v) => {
      const output: PromptOutput = {
        id: Math.random(),
        prompt: ev.prompt,
        output: v,
      }
      this.promptOutputs.unshift(output);
      this.view = 1;
    }

  )
  }

}

Guarda los cambios, ¡y ahora podemos interactuar con Gemini Nano en Chrome Canary!

Conclusión

Aprendimos cómo usar Gemini Nano, un LLM local experimental que se ejecuta dentro de Chrome Canary. Construimos una aplicación de chat con Angular 18 y Kendo UI Conversational AI Prompt y ejecutamos nuestro chatbot localmente con solo unas pocas líneas de código.

La diversión no se detiene aquí. Recomiendo encarecidamente revisar el sitio web oficial para aprender más sobre la API de IA.