Ir al contenido

React a Agente AG-UI

Nx Plugin for AWS proporciona un generador para conectar un sitio web React a un Agente que expone el protocolo AG-UI. Conecta CopilotKit con un HttpAgent de @ag-ui/client en tu sitio web, con soporte para autenticación AWS IAM y Cognito.

Antes de usar este generador, asegúrate de tener:

  1. Un sitio web React (generado usando el generador ts#website)
  2. Un Agente TypeScript o Python con protocol=ag-ui (generado usando el generador ts#agent o py#agent)
  3. Para agentes desplegados, autenticación Cognito agregada a través del generador ts#website#auth

Ejecute este generador@aws/nx-plugin:connection

pnpm nx g @aws/nx-plugin:connection
Construya su comando5

Requerido

Requerido

Se te pedirá que selecciones tu sitio web React como el proyecto de origen y el proyecto que contiene tu Agente AG-UI como el proyecto de destino. Si tu proyecto de destino contiene múltiples componentes (como múltiples agentes u otros tipos de componentes), se te pedirá que especifiques un targetComponent para desambiguar.

targetComponent acepta el --name que le diste al generador de agente en cualquier formato de mayúsculas/minúsculas (StoryAgent, story-agent), la ruta del componente relativa a la raíz del proyecto de destino, o su id de generador.

Opciones del generador5 opciones
sourceProjectRequeridostring

El proyecto de origen

targetProjectRequeridostring

El proyecto de destino al que conectar

sourceComponentstring

El componente de origen desde el cual conectar (nombre del componente, ruta relativa a la raíz del proyecto de origen, o id del generador). Usa '.' para seleccionar explícitamente el proyecto como origen.

targetComponentstring

El componente de destino al cual conectar (nombre del componente, ruta relativa a la raíz del proyecto de destino, o id del generador). Usa '.' para seleccionar explícitamente el proyecto como destino.

preferInstallDependenciesbooleanPredeterminado: true

Si se prefiere instalar las dependencias después de que se ejecute el generador. Establecer en false para diferir la instalación al ejecutar múltiples generadores en lote (la instalación aún se ejecuta si es necesario para que los generadores subsiguientes puedan calcular el grafo de proyectos de Nx); instalar una vez al final.

El generador crea un componente AguiProvider compartido único, un hook por agente conectado, y un wrapper temático para los componentes de chat de CopilotKit:

  • Directoriosrc
    • Directoriocomponents
      • AguiProvider.tsx CopilotKitProvider único para cada agente AG-UI. Creado en la primera ejecución de connection y actualizado en ejecuciones posteriores para registrar cada nuevo agente.
      • <agent-name>-chat.tsx Un <AgentName>Chat temático vinculado al id de este agente. Un archivo por ejecución de connection.
      • Directoriocopilot
        • index.tsx Re-exporta CopilotChat, CopilotSidebar y CopilotPopup con valores predeterminados de slot que coinciden con el ux de tu sitio web (Cloudscape, Shadcn, o sin tema).
        • ThemeComponents .tsx Componentes de tema por slot (ej. CloudscapeAssistantMessage.tsx, ShadcnChatInput.tsx). Solo se proporcionan cuando ux es cloudscape o shadcn.
    • Directoriohooks
      • useAgui<AgentName>.tsx Registra un agente AG-UI y exporta su id como <AGENT_NAME>_ID. Un archivo por ejecución de connection.
      • useSigV4.tsx Firma SigV4 (solo IAM)

Ejecutar connection una segunda vez para un agente diferente agrega un nuevo hook useAgui<AgentName>.tsx y componente <agent-name>-chat.tsx y actualiza AguiProvider.tsx para registrar ambos hooks — cualquier edición personalizada que hayas hecho al proveedor se preserva. main.tsx mantiene su único wrapper <AguiProvider> — nunca terminas con proveedores anidados.

Las siguientes dependencias se agregan al package.json raíz:

  • @copilotkit/react-core — incluye CopilotKitProvider y componentes de chat (CopilotChat, CopilotSidebar, CopilotPopup)
  • @ag-ui/clientHttpAgent usado por los hooks generados
  • aws4fetch, oidc-client-ts, react-oidc-context, @aws-sdk/credential-providers — solo autenticación IAM
  • react-oidc-context — autenticación Cognito

Cada hook useAgui<AgentName> lee el valor de runtime de su agente desde la Configuración de Runtime e instancia un HttpAgent de @ag-ui/client:

  • Desplegado: el valor de runtime es un ARN de Bedrock AgentCore Runtime, que se convierte al endpoint HTTPS de AgentCore: https://bedrock-agentcore.<region>.amazonaws.com/runtimes/<encoded-arn>/invocations?qualifier=DEFAULT
  • Desarrollo local: dev sobrescribe el valor a la URL local del agente (ej. http://localhost:8081)

El AguiProvider compartido llama a cada hook generado y distribuye cada uno en selfManagedAgents en un único CopilotKitProvider, que los expone todos a los componentes de CopilotKit.

CopilotKit es el cliente React de referencia de primera parte para el protocolo AG-UI e incluye componentes de chat listos para usar:

  • <CopilotChat /> — interfaz de chat completa
  • <CopilotSidebar /> — chat en panel lateral fijo
  • <CopilotPopup /> — popup de chat flotante

Coloca cualquiera de estos en cualquier lugar dentro del wrapper <AguiProvider> (ya conectado en main.tsx para ti).

El código generado maneja la autenticación dependiendo de la configuración de tu agente:

  • IAM (predeterminado): usa solicitudes HTTP firmadas con AWS SigV4. Las credenciales se obtienen del Identity Pool de Cognito configurado con la autenticación de tu sitio web.
  • Cognito: incrusta el token de acceso JWT en el encabezado Authorization como un token Bearer.

AG-UI y AgentCore Runtime identifican una conversación de manera diferente, y el hook generado los vincula:

  • threadId — el identificador de conversación de AG-UI, enviado en el cuerpo de la solicitud. CopilotKit genera un UUID aleatorio por chat a menos que pases un threadId explícito.
  • Session ID — la sesión de AgentCore Runtime, enviada en el encabezado X-Amzn-Bedrock-AgentCore-Runtime-Session-Id. Selecciona la microVM que atiende la solicitud, y es en lo que el session.ts / session.py de tu agente basa el estado de la conversación.

El hook deriva el ID de sesión del ID de hilo, rellenándolo a la derecha hasta los 33 caracteres que requiere AgentCore Runtime:

function agentCoreSessionId(input: RunAgentInput): string {
return (input.threadId ?? '').padEnd(33, '0');
}

Dejar threadId sin establecer es lo más simple — el UUID generado por CopilotKit ya tiene 36 caracteres. Si pasas uno explícitamente, hazlo de al menos 33 caracteres, ya que el relleno mapea IDs de hilo que difieren solo en caracteres finales a la misma sesión.

Tanto el Session ID como el Thread ID son proporcionados por el navegador. Para restringir a cada usuario a sus propias conversaciones, consulta la guía py#agent o ts#agent.

Si tu agente utiliza autenticación IAM, el rol autenticado del Cognito Identity Pool debe tener permiso para invocar el agente.

packages/infra/src/stacks/application-stack.ts
const identity = new UserIdentity(this, 'Identity');
const myAgent = new MyAgent(this, 'MyAgent');
// Grant the authenticated Cognito role permission to invoke the agent
myAgent.grantInvokeAccess(identity.identityPool.authenticatedRole);

grantInvokeAccess conecta todas las acciones de invocación de AgentCore (InvokeAgentRuntime, InvokeAgentRuntimeWithWebSocketStream) en el ARN de runtime del agente.

Si tu agente utiliza autenticación Cognito, no necesitas definir ninguna infraestructura adicional para conectar tu sitio web a tu agente.

El generador proporciona un componente <AgentName>Chat por agente conectado, ya vinculado al id de ese agente y temático para coincidir con el ux de tu sitio web. Colócalo en cualquier lugar dentro del wrapper <AguiProvider>:

import { StoryAgentChat } from './components/story-agent-chat';
function ChatPage() {
return (
<StoryAgentChat
labels={{
welcomeMessageText: 'How can I help you today?',
chatInputPlaceholder: 'Ask me anything...',
}}
/>
);
}

Reenvía cada prop de CopilotChat excepto agentId, por lo que cualquier cosa que puedas pasar a <CopilotChat /> funciona aquí también.

Ejecuta el generador connection una vez por agente. Cada ejecución proporciona el componente de chat propio de ese agente, por lo que enrutar un chat a un agente en particular es cuestión de qué componente renderizas:

import { StoryAgentChat } from './components/story-agent-chat';
import { ResearchAgentChat } from './components/research-agent-chat';
<StoryAgentChat /> {/* talks to StoryAgent */}
<ResearchAgentChat /> {/* talks to ResearchAgent */}

Si necesitas el id sin procesar — para llamar a los propios hooks de CopilotKit, digamos — cada hook generado lo exporta:

import { STORY_AGENT_ID } from './hooks/useAguiStoryAgent';

<CopilotChat /> (y <CopilotSidebar />, <CopilotPopup />) usan un sistema de slots recursivo — puedes sobrescribir cualquier subcomponente con una cadena de clase Tailwind, un objeto de props, o un componente React personalizado. Consulta la guía de slots de CopilotKit para el árbol completo de slots.

El generador lee metadata.ux de tu proyecto de sitio web React y proporciona un módulo wrapper temático en src/components/copilot/index.tsx para que los componentes de chat coincidan con el resto de tu UI sin ninguna configuración adicional:

uxEstilo aplicado a CopilotChat / CopilotSidebar / CopilotPopup
cloudscapeLos mensajes se renderizan dentro de ChatBubbles de Cloudscape con Avatars de gen-AI (coincidiendo con el patrón de chat de IA generativa de Cloudscape); el indicador de escritura se convierte en un LoadingBar y la entrada es un PromptInput. Construido desde @cloudscape-design/components y @cloudscape-design/chat-components.
shadcnLos mensajes del asistente se renderizan en una burbuja bg-muted con un avatar Sparkles; los mensajes del usuario se renderizan alineados a la derecha en una burbuja bg-primary con un avatar User. La entrada es un Textarea redondeado + Button de envío/detención en forma de píldora (Enter envía, Shift+Enter nueva línea). Usa primitivas shadcn del paquete compartido common-shadcn.
none (o cualquier otra cosa)Sin tema — el módulo solo re-exporta los componentes predeterminados de CopilotKit.

Los componentes <AgentName>Chat proporcionados ya están temáticos. Para un chat que configures tú mismo, importa desde el módulo de tema local (no directamente desde @copilotkit/react-core/v2) para que el tema se aplique automáticamente:

import { CopilotChat } from './components/copilot';
import { STORY_AGENT_ID } from './hooks/useAguiStoryAgent';
<CopilotChat agentId={STORY_AGENT_ID} />

El tema se aplica como valores predeterminados de slot, por lo que cualquier slot que pases explícitamente aún gana — mantienes el control total cuando necesitas una sobrescritura puntual.

El tema generado vive completamente dentro de tu proyecto:

  • src/components/copilot/index.tsx — exporta los CopilotChat / CopilotSidebar / CopilotPopup temáticos y los objetos cloudscapeCopilotTheme / shadcnCopilotTheme. Edita este archivo para cambiar la conexión de slot predeterminada para cada chat en tu aplicación.
  • src/components/copilot/<ThemeComponent>.tsx — componentes de tema por slot (ej. CloudscapeAssistantMessage, ShadcnChatInput). Edita estos para ajustar la apariencia de un solo slot sin reconfigurar el tema.

Por ejemplo, para insertar tu propio renderizador de mensajes de usuario mientras mantienes el resto del tema, edita el archivo relevante en src/components/copilot/ y re-expórtalo desde index.tsx.

Las sobrescrituras por chat aún funcionan junto con el tema — cualquier cosa que pases como prop de slot sobrescribe el valor predeterminado temático:

<StoryAgentChat
// style the input and its children
input={{
textArea: 'text-blue-600',
sendButton: 'bg-blue-600 hover:bg-blue-700',
}}
// style nested message slots
messageView={{
assistantMessage: 'bg-blue-50 rounded-xl p-2',
userMessage: 'bg-blue-100 rounded-xl',
}}
/>

Reemplazar un slot con un componente personalizado

Sección titulada «Reemplazar un slot con un componente personalizado»

Cualquier slot puede tomar un componente React en lugar de un className, por lo que puedes reemplazar completamente el predeterminado. Tipifica tu componente contra las props que el slot declara — sendButton renderiza un <button>, por lo que recibe ButtonHTMLAttributes:

import { StoryAgentChat } from './components/story-agent-chat';
const MySendButton: React.FC<React.ButtonHTMLAttributes<HTMLButtonElement>> = ({
onClick,
}) => (
<button onClick={onClick} className="my-send-btn">
Send
</button>
);
<StoryAgentChat input={{ sendButton: MySendButton }} />;

Las sobrescrituras más profundas siguen la misma forma — ej. reemplaza solo el botón de copiar en los mensajes del asistente:

<StoryAgentChat
messageView={{
assistantMessage: {
copyButton: ({ onClick }) => <button onClick={onClick}>Copy</button>,
},
}}
/>

El generador de conexión configura automáticamente la integración con dev:

  1. Ejecutar nx dev <website> también iniciará el servidor local del agente
  2. La configuración de runtime se sobrescribe para apuntar a la URL AG-UI local (ej. http://localhost:8081)
  3. Tanto el sitio web como el agente se recargan en caliente juntos
Terminal window
pnpm nx dev <WebsiteProject>