together 1.5.35__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 +66 -84
  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 -490
  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.35.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.35.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 -465
  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.35.dist-info/METADATA +0 -583
  204. together-1.5.35.dist-info/RECORD +0 -77
  205. together-1.5.35.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:
@@ -98,7 +89,7 @@ def endpoints(ctx: click.Context) -> None:
98
89
  )
99
90
  @click.option(
100
91
  "--gpu",
101
- type=click.Choice(["b200", "h200", "h100", "a100", "l40", "l40s", "rtx-6000"]),
92
+ type=click.Choice(["h100", "a100", "l40", "l40s", "rtx-6000"]),
102
93
  required=True,
103
94
  help="GPU type to use for inference",
104
95
  )
@@ -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,
@@ -161,8 +153,6 @@ def create(
161
153
  """Create a new dedicated inference endpoint."""
162
154
  # Map GPU types to their full hardware ID names
163
155
  gpu_map = {
164
- "b200": "nvidia_b200_180gb_sxm",
165
- "h200": "nvidia_h200_140gb_sxm",
166
156
  "h100": "nvidia_h100_80gb_sxm",
167
157
  "a100": "nvidia_a100_80gb_pcie" if gpu_count == 1 else "nvidia_a100_80gb_sxm",
168
158
  "l40": "nvidia_l40",
@@ -176,21 +166,21 @@ def create(
176
166
  response = client.endpoints.create(
177
167
  model=model,
178
168
  hardware=hardware_id,
179
- min_replicas=min_replicas,
180
- max_replicas=max_replicas,
181
- display_name=display_name,
182
- disable_prompt_cache=no_prompt_cache,
183
- 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,
184
176
  state="STOPPED" if no_auto_start else "STARTED",
185
177
  inactive_timeout=inactive_timeout,
186
- availability_zone=availability_zone,
178
+ extra_query={"availability_zone": availability_zone or omit},
187
179
  )
188
- except InvalidRequestError as e:
180
+ except APIError as e:
189
181
  print_api_error(e)
190
- if "check the hardware api" in str(e).lower():
191
- fetch_and_print_hardware_options(
192
- client=client, model=model, print_json=False, available=True
193
- )
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)
194
184
 
195
185
  sys.exit(1)
196
186
 
@@ -219,7 +209,7 @@ def create(
219
209
  import time
220
210
 
221
211
  click.echo("Waiting for endpoint to be ready...", err=True)
222
- while client.endpoints.get(response.id).state != "STARTED":
212
+ while client.endpoints.retrieve(response.id).state != "STARTED":
223
213
  time.sleep(1)
224
214
  click.echo("Endpoint ready", err=True)
225
215
 
@@ -234,11 +224,11 @@ def create(
234
224
  @handle_api_errors
235
225
  def get(client: Together, endpoint_id: str, json: bool) -> None:
236
226
  """Get a dedicated inference endpoint."""
237
- endpoint = client.endpoints.get(endpoint_id)
227
+ endpoint = client.endpoints.retrieve(endpoint_id)
238
228
  if json:
239
229
  import json as json_lib
240
230
 
241
- click.echo(json_lib.dumps(endpoint.model_dump(), indent=2))
231
+ click.echo(json_lib.dumps(endpoint.model_dump(), indent=2, default=datetime_serializer))
242
232
  else:
243
233
  print_endpoint(endpoint)
244
234
 
@@ -258,37 +248,31 @@ def hardware(client: Together, model: str | None, json: bool, available: bool) -
258
248
  fetch_and_print_hardware_options(client, model, json, available)
259
249
 
260
250
 
261
- def fetch_and_print_hardware_options(
262
- client: Together, model: str | None, print_json: bool, available: bool
263
- ) -> None:
251
+ def fetch_and_print_hardware_options(client: Together, model: str | None, print_json: bool, available: bool) -> None:
264
252
  """Print hardware options for a model."""
265
253
 
266
254
  message = "Available hardware options:" if available else "All hardware options:"
267
255
  click.echo(message, err=True)
268
- 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)
269
258
  if available:
270
- hardware_options = [
259
+ hardware_options.data = [
271
260
  hardware
272
- for hardware in hardware_options
273
- if hardware.availability is not None
274
- and hardware.availability.status == "available"
261
+ for hardware in hardware_options.data
262
+ if hardware.availability is not None and hardware.availability.status == "available"
275
263
  ]
276
264
 
277
265
  if print_json:
278
- json_output = [hardware.model_dump() for hardware in hardware_options]
279
- 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))
280
268
  else:
281
- for hardware in hardware_options:
269
+ for hardware in hardware_options.data:
282
270
  click.echo(f" {hardware.id}", err=True)
283
271
 
284
272
 
285
273
  @endpoints.command()
286
274
  @click.argument("endpoint-id", required=True)
287
- @click.option(
288
- "--wait/--no-wait",
289
- default=True,
290
- help="Wait for the endpoint to stop",
291
- )
275
+ @click.option("--wait", is_flag=True, default=True, help="Wait for the endpoint to stop")
292
276
  @click.pass_obj
293
277
  @handle_api_errors
294
278
  def stop(client: Together, endpoint_id: str, wait: bool) -> None:
@@ -300,7 +284,7 @@ def stop(client: Together, endpoint_id: str, wait: bool) -> None:
300
284
  import time
301
285
 
302
286
  click.echo("Waiting for endpoint to stop...", err=True)
303
- while client.endpoints.get(endpoint_id).state != "STOPPED":
287
+ while client.endpoints.retrieve(endpoint_id).state != "STOPPED":
304
288
  time.sleep(1)
305
289
  click.echo("Endpoint stopped", err=True)
306
290
 
@@ -309,11 +293,7 @@ def stop(client: Together, endpoint_id: str, wait: bool) -> None:
309
293
 
310
294
  @endpoints.command()
311
295
  @click.argument("endpoint-id", required=True)
312
- @click.option(
313
- "--wait/--no-wait",
314
- default=True,
315
- help="Wait for the endpoint to start",
316
- )
296
+ @click.option("--wait", is_flag=True, default=True, help="Wait for the endpoint to start")
317
297
  @click.pass_obj
318
298
  @handle_api_errors
319
299
  def start(client: Together, endpoint_id: str, wait: bool) -> None:
@@ -325,7 +305,7 @@ def start(client: Together, endpoint_id: str, wait: bool) -> None:
325
305
  import time
326
306
 
327
307
  click.echo("Waiting for endpoint to start...", err=True)
328
- while client.endpoints.get(endpoint_id).state != "STARTED":
308
+ while client.endpoints.retrieve(endpoint_id).state != "STARTED":
329
309
  time.sleep(1)
330
310
  click.echo("Endpoint started", err=True)
331
311
 
@@ -371,8 +351,10 @@ def list(
371
351
  mine: bool | None,
372
352
  ) -> None:
373
353
  """List all inference endpoints (includes both dedicated and serverless endpoints)."""
374
- endpoints: List[ListEndpoint] = client.endpoints.list(
375
- 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,
376
358
  )
377
359
 
378
360
  if not endpoints:
@@ -384,10 +366,12 @@ def list(
384
366
  import json as json_lib
385
367
 
386
368
  click.echo(
387
- 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
+ )
388
372
  )
389
373
  else:
390
- for endpoint in endpoints:
374
+ for endpoint in endpoints.data:
391
375
  print_endpoint(
392
376
  endpoint,
393
377
  )
@@ -430,32 +414,30 @@ def update(
430
414
  click.echo("Error: At least one update option must be specified", err=True)
431
415
  sys.exit(1)
432
416
 
433
- # If only one of min/max replicas is specified, we need both for the update
434
- if (min_replicas is None) != (max_replicas is None):
435
- click.echo(
436
- "Error: Both --min-replicas and --max-replicas must be specified together",
437
- err=True,
438
- )
439
- sys.exit(1)
440
-
441
417
  # Build kwargs for the update
442
418
  kwargs: Dict[str, Any] = {}
443
419
  if display_name is not None:
444
420
  kwargs["display_name"] = display_name
445
- if min_replicas is not None and max_replicas is not None:
446
- kwargs["min_replicas"] = min_replicas
447
- 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
+
448
429
  if inactive_timeout is not None:
449
430
  kwargs["inactive_timeout"] = inactive_timeout
450
431
 
451
- _response = client.endpoints.update(endpoint_id, **kwargs)
432
+ client.endpoints.update(endpoint_id, **kwargs)
452
433
 
453
434
  # Print what was updated
454
435
  click.echo("Updated endpoint configuration:", err=True)
455
436
  if display_name:
456
437
  click.echo(f" Display name: {display_name}", err=True)
457
- if min_replicas is not None and max_replicas is not None:
438
+ if min_replicas:
458
439
  click.echo(f" Min replicas: {min_replicas}", err=True)
440
+ if max_replicas:
459
441
  click.echo(f" Max replicas: {max_replicas}", err=True)
460
442
  if inactive_timeout is not None:
461
443
  click.echo(f" Inactive timeout: {inactive_timeout} minutes", err=True)
@@ -479,8 +461,8 @@ def availability_zones(client: Together, json: bool) -> None:
479
461
  if json:
480
462
  import json as json_lib
481
463
 
482
- click.echo(json_lib.dumps({"avzones": avzones}, indent=2))
464
+ click.echo(json_lib.dumps(avzones.model_dump(), indent=2))
483
465
  else:
484
466
  click.echo("Available zones:", err=True)
485
- for availability_zone in sorted(avzones):
467
+ for availability_zone in sorted(avzones.avzones):
486
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