together 1.5.34__py3-none-any.whl → 2.0.0a6__py3-none-any.whl

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 (208) hide show
  1. together/__init__.py +101 -114
  2. together/_base_client.py +1995 -0
  3. together/_client.py +1033 -0
  4. together/_compat.py +219 -0
  5. together/_constants.py +14 -0
  6. together/_exceptions.py +108 -0
  7. together/_files.py +123 -0
  8. together/_models.py +857 -0
  9. together/_qs.py +150 -0
  10. together/_resource.py +43 -0
  11. together/_response.py +830 -0
  12. together/_streaming.py +370 -0
  13. together/_types.py +260 -0
  14. together/_utils/__init__.py +64 -0
  15. together/_utils/_compat.py +45 -0
  16. together/_utils/_datetime_parse.py +136 -0
  17. together/_utils/_logs.py +25 -0
  18. together/_utils/_proxy.py +65 -0
  19. together/_utils/_reflection.py +42 -0
  20. together/_utils/_resources_proxy.py +24 -0
  21. together/_utils/_streams.py +12 -0
  22. together/_utils/_sync.py +58 -0
  23. together/_utils/_transform.py +457 -0
  24. together/_utils/_typing.py +156 -0
  25. together/_utils/_utils.py +421 -0
  26. together/_version.py +4 -0
  27. together/lib/.keep +4 -0
  28. together/lib/__init__.py +23 -0
  29. together/{cli → lib/cli}/api/endpoints.py +65 -81
  30. together/{cli/api/evaluation.py → lib/cli/api/evals.py} +152 -43
  31. together/{cli → lib/cli}/api/files.py +20 -17
  32. together/{cli/api/finetune.py → lib/cli/api/fine_tuning.py} +116 -172
  33. together/{cli → lib/cli}/api/models.py +34 -27
  34. together/lib/cli/api/utils.py +50 -0
  35. together/{cli → lib/cli}/cli.py +16 -26
  36. together/{constants.py → lib/constants.py} +11 -24
  37. together/lib/resources/__init__.py +11 -0
  38. together/lib/resources/files.py +999 -0
  39. together/lib/resources/fine_tuning.py +280 -0
  40. together/lib/resources/models.py +35 -0
  41. together/lib/types/__init__.py +13 -0
  42. together/lib/types/error.py +9 -0
  43. together/lib/types/fine_tuning.py +397 -0
  44. together/{utils → lib/utils}/__init__.py +6 -14
  45. together/{utils → lib/utils}/_log.py +11 -16
  46. together/{utils → lib/utils}/files.py +90 -288
  47. together/lib/utils/serializer.py +10 -0
  48. together/{utils → lib/utils}/tools.py +19 -55
  49. together/resources/__init__.py +225 -39
  50. together/resources/audio/__init__.py +72 -48
  51. together/resources/audio/audio.py +198 -0
  52. together/resources/audio/speech.py +574 -128
  53. together/resources/audio/transcriptions.py +247 -261
  54. together/resources/audio/translations.py +221 -241
  55. together/resources/audio/voices.py +111 -41
  56. together/resources/batches.py +417 -0
  57. together/resources/chat/__init__.py +30 -21
  58. together/resources/chat/chat.py +102 -0
  59. together/resources/chat/completions.py +1063 -263
  60. together/resources/code_interpreter/__init__.py +33 -0
  61. together/resources/code_interpreter/code_interpreter.py +258 -0
  62. together/resources/code_interpreter/sessions.py +135 -0
  63. together/resources/completions.py +884 -225
  64. together/resources/embeddings.py +172 -68
  65. together/resources/endpoints.py +589 -477
  66. together/resources/evals.py +452 -0
  67. together/resources/files.py +397 -129
  68. together/resources/fine_tuning.py +1033 -0
  69. together/resources/hardware.py +181 -0
  70. together/resources/images.py +258 -104
  71. together/resources/jobs.py +214 -0
  72. together/resources/models.py +223 -193
  73. together/resources/rerank.py +190 -92
  74. together/resources/videos.py +286 -214
  75. together/types/__init__.py +66 -167
  76. together/types/audio/__init__.py +10 -0
  77. together/types/audio/speech_create_params.py +75 -0
  78. together/types/audio/transcription_create_params.py +54 -0
  79. together/types/audio/transcription_create_response.py +111 -0
  80. together/types/audio/translation_create_params.py +40 -0
  81. together/types/audio/translation_create_response.py +70 -0
  82. together/types/audio/voice_list_response.py +23 -0
  83. together/types/audio_speech_stream_chunk.py +16 -0
  84. together/types/autoscaling.py +13 -0
  85. together/types/autoscaling_param.py +15 -0
  86. together/types/batch_create_params.py +24 -0
  87. together/types/batch_create_response.py +14 -0
  88. together/types/batch_job.py +45 -0
  89. together/types/batch_list_response.py +10 -0
  90. together/types/chat/__init__.py +18 -0
  91. together/types/chat/chat_completion.py +60 -0
  92. together/types/chat/chat_completion_chunk.py +61 -0
  93. together/types/chat/chat_completion_structured_message_image_url_param.py +18 -0
  94. together/types/chat/chat_completion_structured_message_text_param.py +13 -0
  95. together/types/chat/chat_completion_structured_message_video_url_param.py +18 -0
  96. together/types/chat/chat_completion_usage.py +13 -0
  97. together/types/chat/chat_completion_warning.py +9 -0
  98. together/types/chat/completion_create_params.py +329 -0
  99. together/types/code_interpreter/__init__.py +5 -0
  100. together/types/code_interpreter/session_list_response.py +31 -0
  101. together/types/code_interpreter_execute_params.py +45 -0
  102. together/types/completion.py +42 -0
  103. together/types/completion_chunk.py +66 -0
  104. together/types/completion_create_params.py +138 -0
  105. together/types/dedicated_endpoint.py +44 -0
  106. together/types/embedding.py +24 -0
  107. together/types/embedding_create_params.py +31 -0
  108. together/types/endpoint_create_params.py +43 -0
  109. together/types/endpoint_list_avzones_response.py +11 -0
  110. together/types/endpoint_list_params.py +18 -0
  111. together/types/endpoint_list_response.py +41 -0
  112. together/types/endpoint_update_params.py +27 -0
  113. together/types/eval_create_params.py +263 -0
  114. together/types/eval_create_response.py +16 -0
  115. together/types/eval_list_params.py +21 -0
  116. together/types/eval_list_response.py +10 -0
  117. together/types/eval_status_response.py +100 -0
  118. together/types/evaluation_job.py +139 -0
  119. together/types/execute_response.py +108 -0
  120. together/types/file_delete_response.py +13 -0
  121. together/types/file_list.py +12 -0
  122. together/types/file_purpose.py +9 -0
  123. together/types/file_response.py +31 -0
  124. together/types/file_type.py +7 -0
  125. together/types/fine_tuning_cancel_response.py +194 -0
  126. together/types/fine_tuning_content_params.py +24 -0
  127. together/types/fine_tuning_delete_params.py +11 -0
  128. together/types/fine_tuning_delete_response.py +12 -0
  129. together/types/fine_tuning_list_checkpoints_response.py +21 -0
  130. together/types/fine_tuning_list_events_response.py +12 -0
  131. together/types/fine_tuning_list_response.py +199 -0
  132. together/types/finetune_event.py +41 -0
  133. together/types/finetune_event_type.py +33 -0
  134. together/types/finetune_response.py +177 -0
  135. together/types/hardware_list_params.py +16 -0
  136. together/types/hardware_list_response.py +58 -0
  137. together/types/image_data_b64.py +15 -0
  138. together/types/image_data_url.py +15 -0
  139. together/types/image_file.py +23 -0
  140. together/types/image_generate_params.py +85 -0
  141. together/types/job_list_response.py +47 -0
  142. together/types/job_retrieve_response.py +43 -0
  143. together/types/log_probs.py +18 -0
  144. together/types/model_list_response.py +10 -0
  145. together/types/model_object.py +42 -0
  146. together/types/model_upload_params.py +36 -0
  147. together/types/model_upload_response.py +23 -0
  148. together/types/rerank_create_params.py +36 -0
  149. together/types/rerank_create_response.py +36 -0
  150. together/types/tool_choice.py +23 -0
  151. together/types/tool_choice_param.py +23 -0
  152. together/types/tools_param.py +23 -0
  153. together/types/training_method_dpo.py +22 -0
  154. together/types/training_method_sft.py +18 -0
  155. together/types/video_create_params.py +86 -0
  156. together/types/video_create_response.py +10 -0
  157. together/types/video_job.py +57 -0
  158. together-2.0.0a6.dist-info/METADATA +729 -0
  159. together-2.0.0a6.dist-info/RECORD +165 -0
  160. {together-1.5.34.dist-info → together-2.0.0a6.dist-info}/WHEEL +1 -1
  161. together-2.0.0a6.dist-info/entry_points.txt +2 -0
  162. {together-1.5.34.dist-info → together-2.0.0a6.dist-info}/licenses/LICENSE +1 -1
  163. together/abstract/api_requestor.py +0 -770
  164. together/cli/api/chat.py +0 -298
  165. together/cli/api/completions.py +0 -119
  166. together/cli/api/images.py +0 -93
  167. together/cli/api/utils.py +0 -139
  168. together/client.py +0 -186
  169. together/error.py +0 -194
  170. together/filemanager.py +0 -635
  171. together/legacy/__init__.py +0 -0
  172. together/legacy/base.py +0 -27
  173. together/legacy/complete.py +0 -93
  174. together/legacy/embeddings.py +0 -27
  175. together/legacy/files.py +0 -146
  176. together/legacy/finetune.py +0 -177
  177. together/legacy/images.py +0 -27
  178. together/legacy/models.py +0 -44
  179. together/resources/batch.py +0 -165
  180. together/resources/code_interpreter.py +0 -82
  181. together/resources/evaluation.py +0 -808
  182. together/resources/finetune.py +0 -1388
  183. together/together_response.py +0 -50
  184. together/types/abstract.py +0 -26
  185. together/types/audio_speech.py +0 -311
  186. together/types/batch.py +0 -54
  187. together/types/chat_completions.py +0 -210
  188. together/types/code_interpreter.py +0 -57
  189. together/types/common.py +0 -67
  190. together/types/completions.py +0 -107
  191. together/types/embeddings.py +0 -35
  192. together/types/endpoints.py +0 -123
  193. together/types/error.py +0 -16
  194. together/types/evaluation.py +0 -93
  195. together/types/files.py +0 -93
  196. together/types/finetune.py +0 -464
  197. together/types/images.py +0 -42
  198. together/types/models.py +0 -96
  199. together/types/rerank.py +0 -43
  200. together/types/videos.py +0 -69
  201. together/utils/api_helpers.py +0 -124
  202. together/version.py +0 -6
  203. together-1.5.34.dist-info/METADATA +0 -583
  204. together-1.5.34.dist-info/RECORD +0 -77
  205. together-1.5.34.dist-info/entry_points.txt +0 -3
  206. /together/{abstract → lib/cli}/__init__.py +0 -0
  207. /together/{cli → lib/cli/api}/__init__.py +0 -0
  208. /together/{cli/api/__init__.py → py.typed} +0 -0
@@ -1,34 +1,30 @@
1
1
  from __future__ import annotations
2
2
 
3
- import json
4
3
  import sys
4
+ import json
5
+ from typing import Any, Dict, Literal, TypeVar, Callable, cast
5
6
  from functools import wraps
6
- from typing import Any, Callable, Dict, List, Literal, TypeVar, Union
7
7
 
8
8
  import click
9
9
 
10
- from together import Together
11
- from together.error import InvalidRequestError
12
- from together.types import DedicatedEndpoint, ListEndpoint
10
+ from together import Together, omit
11
+ from together.types import DedicatedEndpoint
12
+ from together._exceptions import APIError
13
+ from together.lib.utils.serializer import datetime_serializer
14
+ from together.types.endpoint_list_response import Data as DedicatedEndpointListItem
13
15
 
14
16
 
15
- def print_endpoint(
16
- endpoint: Union[DedicatedEndpoint, ListEndpoint],
17
- ) -> None:
17
+ def print_endpoint(endpoint: DedicatedEndpoint | DedicatedEndpointListItem) -> None:
18
18
  """Print endpoint details in a Docker-like format or JSON."""
19
19
 
20
20
  # Print header info
21
21
  click.echo(f"ID:\t\t{endpoint.id}")
22
22
  click.echo(f"Name:\t\t{endpoint.name}")
23
23
 
24
- # Print type-specific fields
25
24
  if isinstance(endpoint, DedicatedEndpoint):
26
25
  click.echo(f"Display Name:\t{endpoint.display_name}")
27
26
  click.echo(f"Hardware:\t{endpoint.hardware}")
28
- click.echo(
29
- f"Autoscaling:\tMin={endpoint.autoscaling.min_replicas}, "
30
- f"Max={endpoint.autoscaling.max_replicas}"
31
- )
27
+ click.echo(f"Autoscaling:\tMin={endpoint.autoscaling.min_replicas}, Max={endpoint.autoscaling.max_replicas}")
32
28
 
33
29
  click.echo(f"Model:\t\t{endpoint.model}")
34
30
  click.echo(f"Type:\t\t{endpoint.type}")
@@ -40,15 +36,10 @@ def print_endpoint(
40
36
  F = TypeVar("F", bound=Callable[..., Any])
41
37
 
42
38
 
43
- def print_api_error(
44
- e: InvalidRequestError,
45
- ) -> None:
46
- error_details = e.api_response.message
39
+ def print_api_error(e: APIError) -> None:
40
+ error_details = cast(Dict[str, Any], e.body)["error"]["message"]
47
41
 
48
- if error_details and (
49
- "credentials" in error_details.lower()
50
- or "authentication" in error_details.lower()
51
- ):
42
+ if error_details and ("credentials" in error_details.lower() or "authentication" in error_details.lower()):
52
43
  click.echo("Error: Invalid API key or authentication failed", err=True)
53
44
  else:
54
45
  click.echo(f"Error: {error_details}", err=True)
@@ -61,7 +52,7 @@ def handle_api_errors(f: F) -> F:
61
52
  def wrapper(*args: Any, **kwargs: Any) -> Any:
62
53
  try:
63
54
  return f(*args, **kwargs)
64
- except InvalidRequestError as e:
55
+ except APIError as e:
65
56
  print_api_error(e)
66
57
  sys.exit(1)
67
58
  except Exception as e:
@@ -137,7 +128,8 @@ def endpoints(ctx: click.Context) -> None:
137
128
  help="Start endpoint in specified availability zone (e.g., us-central-4b)",
138
129
  )
139
130
  @click.option(
140
- "--wait/--no-wait",
131
+ "--wait",
132
+ is_flag=True,
141
133
  default=True,
142
134
  help="Wait for the endpoint to be ready after creation",
143
135
  )
@@ -151,8 +143,8 @@ def create(
151
143
  gpu: str,
152
144
  gpu_count: int,
153
145
  display_name: str | None,
154
- no_prompt_cache: bool,
155
- no_speculative_decoding: bool,
146
+ no_prompt_cache: bool | None,
147
+ no_speculative_decoding: bool | None,
156
148
  no_auto_start: bool,
157
149
  inactive_timeout: int | None,
158
150
  availability_zone: str | None,
@@ -174,21 +166,21 @@ def create(
174
166
  response = client.endpoints.create(
175
167
  model=model,
176
168
  hardware=hardware_id,
177
- min_replicas=min_replicas,
178
- max_replicas=max_replicas,
179
- display_name=display_name,
180
- disable_prompt_cache=no_prompt_cache,
181
- disable_speculative_decoding=no_speculative_decoding,
169
+ autoscaling={
170
+ "min_replicas": min_replicas,
171
+ "max_replicas": max_replicas,
172
+ },
173
+ display_name=display_name or omit,
174
+ disable_prompt_cache=no_prompt_cache or omit,
175
+ disable_speculative_decoding=no_speculative_decoding or omit,
182
176
  state="STOPPED" if no_auto_start else "STARTED",
183
177
  inactive_timeout=inactive_timeout,
184
- availability_zone=availability_zone,
178
+ extra_query={"availability_zone": availability_zone or omit},
185
179
  )
186
- except InvalidRequestError as e:
180
+ except APIError as e:
187
181
  print_api_error(e)
188
- if "check the hardware api" in str(e).lower():
189
- fetch_and_print_hardware_options(
190
- client=client, model=model, print_json=False, available=True
191
- )
182
+ if "check the hardware api" in str(e.args[0]).lower() or "invalid hardware provided" in str(e.args[0]).lower():
183
+ fetch_and_print_hardware_options(client=client, model=model, print_json=False, available=True)
192
184
 
193
185
  sys.exit(1)
194
186
 
@@ -217,7 +209,7 @@ def create(
217
209
  import time
218
210
 
219
211
  click.echo("Waiting for endpoint to be ready...", err=True)
220
- while client.endpoints.get(response.id).state != "STARTED":
212
+ while client.endpoints.retrieve(response.id).state != "STARTED":
221
213
  time.sleep(1)
222
214
  click.echo("Endpoint ready", err=True)
223
215
 
@@ -232,11 +224,11 @@ def create(
232
224
  @handle_api_errors
233
225
  def get(client: Together, endpoint_id: str, json: bool) -> None:
234
226
  """Get a dedicated inference endpoint."""
235
- endpoint = client.endpoints.get(endpoint_id)
227
+ endpoint = client.endpoints.retrieve(endpoint_id)
236
228
  if json:
237
229
  import json as json_lib
238
230
 
239
- click.echo(json_lib.dumps(endpoint.model_dump(), indent=2))
231
+ click.echo(json_lib.dumps(endpoint.model_dump(), indent=2, default=datetime_serializer))
240
232
  else:
241
233
  print_endpoint(endpoint)
242
234
 
@@ -256,37 +248,31 @@ def hardware(client: Together, model: str | None, json: bool, available: bool) -
256
248
  fetch_and_print_hardware_options(client, model, json, available)
257
249
 
258
250
 
259
- def fetch_and_print_hardware_options(
260
- client: Together, model: str | None, print_json: bool, available: bool
261
- ) -> None:
251
+ def fetch_and_print_hardware_options(client: Together, model: str | None, print_json: bool, available: bool) -> None:
262
252
  """Print hardware options for a model."""
263
253
 
264
254
  message = "Available hardware options:" if available else "All hardware options:"
265
255
  click.echo(message, err=True)
266
- hardware_options = client.endpoints.list_hardware(model)
256
+ hardware_options = client.hardware.list(model=model or omit)
257
+ # hardware_options = client.endpoints.list_hardware(model)
267
258
  if available:
268
- hardware_options = [
259
+ hardware_options.data = [
269
260
  hardware
270
- for hardware in hardware_options
271
- if hardware.availability is not None
272
- and hardware.availability.status == "available"
261
+ for hardware in hardware_options.data
262
+ if hardware.availability is not None and hardware.availability.status == "available"
273
263
  ]
274
264
 
275
265
  if print_json:
276
- json_output = [hardware.model_dump() for hardware in hardware_options]
277
- click.echo(json.dumps(json_output, indent=2))
266
+ json_output = [hardware.model_dump() for hardware in hardware_options.data]
267
+ click.echo(json.dumps(json_output, default=datetime_serializer, indent=2))
278
268
  else:
279
- for hardware in hardware_options:
269
+ for hardware in hardware_options.data:
280
270
  click.echo(f" {hardware.id}", err=True)
281
271
 
282
272
 
283
273
  @endpoints.command()
284
274
  @click.argument("endpoint-id", required=True)
285
- @click.option(
286
- "--wait/--no-wait",
287
- default=True,
288
- help="Wait for the endpoint to stop",
289
- )
275
+ @click.option("--wait", is_flag=True, default=True, help="Wait for the endpoint to stop")
290
276
  @click.pass_obj
291
277
  @handle_api_errors
292
278
  def stop(client: Together, endpoint_id: str, wait: bool) -> None:
@@ -298,7 +284,7 @@ def stop(client: Together, endpoint_id: str, wait: bool) -> None:
298
284
  import time
299
285
 
300
286
  click.echo("Waiting for endpoint to stop...", err=True)
301
- while client.endpoints.get(endpoint_id).state != "STOPPED":
287
+ while client.endpoints.retrieve(endpoint_id).state != "STOPPED":
302
288
  time.sleep(1)
303
289
  click.echo("Endpoint stopped", err=True)
304
290
 
@@ -307,11 +293,7 @@ def stop(client: Together, endpoint_id: str, wait: bool) -> None:
307
293
 
308
294
  @endpoints.command()
309
295
  @click.argument("endpoint-id", required=True)
310
- @click.option(
311
- "--wait/--no-wait",
312
- default=True,
313
- help="Wait for the endpoint to start",
314
- )
296
+ @click.option("--wait", is_flag=True, default=True, help="Wait for the endpoint to start")
315
297
  @click.pass_obj
316
298
  @handle_api_errors
317
299
  def start(client: Together, endpoint_id: str, wait: bool) -> None:
@@ -323,7 +305,7 @@ def start(client: Together, endpoint_id: str, wait: bool) -> None:
323
305
  import time
324
306
 
325
307
  click.echo("Waiting for endpoint to start...", err=True)
326
- while client.endpoints.get(endpoint_id).state != "STARTED":
308
+ while client.endpoints.retrieve(endpoint_id).state != "STARTED":
327
309
  time.sleep(1)
328
310
  click.echo("Endpoint started", err=True)
329
311
 
@@ -369,8 +351,10 @@ def list(
369
351
  mine: bool | None,
370
352
  ) -> None:
371
353
  """List all inference endpoints (includes both dedicated and serverless endpoints)."""
372
- endpoints: List[ListEndpoint] = client.endpoints.list(
373
- type=type, usage_type=usage_type, mine=mine
354
+ endpoints = client.endpoints.list(
355
+ type=type or omit,
356
+ usage_type=usage_type or omit,
357
+ mine=mine if mine is not None else omit,
374
358
  )
375
359
 
376
360
  if not endpoints:
@@ -382,10 +366,12 @@ def list(
382
366
  import json as json_lib
383
367
 
384
368
  click.echo(
385
- json_lib.dumps([endpoint.model_dump() for endpoint in endpoints], indent=2)
369
+ json_lib.dumps(
370
+ [endpoint.model_dump() for endpoint in endpoints.data], default=datetime_serializer, indent=2
371
+ )
386
372
  )
387
373
  else:
388
- for endpoint in endpoints:
374
+ for endpoint in endpoints.data:
389
375
  print_endpoint(
390
376
  endpoint,
391
377
  )
@@ -428,32 +414,30 @@ def update(
428
414
  click.echo("Error: At least one update option must be specified", err=True)
429
415
  sys.exit(1)
430
416
 
431
- # If only one of min/max replicas is specified, we need both for the update
432
- if (min_replicas is None) != (max_replicas is None):
433
- click.echo(
434
- "Error: Both --min-replicas and --max-replicas must be specified together",
435
- err=True,
436
- )
437
- sys.exit(1)
438
-
439
417
  # Build kwargs for the update
440
418
  kwargs: Dict[str, Any] = {}
441
419
  if display_name is not None:
442
420
  kwargs["display_name"] = display_name
443
- if min_replicas is not None and max_replicas is not None:
444
- kwargs["min_replicas"] = min_replicas
445
- kwargs["max_replicas"] = max_replicas
421
+
422
+ if min_replicas is not None or max_replicas is not None:
423
+ kwargs["autoscaling"] = {}
424
+ if min_replicas is not None:
425
+ kwargs["autoscaling"]["min_replicas"] = min_replicas
426
+ if max_replicas is not None:
427
+ kwargs["autoscaling"]["max_replicas"] = max_replicas
428
+
446
429
  if inactive_timeout is not None:
447
430
  kwargs["inactive_timeout"] = inactive_timeout
448
431
 
449
- _response = client.endpoints.update(endpoint_id, **kwargs)
432
+ client.endpoints.update(endpoint_id, **kwargs)
450
433
 
451
434
  # Print what was updated
452
435
  click.echo("Updated endpoint configuration:", err=True)
453
436
  if display_name:
454
437
  click.echo(f" Display name: {display_name}", err=True)
455
- if min_replicas is not None and max_replicas is not None:
438
+ if min_replicas:
456
439
  click.echo(f" Min replicas: {min_replicas}", err=True)
440
+ if max_replicas:
457
441
  click.echo(f" Max replicas: {max_replicas}", err=True)
458
442
  if inactive_timeout is not None:
459
443
  click.echo(f" Inactive timeout: {inactive_timeout} minutes", err=True)
@@ -477,8 +461,8 @@ def availability_zones(client: Together, json: bool) -> None:
477
461
  if json:
478
462
  import json as json_lib
479
463
 
480
- click.echo(json_lib.dumps({"avzones": avzones}, indent=2))
464
+ click.echo(json_lib.dumps(avzones.model_dump(), indent=2))
481
465
  else:
482
466
  click.echo("Available zones:", err=True)
483
- for availability_zone in sorted(avzones):
467
+ for availability_zone in sorted(avzones.avzones):
484
468
  click.echo(f" {availability_zone}")
@@ -1,21 +1,72 @@
1
+ import sys
1
2
  import json
2
- from typing import Optional, Dict, Union, Any
3
+ from typing import Any, Dict, List, Union, Literal, TypeVar, Callable, Optional, cast
4
+ from functools import wraps
3
5
 
4
6
  import click
5
7
  from tabulate import tabulate
6
8
 
7
- from together import Together
8
- from together.utils import convert_unix_timestamp
9
+ from together import APIError, Together, TogetherError
10
+ from together._types import omit
11
+ from together.lib.utils.serializer import datetime_serializer
12
+ from together.types.eval_create_params import (
13
+ ParametersEvaluationScoreParameters,
14
+ ParametersEvaluationCompareParameters,
15
+ ParametersEvaluationClassifyParameters,
16
+ ParametersEvaluationScoreParametersJudge,
17
+ ParametersEvaluationCompareParametersJudge,
18
+ ParametersEvaluationClassifyParametersJudge,
19
+ ParametersEvaluationScoreParametersModelToEvaluate,
20
+ ParametersEvaluationClassifyParametersModelToEvaluate,
21
+ ParametersEvaluationCompareParametersModelAEvaluationModelRequest,
22
+ ParametersEvaluationCompareParametersModelBEvaluationModelRequest,
23
+ )
24
+
25
+
26
+ def print_api_error(e: Union[APIError, TogetherError]) -> None:
27
+ if isinstance(e, APIError):
28
+ error_details = cast(Dict[str, Any], e.body)["error"]["message"]
29
+
30
+ if error_details and ("credentials" in error_details.lower() or "authentication" in error_details.lower()):
31
+ click.echo("Error: Invalid API key or authentication failed", err=True)
32
+ else:
33
+ click.echo(f"Error: {error_details}", err=True)
34
+
35
+ click.echo(f"Error: {e}", err=True)
36
+ return
37
+
38
+
39
+ F = TypeVar("F", bound=Callable[..., Any])
40
+
41
+
42
+ def handle_api_errors(f: F) -> F:
43
+ """Decorator to handle common API errors in CLI commands."""
44
+
45
+ @wraps(f)
46
+ def wrapper(*args: Any, **kwargs: Any) -> Any:
47
+ try:
48
+ return f(*args, **kwargs)
49
+ except APIError as e:
50
+ print_api_error(e)
51
+ sys.exit(1)
52
+ except TogetherError as e:
53
+ print_api_error(e)
54
+ sys.exit(1)
55
+ except Exception as e:
56
+ click.echo(f"Error: An unexpected error occurred - {str(e)}", err=True)
57
+ sys.exit(1)
58
+
59
+ return wrapper # type: ignore
9
60
 
10
61
 
11
62
  @click.group()
12
63
  @click.pass_context
13
- def evaluation(ctx: click.Context) -> None:
14
- """Evaluation API commands"""
64
+ def evals(ctx: click.Context) -> None:
65
+ """Evals API commands"""
15
66
  pass
16
67
 
17
68
 
18
- @evaluation.command()
69
+ @evals.command()
19
70
  @click.pass_context
20
71
  @click.option(
21
72
  "--type",
@@ -222,11 +273,12 @@ def evaluation(ctx: click.Context) -> None:
222
273
  type=str,
223
274
  help="Input template for model B.",
224
275
  )
276
+ @handle_api_errors
225
277
  def create(
226
278
  ctx: click.Context,
227
- type: str,
279
+ type: Literal["classify", "score", "compare"],
228
280
  judge_model: str,
229
- judge_model_source: str,
281
+ judge_model_source: Literal["serverless", "dedicated", "external"],
230
282
  judge_system_template: str,
231
283
  judge_external_api_token: Optional[str],
232
284
  judge_external_base_url: Optional[str],
@@ -307,13 +359,9 @@ def create(
307
359
  "input_template": model_to_evaluate_input_template,
308
360
  }
309
361
  if model_to_evaluate_external_api_token:
310
- model_to_evaluate_final["external_api_token"] = (
311
- model_to_evaluate_external_api_token
312
- )
362
+ model_to_evaluate_final["external_api_token"] = model_to_evaluate_external_api_token
313
363
  if model_to_evaluate_external_base_url:
314
- model_to_evaluate_final["external_base_url"] = (
315
- model_to_evaluate_external_base_url
316
- )
364
+ model_to_evaluate_final["external_base_url"] = model_to_evaluate_external_base_url
317
365
 
318
366
  # Build model-a configuration
319
367
  model_a_final: Union[Dict[str, Any], None, str] = None
@@ -385,35 +433,55 @@ def create(
385
433
  if model_b_external_base_url:
386
434
  model_b_final["external_base_url"] = model_b_external_base_url
387
435
 
388
- try:
389
- response = client.evaluation.create(
436
+ judge_config = _build_judge(
437
+ type, judge_model, judge_model_source, judge_system_template, judge_external_api_token, judge_external_base_url
438
+ )
439
+
440
+ if type == "classify":
441
+ response = client.evals.create(
442
+ type=type,
443
+ parameters=ParametersEvaluationClassifyParameters(
444
+ input_data_file_path=input_data_file_path,
445
+ judge=judge_config,
446
+ labels=labels_list or [],
447
+ pass_labels=pass_labels_list or [],
448
+ model_to_evaluate=cast(ParametersEvaluationClassifyParametersModelToEvaluate, model_to_evaluate_final),
449
+ ),
450
+ )
451
+ elif type == "score":
452
+ if max_score is None or min_score is None or pass_threshold is None:
453
+ raise TogetherError("max_score, min_score, and pass_threshold are required for score type")
454
+
455
+ response = client.evals.create(
456
+ type="score",
457
+ parameters=ParametersEvaluationScoreParameters(
458
+ input_data_file_path=input_data_file_path,
459
+ judge=judge_config,
460
+ max_score=max_score,
461
+ min_score=min_score,
462
+ pass_threshold=pass_threshold,
463
+ model_to_evaluate=cast(ParametersEvaluationScoreParametersModelToEvaluate, model_to_evaluate_final),
464
+ ),
465
+ )
466
+ elif type == "compare":
467
+ response = client.evals.create(
390
468
  type=type,
391
- judge_model=judge_model,
392
- judge_model_source=judge_model_source,
393
- judge_system_template=judge_system_template,
394
- judge_external_api_token=judge_external_api_token,
395
- judge_external_base_url=judge_external_base_url,
396
- input_data_file_path=input_data_file_path,
397
- model_to_evaluate=model_to_evaluate_final,
398
- labels=labels_list,
399
- pass_labels=pass_labels_list,
400
- min_score=min_score,
401
- max_score=max_score,
402
- pass_threshold=pass_threshold,
403
- model_a=model_a_final,
404
- model_b=model_b_final,
469
+ parameters=ParametersEvaluationCompareParameters(
470
+ input_data_file_path=input_data_file_path,
471
+ judge=judge_config,
472
+ model_a=cast(ParametersEvaluationCompareParametersModelAEvaluationModelRequest, model_a_final),
473
+ model_b=cast(ParametersEvaluationCompareParametersModelBEvaluationModelRequest, model_b_final),
474
+ ),
405
475
  )
406
- except ValueError as e:
407
- raise click.BadParameter(str(e))
408
476
 
409
477
  click.echo(json.dumps(response.model_dump(exclude_none=True), indent=4))
410
478
 
411
479
 
412
- @evaluation.command()
480
+ @evals.command()
413
481
  @click.pass_context
414
482
  @click.option(
415
483
  "--status",
416
- type=str,
484
+ type=click.Choice(["pending", "queued", "running", "completed", "error", "user_error"]),
417
485
  help="Filter by job status.",
418
486
  )
419
487
  @click.option(
@@ -421,14 +489,18 @@ def create(
421
489
  type=int,
422
490
  help="Limit number of results (max 100).",
423
491
  )
424
- def list(ctx: click.Context, status: Optional[str], limit: Optional[int]) -> None:
425
- """List evaluation jobs"""
492
+ def list(
493
+ ctx: click.Context,
494
+ status: Union[Literal["pending", "queued", "running", "completed", "error", "user_error"], None],
495
+ limit: Union[int, None],
496
+ ) -> None:
497
+ """List evals"""
426
498
 
427
499
  client: Together = ctx.obj
428
500
 
429
- response = client.evaluation.list(status=status, limit=limit)
501
+ response = client.evals.list(status=status or omit, limit=limit or omit)
430
502
 
431
- display_list = []
503
+ display_list: List[Dict[str, Any]] = []
432
504
  for job in response:
433
505
  if job.parameters:
434
506
  model = job.parameters.get("model_to_evaluate", "")
@@ -436,6 +508,8 @@ def list(ctx: click.Context, status: Optional[str], limit: Optional[int]) -> Non
436
508
  model_b = job.parameters.get("model_b", "")
437
509
  else:
438
510
  model = ""
511
+ model_a = ""
512
+ model_b = ""
439
513
 
440
514
  display_list.append(
441
515
  {
@@ -453,7 +527,7 @@ def list(ctx: click.Context, status: Optional[str], limit: Optional[int]) -> Non
453
527
  click.echo(table)
454
528
 
455
529
 
456
- @evaluation.command()
530
+ @evals.command()
457
531
  @click.pass_context
458
532
  @click.argument("evaluation_id", type=str, required=True)
459
533
  def retrieve(ctx: click.Context, evaluation_id: str) -> None:
@@ -461,12 +535,12 @@ def retrieve(ctx: click.Context, evaluation_id: str) -> None:
461
535
 
462
536
  client: Together = ctx.obj
463
537
 
464
- response = client.evaluation.retrieve(evaluation_id=evaluation_id)
538
+ response = client.evals.retrieve(evaluation_id)
465
539
 
466
- click.echo(json.dumps(response.model_dump(exclude_none=True), indent=4))
540
+ click.echo(json.dumps(response.model_dump(exclude_none=True), default=datetime_serializer, indent=4))
467
541
 
468
542
 
469
- @evaluation.command()
543
+ @evals.command()
470
544
  @click.pass_context
471
545
  @click.argument("evaluation_id", type=str, required=True)
472
546
  def status(ctx: click.Context, evaluation_id: str) -> None:
@@ -474,6 +548,41 @@ def status(ctx: click.Context, evaluation_id: str) -> None:
474
548
 
475
549
  client: Together = ctx.obj
476
550
 
477
- response = client.evaluation.status(evaluation_id=evaluation_id)
551
+ response = client.evals.status(evaluation_id)
478
552
 
479
553
  click.echo(json.dumps(response.model_dump(exclude_none=True), indent=4))
554
+
555
+
556
+ def _build_judge(
557
+ type: Literal["classify", "score", "compare"],
558
+ judge_model: str,
559
+ judge_model_source: Literal["serverless", "dedicated", "external"],
560
+ judge_system_template: str,
561
+ judge_external_api_token: Optional[str],
562
+ judge_external_base_url: Optional[str],
563
+ ) -> ParametersEvaluationClassifyParametersJudge:
564
+ if type == "classify":
565
+ judge_config = ParametersEvaluationClassifyParametersJudge(
566
+ model=judge_model,
567
+ model_source=judge_model_source,
568
+ system_template=judge_system_template,
569
+ )
570
+ elif type == "score":
571
+ judge_config = ParametersEvaluationScoreParametersJudge(
572
+ model=judge_model,
573
+ model_source=judge_model_source,
574
+ system_template=judge_system_template,
575
+ )
576
+ elif type == "compare":
577
+ judge_config = ParametersEvaluationCompareParametersJudge(
578
+ model=judge_model,
579
+ model_source=judge_model_source,
580
+ system_template=judge_system_template,
581
+ )
582
+
583
+ if judge_external_api_token:
584
+ judge_config["external_api_token"] = judge_external_api_token
585
+ if judge_external_base_url:
586
+ judge_config["external_base_url"] = judge_external_base_url
587
+
588
+ return judge_config
@@ -1,5 +1,6 @@
1
1
  import json
2
2
  import pathlib
3
+ from typing import Any, Dict, List, get_args
3
4
  from textwrap import wrap
4
5
 
5
6
  import click
@@ -7,7 +8,9 @@ from tabulate import tabulate
7
8
 
8
9
  from together import Together
9
10
  from together.types import FilePurpose
10
- from together.utils import check_file, convert_bytes, convert_unix_timestamp
11
+
12
+ # from together.utils import check_file, convert_bytes, convert_unix_timestamp
13
+ from ...utils import check_file, convert_bytes, convert_unix_timestamp
11
14
 
12
15
 
13
16
  @click.group()
@@ -21,15 +24,13 @@ def files(ctx: click.Context) -> None:
21
24
  @click.pass_context
22
25
  @click.argument(
23
26
  "file",
24
- type=click.Path(
25
- exists=True, file_okay=True, resolve_path=True, readable=True, dir_okay=False
26
- ),
27
+ type=click.Path(exists=True, file_okay=True, resolve_path=True, readable=True, dir_okay=False),
27
28
  required=True,
28
29
  )
29
30
  @click.option(
30
31
  "--purpose",
31
- type=str,
32
- default=FilePurpose.FineTune.value,
32
+ type=click.Choice(get_args(FilePurpose)),
33
+ default="fine-tune",
33
34
  help="Purpose of file upload. Acceptable values in enum `together.types.FilePurpose`. Defaults to `fine-tunes`.",
34
35
  )
35
36
  @click.option(
@@ -37,7 +38,7 @@ def files(ctx: click.Context) -> None:
37
38
  default=True,
38
39
  help="Whether to check the file before uploading.",
39
40
  )
40
- def upload(ctx: click.Context, file: pathlib.Path, purpose: str, check: bool) -> None:
41
+ def upload(ctx: click.Context, file: pathlib.Path, purpose: FilePurpose, check: bool) -> None:
41
42
  """Upload file"""
42
43
 
43
44
  client: Together = ctx.obj
@@ -55,15 +56,13 @@ def list(ctx: click.Context) -> None:
55
56
 
56
57
  response = client.files.list()
57
58
 
58
- display_list = []
59
+ display_list: List[Dict[str, Any]] = []
59
60
  for i in response.data or []:
60
61
  display_list.append(
61
62
  {
62
63
  "File name": "\n".join(wrap(i.filename or "", width=30)),
63
64
  "File ID": i.id,
64
- "Size": convert_bytes(
65
- float(str(i.bytes))
66
- ), # convert to string for mypy typing
65
+ "Size": convert_bytes(float(str(i.bytes))), # convert to string for mypy typing
67
66
  "Created At": convert_unix_timestamp(i.created_at or 0),
68
67
  "Line Count": i.line_count,
69
68
  }
@@ -95,9 +94,15 @@ def retrieve_content(ctx: click.Context, id: str, output: str) -> None:
95
94
 
96
95
  client: Together = ctx.obj
97
96
 
98
- response = client.files.retrieve_content(id=id, output=output)
97
+ response = client.files.content(id=id)
99
98
 
100
- click.echo(json.dumps(response.model_dump(exclude_none=True), indent=4))
99
+ if output:
100
+ with open(output, "wb") as f:
101
+ f.write(response.read())
102
+ click.echo(f"File saved to {output}")
103
+
104
+ else:
105
+ click.echo(response.read().decode("utf-8"))
101
106
 
102
107
 
103
108
  @files.command()
@@ -117,12 +122,10 @@ def delete(ctx: click.Context, id: str) -> None:
117
122
  @click.pass_context
118
123
  @click.argument(
119
124
  "file",
120
- type=click.Path(
121
- exists=True, file_okay=True, resolve_path=True, readable=True, dir_okay=False
122
- ),
125
+ type=click.Path(exists=True, file_okay=True, resolve_path=True, readable=True, dir_okay=False),
123
126
  required=True,
124
127
  )
125
- def check(ctx: click.Context, file: pathlib.Path) -> None:
128
+ def check(_ctx: click.Context, file: pathlib.Path) -> None:
126
129
  """Check file for issues"""
127
130
 
128
131
  report = check_file(file)