@myscheme/voice-navigation-sdk 0.1.9 → 0.1.10

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.
Files changed (48) hide show
  1. package/README.md +130 -17
  2. package/dist/actions.d.ts +1 -0
  3. package/dist/actions.d.ts.map +1 -1
  4. package/dist/actions.js +51 -1
  5. package/dist/index.d.ts +38 -1
  6. package/dist/index.d.ts.map +1 -1
  7. package/dist/index.js +158 -1
  8. package/dist/languages.d.ts +17 -0
  9. package/dist/languages.d.ts.map +1 -0
  10. package/dist/languages.js +86 -0
  11. package/dist/microphone-handler.d.ts +8 -2
  12. package/dist/microphone-handler.d.ts.map +1 -1
  13. package/dist/microphone-handler.js +89 -2
  14. package/dist/navigation-controller.d.ts +2 -0
  15. package/dist/navigation-controller.d.ts.map +1 -1
  16. package/dist/navigation-controller.js +58 -22
  17. package/dist/server/azure-speech-handler.d.ts +3 -2
  18. package/dist/server/azure-speech-handler.d.ts.map +1 -1
  19. package/dist/server/azure-speech-handler.js +26 -2
  20. package/dist/server/bedrock-embedding-handler.d.ts +2 -1
  21. package/dist/server/bedrock-embedding-handler.d.ts.map +1 -1
  22. package/dist/server/bedrock-embedding-handler.js +73 -19
  23. package/dist/server/bedrock-handler.d.ts +2 -1
  24. package/dist/server/bedrock-handler.d.ts.map +1 -1
  25. package/dist/server/bedrock-handler.js +124 -19
  26. package/dist/server/index.d.ts +5 -0
  27. package/dist/server/index.d.ts.map +1 -1
  28. package/dist/server/index.js +3 -0
  29. package/dist/server/opensearch-env-handler.d.ts +2 -1
  30. package/dist/server/opensearch-env-handler.d.ts.map +1 -1
  31. package/dist/server/opensearch-env-handler.js +23 -53
  32. package/dist/server/sarvam-stt-relay.d.ts +8 -0
  33. package/dist/server/sarvam-stt-relay.d.ts.map +1 -0
  34. package/dist/server/sarvam-stt-relay.js +111 -0
  35. package/dist/server/security.d.ts +26 -0
  36. package/dist/server/security.d.ts.map +1 -0
  37. package/dist/server/security.js +89 -0
  38. package/dist/services/bedrock.d.ts.map +1 -1
  39. package/dist/services/bedrock.js +13 -0
  40. package/dist/services/sarvam-speech.d.ts +47 -0
  41. package/dist/services/sarvam-speech.d.ts.map +1 -0
  42. package/dist/services/sarvam-speech.js +418 -0
  43. package/dist/services/voice-feedback.d.ts +7 -2
  44. package/dist/services/voice-feedback.d.ts.map +1 -1
  45. package/dist/services/voice-feedback.js +45 -5
  46. package/dist/types.d.ts +11 -0
  47. package/dist/types.d.ts.map +1 -1
  48. package/package.json +4 -2
package/README.md CHANGED
@@ -1,10 +1,11 @@
1
1
  # Voice Navigation SDK
2
2
 
3
- A TypeScript SDK for voice-controlled navigation using Azure Speech-to-Text and AWS Bedrock for intent understanding.
3
+ A TypeScript SDK for voice-controlled navigation using Sarvam or Azure Speech, and AWS Bedrock for intent understanding.
4
4
 
5
5
  ## 🚀 Features
6
6
 
7
- - 🎤 **Real-time speech recognition** using Azure Speech SDK
7
+ - 🎤 **Real-time speech recognition** using Sarvam (default) or Azure Speech SDK
8
+ - 🔊 **Text-to-speech feedback** using Sarvam (default) or Azure Speech SDK
8
9
  - 🤖 **AI-powered intent extraction** using AWS Bedrock (Claude)
9
10
  - 🌐 **Dynamic page navigation** via XML configuration
10
11
  - 🧭 **Voice-controlled navigation** actions
@@ -48,9 +49,23 @@ The guide covers:
48
49
  ### Basic Setup
49
50
 
50
51
  ```typescript
51
- import { initNavigationOnMicrophone } from "@myscheme/voice-navigation-sdk";
52
+ import {
53
+ initNavigationOnMicrophone,
54
+ buildSpeechProviderConfig,
55
+ } from "@myscheme/voice-navigation-sdk";
52
56
 
53
57
  const controller = initNavigationOnMicrophone({
58
+ ...buildSpeechProviderConfig({
59
+ provider: "sarvam",
60
+ sarvam: {
61
+ apiKey: "your-sarvam-api-key",
62
+ sttModel: "saaras:v3",
63
+ ttsModel: "bulbul:v3",
64
+ speaker: "manan",
65
+ },
66
+ }),
67
+
68
+ // Azure Speech configuration (required only when speechProvider is "azure")
54
69
  // Azure Speech configuration
55
70
  azure: {
56
71
  subscriptionKey: "your-azure-subscription-key",
@@ -73,6 +88,97 @@ const controller = initNavigationOnMicrophone({
73
88
  });
74
89
  ```
75
90
 
91
+ ### Runtime Provider Switch Helper
92
+
93
+ Switch between Sarvam and Azure without rebuilding your app:
94
+
95
+ ```typescript
96
+ import { switchVoiceProvider } from "@myscheme/voice-navigation-sdk";
97
+
98
+ // Switch to Azure at runtime
99
+ switchVoiceProvider("azure", {
100
+ azure: {
101
+ tokenEndpoint: "/api/speech/token",
102
+ // or: subscriptionKey + region for direct mode
103
+ },
104
+ });
105
+
106
+ // Switch back to Sarvam at runtime
107
+ switchVoiceProvider("sarvam", {
108
+ sarvam: {
109
+ apiKey: process.env.NEXT_PUBLIC_SARVAM_API_KEY,
110
+ sttModel: "saaras:v3",
111
+ ttsModel: "bulbul:v3",
112
+ speaker: "manan",
113
+ },
114
+ });
115
+ ```
116
+
117
+ ### Ready-to-Copy Config
118
+
119
+ Sarvam (default provider):
120
+
121
+ ```typescript
122
+ import { initNavigationOnMicrophone } from "@myscheme/voice-navigation-sdk";
123
+
124
+ const controller = initNavigationOnMicrophone({
125
+ speechProvider: "sarvam",
126
+ sarvam: {
127
+ apiKey: process.env.NEXT_PUBLIC_SARVAM_API_KEY,
128
+ sttModel: "saaras:v3",
129
+ ttsModel: "bulbul:v3",
130
+ speaker: "manan",
131
+ sttSampleRate: 16000,
132
+ ttsSampleRate: 22050,
133
+ },
134
+ aws: {
135
+ bedrockEndpoint: "/api/speech/bedrock",
136
+ embeddingEndpoint: "/api/speech/bedrock-embedding",
137
+ },
138
+ language: "en-IN",
139
+ autoStart: false,
140
+ });
141
+ ```
142
+
143
+ Azure (proxy mode recommended):
144
+
145
+ ```typescript
146
+ import { initNavigationOnMicrophone } from "@myscheme/voice-navigation-sdk";
147
+
148
+ const controller = initNavigationOnMicrophone({
149
+ speechProvider: "azure",
150
+ azure: {
151
+ tokenEndpoint: "/api/speech/token",
152
+ },
153
+ aws: {
154
+ bedrockEndpoint: "/api/speech/bedrock",
155
+ embeddingEndpoint: "/api/speech/bedrock-embedding",
156
+ },
157
+ language: "en-IN",
158
+ autoStart: false,
159
+ });
160
+ ```
161
+
162
+ Azure (direct credentials, use only in trusted/server contexts):
163
+
164
+ ```typescript
165
+ import { initNavigationOnMicrophone } from "@myscheme/voice-navigation-sdk";
166
+
167
+ const controller = initNavigationOnMicrophone({
168
+ speechProvider: "azure",
169
+ azure: {
170
+ subscriptionKey: process.env.NEXT_PUBLIC_AZURE_SPEECH_KEY,
171
+ region: process.env.NEXT_PUBLIC_AZURE_SPEECH_REGION,
172
+ },
173
+ aws: {
174
+ bedrockEndpoint: "/api/speech/bedrock",
175
+ embeddingEndpoint: "/api/speech/bedrock-embedding",
176
+ },
177
+ language: "en-IN",
178
+ autoStart: false,
179
+ });
180
+ ```
181
+
76
182
  ### With Dynamic Pages
77
183
 
78
184
  ```typescript
@@ -96,20 +202,27 @@ const controller = initNavigationOnMicrophone({
96
202
 
97
203
  ### NavigationConfig Options
98
204
 
99
- | Property | Type | Required | Description |
100
- | ----------------------- | --------- | -------- | ---------------------------------------------- |
101
- | `azure.subscriptionKey` | `string` | | Azure Speech subscription key |
102
- | `azure.region` | `string` | | Azure Speech region |
103
- | `aws.accessKeyId` | `string` | | AWS access key ID |
104
- | `aws.secretAccessKey` | `string` | | AWS secret access key |
105
- | `aws.modelId` | `string` | | AWS Bedrock model ID |
106
- | `aws.region` | `string` | | AWS region (default: 'ap-south-1') |
107
- | `language` | `string` | | Speech recognition language (default: 'en-IN') |
108
- | `autoStart` | `boolean` | | Auto-start voice control (default: false) |
109
- | `actionHandlers` | `object` | | Custom action callbacks |
110
- | `pages` | `object` | | Dynamic page navigation configuration |
111
- | `opensearch` | `object` | ❌ | OpenSearch vector search configuration |
112
- | `ui` | `object` | ❌ | UI customization (button placement & style) |
205
+ | Property | Type | Required | Description |
206
+ | ----------------------- | --------- | -------- | ------------------------------------------------ |
207
+ | `speechProvider` | `string` | | Speech provider: `sarvam` (default) or `azure` |
208
+ | `sarvam.apiKey` | `string` | ✅\* | Sarvam API key (\*required when provider=sarvam) |
209
+ | `sarvam.sttModel` | `string` | | STT model (default: `saaras:v3`) |
210
+ | `sarvam.ttsModel` | `string` | | TTS model (default: `bulbul:v3`) |
211
+ | `sarvam.speaker` | `string` | | Sarvam speaker name |
212
+ | `azure.subscriptionKey` | `string` | | Azure Speech subscription key |
213
+ | `azure.region` | `string` | | Azure Speech region |
214
+ | `aws.accessKeyId` | `string` | | AWS access key ID |
215
+ | `aws.secretAccessKey` | `string` | | AWS secret access key |
216
+ | `aws.modelId` | `string` | | AWS Bedrock model ID |
217
+ | `aws.region` | `string` | ❌ | AWS region (default: 'ap-south-1') |
218
+ | `language` | `string` | ❌ | Speech recognition language (default: 'en-IN') |
219
+ | `autoStart` | `boolean` | ❌ | Auto-start voice control (default: false) |
220
+ | `actionHandlers` | `object` | ❌ | Custom action callbacks |
221
+ | `pages` | `object` | ❌ | Dynamic page navigation configuration |
222
+ | `opensearch` | `object` | ❌ | OpenSearch vector search configuration |
223
+ | `ui` | `object` | ❌ | UI customization (button placement & style) |
224
+
225
+ Note: Bhashini is not integrated in this release.
113
226
 
114
227
  ### OpenSearch Configuration
115
228
 
package/dist/actions.d.ts CHANGED
@@ -4,6 +4,7 @@ export declare const setPageRegistry: (registry: PageRegistry) => void;
4
4
  export declare const getPageRegistry: () => PageRegistry | null;
5
5
  export declare const setZoomScale: (scale: number) => number;
6
6
  export declare const adjustZoom: (delta: number) => number;
7
+ export declare const ACTION_LABELS: Record<string, string>;
7
8
  export declare const formatActionLabel: (action: string) => string;
8
9
  export declare const performAgentAction: (action: NavigationAction, context?: ActionContext) => ActionResult;
9
10
  export declare const extractAgentAction: (result: any) => AgentActionResponse | null;
@@ -1 +1 @@
1
- {"version":3,"file":"actions.d.ts","sourceRoot":"","sources":["../src/actions.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EACV,gBAAgB,EAEhB,aAAa,EACb,YAAY,EACZ,mBAAmB,EACpB,MAAM,YAAY,CAAC;AACpB,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AA4DhE,eAAO,MAAM,eAAe,GAAI,UAAU,YAAY,KAAG,IAOxD,CAAC;AAKF,eAAO,MAAM,eAAe,QAAO,YAAY,GAAG,IAEjD,CAAC;AAgGF,eAAO,MAAM,YAAY,GAAI,OAAO,MAAM,KAAG,MAS5C,CAAC;AAKF,eAAO,MAAM,UAAU,GAAI,OAAO,MAAM,KAAG,MAM1C,CAAC;AAKF,eAAO,MAAM,iBAAiB,GAAI,QAAQ,MAAM,KAAG,MAElD,CAAC;AAKF,eAAO,MAAM,kBAAkB,GAC7B,QAAQ,gBAAgB,EACxB,UAAS,aAAkB,KAC1B,YAg7BF,CAAC;AAKF,eAAO,MAAM,kBAAkB,GAAI,QAAQ,GAAG,KAAG,mBAAmB,GAAG,IAiCtE,CAAC;AAKF,eAAO,MAAM,eAAe,GAAI,QAAQ,MAAM,KAAG,MAAM,IAAI,gBAY1D,CAAC"}
1
+ {"version":3,"file":"actions.d.ts","sourceRoot":"","sources":["../src/actions.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EACV,gBAAgB,EAEhB,aAAa,EACb,YAAY,EACZ,mBAAmB,EACpB,MAAM,YAAY,CAAC;AACpB,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AA4DhE,eAAO,MAAM,eAAe,GAAI,UAAU,YAAY,KAAG,IAOxD,CAAC;AAKF,eAAO,MAAM,eAAe,QAAO,YAAY,GAAG,IAEjD,CAAC;AAgGF,eAAO,MAAM,YAAY,GAAI,OAAO,MAAM,KAAG,MAS5C,CAAC;AAKF,eAAO,MAAM,UAAU,GAAI,OAAO,MAAM,KAAG,MAM1C,CAAC;AAOF,eAAO,MAAM,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAiDhD,CAAC;AAQF,eAAO,MAAM,iBAAiB,GAAI,QAAQ,MAAM,KAAG,MAElD,CAAC;AAKF,eAAO,MAAM,kBAAkB,GAC7B,QAAQ,gBAAgB,EACxB,UAAS,aAAkB,KAC1B,YAg7BF,CAAC;AAKF,eAAO,MAAM,kBAAkB,GAAI,QAAQ,GAAG,KAAG,mBAAmB,GAAG,IAiCtE,CAAC;AAKF,eAAO,MAAM,eAAe,GAAI,QAAQ,MAAM,KAAG,MAAM,IAAI,gBAY1D,CAAC"}
package/dist/actions.js CHANGED
@@ -131,8 +131,58 @@ export const adjustZoom = (delta) => {
131
131
  const current = parseFloat(document.documentElement.dataset.navigateZoom || "1");
132
132
  return setZoomScale(current + delta);
133
133
  };
134
+ export const ACTION_LABELS = {
135
+ zoom_in: "Zoomed in",
136
+ zoom_out: "Zoomed out",
137
+ scroll_up: "Scrolled up",
138
+ scroll_down: "Scrolled down",
139
+ scroll_left: "Scrolled left",
140
+ scroll_right: "Scrolled right",
141
+ page_up: "Moved up a page",
142
+ page_down: "Moved down a page",
143
+ scroll_top: "Jumped to the top of the page",
144
+ scroll_bottom: "Jumped to the bottom of the page",
145
+ go_back: "Went back to the previous page",
146
+ go_forward: "Went forward to the next page",
147
+ reload_page: "Reloaded the page",
148
+ print_page: "Opened the print dialog",
149
+ copy_url: "Copied the page link",
150
+ open_menu: "Opened the menu",
151
+ close_menu: "Closed the menu",
152
+ focus_search: "Moved to the search box",
153
+ toggle_fullscreen: "Turned on full screen",
154
+ exit_fullscreen: "Turned off full screen",
155
+ play_media: "Playing the video",
156
+ pause_media: "Paused the video",
157
+ mute_media: "Muted the sound",
158
+ unmute_media: "Unmuted the sound",
159
+ search_content: "Searching",
160
+ stop: "Stopped listening",
161
+ quit: "Stopped voice navigation",
162
+ tab_next: "Moved to the next item",
163
+ tab_back: "Moved to the previous item",
164
+ focus_next: "Moved focus to the next item",
165
+ focus_previous: "Moved focus to the previous item",
166
+ click: "Clicked",
167
+ enter: "Pressed enter",
168
+ submit: "Submitted the form",
169
+ activate: "Activated",
170
+ move_left: "Moved left",
171
+ move_right: "Moved right",
172
+ move_up: "Moved up",
173
+ move_down: "Moved down",
174
+ move_beginning: "Moved to the beginning",
175
+ move_end: "Moved to the end",
176
+ select_all: "Selected all text",
177
+ select_line: "Selected the line",
178
+ type_text: "Typed the text",
179
+ cut: "Cut the selection",
180
+ copy: "Copied the selection",
181
+ paste: "Pasted",
182
+ clear: "Cleared the field",
183
+ };
134
184
  export const formatActionLabel = (action) => {
135
- return action.replace(/_/g, " ");
185
+ return ACTION_LABELS[action] ?? action.replace(/_/g, " ");
136
186
  };
137
187
  export const performAgentAction = (action, context = {}) => {
138
188
  if (!action) {
package/dist/index.d.ts CHANGED
@@ -1,15 +1,52 @@
1
1
  export { VoiceNavigationController } from "./navigation-controller.js";
2
2
  export { MicrophoneHandler } from "./microphone-handler.js";
3
3
  export { AzureSpeechService } from "./services/azure-speech.js";
4
+ export { SarvamSpeechService } from "./services/sarvam-speech.js";
4
5
  export { BedrockService } from "./services/bedrock.js";
5
6
  export { PageRegistry } from "./services/page-registry.js";
6
7
  export type { NavigablePage } from "./services/page-registry.js";
7
8
  export { parseNavigationXML, fetchNavigationXML, loadNavigationPages, } from "./services/xml-parser.js";
8
9
  export type { PageXMLConfig } from "./services/xml-parser.js";
10
+ export { SARVAM_LANGUAGES, SARVAM_STT_LANGUAGES, SARVAM_TTS_LANGUAGES, DEFAULT_LANGUAGE, isSttSupported, isTtsSupported, resolveTtsLanguage, normalizeLanguageCode, } from "./languages.js";
11
+ export type { SarvamLanguage } from "./languages.js";
9
12
  export * from "./types.js";
10
13
  export * from "./actions.js";
11
14
  export { createFloatingControl, attachToCustomButton, setState, updateStatus, updateTranscript, updateActionIndicator, showError, emitEvent, } from "./ui.js";
12
15
  import { VoiceNavigationController } from "./navigation-controller.js";
13
- import type { NavigationConfig } from "./types.js";
16
+ import type { NavigationConfig, SpeechProvider } from "./types.js";
17
+ type SpeechProviderConfigOptions = {
18
+ provider: SpeechProvider;
19
+ azure?: NavigationConfig["azure"];
20
+ sarvam?: NavigationConfig["sarvam"];
21
+ };
22
+ export type ProviderSwitchOptions = {
23
+ azure?: NavigationConfig["azure"];
24
+ sarvam?: NavigationConfig["sarvam"];
25
+ language?: NavigationConfig["language"];
26
+ voiceFeedback?: NavigationConfig["voiceFeedback"];
27
+ };
28
+ export type VoiceProviderDropdownOption = {
29
+ provider: SpeechProvider;
30
+ label: string;
31
+ disabled?: boolean;
32
+ };
33
+ export type VoiceProviderDropdownConfig = {
34
+ container: string | HTMLElement;
35
+ initialProvider?: SpeechProvider;
36
+ options?: VoiceProviderDropdownOption[];
37
+ className?: string;
38
+ selectId?: string;
39
+ resolveSwitchOptions?: (provider: SpeechProvider) => ProviderSwitchOptions | Promise<ProviderSwitchOptions>;
40
+ onProviderChanged?: (provider: SpeechProvider) => void;
41
+ onError?: (error: unknown, provider: SpeechProvider) => void;
42
+ };
43
+ export type VoiceProviderDropdownHandle = {
44
+ element: HTMLSelectElement;
45
+ destroy: () => void;
46
+ setProvider: (provider: SpeechProvider) => void;
47
+ };
48
+ export declare function buildSpeechProviderConfig(options: SpeechProviderConfigOptions): Pick<NavigationConfig, "speechProvider" | "azure" | "sarvam">;
49
+ export declare function switchVoiceProvider(provider: SpeechProvider, options?: ProviderSwitchOptions): VoiceNavigationController;
50
+ export declare function createVoiceProviderDropdown(config: VoiceProviderDropdownConfig): VoiceProviderDropdownHandle;
14
51
  export declare function initNavigationOnMicrophone(config: NavigationConfig): VoiceNavigationController;
15
52
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,yBAAyB,EAAE,MAAM,4BAA4B,CAAC;AACvE,OAAO,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAC5D,OAAO,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAC;AAChE,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAC3D,YAAY,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAC;AACjE,OAAO,EACL,kBAAkB,EAClB,kBAAkB,EAClB,mBAAmB,GACpB,MAAM,0BAA0B,CAAC;AAClC,YAAY,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AAC9D,cAAc,YAAY,CAAC;AAC3B,cAAc,cAAc,CAAC;AAC7B,OAAO,EACL,qBAAqB,EACrB,oBAAoB,EACpB,QAAQ,EACR,YAAY,EACZ,gBAAgB,EAChB,qBAAqB,EACrB,SAAS,EACT,SAAS,GACV,MAAM,SAAS,CAAC;AAEjB,OAAO,EAAE,yBAAyB,EAAE,MAAM,4BAA4B,CAAC;AACvE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AA+JnD,wBAAgB,0BAA0B,CACxC,MAAM,EAAE,gBAAgB,GACvB,yBAAyB,CAoG3B"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/dist/index.js CHANGED
@@ -1,9 +1,11 @@
1
1
  export { VoiceNavigationController } from "./navigation-controller.js";
2
2
  export { MicrophoneHandler } from "./microphone-handler.js";
3
3
  export { AzureSpeechService } from "./services/azure-speech.js";
4
+ export { SarvamSpeechService } from "./services/sarvam-speech.js";
4
5
  export { BedrockService } from "./services/bedrock.js";
5
6
  export { PageRegistry } from "./services/page-registry.js";
6
7
  export { parseNavigationXML, fetchNavigationXML, loadNavigationPages, } from "./services/xml-parser.js";
8
+ export { SARVAM_LANGUAGES, SARVAM_STT_LANGUAGES, SARVAM_TTS_LANGUAGES, DEFAULT_LANGUAGE, isSttSupported, isTtsSupported, resolveTtsLanguage, normalizeLanguageCode, } from "./languages.js";
7
9
  export * from "./types.js";
8
10
  export * from "./actions.js";
9
11
  export { createFloatingControl, attachToCustomButton, setState, updateStatus, updateTranscript, updateActionIndicator, showError, emitEvent, } from "./ui.js";
@@ -29,8 +31,12 @@ const buildPersistableConfig = (config) => {
29
31
  const hasCustomHandlers = Boolean(normalizedConfig.actionHandlers &&
30
32
  Object.keys(normalizedConfig.actionHandlers).length > 0);
31
33
  const persistable = {
34
+ speechProvider: normalizedConfig.speechProvider,
32
35
  aws: normalizedConfig.aws ? { ...normalizedConfig.aws } : undefined,
33
36
  azure: normalizedConfig.azure ? { ...normalizedConfig.azure } : undefined,
37
+ sarvam: normalizedConfig.sarvam
38
+ ? { ...normalizedConfig.sarvam }
39
+ : undefined,
34
40
  opensearch: normalizedConfig.opensearch
35
41
  ? { ...normalizedConfig.opensearch }
36
42
  : undefined,
@@ -87,6 +93,155 @@ const describeOpenSearchConfig = (config) => {
87
93
  apiPath,
88
94
  });
89
95
  };
96
+ const resolveSpeechProviderConfig = (currentConfig, provider, options) => {
97
+ const merged = {
98
+ ...currentConfig,
99
+ ...options,
100
+ speechProvider: provider,
101
+ azure: {
102
+ ...(currentConfig.azure ?? {}),
103
+ ...(options?.azure ?? {}),
104
+ },
105
+ sarvam: {
106
+ ...(currentConfig.sarvam ?? {}),
107
+ ...(options?.sarvam ?? {}),
108
+ },
109
+ };
110
+ if (provider === "sarvam") {
111
+ const hasTtsUrl = Boolean(merged.sarvam?.ttsUrl);
112
+ const hasSttUrl = Boolean(merged.sarvam?.sttUrl);
113
+ if (!hasTtsUrl || !hasSttUrl) {
114
+ merged.sarvam = {
115
+ ...(merged.sarvam ?? {}),
116
+ ttsUrl: merged.sarvam?.ttsUrl ?? "/api/speech/sarvam",
117
+ sttUrl: merged.sarvam?.sttUrl ?? "/api/speech/sarvam-stt",
118
+ };
119
+ }
120
+ }
121
+ if (provider === "azure") {
122
+ const hasTokenEndpoint = Boolean(merged.azure?.tokenEndpoint);
123
+ const hasDirectCredentials = Boolean(merged.azure?.subscriptionKey && merged.azure?.region);
124
+ if (!hasTokenEndpoint && !hasDirectCredentials) {
125
+ merged.azure = {
126
+ ...(merged.azure ?? {}),
127
+ tokenEndpoint: "/api/speech/token",
128
+ };
129
+ }
130
+ }
131
+ return normalizeNavigationConfig(merged);
132
+ };
133
+ export function buildSpeechProviderConfig(options) {
134
+ return resolveSpeechProviderConfig({}, options.provider, {
135
+ azure: options.azure,
136
+ sarvam: options.sarvam,
137
+ });
138
+ }
139
+ export function switchVoiceProvider(provider, options = {}) {
140
+ if (typeof window === "undefined" || typeof document === "undefined") {
141
+ throw new Error("switchVoiceProvider requires a browser environment");
142
+ }
143
+ const globalWindow = window;
144
+ const currentRaw = globalWindow.__voiceNavigationConfig ||
145
+ readPersistedConfig() ||
146
+ {};
147
+ const currentConfig = normalizeNavigationConfig(currentRaw);
148
+ const nextConfig = resolveSpeechProviderConfig(currentConfig, provider, options);
149
+ return initNavigationOnMicrophone(nextConfig);
150
+ }
151
+ export function createVoiceProviderDropdown(config) {
152
+ if (typeof window === "undefined" || typeof document === "undefined") {
153
+ throw new Error("createVoiceProviderDropdown requires a browser environment");
154
+ }
155
+ const container = typeof config.container === "string"
156
+ ? document.querySelector(config.container)
157
+ : config.container;
158
+ if (!container) {
159
+ throw new Error("Voice provider dropdown container was not found");
160
+ }
161
+ const defaultOptions = [
162
+ { provider: "sarvam", label: "Sarvam" },
163
+ { provider: "azure", label: "Azure" },
164
+ ];
165
+ const options = config.options?.length ? config.options : defaultOptions;
166
+ const wrapper = document.createElement("div");
167
+ wrapper.className = config.className ?? "voice-provider-dropdown";
168
+ wrapper.setAttribute("data-voice-provider-dropdown", "true");
169
+ const select = document.createElement("select");
170
+ select.id = config.selectId ?? "voice-provider-select";
171
+ select.setAttribute("aria-label", "Voice provider");
172
+ options.forEach((option) => {
173
+ const opt = document.createElement("option");
174
+ opt.value = option.provider;
175
+ opt.textContent = option.label;
176
+ opt.disabled = Boolean(option.disabled);
177
+ select.appendChild(opt);
178
+ });
179
+ let currentProvider = config.initialProvider ?? "sarvam";
180
+ const activeOption = options.find((option) => option.provider === currentProvider);
181
+ if (!activeOption || activeOption.disabled) {
182
+ const fallback = options.find((option) => !option.disabled);
183
+ if (!fallback) {
184
+ throw new Error("No enabled voice providers are available for dropdown");
185
+ }
186
+ currentProvider = fallback.provider;
187
+ }
188
+ select.value = currentProvider;
189
+ let isSwitching = false;
190
+ const handleChange = async () => {
191
+ if (isSwitching) {
192
+ return;
193
+ }
194
+ const nextProvider = select.value;
195
+ if (nextProvider === currentProvider) {
196
+ return;
197
+ }
198
+ const option = options.find((item) => item.provider === nextProvider);
199
+ if (option?.disabled) {
200
+ select.value = currentProvider;
201
+ return;
202
+ }
203
+ isSwitching = true;
204
+ select.disabled = true;
205
+ try {
206
+ const switchOptions = config.resolveSwitchOptions
207
+ ? await config.resolveSwitchOptions(nextProvider)
208
+ : {};
209
+ switchVoiceProvider(nextProvider, switchOptions);
210
+ currentProvider = nextProvider;
211
+ config.onProviderChanged?.(nextProvider);
212
+ }
213
+ catch (error) {
214
+ select.value = currentProvider;
215
+ config.onError?.(error, nextProvider);
216
+ }
217
+ finally {
218
+ isSwitching = false;
219
+ select.disabled = false;
220
+ }
221
+ };
222
+ select.addEventListener("change", handleChange);
223
+ wrapper.appendChild(select);
224
+ const existing = container.querySelector("[data-voice-provider-dropdown='true']");
225
+ if (existing) {
226
+ existing.remove();
227
+ }
228
+ container.appendChild(wrapper);
229
+ return {
230
+ element: select,
231
+ destroy: () => {
232
+ select.removeEventListener("change", handleChange);
233
+ wrapper.remove();
234
+ },
235
+ setProvider: (provider) => {
236
+ const option = options.find((item) => item.provider === provider);
237
+ if (!option || option.disabled) {
238
+ return;
239
+ }
240
+ currentProvider = provider;
241
+ select.value = provider;
242
+ },
243
+ };
244
+ }
90
245
  export function initNavigationOnMicrophone(config) {
91
246
  if (typeof window === "undefined" || typeof document === "undefined") {
92
247
  throw new Error("Voice navigation requires a browser environment");
@@ -129,11 +284,13 @@ export function initNavigationOnMicrophone(config) {
129
284
  previousUI?.customButtonSelector !== currentUI?.customButtonSelector ||
130
285
  previousUI?.buttonPlacement !== currentUI?.buttonPlacement;
131
286
  console.log("[Voice Navigation] UI config changed:", uiConfigChanged);
287
+ const providerChanged = previousConfig?.speechProvider !== normalizedConfig.speechProvider;
132
288
  if ((wantsVectorSearch &&
133
289
  (!hadVectorSearch ||
134
290
  vectorConfigChanged ||
135
291
  !existingHasVectorSearch)) ||
136
- uiConfigChanged) {
292
+ uiConfigChanged ||
293
+ providerChanged) {
137
294
  console.log("[Voice Navigation] Destroying existing controller and recreating...");
138
295
  try {
139
296
  existingController?.destroy?.();
@@ -0,0 +1,17 @@
1
+ export interface SarvamLanguage {
2
+ code: string;
3
+ name: string;
4
+ nativeName: string;
5
+ script: string;
6
+ stt: boolean;
7
+ tts: boolean;
8
+ }
9
+ export declare const SARVAM_LANGUAGES: SarvamLanguage[];
10
+ export declare const DEFAULT_LANGUAGE = "en-IN";
11
+ export declare const SARVAM_STT_LANGUAGES: string[];
12
+ export declare const SARVAM_TTS_LANGUAGES: string[];
13
+ export declare function isSttSupported(code: string): boolean;
14
+ export declare function isTtsSupported(code: string): boolean;
15
+ export declare function resolveTtsLanguage(code: string): string;
16
+ export declare function normalizeLanguageCode(input?: string | null): string;
17
+ //# sourceMappingURL=languages.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"languages.d.ts","sourceRoot":"","sources":["../src/languages.ts"],"names":[],"mappings":"AAcA,MAAM,WAAW,cAAc;IAE7B,IAAI,EAAE,MAAM,CAAC;IAEb,IAAI,EAAE,MAAM,CAAC;IAEb,UAAU,EAAE,MAAM,CAAC;IAEnB,MAAM,EAAE,MAAM,CAAC;IAEf,GAAG,EAAE,OAAO,CAAC;IAEb,GAAG,EAAE,OAAO,CAAC;CACd;AAMD,eAAO,MAAM,gBAAgB,EAAE,cAAc,EAwB5C,CAAC;AAGF,eAAO,MAAM,gBAAgB,UAAU,CAAC;AAGxC,eAAO,MAAM,oBAAoB,EAAE,MAAM,EAEf,CAAC;AAG3B,eAAO,MAAM,oBAAoB,EAAE,MAAM,EAEf,CAAC;AAuC3B,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAEpD;AAGD,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAEpD;AAOD,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAGvD;AAOD,wBAAgB,qBAAqB,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,CA0BnE"}
@@ -0,0 +1,86 @@
1
+ export const SARVAM_LANGUAGES = [
2
+ { code: "en-IN", name: "English (India)", nativeName: "English", script: "Latin", stt: true, tts: true },
3
+ { code: "hi-IN", name: "Hindi", nativeName: "हिन्दी", script: "Devanagari", stt: true, tts: true },
4
+ { code: "bn-IN", name: "Bengali", nativeName: "বাংলা", script: "Bengali", stt: true, tts: true },
5
+ { code: "ta-IN", name: "Tamil", nativeName: "தமிழ்", script: "Tamil", stt: true, tts: true },
6
+ { code: "te-IN", name: "Telugu", nativeName: "తెలుగు", script: "Telugu", stt: true, tts: true },
7
+ { code: "gu-IN", name: "Gujarati", nativeName: "ગુજરાતી", script: "Gujarati", stt: true, tts: true },
8
+ { code: "kn-IN", name: "Kannada", nativeName: "ಕನ್ನಡ", script: "Kannada", stt: true, tts: true },
9
+ { code: "ml-IN", name: "Malayalam", nativeName: "മലയാളം", script: "Malayalam", stt: true, tts: true },
10
+ { code: "mr-IN", name: "Marathi", nativeName: "मराठी", script: "Devanagari", stt: true, tts: true },
11
+ { code: "pa-IN", name: "Punjabi", nativeName: "ਪੰਜਾਬੀ", script: "Gurmukhi", stt: true, tts: true },
12
+ { code: "od-IN", name: "Odia", nativeName: "ଓଡ଼ିଆ", script: "Odia", stt: true, tts: true },
13
+ { code: "as-IN", name: "Assamese", nativeName: "অসমীয়া", script: "Bengali", stt: true, tts: false },
14
+ { code: "ur-IN", name: "Urdu", nativeName: "اردو", script: "Perso-Arabic", stt: true, tts: false },
15
+ { code: "ne-IN", name: "Nepali", nativeName: "नेपाली", script: "Devanagari", stt: true, tts: false },
16
+ { code: "kok-IN", name: "Konkani", nativeName: "कोंकणी", script: "Devanagari", stt: true, tts: false },
17
+ { code: "ks-IN", name: "Kashmiri", nativeName: "کٲشُر", script: "Perso-Arabic", stt: true, tts: false },
18
+ { code: "sd-IN", name: "Sindhi", nativeName: "سنڌي", script: "Perso-Arabic", stt: true, tts: false },
19
+ { code: "sa-IN", name: "Sanskrit", nativeName: "संस्कृतम्", script: "Devanagari", stt: true, tts: false },
20
+ { code: "sat-IN", name: "Santali", nativeName: "ᱥᱟᱱᱛᱟᱲᱤ", script: "Ol Chiki", stt: true, tts: false },
21
+ { code: "mni-IN", name: "Manipuri", nativeName: "ꯃꯤꯇꯩ ꯂꯣꯟ", script: "Meitei", stt: true, tts: false },
22
+ { code: "brx-IN", name: "Bodo", nativeName: "बड़ो", script: "Devanagari", stt: true, tts: false },
23
+ { code: "mai-IN", name: "Maithili", nativeName: "मैथिली", script: "Devanagari", stt: true, tts: false },
24
+ { code: "doi-IN", name: "Dogri", nativeName: "डोगरी", script: "Devanagari", stt: true, tts: false },
25
+ ];
26
+ export const DEFAULT_LANGUAGE = "en-IN";
27
+ export const SARVAM_STT_LANGUAGES = SARVAM_LANGUAGES.filter((lang) => lang.stt).map((lang) => lang.code);
28
+ export const SARVAM_TTS_LANGUAGES = SARVAM_LANGUAGES.filter((lang) => lang.tts).map((lang) => lang.code);
29
+ const LANGUAGE_BY_CODE = new Map(SARVAM_LANGUAGES.map((lang) => [lang.code, lang]));
30
+ const APP_LOCALE_ALIASES = {
31
+ en: "en-IN",
32
+ hi: "hi-IN",
33
+ bn: "bn-IN",
34
+ ta: "ta-IN",
35
+ te: "te-IN",
36
+ gu: "gu-IN",
37
+ kn: "kn-IN",
38
+ ml: "ml-IN",
39
+ mr: "mr-IN",
40
+ pa: "pa-IN",
41
+ or: "od-IN",
42
+ od: "od-IN",
43
+ as: "as-IN",
44
+ ur: "ur-IN",
45
+ ne: "ne-IN",
46
+ kok: "kok-IN",
47
+ ks: "ks-IN",
48
+ sd: "sd-IN",
49
+ sa: "sa-IN",
50
+ sat: "sat-IN",
51
+ mni: "mni-IN",
52
+ brx: "brx-IN",
53
+ mai: "mai-IN",
54
+ doi: "doi-IN",
55
+ };
56
+ export function isSttSupported(code) {
57
+ return LANGUAGE_BY_CODE.get(normalizeLanguageCode(code))?.stt ?? false;
58
+ }
59
+ export function isTtsSupported(code) {
60
+ return LANGUAGE_BY_CODE.get(normalizeLanguageCode(code))?.tts ?? false;
61
+ }
62
+ export function resolveTtsLanguage(code) {
63
+ const normalized = normalizeLanguageCode(code);
64
+ return LANGUAGE_BY_CODE.get(normalized)?.tts ? normalized : DEFAULT_LANGUAGE;
65
+ }
66
+ export function normalizeLanguageCode(input) {
67
+ if (!input) {
68
+ return DEFAULT_LANGUAGE;
69
+ }
70
+ const trimmed = input.trim();
71
+ if (!trimmed) {
72
+ return DEFAULT_LANGUAGE;
73
+ }
74
+ const lowered = trimmed.toLowerCase();
75
+ for (const lang of SARVAM_LANGUAGES) {
76
+ if (lang.code.toLowerCase() === lowered) {
77
+ return lang.code;
78
+ }
79
+ }
80
+ const baseLocale = lowered.split("-")[0];
81
+ const aliased = APP_LOCALE_ALIASES[lowered] ?? APP_LOCALE_ALIASES[baseLocale];
82
+ if (aliased) {
83
+ return aliased;
84
+ }
85
+ return DEFAULT_LANGUAGE;
86
+ }
@@ -1,15 +1,20 @@
1
1
  import * as sdk from "microsoft-cognitiveservices-speech-sdk";
2
- import type { NavigationCallbacks, AgentActionResponse } from "./types.js";
2
+ import type { NavigationCallbacks, AgentActionResponse, SpeechProvider } from "./types.js";
3
3
  import { AzureSpeechService } from "./services/azure-speech.js";
4
4
  import { BedrockService } from "./services/bedrock.js";
5
+ import { SarvamSpeechService } from "./services/sarvam-speech.js";
5
6
  interface MicrophoneConfig {
6
- azureSpeechService: AzureSpeechService;
7
+ speechProvider?: SpeechProvider;
8
+ azureSpeechService?: AzureSpeechService;
9
+ sarvamSpeechService?: SarvamSpeechService;
7
10
  bedrockService: BedrockService;
8
11
  language?: string;
9
12
  silenceTimeout?: number;
10
13
  }
11
14
  export declare class MicrophoneHandler {
15
+ private speechProvider;
12
16
  private azureSpeechService;
17
+ private sarvamSpeechService;
13
18
  private bedrockService;
14
19
  private language;
15
20
  private silenceTimeout;
@@ -50,6 +55,7 @@ export declare class MicrophoneHandler {
50
55
  disposeRecognizer(): void;
51
56
  createRecognizer(): Promise<sdk.SpeechRecognizer>;
52
57
  startRecording(callbacks?: NavigationCallbacks): Promise<boolean>;
58
+ private startSarvamRecording;
53
59
  stopRecording(): Promise<string>;
54
60
  static isMicrophoneAvailable(): Promise<boolean>;
55
61
  }