Usando la API de Google Gemini desde Europa con Apps Script

A raíz de esta publicación en X de hace unos días se han puesto en contacto conmigo algunas personas para pedirme más detalles sobre cómo utilizar la API de Gemini en Apps Script desde España.

Visto lo visto, vamos a repasar brevemente los pasos necesarios para lograrlo, que igual resulta que esto interesa y todo. Y ya que nos ponemos, veremos también como construir un par de funciones personalizadas Apps Script para hojas de cálculo que tiran de Gemini, así en plan prueba de concepto, nada más.

 

TABLA DE CONTENIDO

El artista anteriormente conocido como Bard

A menos que hayas estado escondido debajo de una piedra durante las últimas semanas, estoy seguro de que ya sabes que Bard se ha convertido en Gemini. O Gemini en Bard, según se mire 😜.

Banner oficial con el logo de Gemini.
Fuente: https://blog.google/intl/es-es/productos/tecnologia/presentamos-gemini-nuestro-modelo-del-ia

Este cambio de denominación, llegado poco después de que Gemini, en su sabor Pro, comenzara a estar disponible de manera global en Bard, ha traído de la mano nuevas apps para dispositivos móviles (que aún no han llegado a la vieja Europa, para variar) y una versión de pago denominada Gemini Advanced, que corre que se las pela, dicen, propulsada por un modelo generativo más potente llamado «Ultra».

Lo has adivinado, Ultra, Pro y Nano (que también lo hay) hacen referencia al tamaño y las capacidades de cada uno de estos modelos fundacionales cobijados bajo el paraguas de Gemini.

Descripción general de los modelos Ultra, Pro y Nano.
Fuente: https://deepmind.google/technologies/gemini

Y por si fuera poco, se acaba de anunciar una nueva versión de Gemini, la 1.5 Pro que maneja una ventana de contexto de 1.000.000 de tokens 🤯, aunque de momento solo está disponible en prueba privada para un grupo limitado de desarrolladores.

¿Confundido por esta 🥗 ensalada de términos? No eres el único 😵. Lo de usar Gemini indistintamente para identificar tanto a una familia de modelos de lenguaje a gran escala como al asistente conversacional bajo el que se ocultan puede que no contribuya a clarificar las cosas.

Pero espera, que la fiesta continúa.

Resulta que el muchache (Gemini) tiene, cómo no, una estupenda API REST que puede consumirse en estos momentos —26 de febrero de 2024— siguiendo dos caminos paralelos, pero tan distintos que hasta utilizan mecanismos de autenticación y puntos finales (los endpoints de toda la vida) diferentes:

En ambos casos, y al menos en tanto la API de Gemini siga en fase de pruebas, vamos a poder utilizarla sin coste, aunque con ciertas limitaciones (muy razonables, debo decir) de uso.

¿Y qué pasará después? Honestamente, no lo sé, pero de momento la página de precios de Gemini 1.0 Pro menciona un oportuno plan gratuito,  así que ¡aprovechémoslo!

Planes de precios de la API de Gemini 1.0 Pro.
Fuente: https://ai.google.dev/pricing

⚠️ Gemini, en su plan gratuito, se alimenta tanto de las instrucciones que le des al modelo como de las respuestas que este devuelva para su propio entrenamiento. Y cuidado también con las condiciones de uso, tienes que ser mayor de edad.

Bien, hechas las presentaciones, ya podemos seguir.

☝ Yo voy a mostrarte cómo explotar la API de Gemini usando el primer camino, Google AI Studio. ¿Por qué? Pues por dos razones:

  • Aunque también vas a necesitar un proyecto de Google Cloud, no hace falta que habilites en él la facturación. En otras palabras: no tendrás que introducir un método de pago. Mantén la tarjeta de crédito en tu bolsillo y ahórrate sobresaltos, que Gemini y tú solo os estáis conociendo.
  • Si utilizas una clave de la API podrás construir con facilidad funciones personalizadas para las hojas de cálculo de Google que hagan cositas interesantes, como por ejemplo responder a los prompts que introduzcas mediante fórmulas. Si usas Vertex, en cambio, la cosa no es tan inmediata dado que las funciones personalizadas Apps Script no pueden solicitar permisos del usuario de manera interactiva.

El problema es que Google AI Studio está disponible en una lista interminable de países... que no incluye ninguno en Europa (otra vez 🤬). ¿No me crees? Anda, compruébalo tu mismo si me estás leyendo desde España:

https://aistudio.google.com

Mensaje que indica que Google AI Studio y Gemini API no están disponibles en varias regiones.
¿Esto será cosa del RGPD?

Y esto precisamente será lo primero que vamos a solucionar.

Una VPN siempre será tu amiga

Decidido, usaremos una VPN que nos permita falsear la ubicación de nuestra conexión a Internet para registrarnos en Google AI Studio y obtener de él una preciada clave secreta con la que invocar a la API de Gemini.

Servicios de VPN hay muchos, los buenos normalmente de pago. Personalmente me gusta mucho el que ofrece la compañía suiza Proton, que incluso dispone de una estupenda versión gratuita sin anuncios y con tráfico ilimitado.

Banner de Proton VPN.
Proton VPN está disponible para Android, iOS, Windows, macOS, Linux y Chromebook. No se puede pedir más.

Pero tal vez el modo más sencillo y directo sea utilizar la VPN integrada en el excelente navegador Opera.

Logo del navegador Opera.
No uso Opera como navegador habitual... pero tal vez debería.

Solo tienes que activar esta función en sus ajustes...

Ajustes de VPN en Chrome.
Opera, también con versión parar Linux.

...y seleccionar «Americas» como región desde la que navegar.

Botón de control de la navegación con VPN en la barra de direcciones de Opera.
Viajando sin salir de casa.

La página web del AI Studio de Google se abrirá inmediatamente de par en para para ti. Solo tendrás que hacer clic sobre Get API key para generar tu clave mágica.

Animación que muestra el proceso de obtención de una clave para la API de Gemini en AI Studio.
AI Studio es una herramienta interesantísima para experimentar con diferentes prompts para Gemini, no dejes de jugar con ella.

Como te adelantaba más arriba, es necesario asociar la clave de la API con un proyecto en  GCP. Puedes crear uno nuevo exclusivamente para tus pruebas en la consola de Google Cloud siguiendo las instrucciones habituales. E insisto, no necesitarás activar en él la facturación 💸.

☝ AI Studio se encargará de habilitar automáticamente la API de Generative Language en la consola de Google Cloud Platform (CGP) para el proyecto que indiques en el asistente.

Detalles de la API de Gemini  en la consola de Google Cloud, mostrando que está activado.
Aunque lo de "Generative language API" despista de entrada, esto es Gemini.

Copia raudo y veloz en el portapapeles tu clave y ponla a buen recaudo, desconecta la VPN de Opera (ya no la volverás a necesitar) y crea una nueva hoja de cálculo usando tu navegador habitual.

Vamos a jugar ahora con Apps Script.

Ma chère Gemini

Vale, ya tienes la clave. Pero ¿qué pasa con la API? ¿Se puede usar desde Europa, sin más?

No. Pero sí 🙃.

Si intentas hablar con la API desde cualquier dispositivo con una IP europea, aunque sea usando educadamente tu flamante clave de la API,  te vas a dar de bruces con un contundente a la par que igualmente educado «User location is not supported».

Captura de un intento de conexión con la API de Gemini usando la instrucción curl en una terminal de comandos.
¡Pues no empezamos nada bien!

Yo he utilizado aquí arriba el comando curl, típico de las linuxcosas, que entre muchas otras virtudes va muy bien para probar en plan rápido una API REST. Y ya puedes ver que no ha ido bien.

Pues claro, ¿qué esperabas? Si no se puede, no se pude.

Pero resulta que el entorno de ejecución de Apps Script corre en la nube de Google. Ya sabes, los ordenadores de otro. Y esa infraestructura de computación parece que no anda por aquí cerca —supondremos que toda la maquinaria que propulsa Apps Script se encuentra en Estados Unidos—, así que lo tenemos hecho.

🤔 Eso de que Apps Script se ejecute en servidores americanos, parece que con independencia del tipo de cuenta de Google que estés usando, enciende algunas luces de color ámbar (calma, solo ámbar) dentro de mi cabeza por lo que hace a las regiones de datos seleccionables en ciertas ediciones de Workspace. Pero de eso igual hablamos otro día, que hoy no viene para nada al caso.

Como ves, no te mentía hace un momento cuando te indicaba que ya podías apagar definitivamente tu VPN. Prosigamos por tanto con lo que teníamos entre manos.

No te confundas, estamos usando AI Studio, no Vertex AI, así que la página de la documentación que deberías leer concienzudamente antes de mover un solo dedo es esta:

https://ai.google.dev/docs/gemini_api_overview

No pierdas de vista sin embargo la estructura de una petición a la API de Gemini, que encontrarás detallada en la documentación para desarrolladores de Vertex AI (recuerda, ni caso a los endpoints que veas allí).


{
  "contents": [
    {
      "role": string,
      "parts": [
        {
          // Union field data can be only one of the following:
          "text": string,
          "inlineData": {
            "mimeType": string,
            "data": string
          },
          "fileData": {
            "mimeType": string,
            "fileUri": string
          },
          // End of list of possible types for union field data.

          "videoMetadata": {
            "startOffset": {
              "seconds": integer,
              "nanos": integer
            },
            "endOffset": {
              "seconds": integer,
              "nanos": integer
            }
          }
        }
      ]
    }
  ],
  "tools": [
    {
      "functionDeclarations": [
        {
          "name": string,
          "description": string,
          "parameters": {
            object (OpenAPI Object Schema)
          }
        }
      ]
    }
  ],
  "safetySettings": [
    {
      "category": enum (HarmCategory),
      "threshold": enum (HarmBlockThreshold)
    }
  ],
  "generationConfig": {
    "temperature": number,
    "topP": number,
    "topK": number,
    "candidateCount": integer,
    "maxOutputTokens": integer,
    "stopSequences": [
      string
    ]
  }
}

De todas maneras, la referencia completa de la API REST de Gemini la tienes aquí:

https://ai.google.dev/api/rest

Hoy trabajaremos exclusivamente con el método models.generateContent y todas las estructuras de datos que cuelgan de él, tanto la utilizada para diseñar la petición como el objeto GenerateContentResponse recibido como respuesta a ella.

Debes saber también que la API de Gemini permite escoger distintos modelos:

Tabla de modelos de la API de Gemini.
Fuente: https://ai.google.dev/models

Por una parte tenemos Gemini Pro y Gemini Pro Vision, que son los que creo te han traído hasta este artículo. El primero solo acepta prompts de texto. Al segundo, en cambio, le podemos pedir que analice también imágenes e incluso vídeos, puesto que es multimodal. Sí, una pasada.

Por otra, también contamos con PaLM, del que probablemente no querrás saber nada a menos que necesites ajustar el modelo con tus propios datos, algo que Gemini aún no soporta.

Pero pasemos al editor de Apps Script de esa hoja de cálculo que acabas de crear.

Extensiones → Apps Script

Gemini Pro

Vamos con una función personalizada de primero de Apps Script para pedirle a Gemini Pro (sin el -Vision) que nos haga casito.

Lo mejor será comenzar poniendo encima de la mesa unas cuantas constantes para entender cómo de parametrizable es este tinglado.

/**
 * @OnlyCurrentDoc
 * [AI Studio] https://ai.google.dev/docs/gemini_api_overview
 * [Vertex AI] https://cloud.google.com/vertex-ai/docs/generative-ai/model-reference/gemini
 */

const CLAVE_API = 'AIzaSyAic0omO1oaVkBBEzIj5latSZgG8Gw0ChE';
const VERSION = 'v1beta';
const MODELO = { texto: 'gemini-pro', imagen: 'gemini-pro-vision' };
const TEMPERATURA = 0.9;
const TOP_K = 32;
const TOP_P = 1.0;
const MAX_TOKENS = 8192;
const NUM_CANDIDATOS = 1;
const PUNTO_FINAL = `https://generativelanguage.googleapis.com/${VERSION}/models/{{MODELO}}:generateContent?key=${CLAVE_API}`;
 

Introduce tu propia clave para la API de Gemini en la línea 7, obviamente. No intentes utilizar la que verás en el código de ejemplo, ya no existe 😋.

Es conveniente que revises cuidadosamente la información facilitada en la documentación para conocer detalles tan importantes como:

  • Las distintas versiones de la API que están disponibles. En este ejemplo usaremos la v1beta (línea 8), que es más inestable pero también más rica en características (admite, por ejemplo, la llamada a funciones).
  • Las diferentes versiones de los modelos con los que es posible interactuar a través de la API (línea 9). Por cierto,  gemini-pro es un alias de gemini-1.0-pro.
  • Los ajustes habituales propios de los modelos de lenguaje a gran escala que permiten controlar hasta cierto punto su comportamiento (líneas 10 - 14). Gemini puede devolvernos varias respuestas en cada petición que le lancemos, pero en este ejemplo le pediremos una sola (línea 15), que por otra parte es el comportamiento predeterminado.

Fíjate en el modo en que se parametrizan tanto la versión como el modelo en la propia ruta del URL del punto final y cómo se pasa finalmente la clave (secreta) de la API como un parámetro de consulta (línea 14).

https://generativelanguage.googleapis.com/v1beta/models/gemini-pro:generateContent?key=CLAVE_API

A continuación, el código de la función personalizada que realiza la petición a Gemini:


/**
 * Envía un prompt de texto a Gemini Pro y devuelve su respuesta.
 * @param {"Gemini, cuéntame tu vida..."} prompt
 * @customfunction
 */
function HDCP_GEMINI(prompt) {

  if (prompt) {

    const solicitud = {
      contents: [
        {
          role: 'USER',
          parts: [{ text: prompt }]
        }
      ],
      generationConfig: {
        temperature: TEMPERATURA,
        topP: TOP_P,
        topK: TOP_K,
        candidateCount: NUM_CANDIDATOS,
        maxOutputTokens: MAX_TOKENS
      }
    };

    const respuesta = UrlFetchApp.fetch(
      // Esta sustitución tan aparentemente innecesaria obedece a que reutilizaremos la constante PUNTO_FINAL en otra función
      PUNTO_FINAL.replace('{{MODELO}}', MODELO.texto),
      {
        method: 'POST',
        payload: JSON.stringify(solicitud),
        contentType: 'application/json',
        muteHttpExceptions: true
      }
    );

    const codigoResultado = respuesta.getResponseCode();
    const resultado = JSON.parse(respuesta.getContentText());

    if (codigoResultado == 200) {
      if (resultado.promptFeedback?.blockReason) throw `Respuesta bloqueada, motivo: ${resultado.promptFeedback.blockReason}`;
      else if (resultado.candidates[0].finishReason != 'STOP') throw `Respuesta incompleta, motivo: ${resultado.candidates[0].finishReason}`;
      else return resultado.candidates[0].content.parts[0].text;
    } else throw `Gemini no responde (${codigoResultado})${resultado ? `: ${resultado.error.message}` : ''}`;

  }

}

Muy rapidito:

  1. En las líneas 26 - 40 se construye el objeto que pasaremos en la solicitud a la API.
  2. En 42 - 51 es donde se efectúa realmente la petición.
  3. Finalmente, en 53 - 60 se determina si todo ha ido bien, en cuyo caso se devolverá la respuesta generada por el modelo o, en caso contrario, el mensaje de error correspondiente.

☝ Atención, porque el tratamiento de un posible error en (iii) no está exento de ciertas sutilezas.

Para empezar, el hecho de que Gemini nos devuelva un resultado aparentemente valido (líneas 56 - 59) no garantiza que este sea la respuesta a nuestro prompt. Esto es así porque el modelo dispone de una serie de mecanismos de seguridad que pueden impedirlo cuando este determina por sí mismo que la probabilidad de que la respuesta que nos proporciona incluya contenidos inapropiados (de tipo sexual, relacionados con el acoso, los discursos de odio o simplemente peligrosos) excede ciertos umbrales.

Umbrales de filtrado de contenidos de la API de Gemini.
Fuente: https://ai.google.dev/docs/safety_setting_gemini#safety-settings

Afortunadamente, estos umbrales pueden ajustarse al realizar la petición, incluso hasta el punto de desactivar totalmente el filtrado de contenidos, aunque el código de ejemplo que te muestro no aprovecha esta posibilidad.

Por todo esto, aunque Gemini se ponga al teléfono y conteste diligentemente a la llamada de nuestro script, es necesario comprobar si la propiedad blockReason del objeto PromptFeedback devuelto por la API adopta algún valor (línea 57), e informar en ese caso al usuario de que la respuesta a sus instrucciones ha sido bloqueada.

Causas por las que un prompt puede haber sido bloqueado por Gemini.

Fuente: https://ai.google.dev/api/rest/v1/GenerateContentResponse#blockreason

Aunque la respuesta no haya sido bloqueada por el mecanismo de filtrado de contenidos, también puede darse la circunstancia de que el modelo no haya podido generarla en su totalidad. Hay varias razones para ello: 

Causas por las que ha finalizado la generación de la respuesta a un prompt.
Probablemente querrás mostrar un resultado parcial cuando la generación haya finalizado por superarse el límite de tokens.

Por tanto, habrá que revisar de un modo análogo el valor de la propiedad finishReason de cada uno de los objetos Candidate del vector de respuestas generadas para determinar si ha ocurrido algo desagradable (línea 58), es decir, un valor distinto a STOP. Recuerda que en este ejemplo obtenemos una única respuesta, de ahí que únicamente se realice esta comprobación sobre el primer elemento del vector.

Solo si no se da ninguna de las circunstancias anteriores se dará la primera (y única) respuesta obtenida de la API por buena (línea 59).

Finalmente (sí, aún hay más 😅), en el transcurso de mis pruebas me he encontrado con que incluso cuando la llamada a la API falla, es probable que Gemini nos indique amablemente el motivo a través de un objeto de error específico, del que conviene informar de manera adecuada (línea 60).

Objeto de error devuelto por Gemini cuando se produce un error 503.
No, aquí no estoy mostrando el error adecuadamente. Échale un vistazo al código, anda.

Y dejo ya de hablar de errores para mostrarte el resultado de nuestra pequeña prueba de uso de Gemini Pro.

Captura de pantalla de un intervalo de celdas con 3 prompts (columna A) y las repuestas de Gemini (columna B).
Gemini ha estado bastante acertado en esta ocasión, aunque creo que debí haber usado una temperatura inferior.

🔥 Actualización 01/03/24 🔥
Unas horas después de la publicación de este artículo, veo que la documentación para desarrolladores ya contempla por fin la posibilidad de utilizar las cabeceras (headers) de la petición POST para enviar la clave de la API , algo que me resulta más tranquilizador que hacerlo en el propio URL del punto final, por más que la conexión esté cifrada. Ten en cuenta que estos URL podrían almacenarse en texto claro en algún registro de servicio, por ejemplo.

Para ello, basta con eliminar el parámetro de consulta de la constante PUNTO_FINAL y añadir la correspondiente cabecera x-goog-api-key en el campo headers del objeto incluido en la petición realizada mediante UrlFetchApp.fetch():


const PUNTO_FINAL = `https://generativelanguage.googleapis.com/${VERSION}/models/{{MODELO}}:generateContent`

// Resto del código...

    const respuesta = UrlFetchApp.fetch(
      PUNTO_FINAL.replace('{{MODELO}}', MODELO.texto),
      {
        headers: { 'x-goog-api-key': CLAVE_API },
		// Resto de propiedades de la petición...
      }
    );

// Resto del código...

Gemini Pro Vision

Para terminar, ¿a quién le apetece probar también la variante multimodal de Gemini 🙋‍♂️?

Que Gemini Pro Vision sea multimodal significa que, además de darle instrucciones vía texto, podemos además utilizar imágenes y vídeos. Centrémonos en lo primero (imágenes) y sigamos trabajando en la hoja de cálculo del ejemplo anterior.

Para demostrar esta capacidad de Gemini Pro Vision vamos a diseñar a continuación una nueva función personalizada que permita obtener una descripción un texto descriptivo del contenido de una imagen. Algo como esto.

Animación que demuestra el funcionamiento de la función personalizada HDCP_GEMINI_VISION_DESCRIBIR().
Probando la visión de Gemini. 

Como no hay un modo directo de obtener las imágenes almacenadas en las celdas de una hoja de cálculo usando Apps Script, aunque ciertamente puedes dar un par de saltos mortales hacia atrás para lograrlo, nos vamos a contentar con pasarle a nuestra nueva y visionaria función el URL de la imagen a analizar.

☝ Mantendremos el bloque de declaración de constantes que hemos utilizado en el apartado anterior:

/**
 * @OnlyCurrentDoc
 * [AI Studio] https://ai.google.dev/docs/gemini_api_overview
 * [Vertex AI] https://cloud.google.com/vertex-ai/docs/generative-ai/model-reference/gemini
 */

const CLAVE_API = 'AIzaSyAic0omO1oaVkBBEzIj5latSZgG8Gw0ChE';
const VERSION = 'v1beta';
const MODELO = { texto: 'gemini-pro', imagen: 'gemini-pro-vision' };
const TEMPERATURA = 0.9;
const TOP_K = 32;
const TOP_P = 1.0;
const MAX_TOKENS = 8192;
const NUM_CANDIDATOS = 1;
const PUNTO_FINAL = `https://generativelanguage.googleapis.com/${VERSION}/models/{{MODELO}}:generateContent?key=${CLAVE_API}`;
 

La documentación de Gemini es contradictoria (aquí se dice una cosa, aquí otra), pero te aseguro que es bien capaz de masticar imágenes en los formatos PNG, JPEG, WebP, HEIC y HEIF. Limitaremos nuestra prueba de concepto a los tres primeros para evitar un pequeño inconveniente con los formatos HEIC y HEIF que no viene demasiado al caso.

Las imágenes (o vídeos) pueden enviarse a Gemini de dos maneras:

  1. Dentro de la propia solicitud a la API como datos binarios codificados en Base64 dentro de un Blob, un objeto utilizado para intercambiar datos entre los distintos servicios Apps Script.
  2. Mediante el URI de un archivo en un bucket de Cloud Storage, aunque esta posibilidad únicamente se menciona en la documentación para desarrolladores de Vertex AI 🤔 (fíjate en el objeto contenido en la propiedad fileData del Request body de la petición).

Naturalmente, he escogido la primera opción dado que aunque Cloud Storage tiene un umbral de uso bajo el cual es gratuito (5 GB y cierto número de operaciones mensuales), es necesario activar la facturación en el proyecto GCP en el que lo uses.

Por tanto, en el fragmento de código inicial de la nueva función:

/**
 * Envía un prompt de multimodal a Gemini Vison Pro (texto + imagen jpg/png/webp) y devuelve su respuesta.
 * @param {"https://www.komar.de/media/catalog/product/cache/13/image/9df78eab33525d08d6e5fb8d27136e95/I/A/IADX10-065.jpg"} urlImagen URL de una imagen JPG/PNG/WEBP.
 * @customfunction
 */
function HDCP_GEMINI_VISION_DESCRIBIR(urlImagen) {

  const prompt = 'Describe y contextualiza la imagen siguiente en un solo párrafo';
  const blob = UrlFetchApp.fetch(urlImagen).getBlob();
  const tipoMime = blob.getContentType();
  const b64datos = Utilities.base64Encode(blob.getBytes());

  1. Se define el prompt de texto que se aplicará sobre la imagen (línea 73).
  2. Se obtiene un blob a partir del URL de la imagen (línea 74).
  3. Se identifica su tipo MIME (línea 75).
  4. Se codifica el blob en el formato Base64 que espera la API de Gemini (línea 76).

⚠️ En el fragmento de código anterior no se realiza ningún tratamiento de errores al recuperar el blob de la imagen con el UrlFetchApp.fetch() de la línea 74. Aquí nos da un poco igual, pero en producción deberías controlarlo.

A continuación, si el tipo MIME de la imagen es uno de los soportados, se construye la petición a la API.


    if (b64datos && ['image/png', 'image/jpeg', 'image/webp'].includes(tipoMime)) {

    const solicitud = {
      contents: [
        {
          role: 'USER',
          parts: [
            { text: prompt },
            { inlineData: { mimeType: tipoMime, data: b64datos } }
          ]
        }
      ],
      generationConfig: {
        temperature: TEMPERATURA,
        topP: TOP_P,
        topK: TOP_K,
        candidateCount: NUM_CANDIDATOS,
        maxOutputTokens: MAX_TOKENS
      }
    };
    

Observa las líneas 84 - 87. Ahora introduciremos dos objetos Part, uno para las instrucciones (de tipo text), y otro para el blob codificado en Base64 de la imagen (de tipo inlineData).

Ya solo resta hacer la petición y comprobar si se ha producido algún problema antes de devolver el resultado, de modo análogo a la función que hemos preparado en el apartado anterior.


    const respuesta = UrlFetchApp.fetch(
      PUNTO_FINAL.replace('{{MODELO}}', MODELO.imagen),
      {
        method: 'POST',
        payload: JSON.stringify(solicitud),
        contentType: 'application/json',
        muteHttpExceptions: true
      }
    );

    const codigoResultado = respuesta.getResponseCode();
    const resultado = JSON.parse(respuesta.getContentText());

    if (codigoResultado == 200) {
      if (resultado.promptFeedback?.blockReason) throw `Respuesta bloqueada, motivo: ${resultado.promptFeedback.blockReason}`;
      else if (resultado.candidates[0].finishReason != 'STOP') throw `Respuesta incompleta, motivo: ${resultado.candidates[0].finishReason}`;
      else return resultado.candidates[0].content.parts[0].text.slice(1); // por alguna razón se añade un carácter en blanco como prefijo ¿?
    } else throw `Gemini no responde (${codigoResultado})${resultado ? `: ${resultado.error.message}` : ''}`;

  } else throw 'Tipo de imagen no soportado';

}

Aquí tienes el resultado, en estático, para que puedas valorar la calidad de las descripciones de Gemini. Naturalmente, la temperatura 🌡 ha hecho de las suyas con respecto a la animación que te mostraba hace un momento.

Imagen estática que demuestra el funcionamiento de la función personalizada HDCP_GEMINI_VISION_DESCRIBIR().
Parece que Gemini no ha identificado a los entrañables Ico y Yorda (fila 5). ¡Qué decepción!

Aquí puedes encontrar la hoja de cálculo que he utilizado a lo largo del artículo, junto con el código de ambas funciones. No te olvides de modificar la constante CLAVE_API con tu propia clave obtenida de AI Studio.

👉 Gemini API test | publico 👈

🔥 Actualización 01/03/24 🔥
He modificado el código Apps Script que encontrarás dentro de la hoja de cálculo anterior para que envíe la clave de la API usando las cabeceras de la petición POST. Por esta razón, la numeración ya no coincidirá exactamente con la de los fragmentos de código que te muestro en este artículo .

Comentarios finales

Tengo que reconocer que descubrir contigo la API de Gemini me ha parecido emocionante.

Los ejemplos que te he mostrado son extremadamente sencillos. De hecho, la API de Gemini ofrece muchas otras posibilidades que no hemos explorado, como trabajar con embeddings y documentos o articular secuencias conversacionales. Pero al mismo tiempo, creo que demuestran con suficiente claridad lo fácil que resulta hablar con Gemini desde nuestro adorado Apps Script.

Soy muy consciente de que me estoy tomando todo esto de la inteligencia artificial, modelos generativos, etc. con ciertas reservas, cuando no un abierto escepticismo. Creo que hay un exceso de confianza en sus capacidades y demasiado entusiasmo a la hora de aplicar estas tecnologías en todo tipo de contextos, algunos muy sensibles como son los educativos, tal vez sin reflexionar lo suficiente sobre sus limitaciones ni valorar con detenimiento las implicaciones éticas que su uso supone.

El ciclo del "hype" de Gartner.
El Ciclo del “hype” de Gartner, aún con sus críticas, aplicable a tantas y tantas cosas (fuente).

En cualquier caso, la tecnología, un enorme elefante en la habitación ya imposible de ignorar, me parece fascinante.

La irrupción de los modelos masivos de lenguaje en nuestras vidas, y me refiero a las de todos, es inevitable. Tanto como ineludible resulta explorar sus posibilidades para destilar lo mejor de ellas, algo que dependerá en gran medida de hasta qué punto estas herramientas puedan democratizarse para permanecer al alcance de todos.

Gracias por acompañarme hasta aquí. Estoy seguro de que no va a ser la última vez que hablemos de Gemini en este espacio.


Comentarios