@makemore/agent-frontend 1.5.0 → 1.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -131,6 +131,9 @@ See `django-tts-example.py` for the complete Django backend implementation.
131
131
  anonymousSession: '/api/auth/session/',
132
132
  runs: '/api/chat/runs/',
133
133
  runEvents: '/api/chat/runs/{runId}/events/',
134
+ simulateCustomer: '/api/chat/simulate-customer/',
135
+ ttsVoices: '/api/tts/voices/', // For voice settings UI (proxy mode)
136
+ ttsSetVoice: '/api/tts/set-voice/', // For voice settings UI (proxy mode)
134
137
  },
135
138
  });
136
139
  </script>
@@ -169,7 +172,7 @@ See `django-tts-example.py` for the complete Django backend implementation.
169
172
  | `showClearButton` | boolean | `true` | Show clear conversation button in header |
170
173
  | `showDebugButton` | boolean | `true` | Show debug mode toggle button in header |
171
174
  | `showTTSButton` | boolean | `true` | Show TTS toggle button in header |
172
- | `showVoiceSettings` | boolean | `true` | Show voice settings button in header (direct API only) |
175
+ | `showVoiceSettings` | boolean | `true` | Show voice settings button in header (works with proxy and direct API) |
173
176
  | `showExpandButton` | boolean | `true` | Show expand/minimize button in header |
174
177
 
175
178
  ### Text-to-Speech (ElevenLabs)
@@ -201,8 +204,66 @@ ELEVENLABS_VOICES = {
201
204
  'user': 'pNInz6obpgDQGcFmaJgB', # Adam
202
205
  }
203
206
  ```
204
- 3. Add view from `django-tts-example.py` to your Django app
205
- 4. Add URL route: `path('api/tts/speak/', views.text_to_speech)`
207
+ 3. Add views from `django-tts-example.py` to your Django app
208
+ 4. Add URL routes:
209
+ ```python
210
+ path('api/tts/speak/', views.text_to_speech),
211
+ path('api/tts/voices/', views.get_voices), # For voice settings UI
212
+ path('api/tts/set-voice/', views.set_voice), # For voice settings UI
213
+ ```
214
+
215
+ **Voice Settings Support:**
216
+
217
+ The widget now supports voice settings UI in proxy mode! Add these endpoints to enable the voice picker:
218
+
219
+ ```python
220
+ # Get available voices
221
+ @api_view(['GET'])
222
+ def get_voices(request):
223
+ """Fetch available voices from ElevenLabs"""
224
+ try:
225
+ response = requests.get(
226
+ 'https://api.elevenlabs.io/v1/voices',
227
+ headers={'xi-api-key': settings.ELEVENLABS_API_KEY}
228
+ )
229
+ return JsonResponse(response.json())
230
+ except Exception as e:
231
+ return JsonResponse({'error': str(e)}, status=500)
232
+
233
+ # Set voice for user session
234
+ @api_view(['POST'])
235
+ def set_voice(request):
236
+ """Update voice preference for user's session"""
237
+ role = request.data.get('role') # 'assistant' or 'user'
238
+ voice_id = request.data.get('voice_id')
239
+
240
+ # Store in session or database
241
+ if not hasattr(request, 'session'):
242
+ return JsonResponse({'error': 'Session not available'}, status=400)
243
+
244
+ if role not in ['assistant', 'user']:
245
+ return JsonResponse({'error': 'Invalid role'}, status=400)
246
+
247
+ # Store voice preference in session
248
+ if 'tts_voices' not in request.session:
249
+ request.session['tts_voices'] = {}
250
+ request.session['tts_voices'][role] = voice_id
251
+ request.session.modified = True
252
+
253
+ return JsonResponse({'success': True, 'role': role, 'voice_id': voice_id})
254
+
255
+ # Update text_to_speech view to use session voices
256
+ @api_view(['POST'])
257
+ def text_to_speech(request):
258
+ text = request.data.get('text', '')
259
+ role = request.data.get('role', 'assistant')
260
+
261
+ # Get voice from session or fall back to settings
262
+ session_voices = request.session.get('tts_voices', {})
263
+ voice_id = session_voices.get(role) or settings.ELEVENLABS_VOICES.get(role)
264
+
265
+ # ... rest of TTS implementation
266
+ ```
206
267
 
207
268
  #### Option 2: Direct API (Client-Side)
208
269
 
@@ -249,10 +310,12 @@ ChatWidget.setVoice('user', 'voice_id'); // Change user voice
249
310
 
250
311
  **Voice Settings UI:**
251
312
 
252
- When using direct API mode (not proxy), a voice settings button (🎙️) appears in the header. Click it to:
313
+ A voice settings button (🎙️) appears in the header when TTS is enabled. Click it to:
253
314
  - Select assistant voice from dropdown
254
315
  - Select customer voice for demo mode
255
- - Voices are automatically fetched from your ElevenLabs account
316
+ - Voices are automatically fetched from your ElevenLabs account (direct API) or Django backend (proxy mode)
317
+
318
+ **Works with both proxy and direct API modes!** Just implement the `/api/tts/voices/` and `/api/tts/set-voice/` endpoints in your Django backend (see above).
256
319
 
257
320
  **Customize Header Buttons:**
258
321
  ```javascript
@@ -42,6 +42,8 @@
42
42
  runs: '/api/agent-runtime/runs/',
43
43
  runEvents: '/api/agent-runtime/runs/{runId}/events/',
44
44
  simulateCustomer: '/api/agent-runtime/simulate-customer/',
45
+ ttsVoices: '/api/tts/voices/', // For fetching available voices (proxy mode)
46
+ ttsSetVoice: '/api/tts/set-voice/', // For setting voice (proxy mode)
45
47
  },
46
48
  // Demo flow control
47
49
  autoRunDelay: 1000, // Delay in ms before auto-generating next message
@@ -298,25 +300,69 @@
298
300
  render();
299
301
  }
300
302
 
301
- function setVoice(role, voiceId) {
303
+ async function setVoice(role, voiceId) {
302
304
  config.ttsVoices[role] = voiceId;
305
+
306
+ // If using proxy, notify backend of voice change
307
+ if (config.ttsProxyUrl) {
308
+ try {
309
+ const token = await getOrCreateSession();
310
+ const headers = {
311
+ 'Content-Type': 'application/json',
312
+ };
313
+ if (token) {
314
+ headers[config.anonymousTokenHeader] = token;
315
+ }
316
+
317
+ await fetch(`${config.backendUrl}${config.apiPaths.ttsSetVoice}`, {
318
+ method: 'POST',
319
+ headers,
320
+ body: JSON.stringify({ role, voice_id: voiceId }),
321
+ });
322
+ } catch (err) {
323
+ console.error('[ChatWidget] Failed to set voice on backend:', err);
324
+ }
325
+ }
326
+
303
327
  render();
304
328
  }
305
329
 
306
330
  async function fetchAvailableVoices() {
307
- if (!config.elevenLabsApiKey) return;
308
-
309
331
  try {
310
- const response = await fetch('https://api.elevenlabs.io/v1/voices', {
311
- headers: {
312
- 'xi-api-key': config.elevenLabsApiKey,
313
- },
314
- });
332
+ let voices = [];
315
333
 
316
- if (response.ok) {
317
- const data = await response.json();
318
- config.availableVoices = data.voices || [];
334
+ if (config.ttsProxyUrl) {
335
+ // Fetch voices from Django backend
336
+ const token = await getOrCreateSession();
337
+ const headers = {};
338
+ if (token) {
339
+ headers[config.anonymousTokenHeader] = token;
340
+ }
341
+
342
+ const response = await fetch(`${config.backendUrl}${config.apiPaths.ttsVoices}`, {
343
+ headers,
344
+ });
345
+
346
+ if (response.ok) {
347
+ const data = await response.json();
348
+ voices = data.voices || [];
349
+ }
350
+ } else if (config.elevenLabsApiKey) {
351
+ // Fetch voices directly from ElevenLabs
352
+ const response = await fetch('https://api.elevenlabs.io/v1/voices', {
353
+ headers: {
354
+ 'xi-api-key': config.elevenLabsApiKey,
355
+ },
356
+ });
357
+
358
+ if (response.ok) {
359
+ const data = await response.json();
360
+ voices = data.voices || [];
361
+ }
319
362
  }
363
+
364
+ config.availableVoices = voices;
365
+ render(); // Re-render to update dropdowns
320
366
  } catch (err) {
321
367
  console.error('[ChatWidget] Failed to fetch voices:', err);
322
368
  }
@@ -922,7 +968,7 @@
922
968
  ${state.isSpeaking ? '🔊' : (config.enableTTS ? '🔉' : '🔇')}
923
969
  </button>
924
970
  ` : ''}
925
- ${config.showVoiceSettings && config.elevenLabsApiKey && !config.ttsProxyUrl ? `
971
+ ${config.showVoiceSettings && (config.elevenLabsApiKey || config.ttsProxyUrl) ? `
926
972
  <button class="cw-header-btn ${state.voiceSettingsOpen ? 'cw-btn-active' : ''}" data-action="toggle-voice-settings" title="Voice Settings">
927
973
  🎙️
928
974
  </button>
@@ -1055,8 +1101,8 @@
1055
1101
  // Initial render
1056
1102
  render();
1057
1103
 
1058
- // Fetch available voices if using direct API
1059
- if (config.elevenLabsApiKey && !config.ttsProxyUrl) {
1104
+ // Fetch available voices if TTS is configured
1105
+ if (config.elevenLabsApiKey || config.ttsProxyUrl) {
1060
1106
  fetchAvailableVoices();
1061
1107
  }
1062
1108
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@makemore/agent-frontend",
3
- "version": "1.5.0",
3
+ "version": "1.6.0",
4
4
  "description": "A standalone, zero-dependency chat widget for AI agents. Embed conversational AI into any website with a single script tag.",
5
5
  "main": "dist/chat-widget.js",
6
6
  "files": [