@myscheme/voice-navigation-sdk 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/README.md +359 -0
  2. package/dist/actions.d.ts +8 -0
  3. package/dist/actions.d.ts.map +1 -0
  4. package/dist/actions.js +478 -0
  5. package/dist/constants.d.ts +2 -0
  6. package/dist/constants.d.ts.map +1 -0
  7. package/dist/constants.js +1 -0
  8. package/dist/index.d.ts +11 -0
  9. package/dist/index.d.ts.map +1 -0
  10. package/dist/index.js +156 -0
  11. package/dist/microphone-handler.d.ts +47 -0
  12. package/dist/microphone-handler.d.ts.map +1 -0
  13. package/dist/microphone-handler.js +341 -0
  14. package/dist/navigation-controller.d.ts +50 -0
  15. package/dist/navigation-controller.d.ts.map +1 -0
  16. package/dist/navigation-controller.js +782 -0
  17. package/dist/server/index.d.ts +3 -0
  18. package/dist/server/index.d.ts.map +1 -0
  19. package/dist/server/index.js +1 -0
  20. package/dist/server/opensearch-handler.d.ts +52 -0
  21. package/dist/server/opensearch-handler.d.ts.map +1 -0
  22. package/dist/server/opensearch-handler.js +279 -0
  23. package/dist/services/azure-speech.d.ts +13 -0
  24. package/dist/services/azure-speech.d.ts.map +1 -0
  25. package/dist/services/azure-speech.js +33 -0
  26. package/dist/services/bedrock.d.ts +18 -0
  27. package/dist/services/bedrock.d.ts.map +1 -0
  28. package/dist/services/bedrock.js +132 -0
  29. package/dist/services/schemes.d.ts +2 -0
  30. package/dist/services/schemes.d.ts.map +1 -0
  31. package/dist/services/schemes.js +1 -0
  32. package/dist/services/vector-search.d.ts +21 -0
  33. package/dist/services/vector-search.d.ts.map +1 -0
  34. package/dist/services/vector-search.js +181 -0
  35. package/dist/types.d.ts +107 -0
  36. package/dist/types.d.ts.map +1 -0
  37. package/dist/types.js +1 -0
  38. package/dist/ui.d.ts +10 -0
  39. package/dist/ui.d.ts.map +1 -0
  40. package/dist/ui.js +225 -0
  41. package/package.json +55 -0
@@ -0,0 +1,3 @@
1
+ export { createOpenSearchProxyHandler, DEFAULT_OPENSEARCH_PROXY_PATH, } from "./opensearch-handler.js";
2
+ export type { VectorSearchProxyRequest, VectorSearchProxyResponse, } from "./opensearch-handler.js";
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/server/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,4BAA4B,EAC5B,6BAA6B,GAC9B,MAAM,yBAAyB,CAAC;AACjC,YAAY,EACV,wBAAwB,EACxB,yBAAyB,GAC1B,MAAM,yBAAyB,CAAC"}
@@ -0,0 +1 @@
1
+ export { createOpenSearchProxyHandler, DEFAULT_OPENSEARCH_PROXY_PATH, } from "./opensearch-handler.js";
@@ -0,0 +1,52 @@
1
+ import type { IncomingMessage, ServerResponse } from "http";
2
+ import type { OpenSearchConfig } from "../types.js";
3
+ interface OpenSearchCredentials {
4
+ node: string;
5
+ username: string;
6
+ password: string;
7
+ ssl?: OpenSearchConfig["ssl"];
8
+ }
9
+ interface VectorSearchParams {
10
+ index: string;
11
+ vectorField: string;
12
+ size: number;
13
+ numCandidates: number;
14
+ minScore: number;
15
+ sourceFields?: string[];
16
+ queryText?: string;
17
+ }
18
+ export interface VectorSearchProxyRequest {
19
+ vector: number[];
20
+ credentials: OpenSearchCredentials;
21
+ params: VectorSearchParams;
22
+ queryText?: string;
23
+ }
24
+ export interface VectorSearchProxyResponse {
25
+ hits: Array<{
26
+ id: string;
27
+ score: number;
28
+ source: Record<string, unknown>;
29
+ }>;
30
+ }
31
+ type CompatibleRequest = IncomingMessage & {
32
+ body?: unknown;
33
+ method?: string;
34
+ };
35
+ type CompatibleResponse = ServerResponse & {
36
+ status?: (code: number) => CompatibleResponse;
37
+ json?: (payload: unknown) => void;
38
+ setHeader: (name: string, value: string) => void;
39
+ };
40
+ interface CreateOpenSearchProxyHandlerOptions {
41
+ allowedOrigins?: string[];
42
+ cacheClients?: boolean;
43
+ requestTimeoutMs?: number;
44
+ logger?: {
45
+ error: (message: string, meta?: Record<string, unknown>) => void;
46
+ info?: (message: string, meta?: Record<string, unknown>) => void;
47
+ };
48
+ }
49
+ export declare const DEFAULT_OPENSEARCH_PROXY_PATH = "/api/voice-navigation/vector-search";
50
+ export declare const createOpenSearchProxyHandler: (options?: CreateOpenSearchProxyHandlerOptions) => (req: CompatibleRequest, res: CompatibleResponse) => Promise<void>;
51
+ export {};
52
+ //# sourceMappingURL=opensearch-handler.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"opensearch-handler.d.ts","sourceRoot":"","sources":["../../src/server/opensearch-handler.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,MAAM,CAAC;AAG5D,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAEpD,UAAU,qBAAqB;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,GAAG,CAAC,EAAE,gBAAgB,CAAC,KAAK,CAAC,CAAC;CAC/B;AAED,UAAU,kBAAkB;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,aAAa,EAAE,MAAM,CAAC;IACtB,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,wBAAwB;IACvC,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,WAAW,EAAE,qBAAqB,CAAC;IACnC,MAAM,EAAE,kBAAkB,CAAC;IAC3B,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,yBAAyB;IACxC,IAAI,EAAE,KAAK,CAAC;QACV,EAAE,EAAE,MAAM,CAAC;QACX,KAAK,EAAE,MAAM,CAAC;QACd,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;KACjC,CAAC,CAAC;CACJ;AAED,KAAK,iBAAiB,GAAG,eAAe,GAAG;IACzC,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF,KAAK,kBAAkB,GAAG,cAAc,GAAG;IACzC,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,kBAAkB,CAAC;IAC9C,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,CAAC;IAClC,SAAS,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;CAClD,CAAC;AAEF,UAAU,mCAAmC;IAC3C,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;IAC1B,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,MAAM,CAAC,EAAE;QACP,KAAK,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,CAAC;QACjE,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,CAAC;KAClE,CAAC;CACH;AA4RD,eAAO,MAAM,6BAA6B,wCAAiC,CAAC;AAE5E,eAAO,MAAM,4BAA4B,GACvC,UAAS,mCAAwC,MAMnC,KAAK,iBAAiB,EAAE,KAAK,kBAAkB,KAAG,OAAO,CAAC,IAAI,CA6D7E,CAAC"}
@@ -0,0 +1,279 @@
1
+ import { Client } from "@opensearch-project/opensearch";
2
+ import { DEFAULT_VECTOR_SEARCH_API_PATH } from "../constants.js";
3
+ const defaultLogger = {
4
+ error: (message, meta) => {
5
+ console.error(`[VoiceNavigation][OpenSearchProxy] ${message}`, meta);
6
+ },
7
+ info: (message, meta) => {
8
+ console.log(`[VoiceNavigation][OpenSearchProxy] ${message}`, meta);
9
+ },
10
+ };
11
+ const CLIENT_CACHE = new Map();
12
+ const DEFAULT_REQUEST_TIMEOUT_MS = 15000;
13
+ const normalizeRequestTimeoutMs = (value) => {
14
+ if (typeof value === "number" && Number.isFinite(value) && value > 0) {
15
+ return Math.floor(value);
16
+ }
17
+ return DEFAULT_REQUEST_TIMEOUT_MS;
18
+ };
19
+ const getClientCacheKey = (credentials, requestTimeoutMs) => {
20
+ return `${normalizeOpenSearchNode(credentials.node)}::${credentials.username}::${requestTimeoutMs}`;
21
+ };
22
+ const getCachedClient = (credentials, requestTimeoutMs, cacheEnabled) => {
23
+ if (!cacheEnabled) {
24
+ return createClient(credentials, requestTimeoutMs);
25
+ }
26
+ const key = getClientCacheKey(credentials, requestTimeoutMs);
27
+ const existing = CLIENT_CACHE.get(key);
28
+ if (existing) {
29
+ return existing;
30
+ }
31
+ const client = createClient(credentials, requestTimeoutMs);
32
+ CLIENT_CACHE.set(key, client);
33
+ return client;
34
+ };
35
+ const createClient = (credentials, requestTimeoutMs) => {
36
+ return new Client({
37
+ node: normalizeOpenSearchNode(credentials.node),
38
+ auth: {
39
+ username: credentials.username,
40
+ password: credentials.password,
41
+ },
42
+ ssl: credentials.ssl,
43
+ requestTimeout: requestTimeoutMs,
44
+ });
45
+ };
46
+ const normalizeOpenSearchNode = (node) => {
47
+ if (!node || typeof node !== "string") {
48
+ return node;
49
+ }
50
+ try {
51
+ const parsed = new URL(node);
52
+ if (!parsed.username && !parsed.password) {
53
+ return node;
54
+ }
55
+ parsed.username = "";
56
+ parsed.password = "";
57
+ return parsed.toString();
58
+ }
59
+ catch {
60
+ return node;
61
+ }
62
+ };
63
+ const parseJsonBody = async (req) => {
64
+ if (req.body) {
65
+ if (typeof req.body === "string") {
66
+ return req.body ? JSON.parse(req.body) : null;
67
+ }
68
+ if (Buffer.isBuffer(req.body)) {
69
+ const raw = req.body.toString("utf-8");
70
+ return raw ? JSON.parse(raw) : null;
71
+ }
72
+ if (typeof req.body === "object") {
73
+ return req.body;
74
+ }
75
+ }
76
+ const chunks = [];
77
+ for await (const chunk of req) {
78
+ chunks.push(typeof chunk === "string" ? Buffer.from(chunk) : chunk);
79
+ }
80
+ const buffer = Buffer.concat(chunks);
81
+ if (!buffer.length) {
82
+ return null;
83
+ }
84
+ const raw = buffer.toString("utf-8");
85
+ return raw ? JSON.parse(raw) : null;
86
+ };
87
+ const sendJson = (res, statusCode, payload) => {
88
+ if (typeof res.status === "function") {
89
+ res.status(statusCode);
90
+ }
91
+ else {
92
+ res.statusCode = statusCode;
93
+ }
94
+ res.setHeader("Content-Type", "application/json");
95
+ if (typeof res.json === "function") {
96
+ res.json(payload);
97
+ }
98
+ else {
99
+ res.end(JSON.stringify(payload));
100
+ }
101
+ };
102
+ const withTimeout = async (operation, timeoutMs, timeoutMessage) => {
103
+ let timer = null;
104
+ try {
105
+ return await Promise.race([
106
+ operation,
107
+ new Promise((_resolve, reject) => {
108
+ timer = setTimeout(() => {
109
+ reject(new Error(timeoutMessage));
110
+ }, timeoutMs);
111
+ }),
112
+ ]);
113
+ }
114
+ finally {
115
+ if (timer !== null) {
116
+ clearTimeout(timer);
117
+ }
118
+ }
119
+ };
120
+ function validateRequest(payload) {
121
+ if (!payload || typeof payload !== "object") {
122
+ throw new Error("Invalid payload: expected JSON object");
123
+ }
124
+ const { vector, credentials, params } = payload;
125
+ if (!Array.isArray(vector) || vector.length === 0) {
126
+ throw new Error("Invalid payload: 'vector' must be a non-empty array");
127
+ }
128
+ if (!credentials || typeof credentials !== "object") {
129
+ throw new Error("Invalid payload: 'credentials' is required");
130
+ }
131
+ if (!params || typeof params !== "object") {
132
+ throw new Error("Invalid payload: 'params' is required");
133
+ }
134
+ if (!credentials.node || !credentials.username || !credentials.password) {
135
+ throw new Error("Invalid credentials: 'node', 'username', and 'password' are required");
136
+ }
137
+ if (!params.index || !params.vectorField) {
138
+ throw new Error("Invalid params: 'index' and 'vectorField' are required");
139
+ }
140
+ }
141
+ const buildSearchBody = (payload) => {
142
+ const queryText = payload.queryText || payload.params.queryText;
143
+ const vectorField = payload.params.vectorField;
144
+ const size = payload.params.size;
145
+ if (queryText && queryText.trim()) {
146
+ return {
147
+ size: size,
148
+ query: {
149
+ bool: {
150
+ should: [
151
+ {
152
+ knn: {
153
+ [vectorField]: {
154
+ vector: payload.vector,
155
+ k: size,
156
+ },
157
+ },
158
+ },
159
+ {
160
+ multi_match: {
161
+ query: queryText,
162
+ fields: [
163
+ "metadata.scheme_name^5",
164
+ "metadata.scheme_short_name^4",
165
+ "metadata.tags^3",
166
+ "metadata.category^2",
167
+ "text^1",
168
+ ],
169
+ type: "best_fields",
170
+ fuzziness: "AUTO",
171
+ },
172
+ },
173
+ {
174
+ term: {
175
+ "metadata.content_type.keyword": {
176
+ value: "Scheme Description",
177
+ boost: 2.0,
178
+ },
179
+ },
180
+ },
181
+ {
182
+ term: {
183
+ "metadata.content_type.keyword": {
184
+ value: "Scheme Overview",
185
+ boost: 1.8,
186
+ },
187
+ },
188
+ },
189
+ {
190
+ term: {
191
+ "metadata.content_type.keyword": {
192
+ value: "Benefits",
193
+ boost: 1.5,
194
+ },
195
+ },
196
+ },
197
+ ],
198
+ must_not: [],
199
+ },
200
+ },
201
+ _source: payload.params.sourceFields ?? true,
202
+ };
203
+ }
204
+ return {
205
+ size: size,
206
+ query: {
207
+ knn: {
208
+ [vectorField]: {
209
+ vector: payload.vector,
210
+ k: size,
211
+ },
212
+ },
213
+ },
214
+ _source: payload.params.sourceFields ?? true,
215
+ };
216
+ };
217
+ const mapHits = (response) => {
218
+ const hits = response?.body?.hits?.hits ??
219
+ response?.hits?.hits ??
220
+ [];
221
+ if (!Array.isArray(hits)) {
222
+ return [];
223
+ }
224
+ return hits.map((hit) => ({
225
+ id: typeof hit._id === "string" ? hit._id : "",
226
+ score: typeof hit._score === "number" ? hit._score : 0,
227
+ source: hit._source ?? {},
228
+ }));
229
+ };
230
+ export const DEFAULT_OPENSEARCH_PROXY_PATH = DEFAULT_VECTOR_SEARCH_API_PATH;
231
+ export const createOpenSearchProxyHandler = (options = {}) => {
232
+ const logger = options.logger ?? defaultLogger;
233
+ const cacheClients = options.cacheClients !== false;
234
+ const requestTimeoutMs = normalizeRequestTimeoutMs(options.requestTimeoutMs);
235
+ return async (req, res) => {
236
+ try {
237
+ if (!req.method || req.method.toUpperCase() !== "POST") {
238
+ res.setHeader("Allow", "POST");
239
+ sendJson(res, 405, { error: "Method not allowed" });
240
+ return;
241
+ }
242
+ if (options.allowedOrigins && options.allowedOrigins.length > 0) {
243
+ const originHeader = req.headers?.origin || "";
244
+ if (originHeader && !options.allowedOrigins.includes(originHeader)) {
245
+ sendJson(res, 403, { error: "Origin not allowed" });
246
+ return;
247
+ }
248
+ }
249
+ const payload = (await parseJsonBody(req));
250
+ validateRequest(payload);
251
+ const client = getCachedClient(payload.credentials, requestTimeoutMs, cacheClients);
252
+ const searchBody = buildSearchBody(payload);
253
+ const response = await withTimeout(client.search({
254
+ index: payload.params.index,
255
+ body: searchBody,
256
+ }), requestTimeoutMs, `OpenSearch request timed out after ${requestTimeoutMs}ms`);
257
+ const hits = mapHits(response);
258
+ sendJson(res, 200, { hits });
259
+ }
260
+ catch (error) {
261
+ const errorMessage = error.message;
262
+ const isTimeout = /timed out/i.test(errorMessage);
263
+ logger.error("OpenSearch proxy request failed", {
264
+ message: errorMessage,
265
+ timeout: isTimeout,
266
+ });
267
+ const statusCode = error instanceof SyntaxError || /invalid payload/i.test(error.message)
268
+ ? 400
269
+ : isTimeout
270
+ ? 504
271
+ : 500;
272
+ sendJson(res, statusCode, {
273
+ error: statusCode === 400 || statusCode === 504
274
+ ? errorMessage
275
+ : "OpenSearch proxy error",
276
+ });
277
+ }
278
+ };
279
+ };
@@ -0,0 +1,13 @@
1
+ import type { AzureTokenResponse } from "../types.js";
2
+ export interface AzureSpeechConfig {
3
+ subscriptionKey: string;
4
+ region: string;
5
+ }
6
+ export declare class AzureSpeechService {
7
+ private subscriptionKey;
8
+ private region;
9
+ constructor(config: AzureSpeechConfig);
10
+ fetchToken(): Promise<AzureTokenResponse>;
11
+ static create(config: AzureSpeechConfig): AzureSpeechService;
12
+ }
13
+ //# sourceMappingURL=azure-speech.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"azure-speech.d.ts","sourceRoot":"","sources":["../../src/services/azure-speech.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAEtD,MAAM,WAAW,iBAAiB;IAChC,eAAe,EAAE,MAAM,CAAC;IACxB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,qBAAa,kBAAkB;IAC7B,OAAO,CAAC,eAAe,CAAS;IAChC,OAAO,CAAC,MAAM,CAAS;gBAEX,MAAM,EAAE,iBAAiB;IAQ/B,UAAU,IAAI,OAAO,CAAC,kBAAkB,CAAC;IAiC/C,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,iBAAiB,GAAG,kBAAkB;CAG7D"}
@@ -0,0 +1,33 @@
1
+ export class AzureSpeechService {
2
+ constructor(config) {
3
+ this.subscriptionKey = config.subscriptionKey;
4
+ this.region = config.region;
5
+ }
6
+ async fetchToken() {
7
+ const url = `https://${this.region}.api.cognitive.microsoft.com/sts/v1.0/issueToken`;
8
+ try {
9
+ const response = await fetch(url, {
10
+ method: "POST",
11
+ headers: {
12
+ "Ocp-Apim-Subscription-Key": this.subscriptionKey,
13
+ "Content-Type": "application/x-www-form-urlencoded",
14
+ },
15
+ });
16
+ if (!response.ok) {
17
+ throw new Error(`Failed to fetch token: ${response.status} ${response.statusText}`);
18
+ }
19
+ const token = await response.text();
20
+ return {
21
+ token,
22
+ region: this.region,
23
+ };
24
+ }
25
+ catch (error) {
26
+ console.error("Failed to fetch Azure speech token:", error);
27
+ throw error;
28
+ }
29
+ }
30
+ static create(config) {
31
+ return new AzureSpeechService(config);
32
+ }
33
+ }
@@ -0,0 +1,18 @@
1
+ import type { AgentActionResponse } from "../types.js";
2
+ export interface BedrockConfig {
3
+ region: string;
4
+ accessKeyId: string;
5
+ secretAccessKey: string;
6
+ modelId: string;
7
+ embeddingModelId?: string | null;
8
+ }
9
+ export declare class BedrockService {
10
+ private client;
11
+ private actionModelId;
12
+ private embeddingModelId;
13
+ constructor(config: BedrockConfig);
14
+ extractAction(userInput: string): Promise<AgentActionResponse>;
15
+ static create(config: BedrockConfig): BedrockService;
16
+ embedText(text: string): Promise<number[]>;
17
+ }
18
+ //# sourceMappingURL=bedrock.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bedrock.d.ts","sourceRoot":"","sources":["../../src/services/bedrock.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,mBAAmB,EAAmB,MAAM,aAAa,CAAC;AAExE,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;IACpB,eAAe,EAAE,MAAM,CAAC;IACxB,OAAO,EAAE,MAAM,CAAC;IAChB,gBAAgB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAClC;AAmBD,qBAAa,cAAc;IACzB,OAAO,CAAC,MAAM,CAAuB;IACrC,OAAO,CAAC,aAAa,CAAS;IAC9B,OAAO,CAAC,gBAAgB,CAAgB;gBAE5B,MAAM,EAAE,aAAa;IAuB3B,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,mBAAmB,CAAC;IAiEpE,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,aAAa,GAAG,cAAc;IAO9C,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;CAyDjD"}
@@ -0,0 +1,132 @@
1
+ import { BedrockRuntimeClient, InvokeModelCommand, } from "@aws-sdk/client-bedrock-runtime";
2
+ const SYSTEM_PROMPT = `You are an action extraction assistant.
3
+
4
+ VERY IMPORTANT INSTRUCTIONS:
5
+ - RESPOND ONLY IN THE SPECIFIED JSON FORMAT.
6
+ - ALWAYS RETURN A SINGLE JSON OBJECT.
7
+ - IF THE USER INPUT DOES NOT CLEARLY INDICATE AN ACTION, RESPOND WITH {"action": "unknown"}.
8
+ - DO NOT RESPOND WITH ANYTHING OTHER THAN THE JSON OBJECT.
9
+ - CHOOSE THE MOST SPECIFIC ACTION FROM THE ALLOWED LIST. IF MULTIPLE ACTIONS APPLY, CHOOSE THE ONE THAT BEST MATCHES THE USER REQUEST.
10
+ - DO NOT INVENT YOUR OWN ACTION. IT MUST BE ONE OF THE GIVEN ACTIONS OR "unknown".
11
+ - WHEN THE USER CLEARLY EXPRESSES A DESIRE TO SEARCH OR FIND INFORMATION, RESPOND WITH {"action": "search_content", "query": "concise keywords"}.
12
+ - WHEN YOU RETURN THE "search_content" ACTION, THE "query" FIELD MUST BE A SHORT PHRASE CAPTURING THE USER'S INTENT WITHOUT FILLER WORDS.
13
+
14
+ The only possible actions are:
15
+ "zoom_in", "zoom_out", "scroll_up", "scroll_down", "scroll_left", "scroll_right", "page_up", "page_down", "scroll_top", "scroll_bottom", "go_back", "go_forward", "reload_page", "print_page", "copy_url", "open_menu", "close_menu", "focus_search", "toggle_fullscreen", "exit_fullscreen", "play_media", "pause_media", "mute_media", "unmute_media", "navigate_home", "search_content", "navigate_search", "navigate_faqs", "navigate_help", "navigate_contact", "navigate_about", "navigate_screen_reader", "navigate_accessibility", "navigate_disclaimer", "navigate_terms_conditions", "stop".`;
16
+ const DEFAULT_EMBEDDING_MODEL_ID = "cohere.embed-multilingual-v3";
17
+ export class BedrockService {
18
+ constructor(config) {
19
+ this.client = new BedrockRuntimeClient({
20
+ region: config.region,
21
+ credentials: {
22
+ accessKeyId: config.accessKeyId,
23
+ secretAccessKey: config.secretAccessKey,
24
+ },
25
+ });
26
+ this.actionModelId = config.modelId;
27
+ if (config.embeddingModelId === null) {
28
+ this.embeddingModelId = null;
29
+ }
30
+ else if (typeof config.embeddingModelId === "string") {
31
+ const trimmed = config.embeddingModelId.trim();
32
+ this.embeddingModelId = trimmed.length > 0 ? trimmed : DEFAULT_EMBEDDING_MODEL_ID;
33
+ }
34
+ else {
35
+ this.embeddingModelId = DEFAULT_EMBEDDING_MODEL_ID;
36
+ }
37
+ }
38
+ async extractAction(userInput) {
39
+ const body = {
40
+ anthropic_version: "bedrock-2023-05-31",
41
+ max_tokens: 70,
42
+ system: SYSTEM_PROMPT,
43
+ messages: [
44
+ {
45
+ role: "user",
46
+ content: [
47
+ {
48
+ type: "text",
49
+ text: `User input: "${userInput}". Identify the intended action from the allowed list and respond ONLY with a JSON object. If the user input is unclear, respond with {"action": "unknown"}. Include no additional text.`,
50
+ },
51
+ ],
52
+ },
53
+ ],
54
+ };
55
+ const input = {
56
+ modelId: this.actionModelId,
57
+ contentType: "application/json",
58
+ accept: "application/json",
59
+ body: JSON.stringify(body),
60
+ };
61
+ try {
62
+ const command = new InvokeModelCommand(input);
63
+ const response = await this.client.send(command);
64
+ if (!response.body) {
65
+ throw new Error("No response body from Bedrock");
66
+ }
67
+ const responseBody = JSON.parse(new TextDecoder().decode(response.body));
68
+ let rawText = "";
69
+ if (responseBody.content && responseBody.content.length > 0) {
70
+ rawText = responseBody.content[0].text || "";
71
+ }
72
+ if (!rawText) {
73
+ return { action: "unknown" };
74
+ }
75
+ try {
76
+ const parsed = JSON.parse(rawText);
77
+ return parsed;
78
+ }
79
+ catch (parseError) {
80
+ console.error("Failed to parse Bedrock response:", rawText, parseError);
81
+ return { action: "unknown" };
82
+ }
83
+ }
84
+ catch (error) {
85
+ console.error("Bedrock API error:", error);
86
+ throw error;
87
+ }
88
+ }
89
+ static create(config) {
90
+ return new BedrockService(config);
91
+ }
92
+ async embedText(text) {
93
+ const prompt = text.trim();
94
+ if (!prompt) {
95
+ return [];
96
+ }
97
+ if (!this.embeddingModelId) {
98
+ throw new Error("Bedrock embedding model is not configured");
99
+ }
100
+ const payload = {
101
+ inputText: prompt,
102
+ };
103
+ const input = {
104
+ modelId: this.embeddingModelId,
105
+ contentType: "application/json",
106
+ accept: "application/json",
107
+ body: JSON.stringify(payload),
108
+ };
109
+ try {
110
+ const command = new InvokeModelCommand(input);
111
+ const response = await this.client.send(command);
112
+ if (!response.body) {
113
+ throw new Error("No response body from Bedrock embedding request");
114
+ }
115
+ const decoded = JSON.parse(new TextDecoder().decode(response.body));
116
+ const embedding = decoded.embedding ||
117
+ decoded.vector ||
118
+ (Array.isArray(decoded.embeddings)
119
+ ? decoded.embeddings
120
+ : decoded.embeddings?.values) ||
121
+ decoded.results?.find((item) => Array.isArray(item.embedding))?.embedding;
122
+ if (!Array.isArray(embedding)) {
123
+ throw new Error("Unexpected embedding response structure");
124
+ }
125
+ return embedding.map((value) => Number(value));
126
+ }
127
+ catch (error) {
128
+ console.error("Bedrock embedding error:", error);
129
+ throw error;
130
+ }
131
+ }
132
+ }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=schemes.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schemes.d.ts","sourceRoot":"","sources":["../../src/services/schemes.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,CAAC"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,21 @@
1
+ import type { OpenSearchConfig } from "../types.js";
2
+ export interface VectorSearchHit {
3
+ id: string;
4
+ score: number;
5
+ source: Record<string, unknown>;
6
+ }
7
+ export declare class VectorSearchService {
8
+ private readonly config;
9
+ private readonly endpoint;
10
+ constructor(config: OpenSearchConfig);
11
+ search(vector: number[], queryText?: string): Promise<VectorSearchHit[]>;
12
+ getSourceUrl(hit: VectorSearchHit): string | null;
13
+ findBestMatchWithUrl(hits: VectorSearchHit[]): {
14
+ hit: VectorSearchHit;
15
+ url: string;
16
+ } | null;
17
+ private buildRequestPayload;
18
+ private resolveEndpoint;
19
+ private safeReadText;
20
+ }
21
+ //# sourceMappingURL=vector-search.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vector-search.d.ts","sourceRoot":"","sources":["../../src/services/vector-search.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAEpD,MAAM,WAAW,eAAe;IAC9B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACjC;AAwCD,qBAAa,mBAAmB;IAC9B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAA6B;IACpD,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;gBAEtB,MAAM,EAAE,gBAAgB;IA6B9B,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,EAAE,CAAC;IAyF9E,YAAY,CAAC,GAAG,EAAE,eAAe,GAAG,MAAM,GAAG,IAAI;IAoCjD,oBAAoB,CAAC,IAAI,EAAE,eAAe,EAAE,GAAG;QAC7C,GAAG,EAAE,eAAe,CAAC;QACrB,GAAG,EAAE,MAAM,CAAC;KACb,GAAG,IAAI;IAaR,OAAO,CAAC,mBAAmB;IAsB3B,OAAO,CAAC,eAAe;YAkBT,YAAY;CAQ3B"}