Skip to main content
Version: Próxima

Uso Básico

Instalação​

O hook vem na mesma biblioteca do useAgent. O eitri-agents entra na raiz do eitri-app.conf.js, ao lado do eitri-luminus e do eitri-bifrost — e não em eitri-app-dependencies, que é para Eitri-Apps compartilhados:

// eitri-app.conf.js
module.exports = {
name: 'meu-app',
'eitri-luminus': '2.4.1',
'eitri-bifrost': '4.8.0',
'eitri-agents': '1.14.3',
// ...
};

Não é preciso rodar eitri agents setup — quem responde é o agente do CX.

Primeira conversa​

import { useState } from 'react';
import { useAgentVtexCX, AgentRole } from 'eitri-agents';
import { Button, Chat, Page, Text, TextInput, View } from 'eitri-luminus';

export default function Atendimento() {
const agent = useAgentVtexCX({ channelUuid: '04fa9af0-f043-447e-bb55-7585c45192da' });
const [draft, setDraft] = useState('');

const enviar = async () => {
const texto = draft.trim();
if (!texto) return;
setDraft('');
await agent.call({ content: texto, role: AgentRole.User });
};

return (
<Page>
<Chat>
{agent.messages.map((message) =>
message.role === AgentRole.User ? (
<Chat.End key={message.id}>
<Chat.Bubble>{message.content}</Chat.Bubble>
</Chat.End>
) : (
<Chat.Start key={message.id}>
<Chat.Bubble>
{message.content}
{message.status === 'streaming' ? ' ▍' : ''}
</Chat.Bubble>
</Chat.Start>
),
)}
</Chat>

<View>
<TextInput value={draft} onChange={(e) => setDraft(e.target.value)} />
<Button onClick={enviar} disabled={!agent.isReady}>
Enviar
</Button>
</View>
</Page>
);
}

Repare que não é preciso tratar o streaming: agent.messages é atualizado a cada pedaço de texto que chega, e o React re-renderiza. O ▍ é só um sinal visual de que aquela mensagem ainda está sendo escrita.

Parâmetros​

useAgentVtexCX(options)

OpçãoTipoPadrãoDescrição
channelUuidstring—Obrigatório. Canal do CX Platform.
hoststringhttps://flows.weni.aiHost de flows; monta o callback do registro.
socketUrlstringhttps://websocket.weni.aiHost do WebSocket, na forma https://.
sessionIdstringid anônimo persistidoIdentidade da conversa (campo from).
initPayloadstring—Mensagem oculta que dispara o flow numa conversa nova.
autoConnectbooleantrueConectar ao montar o hook.
historyLimitnumber20Tamanho da página do histórico.
responseTimeoutnumber60000Espera máxima, em ms, pela resposta que resolve o call().
verbosebooleanfalseLiga os logs da biblioteca (mesmo Logger do useAgent).
onDelta(chunk, message) => void—Chamado a cada pedaço de texto recebido.

Para escopar a conversa por usuário, passe o sessionId — o padrão da loja é email:conta:

const agent = useAgentVtexCX({
channelUuid,
sessionId: `${cliente.email}:${vtexAccount}`,
});

Sem sessionId, o hook gera um id anônimo e o reaproveita nas próximas aberturas, então a conversa continua de onde parou.

Valores de Retorno​

CampoTipoDescrição
messagesVtexCXMessage[]A conversa em ordem cronológica; muda a cada delta.
statusVtexCXAgentStatusidle, connecting, thinking, typing, streaming ou error.
connectionStatusVtexCXConnectionStatusdisconnected, connecting, connected, reconnecting, error.
isReadybooleanSessão resolvida e socket conectado.
errorError | nullÚltimo erro não recuperado.
call(message) => Promise<{ message: string }>Envia texto e/ou arquivo.
connect / disconnect() => …Controle manual da conexão.
newConversation() => Promise<void>Começa uma conversa nova.
conversationsVtexCXConversation[]Conversas que este app já abriu.
openConversation(sessionId) => Promise<void>Reabre uma conversa existente.
loadMore / hasMore—Paginação do histórico.
sessionIdstring | nullIdentidade em uso.

A mensagem​

Cada item de messages tem esta forma — os campos estruturados mantêm o nome do protocolo do CX:

{
id: string;
role: AgentRole; // User | Assistant
content: string; // texto (acumulado durante o streaming)
status: 'pending' | 'sent' | 'streaming' | 'delivered' | 'error';
timestamp: number; // epoch em ms
type?: 'text' | 'image' | 'video' | 'audio' | 'file';
media?: string;
caption?: string;
quick_replies?: VtexCXQuickReply[];
list_message?: VtexCXListMessage;
cta_message?: { url: string; display_text: string };
product_list?: VtexCXProductList;
header?: string;
footer?: string;
raw?: Record<string, unknown>; // o payload cru do frame
}

Ver Uso Avançado para renderizar esses campos.

Enviando imagem​

O formato é o mesmo do useAgent (file: { mimeType, data }), então o código do picker não muda:

const [file] = await Eitri.fs.openImagePicker({ allowsMultipleSelection: false });

await agent.call({
content: 'O que você consegue me dizer sobre esta imagem?',
role: AgentRole.User,
file: { mimeType: file.mimeType, data: await file.toBase64() },
});

O hook converte o base64 em data: URL antes de enviar.

O retorno do call()​

O protocolo do CX não correlaciona pergunta e resposta. A Promise resolve no primeiro texto finalizado que o agente enviar depois do seu envio:

const { message } = await agent.call({ content: 'quais as promoções?' });

Em flows que respondem com várias mensagens, leia agent.messages em vez do retorno do call(). Se nada chegar dentro de responseTimeout, a Promise é rejeitada — trate com try/catch.