Saltar al contenido principal

Entradas y eventos

Cómo se manejan las entradas y los eventos en las aplicaciones GenUI.

Esta guía explica cómo se manejan las interacciones del usuario dentro del paquete GenUI, desde la interacción inicial con el widget hasta que el agente de IA recibe el evento.

Resumen

#

En la arquitectura GenUI, la interfaz de usuario es impulsada por la IA, pero las interacciones del usuario (como hacer clic en un botón o enviar un formulario) se deben comunicar de vuelta al agente de IA. Esto permite que el agente actualice la interfaz de usuario o realice acciones en respuesta a la entrada del usuario.

El flujo de un evento es el siguiente:

  1. Interacción: El usuario interactúa con un widget; por ejemplo, el usuario toca un botón.
  2. Captura: La implementación del widget despacha un UiEvent.
  3. Procesamiento: El framework añade contexto (como un surfaceId o valores del modelo de datos) y reenvía el evento.
  4. Transmisión: El widget de Flutter genera el evento, añade el contexto adecuado y lo dirige a la IA a través del ContentGenerator, el cual lo reenvía al agente de IA.

Definir eventos

#

Nivel de protocolo

#

El protocolo A2UI define un mensaje de action que se utiliza para reportar eventos. Una action contiene:

  • name: El nombre de la acción (definido por la IA al generar el componente).
  • surfaceId: El ID de la superficie de la interfaz de usuario donde ocurrió el evento.
  • sourceComponentId: El ID del componente que desencadenó el evento.
  • context: Un objeto JSON que contiene datos relevantes para el evento.
  • timestamp: Cuándo ocurrió el evento.

Implementación en Dart

#

En package:genui, los eventos de usuario están representados por el tipo de extensión UiEvent y su implementación concreta UserActionEvent.

Las siguientes estructuras están definidas en lib/src/model/ui_models.dart:

lib/src/model/ui_models.dart
dart
/// A data object that represents a user interaction event in the UI.
extension type UiEvent.fromMap(JsonMap _json) { ... }

/// A UI event that represents a user action.
extension type UserActionEvent.fromMap(JsonMap _json) implements UiEvent {
  UserActionEvent({
    String? surfaceId,
    required String name,
    required String sourceComponentId,
    JsonMap? context,
    // ...
  }) : ...
}

Capturar eventos en widgets

#

Los widgets en GenUI se definen en un Catalog, el cual incluye información sobre qué eventos puede enviar el widget a la IA. La IA puede entonces enviar información sobre cómo comunicar de vuelta esos eventos. Cuando implementas un widget personalizado (o usas los widgets estándar), utilizas el método dispatchEvent en CatalogItemContext para despachar eventos.

Ejemplo: Implementación de Button

#

El siguiente ejemplo muestra cómo un widget Button captura típicamente un toque y despacha un evento. Recupera la definición de la acción (proporcionada por la IA) a partir de sus propiedades, resuelve cualquier vinculación de datos en el contexto y envía el evento.

dart
// Inside a CatalogItem widgetBuilder:
widgetBuilder: (itemContext) {
  // 1. Extract action data from the component properties.
  final buttonData = _ButtonData.fromMap(itemContext.data as JsonMap);
  final JsonMap actionData = buttonData.action;
  final actionName = actionData['name'] as String;

  // 2. Extract context definition (which data to send back).
  final List<Object?> contextDefinition =
      (actionData['context'] as List<Object?>?) ?? <Object?>[];

  return ElevatedButton(
    onPressed: () {
      // 3. Resolve the context values from the data model.
      final JsonMap resolvedContext = resolveContext(
        itemContext.dataContext,
        contextDefinition,
      );

      // 4. Dispatch the event.
      itemContext.dispatchEvent(
        UserActionEvent(
          name: actionName,
          sourceComponentId: itemContext.id,
          context: resolvedContext,
        ),
      );
    },
    child: /* ... */
  );
},

Pipeline de procesamiento de eventos

#

Una vez que se llama a dispatchEvent, el evento viaja a través de las capas principales de GenUI.

Surface

#

El widget Surface (en lib/src/core/surface.dart) envuelve los widgets renderizados. Proporciona la implementación de la función callback dispatchEvent.

Cuando se llama a _dispatchEvent:

  1. Inyecta automáticamente el surfaceId en el evento, garantizando que la IA sepa de qué superficie provino la interacción.
  2. Delega el manejo al SurfaceHost (implementado por SurfaceController).
dart
// Surface implementation details
void _dispatchEvent(UiEvent event) {
  // ...
  final Map<String, Object?> eventMap = {
    ...event.toMap(),
    surfaceIdKey: widget.surfaceId, // Inject surfaceId
  };
  final UiEvent newEvent = UserActionEvent.fromMap(eventMap);
  widget.host.handleUiEvent(newEvent);
}

SurfaceController

#

El SurfaceController (en lib/src/core/surface_controller.dart) es el centro neurálgico para gestionar el State de la interfaz de usuario.

Cuando se llama a handleUiEvent, hace lo siguiente:

  1. Verifica el tipo de evento.
  2. Envuelve el evento en el sobre JSON de action requerido por el protocolo.
  3. Emite un UserUiInteractionMessage en su flujo onSubmit.
dart
// SurfaceController implementation details
@override
void handleUiEvent(UiEvent event) {
  if (event is! UserActionEvent) return;

  // Wrap in protocol 'action' envelope
  final String eventJsonString = jsonEncode({'action': event.toMap()});

  // Emit for listeners (like Conversation)
  _onSubmit.add(UserUiInteractionMessage.text(eventJsonString));
}

Transmisión a la IA

#

El último paso envía el evento al Agente de IA. Esto es manejado típicamente por Conversation (en lib/src/facade/conversation.dart). La Conversation escucha el flujo onSubmit del procesador de mensajes.

dart
// Conversation constructor
_userEventSubscription = surfaceController.onSubmit.listen(sendRequest);

Cuando se recibe un evento, el método sendRequest:

  1. Envuelve el UserUiInteractionMessage de vuelta al código de cliente del desarrollador.
  2. La integración personalizada o el adaptador de transporte predefinido reenvía el mensaje al transporte de red del agente LLM.

El Agente de IA recibe este mensaje JSON, procesa la acción del usuario, y podría transmitir de vuelta nuevos mensajes surfaceUpdate o dataModelUpdate para modificar la interfaz de usuario, o alguna otra acción, completando el ciclo completo de interacción.