Guía para integrar ChatGPT en una aplicación Flutter


Avatar de Pedro Cortez

¿Quieres añadir ChatGPT a tu aplicación Flutter para hacerla más dinámica? En esta guía, te explico cómo implementarlo de manera muy rápida.


Integrar ChatGPT en una aplicación Flutter

A continuación, se presentan los pasos a seguir para integrar ChatGPT en una de tus aplicaciones Flutter.

Crear una clave API

El primer paso consiste en acceder a la API de ChatGPT a través de la plataforma de OpenAI para crear una clave API que permitirá realizar solicitudes. Para ello:

  1. Dirígete al sitio oficial de OpenAI e inscríbete si aún no tienes una cuenta.
  2. Una vez que hayas iniciado sesión, ve a la sección «API keys» y crea una nueva clave. Puedes modificar sus permisos según tus necesidades, pero lo más sencillo para realizar pruebas es otorgarle todos los permisos inicialmente.
  3. Por último, asegúrate de agregar un método de pago válido a tu cuenta, ya que sin esto la API no funcionará. Para ello, haz clic en «Your Profile» en la parte superior derecha y selecciona «Billing» en el menú de la izquierda.
  4. Añade un método de pago y algo de crédito para que la API funcione.
  5. También considera establecer límites de uso en la sección «Limits» para evitar sorpresas desagradables.

Nunca compartas tu clave API públicamente, ya que otorga acceso a tu cuenta de OpenAI. En otro artículo, te explico cómo proteger tus claves API almacenándolas en un backend de Firebase.

Tarifas de ChatGPT

OpenAI ofrece una variedad de modelos y funcionalidades, cada uno con capacidades específicas y tarifas basadas en el número de tokens (aproximadamente 750 palabras por cada 1,000 tokens) o en unidades específicas (imágenes, minutos, etc.). A continuación, se presentan los precios actuales para los modelos y herramientas más utilizados en aplicaciones Flutter, que generalmente se indican por millón de tokens.

Modelo/FuncionalidadContextoEntrada TextoSalida TextoBatch API (Reducción 50%)
gpt-4o (2024-08-06)128,0002.50 $10.00 $1.25 $ (entrada), 5.00 $ (salida)
gpt-4o-mini (2024-07-18)128,0000.15 $0.60 $0.075 $ (entrada), 0.30 $ (salida)
gpt-3.5-turbo (0125)16,3840.50 $1.50 $0.25 $ (entrada), 0.75 $ (salida)
Whisper (Transcripción)0.006 $ por minuto
DALL·E 3 (Generación de imágenes)0.04 $ por imagen (1024×1024, estándar)

Explicación de los modelos y sus usos:

  • gpt-4o: El modelo más conocido de OpenAI, capaz de procesar texto e imágenes como entrada, con una salida textual. Puedes usarlo para tareas complejas como el análisis de documentos o la generación de contenido detallado. Por ejemplo: «Resume un informe de 10,000 palabras» o «Describe esta imagen».
  • gpt-4o-mini: Una versión económica de gpt-4o, que ofrece un rendimiento bastante similar, pero a un costo mucho menor. Es más adecuado para aplicaciones Flutter que requieren respuestas cortas, como «¿Cuál es la capital de Francia?» o «Genera un párrafo corto sobre este tema».
  • gpt-3.5-turbo: Un modelo aún más rápido y asequible que gpt-4o-mini, optimizado para diálogos y tareas textuales muy simples. Su ventana de contexto está limitada a 16,384 tokens, por lo que es más adecuado para aplicaciones de chat o respuestas cortas. Por ejemplo: «Explica un concepto en 100 palabras».
  • Whisper: Herramienta de transcripción de audio a texto, que permite convertir grabaciones de voz en texto. Puedes usarla, por ejemplo, para transcribir una entrevista.
  • DALL·E 3: Generador de imágenes a partir de descripciones textuales, ideal para aplicaciones que requieren contenido visual (marketing, diseño). Por ejemplo: «Crea una imagen de un paisaje rural».

Configurar los permisos en Android e iOS

Para que tu aplicación Flutter pueda comunicarse con la API de ChatGPT, debes configurar los permisos de red en Android e iOS:

Para Android

Modifica el archivo AndroidManifest.xml ubicado en android/app/src/main/ para incluir el siguiente permiso:

<manifest xmlns:android="http://schemas.android.com/apk/res/android">
...
  <uses-permission android:name="android.permission.INTERNET" /> <!-- Agregar esta línea -->
</manifest>

Esta línea permite que tu aplicación Android realice solicitudes HTTP a la API.

Para iOS

Abre el archivo Info.plist en ios/Runner/ y agrega esta clave para autorizar las conexiones de red:

<key>NSAppTransportSecurity</key>
<dict>
    <key>NSAllowsArbitraryLoads</key>
    <true/>
</dict>

Implementar ChatGPT en una aplicación

A continuación, se muestra cómo integrar ChatGPT en una aplicación Flutter con un ejemplo de código funcional. Utilizaremos el paquete http para realizar solicitudes a la API y dart:convert para procesar las respuestas de ChatGPT.

En este ejemplo, utilizo flutter_riverpod para gestionar el estado de la aplicación, pero puedes usar setState u otro administrador de estado como Provider.

import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:http/http.dart' as http;

void main() {
  runApp(ProviderScope(child: MyApp()));
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: ChatGPTScreen(),
    );
  }
}

final _controller = StateProvider<TextEditingController>((ref) => TextEditingController());
final _isLoading = StateProvider<bool>((ref) => false);
final _response = StateProvider<String>((ref) => '');

class ChatGPTScreen extends ConsumerWidget {
  final ChatGPTService _chatGPTService = ChatGPTService();

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final _currentController = ref.watch(_controller);
    final _currentIsLoading = ref.watch(_isLoading);
    final _currentResponse = ref.watch(_response);

    void _sendRequest() async {
      ref.read(_isLoading.notifier).state = true;

      try {
        final response = await _chatGPTService.sendPrompt(_currentController.text);
        ref.read(_response.notifier).state = response;
        ref.read(_isLoading.notifier).state = false;
      } catch (e) {
        ref.read(_isLoading.notifier).state = false;
        ref.read(_response.notifier).state = 'Error: $e';
        ref.read(_controller.notifier).state.clear();
      }
    }

    return Scaffold(
      appBar: AppBar(title: Text('ChatGPT Chat')),
      body: Padding(
        padding: const EdgeInsets.all(16.0),
        child: Column(
          children: [
            TextField(
              controller: _currentController,
              decoration: InputDecoration(labelText: 'Ingresa tu mensaje'),
            ),
            SizedBox(height: 20),
            ElevatedButton(
              onPressed: _sendRequest,
              child: Text('Enviar'),
            ),
            SizedBox(height: 20),
            _currentIsLoading
                ? CircularProgressIndicator()
                : Text(
                    _currentResponse.isEmpty
                        ? 'Sin respuesta'
                        : _currentResponse,
                    style: TextStyle(fontSize: 16),
                  ),
          ],
        ),
      ),
    );
  }
}

class ChatGPTService {
  static const String _baseUrl = 'https://api.openai.com/v1';
  static const String _apiKey = 'TU_CLAVE_API'; // Reemplaza con tu clave API

  Future<String> sendPrompt(String userPrompt) async {
    final response = await http.post(
      Uri.parse('$_baseUrl/chat/completions'),
      headers: {
        'Authorization': 'Bearer $_apiKey',
        'Content-Type': 'application/json',
      },
      body: jsonEncode({
        'messages': [
          {
            'role': 'system',
            'content': 'Eres un asistente útil.',
          },
          {
            'role': 'user',
            'content': userPrompt,
          },
        ],
        'model': 'gpt-3.5-turbo',
        'stream': false,
        'temperature': 0.7,
      }),
    );

    if (response.statusCode == 200) {
      final String responseBody = utf8.decode(response.bodyBytes);
      final data = jsonDecode(responseBody);
      final promptTokens = data['usage']['prompt_tokens'];
      final completionTokens = data['usage']['completion_tokens'];
      final totalTokens = data['usage']['total_tokens'];
      print(
          'Tokens utilizados: Entrada = $promptTokens, Salida = $completionTokens, Total = $totalTokens');
      return data['choices'][0]['message']['content'];
    } else {
      throw Exception('Error API: ${response.statusCode} - ${response.body}');
    }
  }
}

A continuación, se explican algunos detalles sobre cómo funciona todo esto:

Enviar una solicitud a ChatGPT

La clase ChatGPTService envía una solicitud POST a la API de ChatGPT (https://api.openai.com/v1/chat/completions) con los encabezados necesarios (clave API y tipo de contenido) y un cuerpo JSON que contiene el mensaje del usuario.

Future<String> sendPrompt(String userPrompt) async {
  final response = await http.post(
    Uri.parse('$_baseUrl/chat/completions'),
    headers: {
      'Authorization': 'Bearer $_apiKey',
      'Content-Type': 'application/json',
    },
    body: jsonEncode({
      'messages': [
        {
          'role': 'system',
          'content': 'Eres un asistente útil.',
        },
        {
          'role': 'user',
          'content': userPrompt,
        },
      ],
      'model': 'gpt-3.5-turbo',
      'stream': false,
      'temperature': 0.7,
    }),
  );

En el método _sendRequest(), el usuario ingresa un mensaje en el TextField. Cuando hace clic en «Enviar», la función llama a ChatGPTService.sendPrompt() con el texto ingresado.

Recuperar y mostrar la respuesta

Si la solicitud es exitosa (código 200), la respuesta de la API se decodifica y se extrae del campo choices[0].message.content. Esta respuesta se almacena en la variable _response y se muestra en un widget Text.

En caso de error, se muestra un mensaje de error y se vacía el campo de texto.

if (response.statusCode == 200) {
  final String responseBody = utf8.decode(response.bodyBytes);
  final data = jsonDecode(responseBody);
  final promptTokens = data['usage']['prompt_tokens'];
  final completionTokens = data['usage']['completion_tokens];
  final totalTokens = data['usage']['total_tokens'];
  print(
      'Tokens utilizados: Entrada = $promptTokens, Salida = $completionTokens, Total = $totalTokens');
  return data['choices'][0]['message']['content'];
} else {
  throw Exception('Error API: ${response.statusCode} - ${response.body}');
}

Recuperar el número de tokens consumidos

Para seguir el consumo de tokens en cada llamada a la API, puedes recuperar los siguientes valores:

  • data[‘usage’][‘prompt_tokens’]: El número de tokens utilizados para el mensaje enviado (entrada).
  • data[‘usage’][‘completion_tokens’]: El número de tokens generados en la respuesta de ChatGPT (salida).
  • data[‘usage’][‘total_tokens’]: La suma de ambos, que representa el consumo total.

Convertir el mensaje devuelto por ChatGPT

Cuando la API de ChatGPT responde, envía datos en forma de bytes. La línea final String responseBody = utf8.decode(response.bodyBytes); transforma estos bytes en texto legible. Luego, jsonDecode() extrae la información útil, como la respuesta de ChatGPT o el número de tokens utilizados.

Avatar de Pedro Cortez