Pular para o conteúdo

React para Agente AG-UI

Nx Plugin for AWS fornece um gerador para conectar um site React a um Agente que expõe o protocolo AG-UI. Ele configura o CopilotKit com um HttpAgent @ag-ui/client no seu site, com suporte para autenticação AWS IAM e Cognito.

Antes de usar este gerador, certifique-se de ter:

  1. Um site React (gerado usando o gerador ts#website)
  2. Um Agente TypeScript ou Python com protocol=ag-ui (gerado usando o gerador ts#agent ou py#agent)
  3. Para agentes implantados, Cognito Auth adicionado via gerador ts#website#auth

Execute este gerador@aws/nx-plugin:connection

pnpm nx g @aws/nx-plugin:connection
Monte seu comando5

Obrigatório

Obrigatório

Você será solicitado a selecionar seu site React como o projeto de origem e o projeto contendo seu Agente AG-UI como o projeto de destino. Se o seu projeto de destino contiver múltiplos componentes (como múltiplos agentes ou outros tipos de componentes), você será solicitado a especificar um targetComponent para desambiguar.

targetComponent aceita o --name que você deu ao gerador de agente em qualquer formato de capitalização (StoryAgent, story-agent), o caminho do componente relativo à raiz do projeto de destino, ou seu id de gerador.

Opções do gerador5 opções
sourceProjectObrigatóriostring

O projeto de origem

targetProjectObrigatóriostring

O projeto de destino para conectar

sourceComponentstring

O componente de origem a partir do qual conectar (nome do componente, caminho relativo à raiz do projeto de origem, ou id do gerador). Use '.' para selecionar explicitamente o projeto como origem.

targetComponentstring

O componente de destino ao qual conectar (nome do componente, caminho relativo à raiz do projeto de destino, ou id do gerador). Use '.' para selecionar explicitamente o projeto como destino.

preferInstallDependenciesbooleanPadrão: true

Se deve preferir instalar dependências após a execução do gerador. Defina como false para adiar a instalação ao executar múltiplos geradores em lote (uma instalação ainda é executada se necessário para que os geradores subsequentes possam calcular o grafo de projetos Nx); instale uma vez no final.

O gerador cria um componente AguiProvider único compartilhado, um hook por agente conectado e um wrapper temático para os componentes de chat do CopilotKit:

  • Directorysrc
    • Directorycomponents
      • AguiProvider.tsx CopilotKitProvider único para cada agente AG-UI. Criado na primeira execução de connection e atualizado em execuções subsequentes para registrar cada novo agente.
      • <agent-name>-chat.tsx Um <AgentName>Chat temático vinculado ao id deste agente. Um arquivo por execução de connection.
      • Directorycopilot
        • index.tsx Re-exporta CopilotChat, CopilotSidebar e CopilotPopup com padrões de slot que correspondem ao ux do seu site (Cloudscape, Shadcn, ou nenhum tema).
        • ThemeComponents .tsx Componentes de tema por slot (por exemplo, CloudscapeAssistantMessage.tsx, ShadcnChatInput.tsx). Fornecidos apenas quando ux é cloudscape ou shadcn.
    • Directoryhooks
      • useAgui<AgentName>.tsx Registra um agente AG-UI e exporta seu id como <AGENT_NAME>_ID. Um arquivo por execução de connection.
      • useSigV4.tsx Assinatura SigV4 (apenas IAM)

Executar connection uma segunda vez para um agente diferente adiciona um novo hook useAgui<AgentName>.tsx e componente <agent-name>-chat.tsx e atualiza AguiProvider.tsx para registrar ambos os hooks — quaisquer edições personalizadas que você tenha feito no provider são preservadas. main.tsx mantém seu único wrapper <AguiProvider> — você nunca acaba com providers aninhados.

As seguintes dependências são adicionadas ao package.json raiz:

  • @copilotkit/react-core — fornece CopilotKitProvider e componentes de chat (CopilotChat, CopilotSidebar, CopilotPopup)
  • @ag-ui/clientHttpAgent usado pelos hooks gerados
  • aws4fetch, oidc-client-ts, react-oidc-context, @aws-sdk/credential-providers — apenas autenticação IAM
  • react-oidc-context — autenticação Cognito

Cada hook useAgui<AgentName> lê o valor de runtime do seu agente da Configuração de Runtime e instancia um HttpAgent @ag-ui/client:

  • Implantado: o valor de runtime é um ARN do Bedrock AgentCore Runtime, que é convertido para o endpoint HTTPS do AgentCore: https://bedrock-agentcore.<region>.amazonaws.com/runtimes/<encoded-arn>/invocations?qualifier=DEFAULT
  • Desenvolvimento local: dev substitui o valor pela URL local do agente (por exemplo, http://localhost:8081)

O AguiProvider compartilhado chama cada hook gerado e espalha cada um em selfManagedAgents em um único CopilotKitProvider, que os expõe todos aos componentes CopilotKit.

CopilotKit é o cliente React de referência de primeira parte para o protocolo AG-UI e vem com componentes de chat prontos:

  • <CopilotChat /> — interface de chat completa
  • <CopilotSidebar /> — chat em painel lateral fixo
  • <CopilotPopup /> — popup de chat flutuante

Coloque qualquer um destes em qualquer lugar dentro do wrapper <AguiProvider> (já conectado em main.tsx para você).

O código gerado lida com autenticação dependendo da configuração do seu agente:

  • IAM (padrão): usa requisições HTTP assinadas com AWS SigV4. As credenciais são obtidas do Cognito Identity Pool configurado com a autenticação do seu site.
  • Cognito: incorpora o token de acesso JWT no cabeçalho Authorization como um Bearer token.

AG-UI e AgentCore Runtime identificam uma conversa de maneira diferente, e o hook gerado os conecta:

  • threadId — o identificador de conversa AG-UI, enviado no corpo da requisição. CopilotKit gera um UUID aleatório por chat, a menos que você passe um threadId explícito.
  • Session ID — a sessão do AgentCore Runtime, enviada no cabeçalho X-Amzn-Bedrock-AgentCore-Runtime-Session-Id. Ele seleciona a microVM que atende a requisição, e é o que o session.ts / session.py do seu agente usa como chave para o estado da conversa.

O hook deriva o session ID do thread ID, preenchendo-o à direita até os 33 caracteres que o AgentCore Runtime requer:

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

Deixar threadId não definido é mais simples — o UUID gerado pelo CopilotKit já tem 36 caracteres. Se você passar um explicitamente, faça com que tenha pelo menos 33 caracteres, pois o preenchimento mapeia thread IDs que diferem apenas em caracteres finais para a mesma sessão.

Tanto o Session ID quanto o Thread ID são fornecidos pelo navegador. Para restringir cada usuário às suas próprias conversas, consulte o guia py#agent ou ts#agent.

Se o seu agent usa autenticação IAM, a função autenticada do Cognito Identity Pool deve receber permissão para invocar o agent.

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 as ações de invocação do AgentCore (InvokeAgentRuntime, InvokeAgentRuntimeWithWebSocketStream) no ARN de runtime do agent.

Se o seu agent usa autenticação Cognito, você não precisa definir nenhuma infraestrutura adicional para conectar seu website ao seu agent.

O gerador fornece um componente <AgentName>Chat por agente conectado, já vinculado ao id desse agente e temático para corresponder ao ux do seu site. Coloque-o em qualquer lugar dentro do 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...',
}}
/>
);
}

Ele encaminha todas as props de CopilotChat exceto agentId, então qualquer coisa que você possa passar para <CopilotChat /> funciona aqui também.

Execute o gerador connection uma vez por agente. Cada execução fornece o componente de chat próprio desse agente, então rotear um chat para um agente específico é uma questão de qual componente você renderiza:

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

Se você precisar do id bruto — para chamar os próprios hooks do CopilotKit, digamos — cada hook gerado o exporta:

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

<CopilotChat /> (e <CopilotSidebar />, <CopilotPopup />) usam um sistema de slots recursivo — você pode substituir qualquer subcomponente com uma string de classe Tailwind, um objeto de props ou um componente React personalizado. Veja o guia de slots do CopilotKit para a árvore completa de slots.

O gerador lê metadata.ux do seu projeto de site React e fornece um módulo wrapper temático em src/components/copilot/index.tsx para que os componentes de chat correspondam ao resto da sua UI sem nenhuma configuração extra:

uxEstilo aplicado a CopilotChat / CopilotSidebar / CopilotPopup
cloudscapeMensagens renderizadas dentro de ChatBubbles Cloudscape com Avatars gen-AI (correspondendo ao padrão de chat de IA generativa Cloudscape); o indicador de digitação se torna uma LoadingBar e a entrada é um PromptInput. Construído a partir de @cloudscape-design/components e @cloudscape-design/chat-components.
shadcnMensagens do assistente renderizadas em uma bolha bg-muted com um avatar Sparkles; mensagens do usuário renderizadas alinhadas à direita em uma bolha bg-primary com um avatar User. A entrada é um Textarea arredondado + Button de enviar/parar em forma de pílula (Enter envia, Shift+Enter nova linha). Usa primitivos shadcn do pacote compartilhado common-shadcn.
none (ou qualquer outra coisa)Sem tema — o módulo apenas re-exporta os componentes padrão do CopilotKit.

Os componentes <AgentName>Chat fornecidos já estão temáticos. Para um chat que você configura manualmente, importe do módulo de tema local (não diretamente de @copilotkit/react-core/v2) para que o tema seja aplicado automaticamente:

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

O tema é aplicado como padrões de slot, então qualquer slot que você passar explicitamente ainda prevalece — você mantém controle total sempre que precisar de uma substituição pontual.

O tema gerado vive inteiramente dentro do seu projeto:

  • src/components/copilot/index.tsx — exporta os CopilotChat / CopilotSidebar / CopilotPopup temáticos e os objetos cloudscapeCopilotTheme / shadcnCopilotTheme. Edite este arquivo para alterar a conexão de slot padrão para cada chat no seu aplicativo.
  • src/components/copilot/<ThemeComponent>.tsx — componentes de tema por slot (por exemplo, CloudscapeAssistantMessage, ShadcnChatInput). Edite estes para ajustar a aparência de um único slot sem reconectar o tema.

Por exemplo, para inserir seu próprio renderizador de mensagem de usuário mantendo o resto do tema, edite o arquivo relevante em src/components/copilot/ e re-exporte-o de index.tsx.

Substituições por chat ainda funcionam junto com o tema — qualquer coisa que você passar como prop de slot substitui o padrão 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',
}}
/>

Substituindo um slot com um componente personalizado

Seção intitulada “Substituindo um slot com um componente personalizado”

Qualquer slot pode receber um componente React em vez de um className, então você pode substituir o padrão inteiramente. Tipifique seu componente de acordo com as props que o slot declara — sendButton renderiza um <button>, então ele recebe 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 }} />;

Substituições mais profundas seguem a mesma forma — por exemplo, substitua apenas o botão de copiar nas mensagens do assistente:

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

O gerador de conexão configura automaticamente a integração dev:

  1. Executar nx dev <website> também iniciará o servidor local do agente
  2. A configuração de runtime é substituída para apontar para a URL AG-UI local (por exemplo, http://localhost:8081)
  3. Tanto o site quanto o agente recarregam automaticamente juntos
Terminal window
pnpm nx dev <WebsiteProject>