@drakulavich/parakeet-cli 0.6.0 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +23 -10
  2. package/package.json +3 -2
  3. package/src/models.ts +67 -14
package/README.md CHANGED
@@ -1,10 +1,9 @@
1
1
  # 🦜 parakeet-cli
2
2
 
3
- [![npm version](https://img.shields.io/npm/v/@drakulavich/parakeet-cli)](https://www.npmjs.com/package/@drakulavich/parakeet-cli)
4
3
  [![CI](https://github.com/drakulavich/parakeet-cli/actions/workflows/ci.yml/badge.svg)](https://github.com/drakulavich/parakeet-cli/actions/workflows/ci.yml)
4
+ [![npm version](https://img.shields.io/npm/v/@drakulavich/parakeet-cli)](https://www.npmjs.com/package/@drakulavich/parakeet-cli)
5
5
  [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)
6
6
  [![Bun](https://img.shields.io/badge/runtime-Bun-f9f1e1?logo=bun)](https://bun.sh)
7
- [![Test Report](https://img.shields.io/badge/Test_Report-Allure-orange)](https://drakulavich.github.io/parakeet-cli/reports/allure)
8
7
 
9
8
  Fast local speech-to-text. 25 languages. ~18x faster than Whisper on Apple Silicon.
10
9
 
@@ -39,21 +38,31 @@ Stdout: transcript. Stderr: errors. Pipe-friendly.
39
38
  - [ffmpeg](https://ffmpeg.org) in PATH (ONNX backend only)
40
39
  - ~3GB disk (ONNX models)
41
40
 
42
- ## Supported Languages
41
+ ## Benchmark
43
42
 
44
- :bulgaria: Bulgarian, :croatia: Croatian, :czech_republic: Czech, :denmark: Danish, :netherlands: Dutch, :gb: English, :estonia: Estonian, :finland: Finnish, :fr: French, :de: German, :greece: Greek, :hungary: Hungarian, :it: Italian, :latvia: Latvian, :lithuania: Lithuanian, :malta: Maltese, :poland: Polish, :portugal: Portuguese, :romania: Romanian, :ru: Russian, :slovakia: Slovak, :slovenia: Slovenian, :es: Spanish, :sweden: Swedish, :ukraine: Ukrainian
43
+ > **~18x faster than Whisper** on Apple Silicon (CoreML)
45
44
 
46
- ## Benchmark
45
+ <details>
46
+ <summary>MacBook Pro M3 Pro — 10 Russian voice messages</summary>
47
47
 
48
- MacBook Pro M3 Pro — 10 Russian voice messages:
48
+ ```
49
+ faster-whisper (CPU): 35.3s ██████████████████████████████████████
50
+ Parakeet (CoreML): 1.9s ██
51
+ ```
49
52
 
50
- | | faster-whisper (CPU) | Parakeet (CoreML) |
51
- |---|---|---|
52
- | **Total** | 35.3s | 1.9s |
53
- | **Speed** | | **~18x faster** |
53
+ | | faster-whisper | Parakeet | Speedup |
54
+ |---|---|---|---|
55
+ | Apple Silicon (CoreML) | 35.3s | **1.9s** | **~18x** |
56
+ | Linux CI (ONNX) | 79.2s | **45.4s** | **~1.7x** |
57
+
58
+ </details>
54
59
 
55
60
  Full results with transcripts: [BENCHMARK.md](BENCHMARK.md)
56
61
 
62
+ ## Supported Languages
63
+
64
+ :bulgaria: Bulgarian, :croatia: Croatian, :czech_republic: Czech, :denmark: Danish, :netherlands: Dutch, :gb: English, :estonia: Estonian, :finland: Finnish, :fr: French, :de: German, :greece: Greek, :hungary: Hungarian, :it: Italian, :latvia: Latvian, :lithuania: Lithuanian, :malta: Maltese, :poland: Polish, :portugal: Portuguese, :romania: Romanian, :ru: Russian, :slovakia: Slovak, :slovenia: Slovenian, :es: Spanish, :sweden: Swedish, :ukraine: Ukrainian
65
+
57
66
  ## How It Works
58
67
 
59
68
  ```
@@ -83,6 +92,10 @@ Drop-in replacement for OpenClaw voice processing — no API keys, runs locally.
83
92
  }
84
93
  ```
85
94
 
95
+ ## Contributing
96
+
97
+ See [CONTRIBUTING.md](CONTRIBUTING.md).
98
+
86
99
  ## License
87
100
 
88
101
  MIT
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@drakulavich/parakeet-cli",
3
- "version": "0.6.0",
4
- "description": "Fast multilingual speech-to-text CLI powered by NVIDIA Parakeet ONNX models",
3
+ "version": "0.7.0",
4
+ "description": "Fast local speech-to-text CLI. CoreML on Apple Silicon, ONNX on CPU. 25 languages.",
5
5
  "type": "module",
6
6
  "bin": {
7
7
  "parakeet": "bin/parakeet.js"
@@ -49,6 +49,7 @@
49
49
  },
50
50
  "devDependencies": {
51
51
  "@types/bun": "latest",
52
+ "fast-xml-parser": "^5.5.10",
52
53
  "typescript": "^6.0.2"
53
54
  },
54
55
  "dependencies": {
package/src/models.ts CHANGED
@@ -53,7 +53,7 @@ export async function downloadModel(noCache = false, modelDir?: string): Promise
53
53
  const dir = modelDir ?? getModelDir();
54
54
 
55
55
  if (!noCache && isModelCached(dir)) {
56
- console.error("Model already downloaded.");
56
+ console.log("Model already downloaded.");
57
57
  return dir;
58
58
  }
59
59
 
@@ -67,21 +67,50 @@ export async function downloadModel(noCache = false, modelDir?: string): Promise
67
67
 
68
68
  console.error(`Downloading ${file}...`);
69
69
 
70
- const res = await fetch(url, { redirect: "follow" });
70
+ let res: Response;
71
+ try {
72
+ res = await fetch(url, { redirect: "follow" });
73
+ } catch (e) {
74
+ throw new Error(`failed to fetch ${file}: ${e instanceof Error ? e.message : e}`);
75
+ }
71
76
 
72
77
  if (!res.ok) {
73
- throw new Error(`failed to download model: ${url} (${res.status})`);
78
+ throw new Error(`failed to download ${file}: HTTP ${res.status}`);
79
+ }
80
+
81
+ if (!res.body) {
82
+ throw new Error(`empty response body for ${file}`);
83
+ }
84
+
85
+ const writer = Bun.file(dest).writer();
86
+ let bytes = 0;
87
+ try {
88
+ for await (const chunk of res.body) {
89
+ writer.write(chunk);
90
+ bytes += chunk.length;
91
+ }
92
+ } finally {
93
+ writer.end();
74
94
  }
75
95
 
76
- await Bun.write(dest, res);
96
+ if (bytes === 0) {
97
+ throw new Error(`downloaded 0 bytes for ${file}`);
98
+ }
77
99
  }
78
100
 
79
- console.error("Model downloaded successfully.");
101
+ console.log("Model downloaded successfully.");
80
102
  return dir;
81
103
  }
82
104
 
105
+ const COREML_BINARY_NAME = "parakeet-coreml-darwin-arm64";
106
+ const GITHUB_REPO = "drakulavich/parakeet-cli";
107
+
83
108
  export function getCoreMLDownloadURL(version: string): string {
84
- return `https://github.com/drakulavich/parakeet-cli/releases/download/v${version}/parakeet-coreml-darwin-arm64`;
109
+ return `https://github.com/${GITHUB_REPO}/releases/download/v${version}/${COREML_BINARY_NAME}`;
110
+ }
111
+
112
+ export function getCoreMLLatestDownloadURL(): string {
113
+ return `https://github.com/${GITHUB_REPO}/releases/latest/download/${COREML_BINARY_NAME}`;
85
114
  }
86
115
 
87
116
  export async function downloadCoreML(noCache = false): Promise<string> {
@@ -89,19 +118,32 @@ export async function downloadCoreML(noCache = false): Promise<string> {
89
118
  const binPath = getCoreMLBinPath();
90
119
 
91
120
  if (!noCache && existsSync(binPath)) {
92
- console.error("CoreML backend already installed.");
93
- return binPath;
121
+ // Binary exists — ensure models are also downloaded
122
+ const checkProc = Bun.spawnSync([binPath, "--download-only"], {
123
+ stdout: "pipe",
124
+ stderr: "pipe",
125
+ });
126
+ if (checkProc.exitCode === 0) {
127
+ console.log("CoreML backend already installed.");
128
+ return binPath;
129
+ }
130
+ // Models missing — continue to re-download
94
131
  }
95
132
 
96
- const pkg = await Bun.file(new URL("../package.json", import.meta.url)).json();
97
- const url = getCoreMLDownloadURL(pkg.version);
98
-
133
+ // Try latest release first, fall back to version-specific
134
+ const latestUrl = getCoreMLLatestDownloadURL();
99
135
  console.error("Downloading parakeet-coreml binary...");
100
136
 
101
- const res = await fetch(url, { redirect: "follow" });
137
+ let res = await fetch(latestUrl, { redirect: "follow" });
138
+
139
+ if (!res.ok) {
140
+ const pkg = await Bun.file(new URL("../package.json", import.meta.url)).json();
141
+ const versionUrl = getCoreMLDownloadURL(pkg.version);
142
+ res = await fetch(versionUrl, { redirect: "follow" });
143
+ }
102
144
 
103
145
  if (!res.ok) {
104
- throw new Error(`Failed to download CoreML binary: ${url} (${res.status})`);
146
+ throw new Error(`Failed to download CoreML binary (HTTP ${res.status}). No release found with ${COREML_BINARY_NAME}.`);
105
147
  }
106
148
 
107
149
  mkdirSync(dirname(binPath), { recursive: true });
@@ -110,6 +152,17 @@ export async function downloadCoreML(noCache = false): Promise<string> {
110
152
 
111
153
  chmodSync(binPath, 0o755);
112
154
 
113
- console.error("CoreML backend installed successfully.");
155
+ // Download CoreML model files (first transcription would be slow without this)
156
+ console.error("Downloading CoreML models...");
157
+ const downloadProc = Bun.spawnSync([binPath, "--download-only"], {
158
+ stdout: "inherit",
159
+ stderr: "inherit",
160
+ });
161
+
162
+ if (downloadProc.exitCode !== 0) {
163
+ throw new Error("Failed to download CoreML models");
164
+ }
165
+
166
+ console.log("CoreML backend installed successfully.");
114
167
  return binPath;
115
168
  }