echogarden 1.1.0 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/data/schemas/options.json +56 -1
- package/dist/alignment/DTWSequenceAlignmentWindowed.js +6 -1
- package/dist/alignment/DTWSequenceAlignmentWindowed.js.map +1 -1
- package/dist/alignment/SpeechAlignment.js +1 -1
- package/dist/alignment/SpeechAlignment.js.map +1 -1
- package/dist/api/APIOptions.d.ts +3 -0
- package/dist/api/Alignment.js +2 -1
- package/dist/api/Alignment.js.map +1 -1
- package/dist/api/GlobalOptions.d.ts +10 -0
- package/dist/api/GlobalOptions.js +21 -3
- package/dist/api/GlobalOptions.js.map +1 -1
- package/dist/cli/CLI.js +224 -191
- package/dist/cli/CLI.js.map +1 -1
- package/dist/cli/CLIConfigFile.js +7 -7
- package/dist/cli/CLIConfigFile.js.map +1 -1
- package/dist/cli/CLIOptions.d.ts +7 -0
- package/dist/cli/CLIOptions.js +2 -0
- package/dist/cli/CLIOptions.js.map +1 -0
- package/dist/cli/CLIParser.d.ts +5 -6
- package/dist/cli/CLIParser.js +16 -12
- package/dist/cli/CLIParser.js.map +1 -1
- package/dist/recognition/WhisperSTT.js +34 -17
- package/dist/recognition/WhisperSTT.js.map +1 -1
- package/dist/utilities/FileDownloader.js +3 -1
- package/dist/utilities/FileDownloader.js.map +1 -1
- package/dist/utilities/Logger.d.ts +5 -4
- package/dist/utilities/Logger.js +19 -8
- package/dist/utilities/Logger.js.map +1 -1
- package/dist/utilities/PackageManager.js +3 -2
- package/dist/utilities/PackageManager.js.map +1 -1
- package/dist/utilities/Utilities.d.ts +1 -1
- package/dist/utilities/Utilities.js +9 -9
- package/dist/utilities/Utilities.js.map +1 -1
- package/docs/API.md +3 -10
- package/docs/CLI.md +88 -77
- package/docs/Contributing.md +4 -4
- package/docs/Engines.md +1 -1
- package/docs/Options.md +25 -2
- package/docs/Releases.md +8 -8
- package/docs/Tasklist.md +17 -19
- package/docs/Technical.md +2 -2
- package/package.json +3 -3
- package/src/alignment/DTWSequenceAlignmentWindowed.ts +7 -1
- package/src/alignment/SpeechAlignment.ts +1 -1
- package/src/api/APIOptions.ts +3 -0
- package/src/api/Alignment.ts +4 -2
- package/src/api/GlobalOptions.ts +33 -5
- package/src/cli/CLI.ts +257 -205
- package/src/cli/CLIConfigFile.ts +7 -7
- package/src/cli/CLIOptions.ts +8 -0
- package/src/cli/CLIParser.ts +20 -17
- package/src/recognition/WhisperSTT.ts +38 -17
- package/src/utilities/FileDownloader.ts +5 -1
- package/src/utilities/Logger.ts +22 -8
- package/src/utilities/PackageManager.ts +4 -3
- package/src/utilities/Utilities.ts +9 -9
package/src/cli/CLIConfigFile.ts
CHANGED
|
@@ -56,10 +56,10 @@ export async function parseJSONConfigFile(path: string): Promise<ParsedConfigFil
|
|
|
56
56
|
|
|
57
57
|
const result: ParsedConfigFile = new Map()
|
|
58
58
|
|
|
59
|
-
for (const
|
|
60
|
-
const
|
|
59
|
+
for (const operation in parsedJSON) {
|
|
60
|
+
const operationObj = parsedJSON[operation]
|
|
61
61
|
|
|
62
|
-
const
|
|
62
|
+
const operationMap = new Map<string, string>()
|
|
63
63
|
|
|
64
64
|
function addFromObject(obj: any, pathPrefix: string): void {
|
|
65
65
|
for (const propertyName in obj) {
|
|
@@ -70,19 +70,19 @@ export async function parseJSONConfigFile(path: string): Promise<ParsedConfigFil
|
|
|
70
70
|
|
|
71
71
|
if (typeof propertyValue == 'object') {
|
|
72
72
|
if (Array.isArray(propertyValue)) {
|
|
73
|
-
|
|
73
|
+
operationMap.set(propertyPath, JSON.stringify(propertyValue))
|
|
74
74
|
} else {
|
|
75
75
|
addFromObject(propertyValue, propertyPath)
|
|
76
76
|
}
|
|
77
77
|
} else {
|
|
78
|
-
|
|
78
|
+
operationMap.set(propertyPath, propertyValue)
|
|
79
79
|
}
|
|
80
80
|
}
|
|
81
81
|
}
|
|
82
82
|
|
|
83
|
-
addFromObject(
|
|
83
|
+
addFromObject(operationObj, '')
|
|
84
84
|
|
|
85
|
-
result.set(
|
|
85
|
+
result.set(operation, operationMap)
|
|
86
86
|
}
|
|
87
87
|
|
|
88
88
|
return result
|
package/src/cli/CLIParser.ts
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
export function parseCLIArguments(
|
|
2
|
-
const parsedArgs:
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
options: new Map(),
|
|
1
|
+
export function parseCLIArguments(args: string[]): ParsedCLIArguments {
|
|
2
|
+
const parsedArgs: ParsedCLIArguments = {
|
|
3
|
+
operationArgs: [],
|
|
4
|
+
parsedArgumentsLookup: new Map(),
|
|
6
5
|
}
|
|
7
6
|
|
|
8
7
|
for (let i = 0; i < args.length; i++) {
|
|
@@ -16,26 +15,30 @@ export function parseCLIArguments(command: string, args: string[]): CLIArguments
|
|
|
16
15
|
|
|
17
16
|
const optionText = currentArg.substring(2)
|
|
18
17
|
|
|
19
|
-
const
|
|
20
|
-
if (
|
|
18
|
+
const indexOfEqualSign = optionText.indexOf('=')
|
|
19
|
+
if (indexOfEqualSign == 0) {
|
|
21
20
|
throw new Error('An option cannot have an empty name.')
|
|
22
|
-
} else if (
|
|
23
|
-
|
|
21
|
+
} else if (indexOfEqualSign == -1) {
|
|
22
|
+
if (optionText.startsWith('no-')) {
|
|
23
|
+
parsedArgs.parsedArgumentsLookup.set(optionText.substring(3), 'false')
|
|
24
|
+
} else {
|
|
25
|
+
parsedArgs.parsedArgumentsLookup.set(optionText, '')
|
|
26
|
+
}
|
|
24
27
|
} else {
|
|
25
|
-
const key = optionText.substring(0,
|
|
26
|
-
const value = optionText.substring(
|
|
27
|
-
|
|
28
|
+
const key = optionText.substring(0, indexOfEqualSign)
|
|
29
|
+
const value = optionText.substring(indexOfEqualSign + 1)
|
|
30
|
+
|
|
31
|
+
parsedArgs.parsedArgumentsLookup.set(key, value)
|
|
28
32
|
}
|
|
29
33
|
} else {
|
|
30
|
-
parsedArgs.
|
|
34
|
+
parsedArgs.operationArgs.push(currentArg)
|
|
31
35
|
}
|
|
32
36
|
}
|
|
33
37
|
|
|
34
38
|
return parsedArgs
|
|
35
39
|
}
|
|
36
40
|
|
|
37
|
-
export
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
options: Map<string, string>
|
|
41
|
+
export interface ParsedCLIArguments {
|
|
42
|
+
operationArgs: string[]
|
|
43
|
+
parsedArgumentsLookup: Map<string, string>
|
|
41
44
|
}
|
|
@@ -442,7 +442,7 @@ export class Whisper {
|
|
|
442
442
|
partTokensConfidence = partTokensConfidence.slice(initialTokens.length)
|
|
443
443
|
partCrossAttentionQKs = partCrossAttentionQKs.slice(initialTokens.length)
|
|
444
444
|
|
|
445
|
-
// Compute compression ratio for part
|
|
445
|
+
// Compute compression ratio for part (disabled for now)
|
|
446
446
|
if (false) {
|
|
447
447
|
const compressionRatioForPart = (await getDeflateCompressionMetricsForString(this.tokensToText(partTokens))).ratio
|
|
448
448
|
}
|
|
@@ -453,9 +453,11 @@ export class Whisper {
|
|
|
453
453
|
// Generate timeline from alignment path
|
|
454
454
|
const partTimeline = await this.getTokenTimelineFromAlignmentPath(alignmentPath, partTokens, segmentStartTime, segmentEndTime, partTokensConfidence)
|
|
455
455
|
|
|
456
|
+
// Add tokens to output
|
|
456
457
|
allDecodedTokens.push(...partTokens)
|
|
457
458
|
timeline.push(...partTimeline)
|
|
458
459
|
|
|
460
|
+
// Update previous text tokens
|
|
459
461
|
previousPartTextTokens = partTokens.filter(token => this.isTextToken(token))
|
|
460
462
|
|
|
461
463
|
audioOffset = audioEndOffset
|
|
@@ -479,22 +481,24 @@ export class Whisper {
|
|
|
479
481
|
|
|
480
482
|
whisperAlignmentOptions = extendDeep(defaultWhisperAlignmentOptions, whisperAlignmentOptions)
|
|
481
483
|
|
|
484
|
+
const targetLanguage = task === 'transcribe' ? sourceLanguage : 'en'
|
|
485
|
+
|
|
482
486
|
const shouldSplitToSentences = false
|
|
483
487
|
|
|
484
488
|
let simplifiedTranscript = ''
|
|
485
489
|
|
|
486
490
|
if (shouldSplitToSentences) {
|
|
487
|
-
const sentences = splitToSentences(transcript,
|
|
491
|
+
const sentences = splitToSentences(transcript, targetLanguage)
|
|
488
492
|
|
|
489
493
|
for (const sentence of sentences) {
|
|
490
|
-
let sentenceWords = await splitToWords(sentence,
|
|
494
|
+
let sentenceWords = await splitToWords(sentence, targetLanguage)
|
|
491
495
|
sentenceWords = sentenceWords.filter(word => isWord(word))
|
|
492
496
|
|
|
493
497
|
simplifiedTranscript += sentenceWords.join(' ')
|
|
494
498
|
simplifiedTranscript += ' '
|
|
495
499
|
}
|
|
496
500
|
} else {
|
|
497
|
-
let words = await splitToWords(transcript,
|
|
501
|
+
let words = await splitToWords(transcript, targetLanguage)
|
|
498
502
|
words = words.map(word => word.trim())
|
|
499
503
|
words = words.filter(word => isWord(word))
|
|
500
504
|
simplifiedTranscript = words.join(' ')
|
|
@@ -712,7 +716,8 @@ export class Whisper {
|
|
|
712
716
|
|
|
713
717
|
// Start decoding loop
|
|
714
718
|
for (let decodedTokenCount = 0; decodedTokenCount < options.maxTokensPerPart!; decodedTokenCount++) {
|
|
715
|
-
const isInitialState = decodedTokens.length
|
|
719
|
+
const isInitialState = decodedTokens.length === initialTokens.length
|
|
720
|
+
const atLeastOneTextTokenDecoded = decodedTokens.slice(initialTokens.length).some(token => this.isTextToken(token))
|
|
716
721
|
|
|
717
722
|
// If not in initial state, reshape KV Cache tensor to accomodate a new output token
|
|
718
723
|
if (!isInitialState) {
|
|
@@ -744,10 +749,10 @@ export class Whisper {
|
|
|
744
749
|
offset: offsetTensor
|
|
745
750
|
}
|
|
746
751
|
|
|
747
|
-
// Run decoder
|
|
752
|
+
// Run decoder model
|
|
748
753
|
const decoderOutputs = await this.textDecoder!.run(decoderInputs)
|
|
749
754
|
|
|
750
|
-
//
|
|
755
|
+
// Extract decoder model results
|
|
751
756
|
const logitsBuffer = decoderOutputs['logits'].data as Float32Array
|
|
752
757
|
kvCacheTensor = decoderOutputs['output_kv_cache'] as any
|
|
753
758
|
|
|
@@ -764,7 +769,7 @@ export class Whisper {
|
|
|
764
769
|
allTokenLogits[suppressedTokenIndex] = -Infinity
|
|
765
770
|
}
|
|
766
771
|
|
|
767
|
-
if (
|
|
772
|
+
if (!atLeastOneTextTokenDecoded) {
|
|
768
773
|
// If in initial state, suppress end-of-text token
|
|
769
774
|
allTokenLogits[endOfTextToken] = -Infinity
|
|
770
775
|
}
|
|
@@ -779,13 +784,19 @@ export class Whisper {
|
|
|
779
784
|
return false
|
|
780
785
|
}
|
|
781
786
|
|
|
787
|
+
if (isInitialState) {
|
|
788
|
+
addToken(timestampTokensStart, timestampTokenLogits, 1.0, crossAttentionQKsForToken)
|
|
789
|
+
|
|
790
|
+
return true
|
|
791
|
+
}
|
|
792
|
+
|
|
782
793
|
const previousTokenWasTimestamp = this.isTimestampToken(decodedTokens[decodedTokens.length - 1])
|
|
783
794
|
const secondPreviousTokenWasTimestamp = this.isTimestampToken(decodedTokens[decodedTokens.length - 2])
|
|
784
795
|
|
|
785
796
|
// If there are two successive timestamp tokens decoded, or the previous timestamp was the first token,
|
|
786
797
|
// don't decode a timestamp
|
|
787
798
|
if (previousTokenWasTimestamp &&
|
|
788
|
-
(decodedTokens.length === initialTokens.length + 1) || secondPreviousTokenWasTimestamp) {
|
|
799
|
+
((decodedTokens.length === initialTokens.length + 1) || secondPreviousTokenWasTimestamp)) {
|
|
789
800
|
return false
|
|
790
801
|
}
|
|
791
802
|
|
|
@@ -817,6 +828,7 @@ export class Whisper {
|
|
|
817
828
|
timestampTokenSeenCount += 1
|
|
818
829
|
|
|
819
830
|
if (previousTokenWasTimestamp) {
|
|
831
|
+
// If previously decoded token was a timestamp token, repeat it
|
|
820
832
|
const previousToken = decodedTokens[decodedTokens.length - 1]
|
|
821
833
|
const previousTokenTimestampLogits = decodedTokensTimestampLogits[decodedTokensTimestampLogits.length - 1]
|
|
822
834
|
const previousTokenConfidence = decodedTokensConfidence[decodedTokensConfidence.length - 1]
|
|
@@ -825,6 +837,7 @@ export class Whisper {
|
|
|
825
837
|
|
|
826
838
|
lastTimestampTokenIndex = decodedTokens.length
|
|
827
839
|
} else {
|
|
840
|
+
// Otherwise decode the highest probability timestamp
|
|
828
841
|
const timestampToken = timestampTokensStart + indexOfMaxTimestampLogProb
|
|
829
842
|
const confidence = probabilities[timestampToken]
|
|
830
843
|
|
|
@@ -834,6 +847,7 @@ export class Whisper {
|
|
|
834
847
|
return true
|
|
835
848
|
}
|
|
836
849
|
|
|
850
|
+
// Call the method to decode timestamp token if needed
|
|
837
851
|
const timestampTokenDecoded = decodeTimestampTokenIfNeeded()
|
|
838
852
|
|
|
839
853
|
if (timestampTokenDecoded) {
|
|
@@ -847,9 +861,9 @@ export class Whisper {
|
|
|
847
861
|
|
|
848
862
|
let shouldDecodeEndfOfTextToken = false
|
|
849
863
|
|
|
850
|
-
// If
|
|
851
|
-
// the second highest ranked token, then
|
|
852
|
-
if (
|
|
864
|
+
// If at least one text token was decoded, and the end-of-text token's probability is
|
|
865
|
+
// sufficiently higher than the second highest ranked token, then accept end-of-text
|
|
866
|
+
if (atLeastOneTextTokenDecoded) {
|
|
853
867
|
const endOfTextTokenLogit = nonTimestampTokenLogits[endOfTextToken]
|
|
854
868
|
|
|
855
869
|
const otherTokensLogits = nonTimestampTokenLogits.slice()
|
|
@@ -888,7 +902,7 @@ export class Whisper {
|
|
|
888
902
|
nonTimestampTokenLogits[suppressedTokenIndex] = -Infinity
|
|
889
903
|
}
|
|
890
904
|
|
|
891
|
-
// Suppress space token if at initial state
|
|
905
|
+
// Suppress the space token if at initial state
|
|
892
906
|
if (isInitialState) {
|
|
893
907
|
nonTimestampTokenLogits[spaceToken] = -Infinity
|
|
894
908
|
}
|
|
@@ -902,7 +916,7 @@ export class Whisper {
|
|
|
902
916
|
break
|
|
903
917
|
}
|
|
904
918
|
|
|
905
|
-
// Suppress end-of-text if it shouldn't be included in candidates
|
|
919
|
+
// Suppress end-of-text token if it shouldn't be included in candidates
|
|
906
920
|
if (!options.includeEndTokenInCandidates) {
|
|
907
921
|
nonTimestampTokenLogits[endOfTextToken] = -Infinity
|
|
908
922
|
}
|
|
@@ -924,8 +938,8 @@ export class Whisper {
|
|
|
924
938
|
if (options.suppressRepetition) {
|
|
925
939
|
// Using some hardcoded constants, for now
|
|
926
940
|
const tokenWindowSize = 30
|
|
927
|
-
const thresholdMatchLength =
|
|
928
|
-
const thresholdCycleRepetition =
|
|
941
|
+
const thresholdMatchLength = 4
|
|
942
|
+
const thresholdCycleRepetition = 3
|
|
929
943
|
|
|
930
944
|
const filteredCandidates: typeof topCandidates = []
|
|
931
945
|
|
|
@@ -1072,8 +1086,10 @@ export class Whisper {
|
|
|
1072
1086
|
throw new Error(`Audio part is longer than 30 seconds`)
|
|
1073
1087
|
}
|
|
1074
1088
|
|
|
1089
|
+
// Compute a mel spectogram
|
|
1075
1090
|
await logger.startAsync('Extract mel spectogram from audio part')
|
|
1076
1091
|
|
|
1092
|
+
// Pad audio samples to ensure that have a duration of 30 seconds
|
|
1077
1093
|
const paddedAudioSamples = new Float32Array(maxAudioSamples)
|
|
1078
1094
|
paddedAudioSamples.set(audioSamples, 0)
|
|
1079
1095
|
|
|
@@ -1084,6 +1100,8 @@ export class Whisper {
|
|
|
1084
1100
|
await logger.startAsync('Normalize mel spectogram')
|
|
1085
1101
|
|
|
1086
1102
|
const logMelSpectogram = melSpectogram.map(spectrum => spectrum.map(mel => Math.log10(Math.max(mel, 1e-10))))
|
|
1103
|
+
|
|
1104
|
+
// Find maximum log mel value in the spectrum
|
|
1087
1105
|
let maxLogMel = -Infinity
|
|
1088
1106
|
|
|
1089
1107
|
for (const spectrum of logMelSpectogram) {
|
|
@@ -1094,9 +1112,11 @@ export class Whisper {
|
|
|
1094
1112
|
}
|
|
1095
1113
|
}
|
|
1096
1114
|
|
|
1115
|
+
// Normalize log mel spectogram (based on Python reference code)
|
|
1097
1116
|
const normalizedLogMelSpectogram = logMelSpectogram.map(spectrum => spectrum.map(
|
|
1098
1117
|
logMel => (Math.max(logMel, maxLogMel - 8) + 4) / 4))
|
|
1099
1118
|
|
|
1119
|
+
// Flatten the normalized log mel spectogram
|
|
1100
1120
|
const flattenedNormalizedLogMelSpectogram = new Float32Array(maxAudioFrames * filterbankCount)
|
|
1101
1121
|
|
|
1102
1122
|
for (let i = 0; i < filterbankCount; i++) {
|
|
@@ -1105,6 +1125,7 @@ export class Whisper {
|
|
|
1105
1125
|
}
|
|
1106
1126
|
}
|
|
1107
1127
|
|
|
1128
|
+
// Run the encoder model
|
|
1108
1129
|
await logger.startAsync('Encode mel spectogram with Whisper encoder model')
|
|
1109
1130
|
|
|
1110
1131
|
const inputTensor = new Onnx.Tensor('float32', flattenedNormalizedLogMelSpectogram, [1, filterbankCount, maxAudioFrames])
|
|
@@ -1758,7 +1779,7 @@ export function normalizeWhisperModelName(modelName: WhisperModelName, languageC
|
|
|
1758
1779
|
modelName = modelName.slice(0, modelName.length - 3) as WhisperModelName
|
|
1759
1780
|
|
|
1760
1781
|
const logger = new Logger()
|
|
1761
|
-
logger.logTitledMessage(`Warning`, `The model '${originalModelName}' is English only and cannot be used to transcribe language '${languageCode}'.
|
|
1782
|
+
logger.logTitledMessage(`Warning`, `The model '${originalModelName}' is English only and cannot be used to transcribe language '${languageCode}'. Using '${modelName}' instead.`, chalk.yellowBright, 'warning')
|
|
1762
1783
|
}
|
|
1763
1784
|
|
|
1764
1785
|
return modelName
|
|
@@ -9,6 +9,7 @@ import path from 'node:path'
|
|
|
9
9
|
import { extractTarball } from './Compression.js'
|
|
10
10
|
import { createWriteStream, move, remove, readdir, ensureDir } from './FileSystem.js'
|
|
11
11
|
import chalk from 'chalk'
|
|
12
|
+
import { logLevelGreaterOrEqualTo } from '../api/GlobalOptions.js'
|
|
12
13
|
|
|
13
14
|
export async function downloadAndExtractTarball(options: GaxiosOptions, targetDir: string, baseTempPath: string, displayName = 'archive') {
|
|
14
15
|
const logger = new Logger()
|
|
@@ -18,7 +19,10 @@ export async function downloadAndExtractTarball(options: GaxiosOptions, targetDi
|
|
|
18
19
|
const tempDirPath = path.join(baseTempPath, `/${randomID}`)
|
|
19
20
|
await ensureDir(tempDirPath)
|
|
20
21
|
|
|
22
|
+
logger.end()
|
|
23
|
+
|
|
21
24
|
await downloadFile(options, tempTarballPath, `${chalk.cyanBright('Downloading')} ${chalk.greenBright(displayName)}`)
|
|
25
|
+
|
|
22
26
|
logger.end()
|
|
23
27
|
|
|
24
28
|
logger.start(`Extracting ${displayName}`)
|
|
@@ -40,7 +44,7 @@ export async function downloadAndExtractTarball(options: GaxiosOptions, targetDi
|
|
|
40
44
|
}
|
|
41
45
|
|
|
42
46
|
export async function downloadFile(options: GaxiosOptions, targetFilePath: string, prompt = 'Downloading') {
|
|
43
|
-
const write = writeToStderr
|
|
47
|
+
const write = logLevelGreaterOrEqualTo('info') ? writeToStderr : () => {}
|
|
44
48
|
|
|
45
49
|
const downloadPromise = new OpenPromise<void>()
|
|
46
50
|
|
package/src/utilities/Logger.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import chalk from 'chalk'
|
|
2
2
|
import { Timer } from './Timer.js'
|
|
3
3
|
import { logToStderr, writeToStderr, yieldToEventLoop } from './Utilities.js'
|
|
4
|
+
import { LogLevel, logLevelGreaterOrEqualTo, logLevelSmallerThan } from '../api/GlobalOptions.js'
|
|
4
5
|
|
|
5
6
|
let currentActiveLogger: Logger | null = null
|
|
6
7
|
|
|
@@ -23,7 +24,9 @@ export class Logger {
|
|
|
23
24
|
await yieldToEventLoop()
|
|
24
25
|
}
|
|
25
26
|
|
|
26
|
-
|
|
27
|
+
if (logLevelGreaterOrEqualTo('info')) {
|
|
28
|
+
writeToStderr(`${titleColor(title)}.. `)
|
|
29
|
+
}
|
|
27
30
|
|
|
28
31
|
this.setAsActiveLogger()
|
|
29
32
|
|
|
@@ -44,30 +47,41 @@ export class Logger {
|
|
|
44
47
|
if (this.active && currentActiveLogger == this) {
|
|
45
48
|
const elapsedTime = this.timer.elapsedTime
|
|
46
49
|
|
|
47
|
-
|
|
50
|
+
if (logLevelGreaterOrEqualTo('info')) {
|
|
51
|
+
writeToStderr(`${elapsedTime.toFixed(1)}ms\n`)
|
|
52
|
+
}
|
|
53
|
+
|
|
48
54
|
currentActiveLogger = null
|
|
49
55
|
}
|
|
50
56
|
|
|
51
57
|
this.active = false
|
|
52
58
|
}
|
|
53
59
|
|
|
54
|
-
logDuration(message: any, startTime: number, titleColor = chalk.cyanBright) {
|
|
60
|
+
logDuration(message: any, startTime: number, titleColor = chalk.cyanBright, logLevel: LogLevel = 'info') {
|
|
55
61
|
const duration = Timer.currentTime - startTime
|
|
56
62
|
|
|
57
|
-
this.log(`${titleColor(message)}: ${duration.toFixed(1)}ms
|
|
63
|
+
this.log(`${titleColor(message)}: ${duration.toFixed(1)}ms`, logLevel)
|
|
58
64
|
}
|
|
59
65
|
|
|
60
|
-
logTitledMessage(title: string, content:
|
|
61
|
-
this.log(`${titleColor(title)}: ${content}
|
|
66
|
+
logTitledMessage(title: string, content: any, titleColor = chalk.cyanBright, logLevel: LogLevel = 'info') {
|
|
67
|
+
this.log(`${titleColor(title)}: ${content}`, logLevel)
|
|
62
68
|
}
|
|
63
69
|
|
|
64
|
-
log(message: any) {
|
|
70
|
+
log(message: any, logLevel: LogLevel = 'info') {
|
|
71
|
+
if (logLevelSmallerThan(logLevel)) {
|
|
72
|
+
return
|
|
73
|
+
}
|
|
74
|
+
|
|
65
75
|
if (currentActiveLogger == this || currentActiveLogger == null) {
|
|
66
76
|
logToStderr(message)
|
|
67
77
|
}
|
|
68
78
|
}
|
|
69
79
|
|
|
70
|
-
write(message: any) {
|
|
80
|
+
write(message: any, logLevel: LogLevel = 'info') {
|
|
81
|
+
if (logLevelSmallerThan(logLevel)) {
|
|
82
|
+
return
|
|
83
|
+
}
|
|
84
|
+
|
|
71
85
|
if (currentActiveLogger == this || currentActiveLogger == null) {
|
|
72
86
|
writeToStderr(message)
|
|
73
87
|
}
|
|
@@ -4,6 +4,7 @@ import { getAppDataDir, ensureDir, existsSync, remove } from './FileSystem.js'
|
|
|
4
4
|
import { appName } from '../api/Common.js'
|
|
5
5
|
import { GaxiosOptions } from 'gaxios'
|
|
6
6
|
import { getAppTempDir } from './PathUtilities.js'
|
|
7
|
+
import { getGlobalOption } from '../api/GlobalOptions.js'
|
|
7
8
|
|
|
8
9
|
export async function loadPackage(packageName: string) {
|
|
9
10
|
packageName = resolveToVersionedPackageNameIfNeeded(packageName)
|
|
@@ -16,13 +17,15 @@ export async function loadPackage(packageName: string) {
|
|
|
16
17
|
return packagePath
|
|
17
18
|
}
|
|
18
19
|
|
|
20
|
+
const packageBaseURL = getGlobalOption('packageBaseURL')
|
|
21
|
+
|
|
19
22
|
const tempPath = getAppTempDir(appName)
|
|
20
23
|
|
|
21
24
|
const headers = {
|
|
22
25
|
}
|
|
23
26
|
|
|
24
27
|
const options: GaxiosOptions = {
|
|
25
|
-
url: `${
|
|
28
|
+
url: `${packageBaseURL}${packageName}.tar.gz`,
|
|
26
29
|
headers
|
|
27
30
|
}
|
|
28
31
|
|
|
@@ -76,8 +79,6 @@ export function resolveVersionTagForUnversionedPackageName(unversionedPackageNam
|
|
|
76
79
|
return packageVersionTagResolutionLookup[unversionedPackageName] || defaultVersionTag
|
|
77
80
|
}
|
|
78
81
|
|
|
79
|
-
const basePackageUrl = 'https://huggingface.co/echogarden/echogarden-packages/resolve/main/'
|
|
80
|
-
|
|
81
82
|
const defaultVersionTag = '20230718'
|
|
82
83
|
|
|
83
84
|
const packageVersionTagResolutionLookup: { [packageName: string]: string } = {
|
|
@@ -517,7 +517,7 @@ export function getTokenRepetitionScore(tokens: string[] | number[]) {
|
|
|
517
517
|
longestMatch = matchLength
|
|
518
518
|
}
|
|
519
519
|
|
|
520
|
-
const cycleCount = matchLength / i
|
|
520
|
+
const cycleCount = (matchLength / i) + 1
|
|
521
521
|
|
|
522
522
|
if (cycleCount > longestCycleRepetition) {
|
|
523
523
|
longestCycleRepetition = cycleCount
|
|
@@ -540,7 +540,7 @@ export async function resolveModuleScriptPath(moduleName: string) {
|
|
|
540
540
|
export async function runOperationWithRetries<R>(
|
|
541
541
|
operationFunc: () => Promise<R>,
|
|
542
542
|
logger: Logger,
|
|
543
|
-
|
|
543
|
+
operationName = 'Operation',
|
|
544
544
|
delayBetweenRetries = 2000,
|
|
545
545
|
maxRetries = 200) {
|
|
546
546
|
|
|
@@ -560,21 +560,21 @@ export async function runOperationWithRetries<R>(
|
|
|
560
560
|
|
|
561
561
|
logger.setAsActiveLogger()
|
|
562
562
|
|
|
563
|
-
logger.logTitledMessage(`Error`, e.message, chalk.redBright)
|
|
564
|
-
logger.log(
|
|
565
|
-
logger.logTitledMessage(`${
|
|
563
|
+
logger.logTitledMessage(`Error`, e.message, chalk.redBright, 'error')
|
|
564
|
+
logger.log('', 'error')
|
|
565
|
+
logger.logTitledMessage(`${operationName} failed`, `Trying again in ${delayBetweenRetries}ms..`, chalk.redBright, 'error')
|
|
566
566
|
|
|
567
567
|
await delay(delayBetweenRetries)
|
|
568
568
|
|
|
569
|
-
logger.log(
|
|
570
|
-
logger.logTitledMessage(`Starting retry attempt`, `${retryIndex} / ${maxRetries}`, chalk.yellowBright)
|
|
571
|
-
logger.log(
|
|
569
|
+
logger.log(``, 'warning')
|
|
570
|
+
logger.logTitledMessage(`Starting retry attempt`, `${retryIndex} / ${maxRetries}`, chalk.yellowBright, 'warning')
|
|
571
|
+
logger.log(``, 'warning')
|
|
572
572
|
|
|
573
573
|
logger.unsetAsActiveLogger()
|
|
574
574
|
}
|
|
575
575
|
}
|
|
576
576
|
|
|
577
|
-
throw new Error(`${
|
|
577
|
+
throw new Error(`${operationName} failed after ${maxRetries} retry attempts`)
|
|
578
578
|
}
|
|
579
579
|
|
|
580
580
|
export function writeToStdinInChunks(process: ChildProcessWithoutNullStreams, buffer: Buffer, chunkSize: number) {
|