@myscheme/voice-navigation-sdk 0.1.6 → 0.1.8
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 +13 -5
- package/dist/actions.d.ts.map +1 -1
- package/dist/actions.js +172 -32
- package/dist/microphone-handler.d.ts +7 -0
- package/dist/microphone-handler.d.ts.map +1 -1
- package/dist/microphone-handler.js +106 -22
- package/dist/navigation-controller.d.ts +9 -1
- package/dist/navigation-controller.d.ts.map +1 -1
- package/dist/navigation-controller.js +205 -6
- package/dist/server/azure-speech-handler.d.ts +1 -0
- package/dist/server/azure-speech-handler.d.ts.map +1 -1
- package/dist/server/azure-speech-handler.js +55 -16
- package/dist/server/bedrock-embedding-handler.d.ts +1 -0
- package/dist/server/bedrock-embedding-handler.d.ts.map +1 -1
- package/dist/server/bedrock-embedding-handler.js +84 -24
- package/dist/server/bedrock-handler.d.ts +1 -0
- package/dist/server/bedrock-handler.d.ts.map +1 -1
- package/dist/server/bedrock-handler.js +79 -22
- package/dist/server/index.d.ts +3 -0
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +3 -0
- package/dist/services/bedrock.d.ts +3 -0
- package/dist/services/bedrock.d.ts.map +1 -1
- package/dist/services/bedrock.js +135 -1
- package/dist/services/voice-feedback.d.ts +46 -0
- package/dist/services/voice-feedback.d.ts.map +1 -0
- package/dist/services/voice-feedback.js +332 -0
- package/dist/types.d.ts +4 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/ui.d.ts.map +1 -1
- package/dist/ui.js +32 -7
- package/package.json +1 -1
|
@@ -2,6 +2,7 @@ import { MicrophoneHandler } from "./microphone-handler.js";
|
|
|
2
2
|
import { AzureSpeechService } from "./services/azure-speech.js";
|
|
3
3
|
import { BedrockService } from "./services/bedrock.js";
|
|
4
4
|
import { PageRegistry } from "./services/page-registry.js";
|
|
5
|
+
import { VoiceFeedbackService } from "./services/voice-feedback.js";
|
|
5
6
|
import { loadNavigationPages } from "./services/xml-parser.js";
|
|
6
7
|
import { VectorSearchService, } from "./services/vector-search.js";
|
|
7
8
|
import { performAgentAction, formatActionLabel, extractAgentAction, setPageRegistry, } from "./actions.js";
|
|
@@ -37,6 +38,7 @@ export class VoiceNavigationController {
|
|
|
37
38
|
this.autoStartPendingUserInteraction = false;
|
|
38
39
|
this.removeAutoStartListeners = null;
|
|
39
40
|
this.vectorSearchService = null;
|
|
41
|
+
this.isSyncingButtonState = false;
|
|
40
42
|
const openSearchConfig = resolveOpenSearchConfig(config);
|
|
41
43
|
this.config = {
|
|
42
44
|
...config,
|
|
@@ -99,6 +101,15 @@ export class VoiceNavigationController {
|
|
|
99
101
|
bedrockService: this.bedrockService,
|
|
100
102
|
language: this.config.language || "en-IN",
|
|
101
103
|
});
|
|
104
|
+
const voiceFeedbackEnabled = this.config.voiceFeedback?.enabled !== false;
|
|
105
|
+
console.log("[VoiceNavigation] Initializing voice feedback service - enabled:", voiceFeedbackEnabled);
|
|
106
|
+
this.voiceFeedbackService = new VoiceFeedbackService({
|
|
107
|
+
enabled: voiceFeedbackEnabled,
|
|
108
|
+
language: this.config.language || "en-IN",
|
|
109
|
+
azureSpeechService: this.azureSpeechService,
|
|
110
|
+
bedrockService: this.bedrockService,
|
|
111
|
+
});
|
|
112
|
+
console.log("[VoiceNavigation] Voice feedback service initialized successfully");
|
|
102
113
|
if (openSearchConfig) {
|
|
103
114
|
try {
|
|
104
115
|
this.vectorSearchService = new VectorSearchService(openSearchConfig);
|
|
@@ -225,13 +236,17 @@ export class VoiceNavigationController {
|
|
|
225
236
|
});
|
|
226
237
|
}
|
|
227
238
|
async toggleRecording() {
|
|
239
|
+
if (this.isSyncingButtonState) {
|
|
240
|
+
console.log("[VoiceNavigation] Skipping toggle - syncing button state");
|
|
241
|
+
return;
|
|
242
|
+
}
|
|
228
243
|
if (!this.ui || !this.ui.root) {
|
|
229
244
|
console.warn("UI not initialized, cannot toggle recording");
|
|
230
245
|
return;
|
|
231
246
|
}
|
|
232
247
|
const currentState = this.ui.root.dataset.state;
|
|
233
248
|
if (currentState === "listening" || currentState === "thinking") {
|
|
234
|
-
await this.stop();
|
|
249
|
+
await this.stop({ triggeredByUser: true });
|
|
235
250
|
return;
|
|
236
251
|
}
|
|
237
252
|
if (currentState === "loading") {
|
|
@@ -276,7 +291,9 @@ export class VoiceNavigationController {
|
|
|
276
291
|
}
|
|
277
292
|
catch (error) {
|
|
278
293
|
setState(this.ui, "idle");
|
|
279
|
-
|
|
294
|
+
if (!this.ui.isCustomButton) {
|
|
295
|
+
updateStatus(this.ui, "Tap to enable voice navigation");
|
|
296
|
+
}
|
|
280
297
|
return;
|
|
281
298
|
}
|
|
282
299
|
}
|
|
@@ -308,7 +325,8 @@ export class VoiceNavigationController {
|
|
|
308
325
|
emitEvent("error", { error });
|
|
309
326
|
}
|
|
310
327
|
}
|
|
311
|
-
async stop() {
|
|
328
|
+
async stop(options = {}) {
|
|
329
|
+
const triggeredByUser = options.triggeredByUser ?? false;
|
|
312
330
|
if (!this.ui || !this.ui.root) {
|
|
313
331
|
console.warn("UI not initialized, cannot stop");
|
|
314
332
|
return;
|
|
@@ -325,12 +343,38 @@ export class VoiceNavigationController {
|
|
|
325
343
|
setState(this.ui, "idle");
|
|
326
344
|
emitEvent("state-change", { state: "idle" });
|
|
327
345
|
updateTranscript(this.ui, "");
|
|
346
|
+
if (!triggeredByUser && this.ui.isCustomButton) {
|
|
347
|
+
console.log("[VoiceNavigation] Programmatically clicking custom button to sync external UI state");
|
|
348
|
+
this.syncCustomButtonState();
|
|
349
|
+
}
|
|
328
350
|
}
|
|
329
351
|
catch (error) {
|
|
330
352
|
console.error("Failed to stop recording:", error);
|
|
331
353
|
showError(this.ui, error);
|
|
332
354
|
setState(this.ui, "error");
|
|
333
355
|
emitEvent("error", { error });
|
|
356
|
+
if (!triggeredByUser && this.ui.isCustomButton) {
|
|
357
|
+
console.log("[VoiceNavigation] Programmatically clicking custom button after error");
|
|
358
|
+
this.syncCustomButtonState();
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
syncCustomButtonState() {
|
|
363
|
+
if (!this.ui.isCustomButton) {
|
|
364
|
+
return;
|
|
365
|
+
}
|
|
366
|
+
try {
|
|
367
|
+
this.isSyncingButtonState = true;
|
|
368
|
+
this.ui.button.click();
|
|
369
|
+
console.log("[VoiceNavigation] Custom button clicked to sync external UI state");
|
|
370
|
+
}
|
|
371
|
+
catch (error) {
|
|
372
|
+
console.error("[VoiceNavigation] Failed to click custom button:", error);
|
|
373
|
+
}
|
|
374
|
+
finally {
|
|
375
|
+
setTimeout(() => {
|
|
376
|
+
this.isSyncingButtonState = false;
|
|
377
|
+
}, 100);
|
|
334
378
|
}
|
|
335
379
|
}
|
|
336
380
|
handlePartial(text) {
|
|
@@ -387,7 +431,9 @@ export class VoiceNavigationController {
|
|
|
387
431
|
this.isPermissionRelatedError(error)) {
|
|
388
432
|
setState(this.ui, "idle");
|
|
389
433
|
this.handleAutoStartFallback("auto");
|
|
390
|
-
|
|
434
|
+
if (!this.ui.isCustomButton) {
|
|
435
|
+
updateStatus(this.ui, "Tap to start voice control");
|
|
436
|
+
}
|
|
391
437
|
return;
|
|
392
438
|
}
|
|
393
439
|
showError(this.ui, error);
|
|
@@ -439,6 +485,30 @@ export class VoiceNavigationController {
|
|
|
439
485
|
query,
|
|
440
486
|
originalText,
|
|
441
487
|
});
|
|
488
|
+
if (actionResult.performed && actionResult.info?.pendingNavigation) {
|
|
489
|
+
console.log("[VoiceNavigation] Search result with navigation - waiting for feedback:", action);
|
|
490
|
+
try {
|
|
491
|
+
await this.provideActionFeedback(action, actionResult, originalText);
|
|
492
|
+
console.log("[VoiceNavigation] Search feedback complete, now navigating to:", actionResult.info.navigationUrl);
|
|
493
|
+
if (actionResult.info.navigationUrl) {
|
|
494
|
+
window.location.href = actionResult.info.navigationUrl;
|
|
495
|
+
actionResult.info.navigated = true;
|
|
496
|
+
actionResult.info.pendingNavigation = false;
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
catch (err) {
|
|
500
|
+
console.error("[VoiceNavigation] Voice feedback error (search):", err);
|
|
501
|
+
if (actionResult.info.navigationUrl) {
|
|
502
|
+
window.location.href = actionResult.info.navigationUrl;
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
else if (actionResult.performed) {
|
|
507
|
+
console.log("[VoiceNavigation] Search result without navigation - non-blocking feedback");
|
|
508
|
+
void this.provideActionFeedback(action, actionResult, originalText).catch((err) => {
|
|
509
|
+
console.error("[VoiceNavigation] Voice feedback error (search):", err);
|
|
510
|
+
});
|
|
511
|
+
}
|
|
442
512
|
if (typeof this.config.actionHandlers?.[action] === "function") {
|
|
443
513
|
try {
|
|
444
514
|
this.config.actionHandlers[action]({
|
|
@@ -475,6 +545,75 @@ export class VoiceNavigationController {
|
|
|
475
545
|
query: query,
|
|
476
546
|
},
|
|
477
547
|
});
|
|
548
|
+
console.log("[VoiceNavigation] Action performed:", action, "result:", actionResult.performed);
|
|
549
|
+
const isCriticalAction = action.startsWith("navigate_") ||
|
|
550
|
+
action === "focus_next" ||
|
|
551
|
+
action === "tab_next" ||
|
|
552
|
+
action === "focus_previous" ||
|
|
553
|
+
action === "tab_back" ||
|
|
554
|
+
action === "go_back" ||
|
|
555
|
+
action === "go_forward" ||
|
|
556
|
+
action === "reload_page";
|
|
557
|
+
if (actionResult.performed && isCriticalAction) {
|
|
558
|
+
console.log("[VoiceNavigation] Critical action - waiting for voice feedback to complete:", action);
|
|
559
|
+
try {
|
|
560
|
+
await this.provideActionFeedback(action, actionResult, originalText);
|
|
561
|
+
console.log("[VoiceNavigation] Voice feedback completed for critical action:", action);
|
|
562
|
+
if (actionResult.info?.pendingNavigation) {
|
|
563
|
+
if (actionResult.info.navigationUrl) {
|
|
564
|
+
console.log("[VoiceNavigation] Feedback complete, now navigating to:", actionResult.info.navigationUrl);
|
|
565
|
+
window.location.href = actionResult.info.navigationUrl;
|
|
566
|
+
actionResult.info.navigated = true;
|
|
567
|
+
actionResult.info.pendingNavigation = false;
|
|
568
|
+
}
|
|
569
|
+
else if (actionResult.info.navigationType === "back") {
|
|
570
|
+
console.log("[VoiceNavigation] Feedback complete, navigating back");
|
|
571
|
+
window.history.back();
|
|
572
|
+
actionResult.info.navigated = true;
|
|
573
|
+
actionResult.info.pendingNavigation = false;
|
|
574
|
+
}
|
|
575
|
+
else if (actionResult.info.navigationType === "forward") {
|
|
576
|
+
console.log("[VoiceNavigation] Feedback complete, navigating forward");
|
|
577
|
+
window.history.forward();
|
|
578
|
+
actionResult.info.navigated = true;
|
|
579
|
+
actionResult.info.pendingNavigation = false;
|
|
580
|
+
}
|
|
581
|
+
else if (actionResult.info.navigationType === "reload") {
|
|
582
|
+
console.log("[VoiceNavigation] Feedback complete, reloading page");
|
|
583
|
+
window.location.reload();
|
|
584
|
+
actionResult.info.reloaded = true;
|
|
585
|
+
actionResult.info.pendingNavigation = false;
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
catch (err) {
|
|
590
|
+
console.error("[VoiceNavigation] Voice feedback error (critical action):", err);
|
|
591
|
+
if (actionResult.info?.pendingNavigation) {
|
|
592
|
+
if (actionResult.info.navigationUrl) {
|
|
593
|
+
console.log("[VoiceNavigation] Feedback failed, but proceeding with navigation:", actionResult.info.navigationUrl);
|
|
594
|
+
window.location.href = actionResult.info.navigationUrl;
|
|
595
|
+
}
|
|
596
|
+
else if (actionResult.info.navigationType === "back") {
|
|
597
|
+
window.history.back();
|
|
598
|
+
}
|
|
599
|
+
else if (actionResult.info.navigationType === "forward") {
|
|
600
|
+
window.history.forward();
|
|
601
|
+
}
|
|
602
|
+
else if (actionResult.info.navigationType === "reload") {
|
|
603
|
+
window.location.reload();
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
}
|
|
608
|
+
else if (actionResult.performed) {
|
|
609
|
+
console.log("[VoiceNavigation] Non-critical action - providing feedback non-blocking:", action);
|
|
610
|
+
void this.provideActionFeedback(action, actionResult, originalText).catch((err) => {
|
|
611
|
+
console.error("[VoiceNavigation] Voice feedback error:", err);
|
|
612
|
+
});
|
|
613
|
+
}
|
|
614
|
+
else {
|
|
615
|
+
console.log("[VoiceNavigation] Action not performed, skipping voice feedback");
|
|
616
|
+
}
|
|
478
617
|
if (typeof this.config.actionHandlers?.[action] === "function") {
|
|
479
618
|
try {
|
|
480
619
|
this.config.actionHandlers[action]({
|
|
@@ -503,6 +642,62 @@ export class VoiceNavigationController {
|
|
|
503
642
|
}
|
|
504
643
|
setLanguage(language) {
|
|
505
644
|
this.microphoneHandler.setLanguage(language);
|
|
645
|
+
this.voiceFeedbackService.setLanguage(language);
|
|
646
|
+
}
|
|
647
|
+
setVoiceFeedback(enabled) {
|
|
648
|
+
this.voiceFeedbackService.setEnabled(enabled);
|
|
649
|
+
}
|
|
650
|
+
isVoiceFeedbackEnabled() {
|
|
651
|
+
return this.voiceFeedbackService.isEnabled();
|
|
652
|
+
}
|
|
653
|
+
async provideActionFeedback(action, actionResult, transcript) {
|
|
654
|
+
console.log("[VoiceNavigation] provideActionFeedback called for action:", action);
|
|
655
|
+
if (!this.voiceFeedbackService) {
|
|
656
|
+
console.error("[VoiceNavigation] Voice feedback service not initialized!");
|
|
657
|
+
return;
|
|
658
|
+
}
|
|
659
|
+
console.log("[VoiceNavigation] Voice feedback service exists, checking if enabled...");
|
|
660
|
+
console.log("[VoiceNavigation] Voice feedback enabled:", this.voiceFeedbackService.isEnabled());
|
|
661
|
+
try {
|
|
662
|
+
if (action.startsWith("navigate_") && actionResult.info?.pageName) {
|
|
663
|
+
console.log("[VoiceNavigation] Handling navigation feedback");
|
|
664
|
+
await this.voiceFeedbackService.provideFeedback({
|
|
665
|
+
action,
|
|
666
|
+
actionResult,
|
|
667
|
+
transcript,
|
|
668
|
+
});
|
|
669
|
+
return;
|
|
670
|
+
}
|
|
671
|
+
if ((action === "focus_next" ||
|
|
672
|
+
action === "tab_next" ||
|
|
673
|
+
action === "focus_previous" ||
|
|
674
|
+
action === "tab_back") &&
|
|
675
|
+
actionResult.performed) {
|
|
676
|
+
console.log("[VoiceNavigation] Handling focus feedback");
|
|
677
|
+
const focusedElement = document.activeElement;
|
|
678
|
+
if (focusedElement && focusedElement !== document.body) {
|
|
679
|
+
await this.voiceFeedbackService.announceFocusChange(focusedElement, actionResult.info.direction === "next" ||
|
|
680
|
+
actionResult.info.direction === "forward"
|
|
681
|
+
? "next"
|
|
682
|
+
: "previous");
|
|
683
|
+
return;
|
|
684
|
+
}
|
|
685
|
+
}
|
|
686
|
+
if (actionResult.performed) {
|
|
687
|
+
console.log("[VoiceNavigation] Handling general action feedback");
|
|
688
|
+
await this.voiceFeedbackService.provideFeedback({
|
|
689
|
+
action,
|
|
690
|
+
actionResult,
|
|
691
|
+
transcript,
|
|
692
|
+
});
|
|
693
|
+
}
|
|
694
|
+
else {
|
|
695
|
+
console.log("[VoiceNavigation] Action not performed in feedback check");
|
|
696
|
+
}
|
|
697
|
+
}
|
|
698
|
+
catch (error) {
|
|
699
|
+
console.error("[VoiceNavigation] Failed to provide voice feedback:", error);
|
|
700
|
+
}
|
|
506
701
|
}
|
|
507
702
|
async executeVectorSearchAction(options) {
|
|
508
703
|
const vectorService = this.vectorSearchService;
|
|
@@ -628,7 +823,9 @@ export class VoiceNavigationController {
|
|
|
628
823
|
});
|
|
629
824
|
this.prepareForNavigation({ target: resolvedUrl });
|
|
630
825
|
baseInfo.message = "Opening the best matching result.";
|
|
631
|
-
|
|
826
|
+
baseInfo.pendingNavigation = true;
|
|
827
|
+
baseInfo.navigationUrl = resolvedUrl;
|
|
828
|
+
baseInfo.navigated = false;
|
|
632
829
|
return {
|
|
633
830
|
performed: true,
|
|
634
831
|
info: baseInfo,
|
|
@@ -884,7 +1081,9 @@ export class VoiceNavigationController {
|
|
|
884
1081
|
window.removeEventListener(event, resume);
|
|
885
1082
|
});
|
|
886
1083
|
};
|
|
887
|
-
|
|
1084
|
+
if (!this.ui.isCustomButton) {
|
|
1085
|
+
updateStatus(this.ui, "Tap to enable voice navigation");
|
|
1086
|
+
}
|
|
888
1087
|
}
|
|
889
1088
|
clearAutoStartFallback() {
|
|
890
1089
|
if (this.removeAutoStartListeners) {
|
|
@@ -8,5 +8,6 @@ type NextApiResponse = {
|
|
|
8
8
|
setHeader: (name: string, value: string | string[]) => void;
|
|
9
9
|
};
|
|
10
10
|
export declare function azureSpeechTokenHandler(req: NextApiRequest, res: NextApiResponse): Promise<void>;
|
|
11
|
+
export declare function POST(_request: Request): Promise<Response>;
|
|
11
12
|
export {};
|
|
12
13
|
//# sourceMappingURL=azure-speech-handler.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"azure-speech-handler.d.ts","sourceRoot":"","sources":["../../src/server/azure-speech-handler.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"azure-speech-handler.d.ts","sourceRoot":"","sources":["../../src/server/azure-speech-handler.ts"],"names":[],"mappings":"AAwBA,KAAK,cAAc,GAAG;IACpB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB,CAAC;AAEF,KAAK,eAAe,GAAG;IACrB,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,eAAe,CAAC;IAC1C,IAAI,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;IAC9B,SAAS,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,KAAK,IAAI,CAAC;CAC7D,CAAC;AA6EF,wBAAsB,uBAAuB,CAC3C,GAAG,EAAE,cAAc,EACnB,GAAG,EAAE,eAAe,GACnB,OAAO,CAAC,IAAI,CAAC,CAqBf;AAMD,wBAAsB,IAAI,CAAC,QAAQ,EAAE,OAAO,GAAG,OAAO,CAAC,QAAQ,CAAC,CAmB/D"}
|
|
@@ -1,18 +1,16 @@
|
|
|
1
|
-
|
|
2
|
-
if (req.method !== "POST") {
|
|
3
|
-
res.setHeader("Allow", ["POST"]);
|
|
4
|
-
res.status(405).json({ error: "Method not allowed" });
|
|
5
|
-
return;
|
|
6
|
-
}
|
|
1
|
+
async function getAzureSpeechToken() {
|
|
7
2
|
const key = process.env.AZURE_SPEECH_KEY;
|
|
8
3
|
const region = process.env.AZURE_SPEECH_REGION;
|
|
9
4
|
if (!key || !region) {
|
|
10
5
|
console.error("[VoiceNavigation] Missing Azure Speech credentials. Set AZURE_SPEECH_KEY and AZURE_SPEECH_REGION in your .env file");
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
6
|
+
return {
|
|
7
|
+
success: false,
|
|
8
|
+
statusCode: 500,
|
|
9
|
+
error: {
|
|
10
|
+
error: "Azure Speech configuration is missing",
|
|
11
|
+
hint: "Set AZURE_SPEECH_KEY and AZURE_SPEECH_REGION environment variables",
|
|
12
|
+
},
|
|
13
|
+
};
|
|
16
14
|
}
|
|
17
15
|
try {
|
|
18
16
|
const url = `https://${region}.api.cognitive.microsoft.com/sts/v1.0/issueToken`;
|
|
@@ -26,15 +24,56 @@ export async function azureSpeechTokenHandler(req, res) {
|
|
|
26
24
|
throw new Error(`Azure API responded with status ${response.status}`);
|
|
27
25
|
}
|
|
28
26
|
const token = await response.text();
|
|
29
|
-
res.setHeader("Cache-Control", "no-store, no-cache, max-age=0, must-revalidate");
|
|
30
27
|
const responseData = { token, region };
|
|
31
|
-
|
|
28
|
+
return {
|
|
29
|
+
success: true,
|
|
30
|
+
statusCode: 200,
|
|
31
|
+
data: responseData,
|
|
32
|
+
};
|
|
32
33
|
}
|
|
33
34
|
catch (error) {
|
|
34
35
|
console.error("[VoiceNavigation] Failed to fetch Azure speech token:", error);
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
36
|
+
return {
|
|
37
|
+
success: false,
|
|
38
|
+
statusCode: 500,
|
|
39
|
+
error: {
|
|
40
|
+
error: "Failed to get token",
|
|
41
|
+
message: error instanceof Error ? error.message : "Unknown error",
|
|
42
|
+
},
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
export async function azureSpeechTokenHandler(req, res) {
|
|
47
|
+
if (req.method !== "POST") {
|
|
48
|
+
res.setHeader("Allow", ["POST"]);
|
|
49
|
+
res.status(405).json({ error: "Method not allowed" });
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
const result = await getAzureSpeechToken();
|
|
53
|
+
res.setHeader("Cache-Control", "no-store, no-cache, max-age=0, must-revalidate");
|
|
54
|
+
if (result.success && result.data) {
|
|
55
|
+
res.status(result.statusCode).json(result.data);
|
|
56
|
+
}
|
|
57
|
+
else {
|
|
58
|
+
res.status(result.statusCode).json(result.error);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
export async function POST(_request) {
|
|
62
|
+
const result = await getAzureSpeechToken();
|
|
63
|
+
const headers = {
|
|
64
|
+
"Content-Type": "application/json",
|
|
65
|
+
"Cache-Control": "no-store, no-cache, max-age=0, must-revalidate",
|
|
66
|
+
};
|
|
67
|
+
if (result.success && result.data) {
|
|
68
|
+
return new Response(JSON.stringify(result.data), {
|
|
69
|
+
status: result.statusCode,
|
|
70
|
+
headers,
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
else {
|
|
74
|
+
return new Response(JSON.stringify(result.error), {
|
|
75
|
+
status: result.statusCode,
|
|
76
|
+
headers,
|
|
38
77
|
});
|
|
39
78
|
}
|
|
40
79
|
}
|
|
@@ -11,5 +11,6 @@ type NextApiResponse = {
|
|
|
11
11
|
setHeader: (name: string, value: string | string[]) => void;
|
|
12
12
|
};
|
|
13
13
|
export declare function bedrockEmbeddingHandler(req: NextApiRequest, res: NextApiResponse): Promise<void>;
|
|
14
|
+
export declare function BedrockEmbeddingPOST(request: Request): Promise<Response>;
|
|
14
15
|
export {};
|
|
15
16
|
//# sourceMappingURL=bedrock-embedding-handler.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bedrock-embedding-handler.d.ts","sourceRoot":"","sources":["../../src/server/bedrock-embedding-handler.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"bedrock-embedding-handler.d.ts","sourceRoot":"","sources":["../../src/server/bedrock-embedding-handler.ts"],"names":[],"mappings":"AA4BA,KAAK,cAAc,GAAG;IACpB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE;QACL,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;QACzB,SAAS,CAAC,EAAE,MAAM,CAAC;KACpB,CAAC;CACH,CAAC;AAEF,KAAK,eAAe,GAAG;IACrB,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,eAAe,CAAC;IAC1C,IAAI,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;IAC9B,SAAS,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,KAAK,IAAI,CAAC;CAC7D,CAAC;AA0MF,wBAAsB,uBAAuB,CAC3C,GAAG,EAAE,cAAc,EACnB,GAAG,EAAE,eAAe,GACnB,OAAO,CAAC,IAAI,CAAC,CAiBf;AAMD,wBAAsB,oBAAoB,CACxC,OAAO,EAAE,OAAO,GACf,OAAO,CAAC,QAAQ,CAAC,CAkCnB"}
|
|
@@ -9,12 +9,7 @@ function getSignatureKey(key, dateStamp, region, service) {
|
|
|
9
9
|
const kSigning = sign(kService, "aws4_request");
|
|
10
10
|
return kSigning;
|
|
11
11
|
}
|
|
12
|
-
|
|
13
|
-
if (req.method !== "POST") {
|
|
14
|
-
res.setHeader("Allow", ["POST"]);
|
|
15
|
-
res.status(405).json({ error: "Method not allowed" });
|
|
16
|
-
return;
|
|
17
|
-
}
|
|
12
|
+
async function generateBedrockEmbedding(text, inputType = "search_query") {
|
|
18
13
|
const region = process.env.AWS_REGION || "ap-south-1";
|
|
19
14
|
const accessKey = process.env.AWS_ACCESS_KEY_ID;
|
|
20
15
|
const secretKey = process.env.AWS_SECRET_ACCESS_KEY;
|
|
@@ -22,20 +17,28 @@ export async function bedrockEmbeddingHandler(req, res) {
|
|
|
22
17
|
const embeddingModelId = process.env.AWS_EMBEDDING_MODEL_ID || "cohere.embed-multilingual-v3";
|
|
23
18
|
if (!accessKey || !secretKey) {
|
|
24
19
|
console.error("[VoiceNavigation] Missing AWS credentials. Set AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY in your .env file");
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
20
|
+
return {
|
|
21
|
+
success: false,
|
|
22
|
+
statusCode: 500,
|
|
23
|
+
error: {
|
|
24
|
+
error: "Missing AWS credentials",
|
|
25
|
+
hint: "Set AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY environment variables",
|
|
26
|
+
},
|
|
27
|
+
};
|
|
30
28
|
}
|
|
31
29
|
if (!embeddingModelId) {
|
|
32
|
-
|
|
33
|
-
|
|
30
|
+
return {
|
|
31
|
+
success: false,
|
|
32
|
+
statusCode: 500,
|
|
33
|
+
error: { error: "Missing AWS_EMBEDDING_MODEL_ID" },
|
|
34
|
+
};
|
|
34
35
|
}
|
|
35
|
-
const { text, inputType = "search_query" } = req.body || {};
|
|
36
36
|
if (!text) {
|
|
37
|
-
|
|
38
|
-
|
|
37
|
+
return {
|
|
38
|
+
success: false,
|
|
39
|
+
statusCode: 400,
|
|
40
|
+
error: { error: "Missing text parameter" },
|
|
41
|
+
};
|
|
39
42
|
}
|
|
40
43
|
const requestBody = {
|
|
41
44
|
texts: Array.isArray(text) ? text : [text],
|
|
@@ -104,20 +107,77 @@ export async function bedrockEmbeddingHandler(req, res) {
|
|
|
104
107
|
if (!response.ok) {
|
|
105
108
|
const errorText = await response.text();
|
|
106
109
|
console.error("[VoiceNavigation] Bedrock Embedding API error:", response.status, errorText);
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
110
|
+
return {
|
|
111
|
+
success: false,
|
|
112
|
+
statusCode: response.status,
|
|
113
|
+
error: {
|
|
114
|
+
error: "Bedrock Embedding API error",
|
|
115
|
+
details: errorText,
|
|
116
|
+
},
|
|
117
|
+
};
|
|
112
118
|
}
|
|
113
119
|
const json = await response.json();
|
|
114
|
-
|
|
120
|
+
return {
|
|
121
|
+
success: true,
|
|
122
|
+
statusCode: 200,
|
|
123
|
+
data: json,
|
|
124
|
+
};
|
|
115
125
|
}
|
|
116
126
|
catch (error) {
|
|
117
127
|
console.error("[VoiceNavigation] Bedrock embedding generation error:", error);
|
|
118
|
-
|
|
119
|
-
|
|
128
|
+
return {
|
|
129
|
+
success: false,
|
|
130
|
+
statusCode: 500,
|
|
131
|
+
error: {
|
|
132
|
+
error: "Failed to generate embeddings",
|
|
133
|
+
message: error instanceof Error ? error.message : "Unknown error",
|
|
134
|
+
},
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
export async function bedrockEmbeddingHandler(req, res) {
|
|
139
|
+
if (req.method !== "POST") {
|
|
140
|
+
res.setHeader("Allow", ["POST"]);
|
|
141
|
+
res.status(405).json({ error: "Method not allowed" });
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
const { text, inputType = "search_query" } = req.body || {};
|
|
145
|
+
const result = await generateBedrockEmbedding(text, inputType);
|
|
146
|
+
if (result.success && result.data) {
|
|
147
|
+
res.status(result.statusCode).json(result.data);
|
|
148
|
+
}
|
|
149
|
+
else {
|
|
150
|
+
res.status(result.statusCode).json(result.error);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
export async function BedrockEmbeddingPOST(request) {
|
|
154
|
+
try {
|
|
155
|
+
const body = await request.json();
|
|
156
|
+
const { text, inputType = "search_query" } = body || {};
|
|
157
|
+
const result = await generateBedrockEmbedding(text, inputType);
|
|
158
|
+
const headers = {
|
|
159
|
+
"Content-Type": "application/json",
|
|
160
|
+
};
|
|
161
|
+
if (result.success && result.data) {
|
|
162
|
+
return new Response(JSON.stringify(result.data), {
|
|
163
|
+
status: result.statusCode,
|
|
164
|
+
headers,
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
else {
|
|
168
|
+
return new Response(JSON.stringify(result.error), {
|
|
169
|
+
status: result.statusCode,
|
|
170
|
+
headers,
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
catch (error) {
|
|
175
|
+
return new Response(JSON.stringify({
|
|
176
|
+
error: "Invalid request",
|
|
120
177
|
message: error instanceof Error ? error.message : "Unknown error",
|
|
178
|
+
}), {
|
|
179
|
+
status: 400,
|
|
180
|
+
headers: { "Content-Type": "application/json" },
|
|
121
181
|
});
|
|
122
182
|
}
|
|
123
183
|
}
|
|
@@ -10,5 +10,6 @@ type NextApiResponse = {
|
|
|
10
10
|
setHeader: (name: string, value: string | string[]) => void;
|
|
11
11
|
};
|
|
12
12
|
export declare function bedrockHandler(req: NextApiRequest, res: NextApiResponse): Promise<void>;
|
|
13
|
+
export declare function BedrockPOST(request: Request): Promise<Response>;
|
|
13
14
|
export {};
|
|
14
15
|
//# sourceMappingURL=bedrock-handler.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bedrock-handler.d.ts","sourceRoot":"","sources":["../../src/server/bedrock-handler.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"bedrock-handler.d.ts","sourceRoot":"","sources":["../../src/server/bedrock-handler.ts"],"names":[],"mappings":"AA4BA,KAAK,cAAc,GAAG;IACpB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE;QACL,IAAI,CAAC,EAAE,OAAO,CAAC;KAChB,CAAC;CACH,CAAC;AAEF,KAAK,eAAe,GAAG;IACrB,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,eAAe,CAAC;IAC1C,IAAI,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;IAC9B,SAAS,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,KAAK,IAAI,CAAC;CAC7D,CAAC;AAqLF,wBAAsB,cAAc,CAClC,GAAG,EAAE,cAAc,EACnB,GAAG,EAAE,eAAe,GACnB,OAAO,CAAC,IAAI,CAAC,CAkBf;AAMD,wBAAsB,WAAW,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,QAAQ,CAAC,CAkCrE"}
|