Cómo integrar ChatGPT y DALL-E en Oracle APEX usando OpenAI

ChatGPT y DALL-E en Oracle APEX

Oracle APEX es una herramienta de gran velocidad de desarrollo, que nos permite implementar muchos tipos de aplicaciones, APIs, procesos, etc, ya que funciona integrado en la base de datos Oracle, junto con toda la seguridad y recursos que esta nos pueda ofrecer.

Es por ello que hoy aprenderemos cómo integrar ChatGPT y DALL-E en Oracle APEX usando el API que ofrece OpenAI.

Sin embargo, antes de hacer el paso a paso, es importante entender algunos conceptos:

Modelos de lenguaje

Los modelos de lenguaje, son versiones de GPT entrenadas para responder con una base de información hasta cierta fecha, adicionalmente, tienen subversiones con capacidades variadas, diferentes costos y precisión en las respuestas, esto lo podemos comprobar en el siguiente enlace: Modelos de lenguaje

Uso de datos

Debemos tomar en cuenta que una de las mayores diferencias de usar el servicio de pago vs el gratuito, es el uso de nuestros datos, debido a que al pagar, evitamos que nuestros datos sean usados para el entrenamiento de futuros modelos de AI.

Desde el 1 de marzo del 2023 los datos enviados a los API no serán usados para el entrenamiento. Esto no se aplica de igual manera al usar los servicios gratuitos de ChatGPT.

Normalmente los modelos de pago tienen un tiempo de retención de la información, esto lo podemos verificar en el siguiente enlance: Tiempo de retención de la información

Costos

Cuando usamos los servicios de pago, es importante aclarar cuales son los costos de entrada y los costos de salida de los tokens, al interactuar con los diferentes modelos de GTP.

Pero primero, qué son los tokens?

Representan la cantidad de caracteres o frases que se pueden realizar por petición al API de ChatGPT o DALL-E.

Los costos asociados para usar cada modelo, varian dependiento el modelo que se utilice, es por ello que les dejo el siguiente enlace para que lo verifiquen: Costos

Roles

Los roles son mensajes que proporcionan las instrucciones necesarias para que el modelo de lenguaje pueda dar una respuesta más precisa, se pueden especificar datos como los siguientes:

  • Una breve descripción del asistente
  • Rasgos de personalidad del asistente
  • Instrucciones o reglas que le gustaría que siguiera el asistente
  • Datos o información necesarios para el modelo, como preguntas relevantes provenientes de preguntas frecuentes

Existen 3 roles:

  • System
  • User
  • Assistant

System: El rol de system es el campo donde brindaremos contexto sobre el tema del que se está hablando, eso significa que si queremos hacer un asistente que use un lenguaje técnico de algún área, como un abogado, doctor, informático, o bien hasta le podemos decir que será un profesor de idiomas, de esta manera se comportará como tal, es aquí donde definimos como será nuestro asistente.

User: En el rol de user, le indicaremos como sería un ejemplo de una pregunta que haría un usuario.

Assistant: En este rol, le indicaremos al AI como sería un ejemplo de respuesta que le debe dar al usuario.

Model: Indicaremos el modelo que usaremos de GPT, puede consultar todos lo que hay en el siguiente enlace: Modelos GPT

Temperature: Esta función nos permitirá indicarle al asistente que tan preciso debe ser al momento de brindar las respuesta, el valor varía entre 0.0 a 2, mientras más cerca esté del 0 más concisas y directas serán sus respuestas, en caso contrario, mientras más cerca este del 2, las respuestas serán mucho más creativas.

Este es un ejemplo del json a enviar cuando envíemos el request:

{
    "model": "gpt-4o-mini",
    "messages": [
      {"role": "system", "content": "Un niño perdió a su abuelita y ella siempre le responde con amor y cariño, responde todas las preguntas, nunca le decía que no sabía algo sobre un tema y sino lo sabía se lo inventaba o le decías las respuestas de forma hipotetica"},
{"role": "user", "content": "¿Cómo se hace una torta de chocolate?"}, 
{"role": "assistant", "content": "Mi nieto lindo los materiales son huevo, leche, harina, chocolate amargo"}, 
{"role": "user", "content": "' || l_contenido || '"}
    ],
    "temperature": 0.5
  }

Pasos para obtener un API Key de OpenAI

Ahora bien, vamos a generar un API Key, esta clave será necesaria para todo el proceso de integración con Oracle APEX, para ello, seguiremos los siguiente pasos:

  1. Registrarse en OpenAI
  2. Es necesario configurar una tarjeta de crédito ya que usaremos servicios de pago, les recomiendo recargar 5 dólares, con eso es más que suficiente para preubas.
  3. Crear un API Key, esto se puede hacer desde el perfil o usando el siguiente enlace: API Key

Nota: El API Key solo se podrá ver la vez en que se genera, es recomendable guardarla ya que para hacer la autenticación será necesario usarla.

Configuración del certificado SSL

Si trabajamos en un ambiente on-premise será necesario bajar el certificado del url donde se realizarán las consulta al API de OpenAI esto es necesario localmente ya que sino la base de datos no resolverá la conexión, además será necesario crear un ACL también.

Nota: En caso de trabajar en Autonomous Database, solo es necesaria la creación del ACL.

En el caso de tener la BD on-premise, vamos a consultar al API usando el siguiente enlace:

https://api.openai.com/v1/chat/completions

Para descargar el certificado solamente debemos ingresar a la información del URL para ver el certificado SSL:

Luego iremos a la pestaña de detalles y presionaremos exportar, cuando lo guarden, lo pueden realizar con la extesión .crt:

Posterios a ello será necesario crear el wallet en sistema operativo, para hacerlo en linux, pueden hacerlo usando los siguientes comandos, donde creamos el wallet e importamos el certificado como confiable:

mkdir -p /u01/wallet
orapki wallet create -wallet /u01/wallet -pwd pruebas1234 -auto_login
orapki wallet add -wallet /u01/wallet -trusted_cert -cert "/tmp/Baltimore CyberTrust Root.crt" -pwd prueba1234

Creación del ACL

Posterior a ello, en la base de datos, será necesario crea un ACL para lograr acceder al dominio de OpenAI para que permita la conexión, dependiendo la versión de la base de datos, esto puede cambiar, sin embargo, les dejo un ejemplo de ACL:

Nota: El ACL se debe crear tanto para el schema de APEX como para el schema donde se ejecutan las aplicaciones de APEX.

BEGIN
    DBMS_NETWORK_ACL_ADMIN.APPEND_HOST_ACE(
        host => '*',
        ace => xs$ace_type(privilege_list => xs$name_list('connect', 'resolve'),
                           principal_name => 'APEX_240100',
                           principal_type => xs_acl.ptype_db));
END;
                           
BEGIN
    DBMS_NETWORK_ACL_ADMIN.APPEND_HOST_ACE(
        host => '*',
        ace => xs$ace_type(privilege_list => xs$name_list('connect', 'resolve'),
                           principal_name => 'SCHEMA_APPS_APEX',
                           principal_type => xs_acl.ptype_db));
END;

Creación del request en Oracle APEX

Vamos a crear una interfaz como la siguiente, donde le haremos una consulta al API de ChatGPT simulando que es una abuelita que nos tratará de una forma cariñosa:

Ahora vamos a importar una librería llamada Sweet Alert, para ello iremos a las propiedades de la aplicación -> Interfaz de Usuario -> JavaScript -> URL de Archivos y colocaremos la siguiente url:

https://cdn.jsdelivr.net/npm/sweetalert2@11

Ahora dentro de la aplicación, crearemos un AJAX Callback function, le llamaremos Call_request:

Dentro del Código PL/SQL agregaremos lo siguiente, donde haremos la autenticación con el API y en la línea 27 colocaremos el API Key que generamos en pasos anteriores, luego en la línea 33 colocaremos la ruta al wallet con el certificado de OpenAI que descargamos:

declare
  l_contenido VARCHAR(4500);
  l_url       VARCHAR2(4000) := 'https://api.openai.com/v1/chat/completions';
  l_request   CLOB;
  l_response  CLOB;

begin
l_contenido := apex_application.g_x01;

l_request := '
  {
    "model": "gpt-4o-mini",
    "messages": [
      {"role": "system", "content": "Un niño perdió a su abuelita y ella siempre le responde con amor y cariño, responde todas las preguntas, nunca le decía que no sabía algo sobre un tema y sino lo sabía se lo inventaba o le decías las respuestas de forma hipotetica"},
{"role": "user", "content": "¿Cómo se hace una torta de chocolate?"}, 
{"role": "assistant", "content": "Mi nieto lindo los materiales son huevo, leche, harina, chocolate amargo"}, 
{"role": "user", "content": "' || l_contenido || '"}
    ],
    "temperature": 0.5
  }';

 
-- Establece los encabezados
  apex_web_service.g_request_headers(1).name := 'Content-Type';
  apex_web_service.g_request_headers(1).value := 'application/json';
  apex_web_service.g_request_headers(2).name := 'Authorization';
  apex_web_service.g_request_headers(2).value := 'Bearer sk-proj-C98p5R18vm8u436I6G7XYtfYdW2GfQDC';


  l_response := apex_web_service.make_rest_request(
    p_url         => l_url,
    p_http_method => 'POST',
    p_wallet_path => 'file:/u01/wallet',
    p_body        => l_request
  );


apex_json.open_object; --Abrimos el objeto -- {

apex_json.write(
    p_name => 'Estado',
    p_value => 1
);
apex_json.write(
    p_name => 'Mensaje',
    p_value => l_response
);

apex_json.close_object; --Cerramos el objeto -- }

exception when no_data_found then

apex_json.open_object; --Abrimos el objeto -- {

apex_json.write(
    p_name => 'Estado',
    p_value => 0
);
apex_json.write(
    p_name => 'Mensaje',
    p_value => 'Error en el request'
);

apex_json.close_object; --Cerramos el objeto -- }

end;

Ahora nos iremos al botón y crearemos una acción dinámica de tipo «Ejecutar código JavaScript»:

Dentro del código de acción en TRUE colocaremos lo siguiente:

let timerInterval
Swal.fire({
  title: 'Realizando consulta a GPT...',
  html: 'No cierre la ventana del navegador',
  timer: 50000,
  timerProgressBar: false,
  didOpen: () => {
    Swal.showLoading()
    timerInterval = setInterval(() => {
      const content = Swal.getHtmlContainer()
      if (content) {
        const b = content.querySelector('b')
        if (b) {
          b.textContent = Swal.getTimerLeft()
        }
      }
    }, 100)

apex.server.process("Call_request", 
    {
        x01: $v("MENSAJE_REQ")
    },  
    {
      success: function (datos) {   
          console.log(datos);  
        if(datos.Estado == 1){
            console.log(datos.Mensaje);
        var jsonObject = JSON.parse(datos.Mensaje);
            $s("MENSAJE_RES",jsonObject.choices[0].message.content);
            apex.message.clearErrors();
            const box = document.getElementsByClassName('swal2-container')[0];
            box.style.opacity = '1';
            function fadeOut(element) {
            let opacity = 1;
            const interval = 50;
            const duration = 100; // milisegundos (tiempo total para el fade out)
            const steps = duration / interval;
            const delta = opacity / steps;

            const fadeOutInterval = setInterval(() => {
                opacity -= delta;
                element.style.opacity = opacity;

                if (opacity <= 0) {
                clearInterval(fadeOutInterval);
                element.style.visibility = 'hidden';
                }
            }, interval);
            }
            fadeOut(box);
        }else{
            apex.message.clearErrors();
            apex.message.showErrors({
                type: "errors",
                location: "page",
                message: "Error de comunicación con ChatGPT",
                unsafe: false
            });
            const box = document.getElementsByClassName('swal2-container')[0];
            box.style.opacity = '1';
            function fadeOut(element) {
            let opacity = 1;
            const interval = 50;
            const duration = 100; // milisegundos (tiempo total para el fade out)
            const steps = duration / interval;
            const delta = opacity / steps;

            const fadeOutInterval = setInterval(() => {
                opacity -= delta;
                element.style.opacity = opacity;

                if (opacity <= 0) {
                clearInterval(fadeOutInterval);
                element.style.visibility = 'hidden';
                }
            }, interval);
            }
            fadeOut(box);
        }
    }
    }
  );

  },
  willClose: () => {
    clearInterval(timerInterval)
  }
}).then((result) => {
  if (result.dismiss === Swal.DismissReason.timer) {
    console.log('I was closed by the timer')
  }
})

Luego crearemos otra acción dinámica pero dentro de la opción FALSE y le añadiremos el siguiente código:

// First clear the errors
apex.message.clearErrors();
// Mostramos los nuevos mensajes de error
if ($v('MENSAJE_REQ') == ''){
apex.message.showErrors([
{
type: "error",
location: [ "page", "inline" ],
pageItem: "MENSAJE_REQ",
message: "Debe ingresar un mensaje",
unsafe: false
}
]);
}//cierre if

Guardamos y ejecutaremos la aplicación, le preguntaremos algo a la abuela, con la librería que importamos, veremos una ventana de carga flotante:

Luego veremos la respuesta cariñosa de la abuela:

De esta forma podemos crear diferentes modelos de GPT que usen funciones o lenguaje propio que nosotros le indiquemos, todo depende del contexto que le brindemos en el rol de System.

Integrando DALL-E

Para integrar DALL-E en Oracle APEX, el proceso es el mismo con la diferencia de que el AJAX Callback function cambiará el url de consulta y el json que se envía, lo podemos ver en el siguiente ejemplo:

declare
  l_contenido VARCHAR(4500);
  l_url       VARCHAR2(4000) := 'https://api.openai.com/v1/images/generations';
  l_request   CLOB;
  l_response  CLOB;

begin
l_contenido := apex_application.g_x01;

l_request := '
  {
    "model": "dall-e-3",
    "prompt": "'||l_contenido||'",
    "n": 1,
    "size": "1024x1024"
  }';

  

-- Establece los encabezados
  apex_web_service.g_request_headers(1).name := 'Content-Type';
  apex_web_service.g_request_headers(1).value := 'application/json';
  apex_web_service.g_request_headers(2).name := 'Authorization';
  apex_web_service.g_request_headers(2).value := 'Bearer sk-proj-pRZIbpMGxR1DLUEJmfZ1T3BlbkFJfhvb7gTIYNiz81G7h3D2';


  l_response := apex_web_service.make_rest_request(
    p_url         => l_url,
    p_http_method => 'POST',
    p_wallet_path => 'file:/u01/wallet',
    p_body        => l_request
  );


apex_json.open_object; --Abrimos el objeto -- {

apex_json.write(
    p_name => 'Estado',
    p_value => 1
);
apex_json.write(
    p_name => 'Mensaje',
    p_value => l_response
);

apex_json.close_object; --Cerramos el objeto -- }

exception when no_data_found then

apex_json.open_object; --Abrimos el objeto -- {

apex_json.write(
    p_name => 'Estado',
    p_value => 0
);
apex_json.write(
    p_name => 'Mensaje',
    p_value => 'Error en el request'
);

apex_json.close_object; --Cerramos el objeto -- }

end;

Ahora, solo debemos crear una interfaz como la siguiente:

Y para procesar el AJAX Callback function, en la acción dinámica del botón, colocaremos el siguiente código:

let timerInterval
Swal.fire({
  title: 'Realizando consulta a DALL-E...',
  html: 'No cierre la ventana del navegador',
  timer: 50000,
  timerProgressBar: false,
  didOpen: () => {
    Swal.showLoading()
    timerInterval = setInterval(() => {
      const content = Swal.getHtmlContainer()
      if (content) {
        const b = content.querySelector('b')
        if (b) {
          b.textContent = Swal.getTimerLeft()
        }
      }
    }, 100)

apex.server.process("dalle", 
    {
        x01: $v("P2_TEXT_REQUEST")
    },  
    {
      success: function (datos) {   
          console.log(datos);  
        if(datos.Estado == 1){
            console.log(datos.Mensaje);
        var jsonObject = JSON.parse(datos.Mensaje);
            apex.message.clearErrors();
            const box = document.getElementsByClassName('swal2-container')[0];
            box.style.opacity = '1';
            function fadeOut(element) {
            let opacity = 1;
            const interval = 50;
            const duration = 100; // milisegundos (tiempo total para el fade out)
            const steps = duration / interval;
            const delta = opacity / steps;

            const fadeOutInterval = setInterval(() => {
                opacity -= delta;
                element.style.opacity = opacity;

                if (opacity <= 0) {
                clearInterval(fadeOutInterval);
                element.style.visibility = 'hidden';
                }
            }, interval);
            }
            fadeOut(box);

            const imageUrl = jsonObject.data[0].url;
            Swal.fire({
                title: 'Tu imagen',
                html: `<img src="${imageUrl}" alt="Imagen" style="max-width: 100%; height: auto;">
                       <br>
                       <a href="${imageUrl}" target="_blank" download="imagen.jpg" class="t-Button" style="margin-top: 20px;">Agrandar Imagen</a>`,
                showConfirmButton: false
            });


        }else{
            apex.message.clearErrors();
            apex.message.showErrors({
                type: "errors",
                location: "page",
                message: "Error de comunicación con ChatGPT",
                unsafe: false
            });
            const box = document.getElementsByClassName('swal2-container')[0];
            box.style.opacity = '1';
            function fadeOut(element) {
            let opacity = 1;
            const interval = 50;
            const duration = 100; // milisegundos (tiempo total para el fade out)
            const steps = duration / interval;
            const delta = opacity / steps;

            const fadeOutInterval = setInterval(() => {
                opacity -= delta;
                element.style.opacity = opacity;

                if (opacity <= 0) {
                clearInterval(fadeOutInterval);
                element.style.visibility = 'hidden';
                }
            }, interval);
            }
            fadeOut(box);
        }
    }
    }
  );

  },
  willClose: () => {
    clearInterval(timerInterval)
  }
}).then((result) => {
  if (result.dismiss === Swal.DismissReason.timer) {
    console.log('I was closed by the timer')
  }
})

El json que retorna la imagen, nos mostrará un URL donde la podremos descargar, para ello cree un popup con la carga de la imagen y un botón para agrandarla y descargarla.

De esta manera podemos generar imágenes en Oracle APEX integrado con DALL-E.

Posibles aplicaciones

Lo más importante al integrar AI en Oracle APEX, es entender cuales podrían ser las posibles aplicaciones que le daremos, todo está en nuestra creatividad, sin embargo, les brindo algunos posibles ejemplos de cada uno:

ChatGPT

  • Onboarding y capacitación
  • Profesor de idiomas
  • Coach motivacional
  • Novia virtual
  • Consejero personal
  • Doctor para saber qué medicinas puedo tomar

DALL-E

  • E-commerce
  • Marketing y Publicidad
  • Control sobre el uso de AI
  • Material didáctico visual
  • Prototipado visual
  • Portadas y carteles

De esta forma es como logramos integrar los modelos de GPT de OpenAI en nuestras aplicaciones hechas en Oracle APEX.

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *