together 2.0.0a10__py3-none-any.whl → 2.0.0a11__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.
together/_base_client.py CHANGED
@@ -1247,9 +1247,12 @@ class SyncAPIClient(BaseClient[httpx.Client, Stream[Any]]):
1247
1247
  *,
1248
1248
  cast_to: Type[ResponseT],
1249
1249
  body: Body | None = None,
1250
+ files: RequestFiles | None = None,
1250
1251
  options: RequestOptions = {},
1251
1252
  ) -> ResponseT:
1252
- opts = FinalRequestOptions.construct(method="patch", url=path, json_data=body, **options)
1253
+ opts = FinalRequestOptions.construct(
1254
+ method="patch", url=path, json_data=body, files=to_httpx_files(files), **options
1255
+ )
1253
1256
  return self.request(cast_to, opts)
1254
1257
 
1255
1258
  def put(
@@ -1767,9 +1770,12 @@ class AsyncAPIClient(BaseClient[httpx.AsyncClient, AsyncStream[Any]]):
1767
1770
  *,
1768
1771
  cast_to: Type[ResponseT],
1769
1772
  body: Body | None = None,
1773
+ files: RequestFiles | None = None,
1770
1774
  options: RequestOptions = {},
1771
1775
  ) -> ResponseT:
1772
- opts = FinalRequestOptions.construct(method="patch", url=path, json_data=body, **options)
1776
+ opts = FinalRequestOptions.construct(
1777
+ method="patch", url=path, json_data=body, files=to_httpx_files(files), **options
1778
+ )
1773
1779
  return await self.request(cast_to, opts)
1774
1780
 
1775
1781
  async def put(
together/_version.py CHANGED
@@ -1,4 +1,4 @@
1
1
  # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
2
2
 
3
3
  __title__ = "together"
4
- __version__ = "2.0.0-alpha.10" # x-release-please-version
4
+ __version__ = "2.0.0-alpha.11" # x-release-please-version
@@ -10,6 +10,7 @@ from textwrap import wrap
10
10
  import click
11
11
  from rich import print as rprint
12
12
  from tabulate import tabulate
13
+ from rich.json import JSON
13
14
  from click.core import ParameterSource # type: ignore[attr-defined]
14
15
 
15
16
  from together import Together
@@ -17,7 +18,7 @@ from together.types import fine_tuning_estimate_price_params as pe_params
17
18
  from together._types import NOT_GIVEN, NotGiven
18
19
  from together.lib.utils import log_warn
19
20
  from together.lib.utils.tools import format_timestamp, finetune_price_to_dollars
20
- from together.lib.cli.api.utils import INT_WITH_MAX, BOOL_WITH_AUTO
21
+ from together.lib.cli.api.utils import INT_WITH_MAX, BOOL_WITH_AUTO, generate_progress_bar
21
22
  from together.lib.resources.files import DownloadManager
22
23
  from together.lib.utils.serializer import datetime_serializer
23
24
  from together.types.finetune_response import TrainingTypeFullTrainingType, TrainingTypeLoRaTrainingType
@@ -361,7 +362,7 @@ def create(
361
362
  rpo_alpha=rpo_alpha or 0,
362
363
  simpo_gamma=simpo_gamma or 0,
363
364
  )
364
-
365
+
365
366
  finetune_price_estimation_result = client.fine_tuning.estimate_price(
366
367
  training_file=training_file,
367
368
  validation_file=validation_file,
@@ -425,6 +426,9 @@ def list(ctx: click.Context) -> None:
425
426
  "Price": f"""${
426
427
  finetune_price_to_dollars(float(str(i.total_price)))
427
428
  }""", # convert to string for mypy typing
429
+ "Progress": generate_progress_bar(
430
+ i, datetime.now().astimezone(), use_rich=False
431
+ ),
428
432
  }
429
433
  )
430
434
  table = tabulate(display_list, headers="keys", tablefmt="grid", showindex=True)
@@ -444,7 +448,12 @@ def retrieve(ctx: click.Context, fine_tune_id: str) -> None:
444
448
  # remove events from response for cleaner output
445
449
  response.events = None
446
450
 
447
- click.echo(json.dumps(response.model_dump(exclude_none=True), indent=4))
451
+ rprint(JSON.from_data(response.model_json_schema()))
452
+ progress_text = generate_progress_bar(
453
+ response, datetime.now().astimezone(), use_rich=True
454
+ )
455
+ prefix = f"Status: [bold]{response.status}[/bold],"
456
+ rprint(f"{prefix} {progress_text}")
448
457
 
449
458
 
450
459
  @fine_tuning.command()
@@ -1,18 +1,25 @@
1
1
  from __future__ import annotations
2
2
 
3
- from typing import Literal
3
+ import re
4
+ import math
5
+ from typing import List, Union, Literal
4
6
  from gettext import gettext as _
5
- from typing_extensions import override
7
+ from datetime import datetime
6
8
 
7
9
  import click
8
10
 
11
+ from together.lib.types.fine_tuning import COMPLETED_STATUSES, FinetuneResponse
12
+ from together.types.finetune_response import FinetuneResponse as _FinetuneResponse
13
+ from together.types.fine_tuning_list_response import Data
14
+
15
+ _PROGRESS_BAR_WIDTH = 40
16
+
9
17
 
10
18
  class AutoIntParamType(click.ParamType):
11
19
  name = "integer_or_max"
12
20
  _number_class = int
13
21
 
14
- @override
15
- def convert(
22
+ def convert( # pyright: ignore[reportImplicitOverride]
16
23
  self, value: str, param: click.Parameter | None, ctx: click.Context | None
17
24
  ) -> int | Literal["max"] | None:
18
25
  if value == "max":
@@ -21,7 +28,9 @@ class AutoIntParamType(click.ParamType):
21
28
  return int(value)
22
29
  except ValueError:
23
30
  self.fail(
24
- _("{value!r} is not a valid {number_type}.").format(value=value, number_type=self.name),
31
+ _("{value!r} is not a valid {number_type}.").format(
32
+ value=value, number_type=self.name
33
+ ),
25
34
  param,
26
35
  ctx,
27
36
  )
@@ -30,8 +39,7 @@ class AutoIntParamType(click.ParamType):
30
39
  class BooleanWithAutoParamType(click.ParamType):
31
40
  name = "boolean_or_auto"
32
41
 
33
- @override
34
- def convert(
42
+ def convert( # pyright: ignore[reportImplicitOverride]
35
43
  self, value: str, param: click.Parameter | None, ctx: click.Context | None
36
44
  ) -> bool | Literal["auto"] | None:
37
45
  if value == "auto":
@@ -40,7 +48,9 @@ class BooleanWithAutoParamType(click.ParamType):
40
48
  return bool(value)
41
49
  except ValueError:
42
50
  self.fail(
43
- _("{value!r} is not a valid {type}.").format(value=value, type=self.name),
51
+ _("{value!r} is not a valid {type}.").format(
52
+ value=value, type=self.name
53
+ ),
44
54
  param,
45
55
  ctx,
46
56
  )
@@ -48,3 +58,82 @@ class BooleanWithAutoParamType(click.ParamType):
48
58
 
49
59
  INT_WITH_MAX = AutoIntParamType()
50
60
  BOOL_WITH_AUTO = BooleanWithAutoParamType()
61
+
62
+
63
+ def _human_readable_time(timedelta: float) -> str:
64
+ """Convert a timedelta to a compact human-readble string
65
+ Examples:
66
+ 00:00:10 -> 10s
67
+ 01:23:45 -> 1h 23min 45s
68
+ 1 Month 23 days 04:56:07 -> 1month 23d 4h 56min 7s
69
+ Args:
70
+ timedelta (float): The timedelta in seconds to convert.
71
+ Returns:
72
+ A string representing the timedelta in a human-readable format.
73
+ """
74
+ units = [
75
+ (30 * 24 * 60 * 60, "month"), # 30 days
76
+ (24 * 60 * 60, "d"),
77
+ (60 * 60, "h"),
78
+ (60, "min"),
79
+ (1, "s"),
80
+ ]
81
+
82
+ total_seconds = int(timedelta)
83
+ parts: List[str] = []
84
+
85
+ for unit_seconds, unit_name in units:
86
+ if total_seconds >= unit_seconds:
87
+ value = total_seconds // unit_seconds
88
+ total_seconds %= unit_seconds
89
+ parts.append(f"{value}{unit_name}")
90
+
91
+ return " ".join(parts) if parts else "0s"
92
+
93
+
94
+ def generate_progress_bar(
95
+ finetune_job: Union[Data, FinetuneResponse, _FinetuneResponse], current_time: datetime, use_rich: bool = False
96
+ ) -> str:
97
+ """Generate a progress bar for a finetune job.
98
+ Args:
99
+ finetune_job: The finetune job to generate a progress bar for.
100
+ current_time: The current time.
101
+ use_rich: Whether to use rich formatting.
102
+ Returns:
103
+ A string representing the progress bar.
104
+ """
105
+ progress = "Progress: [bold red]unavailable[/bold red]"
106
+ if finetune_job.status in COMPLETED_STATUSES:
107
+ progress = "Progress: [bold green]completed[/bold green]"
108
+ elif finetune_job.updated_at is not None:
109
+ update_at = finetune_job.updated_at.astimezone()
110
+
111
+ if finetune_job.progress is not None:
112
+ if current_time < update_at:
113
+ return progress
114
+
115
+ if not finetune_job.progress.estimate_available:
116
+ return progress
117
+
118
+ if finetune_job.progress.seconds_remaining <= 0:
119
+ return progress
120
+
121
+ elapsed_time = (current_time - update_at).total_seconds()
122
+ ratio_filled = min(
123
+ elapsed_time / finetune_job.progress.seconds_remaining, 1.0
124
+ )
125
+ percentage = ratio_filled * 100
126
+ filled = math.ceil(ratio_filled * _PROGRESS_BAR_WIDTH)
127
+ bar = "█" * filled + "░" * (_PROGRESS_BAR_WIDTH - filled)
128
+ time_left = "N/A"
129
+ if finetune_job.progress.seconds_remaining > elapsed_time:
130
+ time_left = _human_readable_time(
131
+ finetune_job.progress.seconds_remaining - elapsed_time
132
+ )
133
+ time_text = f"{time_left} left"
134
+ progress = f"Progress: {bar} [bold]{percentage:>3.0f}%[/bold] [yellow]{time_text}[/yellow]"
135
+
136
+ if use_rich:
137
+ return progress
138
+
139
+ return re.sub(r"\[/?[^\]]+\]", "", progress)
together/lib/constants.py CHANGED
@@ -14,6 +14,9 @@ import enum
14
14
  # Download defaults
15
15
  DOWNLOAD_BLOCK_SIZE = 10 * 1024 * 1024 # 10 MB
16
16
  DISABLE_TQDM = False
17
+ MAX_DOWNLOAD_RETRIES = 5 # Maximum retries for download failures
18
+ DOWNLOAD_INITIAL_RETRY_DELAY = 1.0 # Initial retry delay in seconds
19
+ DOWNLOAD_MAX_RETRY_DELAY = 30.0 # Maximum retry delay in seconds
17
20
 
18
21
  # Upload defaults
19
22
  MAX_CONCURRENT_PARTS = 4 # Maximum concurrent parts for multipart upload
@@ -3,6 +3,7 @@ from __future__ import annotations
3
3
  import os
4
4
  import math
5
5
  import stat
6
+ import time
6
7
  import uuid
7
8
  import shutil
8
9
  import asyncio
@@ -29,12 +30,15 @@ from ..constants import (
29
30
  MAX_MULTIPART_PARTS,
30
31
  TARGET_PART_SIZE_MB,
31
32
  MAX_CONCURRENT_PARTS,
33
+ MAX_DOWNLOAD_RETRIES,
32
34
  MULTIPART_THRESHOLD_GB,
35
+ DOWNLOAD_MAX_RETRY_DELAY,
33
36
  MULTIPART_UPLOAD_TIMEOUT,
37
+ DOWNLOAD_INITIAL_RETRY_DELAY,
34
38
  )
35
39
  from ..._resource import SyncAPIResource, AsyncAPIResource
36
40
  from ..types.error import DownloadError, FileTypeError
37
- from ..._exceptions import APIStatusError, AuthenticationError
41
+ from ..._exceptions import APIStatusError, APIConnectionError, AuthenticationError
38
42
 
39
43
  log: logging.Logger = logging.getLogger(__name__)
40
44
 
@@ -198,6 +202,11 @@ class DownloadManager(SyncAPIResource):
198
202
 
199
203
  assert file_size != 0, "Unable to retrieve remote file."
200
204
 
205
+ # Download with retry logic
206
+ bytes_downloaded = 0
207
+ retry_count = 0
208
+ retry_delay = DOWNLOAD_INITIAL_RETRY_DELAY
209
+
201
210
  with tqdm(
202
211
  total=file_size,
203
212
  unit="B",
@@ -205,14 +214,64 @@ class DownloadManager(SyncAPIResource):
205
214
  desc=f"Downloading file {file_path.name}",
206
215
  disable=bool(DISABLE_TQDM),
207
216
  ) as pbar:
208
- for chunk in response.iter_bytes(DOWNLOAD_BLOCK_SIZE):
209
- pbar.update(len(chunk))
210
- temp_file.write(chunk) # type: ignore
217
+ while bytes_downloaded < file_size:
218
+ try:
219
+ # If this is a retry, close the previous response and create a new one with Range header
220
+ if bytes_downloaded > 0:
221
+ response.close()
222
+
223
+ log.info(f"Resuming download from byte {bytes_downloaded}")
224
+ response = self._client.get(
225
+ path=url,
226
+ cast_to=httpx.Response,
227
+ stream=True,
228
+ options=RequestOptions(
229
+ headers={"Range": f"bytes={bytes_downloaded}-"},
230
+ ),
231
+ )
232
+
233
+ # Download chunks
234
+ for chunk in response.iter_bytes(DOWNLOAD_BLOCK_SIZE):
235
+ temp_file.write(chunk) # type: ignore
236
+ bytes_downloaded += len(chunk)
237
+ pbar.update(len(chunk))
238
+
239
+ # Successfully completed download
240
+ break
241
+
242
+ except (httpx.RequestError, httpx.StreamError, APIConnectionError) as e:
243
+ if retry_count >= MAX_DOWNLOAD_RETRIES:
244
+ log.error(f"Download failed after {retry_count} retries")
245
+ raise DownloadError(
246
+ f"Download failed after {retry_count} retries. Last error: {str(e)}"
247
+ ) from e
248
+
249
+ retry_count += 1
250
+ log.warning(
251
+ f"Download interrupted at {bytes_downloaded}/{file_size} bytes. "
252
+ f"Retry {retry_count}/{MAX_DOWNLOAD_RETRIES} in {retry_delay}s..."
253
+ )
254
+ time.sleep(retry_delay)
255
+
256
+ # Exponential backoff with max delay cap
257
+ retry_delay = min(retry_delay * 2, DOWNLOAD_MAX_RETRY_DELAY)
258
+
259
+ except APIStatusError as e:
260
+ # For API errors, don't retry
261
+ log.error(f"API error during download: {e}")
262
+ raise APIStatusError(
263
+ "Error downloading file",
264
+ response=e.response,
265
+ body=e.response,
266
+ ) from e
267
+
268
+ # Close the response
269
+ response.close()
211
270
 
212
271
  # Raise exception if remote file size does not match downloaded file size
213
272
  if os.stat(temp_file.name).st_size != file_size:
214
- DownloadError(
215
- f"Downloaded file size `{pbar.n}` bytes does not match remote file size `{file_size}` bytes."
273
+ raise DownloadError(
274
+ f"Downloaded file size `{bytes_downloaded}` bytes does not match remote file size `{file_size}` bytes."
216
275
  )
217
276
 
218
277
  # Moves temp file to output file path
@@ -25,6 +25,14 @@ class FinetuneJobStatus(str, Enum):
25
25
  STATUS_COMPLETED = "completed"
26
26
 
27
27
 
28
+ COMPLETED_STATUSES = [
29
+ FinetuneJobStatus.STATUS_ERROR,
30
+ FinetuneJobStatus.STATUS_USER_ERROR,
31
+ FinetuneJobStatus.STATUS_COMPLETED,
32
+ FinetuneJobStatus.STATUS_CANCELLED,
33
+ ]
34
+
35
+
28
36
  class FinetuneEventType(str, Enum):
29
37
  """
30
38
  Fine-tune job event types
@@ -260,6 +268,15 @@ FinetuneLRScheduler: TypeAlias = Union[
260
268
  ]
261
269
 
262
270
 
271
+ class FinetuneProgress(BaseModel):
272
+ """
273
+ Fine-tune job progress
274
+ """
275
+
276
+ estimate_available: bool = False
277
+ seconds_remaining: float = 0
278
+
279
+
263
280
  class FinetuneResponse(BaseModel):
264
281
  """
265
282
  Fine-tune API response type
@@ -393,6 +410,8 @@ class FinetuneResponse(BaseModel):
393
410
  training_file_size: Optional[int] = Field(None, alias="TrainingFileSize")
394
411
  train_on_inputs: Union[StrictBool, Literal["auto"], None] = "auto"
395
412
 
413
+ progress: Union[FinetuneProgress, None] = None
414
+
396
415
  @classmethod
397
416
  def validate_training_type(cls, v: TrainingType) -> TrainingType:
398
417
  if v.type == "Full" or v.type == "":
@@ -47,7 +47,7 @@ class TranscriptionsResource(SyncAPIResource):
47
47
  def create(
48
48
  self,
49
49
  *,
50
- file: FileTypes,
50
+ file: Union[FileTypes, str],
51
51
  diarize: bool | Omit = omit,
52
52
  language: str | Omit = omit,
53
53
  max_speakers: int | Omit = omit,
@@ -68,7 +68,8 @@ class TranscriptionsResource(SyncAPIResource):
68
68
  Transcribes audio into text
69
69
 
70
70
  Args:
71
- file: Audio file to transcribe
71
+ file: Audio file upload or public HTTP/HTTPS URL. Supported formats .wav, .mp3, .m4a,
72
+ .webm, .flac.
72
73
 
73
74
  diarize: Whether to enable speaker diarization. When enabled, you will get the speaker id
74
75
  for each word in the transcription. In the response, in the words array, you
@@ -168,7 +169,7 @@ class AsyncTranscriptionsResource(AsyncAPIResource):
168
169
  async def create(
169
170
  self,
170
171
  *,
171
- file: FileTypes,
172
+ file: Union[FileTypes, str],
172
173
  diarize: bool | Omit = omit,
173
174
  language: str | Omit = omit,
174
175
  max_speakers: int | Omit = omit,
@@ -189,7 +190,8 @@ class AsyncTranscriptionsResource(AsyncAPIResource):
189
190
  Transcribes audio into text
190
191
 
191
192
  Args:
192
- file: Audio file to transcribe
193
+ file: Audio file upload or public HTTP/HTTPS URL. Supported formats .wav, .mp3, .m4a,
194
+ .webm, .flac.
193
195
 
194
196
  diarize: Whether to enable speaker diarization. When enabled, you will get the speaker id
195
197
  for each word in the transcription. In the response, in the words array, you
@@ -47,7 +47,7 @@ class TranslationsResource(SyncAPIResource):
47
47
  def create(
48
48
  self,
49
49
  *,
50
- file: FileTypes,
50
+ file: Union[FileTypes, str],
51
51
  language: str | Omit = omit,
52
52
  model: Literal["openai/whisper-large-v3"] | Omit = omit,
53
53
  prompt: str | Omit = omit,
@@ -65,7 +65,8 @@ class TranslationsResource(SyncAPIResource):
65
65
  Translates audio into English
66
66
 
67
67
  Args:
68
- file: Audio file to translate
68
+ file: Audio file upload or public HTTP/HTTPS URL. Supported formats .wav, .mp3, .m4a,
69
+ .webm, .flac.
69
70
 
70
71
  language: Target output language. Optional ISO 639-1 language code. If omitted, language
71
72
  is set to English.
@@ -145,7 +146,7 @@ class AsyncTranslationsResource(AsyncAPIResource):
145
146
  async def create(
146
147
  self,
147
148
  *,
148
- file: FileTypes,
149
+ file: Union[FileTypes, str],
149
150
  language: str | Omit = omit,
150
151
  model: Literal["openai/whisper-large-v3"] | Omit = omit,
151
152
  prompt: str | Omit = omit,
@@ -163,7 +164,8 @@ class AsyncTranslationsResource(AsyncAPIResource):
163
164
  Translates audio into English
164
165
 
165
166
  Args:
166
- file: Audio file to translate
167
+ file: Audio file upload or public HTTP/HTTPS URL. Supported formats .wav, .mp3, .m4a,
168
+ .webm, .flac.
167
169
 
168
170
  language: Target output language. Optional ISO 639-1 language code. If omitted, language
169
171
  is set to English.
@@ -11,8 +11,11 @@ __all__ = ["TranscriptionCreateParams"]
11
11
 
12
12
 
13
13
  class TranscriptionCreateParams(TypedDict, total=False):
14
- file: Required[FileTypes]
15
- """Audio file to transcribe"""
14
+ file: Required[Union[FileTypes, str]]
15
+ """Audio file upload or public HTTP/HTTPS URL.
16
+
17
+ Supported formats .wav, .mp3, .m4a, .webm, .flac.
18
+ """
16
19
 
17
20
  diarize: bool
18
21
  """Whether to enable speaker diarization.
@@ -11,8 +11,11 @@ __all__ = ["TranslationCreateParams"]
11
11
 
12
12
 
13
13
  class TranslationCreateParams(TypedDict, total=False):
14
- file: Required[FileTypes]
15
- """Audio file to translate"""
14
+ file: Required[Union[FileTypes, str]]
15
+ """Audio file upload or public HTTP/HTTPS URL.
16
+
17
+ Supported formats .wav, .mp3, .m4a, .webm, .flac.
18
+ """
16
19
 
17
20
  language: str
18
21
  """Target output language.
@@ -15,6 +15,7 @@ __all__ = [
15
15
  "LrSchedulerLrSchedulerArgs",
16
16
  "LrSchedulerLrSchedulerArgsLinearLrSchedulerArgs",
17
17
  "LrSchedulerLrSchedulerArgsCosineLrSchedulerArgs",
18
+ "Progress",
18
19
  "TrainingMethod",
19
20
  "TrainingMethodTrainingMethodSft",
20
21
  "TrainingMethodTrainingMethodDpo",
@@ -50,6 +51,16 @@ class LrScheduler(BaseModel):
50
51
  lr_scheduler_args: Optional[LrSchedulerLrSchedulerArgs] = None
51
52
 
52
53
 
54
+ class Progress(BaseModel):
55
+ """Progress information for the fine-tuning job"""
56
+
57
+ estimate_available: bool
58
+ """Whether time estimate is available"""
59
+
60
+ seconds_remaining: int
61
+ """Estimated time remaining in seconds for the fine-tuning job to next state"""
62
+
63
+
53
64
  class TrainingMethodTrainingMethodSft(BaseModel):
54
65
  method: Literal["sft"]
55
66
 
@@ -163,6 +174,9 @@ class FineTuningCancelResponse(BaseModel):
163
174
  owner_address: Optional[str] = None
164
175
  """Owner address information"""
165
176
 
177
+ progress: Optional[Progress] = None
178
+ """Progress information for the fine-tuning job"""
179
+
166
180
  suffix: Optional[str] = None
167
181
  """Suffix added to the fine-tuned model name"""
168
182
 
@@ -16,6 +16,7 @@ __all__ = [
16
16
  "DataLrSchedulerLrSchedulerArgs",
17
17
  "DataLrSchedulerLrSchedulerArgsLinearLrSchedulerArgs",
18
18
  "DataLrSchedulerLrSchedulerArgsCosineLrSchedulerArgs",
19
+ "DataProgress",
19
20
  "DataTrainingMethod",
20
21
  "DataTrainingMethodTrainingMethodSft",
21
22
  "DataTrainingMethodTrainingMethodDpo",
@@ -51,6 +52,16 @@ class DataLrScheduler(BaseModel):
51
52
  lr_scheduler_args: Optional[DataLrSchedulerLrSchedulerArgs] = None
52
53
 
53
54
 
55
+ class DataProgress(BaseModel):
56
+ """Progress information for the fine-tuning job"""
57
+
58
+ estimate_available: bool
59
+ """Whether time estimate is available"""
60
+
61
+ seconds_remaining: int
62
+ """Estimated time remaining in seconds for the fine-tuning job to next state"""
63
+
64
+
54
65
  class DataTrainingMethodTrainingMethodSft(BaseModel):
55
66
  method: Literal["sft"]
56
67
 
@@ -164,6 +175,9 @@ class Data(BaseModel):
164
175
  owner_address: Optional[str] = None
165
176
  """Owner address information"""
166
177
 
178
+ progress: Optional[DataProgress] = None
179
+ """Progress information for the fine-tuning job"""
180
+
167
181
  suffix: Optional[str] = None
168
182
  """Suffix added to the fine-tuned model name"""
169
183
 
@@ -1,6 +1,7 @@
1
1
  # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
2
2
 
3
3
  from typing import List, Union, Optional
4
+ from datetime import datetime
4
5
  from typing_extensions import Literal, TypeAlias
5
6
 
6
7
  from pydantic import Field as FieldInfo
@@ -14,6 +15,7 @@ __all__ = [
14
15
  "LrSchedulerLrSchedulerArgs",
15
16
  "LrSchedulerLrSchedulerArgsLinearLrSchedulerArgs",
16
17
  "LrSchedulerLrSchedulerArgsCosineLrSchedulerArgs",
18
+ "Progress",
17
19
  "TrainingMethod",
18
20
  "TrainingMethodTrainingMethodSft",
19
21
  "TrainingMethodTrainingMethodDpo",
@@ -47,6 +49,16 @@ class LrScheduler(BaseModel):
47
49
  lr_scheduler_args: Optional[LrSchedulerLrSchedulerArgs] = None
48
50
 
49
51
 
52
+ class Progress(BaseModel):
53
+ """Progress information for a fine-tuning job"""
54
+
55
+ estimate_available: bool
56
+ """Whether time estimate is available"""
57
+
58
+ seconds_remaining: int
59
+ """Estimated time remaining in seconds for the fine-tuning job to next state"""
60
+
61
+
50
62
  class TrainingMethodTrainingMethodSft(BaseModel):
51
63
  method: Literal["sft"]
52
64
 
@@ -110,7 +122,7 @@ class FinetuneResponse(BaseModel):
110
122
 
111
123
  batch_size: Union[int, Literal["max"], None] = None
112
124
 
113
- created_at: Optional[str] = None
125
+ created_at: Optional[datetime] = None
114
126
 
115
127
  epochs_completed: Optional[int] = None
116
128
 
@@ -146,6 +158,9 @@ class FinetuneResponse(BaseModel):
146
158
 
147
159
  param_count: Optional[int] = None
148
160
 
161
+ progress: Optional[Progress] = None
162
+ """Progress information for a fine-tuning job"""
163
+
149
164
  queue_depth: Optional[int] = None
150
165
 
151
166
  token_count: Optional[int] = None
@@ -164,7 +179,7 @@ class FinetuneResponse(BaseModel):
164
179
 
165
180
  trainingfile_size: Optional[int] = None
166
181
 
167
- updated_at: Optional[str] = None
182
+ updated_at: Optional[datetime] = None
168
183
 
169
184
  validation_file: Optional[str] = None
170
185
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: together
3
- Version: 2.0.0a10
3
+ Version: 2.0.0a11
4
4
  Summary: The official Python library for the together API
5
5
  Project-URL: Homepage, https://github.com/togethercomputer/together-py
6
6
  Project-URL: Repository, https://github.com/togethercomputer/together-py
@@ -1,5 +1,5 @@
1
1
  together/__init__.py,sha256=ghwEH6EUrPERUwHVSXaCJVqS7QmLN7NsUxKJNXQrOYM,2842
2
- together/_base_client.py,sha256=D18Ze-MTwyuEjCRc8jPXF8Iv7G0exCB3XzMHz6HCsgI,67049
2
+ together/_base_client.py,sha256=yP8nFDC-tFtGBrXHhcfcW4xF7KzAV5GQCLKlCZyp8sg,67237
3
3
  together/_client.py,sha256=Nw2fyh2kf3RABNaHuqWKKhTujaHttGXoUZENECGaqyI,37788
4
4
  together/_compat.py,sha256=DQBVORjFb33zch24jzkhM14msvnzY7mmSmgDLaVFUM8,6562
5
5
  together/_constants.py,sha256=i39tJ7BP8nnqvdHFJwMhN6LWR6-jg5LLYiFudwCD3Ic,463
@@ -11,7 +11,7 @@ together/_resource.py,sha256=-ZTq9O5qf2YsgjJk_gwJs-CM_OG4p6gdMLcNWjuxFwQ,1112
11
11
  together/_response.py,sha256=lvqEsCbpD8SRJTjlhhUFGbnLUR_4-Qva-OApxfVdiY4,28800
12
12
  together/_streaming.py,sha256=sk6fVYbpdO3Y-0S5iwZTHQJ3N24UkK0KaupgUTftWZk,11825
13
13
  together/_types.py,sha256=LzaeqN09mUAEvRg_XrLzihdOaW0D_R9qrG7jKsFjnQY,7297
14
- together/_version.py,sha256=XmBi_Hs7NKx8HCFG-ERQy7tKZ3NfYmSV0kJTKf_6-o0,169
14
+ together/_version.py,sha256=F4XFYcFCYn77Ecwy5AT-x99HNaeVVRSV5vbTWx512a8,169
15
15
  together/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
16
16
  together/_utils/__init__.py,sha256=7fch0GT9zpNnErbciSpUNa-SjTxxjY6kxHxKMOM4AGs,2305
17
17
  together/_utils/_compat.py,sha256=rN17SSvjMoQE1GmKFTLniRuG1sKj2WAD5VjdLPeRlF0,1231
@@ -27,22 +27,22 @@ together/_utils/_typing.py,sha256=N_5PPuFNsaygbtA_npZd98SVN1LQQvFTKL6bkWPBZGU,47
27
27
  together/_utils/_utils.py,sha256=g9ftElB09kVT6EVfCIlD_nUfANhDX5_vZO61FDWoIQI,12334
28
28
  together/lib/.keep,sha256=wuNrz-5SXo3jJaJOJgz4vFHM41YH_g20F5cRQo0vLes,224
29
29
  together/lib/__init__.py,sha256=Qtdi6geFNzxE-F51eNDk1ESXYyYDt8b82MR1POANQBQ,394
30
- together/lib/constants.py,sha256=EgcTlmk4QqVqjZjGej5k5JEwoRqidlITQ8LQqzW0dXI,1795
30
+ together/lib/constants.py,sha256=pFWjUtItT-6_-Gpky0P2Q7bSj4iRmApbSqkcYetE7V0,1996
31
31
  together/lib/cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
32
32
  together/lib/cli/cli.py,sha256=bNzYeLF8JdlMnSmIqFClp28MzjLGCwQ9hqSpaXHBQ0s,1939
33
33
  together/lib/cli/api/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
34
34
  together/lib/cli/api/endpoints.py,sha256=MyliUrTuJWw2qFd80J27pFs9xTazIVAP0mqgRYxdVsw,14851
35
35
  together/lib/cli/api/evals.py,sha256=KkSvz2wIYmPQ3sFQBte6inNBZt1aptIkMVL5TKWTW5k,19074
36
36
  together/lib/cli/api/files.py,sha256=HbflC45PpzBIF0CE0TLucQaVr319ScL05VyAFKf2T6Y,3596
37
- together/lib/cli/api/fine_tuning.py,sha256=XlkS8cUrlKIJAsPgtlSitDSCd66X86zRcT4bTk-36u4,22202
37
+ together/lib/cli/api/fine_tuning.py,sha256=-PdNR_16WiwI_wZkqemb_m5SRceRuSvpOdEL8LGFGJU,22571
38
38
  together/lib/cli/api/models.py,sha256=Jfrl7gcbWAkbBQ1i1gCy485HHT2C4C784OMaaHZPiPw,4084
39
- together/lib/cli/api/utils.py,sha256=qppHd3MdATVA7MI32e_--OVeFQdftCq9s9tvKz8xaNY,1317
39
+ together/lib/cli/api/utils.py,sha256=1W9Ebc_deJ6vss1IBGa80SFx61xENCyvnYRYwjCZ_Xo,4524
40
40
  together/lib/resources/__init__.py,sha256=ystIb0pBHQLuwUBtHJwhRgtjK3_TV6K0KuM8NGuuNoU,172
41
- together/lib/resources/files.py,sha256=jM7Ra43eCKAa4pu9xA-1rPtP-0fXs8CkUq-hc6X8CaA,34418
41
+ together/lib/resources/files.py,sha256=Z_D23IvjYYWBpYrfYolCNfUslJBcE4PnU0WtuLsN67M,37277
42
42
  together/lib/resources/fine_tuning.py,sha256=-yCS6AIbgXpAcUkeqJcia8CPrnVcmyiaUN4dvksEjcc,12642
43
43
  together/lib/types/__init__.py,sha256=1-kHsAp9Sh9HxjTGKfdHnF1nTS_cM_Tazv-3Z9hrEbY,205
44
44
  together/lib/types/error.py,sha256=i-rnTZPRZuJDUf1lM-52abG2JHWOUBTCh55zPNGoakg,135
45
- together/lib/types/fine_tuning.py,sha256=DN8mTSna3ptO-gQJBy_MnAZ-Zw6jOwZNuA73eVBt5l8,12555
45
+ together/lib/types/fine_tuning.py,sha256=uK_hke1huHjbjzQQBzz7EYMj1UHgCFVS0o7AG5L5xwY,12942
46
46
  together/lib/utils/__init__.py,sha256=F_CVqnvK-aEshMg-5FLFincPbhuVbsM6IKSCNyEByKs,545
47
47
  together/lib/utils/_log.py,sha256=mo5tDhyFTNqEj8MOcpy3bLmLBcC0OQ67orTw_nxFdcU,1930
48
48
  together/lib/utils/files.py,sha256=S6orZixBPeRtV_iq_IktuYHIm41irrHuOuexz_NYZJ0,24863
@@ -65,8 +65,8 @@ together/resources/videos.py,sha256=AdcC08JrUtbcEJV-G0viH4CF1qU9oNjdjQ7U38QCEkU,
65
65
  together/resources/audio/__init__.py,sha256=MKUWFwFsAdCf9LrO8AiUCeIzdknPNDPr4lpAt-pkYSw,2521
66
66
  together/resources/audio/audio.py,sha256=stpvzuxIwMnAQLQnqW1KRxx3G_DI-oDSnx3uDN_X1R8,7180
67
67
  together/resources/audio/speech.py,sha256=ZavAHDhi8rKzIQ0tRTv1UOIlUJQ5_ArvH3JG1JdN41M,27560
68
- together/resources/audio/transcriptions.py,sha256=k5zLDKXNqISjeieSyi1FKbyKsWyhnkeIkApG3l7QzeY,12965
69
- together/resources/audio/translations.py,sha256=zeV1wJPGzBmQGGgSPNA_vigy_4yuV3aBq6sSLa19-jg,10251
68
+ together/resources/audio/transcriptions.py,sha256=HtegYl2NUfx_fph-iqKkQ5GKm-3V4yQagBKueS8IIqI,13155
69
+ together/resources/audio/translations.py,sha256=VPkg5ZUDw5LZwiaRYqEjETKwSMMU1odTeStl5PZSrls,10443
70
70
  together/resources/audio/voices.py,sha256=Lj9DtOcv_Dhaq3E5p7Oty1T_JkhrsGDZcDF91HHA3Yw,4905
71
71
  together/resources/chat/__init__.py,sha256=BVAfz9TM3DT5W9f_mt0P9YRxL_MsUxKCWAH6u1iogmA,1041
72
72
  together/resources/chat/chat.py,sha256=aJQQ4Zlof2NlrG53auI5omXPJVdG3c_vwnEkj-ahLG4,3688
@@ -106,7 +106,7 @@ together/types/file_list.py,sha256=AE9muto7B4HyABgt3k9obSbUG1GW09pVvB0POnEQeUg,2
106
106
  together/types/file_purpose.py,sha256=9sHEbQ1qy4V99KMT7R5Ya_VXutu1ZZG1pho-w2ZZ9OM,302
107
107
  together/types/file_response.py,sha256=Abmu-Ph-masbhAFePB64VhiswHEFmExWF34jaiTm4Lg,626
108
108
  together/types/file_type.py,sha256=lrvFtcJr0Wn5thWP4NihmrmK23AouK2e6Ev4LCCPg5A,218
109
- together/types/fine_tuning_cancel_response.py,sha256=POZIRboDF3WBj6IHGaKpyUvKux7Kt3Fkuwo9TRKJn5Q,5423
109
+ together/types/fine_tuning_cancel_response.py,sha256=gTrdjW-UdZ1nN151V4YJeDlozcq8FhKEZV6fw2pCflU,5806
110
110
  together/types/fine_tuning_content_params.py,sha256=_5rqZ2lk6FShmX-5Hj4A9jQdpPZP6lcgjXvnrC30G8k,711
111
111
  together/types/fine_tuning_delete_params.py,sha256=YwUcN_gFl9N2zuUJqyOrE0ngONL2N_OgfVw6h-sH2RE,273
112
112
  together/types/fine_tuning_delete_response.py,sha256=oWoJM51-2b_RIINhJSMyMelSKQkHFYrJjDLeD51dUgo,323
@@ -114,10 +114,10 @@ together/types/fine_tuning_estimate_price_params.py,sha256=LIlP1Z5C1EAQgOg3b3BZ1
114
114
  together/types/fine_tuning_estimate_price_response.py,sha256=lA2MUesE_C_Ia8U-rJRsqRGRzkZJno2dsIsrMmoQMIo,770
115
115
  together/types/fine_tuning_list_checkpoints_response.py,sha256=9S0kRl7ItqFU6CeodrB9jb1zgf7-Ehb7VGjsbKq_hBY,377
116
116
  together/types/fine_tuning_list_events_response.py,sha256=DeDJLF1IxQV47HOwfuVt8Zis5W2CKs3iKkKvwDxyswk,309
117
- together/types/fine_tuning_list_response.py,sha256=4owaCLVcmOWWlSVEQ7x3DZlJ-PPMlIymWkfj8vZ8ObI,5597
117
+ together/types/fine_tuning_list_response.py,sha256=vdVZBQ4V1GFzHUBiRR0drEvKg1ASTyG5g84P0oM1S_c,5992
118
118
  together/types/finetune_event.py,sha256=0apAXe6Anx2_ffse2pOBJDxngCeuSvuDMYczZ0DtzZg,787
119
119
  together/types/finetune_event_type.py,sha256=Bm4lkBhsLI_kaD5yabsvW6BpnjXzZO_lwDtiEeMNXnw,824
120
- together/types/finetune_response.py,sha256=BrdlilnNFAenIR73cbu9Oj2IIIgudqTD_Qcpo_KGqmg,4289
120
+ together/types/finetune_response.py,sha256=7W2_HBLCleIAF_u-qwvh8f4QEMsVyYkwtSPO3BqFI2Q,4708
121
121
  together/types/hardware_list_params.py,sha256=BbfiigtdQE0QNGFGr6o-Twg912x_riH5mbUNpZWYWO4,435
122
122
  together/types/hardware_list_response.py,sha256=KfGhnEy7qEW2Bzt4Q8b-JSvxG-IKIIFfcpWEQHS9YdM,1732
123
123
  together/types/image_data_b64.py,sha256=pLY7JDBb1HF1T29ACbae_xn6JQfttpqQVeG_jJeenZU,284
@@ -143,9 +143,9 @@ together/types/video_create_params.py,sha256=9Mx7TWTaPHOOpaMezz9FD5VC7hN6jGbnynG
143
143
  together/types/video_job.py,sha256=49S_V4lygUJUZR2x8vr_L39mUZV6YisXK3kuHEQXHOo,1740
144
144
  together/types/audio/__init__.py,sha256=FRPjWqhXrrSZgg615cnF6cWNqEowSovw__2V7BR3kgo,654
145
145
  together/types/audio/speech_create_params.py,sha256=SwoTcRMG5NnO_LpT3eAXFfOqqxyFh6C-cU8mtY2v4lk,2867
146
- together/types/audio/transcription_create_params.py,sha256=utzevQa2y_fiznVYVIUK7J76HzH_YWgLsvtbtFIH_Gs,2156
146
+ together/types/audio/transcription_create_params.py,sha256=pAC6G0jQHpgRBa17T-gNFbZl-FbSJ8ZUEZDqKx6kEkU,2247
147
147
  together/types/audio/transcription_create_response.py,sha256=z8_pzJlzYjP4QxJhwbKuDgAeVpWbgee6jt3QLFVVSjM,3059
148
- together/types/audio/translation_create_params.py,sha256=RjKaaR2RNSE4DxuBHCBKDURxxqalZJmApIhtmDV7MBM,1140
148
+ together/types/audio/translation_create_params.py,sha256=6-iHFC2K2o72K5tj0lfD-Lb69JzV4_5t_x2sdj2Pafs,1232
149
149
  together/types/audio/translation_create_response.py,sha256=T6SUCExVMin1qSGamHuiWGWS84MZ92tZPBHD7NYm4IU,1843
150
150
  together/types/audio/voice_list_response.py,sha256=vS2yvGBz7U2cxnJkEr7BewT7j5ActDjn99k3QhhEKk4,517
151
151
  together/types/chat/__init__.py,sha256=XuRK_yunfmDRsbxLftYqP_yl51m8hpM6iqPGJGnUx_Y,997
@@ -159,8 +159,8 @@ together/types/chat/chat_completion_warning.py,sha256=_Dp7YKlxyY2HeZopTvT-Go7qqK
159
159
  together/types/chat/completion_create_params.py,sha256=Ia2dWuVxjderCRfpqV4Zpl-9rVng0p8deILUXVC3O0s,13687
160
160
  together/types/code_interpreter/__init__.py,sha256=dAXfb3ryLMtcBalCfxxNu2wJVswVP8G1xXryZnahPQY,201
161
161
  together/types/code_interpreter/session_list_response.py,sha256=TRxLGFTmIY-KLpStKjJtsrm4EI6BBvakpx43B6pkhnw,662
162
- together-2.0.0a10.dist-info/METADATA,sha256=w9kCFvckPuJ7x5S6JIi7g6O9-Ah9ixwOVCgQajuHp9Q,20247
163
- together-2.0.0a10.dist-info/WHEEL,sha256=C2FUgwZgiLbznR-k0b_5k3Ai_1aASOXDss3lzCUsUug,87
164
- together-2.0.0a10.dist-info/entry_points.txt,sha256=4f4RAX89wQkx3AnfHXiGrKyg2fCPnwMd2UdPX48OczA,55
165
- together-2.0.0a10.dist-info/licenses/LICENSE,sha256=5I5MO2DiiBFcD_p4ZF2T4GDb-WeBMD591ALtADdtXDc,11338
166
- together-2.0.0a10.dist-info/RECORD,,
162
+ together-2.0.0a11.dist-info/METADATA,sha256=YQHojaT_Y39KC8RAq-bktHXAKF3SUO4YEd2jrscrp9U,20247
163
+ together-2.0.0a11.dist-info/WHEEL,sha256=C2FUgwZgiLbznR-k0b_5k3Ai_1aASOXDss3lzCUsUug,87
164
+ together-2.0.0a11.dist-info/entry_points.txt,sha256=4f4RAX89wQkx3AnfHXiGrKyg2fCPnwMd2UdPX48OczA,55
165
+ together-2.0.0a11.dist-info/licenses/LICENSE,sha256=5I5MO2DiiBFcD_p4ZF2T4GDb-WeBMD591ALtADdtXDc,11338
166
+ together-2.0.0a11.dist-info/RECORD,,