@drakulavich/parakeet-cli 0.2.0 → 0.4.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/package.json CHANGED
@@ -1,11 +1,15 @@
1
1
  {
2
2
  "name": "@drakulavich/parakeet-cli",
3
- "version": "0.2.0",
3
+ "version": "0.4.0",
4
4
  "description": "Fast multilingual speech-to-text CLI powered by NVIDIA Parakeet ONNX models",
5
5
  "type": "module",
6
6
  "bin": {
7
7
  "parakeet": "bin/parakeet.js"
8
8
  },
9
+ "exports": {
10
+ ".": "./src/cli.ts",
11
+ "./core": "./src/lib.ts"
12
+ },
9
13
  "files": [
10
14
  "bin/",
11
15
  "src/",
@@ -0,0 +1,12 @@
1
+ import { describe, expect, it } from "bun:test";
2
+ import { transcribe } from "../lib";
3
+
4
+ describe("lib API", () => {
5
+ it("exports transcribe function", () => {
6
+ expect(typeof transcribe).toBe("function");
7
+ });
8
+
9
+ it("rejects missing file", async () => {
10
+ await expect(transcribe("/nonexistent/audio.wav")).rejects.toThrow("File not found");
11
+ });
12
+ });
package/src/cli.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env bun
2
2
 
3
- import { existsSync } from "fs";
4
- import { transcribe } from "./transcribe";
3
+ import { transcribe } from "./lib";
4
+ import { downloadModel } from "./models";
5
5
 
6
6
  async function main(): Promise<void> {
7
7
  const args = process.argv.slice(2);
@@ -12,25 +12,34 @@ async function main(): Promise<void> {
12
12
  process.exit(0);
13
13
  }
14
14
 
15
- const noCache = args.includes("--no-cache");
16
- const file = args.filter((a) => !a.startsWith("--"))[0];
17
-
18
- if (!file) {
19
- console.error("Usage: parakeet [--no-cache] <audio_file>");
20
- process.exit(1);
15
+ const positional = args.filter((a) => !a.startsWith("--"));
16
+
17
+ if (positional[0] === "install") {
18
+ const noCache = args.includes("--no-cache");
19
+ try {
20
+ await downloadModel(noCache);
21
+ } catch (err: unknown) {
22
+ const message = err instanceof Error ? err.message : String(err);
23
+ console.error(`Error: ${message}`);
24
+ process.exit(1);
25
+ }
26
+ process.exit(0);
21
27
  }
22
28
 
23
- if (!existsSync(file)) {
24
- console.error(`Error: file not found: ${file}`);
29
+ const file = positional[0];
30
+
31
+ if (!file) {
32
+ console.error("Usage: parakeet [--version] <audio_file>");
33
+ console.error(" parakeet install [--no-cache]");
25
34
  process.exit(1);
26
35
  }
27
36
 
28
37
  try {
29
- const text = await transcribe(file, { noCache });
38
+ const text = await transcribe(file);
30
39
  if (text) process.stdout.write(text + "\n");
31
40
  } catch (err: unknown) {
32
41
  const message = err instanceof Error ? err.message : String(err);
33
- console.error(`Error: ${message}`);
42
+ console.error(message);
34
43
  process.exit(1);
35
44
  }
36
45
  }
package/src/lib.ts ADDED
@@ -0,0 +1,17 @@
1
+ import { existsSync } from "fs";
2
+ import { transcribe as internalTranscribe, type TranscribeOptions } from "./transcribe";
3
+ import { downloadModel } from "./models";
4
+
5
+ export type { TranscribeOptions };
6
+ export { downloadModel };
7
+
8
+ export async function transcribe(
9
+ audioPath: string,
10
+ options: TranscribeOptions = {},
11
+ ): Promise<string> {
12
+ if (!existsSync(audioPath)) {
13
+ throw new Error(`File not found: ${audioPath}`);
14
+ }
15
+
16
+ return internalTranscribe(audioPath, options);
17
+ }
package/src/models.ts CHANGED
@@ -16,15 +16,36 @@ export function getModelDir(): string {
16
16
  return join(homedir(), ".cache", "parakeet", "v3");
17
17
  }
18
18
 
19
- export function isModelCached(): boolean {
20
- const dir = getModelDir();
21
- return MODEL_FILES.every((f) => existsSync(join(dir, f)));
19
+ export function isModelCached(dir?: string): boolean {
20
+ const d = dir ?? getModelDir();
21
+ return MODEL_FILES.every((f) => existsSync(join(d, f)));
22
22
  }
23
23
 
24
- export async function ensureModel(noCache = false): Promise<string> {
25
- const dir = getModelDir();
24
+ export function requireModel(modelDir?: string): string {
25
+ const dir = modelDir ?? getModelDir();
26
26
 
27
- if (!noCache && isModelCached()) {
27
+ if (!isModelCached(dir)) {
28
+ const lines = [
29
+ `Error: Model not found at ${dir}`,
30
+ "",
31
+ "╔══════════════════════════════════════════════════════════╗",
32
+ "║ Looks like Parakeet model is not downloaded yet. ║",
33
+ "║ Please run the following command to download the model: ║",
34
+ "║ ║",
35
+ "║ npx @drakulavich/parakeet-cli install ║",
36
+ "╚══════════════════════════════════════════════════════════╝",
37
+ ];
38
+ throw new Error(lines.join("\n"));
39
+ }
40
+
41
+ return dir;
42
+ }
43
+
44
+ export async function downloadModel(noCache = false, modelDir?: string): Promise<string> {
45
+ const dir = modelDir ?? getModelDir();
46
+
47
+ if (!noCache && isModelCached(dir)) {
48
+ console.error("Model already downloaded.");
28
49
  return dir;
29
50
  }
30
51
 
@@ -47,5 +68,6 @@ export async function ensureModel(noCache = false): Promise<string> {
47
68
  await Bun.write(dest, res);
48
69
  }
49
70
 
71
+ console.error("Model downloaded successfully.");
50
72
  return dir;
51
73
  }
package/src/transcribe.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { ensureModel } from "./models";
1
+ import { requireModel } from "./models";
2
2
  import { convertToFloat32PCM } from "./audio";
3
3
  import { initPreprocessor, preprocess } from "./preprocess";
4
4
  import { initEncoder, encode } from "./encoder";
@@ -25,7 +25,8 @@ const DECODER_LAYERS = 2;
25
25
  const DECODER_HIDDEN = 640;
26
26
 
27
27
  export interface TranscribeOptions {
28
- noCache?: boolean;
28
+ beamWidth?: number;
29
+ modelDir?: string;
29
30
  }
30
31
 
31
32
  // Minimum 0.1s of audio at 16kHz to produce meaningful output
@@ -38,8 +39,8 @@ export async function transcribe(audioPath: string, opts: TranscribeOptions = {}
38
39
  return "";
39
40
  }
40
41
 
41
- const noCache = opts.noCache ?? false;
42
- const modelDir = await ensureModel(noCache);
42
+ const beamWidth = opts.beamWidth ?? 4;
43
+ const modelDir = requireModel(opts.modelDir);
43
44
  const tokenizer = await Tokenizer.fromFile(join(modelDir, "vocab.txt"));
44
45
 
45
46
  await initPreprocessor(modelDir);
@@ -63,6 +64,6 @@ export async function transcribe(audioPath: string, opts: TranscribeOptions = {}
63
64
  DECODER_HIDDEN,
64
65
  );
65
66
 
66
- const tokens = await beamDecode(session, encodedLength, transposed, D);
67
+ const tokens = await beamDecode(session, encodedLength, transposed, D, beamWidth);
67
68
  return tokenizer.detokenize(tokens);
68
69
  }