Skip to main content

Adding Event Listeners

After initializing the Anam client, you can register event listeners using the addListener method:
import { AnamEvent } from "@anam-ai/js-sdk/dist/module/types

anamClient.addListener(AnamEvent.CONNECTION_ESTABLISHED, () => {
  console.log('Connection Established');
});

anamClient.addListener(AnamEvent.MESSAGE_HISTORY_UPDATED, (messages) => {
  console.log('Updated Messages:', messages);
});

Available Events

Event NameDescription
CONNECTION_ESTABLISHEDCalled when the direct connection between the browser and the Anam Engine has been established
SESSION_READYCalled after the session initialises and all the backend components are ready. Useful for removing loading indicators
CONNECTION_CLOSEDCalled when the direct connection between the browser and the Anam Engine has been closed
VIDEO_PLAY_STARTEDFired when the first frames start playing during video streaming.
MESSAGE_HISTORY_UPDATEDCalled with the message history transcription each time the user or persona finishes speaking
MESSAGE_STREAM_EVENT_RECEIVEDFor persona speech: Updated with each transcribed chunk as the persona speaks
For user speech: Updated with complete transcription once they finish speaking
INPUT_AUDIO_STREAM_STARTEDCalled with the user’s input audio stream when microphone input has been initialized
TALK_STREAM_INTERRUPTEDCalled when the user interrupts a TalkMessageStream by speaking. Includes the interrupted stream’s correlationId
TOOL_CALLCalled when the AI persona invokes a client tool. Includes the tool name and arguments for handling UI actions like navigation or modal displays

Example Usage

Loading States

Use connection events to manage loading states:
anamClient.addListener(AnamEvent.CONNECTION_ESTABLISHED, () => {
  setIsConnecting(false);
});

anamClient.addListener(AnamEvent.SESSION_READY, () => {
  setIsLoading(false);
});

Message History

Track conversation history:
anamClient.addListener(AnamEvent.MESSAGE_HISTORY_UPDATED, (messages) => {
  setConversationHistory(messages);
});

Real-time Transcription

Monitor speech in real-time:
anamClient.addListener(AnamEvent.MESSAGE_STREAM_EVENT_RECEIVED, (event) => {
  if (event.role === "persona") {
    updatePersonaSpeech(event.content);
  } else {
    updateUserSpeech(event.content);
  }
});

Client Tool Events

Handle UI actions triggered by the AI persona:
anamClient.addListener(AnamEvent.TOOL_CALL, (event) => {
  const { toolName, arguments: args } = event;

  switch (toolName) {
    case "navigate_to_page":
      router.push(`/${args.page}`);
      break;
    case "open_modal":
      openModal(args.modalType, args.data);
      break;
    default:
      console.warn(`Unhandled tool: ${toolName}`);
  }
});

Tool Call Events (Detailed Guide)

Client tools enable your AI persona to trigger events in your client application - like navigating to pages, opening modals, updating UI state, or controlling media playback. When the LLM invokes a client tool during a conversation, the Anam SDK emits a TOOL_CALL event that you can handle in your application code.
For a comprehensive guide on client tools, including how to create them, common use cases, real-world examples, and advanced patterns, see the Client Tools Guide.

Complete Example

Use the addListener method to register a handler for tool call events. The event name is provided by the AnamEvent enum, and the payload is typed with ClientToolEvent.
import { AnamClient, AnamEvent, ClientToolEvent } from "@anam-ai/js-sdk";

const client = new AnamClient({
  sessionToken: "YOUR_SESSION_TOKEN",
});

const handleToolCall = (event: ClientToolEvent) => {
  console.log("Tool call received:", event);

  const { toolName, arguments: args } = event;

  // Handle the tool call based on its name
  switch (toolName) {
    case "navigate_to_page":
      // Example: navigate using your app's router
      router.push(`/${args.page}`);
      break;

    case "open_checkout_modal":
      // Example: open a modal with product data
      openCheckout(args.productId);
      break;

    case "apply_filters":
      // Example: update UI state
      applyProductFilters(args);
      break;

    default:
      console.warn(`Unhandled tool call: ${toolName}`);
  }
};

// Add the listener
client.addListener(AnamEvent.TOOL_CALL, handleToolCall);

// Connect the client to start receiving events
await client.connect();

Event Payload

The TOOL_CALL event provides a payload object with the following structure:
toolName
string
The name of the client tool that was invoked.
arguments
object
An object containing the parameters and their values as extracted by the LLM from the conversation. The structure of this object matches the parameters JSON schema you defined for the tool.
Example Payload: If you have a tool defined like this:
{
  "type": "client",
  "name": "open_product_page",
  "description": "Opens a product page in the app",
  "parameters": {
    "type": "object",
    "properties": {
      "productId": { "type": "string" },
      "category": { "type": "string" }
    }
  }
}
And the user says, “Show me product ABC-123 in the electronics category,” the event payload would look like this:
{
  "toolName": "open_product_page",
  "arguments": {
    "productId": "ABC-123",
    "category": "electronics"
  }
}

Removing Event Listeners

It’s important to clean up listeners to prevent memory leaks, especially in single-page applications. Use the removeListener method. This is often done in a cleanup function, such as React’s useEffect hook.
// In a React component
useEffect(() => {
  const handleToolCall = (event: ClientToolEvent) => {
    // ... your logic
  };

  // Add listener when the component mounts
  client.addListener(AnamEvent.TOOL_CALL, handleToolCall);

  // Return a cleanup function to remove the listener when the component unmounts
  return () => {
    client.removeListener(AnamEvent.TOOL_CALL, handleToolCall);
  };
}, [client]); // Rerun effect if the client instance changes

Best Practices

A switch statement is a clean way to handle different tool calls. Always include a default case to log unhandled tools.
Never trust the arguments from a tool call without validation. Even though the LLM tries to provide correct data, it can sometimes make mistakes. Sanitize and validate all inputs before performing actions.
case 'navigate_to_page':
  const allowedPages = ['home', 'pricing', 'about'];
  if (allowedPages.includes(args.page)) {
    router.push(`/${args.page}`);
  } else {
    console.error(`Invalid page navigation attempt: ${args.page}`);
  }
  break;
After handling a tool call, provide some form of visual feedback in your UI to confirm the action was performed (e.g., a toast notification, a loading spinner, or the navigation itself).
Wrap your tool handling logic in try...catch blocks to gracefully handle any errors that occur during execution.

Learn More