together 1.5.30__tar.gz → 1.5.31__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 (76) hide show
  1. {together-1.5.30 → together-1.5.31}/PKG-INFO +1 -1
  2. {together-1.5.30 → together-1.5.31}/pyproject.toml +1 -1
  3. {together-1.5.30 → together-1.5.31}/src/together/cli/api/chat.py +24 -2
  4. {together-1.5.30 → together-1.5.31}/src/together/cli/api/endpoints.py +7 -4
  5. {together-1.5.30 → together-1.5.31}/src/together/constants.py +3 -3
  6. {together-1.5.30 → together-1.5.31}/src/together/filemanager.py +45 -22
  7. {together-1.5.30 → together-1.5.31}/src/together/types/chat_completions.py +6 -0
  8. {together-1.5.30 → together-1.5.31}/src/together/types/common.py +1 -0
  9. {together-1.5.30 → together-1.5.31}/src/together/types/files.py +1 -0
  10. {together-1.5.30 → together-1.5.31}/src/together/utils/files.py +8 -2
  11. {together-1.5.30 → together-1.5.31}/LICENSE +0 -0
  12. {together-1.5.30 → together-1.5.31}/README.md +0 -0
  13. {together-1.5.30 → together-1.5.31}/src/together/__init__.py +0 -0
  14. {together-1.5.30 → together-1.5.31}/src/together/abstract/__init__.py +0 -0
  15. {together-1.5.30 → together-1.5.31}/src/together/abstract/api_requestor.py +0 -0
  16. {together-1.5.30 → together-1.5.31}/src/together/cli/__init__.py +0 -0
  17. {together-1.5.30 → together-1.5.31}/src/together/cli/api/__init__.py +0 -0
  18. {together-1.5.30 → together-1.5.31}/src/together/cli/api/completions.py +0 -0
  19. {together-1.5.30 → together-1.5.31}/src/together/cli/api/evaluation.py +0 -0
  20. {together-1.5.30 → together-1.5.31}/src/together/cli/api/files.py +0 -0
  21. {together-1.5.30 → together-1.5.31}/src/together/cli/api/finetune.py +0 -0
  22. {together-1.5.30 → together-1.5.31}/src/together/cli/api/images.py +0 -0
  23. {together-1.5.30 → together-1.5.31}/src/together/cli/api/models.py +0 -0
  24. {together-1.5.30 → together-1.5.31}/src/together/cli/api/utils.py +0 -0
  25. {together-1.5.30 → together-1.5.31}/src/together/cli/cli.py +0 -0
  26. {together-1.5.30 → together-1.5.31}/src/together/client.py +0 -0
  27. {together-1.5.30 → together-1.5.31}/src/together/error.py +0 -0
  28. {together-1.5.30 → together-1.5.31}/src/together/legacy/__init__.py +0 -0
  29. {together-1.5.30 → together-1.5.31}/src/together/legacy/base.py +0 -0
  30. {together-1.5.30 → together-1.5.31}/src/together/legacy/complete.py +0 -0
  31. {together-1.5.30 → together-1.5.31}/src/together/legacy/embeddings.py +0 -0
  32. {together-1.5.30 → together-1.5.31}/src/together/legacy/files.py +0 -0
  33. {together-1.5.30 → together-1.5.31}/src/together/legacy/finetune.py +0 -0
  34. {together-1.5.30 → together-1.5.31}/src/together/legacy/images.py +0 -0
  35. {together-1.5.30 → together-1.5.31}/src/together/legacy/models.py +0 -0
  36. {together-1.5.30 → together-1.5.31}/src/together/resources/__init__.py +0 -0
  37. {together-1.5.30 → together-1.5.31}/src/together/resources/audio/__init__.py +0 -0
  38. {together-1.5.30 → together-1.5.31}/src/together/resources/audio/speech.py +0 -0
  39. {together-1.5.30 → together-1.5.31}/src/together/resources/audio/transcriptions.py +0 -0
  40. {together-1.5.30 → together-1.5.31}/src/together/resources/audio/translations.py +0 -0
  41. {together-1.5.30 → together-1.5.31}/src/together/resources/audio/voices.py +0 -0
  42. {together-1.5.30 → together-1.5.31}/src/together/resources/batch.py +0 -0
  43. {together-1.5.30 → together-1.5.31}/src/together/resources/chat/__init__.py +0 -0
  44. {together-1.5.30 → together-1.5.31}/src/together/resources/chat/completions.py +0 -0
  45. {together-1.5.30 → together-1.5.31}/src/together/resources/code_interpreter.py +0 -0
  46. {together-1.5.30 → together-1.5.31}/src/together/resources/completions.py +0 -0
  47. {together-1.5.30 → together-1.5.31}/src/together/resources/embeddings.py +0 -0
  48. {together-1.5.30 → together-1.5.31}/src/together/resources/endpoints.py +0 -0
  49. {together-1.5.30 → together-1.5.31}/src/together/resources/evaluation.py +0 -0
  50. {together-1.5.30 → together-1.5.31}/src/together/resources/files.py +0 -0
  51. {together-1.5.30 → together-1.5.31}/src/together/resources/finetune.py +0 -0
  52. {together-1.5.30 → together-1.5.31}/src/together/resources/images.py +0 -0
  53. {together-1.5.30 → together-1.5.31}/src/together/resources/models.py +0 -0
  54. {together-1.5.30 → together-1.5.31}/src/together/resources/rerank.py +0 -0
  55. {together-1.5.30 → together-1.5.31}/src/together/resources/videos.py +0 -0
  56. {together-1.5.30 → together-1.5.31}/src/together/together_response.py +0 -0
  57. {together-1.5.30 → together-1.5.31}/src/together/types/__init__.py +0 -0
  58. {together-1.5.30 → together-1.5.31}/src/together/types/abstract.py +0 -0
  59. {together-1.5.30 → together-1.5.31}/src/together/types/audio_speech.py +0 -0
  60. {together-1.5.30 → together-1.5.31}/src/together/types/batch.py +0 -0
  61. {together-1.5.30 → together-1.5.31}/src/together/types/code_interpreter.py +0 -0
  62. {together-1.5.30 → together-1.5.31}/src/together/types/completions.py +0 -0
  63. {together-1.5.30 → together-1.5.31}/src/together/types/embeddings.py +0 -0
  64. {together-1.5.30 → together-1.5.31}/src/together/types/endpoints.py +0 -0
  65. {together-1.5.30 → together-1.5.31}/src/together/types/error.py +0 -0
  66. {together-1.5.30 → together-1.5.31}/src/together/types/evaluation.py +0 -0
  67. {together-1.5.30 → together-1.5.31}/src/together/types/finetune.py +0 -0
  68. {together-1.5.30 → together-1.5.31}/src/together/types/images.py +0 -0
  69. {together-1.5.30 → together-1.5.31}/src/together/types/models.py +0 -0
  70. {together-1.5.30 → together-1.5.31}/src/together/types/rerank.py +0 -0
  71. {together-1.5.30 → together-1.5.31}/src/together/types/videos.py +0 -0
  72. {together-1.5.30 → together-1.5.31}/src/together/utils/__init__.py +0 -0
  73. {together-1.5.30 → together-1.5.31}/src/together/utils/_log.py +0 -0
  74. {together-1.5.30 → together-1.5.31}/src/together/utils/api_helpers.py +0 -0
  75. {together-1.5.30 → together-1.5.31}/src/together/utils/tools.py +0 -0
  76. {together-1.5.30 → together-1.5.31}/src/together/version.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: together
3
- Version: 1.5.30
3
+ Version: 1.5.31
4
4
  Summary: Python client for Together's Cloud Platform!
5
5
  License: Apache-2.0
6
6
  License-File: LICENSE
@@ -12,7 +12,7 @@ build-backend = "poetry.masonry.api"
12
12
 
13
13
  [tool.poetry]
14
14
  name = "together"
15
- version = "1.5.30"
15
+ version = "1.5.31"
16
16
  authors = ["Together AI <support@together.ai>"]
17
17
  description = "Python client for Together's Cloud Platform!"
18
18
  readme = "README.md"
@@ -2,7 +2,7 @@ from __future__ import annotations
2
2
 
3
3
  import cmd
4
4
  import json
5
- from typing import List, Tuple
5
+ from typing import Any, Dict, List, Tuple
6
6
 
7
7
  import click
8
8
 
@@ -181,6 +181,12 @@ def interactive(
181
181
  "--frequency-penalty", type=float, help="Frequency penalty sampling method"
182
182
  )
183
183
  @click.option("--min-p", type=float, help="Min p sampling")
184
+ @click.option(
185
+ "--audio-url",
186
+ type=str,
187
+ multiple=True,
188
+ help="Audio URL to attach to the last user message",
189
+ )
184
190
  @click.option("--no-stream", is_flag=True, help="Disable streaming")
185
191
  @click.option("--logprobs", type=int, help="Return logprobs. Only works with --raw.")
186
192
  @click.option("--echo", is_flag=True, help="Echo prompt. Only works with --raw.")
@@ -200,6 +206,7 @@ def chat(
200
206
  presence_penalty: float | None = None,
201
207
  frequency_penalty: float | None = None,
202
208
  min_p: float | None = None,
209
+ audio_url: List[str] | None = None,
203
210
  no_stream: bool = False,
204
211
  logprobs: int | None = None,
205
212
  echo: bool | None = None,
@@ -210,7 +217,22 @@ def chat(
210
217
  """Generate chat completions from messages"""
211
218
  client: Together = ctx.obj
212
219
 
213
- messages = [{"role": msg[0], "content": msg[1]} for msg in message]
220
+ messages: List[Dict[str, Any]] = [
221
+ {"role": msg[0], "content": msg[1]} for msg in message
222
+ ]
223
+
224
+ if audio_url and messages:
225
+ last_msg = messages[-1]
226
+ if last_msg["role"] == "user":
227
+ # Convert content to list if it is string
228
+ if isinstance(last_msg["content"], str):
229
+ last_msg["content"] = [{"type": "text", "text": last_msg["content"]}]
230
+
231
+ # Append audio URLs
232
+ for url in audio_url:
233
+ last_msg["content"].append(
234
+ {"type": "audio_url", "audio_url": {"url": url}}
235
+ )
214
236
 
215
237
  response = client.chat.completions.create(
216
238
  model=model,
@@ -137,8 +137,7 @@ def endpoints(ctx: click.Context) -> None:
137
137
  help="Start endpoint in specified availability zone (e.g., us-central-4b)",
138
138
  )
139
139
  @click.option(
140
- "--wait",
141
- is_flag=True,
140
+ "--wait/--no-wait",
142
141
  default=True,
143
142
  help="Wait for the endpoint to be ready after creation",
144
143
  )
@@ -284,7 +283,9 @@ def fetch_and_print_hardware_options(
284
283
  @endpoints.command()
285
284
  @click.argument("endpoint-id", required=True)
286
285
  @click.option(
287
- "--wait", is_flag=True, default=True, help="Wait for the endpoint to stop"
286
+ "--wait/--no-wait",
287
+ default=True,
288
+ help="Wait for the endpoint to stop",
288
289
  )
289
290
  @click.pass_obj
290
291
  @handle_api_errors
@@ -307,7 +308,9 @@ def stop(client: Together, endpoint_id: str, wait: bool) -> None:
307
308
  @endpoints.command()
308
309
  @click.argument("endpoint-id", required=True)
309
310
  @click.option(
310
- "--wait", is_flag=True, default=True, help="Wait for the endpoint to start"
311
+ "--wait/--no-wait",
312
+ default=True,
313
+ help="Wait for the endpoint to start",
311
314
  )
312
315
  @click.pass_obj
313
316
  @handle_api_errors
@@ -20,13 +20,13 @@ MAX_CONCURRENT_PARTS = 4 # Maximum concurrent parts for multipart upload
20
20
 
21
21
  # Multipart upload constants
22
22
  MIN_PART_SIZE_MB = 5 # Minimum part size (S3 requirement)
23
- TARGET_PART_SIZE_MB = 100 # Target part size for optimal performance
24
- MAX_MULTIPART_PARTS = 250 # Maximum parts per upload (S3 limit)
23
+ TARGET_PART_SIZE_MB = 250 # Target part size
24
+ MAX_MULTIPART_PARTS = 250 # Maximum parts per upload
25
25
  MULTIPART_UPLOAD_TIMEOUT = 300 # Timeout in seconds for uploading each part
26
26
  MULTIPART_THRESHOLD_GB = 5.0 # threshold for switching to multipart upload
27
27
 
28
28
  # maximum number of GB sized files we support finetuning for
29
- MAX_FILE_SIZE_GB = 25.0
29
+ MAX_FILE_SIZE_GB = 50.1
30
30
 
31
31
 
32
32
  # Messages
@@ -6,10 +6,10 @@ import shutil
6
6
  import stat
7
7
  import tempfile
8
8
  import uuid
9
- from concurrent.futures import ThreadPoolExecutor, as_completed
9
+ from concurrent.futures import Future, ThreadPoolExecutor, as_completed
10
10
  from functools import partial
11
11
  from pathlib import Path
12
- from typing import Any, Dict, List, Tuple
12
+ from typing import Any, BinaryIO, Dict, List, Tuple
13
13
 
14
14
  import requests
15
15
  from filelock import FileLock
@@ -212,6 +212,7 @@ class DownloadManager:
212
212
  ),
213
213
  remaining_retries=MAX_RETRIES,
214
214
  stream=True,
215
+ request_timeout=3600,
215
216
  )
216
217
 
217
218
  try:
@@ -512,6 +513,18 @@ class MultipartUploadManager:
512
513
 
513
514
  return response.data
514
515
 
516
+ def _submit_part(
517
+ self,
518
+ executor: ThreadPoolExecutor,
519
+ f: BinaryIO,
520
+ part_info: Dict[str, Any],
521
+ part_size: int,
522
+ ) -> Future[str]:
523
+ """Submit a single part for upload and return the future"""
524
+ f.seek((part_info["PartNumber"] - 1) * part_size)
525
+ part_data = f.read(part_size)
526
+ return executor.submit(self._upload_single_part, part_info, part_data)
527
+
515
528
  def _upload_parts_concurrent(
516
529
  self, file: Path, upload_info: Dict[str, Any], part_size: int
517
530
  ) -> List[Dict[str, Any]]:
@@ -522,29 +535,39 @@ class MultipartUploadManager:
522
535
 
523
536
  with ThreadPoolExecutor(max_workers=self.max_concurrent_parts) as executor:
524
537
  with tqdm(total=len(parts), desc="Uploading parts", unit="part") as pbar:
525
- future_to_part = {}
526
-
527
538
  with open(file, "rb") as f:
528
- for part_info in parts:
529
- f.seek((part_info["PartNumber"] - 1) * part_size)
530
- part_data = f.read(part_size)
539
+ future_to_part = {}
540
+ part_index = 0
531
541
 
532
- future = executor.submit(
533
- self._upload_single_part, part_info, part_data
534
- )
542
+ # Submit initial batch limited by max_concurrent_parts
543
+ for _ in range(min(self.max_concurrent_parts, len(parts))):
544
+ part_info = parts[part_index]
545
+ future = self._submit_part(executor, f, part_info, part_size)
535
546
  future_to_part[future] = part_info["PartNumber"]
536
-
537
- # Collect results
538
- for future in as_completed(future_to_part):
539
- part_number = future_to_part[future]
540
- try:
541
- etag = future.result()
542
- completed_parts.append(
543
- {"part_number": part_number, "etag": etag}
544
- )
545
- pbar.update(1)
546
- except Exception as e:
547
- raise Exception(f"Failed to upload part {part_number}: {e}")
547
+ part_index += 1
548
+
549
+ # Process completions and submit new parts (sliding window)
550
+ while future_to_part:
551
+ done_future = next(as_completed(future_to_part))
552
+ part_number = future_to_part.pop(done_future)
553
+
554
+ try:
555
+ etag = done_future.result()
556
+ completed_parts.append(
557
+ {"part_number": part_number, "etag": etag}
558
+ )
559
+ pbar.update(1)
560
+ except Exception as e:
561
+ raise Exception(f"Failed to upload part {part_number}: {e}")
562
+
563
+ # Submit next part if available
564
+ if part_index < len(parts):
565
+ part_info = parts[part_index]
566
+ future = self._submit_part(
567
+ executor, f, part_info, part_size
568
+ )
569
+ future_to_part[future] = part_info["PartNumber"]
570
+ part_index += 1
548
571
 
549
572
  completed_parts.sort(key=lambda x: x["part_number"])
550
573
  return completed_parts
@@ -46,6 +46,7 @@ class ChatCompletionMessageContentType(str, Enum):
46
46
  TEXT = "text"
47
47
  IMAGE_URL = "image_url"
48
48
  VIDEO_URL = "video_url"
49
+ AUDIO_URL = "audio_url"
49
50
 
50
51
 
51
52
  class ChatCompletionMessageContentImageURL(BaseModel):
@@ -56,11 +57,16 @@ class ChatCompletionMessageContentVideoURL(BaseModel):
56
57
  url: str
57
58
 
58
59
 
60
+ class ChatCompletionMessageContentAudioURL(BaseModel):
61
+ url: str
62
+
63
+
59
64
  class ChatCompletionMessageContent(BaseModel):
60
65
  type: ChatCompletionMessageContentType
61
66
  text: str | None = None
62
67
  image_url: ChatCompletionMessageContentImageURL | None = None
63
68
  video_url: ChatCompletionMessageContentVideoURL | None = None
69
+ audio_url: ChatCompletionMessageContentAudioURL | None = None
64
70
 
65
71
 
66
72
  class ChatCompletionMessage(BaseModel):
@@ -26,6 +26,7 @@ class UsageData(BaseModel):
26
26
 
27
27
 
28
28
  class ObjectType(str, Enum):
29
+ TextCompletion = "text_completion"
29
30
  Completion = "text.completion"
30
31
  CompletionChunk = "completion.chunk"
31
32
  ChatCompletion = "chat.completion"
@@ -15,6 +15,7 @@ class FilePurpose(str, Enum):
15
15
  FineTune = "fine-tune"
16
16
  BatchAPI = "batch-api"
17
17
  Eval = "eval"
18
+ EvalOutput = "eval-output"
18
19
 
19
20
 
20
21
  class FileType(str, Enum):
@@ -7,6 +7,7 @@ from pathlib import Path
7
7
  from traceback import format_exc
8
8
  from typing import Any, Dict, List
9
9
 
10
+ from tqdm import tqdm
10
11
 
11
12
  from together.constants import (
12
13
  MAX_FILE_SIZE_GB,
@@ -363,14 +364,19 @@ def _check_utf8(file: Path) -> Dict[str, Any]:
363
364
  Dict[str, Any]: A dictionary with the results of the check.
364
365
  """
365
366
  report_dict: Dict[str, Any] = {}
367
+
366
368
  try:
369
+ # Dry-run UTF-8 decode: iterate through file to validate encoding
367
370
  with file.open(encoding="utf-8") as f:
368
- f.read()
371
+ for _ in f:
372
+ pass
373
+
369
374
  report_dict["utf8"] = True
370
375
  except UnicodeDecodeError as e:
371
376
  report_dict["utf8"] = False
372
377
  report_dict["message"] = f"File is not UTF-8 encoded. Error raised: {e}."
373
378
  report_dict["is_check_passed"] = False
379
+
374
380
  return report_dict
375
381
 
376
382
 
@@ -470,7 +476,7 @@ def _check_jsonl(file: Path, purpose: FilePurpose | str) -> Dict[str, Any]:
470
476
  with file.open() as f:
471
477
  idx = -1
472
478
  try:
473
- for idx, line in enumerate(f):
479
+ for idx, line in tqdm(enumerate(f), desc="Validating file", unit=" lines"):
474
480
  json_line = json.loads(line)
475
481
 
476
482
  if not isinstance(json_line, dict):
File without changes
File without changes