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.
- together/cli/api/finetune.py +14 -2
- together/cli/api/utils.py +88 -0
- together/types/finetune.py +21 -2
- {together-1.5.32.dist-info → together-1.5.33.dist-info}/METADATA +1 -1
- {together-1.5.32.dist-info → together-1.5.33.dist-info}/RECORD +8 -8
- {together-1.5.32.dist-info → together-1.5.33.dist-info}/WHEEL +0 -0
- {together-1.5.32.dist-info → together-1.5.33.dist-info}/entry_points.txt +0 -0
- {together-1.5.32.dist-info → together-1.5.33.dist-info}/licenses/LICENSE +0 -0
together/cli/api/finetune.py
CHANGED
|
@@ -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
|
-
|
|
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)
|
together/types/finetune.py
CHANGED
|
@@ -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:
|
|
322
|
-
training_method:
|
|
340
|
+
training_type: LoRATrainingType | FullTrainingType
|
|
341
|
+
training_method: TrainingMethodSFT | TrainingMethodDPO
|
|
323
342
|
|
|
324
343
|
|
|
325
344
|
class FinetunePriceEstimationResponse(BaseModel):
|
|
@@ -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=
|
|
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=
|
|
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=
|
|
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.
|
|
74
|
-
together-1.5.
|
|
75
|
-
together-1.5.
|
|
76
|
-
together-1.5.
|
|
77
|
-
together-1.5.
|
|
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,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|