everypixel-cli 0.5.0__tar.gz → 0.5.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.
Files changed (25) hide show
  1. {everypixel_cli-0.5.0 → everypixel_cli-0.5.1}/CHANGELOG.md +5 -0
  2. {everypixel_cli-0.5.0 → everypixel_cli-0.5.1}/PKG-INFO +24 -2
  3. {everypixel_cli-0.5.0 → everypixel_cli-0.5.1}/README.md +23 -1
  4. everypixel_cli-0.5.1/src/everypixel_cli/__init__.py +1 -0
  5. {everypixel_cli-0.5.0 → everypixel_cli-0.5.1}/src/everypixel_cli/application/models.py +2 -0
  6. {everypixel_cli-0.5.0 → everypixel_cli-0.5.1}/src/everypixel_cli/application/services.py +78 -1
  7. {everypixel_cli-0.5.0 → everypixel_cli-0.5.1}/src/everypixel_cli/cli.py +61 -1
  8. {everypixel_cli-0.5.0 → everypixel_cli-0.5.1}/src/everypixel_cli/resources/openapi.json +527 -24
  9. {everypixel_cli-0.5.0 → everypixel_cli-0.5.1}/src/everypixel_cli/schemas.py +53 -3
  10. everypixel_cli-0.5.0/src/everypixel_cli/__init__.py +0 -1
  11. {everypixel_cli-0.5.0 → everypixel_cli-0.5.1}/.gitignore +0 -0
  12. {everypixel_cli-0.5.0 → everypixel_cli-0.5.1}/LICENSE +0 -0
  13. {everypixel_cli-0.5.0 → everypixel_cli-0.5.1}/pyproject.toml +0 -0
  14. {everypixel_cli-0.5.0 → everypixel_cli-0.5.1}/src/everypixel_cli/__main__.py +0 -0
  15. {everypixel_cli-0.5.0 → everypixel_cli-0.5.1}/src/everypixel_cli/application/__init__.py +0 -0
  16. {everypixel_cli-0.5.0 → everypixel_cli-0.5.1}/src/everypixel_cli/application/chat.py +0 -0
  17. {everypixel_cli-0.5.0 → everypixel_cli-0.5.1}/src/everypixel_cli/application/serialization.py +0 -0
  18. {everypixel_cli-0.5.0 → everypixel_cli-0.5.1}/src/everypixel_cli/client.py +0 -0
  19. {everypixel_cli-0.5.0 → everypixel_cli-0.5.1}/src/everypixel_cli/config.py +0 -0
  20. {everypixel_cli-0.5.0 → everypixel_cli-0.5.1}/src/everypixel_cli/errors.py +0 -0
  21. {everypixel_cli-0.5.0 → everypixel_cli-0.5.1}/src/everypixel_cli/files.py +0 -0
  22. {everypixel_cli-0.5.0 → everypixel_cli-0.5.1}/src/everypixel_cli/mcp_server.py +0 -0
  23. {everypixel_cli-0.5.0 → everypixel_cli-0.5.1}/src/everypixel_cli/openapi.py +0 -0
  24. {everypixel_cli-0.5.0 → everypixel_cli-0.5.1}/src/everypixel_cli/output.py +0 -0
  25. {everypixel_cli-0.5.0 → everypixel_cli-0.5.1}/src/everypixel_cli/resources/__init__.py +0 -0
@@ -1,5 +1,10 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.5.1] - 2026-10-28
4
+
5
+ - Add account balance lookup and request cost estimation with `--estimate-cost`.
6
+ - Support Seedance generation with first/last frames or reference images.
7
+
3
8
  ## [0.5.0] - 2026-10-12
4
9
 
5
10
  - Add FLUX.3 images, Nano Banana 2.1, Nano Banana 2 Lite, and Seedream 5 Flash.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: everypixel-cli
3
- Version: 0.5.0
3
+ Version: 0.5.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
@@ -137,6 +137,26 @@ CLI falls back to its user configuration file when a keyring is unavailable;
137
137
 
138
138
  ## Commands
139
139
 
140
+ ### Balance and cost estimates
141
+
142
+ `everypixel balance` shows the account balance in USD and the owner's email.
143
+ The balance is settled; recent usage may not yet be reflected.
144
+
145
+ Add `--estimate-cost` to image, video, lipsync, transcription, or speech generation
146
+ commands to estimate the request's USD cost without creating a task or consuming
147
+ quota. Estimates include available free quota and personal rates; they do not
148
+ reserve funds. Task polling and downloads are skipped for estimate responses.
149
+
150
+ ```bash
151
+ everypixel balance --output-json
152
+ everypixel image generate "a lake" --estimate-cost
153
+ everypixel run image_generate --input prompt="a lake" --estimate-cost --output-json
154
+ ```
155
+
156
+ `run --estimate-cost` supports the same API operations, including background
157
+ removal, video matting, and video soundtrack generation. Chat, classic analysis,
158
+ and saved voice management endpoints do not support cost estimation.
159
+
140
160
  ### Images
141
161
 
142
162
  | Command | Description |
@@ -234,7 +254,9 @@ Input options depend on the model:
234
254
  - `first-last` accepts `--image` and `--last-image` for models that support frame pairs.
235
255
  - MiniMax and WAN 3.0 accept repeatable reference options, separately from frame inputs.
236
256
  Use `--reference-image`, `--reference-video`, and, for MiniMax, `--reference-audio`.
237
- - Gemini Omni generation accepts frame images or `--reference-image`, separately.
257
+ - Gemini Omni and Seedance generation accept frame images or `--reference-image`, separately.
258
+ Use `--model seedance2`, `seedance2-mini`, or `seedance2.5` with `from-image` or
259
+ `first-last`. Seedance 2.5 derives the aspect ratio from the first frame.
238
260
  - Grok Imagine 1.5 (`grok-imagine-1.5`) requires `--image` and supports 1080p.
239
261
  - Aleph 2 accepts repeatable `--keyframe` JSON; inspect `video edit --help` for its fields.
240
262
 
@@ -103,6 +103,26 @@ CLI falls back to its user configuration file when a keyring is unavailable;
103
103
 
104
104
  ## Commands
105
105
 
106
+ ### Balance and cost estimates
107
+
108
+ `everypixel balance` shows the account balance in USD and the owner's email.
109
+ The balance is settled; recent usage may not yet be reflected.
110
+
111
+ Add `--estimate-cost` to image, video, lipsync, transcription, or speech generation
112
+ commands to estimate the request's USD cost without creating a task or consuming
113
+ quota. Estimates include available free quota and personal rates; they do not
114
+ reserve funds. Task polling and downloads are skipped for estimate responses.
115
+
116
+ ```bash
117
+ everypixel balance --output-json
118
+ everypixel image generate "a lake" --estimate-cost
119
+ everypixel run image_generate --input prompt="a lake" --estimate-cost --output-json
120
+ ```
121
+
122
+ `run --estimate-cost` supports the same API operations, including background
123
+ removal, video matting, and video soundtrack generation. Chat, classic analysis,
124
+ and saved voice management endpoints do not support cost estimation.
125
+
106
126
  ### Images
107
127
 
108
128
  | Command | Description |
@@ -200,7 +220,9 @@ Input options depend on the model:
200
220
  - `first-last` accepts `--image` and `--last-image` for models that support frame pairs.
201
221
  - MiniMax and WAN 3.0 accept repeatable reference options, separately from frame inputs.
202
222
  Use `--reference-image`, `--reference-video`, and, for MiniMax, `--reference-audio`.
203
- - Gemini Omni generation accepts frame images or `--reference-image`, separately.
223
+ - Gemini Omni and Seedance generation accept frame images or `--reference-image`, separately.
224
+ Use `--model seedance2`, `seedance2-mini`, or `seedance2.5` with `from-image` or
225
+ `first-last`. Seedance 2.5 derives the aspect ratio from the first frame.
204
226
  - Grok Imagine 1.5 (`grok-imagine-1.5`) requires `--image` and supports 1080p.
205
227
  - Aleph 2 accepts repeatable `--keyframe` JSON; inspect `video edit --help` for its fields.
206
228
 
@@ -0,0 +1 @@
1
+ __version__ = "0.5.1"
@@ -26,6 +26,8 @@ class ExecutionOptions:
26
26
  compare=False,
27
27
  )
28
28
 
29
+ estimate_cost: bool = False
30
+
29
31
  @property
30
32
  def wait_for_result(self) -> bool:
31
33
  """Downloads always require a completed task."""
@@ -66,6 +66,43 @@ from .models import (
66
66
  )
67
67
 
68
68
 
69
+ COST_ESTIMATE_ENDPOINTS = frozenset(
70
+ f"/v1/{name}"
71
+ for name in (
72
+ "image_vectorize",
73
+ "image_generate",
74
+ "image_upscale",
75
+ "image_remove_background",
76
+ "image_edit",
77
+ "image_edit_angles",
78
+ "image_edit_colors",
79
+ "video_lipsync",
80
+ "image_lipsync",
81
+ "video_generate",
82
+ "video_edit",
83
+ "video_upscale",
84
+ "video_matting",
85
+ "video_soundtrack",
86
+ "tts_design",
87
+ "tts_clone",
88
+ "tts_create",
89
+ "transcribe",
90
+ )
91
+ )
92
+
93
+
94
+ def estimate_cost_params(
95
+ endpoint: str, method: str, execution: ExecutionOptions
96
+ ) -> dict[str, Any]:
97
+ """Keep the quote flag in the query and reject unsupported operations."""
98
+
99
+ if not execution.estimate_cost:
100
+ return {}
101
+ if method.upper() != "POST" or endpoint not in COST_ESTIMATE_ENDPOINTS:
102
+ raise ValidationCLIError("estimate_cost is not supported for this endpoint")
103
+ return {"estimate_cost": 1}
104
+
105
+
69
106
  _VIDEO_GENERATE_ADAPTER: TypeAdapter[VideoGenerateRequest] = TypeAdapter(
70
107
  VideoGenerateRequest
71
108
  )
@@ -256,11 +293,14 @@ class OperationService:
256
293
  use_query = method == "GET" or (
257
294
  method == "DELETE" and request.endpoint == "/v1/tts_voice"
258
295
  )
296
+ params = estimate_cost_params(request.endpoint, method, request.execution)
297
+ if use_query:
298
+ params.update(payload)
259
299
  response = self.client.request(
260
300
  method,
261
301
  request.endpoint,
262
302
  json=None if use_query else payload,
263
- params=payload if use_query else None,
303
+ params=params or None,
264
304
  )
265
305
  if not isinstance(response, Mapping) or "task_id" not in response:
266
306
  return OperationResult(value=response)
@@ -687,6 +727,30 @@ def build_image_upscale_payload(**values: Any) -> dict[str, Any]:
687
727
  ).model_dump(exclude_none=True)
688
728
 
689
729
 
730
+ def _build_seedance_video_generate_payload(payload: dict[str, Any]) -> dict[str, Any]:
731
+ """Translate CLI frame and reference options into Seedance image content."""
732
+
733
+ content = []
734
+ for key, role in (("image_url", "first_frame"), ("image_last_url", "last_frame")):
735
+ url = payload.pop(key, None)
736
+ if url is not None:
737
+ content.append(
738
+ {"type": "image_url", "role": role, "image_url": {"url": url}}
739
+ )
740
+ for url in payload.pop("reference_image_urls", []):
741
+ content.append(
742
+ {"type": "image_url", "role": "reference_image", "image_url": {"url": url}}
743
+ )
744
+ return {**payload, "content": content}
745
+
746
+
747
+ _VIDEO_GENERATE_BUILDERS = {
748
+ "seedance2": _build_seedance_video_generate_payload,
749
+ "seedance2-mini": _build_seedance_video_generate_payload,
750
+ "seedance2.5": _build_seedance_video_generate_payload,
751
+ }
752
+
753
+
690
754
  def build_video_generate_payload(**values: Any) -> dict[str, Any]:
691
755
  """Build text/image video generation payloads with typed model validation."""
692
756
 
@@ -725,6 +789,10 @@ def build_video_generate_payload(**values: Any) -> dict[str, Any]:
725
789
  payload["reference_audio_urls"] = [
726
790
  media_value(value) for value in reference_audios
727
791
  ]
792
+ model = payload.get("model")
793
+ builder = _VIDEO_GENERATE_BUILDERS.get(model) if isinstance(model, str) else None
794
+ if builder is not None:
795
+ payload = builder(payload)
728
796
  validated = _VIDEO_GENERATE_ADAPTER.validate_python(
729
797
  {key: value for key, value in payload.items() if value is not None}
730
798
  )
@@ -1498,12 +1566,14 @@ class ApplicationServices:
1498
1566
  if path == "/v1/tts_voices" and resolved_method == "GET":
1499
1567
  TTSVoicesPayload.model_validate(payload)
1500
1568
  validate_payload_against_operation(operation, payload)
1569
+ params = estimate_cost_params(path, resolved_method, execution)
1501
1570
  if dry_run:
1502
1571
  return OperationResult(
1503
1572
  value={
1504
1573
  "method": resolved_method,
1505
1574
  "path": path,
1506
1575
  "body": payload,
1576
+ **({"params": params} if params else {}),
1507
1577
  "headers": {
1508
1578
  "Authorization": "Basic ***" if client_id_present else None
1509
1579
  },
@@ -1586,6 +1656,13 @@ class ApplicationServices:
1586
1656
  def execute_video_keywords(self, *, video: str) -> OperationResult:
1587
1657
  return self._execute_classic(path="/v1/video_keywords", media=video)
1588
1658
 
1659
+ def get_balance(self) -> OperationResult:
1660
+ """Return the authenticated owner's settled USD balance."""
1661
+
1662
+ return self._operations.execute(
1663
+ OperationRequest(endpoint="/v1/account/balance", method="GET", payload={})
1664
+ )
1665
+
1589
1666
  def check_auth(self) -> OperationResult:
1590
1667
  """Run an authentication check without a CLI dependency."""
1591
1668
 
@@ -162,6 +162,10 @@ def positive_float(value: float) -> float:
162
162
  WaitOption = Annotated[
163
163
  Optional[bool], typer.Option("--wait/--no-wait", help="Override wait mode.")
164
164
  ]
165
+ EstimateCostOption = Annotated[
166
+ bool,
167
+ typer.Option("--estimate-cost", help="Estimate USD cost without creating a task."),
168
+ ]
165
169
  DownloadOption = Annotated[
166
170
  Optional[Path],
167
171
  typer.Option("--download", "-d", help="Download result URLs into a directory."),
@@ -190,6 +194,7 @@ class Runtime:
190
194
  download: Path | None
191
195
  no_color: bool
192
196
  debug: bool
197
+ estimate_cost: bool = False
193
198
  _services: ApplicationServices | None = field(
194
199
  default=None,
195
200
  init=False,
@@ -223,6 +228,7 @@ class Runtime:
223
228
 
224
229
  return ExecutionOptions(
225
230
  wait=self.wait if wait is None else wait,
231
+ estimate_cost=self.estimate_cost,
226
232
  download_directory=self.download,
227
233
  timeout=self.timeout,
228
234
  poll_interval=self.poll_interval,
@@ -424,10 +430,13 @@ def apply_common_options(
424
430
  download: Path | None = None,
425
431
  output_json: bool = False,
426
432
  jq_expr: str | None = None,
433
+ estimate_cost: bool = False,
427
434
  ) -> Runtime:
428
435
  """Apply command-local common flags on top of global runtime settings."""
429
436
 
430
437
  runtime = get_runtime(ctx)
438
+ if estimate_cost:
439
+ runtime.estimate_cost = True
431
440
  if output_json or jq_expr is not None:
432
441
  runtime.output_json = True
433
442
  if jq_expr is not None:
@@ -679,6 +688,7 @@ def image_generate(
679
688
  ] = None,
680
689
  seed: Annotated[int, typer.Option("--seed")] = -1,
681
690
  callback_url: Annotated[Optional[str], typer.Option("--callback-url")] = None,
691
+ estimate_cost: EstimateCostOption = False,
682
692
  wait_option: WaitOption = None,
683
693
  download: DownloadOption = None,
684
694
  output_json: OutputJsonOption = False,
@@ -688,6 +698,7 @@ def image_generate(
688
698
 
689
699
  runtime = apply_common_options(
690
700
  ctx,
701
+ estimate_cost=estimate_cost,
691
702
  wait_option=wait_option,
692
703
  download=download,
693
704
  output_json=output_json,
@@ -733,6 +744,7 @@ def image_edit(
733
744
  ] = None,
734
745
  seed: Annotated[int, typer.Option("--seed")] = -1,
735
746
  callback_url: Annotated[Optional[str], typer.Option("--callback-url")] = None,
747
+ estimate_cost: EstimateCostOption = False,
736
748
  wait_option: WaitOption = None,
737
749
  download: DownloadOption = None,
738
750
  output_json: OutputJsonOption = False,
@@ -742,6 +754,7 @@ def image_edit(
742
754
 
743
755
  runtime = apply_common_options(
744
756
  ctx,
757
+ estimate_cost=estimate_cost,
745
758
  wait_option=wait_option,
746
759
  download=download,
747
760
  output_json=output_json,
@@ -769,6 +782,7 @@ def image_vectorize(
769
782
  ctx: typer.Context,
770
783
  image: Annotated[str, typer.Argument(help="Image URL or local path.")],
771
784
  callback_url: Annotated[Optional[str], typer.Option("--callback-url")] = None,
785
+ estimate_cost: EstimateCostOption = False,
772
786
  wait_option: WaitOption = None,
773
787
  download: DownloadOption = None,
774
788
  output_json: OutputJsonOption = False,
@@ -778,6 +792,7 @@ def image_vectorize(
778
792
 
779
793
  runtime = apply_common_options(
780
794
  ctx,
795
+ estimate_cost=estimate_cost,
781
796
  wait_option=wait_option,
782
797
  download=download,
783
798
  output_json=output_json,
@@ -800,6 +815,7 @@ def image_upscale(
800
815
  task_id: Annotated[Optional[str], typer.Option("--task-id")] = None,
801
816
  model: Annotated[str, typer.Option("--model")] = "seedvr2",
802
817
  callback_url: Annotated[Optional[str], typer.Option("--callback-url")] = None,
818
+ estimate_cost: EstimateCostOption = False,
803
819
  wait_option: WaitOption = None,
804
820
  download: DownloadOption = None,
805
821
  output_json: OutputJsonOption = False,
@@ -809,6 +825,7 @@ def image_upscale(
809
825
 
810
826
  runtime = apply_common_options(
811
827
  ctx,
828
+ estimate_cost=estimate_cost,
812
829
  wait_option=wait_option,
813
830
  download=download,
814
831
  output_json=output_json,
@@ -834,6 +851,7 @@ def image_angles(
834
851
  elevation: str = "eye_level",
835
852
  distance: str = "medium",
836
853
  prompt: Optional[str] = None,
854
+ estimate_cost: EstimateCostOption = False,
837
855
  wait_option: WaitOption = None,
838
856
  download: DownloadOption = None,
839
857
  output_json: OutputJsonOption = False,
@@ -843,6 +861,7 @@ def image_angles(
843
861
 
844
862
  runtime = apply_common_options(
845
863
  ctx,
864
+ estimate_cost=estimate_cost,
846
865
  wait_option=wait_option,
847
866
  download=download,
848
867
  output_json=output_json,
@@ -866,6 +885,7 @@ def image_colors(
866
885
  ctx: typer.Context,
867
886
  image: Annotated[str, typer.Option("--image")],
868
887
  reference: Annotated[str, typer.Option("--reference")],
888
+ estimate_cost: EstimateCostOption = False,
869
889
  wait_option: WaitOption = None,
870
890
  download: DownloadOption = None,
871
891
  output_json: OutputJsonOption = False,
@@ -875,6 +895,7 @@ def image_colors(
875
895
 
876
896
  runtime = apply_common_options(
877
897
  ctx,
898
+ estimate_cost=estimate_cost,
878
899
  wait_option=wait_option,
879
900
  download=download,
880
901
  output_json=output_json,
@@ -902,7 +923,10 @@ def video_generate(
902
923
  list[str],
903
924
  typer.Option(
904
925
  "--reference-image",
905
- help="MiniMax, Wan or Omni reference image URL/path. Can be repeated.",
926
+ help=(
927
+ "MiniMax, Wan, Omni or Seedance reference image URL/path. "
928
+ "Can be repeated."
929
+ ),
906
930
  ),
907
931
  ] = [],
908
932
  reference_video: Annotated[
@@ -924,6 +948,7 @@ def video_generate(
924
948
  Optional[bool], typer.Option("--generate-audio/--no-generate-audio")
925
949
  ] = None,
926
950
  callback_url: Annotated[Optional[str], typer.Option("--callback-url")] = None,
951
+ estimate_cost: EstimateCostOption = False,
927
952
  wait_option: WaitOption = None,
928
953
  download: DownloadOption = None,
929
954
  output_json: OutputJsonOption = False,
@@ -933,6 +958,7 @@ def video_generate(
933
958
 
934
959
  runtime = apply_common_options(
935
960
  ctx,
961
+ estimate_cost=estimate_cost,
936
962
  wait_option=wait_option,
937
963
  download=download,
938
964
  output_json=output_json,
@@ -1002,6 +1028,7 @@ def video_edit(
1002
1028
  Optional[bool], typer.Option("--generate-audio/--no-generate-audio")
1003
1029
  ] = None,
1004
1030
  callback_url: Annotated[Optional[str], typer.Option("--callback-url")] = None,
1031
+ estimate_cost: EstimateCostOption = False,
1005
1032
  wait_option: WaitOption = None,
1006
1033
  download: DownloadOption = None,
1007
1034
  output_json: OutputJsonOption = False,
@@ -1011,6 +1038,7 @@ def video_edit(
1011
1038
 
1012
1039
  runtime = apply_common_options(
1013
1040
  ctx,
1041
+ estimate_cost=estimate_cost,
1014
1042
  wait_option=wait_option,
1015
1043
  download=download,
1016
1044
  output_json=output_json,
@@ -1052,6 +1080,7 @@ def video_from_image(
1052
1080
  Optional[bool], typer.Option("--generate-audio/--no-generate-audio")
1053
1081
  ] = None,
1054
1082
  callback_url: Annotated[Optional[str], typer.Option("--callback-url")] = None,
1083
+ estimate_cost: EstimateCostOption = False,
1055
1084
  wait_option: WaitOption = None,
1056
1085
  download: DownloadOption = None,
1057
1086
  output_json: OutputJsonOption = False,
@@ -1061,6 +1090,7 @@ def video_from_image(
1061
1090
 
1062
1091
  runtime = apply_common_options(
1063
1092
  ctx,
1093
+ estimate_cost=estimate_cost,
1064
1094
  wait_option=wait_option,
1065
1095
  download=download,
1066
1096
  output_json=output_json,
@@ -1098,6 +1128,7 @@ def video_first_last(
1098
1128
  Optional[bool], typer.Option("--generate-audio/--no-generate-audio")
1099
1129
  ] = None,
1100
1130
  callback_url: Annotated[Optional[str], typer.Option("--callback-url")] = None,
1131
+ estimate_cost: EstimateCostOption = False,
1101
1132
  wait_option: WaitOption = None,
1102
1133
  download: DownloadOption = None,
1103
1134
  output_json: OutputJsonOption = False,
@@ -1107,6 +1138,7 @@ def video_first_last(
1107
1138
 
1108
1139
  runtime = apply_common_options(
1109
1140
  ctx,
1141
+ estimate_cost=estimate_cost,
1110
1142
  wait_option=wait_option,
1111
1143
  download=download,
1112
1144
  output_json=output_json,
@@ -1147,6 +1179,7 @@ def video_upscale(
1147
1179
  ),
1148
1180
  ),
1149
1181
  ] = "1080p",
1182
+ estimate_cost: EstimateCostOption = False,
1150
1183
  wait_option: WaitOption = None,
1151
1184
  download: DownloadOption = None,
1152
1185
  output_json: OutputJsonOption = False,
@@ -1160,6 +1193,7 @@ def video_upscale(
1160
1193
 
1161
1194
  runtime = apply_common_options(
1162
1195
  ctx,
1196
+ estimate_cost=estimate_cost,
1163
1197
  wait_option=wait_option,
1164
1198
  download=download,
1165
1199
  output_json=output_json,
@@ -1187,6 +1221,7 @@ def lipsync_video(
1187
1221
  prompt: Optional[str] = None,
1188
1222
  seed: int = -1,
1189
1223
  callback_url: Annotated[Optional[str], typer.Option("--callback-url")] = None,
1224
+ estimate_cost: EstimateCostOption = False,
1190
1225
  wait_option: WaitOption = None,
1191
1226
  download: DownloadOption = None,
1192
1227
  output_json: OutputJsonOption = False,
@@ -1196,6 +1231,7 @@ def lipsync_video(
1196
1231
 
1197
1232
  runtime = apply_common_options(
1198
1233
  ctx,
1234
+ estimate_cost=estimate_cost,
1199
1235
  wait_option=wait_option,
1200
1236
  download=download,
1201
1237
  output_json=output_json,
@@ -1225,6 +1261,7 @@ def lipsync_image(
1225
1261
  prompt: Optional[str] = None,
1226
1262
  seed: int = -1,
1227
1263
  callback_url: Annotated[Optional[str], typer.Option("--callback-url")] = None,
1264
+ estimate_cost: EstimateCostOption = False,
1228
1265
  wait_option: WaitOption = None,
1229
1266
  download: DownloadOption = None,
1230
1267
  output_json: OutputJsonOption = False,
@@ -1234,6 +1271,7 @@ def lipsync_image(
1234
1271
 
1235
1272
  runtime = apply_common_options(
1236
1273
  ctx,
1274
+ estimate_cost=estimate_cost,
1237
1275
  wait_option=wait_option,
1238
1276
  download=download,
1239
1277
  output_json=output_json,
@@ -1261,6 +1299,7 @@ def audio_transcribe(
1261
1299
  language: str = "auto",
1262
1300
  hints: str = "",
1263
1301
  denoise: bool = True,
1302
+ estimate_cost: EstimateCostOption = False,
1264
1303
  wait_option: WaitOption = None,
1265
1304
  download: DownloadOption = None,
1266
1305
  output_json: OutputJsonOption = False,
@@ -1270,6 +1309,7 @@ def audio_transcribe(
1270
1309
 
1271
1310
  runtime = apply_common_options(
1272
1311
  ctx,
1312
+ estimate_cost=estimate_cost,
1273
1313
  wait_option=wait_option,
1274
1314
  download=download,
1275
1315
  output_json=output_json,
@@ -1299,6 +1339,7 @@ def tts_create(
1299
1339
  language: Optional[str] = None,
1300
1340
  prompt: Optional[str] = None,
1301
1341
  seed: Optional[int] = None,
1342
+ estimate_cost: EstimateCostOption = False,
1302
1343
  wait_option: WaitOption = None,
1303
1344
  download: DownloadOption = None,
1304
1345
  output_json: OutputJsonOption = False,
@@ -1308,6 +1349,7 @@ def tts_create(
1308
1349
 
1309
1350
  runtime = apply_common_options(
1310
1351
  ctx,
1352
+ estimate_cost=estimate_cost,
1311
1353
  wait_option=wait_option,
1312
1354
  download=download,
1313
1355
  output_json=output_json,
@@ -1340,6 +1382,7 @@ def tts_clone(
1340
1382
  model: str = "qwen3",
1341
1383
  language: Optional[str] = None,
1342
1384
  seed: Optional[int] = None,
1385
+ estimate_cost: EstimateCostOption = False,
1343
1386
  wait_option: WaitOption = None,
1344
1387
  download: DownloadOption = None,
1345
1388
  output_json: OutputJsonOption = False,
@@ -1349,6 +1392,7 @@ def tts_clone(
1349
1392
 
1350
1393
  runtime = apply_common_options(
1351
1394
  ctx,
1395
+ estimate_cost=estimate_cost,
1352
1396
  wait_option=wait_option,
1353
1397
  download=download,
1354
1398
  output_json=output_json,
@@ -1380,6 +1424,7 @@ def tts_design(
1380
1424
  language: Optional[str] = None,
1381
1425
  prompt: Optional[str] = None,
1382
1426
  seed: Optional[int] = None,
1427
+ estimate_cost: EstimateCostOption = False,
1383
1428
  wait_option: WaitOption = None,
1384
1429
  download: DownloadOption = None,
1385
1430
  output_json: OutputJsonOption = False,
@@ -1389,6 +1434,7 @@ def tts_design(
1389
1434
 
1390
1435
  runtime = apply_common_options(
1391
1436
  ctx,
1437
+ estimate_cost=estimate_cost,
1392
1438
  wait_option=wait_option,
1393
1439
  download=download,
1394
1440
  output_json=output_json,
@@ -1468,6 +1514,18 @@ def tts_delete(
1468
1514
  run_action(ctx, lambda: runtime.services().delete_tts_voice(voice_id=voice_id))
1469
1515
 
1470
1516
 
1517
+ @app.command("balance")
1518
+ def balance(
1519
+ ctx: typer.Context,
1520
+ output_json: OutputJsonOption = False,
1521
+ jq_expr: JqOption = None,
1522
+ ) -> None:
1523
+ """Show the account balance in USD and the owner's email."""
1524
+
1525
+ runtime = apply_common_options(ctx, output_json=output_json, jq_expr=jq_expr)
1526
+ run_action(ctx, lambda: runtime.services().get_balance())
1527
+
1528
+
1471
1529
  @app.command("status")
1472
1530
  def status(
1473
1531
  ctx: typer.Context,
@@ -1631,6 +1689,7 @@ def generic_run(
1631
1689
  method: Annotated[Optional[str], typer.Option("--method")] = None,
1632
1690
  dry_run: Annotated[bool, typer.Option("--dry-run")] = False,
1633
1691
  help_schema: Annotated[bool, typer.Option("--help-schema")] = False,
1692
+ estimate_cost: EstimateCostOption = False,
1634
1693
  wait_option: WaitOption = None,
1635
1694
  download: DownloadOption = None,
1636
1695
  output_json: OutputJsonOption = False,
@@ -1640,6 +1699,7 @@ def generic_run(
1640
1699
 
1641
1700
  runtime = apply_common_options(
1642
1701
  ctx,
1702
+ estimate_cost=estimate_cost,
1643
1703
  wait_option=wait_option,
1644
1704
  download=download,
1645
1705
  output_json=output_json,