together 1.5.32__py3-none-any.whl → 1.5.33__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.
@@ -9,10 +9,11 @@ from typing import Any, Literal
9
9
  import click
10
10
  from click.core import ParameterSource # type: ignore[attr-defined]
11
11
  from rich import print as rprint
12
+ from rich.json import JSON
12
13
  from tabulate import tabulate
13
14
 
14
15
  from together import Together
15
- from together.cli.api.utils import BOOL_WITH_AUTO, INT_WITH_MAX
16
+ from together.cli.api.utils import BOOL_WITH_AUTO, INT_WITH_MAX, generate_progress_bar
16
17
  from together.types.finetune import (
17
18
  DownloadCheckpointType,
18
19
  FinetuneEventType,
@@ -435,6 +436,9 @@ def list(ctx: click.Context) -> None:
435
436
  "Price": f"""${
436
437
  finetune_price_to_dollars(float(str(i.total_price)))
437
438
  }""", # convert to string for mypy typing
439
+ "Progress": generate_progress_bar(
440
+ i, datetime.now().astimezone(), use_rich=False
441
+ ),
438
442
  }
439
443
  )
440
444
  table = tabulate(display_list, headers="keys", tablefmt="grid", showindex=True)
@@ -454,7 +458,15 @@ def retrieve(ctx: click.Context, fine_tune_id: str) -> None:
454
458
  # remove events from response for cleaner output
455
459
  response.events = None
456
460
 
457
- click.echo(json.dumps(response.model_dump(exclude_none=True), indent=4))
461
+ rprint(JSON.from_data(response.model_dump(exclude_none=True)))
462
+ progress_text = generate_progress_bar(
463
+ response, datetime.now().astimezone(), use_rich=True
464
+ )
465
+ status = "Unknown"
466
+ if response.status is not None:
467
+ status = response.status.value
468
+ prefix = f"Status: [bold]{status}[/bold],"
469
+ rprint(f"{prefix} {progress_text}")
458
470
 
459
471
 
460
472
  @fine_tuning.command()
together/cli/api/utils.py CHANGED
@@ -1,10 +1,17 @@
1
1
  from __future__ import annotations
2
2
 
3
+ import math
4
+ import re
3
5
  from gettext import gettext as _
4
6
  from typing import Literal
7
+ from datetime import datetime
5
8
 
6
9
  import click
7
10
 
11
+ from together.types.finetune import FinetuneResponse, COMPLETED_STATUSES
12
+
13
+ _PROGRESS_BAR_WIDTH = 40
14
+
8
15
 
9
16
  class AutoIntParamType(click.ParamType):
10
17
  name = "integer_or_max"
@@ -49,3 +56,84 @@ class BooleanWithAutoParamType(click.ParamType):
49
56
 
50
57
  INT_WITH_MAX = AutoIntParamType()
51
58
  BOOL_WITH_AUTO = BooleanWithAutoParamType()
59
+
60
+
61
+ def _human_readable_time(timedelta: float) -> str:
62
+ """Convert a timedelta to a compact human-readble string
63
+ Examples:
64
+ 00:00:10 -> 10s
65
+ 01:23:45 -> 1h 23min 45s
66
+ 1 Month 23 days 04:56:07 -> 1month 23d 4h 56min 7s
67
+ Args:
68
+ timedelta (float): The timedelta in seconds to convert.
69
+ Returns:
70
+ A string representing the timedelta in a human-readable format.
71
+ """
72
+ units = [
73
+ (30 * 24 * 60 * 60, "month"), # 30 days
74
+ (24 * 60 * 60, "d"),
75
+ (60 * 60, "h"),
76
+ (60, "min"),
77
+ (1, "s"),
78
+ ]
79
+
80
+ total_seconds = int(timedelta)
81
+ parts = []
82
+
83
+ for unit_seconds, unit_name in units:
84
+ if total_seconds >= unit_seconds:
85
+ value = total_seconds // unit_seconds
86
+ total_seconds %= unit_seconds
87
+ parts.append(f"{value}{unit_name}")
88
+
89
+ return " ".join(parts) if parts else "0s"
90
+
91
+
92
+ def generate_progress_bar(
93
+ finetune_job: FinetuneResponse, current_time: datetime, use_rich: bool = False
94
+ ) -> str:
95
+ """Generate a progress bar for a finetune job.
96
+ Args:
97
+ finetune_job: The finetune job to generate a progress bar for.
98
+ current_time: The current time.
99
+ use_rich: Whether to use rich formatting.
100
+ Returns:
101
+ A string representing the progress bar.
102
+ """
103
+ progress = "Progress: [bold red]unavailable[/bold red]"
104
+ if finetune_job.status in COMPLETED_STATUSES:
105
+ progress = "Progress: [bold green]completed[/bold green]"
106
+ elif finetune_job.updated_at is not None:
107
+ # Replace 'Z' with '+00:00' for Python 3.10 compatibility
108
+ updated_at_str = finetune_job.updated_at.replace("Z", "+00:00")
109
+ update_at = datetime.fromisoformat(updated_at_str).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)
@@ -28,6 +28,14 @@ class FinetuneJobStatus(str, Enum):
28
28
  STATUS_COMPLETED = "completed"
29
29
 
30
30
 
31
+ COMPLETED_STATUSES = [
32
+ FinetuneJobStatus.STATUS_ERROR,
33
+ FinetuneJobStatus.STATUS_USER_ERROR,
34
+ FinetuneJobStatus.STATUS_COMPLETED,
35
+ FinetuneJobStatus.STATUS_CANCELLED,
36
+ ]
37
+
38
+
31
39
  class FinetuneEventLevels(str, Enum):
32
40
  """
33
41
  Fine-tune job event status levels
@@ -167,6 +175,15 @@ class TrainingMethodDPO(TrainingMethod):
167
175
  simpo_gamma: float | None = None
168
176
 
169
177
 
178
+ class FinetuneProgress(BaseModel):
179
+ """
180
+ Fine-tune job progress
181
+ """
182
+
183
+ estimate_available: bool = False
184
+ seconds_remaining: float = 0
185
+
186
+
170
187
  class FinetuneRequest(BaseModel):
171
188
  """
172
189
  Fine-tune request type
@@ -297,6 +314,8 @@ class FinetuneResponse(BaseModel):
297
314
  train_on_inputs: StrictBool | Literal["auto"] | None = "auto"
298
315
  from_checkpoint: str | None = None
299
316
 
317
+ progress: FinetuneProgress | None = None
318
+
300
319
  @field_validator("training_type")
301
320
  @classmethod
302
321
  def validate_training_type(cls, v: TrainingType) -> TrainingType:
@@ -318,8 +337,8 @@ class FinetunePriceEstimationRequest(BaseModel):
318
337
  model: str
319
338
  n_epochs: int
320
339
  n_evals: int
321
- training_type: TrainingType
322
- training_method: TrainingMethod
340
+ training_type: LoRATrainingType | FullTrainingType
341
+ training_method: TrainingMethodSFT | TrainingMethodDPO
323
342
 
324
343
 
325
344
  class FinetunePriceEstimationResponse(BaseModel):
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: together
3
- Version: 1.5.32
3
+ Version: 1.5.33
4
4
  Summary: Python client for Together's Cloud Platform! Note: SDK 2.0 is now available at https://github.com/togethercomputer/together-py
5
5
  License: Apache-2.0
6
6
  License-File: LICENSE
@@ -8,10 +8,10 @@ together/cli/api/completions.py,sha256=l-Zw5t7hojL3w8xd_mitS2NRB72i5Z0xwkzH0rT5X
8
8
  together/cli/api/endpoints.py,sha256=S3px19iGTKy5KS1nuKrvUUMoqc_KtrZHyIwjwjqX7uQ,14624
9
9
  together/cli/api/evaluation.py,sha256=36SsujC5qicf-8l8GA8wqRtEC8NKzsAjL-_nYhePpQM,14691
10
10
  together/cli/api/files.py,sha256=QLYEXRkY8J2Gg1SbTCtzGfoTMvosoeACNK83L_oLubs,3397
11
- together/cli/api/finetune.py,sha256=Hmn8UrDNCPiLPDilnKPjnx8V27WliAVTZgQKb6SnHwc,19625
11
+ together/cli/api/finetune.py,sha256=FQtMioZkgD_c2Qy4cftSw5IICcGgidbh8tAiGWPN88Q,20097
12
12
  together/cli/api/images.py,sha256=GADSeaNUHUVMtWovmccGuKc28IJ9E_v4vAEwYHJhu5o,2645
13
13
  together/cli/api/models.py,sha256=BRWRiguuJ8OwAD8crajpZ7RyCHA35tyOZvi3iLWQ7k4,3679
14
- together/cli/api/utils.py,sha256=IuqYWPnLI38_Bqd7lj8V_SnGdYc59pRmMbQmciS4FsM,1326
14
+ together/cli/api/utils.py,sha256=MRK6siAmDtVuXz4Vi0Jkf9RhlSwB2Sjk88OK34Z-I6E,4388
15
15
  together/cli/cli.py,sha256=PVahUjOfAQIjo209FoPKljcCA_OIpOYQ9MAsCjfEMu0,2134
16
16
  together/client.py,sha256=KD33kAPkWTcnXjge4rLK_L3UsJYsxNUkvL6b9SgTEf0,6324
17
17
  together/constants.py,sha256=IaKMIamFia9nyq8jPrmqu5y0YL5mC_474AAIUXYFsdk,1964
@@ -59,7 +59,7 @@ together/types/endpoints.py,sha256=EzNhHOoQ_D9fUdNQtxQPeSWiFzdFLqpNodN0YLmv_h0,4
59
59
  together/types/error.py,sha256=OVlCs3cx_2WhZK4JzHT8SQyRIIqKOP1AZQ4y1PydjAE,370
60
60
  together/types/evaluation.py,sha256=9gCAgzAwFD95MWnSgvxnSYFF27wKOTqIGn-wSOpFt2M,2385
61
61
  together/types/files.py,sha256=_pB_q8kU5QH7WE3Y8Uro6LGsgK_5zrGYzJREZL9cRH0,2025
62
- together/types/finetune.py,sha256=vpbmyRRV0gJryi0F7YUIbUk5Ya8CPmi0mJ95ZjpfpbE,11959
62
+ together/types/finetune.py,sha256=HQ1FL16DkFIaC84bdbwEWEJUwvFnPTcarEwYtI0VNQg,12386
63
63
  together/types/images.py,sha256=IsrmIM2FVeG-kP4vhZUx5fG5EhOJ-d8fefrAmOVKNDs,926
64
64
  together/types/models.py,sha256=V8bcy1c3uTmqwnTVphbYLF2AJ6l2P2724njl36TzfHQ,2878
65
65
  together/types/rerank.py,sha256=qZfuXOn7MZ6ly8hpJ_MZ7OU_Bi1-cgYNSB20Wja8Qkk,1061
@@ -70,8 +70,8 @@ together/utils/api_helpers.py,sha256=2K0O6qeEQ2zVFvi5NBN5m2kjZJaS3-JfKFecQ7SmGaw
70
70
  together/utils/files.py,sha256=mWFFpsgVPDQg1ZCb-oTrDUFv3aXg1AItgtwXvDsFegI,25047
71
71
  together/utils/tools.py,sha256=H2MTJhEqtBllaDvOyZehIO_IVNK3P17rSDeILtJIVag,2964
72
72
  together/version.py,sha256=p03ivHyE0SyWU4jAnRTBi_sOwywVWoZPU4g2gzRgG-Y,126
73
- together-1.5.32.dist-info/METADATA,sha256=lQExfe_6VE3LiQDX6E3zbVVsNwlPZ2vzQMuxtTaV7M8,17415
74
- together-1.5.32.dist-info/WHEEL,sha256=zp0Cn7JsFoX2ATtOhtaFYIiE2rmFAD4OcMhtUki8W3U,88
75
- together-1.5.32.dist-info/entry_points.txt,sha256=G-b5NKW6lUUf1V1fH8IPTBb7jXnK7lhbX9H1zTEJXPs,50
76
- together-1.5.32.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
77
- together-1.5.32.dist-info/RECORD,,
73
+ together-1.5.33.dist-info/METADATA,sha256=9syOtPDummSAPlru8OCACrQH4L1A6hY_VtA66JD8qKw,17415
74
+ together-1.5.33.dist-info/WHEEL,sha256=zp0Cn7JsFoX2ATtOhtaFYIiE2rmFAD4OcMhtUki8W3U,88
75
+ together-1.5.33.dist-info/entry_points.txt,sha256=G-b5NKW6lUUf1V1fH8IPTBb7jXnK7lhbX9H1zTEJXPs,50
76
+ together-1.5.33.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
77
+ together-1.5.33.dist-info/RECORD,,