coolbeanz79's picture
Add hosted npm client (#4)
c017511
Raw
History Blame Contribute Delete
3.74 kB
#!/usr/bin/env node
import { readFile, writeFile } from "node:fs/promises";
import path from "node:path";
import process from "node:process";
const DEFAULT_SPACE = "build-small-hackathon/between-the-lines";
const MODEL_LABELS = {
base: "Base Mellum2 (richer)",
tuned: "Fine-tuned LoRA (concise)"
};
function usage() {
return `between-the-lines <file.py> [options]
Options:
--model base|tuned Comment model to use. Default: base
--output <file.py> Write annotated code to a specific file
--in-place Replace the input file after validation
--summary Print summary and validation status to stderr
--space <repo-id|url> Hosted Gradio Space. Default: ${DEFAULT_SPACE}
-h, --help Show this help
`;
}
function parseArgs(argv) {
const args = {
input: "",
model: "base",
output: "",
inPlace: false,
summary: false,
space: DEFAULT_SPACE
};
for (let index = 0; index < argv.length; index += 1) {
const arg = argv[index];
if (arg === "-h" || arg === "--help") {
args.help = true;
} else if (arg === "--model") {
args.model = argv[++index] ?? "";
} else if (arg === "--output" || arg === "-o") {
args.output = argv[++index] ?? "";
} else if (arg === "--in-place") {
args.inPlace = true;
} else if (arg === "--summary") {
args.summary = true;
} else if (arg === "--space") {
args.space = argv[++index] ?? "";
} else if (!args.input) {
args.input = arg;
} else {
throw new Error(`unexpected argument: ${arg}`);
}
}
if (args.help) {
return args;
}
if (!args.input) {
throw new Error("missing input file");
}
if (!Object.hasOwn(MODEL_LABELS, args.model)) {
throw new Error("--model must be 'base' or 'tuned'");
}
if (args.output && args.inPlace) {
throw new Error("use either --output or --in-place, not both");
}
return args;
}
function defaultOutputPath(inputPath) {
const parsed = path.parse(inputPath);
return path.join(parsed.dir, `${parsed.name}.annotated${parsed.ext || ".py"}`);
}
async function predict(client, source, modelLabel) {
try {
return await client.predict("/annotate", [source, modelLabel]);
} catch (arrayError) {
try {
return await client.predict("/annotate", {
source,
model_label: modelLabel
});
} catch {
throw arrayError;
}
}
}
async function main() {
let args;
try {
args = parseArgs(process.argv.slice(2));
} catch (error) {
console.error(`error: ${error.message}\n`);
console.error(usage());
return 2;
}
if (args.help) {
console.log(usage());
return 0;
}
const source = await readFile(args.input, "utf8");
const { Client } = await import("@gradio/client");
const client = await Client.connect(args.space);
const result = await predict(client, source, MODEL_LABELS[args.model]);
const [summary, annotated, status] = result.data;
if (args.summary) {
if (summary) {
console.error(summary);
}
console.error(status);
}
if (!String(status).startsWith("Validated:") && !String(status).startsWith("Parsed successfully.")) {
console.error(status);
return 1;
}
if (args.inPlace) {
await writeFile(args.input, annotated, "utf8");
} else if (args.output) {
await writeFile(args.output, annotated, "utf8");
} else {
const outputPath = defaultOutputPath(args.input);
await writeFile(outputPath, annotated, "utf8");
console.error(`Wrote ${outputPath}`);
}
return 0;
}
main()
.then((code) => {
process.exitCode = code;
})
.catch((error) => {
console.error(`error: ${error.message}`);
process.exitCode = 1;
});