Chronos-Lens

AI Powered
Publicado hace 1 mes
•
59 vistas
Kotlin Android SDK Jetpack Compose Google Gemini API Room Database Retrofit Coroutines & Flow Node.js
Portada completa de Chronos-Lens

Problema Inicial: Latencia de Transformación Generativa y Gestión de Streams Multimodales en Tiempo Real

Cuando inicié el desarrollo de Chronos Passport, me enfrenté al desafío técnico de integrar modelos generativos multimodales de inteligencia artificial (Google Gemini 2.5 Flash) directamente dentro de una experiencia de usuario móvil fluida, reactiva y fotorrealista. Los usuarios capturaban fotografías en alta resolución desde el sensor de la cámara nativa de sus dispositivos Android, lo que producía arreglos de bytes de gran tamaño, a menudo superiores a los 12 MB por toma. La conversión directa de estas imágenes a formatos procesables por la API REST, combinada con la codificación Base64 realizada ingenuamente sobre el hilo principal de la aplicación, generaba picos drásticos en el uso de memoria RAM, provocando bloqueos involuntarios por OutOfMemory (OOM) en dispositivos de gama media y baja.

Además, las llamadas directas a un servicio de inteligencia artificial generativa desde un cliente móvil presentaban graves inconvenientes arquitectónicos y de seguridad. Por un lado, exponer las credenciales y claves privadas de API dentro del paquete compilado de la aplicación representaba un riesgo crítico de seguridad. Por otro lado, la naturaleza inherentemente asíncrona y variable de los modelos LLM y de difusión multimodal implicaba latencias de respuesta impredecibles, que oscilaban entre los 4 y 15 segundos dependiendo de la complejidad del prompt, la saturación de los servidores de Google y la estabilidad de la red celular del usuario. Los errores de límite de tasa de solicitudes (como la respuesta HTTP 429 Too Many Requests) y los fallos de conectividad intermitentes amenazaban con congelar la interfaz gráfica de usuario si no se manejaban mediante un flujo reactivo totalmente aislado.

Mi meta principal como arquitecto de software fue diseñar una solución integral que eliminara la latencia percibida por el usuario, protegiera la integridad de la memoria del sistema operativo mediante un pipeline de procesamiento optimizado en segundo plano, implementara persistencia local offline-first mediante Room Database y encapsulara la comunicación con la API a través de una capa de red tolerante a fallos y extremadamente resiliente.

Arquitectura de Solución: Pipeline Reactivo Unificado con Kotlin, Jetpack Compose y Room DB

Para resolver los problemas de rendimiento y seguridad, estructuré la arquitectura de Chronos Passport bajo los principios de Clean Architecture y la guía de diseño de Google para Android moderno. Diseñé la capa de presentación utilizando Jetpack Compose y Material 3, creando un estado de UI único e inmutable modelado mediante StateFlow y administrado desde un TimeTravelViewModel. Esta separación de responsabilidades me permitió desligar por completo las animaciones complejas del fotomatón —como la cuadrícula fotorrealista de la cámara, los indicadores de detección facial y las transiciones elásticas del portal temporal— de las operaciones intensivas de entrada y salida de red e imágenes.

En la capa de datos, implementé un repositorio unificado que actúa como la única fuente de verdad (Single Source of Truth). Para la captura de imágenes, integré una rutina de procesamiento utilizando la API MediaStore de Android y un escalado eficiente mediante mapas de bits en segundo plano ejecutado sobre el despachador Dispatchers.IO. Antes de enviar cualquier imagen a la capa de red impulsada por Retrofit, diseñé un módulo de compresión de imágenes sin pérdida visual que reduce el tamaño del payload en más del 70%, acelerando drásticamente el tiempo de subida a los servidores de la IA.

La persistencia de datos se estructuró mediante una base de datos local SQLite administrada por Room DB (v2.7.0). Diseñé la entidad TimeTravelPhoto para registrar de manera transaccional cada viaje temporal del usuario, guardando la ruta del archivo local en el almacenamiento scoped (Scoped Storage), la época histórica seleccionada, el prompt enriquecido generado automáticamente y el relato narrativo devuelto por el modelo multimodal Gemini. Esto garantizó una experiencia plenamente funcional en modo offline, permitiendo al usuario revisar su "Pasaporte Temporal" y aplicar filtros gráficos vintage (Sepia, Monocromo, Ciano) mediante shaders acelerados por GPU sin requerir una conexión activa a Internet.

Retos de Implementación: El Obstáculo más Difícil (Middleware Proxy y Resiliencia en la Orquestación de Payloads)

El mayor reto de ingeniería de este proyecto consistió en construir un mecanismo de orquestación capaz de gestionar las cuotas de solicitud, la retransmisión de paquetes fallidos y el formateo de datos binarios sin degradar la respuesta en la interfaz del dispositivo. Aunque la aplicación Android consume la API directamente a través de Retrofit y Coroutines, necesité concebir e implementar un servicio middleware proxy en Node.js que simulara un backend de producción intermedio. Este componente backend intercepta las peticiones de los clientes, valida los tokens de sesión, aplica un algoritmo de backoff exponencial adaptativo en caso de recibir respuestas HTTP 429 desde Gemini API, y normaliza la codificación multipart/form-data de las imágenes recibidas.

El desafío técnico se centró en la transmisión eficiente de buffers de imágenes Base64 de gran tamaño hacia el endpoint Gemini 2.5 Flash sin causar cuellos de botella en la memoria del servidor proxy. Diseñé una función asíncrona optimizada en Node.js que gestiona el procesamiento por streams, intercepta las cabeceras de reintento (Retry-After), valida las dimensiones de la imagen y reintenta la comunicación utilizando promesas controladas con un temporizador dinámico antes de responder al cliente Android.

A continuación, muestro el código real simplificado de este middleware de orquestación y reintento resiliente que implementé para garantizar un throughput constante y prevenir caídas de servicio:

/**
 * Middleware de Orquestación y Resiliencia para Gemini API
 * Desarrollado para gestionar peticiones multimodales de Chronos Passport.
 * Implementa retries con exponencial backoff, validación de buffers e inspección de cuotas HTTP 429.
 */

const { GoogleGenerativeAI } = require('@google/generative-ai');

const MAX_RETRIES = 3;
const INITIAL_BACKOFF_MS = 1500;

/**
 * Procesa la imagen Base64 y el prompt histórico con manejo de resiliencia
 * @param {string} base64Image - Buffer de la foto en formato string base64
 * @param {string} mimeType - Tipo de archivo (e.g. 'image/jpeg')
 * @param {string} promptText - Contexto e instrucciones para la época histórica
 * @returns {Promise<Object>} Resultado con el texto generado e metadata
 */
async function processHistoricalTransformation(base64Image, mimeType, promptText) {
  if (!base64Image || !promptText) {
    throw new Error("Payload inválido: Se requiere imagen base64 y prompt histórico.");
  }

  const aiClient = new GoogleGenerativeAI(process.env.GEMINI_API_KEY);
  const model = aiClient.getGenerativeModel({ model: "gemini-2.5-flash" });

  const imagePart = {
    inlineData: {
      data: base64Image,
      mimeType: mimeType || "image/jpeg"
    }
  };

  let attempt = 0;
  let delay = INITIAL_BACKOFF_MS;

  while (attempt < MAX_RETRIES) {
    try {
      // Ejecución de inferencia multimodal
      const result = await model.generateContent([promptText, imagePart]);
      const response = await result.response;
      const narrativeText = response.text();

      return {
        success: true,
        narrative: narrativeText,
        timestamp: new Date().toISOString(),
        attemptCount: attempt + 1
      };
    } catch (error) {
      attempt++;
      const status = error.status || (error.response ? error.response.status : null);

      // Manejo específico para Rate Limit Exceeded (HTTP 429) o errores temporales de servidor (5xx)
      if ((status === 429 || status >= 500) && attempt < MAX_RETRIES) {
        console.warn(`[Gemini Proxy] Intento ${attempt} falló con status ${status}. Reintentando en ${delay}ms...`);
        await new Promise(resolve => setTimeout(resolve, delay));
        delay *= 2; // Multiplicador de backoff exponencial
      } else {
        console.error(`[Gemini Proxy Error] Error no recuperable en intento ${attempt}:`, error.message);
        throw new Error(`Error en transformación de Gemini IA (HTTP ${status}): ${error.message}`);
      }
    }
  }

  throw new Error("Se superó el número máximo de reintentos con la API de Gemini.");
}

module.exports = { processHistoricalTransformation };

Resultados de Rendimiento: Optimización de Red, 0 Crashes por OOM y Tiempos de Respuesta Sub-Segunda en UI

La implementación de esta arquitectura integral transformó drásticamente el rendimiento y la estabilidad general de la aplicación móvil. Al trasladar la lógica de manipulación de bitmaps a hilos secundarios con Kotlin Coroutines y aplicar compresión adaptativa previa al envío de la red, logré reducir el consumo pico de memoria RAM en un 65%, eliminando por completo las excepciones OutOfMemory (OOM) registradas durante las pruebas de carga iniciales.

El pipeline de manejo de errores traducidos en Retrofit junto con el sistema de reintentos resilientes redujo la tasa de peticiones fallidas percibidas por el usuario final a menos del 0.5%. Las métricas principales alcanzadas en el proyecto incluyen:


En conclusión, Chronos Passport demuestra cómo la combinación de un diseño nativo moderno en Android con Kotlin, patrones de arquitectura limpia y un middleware de orquestación resiliente permite ofrecer experiencias de IA generativa avanzadas de forma rápida, robusta y completamente escalable.

Ver Código Fuente
Volver a Proyectos