together 1.4.0__tar.gz → 1.4.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (64) hide show
  1. {together-1.4.0 → together-1.4.1}/PKG-INFO +1 -1
  2. {together-1.4.0 → together-1.4.1}/pyproject.toml +3 -3
  3. {together-1.4.0 → together-1.4.1}/src/together/abstract/api_requestor.py +7 -9
  4. together-1.4.1/src/together/cli/api/endpoints.py +415 -0
  5. {together-1.4.0 → together-1.4.1}/src/together/cli/cli.py +2 -0
  6. {together-1.4.0 → together-1.4.1}/src/together/client.py +1 -0
  7. {together-1.4.0 → together-1.4.1}/src/together/error.py +3 -0
  8. {together-1.4.0 → together-1.4.1}/src/together/resources/__init__.py +4 -1
  9. together-1.4.1/src/together/resources/endpoints.py +488 -0
  10. {together-1.4.0 → together-1.4.1}/src/together/types/__init__.py +19 -20
  11. together-1.4.1/src/together/types/endpoints.py +123 -0
  12. {together-1.4.0 → together-1.4.1}/LICENSE +0 -0
  13. {together-1.4.0 → together-1.4.1}/README.md +0 -0
  14. {together-1.4.0 → together-1.4.1}/src/together/__init__.py +0 -0
  15. {together-1.4.0 → together-1.4.1}/src/together/abstract/__init__.py +0 -0
  16. {together-1.4.0 → together-1.4.1}/src/together/cli/__init__.py +0 -0
  17. {together-1.4.0 → together-1.4.1}/src/together/cli/api/__init__.py +0 -0
  18. {together-1.4.0 → together-1.4.1}/src/together/cli/api/chat.py +0 -0
  19. {together-1.4.0 → together-1.4.1}/src/together/cli/api/completions.py +0 -0
  20. {together-1.4.0 → together-1.4.1}/src/together/cli/api/files.py +0 -0
  21. {together-1.4.0 → together-1.4.1}/src/together/cli/api/finetune.py +0 -0
  22. {together-1.4.0 → together-1.4.1}/src/together/cli/api/images.py +0 -0
  23. {together-1.4.0 → together-1.4.1}/src/together/cli/api/models.py +0 -0
  24. {together-1.4.0 → together-1.4.1}/src/together/cli/api/utils.py +0 -0
  25. {together-1.4.0 → together-1.4.1}/src/together/constants.py +0 -0
  26. {together-1.4.0 → together-1.4.1}/src/together/filemanager.py +0 -0
  27. {together-1.4.0 → together-1.4.1}/src/together/legacy/__init__.py +0 -0
  28. {together-1.4.0 → together-1.4.1}/src/together/legacy/base.py +0 -0
  29. {together-1.4.0 → together-1.4.1}/src/together/legacy/complete.py +0 -0
  30. {together-1.4.0 → together-1.4.1}/src/together/legacy/embeddings.py +0 -0
  31. {together-1.4.0 → together-1.4.1}/src/together/legacy/files.py +0 -0
  32. {together-1.4.0 → together-1.4.1}/src/together/legacy/finetune.py +0 -0
  33. {together-1.4.0 → together-1.4.1}/src/together/legacy/images.py +0 -0
  34. {together-1.4.0 → together-1.4.1}/src/together/legacy/models.py +0 -0
  35. {together-1.4.0 → together-1.4.1}/src/together/resources/audio/__init__.py +0 -0
  36. {together-1.4.0 → together-1.4.1}/src/together/resources/audio/speech.py +0 -0
  37. {together-1.4.0 → together-1.4.1}/src/together/resources/chat/__init__.py +0 -0
  38. {together-1.4.0 → together-1.4.1}/src/together/resources/chat/completions.py +0 -0
  39. {together-1.4.0 → together-1.4.1}/src/together/resources/completions.py +0 -0
  40. {together-1.4.0 → together-1.4.1}/src/together/resources/embeddings.py +0 -0
  41. {together-1.4.0 → together-1.4.1}/src/together/resources/files.py +0 -0
  42. {together-1.4.0 → together-1.4.1}/src/together/resources/finetune.py +0 -0
  43. {together-1.4.0 → together-1.4.1}/src/together/resources/images.py +0 -0
  44. {together-1.4.0 → together-1.4.1}/src/together/resources/models.py +0 -0
  45. {together-1.4.0 → together-1.4.1}/src/together/resources/rerank.py +0 -0
  46. {together-1.4.0 → together-1.4.1}/src/together/together_response.py +0 -0
  47. {together-1.4.0 → together-1.4.1}/src/together/types/abstract.py +0 -0
  48. {together-1.4.0 → together-1.4.1}/src/together/types/audio_speech.py +0 -0
  49. {together-1.4.0 → together-1.4.1}/src/together/types/chat_completions.py +0 -0
  50. {together-1.4.0 → together-1.4.1}/src/together/types/common.py +0 -0
  51. {together-1.4.0 → together-1.4.1}/src/together/types/completions.py +0 -0
  52. {together-1.4.0 → together-1.4.1}/src/together/types/embeddings.py +0 -0
  53. {together-1.4.0 → together-1.4.1}/src/together/types/error.py +0 -0
  54. {together-1.4.0 → together-1.4.1}/src/together/types/files.py +0 -0
  55. {together-1.4.0 → together-1.4.1}/src/together/types/finetune.py +0 -0
  56. {together-1.4.0 → together-1.4.1}/src/together/types/images.py +0 -0
  57. {together-1.4.0 → together-1.4.1}/src/together/types/models.py +0 -0
  58. {together-1.4.0 → together-1.4.1}/src/together/types/rerank.py +0 -0
  59. {together-1.4.0 → together-1.4.1}/src/together/utils/__init__.py +0 -0
  60. {together-1.4.0 → together-1.4.1}/src/together/utils/_log.py +0 -0
  61. {together-1.4.0 → together-1.4.1}/src/together/utils/api_helpers.py +0 -0
  62. {together-1.4.0 → together-1.4.1}/src/together/utils/files.py +0 -0
  63. {together-1.4.0 → together-1.4.1}/src/together/utils/tools.py +0 -0
  64. {together-1.4.0 → together-1.4.1}/src/together/version.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: together
3
- Version: 1.4.0
3
+ Version: 1.4.1
4
4
  Summary: Python client for Together's Cloud Platform!
5
5
  License: Apache-2.0
6
6
  Author: Together AI
@@ -12,7 +12,7 @@ build-backend = "poetry.masonry.api"
12
12
 
13
13
  [tool.poetry]
14
14
  name = "together"
15
- version = "1.4.0"
15
+ version = "1.4.1"
16
16
  authors = [
17
17
  "Together AI <support@together.ai>"
18
18
  ]
@@ -50,11 +50,11 @@ pillow = "^11.1.0"
50
50
  optional = true
51
51
 
52
52
  [tool.poetry.group.quality.dependencies]
53
- black = ">=23.1,<25.0"
53
+ black = ">=23.1,<26.0"
54
54
  ruff = ">=0.3.2,<0.10.0"
55
55
  types-tqdm = "^4.65.0.0"
56
56
  types-tabulate = "^0.9.0.3"
57
- pre-commit = "3.5.0"
57
+ pre-commit = "4.1.0"
58
58
  types-requests = "^2.31.0.20240218"
59
59
  pyarrow-stubs = ">=10.0.1.7,<20240831.0.0.0"
60
60
  mypy = "^1.9.0"
@@ -437,7 +437,7 @@ class APIRequestor:
437
437
  [(k, v) for k, v in options.params.items() if v is not None]
438
438
  )
439
439
  abs_url = _build_api_url(abs_url, encoded_params)
440
- elif options.method.lower() in {"post", "put"}:
440
+ elif options.method.lower() in {"post", "put", "patch"}:
441
441
  if options.params and (options.files or options.override_headers):
442
442
  data = options.params
443
443
  elif options.params and not options.files:
@@ -587,16 +587,14 @@ class APIRequestor:
587
587
  )
588
588
  headers["Content-Type"] = content_type
589
589
 
590
- request_kwargs = {
591
- "headers": headers,
592
- "data": data,
593
- "timeout": timeout,
594
- "allow_redirects": options.allow_redirects,
595
- }
596
-
597
590
  try:
598
591
  result = await session.request(
599
- method=options.method, url=abs_url, **request_kwargs
592
+ method=options.method,
593
+ url=abs_url,
594
+ headers=headers,
595
+ data=data,
596
+ timeout=timeout,
597
+ allow_redirects=options.allow_redirects,
600
598
  )
601
599
  utils.log_debug(
602
600
  "Together API response",
@@ -0,0 +1,415 @@
1
+ from __future__ import annotations
2
+
3
+ import json
4
+ import sys
5
+ from functools import wraps
6
+ from typing import Any, Callable, Dict, List, Literal, TypeVar, Union
7
+
8
+ import click
9
+
10
+ from together import Together
11
+ from together.error import InvalidRequestError
12
+ from together.types import DedicatedEndpoint, ListEndpoint
13
+
14
+
15
+ def print_endpoint(
16
+ endpoint: Union[DedicatedEndpoint, ListEndpoint],
17
+ ) -> None:
18
+ """Print endpoint details in a Docker-like format or JSON."""
19
+
20
+ # Print header info
21
+ click.echo(f"ID:\t\t{endpoint.id}")
22
+ click.echo(f"Name:\t\t{endpoint.name}")
23
+
24
+ # Print type-specific fields
25
+ if isinstance(endpoint, DedicatedEndpoint):
26
+ click.echo(f"Display Name:\t{endpoint.display_name}")
27
+ 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
+ )
32
+
33
+ click.echo(f"Model:\t\t{endpoint.model}")
34
+ click.echo(f"Type:\t\t{endpoint.type}")
35
+ click.echo(f"Owner:\t\t{endpoint.owner}")
36
+ click.echo(f"State:\t\t{endpoint.state}")
37
+ click.echo(f"Created:\t{endpoint.created_at}")
38
+
39
+
40
+ F = TypeVar("F", bound=Callable[..., Any])
41
+
42
+
43
+ def print_api_error(
44
+ e: InvalidRequestError,
45
+ ) -> None:
46
+ error_details = e.api_response.message
47
+
48
+ if error_details and (
49
+ "credentials" in error_details.lower()
50
+ or "authentication" in error_details.lower()
51
+ ):
52
+ click.echo("Error: Invalid API key or authentication failed", err=True)
53
+ else:
54
+ click.echo(f"Error: {error_details}", err=True)
55
+
56
+
57
+ def handle_api_errors(f: F) -> F:
58
+ """Decorator to handle common API errors in CLI commands."""
59
+
60
+ @wraps(f)
61
+ def wrapper(*args: Any, **kwargs: Any) -> Any:
62
+ try:
63
+ return f(*args, **kwargs)
64
+ except InvalidRequestError as e:
65
+ print_api_error(e)
66
+ sys.exit(1)
67
+ except Exception as e:
68
+ click.echo(f"Error: An unexpected error occurred - {str(e)}", err=True)
69
+ sys.exit(1)
70
+
71
+ return wrapper # type: ignore
72
+
73
+
74
+ @click.group()
75
+ @click.pass_context
76
+ def endpoints(ctx: click.Context) -> None:
77
+ """Endpoints API commands"""
78
+ pass
79
+
80
+
81
+ @endpoints.command()
82
+ @click.option(
83
+ "--model",
84
+ required=True,
85
+ help="The model to deploy (e.g. mistralai/Mixtral-8x7B-Instruct-v0.1)",
86
+ )
87
+ @click.option(
88
+ "--min-replicas",
89
+ type=int,
90
+ default=1,
91
+ help="Minimum number of replicas to deploy",
92
+ )
93
+ @click.option(
94
+ "--max-replicas",
95
+ type=int,
96
+ default=1,
97
+ help="Maximum number of replicas to deploy",
98
+ )
99
+ @click.option(
100
+ "--gpu",
101
+ type=click.Choice(["h100", "a100", "l40", "l40s", "rtx-6000"]),
102
+ required=True,
103
+ help="GPU type to use for inference",
104
+ )
105
+ @click.option(
106
+ "--gpu-count",
107
+ type=int,
108
+ default=1,
109
+ help="Number of GPUs to use per replica",
110
+ )
111
+ @click.option(
112
+ "--display-name",
113
+ help="A human-readable name for the endpoint",
114
+ )
115
+ @click.option(
116
+ "--no-prompt-cache",
117
+ is_flag=True,
118
+ help="Disable the prompt cache for this endpoint",
119
+ )
120
+ @click.option(
121
+ "--no-speculative-decoding",
122
+ is_flag=True,
123
+ help="Disable speculative decoding for this endpoint",
124
+ )
125
+ @click.option(
126
+ "--no-auto-start",
127
+ is_flag=True,
128
+ help="Create the endpoint in STOPPED state instead of auto-starting it",
129
+ )
130
+ @click.option(
131
+ "--wait",
132
+ is_flag=True,
133
+ default=True,
134
+ help="Wait for the endpoint to be ready after creation",
135
+ )
136
+ @click.pass_obj
137
+ @handle_api_errors
138
+ def create(
139
+ client: Together,
140
+ model: str,
141
+ min_replicas: int,
142
+ max_replicas: int,
143
+ gpu: str,
144
+ gpu_count: int,
145
+ display_name: str | None,
146
+ no_prompt_cache: bool,
147
+ no_speculative_decoding: bool,
148
+ no_auto_start: bool,
149
+ wait: bool,
150
+ ) -> None:
151
+ """Create a new dedicated inference endpoint."""
152
+ # Map GPU types to their full hardware ID names
153
+ gpu_map = {
154
+ "h100": "nvidia_h100_80gb_sxm",
155
+ "a100": "nvidia_a100_80gb_pcie" if gpu_count == 1 else "nvidia_a100_80gb_sxm",
156
+ "l40": "nvidia_l40",
157
+ "l40s": "nvidia_l40s",
158
+ "rtx-6000": "nvidia_rtx_6000_ada",
159
+ }
160
+
161
+ hardware_id = f"{gpu_count}x_{gpu_map[gpu]}"
162
+
163
+ try:
164
+ response = client.endpoints.create(
165
+ model=model,
166
+ hardware=hardware_id,
167
+ min_replicas=min_replicas,
168
+ max_replicas=max_replicas,
169
+ display_name=display_name,
170
+ disable_prompt_cache=no_prompt_cache,
171
+ disable_speculative_decoding=no_speculative_decoding,
172
+ state="STOPPED" if no_auto_start else "STARTED",
173
+ )
174
+ except InvalidRequestError as e:
175
+ print_api_error(e)
176
+ if "check the hardware api" in str(e).lower():
177
+ fetch_and_print_hardware_options(
178
+ client=client, model=model, print_json=False, available=True
179
+ )
180
+
181
+ sys.exit(1)
182
+
183
+ # Print detailed information to stderr
184
+ click.echo("Created dedicated endpoint with:", err=True)
185
+ click.echo(f" Model: {model}", err=True)
186
+ click.echo(f" Min replicas: {min_replicas}", err=True)
187
+ click.echo(f" Max replicas: {max_replicas}", err=True)
188
+ click.echo(f" Hardware: {hardware_id}", err=True)
189
+ if display_name:
190
+ click.echo(f" Display name: {display_name}", err=True)
191
+ if no_prompt_cache:
192
+ click.echo(" Prompt cache: disabled", err=True)
193
+ if no_speculative_decoding:
194
+ click.echo(" Speculative decoding: disabled", err=True)
195
+ if no_auto_start:
196
+ click.echo(" Auto-start: disabled", err=True)
197
+
198
+ click.echo(f"Endpoint created successfully, id: {response.id}", err=True)
199
+
200
+ if wait:
201
+ import time
202
+
203
+ click.echo("Waiting for endpoint to be ready...", err=True)
204
+ while client.endpoints.get(response.id).state != "STARTED":
205
+ time.sleep(1)
206
+ click.echo("Endpoint ready", err=True)
207
+
208
+ # Print only the endpoint ID to stdout
209
+ click.echo(response.id)
210
+
211
+
212
+ @endpoints.command()
213
+ @click.argument("endpoint-id", required=True)
214
+ @click.option("--json", is_flag=True, help="Print output in JSON format")
215
+ @click.pass_obj
216
+ @handle_api_errors
217
+ def get(client: Together, endpoint_id: str, json: bool) -> None:
218
+ """Get a dedicated inference endpoint."""
219
+ endpoint = client.endpoints.get(endpoint_id)
220
+ if json:
221
+ import json as json_lib
222
+
223
+ click.echo(json_lib.dumps(endpoint.model_dump(), indent=2))
224
+ else:
225
+ print_endpoint(endpoint)
226
+
227
+
228
+ @endpoints.command()
229
+ @click.option("--model", help="Filter hardware options by model")
230
+ @click.option("--json", is_flag=True, help="Print output in JSON format")
231
+ @click.option(
232
+ "--available",
233
+ is_flag=True,
234
+ help="Print only available hardware options (can only be used if model is passed in)",
235
+ )
236
+ @click.pass_obj
237
+ @handle_api_errors
238
+ def hardware(client: Together, model: str | None, json: bool, available: bool) -> None:
239
+ """List all available hardware options, optionally filtered by model."""
240
+ fetch_and_print_hardware_options(client, model, json, available)
241
+
242
+
243
+ def fetch_and_print_hardware_options(
244
+ client: Together, model: str | None, print_json: bool, available: bool
245
+ ) -> None:
246
+ """Print hardware options for a model."""
247
+
248
+ message = "Available hardware options:" if available else "All hardware options:"
249
+ click.echo(message, err=True)
250
+ hardware_options = client.endpoints.list_hardware(model)
251
+ if available:
252
+ hardware_options = [
253
+ hardware
254
+ for hardware in hardware_options
255
+ if hardware.availability is not None
256
+ and hardware.availability.status == "available"
257
+ ]
258
+
259
+ if print_json:
260
+ json_output = [hardware.model_dump() for hardware in hardware_options]
261
+ click.echo(json.dumps(json_output, indent=2))
262
+ else:
263
+ for hardware in hardware_options:
264
+ click.echo(f" {hardware.id}", err=True)
265
+
266
+
267
+ @endpoints.command()
268
+ @click.argument("endpoint-id", required=True)
269
+ @click.option(
270
+ "--wait", is_flag=True, default=True, help="Wait for the endpoint to stop"
271
+ )
272
+ @click.pass_obj
273
+ @handle_api_errors
274
+ def stop(client: Together, endpoint_id: str, wait: bool) -> None:
275
+ """Stop a dedicated inference endpoint."""
276
+ client.endpoints.update(endpoint_id, state="STOPPED")
277
+ click.echo("Successfully marked endpoint as stopping", err=True)
278
+
279
+ if wait:
280
+ import time
281
+
282
+ click.echo("Waiting for endpoint to stop...", err=True)
283
+ while client.endpoints.get(endpoint_id).state != "STOPPED":
284
+ time.sleep(1)
285
+ click.echo("Endpoint stopped", err=True)
286
+
287
+ click.echo(endpoint_id)
288
+
289
+
290
+ @endpoints.command()
291
+ @click.argument("endpoint-id", required=True)
292
+ @click.option(
293
+ "--wait", is_flag=True, default=True, help="Wait for the endpoint to start"
294
+ )
295
+ @click.pass_obj
296
+ @handle_api_errors
297
+ def start(client: Together, endpoint_id: str, wait: bool) -> None:
298
+ """Start a dedicated inference endpoint."""
299
+ client.endpoints.update(endpoint_id, state="STARTED")
300
+ click.echo("Successfully marked endpoint as starting", err=True)
301
+
302
+ if wait:
303
+ import time
304
+
305
+ click.echo("Waiting for endpoint to start...", err=True)
306
+ while client.endpoints.get(endpoint_id).state != "STARTED":
307
+ time.sleep(1)
308
+ click.echo("Endpoint started", err=True)
309
+
310
+ click.echo(endpoint_id)
311
+
312
+
313
+ @endpoints.command()
314
+ @click.argument("endpoint-id", required=True)
315
+ @click.pass_obj
316
+ @handle_api_errors
317
+ def delete(client: Together, endpoint_id: str) -> None:
318
+ """Delete a dedicated inference endpoint."""
319
+ client.endpoints.delete(endpoint_id)
320
+ click.echo("Successfully deleted endpoint", err=True)
321
+ click.echo(endpoint_id)
322
+
323
+
324
+ @endpoints.command()
325
+ @click.option("--json", is_flag=True, help="Print output in JSON format")
326
+ @click.option(
327
+ "--type",
328
+ type=click.Choice(["dedicated", "serverless"]),
329
+ help="Filter by endpoint type",
330
+ )
331
+ @click.pass_obj
332
+ @handle_api_errors
333
+ def list(
334
+ client: Together, json: bool, type: Literal["dedicated", "serverless"] | None
335
+ ) -> None:
336
+ """List all inference endpoints (includes both dedicated and serverless endpoints)."""
337
+ endpoints: List[ListEndpoint] = client.endpoints.list(type=type)
338
+
339
+ if not endpoints:
340
+ click.echo("No dedicated endpoints found", err=True)
341
+ return
342
+
343
+ click.echo("Endpoints:", err=True)
344
+ if json:
345
+ import json as json_lib
346
+
347
+ click.echo(
348
+ json_lib.dumps([endpoint.model_dump() for endpoint in endpoints], indent=2)
349
+ )
350
+ else:
351
+ for endpoint in endpoints:
352
+ print_endpoint(
353
+ endpoint,
354
+ )
355
+ click.echo()
356
+
357
+
358
+ @endpoints.command()
359
+ @click.argument("endpoint-id", required=True)
360
+ @click.option(
361
+ "--display-name",
362
+ help="A new human-readable name for the endpoint",
363
+ )
364
+ @click.option(
365
+ "--min-replicas",
366
+ type=int,
367
+ help="New minimum number of replicas to maintain",
368
+ )
369
+ @click.option(
370
+ "--max-replicas",
371
+ type=int,
372
+ help="New maximum number of replicas to scale up to",
373
+ )
374
+ @click.pass_obj
375
+ @handle_api_errors
376
+ def update(
377
+ client: Together,
378
+ endpoint_id: str,
379
+ display_name: str | None,
380
+ min_replicas: int | None,
381
+ max_replicas: int | None,
382
+ ) -> None:
383
+ """Update a dedicated inference endpoint's configuration."""
384
+ if not any([display_name, min_replicas, max_replicas]):
385
+ click.echo("Error: At least one update option must be specified", err=True)
386
+ sys.exit(1)
387
+
388
+ # If only one of min/max replicas is specified, we need both for the update
389
+ if (min_replicas is None) != (max_replicas is None):
390
+ click.echo(
391
+ "Error: Both --min-replicas and --max-replicas must be specified together",
392
+ err=True,
393
+ )
394
+ sys.exit(1)
395
+
396
+ # Build kwargs for the update
397
+ kwargs: Dict[str, Any] = {}
398
+ if display_name is not None:
399
+ kwargs["display_name"] = display_name
400
+ if min_replicas is not None and max_replicas is not None:
401
+ kwargs["min_replicas"] = min_replicas
402
+ kwargs["max_replicas"] = max_replicas
403
+
404
+ _response = client.endpoints.update(endpoint_id, **kwargs)
405
+
406
+ # Print what was updated
407
+ click.echo("Updated endpoint configuration:", err=True)
408
+ if display_name:
409
+ click.echo(f" Display name: {display_name}", err=True)
410
+ if min_replicas is not None and max_replicas is not None:
411
+ click.echo(f" Min replicas: {min_replicas}", err=True)
412
+ click.echo(f" Max replicas: {max_replicas}", err=True)
413
+
414
+ click.echo("Successfully updated endpoint", err=True)
415
+ click.echo(endpoint_id)
@@ -8,6 +8,7 @@ import click
8
8
  import together
9
9
  from together.cli.api.chat import chat, interactive
10
10
  from together.cli.api.completions import completions
11
+ from together.cli.api.endpoints import endpoints
11
12
  from together.cli.api.files import files
12
13
  from together.cli.api.finetune import fine_tuning
13
14
  from together.cli.api.images import images
@@ -72,6 +73,7 @@ main.add_command(images)
72
73
  main.add_command(files)
73
74
  main.add_command(fine_tuning)
74
75
  main.add_command(models)
76
+ main.add_command(endpoints)
75
77
 
76
78
  if __name__ == "__main__":
77
79
  main()
@@ -81,6 +81,7 @@ class Together:
81
81
  self.fine_tuning = resources.FineTuning(self.client)
82
82
  self.rerank = resources.Rerank(self.client)
83
83
  self.audio = resources.Audio(self.client)
84
+ self.endpoints = resources.Endpoints(self.client)
84
85
 
85
86
 
86
87
  class AsyncTogether:
@@ -18,6 +18,9 @@ class TogetherException(Exception):
18
18
  request_id: str | None = None,
19
19
  http_status: int | None = None,
20
20
  ) -> None:
21
+ if isinstance(message, TogetherErrorResponse):
22
+ self.api_response = message
23
+
21
24
  _message = (
22
25
  json.dumps(message.model_dump(exclude_none=True))
23
26
  if isinstance(message, TogetherErrorResponse)
@@ -1,12 +1,13 @@
1
+ from together.resources.audio import AsyncAudio, Audio
1
2
  from together.resources.chat import AsyncChat, Chat
2
3
  from together.resources.completions import AsyncCompletions, Completions
3
4
  from together.resources.embeddings import AsyncEmbeddings, Embeddings
5
+ from together.resources.endpoints import AsyncEndpoints, Endpoints
4
6
  from together.resources.files import AsyncFiles, Files
5
7
  from together.resources.finetune import AsyncFineTuning, FineTuning
6
8
  from together.resources.images import AsyncImages, Images
7
9
  from together.resources.models import AsyncModels, Models
8
10
  from together.resources.rerank import AsyncRerank, Rerank
9
- from together.resources.audio import AsyncAudio, Audio
10
11
 
11
12
 
12
13
  __all__ = [
@@ -28,4 +29,6 @@ __all__ = [
28
29
  "Rerank",
29
30
  "AsyncAudio",
30
31
  "Audio",
32
+ "AsyncEndpoints",
33
+ "Endpoints",
31
34
  ]