Tutorial 15: Creando el Método para Preparar los Datos de la API


¡Hola y bienvenido de nuevo!

En esta lección vamos a crear un método que prepare los datos que enviaremos a la API de OpenAI. Este método será fundamental para estructurar correctamente la información según el tipo de operación que queramos realizar. ¡Vamos a ello!


📋 Contenido del Tutorial

  1. Entendiendo los datos necesarios para la API

  2. Agregando nuevas propiedades

  3. Creando el método getData

  4. Datos para ASR (Transcripción)

  5. Datos para Traducción

  6. Código completo

  7. Explicación detallada

  8. Próximos pasos


📊 1. Entendiendo los datos necesarios para la API

¿Qué datos necesita la API?

Para comunicarnos con la API de OpenAI, necesitamos enviar diferentes tipos de datos según la operación:

OperaciónDatos necesariosFormato
ASRArchivo + Modelomultipart/form-data
TraducciónModelo + Mensajesapplication/json

Documentación de referencia

Para ASR (Speech to Text):

text
https://api.openai.com/v1/audio/transcriptions

Datos requeridos:

json
{
  "file": "archivo.mp3",
  "model": "whisper-1"
}

Para Traducción (Chat Completions):

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

Datos requeridos:

json
{
  "model": "gpt-3.5-turbo",
  "messages": [
    {
      "role": "system",
      "content": "Instrucción para el sistema"
    },
    {
      "role": "user",
      "content": "Texto a traducir"
    }
  ]
}

🏷️ 2. Agregando nuevas propiedades

Propiedades necesarias

php
public $file;      // Ruta del archivo a transcribir
public $content;   // Texto a traducir
public $lang;      // Idioma destino para la traducción

¿Para qué sirve cada una?

PropiedadTipoUsoEjemplo
$filestringRuta del archivo subidofiles/video.mp4
$contentstringTexto a traducir"Hello world"
$langstringIdioma destino"Spanish"

Agregando las propiedades a la clase

php
class Whisper {
    public $error;
    public $dataType;
    public $file;      // ← Nueva propiedad
    public $content;   // ← Nueva propiedad
    public $lang;      // ← Nueva propiedad
    private $DB;
    
    // ... resto de la clase
}

🛠️ 3. Creando el método getData

Estructura básica

php
public function getData() {
    if ($this->dataType === "ASR") {
        // Datos para transcripción
        return [
            'file' => $this->file,
            'model' => 'whisper-1'
        ];
    } else {
        // Datos para traducción
        return json_encode([
            'model' => 'gpt-3.5-turbo',
            'messages' => [
                // Mensajes para el chat
            ]
        ]);
    }
}

¿Qué hace este método?

  1. Verifica el tipo de operación (dataType)

  2. Si es ASR: Prepara datos para transcripción

  3. Si es otro: Prepara datos para traducción

  4. Retorna los datos en el formato correcto


🎙️ 4. Datos para ASR (Transcripción)

Estructura de datos

php
if ($this->dataType === "ASR") {
    return [
        'file' => $this->file,
        'model' => 'whisper-1'
    ];
}

Explicación de los campos

CampoValorDescripción
file$this->fileRuta del archivo a transcribir
modelwhisper-1Modelo de Whisper a usar

¿Cómo se usa?

php
// En index.php
$whisperObj->dataType = "ASR";
$whisperObj->file = $filePath; // Ruta del archivo subido
$data = $whisperObj->getData();
// $data = ['file' => 'files/video.mp4', 'model' => 'whisper-1']

Parámetros opcionales

También podemos agregar parámetros adicionales:

php
return [
    'file' => $this->file,
    'model' => 'whisper-1',
    'language' => 'es',           // Idioma del audio
    'response_format' => 'json',  // Formato de respuesta
    'temperature' => 0.0          // Creatividad (0-1)
];

🌍 5. Datos para Traducción

Estructura de datos

php
else {
    return json_encode([
        'model' => 'gpt-3.5-turbo',
        'messages' => [
            [
                'role' => 'system',
                'content' => 'You will be provided with a text, and your task is to translate it into ' . $this->lang
            ],
            [
                'role' => 'user',
                'content' => $this->content
            ]
        ]
    ]);
}

Explicación de los campos

CampoValorDescripción
modelgpt-3.5-turboModelo de ChatGPT a usar
messagesArrayMensajes para el chat
role: systemInstrucciónDefine el comportamiento del asistente
role: userContenidoTexto a traducir

Estructura de mensajes

php
'messages' => [
    [
        'role' => 'system',
        'content' => 'Instrucción para el sistema'
    ],
    [
        'role' => 'user',
        'content' => 'Texto del usuario'
    ]
]

¿Qué es cada role?

RolePropósitoEjemplo
systemDefine el comportamiento del asistente"Eres un traductor profesional"
userMensaje del usuario"Hello, how are you?"
assistantRespuesta del asistente(Lo genera la API)

Ejemplo de traducción

php
// Configuración
$this->lang = "Spanish";
$this->content = "Hello, how are you today?";

// Resultado en JSON
{
    "model": "gpt-3.5-turbo",
    "messages": [
        {
            "role": "system",
            "content": "You will be provided with a text, and your task is to translate it into Spanish"
        },
        {
            "role": "user",
            "content": "Hello, how are you today?"
        }
    ]
}

💻 6. Código completo

Archivo: backend/classes/Whisper.php

php
<?php
/**
 * Clase Whisper
 * 
 * Maneja la subida, transcripción y traducción de archivos
 * utilizando la API de OpenAI
 */
class Whisper {
    /**
     * @var string Mensajes de error
     */
    public $error;
    
    /**
     * @var string Tipo de operación (ASR o TRANSLATE)
     */
    public $dataType;
    
    /**
     * @var string Ruta del archivo para transcripción
     */
    public $file;
    
    /**
     * @var string Texto para traducción
     */
    public $content;
    
    /**
     * @var string Idioma destino para la traducción
     */
    public $lang;
    
    /**
     * @var PDO Conexión a la base de datos
     */
    private $DB;
    
    /**
     * Constructor - Establece conexión a la base de datos
     */
    public function __construct() {
        $db = new DB();
        $this->DB = $db->connect();
    }
    
    /**
     * Obtiene el mensaje de error actual
     * 
     * @return string El mensaje de error
     */
    public function errors() {
        return $this->error;
    }
    
    /**
     * Obtiene la URL de la API según el tipo de operación
     * 
     * @return string URL del endpoint de la API
     */
    public function getApiUrl() {
        if ($this->dataType === "ASR") {
            return "https://api.openai.com/v1/audio/transcriptions";
        } else {
            return "https://api.openai.com/v1/chat/completions";
        }
    }
    
    /**
     * Obtiene los headers necesarios para la API
     * 
     * @return array|false Headers para la solicitud HTTP
     */
    public function getHeader() {
        // Verificar que la API Key esté definida
        if (!defined('API_TOKEN')) {
            $this->error = "API_TOKEN no está definido";
            return false;
        }
        
        // Headers para ASR (subida de archivos)
        if ($this->dataType === "ASR") {
            return [
                'Authorization: Bearer ' . API_TOKEN,
                'Content-Type: multipart/form-data'
            ];
        } 
        // Headers para otros (JSON)
        else {
            return [
                'Authorization: Bearer ' . API_TOKEN,
                'Content-Type: application/json'
            ];
        }
    }
    
    /**
     * Prepara los datos para enviar a la API
     * 
     * @return array|string Datos preparados según el tipo de operación
     */
    public function getData() {
        // Caso 1: ASR - Transcripción de audio/video
        if ($this->dataType === "ASR") {
            // Verificar que el archivo existe
            if (!file_exists($this->file)) {
                $this->error = "El archivo no existe";
                return false;
            }
            
            // Crear el archivo para cURL
            $fileData = curl_file_create($this->file);
            
            // Datos para la transcripción
            return [
                'file' => $fileData,
                'model' => 'whisper-1',
                'language' => 'es',
                'response_format' => 'json'
            ];
        } 
        // Caso 2: Traducción de texto
        else {
            // Verificar que hay contenido para traducir
            if (empty($this->content)) {
                $this->error = "No hay contenido para traducir";
                return false;
            }
            
            // Verificar que hay un idioma destino
            if (empty($this->lang)) {
                $this->error = "No se especificó el idioma destino";
                return false;
            }
            
            // Preparar los mensajes para la traducción
            $messages = [
                [
                    'role' => 'system',
                    'content' => 'Eres un traductor profesional. Traduce el siguiente texto al ' . $this->lang . ' de forma precisa y natural.'
                ],
                [
                    'role' => 'user',
                    'content' => $this->content
                ]
            ];
            
            // Datos para la traducción
            return json_encode([
                'model' => 'gpt-3.5-turbo',
                'messages' => $messages,
                'temperature' => 0.7
            ]);
        }
    }
    
    /**
     * Sube un archivo al servidor
     * 
     * @param array $file Arreglo $_FILES
     * @return string|false Ruta del archivo o false en caso de error
     */
    public function upload($file) {
        // 1. Extraer información del archivo
        $fileTmp  = $file['tmp_name'];
        $filename = basename($file['name']);
        $fileSize = $file['size'];
        $errors   = $file['error'];
        $mime     = $file['type'];
        
        // 2. Obtener la extensión del archivo
        $ext = pathinfo($filename, PATHINFO_EXTENSION);
        $ext = strtolower($ext);
        
        // 3. Obtener el directorio raíz del proyecto
        $parentDirectory = dirname(dirname(dirname(__FILE__)));
        
        // 4. Definir los tipos de archivo permitidos
        $allowedMedia = [
            'video/mp4',
            'video/mpeg',
            'audio/mpeg',
            'audio/mpeg3',
            'audio/wav'
        ];
        
        // 5. Validar el tipo de archivo
        if (!in_array($mime, $allowedMedia)) {
            $this->error = "Formato de archivo inválido";
            return false;
        }
        
        // 6. Validar el tamaño del archivo (máximo 20 MB)
        if ($fileSize > 20000000) {
            $this->error = "El archivo es demasiado grande (máximo 20 MB)";
            return false;
        }
        
        // 7. Crear nombre único y subir el archivo
        $folder = 'files/';
        $uniqueName = md5(time() . mt_rand());
        $file = $folder . $uniqueName . '.' . $ext;
        
        // 8. Mover el archivo
        if (move_uploaded_file($fileTmp, $parentDirectory . '/' . $file)) {
            return $file;
        } else {
            $this->error = "Error al mover el archivo";
            return false;
        }
    }
    
    /**
     * Prepara la solicitud completa para la API
     * 
     * @return array|false Datos de la solicitud o false en caso de error
     */
    public function prepareRequest() {
        // 1. Obtener URL
        $url = $this->getApiUrl();
        if (!$url) {
            return false;
        }
        
        // 2. Obtener headers
        $headers = $this->getHeader();
        if (!$headers) {
            return false;
        }
        
        // 3. Obtener datos
        $data = $this->getData();
        if (!$data) {
            return false;
        }
        
        // 4. Retornar todos los datos de la solicitud
        return [
            'url' => $url,
            'headers' => $headers,
            'data' => $data,
            'dataType' => $this->dataType
        ];
    }
    
    /**
     * Envía la solicitud a la API de OpenAI
     * 
     * @param array $requestData Datos preparados
     * @return string|false Respuesta de la API o false en caso de error
     */
    public function sendRequest($requestData) {
        // Inicializar cURL
        $ch = curl_init();
        
        // Configurar cURL
        curl_setopt($ch, CURLOPT_URL, $requestData['url']);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_POST, true);
        curl_setopt($ch, CURLOPT_TIMEOUT, 120);
        
        // Configurar headers
        curl_setopt($ch, CURLOPT_HTTPHEADER, $requestData['headers']);
        
        // Configurar datos POST
        curl_setopt($ch, CURLOPT_POSTFIELDS, $requestData['data']);
        
        // Ejecutar la solicitud
        $response = curl_exec($ch);
        $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        $curlError = curl_error($ch);
        
        // Cerrar cURL
        curl_close($ch);
        
        // Verificar errores
        if ($curlError) {
            $this->error = "Error cURL: " . $curlError;
            return false;
        }
        
        if ($httpCode !== 200) {
            $this->error = "Error HTTP: " . $httpCode . " - " . $response;
            return false;
        }
        
        return $response;
    }
    
    /**
     * Procesa la transcripción de un archivo
     * 
     * @param string $filePath Ruta del archivo
     * @return string|false Texto transcrito o false en caso de error
     */
    public function transcribe($filePath) {
        // Configurar el tipo de operación
        $this->dataType = "ASR";
        $this->file = $filePath;
        
        // Preparar la solicitud
        $request = $this->prepareRequest();
        if (!$request) {
            return false;
        }
        
        // Enviar la solicitud
        $response = $this->sendRequest($request);
        if (!$response) {
            return false;
        }
        
        // Procesar la respuesta
        $data = json_decode($response, true);
        if (isset($data['text'])) {
            return $data['text'];
        } else {
            $this->error = "Respuesta inesperada de la API";
            return false;
        }
    }
    
    /**
     * Traduce un texto a otro idioma
     * 
     * @param string $content Texto a traducir
     * @param string $lang Idioma destino
     * @return string|false Texto traducido o false en caso de error
     */
    public function translate($content, $lang) {
        // Configurar el tipo de operación
        $this->dataType = "TRANSLATE";
        $this->content = $content;
        $this->lang = $lang;
        
        // Preparar la solicitud
        $request = $this->prepareRequest();
        if (!$request) {
            return false;
        }
        
        // Enviar la solicitud
        $response = $this->sendRequest($request);
        if (!$response) {
            return false;
        }
        
        // Procesar la respuesta
        $data = json_decode($response, true);
        if (isset($data['choices'][0]['message']['content'])) {
            return $data['choices'][0]['message']['content'];
        } else {
            $this->error = "Respuesta inesperada de la API";
            return false;
        }
    }
}
?>

Archivo: index.php (sección PHP actualizada)

php
<?php
    include 'backend/init.php';
    
    $error = null;
    $success = null;
    
    if ($_SERVER['REQUEST_METHOD'] === "POST") {
        if (isset($_FILES['file']) && !empty($_FILES['file']['name'])) {
            
            // 1. Subir el archivo
            $filePath = $whisperObj->upload($_FILES['file']);
            
            if ($filePath) {
                // 2. Configurar para transcripción
                $whisperObj->dataType = "ASR";
                $whisperObj->file = $filePath;
                
                // 3. Obtener los datos
                $data = $whisperObj->getData();
                
                // 4. Mostrar información para debug
                echo "<h3>Datos preparados:</h3>";
                echo "<pre>";
                print_r($data);
                echo "</pre>";
                
                // 5. Aquí llamaremos a la API en la próxima lección
                
            } else {
                $error = $whisperObj->errors();
            }
        } else {
            $error = "Por favor selecciona un archivo";
        }
    }
?>

📖 7. Explicación detallada

El método getData paso a paso

php
public function getData() {
    // 1. ASR - Transcripción
    if ($this->dataType === "ASR") {
        // Verificar que el archivo existe
        if (!file_exists($this->file)) {
            $this->error = "El archivo no existe";
            return false;
        }
        
        // Preparar los datos
        return [
            'file' => curl_file_create($this->file),
            'model' => 'whisper-1'
        ];
    } 
    // 2. Traducción
    else {
        // Verificar que hay contenido
        if (empty($this->content)) {
            $this->error = "No hay contenido para traducir";
            return false;
        }
        
        // Preparar los mensajes
        $messages = [
            [
                'role' => 'system',
                'content' => 'Traduce al ' . $this->lang
            ],
            [
                'role' => 'user',
                'content' => $this->content
            ]
        ];
        
        // Convertir a JSON
        return json_encode([
            'model' => 'gpt-3.5-turbo',
            'messages' => $messages
        ]);
    }
}

¿Qué hace curl_file_create()?

curl_file_create() crea un objeto de archivo para cURL:

php
$fileData = curl_file_create('/ruta/al/archivo.mp3');

Equivalente a:

php
$fileData = new CURLFile('/ruta/al/archivo.mp3');

Estructura de mensajes para traducción

php
// Sistema: Define el comportamiento
[
    'role' => 'system',
    'content' => 'Traduce al Spanish'
]

// Usuario: El texto a traducir
[
    'role' => 'user',
    'content' => 'Hello world'
]

¿Por qué JSON encode?

php
return json_encode([
    'model' => 'gpt-3.5-turbo',
    'messages' => $messages
]);
  • La API espera datos en formato JSON

  • json_encode() convierte el array a JSON

  • Ejemplo: {"model":"gpt-3.5-turbo","messages":[...]}


🧪 8. Probando la funcionalidad

Prueba 1: Datos para ASR

php
$whisperObj->dataType = "ASR";
$whisperObj->file = "files/video.mp4";
$data = $whisperObj->getData();
print_r($data);

Resultado esperado:

text
Array
(
    [file] => CURLFile Object
        (
            [name] => files/video.mp4
            [mime] => 
            [postname] => 
        )
    [model] => whisper-1
)

Prueba 2: Datos para Traducción

php
$whisperObj->dataType = "TRANSLATE";
$whisperObj->content = "Hello world";
$whisperObj->lang = "Spanish";
$data = $whisperObj->getData();
echo $data;

Resultado esperado:

json
{
    "model": "gpt-3.5-turbo",
    "messages": [
        {
            "role": "system",
            "content": "Traduce al Spanish"
        },
        {
            "role": "user",
            "content": "Hello world"
        }
    ]
}

Prueba 3: Verificar errores

php
// Sin archivo
$whisperObj->file = "archivo_inexistente.mp3";
$data = $whisperObj->getData();
if ($data === false) {
    echo "Error: " . $whisperObj->errors();
}

📊 9. Resumen de la clase Whisper hasta ahora

Propiedades

PropiedadTipoDescripción
$errorstringMensajes de error
$dataTypestringTipo de operación (ASR/TRANSLATE)
$filestringRuta del archivo
$contentstringTexto para traducir
$langstringIdioma destino
$DBPDOConexión a la base de datos

Métodos

MétodoParámetrosRetornoDescripción
__construct()-voidConstructor, conecta a BD
errors()-stringDevuelve el error actual
getApiUrl()-stringURL de la API
getHeader()-array/falseHeaders HTTP
getData()-array/string/falseDatos para la API
upload()$filestring/falseSube un archivo
prepareRequest()-array/falsePrepara la solicitud
sendRequest()$requestDatastring/falseEnvía solicitud
transcribe()$filePathstring/falseTranscribe archivo
translate()$content, $langstring/falseTraduce texto

Diagrama de flujo completo

text
┌─────────────────────────────────────────────────────────┐
│              SOLICITUD COMPLETA A LA API                │
├─────────────────────────────────────────────────────────┤
│                                                         │
│  1. Configurar dataType                                │
│         ↓                                              │
│  2. Configurar propiedades (file/content/lang)         │
│         ↓                                              │
│  3. getApiUrl() → Endpoint correcto                   │
│         ↓                                              │
│  4. getHeader() → Headers correctos                   │
│         ↓                                              │
│  5. getData() → Datos formateados                     │
│         ↓                                              │
│  6. prepareRequest() → Ensamblar todo                 │
│         ↓                                              │
│  7. sendRequest() → Enviar con cURL                   │
│         ↓                                              │
│  8. Procesar respuesta                                │
│         ↓                                              │
│  9. Devolver resultado                                │
│                                                         │
└─────────────────────────────────────────────────────────┘

🎯 10. Próximos pasos

Lo que hemos logrado

✅ Método getData() implementado
✅ Datos para ASR configurados
✅ Datos para Traducción configurados
✅ Propiedades $file, $content, $lang agregadas
✅ Verificación de datos antes de enviar

Lo que viene en la próxima lección

En la siguiente lección vamos a:

  1. Implementar cURL para enviar las solicitudes

  2. Enviar archivos a la API de OpenAI

  3. Recibir respuestas de la API

  4. Procesar los resultados y mostrarlos

Avance del código de la próxima lección

php
// En Whisper.php - Método completo de envío
public function sendToAPI() {
    // 1. Preparar la solicitud
    $request = $this->prepareRequest();
    if (!$request) {
        return false;
    }
    
    // 2. Iniciar cURL
    $ch = curl_init();
    curl_setopt($ch, CURLOPT_URL, $request['url']);
    curl_setopt($ch, CURLOPT_HTTPHEADER, $request['headers']);
    curl_setopt($ch, CURLOPT_POSTFIELDS, $request['data']);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    
    // 3. Enviar y obtener respuesta
    $response = curl_exec($ch);
    curl_close($ch);
    
    // 4. Procesar respuesta
    return json_decode($response, true);
}

❓ Preguntas frecuentes

¿Por qué necesito preparar los datos?

  • La API espera datos en un formato específico

  • Los datos deben estructurarse correctamente

¿Qué es curl_file_create()?

  • Crea un objeto CURLFile para enviar archivos

  • Necesario para subir archivos con cURL

¿Por qué usar json_encode()?

  • La API espera datos en formato JSON

  • Convierte el array PHP a JSON

¿Qué pasa si falta algún dato?

  • La API devolverá un error

  • Nuestro método verifica los datos antes de enviar

¿Puedo usar otros modelos de OpenAI?

  • Sí, puedes usar whisper-1, gpt-3.5-turbo, gpt-4, etc.

  • Cada modelo tiene diferentes capacidades y costos

¿Qué es el role 'system'?

  • Define el comportamiento del asistente

  • Establece el contexto de la conversación


🎓 Ejercicio práctico

Ejercicio 1: Agregar más parámetros a ASR

Añade parámetros opcionales a la transcripción:

php
public function getData() {
    if ($this->dataType === "ASR") {
        return [
            'file' => curl_file_create($this->file),
            'model' => 'whisper-1',
            'language' => $this->lang ?? 'es',
            'response_format' => 'json',
            'temperature' => 0.0
        ];
    }
    // ...
}

Ejercicio 2: Soporte para múltiples idiomas

Permite seleccionar el idioma de traducción:

php
public function translate($content, $targetLang, $sourceLang = 'auto') {
    $this->dataType = "TRANSLATE";
    $this->content = $content;
    $this->lang = $targetLang;
    $this->sourceLang = $sourceLang;
    // ...
}

Ejercicio 3: Validar formato de archivo

Agrega validación del archivo antes de enviarlo:

php
public function validateFile() {
    // Verificar extensión
    $ext = pathinfo($this->file, PATHINFO_EXTENSION);
    $allowed = ['mp3', 'mp4', 'wav', 'm4a'];
    if (!in_array($ext, $allowed)) {
        $this->error = "Extensión de archivo no permitida";
        return false;
    }
    
    // Verificar tamaño
    $size = filesize($this->file);
    if ($size > 25000000) {
        $this->error = "El archivo es demasiado grande";
        return false;
    }
    
    return true;
}

¡Excelente trabajo! Ahora tenemos todos los datos preparados para enviar a la API. En la próxima lección, implementaremos la comunicación real con cURL.

Comentarios

Entradas más populares de este blog

Cómo usar Whisper para sacar el texto de un video

Tutorial 18: Creando la Página de Visualización de Archivos Recientes

Tutorial 11: ¿Qué es Whisper AI y Cómo Funciona?