everypixel-cli 0.1.0__tar.gz → 0.2.0__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.
Files changed (24) hide show
  1. {everypixel_cli-0.1.0 → everypixel_cli-0.2.0}/CHANGELOG.md +7 -0
  2. {everypixel_cli-0.1.0 → everypixel_cli-0.2.0}/PKG-INFO +49 -1
  3. {everypixel_cli-0.1.0 → everypixel_cli-0.2.0}/README.md +48 -0
  4. everypixel_cli-0.2.0/src/everypixel_cli/__init__.py +1 -0
  5. {everypixel_cli-0.1.0 → everypixel_cli-0.2.0}/src/everypixel_cli/application/services.py +129 -28
  6. {everypixel_cli-0.1.0 → everypixel_cli-0.2.0}/src/everypixel_cli/cli.py +58 -10
  7. {everypixel_cli-0.1.0 → everypixel_cli-0.2.0}/src/everypixel_cli/mcp_server.py +33 -5
  8. {everypixel_cli-0.1.0 → everypixel_cli-0.2.0}/src/everypixel_cli/resources/openapi.json +604 -123
  9. {everypixel_cli-0.1.0 → everypixel_cli-0.2.0}/src/everypixel_cli/schemas.py +241 -75
  10. everypixel_cli-0.1.0/src/everypixel_cli/__init__.py +0 -1
  11. {everypixel_cli-0.1.0 → everypixel_cli-0.2.0}/.gitignore +0 -0
  12. {everypixel_cli-0.1.0 → everypixel_cli-0.2.0}/LICENSE +0 -0
  13. {everypixel_cli-0.1.0 → everypixel_cli-0.2.0}/pyproject.toml +0 -0
  14. {everypixel_cli-0.1.0 → everypixel_cli-0.2.0}/src/everypixel_cli/__main__.py +0 -0
  15. {everypixel_cli-0.1.0 → everypixel_cli-0.2.0}/src/everypixel_cli/application/__init__.py +0 -0
  16. {everypixel_cli-0.1.0 → everypixel_cli-0.2.0}/src/everypixel_cli/application/models.py +0 -0
  17. {everypixel_cli-0.1.0 → everypixel_cli-0.2.0}/src/everypixel_cli/application/serialization.py +0 -0
  18. {everypixel_cli-0.1.0 → everypixel_cli-0.2.0}/src/everypixel_cli/client.py +0 -0
  19. {everypixel_cli-0.1.0 → everypixel_cli-0.2.0}/src/everypixel_cli/config.py +0 -0
  20. {everypixel_cli-0.1.0 → everypixel_cli-0.2.0}/src/everypixel_cli/errors.py +0 -0
  21. {everypixel_cli-0.1.0 → everypixel_cli-0.2.0}/src/everypixel_cli/files.py +0 -0
  22. {everypixel_cli-0.1.0 → everypixel_cli-0.2.0}/src/everypixel_cli/openapi.py +0 -0
  23. {everypixel_cli-0.1.0 → everypixel_cli-0.2.0}/src/everypixel_cli/output.py +0 -0
  24. {everypixel_cli-0.1.0 → everypixel_cli-0.2.0}/src/everypixel_cli/resources/__init__.py +0 -0
@@ -1,5 +1,12 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.2.0] - 2026-09-09
4
+
5
+ - Add MiniMax H3/Turbo, WAN 3.0, Flux 3, and Seedance 2.5 video models.
6
+ - Add Recraft vector generation, palette controls, and image-to-SVG conversion.
7
+ - Update Grok Imagine models, image-edit input limits, and megapixel controls.
8
+ - Add Topaz 4K video upscaling and 720p lipsync.
9
+
3
10
  ## [0.1.0] - 2026-08-17
4
11
 
5
12
  Initial public release.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: everypixel-cli
3
- Version: 0.1.0
3
+ Version: 0.2.0
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
@@ -144,6 +144,7 @@ CLI falls back to its user configuration file when a keyring is unavailable;
144
144
  | --- | --- |
145
145
  | `everypixel image generate PROMPT` | Generate an image from a text prompt |
146
146
  | `everypixel image edit PROMPT --image FILE` | Edit one or more images |
147
+ | `everypixel image vectorize FILE` | Convert an image to SVG |
147
148
  | `everypixel image upscale` | Upscale an image, URL, or existing task result |
148
149
  | `everypixel image angles --image FILE` | Change the camera angle of an image |
149
150
  | `everypixel image colors --image FILE --reference FILE` | Transfer colors from a reference image |
@@ -159,6 +160,13 @@ everypixel image edit \
159
160
 
160
161
  everypixel image upscale --image ./input.png --download ./outputs
161
162
 
163
+ everypixel image generate "a mountain icon" \
164
+ --model recraftv4_1_vector \
165
+ --controls '{"colors":[{"rgb":[20,100,180],"weight":1}]}' \
166
+ --download ./outputs
167
+
168
+ everypixel image vectorize ./input.png --download ./outputs
169
+
162
170
  everypixel image angles \
163
171
  --image ./input.png \
164
172
  --azimuth right \
@@ -167,6 +175,16 @@ everypixel image angles \
167
175
  --download ./outputs
168
176
  ```
169
177
 
178
+ Recraft V4.1 vector models (`recraftv4_1_vector`, `recraftv4_1_pro_vector`)
179
+ produce SVG files and accept optional JSON palette `--controls`. Use `--size`
180
+ to select their aspect ratio. Raster models also accept `--resolution`.
181
+
182
+ `--lora-url` is supported only by `zimage`.
183
+
184
+ Grok image models are `grok-imagine`, `grok-imagine-2`, and
185
+ `grok-imagine-2-low`. The `grok` alias remains supported. `grok_quality` and
186
+ `wan22` have been removed from the API.
187
+
170
188
  ### Video
171
189
 
172
190
  | Command | Description |
@@ -197,8 +215,36 @@ everypixel video edit \
197
215
  "make the scene cinematic" \
198
216
  --video ./input.mp4 \
199
217
  --download ./outputs
218
+
219
+ everypixel video generate "a slow aerial shot over mountain lakes" \
220
+ --model minimax-h3-turbo --duration 10 --download ./outputs
221
+
222
+ everypixel video edit "continue the scene with a slow camera pan" \
223
+ --model flux3 --video ./input.mp4 --download ./outputs
224
+
225
+ everypixel video upscale --video ./input.mp4 \
226
+ --model topaz-prob-4 --resolution 4k --download ./outputs
200
227
  ```
201
228
 
229
+ New video models use these provider defaults and limits:
230
+
231
+ | Model | Duration | Default resolution / aspect ratio | Inputs |
232
+ | --- | --- | --- | --- |
233
+ | `minimax-h3-turbo`, `minimax-h3` | 3–15 s | 768p / 7:4 | Text, first/last frames, or one `--reference-image` |
234
+ | `wan3.0` | 2–30 s | 1080p / adaptive | Text, frames, or up to 10 reference images and 5 reference videos |
235
+ | `flux3` | 5–20 s generation; 5–15 s continuation | 720p / 16:9 | Text, frames, or `video edit --video` for continuation |
236
+ | `seedance2.5` | 4–30 s | 720p / 16:9 | Text or `video edit` references, including audio alone |
237
+
238
+ MiniMax reference images cannot be combined with first/last frames. WAN 3.0
239
+ also separates frame and reference inputs. Seedance 2.5 supports up to 1080p;
240
+ Seedance 2 and Mini retain their 15-second limit. Grok video model names are
241
+ `grok-imagine` and `grok-imagine-1.5` (image required); `grok` and `grok15`
242
+ remain accepted aliases. The CLI's default video model remains `ltx23`.
243
+
244
+ Topaz upscaling models are `topaz-prob-4`, `topaz-slp-2.5`, and `topaz-ast-2`.
245
+ They support 4K output. The default `seedvr2` supports up to 1440p, with a
246
+ 20-second source limit at 1440p.
247
+
202
248
  ### Lipsync and Audio
203
249
 
204
250
  | Command | Description |
@@ -210,6 +256,8 @@ everypixel video edit \
210
256
  | `everypixel audio tts-clone` | Create speech from a cloned voice sample |
211
257
  | `everypixel audio tts-voice` | Create speech with a character voice |
212
258
 
259
+ Lipsync supports 720p; the API checks source media dimensions and duration.
260
+
213
261
  Examples:
214
262
 
215
263
  ```bash
@@ -110,6 +110,7 @@ CLI falls back to its user configuration file when a keyring is unavailable;
110
110
  | --- | --- |
111
111
  | `everypixel image generate PROMPT` | Generate an image from a text prompt |
112
112
  | `everypixel image edit PROMPT --image FILE` | Edit one or more images |
113
+ | `everypixel image vectorize FILE` | Convert an image to SVG |
113
114
  | `everypixel image upscale` | Upscale an image, URL, or existing task result |
114
115
  | `everypixel image angles --image FILE` | Change the camera angle of an image |
115
116
  | `everypixel image colors --image FILE --reference FILE` | Transfer colors from a reference image |
@@ -125,6 +126,13 @@ everypixel image edit \
125
126
 
126
127
  everypixel image upscale --image ./input.png --download ./outputs
127
128
 
129
+ everypixel image generate "a mountain icon" \
130
+ --model recraftv4_1_vector \
131
+ --controls '{"colors":[{"rgb":[20,100,180],"weight":1}]}' \
132
+ --download ./outputs
133
+
134
+ everypixel image vectorize ./input.png --download ./outputs
135
+
128
136
  everypixel image angles \
129
137
  --image ./input.png \
130
138
  --azimuth right \
@@ -133,6 +141,16 @@ everypixel image angles \
133
141
  --download ./outputs
134
142
  ```
135
143
 
144
+ Recraft V4.1 vector models (`recraftv4_1_vector`, `recraftv4_1_pro_vector`)
145
+ produce SVG files and accept optional JSON palette `--controls`. Use `--size`
146
+ to select their aspect ratio. Raster models also accept `--resolution`.
147
+
148
+ `--lora-url` is supported only by `zimage`.
149
+
150
+ Grok image models are `grok-imagine`, `grok-imagine-2`, and
151
+ `grok-imagine-2-low`. The `grok` alias remains supported. `grok_quality` and
152
+ `wan22` have been removed from the API.
153
+
136
154
  ### Video
137
155
 
138
156
  | Command | Description |
@@ -163,8 +181,36 @@ everypixel video edit \
163
181
  "make the scene cinematic" \
164
182
  --video ./input.mp4 \
165
183
  --download ./outputs
184
+
185
+ everypixel video generate "a slow aerial shot over mountain lakes" \
186
+ --model minimax-h3-turbo --duration 10 --download ./outputs
187
+
188
+ everypixel video edit "continue the scene with a slow camera pan" \
189
+ --model flux3 --video ./input.mp4 --download ./outputs
190
+
191
+ everypixel video upscale --video ./input.mp4 \
192
+ --model topaz-prob-4 --resolution 4k --download ./outputs
166
193
  ```
167
194
 
195
+ New video models use these provider defaults and limits:
196
+
197
+ | Model | Duration | Default resolution / aspect ratio | Inputs |
198
+ | --- | --- | --- | --- |
199
+ | `minimax-h3-turbo`, `minimax-h3` | 3–15 s | 768p / 7:4 | Text, first/last frames, or one `--reference-image` |
200
+ | `wan3.0` | 2–30 s | 1080p / adaptive | Text, frames, or up to 10 reference images and 5 reference videos |
201
+ | `flux3` | 5–20 s generation; 5–15 s continuation | 720p / 16:9 | Text, frames, or `video edit --video` for continuation |
202
+ | `seedance2.5` | 4–30 s | 720p / 16:9 | Text or `video edit` references, including audio alone |
203
+
204
+ MiniMax reference images cannot be combined with first/last frames. WAN 3.0
205
+ also separates frame and reference inputs. Seedance 2.5 supports up to 1080p;
206
+ Seedance 2 and Mini retain their 15-second limit. Grok video model names are
207
+ `grok-imagine` and `grok-imagine-1.5` (image required); `grok` and `grok15`
208
+ remain accepted aliases. The CLI's default video model remains `ltx23`.
209
+
210
+ Topaz upscaling models are `topaz-prob-4`, `topaz-slp-2.5`, and `topaz-ast-2`.
211
+ They support 4K output. The default `seedvr2` supports up to 1440p, with a
212
+ 20-second source limit at 1440p.
213
+
168
214
  ### Lipsync and Audio
169
215
 
170
216
  | Command | Description |
@@ -176,6 +222,8 @@ everypixel video edit \
176
222
  | `everypixel audio tts-clone` | Create speech from a cloned voice sample |
177
223
  | `everypixel audio tts-voice` | Create speech with a character voice |
178
224
 
225
+ Lipsync supports 720p; the API checks source media dimensions and duration.
226
+
179
227
  Examples:
180
228
 
181
229
  ```bash
@@ -0,0 +1 @@
1
+ __version__ = "0.2.0"
@@ -7,9 +7,9 @@ import time
7
7
  from collections.abc import Callable, Mapping
8
8
  from dataclasses import dataclass
9
9
  from pathlib import Path
10
- from typing import Any, Protocol
10
+ from typing import Any, Protocol, get_args
11
11
 
12
- from pydantic import TypeAdapter, ValidationError
12
+ from pydantic import BaseModel, TypeAdapter, ValidationError
13
13
 
14
14
  from ..errors import (
15
15
  APIResponseError,
@@ -36,12 +36,16 @@ from ..schemas import (
36
36
  ImageEditPayload,
37
37
  ImageGeneratePayload,
38
38
  ImageUpscalePayload,
39
+ ImageVectorizePayload,
39
40
  LipsyncImagePayload,
40
41
  LipsyncVideoPayload,
42
+ RecraftGeneratePayload,
41
43
  TaskResponse,
42
44
  VideoEditRequest,
43
45
  VideoGenerateRequest,
46
+ VideoGenerateModel,
44
47
  VideoUpscalePayload,
48
+ normalize_grok_model,
45
49
  )
46
50
  from .models import (
47
51
  ExecutionOptions,
@@ -328,6 +332,8 @@ def fallback_extension_for_endpoint(path: str) -> str:
328
332
  """Infer the existing conservative extension fallback from an endpoint path."""
329
333
 
330
334
  lowered = path.lower()
335
+ if lowered == "/v1/image_vectorize":
336
+ return ".svg"
331
337
  if "transcribe" in lowered or "asr" in lowered:
332
338
  return ".txt"
333
339
  if "video" in lowered or "lipsync" in lowered:
@@ -468,6 +474,31 @@ def _build_wan_video_edit_payload(inputs: _VideoEditInputs) -> dict[str, Any]:
468
474
  )
469
475
 
470
476
 
477
+ def _build_flux3_video_edit_payload(inputs: _VideoEditInputs) -> dict[str, Any]:
478
+ _reject_video_edit_options(
479
+ inputs.model,
480
+ {
481
+ "image": inputs.images,
482
+ "audio": inputs.audio,
483
+ "seed": inputs.seed,
484
+ "keyframe": inputs.keyframes,
485
+ "public_figure_threshold": inputs.public_figure_threshold,
486
+ },
487
+ )
488
+ return _validated_video_edit_payload(
489
+ {
490
+ "prompt": inputs.prompt,
491
+ "model": inputs.model,
492
+ "duration": inputs.duration,
493
+ "resolution": inputs.resolution,
494
+ "aspect_ratio": inputs.aspect_ratio,
495
+ "video_url": media_value(inputs.video) if inputs.video else None,
496
+ "generate_audio": inputs.generate_audio,
497
+ "callback_url": inputs.callback_url,
498
+ }
499
+ )
500
+
501
+
471
502
  def _parse_aleph_keyframe(value: str) -> dict[str, Any]:
472
503
  try:
473
504
  keyframe = json.loads(value)
@@ -509,9 +540,11 @@ def _build_aleph_video_edit_payload(inputs: _VideoEditInputs) -> dict[str, Any]:
509
540
  _VIDEO_EDIT_BUILDERS: dict[str, Callable[[_VideoEditInputs], dict[str, Any]]] = {
510
541
  "seedance2": _build_seedance_video_edit_payload,
511
542
  "seedance2-mini": _build_seedance_video_edit_payload,
543
+ "seedance2.5": _build_seedance_video_edit_payload,
512
544
  "kling-3-omni": _build_kling_video_edit_payload,
513
545
  "wan2.7": _build_wan_video_edit_payload,
514
546
  "aleph2": _build_aleph_video_edit_payload,
547
+ "flux3": _build_flux3_video_edit_payload,
515
548
  }
516
549
 
517
550
 
@@ -557,14 +590,30 @@ def build_video_edit_payload(
557
590
  return builder(inputs)
558
591
 
559
592
 
593
+ _IMAGE_GENERATE_BUILDERS: dict[str, type[BaseModel]] = {
594
+ "recraftv4_1_vector": RecraftGeneratePayload,
595
+ "recraftv4_1_pro_vector": RecraftGeneratePayload,
596
+ }
597
+
598
+
560
599
  def build_image_generate_payload(**values: Any) -> dict[str, Any]:
561
600
  """Validate and build image generation payloads including local media."""
562
601
 
563
602
  image = values.pop("image", None)
564
- return ImageGeneratePayload(
565
- **{key: value for key, value in values.items() if value is not None},
566
- image_url=media_value(image) if image else None,
567
- ).model_dump(exclude_none=True)
603
+ if image is not None:
604
+ values["image_url"] = media_value(image)
605
+ controls = values.get("controls")
606
+ if isinstance(controls, str):
607
+ try:
608
+ values["controls"] = json.loads(controls)
609
+ except json.JSONDecodeError as exc:
610
+ raise InputParsingError("Unable to parse Recraft controls JSON") from exc
611
+ builder = _IMAGE_GENERATE_BUILDERS.get(
612
+ values.get("model", "zimage"), ImageGeneratePayload
613
+ )
614
+ return builder.model_validate(
615
+ {key: value for key, value in values.items() if value is not None}
616
+ ).model_dump(mode="json", exclude_none=True)
568
617
 
569
618
 
570
619
  def build_image_edit_payload(**values: Any) -> dict[str, Any]:
@@ -574,7 +623,15 @@ def build_image_edit_payload(**values: Any) -> dict[str, Any]:
574
623
  return ImageEditPayload(
575
624
  **{key: value for key, value in values.items() if value is not None},
576
625
  image_urls=[media_value(item) for item in images],
577
- ).model_dump(exclude_none=True)
626
+ ).model_dump(mode="json", exclude_none=True)
627
+
628
+
629
+ def build_image_vectorize_payload(
630
+ *, image: str, callback_url: str | None
631
+ ) -> dict[str, Any]:
632
+ return ImageVectorizePayload.model_validate(
633
+ {"image_url": media_value(image), "callback_url": callback_url}
634
+ ).model_dump(mode="json", exclude_none=True)
578
635
 
579
636
 
580
637
  def build_image_upscale_payload(**values: Any) -> dict[str, Any]:
@@ -594,13 +651,15 @@ def build_video_generate_payload(**values: Any) -> dict[str, Any]:
594
651
  last_image = values.pop("last_image", None)
595
652
  reference_images = values.pop("reference_images", [])
596
653
  reference_videos = values.pop("reference_videos", [])
654
+ values = normalize_grok_model(values)
597
655
  if values.get("duration") is None:
598
656
  model = values.get("model")
599
657
  required_duration_defaults = {
600
- "wan22": 5,
658
+ "minimax-h3-turbo": 5,
659
+ "minimax-h3": 5,
601
660
  "ltx23": 5,
602
- "grok": 5,
603
- "grok15": 5,
661
+ "grok-imagine": 5,
662
+ "grok-imagine-1.5": 5,
604
663
  }
605
664
  values["duration"] = (
606
665
  required_duration_defaults.get(model) if isinstance(model, str) else None
@@ -771,6 +830,8 @@ class ApplicationServices:
771
830
  seed: int,
772
831
  callback_url: str | None,
773
832
  execution: ExecutionOptions,
833
+ lora_url: str | None = None,
834
+ controls: dict[str, Any] | str | None = None,
774
835
  ) -> OperationResult:
775
836
  payload = build_image_generate_payload(
776
837
  prompt=prompt,
@@ -781,12 +842,33 @@ class ApplicationServices:
781
842
  resolution=resolution,
782
843
  seed=seed,
783
844
  callback_url=callback_url,
845
+ lora_url=lora_url,
846
+ controls=controls,
784
847
  )
785
848
  return self._execute_async(
786
849
  endpoint="/v1/image_generate",
787
850
  payload=payload,
788
851
  execution=execution,
789
- fallback_extension=".png",
852
+ fallback_extension=(
853
+ ".svg" if model in _IMAGE_GENERATE_BUILDERS else ".png"
854
+ ),
855
+ )
856
+
857
+ def execute_image_vectorize(
858
+ self,
859
+ *,
860
+ image: str,
861
+ callback_url: str | None = None,
862
+ execution: ExecutionOptions,
863
+ ) -> OperationResult:
864
+ return self._execute_async(
865
+ endpoint="/v1/image_vectorize",
866
+ payload=build_image_vectorize_payload(
867
+ image=image,
868
+ callback_url=callback_url,
869
+ ),
870
+ execution=execution,
871
+ fallback_extension=".svg",
790
872
  )
791
873
 
792
874
  def execute_image_edit(
@@ -800,6 +882,7 @@ class ApplicationServices:
800
882
  seed: int,
801
883
  callback_url: str | None,
802
884
  execution: ExecutionOptions,
885
+ megapixel_ratio: float = 1.0,
803
886
  ) -> OperationResult:
804
887
  payload = build_image_edit_payload(
805
888
  prompt=prompt,
@@ -807,6 +890,7 @@ class ApplicationServices:
807
890
  model=model,
808
891
  image_size=image_size,
809
892
  resolution=resolution,
893
+ megapixel_ratio=megapixel_ratio,
810
894
  seed=seed,
811
895
  callback_url=callback_url,
812
896
  )
@@ -885,8 +969,6 @@ class ApplicationServices:
885
969
  duration: int | None,
886
970
  resolution: str | None,
887
971
  aspect_ratio: str | None,
888
- lora_high_url: str | None = None,
889
- lora_low_url: str | None = None,
890
972
  reference_images: list[str] | None = None,
891
973
  reference_videos: list[str] | None = None,
892
974
  image: str | None = None,
@@ -902,8 +984,6 @@ class ApplicationServices:
902
984
  duration=duration,
903
985
  resolution=resolution,
904
986
  aspect_ratio=aspect_ratio,
905
- lora_high_url=lora_high_url,
906
- lora_low_url=lora_low_url,
907
987
  reference_images=reference_images or [],
908
988
  reference_videos=reference_videos or [],
909
989
  image=image,
@@ -966,11 +1046,15 @@ class ApplicationServices:
966
1046
  task_id: str | None,
967
1047
  resolution: str,
968
1048
  execution: ExecutionOptions,
1049
+ model: str = "seedvr2",
1050
+ callback_url: str | None = None,
969
1051
  ) -> OperationResult:
970
1052
  payload = build_video_upscale_payload(
971
1053
  video=video,
972
1054
  video_from_task_id=task_id,
973
1055
  resolution=resolution,
1056
+ model=model,
1057
+ callback_url=callback_url,
974
1058
  )
975
1059
  return self._execute_async(
976
1060
  endpoint="/v1/video_upscale",
@@ -1169,8 +1253,10 @@ class ApplicationServices:
1169
1253
  path = operation.path
1170
1254
  resolved_method = operation.method
1171
1255
  resolved_method = resolved_method or "POST"
1172
- if path == "/v1/video_edit":
1256
+ if path in {"/v1/video_edit", "/v1/video_generate"}:
1173
1257
  normalize_video_edit_content(payload)
1258
+ if path == "/v1/image_vectorize" and isinstance(payload.get("image_url"), str):
1259
+ payload["image_url"] = media_value(payload["image_url"])
1174
1260
  if help_schema:
1175
1261
  return OperationResult(
1176
1262
  value=operation_help(
@@ -1181,6 +1267,14 @@ class ApplicationServices:
1181
1267
  schema_source=schema_source,
1182
1268
  )
1183
1269
  )
1270
+ if path in {"/v1/image_generate", "/v1/image_edit", "/v1/video_generate"}:
1271
+ payload = normalize_grok_model(payload)
1272
+ if path == "/v1/video_generate" and payload.get("model") in get_args(
1273
+ VideoGenerateModel
1274
+ ):
1275
+ _VIDEO_GENERATE_ADAPTER.validate_python(payload, extra="ignore")
1276
+ if path == "/v1/image_vectorize":
1277
+ ImageVectorizePayload.model_validate(payload)
1184
1278
  if path == "/v1/video_edit":
1185
1279
  # Generic callers may send forward-compatible fields that the local
1186
1280
  # specialized model does not know yet; validate known semantics
@@ -1205,7 +1299,12 @@ class ApplicationServices:
1205
1299
  method=resolved_method,
1206
1300
  payload=payload,
1207
1301
  execution=execution,
1208
- fallback_extension=fallback_extension_for_endpoint(path),
1302
+ fallback_extension=(
1303
+ ".svg"
1304
+ if path == "/v1/image_generate"
1305
+ and payload.get("model") in _IMAGE_GENERATE_BUILDERS
1306
+ else fallback_extension_for_endpoint(path)
1307
+ ),
1209
1308
  )
1210
1309
  )
1211
1310
 
@@ -1322,17 +1421,19 @@ def parse_generic_payload(
1322
1421
 
1323
1422
 
1324
1423
  def normalize_video_edit_content(payload: dict[str, Any]) -> None:
1325
- """Encode local generic ``video_edit`` references as data URIs."""
1326
-
1327
- video_url = payload.get("video_url")
1328
- if isinstance(video_url, str):
1329
- payload["video_url"] = media_value(video_url)
1330
- reference_image_urls = payload.get("reference_image_urls")
1331
- if isinstance(reference_image_urls, list):
1332
- payload["reference_image_urls"] = [
1333
- media_value(value) if isinstance(value, str) else value
1334
- for value in reference_image_urls
1335
- ]
1424
+ """Encode local generic video frames and references as data URIs."""
1425
+
1426
+ for key in ("video_url", "image_url", "image_last_url"):
1427
+ value = payload.get(key)
1428
+ if isinstance(value, str):
1429
+ payload[key] = media_value(value)
1430
+ for key in ("reference_image_urls", "reference_video_urls"):
1431
+ values = payload.get(key)
1432
+ if isinstance(values, list):
1433
+ payload[key] = [
1434
+ media_value(value) if isinstance(value, str) else value
1435
+ for value in values
1436
+ ]
1336
1437
  keyframes = payload.get("keyframes")
1337
1438
  if isinstance(keyframes, list):
1338
1439
  for keyframe in keyframes:
@@ -578,6 +578,11 @@ def image_generate(
578
578
  size: Annotated[str, typer.Option("--size")] = "square",
579
579
  style: Annotated[Optional[str], typer.Option("--style")] = None,
580
580
  image: Annotated[Optional[str], typer.Option("--image")] = None,
581
+ lora_url: Annotated[Optional[str], typer.Option("--lora-url")] = None,
582
+ controls: Annotated[
583
+ Optional[str],
584
+ typer.Option("--controls", help="Recraft palette controls as JSON."),
585
+ ] = None,
581
586
  resolution: Annotated[Optional[str], typer.Option("--resolution")] = None,
582
587
  seed: Annotated[int, typer.Option("--seed")] = -1,
583
588
  callback_url: Annotated[Optional[str], typer.Option("--callback-url")] = None,
@@ -603,6 +608,8 @@ def image_generate(
603
608
  image_size=size,
604
609
  style=style,
605
610
  image=image,
611
+ lora_url=lora_url,
612
+ controls=controls,
606
613
  resolution=resolution,
607
614
  seed=seed,
608
615
  callback_url=callback_url,
@@ -622,6 +629,7 @@ def image_edit(
622
629
  model: Annotated[str, typer.Option("--model")] = "flux2",
623
630
  size: Annotated[Optional[str], typer.Option("--size")] = None,
624
631
  resolution: Annotated[Optional[str], typer.Option("--resolution")] = None,
632
+ megapixel_ratio: Annotated[float, typer.Option("--megapixel-ratio")] = 1.0,
625
633
  seed: Annotated[int, typer.Option("--seed")] = -1,
626
634
  callback_url: Annotated[Optional[str], typer.Option("--callback-url")] = None,
627
635
  wait_option: WaitOption = None,
@@ -646,6 +654,7 @@ def image_edit(
646
654
  model=model,
647
655
  image_size=size,
648
656
  resolution=resolution,
657
+ megapixel_ratio=megapixel_ratio,
649
658
  seed=seed,
650
659
  callback_url=callback_url,
651
660
  execution=runtime.execution_options(),
@@ -653,6 +662,35 @@ def image_edit(
653
662
  )
654
663
 
655
664
 
665
+ @image_app.command("vectorize")
666
+ def image_vectorize(
667
+ ctx: typer.Context,
668
+ image: Annotated[str, typer.Argument(help="Image URL or local path.")],
669
+ callback_url: Annotated[Optional[str], typer.Option("--callback-url")] = None,
670
+ wait_option: WaitOption = None,
671
+ download: DownloadOption = None,
672
+ output_json: OutputJsonOption = False,
673
+ jq_expr: JqOption = None,
674
+ ) -> None:
675
+ """Convert an image to SVG."""
676
+
677
+ runtime = apply_common_options(
678
+ ctx,
679
+ wait_option=wait_option,
680
+ download=download,
681
+ output_json=output_json,
682
+ jq_expr=jq_expr,
683
+ )
684
+ run_action(
685
+ ctx,
686
+ lambda: runtime.services().execute_image_vectorize(
687
+ image=image,
688
+ callback_url=callback_url,
689
+ execution=runtime.execution_options(),
690
+ ),
691
+ )
692
+
693
+
656
694
  @image_app.command("upscale")
657
695
  def image_upscale(
658
696
  ctx: typer.Context,
@@ -757,14 +795,12 @@ def video_generate(
757
795
  model: str = "ltx23",
758
796
  duration: Optional[int] = None,
759
797
  resolution: Optional[str] = None,
760
- aspect_ratio: Annotated[str, typer.Option("--aspect-ratio")] = "16:9",
761
- lora_high_url: Annotated[Optional[str], typer.Option("--lora-high-url")] = None,
762
- lora_low_url: Annotated[Optional[str], typer.Option("--lora-low-url")] = None,
798
+ aspect_ratio: Annotated[Optional[str], typer.Option("--aspect-ratio")] = None,
763
799
  reference_image: Annotated[
764
800
  list[str],
765
801
  typer.Option(
766
802
  "--reference-image",
767
- help="Wan or Veo reference image URL/local path. Can be repeated.",
803
+ help="MiniMax, Wan or Veo reference image URL/local path. Can be repeated.",
768
804
  ),
769
805
  ] = [],
770
806
  reference_video: Annotated[
@@ -801,8 +837,6 @@ def video_generate(
801
837
  duration=duration,
802
838
  resolution=resolution,
803
839
  aspect_ratio=aspect_ratio,
804
- lora_high_url=lora_high_url,
805
- lora_low_url=lora_low_url,
806
840
  reference_images=reference_image,
807
841
  reference_videos=reference_video,
808
842
  seed=seed,
@@ -895,8 +929,11 @@ def video_from_image(
895
929
  model: str = "ltx23",
896
930
  duration: Optional[int] = None,
897
931
  resolution: Optional[str] = None,
898
- aspect_ratio: Annotated[str, typer.Option("--aspect-ratio")] = "16:9",
932
+ aspect_ratio: Annotated[Optional[str], typer.Option("--aspect-ratio")] = None,
899
933
  seed: Optional[int] = None,
934
+ generate_audio: Annotated[
935
+ Optional[bool], typer.Option("--generate-audio/--no-generate-audio")
936
+ ] = None,
900
937
  callback_url: Annotated[Optional[str], typer.Option("--callback-url")] = None,
901
938
  wait_option: WaitOption = None,
902
939
  download: DownloadOption = None,
@@ -922,6 +959,7 @@ def video_from_image(
922
959
  aspect_ratio=aspect_ratio,
923
960
  image=image,
924
961
  seed=seed,
962
+ generate_audio=generate_audio,
925
963
  callback_url=callback_url,
926
964
  execution=runtime.execution_options(),
927
965
  ),
@@ -937,8 +975,11 @@ def video_first_last(
937
975
  model: str = "ltx23",
938
976
  duration: Optional[int] = None,
939
977
  resolution: Optional[str] = None,
940
- aspect_ratio: Annotated[str, typer.Option("--aspect-ratio")] = "16:9",
978
+ aspect_ratio: Annotated[Optional[str], typer.Option("--aspect-ratio")] = None,
941
979
  seed: Optional[int] = None,
980
+ generate_audio: Annotated[
981
+ Optional[bool], typer.Option("--generate-audio/--no-generate-audio")
982
+ ] = None,
942
983
  callback_url: Annotated[Optional[str], typer.Option("--callback-url")] = None,
943
984
  wait_option: WaitOption = None,
944
985
  download: DownloadOption = None,
@@ -965,6 +1006,7 @@ def video_first_last(
965
1006
  image=image,
966
1007
  last_image=last_image,
967
1008
  seed=seed,
1009
+ generate_audio=generate_audio,
968
1010
  callback_url=callback_url,
969
1011
  execution=runtime.execution_options(),
970
1012
  ),
@@ -976,11 +1018,15 @@ def video_upscale(
976
1018
  ctx: typer.Context,
977
1019
  video: Annotated[Optional[str], typer.Option("--video")] = None,
978
1020
  task_id: Annotated[Optional[str], typer.Option("--task-id")] = None,
1021
+ model: str = "seedvr2",
1022
+ callback_url: Annotated[Optional[str], typer.Option("--callback-url")] = None,
979
1023
  resolution: Annotated[
980
1024
  str,
981
1025
  typer.Option(
982
1026
  "--resolution",
983
- help="Output resolution: 720p, 1080p, or 1440p (up to 20s at 1440p).",
1027
+ help=(
1028
+ "720p, 1080p, 1440p, or 4k (Topaz only). SeedVR2: up to 20s at 1440p."
1029
+ ),
984
1030
  ),
985
1031
  ] = "1080p",
986
1032
  wait_option: WaitOption = None,
@@ -990,7 +1036,7 @@ def video_upscale(
990
1036
  ) -> None:
991
1037
  """Start video upscale from a URL/file or task_id.
992
1038
 
993
- Source videos longer than 20 seconds cannot be upscaled to 1440p.
1039
+ SeedVR2 source videos longer than 20 seconds cannot be upscaled to 1440p.
994
1040
  """
995
1041
 
996
1042
  runtime = apply_common_options(
@@ -1006,6 +1052,8 @@ def video_upscale(
1006
1052
  video=video,
1007
1053
  task_id=task_id,
1008
1054
  resolution=resolution,
1055
+ model=model,
1056
+ callback_url=callback_url,
1009
1057
  execution=runtime.execution_options(),
1010
1058
  ),
1011
1059
  )