@glydeunity/voice-sdk 1.2.1 → 1.2.3
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/dist/voice-sdk.es.js +141 -32
- package/dist/voice-sdk.umd.js +74 -2
- package/package.json +1 -1
package/dist/voice-sdk.es.js
CHANGED
|
@@ -179,11 +179,11 @@ class g {
|
|
|
179
179
|
headers: this.getAuthHeaders()
|
|
180
180
|
});
|
|
181
181
|
if (!i.ok) {
|
|
182
|
-
const
|
|
183
|
-
throw new Error(
|
|
182
|
+
const o = await i.json();
|
|
183
|
+
throw new Error(o.error?.message || o.message || "Failed to fetch voice config");
|
|
184
184
|
}
|
|
185
|
-
const { data:
|
|
186
|
-
return
|
|
185
|
+
const { data: a } = await i.json();
|
|
186
|
+
return a;
|
|
187
187
|
}
|
|
188
188
|
/**
|
|
189
189
|
* Initialize and start the voice session
|
|
@@ -207,15 +207,77 @@ class g {
|
|
|
207
207
|
const s = await t.json();
|
|
208
208
|
throw new Error(s.error?.message || s.message || "Failed to authenticate voice session");
|
|
209
209
|
}
|
|
210
|
-
const { data: i } = await t.json(), { token:
|
|
210
|
+
const { data: i } = await t.json(), { token: a, agent_config: o, deepgram_config: r } = i, h = this.config.systemPrompt || o.instructions || this.serverConfig?.system_prompt || "You are a helpful AI assistant.";
|
|
211
211
|
await this.initializeAudio();
|
|
212
|
-
const
|
|
213
|
-
this.ws = new WebSocket(
|
|
212
|
+
const p = "wss://agent.deepgram.com/v1/agent/converse";
|
|
213
|
+
this.ws = new WebSocket(p, ["bearer", a]), this.ws.onopen = () => {
|
|
214
214
|
const s = this.config.deepgramConfig || r || this.serverConfig?.deepgram_config || {
|
|
215
|
-
think: {
|
|
215
|
+
think: {
|
|
216
|
+
provider: { type: "open_ai", model: "gpt-4.1-mini" },
|
|
217
|
+
functions: [
|
|
218
|
+
{
|
|
219
|
+
name: "end_conversation",
|
|
220
|
+
description: `You are an AI assistant that monitors conversations and ends them when specific stop phrases are detected.
|
|
221
|
+
|
|
222
|
+
Here is a list of phrases to listen for but not restricted to:
|
|
223
|
+
-stop
|
|
224
|
+
-shut up
|
|
225
|
+
-go away
|
|
226
|
+
-turn off
|
|
227
|
+
-stop listening
|
|
228
|
+
|
|
229
|
+
Before ending the conversation, always say a brief, polite goodbye such as "Goodbye!", "Take care!", or "Have a great day!".
|
|
230
|
+
|
|
231
|
+
When monitoring the conversation, pay close attention to any input that matches or closely resembles the phrases listed above. The matching should be case-insensitive and allow for minor variations or typos.
|
|
232
|
+
|
|
233
|
+
End the conversation immediately if:
|
|
234
|
+
1. The user's input exactly matches any phrase in the list.
|
|
235
|
+
2. The user's input is a close variation of any phrase in the list (e.g., "please shut up" instead of "shut up").
|
|
236
|
+
3. The user's input clearly expresses a desire to end the conversation, even if it doesn't use the exact phrases listed.`,
|
|
237
|
+
parameters: {
|
|
238
|
+
type: "object",
|
|
239
|
+
properties: {
|
|
240
|
+
item: { type: "string", description: "The phrase or text that triggered the end of conversation" }
|
|
241
|
+
},
|
|
242
|
+
required: ["item"]
|
|
243
|
+
}
|
|
244
|
+
},
|
|
245
|
+
{
|
|
246
|
+
name: "other_opportunities",
|
|
247
|
+
description: `You are an AI assistant that monitors conversations to identify whether the candidate should be informed about other job opportunities.
|
|
248
|
+
|
|
249
|
+
If the candidate appears to be a poor fit for any of the must have requirements, gently suggest that there are other job opportunities with the company and you could inform about other roles if they are interested. If they are not interested, you should continue with the conversation about the current role.
|
|
250
|
+
|
|
251
|
+
Here is a list of phrases to listen for but not restricted to:
|
|
252
|
+
-other opportunities
|
|
253
|
+
-other jobs
|
|
254
|
+
-other roles
|
|
255
|
+
-other job opportunities
|
|
256
|
+
-other job roles
|
|
257
|
+
-other job opportunities
|
|
258
|
+
|
|
259
|
+
When monitoring the conversation, pay close attention to any input that matches or closely resembles the phrases listed above. The matching should be case-insensitive and allow for minor variations or typos. Additionally monitor for input that suggests the candidate does not meet the criteria for the current role or if they'd like to know about urgent or immediate opportunities.
|
|
260
|
+
|
|
261
|
+
Suggest other opportunities if:
|
|
262
|
+
1. The user's input exactly matches any phrase in the list.
|
|
263
|
+
2. The user's input is a close variation of any phrase in the list (e.g., "please other opportunities" instead of "other opportunities").
|
|
264
|
+
3. The user's input clearly expresses a desire to end the conversation, even if it doesn't use the exact phrases listed.
|
|
265
|
+
4. The user's input clearly expresses a desire to know about urgent or immediate opportunities.
|
|
266
|
+
|
|
267
|
+
If the candidate is interested in other opportunities, you should call a GLYDE Unity MCP tool to identify other job openings.`,
|
|
268
|
+
parameters: {
|
|
269
|
+
type: "object",
|
|
270
|
+
properties: {
|
|
271
|
+
item: { type: "string", description: "The phrase or text that triggered the suggestion of other opportunities" }
|
|
272
|
+
},
|
|
273
|
+
required: ["item"]
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
]
|
|
277
|
+
},
|
|
216
278
|
speak: { provider: { type: "deepgram", model: "aura-2-thalia-en" } },
|
|
217
|
-
listen: { provider: { type: "deepgram",
|
|
218
|
-
},
|
|
279
|
+
listen: { provider: { type: "deepgram", version: "v2", model: "flux-general-en" } }
|
|
280
|
+
}, l = {
|
|
219
281
|
type: "Settings",
|
|
220
282
|
audio: {
|
|
221
283
|
input: {
|
|
@@ -237,36 +299,83 @@ class g {
|
|
|
237
299
|
provider: { type: "deepgram", version: "v2", model: "flux-general-en" }
|
|
238
300
|
},
|
|
239
301
|
think: {
|
|
240
|
-
provider: s.think?.provider || { type: "open_ai", model: "gpt-
|
|
302
|
+
provider: s.think?.provider || { type: "open_ai", model: "gpt-4.1-mini" },
|
|
241
303
|
functions: s.think?.functions || [
|
|
242
304
|
{
|
|
243
305
|
name: "end_conversation",
|
|
244
|
-
description:
|
|
306
|
+
description: `You are an AI assistant that monitors conversations and ends them when specific stop phrases are detected.
|
|
307
|
+
|
|
308
|
+
Here is a list of phrases to listen for but not restricted to:
|
|
309
|
+
-stop
|
|
310
|
+
-shut up
|
|
311
|
+
-go away
|
|
312
|
+
-turn off
|
|
313
|
+
-stop listening
|
|
314
|
+
|
|
315
|
+
Before ending the conversation, always say a brief, polite goodbye such as "Goodbye!", "Take care!", or "Have a great day!".
|
|
316
|
+
|
|
317
|
+
When monitoring the conversation, pay close attention to any input that matches or closely resembles the phrases listed above. The matching should be case-insensitive and allow for minor variations or typos.
|
|
318
|
+
|
|
319
|
+
End the conversation immediately if:
|
|
320
|
+
1. The user's input exactly matches any phrase in the list.
|
|
321
|
+
2. The user's input is a close variation of any phrase in the list (e.g., "please shut up" instead of "shut up").
|
|
322
|
+
3. The user's input clearly expresses a desire to end the conversation, even if it doesn't use the exact phrases listed.`,
|
|
323
|
+
parameters: {
|
|
324
|
+
type: "object",
|
|
325
|
+
properties: {
|
|
326
|
+
item: { type: "string", description: "The phrase or text that triggered the end of conversation" }
|
|
327
|
+
},
|
|
328
|
+
required: ["item"]
|
|
329
|
+
}
|
|
330
|
+
},
|
|
331
|
+
{
|
|
332
|
+
name: "other_opportunities",
|
|
333
|
+
description: `You are an AI assistant that monitors conversations to identify whether the candidate should be informed about other job opportunities.
|
|
334
|
+
|
|
335
|
+
If the candidate appears to be a poor fit for any of the must have requirements, gently suggest that there are other job opportunities with the company and you could inform about other roles if they are interested. If they are not interested, you should continue with the conversation about the current role.
|
|
336
|
+
|
|
337
|
+
Here is a list of phrases to listen for but not restricted to:
|
|
338
|
+
-other opportunities
|
|
339
|
+
-other jobs
|
|
340
|
+
-other roles
|
|
341
|
+
-other job opportunities
|
|
342
|
+
-other job roles
|
|
343
|
+
-other job opportunities
|
|
344
|
+
|
|
345
|
+
When monitoring the conversation, pay close attention to any input that matches or closely resembles the phrases listed above. The matching should be case-insensitive and allow for minor variations or typos. Additionally monitor for input that suggests the candidate does not meet the criteria for the current role or if they'd like to know about urgent or immediate opportunities.
|
|
346
|
+
|
|
347
|
+
Suggest other opportunities if:
|
|
348
|
+
1. The user's input exactly matches any phrase in the list.
|
|
349
|
+
2. The user's input is a close variation of any phrase in the list (e.g., "please other opportunities" instead of "other opportunities").
|
|
350
|
+
3. The user's input clearly expresses a desire to end the conversation, even if it doesn't use the exact phrases listed.
|
|
351
|
+
4. The user's input clearly expresses a desire to know about urgent or immediate opportunities.
|
|
352
|
+
|
|
353
|
+
If the candidate is interested in other opportunities, you should call a GLYDE Unity MCP tool to identify other job openings.`,
|
|
245
354
|
parameters: {
|
|
246
355
|
type: "object",
|
|
247
356
|
properties: {
|
|
248
|
-
item: { type: "string", description: "The phrase that triggered
|
|
357
|
+
item: { type: "string", description: "The phrase or text that triggered the suggestion of other opportunities" }
|
|
249
358
|
},
|
|
250
359
|
required: ["item"]
|
|
251
360
|
}
|
|
252
361
|
}
|
|
253
362
|
]
|
|
254
363
|
},
|
|
255
|
-
greeting: "Hi! I'm
|
|
364
|
+
greeting: "Hi! I'm excited you chose to speak with me. Are you ready to start?"
|
|
256
365
|
}
|
|
257
366
|
};
|
|
258
|
-
this.ws.send(JSON.stringify(
|
|
367
|
+
this.ws.send(JSON.stringify(l)), this.emit({ type: "open", payload: { config: o, serverConfig: this.serverConfig } });
|
|
259
368
|
};
|
|
260
|
-
const n =
|
|
369
|
+
const n = h;
|
|
261
370
|
this.ws.onmessage = (s) => {
|
|
262
371
|
if (typeof s.data == "string") {
|
|
263
372
|
try {
|
|
264
373
|
if (JSON.parse(s.data).type === "SettingsApplied") {
|
|
265
|
-
const
|
|
374
|
+
const c = {
|
|
266
375
|
type: "UpdatePrompt",
|
|
267
376
|
prompt: n
|
|
268
377
|
};
|
|
269
|
-
this.ws.send(JSON.stringify(
|
|
378
|
+
this.ws.send(JSON.stringify(c)), this.startMicrophone();
|
|
270
379
|
}
|
|
271
380
|
} catch {
|
|
272
381
|
}
|
|
@@ -308,8 +417,8 @@ class g {
|
|
|
308
417
|
URL.revokeObjectURL(e), URL.revokeObjectURL(t);
|
|
309
418
|
}
|
|
310
419
|
this.playbackWorkletNode = new AudioWorkletNode(this.audioContext, "audio-playback-processor"), this.playbackWorkletNode.connect(this.audioContext.destination), this.playbackWorkletNode.port.onmessage = (i) => {
|
|
311
|
-
const { type:
|
|
312
|
-
(
|
|
420
|
+
const { type: a } = i.data;
|
|
421
|
+
(a === "cleared" || a === "bufferEmpty") && (this.isAgentSpeaking = !1, this.agentAudioDoneReceived = !1, this.emit({ type: "agent_speaking", payload: !1 }));
|
|
313
422
|
};
|
|
314
423
|
}
|
|
315
424
|
/**
|
|
@@ -368,28 +477,28 @@ class g {
|
|
|
368
477
|
if (t === 0) return;
|
|
369
478
|
const i = t - t % 2;
|
|
370
479
|
if (i === 0) return;
|
|
371
|
-
const
|
|
372
|
-
for (let n = 0; n <
|
|
373
|
-
r[n] =
|
|
374
|
-
const
|
|
480
|
+
const a = i === t ? e : e.slice(0, i), o = new Int16Array(a), r = new Float32Array(o.length);
|
|
481
|
+
for (let n = 0; n < o.length; n++)
|
|
482
|
+
r[n] = o[n] / 32768;
|
|
483
|
+
const h = this.resample24kTo48k(r);
|
|
375
484
|
!this.isAgentSpeaking && !this.agentAudioDoneReceived && (this.isAgentSpeaking = !0, this.emit({ type: "agent_speaking", payload: !0 }));
|
|
376
|
-
const
|
|
485
|
+
const p = new Float32Array(h);
|
|
377
486
|
this.playbackWorkletNode.port.postMessage({
|
|
378
487
|
type: "audio",
|
|
379
|
-
data:
|
|
380
|
-
}, [
|
|
488
|
+
data: p
|
|
489
|
+
}, [p.buffer]);
|
|
381
490
|
}
|
|
382
491
|
/**
|
|
383
492
|
* Resample audio from 24kHz to 48kHz using linear interpolation
|
|
384
493
|
*/
|
|
385
494
|
resample24kTo48k(e) {
|
|
386
495
|
const t = e.length * 2, i = new Float32Array(t);
|
|
387
|
-
for (let
|
|
388
|
-
const r = e[
|
|
389
|
-
i[
|
|
496
|
+
for (let o = 0; o < e.length - 1; o++) {
|
|
497
|
+
const r = e[o], h = e[o + 1];
|
|
498
|
+
i[o * 2] = r, i[o * 2 + 1] = (r + h) / 2;
|
|
390
499
|
}
|
|
391
|
-
const
|
|
392
|
-
return i[
|
|
500
|
+
const a = e.length - 1;
|
|
501
|
+
return i[a * 2] = e[a], i[a * 2 + 1] = e[a], i;
|
|
393
502
|
}
|
|
394
503
|
/**
|
|
395
504
|
* Clear the playback buffer (for interruption handling)
|
package/dist/voice-sdk.umd.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
(function(n,
|
|
1
|
+
(function(n,l){typeof exports=="object"&&typeof module<"u"?l(exports):typeof define=="function"&&define.amd?define(["exports"],l):(n=typeof globalThis<"u"?globalThis:n||self,l(n.GlydeVoice={}))})(this,(function(n){"use strict";const l=`
|
|
2
2
|
class AudioCaptureProcessor extends AudioWorkletProcessor {
|
|
3
3
|
constructor() {
|
|
4
4
|
super();
|
|
@@ -130,7 +130,79 @@ class AudioPlaybackProcessor extends AudioWorkletProcessor {
|
|
|
130
130
|
}
|
|
131
131
|
|
|
132
132
|
registerProcessor('audio-playback-processor', AudioPlaybackProcessor);
|
|
133
|
-
`;class f{config;unityUrl;active=!1;serverConfig=null;ws=null;audioContext=null;mediaStream=null;captureWorkletNode=null;playbackWorkletNode=null;isMuted=!1;outputSampleRate=24e3;inputSampleRate=48e3;isAgentSpeaking=!1;agentAudioDoneReceived=!1;constructor(e){this.config=e,this.unityUrl=e.unityBaseUrl||"https://api.glydeunity.com",!e.publishableKey&&!e.apiKey&&!e.authToken&&console.warn("[GlydeVoice] No authentication method provided. One of publishableKey, apiKey, or authToken is required.")}getAuthHeaders(){const e={"Content-Type":"application/json"};return this.config.publishableKey&&(e["x-publishable-key"]=this.config.publishableKey),this.config.apiKey&&(e["x-api-key"]=this.config.apiKey),this.config.authToken&&(e.Authorization=`Bearer ${this.config.authToken}`),e}async fetchConfig(){const e=`${this.unityUrl}/api/unity/voice/config/${this.config.contextType}`,t=this.config.contextId?`${e}/${this.config.contextId}`:e,i=await fetch(t,{method:"GET",headers:this.getAuthHeaders()});if(!i.ok){const o=await i.json();throw new Error(o.error?.message||o.message||"Failed to fetch voice config")}const{data:a}=await i.json();return a}async start(){if(!this.active){this.active=!0;try{this.config.systemPrompt||(this.serverConfig=await this.fetchConfig(),console.log("[GlydeVoice] Fetched config:",this.serverConfig));const e={context_id:this.config.contextId,domain:typeof window<"u"?window.location.hostname:"localhost"};this.config.systemPrompt&&(e.system_prompt=this.config.systemPrompt),this.config.deepgramConfig&&(e.deepgram_config=this.config.deepgramConfig);const t=await fetch(`${this.unityUrl}/api/unity/voice/auth`,{method:"POST",headers:this.getAuthHeaders(),body:JSON.stringify(e)});if(!t.ok){const s=await t.json();throw new Error(s.error?.message||s.message||"Failed to authenticate voice session")}const{data:i}=await t.json(),{token:a,agent_config:o,deepgram_config:r}=i,
|
|
133
|
+
`;class f{config;unityUrl;active=!1;serverConfig=null;ws=null;audioContext=null;mediaStream=null;captureWorkletNode=null;playbackWorkletNode=null;isMuted=!1;outputSampleRate=24e3;inputSampleRate=48e3;isAgentSpeaking=!1;agentAudioDoneReceived=!1;constructor(e){this.config=e,this.unityUrl=e.unityBaseUrl||"https://api.glydeunity.com",!e.publishableKey&&!e.apiKey&&!e.authToken&&console.warn("[GlydeVoice] No authentication method provided. One of publishableKey, apiKey, or authToken is required.")}getAuthHeaders(){const e={"Content-Type":"application/json"};return this.config.publishableKey&&(e["x-publishable-key"]=this.config.publishableKey),this.config.apiKey&&(e["x-api-key"]=this.config.apiKey),this.config.authToken&&(e.Authorization=`Bearer ${this.config.authToken}`),e}async fetchConfig(){const e=`${this.unityUrl}/api/unity/voice/config/${this.config.contextType}`,t=this.config.contextId?`${e}/${this.config.contextId}`:e,i=await fetch(t,{method:"GET",headers:this.getAuthHeaders()});if(!i.ok){const o=await i.json();throw new Error(o.error?.message||o.message||"Failed to fetch voice config")}const{data:a}=await i.json();return a}async start(){if(!this.active){this.active=!0;try{this.config.systemPrompt||(this.serverConfig=await this.fetchConfig(),console.log("[GlydeVoice] Fetched config:",this.serverConfig));const e={context_id:this.config.contextId,domain:typeof window<"u"?window.location.hostname:"localhost"};this.config.systemPrompt&&(e.system_prompt=this.config.systemPrompt),this.config.deepgramConfig&&(e.deepgram_config=this.config.deepgramConfig);const t=await fetch(`${this.unityUrl}/api/unity/voice/auth`,{method:"POST",headers:this.getAuthHeaders(),body:JSON.stringify(e)});if(!t.ok){const s=await t.json();throw new Error(s.error?.message||s.message||"Failed to authenticate voice session")}const{data:i}=await t.json(),{token:a,agent_config:o,deepgram_config:r}=i,p=this.config.systemPrompt||o.instructions||this.serverConfig?.system_prompt||"You are a helpful AI assistant.";await this.initializeAudio();const c="wss://agent.deepgram.com/v1/agent/converse";this.ws=new WebSocket(c,["bearer",a]),this.ws.onopen=()=>{const s=this.config.deepgramConfig||r||this.serverConfig?.deepgram_config||{think:{provider:{type:"open_ai",model:"gpt-4.1-mini"},functions:[{name:"end_conversation",description:`You are an AI assistant that monitors conversations and ends them when specific stop phrases are detected.
|
|
134
|
+
|
|
135
|
+
Here is a list of phrases to listen for but not restricted to:
|
|
136
|
+
-stop
|
|
137
|
+
-shut up
|
|
138
|
+
-go away
|
|
139
|
+
-turn off
|
|
140
|
+
-stop listening
|
|
141
|
+
|
|
142
|
+
Before ending the conversation, always say a brief, polite goodbye such as "Goodbye!", "Take care!", or "Have a great day!".
|
|
143
|
+
|
|
144
|
+
When monitoring the conversation, pay close attention to any input that matches or closely resembles the phrases listed above. The matching should be case-insensitive and allow for minor variations or typos.
|
|
145
|
+
|
|
146
|
+
End the conversation immediately if:
|
|
147
|
+
1. The user's input exactly matches any phrase in the list.
|
|
148
|
+
2. The user's input is a close variation of any phrase in the list (e.g., "please shut up" instead of "shut up").
|
|
149
|
+
3. The user's input clearly expresses a desire to end the conversation, even if it doesn't use the exact phrases listed.`,parameters:{type:"object",properties:{item:{type:"string",description:"The phrase or text that triggered the end of conversation"}},required:["item"]}},{name:"other_opportunities",description:`You are an AI assistant that monitors conversations to identify whether the candidate should be informed about other job opportunities.
|
|
150
|
+
|
|
151
|
+
If the candidate appears to be a poor fit for any of the must have requirements, gently suggest that there are other job opportunities with the company and you could inform about other roles if they are interested. If they are not interested, you should continue with the conversation about the current role.
|
|
152
|
+
|
|
153
|
+
Here is a list of phrases to listen for but not restricted to:
|
|
154
|
+
-other opportunities
|
|
155
|
+
-other jobs
|
|
156
|
+
-other roles
|
|
157
|
+
-other job opportunities
|
|
158
|
+
-other job roles
|
|
159
|
+
-other job opportunities
|
|
160
|
+
|
|
161
|
+
When monitoring the conversation, pay close attention to any input that matches or closely resembles the phrases listed above. The matching should be case-insensitive and allow for minor variations or typos. Additionally monitor for input that suggests the candidate does not meet the criteria for the current role or if they'd like to know about urgent or immediate opportunities.
|
|
162
|
+
|
|
163
|
+
Suggest other opportunities if:
|
|
164
|
+
1. The user's input exactly matches any phrase in the list.
|
|
165
|
+
2. The user's input is a close variation of any phrase in the list (e.g., "please other opportunities" instead of "other opportunities").
|
|
166
|
+
3. The user's input clearly expresses a desire to end the conversation, even if it doesn't use the exact phrases listed.
|
|
167
|
+
4. The user's input clearly expresses a desire to know about urgent or immediate opportunities.
|
|
168
|
+
|
|
169
|
+
If the candidate is interested in other opportunities, you should call a GLYDE Unity MCP tool to identify other job openings.`,parameters:{type:"object",properties:{item:{type:"string",description:"The phrase or text that triggered the suggestion of other opportunities"}},required:["item"]}}]},speak:{provider:{type:"deepgram",model:"aura-2-thalia-en"}},listen:{provider:{type:"deepgram",version:"v2",model:"flux-general-en"}}},d={type:"Settings",audio:{input:{encoding:"linear16",sample_rate:this.inputSampleRate},output:{encoding:"linear16",sample_rate:this.outputSampleRate,container:"none"}},agent:{language:"en",speak:s.speak||{provider:{type:"deepgram",model:"aura-2-thalia-en"}},listen:s.listen||{provider:{type:"deepgram",version:"v2",model:"flux-general-en"}},think:{provider:s.think?.provider||{type:"open_ai",model:"gpt-4.1-mini"},functions:s.think?.functions||[{name:"end_conversation",description:`You are an AI assistant that monitors conversations and ends them when specific stop phrases are detected.
|
|
170
|
+
|
|
171
|
+
Here is a list of phrases to listen for but not restricted to:
|
|
172
|
+
-stop
|
|
173
|
+
-shut up
|
|
174
|
+
-go away
|
|
175
|
+
-turn off
|
|
176
|
+
-stop listening
|
|
177
|
+
|
|
178
|
+
Before ending the conversation, always say a brief, polite goodbye such as "Goodbye!", "Take care!", or "Have a great day!".
|
|
179
|
+
|
|
180
|
+
When monitoring the conversation, pay close attention to any input that matches or closely resembles the phrases listed above. The matching should be case-insensitive and allow for minor variations or typos.
|
|
181
|
+
|
|
182
|
+
End the conversation immediately if:
|
|
183
|
+
1. The user's input exactly matches any phrase in the list.
|
|
184
|
+
2. The user's input is a close variation of any phrase in the list (e.g., "please shut up" instead of "shut up").
|
|
185
|
+
3. The user's input clearly expresses a desire to end the conversation, even if it doesn't use the exact phrases listed.`,parameters:{type:"object",properties:{item:{type:"string",description:"The phrase or text that triggered the end of conversation"}},required:["item"]}},{name:"other_opportunities",description:`You are an AI assistant that monitors conversations to identify whether the candidate should be informed about other job opportunities.
|
|
186
|
+
|
|
187
|
+
If the candidate appears to be a poor fit for any of the must have requirements, gently suggest that there are other job opportunities with the company and you could inform about other roles if they are interested. If they are not interested, you should continue with the conversation about the current role.
|
|
188
|
+
|
|
189
|
+
Here is a list of phrases to listen for but not restricted to:
|
|
190
|
+
-other opportunities
|
|
191
|
+
-other jobs
|
|
192
|
+
-other roles
|
|
193
|
+
-other job opportunities
|
|
194
|
+
-other job roles
|
|
195
|
+
-other job opportunities
|
|
196
|
+
|
|
197
|
+
When monitoring the conversation, pay close attention to any input that matches or closely resembles the phrases listed above. The matching should be case-insensitive and allow for minor variations or typos. Additionally monitor for input that suggests the candidate does not meet the criteria for the current role or if they'd like to know about urgent or immediate opportunities.
|
|
198
|
+
|
|
199
|
+
Suggest other opportunities if:
|
|
200
|
+
1. The user's input exactly matches any phrase in the list.
|
|
201
|
+
2. The user's input is a close variation of any phrase in the list (e.g., "please other opportunities" instead of "other opportunities").
|
|
202
|
+
3. The user's input clearly expresses a desire to end the conversation, even if it doesn't use the exact phrases listed.
|
|
203
|
+
4. The user's input clearly expresses a desire to know about urgent or immediate opportunities.
|
|
204
|
+
|
|
205
|
+
If the candidate is interested in other opportunities, you should call a GLYDE Unity MCP tool to identify other job openings.`,parameters:{type:"object",properties:{item:{type:"string",description:"The phrase or text that triggered the suggestion of other opportunities"}},required:["item"]}}]},greeting:"Hi! I'm excited you chose to speak with me. Are you ready to start?"}};this.ws.send(JSON.stringify(d)),this.emit({type:"open",payload:{config:o,serverConfig:this.serverConfig}})};const h=p;this.ws.onmessage=s=>{if(typeof s.data=="string"){try{if(JSON.parse(s.data).type==="SettingsApplied"){const y={type:"UpdatePrompt",prompt:h};this.ws.send(JSON.stringify(y)),this.startMicrophone()}}catch{}this.handleTextMessage(s.data)}else s.data instanceof Blob?this.handleAudioData(s.data):s.data instanceof ArrayBuffer&&this.handleAudioBuffer(s.data)},this.ws.onerror=s=>{console.error("[GlydeVoice] WebSocket error:",s),this.emit({type:"error",payload:s})},this.ws.onclose=()=>{this.cleanup(),this.emit({type:"close"})},this.renderUI()}catch(e){throw console.error("[GlydeVoice] Error starting session:",e),this.active=!1,this.emit({type:"error",payload:e}),e}}}createWorkletBlobUrl(e){const t=new Blob([e],{type:"application/javascript"});return URL.createObjectURL(t)}async initializeAudio(){this.audioContext=new AudioContext({sampleRate:this.inputSampleRate});const e=this.createWorkletBlobUrl(l),t=this.createWorkletBlobUrl(u);try{await Promise.all([this.audioContext.audioWorklet.addModule(e),this.audioContext.audioWorklet.addModule(t)])}finally{URL.revokeObjectURL(e),URL.revokeObjectURL(t)}this.playbackWorkletNode=new AudioWorkletNode(this.audioContext,"audio-playback-processor"),this.playbackWorkletNode.connect(this.audioContext.destination),this.playbackWorkletNode.port.onmessage=i=>{const{type:a}=i.data;(a==="cleared"||a==="bufferEmpty")&&(this.isAgentSpeaking=!1,this.agentAudioDoneReceived=!1,this.emit({type:"agent_speaking",payload:!1}))}}handleTextMessage(e){try{const t=JSON.parse(e);switch(t.type){case"Welcome":this.emit({type:"ready"});break;case"SettingsApplied":break;case"UserStartedSpeaking":this.emit({type:"user_speaking",payload:!0}),this.clearPlaybackBuffer(),this.isAgentSpeaking=!1,this.agentAudioDoneReceived=!1;break;case"UserStoppedSpeaking":this.emit({type:"user_speaking",payload:!1});break;case"ConversationText":if(t.content&&t.content.trim()){const i=t.role==="assistant"?"agent":"user";this.config.onTranscript&&this.config.onTranscript(t.content,i),this.emit({type:"transcript",payload:{text:t.content,role:i}}),this.saveTranscript(t.content,t.role)}break;case"AgentStartedSpeaking":this.isAgentSpeaking=!0,this.agentAudioDoneReceived=!1,this.emit({type:"agent_speaking",payload:!0});break;case"AgentAudioDone":this.agentAudioDoneReceived=!0;break;case"Error":console.error("[GlydeVoice] Agent error:",t),this.emit({type:"error",payload:t});break}}catch(t){console.error("[GlydeVoice] Failed to parse message:",t)}}async handleAudioData(e){const t=await e.arrayBuffer();this.handleAudioBuffer(t)}handleAudioBuffer(e){if(!this.playbackWorkletNode||!this.audioContext)return;this.audioContext.state==="suspended"&&this.audioContext.resume();const t=e.byteLength;if(t===0)return;const i=t-t%2;if(i===0)return;const a=i===t?e:e.slice(0,i),o=new Int16Array(a),r=new Float32Array(o.length);for(let h=0;h<o.length;h++)r[h]=o[h]/32768;const p=this.resample24kTo48k(r);!this.isAgentSpeaking&&!this.agentAudioDoneReceived&&(this.isAgentSpeaking=!0,this.emit({type:"agent_speaking",payload:!0}));const c=new Float32Array(p);this.playbackWorkletNode.port.postMessage({type:"audio",data:c},[c.buffer])}resample24kTo48k(e){const t=e.length*2,i=new Float32Array(t);for(let o=0;o<e.length-1;o++){const r=e[o],p=e[o+1];i[o*2]=r,i[o*2+1]=(r+p)/2}const a=e.length-1;return i[a*2]=e[a],i[a*2+1]=e[a],i}clearPlaybackBuffer(){this.playbackWorkletNode&&this.playbackWorkletNode.port.postMessage({type:"clear"})}async startMicrophone(){if(!this.audioContext)throw new Error("Audio context not initialized");try{this.mediaStream=await navigator.mediaDevices.getUserMedia({audio:{channelCount:1,sampleRate:this.inputSampleRate,echoCancellation:!0,noiseSuppression:!0}});const e=this.audioContext.createMediaStreamSource(this.mediaStream);this.captureWorkletNode=new AudioWorkletNode(this.audioContext,"audio-capture-processor"),this.captureWorkletNode.port.onmessage=t=>{!this.active||!this.ws||this.ws.readyState!==WebSocket.OPEN||this.isMuted||this.ws.send(t.data)},e.connect(this.captureWorkletNode),this.emit({type:"microphone_ready"})}catch(e){throw console.error("[GlydeVoice] Microphone error:",e),e}}async saveTranscript(e,t){if(!(!this.config.contextId||!e))try{await fetch(`${this.unityUrl}/api/unity/voice/transcript`,{method:"POST",headers:this.getAuthHeaders(),body:JSON.stringify({context_id:this.config.contextId,content:e,role:t==="assistant"?"assistant":"user"})})}catch{}}setMuted(e){this.isMuted=e}getMuted(){return this.isMuted}isActive(){return this.active}getServerConfig(){return this.serverConfig}stop(){this.active=!1,this.cleanup()}cleanup(){this.captureWorkletNode&&(this.captureWorkletNode.disconnect(),this.captureWorkletNode.port.close(),this.captureWorkletNode=null),this.playbackWorkletNode&&(this.playbackWorkletNode.disconnect(),this.playbackWorkletNode.port.close(),this.playbackWorkletNode=null),this.mediaStream&&(this.mediaStream.getTracks().forEach(e=>e.stop()),this.mediaStream=null),this.audioContext&&(this.audioContext.close(),this.audioContext=null),this.ws&&(this.ws.readyState===WebSocket.OPEN&&this.ws.close(),this.ws=null)}emit(e){this.config.onEvent&&this.config.onEvent(e)}renderUI(){if(!this.config.container)return;const e=typeof this.config.container=="string"?document.querySelector(this.config.container):this.config.container;e&&(e.innerHTML=`
|
|
134
206
|
<div style="padding: 20px; border: 1px solid #ccc; border-radius: 8px; background: #fff;">
|
|
135
207
|
<h3>Glyde Voice Agent</h3>
|
|
136
208
|
<p>Status: Active</p>
|