@isaacthoman/pulpo 0.138.0 → 0.139.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 +13 -18
  2. package/dist/index.js +5 -30
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -72,23 +72,18 @@ microdollars: `inputPriceMicros`/`outputPriceMicros` per million respective toke
72
72
  audio minute. Token billing requires SSE usage support. Credentials remain in
73
73
  the referenced provider connection.
74
74
 
75
- Manage each voice's optional sample independently:
76
-
77
- ```bash
78
- pulpo speech-model preview upload read-aloud coral ./coral.wav
79
- pulpo speech-model preview download read-aloud coral --output ./coral-preview.wav
80
- pulpo --yes speech-model preview delete read-aloud coral
81
- pulpo --yes speech-model delete read-aloud
82
- ```
83
-
84
- Uploading again replaces that voice's clip. MP3 and WAV files must be nonempty,
85
- at most 5 MiB, and no longer than 30 seconds; the server validates the audio.
86
- Deleting a preview leaves its voice configured. Model updates retain clips for
87
- unchanged voice IDs and remove clips for deleted voices. Model deletion removes
88
- its clips. Listening to an uploaded sample does not generate speech or incur
89
- generation charges.
90
-
91
- Speech catalog and preview commands use `/api/management/v1/speech-models` and
75
+ Set each voice's `previewText` in the model document to customize generated
76
+ previews (up to 500 characters). Use `null` to reset to the shared default;
77
+ updates from older clients that omit this field preserve existing overrides.
78
+ Users generate previews from **Preview speech** at the bottom of speech settings,
79
+ using their selected controls and normal speech billing.
80
+
81
+ `speech-model test-voice <id> <voice> -o clip.mp3` defaults to the configured text;
82
+ `--text` supplies a one-off override. Admin tests incur provider costs without
83
+ charging a user balance. Saved-preview upload/download/delete commands and
84
+ `--save-preview` are retired.
85
+
86
+ Speech catalog and voice asset commands use `/api/management/v1/speech-models` and
92
87
  require a current administrator with `catalog:read` for reads/downloads or
93
88
  `catalog:write` for mutations. Older servers without the `speechModels`
94
89
  capability report that an upgrade is required. User preferences continue to use
@@ -169,7 +164,7 @@ totals. Active or queued model work must finish before deletion can proceed.
169
164
  Use `speech-model preset --adapter mistral` for the Voxtral preset. The
170
165
  `provider-voices`, `provider-sample`, `clone`, `watermark`, `test-voice`, and
171
166
  `cleanup` subcommands manage provider discovery, private reference uploads,
172
- repair, looping watermark settings, synthesized previews, and retryable cleanup.
167
+ repair, looping watermark settings, speech tests, and retryable cleanup.
173
168
  See [the speech administration guide](../../docs/speech.md) for complete commands,
174
169
  upload limits, billing behavior, and FFmpeg setup.
175
170
 
package/dist/index.js CHANGED
@@ -14615,11 +14615,13 @@ var imageGenerationInputSchema = external_exports.object({
14615
14615
  // ../../packages/contracts/dist/speech.js
14616
14616
  var SPEECH_REQUEST_MAX_INPUT_LENGTH = 16384;
14617
14617
  var SPEECH_MAX_INSTRUCTIONS_LENGTH = 4096;
14618
+ var SPEECH_MAX_PREVIEW_TEXT_LENGTH = 500;
14618
14619
  var SPEECH_ASSET_MAX_BYTES = 10 * 1024 * 1024;
14619
14620
  var speechWatermarkSchema = external_exports.object({ enabled: external_exports.boolean().default(false), volume: external_exports.number().min(0.01).max(1).default(0.15) });
14620
14621
  var speechVoiceSchema = external_exports.object({
14621
14622
  id: external_exports.string().trim().min(1).max(200),
14622
14623
  label: external_exports.string().trim().min(1).max(120),
14624
+ previewText: external_exports.string().trim().max(SPEECH_MAX_PREVIEW_TEXT_LENGTH).transform((text) => text || null).nullable().optional(),
14623
14625
  kind: external_exports.enum(["provider", "cloned"]).optional(),
14624
14626
  watermark: speechWatermarkSchema.optional()
14625
14627
  });
@@ -20800,7 +20802,7 @@ async function runModelTest(input) {
20800
20802
  }
20801
20803
 
20802
20804
  // src/index.ts
20803
- var CLI_VERSION = true ? "0.138.0" : "0.1.0";
20805
+ var CLI_VERSION = true ? "0.139.0" : "0.1.0";
20804
20806
  var CLI_BUNDLED = true;
20805
20807
  var commandIo = /* @__PURE__ */ new WeakMap();
20806
20808
  var commandClientFactory = /* @__PURE__ */ new WeakMap();
@@ -21275,33 +21277,6 @@ function createProgram(io = processIo, dependencies = {}) {
21275
21277
  speechModel.command("preset <id>").requiredOption("--provider <id>", "existing provider connection ID").option("--adapter <adapter>", "openai or mistral", "openai").description("Print an editable OpenAI or Voxtral model document without creating it").action((id, options2) => {
21276
21278
  writeOutput(io, speechModelSchema.parse({ ...external_exports.enum(["openai", "mistral"]).parse(options2.adapter) === "mistral" ? VOXTRAL_SPEECH_PRESET : OPENAI_SPEECH_PRESET, id, providerConnectionId: options2.provider }), true);
21277
21279
  });
21278
- const speechPreview = speechModel.command("preview").description("Manage each voice\u2019s optional MP3/WAV preview");
21279
- const speechPreviewPath = (id, voice) => `/api/management/v1/speech-models/${encodeURIComponent(id)}/voices/${encodeURIComponent(voice)}/preview`;
21280
- speechPreview.command("upload <id> <voice> <path>").description("Upload or replace a voice preview (up to 30 seconds and 5 MiB)").action(async (id, voice, path, _options, command) => {
21281
- const filename = basename(path);
21282
- const extension = filename.split(".").at(-1)?.toLowerCase();
21283
- const contentType = extension === "mp3" ? "audio/mpeg" : extension === "wav" ? "audio/wav" : null;
21284
- if (!contentType) throw new Error("Speech previews must be MP3 or WAV files");
21285
- const file2 = await stat(path);
21286
- if (!file2.isFile() || !file2.size || file2.size > 5 * 1024 * 1024) throw new Error("Speech previews must be nonempty files of at most 5 MiB");
21287
- const bytes = new Uint8Array(await readFile2(path));
21288
- if (!bytes.length || bytes.length > 5 * 1024 * 1024) throw new Error("Speech previews must be nonempty and at most 5 MiB");
21289
- const { client } = await clientFor(command);
21290
- emit(io, command, await client.upload(speechPreviewPath(id, voice), { bytes, filename, contentType }));
21291
- });
21292
- speechPreview.command("download <id> <voice>").requiredOption("-o, --output <path>", "destination audio file").action(async (id, voice, options2, command) => {
21293
- const { client } = await clientFor(command);
21294
- const result = await client.download(speechPreviewPath(id, voice));
21295
- await writeFile2(options2.output, result.bytes, { mode: 384 });
21296
- emit(io, command, { id, voice, output: options2.output });
21297
- });
21298
- speechPreview.command("delete <id> <voice>").description("Remove a voice preview without deleting its voice").action(async (id, voice, _options, command) => {
21299
- const options2 = globalOptions(command);
21300
- await confirmExact(io, `${id}/${voice}`, Boolean(options2.yes), Boolean(options2.json));
21301
- const { client } = await clientFor(command);
21302
- await client.request(speechPreviewPath(id, voice), { method: "DELETE" });
21303
- emit(io, command, { id, voice, deleted: true });
21304
- });
21305
21280
  const voicePath = (id, voice) => `/api/management/v1/speech-models/${encodeURIComponent(id)}/voices/${encodeURIComponent(voice)}`;
21306
21281
  speechModel.command("provider-voices <id>").action(async (id, _options, command) => {
21307
21282
  const { client } = await clientFor(command);
@@ -21348,9 +21323,9 @@ function createProgram(io = processIo, dependencies = {}) {
21348
21323
  });
21349
21324
  }
21350
21325
  }
21351
- speechModel.command("test-voice <id> <voice>").requiredOption("-o, --output <path>").option("--text <text>", "text to synthesize", "Hello. This is a sample of my voice.").option("--save-preview", "save generated speech as the user preview").action(async (id, voice, options2, command) => {
21326
+ speechModel.command("test-voice <id> <voice>").requiredOption("-o, --output <path>").option("--text <text>", "text to synthesize (defaults to the voice preview text)").action(async (id, voice, options2, command) => {
21352
21327
  const { client } = await clientFor(command);
21353
- const result = await client.download(`${voicePath(id, voice)}/test`, 12e4, { method: "POST", body: { input: options2.text, savePreview: Boolean(options2.savePreview) } });
21328
+ const result = await client.download(`${voicePath(id, voice)}/test`, 12e4, { method: "POST", body: { ...options2.text !== void 0 ? { input: options2.text } : {} } });
21354
21329
  await writeFile2(options2.output, result.bytes, { mode: 384 });
21355
21330
  emit(io, command, { output: options2.output });
21356
21331
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@isaacthoman/pulpo",
3
- "version": "0.138.0",
3
+ "version": "0.139.0",
4
4
  "description": "Operator-first command-line client for Pulpo",
5
5
  "type": "module",
6
6
  "bin": {