together 0.2.4__py3-none-any.whl → 0.2.5__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/error.py CHANGED
@@ -11,6 +11,7 @@ class TogetherException(Exception):
11
11
  http_status: Optional[int] = None,
12
12
  json_body: Optional[Any] = None,
13
13
  headers: Optional[Union[str, Dict[Any, Any]]] = None,
14
+ request_id: Optional[str] = "",
14
15
  ) -> None:
15
16
  super(TogetherException, self).__init__(message)
16
17
 
@@ -28,12 +29,14 @@ class TogetherException(Exception):
28
29
  self.http_status = http_status
29
30
  self.json_body = json_body
30
31
  self.headers = headers or {}
32
+ self.request_id = request_id
31
33
 
32
34
  def __repr__(self) -> str:
33
- return "%s(message=%r, http_status=%r)" % (
35
+ return "%s(message=%r, http_status=%r, request_id=%r)" % (
34
36
  self.__class__.__name__,
35
37
  self._message,
36
38
  self.http_status,
39
+ self.request_id,
37
40
  )
38
41
 
39
42
 
@@ -59,7 +62,14 @@ class InstanceError(TogetherException):
59
62
  headers: Optional[str] = None,
60
63
  model: Optional[str] = "model",
61
64
  ) -> None:
62
- message = f"No running instances for {model}. You can start an instance by navigating to the Together Playground at api.together.ai"
65
+ message = f"""No running instances for {model}.
66
+ You can start an instance with one of the following methods:
67
+ 1. navigating to the Together Playground at api.together.ai
68
+ 2. starting one in python using together.Models.start(model_name)
69
+ 3. `$ together models start <MODEL_NAME>` at the command line.
70
+ See `together.Models.list()` in python or `$ together models list` in command line
71
+ to get an updated list of valid model names.
72
+ """
63
73
  super(InstanceError, self).__init__(
64
74
  message, http_body, http_status, json_body, headers
65
75
  )
together/files.py CHANGED
@@ -2,58 +2,41 @@ import json
2
2
  import os
3
3
  import posixpath
4
4
  import urllib.parse
5
- from typing import Any, Dict, List, Mapping, Optional, Union, cast
5
+ from typing import Any, Dict, List, Mapping, Optional, Union
6
6
 
7
7
  import requests
8
8
  from tqdm import tqdm
9
9
  from tqdm.utils import CallbackIOWrapper
10
10
 
11
11
  import together
12
- from together.utils.utils import get_logger, verify_api_key
12
+ from together.utils import (
13
+ create_get_request,
14
+ get_logger,
15
+ response_to_dict,
16
+ )
13
17
 
14
18
 
15
- logger = get_logger(str(__name__), log_level=together.log_level)
16
-
17
19
  # the number of bytes in a gigabyte, used to convert bytes to GB for readable comparison
18
20
  NUM_BYTES_IN_GB = 2**30
19
21
 
20
22
  # maximum number of GB sized files we support finetuning for
21
23
  MAX_FT_GB = 4.9
22
24
 
25
+ logger = get_logger(str(__name__))
23
26
 
24
- class Files:
25
- def __init__(
26
- self,
27
- ) -> None:
28
- verify_api_key(logger)
29
27
 
28
+ class Files:
30
29
  @classmethod
31
30
  def list(self) -> Dict[str, List[Dict[str, Union[str, int]]]]:
32
- headers = {
33
- "Authorization": f"Bearer {together.api_key}",
34
- "User-Agent": together.user_agent,
35
- }
36
-
37
31
  # send request
38
- try:
39
- response = requests.get(together.api_base_files, headers=headers)
40
- response.raise_for_status()
41
- except requests.exceptions.RequestException as e:
42
- logger.critical(f"Response error raised: {e}")
43
- raise together.ResponseError(e)
44
- try:
45
- response_json = dict(response.json())
46
- except Exception as e:
47
- logger.critical(
48
- f"JSON Error raised: {e}\nResponse status code = {response.status_code}"
49
- )
50
- raise together.JSONError(e, http_status=response.status_code)
51
-
52
- return response_json
32
+ response = create_get_request(together.api_base_files)
33
+ if not response:
34
+ return {}
35
+ return response_to_dict(response)
53
36
 
54
37
  @classmethod
55
- def check(self, file: str, model: Optional[str] = None) -> Dict[str, object]:
56
- return check_json(file, model)
38
+ def check(self, file: str) -> Dict[str, object]:
39
+ return check_json(file)
57
40
 
58
41
  @classmethod
59
42
  def upload(
@@ -70,10 +53,11 @@ class Files:
70
53
  }
71
54
 
72
55
  if check:
73
- report_dict = check_json(file, model)
56
+ report_dict = check_json(file)
74
57
  if not report_dict["is_check_passed"]:
75
- print(report_dict)
76
- raise together.FileTypeError("Invalid file supplied. Failed to upload.")
58
+ raise together.FileTypeError(
59
+ f"Invalid file supplied. Failed to upload.\nReport:\n {report_dict}"
60
+ )
77
61
  else:
78
62
  report_dict = {}
79
63
 
@@ -116,7 +100,7 @@ class Files:
116
100
  file_id = response.headers["X-Together-File-Id"]
117
101
 
118
102
  logger.info(f"R2 Signed URL: {r2_signed_url}")
119
- logger.info("File-ID")
103
+ logger.info(f"File-ID: {file_id}")
120
104
 
121
105
  logger.info("Uploading file...")
122
106
 
@@ -177,44 +161,16 @@ class Files:
177
161
  logger.critical(f"Response error raised: {e}")
178
162
  raise together.ResponseError(e)
179
163
 
180
- try:
181
- response_json = dict(response.json())
182
- except Exception as e:
183
- logger.critical(
184
- f"JSON Error raised: {e}\nResponse status code = {response.status_code}"
185
- )
186
- raise together.JSONError(e, http_status=response.status_code)
187
-
188
- return response_json
164
+ return response_to_dict(response)
189
165
 
190
166
  @classmethod
191
167
  def retrieve(self, file_id: str) -> Dict[str, Union[str, int]]:
192
168
  retrieve_url = urllib.parse.urljoin(together.api_base_files, file_id)
193
-
194
169
  logger.info(f"Retrieve URL: {retrieve_url}")
195
-
196
- headers = {
197
- "Authorization": f"Bearer {together.api_key}",
198
- "User-Agent": together.user_agent,
199
- }
200
-
201
- # send request
202
- try:
203
- response = requests.get(retrieve_url, headers=headers)
204
- response.raise_for_status()
205
- except requests.exceptions.RequestException as e:
206
- logger.critical(f"Response error raised: {e}")
207
- raise together.ResponseError(e)
208
-
209
- try:
210
- response_json = dict(response.json())
211
- except Exception as e:
212
- logger.critical(
213
- f"JSON Error raised: {e}\nResponse status code = {response.status_code}"
214
- )
215
- raise together.JSONError(e, http_status=response.status_code)
216
-
217
- return response_json
170
+ response = create_get_request(retrieve_url)
171
+ if not response:
172
+ return {}
173
+ return response_to_dict(response)
218
174
 
219
175
  @classmethod
220
176
  def retrieve_content(self, file_id: str, output: Union[str, None] = None) -> str:
@@ -288,23 +244,11 @@ class Files:
288
244
 
289
245
  def check_json(
290
246
  file: str,
291
- model: Optional[str] = None,
292
247
  ) -> Dict[str, object]:
293
248
  report_dict = {
294
249
  "is_check_passed": True,
295
250
  "model_special_tokens": "we are not yet checking end of sentence tokens for this model",
296
251
  }
297
- num_samples_w_eos_token = 0
298
-
299
- model_info_dict = cast(Dict[str, Any], together.model_info_dict)
300
-
301
- eos_token = None
302
- if model is not None and model in model_info_dict:
303
- if "eos_token" in model_info_dict[model]:
304
- eos_token = model_info_dict[model]["eos_token"]
305
- report_dict[
306
- "model_special_tokens"
307
- ] = f"the end of sentence token for this model is {eos_token}"
308
252
 
309
253
  if not os.path.isfile(file):
310
254
  report_dict["file_present"] = f"File not found at given file path {file}"
@@ -358,10 +302,6 @@ def check_json(
358
302
 
359
303
  report_dict["is_check_passed"] = False
360
304
 
361
- elif eos_token:
362
- if eos_token in json_line["text"]:
363
- num_samples_w_eos_token += 1
364
-
365
305
  # make sure this is outside the for idx, line in enumerate(f): for loop
366
306
  if idx + 1 < together.min_samples:
367
307
  report_dict["min_samples"] = (
@@ -383,6 +323,4 @@ def check_json(
383
323
  )
384
324
  report_dict["is_check_passed"] = False
385
325
 
386
- report_dict["num_samples_w_eos_token"] = num_samples_w_eos_token
387
-
388
326
  return report_dict
together/finetune.py CHANGED
@@ -1,4 +1,5 @@
1
1
  import posixpath
2
+ import pprint
2
3
  import urllib.parse
3
4
  from typing import Any, Dict, List, Optional, Union
4
5
 
@@ -7,49 +8,22 @@ from tqdm import tqdm
7
8
 
8
9
  import together
9
10
  from together import Files
10
- from together.utils.utils import get_logger, verify_api_key
11
-
12
-
13
- logger = get_logger(str(__name__), log_level=together.log_level)
14
-
15
-
16
- # this will change soon to be data driven and give a clearer estimate
17
- def model_param_count(name: str) -> int:
18
- pcount = {
19
- "togethercomputer/RedPajama-INCITE-7B-Chat": 6857302016,
20
- "togethercomputer/RedPajama-INCITE-7B-Base": 6857302016,
21
- "togethercomputer/RedPajama-INCITE-7B-Instruct": 6857302016,
22
- "togethercomputer/RedPajama-INCITE-Chat-3B-v1": 2775864320,
23
- "togethercomputer/RedPajama-INCITE-Base-3B-v1": 2775864320,
24
- "togethercomputer/RedPajama-INCITE-Instruct-3B-v1": 2775864320,
25
- "togethercomputer/Pythia-Chat-Base-7B": 6857302016,
26
- "togethercomputer/llama-2-7b": 6738415616,
27
- "togethercomputer/llama-2-7b-chat": 6738415616,
28
- "togethercomputer/llama-2-13b": 13015864320,
29
- "togethercomputer/llama-2-13b-chat": 13015864320,
30
- "togethercomputer/LLaMA-2-7B-32K": 6738415616,
31
- "togethercomputer/Llama-2-7B-32K-Instruct": 6738415616,
32
- "togethercomputer/CodeLlama-7b": 6738546688,
33
- "togethercomputer/CodeLlama-7b-Python": 6738546688,
34
- "togethercomputer/CodeLlama-7b-Instruct": 6738546688,
35
- "togethercomputer/CodeLlama-13b": 13016028160,
36
- "togethercomputer/CodeLlama-13b-Python": 13016028160,
37
- "togethercomputer/CodeLlama-13b-Instruct": 13016028160,
38
- "togethercomputer/llama-2-70b": 68976648192,
39
- "togethercomputer/llama-2-70b-chat": 68976648192,
40
- }
41
- try:
42
- return pcount[name]
43
- except Exception:
44
- return 0
11
+ from together.utils import (
12
+ create_get_request,
13
+ create_post_request,
14
+ get_logger,
15
+ response_to_dict,
16
+ round_to_closest_multiple_of_32,
17
+ )
45
18
 
46
19
 
47
- class Finetune:
48
- def __init__(
49
- self,
50
- ) -> None:
51
- verify_api_key(logger)
20
+ pp = pprint.PrettyPrinter(indent=4)
21
+
22
+ logger = get_logger(str(__name__))
52
23
 
24
+
25
+ class Finetune:
26
+ # TODO @orangetin: cleanup create validation etc
53
27
  @classmethod
54
28
  def create(
55
29
  self,
@@ -71,47 +45,52 @@ class Finetune:
71
45
  ] = None, # resulting finetuned model name will include the suffix
72
46
  estimate_price: bool = False,
73
47
  wandb_api_key: Optional[str] = None,
48
+ confirm_inputs: bool = True,
74
49
  ) -> Dict[Any, Any]:
50
+ adjusted_inputs = False
51
+
75
52
  if n_epochs is None or n_epochs < 1:
76
- logger.fatal("The number of epochs must be specified")
77
- raise ValueError("n_epochs is required")
53
+ n_epochs = 1
54
+ adjusted_inputs = True
78
55
 
79
56
  # Validate parameters
80
57
  if n_checkpoints is None:
81
58
  n_checkpoints = 1
82
59
  elif n_checkpoints < 1:
83
60
  n_checkpoints = 1
84
- logger.warning(
85
- f"The number of checkpoints must be >= 1, setting to {n_checkpoints}"
86
- )
61
+ adjusted_inputs = True
87
62
  elif n_checkpoints > n_epochs:
88
63
  n_checkpoints = n_epochs
89
- logger.warning(
90
- f"The number of checkpoints must be < the number of epochs, setting to {n_checkpoints}"
91
- )
92
-
93
- if (
94
- model
95
- in ["togethercomputer/llama-2-70b", "togethercomputer/llama-2-70b-chat"]
96
- and batch_size != 144
97
- ):
98
- raise ValueError(
99
- f"Batch size must be 144 for {model} model. Please set batch size to 144"
100
- )
64
+ adjusted_inputs = True
101
65
 
66
+ # TODO: Replace with mongodb retrieval for max, min, and default batch size
102
67
  if batch_size is None:
103
68
  batch_size = 32
104
69
  elif batch_size < 4:
105
- raise ValueError("Batch size must be >= 4.")
70
+ batch_size = 4
71
+ adjusted_inputs = True
72
+
73
+ max_batch_size = 128
74
+ if model.startswith("togethercomputer/llama-2-70b"):
75
+ max_batch_size = 64
76
+ batch_size = round_to_closest_multiple_of_32(batch_size)
77
+ adjusted_inputs = True
78
+ elif model.startswith("togethercomputer/CodeLlama-7b"):
79
+ max_batch_size = 16
80
+ elif model.startswith("togethercomputer/CodeLlama-13b"):
81
+ max_batch_size = 8
82
+
83
+ if batch_size > max_batch_size:
84
+ batch_size = max_batch_size
85
+ adjusted_inputs = True
106
86
 
107
87
  # TODO: REMOVE THIS CHECK WHEN WE HAVE CHECKPOINTING WORKING FOR 70B models
108
88
  if n_checkpoints > 1 and model in [
109
89
  "togethercomputer/llama-2-70b",
110
90
  "togethercomputer/llama-2-70b-chat",
111
91
  ]:
112
- raise ValueError(
113
- "Saving checkpoints during training currently not supported for {model}. Please set the number of checkpoints to 1"
114
- )
92
+ n_checkpoints = 1
93
+ adjusted_inputs = True
115
94
 
116
95
  parameter_payload = {
117
96
  "training_file": training_file,
@@ -131,8 +110,8 @@ class Finetune:
131
110
  }
132
111
 
133
112
  # check if model name is one of the models available for finetuning
134
- if parameter_payload["model"] not in together.finetune_model_names:
135
- logger.warning(
113
+ if not together.Models._is_finetune_model(model):
114
+ raise ValueError(
136
115
  "The finetune model name must be one of the subset of models available for finetuning. "
137
116
  "Here is a list of those models https://docs.together.ai/docs/models-fine-tuning"
138
117
  )
@@ -151,7 +130,7 @@ class Finetune:
151
130
  raise together.FileTypeError(training_file_feedback)
152
131
 
153
132
  if estimate_price:
154
- param_size = model_param_count(model)
133
+ param_size = together.Models._param_count(model)
155
134
  if param_size == 0:
156
135
  error = f"Unknown model {model}. Cannot estimate price. Please check the name of the model"
157
136
  raise together.FileTypeError(error)
@@ -169,7 +148,7 @@ class Finetune:
169
148
  {
170
149
  "tokens": token_estimate,
171
150
  "epochs": n_epochs,
172
- "parameters": model_param_count(model),
151
+ "parameters": together.Models._param_count(model),
173
152
  },
174
153
  ],
175
154
  "id": 1,
@@ -181,141 +160,59 @@ class Finetune:
181
160
  print(training_file_feedback)
182
161
  exit()
183
162
 
184
- # Send POST request to SUBMIT FINETUNE JOB
185
- # HTTP headers for authorization
186
- headers = {
187
- "Authorization": f"Bearer {together.api_key}",
188
- "Content-Type": "application/json",
189
- "User-Agent": together.user_agent,
190
- }
191
- try:
192
- response = requests.post(
193
- together.api_base_finetune, headers=headers, json=parameter_payload
194
- )
195
- response.raise_for_status()
196
- except requests.exceptions.RequestException as e:
197
- logger.critical(f"Response error raised: {e}")
198
- raise together.ResponseError(e)
199
-
200
- try:
201
- response_json = dict(response.json())
202
- except Exception as e:
203
- logger.critical(
204
- f"JSON Error raised: {e}\nResponse status code = {response.status_code}"
205
- )
206
- raise together.JSONError(e, http_status=response.status_code)
163
+ if confirm_inputs:
164
+ if adjusted_inputs:
165
+ print(
166
+ "Note: Some hyperparameters have been adjusted with their minimum/maximum values for a given model."
167
+ )
168
+ print("Job creation details:")
169
+ pp.pprint(parameter_payload)
170
+ confirm_response = input("\nDo you want to submit the job? [y/N]")
171
+ if "y" not in confirm_response.lower():
172
+ return {"status": "job not submitted"}
207
173
 
208
- return response_json
174
+ # Send POST request to SUBMIT FINETUNE JOB
175
+ response = create_post_request(
176
+ together.api_base_finetune, json=parameter_payload
177
+ )
178
+ if not response:
179
+ return {}
180
+ return response_to_dict(response)
209
181
 
210
182
  @classmethod
211
183
  def list(self) -> Dict[Any, Any]:
212
- verify_api_key(logger)
213
- headers = {
214
- "Authorization": f"Bearer {together.api_key}",
215
- "User-Agent": together.user_agent,
216
- }
217
-
218
184
  # send request
219
- try:
220
- response = requests.get(together.api_base_finetune, headers=headers)
221
- response.raise_for_status()
222
- except requests.exceptions.RequestException as e:
223
- logger.critical(f"Response error raised: {e}")
224
- raise together.ResponseError(e)
225
-
226
- try:
227
- response_json = dict(response.json())
228
- except Exception as e:
229
- logger.critical(
230
- f"JSON Error raised: {e}\nResponse status code = {response.status_code}"
231
- )
232
- raise together.JSONError(e, http_status=response.status_code)
233
-
234
- return response_json
185
+ response = create_get_request(together.api_base_finetune)
186
+ if not response:
187
+ return {}
188
+ return response_to_dict(response)
235
189
 
236
190
  @classmethod
237
191
  def retrieve(self, fine_tune_id: str) -> Dict[Any, Any]:
238
192
  retrieve_url = urllib.parse.urljoin(together.api_base_finetune, fine_tune_id)
239
-
240
- headers = {
241
- "Authorization": f"Bearer {together.api_key}",
242
- "User-Agent": together.user_agent,
243
- }
244
-
245
- # send request
246
- try:
247
- response = requests.get(retrieve_url, headers=headers)
248
- response.raise_for_status()
249
- except requests.exceptions.RequestException as e:
250
- logger.critical(f"Response error raised: {e}")
251
- raise together.ResponseError(e)
252
-
253
- try:
254
- response_json = dict(response.json())
255
- except Exception as e:
256
- logger.critical(
257
- f"JSON Error raised: {e}\nResponse status code = {response.status_code}"
258
- )
259
- raise together.JSONError(e, http_status=response.status_code)
260
-
261
- return response_json
193
+ response = create_get_request(retrieve_url)
194
+ if not response:
195
+ return {}
196
+ return response_to_dict(response)
262
197
 
263
198
  @classmethod
264
199
  def cancel(self, fine_tune_id: str) -> Dict[Any, Any]:
265
200
  relative_path = posixpath.join(fine_tune_id, "cancel")
266
201
  retrieve_url = urllib.parse.urljoin(together.api_base_finetune, relative_path)
267
-
268
- headers = {
269
- "Authorization": f"Bearer {together.api_key}",
270
- "User-Agent": together.user_agent,
271
- }
272
-
273
- # send request
274
- try:
275
- response = requests.post(retrieve_url, headers=headers)
276
- response.raise_for_status()
277
- except requests.exceptions.RequestException as e:
278
- logger.critical(f"Response error raised: {e}")
279
- raise together.ResponseError(e)
280
-
281
- try:
282
- response_json = dict(response.json())
283
- except Exception as e:
284
- logger.critical(
285
- f"JSON Error raised: {e}\nResponse status code = {response.status_code}"
286
- )
287
- raise together.JSONError(e, http_status=response.status_code)
288
-
289
- return response_json
202
+ response = create_post_request(retrieve_url)
203
+ if not response:
204
+ return {}
205
+ return response_to_dict(response)
290
206
 
291
207
  @classmethod
292
208
  def list_events(self, fine_tune_id: str) -> Dict[Any, Any]:
293
209
  # TODO enable stream
294
210
  relative_path = posixpath.join(fine_tune_id, "events")
295
211
  retrieve_url = urllib.parse.urljoin(together.api_base_finetune, relative_path)
296
-
297
- headers = {
298
- "Authorization": f"Bearer {together.api_key}",
299
- "User-Agent": together.user_agent,
300
- }
301
-
302
- # send request
303
- try:
304
- response = requests.get(retrieve_url, headers=headers)
305
- response.raise_for_status()
306
- except requests.exceptions.RequestException as e:
307
- logger.critical(f"Response error raised: {e}")
308
- raise together.ResponseError(e)
309
-
310
- try:
311
- response_json = dict(response.json())
312
- except Exception as e:
313
- logger.critical(
314
- f"JSON Error raised: {e}\nResponse status code = {response.status_code}"
315
- )
316
- raise together.JSONError(e, http_status=response.status_code)
317
-
318
- return response_json
212
+ response = create_get_request(retrieve_url)
213
+ if not response:
214
+ return {}
215
+ return response_to_dict(response)
319
216
 
320
217
  @classmethod
321
218
  def get_checkpoints(self, fine_tune_id: str) -> List[Dict[str, Any]]:
@@ -370,7 +267,7 @@ class Finetune:
370
267
  if step != -1:
371
268
  model_file_path += f"&checkpoint_step={step}"
372
269
 
373
- logger.info(f"Downloading weights from {model_file_path}...")
270
+ print(f"Downloading weights from {model_file_path}...")
374
271
 
375
272
  headers = {
376
273
  "Authorization": f"Bearer {together.api_key}",
together/image.py CHANGED
@@ -1,20 +1,13 @@
1
1
  from typing import Any, Dict, Optional
2
2
 
3
- import requests
4
-
5
3
  import together
6
- from together.utils.utils import get_logger, verify_api_key
4
+ from together.utils import create_post_request, get_logger, response_to_dict
7
5
 
8
6
 
9
- logger = get_logger(str(__name__), log_level=together.log_level)
7
+ logger = get_logger(str(__name__))
10
8
 
11
9
 
12
10
  class Image:
13
- def __init__(
14
- self,
15
- ) -> None:
16
- verify_api_key(logger)
17
-
18
11
  @classmethod
19
12
  def create(
20
13
  self,
@@ -42,37 +35,10 @@ class Image:
42
35
  "negative_prompt": negative_prompt,
43
36
  }
44
37
 
45
- # HTTP headers for authorization
46
- headers = {
47
- "Authorization": f"Bearer {together.api_key}",
48
- "Content-Type": "application/json",
49
- "User-Agent": together.user_agent,
50
- }
51
-
52
38
  # send request
53
- try:
54
- response = requests.post(
55
- together.api_base_complete,
56
- headers=headers,
57
- json=parameter_payload,
58
- )
59
- except requests.exceptions.RequestException as e:
60
- logger.critical(f"Response error raised: {e}")
61
- raise together.ResponseError(e)
62
-
63
- if response.status_code == 429:
64
- logger.critical(
65
- f"No running instances for {model}. You can start an instance by navigating to the Together Playground at api.together.ai"
66
- )
67
- raise together.InstanceError(model=model)
68
-
69
- response.raise_for_status()
70
-
71
- try:
72
- response_json = dict(response.json())
73
- except Exception as e:
74
- logger.critical(
75
- f"JSON Error raised: {e}\nResponse status code = {response.status_code}"
76
- )
77
- raise together.JSONError(e, http_status=response.status_code)
78
- return response_json
39
+ response = create_post_request(
40
+ together.api_base_complete, json=parameter_payload
41
+ )
42
+ if not response:
43
+ return {}
44
+ return response_to_dict(response)