together 2.0.0a11__py3-none-any.whl → 2.0.0a13__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 +1 -1
- together/_version.py +1 -1
- together/lib/cli/api/fine_tuning.py +14 -6
- together/lib/cli/api/utils.py +5 -13
- together/lib/constants.py +6 -0
- together/lib/resources/fine_tuning.py +15 -1
- together/lib/types/fine_tuning.py +17 -0
- together/lib/utils/files.py +187 -29
- together/resources/chat/completions.py +20 -0
- together/resources/fine_tuning.py +25 -17
- together/types/chat/completion_create_params.py +4 -0
- together/types/finetune_response.py +11 -0
- {together-2.0.0a11.dist-info → together-2.0.0a13.dist-info}/METADATA +3 -3
- {together-2.0.0a11.dist-info → together-2.0.0a13.dist-info}/RECORD +17 -17
- {together-2.0.0a11.dist-info → together-2.0.0a13.dist-info}/licenses/LICENSE +1 -1
- {together-2.0.0a11.dist-info → together-2.0.0a13.dist-info}/WHEEL +0 -0
- {together-2.0.0a11.dist-info → together-2.0.0a13.dist-info}/entry_points.txt +0 -0
together/_base_client.py
CHANGED
|
@@ -1774,7 +1774,7 @@ class AsyncAPIClient(BaseClient[httpx.AsyncClient, AsyncStream[Any]]):
|
|
|
1774
1774
|
options: RequestOptions = {},
|
|
1775
1775
|
) -> ResponseT:
|
|
1776
1776
|
opts = FinalRequestOptions.construct(
|
|
1777
|
-
method="patch", url=path, json_data=body, files=
|
|
1777
|
+
method="patch", url=path, json_data=body, files=await async_to_httpx_files(files), **options
|
|
1778
1778
|
)
|
|
1779
1779
|
return await self.request(cast_to, opts)
|
|
1780
1780
|
|
together/_version.py
CHANGED
|
@@ -176,6 +176,12 @@ def fine_tuning(ctx: click.Context) -> None:
|
|
|
176
176
|
help="Whether to mask the user messages in conversational data or prompts in instruction data. "
|
|
177
177
|
"`auto` will automatically determine whether to mask the inputs based on the data format.",
|
|
178
178
|
)
|
|
179
|
+
@click.option(
|
|
180
|
+
"--train-vision",
|
|
181
|
+
type=bool,
|
|
182
|
+
default=False,
|
|
183
|
+
help="Whether to train the vision encoder. Only supported for multimodal models.",
|
|
184
|
+
)
|
|
179
185
|
@click.option(
|
|
180
186
|
"--from-checkpoint",
|
|
181
187
|
type=str,
|
|
@@ -231,6 +237,7 @@ def create(
|
|
|
231
237
|
lora_dropout: float | None,
|
|
232
238
|
lora_alpha: float | None,
|
|
233
239
|
lora_trainable_modules: str | None,
|
|
240
|
+
train_vision: bool,
|
|
234
241
|
suffix: str | None,
|
|
235
242
|
wandb_api_key: str | None,
|
|
236
243
|
wandb_base_url: str | None,
|
|
@@ -272,6 +279,7 @@ def create(
|
|
|
272
279
|
lora_dropout=lora_dropout,
|
|
273
280
|
lora_alpha=lora_alpha,
|
|
274
281
|
lora_trainable_modules=lora_trainable_modules,
|
|
282
|
+
train_vision=train_vision,
|
|
275
283
|
suffix=suffix,
|
|
276
284
|
wandb_api_key=wandb_api_key,
|
|
277
285
|
wandb_base_url=wandb_base_url,
|
|
@@ -363,6 +371,10 @@ def create(
|
|
|
363
371
|
simpo_gamma=simpo_gamma or 0,
|
|
364
372
|
)
|
|
365
373
|
|
|
374
|
+
if model_limits.supports_vision:
|
|
375
|
+
# Don't show price estimation for multimodal models yet
|
|
376
|
+
confirm = True
|
|
377
|
+
|
|
366
378
|
finetune_price_estimation_result = client.fine_tuning.estimate_price(
|
|
367
379
|
training_file=training_file,
|
|
368
380
|
validation_file=validation_file,
|
|
@@ -426,9 +438,7 @@ def list(ctx: click.Context) -> None:
|
|
|
426
438
|
"Price": f"""${
|
|
427
439
|
finetune_price_to_dollars(float(str(i.total_price)))
|
|
428
440
|
}""", # convert to string for mypy typing
|
|
429
|
-
"Progress": generate_progress_bar(
|
|
430
|
-
i, datetime.now().astimezone(), use_rich=False
|
|
431
|
-
),
|
|
441
|
+
"Progress": generate_progress_bar(i, datetime.now().astimezone(), use_rich=False),
|
|
432
442
|
}
|
|
433
443
|
)
|
|
434
444
|
table = tabulate(display_list, headers="keys", tablefmt="grid", showindex=True)
|
|
@@ -449,9 +459,7 @@ def retrieve(ctx: click.Context, fine_tune_id: str) -> None:
|
|
|
449
459
|
response.events = None
|
|
450
460
|
|
|
451
461
|
rprint(JSON.from_data(response.model_json_schema()))
|
|
452
|
-
progress_text = generate_progress_bar(
|
|
453
|
-
response, datetime.now().astimezone(), use_rich=True
|
|
454
|
-
)
|
|
462
|
+
progress_text = generate_progress_bar(response, datetime.now().astimezone(), use_rich=True)
|
|
455
463
|
prefix = f"Status: [bold]{response.status}[/bold],"
|
|
456
464
|
rprint(f"{prefix} {progress_text}")
|
|
457
465
|
|
together/lib/cli/api/utils.py
CHANGED
|
@@ -28,9 +28,7 @@ class AutoIntParamType(click.ParamType):
|
|
|
28
28
|
return int(value)
|
|
29
29
|
except ValueError:
|
|
30
30
|
self.fail(
|
|
31
|
-
_("{value!r} is not a valid {number_type}.").format(
|
|
32
|
-
value=value, number_type=self.name
|
|
33
|
-
),
|
|
31
|
+
_("{value!r} is not a valid {number_type}.").format(value=value, number_type=self.name),
|
|
34
32
|
param,
|
|
35
33
|
ctx,
|
|
36
34
|
)
|
|
@@ -39,7 +37,7 @@ class AutoIntParamType(click.ParamType):
|
|
|
39
37
|
class BooleanWithAutoParamType(click.ParamType):
|
|
40
38
|
name = "boolean_or_auto"
|
|
41
39
|
|
|
42
|
-
def convert(
|
|
40
|
+
def convert( # pyright: ignore[reportImplicitOverride]
|
|
43
41
|
self, value: str, param: click.Parameter | None, ctx: click.Context | None
|
|
44
42
|
) -> bool | Literal["auto"] | None:
|
|
45
43
|
if value == "auto":
|
|
@@ -48,9 +46,7 @@ class BooleanWithAutoParamType(click.ParamType):
|
|
|
48
46
|
return bool(value)
|
|
49
47
|
except ValueError:
|
|
50
48
|
self.fail(
|
|
51
|
-
_("{value!r} is not a valid {type}.").format(
|
|
52
|
-
value=value, type=self.name
|
|
53
|
-
),
|
|
49
|
+
_("{value!r} is not a valid {type}.").format(value=value, type=self.name),
|
|
54
50
|
param,
|
|
55
51
|
ctx,
|
|
56
52
|
)
|
|
@@ -119,17 +115,13 @@ def generate_progress_bar(
|
|
|
119
115
|
return progress
|
|
120
116
|
|
|
121
117
|
elapsed_time = (current_time - update_at).total_seconds()
|
|
122
|
-
ratio_filled = min(
|
|
123
|
-
elapsed_time / finetune_job.progress.seconds_remaining, 1.0
|
|
124
|
-
)
|
|
118
|
+
ratio_filled = min(elapsed_time / finetune_job.progress.seconds_remaining, 1.0)
|
|
125
119
|
percentage = ratio_filled * 100
|
|
126
120
|
filled = math.ceil(ratio_filled * _PROGRESS_BAR_WIDTH)
|
|
127
121
|
bar = "█" * filled + "░" * (_PROGRESS_BAR_WIDTH - filled)
|
|
128
122
|
time_left = "N/A"
|
|
129
123
|
if finetune_job.progress.seconds_remaining > elapsed_time:
|
|
130
|
-
time_left = _human_readable_time(
|
|
131
|
-
finetune_job.progress.seconds_remaining - elapsed_time
|
|
132
|
-
)
|
|
124
|
+
time_left = _human_readable_time(finetune_job.progress.seconds_remaining - elapsed_time)
|
|
133
125
|
time_text = f"{time_left} left"
|
|
134
126
|
progress = f"Progress: {bar} [bold]{percentage:>3.0f}%[/bold] [yellow]{time_text}[/yellow]"
|
|
135
127
|
|
together/lib/constants.py
CHANGED
|
@@ -37,6 +37,12 @@ NUM_BYTES_IN_GB = 2**30
|
|
|
37
37
|
# maximum number of GB sized files we support finetuning for
|
|
38
38
|
MAX_FILE_SIZE_GB = 50.1
|
|
39
39
|
|
|
40
|
+
# Multimodal limits
|
|
41
|
+
MAX_IMAGES_PER_EXAMPLE = 10
|
|
42
|
+
MAX_IMAGE_BYTES = 10 * 1024 * 1024 # 10MB
|
|
43
|
+
# Max length = Header length + base64 factor (4/3) * image bytes
|
|
44
|
+
MAX_BASE64_IMAGE_LENGTH = len("data:image/jpeg;base64,") + 4 * MAX_IMAGE_BYTES // 3
|
|
45
|
+
|
|
40
46
|
# expected columns for Parquet files
|
|
41
47
|
PARQUET_EXPECTED_COLUMNS = ["input_ids", "attention_mask", "labels"]
|
|
42
48
|
|
|
@@ -22,6 +22,7 @@ from together.lib.types.fine_tuning import (
|
|
|
22
22
|
CosineLRSchedulerArgs,
|
|
23
23
|
LinearLRSchedulerArgs,
|
|
24
24
|
FinetuneTrainingLimits,
|
|
25
|
+
FinetuneMultimodalParams,
|
|
25
26
|
)
|
|
26
27
|
|
|
27
28
|
AVAILABLE_TRAINING_METHODS = {
|
|
@@ -51,6 +52,7 @@ def create_finetune_request(
|
|
|
51
52
|
lora_dropout: float | None = 0,
|
|
52
53
|
lora_alpha: float | None = None,
|
|
53
54
|
lora_trainable_modules: str | None = "all-linear",
|
|
55
|
+
train_vision: bool = False,
|
|
54
56
|
suffix: str | None = None,
|
|
55
57
|
wandb_api_key: str | None = None,
|
|
56
58
|
wandb_base_url: str | None = None,
|
|
@@ -207,6 +209,13 @@ def create_finetune_request(
|
|
|
207
209
|
simpo_gamma=simpo_gamma,
|
|
208
210
|
)
|
|
209
211
|
|
|
212
|
+
if model_limits.supports_vision:
|
|
213
|
+
multimodal_params = FinetuneMultimodalParams(train_vision=train_vision)
|
|
214
|
+
elif not model_limits.supports_vision and train_vision:
|
|
215
|
+
raise ValueError(f"Vision encoder training is not supported for the non-multimodal model `{model}`")
|
|
216
|
+
else:
|
|
217
|
+
multimodal_params = None
|
|
218
|
+
|
|
210
219
|
finetune_request = FinetuneRequest(
|
|
211
220
|
model=model,
|
|
212
221
|
training_file=training_file,
|
|
@@ -227,6 +236,7 @@ def create_finetune_request(
|
|
|
227
236
|
wandb_project_name=wandb_project_name,
|
|
228
237
|
wandb_name=wandb_name,
|
|
229
238
|
training_method=training_method_cls, # pyright: ignore[reportPossiblyUnboundVariable]
|
|
239
|
+
multimodal_params=multimodal_params,
|
|
230
240
|
from_checkpoint=from_checkpoint,
|
|
231
241
|
from_hf_model=from_hf_model,
|
|
232
242
|
hf_model_revision=hf_model_revision,
|
|
@@ -238,7 +248,10 @@ def create_finetune_request(
|
|
|
238
248
|
|
|
239
249
|
return finetune_request, training_type_pe, training_method_pe
|
|
240
250
|
|
|
241
|
-
|
|
251
|
+
|
|
252
|
+
def create_price_estimation_params(
|
|
253
|
+
finetune_request: FinetuneRequest,
|
|
254
|
+
) -> tuple[pe_params.TrainingType, pe_params.TrainingMethod]:
|
|
242
255
|
training_type_cls: pe_params.TrainingType
|
|
243
256
|
if isinstance(finetune_request.training_type, FullTrainingType):
|
|
244
257
|
training_type_cls = pe_params.TrainingTypeFullTrainingType(
|
|
@@ -275,6 +288,7 @@ def create_price_estimation_params(finetune_request: FinetuneRequest) -> tuple[p
|
|
|
275
288
|
|
|
276
289
|
return training_type_cls, training_method_cls
|
|
277
290
|
|
|
291
|
+
|
|
278
292
|
def get_model_limits(client: Together, model: str) -> FinetuneTrainingLimits:
|
|
279
293
|
"""
|
|
280
294
|
Requests training limits for a specific model
|
|
@@ -189,6 +189,7 @@ class TrainingMethodUnknown(BaseModel):
|
|
|
189
189
|
|
|
190
190
|
method: str
|
|
191
191
|
|
|
192
|
+
|
|
192
193
|
TrainingMethod: TypeAlias = Union[
|
|
193
194
|
TrainingMethodSFT,
|
|
194
195
|
TrainingMethodDPO,
|
|
@@ -202,6 +203,7 @@ class FinetuneTrainingLimits(BaseModel):
|
|
|
202
203
|
min_learning_rate: float
|
|
203
204
|
full_training: Optional[FinetuneFullTrainingLimits] = None
|
|
204
205
|
lora_training: Optional[FinetuneLoraTrainingLimits] = None
|
|
206
|
+
supports_vision: bool = False
|
|
205
207
|
|
|
206
208
|
|
|
207
209
|
class LinearLRSchedulerArgs(BaseModel):
|
|
@@ -249,6 +251,7 @@ class EmptyLRScheduler(BaseModel):
|
|
|
249
251
|
lr_scheduler_type: Literal[""]
|
|
250
252
|
lr_scheduler_args: None = None
|
|
251
253
|
|
|
254
|
+
|
|
252
255
|
class UnknownLRScheduler(BaseModel):
|
|
253
256
|
"""
|
|
254
257
|
Unknown learning rate scheduler
|
|
@@ -268,6 +271,14 @@ FinetuneLRScheduler: TypeAlias = Union[
|
|
|
268
271
|
]
|
|
269
272
|
|
|
270
273
|
|
|
274
|
+
class FinetuneMultimodalParams(BaseModel):
|
|
275
|
+
"""
|
|
276
|
+
Multimodal parameters
|
|
277
|
+
"""
|
|
278
|
+
|
|
279
|
+
train_vision: bool = False
|
|
280
|
+
|
|
281
|
+
|
|
271
282
|
class FinetuneProgress(BaseModel):
|
|
272
283
|
"""
|
|
273
284
|
Fine-tune job progress
|
|
@@ -303,6 +314,9 @@ class FinetuneResponse(BaseModel):
|
|
|
303
314
|
from_checkpoint: Optional[str] = None
|
|
304
315
|
"""Checkpoint used to continue training"""
|
|
305
316
|
|
|
317
|
+
multimodal_params: Optional[FinetuneMultimodalParams] = None
|
|
318
|
+
"""Multimodal parameters"""
|
|
319
|
+
|
|
306
320
|
from_hf_model: Optional[str] = None
|
|
307
321
|
"""Hugging Face Hub repo to start training from"""
|
|
308
322
|
|
|
@@ -467,6 +481,9 @@ class FinetuneRequest(BaseModel):
|
|
|
467
481
|
training_method: TrainingMethod = Field(default_factory=TrainingMethodSFT)
|
|
468
482
|
# from step
|
|
469
483
|
from_checkpoint: Union[str, None] = None
|
|
484
|
+
# multimodal parameters
|
|
485
|
+
multimodal_params: Union[FinetuneMultimodalParams, None] = None
|
|
486
|
+
# hugging face related fields
|
|
470
487
|
from_hf_model: Union[str, None] = None
|
|
471
488
|
hf_model_revision: Union[str, None] = None
|
|
472
489
|
# hf related fields
|
together/lib/utils/files.py
CHANGED
|
@@ -3,7 +3,7 @@ from __future__ import annotations
|
|
|
3
3
|
import os
|
|
4
4
|
import csv
|
|
5
5
|
import json
|
|
6
|
-
from typing import Any, Dict, List, cast
|
|
6
|
+
from typing import Any, Dict, List, Union, cast
|
|
7
7
|
from pathlib import Path
|
|
8
8
|
from traceback import format_exc
|
|
9
9
|
|
|
@@ -13,8 +13,11 @@ from together.types import FilePurpose
|
|
|
13
13
|
from together.lib.constants import (
|
|
14
14
|
MIN_SAMPLES,
|
|
15
15
|
DISABLE_TQDM,
|
|
16
|
+
MAX_IMAGE_BYTES,
|
|
16
17
|
NUM_BYTES_IN_GB,
|
|
17
18
|
MAX_FILE_SIZE_GB,
|
|
19
|
+
MAX_IMAGES_PER_EXAMPLE,
|
|
20
|
+
MAX_BASE64_IMAGE_LENGTH,
|
|
18
21
|
PARQUET_EXPECTED_COLUMNS,
|
|
19
22
|
REQUIRED_COLUMNS_MESSAGE,
|
|
20
23
|
JSONL_REQUIRED_COLUMNS_MAP,
|
|
@@ -22,6 +25,15 @@ from together.lib.constants import (
|
|
|
22
25
|
DatasetFormat,
|
|
23
26
|
)
|
|
24
27
|
|
|
28
|
+
# MessageContent is a string or a list of dicts with 'type': 'text' or 'image_url', and 'text' or 'image_url.url'
|
|
29
|
+
# Example: "Hello" or [
|
|
30
|
+
# {"type": "text", "text": "Hello"},
|
|
31
|
+
# {"type": "image_url", "image_url": {
|
|
32
|
+
# "url": "data:image/jpeg;base64,..."
|
|
33
|
+
# }}
|
|
34
|
+
# ]
|
|
35
|
+
MessageContent = Union[str, List[Dict[str, Any]]]
|
|
36
|
+
|
|
25
37
|
|
|
26
38
|
class InvalidFileFormatError(ValueError):
|
|
27
39
|
"""Exception raised for invalid file formats during file checks."""
|
|
@@ -103,7 +115,7 @@ def check_file(
|
|
|
103
115
|
return report_dict
|
|
104
116
|
|
|
105
117
|
|
|
106
|
-
def _check_conversation_type(messages: List[Dict[str, str |
|
|
118
|
+
def _check_conversation_type(messages: List[Dict[str, str | int | MessageContent]], idx: int) -> None:
|
|
107
119
|
"""Check that the conversation has correct type.
|
|
108
120
|
|
|
109
121
|
Args:
|
|
@@ -144,12 +156,6 @@ def _check_conversation_type(messages: List[Dict[str, str | bool]], idx: int) ->
|
|
|
144
156
|
line_number=idx + 1,
|
|
145
157
|
error_source="key_value",
|
|
146
158
|
)
|
|
147
|
-
if not isinstance(message[column], str):
|
|
148
|
-
raise InvalidFileFormatError(
|
|
149
|
-
message=f"Column `{column}` is not a string on line {idx + 1}. Found {type(message[column])}",
|
|
150
|
-
line_number=idx + 1,
|
|
151
|
-
error_source="text_field",
|
|
152
|
-
)
|
|
153
159
|
|
|
154
160
|
|
|
155
161
|
def _check_conversation_roles(require_assistant_role: bool, assistant_role_exists: bool, idx: int) -> None:
|
|
@@ -172,7 +178,7 @@ def _check_conversation_roles(require_assistant_role: bool, assistant_role_exist
|
|
|
172
178
|
)
|
|
173
179
|
|
|
174
180
|
|
|
175
|
-
def _check_message_weight(message: Dict[str, str |
|
|
181
|
+
def _check_message_weight(message: Dict[str, str | int | MessageContent], idx: int) -> int | None:
|
|
176
182
|
"""Check that the message has a weight with the correct type and value.
|
|
177
183
|
|
|
178
184
|
Args:
|
|
@@ -196,9 +202,12 @@ def _check_message_weight(message: Dict[str, str | bool], idx: int) -> None:
|
|
|
196
202
|
line_number=idx + 1,
|
|
197
203
|
error_source="key_value",
|
|
198
204
|
)
|
|
205
|
+
return weight
|
|
206
|
+
|
|
207
|
+
return None
|
|
199
208
|
|
|
200
209
|
|
|
201
|
-
def _check_message_role(message: Dict[str, str |
|
|
210
|
+
def _check_message_role(message: Dict[str, str | int | MessageContent], previous_role: str | None, idx: int) -> str:
|
|
202
211
|
"""Check that the message has correct roles.
|
|
203
212
|
|
|
204
213
|
Args:
|
|
@@ -212,6 +221,14 @@ def _check_message_role(message: Dict[str, str | bool], previous_role: str | boo
|
|
|
212
221
|
Raises:
|
|
213
222
|
InvalidFileFormatError: If the message role is invalid.
|
|
214
223
|
"""
|
|
224
|
+
if not isinstance(message["role"], str):
|
|
225
|
+
raise InvalidFileFormatError(
|
|
226
|
+
message=f"Invalid role `{message['role']}` in conversation on line {idx + 1}. "
|
|
227
|
+
f"Role must be a string. Found {type(message['role'])}",
|
|
228
|
+
line_number=idx + 1,
|
|
229
|
+
error_source="key_value",
|
|
230
|
+
)
|
|
231
|
+
|
|
215
232
|
if message["role"] not in POSSIBLE_ROLES_CONVERSATION:
|
|
216
233
|
raise InvalidFileFormatError(
|
|
217
234
|
message=f"Invalid role `{message['role']}` in conversation on line {idx + 1}. "
|
|
@@ -229,7 +246,130 @@ def _check_message_role(message: Dict[str, str | bool], previous_role: str | boo
|
|
|
229
246
|
return message["role"]
|
|
230
247
|
|
|
231
248
|
|
|
232
|
-
def
|
|
249
|
+
def _check_message_content(message_content: str | int | MessageContent, role: str, idx: int) -> tuple[bool, int]:
|
|
250
|
+
"""Check that the message content has the correct type.
|
|
251
|
+
Message content can be either a) a string or b) an OpenAI-style multimodal list of content items
|
|
252
|
+
Example:
|
|
253
|
+
a) "Hello", or
|
|
254
|
+
b) [
|
|
255
|
+
{"type": "text", "text": "Hello"},
|
|
256
|
+
{"type": "image_url", "image_url": {
|
|
257
|
+
"url": "data:image/jpeg;base64,..."
|
|
258
|
+
}}
|
|
259
|
+
]
|
|
260
|
+
|
|
261
|
+
Args:
|
|
262
|
+
message_content: The message content to check.
|
|
263
|
+
role: The role of the message.
|
|
264
|
+
idx: Line number in the file.
|
|
265
|
+
|
|
266
|
+
Returns:
|
|
267
|
+
tuple[bool, int]: A tuple with message is multimodal and the number of images in the message content.
|
|
268
|
+
"""
|
|
269
|
+
# Text-only message content
|
|
270
|
+
if isinstance(message_content, str):
|
|
271
|
+
return False, 0
|
|
272
|
+
|
|
273
|
+
# Multimodal message content
|
|
274
|
+
if isinstance(message_content, list):
|
|
275
|
+
num_images = 0
|
|
276
|
+
for item in message_content:
|
|
277
|
+
if not isinstance(cast(Any, item), dict):
|
|
278
|
+
raise InvalidFileFormatError(
|
|
279
|
+
"The dataset is malformed, the `content` field must be a list of dicts.",
|
|
280
|
+
line_number=idx + 1,
|
|
281
|
+
error_source="key_value",
|
|
282
|
+
)
|
|
283
|
+
if "type" not in item:
|
|
284
|
+
raise InvalidFileFormatError(
|
|
285
|
+
"The dataset is malformed, the `content` field must be a list of dicts with a `type` field.",
|
|
286
|
+
line_number=idx + 1,
|
|
287
|
+
error_source="key_value",
|
|
288
|
+
)
|
|
289
|
+
|
|
290
|
+
if item["type"] == "text":
|
|
291
|
+
if "text" not in item or not isinstance(item["text"], str):
|
|
292
|
+
raise InvalidFileFormatError(
|
|
293
|
+
"The dataset is malformed, the `text` field must be present in the `content` item field and be"
|
|
294
|
+
f" a string. Got '{item.get('text')!r}' instead.",
|
|
295
|
+
line_number=idx + 1,
|
|
296
|
+
error_source="key_value",
|
|
297
|
+
)
|
|
298
|
+
elif item["type"] == "image_url":
|
|
299
|
+
if role != "user":
|
|
300
|
+
raise InvalidFileFormatError(
|
|
301
|
+
"The dataset is malformed, only user messages can contain images.",
|
|
302
|
+
line_number=idx + 1,
|
|
303
|
+
error_source="key_value",
|
|
304
|
+
)
|
|
305
|
+
|
|
306
|
+
if "image_url" not in item or not isinstance(item["image_url"], dict):
|
|
307
|
+
raise InvalidFileFormatError(
|
|
308
|
+
"The dataset is malformed, the `image_url` field must be present in the `content` field and "
|
|
309
|
+
f"be a dictionary. Got {item.get('image_url')!r} instead.",
|
|
310
|
+
line_number=idx + 1,
|
|
311
|
+
error_source="key_value",
|
|
312
|
+
)
|
|
313
|
+
|
|
314
|
+
image_data = cast(Any, item["image_url"]).get("url")
|
|
315
|
+
if not image_data or not isinstance(image_data, str):
|
|
316
|
+
raise InvalidFileFormatError(
|
|
317
|
+
"The dataset is malformed, the `url` field must be present in the `image_url` field and be "
|
|
318
|
+
f"a string. Got {image_data!r} instead.",
|
|
319
|
+
line_number=idx + 1,
|
|
320
|
+
error_source="key_value",
|
|
321
|
+
)
|
|
322
|
+
|
|
323
|
+
if not any(image_data.startswith(f"data:image/{fmt};base64,") for fmt in ["jpeg", "png", "webp"]):
|
|
324
|
+
raise InvalidFileFormatError(
|
|
325
|
+
"The dataset is malformed, the `url` field must be either a JPEG, PNG or WEBP base64-encoded "
|
|
326
|
+
"image in 'data:image/<format>;base64,<base64_encoded_image>' format. "
|
|
327
|
+
f"Got '{image_data[:100]}...' instead.",
|
|
328
|
+
line_number=idx + 1,
|
|
329
|
+
)
|
|
330
|
+
|
|
331
|
+
if len(image_data) > MAX_BASE64_IMAGE_LENGTH:
|
|
332
|
+
raise InvalidFileFormatError(
|
|
333
|
+
"The dataset is malformed, the `url` field must contain base64-encoded image "
|
|
334
|
+
f"that is less than {MAX_IMAGE_BYTES // (1024**2)}MB, found ~{len(image_data) * 3 // 4} bytes.",
|
|
335
|
+
line_number=idx + 1,
|
|
336
|
+
error_source="key_value",
|
|
337
|
+
)
|
|
338
|
+
|
|
339
|
+
num_images += 1
|
|
340
|
+
else:
|
|
341
|
+
raise InvalidFileFormatError(
|
|
342
|
+
"The dataset is malformed, the `type` field must be either 'text' or 'image_url'. "
|
|
343
|
+
f"Got {item['type']!r}.",
|
|
344
|
+
line_number=idx + 1,
|
|
345
|
+
error_source="key_value",
|
|
346
|
+
)
|
|
347
|
+
|
|
348
|
+
if num_images > MAX_IMAGES_PER_EXAMPLE:
|
|
349
|
+
raise InvalidFileFormatError(
|
|
350
|
+
f"The dataset is malformed, the `content` field must contain at most "
|
|
351
|
+
f"{MAX_IMAGES_PER_EXAMPLE} images, found {num_images}.",
|
|
352
|
+
line_number=idx + 1,
|
|
353
|
+
error_source="key_value",
|
|
354
|
+
)
|
|
355
|
+
|
|
356
|
+
# We still consider text-only messages in such format as multimodal, even if they don't have any images
|
|
357
|
+
# included - so we can process datasets with rather sparse images (i.e. not in each sample) consistently.
|
|
358
|
+
return True, num_images
|
|
359
|
+
|
|
360
|
+
raise InvalidFileFormatError(
|
|
361
|
+
f"Invalid content type on line {idx + 1} of the input file. Expected string or multimodal list of dicts, "
|
|
362
|
+
f"found {type(message_content)}",
|
|
363
|
+
line_number=idx + 1,
|
|
364
|
+
error_source="key_value",
|
|
365
|
+
)
|
|
366
|
+
|
|
367
|
+
|
|
368
|
+
def validate_messages(
|
|
369
|
+
messages: List[Dict[str, str | int | MessageContent]],
|
|
370
|
+
idx: int,
|
|
371
|
+
require_assistant_role: bool = True,
|
|
372
|
+
) -> None:
|
|
233
373
|
"""Validate the messages column.
|
|
234
374
|
|
|
235
375
|
Args:
|
|
@@ -242,15 +382,43 @@ def validate_messages(messages: List[Dict[str, str | bool]], idx: int, require_a
|
|
|
242
382
|
"""
|
|
243
383
|
_check_conversation_type(messages, idx)
|
|
244
384
|
|
|
245
|
-
has_weights = any("weight" in message for message in messages)
|
|
246
385
|
previous_role = None
|
|
247
386
|
assistant_role_exists = False
|
|
248
387
|
|
|
388
|
+
messages_are_multimodal: bool | None = None
|
|
389
|
+
total_number_of_images = 0
|
|
390
|
+
|
|
249
391
|
for message in messages:
|
|
250
|
-
|
|
251
|
-
_check_message_weight(message, idx)
|
|
392
|
+
message_weight = _check_message_weight(message, idx)
|
|
252
393
|
previous_role = _check_message_role(message, previous_role, idx)
|
|
253
394
|
assistant_role_exists |= previous_role == "assistant"
|
|
395
|
+
is_multimodal, number_of_images = _check_message_content(message["content"], role=previous_role, idx=idx)
|
|
396
|
+
# Multimodal validation
|
|
397
|
+
if number_of_images > 0 and message_weight is not None and message_weight != 0:
|
|
398
|
+
raise InvalidFileFormatError(
|
|
399
|
+
"Messages with images cannot have non-zero weights.",
|
|
400
|
+
line_number=idx + 1,
|
|
401
|
+
error_source="key_value",
|
|
402
|
+
)
|
|
403
|
+
if messages_are_multimodal is None:
|
|
404
|
+
# Detect the format of the messages in the conversation.
|
|
405
|
+
messages_are_multimodal = is_multimodal
|
|
406
|
+
elif messages_are_multimodal != is_multimodal:
|
|
407
|
+
# Due to the format limitation, we cannot mix multimodal and text only messages in the same sample.
|
|
408
|
+
raise InvalidFileFormatError(
|
|
409
|
+
"Messages in the conversation must be either all in multimodal or all in text-only format.",
|
|
410
|
+
line_number=idx + 1,
|
|
411
|
+
error_source="key_value",
|
|
412
|
+
)
|
|
413
|
+
total_number_of_images += number_of_images
|
|
414
|
+
|
|
415
|
+
if total_number_of_images > MAX_IMAGES_PER_EXAMPLE:
|
|
416
|
+
raise InvalidFileFormatError(
|
|
417
|
+
f"The dataset is malformed, the `messages` must contain at most {MAX_IMAGES_PER_EXAMPLE} images. "
|
|
418
|
+
f"Found {total_number_of_images} images.",
|
|
419
|
+
line_number=idx + 1,
|
|
420
|
+
error_source="key_value",
|
|
421
|
+
)
|
|
254
422
|
|
|
255
423
|
_check_conversation_roles(require_assistant_role, assistant_role_exists, idx)
|
|
256
424
|
|
|
@@ -279,7 +447,7 @@ def validate_preference_openai(example: Dict[str, Any], idx: int = 0) -> None:
|
|
|
279
447
|
error_source="key_value",
|
|
280
448
|
)
|
|
281
449
|
|
|
282
|
-
messages: List[Dict[str, str |
|
|
450
|
+
messages: List[Dict[str, str | int | MessageContent]] = cast(Any, example["input"]["messages"])
|
|
283
451
|
validate_messages(messages, idx, require_assistant_role=False)
|
|
284
452
|
|
|
285
453
|
if example["input"]["messages"][-1]["role"] == "assistant":
|
|
@@ -341,12 +509,7 @@ def validate_preference_openai(example: Dict[str, Any], idx: int = 0) -> None:
|
|
|
341
509
|
error_source="key_value",
|
|
342
510
|
)
|
|
343
511
|
|
|
344
|
-
|
|
345
|
-
raise InvalidFileFormatError(
|
|
346
|
-
message=f"The dataset is malformed, the 'content' field in `{key}` must be a string on line {idx + 1}.",
|
|
347
|
-
line_number=idx + 1,
|
|
348
|
-
error_source="key_value",
|
|
349
|
-
)
|
|
512
|
+
_check_message_content(example[key][0]["content"], role="assistant", idx=idx)
|
|
350
513
|
|
|
351
514
|
|
|
352
515
|
def _check_utf8(file: Path) -> Dict[str, Any]:
|
|
@@ -514,7 +677,7 @@ def _check_jsonl(file: Path, purpose: FilePurpose | str) -> Dict[str, Any]:
|
|
|
514
677
|
elif current_format == DatasetFormat.CONVERSATION:
|
|
515
678
|
message_column = JSONL_REQUIRED_COLUMNS_MAP[DatasetFormat.CONVERSATION][0]
|
|
516
679
|
require_assistant = purpose != "eval"
|
|
517
|
-
message: List[Dict[str, str |
|
|
680
|
+
message: List[Dict[str, str | int | MessageContent]] = cast(Any, json_line[message_column])
|
|
518
681
|
validate_messages(
|
|
519
682
|
message,
|
|
520
683
|
idx,
|
|
@@ -522,13 +685,8 @@ def _check_jsonl(file: Path, purpose: FilePurpose | str) -> Dict[str, Any]:
|
|
|
522
685
|
)
|
|
523
686
|
else:
|
|
524
687
|
for column in JSONL_REQUIRED_COLUMNS_MAP[current_format]:
|
|
525
|
-
if
|
|
526
|
-
|
|
527
|
-
message=f'Invalid value type for "{column}" key on line {idx + 1}. '
|
|
528
|
-
f"Expected string. Found {type(cast(Any, json_line[column]))}.",
|
|
529
|
-
line_number=idx + 1,
|
|
530
|
-
error_source="key_value",
|
|
531
|
-
)
|
|
688
|
+
role = "assistant" if column in {"completion"} else "user"
|
|
689
|
+
_check_message_content(cast(Any, json_line[column]), role=role, idx=idx)
|
|
532
690
|
|
|
533
691
|
if dataset_format is None:
|
|
534
692
|
dataset_format = current_format
|
|
@@ -62,6 +62,8 @@ class CompletionsResource(SyncAPIResource):
|
|
|
62
62
|
],
|
|
63
63
|
str,
|
|
64
64
|
],
|
|
65
|
+
chat_template_kwargs: object | Omit = omit,
|
|
66
|
+
compliance: Literal["hipaa"] | Omit = omit,
|
|
65
67
|
context_length_exceeded_behavior: Literal["truncate", "error"] | Omit = omit,
|
|
66
68
|
echo: bool | Omit = omit,
|
|
67
69
|
frequency_penalty: float | Omit = omit,
|
|
@@ -208,6 +210,8 @@ class CompletionsResource(SyncAPIResource):
|
|
|
208
210
|
str,
|
|
209
211
|
],
|
|
210
212
|
stream: Literal[True],
|
|
213
|
+
chat_template_kwargs: object | Omit = omit,
|
|
214
|
+
compliance: Literal["hipaa"] | Omit = omit,
|
|
211
215
|
context_length_exceeded_behavior: Literal["truncate", "error"] | Omit = omit,
|
|
212
216
|
echo: bool | Omit = omit,
|
|
213
217
|
frequency_penalty: float | Omit = omit,
|
|
@@ -353,6 +357,8 @@ class CompletionsResource(SyncAPIResource):
|
|
|
353
357
|
str,
|
|
354
358
|
],
|
|
355
359
|
stream: bool,
|
|
360
|
+
chat_template_kwargs: object | Omit = omit,
|
|
361
|
+
compliance: Literal["hipaa"] | Omit = omit,
|
|
356
362
|
context_length_exceeded_behavior: Literal["truncate", "error"] | Omit = omit,
|
|
357
363
|
echo: bool | Omit = omit,
|
|
358
364
|
frequency_penalty: float | Omit = omit,
|
|
@@ -497,6 +503,8 @@ class CompletionsResource(SyncAPIResource):
|
|
|
497
503
|
],
|
|
498
504
|
str,
|
|
499
505
|
],
|
|
506
|
+
chat_template_kwargs: object | Omit = omit,
|
|
507
|
+
compliance: Literal["hipaa"] | Omit = omit,
|
|
500
508
|
context_length_exceeded_behavior: Literal["truncate", "error"] | Omit = omit,
|
|
501
509
|
echo: bool | Omit = omit,
|
|
502
510
|
frequency_penalty: float | Omit = omit,
|
|
@@ -532,6 +540,8 @@ class CompletionsResource(SyncAPIResource):
|
|
|
532
540
|
{
|
|
533
541
|
"messages": messages,
|
|
534
542
|
"model": model,
|
|
543
|
+
"chat_template_kwargs": chat_template_kwargs,
|
|
544
|
+
"compliance": compliance,
|
|
535
545
|
"context_length_exceeded_behavior": context_length_exceeded_behavior,
|
|
536
546
|
"echo": echo,
|
|
537
547
|
"frequency_penalty": frequency_penalty,
|
|
@@ -603,6 +613,8 @@ class AsyncCompletionsResource(AsyncAPIResource):
|
|
|
603
613
|
],
|
|
604
614
|
str,
|
|
605
615
|
],
|
|
616
|
+
chat_template_kwargs: object | Omit = omit,
|
|
617
|
+
compliance: Literal["hipaa"] | Omit = omit,
|
|
606
618
|
context_length_exceeded_behavior: Literal["truncate", "error"] | Omit = omit,
|
|
607
619
|
echo: bool | Omit = omit,
|
|
608
620
|
frequency_penalty: float | Omit = omit,
|
|
@@ -749,6 +761,8 @@ class AsyncCompletionsResource(AsyncAPIResource):
|
|
|
749
761
|
str,
|
|
750
762
|
],
|
|
751
763
|
stream: Literal[True],
|
|
764
|
+
chat_template_kwargs: object | Omit = omit,
|
|
765
|
+
compliance: Literal["hipaa"] | Omit = omit,
|
|
752
766
|
context_length_exceeded_behavior: Literal["truncate", "error"] | Omit = omit,
|
|
753
767
|
echo: bool | Omit = omit,
|
|
754
768
|
frequency_penalty: float | Omit = omit,
|
|
@@ -894,6 +908,8 @@ class AsyncCompletionsResource(AsyncAPIResource):
|
|
|
894
908
|
str,
|
|
895
909
|
],
|
|
896
910
|
stream: bool,
|
|
911
|
+
chat_template_kwargs: object | Omit = omit,
|
|
912
|
+
compliance: Literal["hipaa"] | Omit = omit,
|
|
897
913
|
context_length_exceeded_behavior: Literal["truncate", "error"] | Omit = omit,
|
|
898
914
|
echo: bool | Omit = omit,
|
|
899
915
|
frequency_penalty: float | Omit = omit,
|
|
@@ -1038,6 +1054,8 @@ class AsyncCompletionsResource(AsyncAPIResource):
|
|
|
1038
1054
|
],
|
|
1039
1055
|
str,
|
|
1040
1056
|
],
|
|
1057
|
+
chat_template_kwargs: object | Omit = omit,
|
|
1058
|
+
compliance: Literal["hipaa"] | Omit = omit,
|
|
1041
1059
|
context_length_exceeded_behavior: Literal["truncate", "error"] | Omit = omit,
|
|
1042
1060
|
echo: bool | Omit = omit,
|
|
1043
1061
|
frequency_penalty: float | Omit = omit,
|
|
@@ -1073,6 +1091,8 @@ class AsyncCompletionsResource(AsyncAPIResource):
|
|
|
1073
1091
|
{
|
|
1074
1092
|
"messages": messages,
|
|
1075
1093
|
"model": model,
|
|
1094
|
+
"chat_template_kwargs": chat_template_kwargs,
|
|
1095
|
+
"compliance": compliance,
|
|
1076
1096
|
"context_length_exceeded_behavior": context_length_exceeded_behavior,
|
|
1077
1097
|
"echo": echo,
|
|
1078
1098
|
"frequency_penalty": frequency_penalty,
|
|
@@ -53,6 +53,7 @@ _WARNING_MESSAGE_INSUFFICIENT_FUNDS = (
|
|
|
53
53
|
"Proceed at your own risk."
|
|
54
54
|
)
|
|
55
55
|
|
|
56
|
+
|
|
56
57
|
class FineTuningResource(SyncAPIResource):
|
|
57
58
|
@cached_property
|
|
58
59
|
def with_raw_response(self) -> FineTuningResourceWithRawResponse:
|
|
@@ -95,6 +96,7 @@ class FineTuningResource(SyncAPIResource):
|
|
|
95
96
|
lora_dropout: float | None = 0,
|
|
96
97
|
lora_alpha: float | None = None,
|
|
97
98
|
lora_trainable_modules: str | None = "all-linear",
|
|
99
|
+
train_vision: bool = False,
|
|
98
100
|
suffix: str | None = None,
|
|
99
101
|
wandb_api_key: str | None = None,
|
|
100
102
|
wandb_base_url: str | None = None,
|
|
@@ -140,6 +142,7 @@ class FineTuningResource(SyncAPIResource):
|
|
|
140
142
|
lora_dropout (float, optional): Dropout rate for LoRA adapters. Defaults to 0.
|
|
141
143
|
lora_alpha (float, optional): Alpha for LoRA adapters. Defaults to 8.
|
|
142
144
|
lora_trainable_modules (str, optional): Trainable modules for LoRA adapters. Defaults to "all-linear".
|
|
145
|
+
train_vision (bool, optional): Whether to train the vision encoder (Only for multimodal models). Defaults to False.
|
|
143
146
|
suffix (str, optional): Up to 40 character suffix that will be added to your fine-tuned model name.
|
|
144
147
|
Defaults to None.
|
|
145
148
|
wandb_api_key (str, optional): API key for Weights & Biases integration.
|
|
@@ -214,6 +217,7 @@ class FineTuningResource(SyncAPIResource):
|
|
|
214
217
|
lora_dropout=lora_dropout,
|
|
215
218
|
lora_alpha=lora_alpha,
|
|
216
219
|
lora_trainable_modules=lora_trainable_modules,
|
|
220
|
+
train_vision=train_vision,
|
|
217
221
|
suffix=suffix,
|
|
218
222
|
wandb_api_key=wandb_api_key,
|
|
219
223
|
wandb_base_url=wandb_base_url,
|
|
@@ -232,29 +236,32 @@ class FineTuningResource(SyncAPIResource):
|
|
|
232
236
|
hf_output_repo_name=hf_output_repo_name,
|
|
233
237
|
)
|
|
234
238
|
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
239
|
+
if not model_limits.supports_vision:
|
|
240
|
+
price_estimation_result = self.estimate_price(
|
|
241
|
+
training_file=training_file,
|
|
242
|
+
from_checkpoint=from_checkpoint or Omit(),
|
|
243
|
+
validation_file=validation_file or Omit(),
|
|
244
|
+
model=model or "",
|
|
245
|
+
n_epochs=finetune_request.n_epochs,
|
|
246
|
+
n_evals=finetune_request.n_evals or 0,
|
|
247
|
+
training_type=training_type_cls,
|
|
248
|
+
training_method=training_method_cls,
|
|
249
|
+
)
|
|
250
|
+
price_limit_passed = price_estimation_result.allowed_to_proceed
|
|
251
|
+
else:
|
|
252
|
+
# unsupported case
|
|
253
|
+
price_limit_passed = True
|
|
247
254
|
|
|
248
255
|
if verbose:
|
|
249
256
|
rprint(
|
|
250
257
|
"Submitting a fine-tuning job with the following parameters:",
|
|
251
258
|
finetune_request,
|
|
252
259
|
)
|
|
253
|
-
if not
|
|
260
|
+
if not price_limit_passed:
|
|
254
261
|
rprint(
|
|
255
262
|
"[red]"
|
|
256
263
|
+ _WARNING_MESSAGE_INSUFFICIENT_FUNDS.format(
|
|
257
|
-
price_estimation_result.estimated_total_price
|
|
264
|
+
price_estimation_result.estimated_total_price # pyright: ignore[reportPossiblyUnboundVariable]
|
|
258
265
|
)
|
|
259
266
|
+ "[/red]",
|
|
260
267
|
)
|
|
@@ -627,6 +634,7 @@ class AsyncFineTuningResource(AsyncAPIResource):
|
|
|
627
634
|
lora_dropout: float | None = 0,
|
|
628
635
|
lora_alpha: float | None = None,
|
|
629
636
|
lora_trainable_modules: str | None = "all-linear",
|
|
637
|
+
train_vision: bool = False,
|
|
630
638
|
suffix: str | None = None,
|
|
631
639
|
wandb_api_key: str | None = None,
|
|
632
640
|
wandb_base_url: str | None = None,
|
|
@@ -672,6 +680,7 @@ class AsyncFineTuningResource(AsyncAPIResource):
|
|
|
672
680
|
lora_dropout (float, optional): Dropout rate for LoRA adapters. Defaults to 0.
|
|
673
681
|
lora_alpha (float, optional): Alpha for LoRA adapters. Defaults to 8.
|
|
674
682
|
lora_trainable_modules (str, optional): Trainable modules for LoRA adapters. Defaults to "all-linear".
|
|
683
|
+
train_vision (bool, optional): Whether to train the vision encoder (Only for multimodal models). Defaults to False.
|
|
675
684
|
suffix (str, optional): Up to 40 character suffix that will be added to your fine-tuned model name.
|
|
676
685
|
Defaults to None.
|
|
677
686
|
wandb_api_key (str, optional): API key for Weights & Biases integration.
|
|
@@ -746,6 +755,7 @@ class AsyncFineTuningResource(AsyncAPIResource):
|
|
|
746
755
|
lora_dropout=lora_dropout,
|
|
747
756
|
lora_alpha=lora_alpha,
|
|
748
757
|
lora_trainable_modules=lora_trainable_modules,
|
|
758
|
+
train_vision=train_vision,
|
|
749
759
|
suffix=suffix,
|
|
750
760
|
wandb_api_key=wandb_api_key,
|
|
751
761
|
wandb_base_url=wandb_base_url,
|
|
@@ -764,7 +774,6 @@ class AsyncFineTuningResource(AsyncAPIResource):
|
|
|
764
774
|
hf_output_repo_name=hf_output_repo_name,
|
|
765
775
|
)
|
|
766
776
|
|
|
767
|
-
|
|
768
777
|
price_estimation_result = await self.estimate_price(
|
|
769
778
|
training_file=training_file,
|
|
770
779
|
from_checkpoint=from_checkpoint or Omit(),
|
|
@@ -776,7 +785,6 @@ class AsyncFineTuningResource(AsyncAPIResource):
|
|
|
776
785
|
training_method=training_method_cls,
|
|
777
786
|
)
|
|
778
787
|
|
|
779
|
-
|
|
780
788
|
if verbose:
|
|
781
789
|
rprint(
|
|
782
790
|
"Submitting a fine-tuning job with the following parameters:",
|
|
@@ -786,7 +794,7 @@ class AsyncFineTuningResource(AsyncAPIResource):
|
|
|
786
794
|
rprint(
|
|
787
795
|
"[red]"
|
|
788
796
|
+ _WARNING_MESSAGE_INSUFFICIENT_FUNDS.format(
|
|
789
|
-
price_estimation_result.estimated_total_price
|
|
797
|
+
price_estimation_result.estimated_total_price # pyright: ignore[reportPossiblyUnboundVariable]
|
|
790
798
|
)
|
|
791
799
|
+ "[/red]",
|
|
792
800
|
)
|
|
@@ -60,6 +60,10 @@ class CompletionCreateParamsBase(TypedDict, total=False):
|
|
|
60
60
|
[See all of Together AI's chat models](https://docs.together.ai/docs/serverless-models#chat-models)
|
|
61
61
|
"""
|
|
62
62
|
|
|
63
|
+
chat_template_kwargs: object
|
|
64
|
+
|
|
65
|
+
compliance: Literal["hipaa"]
|
|
66
|
+
|
|
63
67
|
context_length_exceeded_behavior: Literal["truncate", "error"]
|
|
64
68
|
"""
|
|
65
69
|
Defined the behavior of the API when max_tokens exceed the maximum context
|
|
@@ -15,6 +15,7 @@ __all__ = [
|
|
|
15
15
|
"LrSchedulerLrSchedulerArgs",
|
|
16
16
|
"LrSchedulerLrSchedulerArgsLinearLrSchedulerArgs",
|
|
17
17
|
"LrSchedulerLrSchedulerArgsCosineLrSchedulerArgs",
|
|
18
|
+
"MultimodalParams",
|
|
18
19
|
"Progress",
|
|
19
20
|
"TrainingMethod",
|
|
20
21
|
"TrainingMethodTrainingMethodSft",
|
|
@@ -49,6 +50,14 @@ class LrScheduler(BaseModel):
|
|
|
49
50
|
lr_scheduler_args: Optional[LrSchedulerLrSchedulerArgs] = None
|
|
50
51
|
|
|
51
52
|
|
|
53
|
+
class MultimodalParams(BaseModel):
|
|
54
|
+
train_vision: Optional[bool] = None
|
|
55
|
+
"""Whether to train the vision encoder of the model.
|
|
56
|
+
|
|
57
|
+
Only available for multimodal models.
|
|
58
|
+
"""
|
|
59
|
+
|
|
60
|
+
|
|
52
61
|
class Progress(BaseModel):
|
|
53
62
|
"""Progress information for a fine-tuning job"""
|
|
54
63
|
|
|
@@ -150,6 +159,8 @@ class FinetuneResponse(BaseModel):
|
|
|
150
159
|
|
|
151
160
|
x_model_output_path: Optional[str] = FieldInfo(alias="model_output_path", default=None)
|
|
152
161
|
|
|
162
|
+
multimodal_params: Optional[MultimodalParams] = None
|
|
163
|
+
|
|
153
164
|
n_checkpoints: Optional[int] = None
|
|
154
165
|
|
|
155
166
|
n_epochs: Optional[int] = None
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: together
|
|
3
|
-
Version: 2.0.
|
|
3
|
+
Version: 2.0.0a13
|
|
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
|
|
@@ -183,7 +183,7 @@ stream = client.chat.completions.create(
|
|
|
183
183
|
messages=[
|
|
184
184
|
{
|
|
185
185
|
"role": "user",
|
|
186
|
-
"content": "Say this is a test",
|
|
186
|
+
"content": "Say this is a test!",
|
|
187
187
|
}
|
|
188
188
|
],
|
|
189
189
|
model="meta-llama/Meta-Llama-3.1-8B-Instruct-Turbo",
|
|
@@ -204,7 +204,7 @@ stream = await client.chat.completions.create(
|
|
|
204
204
|
messages=[
|
|
205
205
|
{
|
|
206
206
|
"role": "user",
|
|
207
|
-
"content": "Say this is a test",
|
|
207
|
+
"content": "Say this is a test!",
|
|
208
208
|
}
|
|
209
209
|
],
|
|
210
210
|
model="meta-llama/Meta-Llama-3.1-8B-Instruct-Turbo",
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
together/__init__.py,sha256=ghwEH6EUrPERUwHVSXaCJVqS7QmLN7NsUxKJNXQrOYM,2842
|
|
2
|
-
together/_base_client.py,sha256=
|
|
2
|
+
together/_base_client.py,sha256=U6Lhqesx9l1qhaYKaP2jR-_mIyqbNhAOo2r2bgTQegI,67249
|
|
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=
|
|
14
|
+
together/_version.py,sha256=BExhELPV8VqVuQbk2T-lGqnaFEZgTxL2XCJuRpZaadk,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,25 +27,25 @@ 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=
|
|
30
|
+
together/lib/constants.py,sha256=w8-zVl8XZiJxqMdhbWekigHJ0JUMPoV9R3ejUHIcUJk,2237
|
|
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
|
|
37
|
+
together/lib/cli/api/fine_tuning.py,sha256=Tb4J9_LnHZHp73zNW1lHNSl_0UQfOd0yi9-IvMHBY08,22863
|
|
38
38
|
together/lib/cli/api/models.py,sha256=Jfrl7gcbWAkbBQ1i1gCy485HHT2C4C784OMaaHZPiPw,4084
|
|
39
|
-
together/lib/cli/api/utils.py,sha256=
|
|
39
|
+
together/lib/cli/api/utils.py,sha256=j4IYurqcoqisstAQuqWMsUKbMQITNX8ax2Vv6U6qF7I,4381
|
|
40
40
|
together/lib/resources/__init__.py,sha256=ystIb0pBHQLuwUBtHJwhRgtjK3_TV6K0KuM8NGuuNoU,172
|
|
41
41
|
together/lib/resources/files.py,sha256=Z_D23IvjYYWBpYrfYolCNfUslJBcE4PnU0WtuLsN67M,37277
|
|
42
|
-
together/lib/resources/fine_tuning.py,sha256
|
|
42
|
+
together/lib/resources/fine_tuning.py,sha256=A-hOJqcGSPzw24wwX6K27OqV3B-u43dfdrK4nj4ItTg,13088
|
|
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=
|
|
45
|
+
together/lib/types/fine_tuning.py,sha256=gTB66x4jrmxyWuKJGcqX_4aYcI2iGgZvL3MzMLePDC8,13325
|
|
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
|
-
together/lib/utils/files.py,sha256=
|
|
48
|
+
together/lib/utils/files.py,sha256=CVTFwI7yMzpaQ-GsGr1tD4O2kXA-i369Pi0eMnlWMmI,31854
|
|
49
49
|
together/lib/utils/serializer.py,sha256=wJwySGxAL0e1giZzFpl4hHH3s9lkoNN_yzu-P_jdRIo,287
|
|
50
50
|
together/lib/utils/tools.py,sha256=rrpz3EXEVViou5GDPjVoCSt2zDPJYDzWYqTsVO1-OgI,2183
|
|
51
51
|
together/resources/__init__.py,sha256=Cuiy4FcdrfUzb0N-jZbl8Phqjvlzt12Iq7BhI9tFsXw,7577
|
|
@@ -55,7 +55,7 @@ together/resources/embeddings.py,sha256=7EU6DZQd0Nm0Sh7x7v37QQOLNuLqNmcjdJAyOTck
|
|
|
55
55
|
together/resources/endpoints.py,sha256=dYdLlAJ0P7HJNhzZGxlbzEQYpUWsh35cjAMVfdWiifw,27884
|
|
56
56
|
together/resources/evals.py,sha256=FPjvkbsBY5rrzLyQ-X1G9fWt2QmivI9ol5GExGtqYVA,16216
|
|
57
57
|
together/resources/files.py,sha256=0paHeVqNt3NQCXoztCgFS8PEIg_-mMVto-ulHTr7GzE,16854
|
|
58
|
-
together/resources/fine_tuning.py,sha256=
|
|
58
|
+
together/resources/fine_tuning.py,sha256=BiCxQpdTjW5ArBufmWHNQoYY4z7Ulge8dI9GDCa5Dow,54795
|
|
59
59
|
together/resources/hardware.py,sha256=xgfCmMrrwF5o1igax0JGec8RY7kkS0s4kKm62RdC3ME,6850
|
|
60
60
|
together/resources/images.py,sha256=mVPQYpDHKBjLVO_Sv0uT62zYXdtWKN2PW3fCvfQLQCs,12612
|
|
61
61
|
together/resources/jobs.py,sha256=TnzSnvJw4x5pqo1xzrkYH8f0viZrzyOqT-_w7xc0NzY,7797
|
|
@@ -70,7 +70,7 @@ together/resources/audio/translations.py,sha256=VPkg5ZUDw5LZwiaRYqEjETKwSMMU1odT
|
|
|
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
|
|
73
|
-
together/resources/chat/completions.py,sha256=
|
|
73
|
+
together/resources/chat/completions.py,sha256=u45dEoSvgyJZ86yI3-CZzPDOVOjBi_h9ZaxXBhXZPaw,57312
|
|
74
74
|
together/resources/code_interpreter/__init__.py,sha256=qeNVuBUuYy66RDhyh4RDx_xsf0gTMIrrZkZHpkPy9r0,1146
|
|
75
75
|
together/resources/code_interpreter/code_interpreter.py,sha256=ZrWQIn5FO-uau3qTt_HhsHiaclM_ZNfOqZI_AWT2SMk,10373
|
|
76
76
|
together/resources/code_interpreter/sessions.py,sha256=Sl8X6-r1gds2VHhzpjPhfwYNTciZCJxAH-YjJerA_eU,5020
|
|
@@ -117,7 +117,7 @@ together/types/fine_tuning_list_events_response.py,sha256=DeDJLF1IxQV47HOwfuVt8Z
|
|
|
117
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=
|
|
120
|
+
together/types/finetune_response.py,sha256=klN6uuZt1tQ_PJ3rt7OSd_Cf7f0MuUnFZ4uJ6ISdrEU,4975
|
|
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
|
|
@@ -156,11 +156,11 @@ together/types/chat/chat_completion_structured_message_text_param.py,sha256=ogWM
|
|
|
156
156
|
together/types/chat/chat_completion_structured_message_video_url_param.py,sha256=i0VjxkE6xEYr11YBkOd2pkDSu01EiTbYjFDAkt0RE0g,504
|
|
157
157
|
together/types/chat/chat_completion_usage.py,sha256=tkDp4y7jzxFKtK3tXe_bJb7Coew-nt8u3S7bZCvcVXo,269
|
|
158
158
|
together/types/chat/chat_completion_warning.py,sha256=_Dp7YKlxyY2HeZopTvT-Go7qqKsbj3h_Vv06uLzgsTU,216
|
|
159
|
-
together/types/chat/completion_create_params.py,sha256=
|
|
159
|
+
together/types/chat/completion_create_params.py,sha256=GpOQIpL2hODOV-iPoilHxo5UYP_KHJ-zdZMP-VW87-g,13755
|
|
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.
|
|
163
|
-
together-2.0.
|
|
164
|
-
together-2.0.
|
|
165
|
-
together-2.0.
|
|
166
|
-
together-2.0.
|
|
162
|
+
together-2.0.0a13.dist-info/METADATA,sha256=I61HjW1IsoROkaTO3yOCuer8UXQ8wqaEtZHUFfmE_ZA,20249
|
|
163
|
+
together-2.0.0a13.dist-info/WHEEL,sha256=C2FUgwZgiLbznR-k0b_5k3Ai_1aASOXDss3lzCUsUug,87
|
|
164
|
+
together-2.0.0a13.dist-info/entry_points.txt,sha256=4f4RAX89wQkx3AnfHXiGrKyg2fCPnwMd2UdPX48OczA,55
|
|
165
|
+
together-2.0.0a13.dist-info/licenses/LICENSE,sha256=oSs-kmJHhMue4vIIPIxQMvXou9PbxgNdIX-r_AwfO7c,11338
|
|
166
|
+
together-2.0.0a13.dist-info/RECORD,,
|
|
@@ -186,7 +186,7 @@
|
|
|
186
186
|
same "printed page" as the copyright notice for easier
|
|
187
187
|
identification within third-party archives.
|
|
188
188
|
|
|
189
|
-
Copyright
|
|
189
|
+
Copyright 2026 Together
|
|
190
190
|
|
|
191
191
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
192
192
|
you may not use this file except in compliance with the License.
|
|
File without changes
|
|
File without changes
|