everypixel-cli 0.3.0__tar.gz → 0.4.1__tar.gz
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.
- {everypixel_cli-0.3.0 → everypixel_cli-0.4.1}/CHANGELOG.md +10 -0
- {everypixel_cli-0.3.0 → everypixel_cli-0.4.1}/PKG-INFO +32 -4
- {everypixel_cli-0.3.0 → everypixel_cli-0.4.1}/README.md +31 -3
- everypixel_cli-0.4.1/src/everypixel_cli/__init__.py +1 -0
- {everypixel_cli-0.3.0 → everypixel_cli-0.4.1}/src/everypixel_cli/application/serialization.py +2 -0
- {everypixel_cli-0.3.0 → everypixel_cli-0.4.1}/src/everypixel_cli/application/services.py +184 -63
- {everypixel_cli-0.3.0 → everypixel_cli-0.4.1}/src/everypixel_cli/cli.py +86 -18
- {everypixel_cli-0.3.0 → everypixel_cli-0.4.1}/src/everypixel_cli/mcp_server.py +65 -19
- {everypixel_cli-0.3.0 → everypixel_cli-0.4.1}/src/everypixel_cli/output.py +24 -0
- {everypixel_cli-0.3.0 → everypixel_cli-0.4.1}/src/everypixel_cli/resources/openapi.json +406 -40
- {everypixel_cli-0.3.0 → everypixel_cli-0.4.1}/src/everypixel_cli/schemas.py +287 -1
- everypixel_cli-0.3.0/src/everypixel_cli/__init__.py +0 -1
- {everypixel_cli-0.3.0 → everypixel_cli-0.4.1}/.gitignore +0 -0
- {everypixel_cli-0.3.0 → everypixel_cli-0.4.1}/LICENSE +0 -0
- {everypixel_cli-0.3.0 → everypixel_cli-0.4.1}/pyproject.toml +0 -0
- {everypixel_cli-0.3.0 → everypixel_cli-0.4.1}/src/everypixel_cli/__main__.py +0 -0
- {everypixel_cli-0.3.0 → everypixel_cli-0.4.1}/src/everypixel_cli/application/__init__.py +0 -0
- {everypixel_cli-0.3.0 → everypixel_cli-0.4.1}/src/everypixel_cli/application/chat.py +0 -0
- {everypixel_cli-0.3.0 → everypixel_cli-0.4.1}/src/everypixel_cli/application/models.py +0 -0
- {everypixel_cli-0.3.0 → everypixel_cli-0.4.1}/src/everypixel_cli/client.py +0 -0
- {everypixel_cli-0.3.0 → everypixel_cli-0.4.1}/src/everypixel_cli/config.py +0 -0
- {everypixel_cli-0.3.0 → everypixel_cli-0.4.1}/src/everypixel_cli/errors.py +0 -0
- {everypixel_cli-0.3.0 → everypixel_cli-0.4.1}/src/everypixel_cli/files.py +0 -0
- {everypixel_cli-0.3.0 → everypixel_cli-0.4.1}/src/everypixel_cli/openapi.py +0 -0
- {everypixel_cli-0.3.0 → everypixel_cli-0.4.1}/src/everypixel_cli/resources/__init__.py +0 -0
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.4.1] - 2026-10-08
|
|
4
|
+
|
|
5
|
+
- Display estimated and billed generation costs, including chat responses.
|
|
6
|
+
- Report observed queue time while waiting for tasks.
|
|
7
|
+
|
|
8
|
+
## [0.4.0] - 2026-10-07
|
|
9
|
+
|
|
10
|
+
- Add ElevenLabs speech synthesis and voice design.
|
|
11
|
+
- Add saved voice creation, listing, deletion, and Qwen cloning by voice ID.
|
|
12
|
+
|
|
3
13
|
## [0.3.0] - 2026-09-18
|
|
4
14
|
|
|
5
15
|
- Add GPT Image 2.5 Sunburst and quality selection for GPT Image models.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: everypixel-cli
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.4.1
|
|
4
4
|
Summary: Official command line interface for Everypixel API
|
|
5
5
|
Project-URL: Homepage, https://labs.everypixel.com
|
|
6
6
|
Project-URL: Repository, https://github.com/everypixel/everypixel-cli
|
|
@@ -263,9 +263,12 @@ They support 4K output. The default `seedvr2` supports up to 1440p, with a
|
|
|
263
263
|
| `everypixel lipsync video` | Synchronize a video with an audio track |
|
|
264
264
|
| `everypixel lipsync image` | Create a lipsync video from a still image and audio |
|
|
265
265
|
| `everypixel audio transcribe` | Transcribe speech from an audio file |
|
|
266
|
-
| `everypixel audio tts-create` | Create speech with a
|
|
267
|
-
| `everypixel audio tts-clone` | Create speech from
|
|
268
|
-
| `everypixel audio tts-
|
|
266
|
+
| `everypixel audio tts-create` | Create speech with a Qwen preset or saved ElevenLabs voice |
|
|
267
|
+
| `everypixel audio tts-clone` | Create Qwen speech from an audio sample or saved voice |
|
|
268
|
+
| `everypixel audio tts-design` | Design voice previews from a description |
|
|
269
|
+
| `everypixel audio tts-voice` | Save audio samples or a design preview as a voice |
|
|
270
|
+
| `everypixel audio tts-voices` | List presets and saved voices |
|
|
271
|
+
| `everypixel audio tts-delete` | Delete an owned saved voice |
|
|
269
272
|
|
|
270
273
|
Lipsync supports 720p; the API checks source media dimensions and duration.
|
|
271
274
|
|
|
@@ -290,6 +293,25 @@ everypixel audio tts-create \
|
|
|
290
293
|
--download ./outputs
|
|
291
294
|
```
|
|
292
295
|
|
|
296
|
+
`tts-create` lets the API choose the model when `--model` is omitted.
|
|
297
|
+
`tts-clone` and `tts-design` default to `--model qwen3`.
|
|
298
|
+
`--speaker`, `--style`, `--language`, and `--seed` are Qwen controls; `--character` applies only to Qwen voice design.
|
|
299
|
+
Qwen text is limited to 200 characters. ElevenLabs synthesis accepts up to
|
|
300
|
+
2048 characters and requires `--voice-id`. Supported synthesis models are
|
|
301
|
+
`eleven_v4`, `eleven_v4_turbo`, `eleven_v3`, `eleven_v3_conversational`,
|
|
302
|
+
`eleven_multilingual_v2`, and `eleven_flash_v2_5`. ElevenLabs design uses
|
|
303
|
+
`eleven_multilingual_ttv_v2` or `eleven_ttv_v3`, with 100–1000 characters of
|
|
304
|
+
text and a 20–1000 character `--prompt`.
|
|
305
|
+
|
|
306
|
+
Save a returned preview with `tts-voice --name "Narrator" --preview-id UUID`, or save local
|
|
307
|
+
samples with `tts-voice --name "My voice" --provider elevenlabs --audio ./sample.wav`.
|
|
308
|
+
`--audio` is repeatable for ElevenLabs; Qwen (`--provider labs`, the default)
|
|
309
|
+
requires one sample. Saving returns a voice card immediately. Use its ID with
|
|
310
|
+
`tts-create --model eleven_v3 --voice-id UUID --text "Hello"` or
|
|
311
|
+
`tts-clone --voice-id UUID --text "Hello"`. List voices with
|
|
312
|
+
`tts-voices --provider elevenlabs --offset 0 --limit 50`, and delete an owned
|
|
313
|
+
voice with `tts-delete --voice-id UUID`.
|
|
314
|
+
|
|
293
315
|
### Chat
|
|
294
316
|
|
|
295
317
|
```bash
|
|
@@ -369,6 +391,12 @@ Passing `--download` always waits for a successful result before saving files:
|
|
|
369
391
|
everypixel wait TASK_ID --download ./outputs
|
|
370
392
|
```
|
|
371
393
|
|
|
394
|
+
`elapsed_sec` is total local waiting time; `queue_sec` estimates time until the
|
|
395
|
+
first observed `STARTED` response.
|
|
396
|
+
|
|
397
|
+
API costs are USD strings: `estimated_cost` for pending tasks and `billed_cost`
|
|
398
|
+
for successful results and chat. Missing costs are omitted; `"0"` means no charge.
|
|
399
|
+
|
|
372
400
|
A Bash automation example:
|
|
373
401
|
|
|
374
402
|
```bash
|
|
@@ -229,9 +229,12 @@ They support 4K output. The default `seedvr2` supports up to 1440p, with a
|
|
|
229
229
|
| `everypixel lipsync video` | Synchronize a video with an audio track |
|
|
230
230
|
| `everypixel lipsync image` | Create a lipsync video from a still image and audio |
|
|
231
231
|
| `everypixel audio transcribe` | Transcribe speech from an audio file |
|
|
232
|
-
| `everypixel audio tts-create` | Create speech with a
|
|
233
|
-
| `everypixel audio tts-clone` | Create speech from
|
|
234
|
-
| `everypixel audio tts-
|
|
232
|
+
| `everypixel audio tts-create` | Create speech with a Qwen preset or saved ElevenLabs voice |
|
|
233
|
+
| `everypixel audio tts-clone` | Create Qwen speech from an audio sample or saved voice |
|
|
234
|
+
| `everypixel audio tts-design` | Design voice previews from a description |
|
|
235
|
+
| `everypixel audio tts-voice` | Save audio samples or a design preview as a voice |
|
|
236
|
+
| `everypixel audio tts-voices` | List presets and saved voices |
|
|
237
|
+
| `everypixel audio tts-delete` | Delete an owned saved voice |
|
|
235
238
|
|
|
236
239
|
Lipsync supports 720p; the API checks source media dimensions and duration.
|
|
237
240
|
|
|
@@ -256,6 +259,25 @@ everypixel audio tts-create \
|
|
|
256
259
|
--download ./outputs
|
|
257
260
|
```
|
|
258
261
|
|
|
262
|
+
`tts-create` lets the API choose the model when `--model` is omitted.
|
|
263
|
+
`tts-clone` and `tts-design` default to `--model qwen3`.
|
|
264
|
+
`--speaker`, `--style`, `--language`, and `--seed` are Qwen controls; `--character` applies only to Qwen voice design.
|
|
265
|
+
Qwen text is limited to 200 characters. ElevenLabs synthesis accepts up to
|
|
266
|
+
2048 characters and requires `--voice-id`. Supported synthesis models are
|
|
267
|
+
`eleven_v4`, `eleven_v4_turbo`, `eleven_v3`, `eleven_v3_conversational`,
|
|
268
|
+
`eleven_multilingual_v2`, and `eleven_flash_v2_5`. ElevenLabs design uses
|
|
269
|
+
`eleven_multilingual_ttv_v2` or `eleven_ttv_v3`, with 100–1000 characters of
|
|
270
|
+
text and a 20–1000 character `--prompt`.
|
|
271
|
+
|
|
272
|
+
Save a returned preview with `tts-voice --name "Narrator" --preview-id UUID`, or save local
|
|
273
|
+
samples with `tts-voice --name "My voice" --provider elevenlabs --audio ./sample.wav`.
|
|
274
|
+
`--audio` is repeatable for ElevenLabs; Qwen (`--provider labs`, the default)
|
|
275
|
+
requires one sample. Saving returns a voice card immediately. Use its ID with
|
|
276
|
+
`tts-create --model eleven_v3 --voice-id UUID --text "Hello"` or
|
|
277
|
+
`tts-clone --voice-id UUID --text "Hello"`. List voices with
|
|
278
|
+
`tts-voices --provider elevenlabs --offset 0 --limit 50`, and delete an owned
|
|
279
|
+
voice with `tts-delete --voice-id UUID`.
|
|
280
|
+
|
|
259
281
|
### Chat
|
|
260
282
|
|
|
261
283
|
```bash
|
|
@@ -335,6 +357,12 @@ Passing `--download` always waits for a successful result before saving files:
|
|
|
335
357
|
everypixel wait TASK_ID --download ./outputs
|
|
336
358
|
```
|
|
337
359
|
|
|
360
|
+
`elapsed_sec` is total local waiting time; `queue_sec` estimates time until the
|
|
361
|
+
first observed `STARTED` response.
|
|
362
|
+
|
|
363
|
+
API costs are USD strings: `estimated_cost` for pending tasks and `billed_cost`
|
|
364
|
+
for successful results and chat. Missing costs are omitted; `"0"` means no charge.
|
|
365
|
+
|
|
338
366
|
A Bash automation example:
|
|
339
367
|
|
|
340
368
|
```bash
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.4.1"
|
{everypixel_cli-0.3.0 → everypixel_cli-0.4.1}/src/everypixel_cli/application/serialization.py
RENAMED
|
@@ -14,6 +14,8 @@ def serialize_operation_result(result: OperationResult) -> Any:
|
|
|
14
14
|
value = result.value
|
|
15
15
|
if isinstance(value, Mapping):
|
|
16
16
|
payload = dict(value)
|
|
17
|
+
if "task_id" in payload and payload.get("status") == "SUCCESS":
|
|
18
|
+
payload.pop("estimated_cost", None)
|
|
17
19
|
if result.saved_files:
|
|
18
20
|
payload["downloaded"] = [str(path) for path in result.saved_files]
|
|
19
21
|
return payload
|
|
@@ -34,6 +34,13 @@ from ..openapi import (
|
|
|
34
34
|
)
|
|
35
35
|
from ..schemas import (
|
|
36
36
|
ChatRequest,
|
|
37
|
+
TTS_CREATE_PAYLOADS,
|
|
38
|
+
TTS_DESIGN_PAYLOADS,
|
|
39
|
+
TTSClonePayload,
|
|
40
|
+
TTSDefaultCreatePayload,
|
|
41
|
+
TTSVoiceSavePayload,
|
|
42
|
+
TTSVoicesPayload,
|
|
43
|
+
TTSVoiceDeletePayload,
|
|
37
44
|
GPT_IMAGE_MODEL_QUALITIES,
|
|
38
45
|
ImageEditPayload,
|
|
39
46
|
ImageGeneratePayload,
|
|
@@ -205,14 +212,20 @@ class TaskService:
|
|
|
205
212
|
|
|
206
213
|
def wait(self, task_id: str, options: ExecutionOptions) -> dict[str, Any]:
|
|
207
214
|
started = time.monotonic()
|
|
215
|
+
queue_sec: float | None = None
|
|
208
216
|
while True:
|
|
209
217
|
options.check_cancelled()
|
|
210
218
|
payload = self.status(task_id)
|
|
211
219
|
options.check_cancelled()
|
|
220
|
+
elapsed = time.monotonic() - started
|
|
221
|
+
if payload.get("status") == "STARTED" and queue_sec is None:
|
|
222
|
+
queue_sec = round(elapsed, 2)
|
|
212
223
|
if payload.get("status") == "SUCCESS":
|
|
213
|
-
payload["elapsed_sec"] = round(
|
|
224
|
+
payload["elapsed_sec"] = round(elapsed, 2)
|
|
225
|
+
if queue_sec is not None:
|
|
226
|
+
payload["queue_sec"] = queue_sec
|
|
214
227
|
return payload
|
|
215
|
-
if
|
|
228
|
+
if elapsed >= options.timeout:
|
|
216
229
|
raise TaskPollingError(
|
|
217
230
|
f"Timed out waiting for task {task_id}",
|
|
218
231
|
code="timeout",
|
|
@@ -240,11 +253,14 @@ class OperationService:
|
|
|
240
253
|
def execute(self, request: OperationRequest) -> OperationResult:
|
|
241
254
|
method = request.method.upper()
|
|
242
255
|
payload = dict(request.payload)
|
|
256
|
+
use_query = method == "GET" or (
|
|
257
|
+
method == "DELETE" and request.endpoint == "/v1/tts_voice"
|
|
258
|
+
)
|
|
243
259
|
response = self.client.request(
|
|
244
260
|
method,
|
|
245
261
|
request.endpoint,
|
|
246
|
-
json=
|
|
247
|
-
params=payload if
|
|
262
|
+
json=None if use_query else payload,
|
|
263
|
+
params=payload if use_query else None,
|
|
248
264
|
)
|
|
249
265
|
if not isinstance(response, Mapping) or "task_id" not in response:
|
|
250
266
|
return OperationResult(value=response)
|
|
@@ -753,33 +769,64 @@ def read_text_input(text: str | None, text_file: Path | None) -> str:
|
|
|
753
769
|
return text
|
|
754
770
|
|
|
755
771
|
|
|
756
|
-
def
|
|
757
|
-
|
|
772
|
+
def validate_tts_payload(
|
|
773
|
+
path: str, values: dict[str, Any], *, generic: bool = False
|
|
774
|
+
) -> BaseModel:
|
|
775
|
+
"""Select a provider schema without dropping unsupported controls."""
|
|
776
|
+
if generic:
|
|
777
|
+
schemas: list[type[BaseModel]] = [
|
|
778
|
+
*TTS_CREATE_PAYLOADS.values(),
|
|
779
|
+
*TTS_DESIGN_PAYLOADS.values(),
|
|
780
|
+
TTSClonePayload,
|
|
781
|
+
TTSDefaultCreatePayload,
|
|
782
|
+
]
|
|
783
|
+
known_fields = {field for schema in schemas for field in schema.model_fields}
|
|
784
|
+
values = {key: value for key, value in values.items() if key in known_fields}
|
|
785
|
+
if path == "/v1/tts_create" and "model" not in values:
|
|
786
|
+
return TTSDefaultCreatePayload.model_validate(values)
|
|
787
|
+
registries = {
|
|
788
|
+
"/v1/tts_create": TTS_CREATE_PAYLOADS,
|
|
789
|
+
"/v1/tts_design": TTS_DESIGN_PAYLOADS,
|
|
790
|
+
}
|
|
791
|
+
if path in registries:
|
|
792
|
+
model = values.get("model", "qwen3")
|
|
793
|
+
schema = registries[path].get(model) if isinstance(model, str) else None
|
|
794
|
+
if schema is None:
|
|
795
|
+
raise ValidationCLIError("Unsupported TTS model")
|
|
796
|
+
return schema.model_validate(values)
|
|
797
|
+
return TTSClonePayload.model_validate(values)
|
|
798
|
+
|
|
758
799
|
|
|
800
|
+
def _build_tts_payload(path: str, values: dict[str, Any]) -> dict[str, Any]:
|
|
759
801
|
text = values.pop("text")
|
|
760
802
|
text_file = values.pop("text_file")
|
|
761
|
-
|
|
803
|
+
values = {key: value for key, value in values.items() if value is not None}
|
|
804
|
+
values["text"] = read_text_input(text, text_file)
|
|
805
|
+
return validate_tts_payload(path, values).model_dump(mode="json", exclude_none=True)
|
|
762
806
|
|
|
763
807
|
|
|
764
|
-
def
|
|
765
|
-
""
|
|
808
|
+
def build_tts_create_payload(**values: Any) -> dict[str, Any]:
|
|
809
|
+
return _build_tts_payload("/v1/tts_create", values)
|
|
766
810
|
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
return
|
|
811
|
+
|
|
812
|
+
def build_tts_design_payload(**values: Any) -> dict[str, Any]:
|
|
813
|
+
return _build_tts_payload("/v1/tts_design", values)
|
|
770
814
|
|
|
771
815
|
|
|
772
816
|
def build_tts_clone_payload(**values: Any) -> dict[str, Any]:
|
|
773
|
-
|
|
817
|
+
audio = values.pop("audio")
|
|
818
|
+
if audio is not None:
|
|
819
|
+
values["audio_url"] = media_value(audio)
|
|
820
|
+
return _build_tts_payload("/v1/tts_clone", values)
|
|
821
|
+
|
|
774
822
|
|
|
823
|
+
def build_tts_voice_payload(**values: Any) -> dict[str, Any]:
|
|
775
824
|
audio = values.pop("audio")
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
return
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
**values,
|
|
782
|
-
}
|
|
825
|
+
if audio:
|
|
826
|
+
values["audio_urls"] = [media_value(sample) for sample in audio]
|
|
827
|
+
return TTSVoiceSavePayload.model_validate(
|
|
828
|
+
{key: value for key, value in values.items() if value is not None}
|
|
829
|
+
).model_dump(mode="json", exclude_none=True)
|
|
783
830
|
|
|
784
831
|
|
|
785
832
|
@dataclass
|
|
@@ -1147,25 +1194,28 @@ class ApplicationServices:
|
|
|
1147
1194
|
*,
|
|
1148
1195
|
text: str | None,
|
|
1149
1196
|
text_file: Path | None,
|
|
1150
|
-
speaker: str,
|
|
1151
|
-
style: str,
|
|
1152
|
-
language: str,
|
|
1153
|
-
prompt: str,
|
|
1154
|
-
seed: int,
|
|
1155
1197
|
execution: ExecutionOptions,
|
|
1198
|
+
model: str | None = None,
|
|
1199
|
+
voice_id: str | None = None,
|
|
1200
|
+
speaker: str | None = None,
|
|
1201
|
+
style: str | None = None,
|
|
1202
|
+
language: str | None = None,
|
|
1203
|
+
prompt: str | None = None,
|
|
1204
|
+
seed: int | None = None,
|
|
1156
1205
|
) -> OperationResult:
|
|
1157
|
-
payload = build_tts_create_payload(
|
|
1158
|
-
text=text,
|
|
1159
|
-
text_file=text_file,
|
|
1160
|
-
speaker=speaker,
|
|
1161
|
-
style=style,
|
|
1162
|
-
language=language,
|
|
1163
|
-
prompt=prompt,
|
|
1164
|
-
seed=seed,
|
|
1165
|
-
)
|
|
1166
1206
|
return self._execute_async(
|
|
1167
1207
|
endpoint="/v1/tts_create",
|
|
1168
|
-
payload=
|
|
1208
|
+
payload=build_tts_create_payload(
|
|
1209
|
+
text=text,
|
|
1210
|
+
text_file=text_file,
|
|
1211
|
+
model=model,
|
|
1212
|
+
voice_id=voice_id,
|
|
1213
|
+
speaker=speaker,
|
|
1214
|
+
style=style,
|
|
1215
|
+
language=language,
|
|
1216
|
+
prompt=prompt,
|
|
1217
|
+
seed=seed,
|
|
1218
|
+
),
|
|
1169
1219
|
execution=execution,
|
|
1170
1220
|
fallback_extension=".mp3",
|
|
1171
1221
|
)
|
|
@@ -1173,55 +1223,113 @@ class ApplicationServices:
|
|
|
1173
1223
|
def execute_tts_clone(
|
|
1174
1224
|
self,
|
|
1175
1225
|
*,
|
|
1176
|
-
audio: str,
|
|
1177
1226
|
text: str | None,
|
|
1178
1227
|
text_file: Path | None,
|
|
1179
|
-
language: str,
|
|
1180
|
-
seed: int,
|
|
1181
1228
|
execution: ExecutionOptions,
|
|
1229
|
+
audio: str | None = None,
|
|
1230
|
+
voice_id: str | None = None,
|
|
1231
|
+
model: str = "qwen3",
|
|
1232
|
+
language: str | None = None,
|
|
1233
|
+
seed: int | None = None,
|
|
1182
1234
|
) -> OperationResult:
|
|
1183
|
-
payload = build_tts_clone_payload(
|
|
1184
|
-
audio=audio,
|
|
1185
|
-
text=text,
|
|
1186
|
-
text_file=text_file,
|
|
1187
|
-
language=language,
|
|
1188
|
-
seed=seed,
|
|
1189
|
-
)
|
|
1190
1235
|
return self._execute_async(
|
|
1191
1236
|
endpoint="/v1/tts_clone",
|
|
1192
|
-
payload=
|
|
1237
|
+
payload=build_tts_clone_payload(
|
|
1238
|
+
audio=audio,
|
|
1239
|
+
text=text,
|
|
1240
|
+
text_file=text_file,
|
|
1241
|
+
voice_id=voice_id,
|
|
1242
|
+
model=model,
|
|
1243
|
+
language=language,
|
|
1244
|
+
seed=seed,
|
|
1245
|
+
),
|
|
1193
1246
|
execution=execution,
|
|
1194
1247
|
fallback_extension=".mp3",
|
|
1195
1248
|
)
|
|
1196
1249
|
|
|
1197
|
-
def
|
|
1250
|
+
def execute_tts_design(
|
|
1198
1251
|
self,
|
|
1199
1252
|
*,
|
|
1200
1253
|
text: str | None,
|
|
1201
1254
|
text_file: Path | None,
|
|
1202
|
-
character: str,
|
|
1203
|
-
style: str,
|
|
1204
|
-
language: str,
|
|
1205
|
-
prompt: str,
|
|
1206
|
-
seed: int,
|
|
1207
1255
|
execution: ExecutionOptions,
|
|
1256
|
+
model: str = "qwen3",
|
|
1257
|
+
character: str | None = None,
|
|
1258
|
+
style: str | None = None,
|
|
1259
|
+
language: str | None = None,
|
|
1260
|
+
prompt: str | None = None,
|
|
1261
|
+
seed: int | None = None,
|
|
1208
1262
|
) -> OperationResult:
|
|
1209
|
-
payload = build_tts_voice_payload(
|
|
1210
|
-
text=text,
|
|
1211
|
-
text_file=text_file,
|
|
1212
|
-
character=character,
|
|
1213
|
-
style=style,
|
|
1214
|
-
language=language,
|
|
1215
|
-
prompt=prompt,
|
|
1216
|
-
seed=seed,
|
|
1217
|
-
)
|
|
1218
1263
|
return self._execute_async(
|
|
1219
|
-
endpoint="/v1/
|
|
1220
|
-
payload=
|
|
1264
|
+
endpoint="/v1/tts_design",
|
|
1265
|
+
payload=build_tts_design_payload(
|
|
1266
|
+
text=text,
|
|
1267
|
+
text_file=text_file,
|
|
1268
|
+
model=model,
|
|
1269
|
+
character=character,
|
|
1270
|
+
style=style,
|
|
1271
|
+
language=language,
|
|
1272
|
+
prompt=prompt,
|
|
1273
|
+
seed=seed,
|
|
1274
|
+
),
|
|
1221
1275
|
execution=execution,
|
|
1222
1276
|
fallback_extension=".mp3",
|
|
1223
1277
|
)
|
|
1224
1278
|
|
|
1279
|
+
def execute_tts_voice(
|
|
1280
|
+
self,
|
|
1281
|
+
*,
|
|
1282
|
+
name: str,
|
|
1283
|
+
audio: list[str],
|
|
1284
|
+
provider: str | None = None,
|
|
1285
|
+
preview_id: str | None = None,
|
|
1286
|
+
description: str = "",
|
|
1287
|
+
) -> OperationResult:
|
|
1288
|
+
return self._operations.execute(
|
|
1289
|
+
OperationRequest(
|
|
1290
|
+
endpoint="/v1/tts_voice",
|
|
1291
|
+
method="POST",
|
|
1292
|
+
payload=build_tts_voice_payload(
|
|
1293
|
+
name=name,
|
|
1294
|
+
audio=audio,
|
|
1295
|
+
provider=provider,
|
|
1296
|
+
preview_id=preview_id,
|
|
1297
|
+
description=description,
|
|
1298
|
+
),
|
|
1299
|
+
execution=ExecutionOptions(),
|
|
1300
|
+
)
|
|
1301
|
+
)
|
|
1302
|
+
|
|
1303
|
+
def list_tts_voices(
|
|
1304
|
+
self,
|
|
1305
|
+
*,
|
|
1306
|
+
provider: str = "labs",
|
|
1307
|
+
offset: int = 0,
|
|
1308
|
+
limit: int = 50,
|
|
1309
|
+
) -> OperationResult:
|
|
1310
|
+
return self._operations.execute(
|
|
1311
|
+
OperationRequest(
|
|
1312
|
+
endpoint="/v1/tts_voices",
|
|
1313
|
+
method="GET",
|
|
1314
|
+
payload=TTSVoicesPayload.model_validate(
|
|
1315
|
+
{"provider": provider, "offset": offset, "limit": limit}
|
|
1316
|
+
).model_dump(),
|
|
1317
|
+
execution=ExecutionOptions(),
|
|
1318
|
+
)
|
|
1319
|
+
)
|
|
1320
|
+
|
|
1321
|
+
def delete_tts_voice(self, *, voice_id: str) -> OperationResult:
|
|
1322
|
+
return self._operations.execute(
|
|
1323
|
+
OperationRequest(
|
|
1324
|
+
endpoint="/v1/tts_voice",
|
|
1325
|
+
method="DELETE",
|
|
1326
|
+
payload=TTSVoiceDeletePayload.model_validate(
|
|
1327
|
+
{"voice_id": voice_id}
|
|
1328
|
+
).model_dump(mode="json"),
|
|
1329
|
+
execution=ExecutionOptions(),
|
|
1330
|
+
)
|
|
1331
|
+
)
|
|
1332
|
+
|
|
1225
1333
|
def execute_chat(
|
|
1226
1334
|
self,
|
|
1227
1335
|
*,
|
|
@@ -1340,6 +1448,19 @@ class ApplicationServices:
|
|
|
1340
1448
|
_VIDEO_EDIT_ADAPTER.validate_python(payload, extra="ignore")
|
|
1341
1449
|
if path == "/v1/chat/completions":
|
|
1342
1450
|
ChatRequest.model_validate(payload, extra="ignore")
|
|
1451
|
+
if resolved_method == "POST" and path in {
|
|
1452
|
+
"/v1/tts_create",
|
|
1453
|
+
"/v1/tts_clone",
|
|
1454
|
+
"/v1/tts_design",
|
|
1455
|
+
}:
|
|
1456
|
+
validate_tts_payload(path, payload, generic=True)
|
|
1457
|
+
if path == "/v1/tts_voice":
|
|
1458
|
+
if resolved_method == "POST":
|
|
1459
|
+
TTSVoiceSavePayload.model_validate(payload)
|
|
1460
|
+
elif resolved_method == "DELETE":
|
|
1461
|
+
TTSVoiceDeletePayload.model_validate(payload)
|
|
1462
|
+
if path == "/v1/tts_voices" and resolved_method == "GET":
|
|
1463
|
+
TTSVoicesPayload.model_validate(payload)
|
|
1343
1464
|
validate_payload_against_operation(operation, payload)
|
|
1344
1465
|
if dry_run:
|
|
1345
1466
|
return OperationResult(
|
|
@@ -1285,17 +1285,19 @@ def tts_create(
|
|
|
1285
1285
|
ctx: typer.Context,
|
|
1286
1286
|
text: Annotated[Optional[str], typer.Option("--text")] = None,
|
|
1287
1287
|
text_file: Annotated[Optional[Path], typer.Option("--text-file")] = None,
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1288
|
+
model: Optional[str] = None,
|
|
1289
|
+
voice_id: Optional[str] = None,
|
|
1290
|
+
speaker: Optional[str] = None,
|
|
1291
|
+
style: Optional[str] = None,
|
|
1292
|
+
language: Optional[str] = None,
|
|
1293
|
+
prompt: Optional[str] = None,
|
|
1294
|
+
seed: Optional[int] = None,
|
|
1293
1295
|
wait_option: WaitOption = None,
|
|
1294
1296
|
download: DownloadOption = None,
|
|
1295
1297
|
output_json: OutputJsonOption = False,
|
|
1296
1298
|
jq_expr: JqOption = None,
|
|
1297
1299
|
) -> None:
|
|
1298
|
-
"""Create speech
|
|
1300
|
+
"""Create speech using Qwen presets or a saved ElevenLabs voice."""
|
|
1299
1301
|
|
|
1300
1302
|
runtime = apply_common_options(
|
|
1301
1303
|
ctx,
|
|
@@ -1309,6 +1311,8 @@ def tts_create(
|
|
|
1309
1311
|
lambda: runtime.services().execute_tts_create(
|
|
1310
1312
|
text=text,
|
|
1311
1313
|
text_file=text_file,
|
|
1314
|
+
model=model,
|
|
1315
|
+
voice_id=voice_id,
|
|
1312
1316
|
speaker=speaker,
|
|
1313
1317
|
style=style,
|
|
1314
1318
|
language=language,
|
|
@@ -1322,11 +1326,13 @@ def tts_create(
|
|
|
1322
1326
|
@audio_app.command("tts-clone")
|
|
1323
1327
|
def tts_clone(
|
|
1324
1328
|
ctx: typer.Context,
|
|
1325
|
-
audio: Annotated[str, typer.Option("--audio")],
|
|
1329
|
+
audio: Annotated[Optional[str], typer.Option("--audio")] = None,
|
|
1330
|
+
voice_id: Optional[str] = None,
|
|
1326
1331
|
text: Annotated[Optional[str], typer.Option("--text")] = None,
|
|
1327
1332
|
text_file: Annotated[Optional[Path], typer.Option("--text-file")] = None,
|
|
1328
|
-
|
|
1329
|
-
|
|
1333
|
+
model: str = "qwen3",
|
|
1334
|
+
language: Optional[str] = None,
|
|
1335
|
+
seed: Optional[int] = None,
|
|
1330
1336
|
wait_option: WaitOption = None,
|
|
1331
1337
|
download: DownloadOption = None,
|
|
1332
1338
|
output_json: OutputJsonOption = False,
|
|
@@ -1345,8 +1351,10 @@ def tts_clone(
|
|
|
1345
1351
|
ctx,
|
|
1346
1352
|
lambda: runtime.services().execute_tts_clone(
|
|
1347
1353
|
audio=audio,
|
|
1354
|
+
voice_id=voice_id,
|
|
1348
1355
|
text=text,
|
|
1349
1356
|
text_file=text_file,
|
|
1357
|
+
model=model,
|
|
1350
1358
|
language=language,
|
|
1351
1359
|
seed=seed,
|
|
1352
1360
|
execution=runtime.execution_options(),
|
|
@@ -1354,22 +1362,23 @@ def tts_clone(
|
|
|
1354
1362
|
)
|
|
1355
1363
|
|
|
1356
1364
|
|
|
1357
|
-
@audio_app.command("tts-
|
|
1358
|
-
def
|
|
1365
|
+
@audio_app.command("tts-design")
|
|
1366
|
+
def tts_design(
|
|
1359
1367
|
ctx: typer.Context,
|
|
1360
1368
|
text: Annotated[Optional[str], typer.Option("--text")] = None,
|
|
1361
1369
|
text_file: Annotated[Optional[Path], typer.Option("--text-file")] = None,
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1370
|
+
model: str = "qwen3",
|
|
1371
|
+
character: Optional[str] = None,
|
|
1372
|
+
style: Optional[str] = None,
|
|
1373
|
+
language: Optional[str] = None,
|
|
1374
|
+
prompt: Optional[str] = None,
|
|
1375
|
+
seed: Optional[int] = None,
|
|
1367
1376
|
wait_option: WaitOption = None,
|
|
1368
1377
|
download: DownloadOption = None,
|
|
1369
1378
|
output_json: OutputJsonOption = False,
|
|
1370
1379
|
jq_expr: JqOption = None,
|
|
1371
1380
|
) -> None:
|
|
1372
|
-
"""
|
|
1381
|
+
"""Design voice previews from a description."""
|
|
1373
1382
|
|
|
1374
1383
|
runtime = apply_common_options(
|
|
1375
1384
|
ctx,
|
|
@@ -1380,9 +1389,10 @@ def tts_voice(
|
|
|
1380
1389
|
)
|
|
1381
1390
|
run_action(
|
|
1382
1391
|
ctx,
|
|
1383
|
-
lambda: runtime.services().
|
|
1392
|
+
lambda: runtime.services().execute_tts_design(
|
|
1384
1393
|
text=text,
|
|
1385
1394
|
text_file=text_file,
|
|
1395
|
+
model=model,
|
|
1386
1396
|
character=character,
|
|
1387
1397
|
style=style,
|
|
1388
1398
|
language=language,
|
|
@@ -1393,6 +1403,64 @@ def tts_voice(
|
|
|
1393
1403
|
)
|
|
1394
1404
|
|
|
1395
1405
|
|
|
1406
|
+
@audio_app.command("tts-voice")
|
|
1407
|
+
def tts_voice(
|
|
1408
|
+
ctx: typer.Context,
|
|
1409
|
+
name: Annotated[str, typer.Option("--name")],
|
|
1410
|
+
audio: Annotated[list[str], typer.Option("--audio")] = [],
|
|
1411
|
+
provider: Optional[str] = None,
|
|
1412
|
+
preview_id: Optional[str] = None,
|
|
1413
|
+
description: str = "",
|
|
1414
|
+
output_json: OutputJsonOption = False,
|
|
1415
|
+
jq_expr: JqOption = None,
|
|
1416
|
+
) -> None:
|
|
1417
|
+
"""Save a voice from audio samples or a design preview."""
|
|
1418
|
+
runtime = apply_common_options(ctx, output_json=output_json, jq_expr=jq_expr)
|
|
1419
|
+
run_action(
|
|
1420
|
+
ctx,
|
|
1421
|
+
lambda: runtime.services().execute_tts_voice(
|
|
1422
|
+
name=name,
|
|
1423
|
+
audio=audio,
|
|
1424
|
+
provider=provider,
|
|
1425
|
+
preview_id=preview_id,
|
|
1426
|
+
description=description,
|
|
1427
|
+
),
|
|
1428
|
+
)
|
|
1429
|
+
|
|
1430
|
+
|
|
1431
|
+
@audio_app.command("tts-voices")
|
|
1432
|
+
def tts_voices(
|
|
1433
|
+
ctx: typer.Context,
|
|
1434
|
+
provider: str = "labs",
|
|
1435
|
+
offset: int = 0,
|
|
1436
|
+
limit: int = 50,
|
|
1437
|
+
output_json: OutputJsonOption = False,
|
|
1438
|
+
jq_expr: JqOption = None,
|
|
1439
|
+
) -> None:
|
|
1440
|
+
"""List presets and saved voices for a provider."""
|
|
1441
|
+
runtime = apply_common_options(ctx, output_json=output_json, jq_expr=jq_expr)
|
|
1442
|
+
run_action(
|
|
1443
|
+
ctx,
|
|
1444
|
+
lambda: runtime.services().list_tts_voices(
|
|
1445
|
+
provider=provider,
|
|
1446
|
+
offset=offset,
|
|
1447
|
+
limit=limit,
|
|
1448
|
+
),
|
|
1449
|
+
)
|
|
1450
|
+
|
|
1451
|
+
|
|
1452
|
+
@audio_app.command("tts-delete")
|
|
1453
|
+
def tts_delete(
|
|
1454
|
+
ctx: typer.Context,
|
|
1455
|
+
voice_id: Annotated[str, typer.Option("--voice-id")],
|
|
1456
|
+
output_json: OutputJsonOption = False,
|
|
1457
|
+
jq_expr: JqOption = None,
|
|
1458
|
+
) -> None:
|
|
1459
|
+
"""Delete an owned saved voice."""
|
|
1460
|
+
runtime = apply_common_options(ctx, output_json=output_json, jq_expr=jq_expr)
|
|
1461
|
+
run_action(ctx, lambda: runtime.services().delete_tts_voice(voice_id=voice_id))
|
|
1462
|
+
|
|
1463
|
+
|
|
1396
1464
|
@app.command("status")
|
|
1397
1465
|
def status(
|
|
1398
1466
|
ctx: typer.Context,
|