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
Entendiendo los datos necesarios para la API
Agregando nuevas propiedades
Creando el método getData
Datos para ASR (Transcripción)
Datos para Traducción
Código completo
Explicación detallada
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ón | Datos necesarios | Formato |
|---|---|---|
| ASR | Archivo + Modelo | multipart/form-data |
| Traducción | Modelo + Mensajes | application/json |
Documentación de referencia
Para ASR (Speech to Text):
https://api.openai.com/v1/audio/transcriptions
Datos requeridos:
{ "file": "archivo.mp3", "model": "whisper-1" }
Para Traducción (Chat Completions):
https://api.openai.com/v1/chat/completions
Datos requeridos:
{ "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
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?
| Propiedad | Tipo | Uso | Ejemplo |
|---|---|---|---|
$file | string | Ruta del archivo subido | files/video.mp4 |
$content | string | Texto a traducir | "Hello world" |
$lang | string | Idioma destino | "Spanish" |
Agregando las propiedades a la clase
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
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?
Verifica el tipo de operación (
dataType)Si es ASR: Prepara datos para transcripción
Si es otro: Prepara datos para traducción
Retorna los datos en el formato correcto
🎙️ 4. Datos para ASR (Transcripción)
Estructura de datos
if ($this->dataType === "ASR") { return [ 'file' => $this->file, 'model' => 'whisper-1' ]; }
Explicación de los campos
| Campo | Valor | Descripción |
|---|---|---|
file | $this->file | Ruta del archivo a transcribir |
model | whisper-1 | Modelo de Whisper a usar |
¿Cómo se usa?
// 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:
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
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
| Campo | Valor | Descripción |
|---|---|---|
model | gpt-3.5-turbo | Modelo de ChatGPT a usar |
messages | Array | Mensajes para el chat |
role: system | Instrucción | Define el comportamiento del asistente |
role: user | Contenido | Texto a traducir |
Estructura de mensajes
'messages' => [ [ 'role' => 'system', 'content' => 'Instrucción para el sistema' ], [ 'role' => 'user', 'content' => 'Texto del usuario' ] ]
¿Qué es cada role?
| Role | Propósito | Ejemplo |
|---|---|---|
| system | Define el comportamiento del asistente | "Eres un traductor profesional" |
| user | Mensaje del usuario | "Hello, how are you?" |
| assistant | Respuesta del asistente | (Lo genera la API) |
Ejemplo de traducción
// 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 /** * 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 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
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:
$fileData = curl_file_create('/ruta/al/archivo.mp3');
Equivalente a:
$fileData = new CURLFile('/ruta/al/archivo.mp3');
Estructura de mensajes para traducción
// Sistema: Define el comportamiento [ 'role' => 'system', 'content' => 'Traduce al Spanish' ] // Usuario: El texto a traducir [ 'role' => 'user', 'content' => 'Hello world' ]
¿Por qué JSON encode?
return json_encode([ 'model' => 'gpt-3.5-turbo', 'messages' => $messages ]);
La API espera datos en formato JSON
json_encode()convierte el array a JSONEjemplo:
{"model":"gpt-3.5-turbo","messages":[...]}
🧪 8. Probando la funcionalidad
Prueba 1: Datos para ASR
$whisperObj->dataType = "ASR"; $whisperObj->file = "files/video.mp4"; $data = $whisperObj->getData(); print_r($data);
Resultado esperado:
Array
(
[file] => CURLFile Object
(
[name] => files/video.mp4
[mime] =>
[postname] =>
)
[model] => whisper-1
)Prueba 2: Datos para Traducción
$whisperObj->dataType = "TRANSLATE"; $whisperObj->content = "Hello world"; $whisperObj->lang = "Spanish"; $data = $whisperObj->getData(); echo $data;
Resultado esperado:
{ "model": "gpt-3.5-turbo", "messages": [ { "role": "system", "content": "Traduce al Spanish" }, { "role": "user", "content": "Hello world" } ] }
Prueba 3: Verificar errores
// 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
| Propiedad | Tipo | Descripción |
|---|---|---|
$error | string | Mensajes de error |
$dataType | string | Tipo de operación (ASR/TRANSLATE) |
$file | string | Ruta del archivo |
$content | string | Texto para traducir |
$lang | string | Idioma destino |
$DB | PDO | Conexión a la base de datos |
Métodos
| Método | Parámetros | Retorno | Descripción |
|---|---|---|---|
__construct() | - | void | Constructor, conecta a BD |
errors() | - | string | Devuelve el error actual |
getApiUrl() | - | string | URL de la API |
getHeader() | - | array/false | Headers HTTP |
getData() | - | array/string/false | Datos para la API |
upload() | $file | string/false | Sube un archivo |
prepareRequest() | - | array/false | Prepara la solicitud |
sendRequest() | $requestData | string/false | Envía solicitud |
transcribe() | $filePath | string/false | Transcribe archivo |
translate() | $content, $lang | string/false | Traduce texto |
Diagrama de flujo completo
┌─────────────────────────────────────────────────────────┐ │ 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:
Implementar cURL para enviar las solicitudes
Enviar archivos a la API de OpenAI
Recibir respuestas de la API
Procesar los resultados y mostrarlos
Avance del código de la próxima lección
// 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:
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:
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:
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
Publicar un comentario