diff --git a/src/cli.ts b/src/cli.ts index 32a2934..426e6fc 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -302,6 +302,7 @@ Commands: batch [--csv] [-o FILE] Bulk process many lines (alias: b) list [--authority X] [--source-script X] List available systems (alias: l) detect Find best-matching system (alias: d) + ml [-i FILE] [-o FILE] Neural layer: vocalize/diacritize (alias: m) Global options: --maps-dir Directory of .json IR files @@ -322,9 +323,49 @@ Examples: interscript-ts b bgnpcgn-ukr-Cyrl-Latn-2019 names.txt --csv > out.csv interscript-ts l --authority bgnpcgn --source-script Cyrl interscript-ts d "Антон" "Anton" --maps-dir ./ir + echo "السلام عليكم" | interscript-ts m ara-diac-layerdrop-1.0-int4 `) } +async function cmdMl(args: string[], _opts: GlobalOpts): Promise { + const { values, positionals } = parseArgs({ + args, + options: { + input: { type: "string", short: "i" }, + output: { type: "string", short: "o" }, + index: { type: "string" }, + }, + allowPositionals: true, + strict: true, + }) + const modelId = positionals[0] + if (!modelId) { + process.stderr.write("Error: modelId is required as the first positional arg\n") + return 1 + } + const text = values.input + ? readFileSync(resolve(process.cwd(), values.input), "utf8") + : readStdin() + try { + const { imf } = await import("./ml/index.js") + const resolved = values.index + ? await imf.resolve(modelId, values.index) + : await imf.resolve(modelId) + const model = await imf.IMFModel.fromZipBytes(resolved.bytes) + const input = /[\u0600-\u06FF]/.test(text) ? imf.normalizeArabicInput(text) : text + const out = await model.translate(input, Math.max(256, 4 * input.length)) + if (values.output) { + writeFileSync(resolve(process.cwd(), values.output), out + "\n") + } else { + process.stdout.write(out + "\n") + } + return 0 + } catch (e) { + process.stderr.write(`Error: ${(e as Error).message}\n`) + return 1 + } +} + async function main(): Promise { const argv = process.argv.slice(2) if (argv.length === 0 || argv[0] === "-h" || argv[0] === "--help") { @@ -340,6 +381,7 @@ async function main(): Promise { b: "batch", l: "list", d: "detect", + m: "ml", } const cmd = aliasMap[command] ?? command @@ -357,6 +399,9 @@ async function main(): Promise { case "detect": exit = await cmdDetect(rest, opts) break + case "ml": + exit = await cmdMl(rest, opts) + break default: process.stderr.write(`Unknown command: ${command}\n\n`) showHelp() diff --git a/test/cli.test.ts b/test/cli.test.ts index 094f519..4e16f30 100644 --- a/test/cli.test.ts +++ b/test/cli.test.ts @@ -108,3 +108,16 @@ describe("CLI", () => { expect(r.stderr).toContain("not found") }) }) + +describe("CLI ml", () => { + it("rejects unknown model ids with the resolver's message", () => { + const r = runCli(["ml", "nope-9.9"], "hello") + expect(r.status).toBe(1) + expect(r.stderr).toContain("unknown model id") + }) + + it("help mentions the ml command", () => { + const r = runCli(["--help"]) + expect(r.stdout).toContain("ml ") + }) +})