@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.
@@ -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
- updateStatus(this.ui, "Tap to enable voice navigation");
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
- updateStatus(this.ui, "Tap to start voice control");
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
- window.location.href = resolvedUrl;
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
- updateStatus(this.ui, "Tap to enable voice navigation");
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":"AAiBA,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;AAWF,wBAAsB,uBAAuB,CAC3C,GAAG,EAAE,cAAc,EACnB,GAAG,EAAE,eAAe,GACnB,OAAO,CAAC,IAAI,CAAC,CA0Df"}
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
- export async function azureSpeechTokenHandler(req, res) {
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
- res.status(500).json({
12
- error: "Azure Speech configuration is missing",
13
- hint: "Set AZURE_SPEECH_KEY and AZURE_SPEECH_REGION environment variables",
14
- });
15
- return;
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
- res.status(200).json(responseData);
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
- res.status(500).json({
36
- error: "Failed to get token",
37
- message: error instanceof Error ? error.message : "Unknown error",
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":"AAsBA,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;AA0BF,wBAAsB,uBAAuB,CAC3C,GAAG,EAAE,cAAc,EACnB,GAAG,EAAE,eAAe,GACnB,OAAO,CAAC,IAAI,CAAC,CAwJf"}
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
- export async function bedrockEmbeddingHandler(req, res) {
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
- res.status(500).json({
26
- error: "Missing AWS credentials",
27
- hint: "Set AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY environment variables",
28
- });
29
- return;
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
- res.status(500).json({ error: "Missing AWS_EMBEDDING_MODEL_ID" });
33
- return;
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
- res.status(400).json({ error: "Missing text parameter" });
38
- return;
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
- res.status(response.status).json({
108
- error: "Bedrock Embedding API error",
109
- details: errorText,
110
- });
111
- return;
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
- res.status(200).json(json);
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
- res.status(500).json({
119
- error: "Failed to generate embeddings",
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":"AAsBA,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;AA0BF,wBAAsB,cAAc,CAClC,GAAG,EAAE,cAAc,EACnB,GAAG,EAAE,eAAe,GACnB,OAAO,CAAC,IAAI,CAAC,CA0If"}
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"}