@leemour/cli-messaging 0.39.0 → 0.41.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/README.md +1 -1
- package/dist/cli/index.d.ts +3 -2
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +2 -1
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/messenger/after.d.ts +5 -0
- package/dist/cli/messenger/after.d.ts.map +1 -0
- package/dist/cli/messenger/after.js +10 -0
- package/dist/cli/messenger/after.js.map +1 -0
- package/dist/cli/messenger/backfill-jobs.d.ts +1 -5
- package/dist/cli/messenger/backfill-jobs.d.ts.map +1 -1
- package/dist/cli/messenger/backfill-jobs.js +4 -27
- package/dist/cli/messenger/backfill-jobs.js.map +1 -1
- package/dist/cli/messenger/chats-command.d.ts +32 -0
- package/dist/cli/messenger/chats-command.d.ts.map +1 -1
- package/dist/cli/messenger/chats-command.js +90 -4
- package/dist/cli/messenger/chats-command.js.map +1 -1
- package/dist/cli/messenger/context.d.ts +2 -0
- package/dist/cli/messenger/context.d.ts.map +1 -1
- package/dist/cli/messenger/context.js.map +1 -1
- package/dist/cli/messenger/messages-command.d.ts +1 -1
- package/dist/cli/messenger/messages-command.d.ts.map +1 -1
- package/dist/cli/messenger/messages-command.js +16 -6
- package/dist/cli/messenger/messages-command.js.map +1 -1
- package/dist/cli/messenger/models-command.d.ts +9 -0
- package/dist/cli/messenger/models-command.d.ts.map +1 -0
- package/dist/cli/messenger/models-command.js +69 -0
- package/dist/cli/messenger/models-command.js.map +1 -0
- package/dist/cli/messenger/port.d.ts +16 -1
- package/dist/cli/messenger/port.d.ts.map +1 -1
- package/dist/cli/messenger/port.js.map +1 -1
- package/dist/cli/messenger/processes.d.ts +9 -0
- package/dist/cli/messenger/processes.d.ts.map +1 -0
- package/dist/cli/messenger/processes.js +37 -0
- package/dist/cli/messenger/processes.js.map +1 -0
- package/dist/cli/messenger/serve-command.d.ts +4 -5
- package/dist/cli/messenger/serve-command.d.ts.map +1 -1
- package/dist/cli/messenger/serve-command.js +14 -22
- package/dist/cli/messenger/serve-command.js.map +1 -1
- package/dist/cli/messenger/server-command.d.ts +27 -0
- package/dist/cli/messenger/server-command.d.ts.map +1 -0
- package/dist/cli/messenger/server-command.js +453 -0
- package/dist/cli/messenger/server-command.js.map +1 -0
- package/dist/cli/messenger/transcribe-command.d.ts +1 -1
- package/dist/cli/messenger/transcribe-command.d.ts.map +1 -1
- package/dist/cli/messenger/transcribe-command.js +16 -7
- package/dist/cli/messenger/transcribe-command.js.map +1 -1
- package/dist/cli/messenger/watch-command.d.ts +2 -1
- package/dist/cli/messenger/watch-command.d.ts.map +1 -1
- package/dist/cli/messenger/watch-command.js +2 -2
- package/dist/cli/messenger/watch-command.js.map +1 -1
- package/dist/cli/settings.d.ts +4 -0
- package/dist/cli/settings.d.ts.map +1 -1
- package/dist/cli/settings.js +2 -0
- package/dist/cli/settings.js.map +1 -1
- package/dist/domain/models.d.ts +26 -0
- package/dist/domain/models.d.ts.map +1 -1
- package/dist/mcp/server.js +1 -1
- package/dist/mcp/server.js.map +1 -1
- package/dist/mcp/tool.d.ts +4 -0
- package/dist/mcp/tool.d.ts.map +1 -1
- package/dist/mcp/tool.js.map +1 -1
- package/dist/mcp/tools/chats.d.ts.map +1 -1
- package/dist/mcp/tools/chats.js +30 -5
- package/dist/mcp/tools/chats.js.map +1 -1
- package/dist/mcp/tools/messages-transcribe.d.ts.map +1 -1
- package/dist/mcp/tools/messages-transcribe.js +15 -8
- package/dist/mcp/tools/messages-transcribe.js.map +1 -1
- package/dist/mcp/tools/messages.d.ts.map +1 -1
- package/dist/mcp/tools/messages.js +15 -5
- package/dist/mcp/tools/messages.js.map +1 -1
- package/dist/speech/install.d.ts +23 -0
- package/dist/speech/install.d.ts.map +1 -0
- package/dist/speech/install.js +106 -0
- package/dist/speech/install.js.map +1 -0
- package/dist/speech/models.d.ts +38 -0
- package/dist/speech/models.d.ts.map +1 -0
- package/dist/speech/models.js +130 -0
- package/dist/speech/models.js.map +1 -0
- package/dist/speech/recognize.d.ts +24 -0
- package/dist/speech/recognize.d.ts.map +1 -0
- package/dist/speech/recognize.js +106 -0
- package/dist/speech/recognize.js.map +1 -0
- package/dist/speech/transcribe.d.ts +36 -0
- package/dist/speech/transcribe.d.ts.map +1 -0
- package/dist/speech/transcribe.js +80 -0
- package/dist/speech/transcribe.js.map +1 -0
- package/package.json +3 -1
- package/dist/cli/messenger/service-command.d.ts +0 -21
- package/dist/cli/messenger/service-command.d.ts.map +0 -1
- package/dist/cli/messenger/service-command.js +0 -305
- package/dist/cli/messenger/service-command.js.map +0 -1
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The speech models `messages transcribe --local` can use, each **pinned to one commit** and checked by
|
|
3
|
+
* sha256: a file that does not match is refused, so nobody else's model runs in a program that
|
|
4
|
+
* holds the owner's session.
|
|
5
|
+
*
|
|
6
|
+
* Measured 2026-09-24 through the WebAssembly build of sherpa-onnx, one thread, a Ryzen AI 9 HX 470
|
|
7
|
+
* laptop, 5 minutes of Ogg Opus cut by VAD, Node 24 and Bun 1.3.14 alike (`FIND-131`, `FIND-133`):
|
|
8
|
+
* `gigaam-v3` ×7 real time, `gigaam-v3-ctc` ×8, `parakeet-v3` ×5 and 1.3 GB of memory. Whisper small
|
|
9
|
+
* ran at ×1.1 and is not offered.
|
|
10
|
+
*/
|
|
11
|
+
export interface ModelFile {
|
|
12
|
+
name: string;
|
|
13
|
+
url: string;
|
|
14
|
+
sha256: string;
|
|
15
|
+
bytes: number;
|
|
16
|
+
}
|
|
17
|
+
export interface SpeechModel {
|
|
18
|
+
id: string;
|
|
19
|
+
title: string;
|
|
20
|
+
languages: string;
|
|
21
|
+
files: ModelFile[];
|
|
22
|
+
/** The part of sherpa-onnx's `modelConfig` that names the files, given where they are. */
|
|
23
|
+
config: (path: (name: string) => string) => Record<string, unknown>;
|
|
24
|
+
featureDim: number;
|
|
25
|
+
}
|
|
26
|
+
export declare const MODELS: SpeechModel[];
|
|
27
|
+
/** Cuts speech at pauses. Every model needs it: five minutes in one piece crash the WebAssembly build. */
|
|
28
|
+
export declare const VAD: ModelFile;
|
|
29
|
+
/**
|
|
30
|
+
* Parakeet first: it hears 25 languages, and a messenger's CLI whose people mostly speak one language
|
|
31
|
+
* puts that language's model first instead (`Messenger.speechModels`) — max-cli puts GigaAM first.
|
|
32
|
+
*/
|
|
33
|
+
export declare const DEFAULT_ORDER: string[];
|
|
34
|
+
/** Every model, in the CLI's order; those it did not name follow in the default order. */
|
|
35
|
+
export declare const orderedModels: (order?: readonly string[]) => SpeechModel[];
|
|
36
|
+
export declare const findModel: (id: string) => SpeechModel | undefined;
|
|
37
|
+
export declare const speechModel: (id: string) => SpeechModel;
|
|
38
|
+
//# sourceMappingURL=models.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"models.d.ts","sourceRoot":"","sources":["../../src/speech/models.ts"],"names":[],"mappings":"AAEA;;;;;;;;;GASG;AACH,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,CAAA;IACZ,GAAG,EAAE,MAAM,CAAA;IACX,MAAM,EAAE,MAAM,CAAA;IACd,KAAK,EAAE,MAAM,CAAA;CACd;AAED,MAAM,WAAW,WAAW;IAC1B,EAAE,EAAE,MAAM,CAAA;IACV,KAAK,EAAE,MAAM,CAAA;IACb,SAAS,EAAE,MAAM,CAAA;IACjB,KAAK,EAAE,SAAS,EAAE,CAAA;IAClB,0FAA0F;IAC1F,MAAM,EAAE,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,KAAK,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IACnE,UAAU,EAAE,MAAM,CAAA;CACnB;AAeD,eAAO,MAAM,MAAM,EAAE,WAAW,EAqG/B,CAAA;AAED,0GAA0G;AAC1G,eAAO,MAAM,GAAG,EAAE,SAKjB,CAAA;AAED;;;GAGG;AACH,eAAO,MAAM,aAAa,UAAgD,CAAA;AAE1E,0FAA0F;AAC1F,eAAO,MAAM,aAAa,WAAW,SAAS,MAAM,EAAE,KAAmB,WAAW,EACvB,CAAA;AAE7D,eAAO,MAAM,SAAS,OAAQ,MAAM,KAAG,WAAW,GAAG,SAAoD,CAAA;AAEzG,eAAO,MAAM,WAAW,OAAQ,MAAM,KAAG,WASxC,CAAA"}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import { CliError } from "@leemour/cli-core";
|
|
2
|
+
const huggingFace = (repository, commit) => (name) => `https://huggingface.co/csukuangfj/${repository}/resolve/${commit}/${name}`;
|
|
3
|
+
const gigaam = huggingFace("sherpa-onnx-nemo-transducer-punct-giga-am-v3-russian-2025-12-16", "a6039be7cee829a9044a69ac0ebaf1c191217c97");
|
|
4
|
+
const gigaamCtc = huggingFace("sherpa-onnx-nemo-ctc-punct-giga-am-v3-russian-2025-12-16", "4fb5407ff028a69fec516cdf4c10fac9ddea7c16");
|
|
5
|
+
const parakeet = huggingFace("sherpa-onnx-nemo-parakeet-tdt-0.6b-v3-int8", "2bda32ec70b097a55adaa07d9a7173915b43cc78");
|
|
6
|
+
export const MODELS = [
|
|
7
|
+
{
|
|
8
|
+
id: "gigaam-v3",
|
|
9
|
+
title: "GigaAM v3, with punctuation",
|
|
10
|
+
languages: "Russian — the best of the three for Russian",
|
|
11
|
+
featureDim: 64,
|
|
12
|
+
files: [
|
|
13
|
+
{
|
|
14
|
+
name: "encoder.int8.onnx",
|
|
15
|
+
url: gigaam("encoder.int8.onnx"),
|
|
16
|
+
sha256: "369f35a71bf288d3b8e0391fabd8dba5f2314088d440bca474056b7b4b6e66bf",
|
|
17
|
+
bytes: 224_570_820,
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
name: "decoder.onnx",
|
|
21
|
+
url: gigaam("decoder.onnx"),
|
|
22
|
+
sha256: "38fc7475443ea2a26f63211ca350f73ac50fff824ab7a3876ee2bd610c53bbc4",
|
|
23
|
+
bytes: 4_600_132,
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
name: "joiner.onnx",
|
|
27
|
+
url: gigaam("joiner.onnx"),
|
|
28
|
+
sha256: "602ff7017a93311aad34df1437c8d7f49911353c13d6eae7a6ee7b041339465c",
|
|
29
|
+
bytes: 2_712_896,
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
name: "tokens.txt",
|
|
33
|
+
url: gigaam("tokens.txt"),
|
|
34
|
+
sha256: "39abae20e692998290c574e606f11a9edef2902a1995463fcff63d1490cf22b7",
|
|
35
|
+
bytes: 13_354,
|
|
36
|
+
},
|
|
37
|
+
],
|
|
38
|
+
config: (path) => ({
|
|
39
|
+
transducer: { encoder: path("encoder.int8.onnx"), decoder: path("decoder.onnx"), joiner: path("joiner.onnx") },
|
|
40
|
+
tokens: path("tokens.txt"),
|
|
41
|
+
modelType: "nemo_transducer",
|
|
42
|
+
}),
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
id: "gigaam-v3-ctc",
|
|
46
|
+
title: "GigaAM v3 CTC, with punctuation — a little faster",
|
|
47
|
+
languages: "Russian — a little faster, rougher with capital letters",
|
|
48
|
+
featureDim: 64,
|
|
49
|
+
files: [
|
|
50
|
+
{
|
|
51
|
+
name: "model.int8.onnx",
|
|
52
|
+
url: gigaamCtc("model.int8.onnx"),
|
|
53
|
+
sha256: "d5fea8df94263c285e54b21e5774b707c707192d3bdbeffd7b1eb07fb6743b35",
|
|
54
|
+
bytes: 224_893_661,
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
name: "tokens.txt",
|
|
58
|
+
url: gigaamCtc("tokens.txt"),
|
|
59
|
+
sha256: "142de7570b3de5b3035ce111a89c228e80e6085273731d944093ddf24fa539cd",
|
|
60
|
+
bytes: 2_007,
|
|
61
|
+
},
|
|
62
|
+
],
|
|
63
|
+
config: (path) => ({ nemoCtc: { model: path("model.int8.onnx") }, tokens: path("tokens.txt") }),
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
id: "parakeet-v3",
|
|
67
|
+
title: "NVIDIA Parakeet TDT 0.6B v3",
|
|
68
|
+
/** The model card's list: huggingface.co/nvidia/parakeet-tdt-0.6b-v3, read 2026-09-24. */
|
|
69
|
+
languages: "25: bg cs da de el en es et fi fr hr hu it lt lv mt nl pl pt ro ru sk sl sv uk",
|
|
70
|
+
featureDim: 80,
|
|
71
|
+
files: [
|
|
72
|
+
{
|
|
73
|
+
name: "encoder.int8.onnx",
|
|
74
|
+
url: parakeet("encoder.int8.onnx"),
|
|
75
|
+
sha256: "acfc2b4456377e15d04f0243af540b7fe7c992f8d898d751cf134c3a55fd2247",
|
|
76
|
+
bytes: 652_184_281,
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
name: "decoder.int8.onnx",
|
|
80
|
+
url: parakeet("decoder.int8.onnx"),
|
|
81
|
+
sha256: "179e50c43d1a9de79c8a24149a2f9bac6eb5981823f2a2ed88d655b24248db4e",
|
|
82
|
+
bytes: 11_845_275,
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
name: "joiner.int8.onnx",
|
|
86
|
+
url: parakeet("joiner.int8.onnx"),
|
|
87
|
+
sha256: "3164c13fc2821009440d20fcb5fdc78bff28b4db2f8d0f0b329101719c0948b3",
|
|
88
|
+
bytes: 6_355_277,
|
|
89
|
+
},
|
|
90
|
+
{
|
|
91
|
+
name: "tokens.txt",
|
|
92
|
+
url: parakeet("tokens.txt"),
|
|
93
|
+
sha256: "d58544679ea4bc6ac563d1f545eb7d474bd6cfa467f0a6e2c1dc1c7d37e3c35d",
|
|
94
|
+
bytes: 93_939,
|
|
95
|
+
},
|
|
96
|
+
],
|
|
97
|
+
config: (path) => ({
|
|
98
|
+
transducer: {
|
|
99
|
+
encoder: path("encoder.int8.onnx"),
|
|
100
|
+
decoder: path("decoder.int8.onnx"),
|
|
101
|
+
joiner: path("joiner.int8.onnx"),
|
|
102
|
+
},
|
|
103
|
+
tokens: path("tokens.txt"),
|
|
104
|
+
modelType: "nemo_transducer",
|
|
105
|
+
}),
|
|
106
|
+
},
|
|
107
|
+
];
|
|
108
|
+
/** Cuts speech at pauses. Every model needs it: five minutes in one piece crash the WebAssembly build. */
|
|
109
|
+
export const VAD = {
|
|
110
|
+
name: "silero_vad.onnx",
|
|
111
|
+
url: "https://github.com/k2-fsa/sherpa-onnx/releases/download/asr-models/silero_vad.onnx",
|
|
112
|
+
sha256: "9e2449e1087496d8d4caba907f23e0bd3f78d91fa552479bb9c23ac09cbb1fd6",
|
|
113
|
+
bytes: 643_854,
|
|
114
|
+
};
|
|
115
|
+
/**
|
|
116
|
+
* Parakeet first: it hears 25 languages, and a messenger's CLI whose people mostly speak one language
|
|
117
|
+
* puts that language's model first instead (`Messenger.speechModels`) — max-cli puts GigaAM first.
|
|
118
|
+
*/
|
|
119
|
+
export const DEFAULT_ORDER = ["parakeet-v3", "gigaam-v3", "gigaam-v3-ctc"];
|
|
120
|
+
/** Every model, in the CLI's order; those it did not name follow in the default order. */
|
|
121
|
+
export const orderedModels = (order = DEFAULT_ORDER) => [...new Set([...order, ...DEFAULT_ORDER])].map(speechModel);
|
|
122
|
+
export const findModel = (id) => MODELS.find((model) => model.id === id);
|
|
123
|
+
export const speechModel = (id) => {
|
|
124
|
+
const model = findModel(id);
|
|
125
|
+
if (!model) {
|
|
126
|
+
throw new CliError("validation_error", `no speech model "${id}" — one of ${MODELS.map((known) => known.id).join(", ")}`);
|
|
127
|
+
}
|
|
128
|
+
return model;
|
|
129
|
+
};
|
|
130
|
+
//# sourceMappingURL=models.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"models.js","sourceRoot":"","sources":["../../src/speech/models.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { SpeechModel } from "./models.js";
|
|
2
|
+
/** What transcription needs from a speech engine — small enough for a test to stand in for it. */
|
|
3
|
+
export interface Recognizer {
|
|
4
|
+
recognize(pcm: Float32Array): string;
|
|
5
|
+
free(): void;
|
|
6
|
+
}
|
|
7
|
+
export declare const SAMPLE_RATE = 16000;
|
|
8
|
+
/**
|
|
9
|
+
* Voice messages are Ogg Opus at 48 kHz (measured on a MAX one), and the models want 16 kHz. Averaging each three samples is ours rather than the engine's resampler, which announces
|
|
10
|
+
* itself on stderr on every call.
|
|
11
|
+
*/
|
|
12
|
+
export declare const toModelRate: (samples: Float32Array, rate: number) => Float32Array;
|
|
13
|
+
export declare const decodeOgg: (bytes: Uint8Array) => Promise<{
|
|
14
|
+
samples: Float32Array;
|
|
15
|
+
rate: number;
|
|
16
|
+
}>;
|
|
17
|
+
/**
|
|
18
|
+
* The model, and a voice detector that cuts the audio at pauses into pieces of at most 20 seconds.
|
|
19
|
+
* The cutting is not optional: five minutes in one piece crash the WebAssembly build (measured).
|
|
20
|
+
*
|
|
21
|
+
* `sherpa-onnx` is loaded here, on first use, so no other command pays for its 15 MB of WebAssembly.
|
|
22
|
+
*/
|
|
23
|
+
export declare const openRecognizer: (model: SpeechModel, path: (name: string) => string, vadModel: string) => Recognizer;
|
|
24
|
+
//# sourceMappingURL=recognize.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"recognize.d.ts","sourceRoot":"","sources":["../../src/speech/recognize.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAA;AAE9C,kGAAkG;AAClG,MAAM,WAAW,UAAU;IACzB,SAAS,CAAC,GAAG,EAAE,YAAY,GAAG,MAAM,CAAA;IACpC,IAAI,IAAI,IAAI,CAAA;CACb;AAED,eAAO,MAAM,WAAW,QAAS,CAAA;AAQjC;;;GAGG;AACH,eAAO,MAAM,WAAW,YAAa,YAAY,QAAQ,MAAM,KAAG,YAYjE,CAAA;AAED,eAAO,MAAM,SAAS,UAAiB,UAAU,KAAG,OAAO,CAAC;IAAE,OAAO,EAAE,YAAY,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAYlG,CAAA;AA0BD;;;;;GAKG;AACH,eAAO,MAAM,cAAc,UAAW,WAAW,QAAQ,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,YAAY,MAAM,KAAG,UA0DrG,CAAA"}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { createRequire } from "node:module";
|
|
2
|
+
export const SAMPLE_RATE = 16_000;
|
|
3
|
+
/**
|
|
4
|
+
* Half a second of silence ahead of the speech. Without it the voice detector clipped a recording
|
|
5
|
+
* that starts talking at once: "Ничьих" came back as "чьих" (measured 2026-09-24).
|
|
6
|
+
*/
|
|
7
|
+
const LEAD = SAMPLE_RATE / 2;
|
|
8
|
+
/**
|
|
9
|
+
* Voice messages are Ogg Opus at 48 kHz (measured on a MAX one), and the models want 16 kHz. Averaging each three samples is ours rather than the engine's resampler, which announces
|
|
10
|
+
* itself on stderr on every call.
|
|
11
|
+
*/
|
|
12
|
+
export const toModelRate = (samples, rate) => {
|
|
13
|
+
const step = rate / SAMPLE_RATE;
|
|
14
|
+
const length = Math.floor(samples.length / step);
|
|
15
|
+
const pcm = new Float32Array(LEAD + length);
|
|
16
|
+
for (let i = 0; i < length; i++) {
|
|
17
|
+
const from = Math.floor(i * step);
|
|
18
|
+
const to = Math.max(from + 1, Math.floor((i + 1) * step));
|
|
19
|
+
let sum = 0;
|
|
20
|
+
for (let j = from; j < to; j++)
|
|
21
|
+
sum += samples[j] ?? 0;
|
|
22
|
+
pcm[LEAD + i] = sum / (to - from);
|
|
23
|
+
}
|
|
24
|
+
return pcm;
|
|
25
|
+
};
|
|
26
|
+
export const decodeOgg = async (bytes) => {
|
|
27
|
+
const { OggOpusDecoder } = await import("ogg-opus-decoder");
|
|
28
|
+
const decoder = new OggOpusDecoder();
|
|
29
|
+
await decoder.ready;
|
|
30
|
+
try {
|
|
31
|
+
const { channelData, sampleRate, samplesDecoded } = await decoder.decodeFile(bytes);
|
|
32
|
+
const samples = channelData[0];
|
|
33
|
+
if (!samples || samplesDecoded === 0)
|
|
34
|
+
throw new Error("no Ogg Opus audio in it");
|
|
35
|
+
return { samples, rate: sampleRate };
|
|
36
|
+
}
|
|
37
|
+
finally {
|
|
38
|
+
decoder.free();
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
const WINDOW = 512;
|
|
42
|
+
/**
|
|
43
|
+
* The model, and a voice detector that cuts the audio at pauses into pieces of at most 20 seconds.
|
|
44
|
+
* The cutting is not optional: five minutes in one piece crash the WebAssembly build (measured).
|
|
45
|
+
*
|
|
46
|
+
* `sherpa-onnx` is loaded here, on first use, so no other command pays for its 15 MB of WebAssembly.
|
|
47
|
+
*/
|
|
48
|
+
export const openRecognizer = (model, path, vadModel) => {
|
|
49
|
+
const sherpa = createRequire(import.meta.url)("sherpa-onnx");
|
|
50
|
+
const engine = sherpa.createOfflineRecognizer({
|
|
51
|
+
featConfig: { sampleRate: SAMPLE_RATE, featureDim: model.featureDim },
|
|
52
|
+
modelConfig: { ...model.config(path), numThreads: 1, provider: "cpu", debug: 0 },
|
|
53
|
+
decodingMethod: "greedy_search",
|
|
54
|
+
});
|
|
55
|
+
const vad = sherpa.createVad({
|
|
56
|
+
sileroVad: {
|
|
57
|
+
model: vadModel,
|
|
58
|
+
threshold: 0.5,
|
|
59
|
+
minSilenceDuration: 0.5,
|
|
60
|
+
minSpeechDuration: 0.25,
|
|
61
|
+
maxSpeechDuration: 20,
|
|
62
|
+
windowSize: WINDOW,
|
|
63
|
+
},
|
|
64
|
+
sampleRate: SAMPLE_RATE,
|
|
65
|
+
numThreads: 1,
|
|
66
|
+
provider: "cpu",
|
|
67
|
+
debug: 0,
|
|
68
|
+
bufferSizeInSeconds: 30,
|
|
69
|
+
});
|
|
70
|
+
const piece = (samples) => {
|
|
71
|
+
const stream = engine.createStream();
|
|
72
|
+
try {
|
|
73
|
+
stream.acceptWaveform(SAMPLE_RATE, samples);
|
|
74
|
+
engine.decode(stream);
|
|
75
|
+
return engine.getResult(stream).text.trim();
|
|
76
|
+
}
|
|
77
|
+
finally {
|
|
78
|
+
stream.free();
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
return {
|
|
82
|
+
recognize: (pcm) => {
|
|
83
|
+
// One recognizer can hear several recordings in a row; nothing of the last may leak into this one.
|
|
84
|
+
vad.reset();
|
|
85
|
+
const parts = [];
|
|
86
|
+
const drain = () => {
|
|
87
|
+
while (!vad.isEmpty()) {
|
|
88
|
+
parts.push(piece(vad.front().samples));
|
|
89
|
+
vad.pop();
|
|
90
|
+
}
|
|
91
|
+
};
|
|
92
|
+
for (let i = 0; i + WINDOW <= pcm.length; i += WINDOW) {
|
|
93
|
+
vad.acceptWaveform(pcm.subarray(i, i + WINDOW));
|
|
94
|
+
drain();
|
|
95
|
+
}
|
|
96
|
+
vad.flush();
|
|
97
|
+
drain();
|
|
98
|
+
return parts.filter(Boolean).join(" ");
|
|
99
|
+
},
|
|
100
|
+
free: () => {
|
|
101
|
+
vad.free();
|
|
102
|
+
engine.free();
|
|
103
|
+
},
|
|
104
|
+
};
|
|
105
|
+
};
|
|
106
|
+
//# sourceMappingURL=recognize.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"recognize.js","sourceRoot":"","sources":["../../src/speech/recognize.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { CliError } from "@leemour/cli-core";
|
|
2
|
+
import type { Messenger } from "../cli/messenger/context.js";
|
|
3
|
+
import { type MessengerAdapter, type Transcript } from "../cli/messenger/port.js";
|
|
4
|
+
import { type Settings } from "../cli/settings.js";
|
|
5
|
+
import { type SpeechModel } from "./models.js";
|
|
6
|
+
/** `messenger`: its own recognition (Telegram Premium). `local`: a model on this machine. `auto`: the first, then the second. */
|
|
7
|
+
export declare const TRANSCRIBE_WITH: readonly ["auto", "messenger", "local"];
|
|
8
|
+
export type TranscribeWith = (typeof TRANSCRIBE_WITH)[number];
|
|
9
|
+
export interface Heard extends Transcript {
|
|
10
|
+
messageId: string;
|
|
11
|
+
/** The messenger's provider, or `local`. */
|
|
12
|
+
via: string;
|
|
13
|
+
/** The local model, when it was one. */
|
|
14
|
+
model?: string;
|
|
15
|
+
}
|
|
16
|
+
export interface Choice {
|
|
17
|
+
with: TranscribeWith;
|
|
18
|
+
model: SpeechModel;
|
|
19
|
+
/** Where the models are, `modelsDirectory` for the command's environment. */
|
|
20
|
+
directory: string;
|
|
21
|
+
}
|
|
22
|
+
/** The flag, then the profile's `transcribeWith` and `speechModel`, then `auto` and the CLI's first model. */
|
|
23
|
+
export declare const choose: (messenger: Messenger, settings: Pick<Settings, "configured" | "shared">, flags: {
|
|
24
|
+
local?: boolean;
|
|
25
|
+
model?: string;
|
|
26
|
+
}, env?: NodeJS.ProcessEnv) => Choice;
|
|
27
|
+
/** Never a download: a model is hundreds of MB, and whether to fetch one is the owner's call. */
|
|
28
|
+
export declare const notDownloaded: (messenger: Messenger, model: SpeechModel) => CliError;
|
|
29
|
+
/**
|
|
30
|
+
* Over an open connection: the messenger's own transcript, or the recording's bytes for a local model.
|
|
31
|
+
* The model runs after the connection closes — a minute of speech should not hold a socket open.
|
|
32
|
+
*/
|
|
33
|
+
export declare const hearOnline: (messenger: Messenger, connection: MessengerAdapter, chat: string, messageId: string, choice: Choice) => Promise<Heard | Uint8Array>;
|
|
34
|
+
/** The local model, on the bytes `hearOnline` fetched. */
|
|
35
|
+
export declare const hearLocally: (bytes: Uint8Array, messageId: string, { model, directory }: Choice) => Promise<Heard>;
|
|
36
|
+
//# sourceMappingURL=transcribe.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transcribe.d.ts","sourceRoot":"","sources":["../../src/speech/transcribe.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAc,MAAM,mBAAmB,CAAA;AACxD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,6BAA6B,CAAA;AAC5D,OAAO,EAAc,KAAK,gBAAgB,EAAmB,KAAK,UAAU,EAAE,MAAM,0BAA0B,CAAA;AAC9G,OAAO,EAAY,KAAK,QAAQ,EAAE,MAAM,oBAAoB,CAAA;AAE5D,OAAO,EAAiB,KAAK,WAAW,EAAe,MAAM,aAAa,CAAA;AAE1E,iIAAiI;AACjI,eAAO,MAAM,eAAe,YAAI,MAAM,EAAE,WAAW,EAAE,OAAO,CAAU,CAAA;AACtE,MAAM,MAAM,cAAc,GAAG,CAAC,OAAO,eAAe,CAAC,CAAC,MAAM,CAAC,CAAA;AAE7D,MAAM,WAAW,KAAM,SAAQ,UAAU;IACvC,SAAS,EAAE,MAAM,CAAA;IACjB,4CAA4C;IAC5C,GAAG,EAAE,MAAM,CAAA;IACX,wCAAwC;IACxC,KAAK,CAAC,EAAE,MAAM,CAAA;CACf;AAKD,MAAM,WAAW,MAAM;IACrB,IAAI,EAAE,cAAc,CAAA;IACpB,KAAK,EAAE,WAAW,CAAA;IAClB,6EAA6E;IAC7E,SAAS,EAAE,MAAM,CAAA;CAClB;AAED,8GAA8G;AAC9G,eAAO,MAAM,MAAM,cACN,SAAS,YACV,IAAI,CAAC,QAAQ,EAAE,YAAY,GAAG,QAAQ,CAAC,SAC1C;IAAE,KAAK,CAAC,EAAE,OAAO,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,QACrC,MAAM,CAAC,UAAU,KACrB,MAUF,CAAA;AAED,iGAAiG;AACjG,eAAO,MAAM,aAAa,cAAe,SAAS,SAAS,WAAW,KAAG,QAKtE,CAAA;AAEH;;;GAGG;AACH,eAAO,MAAM,UAAU,cACV,SAAS,cACR,gBAAgB,QACtB,MAAM,aACD,MAAM,UACT,MAAM,KACb,OAAO,CAAC,KAAK,GAAG,UAAU,CAkB5B,CAAA;AAED,0DAA0D;AAC1D,eAAO,MAAM,WAAW,UACf,UAAU,aACN,MAAM,wBACK,MAAM,KAC3B,OAAO,CAAC,KAAK,CAef,CAAA"}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { CliError, isCliError } from "@leemour/cli-core";
|
|
2
|
+
import { capability } from "../cli/messenger/port.js";
|
|
3
|
+
import { fromFile } from "../cli/settings.js";
|
|
4
|
+
import { installedBytes, isInstalled, megabytes, modelPath, modelsDirectory, vadPath } from "./install.js";
|
|
5
|
+
import { orderedModels, speechModel } from "./models.js";
|
|
6
|
+
/** `messenger`: its own recognition (Telegram Premium). `local`: a model on this machine. `auto`: the first, then the second. */
|
|
7
|
+
export const TRANSCRIBE_WITH = ["auto", "messenger", "local"];
|
|
8
|
+
/** A voice message is read into memory whole; minutes of Opus are a few megabytes. */
|
|
9
|
+
const LARGEST_VOICE = 32 * 1024 * 1024;
|
|
10
|
+
/** The flag, then the profile's `transcribeWith` and `speechModel`, then `auto` and the CLI's first model. */
|
|
11
|
+
export const choose = (messenger, settings, flags, env = process.env) => {
|
|
12
|
+
const model = flags.model ?? fromFile(settings, "speechModel", undefined).value;
|
|
13
|
+
return {
|
|
14
|
+
directory: modelsDirectory(env),
|
|
15
|
+
with: flags.local || flags.model !== undefined
|
|
16
|
+
? "local"
|
|
17
|
+
: fromFile(settings, "transcribeWith", "auto").value,
|
|
18
|
+
model: model === undefined ? orderedModels(messenger.speechModels)[0] : speechModel(model),
|
|
19
|
+
};
|
|
20
|
+
};
|
|
21
|
+
/** Never a download: a model is hundreds of MB, and whether to fetch one is the owner's call. */
|
|
22
|
+
export const notDownloaded = (messenger, model) => new CliError("not_found", `the speech model ${model.id} is not on this machine — the owner downloads it once with ` +
|
|
23
|
+
`\`${messenger.app.command} models audio download ${model.id}\` (${megabytes(installedBytes(model))})`);
|
|
24
|
+
/**
|
|
25
|
+
* Over an open connection: the messenger's own transcript, or the recording's bytes for a local model.
|
|
26
|
+
* The model runs after the connection closes — a minute of speech should not hold a socket open.
|
|
27
|
+
*/
|
|
28
|
+
export const hearOnline = async (messenger, connection, chat, messageId, choice) => {
|
|
29
|
+
if (choice.with !== "local") {
|
|
30
|
+
const canAsk = typeof connection.transcribe === "function";
|
|
31
|
+
if (choice.with === "messenger" || canAsk) {
|
|
32
|
+
try {
|
|
33
|
+
const heard = await capability(connection, "transcribe", "transcribe voice messages")(chat, messageId);
|
|
34
|
+
return { messageId, ...heard, via: messenger.provider };
|
|
35
|
+
}
|
|
36
|
+
catch (error) {
|
|
37
|
+
// `auto` falls back only when the messenger refuses this account; any other failure is real.
|
|
38
|
+
if (choice.with === "messenger" || !(isCliError(error) && error.code === "permission_error"))
|
|
39
|
+
throw error;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
if (!isInstalled(choice.model, choice.directory))
|
|
44
|
+
throw notDownloaded(messenger, choice.model);
|
|
45
|
+
const { files } = await capability(connection, "download", "download attachments")(chat, messageId);
|
|
46
|
+
const voice = files.find((file) => file.kind === "voice" || file.mime === "audio/ogg");
|
|
47
|
+
if (!voice)
|
|
48
|
+
throw new CliError("validation_error", "that message is not a voice message");
|
|
49
|
+
return readAll(voice);
|
|
50
|
+
};
|
|
51
|
+
/** The local model, on the bytes `hearOnline` fetched. */
|
|
52
|
+
export const hearLocally = async (bytes, messageId, { model, directory }) => {
|
|
53
|
+
const { decodeOgg, openRecognizer, toModelRate } = await import("./recognize.js");
|
|
54
|
+
const { samples, rate } = await decodeOgg(bytes);
|
|
55
|
+
const recognizer = openRecognizer(model, modelPath(directory, model), vadPath(directory));
|
|
56
|
+
try {
|
|
57
|
+
return {
|
|
58
|
+
messageId,
|
|
59
|
+
text: recognizer.recognize(toModelRate(samples, rate)),
|
|
60
|
+
pending: false,
|
|
61
|
+
via: "local",
|
|
62
|
+
model: model.id,
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
finally {
|
|
66
|
+
recognizer.free();
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
const readAll = async (file) => {
|
|
70
|
+
const chunks = [];
|
|
71
|
+
let total = 0;
|
|
72
|
+
for await (const chunk of file.bytes()) {
|
|
73
|
+
total += chunk.length;
|
|
74
|
+
if (total > LARGEST_VOICE)
|
|
75
|
+
throw new CliError("validation_error", "the voice message is larger than 32 MB");
|
|
76
|
+
chunks.push(chunk);
|
|
77
|
+
}
|
|
78
|
+
return new Uint8Array(Buffer.concat(chunks));
|
|
79
|
+
};
|
|
80
|
+
//# sourceMappingURL=transcribe.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transcribe.js","sourceRoot":"","sources":["../../src/speech/transcribe.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@leemour/cli-messaging",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.41.0",
|
|
4
4
|
"description": "The messenger-neutral half of a messaging CLI: domain model, message locators, rendering, name resolution, the SQLite seam, the command skeleton and shared read commands, the send guard, run records and the message store",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Viacheslav Ptsarev",
|
|
@@ -56,7 +56,9 @@
|
|
|
56
56
|
"@leemour/cli-core": "0.8.0",
|
|
57
57
|
"@modelcontextprotocol/server": "2.1.0",
|
|
58
58
|
"@valibot/to-json-schema": "1.8.0",
|
|
59
|
+
"ogg-opus-decoder": "1.7.5",
|
|
59
60
|
"qrcode-generator": "2.0.4",
|
|
61
|
+
"sherpa-onnx": "1.13.8",
|
|
60
62
|
"string-width": "8.3.0",
|
|
61
63
|
"valibot": "1.5.0",
|
|
62
64
|
"wrap-ansi": "10.0.2"
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
import { Command } from "commander";
|
|
2
|
-
import { type Messenger } from "./context.js";
|
|
3
|
-
export interface Ran {
|
|
4
|
-
code: number;
|
|
5
|
-
stdout: string;
|
|
6
|
-
stderr: string;
|
|
7
|
-
}
|
|
8
|
-
/** What the service commands ask of the machine. Tests hand one in; nothing else does. */
|
|
9
|
-
export interface ServiceSystem {
|
|
10
|
-
platform: NodeJS.Platform;
|
|
11
|
-
run: (argv: string[], env: NodeJS.ProcessEnv) => Promise<Ran>;
|
|
12
|
-
/** The command line that starts this CLI: the node binary and the script, both absolute. */
|
|
13
|
-
entry: string[];
|
|
14
|
-
uid: number;
|
|
15
|
-
}
|
|
16
|
-
/**
|
|
17
|
-
* `serve` as a user service: a systemd user unit on Linux, a launchd agent on macOS, one per
|
|
18
|
-
* profile. **`install` only writes the file** — nothing starts or enables it until asked (NEED-9).
|
|
19
|
-
*/
|
|
20
|
-
export declare const serviceCommand: (messenger: Messenger) => Command;
|
|
21
|
-
//# sourceMappingURL=service-command.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"service-command.d.ts","sourceRoot":"","sources":["../../../src/cli/messenger/service-command.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AAGnC,OAAO,EAAE,KAAK,SAAS,EAAoB,MAAM,cAAc,CAAA;AAG/D,MAAM,WAAW,GAAG;IAClB,IAAI,EAAE,MAAM,CAAA;IACZ,MAAM,EAAE,MAAM,CAAA;IACd,MAAM,EAAE,MAAM,CAAA;CACf;AAED,0FAA0F;AAC1F,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,MAAM,CAAC,QAAQ,CAAA;IACzB,GAAG,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,GAAG,EAAE,MAAM,CAAC,UAAU,KAAK,OAAO,CAAC,GAAG,CAAC,CAAA;IAC7D,4FAA4F;IAC5F,KAAK,EAAE,MAAM,EAAE,CAAA;IACf,GAAG,EAAE,MAAM,CAAA;CACZ;AA+MD;;;GAGG;AACH,eAAO,MAAM,cAAc,cAAe,SAAS,KAAG,OAmIrD,CAAA"}
|