@ola-krutrim/ai-sdk
v0.3.1
Published
Drop-in React chat and voice widgets for Agent Bazaar (ChatWidget, ChatButton, VoiceWidget, VoiceButton)
Maintainers
Readme
@ola-krutrim/ai-sdk
Drop-in React components for Agent Bazaar chat and voice. Pass your agent credentials — the SDK handles sessions, streaming, branding, rich content, and voice.
npm install @ola-krutrim/ai-sdk react react-domimport {
ChatWidget,
ChatButton,
VoiceWidget,
VoiceButton,
} from "@ola-krutrim/ai-sdk/react";You need an agent name and agent key from the Agent Bazaar console.
Architecture
Chat and voice are separate widgets. Shared contracts live in base types; each domain has its own widget + launcher.
| Layer | Role |
|-------|------|
| AiWidgetProps | Type-only base — agent credentials, branding, panel size (not a renderable component) |
| AiButtonProps | Shared floating launcher config (type, placement, sizes, defaultOpen, etc.) |
| AiButton | Shared launcher UI (circular bubble or bottom header dock + panel shell) |
| ChatWidget | Chat-only inline panel |
| ChatButton | Chat-only launcher — AiButton + inner ChatWidget |
| VoiceWidget | Voice-only inline panel |
| VoiceButton | Voice-only launcher — AiButton + inner VoiceWidget |
| VoiceMicButton | Inline mic toggle inside the voice panel (not the floating launcher) |
AiButtonProps AiWidgetProps (type-only)
│ │
ChatButton ──► ChatWidget VoiceButton ──► VoiceWidget
│ │
useAgentChat useAgentVoiceWhich component should I use?
| Your goal | Component |
|-----------|-----------|
| Chat embedded in your page | ChatWidget |
| Floating chat launcher | ChatButton |
| Voice embedded in your page | VoiceWidget |
| Floating voice launcher | VoiceButton |
Rule of thumb: use *Widget when you already have a place in your layout. Use *Button for a fixed launcher that opens a panel.
Chat
Inline — ChatWidget
<ChatWidget
agentName="my_agent"
agentKey={import.meta.env.VITE_AGENT_KEY}
size="medium"
placeholder="Ask anything…"
/>Built in automatically:
- Agent logo, colors, title, and description from
GET /details - Persistent chat session and streaming replies
- Text, images, charts/HTML, and custom widget payloads
Floating launcher — ChatButton
Put panel options in chatWidgetProps (required). Launcher options stay on ChatButton.
<ChatButton
type="circular"
launcherPosition="bottom_right"
panelPlacement="top_right"
buttonSize="medium"
chatWidgetProps={{
agentName: "my_agent",
agentKey: import.meta.env.VITE_AGENT_KEY,
size: "medium",
accentColor: "#2563eb",
}}
/>ChatButton creates an inner ChatWidget and shares one useAgentChat call — a single GET /details for launcher and panel.
Header launcher
<ChatButton
type="header"
headerPosition="bottom_right"
headerSize="large"
headerText="medium"
chatWidgetProps={{
agentName: "my_agent",
agentKey: import.meta.env.VITE_AGENT_KEY,
size: "medium",
}}
/>When type="header", the launcher bar acts as the header and the inner ChatWidget hides its own header automatically.
Shared client
import { AgentBazaar } from "@ola-krutrim/ai-sdk";
const client = new AgentBazaar({
agentName: import.meta.env.VITE_AGENT_NAME,
agentKey: import.meta.env.VITE_AGENT_KEY,
});
<ChatButton
type="header"
headerPosition="bottom_right"
chatWidgetProps={{ client, size: "medium" }}
/>Voice
Inline — VoiceWidget
<VoiceWidget
agentName="my_agent"
agentKey={import.meta.env.VITE_AGENT_KEY}
size="medium"
defaultConnect={false}
onVoiceError={(error) => console.error(error)}
/>Tap VoiceMicButton (mic in the panel) to start a session. A waveform appears when agent audio is live. Tap again to hang up.
Floating launcher — VoiceButton
<VoiceButton
type="circular"
launcherPosition="bottom_right"
voiceWidgetProps={{
agentName: "my_agent",
agentKey: import.meta.env.VITE_AGENT_KEY,
size: "medium",
}}
/>Same launcher options as ChatButton (type, launcherPosition, headerPosition, sizes, etc.) — passed on VoiceButton; agent config in voiceWidgetProps.
Voice dev proxy
For local dev when the app and API run on different ports, use voiceAuth="proxy" and proxy /api/v1/voice/embed so your dev server adds the Bearer token:
// vite.config.ts (example)
server: {
proxy: {
"/api/v1/voice/embed": {
target: "http://127.0.0.1:8000",
ws: true,
changeOrigin: true,
configure: (proxy) => {
proxy.on("proxyReqWs", (proxyReq) => {
proxyReq.setHeader("Authorization", `Bearer ${process.env.VITE_AGENT_KEY}`);
});
},
},
},
},Chat and voice on the same page
Each component has its own session (separate GET /details per component):
const agent = {
agentName: "my_agent",
agentKey: import.meta.env.VITE_AGENT_KEY,
};
<>
<ChatWidget {...agent} size="large" />
<VoiceButton
type="header"
headerPosition="bottom_right"
voiceWidgetProps={{ ...agent, size: "medium" }}
/>
</>Launcher behavior (defaultOpen, minimizeKill)
Applies to ChatButton and VoiceButton:
<ChatButton
type="header"
headerPosition="bottom_left"
defaultOpen={true}
minimizeKill={false}
chatWidgetProps={{ agentName: "my_agent", agentKey: "…", size: "medium" }}
/>| Prop | What it does |
|------|----------------|
| First click | Session starts only after the user clicks the launcher — even if defaultOpen is true. |
| defaultOpen | Panel appears open on load; user still clicks once to start chatting / voice. |
| minimizeKill={false} | Close hides the panel; session keeps running. |
| minimizeKill={true} (default) | Close ends the session (unmounts the inner widget). |
Hooks (headless / custom UI)
Use hooks when you build your own layout but want SDK session logic:
import { useAgentChat, useAgentVoice } from "@ola-krutrim/ai-sdk/react";
const chat = useAgentChat({
agentName: "my_agent",
agentKey: import.meta.env.VITE_AGENT_KEY,
});
const voice = useAgentVoice({
agentName: "my_agent",
agentKey: import.meta.env.VITE_AGENT_KEY,
});Pass pre-loaded state into widgets to avoid duplicate setup:
const chat = useAgentChat({ agentName, agentKey });
<ChatWidget agentName={agentName} agentKey={agentKey} chat={chat} />
<ChatButton chatWidgetProps={{ agentName, agentKey, chat }} />Same pattern for voice on VoiceWidget / VoiceButton.
Props reference
Shared — AiWidgetProps
| Prop | Type | Description | Default |
|------|------|-------------|---------|
| Connection |
| client | AgentBazaar | Pre-built SDK client | — |
| agentName | string | Agent identifier | — |
| agentKey | string | API key (Bearer token) | — |
| apiBase | string | API host | SDK default |
| Layout & look |
| size | AiWidgetSize | Panel preset | "medium" |
| width / height | string | Dimension overrides | From size |
| className / style | — | Root styling | — |
| hideHeader | boolean | Hide header bar | false |
| showDescription | boolean | Show subtitle | true |
| headerColor, logoUrl, title, subtitle, accentColor, backgroundColor, textColor, borderRadius, fontFamily | string | Branding overrides | From agent |
Panel size presets:
| Size | Width | Height |
|------|-------|--------|
| small | 320px | 440px |
| medium | 380px | 520px |
| large | 440px | 600px |
| extralarge | 520px | 680px |
Set width or height explicitly to override (e.g. width="100%").
Chat-only — ChatWidgetProps
Extends AiWidgetProps:
| Prop | Type | Default |
|------|------|---------|
| stream | boolean | true |
| openingMessages | string[] | From agent |
| disableWhileLoading | boolean | true |
| placeholder | string | "Type a message…" |
| userBubbleColor / agentBubbleColor | string | Theme defaults |
| renderWidget / renderHtml / visualization | — | Built-in renderers |
| onError / onSessionId | function | — |
| chat | UseAgentChatResult | Internal — pass to share state with ChatButton |
| supportedMultimedia | SupportedMultimediaKind[] | Widget prop — e.g. ["Image", "Pdf"]; omit/[] = no attachments |
| maxMediaAttachments | number | 7 (max 7) |
| uploadMedia | (file, type) => Promise<string> | Optional — defaults to §4 upload with agent_key |
Chat attachments (multimedia)
Pass supportedMultimedia on ChatWidget (or chatWidgetProps on ChatButton). The SDK does not read this from GET /details — you configure it in your app.
<ChatWidget
agentName="my_agent"
agentKey={agentKey}
supportedMultimedia={["Image", "Pdf"]}
maxMediaAttachments={5}
/>When the array is non-empty, the 📎 button appears beside Send. Upload runs automatically with agent_key (presigned URL flow). Optional uploadMedia only if you use custom storage.
Voice-only — VoiceWidgetProps
Extends AiWidgetProps:
| Prop | Type | Default |
|------|------|---------|
| voiceEnable | boolean | From agent details |
| defaultConnect | boolean | false |
| voiceUserId | string | — |
| voiceMetadata | object | — |
| voiceWsUrl | string | Auto |
| voiceAuth | "query" \| "proxy" | "query" |
| recorderSampleRate / playerSampleRate | number | SDK default |
| onVoiceError | function | — |
| voice | UseAgentVoiceResult | Internal — pass to share state with VoiceButton |
Launcher — AiButtonProps
Used by ChatButton and VoiceButton:
| Prop | Type | Default |
|------|------|---------|
| type | "circular" \| "header" | "circular" |
| launcherPosition | AiPanelPlacement | "bottom_right" |
| panelPlacement | AiPanelPlacement | "top_right" |
| headerPosition | "bottom_left" \| "bottom_right" | "bottom_right" |
| panelGap | number | 12 |
| defaultOpen | boolean | false |
| minimizeKill | boolean | true |
| buttonSize / headerSize / headerText | AiWidgetSize | "medium" |
| zIndex | number | 9999 |
| showDescription | boolean | true |
| chatWidgetProps | ChatWidgetProps | Required on ChatButton |
| voiceWidgetProps | VoiceWidgetProps | Required on VoiceButton |
Placement values (AiPanelPlacement): top_left, top_right, top_center, bottom_left, bottom_right, bottom_center, left_center, right_center
Launcher size presets (circular button / header dock width):
| Size | Button | Header dock |
|------|--------|-------------|
| small | 44px | 280px |
| medium | 56px | 360px |
| large | 72px | 440px |
| extralarge | 88px | 520px |
headerText controls logo, title, subtitle, and padding inside the header bar (independent of headerSize).
ChatButtonProps / VoiceButtonProps
interface ChatButtonProps extends AiButtonProps {
chatWidgetProps: ChatWidgetProps; // required
}
interface VoiceButtonProps extends AiButtonProps {
voiceWidgetProps: VoiceWidgetProps; // required
}Other exports
From @ola-krutrim/ai-sdk/react:
| Export | Purpose |
|--------|---------|
| AiButton | Launcher shell for custom compositions |
| useAgentChat / useAgentVoice | Headless session hooks |
| VoiceMicButton, VoiceSessionView, VoiceWaveform | Voice UI building blocks |
| HtmlVisualization, AgentTurnBubble, DefaultWidget | Rich message rendering |
| resolveTheme, groupMessages | Theming and message helpers |
Core HTTP client (no React):
import { AgentBazaar } from "@ola-krutrim/ai-sdk";Breaking changes (chat/voice split)
| Before | After |
|--------|-------|
| Voice props on ChatWidget (voiceEnable, defaultMode, …) | Use VoiceWidget / VoiceButton |
| Flat props on ChatButton | chatWidgetProps required |
| VoiceButton = inline mic | VoiceMicButton (launcher is now VoiceButton) |
| — | New: VoiceWidget, AiButton, AiWidgetProps, AiButtonProps |
Deprecated type aliases (still exported): ChatButtonSize, ChatWidgetSize → use AiWidgetSize; ChatButtonType → AiButtonType; ChatPanelPlacement → AiPanelPlacement.
Environment variables
VITE_AGENT_NAME=my_agent
VITE_AGENT_KEY=your-agent-key
VITE_API_BASE=https://your-api-host.com # optional
VITE_VOICE_PROXY=true # optional — use proxy auth in devNever expose the agent key in client code unless your deployment model allows it; use a server-side proxy or embed tokens when required.
Playground
See the react_test app in this monorepo for a live prop explorer with Chat / Voice nav and widget vs launcher toggle.
License
MIT
