@emblemvault/hustle-react
v1.5.1
Published
React hooks and components for Hustle AI chat integration
Maintainers
Readme
@emblemvault/hustle-react
React hooks and components for AI chat using the Hustle Incognito SDK.
Installation
npm install @emblemvault/hustle-reactQuick Start
Web Application (Recommended)
Use the React component with Emblem Auth for a complete authentication experience:
import { EmblemAuthProvider, ConnectButton } from '@emblemvault/emblem-auth-react';
import { HustleProvider, HustleChat } from '@emblemvault/hustle-react';
function App() {
return (
<EmblemAuthProvider appId="your-app-id">
<HustleProvider>
<ConnectButton />
<HustleChat showSettings />
</HustleProvider>
</EmblemAuthProvider>
);
}With API Key (Deprecated)
⚠️ Deprecated: API key authentication is deprecated and will be removed in a future version. Please migrate to the Emblem Auth React component for better security and user experience.
import { HustleProvider, HustleChat } from '@emblemvault/hustle-react';
function App() {
return (
<HustleProvider apiKey="your-key" vaultId="your-vault">
<HustleChat showSettings />
</HustleProvider>
);
}API Reference
HustleProvider
Provides chat context to your app.
<HustleProvider
// With EmblemAuthProvider (recommended) - uses authentication context
hustleApiUrl="https://agenthustle.ai" // optional
// Multi-instance support
instanceId="unique-id" // Scopes settings per instance
>
{children}
</HustleProvider>Props:
hustleApiUrl(optional) - Hustle API endpoint URL. Defaults tohttps://agenthustle.aiinstanceId(optional) - Unique identifier for multi-instance setups. Scopes settings and plugins per instance.- ~~
apiKey~~ (deprecated) - API key for standalone auth. Deprecated: UseEmblemAuthProviderinstead. - ~~
vaultId~~ (deprecated) - Vault ID for standalone auth. Deprecated: UseEmblemAuthProviderinstead.
useHustle
Access chat state and methods.
const {
// State
isReady, // boolean - client ready to chat
isLoading, // boolean
error, // Error | null
models, // Model[] - available AI models
client, // HustleIncognitoClient | null
// Chat methods
chat, // (options) => Promise<ChatResponse>
chatStream, // (options) => AsyncIterable<StreamChunk>
uploadFile, // (file) => Promise<Attachment>
loadModels, // () => Promise<Model[]>
// Settings (persisted to localStorage)
selectedModel, // string
setSelectedModel, // (model: string) => void
systemPrompt, // string
setSystemPrompt, // (prompt: string) => void
skipServerPrompt, // boolean
setSkipServerPrompt, // (skip: boolean) => void
} = useHustle();HustleChat
Complete chat UI component.
<HustleChat
showSettings // Show settings modal button
showDebug // Show tool call debug info
placeholder="Message" // Input placeholder
initialSystemPrompt="" // Initial system prompt
enableSpeechToText // Enable voice input via microphone (default: false)
// Callbacks
onMessage={(msg) => {}} // When user sends message
onToolCall={(tool) => {}} // When AI calls a tool
onResponse={(content) => {}} // When AI responds
/>HustleChatWidget
Floating chat widget for site-wide chatbot integration. Perfect for customer support or AI assistants that persist across page navigations.
import { EmblemAuthProvider } from '@emblemvault/emblem-auth-react';
import { HustleProvider, HustleChatWidget } from '@emblemvault/hustle-react';
// Add to your Next.js layout for site-wide availability
export default function RootLayout({ children }) {
return (
<html>
<body>
<EmblemAuthProvider appId="your-app-id">
<HustleProvider>
{children}
<HustleChatWidget
config={{
position: 'bottom-right', // 'bottom-right' | 'bottom-left' | 'top-right' | 'top-left'
size: 'md', // 'sm' | 'md' | 'lg' | 'xl' | 'full'
title: 'Support', // Header title
defaultOpen: false, // Start open?
offset: { x: 24, y: 24 }, // Edge offset in pixels
storageKey: 'chat-open', // localStorage key for persistence (false to disable)
showBadge: true, // Show notification badge
badgeContent: 3, // Badge content
onOpen: () => {}, // Called when widget opens
onClose: () => {}, // Called when widget closes
}}
placeholder="How can we help?"
showSettings
/>
</HustleProvider>
</EmblemAuthProvider>
</body>
</html>
);
}Widget Configuration
| Option | Type | Default | Description |
|--------|------|---------|-------------|
| position | 'bottom-right' \| 'bottom-left' \| 'top-right' \| 'top-left' | 'bottom-right' | Screen position |
| size | 'sm' \| 'md' \| 'lg' \| 'xl' \| 'full' | 'md' | Chat panel size |
| title | string | 'Chat' | Panel header title |
| defaultOpen | boolean | false | Whether widget starts open |
| offset | { x: number, y: number } | { x: 24, y: 24 } | Offset from screen edge |
| storageKey | string \| false | 'hustle-widget-open' | Key for persisting state |
| showBadge | boolean | false | Show notification badge |
| badgeContent | string \| number | - | Badge content |
| launcherIcon | React.ReactNode | Chat bubble | Custom launcher icon |
| launcherStyle | React.CSSProperties | - | Custom launcher styles |
| panelStyle | React.CSSProperties | - | Custom panel styles |
| zIndex | number | 9999 | Widget z-index |
| onOpen | () => void | - | Called when widget opens |
| onClose | () => void | - | Called when widget closes |
Plugins
Extend the AI with custom tools.
Using Built-in Plugins
import { usePlugins, availablePlugins } from '@emblemvault/hustle-react';
function PluginManager() {
const { plugins, registerPlugin, enablePlugin, disablePlugin } = usePlugins();
return (
<div>
{availablePlugins.map(plugin => (
<button onClick={() => registerPlugin(plugin)}>
Install {plugin.name}
</button>
))}
</div>
);
}Creating Custom Plugins
import type { HustlePlugin } from '@emblemvault/hustle-react';
const weatherPlugin: HustlePlugin = {
name: 'weather',
version: '1.0.0',
description: 'Get weather information',
enabled: true,
tools: [{
name: 'get_weather',
description: 'Get weather for a city',
parameters: {
type: 'object',
properties: {
city: { type: 'string', description: 'City name' },
},
required: ['city'],
},
}],
executors: {
get_weather: async ({ city }) => {
const response = await fetch(`/api/weather?city=${city}`);
return response.json();
},
},
};
// Register it
const { registerPlugin } = usePlugins();
registerPlugin(weatherPlugin);Plugin Hooks
const plugin: HustlePlugin = {
// ... name, tools, executors
// Modify messages before sending
beforeRequest: (messages) => {
return messages.map(m => ({
...m,
content: m.content.replace(/secret/gi, '[REDACTED]'),
}));
},
// Modify response after receiving
afterResponse: (response) => {
return {
...response,
content: response.content + '\n\n---\nPowered by MyPlugin',
};
},
};Multi-Instance Support
Run multiple isolated chat instances with separate settings and plugins:
import { EmblemAuthProvider } from '@emblemvault/emblem-auth-react';
import { HustleProvider, HustleChat } from '@emblemvault/hustle-react';
function App() {
return (
<EmblemAuthProvider appId="your-app-id">
<HustleProvider instanceId="trading-bot">
<HustleChat />
</HustleProvider>
<HustleProvider instanceId="support-bot">
<HustleChat />
</HustleProvider>
</EmblemAuthProvider>
);
}Each instance has separate settings and plugins stored under its unique instanceId.
Speech-to-Text
Enable voice input in HustleChat via prop, settings modal, or use the hook directly for custom implementations.
Using HustleChat
Via prop:
<HustleChat enableSpeechToText />Via settings modal:
When showSettings is enabled, users can toggle "Voice Input" in the settings modal. This preference is persisted to localStorage.
<HustleChat showSettings />When enabled, a microphone button appears next to the send button. Click to start listening, click again to stop. The button pulses red while actively listening.
Using the Hook Directly
import { useSpeechRecognition } from '@emblemvault/hustle-react';
function CustomInput() {
const [message, setMessage] = useState('');
const {
isSupported, // Browser supports Web Speech API
isListening, // Currently listening
toggleListening,
} = useSpeechRecognition({
onTranscript: (text) => setMessage(prev => prev + ' ' + text),
onInterimTranscript: (text) => console.log('Interim:', text),
onError: (error) => console.error('Speech error:', error),
continuous: true, // Keep listening (default: true)
language: 'en-US', // Recognition language (default: 'en-US')
});
if (!isSupported) return <p>Speech not supported</p>;
return (
<div>
<input value={message} onChange={e => setMessage(e.target.value)} />
<button onClick={toggleListening}>
{isListening ? 'Stop' : 'Speak'}
</button>
</div>
);
}Underlying SDK
This package wraps hustle-incognito for React. The SDK handles:
- Chat API communication
- Streaming responses
- Tool execution
- Model selection
For advanced usage, access the client directly via useHustle().client.
License
MIT
