npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@simplai.ai/voice-agent-sdk

v1.0.1

Published

SimplAI Voice Agent SDK - React hooks for text chat and voice agent integration

Readme

@simplai.ai/voice-agent-sdk

React hooks SDK for integrating SimplAI voice and text chat agents into your application.

Provides a single unified hook (useSimplAIVoiceAgent) that handles agent configuration fetching, SSE-based text chat streaming, and real-time voice conversation - all wired together out of the box.


Installation

npm install @simplai.ai/voice-agent-sdk
# or
yarn add @simplai.ai/voice-agent-sdk
# or
pnpm add @simplai.ai/voice-agent-sdk

Peer Dependencies

The SDK requires React 17+ as a peer dependency:

npm install react react-dom

Quick Start

1. Wrap your app with the provider

import { SimplAIProvider } from "@simplai.ai/voice-agent-sdk";

function App() {
  return (
    <SimplAIProvider
      config={{
        agentId: "your-agent-id",
        apiKey: "your-api-key",
        tenantId: "your-tenant-id",
        projectId: "your-project-id",
        userId: "current-user-id",
      }}
    >
      <ChatBot />
    </SimplAIProvider>
  );
}

2. Use the composite hook

import { useSimplAIVoiceAgent } from "@simplai.ai/voice-agent-sdk";

function ChatBot() {
  const {
    // Agent
    agentDetails,
    agentLoading,
    agentError,

    // Text chat
    messages,
    input,
    handleInputChange,
    handleSubmit,
    isLoading,
    chatStreaming,
    stopStream,

    // Voice
    voiceStatus,
    connectToRoom,
    disconnectFromRoom,
    interruptAgent,
    toggleMute,
    isMicrophoneEnabled,
    agentConnected,
  } = useSimplAIVoiceAgent();

  if (agentLoading) return <div>Loading agent...</div>;
  if (agentError) return <div>Error: {agentError}</div>;

  return (
    <div>
      {/* Message list */}
      <div>
        {messages.map((msg) => (
          <div key={msg.id} className={msg.role === "user" ? "user" : "agent"}>
            {msg.content}
          </div>
        ))}
      </div>

      {/* Text input */}
      <form onSubmit={handleSubmit}>
        <input value={input} onChange={handleInputChange} />
        <button type="submit" disabled={isLoading}>
          Send
        </button>
        {chatStreaming && <button onClick={stopStream}>Stop</button>}
      </form>

      {/* Voice controls */}
      <div>
        {voiceStatus === "idle" && (
          <button onClick={connectToRoom}>Start Voice</button>
        )}
        {voiceStatus === "connecting" && <span>Connecting...</span>}
        {voiceStatus === "connected" && (
          <>
            <button onClick={() => toggleMute(!isMicrophoneEnabled)}>
              {isMicrophoneEnabled ? "Mute" : "Unmute"}
            </button>
            <button onClick={interruptAgent}>Interrupt</button>
            <button onClick={disconnectFromRoom}>End Call</button>
          </>
        )}
      </div>
    </div>
  );
}

Configuration

SimplAISDKConfig

| Property | Type | Required | Description | | ----------- | -------- | -------- | -------------------------------------------------- | | agentId | string | Yes | The ID of your SimplAI agent | | apiKey | string | Yes | Your SimplAI API key (passed as PIM-SID header) | | tenantId | string | Yes | Your tenant identifier | | projectId | string | Yes | The project the agent belongs to | | userId | string | Yes | Current user's unique identifier | | deviceId | string | No | Custom device identifier (defaults to "simplai") |


API Reference

Provider

<SimplAIProvider config={...}>

Wraps your component tree and provides the SDK context (HTTP client with auth headers, endpoint config) to all child hooks.

<SimplAIProvider config={sdkConfig}>{children}</SimplAIProvider>

Hooks

useSimplAIVoiceAgent(options?)

The primary hook. Fetches agent details automatically, initialises text chat and voice, and returns a single flat API.

Options (UseSimplAIVoiceAgentOptions):

| Option | Type | Description | | -------------------------- | ------------------------ | ------------------------------------------------------- | | conversationId | string | Resume an existing conversation | | customAttributes | object | Additional attributes sent with chat requests | | startSession | () => void | Callback when voice session starts (avatar mode) | | endSession | () => void | Callback when voice session ends (avatar mode) | | handleChunkSpeak | (text: string) => void | Receive avatar-voice text chunks | | enableAgentThinkingMode | () => void | Callback when agent enters thinking mode | | disableAgentThinkingMode | () => void | Callback when agent exits thinking mode | | hasAvatar | boolean | Set true if using avatar mode (changes audio routing) |

Return value (UseSimplAIVoiceAgentReturn):

| Field | Type | Description | | --------------------- | -------------------- | ------------------------------- | | agentDetails | AgentDetails\|null | Fetched agent configuration | | agentLoading | boolean | Agent details are loading | | agentError | string\|null | Agent fetch error message | | refetchAgentDetails | () => void | Manually re-fetch agent details |

| Field | Type | Description | | ------------------------------- | ------------------------------------------------- | ------------------------------------------- | | messages | ChatMessage[] | Array of conversation messages | | setMessages | Dispatch<SetStateAction<ChatMessage[]>> | Directly set messages | | input | string | Current text input value | | setInput | Dispatch<SetStateAction<string>> | Set input value | | handleInputChange | (e) => void | Bind to <input onChange> | | handleSubmit | (e?, newMessage?, additionalConfig?) => Promise | Submit a message | | isLoading | boolean | Waiting for AI response | | chatStreaming | boolean | AI response is actively streaming | | stopStream | () => Promise<void> | Abort the active stream | | chatConfig | ChatConfig | Current chat configuration | | setChatConfig | Dispatch<SetStateAction<any>> | Update chat config | | conversationId | string\|undefined | Active conversation ID | | setConversationId | Dispatch<SetStateAction<string\|undefined>> | Set conversation ID | | changeConversation | (convId) => Promise<void> | Load a different conversation | | changeConversationLoading | boolean | Conversation switch in progress | | submitMessageFeedback | (liked, messageObj, remark?) => Promise<void> | Submit like/dislike feedback | | stopConversation | () => Promise<void> | Stop the current conversation on the server | | resetConversation | () => Promise<void> | Clear all state and start fresh | | artifacts | ChatArtifacts | Code/text artifacts from the agent | | setArtifacts | Dispatch<SetStateAction<ChatArtifacts>> | Set artifacts directly | | agentArtifactDrawerVisible | boolean | Artifact drawer visibility state | | setAgentArtifactDrawerVisible | Dispatch<SetStateAction<boolean>> | Toggle artifact drawer | | closeAgentArtifactDrawer | () => void | Close the artifact drawer | | updateArtifact | function | Persist artifact changes to the server | | agentToolDrawerConfig | AgentToolDrawerConfig | Tool drawer configuration | | setAgentToolDrawerConfig | Dispatch<SetStateAction<...>> | Set tool drawer config | | agentToolDrawerVisible | boolean | Tool drawer visibility state | | setAgentToolDrawerVisible | Dispatch<SetStateAction<boolean>> | Toggle tool drawer | | custAtrr | UnknownObject\|null\|undefined | Custom attributes state | | setCustAtrr | Dispatch<SetStateAction<...>> | Set custom attributes | | resetCustAtrr | () => void | Reset custom attributes | | projectId | string\|null\|undefined | Active project ID | | setProjectId | Dispatch<SetStateAction<...>> | Set project ID |

| Field | Type | Description | | -------------------------- | -------------------------------------------- | ------------------------------------------ | | voiceStatus | "idle"\|"connecting"\|"connected"\|"error" | Current voice connection state | | voiceRoom | Room\|null | Room instance | | voiceParticipants | Participant[] | All room participants | | voiceError | string\|null | Voice connection error | | voiceAudioTracks | { [key: string]: Track\|null } | Audio tracks keyed by participant identity | | agentConnected | boolean | Whether the AI agent has joined the room | | isMicrophoneEnabled | boolean | Whether the local mic is on | | connectToRoom | () => Promise<void> | Start a voice session | | disconnectFromRoom | () => void | End the voice session | | interruptAgent | () => void | Interrupt the agent mid-speech | | toggleMute | (isMuted: boolean) => void | Toggle local microphone (true = mute) | | voiceConversationId | any | Conversation ID for the voice session | | setVoiceConversationId | Dispatch<SetStateAction<any>> | Set voice conversation ID | | conversationProjectId | string\|null\|undefined | Project ID for the voice conversation | | setConversationProjectId | Dispatch<SetStateAction<...>> | Set voice conversation project ID |


useChatStream(input)

Lower-level hook for text-only chat. Use this if you do not need voice capabilities.

import { useChatStream } from "@simplai.ai/voice-agent-sdk";

const chat = useChatStream({
  chatConfig: {
    model: "",
    language_code: "EN",
    source: "APP",
    app_id: "your-agent-id",
    model_id: "",
    version_id: "",
  },
});

Must be used inside <SimplAIProvider>.


useSimplaiAudio(props)

Lower-level hook for voice-only functionality. Use this if you are managing chat state separately.

import { useSimplaiAudio } from "@simplai.ai/voice-agent-sdk";

const voice = useSimplaiAudio({
  agentDetails: { agent_id: "...", agent_name: "..." },
  userDetails: { name: "User", id: "user-1" },
  setMessages: setMessages,
  changeConversation: changeConversation,
  conversationId: currentConvId,
  projectId: "project-id",
});

Must be used inside <SimplAIProvider>.


Types

All types are exported and can be imported for TypeScript usage:

import type {
  SimplAISDKConfig,
  ChatMessage,
  Artifact,
  ChatArtifacts,
  VoiceStatus,
  AgentDetails,
  UseSimplAIVoiceAgentOptions,
  UseSimplAIVoiceAgentReturn,
} from "@simplai.ai/voice-agent-sdk";

Examples

Text Chat Only

import { SimplAIProvider, useChatStream } from "@simplai.ai/voice-agent-sdk";

function TextChatApp() {
  return (
    <SimplAIProvider
      config={{
        agentId: "agent-123",
        apiKey: "sk-xxx",
        tenantId: "tenant-1",
        projectId: "proj-1",
        userId: "user-1",
      }}
    >
      <TextChat />
    </SimplAIProvider>
  );
}

function TextChat() {
  const {
    messages,
    input,
    handleInputChange,
    handleSubmit,
    isLoading,
    chatStreaming,
    stopStream,
    resetConversation,
  } = useChatStream({
    chatConfig: {
      model: "",
      language_code: "EN",
      source: "APP",
      app_id: "agent-123",
      model_id: "",
    },
  });

  return (
    <div>
      <div style={{ height: 400, overflowY: "auto" }}>
        {messages.map((msg) => (
          <div key={msg.id}>
            <strong>{msg.role === "user" ? "You" : "Agent"}:</strong>{" "}
            {msg.content}
          </div>
        ))}
        {isLoading && <div>Thinking...</div>}
      </div>

      <form onSubmit={handleSubmit} style={{ display: "flex", gap: 8 }}>
        <input
          value={input}
          onChange={handleInputChange}
          placeholder="Type a message..."
          style={{ flex: 1 }}
        />
        <button type="submit" disabled={isLoading}>
          Send
        </button>
        {chatStreaming && (
          <button type="button" onClick={stopStream}>
            Stop
          </button>
        )}
      </form>

      <button onClick={resetConversation}>New Conversation</button>
    </div>
  );
}

Voice Agent with Avatar

import {
  SimplAIProvider,
  useSimplAIVoiceAgent,
} from "@simplai.ai/voice-agent-sdk";

function AvatarApp() {
  return (
    <SimplAIProvider
      config={{
        agentId: "voice-agent-456",
        apiKey: "sk-xxx",
        tenantId: "tenant-1",
        projectId: "proj-1",
        userId: "user-1",
      }}
    >
      <AvatarAgent />
    </SimplAIProvider>
  );
}

function AvatarAgent() {
  const {
    agentDetails,
    agentLoading,
    messages,
    voiceStatus,
    agentConnected,
    isMicrophoneEnabled,
    connectToRoom,
    disconnectFromRoom,
    interruptAgent,
    toggleMute,
    voiceError,
  } = useSimplAIVoiceAgent({
    hasAvatar: true,
    startSession: () => console.log("Avatar session started"),
    endSession: () => console.log("Avatar session ended"),
    handleChunkSpeak: (text) => {
      // Feed text to your avatar TTS system
      console.log("Avatar speak:", text);
    },
  });

  if (agentLoading) return <div>Loading...</div>;

  return (
    <div>
      <h2>{agentDetails?.agent_name || "Voice Agent"}</h2>

      {/* Transcript */}
      <div style={{ height: 300, overflowY: "auto" }}>
        {messages.map((msg) => (
          <p key={msg.id}>
            <strong>{msg.role === "user" ? "You" : "Agent"}:</strong>{" "}
            {msg.content}
          </p>
        ))}
      </div>

      {/* Voice controls */}
      {voiceStatus === "idle" && (
        <button onClick={connectToRoom}>Start Conversation</button>
      )}

      {voiceStatus === "connecting" && <p>Connecting to agent...</p>}

      {voiceStatus === "connected" && (
        <div style={{ display: "flex", gap: 8 }}>
          <button onClick={() => toggleMute(!isMicrophoneEnabled)}>
            {isMicrophoneEnabled ? "Mute Mic" : "Unmute Mic"}
          </button>
          <button onClick={interruptAgent}>Interrupt</button>
          <button onClick={disconnectFromRoom}>End Call</button>
          {agentConnected && <span>Agent connected</span>}
        </div>
      )}

      {voiceStatus === "error" && (
        <p style={{ color: "red" }}>Error: {voiceError}</p>
      )}
    </div>
  );
}

Conversation History / Switch Conversations

import { useSimplAIVoiceAgent } from "@simplai.ai/voice-agent-sdk";

function ChatWithHistory() {
  const {
    messages,
    input,
    handleInputChange,
    handleSubmit,
    conversationId,
    changeConversation,
    changeConversationLoading,
    resetConversation,
  } = useSimplAIVoiceAgent();

  const [savedConversations] = useState(["conv-1", "conv-2", "conv-3"]);

  return (
    <div style={{ display: "flex" }}>
      {/* Sidebar */}
      <aside style={{ width: 200 }}>
        <button onClick={resetConversation}>New Chat</button>
        <h4>History</h4>
        {savedConversations.map((id) => (
          <button
            key={id}
            onClick={() => changeConversation(id)}
            disabled={changeConversationLoading}
            style={{ fontWeight: id === conversationId ? "bold" : "normal" }}
          >
            {id}
          </button>
        ))}
      </aside>

      {/* Chat area */}
      <main style={{ flex: 1 }}>
        {changeConversationLoading ? (
          <p>Loading conversation...</p>
        ) : (
          <>
            {messages.map((msg) => (
              <div key={msg.id}>
                <strong>{msg.role}:</strong> {msg.content}
              </div>
            ))}
            <form onSubmit={handleSubmit}>
              <input value={input} onChange={handleInputChange} />
              <button type="submit">Send</button>
            </form>
          </>
        )}
      </main>
    </div>
  );
}

Message Feedback

import { useSimplAIVoiceAgent, ChatMessage } from "@simplai.ai/voice-agent-sdk";

function ChatWithFeedback() {
  const {
    messages,
    submitMessageFeedback,
    input,
    handleInputChange,
    handleSubmit,
  } = useSimplAIVoiceAgent();

  const handleFeedback = async (msg: ChatMessage, liked: boolean) => {
    await submitMessageFeedback(liked, msg, liked ? "" : "Not helpful");
  };

  return (
    <div>
      {messages.map((msg) => (
        <div key={msg.id}>
          <p>
            <strong>{msg.role}:</strong> {msg.content}
          </p>
          {msg.role === "SimplAi" && (
            <div>
              <button
                onClick={() => handleFeedback(msg, true)}
                style={{ opacity: msg.message_liked === true ? 1 : 0.4 }}
              >
                👍
              </button>
              <button
                onClick={() => handleFeedback(msg, false)}
                style={{ opacity: msg.message_liked === false ? 1 : 0.4 }}
              >
                👎
              </button>
            </div>
          )}
        </div>
      ))}

      <form onSubmit={handleSubmit}>
        <input value={input} onChange={handleInputChange} />
        <button type="submit">Send</button>
      </form>
    </div>
  );
}

Browser Requirements

  • Modern browser with WebRTC support (Chrome 74+, Firefox 66+, Safari 14.1+, Edge 79+)
  • Microphone access permission for voice features
  • WebAssembly support for RNNoise audio denoising

License

MIT