From d3584a6436869780be5941d3f469e9d09425f0e8 Mon Sep 17 00:00:00 2001 From: Albert Ho Date: Fri, 23 Aug 2024 16:36:32 -0700 Subject: [PATCH 1/5] add nodejs llm-voice-assistant --- .github/workflows/nodejs-codestyle.yml | 35 ++ recipes/llm-voice-assistant/README.md | 1 + recipes/llm-voice-assistant/nodejs/.gitignore | 2 + recipes/llm-voice-assistant/nodejs/.npmignore | 1 + recipes/llm-voice-assistant/nodejs/index.js | 329 ++++++++++++++++++ .../llm-voice-assistant/nodejs/package.json | 42 +++ recipes/llm-voice-assistant/nodejs/yarn.lock | 38 ++ 7 files changed, 448 insertions(+) create mode 100644 .github/workflows/nodejs-codestyle.yml create mode 100644 recipes/llm-voice-assistant/nodejs/.gitignore create mode 100644 recipes/llm-voice-assistant/nodejs/.npmignore create mode 100755 recipes/llm-voice-assistant/nodejs/index.js create mode 100644 recipes/llm-voice-assistant/nodejs/package.json create mode 100644 recipes/llm-voice-assistant/nodejs/yarn.lock diff --git a/.github/workflows/nodejs-codestyle.yml b/.github/workflows/nodejs-codestyle.yml new file mode 100644 index 0000000..0c1a860 --- /dev/null +++ b/.github/workflows/nodejs-codestyle.yml @@ -0,0 +1,35 @@ +name: Node.js Codestyle + +on: + workflow_dispatch: + push: + branches: [ main ] + paths: + - '**/nodejs/*.js' + - '**/nodejs/*.ts' + - '.github/workflows/nodejs-codestyle.yml' + pull_request: + branches: [ main, 'v[0-9]+.[0-9]+' ] + paths: + - '**/nodejs/*.js' + - '**/nodejs/*.ts' + - '.github/workflows/nodejs-codestyle.yml' + +jobs: + check-nodejs-codestyle: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + + - name: Set up Node.js LTS + uses: actions/setup-node@v3 + with: + node-version: lts/* + + - name: Pre-build dependencies + run: npm install yarn + + - name: Run Binding Linter + run: yarn && yarn lint + working-directory: recipes/llm-voice-assistant/nodejs diff --git a/recipes/llm-voice-assistant/README.md b/recipes/llm-voice-assistant/README.md index 1f44579..11dff48 100644 --- a/recipes/llm-voice-assistant/README.md +++ b/recipes/llm-voice-assistant/README.md @@ -15,3 +15,4 @@ Hands-free voice assistant powered by a large language model (LLM), all voice re - [Android](android) - [iOS](ios) - [Web](web) +- [Node.js](nodejs) diff --git a/recipes/llm-voice-assistant/nodejs/.gitignore b/recipes/llm-voice-assistant/nodejs/.gitignore new file mode 100644 index 0000000..d5f19d8 --- /dev/null +++ b/recipes/llm-voice-assistant/nodejs/.gitignore @@ -0,0 +1,2 @@ +node_modules +package-lock.json diff --git a/recipes/llm-voice-assistant/nodejs/.npmignore b/recipes/llm-voice-assistant/nodejs/.npmignore new file mode 100644 index 0000000..3c3629e --- /dev/null +++ b/recipes/llm-voice-assistant/nodejs/.npmignore @@ -0,0 +1 @@ +node_modules diff --git a/recipes/llm-voice-assistant/nodejs/index.js b/recipes/llm-voice-assistant/nodejs/index.js new file mode 100755 index 0000000..655a2cd --- /dev/null +++ b/recipes/llm-voice-assistant/nodejs/index.js @@ -0,0 +1,329 @@ +#! /usr/bin/env node +"use strict"; + +const { Porcupine, BuiltinKeyword } = require("@picovoice/porcupine-node"); +const { Cheetah } = require("@picovoice/cheetah-node"); +const { PicoLLM } = require("@picovoice/picollm-node"); +const { Orca } = require("@picovoice/orca-node"); +const { PvRecorder } = require("@picovoice/pvrecorder-node"); +const { PvSpeaker } = require("@picovoice/pvspeaker-node"); + +const { program } = require("commander"); +const { performance } = require("perf_hooks"); + + +class RTFProfiler { + constructor(sampleRate) { + this._sampleRate = sampleRate; + this._computeSec = 0; + this._audioSec = 0; + this._tickMillisec = 0; + } + + tick() { + this._tickMillisec = performance.now(); + } + + tock(audio) { + const elapsedTimeMillisec = performance.now() - this._tickMillisec; + this._computeSec += (elapsedTimeMillisec / 1000); + if (audio !== null && audio !== undefined) { + this._audioSec += audio.length / this._sampleRate; + } + } + + rtf() { + const rtf = this._computeSec / this._audioSec; + this._computeSec = 0; + this._audioSec = 0; + return rtf.toFixed(3); + } +} + +class TPSProfiler { + constructor() { + this._numTokens = 0; + this._startMillisec = 0; + } + + tock() { + if (this._startMillisec === 0) { + this._startMillisec = performance.now(); + } else { + this._numTokens += 1; + } + } + + tps() { + const elapsedTimeMillisec = performance.now() - this._startMillisec; + const tps = this._numTokens / (elapsedTimeMillisec / 1000); + this._numTokens = 0; + this._startMillisec = 0; + return tps.toFixed(3); + } +} + +let isInterrupted = false; + +program + .requiredOption( + "-a, --access_key ", + "`AccessKey` obtained from the `Picovoice Console` (https://console.picovoice.ai/).", + ) + .requiredOption( + "-p, --picollm_model_path ", + "Absolute path to the file containing LLM parameters (`.pllm`).", + ) + .option( + "--keyword_model_path ", + "Absolute path to the keyword model file (`.ppn`). If not set, `Picovoice` will be the wake phrase.", + ) + .option( + "--cheetah_endpoint_duration_sec ", + "Duration of silence (pause) after the user's utterance to consider it the end of the utterance.", + "1" + ) + .option( + "--picollm_device ", + "String representation of the device (e.g., CPU or GPU) to use for inference. If set to `best`, picoLLM \n" + + "picks the most suitable device. If set to `gpu`, the engine uses the first available GPU device. To \n" + + "select a specific GPU device, set this argument to `gpu:${GPU_INDEX}`, where `${GPU_INDEX}` is the index \n" + + "of the target GPU. If set to `cpu`, the engine will run on the CPU with the default number of threads. \n" + + "To specify the number of threads, set this argument to `cpu:${NUM_THREADS}`, where `${NUM_THREADS}` is \n" + + "the desired number of threads.", + ) + .option( + "--picollm_completion_token_limit ", + "Maximum number of tokens in the completion. Set to `None` to impose no limit.", + "256" + ) + .option( + "--picollm_presence_penalty ", + "It penalizes logits already appearing in the partial completion if set to a positive value. If set to \n" + + "`0.0`, it has no effect.", + "0" + ) + .option( + "--picollm_frequency_penalty ", + "If set to a positive floating-point value, it penalizes logits proportional to the frequency of their \n" + + "appearance in the partial completion. If set to `0.0`, it has no effect.", + "0" + ) + .option( + "--picollm_temperature ", + "Sampling temperature. Temperature is a non-negative floating-point value that controls the randomness of \n" + + "the sampler. A higher temperature smoothens the samplers' output, increasing the randomness. In \n" + + "contrast, a lower temperature creates a narrower distribution and reduces variability. Setting it to \n" + + "`0` selects the maximum logit during sampling.", + "0" + ) + .option( + "--picollm_top_p ", + "A positive floating-point number within (0, 1]. It restricts the sampler's choices to high-probability \n" + + "logits that form the `top_p` portion of the probability mass. Hence, it avoids randomly selecting \n" + + "unlikely logits. A value of `1.` enables the sampler to pick any token with non-zero probability, \n" + + "turning off the feature.", + "1" + ) + .option( + "--orca_warmup_sec ", + "Duration of the synthesized audio to buffer before streaming it out. A higher value helps slower \n" + + "(e.g., Raspberry Pi) to keep up with real-time at the cost of increasing the initial delay.", + "0" + ) + .option("--profile", "Show runtime profiling information.") + .option("--short_answers"); + +if (process.argv.length < 2) { + program.help(); +} +program.parse(process.argv); + +async function LlmVoiceAssistant() { + const accessKey = program["access_key"]; + const picollmModelPath = program["picollm_model_path"]; + const keywordModelPath = program["keyword_model_path"]; + const endpointDurationSec = Number(program["cheetah_endpoint_duration_sec"]); + const picollmDevice = program["picollm_device"]; + const picollmCompletionTokenLimit = Number(program["picollm_completion_token_limit"]); + const picollmPresencePenalty = Number(program["picollm_presence_penalty"]); + const picollmFrequencyPenalty = Number(program["picollm_frequency_penalty"]); + const picollmTemperature = Number(program["picollm_temperature"]); + const picollmTopP = Number(program["picollm_top_p"]); + const orcaWarmupSec = Number(program["orca_warmup_sec"]); + const profile = program["profile"]; + const isShortAnswers = program["short_answers"]; + + let porcupine = new Porcupine(accessKey, [keywordModelPath ?? BuiltinKeyword.PICOVOICE], [0.5]); + console.log(`→ Porcupine V${porcupine.version}`); + + const cheetah = new Cheetah(accessKey, { endpointDurationSec, enableAutomaticPunctuation: true }); + console.log(`→ Cheetah V${cheetah.version}`); + + const pllm = new PicoLLM(accessKey, picollmModelPath, { device: picollmDevice }); + console.log(`→ picoLLM V${pllm.version} <${pllm.model}>`); + + const orca = new Orca(accessKey); + console.log(`→ Orca V${orca.version}`); + + const dialog = pllm.getDialog(); + const orcaStream = orca.streamOpen(); + const speaker = new PvSpeaker(orca.sampleRate, 16); + const recorder = new PvRecorder(porcupine.frameLength); + recorder.start(); + + const ppnPrompt = `\n$ Say ${keywordModelPath ? "the wake word" : "`Picovoice`"} ...`; + console.log(ppnPrompt); + + let isWakeWordDetected = false; + let isEndpointReached = false; + let userRequest = ""; + let pcmBuffer = []; + + const porcupineProfiler = new RTFProfiler(porcupine.sampleRate); + const cheetahProfiler = new RTFProfiler(cheetah.sampleRate); + const orcaProfiler = new RTFProfiler(orca.sampleRate); + let utteranceEndMillisec = 0; + let delaySec = -1; + + try { + while (!isInterrupted) { + if (!isWakeWordDetected) { + const pcm = await recorder.read(); + porcupineProfiler.tick(); + isWakeWordDetected = porcupine.process(pcm) === 0; + porcupineProfiler.tock(pcm); + if (isWakeWordDetected) { + if (profile) { + console.log(`\n[Porcupine RTF: ${porcupineProfiler.rtf()}]`); + } + process.stdout.write("\n$ Wake word detected, utter your request or question ..."); + process.stdout.write("\n\nUser > "); + } + } else if (!isEndpointReached) { + const pcm = await recorder.read(); + cheetahProfiler.tick(); + const [partialTranscript, isEndpoint] = cheetah.process(pcm); + cheetahProfiler.tock(pcm); + process.stdout.write(partialTranscript); + userRequest += partialTranscript; + if (isEndpoint) { + utteranceEndMillisec = performance.now(); + cheetahProfiler.tick(); + const remainingTranscript = cheetah.flush(); + cheetahProfiler.tock(pcm); + userRequest += remainingTranscript; + process.stdout.write(`${remainingTranscript}\n\n`); + isEndpointReached = isEndpoint; + if (profile) { + console.log(`[Cheetah RTF: ${cheetahProfiler.rtf()}]`); + } + } + } else { + dialog.addHumanRequest(isShortAnswers ? `You are a voice assistant and your answers are very short but informative. ${userRequest}` : userRequest); + + speaker.start(); + + const picollmProfiler = new TPSProfiler(); + + const stopPhrases = [ + "", // Llama-2, Mistral, and Mixtral + "", // Gemma + "<|endoftext|>", // Phi-2 + "<|eot_id|>", // Llama-3 + ]; + + let completion = ""; + let isStartedPlaying = false; + + function llmCallback(text) { + picollmProfiler.tock(); + completion += text; + if (!stopPhrases.some(x => completion.includes(x))) { + process.stdout.write(text); + orcaProfiler.tick(); + const pcm = orcaStream.synthesize(text.replace("\n", " . ")); + orcaProfiler.tock(pcm); + if (pcm !== null) { + if (delaySec === -1) { + delaySec = (performance.now() - utteranceEndMillisec) / 1000; + } + pcmBuffer.push(...pcm); + } + + if (pcmBuffer.length > 0) { + if (pcmBuffer.length >= orcaWarmupSec * orca.sampleRate || isStartedPlaying) { + const arrayBuffer = new Int16Array(pcmBuffer).buffer; + const written = speaker.write(arrayBuffer); + pcmBuffer = pcmBuffer.slice(written); + isStartedPlaying = true; + } + } + } + } + + process.stdout.write(`LLM >`); + const res = await pllm.generate( + dialog.prompt(), + { + completionTokenLimit: picollmCompletionTokenLimit, + stopPhrases: stopPhrases, + presencePenalty: picollmPresencePenalty, + frequencyPenalty: picollmFrequencyPenalty, + temperature: picollmTemperature, + topP: picollmTopP, + streamCallback: llmCallback + } + ); + + dialog.addLLMResponse(res.completion); + if (profile) { + console.log(`\n[picoLLM TPS: ${picollmProfiler.tps()}]`); + } + + orcaProfiler.tick(); + const flushedPcm = orcaStream.flush(); + orcaProfiler.tock(flushedPcm); + console.log(`\n[Orca RTF: ${orcaProfiler.rtf()}]`); + console.log(`[Delay: ${delaySec.toFixed(3)} sec]`); + if (flushedPcm !== null) { + pcmBuffer.push(...flushedPcm); + } + + const arrayBuffer = new Int16Array(pcmBuffer).buffer; + speaker.flush(arrayBuffer); + speaker.stop(); + delaySec = -1; + + isWakeWordDetected = false; + isEndpointReached = false; + userRequest = ""; + pcmBuffer = []; + console.log(ppnPrompt); + } + } + } finally { + recorder.stop(); + recorder.release(); + speaker.stop(); + speaker.release(); + porcupine.release(); + cheetah.release(); + pllm.release(); + orcaStream.close(); + orca.release(); + } +} + +// setup interrupt +process.on("SIGINT", function () { + isInterrupted = true; +}); + +(async function () { + try { + await LlmVoiceAssistant(); + } catch (e) { + console.error(e.toString()); + } +})(); diff --git a/recipes/llm-voice-assistant/nodejs/package.json b/recipes/llm-voice-assistant/nodejs/package.json new file mode 100644 index 0000000..013b640 --- /dev/null +++ b/recipes/llm-voice-assistant/nodejs/package.json @@ -0,0 +1,42 @@ +{ + "name": "llm-voice-assistant", + "version": "1.0.0", + "description": "", + "scripts": { + "start": "node index.js" + }, + "bin": { + "llm-voice-assistant": "./index.js" + }, + "keywords": [ + "Picovoice", + "generative ai", + "llm" + ], + "author": "Picovoice Inc.", + "license": "Apache-2.0", + "dependencies": { + "@picovoice/cheetah-node": "^2.0.2", + "@picovoice/orca-node": "^1.0.0", + "@picovoice/picollm-node": "=1.0.2", + "@picovoice/porcupine-node": "^3.0.3", + "@picovoice/pvrecorder-node": "^1.2.3", + "@picovoice/pvspeaker-node": "^1.0.0", + "commander": "^6.1.0" + }, + "homepage": "https://picovoice.ai/", + "repository": { + "type": "git", + "url": "git+https://github.com/Picovoice/pico-cookbook.git", + "directory": "recipes/llm-voice-assistant/nodejs" + }, + "engines": { + "node": ">=16.0.0" + }, + "cpu": [ + "!ia32", + "!mips", + "!ppc", + "!ppc64" + ] +} diff --git a/recipes/llm-voice-assistant/nodejs/yarn.lock b/recipes/llm-voice-assistant/nodejs/yarn.lock new file mode 100644 index 0000000..1df7f5d --- /dev/null +++ b/recipes/llm-voice-assistant/nodejs/yarn.lock @@ -0,0 +1,38 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@picovoice/cheetah-node@^2.0.2": + version "2.0.2" + resolved "https://registry.yarnpkg.com/@picovoice/cheetah-node/-/cheetah-node-2.0.2.tgz#6d1153f0ada1e625d13b4521c8dcac5a62eeaf24" + integrity sha512-4bmE+OnvEqGoT3V1q+0wVbOvMy0nbSA+nAUHdG+GlMfJaEaC0QNzk9WTNevkV7eAEtbmjVj9tB2VkFfGNoQpNA== + +"@picovoice/orca-node@^1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@picovoice/orca-node/-/orca-node-1.0.0.tgz#812728c3183a914eff6b3189dfa958ef4d44f2f7" + integrity sha512-YDTqJ5KsueBC4Nj0Zo287VF+/y7SRjXbOyHy8h66joJYPF0QNsz8oDCzbQO7hzymNbkFXd0crMPK+gQElvd83w== + +"@picovoice/picollm-node@=1.0.2": + version "1.0.2" + resolved "https://registry.yarnpkg.com/@picovoice/picollm-node/-/picollm-node-1.0.2.tgz#7262687eb6f0729af0e3ac7def495ccad092d525" + integrity sha512-VfJ5cVcF70BLRBCCyRsY2jFJXrXVpQqITT6Denr9Nd1poVzFk7x3KlJN8UtscXbKuqM+xfV1Jot5UVs9z+cj5g== + +"@picovoice/porcupine-node@^3.0.3": + version "3.0.3" + resolved "https://registry.yarnpkg.com/@picovoice/porcupine-node/-/porcupine-node-3.0.3.tgz#c5c9f45da75d0710acedb2387e54c0d3439df45d" + integrity sha512-GL2ecilHYUsCiYLI9xHZasULWwkPwE/+B0u9mdSjb9WiSIEuYrPE5K7hEBToB+H6amibhXlbIX1/aQxX1sFflw== + +"@picovoice/pvrecorder-node@^1.2.3": + version "1.2.3" + resolved "https://registry.yarnpkg.com/@picovoice/pvrecorder-node/-/pvrecorder-node-1.2.3.tgz#d0cb1d1ab82fbd2e914fda4e19b1dd0022609b48" + integrity sha512-6QOODFyGDgfWZI1NeXwMxylgaxJ+c5aVbvpDMGMen287eCajp/LDnxy3J1bZHceHsW3ilk/d4rCRLyifqcr24Q== + +"@picovoice/pvspeaker-node@^1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@picovoice/pvspeaker-node/-/pvspeaker-node-1.0.0.tgz#6b87b86138a8bb256e46ea41cb7d258ff8534337" + integrity sha512-qhH4ktObGj/Dd7iiptPXqf89U3l7FbCiaB1xY439YE3EFLsfsvZvjAzEwsF5A01h47AbXJTCdAjvkXrcsdt0/w== + +commander@^6.1.0: + version "6.2.1" + resolved "https://registry.yarnpkg.com/commander/-/commander-6.2.1.tgz#0792eb682dfbc325999bb2b84fddddba110ac73c" + integrity sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA== From d4be9709c281baf7497213962c1be1d5856627da Mon Sep 17 00:00:00 2001 From: Albert Ho Date: Fri, 23 Aug 2024 16:39:19 -0700 Subject: [PATCH 2/5] rm bin --- recipes/llm-voice-assistant/nodejs/package.json | 3 --- 1 file changed, 3 deletions(-) diff --git a/recipes/llm-voice-assistant/nodejs/package.json b/recipes/llm-voice-assistant/nodejs/package.json index 013b640..e7b2ca5 100644 --- a/recipes/llm-voice-assistant/nodejs/package.json +++ b/recipes/llm-voice-assistant/nodejs/package.json @@ -5,9 +5,6 @@ "scripts": { "start": "node index.js" }, - "bin": { - "llm-voice-assistant": "./index.js" - }, "keywords": [ "Picovoice", "generative ai", From 7e28f0b44362ffa2b54b077e0ee19a9f0c789592 Mon Sep 17 00:00:00 2001 From: Albert Ho Date: Fri, 23 Aug 2024 17:21:55 -0700 Subject: [PATCH 3/5] lint --- .../llm-voice-assistant/nodejs/.eslintignore | 2 + .../llm-voice-assistant/nodejs/.eslintrc.js | 464 +++++++++ .../nodejs/.prettierignore | 1 + .../llm-voice-assistant/nodejs/.prettierrc | 8 + recipes/llm-voice-assistant/nodejs/index.js | 251 ++--- .../llm-voice-assistant/nodejs/package.json | 10 +- recipes/llm-voice-assistant/nodejs/yarn.lock | 899 ++++++++++++++++++ 7 files changed, 1510 insertions(+), 125 deletions(-) create mode 100644 recipes/llm-voice-assistant/nodejs/.eslintignore create mode 100644 recipes/llm-voice-assistant/nodejs/.eslintrc.js create mode 100644 recipes/llm-voice-assistant/nodejs/.prettierignore create mode 100644 recipes/llm-voice-assistant/nodejs/.prettierrc diff --git a/recipes/llm-voice-assistant/nodejs/.eslintignore b/recipes/llm-voice-assistant/nodejs/.eslintignore new file mode 100644 index 0000000..3a1e8e1 --- /dev/null +++ b/recipes/llm-voice-assistant/nodejs/.eslintignore @@ -0,0 +1,2 @@ +node_modules +.eslintrc.js diff --git a/recipes/llm-voice-assistant/nodejs/.eslintrc.js b/recipes/llm-voice-assistant/nodejs/.eslintrc.js new file mode 100644 index 0000000..9f50865 --- /dev/null +++ b/recipes/llm-voice-assistant/nodejs/.eslintrc.js @@ -0,0 +1,464 @@ +// Rules reference: http://eslint.org/docs/rules/ +module.exports = { + env: { + browser: true, + node: true, + es6: true, + jest: true, + jasmine: true, + }, + + parser: '@typescript-eslint/parser', + parserOptions: { + ecmaVersion: 2018, + }, + + ignorePatterns: ['node_modules', 'dist'], + overrides: [ + { + files: ['src/**/*.ts'], + plugins: ['jest'], + extends: ['plugin:@typescript-eslint/recommended', 'plugin:jest/recommended'], + rules: { + '@typescript-eslint/no-parameter-properties': 2, + '@typescript-eslint/no-explicit-any': 0, + '@typescript-eslint/no-var-requires': 2, + '@typescript-eslint/no-non-null-assertion': 0, + '@typescript-eslint/no-use-before-define': 2, + '@typescript-eslint/camelcase': 0, + '@typescript-eslint/no-empty-interface': 2, + '@typescript-eslint/explicit-function-return-type': 2, + '@typescript-eslint/ban-ts-comment': 0, + '@typescript-eslint/no-empty-function': [2, { 'allow': ['constructors'] }], + '@typescript-eslint/no-inferrable-types': [ + 2, + { + ignoreParameters: true, + ignoreProperties: true, + }, + ], + '@typescript-eslint/no-shadow': 2, + }, + }, + ], + + rules: { + //========================================================================= + //==================== Possible Errors ==================================== + //========================================================================= + + // disallow trailing commas in object literals + 'comma-dangle': [0, 'always-multiline'], //TODO: need discussion + // disallow assignment in conditional expressions + 'no-cond-assign': [2, 'always'], + // disallow use of console + 'no-console': 1, + // disallow use of constant expressions in conditions + 'no-constant-condition': [2, { checkLoops: false }], + // disallow control characters in regular expressions + 'no-control-regex': 2, + // disallow use of debugger + 'no-debugger': 2, + // disallow duplicate arguments in functions + 'no-dupe-args': 2, + // disallow duplicate keys when creating object literals + 'no-dupe-keys': 2, + // disallow a duplicate case label. + 'no-duplicate-case': 2, + // disallow the use of empty character classes in regular expressions + 'no-empty-character-class': 2, + // disallow empty statements + 'no-empty': 2, + // disallow assigning to the exception in a catch block + 'no-ex-assign': 2, + // disallow double-negation boolean casts in a boolean context + 'no-extra-boolean-cast': 2, + // disallow unnecessary parentheses + 'no-extra-parens': [2, 'functions'], + // disallow unnecessary semicolons + 'no-extra-semi': 2, + // disallow overwriting functions written as function declarations + 'no-func-assign': 2, + // disallow function or variable declarations in nested blocks + 'no-inner-declarations': 2, + // disallow invalid regular expression strings in the RegExp constructor + 'no-invalid-regexp': 2, + // disallow irregular whitespace outside of strings and comments + 'no-irregular-whitespace': 2, + // disallow negation of the left operand of an in expression + 'no-negated-in-lhs': 2, + // disallow the use of object properties of the global object (Math and JSON) as functions + 'no-obj-calls': 2, + // disallow multiple spaces in a regular expression literal + 'no-regex-spaces': 2, + // disallow sparse arrays + 'no-sparse-arrays': 2, + // Avoid code that looks like two expressions but is actually one + 'no-unexpected-multiline': 2, + // disallow unreachable statements after a return, throw, continue, or break statement + 'no-unreachable': 2, + // disallow comparisons with the value NaN + 'use-isnan': 2, + // ensure JSDoc comments are valid + 'valid-jsdoc': [ + 0, + { + requireReturn: false, + requireReturnDescription: false, + }, + ], + // ensure that the results of typeof are compared against a valid string + 'valid-typeof': 2, + + //========================================================================= + //==================== Best Practices ===================================== + //========================================================================= + // Enforces getter/setter pairs in objects + 'accessor-pairs': 2, + // treat var statements as if they were block scoped + 'block-scoped-var': 2, + // specify the maximum cyclomatic complexity allowed in a program + complexity: [0, 11], + // require return statements to either always or never specify values + 'consistent-return': 2, + // specify curly brace conventions for all control statements + curly: [2, 'multi-line'], + // require default case in switch statements + 'default-case': 2, + // encourages use of dot notation whenever possible + 'dot-notation': [2, { allowKeywords: true }], + // enforces consistent newlines before or after dots + 'dot-location': [2, 'property'], + // require the use of === and !== + eqeqeq: 2, + // make sure for-in loops have an if statement + 'guard-for-in': 2, + // disallow the use of alert, confirm, and prompt + 'no-alert': 2, + // disallow use of arguments.caller or arguments.callee + 'no-caller': 2, + // disallow lexical declarations in case clauses + 'no-case-declarations': 2, + // disallow division operators explicitly at beginning of regular expression + 'no-div-regex': 2, + // disallow else after a return in an if + 'no-else-return': 2, + // disallow use of empty destructuring patterns + 'no-empty-pattern': 2, + // disallow comparisons to null without a type-checking operator + 'no-eq-null': 2, + // disallow use of eval() + 'no-eval': 2, + // disallow adding to native types + 'no-extend-native': 2, + // disallow unnecessary function binding + 'no-extra-bind': 2, + // disallow fallthrough of case statements + 'no-fallthrough': 2, + // disallow the use of leading or trailing decimal points in numeric literals + 'no-floating-decimal': 2, + // disallow the type conversions with shorter notations + 'no-implicit-coercion': 2, + // disallow use of eval()-like methods + 'no-implied-eval': 2, + // disallow this keywords outside of classes or class-like objects + 'no-invalid-this': 0, + // disallow usage of __iterator__ property + 'no-iterator': 2, + // disallow use of labeled statements + 'no-labels': 2, + // disallow unnecessary nested blocks + 'no-lone-blocks': 2, + // disallow creation of functions within loops + 'no-loop-func': 2, + // disallow the use of magic numbers + 'no-magic-numbers': 0, //TODO: need discussion + // disallow use of multiple spaces + 'no-multi-spaces': 2, + // disallow use of multiline strings + 'no-multi-str': 2, + // disallow reassignments of native objects + 'no-native-reassign': 2, + // disallow use of new operator for Function object + 'no-new-func': 2, + // disallows creating new instances of String,Number, and Boolean + 'no-new-wrappers': 2, + // disallow use of new operator when not part of the assignment or comparison + 'no-new': 0, + // disallow use of octal escape sequences in string literals, such as + // var foo = "Copyright \251"; + 'no-octal-escape': 2, + // disallow use of (old style) octal literals + 'no-octal': 2, + // disallow reassignment of function parameters + 'no-param-reassign': 2, + // disallow use of process.env + 'no-process-env': 2, + // disallow usage of __proto__ property + 'no-proto': 2, + // disallow declaring the same variable more than once + 'no-redeclare': 2, + // disallow use of assignment in return statement + 'no-return-assign': 2, + // disallow use of `javascript:` urls. + 'no-script-url': 2, + // disallow comparisons where both sides are exactly the same + 'no-self-compare': 2, + // disallow use of comma operator + 'no-sequences': 2, + // restrict what can be thrown as an exception + 'no-throw-literal': 0, + // disallow usage of expressions in statement position + 'no-unused-expressions': 2, + // disallow unnecessary .call() and .apply() + 'no-useless-call': 2, + // disallow unnecessary concatenation of literals or template literals + 'no-useless-concat': 2, + // disallow use of void operator + 'no-void': 2, + // disallow usage of configurable warning terms in comments: e.g. todo + 'no-warning-comments': [ + 1, + { terms: ['todo', 'fixme', 'xxx'], location: 'start' }, + ], + // disallow use of the with statement + 'no-with': 2, + // require use of the second argument for parseInt() + radix: 2, + // requires to declare all vars on top of their containing scope + 'vars-on-top': 0, + // require immediate function invocation to be wrapped in parentheses + 'wrap-iife': [2, 'any'], + // require or disallow Yoda conditions + yoda: 2, + + // //========================================================================= + // //==================== Strict Mode ======================================== + // //========================================================================= + // require that all functions are run in strict mode + // "strict": [2, "global"], + // + //========================================================================= + //==================== Variables ========================================== + //========================================================================= + // enforce or disallow variable initializations at definition + 'init-declarations': 0, + // disallow the catch clause parameter name being the same as a variable in the outer scope + 'no-catch-shadow': 0, + // disallow deletion of variables + 'no-delete-var': 2, + // disallow labels that share a name with a variable + 'no-label-var': 2, + // disallow shadowing of names such as arguments + 'no-shadow-restricted-names': 2, + // disallow declaration of variables already declared in the outer scope + 'no-shadow': 0, + // disallow use of undefined when initializing variables + 'no-undef-init': 0, + // disallow use of undeclared variables unless mentioned in a /*global */ block + 'no-undef': 2, + // disallow use of undefined variable + 'no-undefined': 0, + // disallow declaration of variables that are not used in the code + 'no-unused-vars': [1, { vars: 'local', args: 'after-used' }], + // disallow use of variables before they are defined + 'no-use-before-define': 2, + + //========================================================================= + //==================== Node.js ============================================ + //========================================================================= + // enforce return after a callback + 'callback-return': 0, + // disallow require() outside of the top-level module scope + 'global-require': 2, + // enforces error handling in callbacks (node environment) + 'handle-callback-err': 2, + // disallow mixing regular variable and require declarations + 'no-mixed-requires': 2, + // disallow use of new operator with the require function + 'no-new-require': 2, + // disallow string concatenation with __dirname and __filename + 'no-path-concat': 1, + // disallow process.exit() + 'no-process-exit': 2, + // restrict usage of specified node modules + 'no-restricted-modules': 0, + // disallow use of synchronous methods (off by default) + 'no-sync': 0, + + //========================================================================= + //==================== Stylistic Issues =================================== + //========================================================================= + // enforce spacing inside array brackets + 'array-bracket-spacing': 0, + // disallow or enforce spaces inside of single line blocks + 'block-spacing': 1, + // enforce one true brace style + 'brace-style': [1, '1tbs', { allowSingleLine: true }], + // require camel case names + camelcase: [1, { properties: 'always' }], + // enforce spacing before and after comma + 'comma-spacing': [1, { before: false, after: true }], + // enforce one true comma style + 'comma-style': [1, 'last'], + // require or disallow padding inside computed properties + 'computed-property-spacing': 0, + // enforces consistent naming when capturing the current execution context + 'consistent-this': 0, + // enforce newline at the end of file, with no multiple empty lines + 'eol-last': 1, + // require function expressions to have a name + 'func-names': 0, + // enforces use of function declarations or expressions + 'func-style': 0, + // this option enforces minimum and maximum identifier lengths (variable names, property names etc.) + 'id-length': 0, + // require identifiers to match the provided regular expression + 'id-match': 0, + // this option sets a specific tab width for your code + indent: [1, 2, { SwitchCase: 1 }], + // specify whether double or single quotes should be used in JSX attributes + 'jsx-quotes': [1, 'prefer-double'], + // enforces spacing between keys and values in object literal properties + 'key-spacing': [1, { beforeColon: false, afterColon: true }], + // disallow mixed "LF" and "CRLF" as linebreaks + 'linebreak-style': 0, + // enforces empty lines around comments + 'lines-around-comment': 0, + // specify the maximum depth that blocks can be nested + 'max-depth': [0, 4], + // specify the maximum length of a line in your program + 'max-len': [0, 80, 4], + // specify the maximum depth callbacks can be nested + 'max-nested-callbacks': 0, + // limits the number of parameters that can be used in the function declaration. + 'max-params': [0, 3], + // specify the maximum number of statement allowed in a function + 'max-statements': [0, 10], + // require a capital letter for constructors + 'new-cap': [1, { newIsCap: true }], + // disallow the omission of parentheses when invoking a constructor with no arguments + 'new-parens': 0, + // allow/disallow an empty newline after var statement + 'newline-after-var': 0, + // disallow use of the Array constructor + 'no-array-constructor': 0, + // disallow use of bitwise operators + 'no-bitwise': 0, + // disallow use of the continue statement + 'no-continue': 0, + // disallow comments inline after code + 'no-inline-comments': 0, + // disallow if as the only statement in an else block + 'no-lonely-if': 0, + // disallow mixed spaces and tabs for indentation + 'no-mixed-spaces-and-tabs': 1, + // disallow multiple empty lines + 'no-multiple-empty-lines': [1, { max: 2, maxEOF: 1 }], + // disallow negated conditions + 'no-negated-condition': 0, + // disallow nested ternary expressions + 'no-nested-ternary': 1, + // disallow use of the Object constructor + 'no-new-object': 1, + // disallow use of unary operators, ++ and -- + 'no-plusplus': 0, + // disallow use of certain syntax in code + 'no-restricted-syntax': 0, + // disallow space between function identifier and application + 'no-spaced-func': 1, + // disallow the use of ternary operators + 'no-ternary': 0, + // disallow trailing whitespace at the end of lines + 'no-trailing-spaces': 1, + // disallow dangling underscores in identifiers + 'no-underscore-dangle': 0, + // disallow the use of Boolean literals in conditional expressions + 'no-unneeded-ternary': 0, + // require or disallow padding inside curly braces + 'object-curly-spacing': ['error', 'always'], + // allow just one var statement per function + 'one-var': [1, 'never'], + // require assignment operator shorthand where possible or prohibit it entirely + 'operator-assignment': 0, + // enforce operators to be placed before or after line breaks + 'operator-linebreak': 0, + // enforce padding within blocks + 'padded-blocks': [1, 'never'], + // require quotes around object literal property names + 'quote-props': 0, + // specify whether double or single quotes should be used + quotes: 0, + // Require JSDoc comment + 'require-jsdoc': 0, + // enforce spacing before and after semicolons + 'semi-spacing': [1, { before: false, after: true }], + // require or disallow use of semicolons instead of ASI + semi: [1, 'always'], + // sort variables within the same declaration block + 'sort-vars': 0, + // require a space after certain keywords + 'keyword-spacing': 1, + // require or disallow space before blocks + 'space-before-blocks': 1, + // require or disallow space before function opening parenthesis + 'space-before-function-paren': [0, { anonymous: 'always', named: 'never' }], + // require or disallow space before blocks + 'space-in-parens': 0, + // require spaces around operators + 'space-infix-ops': 1, + // Require or disallow spaces before/after unary operators + 'space-unary-ops': 0, + // require or disallow a space immediately following the // or /* in a comment + 'spaced-comment': [ + 1, + 'always', + { + exceptions: ['-', '+', '/', '='], + markers: ['=', '!', '/'], // space here to support sprockets directives + }, + ], + // require regex literals to be wrapped in parentheses + 'wrap-regex': 0, + + //========================================================================= + //==================== ES6 Rules ========================================== + //========================================================================= + 'arrow-body-style': [1, 'as-needed'], + // require parens in arrow function arguments + 'arrow-parens': [1, 'as-needed'], + // require space before/after arrow function's arrow + 'arrow-spacing': 1, + // verify super() callings in constructors + 'constructor-super': 1, + // enforce the spacing around the * in generator functions + 'generator-star-spacing': 1, + // disallow arrow functions where a condition is expected + 'no-confusing-arrow': 1, + // disallow modifying variables of class declarations + 'no-class-assign': 1, + // disallow modifying variables that are declared using const + 'no-const-assign': 1, + // disallow duplicate name in class members + 'no-dupe-class-members': 1, + // disallow to use this/super before super() calling in constructors. + 'no-this-before-super': 1, + // require let or const instead of var + 'no-var': 0, //TODO: enable on full migration to es6 + // require method and property shorthand syntax for object literals + 'object-shorthand': 0, + // suggest using arrow functions as callbacks + 'prefer-arrow-callback': 0, //TODO: enable on full migration to es6 + // suggest using of const declaration for variables that are never modified after declared + 'prefer-const': 0, //TODO: enable on full migration to es6 + // suggest using Reflect methods where applicable + 'prefer-reflect': 0, + // suggest using the spread operator instead of .apply() + 'prefer-spread': 0, + // suggest using template literals instead of strings concatenation + 'prefer-template': 0, //TODO: enable on full migration to es6 + // disallow generator functions that do not have yield + 'require-yield': 0, + }, +}; + diff --git a/recipes/llm-voice-assistant/nodejs/.prettierignore b/recipes/llm-voice-assistant/nodejs/.prettierignore new file mode 100644 index 0000000..a9ba028 --- /dev/null +++ b/recipes/llm-voice-assistant/nodejs/.prettierignore @@ -0,0 +1 @@ +.eslintrc.js diff --git a/recipes/llm-voice-assistant/nodejs/.prettierrc b/recipes/llm-voice-assistant/nodejs/.prettierrc new file mode 100644 index 0000000..8a9f380 --- /dev/null +++ b/recipes/llm-voice-assistant/nodejs/.prettierrc @@ -0,0 +1,8 @@ +{ + "semi": true, + "trailingComma": "es5", + "singleQuote": true, + "printWidth": 80, + "tabWidth": 2, + "arrowParens": "avoid" +} diff --git a/recipes/llm-voice-assistant/nodejs/index.js b/recipes/llm-voice-assistant/nodejs/index.js index 655a2cd..57b855f 100755 --- a/recipes/llm-voice-assistant/nodejs/index.js +++ b/recipes/llm-voice-assistant/nodejs/index.js @@ -1,15 +1,16 @@ #! /usr/bin/env node -"use strict"; +'use strict'; -const { Porcupine, BuiltinKeyword } = require("@picovoice/porcupine-node"); -const { Cheetah } = require("@picovoice/cheetah-node"); -const { PicoLLM } = require("@picovoice/picollm-node"); -const { Orca } = require("@picovoice/orca-node"); -const { PvRecorder } = require("@picovoice/pvrecorder-node"); -const { PvSpeaker } = require("@picovoice/pvspeaker-node"); +const { Porcupine, BuiltinKeyword } = require('@picovoice/porcupine-node'); +const { Cheetah } = require('@picovoice/cheetah-node'); +const { PicoLLM } = require('@picovoice/picollm-node'); +const { Orca } = require('@picovoice/orca-node'); +const { PvRecorder } = require('@picovoice/pvrecorder-node'); +const { PvSpeaker } = require('@picovoice/pvspeaker-node'); -const { program } = require("commander"); -const { performance } = require("perf_hooks"); +const { program } = require('commander'); +const { performance } = require('perf_hooks'); +const process = require('process'); class RTFProfiler { @@ -67,104 +68,104 @@ let isInterrupted = false; program .requiredOption( - "-a, --access_key ", - "`AccessKey` obtained from the `Picovoice Console` (https://console.picovoice.ai/).", + '-a, --access_key ', + '`AccessKey` obtained from the `Picovoice Console` (https://console.picovoice.ai/).', ) .requiredOption( - "-p, --picollm_model_path ", - "Absolute path to the file containing LLM parameters (`.pllm`).", + '-p, --picollm_model_path ', + 'Absolute path to the file containing LLM parameters (`.pllm`).', ) .option( - "--keyword_model_path ", - "Absolute path to the keyword model file (`.ppn`). If not set, `Picovoice` will be the wake phrase.", + '--keyword_model_path ', + 'Absolute path to the keyword model file (`.ppn`). If not set, `Picovoice` will be the wake phrase.', ) .option( - "--cheetah_endpoint_duration_sec ", - "Duration of silence (pause) after the user's utterance to consider it the end of the utterance.", - "1" + '--cheetah_endpoint_duration_sec ', + 'Duration of silence (pause) after the user\'s utterance to consider it the end of the utterance.', + '1', ) .option( - "--picollm_device ", - "String representation of the device (e.g., CPU or GPU) to use for inference. If set to `best`, picoLLM \n" + - "picks the most suitable device. If set to `gpu`, the engine uses the first available GPU device. To \n" + - "select a specific GPU device, set this argument to `gpu:${GPU_INDEX}`, where `${GPU_INDEX}` is the index \n" + - "of the target GPU. If set to `cpu`, the engine will run on the CPU with the default number of threads. \n" + - "To specify the number of threads, set this argument to `cpu:${NUM_THREADS}`, where `${NUM_THREADS}` is \n" + - "the desired number of threads.", + '--picollm_device ', + 'String representation of the device (e.g., CPU or GPU) to use for inference. If set to `best`, picoLLM \n' + + 'picks the most suitable device. If set to `gpu`, the engine uses the first available GPU device. To \n' + + 'select a specific GPU device, set this argument to `gpu:${GPU_INDEX}`, where `${GPU_INDEX}` is the index \n' + + 'of the target GPU. If set to `cpu`, the engine will run on the CPU with the default number of threads. \n' + + 'To specify the number of threads, set this argument to `cpu:${NUM_THREADS}`, where `${NUM_THREADS}` is \n' + + 'the desired number of threads.', ) .option( - "--picollm_completion_token_limit ", - "Maximum number of tokens in the completion. Set to `None` to impose no limit.", - "256" + '--picollm_completion_token_limit ', + 'Maximum number of tokens in the completion. Set to `None` to impose no limit.', + '256', ) .option( - "--picollm_presence_penalty ", - "It penalizes logits already appearing in the partial completion if set to a positive value. If set to \n" + - "`0.0`, it has no effect.", - "0" + '--picollm_presence_penalty ', + 'It penalizes logits already appearing in the partial completion if set to a positive value. If set to \n' + + '`0.0`, it has no effect.', + '0', ) .option( - "--picollm_frequency_penalty ", - "If set to a positive floating-point value, it penalizes logits proportional to the frequency of their \n" + - "appearance in the partial completion. If set to `0.0`, it has no effect.", - "0" + '--picollm_frequency_penalty ', + 'If set to a positive floating-point value, it penalizes logits proportional to the frequency of their \n' + + 'appearance in the partial completion. If set to `0.0`, it has no effect.', + '0', ) .option( - "--picollm_temperature ", - "Sampling temperature. Temperature is a non-negative floating-point value that controls the randomness of \n" + - "the sampler. A higher temperature smoothens the samplers' output, increasing the randomness. In \n" + - "contrast, a lower temperature creates a narrower distribution and reduces variability. Setting it to \n" + - "`0` selects the maximum logit during sampling.", - "0" + '--picollm_temperature ', + 'Sampling temperature. Temperature is a non-negative floating-point value that controls the randomness of \n' + + 'the sampler. A higher temperature smoothens the samplers\' output, increasing the randomness. In \n' + + 'contrast, a lower temperature creates a narrower distribution and reduces variability. Setting it to \n' + + '`0` selects the maximum logit during sampling.', + '0', ) .option( - "--picollm_top_p ", - "A positive floating-point number within (0, 1]. It restricts the sampler's choices to high-probability \n" + - "logits that form the `top_p` portion of the probability mass. Hence, it avoids randomly selecting \n" + - "unlikely logits. A value of `1.` enables the sampler to pick any token with non-zero probability, \n" + - "turning off the feature.", - "1" + '--picollm_top_p ', + 'A positive floating-point number within (0, 1]. It restricts the sampler\'s choices to high-probability \n' + + 'logits that form the `top_p` portion of the probability mass. Hence, it avoids randomly selecting \n' + + 'unlikely logits. A value of `1.` enables the sampler to pick any token with non-zero probability, \n' + + 'turning off the feature.', + '1', ) .option( - "--orca_warmup_sec ", - "Duration of the synthesized audio to buffer before streaming it out. A higher value helps slower \n" + - "(e.g., Raspberry Pi) to keep up with real-time at the cost of increasing the initial delay.", - "0" + '--orca_warmup_sec ', + 'Duration of the synthesized audio to buffer before streaming it out. A higher value helps slower \n' + + '(e.g., Raspberry Pi) to keep up with real-time at the cost of increasing the initial delay.', + '0', ) - .option("--profile", "Show runtime profiling information.") - .option("--short_answers"); + .option('--profile', 'Show runtime profiling information.') + .option('--short_answers'); if (process.argv.length < 2) { program.help(); } program.parse(process.argv); -async function LlmVoiceAssistant() { - const accessKey = program["access_key"]; - const picollmModelPath = program["picollm_model_path"]; - const keywordModelPath = program["keyword_model_path"]; - const endpointDurationSec = Number(program["cheetah_endpoint_duration_sec"]); - const picollmDevice = program["picollm_device"]; - const picollmCompletionTokenLimit = Number(program["picollm_completion_token_limit"]); - const picollmPresencePenalty = Number(program["picollm_presence_penalty"]); - const picollmFrequencyPenalty = Number(program["picollm_frequency_penalty"]); - const picollmTemperature = Number(program["picollm_temperature"]); - const picollmTopP = Number(program["picollm_top_p"]); - const orcaWarmupSec = Number(program["orca_warmup_sec"]); - const profile = program["profile"]; - const isShortAnswers = program["short_answers"]; +async function llmVoiceAssistant() { + const accessKey = program.access_key; + const picollmModelPath = program.picollm_model_path; + const keywordModelPath = program.keyword_model_path; + const endpointDurationSec = Number(program.cheetah_endpoint_duration_sec); + const picollmDevice = program.picollm_device; + const picollmCompletionTokenLimit = Number(program.picollm_completion_token_limit); + const picollmPresencePenalty = Number(program.picollm_presence_penalty); + const picollmFrequencyPenalty = Number(program.picollm_frequency_penalty); + const picollmTemperature = Number(program.picollm_temperature); + const picollmTopP = Number(program.picollm_top_p); + const orcaWarmupSec = Number(program.orca_warmup_sec); + const profile = program.profile; + const isShortAnswers = program.short_answers; let porcupine = new Porcupine(accessKey, [keywordModelPath ?? BuiltinKeyword.PICOVOICE], [0.5]); - console.log(`→ Porcupine V${porcupine.version}`); + process.stdout.write(`\n→ Porcupine V${porcupine.version}`); const cheetah = new Cheetah(accessKey, { endpointDurationSec, enableAutomaticPunctuation: true }); - console.log(`→ Cheetah V${cheetah.version}`); + process.stdout.write(`\n→ Cheetah V${cheetah.version}`); const pllm = new PicoLLM(accessKey, picollmModelPath, { device: picollmDevice }); - console.log(`→ picoLLM V${pllm.version} <${pllm.model}>`); + process.stdout.write(`\n→ picoLLM V${pllm.version} <${pllm.model}>`); const orca = new Orca(accessKey); - console.log(`→ Orca V${orca.version}`); + process.stdout.write(`\n→ Orca V${orca.version}`); const dialog = pllm.getDialog(); const orcaStream = orca.streamOpen(); @@ -172,12 +173,12 @@ async function LlmVoiceAssistant() { const recorder = new PvRecorder(porcupine.frameLength); recorder.start(); - const ppnPrompt = `\n$ Say ${keywordModelPath ? "the wake word" : "`Picovoice`"} ...`; - console.log(ppnPrompt); + const ppnPrompt = `\n$ Say ${keywordModelPath ? 'the wake word' : '`Picovoice`'} ...`; + process.stdout.write(`\n${ppnPrompt}`); let isWakeWordDetected = false; let isEndpointReached = false; - let userRequest = ""; + let userRequest = ''; let pcmBuffer = []; const porcupineProfiler = new RTFProfiler(porcupine.sampleRate); @@ -186,6 +187,30 @@ async function LlmVoiceAssistant() { let utteranceEndMillisec = 0; let delaySec = -1; + function handleLlmText(text, isStartedPlaying) { + process.stdout.write(text); + orcaProfiler.tick(); + const pcm = orcaStream.synthesize(text.replace('\n', ' . ')); + orcaProfiler.tock(pcm); + if (pcm !== null) { + if (delaySec === -1) { + delaySec = (performance.now() - utteranceEndMillisec) / 1000; + } + pcmBuffer.push(...pcm); + } + + if (pcmBuffer.length > 0) { + if (pcmBuffer.length >= orcaWarmupSec * orca.sampleRate || isStartedPlaying) { + const arrayBuffer = new Int16Array(pcmBuffer).buffer; + const written = speaker.write(arrayBuffer); + pcmBuffer = pcmBuffer.slice(written); + return true; + } + } + + return false; + } + try { while (!isInterrupted) { if (!isWakeWordDetected) { @@ -195,10 +220,10 @@ async function LlmVoiceAssistant() { porcupineProfiler.tock(pcm); if (isWakeWordDetected) { if (profile) { - console.log(`\n[Porcupine RTF: ${porcupineProfiler.rtf()}]`); + process.stdout.write(`\n[Porcupine RTF: ${porcupineProfiler.rtf()}]\n`); } - process.stdout.write("\n$ Wake word detected, utter your request or question ..."); - process.stdout.write("\n\nUser > "); + process.stdout.write('\n$ Wake word detected, utter your request or question ...'); + process.stdout.write('\n\nUser > '); } } else if (!isEndpointReached) { const pcm = await recorder.read(); @@ -213,10 +238,10 @@ async function LlmVoiceAssistant() { const remainingTranscript = cheetah.flush(); cheetahProfiler.tock(pcm); userRequest += remainingTranscript; - process.stdout.write(`${remainingTranscript}\n\n`); + process.stdout.write(`${remainingTranscript}\n`); isEndpointReached = isEndpoint; if (profile) { - console.log(`[Cheetah RTF: ${cheetahProfiler.rtf()}]`); + process.stdout.write(`\n[Cheetah RTF: ${cheetahProfiler.rtf()}]`); } } } else { @@ -227,42 +252,16 @@ async function LlmVoiceAssistant() { const picollmProfiler = new TPSProfiler(); const stopPhrases = [ - "", // Llama-2, Mistral, and Mixtral - "", // Gemma - "<|endoftext|>", // Phi-2 - "<|eot_id|>", // Llama-3 + '', // Llama-2, Mistral, and Mixtral + '', // Gemma + '<|endoftext|>', // Phi-2 + '<|eot_id|>', // Llama-3 ]; - let completion = ""; + let completion = ''; let isStartedPlaying = false; - function llmCallback(text) { - picollmProfiler.tock(); - completion += text; - if (!stopPhrases.some(x => completion.includes(x))) { - process.stdout.write(text); - orcaProfiler.tick(); - const pcm = orcaStream.synthesize(text.replace("\n", " . ")); - orcaProfiler.tock(pcm); - if (pcm !== null) { - if (delaySec === -1) { - delaySec = (performance.now() - utteranceEndMillisec) / 1000; - } - pcmBuffer.push(...pcm); - } - - if (pcmBuffer.length > 0) { - if (pcmBuffer.length >= orcaWarmupSec * orca.sampleRate || isStartedPlaying) { - const arrayBuffer = new Int16Array(pcmBuffer).buffer; - const written = speaker.write(arrayBuffer); - pcmBuffer = pcmBuffer.slice(written); - isStartedPlaying = true; - } - } - } - } - - process.stdout.write(`LLM >`); + process.stdout.write(`\n\nLLM >`); const res = await pllm.generate( dialog.prompt(), { @@ -272,20 +271,26 @@ async function LlmVoiceAssistant() { frequencyPenalty: picollmFrequencyPenalty, temperature: picollmTemperature, topP: picollmTopP, - streamCallback: llmCallback - } + streamCallback: text => { + picollmProfiler.tock(); + completion += text; + if (!stopPhrases.some(x => completion.includes(x))) { + isStartedPlaying = handleLlmText(text, isStartedPlaying); + } + }, + }, ); dialog.addLLMResponse(res.completion); if (profile) { - console.log(`\n[picoLLM TPS: ${picollmProfiler.tps()}]`); + process.stdout.write(`\n[picoLLM TPS: ${picollmProfiler.tps()}]`); } orcaProfiler.tick(); const flushedPcm = orcaStream.flush(); orcaProfiler.tock(flushedPcm); - console.log(`\n[Orca RTF: ${orcaProfiler.rtf()}]`); - console.log(`[Delay: ${delaySec.toFixed(3)} sec]`); + process.stdout.write(`\n[Orca RTF: ${orcaProfiler.rtf()}]`); + process.stdout.write(`\n[Delay: ${delaySec.toFixed(3)} sec]`); if (flushedPcm !== null) { pcmBuffer.push(...flushedPcm); } @@ -297,9 +302,9 @@ async function LlmVoiceAssistant() { isWakeWordDetected = false; isEndpointReached = false; - userRequest = ""; + userRequest = ''; pcmBuffer = []; - console.log(ppnPrompt); + process.stdout.write(`\n${ppnPrompt}\n`); } } } finally { @@ -316,14 +321,14 @@ async function LlmVoiceAssistant() { } // setup interrupt -process.on("SIGINT", function () { +process.on('SIGINT', function() { isInterrupted = true; }); -(async function () { +(async function() { try { - await LlmVoiceAssistant(); + await llmVoiceAssistant(); } catch (e) { - console.error(e.toString()); + process.stderr.write(`\n${e.toString()}\n`); } })(); diff --git a/recipes/llm-voice-assistant/nodejs/package.json b/recipes/llm-voice-assistant/nodejs/package.json index e7b2ca5..fd79d8c 100644 --- a/recipes/llm-voice-assistant/nodejs/package.json +++ b/recipes/llm-voice-assistant/nodejs/package.json @@ -3,7 +3,8 @@ "version": "1.0.0", "description": "", "scripts": { - "start": "node index.js" + "start": "node index.js", + "lint": "eslint . --ext .js" }, "keywords": [ "Picovoice", @@ -19,7 +20,12 @@ "@picovoice/porcupine-node": "^3.0.3", "@picovoice/pvrecorder-node": "^1.2.3", "@picovoice/pvspeaker-node": "^1.0.0", - "commander": "^6.1.0" + "@typescript-eslint/eslint-plugin": "^5.19.0", + "@typescript-eslint/parser": "^5.19.0", + "commander": "^6.1.0", + "eslint": "^8.13.0", + "eslint-plugin-jest": "^27.1.6", + "typescript": "^4.6.2" }, "homepage": "https://picovoice.ai/", "repository": { diff --git a/recipes/llm-voice-assistant/nodejs/yarn.lock b/recipes/llm-voice-assistant/nodejs/yarn.lock index 1df7f5d..51ea5c7 100644 --- a/recipes/llm-voice-assistant/nodejs/yarn.lock +++ b/recipes/llm-voice-assistant/nodejs/yarn.lock @@ -2,6 +2,78 @@ # yarn lockfile v1 +"@eslint-community/eslint-utils@^4.2.0": + version "4.4.0" + resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz#a23514e8fb9af1269d5f7788aa556798d61c6b59" + integrity sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA== + dependencies: + eslint-visitor-keys "^3.3.0" + +"@eslint-community/regexpp@^4.4.0", "@eslint-community/regexpp@^4.6.1": + version "4.11.0" + resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.11.0.tgz#b0ffd0312b4a3fd2d6f77237e7248a5ad3a680ae" + integrity sha512-G/M/tIiMrTAxEWRfLfQJMmGNX28IxBg4PBz8XqQhqUHLFI6TL2htpIB1iQCj144V5ee/JaKyT9/WZ0MGZWfA7A== + +"@eslint/eslintrc@^2.1.4": + version "2.1.4" + resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-2.1.4.tgz#388a269f0f25c1b6adc317b5a2c55714894c70ad" + integrity sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ== + dependencies: + ajv "^6.12.4" + debug "^4.3.2" + espree "^9.6.0" + globals "^13.19.0" + ignore "^5.2.0" + import-fresh "^3.2.1" + js-yaml "^4.1.0" + minimatch "^3.1.2" + strip-json-comments "^3.1.1" + +"@eslint/js@8.57.0": + version "8.57.0" + resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.57.0.tgz#a5417ae8427873f1dd08b70b3574b453e67b5f7f" + integrity sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g== + +"@humanwhocodes/config-array@^0.11.14": + version "0.11.14" + resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.11.14.tgz#d78e481a039f7566ecc9660b4ea7fe6b1fec442b" + integrity sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg== + dependencies: + "@humanwhocodes/object-schema" "^2.0.2" + debug "^4.3.1" + minimatch "^3.0.5" + +"@humanwhocodes/module-importer@^1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz#af5b2691a22b44be847b0ca81641c5fb6ad0172c" + integrity sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA== + +"@humanwhocodes/object-schema@^2.0.2": + version "2.0.3" + resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz#4a2868d75d6d6963e423bcf90b7fd1be343409d3" + integrity sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA== + +"@nodelib/fs.scandir@2.1.5": + version "2.1.5" + resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5" + integrity sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g== + dependencies: + "@nodelib/fs.stat" "2.0.5" + run-parallel "^1.1.9" + +"@nodelib/fs.stat@2.0.5", "@nodelib/fs.stat@^2.0.2": + version "2.0.5" + resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz#5bd262af94e9d25bd1e71b05deed44876a222e8b" + integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A== + +"@nodelib/fs.walk@^1.2.3", "@nodelib/fs.walk@^1.2.8": + version "1.2.8" + resolved "https://registry.yarnpkg.com/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz#e95737e8bb6746ddedf69c556953494f196fe69a" + integrity sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg== + dependencies: + "@nodelib/fs.scandir" "2.1.5" + fastq "^1.6.0" + "@picovoice/cheetah-node@^2.0.2": version "2.0.2" resolved "https://registry.yarnpkg.com/@picovoice/cheetah-node/-/cheetah-node-2.0.2.tgz#6d1153f0ada1e625d13b4521c8dcac5a62eeaf24" @@ -32,7 +104,834 @@ resolved "https://registry.yarnpkg.com/@picovoice/pvspeaker-node/-/pvspeaker-node-1.0.0.tgz#6b87b86138a8bb256e46ea41cb7d258ff8534337" integrity sha512-qhH4ktObGj/Dd7iiptPXqf89U3l7FbCiaB1xY439YE3EFLsfsvZvjAzEwsF5A01h47AbXJTCdAjvkXrcsdt0/w== +"@types/json-schema@^7.0.9": + version "7.0.15" + resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.15.tgz#596a1747233694d50f6ad8a7869fcb6f56cf5841" + integrity sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA== + +"@types/semver@^7.3.12": + version "7.5.8" + resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.5.8.tgz#8268a8c57a3e4abd25c165ecd36237db7948a55e" + integrity sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ== + +"@typescript-eslint/eslint-plugin@^5.19.0": + version "5.62.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.62.0.tgz#aeef0328d172b9e37d9bab6dbc13b87ed88977db" + integrity sha512-TiZzBSJja/LbhNPvk6yc0JrX9XqhQ0hdh6M2svYfsHGejaKFIAGd9MQ+ERIMzLGlN/kZoYIgdxFV0PuljTKXag== + dependencies: + "@eslint-community/regexpp" "^4.4.0" + "@typescript-eslint/scope-manager" "5.62.0" + "@typescript-eslint/type-utils" "5.62.0" + "@typescript-eslint/utils" "5.62.0" + debug "^4.3.4" + graphemer "^1.4.0" + ignore "^5.2.0" + natural-compare-lite "^1.4.0" + semver "^7.3.7" + tsutils "^3.21.0" + +"@typescript-eslint/parser@^5.19.0": + version "5.62.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.62.0.tgz#1b63d082d849a2fcae8a569248fbe2ee1b8a56c7" + integrity sha512-VlJEV0fOQ7BExOsHYAGrgbEiZoi8D+Bl2+f6V2RrXerRSylnp+ZBHmPvaIa8cz0Ajx7WO7Z5RqfgYg7ED1nRhA== + dependencies: + "@typescript-eslint/scope-manager" "5.62.0" + "@typescript-eslint/types" "5.62.0" + "@typescript-eslint/typescript-estree" "5.62.0" + debug "^4.3.4" + +"@typescript-eslint/scope-manager@5.62.0": + version "5.62.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.62.0.tgz#d9457ccc6a0b8d6b37d0eb252a23022478c5460c" + integrity sha512-VXuvVvZeQCQb5Zgf4HAxc04q5j+WrNAtNh9OwCsCgpKqESMTu3tF/jhZ3xG6T4NZwWl65Bg8KuS2uEvhSfLl0w== + dependencies: + "@typescript-eslint/types" "5.62.0" + "@typescript-eslint/visitor-keys" "5.62.0" + +"@typescript-eslint/type-utils@5.62.0": + version "5.62.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-5.62.0.tgz#286f0389c41681376cdad96b309cedd17d70346a" + integrity sha512-xsSQreu+VnfbqQpW5vnCJdq1Z3Q0U31qiWmRhr98ONQmcp/yhiPJFPq8MXiJVLiksmOKSjIldZzkebzHuCGzew== + dependencies: + "@typescript-eslint/typescript-estree" "5.62.0" + "@typescript-eslint/utils" "5.62.0" + debug "^4.3.4" + tsutils "^3.21.0" + +"@typescript-eslint/types@5.62.0": + version "5.62.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.62.0.tgz#258607e60effa309f067608931c3df6fed41fd2f" + integrity sha512-87NVngcbVXUahrRTqIK27gD2t5Cu1yuCXxbLcFtCzZGlfyVWWh8mLHkoxzjsB6DDNnvdL+fW8MiwPEJyGJQDgQ== + +"@typescript-eslint/typescript-estree@5.62.0": + version "5.62.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.62.0.tgz#7d17794b77fabcac615d6a48fb143330d962eb9b" + integrity sha512-CmcQ6uY7b9y694lKdRB8FEel7JbU/40iSAPomu++SjLMntB+2Leay2LO6i8VnJk58MtE9/nQSFIH6jpyRWyYzA== + dependencies: + "@typescript-eslint/types" "5.62.0" + "@typescript-eslint/visitor-keys" "5.62.0" + debug "^4.3.4" + globby "^11.1.0" + is-glob "^4.0.3" + semver "^7.3.7" + tsutils "^3.21.0" + +"@typescript-eslint/utils@5.62.0", "@typescript-eslint/utils@^5.10.0": + version "5.62.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-5.62.0.tgz#141e809c71636e4a75daa39faed2fb5f4b10df86" + integrity sha512-n8oxjeb5aIbPFEtmQxQYOLI0i9n5ySBEY/ZEHHZqKQSFnxio1rv6dthascc9dLuwrL0RC5mPCxB7vnAVGAYWAQ== + dependencies: + "@eslint-community/eslint-utils" "^4.2.0" + "@types/json-schema" "^7.0.9" + "@types/semver" "^7.3.12" + "@typescript-eslint/scope-manager" "5.62.0" + "@typescript-eslint/types" "5.62.0" + "@typescript-eslint/typescript-estree" "5.62.0" + eslint-scope "^5.1.1" + semver "^7.3.7" + +"@typescript-eslint/visitor-keys@5.62.0": + version "5.62.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.62.0.tgz#2174011917ce582875954ffe2f6912d5931e353e" + integrity sha512-07ny+LHRzQXepkGg6w0mFY41fVUNBrL2Roj/++7V1txKugfjm/Ci/qSND03r2RhlJhJYMcTn9AhhSSqQp0Ysyw== + dependencies: + "@typescript-eslint/types" "5.62.0" + eslint-visitor-keys "^3.3.0" + +"@ungap/structured-clone@^1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@ungap/structured-clone/-/structured-clone-1.2.0.tgz#756641adb587851b5ccb3e095daf27ae581c8406" + integrity sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ== + +acorn-jsx@^5.3.2: + version "5.3.2" + resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937" + integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ== + +acorn@^8.9.0: + version "8.12.1" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.12.1.tgz#71616bdccbe25e27a54439e0046e89ca76df2248" + integrity sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg== + +ajv@^6.12.4: + version "6.12.6" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" + integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== + dependencies: + fast-deep-equal "^3.1.1" + fast-json-stable-stringify "^2.0.0" + json-schema-traverse "^0.4.1" + uri-js "^4.2.2" + +ansi-regex@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" + integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== + +ansi-styles@^4.1.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" + integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== + dependencies: + color-convert "^2.0.1" + +argparse@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" + integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== + +array-union@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d" + integrity sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw== + +balanced-match@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" + integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== + +brace-expansion@^1.1.7: + version "1.1.11" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" + integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== + dependencies: + balanced-match "^1.0.0" + concat-map "0.0.1" + +braces@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.3.tgz#490332f40919452272d55a8480adc0c441358789" + integrity sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA== + dependencies: + fill-range "^7.1.1" + +callsites@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" + integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== + +chalk@^4.0.0: + version "4.1.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" + integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== + dependencies: + ansi-styles "^4.1.0" + supports-color "^7.1.0" + +color-convert@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" + integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== + dependencies: + color-name "~1.1.4" + +color-name@~1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" + integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== + commander@^6.1.0: version "6.2.1" resolved "https://registry.yarnpkg.com/commander/-/commander-6.2.1.tgz#0792eb682dfbc325999bb2b84fddddba110ac73c" integrity sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA== + +concat-map@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" + integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== + +cross-spawn@^7.0.2: + version "7.0.3" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" + integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== + dependencies: + path-key "^3.1.0" + shebang-command "^2.0.0" + which "^2.0.1" + +debug@^4.3.1, debug@^4.3.2, debug@^4.3.4: + version "4.3.6" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.6.tgz#2ab2c38fbaffebf8aa95fdfe6d88438c7a13c52b" + integrity sha512-O/09Bd4Z1fBrU4VzkhFqVgpPzaGbw6Sm9FEkBT1A/YBXQFGuuSxa1dN2nxgxS34JmKXqYx8CZAwEVoJFImUXIg== + dependencies: + ms "2.1.2" + +deep-is@^0.1.3: + version "0.1.4" + resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831" + integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ== + +dir-glob@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-3.0.1.tgz#56dbf73d992a4a93ba1584f4534063fd2e41717f" + integrity sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA== + dependencies: + path-type "^4.0.0" + +doctrine@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-3.0.0.tgz#addebead72a6574db783639dc87a121773973961" + integrity sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w== + dependencies: + esutils "^2.0.2" + +escape-string-regexp@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" + integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== + +eslint-plugin-jest@^27.1.6: + version "27.9.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-jest/-/eslint-plugin-jest-27.9.0.tgz#7c98a33605e1d8b8442ace092b60e9919730000b" + integrity sha512-QIT7FH7fNmd9n4se7FFKHbsLKGQiw885Ds6Y/sxKgCZ6natwCsXdgPOADnYVxN2QrRweF0FZWbJ6S7Rsn7llug== + dependencies: + "@typescript-eslint/utils" "^5.10.0" + +eslint-scope@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.1.tgz#e786e59a66cb92b3f6c1fb0d508aab174848f48c" + integrity sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw== + dependencies: + esrecurse "^4.3.0" + estraverse "^4.1.1" + +eslint-scope@^7.2.2: + version "7.2.2" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-7.2.2.tgz#deb4f92563390f32006894af62a22dba1c46423f" + integrity sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg== + dependencies: + esrecurse "^4.3.0" + estraverse "^5.2.0" + +eslint-visitor-keys@^3.3.0, eslint-visitor-keys@^3.4.1, eslint-visitor-keys@^3.4.3: + version "3.4.3" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz#0cd72fe8550e3c2eae156a96a4dddcd1c8ac5800" + integrity sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag== + +eslint@^8.13.0: + version "8.57.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.57.0.tgz#c786a6fd0e0b68941aaf624596fb987089195668" + integrity sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ== + dependencies: + "@eslint-community/eslint-utils" "^4.2.0" + "@eslint-community/regexpp" "^4.6.1" + "@eslint/eslintrc" "^2.1.4" + "@eslint/js" "8.57.0" + "@humanwhocodes/config-array" "^0.11.14" + "@humanwhocodes/module-importer" "^1.0.1" + "@nodelib/fs.walk" "^1.2.8" + "@ungap/structured-clone" "^1.2.0" + ajv "^6.12.4" + chalk "^4.0.0" + cross-spawn "^7.0.2" + debug "^4.3.2" + doctrine "^3.0.0" + escape-string-regexp "^4.0.0" + eslint-scope "^7.2.2" + eslint-visitor-keys "^3.4.3" + espree "^9.6.1" + esquery "^1.4.2" + esutils "^2.0.2" + fast-deep-equal "^3.1.3" + file-entry-cache "^6.0.1" + find-up "^5.0.0" + glob-parent "^6.0.2" + globals "^13.19.0" + graphemer "^1.4.0" + ignore "^5.2.0" + imurmurhash "^0.1.4" + is-glob "^4.0.0" + is-path-inside "^3.0.3" + js-yaml "^4.1.0" + json-stable-stringify-without-jsonify "^1.0.1" + levn "^0.4.1" + lodash.merge "^4.6.2" + minimatch "^3.1.2" + natural-compare "^1.4.0" + optionator "^0.9.3" + strip-ansi "^6.0.1" + text-table "^0.2.0" + +espree@^9.6.0, espree@^9.6.1: + version "9.6.1" + resolved "https://registry.yarnpkg.com/espree/-/espree-9.6.1.tgz#a2a17b8e434690a5432f2f8018ce71d331a48c6f" + integrity sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ== + dependencies: + acorn "^8.9.0" + acorn-jsx "^5.3.2" + eslint-visitor-keys "^3.4.1" + +esquery@^1.4.2: + version "1.6.0" + resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.6.0.tgz#91419234f804d852a82dceec3e16cdc22cf9dae7" + integrity sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg== + dependencies: + estraverse "^5.1.0" + +esrecurse@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.3.0.tgz#7ad7964d679abb28bee72cec63758b1c5d2c9921" + integrity sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag== + dependencies: + estraverse "^5.2.0" + +estraverse@^4.1.1: + version "4.3.0" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d" + integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw== + +estraverse@^5.1.0, estraverse@^5.2.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.3.0.tgz#2eea5290702f26ab8fe5370370ff86c965d21123" + integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA== + +esutils@^2.0.2: + version "2.0.3" + resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" + integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== + +fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: + version "3.1.3" + resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" + integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== + +fast-glob@^3.2.9: + version "3.3.2" + resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.3.2.tgz#a904501e57cfdd2ffcded45e99a54fef55e46129" + integrity sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow== + dependencies: + "@nodelib/fs.stat" "^2.0.2" + "@nodelib/fs.walk" "^1.2.3" + glob-parent "^5.1.2" + merge2 "^1.3.0" + micromatch "^4.0.4" + +fast-json-stable-stringify@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" + integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== + +fast-levenshtein@^2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" + integrity sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw== + +fastq@^1.6.0: + version "1.17.1" + resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.17.1.tgz#2a523f07a4e7b1e81a42b91b8bf2254107753b47" + integrity sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w== + dependencies: + reusify "^1.0.4" + +file-entry-cache@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-6.0.1.tgz#211b2dd9659cb0394b073e7323ac3c933d522027" + integrity sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg== + dependencies: + flat-cache "^3.0.4" + +fill-range@^7.1.1: + version "7.1.1" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.1.1.tgz#44265d3cac07e3ea7dc247516380643754a05292" + integrity sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg== + dependencies: + to-regex-range "^5.0.1" + +find-up@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc" + integrity sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng== + dependencies: + locate-path "^6.0.0" + path-exists "^4.0.0" + +flat-cache@^3.0.4: + version "3.2.0" + resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-3.2.0.tgz#2c0c2d5040c99b1632771a9d105725c0115363ee" + integrity sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw== + dependencies: + flatted "^3.2.9" + keyv "^4.5.3" + rimraf "^3.0.2" + +flatted@^3.2.9: + version "3.3.1" + resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.3.1.tgz#21db470729a6734d4997002f439cb308987f567a" + integrity sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw== + +fs.realpath@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" + integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== + +glob-parent@^5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" + integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== + dependencies: + is-glob "^4.0.1" + +glob-parent@^6.0.2: + version "6.0.2" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-6.0.2.tgz#6d237d99083950c79290f24c7642a3de9a28f9e3" + integrity sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A== + dependencies: + is-glob "^4.0.3" + +glob@^7.1.3: + version "7.2.3" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b" + integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.1.1" + once "^1.3.0" + path-is-absolute "^1.0.0" + +globals@^13.19.0: + version "13.24.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-13.24.0.tgz#8432a19d78ce0c1e833949c36adb345400bb1171" + integrity sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ== + dependencies: + type-fest "^0.20.2" + +globby@^11.1.0: + version "11.1.0" + resolved "https://registry.yarnpkg.com/globby/-/globby-11.1.0.tgz#bd4be98bb042f83d796f7e3811991fbe82a0d34b" + integrity sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g== + dependencies: + array-union "^2.1.0" + dir-glob "^3.0.1" + fast-glob "^3.2.9" + ignore "^5.2.0" + merge2 "^1.4.1" + slash "^3.0.0" + +graphemer@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/graphemer/-/graphemer-1.4.0.tgz#fb2f1d55e0e3a1849aeffc90c4fa0dd53a0e66c6" + integrity sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag== + +has-flag@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" + integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== + +ignore@^5.2.0: + version "5.3.2" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.3.2.tgz#3cd40e729f3643fd87cb04e50bf0eb722bc596f5" + integrity sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g== + +import-fresh@^3.2.1: + version "3.3.0" + resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b" + integrity sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw== + dependencies: + parent-module "^1.0.0" + resolve-from "^4.0.0" + +imurmurhash@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" + integrity sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA== + +inflight@^1.0.4: + version "1.0.6" + resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" + integrity sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA== + dependencies: + once "^1.3.0" + wrappy "1" + +inherits@2: + version "2.0.4" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" + integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== + +is-extglob@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" + integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ== + +is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3: + version "4.0.3" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" + integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== + dependencies: + is-extglob "^2.1.1" + +is-number@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" + integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== + +is-path-inside@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-3.0.3.tgz#d231362e53a07ff2b0e0ea7fed049161ffd16283" + integrity sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ== + +isexe@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" + integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== + +js-yaml@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602" + integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA== + dependencies: + argparse "^2.0.1" + +json-buffer@3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.1.tgz#9338802a30d3b6605fbe0613e094008ca8c05a13" + integrity sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ== + +json-schema-traverse@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" + integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== + +json-stable-stringify-without-jsonify@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" + integrity sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw== + +keyv@^4.5.3: + version "4.5.4" + resolved "https://registry.yarnpkg.com/keyv/-/keyv-4.5.4.tgz#a879a99e29452f942439f2a405e3af8b31d4de93" + integrity sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw== + dependencies: + json-buffer "3.0.1" + +levn@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/levn/-/levn-0.4.1.tgz#ae4562c007473b932a6200d403268dd2fffc6ade" + integrity sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ== + dependencies: + prelude-ls "^1.2.1" + type-check "~0.4.0" + +locate-path@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-6.0.0.tgz#55321eb309febbc59c4801d931a72452a681d286" + integrity sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw== + dependencies: + p-locate "^5.0.0" + +lodash.merge@^4.6.2: + version "4.6.2" + resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" + integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== + +merge2@^1.3.0, merge2@^1.4.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" + integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== + +micromatch@^4.0.4: + version "4.0.8" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.8.tgz#d66fa18f3a47076789320b9b1af32bd86d9fa202" + integrity sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA== + dependencies: + braces "^3.0.3" + picomatch "^2.3.1" + +minimatch@^3.0.5, minimatch@^3.1.1, minimatch@^3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" + integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== + dependencies: + brace-expansion "^1.1.7" + +ms@2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" + integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== + +natural-compare-lite@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/natural-compare-lite/-/natural-compare-lite-1.4.0.tgz#17b09581988979fddafe0201e931ba933c96cbb4" + integrity sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g== + +natural-compare@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" + integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw== + +once@^1.3.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" + integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w== + dependencies: + wrappy "1" + +optionator@^0.9.3: + version "0.9.4" + resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.4.tgz#7ea1c1a5d91d764fb282139c88fe11e182a3a734" + integrity sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g== + dependencies: + deep-is "^0.1.3" + fast-levenshtein "^2.0.6" + levn "^0.4.1" + prelude-ls "^1.2.1" + type-check "^0.4.0" + word-wrap "^1.2.5" + +p-limit@^3.0.2: + version "3.1.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b" + integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ== + dependencies: + yocto-queue "^0.1.0" + +p-locate@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-5.0.0.tgz#83c8315c6785005e3bd021839411c9e110e6d834" + integrity sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw== + dependencies: + p-limit "^3.0.2" + +parent-module@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2" + integrity sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g== + dependencies: + callsites "^3.0.0" + +path-exists@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" + integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== + +path-is-absolute@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" + integrity sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg== + +path-key@^3.1.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" + integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== + +path-type@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" + integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== + +picomatch@^2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" + integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== + +prelude-ls@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" + integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g== + +punycode@^2.1.0: + version "2.3.1" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.1.tgz#027422e2faec0b25e1549c3e1bd8309b9133b6e5" + integrity sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg== + +queue-microtask@^1.2.2: + version "1.2.3" + resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" + integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== + +resolve-from@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" + integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== + +reusify@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" + integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== + +rimraf@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" + integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== + dependencies: + glob "^7.1.3" + +run-parallel@^1.1.9: + version "1.2.0" + resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.2.0.tgz#66d1368da7bdf921eb9d95bd1a9229e7f21a43ee" + integrity sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA== + dependencies: + queue-microtask "^1.2.2" + +semver@^7.3.7: + version "7.6.3" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.3.tgz#980f7b5550bc175fb4dc09403085627f9eb33143" + integrity sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A== + +shebang-command@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" + integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== + dependencies: + shebang-regex "^3.0.0" + +shebang-regex@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" + integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== + +slash@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" + integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== + +strip-ansi@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + +strip-json-comments@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" + integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== + +supports-color@^7.1.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" + integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== + dependencies: + has-flag "^4.0.0" + +text-table@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" + integrity sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw== + +to-regex-range@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" + integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== + dependencies: + is-number "^7.0.0" + +tslib@^1.8.1: + version "1.14.1" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" + integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== + +tsutils@^3.21.0: + version "3.21.0" + resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.21.0.tgz#b48717d394cea6c1e096983eed58e9d61715b623" + integrity sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA== + dependencies: + tslib "^1.8.1" + +type-check@^0.4.0, type-check@~0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1" + integrity sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew== + dependencies: + prelude-ls "^1.2.1" + +type-fest@^0.20.2: + version "0.20.2" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.20.2.tgz#1bf207f4b28f91583666cb5fbd327887301cd5f4" + integrity sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ== + +typescript@^4.6.2: + version "4.9.5" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.9.5.tgz#095979f9bcc0d09da324d58d03ce8f8374cbe65a" + integrity sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g== + +uri-js@^4.2.2: + version "4.4.1" + resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e" + integrity sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg== + dependencies: + punycode "^2.1.0" + +which@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" + integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== + dependencies: + isexe "^2.0.0" + +word-wrap@^1.2.5: + version "1.2.5" + resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.5.tgz#d2c45c6dd4fbce621a66f136cbe328afd0410b34" + integrity sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA== + +wrappy@1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" + integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== + +yocto-queue@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" + integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== From 47b92417f3908a295f26e38bff28d014a829cc85 Mon Sep 17 00:00:00 2001 From: Albert Ho Date: Fri, 23 Aug 2024 17:23:33 -0700 Subject: [PATCH 4/5] spelling --- res/.lint/spell-check/dict.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/res/.lint/spell-check/dict.txt b/res/.lint/spell-check/dict.txt index 69de399..6b56449 100644 --- a/res/.lint/spell-check/dict.txt +++ b/res/.lint/spell-check/dict.txt @@ -8,6 +8,8 @@ iife llmvoiceassistant logcat logit +logits +Millisec mixtral numpy pico From 62e9115646f27ffae736dcc91b494435b987d2c7 Mon Sep 17 00:00:00 2001 From: Albert Ho Date: Mon, 26 Aug 2024 09:42:06 -0700 Subject: [PATCH 5/5] clean-up --- recipes/llm-voice-assistant/nodejs/index.js | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/recipes/llm-voice-assistant/nodejs/index.js b/recipes/llm-voice-assistant/nodejs/index.js index 57b855f..140b203 100755 --- a/recipes/llm-voice-assistant/nodejs/index.js +++ b/recipes/llm-voice-assistant/nodejs/index.js @@ -220,9 +220,9 @@ async function llmVoiceAssistant() { porcupineProfiler.tock(pcm); if (isWakeWordDetected) { if (profile) { - process.stdout.write(`\n[Porcupine RTF: ${porcupineProfiler.rtf()}]\n`); + process.stdout.write(`\n[Porcupine RTF: ${porcupineProfiler.rtf()}]`); } - process.stdout.write('\n$ Wake word detected, utter your request or question ...'); + process.stdout.write('\n\n$ Wake word detected, utter your request or question ...'); process.stdout.write('\n\nUser > '); } } else if (!isEndpointReached) { @@ -261,7 +261,7 @@ async function llmVoiceAssistant() { let completion = ''; let isStartedPlaying = false; - process.stdout.write(`\n\nLLM >`); + process.stdout.write(`\nLLM >`); const res = await pllm.generate( dialog.prompt(), { @@ -289,8 +289,10 @@ async function llmVoiceAssistant() { orcaProfiler.tick(); const flushedPcm = orcaStream.flush(); orcaProfiler.tock(flushedPcm); - process.stdout.write(`\n[Orca RTF: ${orcaProfiler.rtf()}]`); - process.stdout.write(`\n[Delay: ${delaySec.toFixed(3)} sec]`); + if (profile) { + process.stdout.write(`\n[Orca RTF: ${orcaProfiler.rtf()}]`); + process.stdout.write(`\n[Delay: ${delaySec.toFixed(3)} sec]`); + } if (flushedPcm !== null) { pcmBuffer.push(...flushedPcm); }