DEV Community

Mauricio Arce Torrez
Mauricio Arce Torrez

Posted on

2 1

Angular + Gemini Pro Vision

Esta es la segunda parte de una serie de artículos sobre Angular + Gemini

Integrando Angular y Gemini Pro Vision

Bienvenido a la segunda parte de este tutorial en el que daremos una introducción a cómo integrar Angular y Gemini Vision Pro de una forma muy sencilla.

Empezando:

1. Actualizar nuestro gemini.service para que reciba archivos

Para poder enviar imágenes a nuestro modelo de Gemini, necesitamos actualizar nuestro servicio con una nueva función que reciba un archivo. Para ello agregaremos una nueva función llamada sendImage que reciba un archivo y devuelva una promesa con el resultado.:

async sendImage(file: File): Promise<string> {
}
Enter fullscreen mode Exit fullscreen mode

Debemos usar un nuevo modelo, el gemini-pro-vision, el cual es un modelo que nos permitirá enviar imágenes. Para ello, moveremos la instancia de nuestro modelo dentro de cada función:

async sendPrompt(prompt: string): Promise<string> {
  const model = this.#genAI.getGenerativeModel({ model: 'gemini-pro' });

  try {
    const { response } = await model.generateContent(prompt);
    return response.text();
  } catch (error) {
    console.error(error);
    return 'An error has occurred. Please try again.';
  }
}

async sendImage(file: File): Promise<string> {
  const model = this.#genAI.getGenerativeModel({
    model: 'gemini-pro-vision',
  });
}
Enter fullscreen mode Exit fullscreen mode

El modelo recibe un dato de tipo Part, así que debemos convertir nuestro archio a este tipo de dato:

async sendImage(file: File): Promise<string> {
  const model = this.#genAI.getGenerativeModel({
    model: 'gemini-pro-vision',
  });
  const part = await this.convertFileToGenerativePart(file);
}

async convertFileToGenerativePart(file: File): Promise<Part> {
  const base64EncodedDataPromise = new Promise<string>((resolve) => {
    const reader = new FileReader();
    reader.onloadend = () => resolve((reader.result as string).split(',')[1]);
    reader.readAsDataURL(file);
  });

  return {
    inlineData: { data: await base64EncodedDataPromise, mimeType: file.type },
  };
}
Enter fullscreen mode Exit fullscreen mode

Para concluir, debemos enviar la imagen junto a un prompt al modelo, y asegurarnos de manejar cualquier error que pueda surgir:

async sendImage(file: File): Promise<string> {
  const model = this.#genAI.getGenerativeModel({
    model: 'gemini-pro-vision',
  });

  try {
    const part = await this.convertFileToGenerativePart(file);
    const prompt = '¿Qué es lo que ves en esta imagen?';
    const { response } = await model.generateContent([prompt, part]);

    return response.text();
  } catch (error) {
    console.error(error);
    return 'An error has occurred. Please try again.';
  }
}
Enter fullscreen mode Exit fullscreen mode

Nuestro servicio debería lucir así:

import { Injectable } from '@angular/core';
import { GoogleGenerativeAI, Part } from '@google/generative-ai';

@Injectable({
  providedIn: 'root',
})
export class GeminiService {
  readonly #API_KEY = '<YOUR-API-KEY>';
  readonly #genAI = new GoogleGenerativeAI(this.#API_KEY);

  async sendPrompt(prompt: string): Promise<string> {
    const model = this.#genAI.getGenerativeModel({ model: 'gemini-pro' });

    try {
      const { response } = await model.generateContent(prompt);
      return response.text();
    } catch (error) {
      console.error(error);
      return 'An error has occurred. Please try again.';
    }
  }

  async sendImage(file: File): Promise<string> {
    const model = this.#genAI.getGenerativeModel({
      model: 'gemini-pro-vision',
    });

    try {
      const part = await this.convertFileToGenerativePart(file);
      const prompt = '¿Qué es lo que ves en esta imagen?';
      const { response } = await model.generateContent([prompt, part]);

      return response.text();
    } catch (error) {
      console.error(error);
      return 'An error has occurred. Please try again.';
    }
  }

  async convertFileToGenerativePart(file: File): Promise<Part> {
    const base64EncodedDataPromise = new Promise<string>((resolve) => {
      const reader = new FileReader();
      reader.onloadend = () => resolve((reader.result as string).split(',')[1]);
      reader.readAsDataURL(file);
    });

    return {
      inlineData: { data: await base64EncodedDataPromise, mimeType: file.type },
    };
  }
}

Enter fullscreen mode Exit fullscreen mode

2. Enviar la imagen al modelo

Agregaremos un nuevo input en nuestro chat.component.html para que el usuario pueda enviar una imagen:

<input
  #fileInput
  type="file"
  accept="image/*"
  (change)="sendImageMessage(fileInput.files)"
/>
Enter fullscreen mode Exit fullscreen mode

Podemos ver que cada que se selecciona un archivo, se llama a la función sendImageMessage con el archivo seleccionado, la cual debemos implementar en nuestro chat.component.ts:

sendImageMessage(files: FileList | null): void {
  if (!files) {
    return;
  }

  const file = files[0];

  this.#geminiService
    .sendImage(file)
    .then((response) => this.response.set(response));
}
Enter fullscreen mode Exit fullscreen mode

Nuestra función valida si se seleccionó un archivo, y si es así, llama a nuestro servicio para enviar la imagen y mostrar la respuesta en nuestro chat.

3. Mostrar la imagen

Ya por último, debemos mostrar la imagen en nuestro chat.component.html:

<img *ngIf="imageSrc()" [src]="imageSrc()" />
Enter fullscreen mode Exit fullscreen mode

Estamos asumiendo que existe un signal imageSrc que contiene la URL de la imagen. Para ello, debemos agregarlo a nuestro chat.component.ts:

imageSrc = signal('');
Enter fullscreen mode Exit fullscreen mode

Y actualizar nuestra función sendImageMessage para que guarde la URL de la imagen en nuestro signal:

sendImageMessage(files: FileList | null): void {
  if (!files) {
    return;
  }

  const file = files[0];
  this.imageSrc.set(URL.createObjectURL(file));

  this.#geminiService
    .sendImage(file)
    .then((response) => this.response.set(response));
}
Enter fullscreen mode Exit fullscreen mode

Con estos cambios, ya deberíamos poder enviar imágenes a nuestro modelo de Gemini y mostrar la respuesta en nuestro chat.

Pasar logo de Angular a Gemini

¡Y eso es todo! Espero que este tutorial te haya sido de ayuda. Si tienes alguna duda, no dudes en contactarme.

Heroku

Simplify your DevOps and maximize your time.

Since 2007, Heroku has been the go-to platform for developers as it monitors uptime, performance, and infrastructure concerns, allowing you to focus on writing code.

Learn More

Top comments (0)

A Workflow Copilot. Tailored to You.

Pieces.app image

Our desktop app, with its intelligent copilot, streamlines coding by generating snippets, extracting code from screenshots, and accelerating problem-solving.

Read the docs

👋 Kindness is contagious

Please leave a ❤️ or a friendly comment on this post if you found it helpful!

Okay