Tutorial 14: Creando el Método para los Headers de la API

 


¡Hola y bienvenido de nuevo!

En esta lección vamos a crear un método que nos permita configurar los headers necesarios para comunicarnos con la API de OpenAI. Los headers son fundamentales porque incluyen la autenticación y el tipo de contenido que estamos enviando. ¡Vamos a ello!


📋 Contenido del Tutorial

  1. ¿Qué son los headers HTTP?

  2. Headers necesarios para la API de OpenAI

  3. Creando el método getHeader

  4. Diferencias entre ASR y Traducción

  5. Código completo

  6. Explicación detallada

  7. Próximos pasos


📨 1. ¿Qué son los headers HTTP?

Definición

Los headers HTTP son metadatos que se envían junto con una solicitud o respuesta HTTP. Contienen información adicional sobre la petición, como autenticación, tipo de contenido, formato de respuesta, etc.

Estructura de un header

text
Nombre: Valor

Ejemplos:

text
Authorization: Bearer sk-proj-ABC123
Content-Type: application/json
Accept: application/json

¿Para qué sirven?

HeaderPropósitoEjemplo
AuthorizationAutenticaciónBearer sk-proj-ABC123
Content-TypeTipo de contenidoapplication/json
AcceptFormato de respuestaapplication/json
User-AgentIdentifica el clienteMyApp/1.0

Visualización de headers

Cuando envías una solicitud, los headers se ven así:

text
POST /v1/audio/transcriptions HTTP/1.1
Host: api.openai.com
Authorization: Bearer sk-proj-ABC123
Content-Type: multipart/form-data
Content-Length: 12345

[datos del archivo]

🔑 2. Headers necesarios para la API de OpenAI

Headers obligatorios

HeaderValor¿Por qué?
AuthorizationBearer TU_API_KEYAutentica la solicitud
Content-TypeVariable según el endpointIndica el formato de los datos

Headers para ASR (Transcripción)

php
[
    'Authorization: Bearer ' . API_TOKEN,
    'Content-Type: multipart/form-data'
]

¿Por qué multipart/form-data?

  • Porque estamos enviando un archivo

  • Es el formato estándar para subir archivos

Headers para Traducción (Chat Completions)

php
[
    'Authorization: Bearer ' . API_TOKEN,
    'Content-Type: application/json'
]

¿Por qué application/json?

  • Porque enviamos datos en formato JSON

  • Es el formato estándar para APIs REST


🏗️ 3. Creando el método getHeader

Estructura básica

php
public function getHeader() {
    if ($this->dataType === "ASR") {
        return [
            'Authorization: Bearer ' . API_TOKEN,
            'Content-Type: multipart/form-data'
        ];
    } else {
        return [
            'Authorization: Bearer ' . API_TOKEN,
            'Content-Type: application/json'
        ];
    }
}

¿Qué hace este método?

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

  2. Si es ASR: Retorna headers para subida de archivos

  3. Si es otro: Retorna headers para solicitudes JSON

Componentes del header

php
// 1. Autenticación - SIEMPRE necesaria
'Authorization: Bearer ' . API_TOKEN

// 2. Tipo de contenido - Depende de la operación
'Content-Type: multipart/form-data'  // Para ASR
'Content-Type: application/json'     // Para otros

🔄 4. Diferencias entre ASR y Traducción

Comparativa de headers

AspectoASR (Transcripción)Traducción (Chat)
Content-Typemultipart/form-dataapplication/json
Formato de datosArchivo binarioJSON
Método HTTPPOSTPOST
Endpoint/audio/transcriptions/chat/completions

Ejemplo de solicitud ASR

http
POST /v1/audio/transcriptions HTTP/1.1
Host: api.openai.com
Authorization: Bearer sk-proj-ABC123
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary

------WebKitFormBoundary
Content-Disposition: form-data; name="file"; filename="audio.mp3"
Content-Type: audio/mpeg

[datos binarios del archivo]
------WebKitFormBoundary
Content-Disposition: form-data; name="model"

whisper-1
------WebKitFormBoundary--

Ejemplo de solicitud Traducción

http
POST /v1/chat/completions HTTP/1.1
Host: api.openai.com
Authorization: Bearer sk-proj-ABC123
Content-Type: application/json

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

💻 5. 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 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 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'
            ];
        }
    }
    
    /**
     * 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
     * 
     * @param string $filePath Ruta del archivo
     * @return array|false Datos de la solicitud o false en caso de error
     */
    public function prepareRequest($filePath) {
        // 1. Verificar que el archivo existe
        if (!file_exists($filePath)) {
            $this->error = "El archivo no existe";
            return false;
        }
        
        // 2. Obtener URL y headers
        $url = $this->getApiUrl();
        $headers = $this->getHeader();
        
        if (!$headers) {
            return false;
        }
        
        // 3. Preparar los datos según el tipo
        if ($this->dataType === "ASR") {
            // Para ASR: usar el archivo directamente
            $postFields = [
                'file' => curl_file_create($filePath),
                'model' => 'whisper-1',
                'language' => 'es',
                'response_format' => 'json'
            ];
        } else {
            // Para traducción: leer el archivo y preparar JSON
            $content = file_get_contents($filePath);
            $postFields = json_encode([
                'model' => 'gpt-3.5-turbo',
                'messages' => [
                    [
                        'role' => 'system',
                        'content' => 'Eres un traductor profesional. Traduce el siguiente texto al español de forma precisa y natural.'
                    ],
                    [
                        'role' => 'user',
                        'content' => $content
                    ]
                ],
                'temperature' => 0.7
            ]);
        }
        
        // 4. Retornar todos los datos de la solicitud
        return [
            'url' => $url,
            'headers' => $headers,
            'postFields' => $postFields,
            '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); // 2 minutos máximo
        
        // Configurar headers
        curl_setopt($ch, CURLOPT_HTTPHEADER, $requestData['headers']);
        
        // Configurar datos POST
        curl_setopt($ch, CURLOPT_POSTFIELDS, $requestData['postFields']);
        
        // 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";
        
        // Preparar la solicitud
        $request = $this->prepareRequest($filePath);
        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;
        }
    }
}
?>

Archivo: backend/init.php (actualizado)

php
<?php
/**
 * Archivo de inicialización de la aplicación
 */

// =============================================
// 1. CONFIGURACIÓN DE LA API KEY
// =============================================

// Cargar variables de entorno desde .env
function loadEnv($path = __DIR__ . '/../.env') {
    if (!file_exists($path)) {
        return false;
    }
    
    $lines = file($path, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
    foreach ($lines as $line) {
        if (strpos($line, '#') === 0 || strpos($line, '=') === false) {
            continue;
        }
        
        list($key, $value) = explode('=', $line, 2);
        $key = trim($key);
        $value = trim($value, '"\'');
        putenv("$key=$value");
        $_ENV[$key] = $value;
    }
    return true;
}

// Cargar .env
loadEnv();

// Definir constantes
define('API_TOKEN', getenv('OPENAI_API_KEY') ?: '');
define('APP_NAME', 'My Whisper AI');
define('MAX_FILE_SIZE', 20 * 1024 * 1024);

// =============================================
// 2. VERIFICACIONES
// =============================================

// Verificar API Key
if (API_TOKEN === '') {
    die('⚠️ Error: La API Key de OpenAI no está configurada en .env');
}

// =============================================
// 3. CARGAR CLASES
// =============================================

require 'classes/DB.php';
require 'classes/Whisper.php';

// =============================================
// 4. CREAR INSTANCIAS
// =============================================

$whisperObj = new Whisper();

// Configurar el tipo de operación (por defecto: ASR)
$whisperObj->dataType = "ASR";
?>

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";
                
                // 3. Obtener la URL
                $url = $whisperObj->getApiUrl();
                
                // 4. Obtener los headers
                $headers = $whisperObj->getHeader();
                
                // 5. Mostrar información para debug
                echo "<h3>Información de la solicitud:</h3>";
                echo "<pre>";
                echo "URL: " . $url . "\n";
                echo "Headers:\n";
                print_r($headers);
                echo "</pre>";
                
                // 6. Aquí llamaremos a la API en la próxima lección
                
            } else {
                $error = $whisperObj->errors();
            }
        } else {
            $error = "Por favor selecciona un archivo";
        }
    }
?>

📖 6. Explicación detallada

El método getHeader paso a paso

php
public function getHeader() {
    // 1. Verificar que la constante existe
    if (!defined('API_TOKEN')) {
        $this->error = "API_TOKEN no está definido";
        return false;
    }
    
    // 2. Decidir según el tipo de operación
    if ($this->dataType === "ASR") {
        // 3. Headers para subida de archivos
        return [
            'Authorization: Bearer ' . API_TOKEN,
            'Content-Type: multipart/form-data'
        ];
    } else {
        // 4. Headers para JSON
        return [
            'Authorization: Bearer ' . API_TOKEN,
            'Content-Type: application/json'
        ];
    }
}

¿Qué hace cada header?

Authorization

text
Authorization: Bearer sk-proj-ABC123
  • Propósito: Autenticar la solicitud

  • Formato: Bearer + espacio + API_KEY

  • Obligatorio: Siempre

Content-Type (ASR)

text
Content-Type: multipart/form-data
  • Propósito: Enviar archivos binarios

  • Cuándo: Al subir archivos a la API

  • Formato: Multipart con boundary

Content-Type (Traducción)

text
Content-Type: application/json
  • Propósito: Enviar datos estructurados

  • Cuándo: Al enviar solicitudes JSON

  • Formato: JSON válido


🧪 7. Probando la funcionalidad

Prueba 1: Headers para ASR

php
$whisperObj->dataType = "ASR";
$headers = $whisperObj->getHeader();
print_r($headers);

Resultado esperado:

text
Array
(
    [0] => Authorization: Bearer sk-proj-ABC123
    [1] => Content-Type: multipart/form-data
)

Prueba 2: Headers para Traducción

php
$whisperObj->dataType = "TRANSLATE";
$headers = $whisperObj->getHeader();
print_r($headers);

Resultado esperado:

text
Array
(
    [0] => Authorization: Bearer sk-proj-ABC123
    [1] => Content-Type: application/json
)

Prueba 3: Verificar errores

php
// Si API_TOKEN no está definido
$headers = $whisperObj->getHeader();
if ($headers === false) {
    echo "Error: " . $whisperObj->errors();
}

📊 8. Resumen de la clase Whisper hasta ahora

Propiedades

PropiedadTipoDescripción
$errorstringMensajes de error
$dataTypestringTipo de operación (ASR/TRANSLATE)
$DBPDOConexión a la base de datos

Métodos

MétodoParámetrosRetornoDescripción
__construct()-voidConstructor, conecta a BD
errors()-stringDevuelve el error actual
getApiUrl()-stringDevuelve la URL de la API
getHeader()-array/falseDevuelve los headers HTTP
upload()$filestring/falseSube un archivo
prepareRequest()$filePatharray/falsePrepara la solicitud
sendRequest()$requestDatastring/falseEnvía solicitud a API
transcribe()$filePathstring/falseTranscribe un archivo

Diagrama de flujo con headers

text
┌─────────────────────────────────────────────────────┐
│              SOLICITUD A LA API                      │
├─────────────────────────────────────────────────────┤
│                                                     │
│  1. getApiUrl() → Endpoint correcto                │
│         ↓                                          │
│  2. getHeader() → Headers correctos                │
│         ↓                                          │
│  3. ¿ASR?                                          │
│     ├── Sí → multipart/form-data                   │
│     └── No → application/json                      │
│         ↓                                          │
│  4. curl_setopt() → Configurar headers             │
│         ↓                                          │
│  5. curl_exec() → Enviar solicitud                 │
│         ↓                                          │
│  6. Procesar respuesta                             │
│                                                     │
└─────────────────────────────────────────────────────┘

🎯 9. Próximos pasos

Lo que hemos logrado

✅ Método getHeader() implementado
✅ Headers para ASR configurados
✅ Headers para Traducción configurados
✅ Verificación de API_TOKEN
✅ Preparación para cURL

Lo que viene en la próxima lección

En la siguiente lección vamos a:

  1. Implementar cURL para enviar solicitudes HTTP

  2. Enviar archivos a la API de OpenAI

  3. Recibir respuestas de la API

  4. Procesar los resultados

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

php
// En Whisper.php - Método completo de envío
public function sendToAPI($filePath) {
    // 1. Obtener URL
    $url = $this->getApiUrl();
    
    // 2. Obtener headers
    $headers = $this->getHeader();
    
    // 3. Configurar cURL
    $ch = curl_init();
    curl_setopt($ch, CURLOPT_URL, $url);
    curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
    // ... más configuraciones
    
    // 4. Enviar solicitud
    $response = curl_exec($ch);
    
    // 5. Procesar respuesta
    return json_decode($response, true);
}

❓ Preguntas frecuentes

¿Qué es un header HTTP?

  • Es información adicional que se envía con una solicitud HTTP

  • Incluye autenticación, tipo de contenido, etc.

¿Por qué necesito Authorization?

  • Para que OpenAI sepa quién está haciendo la solicitud

  • Asocia el uso con tu cuenta

¿Qué significa multipart/form-data?

  • Es un formato para enviar archivos y datos binarios

  • Se usa cuando subimos archivos

¿Qué significa application/json?

  • Es un formato para enviar datos estructurados

  • Se usa cuando enviamos objetos JSON

¿Puedo usar otros headers?

  • Sí, puedes añadir Accept, User-Agent, etc.

  • Pero los dos mencionados son obligatorios

¿Qué pasa si el header es incorrecto?

  • La API responderá con un error

  • Generalmente un error HTTP 400 o 401


🎓 Ejercicio práctico

Ejercicio 1: Agregar más headers

Añade el header Accept para especificar el formato de respuesta:

php
public function getHeader() {
    $headers = [
        'Authorization: Bearer ' . API_TOKEN,
        'Accept: application/json'
    ];
    
    if ($this->dataType === "ASR") {
        $headers[] = 'Content-Type: multipart/form-data';
    } else {
        $headers[] = 'Content-Type: application/json';
    }
    
    return $headers;
}

Ejercicio 2: Validar headers

Agrega validación para asegurar que los headers sean correctos:

php
public function validateHeaders($headers) {
    $required = ['Authorization', 'Content-Type'];
    foreach ($required as $header) {
        $found = false;
        foreach ($headers as $h) {
            if (strpos($h, $header . ':') === 0) {
                $found = true;
                break;
            }
        }
        if (!$found) {
            return false;
        }
    }
    return true;
}

Ejercicio 3: Headers personalizados

Crea un método que permita añadir headers personalizados:

php
public $customHeaders = [];

public function addHeader($header) {
    $this->customHeaders[] = $header;
}

public function getHeader() {
    $headers = [
        'Authorization: Bearer ' . API_TOKEN
    ];
    
    // Añadir headers personalizados
    $headers = array_merge($headers, $this->customHeaders);
    
    // Añadir Content-Type según el tipo
    if ($this->dataType === "ASR") {
        $headers[] = 'Content-Type: multipart/form-data';
    } else {
        $headers[] = 'Content-Type: application/json';
    }
    
    return $headers;
}

¡Excelente trabajo! Ahora tenemos los headers correctos para comunicarnos con la API de OpenAI. 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?