@missionsquad/browserai 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 (146) hide show
  1. package/LICENSE +15 -0
  2. package/README.md +229 -0
  3. package/dist/audio/clip.d.ts +51 -0
  4. package/dist/audio/clip.d.ts.map +1 -0
  5. package/dist/audio/clip.js +71 -0
  6. package/dist/audio/clip.js.map +1 -0
  7. package/dist/audio/decode.d.ts +7 -0
  8. package/dist/audio/decode.d.ts.map +1 -0
  9. package/dist/audio/decode.js +19 -0
  10. package/dist/audio/decode.js.map +1 -0
  11. package/dist/audio/pcm.d.ts +13 -0
  12. package/dist/audio/pcm.d.ts.map +1 -0
  13. package/dist/audio/pcm.js +66 -0
  14. package/dist/audio/pcm.js.map +1 -0
  15. package/dist/audio/recorder.d.ts +40 -0
  16. package/dist/audio/recorder.d.ts.map +1 -0
  17. package/dist/audio/recorder.js +127 -0
  18. package/dist/audio/recorder.js.map +1 -0
  19. package/dist/audio/streaming-player.d.ts +29 -0
  20. package/dist/audio/streaming-player.d.ts.map +1 -0
  21. package/dist/audio/streaming-player.js +82 -0
  22. package/dist/audio/streaming-player.js.map +1 -0
  23. package/dist/client.d.ts +195 -0
  24. package/dist/client.d.ts.map +1 -0
  25. package/dist/client.js +638 -0
  26. package/dist/client.js.map +1 -0
  27. package/dist/config.d.ts +81 -0
  28. package/dist/config.d.ts.map +1 -0
  29. package/dist/config.js +19 -0
  30. package/dist/config.js.map +1 -0
  31. package/dist/errors.d.ts +52 -0
  32. package/dist/errors.d.ts.map +1 -0
  33. package/dist/errors.js +74 -0
  34. package/dist/errors.js.map +1 -0
  35. package/dist/events.d.ts +18 -0
  36. package/dist/events.d.ts.map +1 -0
  37. package/dist/events.js +43 -0
  38. package/dist/events.js.map +1 -0
  39. package/dist/generation.d.ts +12 -0
  40. package/dist/generation.d.ts.map +1 -0
  41. package/dist/generation.js +30 -0
  42. package/dist/generation.js.map +1 -0
  43. package/dist/hardware.d.ts +23 -0
  44. package/dist/hardware.d.ts.map +1 -0
  45. package/dist/hardware.js +150 -0
  46. package/dist/hardware.js.map +1 -0
  47. package/dist/history.d.ts +81 -0
  48. package/dist/history.d.ts.map +1 -0
  49. package/dist/history.js +193 -0
  50. package/dist/history.js.map +1 -0
  51. package/dist/index.d.ts +51 -0
  52. package/dist/index.d.ts.map +1 -0
  53. package/dist/index.js +50 -0
  54. package/dist/index.js.map +1 -0
  55. package/dist/inference.d.ts +89 -0
  56. package/dist/inference.d.ts.map +1 -0
  57. package/dist/inference.js +411 -0
  58. package/dist/inference.js.map +1 -0
  59. package/dist/json.d.ts +22 -0
  60. package/dist/json.d.ts.map +1 -0
  61. package/dist/json.js +145 -0
  62. package/dist/json.js.map +1 -0
  63. package/dist/metrics.d.ts +18 -0
  64. package/dist/metrics.d.ts.map +1 -0
  65. package/dist/metrics.js +59 -0
  66. package/dist/metrics.js.map +1 -0
  67. package/dist/model-cache.d.ts +49 -0
  68. package/dist/model-cache.d.ts.map +1 -0
  69. package/dist/model-cache.js +511 -0
  70. package/dist/model-cache.js.map +1 -0
  71. package/dist/model-source.d.ts +27 -0
  72. package/dist/model-source.d.ts.map +1 -0
  73. package/dist/model-source.js +110 -0
  74. package/dist/model-source.js.map +1 -0
  75. package/dist/models.d.ts +103 -0
  76. package/dist/models.d.ts.map +1 -0
  77. package/dist/models.js +1052 -0
  78. package/dist/models.js.map +1 -0
  79. package/dist/multimodal.d.ts +261 -0
  80. package/dist/multimodal.d.ts.map +1 -0
  81. package/dist/multimodal.js +506 -0
  82. package/dist/multimodal.js.map +1 -0
  83. package/dist/prompt.d.ts +13 -0
  84. package/dist/prompt.d.ts.map +1 -0
  85. package/dist/prompt.js +32 -0
  86. package/dist/prompt.js.map +1 -0
  87. package/dist/proxy-verify.d.ts +10 -0
  88. package/dist/proxy-verify.d.ts.map +1 -0
  89. package/dist/proxy-verify.js +98 -0
  90. package/dist/proxy-verify.js.map +1 -0
  91. package/dist/schema-validator.d.ts +6 -0
  92. package/dist/schema-validator.d.ts.map +1 -0
  93. package/dist/schema-validator.js +104 -0
  94. package/dist/schema-validator.js.map +1 -0
  95. package/dist/schema.d.ts +98 -0
  96. package/dist/schema.d.ts.map +1 -0
  97. package/dist/schema.js +115 -0
  98. package/dist/schema.js.map +1 -0
  99. package/dist/text-utils.d.ts +8 -0
  100. package/dist/text-utils.d.ts.map +1 -0
  101. package/dist/text-utils.js +14 -0
  102. package/dist/text-utils.js.map +1 -0
  103. package/dist/voice/helpers.d.ts +35 -0
  104. package/dist/voice/helpers.d.ts.map +1 -0
  105. package/dist/voice/helpers.js +49 -0
  106. package/dist/voice/helpers.js.map +1 -0
  107. package/dist/voice/voice-pipeline.d.ts +155 -0
  108. package/dist/voice/voice-pipeline.d.ts.map +1 -0
  109. package/dist/voice/voice-pipeline.js +407 -0
  110. package/dist/voice/voice-pipeline.js.map +1 -0
  111. package/dist/webllm.worker.d.ts +2 -0
  112. package/dist/webllm.worker.d.ts.map +1 -0
  113. package/dist/webllm.worker.js +8 -0
  114. package/dist/webllm.worker.js.map +1 -0
  115. package/package.json +62 -0
  116. package/src/audio/clip.ts +94 -0
  117. package/src/audio/decode.ts +19 -0
  118. package/src/audio/pcm.ts +62 -0
  119. package/src/audio/recorder.ts +146 -0
  120. package/src/audio/streaming-player.ts +99 -0
  121. package/src/client.ts +784 -0
  122. package/src/config.ts +104 -0
  123. package/src/errors.ts +81 -0
  124. package/src/events.ts +53 -0
  125. package/src/generation.ts +39 -0
  126. package/src/hardware.ts +193 -0
  127. package/src/history.ts +257 -0
  128. package/src/index.ts +203 -0
  129. package/src/inference.ts +579 -0
  130. package/src/json.ts +169 -0
  131. package/src/metrics.ts +63 -0
  132. package/src/model-cache.ts +637 -0
  133. package/src/model-source.ts +142 -0
  134. package/src/models.ts +1171 -0
  135. package/src/multimodal.ts +697 -0
  136. package/src/prompt.ts +33 -0
  137. package/src/proxy-verify.ts +113 -0
  138. package/src/schema-validator.ts +117 -0
  139. package/src/schema.ts +118 -0
  140. package/src/text-utils.ts +12 -0
  141. package/src/voice/helpers.ts +59 -0
  142. package/src/voice/voice-pipeline.ts +513 -0
  143. package/src/webllm.worker.ts +9 -0
  144. package/worker-template/README.md +39 -0
  145. package/worker-template/cloudflare-worker.ts +389 -0
  146. package/worker-template/wrangler.jsonc +19 -0
@@ -0,0 +1,98 @@
1
+ import { ProxyVerificationError } from "./errors.js";
2
+ /**
3
+ * Pre-load health checks for a deployed proxy Worker (the `worker-template/` in this package).
4
+ * Failing fast with an actionable message beats letting an engine surface an opaque artifact
5
+ * fetch error hundreds of megabytes into a download.
6
+ */
7
+ /** The X-Proxy-Worker prefixes accepted as "this looks like a compatible proxy Worker". */
8
+ const KNOWN_WORKER_PREFIXES = ["browserai-proxy/", "browser-json-llm-app/"];
9
+ async function fetchWorkerHealth(proxyOrigin) {
10
+ const healthResponse = await fetch(new URL("/__worker-health", proxyOrigin), { cache: "no-store" });
11
+ if (!healthResponse.ok) {
12
+ throw new ProxyVerificationError(`Proxy Worker is not active: ${proxyOrigin}/__worker-health returned ${healthResponse.status}. Deploy the worker-template with \`npx wrangler deploy\`.`);
13
+ }
14
+ return healthResponse;
15
+ }
16
+ /** Verify the proxy Worker is live and can serve the selected WebLLM model's config + wasm library. */
17
+ export async function verifyWebLLMProxy(proxyOrigin, modelId, appConfig) {
18
+ const healthResponse = await fetchWorkerHealth(proxyOrigin);
19
+ const healthText = await healthResponse.text().catch(() => "");
20
+ let healthBody = healthText;
21
+ try {
22
+ healthBody = JSON.parse(healthText);
23
+ }
24
+ catch {
25
+ // Static HTML/old Worker response stays raw for diagnostics.
26
+ }
27
+ const workerHeader = healthResponse.headers.get("X-Proxy-Worker") ?? "";
28
+ const healthObject = typeof healthBody === "object" && healthBody !== null ? healthBody : {};
29
+ const workerLooksCurrent = KNOWN_WORKER_PREFIXES.some((prefix) => workerHeader.startsWith(prefix)) ||
30
+ (healthObject.ok === true && typeof healthObject.version === "string");
31
+ if (!workerLooksCurrent) {
32
+ throw new ProxyVerificationError(`Proxy Worker health check did not look like a compatible Worker. Status ${healthResponse.status}, X-Proxy-Worker: ${workerHeader || "missing"}.`);
33
+ }
34
+ const selectedRecord = appConfig.model_list.find((record) => record.model_id === modelId);
35
+ if (!selectedRecord) {
36
+ throw new ProxyVerificationError(`Model ${modelId} is not present in the WebLLM AppConfig.`);
37
+ }
38
+ const configProbeUrl = buildModelConfigProbeUrl(selectedRecord, proxyOrigin);
39
+ configProbeUrl.searchParams.set("probe", String(Date.now()));
40
+ const configProbeResponse = await fetch(configProbeUrl, { cache: "no-store" });
41
+ const configProxyHeader = configProbeResponse.headers.get("X-Proxy-Worker");
42
+ if (!configProbeResponse.ok || (configProbeUrl.origin === new URL(proxyOrigin).origin && !configProxyHeader)) {
43
+ throw new ProxyVerificationError(`Proxy /hf model-config probe failed for ${modelId}: ${configProbeResponse.status}. URL: ${configProbeUrl.pathname}. ${await readResponseBodyForError(configProbeResponse)}`);
44
+ }
45
+ const modelLib = selectedRecord.model_lib;
46
+ if (modelLib && modelLib.startsWith(proxyOrigin)) {
47
+ const modelLibProbeUrl = new URL(modelLib);
48
+ modelLibProbeUrl.searchParams.set("probe", String(Date.now()));
49
+ const modelLibProbeResponse = await fetch(modelLibProbeUrl, { method: "HEAD", cache: "no-store" });
50
+ const libProxyHeader = modelLibProbeResponse.headers.get("X-Proxy-Worker");
51
+ if (!modelLibProbeResponse.ok || !libProxyHeader) {
52
+ throw new ProxyVerificationError(`Proxy model-library probe failed for ${modelId}: ${modelLibProbeResponse.status}. URL: ${modelLibProbeUrl.pathname}.`);
53
+ }
54
+ }
55
+ }
56
+ function buildModelConfigProbeUrl(record, proxyOrigin) {
57
+ const modelUrl = new URL(record.model, proxyOrigin);
58
+ const path = modelUrl.pathname.replace(/\/+$/, "");
59
+ if (/\/resolve\/[^/]+(?:\/|$)/.test(path)) {
60
+ const base = new URL(modelUrl.href.endsWith("/") ? modelUrl.href : `${modelUrl.href}/`);
61
+ return new URL("mlc-chat-config.json", base);
62
+ }
63
+ return new URL(`${path}/resolve/main/mlc-chat-config.json`, modelUrl.origin);
64
+ }
65
+ /** Verify the proxy Worker can serve a Transformers.js repo's config through /hf-transformers/. */
66
+ export async function verifyTransformersProxy(proxyOrigin, modelId) {
67
+ await fetchWorkerHealth(proxyOrigin);
68
+ const { owner, repo } = splitHuggingFaceRepoId(modelId);
69
+ let lastStatus = "not fetched";
70
+ for (const file of ["generation_config.json", "config.json"]) {
71
+ const url = new URL(`/hf-transformers/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}/resolve/main/${file}`, proxyOrigin);
72
+ url.searchParams.set("probe", String(Date.now()));
73
+ const response = await fetch(url, { method: "HEAD", cache: "no-store" });
74
+ const proxyHeader = response.headers.get("X-Proxy-Worker");
75
+ lastStatus = `${url.pathname}: ${response.status}, X-Proxy-Worker=${proxyHeader ?? "missing"}`;
76
+ if (response.ok && proxyHeader)
77
+ return;
78
+ }
79
+ throw new ProxyVerificationError(`Proxy Transformers.js probe failed for ${modelId}. Last status: ${lastStatus}`);
80
+ }
81
+ export function splitHuggingFaceRepoId(modelId) {
82
+ const parts = modelId.split("/");
83
+ if (parts.length < 2)
84
+ throw new ProxyVerificationError(`Expected a Hugging Face repo ID in owner/repo form, got ${modelId}`);
85
+ return { owner: parts[0], repo: parts.slice(1).join("/") };
86
+ }
87
+ async function readResponseBodyForError(response) {
88
+ const text = await response.text().catch(() => "");
89
+ if (!text)
90
+ return `${response.status} ${response.statusText}`;
91
+ try {
92
+ return JSON.stringify(JSON.parse(text));
93
+ }
94
+ catch {
95
+ return text.slice(0, 300);
96
+ }
97
+ }
98
+ //# sourceMappingURL=proxy-verify.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"proxy-verify.js","sourceRoot":"","sources":["../src/proxy-verify.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Lightweight JSON Schema validator for the subset used by this demo. It intentionally avoids
3
+ * pulling in a large validation dependency so npm install stays fast.
4
+ */
5
+ export declare function validateAgainstJsonSchema(value: unknown, schema: unknown): string[];
6
+ //# sourceMappingURL=schema-validator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema-validator.d.ts","sourceRoot":"","sources":["../src/schema-validator.ts"],"names":[],"mappings":"AAEA;;;GAGG;AACH,wBAAgB,yBAAyB,CAAC,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,GAAG,MAAM,EAAE,CAKnF"}
@@ -0,0 +1,104 @@
1
+ /**
2
+ * Lightweight JSON Schema validator for the subset used by this demo. It intentionally avoids
3
+ * pulling in a large validation dependency so npm install stays fast.
4
+ */
5
+ export function validateAgainstJsonSchema(value, schema) {
6
+ if (!isObject(schema)) {
7
+ return ["Schema must be a JSON object."];
8
+ }
9
+ return validateNode(value, schema, "$", []);
10
+ }
11
+ function validateNode(value, schema, path, errors) {
12
+ const allowedTypes = normalizeTypeList(schema.type);
13
+ if (allowedTypes.length > 0 && !allowedTypes.some((type) => matchesJsonType(value, type))) {
14
+ errors.push(`${path} must be ${allowedTypes.join(" or ")}.`);
15
+ return errors;
16
+ }
17
+ const enumValues = schema.enum;
18
+ if (Array.isArray(enumValues) && !enumValues.some((candidate) => deepEqual(candidate, value))) {
19
+ errors.push(`${path} must be one of ${enumValues.map(String).join(", ")}.`);
20
+ }
21
+ if (typeof value === "string") {
22
+ if (typeof schema.minLength === "number" && value.length < schema.minLength) {
23
+ errors.push(`${path} must contain at least ${schema.minLength} characters.`);
24
+ }
25
+ if (typeof schema.maxLength === "number" && value.length > schema.maxLength) {
26
+ errors.push(`${path} must contain no more than ${schema.maxLength} characters.`);
27
+ }
28
+ }
29
+ if (typeof value === "number") {
30
+ if (typeof schema.minimum === "number" && value < schema.minimum) {
31
+ errors.push(`${path} must be >= ${schema.minimum}.`);
32
+ }
33
+ if (typeof schema.maximum === "number" && value > schema.maximum) {
34
+ errors.push(`${path} must be <= ${schema.maximum}.`);
35
+ }
36
+ }
37
+ if (Array.isArray(value)) {
38
+ if (typeof schema.minItems === "number" && value.length < schema.minItems) {
39
+ errors.push(`${path} must contain at least ${schema.minItems} items.`);
40
+ }
41
+ if (typeof schema.maxItems === "number" && value.length > schema.maxItems) {
42
+ errors.push(`${path} must contain no more than ${schema.maxItems} items.`);
43
+ }
44
+ if (isObject(schema.items)) {
45
+ value.forEach((item, index) => validateNode(item, schema.items, `${path}[${index}]`, errors));
46
+ }
47
+ }
48
+ if (isObject(value)) {
49
+ const properties = isObject(schema.properties) ? schema.properties : {};
50
+ const required = Array.isArray(schema.required) ? schema.required.filter((item) => typeof item === "string") : [];
51
+ for (const key of required) {
52
+ if (!(key in value)) {
53
+ errors.push(`${path}.${key} is required.`);
54
+ }
55
+ }
56
+ for (const [key, propertySchema] of Object.entries(properties)) {
57
+ if (key in value && isObject(propertySchema)) {
58
+ validateNode(value[key], propertySchema, `${path}.${key}`, errors);
59
+ }
60
+ }
61
+ if (schema.additionalProperties === false) {
62
+ for (const key of Object.keys(value)) {
63
+ if (!(key in properties)) {
64
+ errors.push(`${path}.${key} is not allowed by additionalProperties:false.`);
65
+ }
66
+ }
67
+ }
68
+ }
69
+ return errors;
70
+ }
71
+ function normalizeTypeList(type) {
72
+ if (typeof type === "string")
73
+ return [type];
74
+ if (Array.isArray(type))
75
+ return type.filter((item) => typeof item === "string");
76
+ return [];
77
+ }
78
+ function matchesJsonType(value, type) {
79
+ switch (type) {
80
+ case "object":
81
+ return isObject(value);
82
+ case "array":
83
+ return Array.isArray(value);
84
+ case "string":
85
+ return typeof value === "string";
86
+ case "number":
87
+ return typeof value === "number" && Number.isFinite(value);
88
+ case "integer":
89
+ return Number.isInteger(value);
90
+ case "boolean":
91
+ return typeof value === "boolean";
92
+ case "null":
93
+ return value === null;
94
+ default:
95
+ return true;
96
+ }
97
+ }
98
+ function isObject(value) {
99
+ return typeof value === "object" && value !== null && !Array.isArray(value);
100
+ }
101
+ function deepEqual(a, b) {
102
+ return JSON.stringify(a) === JSON.stringify(b);
103
+ }
104
+ //# sourceMappingURL=schema-validator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema-validator.js","sourceRoot":"","sources":["../src/schema-validator.ts"],"names":[],"mappings":"AAEA;;;GAGG;AACH,MAAM,UAAU,yBAAyB,CAAC,KAAc,EAAE,MAAe;IACvE,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;QACtB,OAAO,CAAC,+BAA+B,CAAC,CAAC;IAC3C,CAAC;IACD,OAAO,YAAY,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,EAAE,CAAC,CAAC;AAC9C,CAAC;AAED,SAAS,YAAY,CAAC,KAAc,EAAE,MAAkB,EAAE,IAAY,EAAE,MAAgB;IACtF,MAAM,YAAY,GAAG,iBAAiB,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IACpD,IAAI,YAAY,CAAC,MAAM,GAAG,CAAC,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,eAAe,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC,EAAE,CAAC;QAC1F,MAAM,CAAC,IAAI,CAAC,GAAG,IAAI,YAAY,YAAY,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QAC7D,OAAO,MAAM,CAAC;IAChB,CAAC;IAED,MAAM,UAAU,GAAG,MAAM,CAAC,IAAI,CAAC;IAC/B,IAAI,KAAK,CAAC,OAAO,CAAC,UAAU,CAAC,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,SAAS,CAAC,SAAS,EAAE,KAAK,CAAC,CAAC,EAAE,CAAC;QAC9F,MAAM,CAAC,IAAI,CAAC,GAAG,IAAI,mBAAmB,UAAU,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAC9E,CAAC;IAED,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC9B,IAAI,OAAO,MAAM,CAAC,SAAS,KAAK,QAAQ,IAAI,KAAK,CAAC,MAAM,GAAG,MAAM,CAAC,SAAS,EAAE,CAAC;YAC5E,MAAM,CAAC,IAAI,CAAC,GAAG,IAAI,0BAA0B,MAAM,CAAC,SAAS,cAAc,CAAC,CAAC;QAC/E,CAAC;QACD,IAAI,OAAO,MAAM,CAAC,SAAS,KAAK,QAAQ,IAAI,KAAK,CAAC,MAAM,GAAG,MAAM,CAAC,SAAS,EAAE,CAAC;YAC5E,MAAM,CAAC,IAAI,CAAC,GAAG,IAAI,8BAA8B,MAAM,CAAC,SAAS,cAAc,CAAC,CAAC;QACnF,CAAC;IACH,CAAC;IAED,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC9B,IAAI,OAAO,MAAM,CAAC,OAAO,KAAK,QAAQ,IAAI,KAAK,GAAG,MAAM,CAAC,OAAO,EAAE,CAAC;YACjE,MAAM,CAAC,IAAI,CAAC,GAAG,IAAI,eAAe,MAAM,CAAC,OAAO,GAAG,CAAC,CAAC;QACvD,CAAC;QACD,IAAI,OAAO,MAAM,CAAC,OAAO,KAAK,QAAQ,IAAI,KAAK,GAAG,MAAM,CAAC,OAAO,EAAE,CAAC;YACjE,MAAM,CAAC,IAAI,CAAC,GAAG,IAAI,eAAe,MAAM,CAAC,OAAO,GAAG,CAAC,CAAC;QACvD,CAAC;IACH,CAAC;IAED,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QACzB,IAAI,OAAO,MAAM,CAAC,QAAQ,KAAK,QAAQ,IAAI,KAAK,CAAC,MAAM,GAAG,MAAM,CAAC,QAAQ,EAAE,CAAC;YAC1E,MAAM,CAAC,IAAI,CAAC,GAAG,IAAI,0BAA0B,MAAM,CAAC,QAAQ,SAAS,CAAC,CAAC;QACzE,CAAC;QACD,IAAI,OAAO,MAAM,CAAC,QAAQ,KAAK,QAAQ,IAAI,KAAK,CAAC,MAAM,GAAG,MAAM,CAAC,QAAQ,EAAE,CAAC;YAC1E,MAAM,CAAC,IAAI,CAAC,GAAG,IAAI,8BAA8B,MAAM,CAAC,QAAQ,SAAS,CAAC,CAAC;QAC7E,CAAC;QACD,IAAI,QAAQ,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC;YAC3B,KAAK,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,KAAmB,EAAE,GAAG,IAAI,IAAI,KAAK,GAAG,EAAE,MAAM,CAAC,CAAC,CAAC;QAC9G,CAAC;IACH,CAAC;IAED,IAAI,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;QACpB,MAAM,UAAU,GAAG,QAAQ,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC,CAAE,MAAM,CAAC,UAAsC,CAAC,CAAC,CAAC,EAAE,CAAC;QACrG,MAAM,QAAQ,GAAG,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,IAAI,EAAkB,EAAE,CAAC,OAAO,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QAElI,KAAK,MAAM,GAAG,IAAI,QAAQ,EAAE,CAAC;YAC3B,IAAI,CAAC,CAAC,GAAG,IAAI,KAAK,CAAC,EAAE,CAAC;gBACpB,MAAM,CAAC,IAAI,CAAC,GAAG,IAAI,IAAI,GAAG,eAAe,CAAC,CAAC;YAC7C,CAAC;QACH,CAAC;QAED,KAAK,MAAM,CAAC,GAAG,EAAE,cAAc,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,UAAU,CAAC,EAAE,CAAC;YAC/D,IAAI,GAAG,IAAI,KAAK,IAAI,QAAQ,CAAC,cAAc,CAAC,EAAE,CAAC;gBAC7C,YAAY,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,cAAc,EAAE,GAAG,IAAI,IAAI,GAAG,EAAE,EAAE,MAAM,CAAC,CAAC;YACrE,CAAC;QACH,CAAC;QAED,IAAI,MAAM,CAAC,oBAAoB,KAAK,KAAK,EAAE,CAAC;YAC1C,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;gBACrC,IAAI,CAAC,CAAC,GAAG,IAAI,UAAU,CAAC,EAAE,CAAC;oBACzB,MAAM,CAAC,IAAI,CAAC,GAAG,IAAI,IAAI,GAAG,gDAAgD,CAAC,CAAC;gBAC9E,CAAC;YACH,CAAC;QACH,CAAC;IACH,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,SAAS,iBAAiB,CAAC,IAAa;IACtC,IAAI,OAAO,IAAI,KAAK,QAAQ;QAAE,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5C,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC;QAAE,OAAO,IAAI,CAAC,MAAM,CAAC,CAAC,IAAI,EAAkB,EAAE,CAAC,OAAO,IAAI,KAAK,QAAQ,CAAC,CAAC;IAChG,OAAO,EAAE,CAAC;AACZ,CAAC;AAED,SAAS,eAAe,CAAC,KAAc,EAAE,IAAY;IACnD,QAAQ,IAAI,EAAE,CAAC;QACb,KAAK,QAAQ;YACX,OAAO,QAAQ,CAAC,KAAK,CAAC,CAAC;QACzB,KAAK,OAAO;YACV,OAAO,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;QAC9B,KAAK,QAAQ;YACX,OAAO,OAAO,KAAK,KAAK,QAAQ,CAAC;QACnC,KAAK,QAAQ;YACX,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;QAC7D,KAAK,SAAS;YACZ,OAAO,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;QACjC,KAAK,SAAS;YACZ,OAAO,OAAO,KAAK,KAAK,SAAS,CAAC;QACpC,KAAK,MAAM;YACT,OAAO,KAAK,KAAK,IAAI,CAAC;QACxB;YACE,OAAO,IAAI,CAAC;IAChB,CAAC;AACH,CAAC;AAED,SAAS,QAAQ,CAAC,KAAc;IAC9B,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;AAC9E,CAAC;AAED,SAAS,SAAS,CAAC,CAAU,EAAE,CAAU;IACvC,OAAO,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC;AACjD,CAAC"}
@@ -0,0 +1,98 @@
1
+ /**
2
+ * Example JSON Schema for structured extraction. Useful as a starting point for schema-constrained
3
+ * generation; hosts supply their own schema for real workloads.
4
+ */
5
+ export declare const DEFAULT_EXTRACTION_SCHEMA: {
6
+ readonly type: "object";
7
+ readonly additionalProperties: false;
8
+ readonly properties: {
9
+ readonly summary: {
10
+ readonly type: "string";
11
+ readonly maxLength: 240;
12
+ readonly description: "One concise sentence summarizing the input text.";
13
+ };
14
+ readonly language: {
15
+ readonly type: "string";
16
+ readonly maxLength: 40;
17
+ readonly description: "The primary language of the input text, e.g. English.";
18
+ };
19
+ readonly sentiment: {
20
+ readonly type: "string";
21
+ readonly enum: readonly ["positive", "neutral", "negative", "mixed"];
22
+ };
23
+ readonly entities: {
24
+ readonly type: "array";
25
+ readonly maxItems: 8;
26
+ readonly items: {
27
+ readonly type: "object";
28
+ readonly additionalProperties: false;
29
+ readonly properties: {
30
+ readonly text: {
31
+ readonly type: "string";
32
+ readonly maxLength: 120;
33
+ };
34
+ readonly type: {
35
+ readonly type: "string";
36
+ readonly enum: readonly ["person", "organization", "product_feature", "integration", "competitor", "date", "money", "other"];
37
+ };
38
+ };
39
+ readonly required: readonly ["text", "type"];
40
+ };
41
+ };
42
+ readonly intents: {
43
+ readonly type: "array";
44
+ readonly maxItems: 5;
45
+ readonly items: {
46
+ readonly type: "string";
47
+ readonly maxLength: 80;
48
+ };
49
+ readonly description: "Likely user intents, such as request_feature, ask_pricing, report_blocker, or schedule_followup.";
50
+ };
51
+ readonly action_items: {
52
+ readonly type: "array";
53
+ readonly maxItems: 5;
54
+ readonly items: {
55
+ readonly type: "object";
56
+ readonly additionalProperties: false;
57
+ readonly properties: {
58
+ readonly owner: {
59
+ readonly type: readonly ["string", "null"];
60
+ readonly maxLength: 80;
61
+ };
62
+ readonly task: {
63
+ readonly type: "string";
64
+ readonly maxLength: 160;
65
+ };
66
+ readonly due_date: {
67
+ readonly type: readonly ["string", "null"];
68
+ readonly maxLength: 80;
69
+ };
70
+ };
71
+ readonly required: readonly ["owner", "task", "due_date"];
72
+ };
73
+ };
74
+ readonly risk_flags: {
75
+ readonly type: "array";
76
+ readonly maxItems: 8;
77
+ readonly items: {
78
+ readonly type: "string";
79
+ readonly maxLength: 120;
80
+ };
81
+ readonly description: "Adoption blockers, competitive risks, deadlines, or security/compliance concerns present in the text.";
82
+ };
83
+ readonly confidence: {
84
+ readonly type: "number";
85
+ readonly minimum: 0;
86
+ readonly maximum: 1;
87
+ readonly description: "Model confidence from 0 to 1.";
88
+ };
89
+ };
90
+ readonly required: readonly ["summary", "language", "sentiment", "entities", "intents", "action_items", "risk_flags", "confidence"];
91
+ };
92
+ export declare function stringifySchema(schema: unknown): string;
93
+ /**
94
+ * The schema sent to the constrained decoder does not need human-only metadata.
95
+ * Removing descriptions keeps the prompt and grammar-construction work smaller.
96
+ */
97
+ export declare function stripSchemaMetadata(value: unknown): unknown;
98
+ //# sourceMappingURL=schema.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,eAAO,MAAM,yBAAyB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAsF5B,CAAC;AAEX,wBAAgB,eAAe,CAAC,MAAM,EAAE,OAAO,GAAG,MAAM,CAEvD;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAiB3D"}
package/dist/schema.js ADDED
@@ -0,0 +1,115 @@
1
+ /**
2
+ * Example JSON Schema for structured extraction. Useful as a starting point for schema-constrained
3
+ * generation; hosts supply their own schema for real workloads.
4
+ */
5
+ export const DEFAULT_EXTRACTION_SCHEMA = {
6
+ type: "object",
7
+ additionalProperties: false,
8
+ properties: {
9
+ summary: {
10
+ type: "string",
11
+ maxLength: 240,
12
+ description: "One concise sentence summarizing the input text.",
13
+ },
14
+ language: {
15
+ type: "string",
16
+ maxLength: 40,
17
+ description: "The primary language of the input text, e.g. English.",
18
+ },
19
+ sentiment: {
20
+ type: "string",
21
+ enum: ["positive", "neutral", "negative", "mixed"],
22
+ },
23
+ entities: {
24
+ type: "array",
25
+ maxItems: 8,
26
+ items: {
27
+ type: "object",
28
+ additionalProperties: false,
29
+ properties: {
30
+ text: { type: "string", maxLength: 120 },
31
+ type: {
32
+ type: "string",
33
+ enum: [
34
+ "person",
35
+ "organization",
36
+ "product_feature",
37
+ "integration",
38
+ "competitor",
39
+ "date",
40
+ "money",
41
+ "other",
42
+ ],
43
+ },
44
+ },
45
+ required: ["text", "type"],
46
+ },
47
+ },
48
+ intents: {
49
+ type: "array",
50
+ maxItems: 5,
51
+ items: { type: "string", maxLength: 80 },
52
+ description: "Likely user intents, such as request_feature, ask_pricing, report_blocker, or schedule_followup.",
53
+ },
54
+ action_items: {
55
+ type: "array",
56
+ maxItems: 5,
57
+ items: {
58
+ type: "object",
59
+ additionalProperties: false,
60
+ properties: {
61
+ owner: { type: ["string", "null"], maxLength: 80 },
62
+ task: { type: "string", maxLength: 160 },
63
+ due_date: { type: ["string", "null"], maxLength: 80 },
64
+ },
65
+ required: ["owner", "task", "due_date"],
66
+ },
67
+ },
68
+ risk_flags: {
69
+ type: "array",
70
+ maxItems: 8,
71
+ items: { type: "string", maxLength: 120 },
72
+ description: "Adoption blockers, competitive risks, deadlines, or security/compliance concerns present in the text.",
73
+ },
74
+ confidence: {
75
+ type: "number",
76
+ minimum: 0,
77
+ maximum: 1,
78
+ description: "Model confidence from 0 to 1.",
79
+ },
80
+ },
81
+ required: [
82
+ "summary",
83
+ "language",
84
+ "sentiment",
85
+ "entities",
86
+ "intents",
87
+ "action_items",
88
+ "risk_flags",
89
+ "confidence",
90
+ ],
91
+ };
92
+ export function stringifySchema(schema) {
93
+ return JSON.stringify(schema, null, 2);
94
+ }
95
+ /**
96
+ * The schema sent to the constrained decoder does not need human-only metadata.
97
+ * Removing descriptions keeps the prompt and grammar-construction work smaller.
98
+ */
99
+ export function stripSchemaMetadata(value) {
100
+ if (Array.isArray(value)) {
101
+ return value.map(stripSchemaMetadata);
102
+ }
103
+ if (!value || typeof value !== "object") {
104
+ return value;
105
+ }
106
+ const result = {};
107
+ for (const [key, child] of Object.entries(value)) {
108
+ if (["description", "examples", "title", "$comment"].includes(key)) {
109
+ continue;
110
+ }
111
+ result[key] = stripSchemaMetadata(child);
112
+ }
113
+ return result;
114
+ }
115
+ //# sourceMappingURL=schema.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema.js","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Remove `<think>…</think>` blocks from model output. Some chat templates emit an empty think pair
3
+ * even when thinking is disabled (enable_thinking:false), and reasoning models emit filled ones;
4
+ * neither belongs in a visible answer — or in text fed to TTS. Also hides an unclosed block while
5
+ * it is still streaming.
6
+ */
7
+ export declare function stripThinkTags(text: string): string;
8
+ //# sourceMappingURL=text-utils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"text-utils.d.ts","sourceRoot":"","sources":["../src/text-utils.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAKnD"}
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Remove `<think>…</think>` blocks from model output. Some chat templates emit an empty think pair
3
+ * even when thinking is disabled (enable_thinking:false), and reasoning models emit filled ones;
4
+ * neither belongs in a visible answer — or in text fed to TTS. Also hides an unclosed block while
5
+ * it is still streaming.
6
+ */
7
+ export function stripThinkTags(text) {
8
+ let out = text.replace(/<think>[\s\S]*?<\/think>/gi, "");
9
+ const openIndex = out.search(/<think>/i);
10
+ if (openIndex !== -1)
11
+ out = out.slice(0, openIndex);
12
+ return out.replace(/^\s+/, "");
13
+ }
14
+ //# sourceMappingURL=text-utils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"text-utils.js","sourceRoot":"","sources":["../src/text-utils.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,MAAM,UAAU,cAAc,CAAC,IAAY;IACzC,IAAI,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,4BAA4B,EAAE,EAAE,CAAC,CAAC;IACzD,MAAM,SAAS,GAAG,GAAG,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;IACzC,IAAI,SAAS,KAAK,CAAC,CAAC;QAAE,GAAG,GAAG,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,SAAS,CAAC,CAAC;IACpD,OAAO,GAAG,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;AACjC,CAAC"}
@@ -0,0 +1,35 @@
1
+ import { type ModelTask } from "../models.js";
2
+ /**
3
+ * Pure helpers for the voice pipeline (mic → STT → text LLM → TTS voice assistant). Kept free of
4
+ * engine and DOM state so the chain-readiness logic, recommended-model map, and history cap are
5
+ * unit-testable.
6
+ */
7
+ /** The capability slots the voice-assistant pipeline chains, in signal order (mic → … → speakers). */
8
+ export declare const PIPELINE_TASKS: readonly ["stt", "text", "tts"];
9
+ export type PipelineTask = (typeof PIPELINE_TASKS)[number];
10
+ /**
11
+ * The recommended model per pipeline slot — low-resource defaults (Whisper Base / Qwen3.5 0.8B /
12
+ * Kokoro 82M). They occupy disjoint capability slots, so loading all three never evicts another;
13
+ * total ≈2.5 GB VRAM.
14
+ */
15
+ export declare const PIPELINE_RECOMMENDED: Record<PipelineTask, string>;
16
+ /** The pipeline slots not yet filled by a loaded model, in {@link PIPELINE_TASKS} order. */
17
+ export declare function missingPipelineModels(filledSlots: ReadonlySet<ModelTask>): PipelineTask[];
18
+ /** Whether a generated reply is worth speaking — skips blank output and the empty-response sentinel. */
19
+ export declare function shouldSpeakReply(reply: string): boolean;
20
+ /** How many user↔assistant exchanges the pipeline keeps as conversation context. */
21
+ export declare const PIPELINE_HISTORY_MAX_TURNS = 6;
22
+ /**
23
+ * Cap a pipeline conversation history (alternating user/assistant entries, system turn excluded) to
24
+ * the most recent `maxTurns` exchanges. Returns the input array unchanged when already within bounds.
25
+ */
26
+ export declare function capPipelineHistory<T>(messages: T[], maxTurns?: number): T[];
27
+ /**
28
+ * The newly-appended suffix when `next` extends `prev` (a monotonically-growing stream); "" when `next`
29
+ * is not an extension of `prev` (e.g. a think-tag block was stripped, shrinking the text). Used to push
30
+ * only new text into a streaming TTS splitter without repeating already-spoken text.
31
+ */
32
+ export declare function streamedDelta(prev: string, next: string): string;
33
+ /** System turn for the voice assistant — replies must be short and speakable (fed to TTS verbatim). */
34
+ export declare const PIPELINE_SYSTEM_PROMPT = "You are a helpful voice assistant. Reply in 1-3 short, natural sentences suitable for being spoken aloud. Do not use markdown, lists, or code blocks.";
35
+ //# sourceMappingURL=helpers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"helpers.d.ts","sourceRoot":"","sources":["../../src/voice/helpers.ts"],"names":[],"mappings":"AAAA,OAAO,EAAmB,KAAK,SAAS,EAAE,MAAM,cAAc,CAAC;AAG/D;;;;GAIG;AAEH,sGAAsG;AACtG,eAAO,MAAM,cAAc,iCAAkC,CAAC;AAC9D,MAAM,MAAM,YAAY,GAAG,CAAC,OAAO,cAAc,CAAC,CAAC,MAAM,CAAC,CAAC;AAE3D;;;;GAIG;AACH,eAAO,MAAM,oBAAoB,EAAE,MAAM,CAAC,YAAY,EAAE,MAAM,CAI7D,CAAC;AAEF,4FAA4F;AAC5F,wBAAgB,qBAAqB,CAAC,WAAW,EAAE,WAAW,CAAC,SAAS,CAAC,GAAG,YAAY,EAAE,CAEzF;AAED,wGAAwG;AACxG,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAGvD;AAED,oFAAoF;AACpF,eAAO,MAAM,0BAA0B,IAAI,CAAC;AAE5C;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,EAAE,EAAE,QAAQ,GAAE,MAAmC,GAAG,CAAC,EAAE,CAGvG;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAEhE;AAED,uGAAuG;AACvG,eAAO,MAAM,sBAAsB,0JACsH,CAAC"}
@@ -0,0 +1,49 @@
1
+ import { KOKORO_MODEL_ID } from "../models.js";
2
+ import { EMPTY_RESPONSE } from "../inference.js";
3
+ /**
4
+ * Pure helpers for the voice pipeline (mic → STT → text LLM → TTS voice assistant). Kept free of
5
+ * engine and DOM state so the chain-readiness logic, recommended-model map, and history cap are
6
+ * unit-testable.
7
+ */
8
+ /** The capability slots the voice-assistant pipeline chains, in signal order (mic → … → speakers). */
9
+ export const PIPELINE_TASKS = ["stt", "text", "tts"];
10
+ /**
11
+ * The recommended model per pipeline slot — low-resource defaults (Whisper Base / Qwen3.5 0.8B /
12
+ * Kokoro 82M). They occupy disjoint capability slots, so loading all three never evicts another;
13
+ * total ≈2.5 GB VRAM.
14
+ */
15
+ export const PIPELINE_RECOMMENDED = {
16
+ stt: "onnx-community/whisper-base",
17
+ text: "Qwen3.5-0.8B-q4f16_1-MLC",
18
+ tts: KOKORO_MODEL_ID,
19
+ };
20
+ /** The pipeline slots not yet filled by a loaded model, in {@link PIPELINE_TASKS} order. */
21
+ export function missingPipelineModels(filledSlots) {
22
+ return PIPELINE_TASKS.filter((task) => !filledSlots.has(task));
23
+ }
24
+ /** Whether a generated reply is worth speaking — skips blank output and the empty-response sentinel. */
25
+ export function shouldSpeakReply(reply) {
26
+ const trimmed = reply.trim();
27
+ return trimmed.length > 0 && trimmed !== EMPTY_RESPONSE;
28
+ }
29
+ /** How many user↔assistant exchanges the pipeline keeps as conversation context. */
30
+ export const PIPELINE_HISTORY_MAX_TURNS = 6;
31
+ /**
32
+ * Cap a pipeline conversation history (alternating user/assistant entries, system turn excluded) to
33
+ * the most recent `maxTurns` exchanges. Returns the input array unchanged when already within bounds.
34
+ */
35
+ export function capPipelineHistory(messages, maxTurns = PIPELINE_HISTORY_MAX_TURNS) {
36
+ const maxEntries = maxTurns * 2;
37
+ return messages.length > maxEntries ? messages.slice(-maxEntries) : messages;
38
+ }
39
+ /**
40
+ * The newly-appended suffix when `next` extends `prev` (a monotonically-growing stream); "" when `next`
41
+ * is not an extension of `prev` (e.g. a think-tag block was stripped, shrinking the text). Used to push
42
+ * only new text into a streaming TTS splitter without repeating already-spoken text.
43
+ */
44
+ export function streamedDelta(prev, next) {
45
+ return next.startsWith(prev) ? next.slice(prev.length) : "";
46
+ }
47
+ /** System turn for the voice assistant — replies must be short and speakable (fed to TTS verbatim). */
48
+ export const PIPELINE_SYSTEM_PROMPT = "You are a helpful voice assistant. Reply in 1-3 short, natural sentences suitable for being spoken aloud. Do not use markdown, lists, or code blocks.";
49
+ //# sourceMappingURL=helpers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"helpers.js","sourceRoot":"","sources":["../../src/voice/helpers.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAkB,MAAM,cAAc,CAAC;AAC/D,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAEjD;;;;GAIG;AAEH,sGAAsG;AACtG,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,CAAU,CAAC;AAG9D;;;;GAIG;AACH,MAAM,CAAC,MAAM,oBAAoB,GAAiC;IAChE,GAAG,EAAE,6BAA6B;IAClC,IAAI,EAAE,0BAA0B;IAChC,GAAG,EAAE,eAAe;CACrB,CAAC;AAEF,4FAA4F;AAC5F,MAAM,UAAU,qBAAqB,CAAC,WAAmC;IACvE,OAAO,cAAc,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,WAAW,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC;AACjE,CAAC;AAED,wGAAwG;AACxG,MAAM,UAAU,gBAAgB,CAAC,KAAa;IAC5C,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC;IAC7B,OAAO,OAAO,CAAC,MAAM,GAAG,CAAC,IAAI,OAAO,KAAK,cAAc,CAAC;AAC1D,CAAC;AAED,oFAAoF;AACpF,MAAM,CAAC,MAAM,0BAA0B,GAAG,CAAC,CAAC;AAE5C;;;GAGG;AACH,MAAM,UAAU,kBAAkB,CAAI,QAAa,EAAE,WAAmB,0BAA0B;IAChG,MAAM,UAAU,GAAG,QAAQ,GAAG,CAAC,CAAC;IAChC,OAAO,QAAQ,CAAC,MAAM,GAAG,UAAU,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC;AAC/E,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,aAAa,CAAC,IAAY,EAAE,IAAY;IACtD,OAAO,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;AAC9D,CAAC;AAED,uGAAuG;AACvG,MAAM,CAAC,MAAM,sBAAsB,GACjC,uJAAuJ,CAAC"}