together 0.2.9__tar.gz → 0.2.11__tar.gz

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.
Files changed (27) hide show
  1. {together-0.2.9 → together-0.2.11}/PKG-INFO +2 -1
  2. {together-0.2.9 → together-0.2.11}/pyproject.toml +2 -1
  3. {together-0.2.9 → together-0.2.11}/src/together/__init__.py +2 -1
  4. {together-0.2.9 → together-0.2.11}/src/together/complete.py +83 -4
  5. {together-0.2.9 → together-0.2.11}/src/together/files.py +36 -8
  6. {together-0.2.9 → together-0.2.11}/src/together/utils.py +25 -28
  7. {together-0.2.9 → together-0.2.11}/LICENSE +0 -0
  8. {together-0.2.9 → together-0.2.11}/README.md +0 -0
  9. {together-0.2.9 → together-0.2.11}/src/together/cli/__init__.py +0 -0
  10. {together-0.2.9 → together-0.2.11}/src/together/cli/cli.py +0 -0
  11. {together-0.2.9 → together-0.2.11}/src/together/commands/__init__.py +0 -0
  12. {together-0.2.9 → together-0.2.11}/src/together/commands/chat.py +0 -0
  13. {together-0.2.9 → together-0.2.11}/src/together/commands/complete.py +0 -0
  14. {together-0.2.9 → together-0.2.11}/src/together/commands/embeddings.py +0 -0
  15. {together-0.2.9 → together-0.2.11}/src/together/commands/files.py +0 -0
  16. {together-0.2.9 → together-0.2.11}/src/together/commands/finetune.py +0 -0
  17. {together-0.2.9 → together-0.2.11}/src/together/commands/image.py +0 -0
  18. {together-0.2.9 → together-0.2.11}/src/together/commands/models.py +0 -0
  19. {together-0.2.9 → together-0.2.11}/src/together/embeddings.py +0 -0
  20. {together-0.2.9 → together-0.2.11}/src/together/error.py +0 -0
  21. {together-0.2.9 → together-0.2.11}/src/together/finetune.py +0 -0
  22. {together-0.2.9 → together-0.2.11}/src/together/image.py +0 -0
  23. {together-0.2.9 → together-0.2.11}/src/together/models.py +0 -0
  24. {together-0.2.9 → together-0.2.11}/src/together/tools/__init__.py +0 -0
  25. {together-0.2.9 → together-0.2.11}/src/together/tools/conversation.py +0 -0
  26. {together-0.2.9 → together-0.2.11}/src/together/types.py +0 -0
  27. {together-0.2.9 → together-0.2.11}/src/together/version.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: together
3
- Version: 0.2.9
3
+ Version: 0.2.11
4
4
  Summary: Python client for Together's Cloud Platform!
5
5
  Home-page: https://github.com/togethercomputer/together
6
6
  License: Apache-2.0
@@ -16,6 +16,7 @@ Classifier: Programming Language :: Python :: 3.9
16
16
  Classifier: Programming Language :: Python :: 3.10
17
17
  Classifier: Programming Language :: Python :: 3.11
18
18
  Classifier: Programming Language :: Python :: 3.12
19
+ Requires-Dist: aiohttp (>=3.7.4,<4.0.0)
19
20
  Requires-Dist: pydantic (>=2.5.0,<3.0.0)
20
21
  Requires-Dist: requests (>=2.31.0,<3.0.0)
21
22
  Requires-Dist: sseclient-py (>=1.7.2,<2.0.0)
@@ -4,7 +4,7 @@ build-backend = "poetry.masonry.api"
4
4
 
5
5
  [tool.poetry]
6
6
  name = "together"
7
- version = "0.2.9"
7
+ version = "0.2.11"
8
8
  authors = [
9
9
  "Together AI <support@together.ai>"
10
10
  ]
@@ -27,6 +27,7 @@ tqdm = "^4.66.1"
27
27
  sseclient-py = "^1.7.2"
28
28
  tabulate = "^0.9.0"
29
29
  pydantic = "^2.5.0"
30
+ aiohttp = "^3.7.4"
30
31
 
31
32
  [tool.poetry.group.quality]
32
33
  optional = true
@@ -33,7 +33,7 @@ BACKOFF_FACTOR = 0.2
33
33
 
34
34
  min_samples = 100
35
35
 
36
- from .complete import Complete, Completion
36
+ from .complete import AsyncComplete, Complete, Completion
37
37
  from .embeddings import Embeddings
38
38
  from .error import *
39
39
  from .files import Files
@@ -77,6 +77,7 @@ __all__ = [
77
77
  "Models",
78
78
  "Complete",
79
79
  "Completion",
80
+ "AsyncComplete",
80
81
  "Files",
81
82
  "Finetune",
82
83
  "Image",
@@ -1,9 +1,11 @@
1
1
  import json
2
2
  from typing import Any, Dict, Iterator, List, Optional, Union
3
3
 
4
+ from aiohttp import ClientSession, ClientTimeout
5
+
4
6
  import together
5
7
  from together.types import TogetherResponse
6
- from together.utils import create_post_request, get_logger, sse_client
8
+ from together.utils import create_post_request, get_headers, get_logger, sse_client
7
9
 
8
10
 
9
11
  logger = get_logger(str(__name__))
@@ -12,7 +14,7 @@ logger = get_logger(str(__name__))
12
14
  class Complete:
13
15
  @classmethod
14
16
  def create(
15
- self,
17
+ cls,
16
18
  prompt: str,
17
19
  model: Optional[str] = "",
18
20
  max_tokens: Optional[int] = 128,
@@ -60,7 +62,7 @@ class Complete:
60
62
 
61
63
  @classmethod
62
64
  def create_streaming(
63
- self,
65
+ cls,
64
66
  prompt: str,
65
67
  model: Optional[str] = "",
66
68
  max_tokens: Optional[int] = 128,
@@ -130,7 +132,7 @@ class Complete:
130
132
  class Completion:
131
133
  @classmethod
132
134
  def create(
133
- self,
135
+ cls,
134
136
  prompt: str,
135
137
  model: Optional[str] = "",
136
138
  max_tokens: Optional[int] = 128,
@@ -172,3 +174,80 @@ class Completion:
172
174
  api_key=api_key,
173
175
  cast=True,
174
176
  )
177
+
178
+
179
+ class AsyncComplete:
180
+ @classmethod
181
+ async def create(
182
+ cls,
183
+ prompt: str,
184
+ model: Optional[str],
185
+ max_tokens: int = 20,
186
+ repetition_penalty: Optional[float] = None,
187
+ stop: Optional[List[str]] = None,
188
+ temperature: Optional[float] = None,
189
+ top_k: Optional[int] = None,
190
+ top_p: Optional[float] = None,
191
+ raw: Optional[bool] = False,
192
+ logprobs: Optional[int] = None,
193
+ safety_model: Optional[str] = None,
194
+ stream: Optional[bool] = False,
195
+ timeout: Optional[int] = 10,
196
+ ) -> Any:
197
+ if model == "":
198
+ model = together.default_text_model
199
+
200
+ headers = get_headers()
201
+
202
+ # Provide a default value for timeout if it is None
203
+ timeout = timeout or 10
204
+ client_timeout = ClientTimeout(timeout * 60)
205
+
206
+ parameter_payload = {
207
+ "model": model,
208
+ "prompt": prompt,
209
+ "top_p": top_p,
210
+ "top_k": top_k,
211
+ "temperature": temperature,
212
+ "max_tokens": max_tokens,
213
+ "stop": stop,
214
+ "repetition_penalty": repetition_penalty,
215
+ "logprobs": logprobs,
216
+ "safety_model": safety_model,
217
+ }
218
+
219
+ async with ClientSession(headers=headers, timeout=client_timeout) as session:
220
+ async with session.post(
221
+ together.api_base_complete, json=parameter_payload
222
+ ) as resp:
223
+
224
+ async def streamer() -> Any:
225
+ # Parse ServerSentEvents
226
+ async for byte_payload in resp.content:
227
+ # Skip line
228
+ if byte_payload == b"\n":
229
+ continue
230
+
231
+ payload = byte_payload.decode("utf-8")
232
+
233
+ # Event data
234
+ if payload.startswith("data:"):
235
+ # Decode payload
236
+ json_payload = json.loads(
237
+ payload.lstrip("data:").rstrip("/n")
238
+ )
239
+
240
+ if raw:
241
+ yield json_payload
242
+ else:
243
+ # Parse payload
244
+ response = TogetherResponse(**json_payload)
245
+
246
+ yield response
247
+
248
+ if stream:
249
+ return await streamer()
250
+ else:
251
+ payload = await resp.json()
252
+ response = TogetherResponse(**payload)
253
+ return response
@@ -60,6 +60,9 @@ class Files:
60
60
  f"Invalid file supplied. Failed to upload.\nReport:\n {report_dict}"
61
61
  )
62
62
  else:
63
+ logger.warning(
64
+ "Caution: File check is disabled. Together.ai will not be able to detect errors in your file."
65
+ )
63
66
  report_dict = {}
64
67
 
65
68
  session = requests.Session()
@@ -256,6 +259,7 @@ def check_json(
256
259
  if not os.path.isfile(file):
257
260
  report_dict["file_present"] = f"File not found at given file path {file}"
258
261
  report_dict["is_check_passed"] = False
262
+ return report_dict
259
263
  else:
260
264
  report_dict["file_present"] = "File found"
261
265
 
@@ -266,10 +270,28 @@ def check_json(
266
270
  "file_size"
267
271
  ] = f"File size {round(file_size / NUM_BYTES_IN_GB ,3)} GB is greater than our limit of 4.9 GB"
268
272
  report_dict["is_check_passed"] = False
273
+ elif file_size == 0:
274
+ report_dict["file_size"] = "File is empty"
275
+ report_dict["is_check_passed"] = False
276
+ return report_dict
269
277
  else:
270
278
  report_dict["file_size"] = f"File size {round(file_size / (2**30) ,3)} GB"
271
279
 
280
+ # Check that the file is UTF-8 encoded. If not report where the error occurs.
281
+ try:
282
+ with open(file, "r", encoding="utf-8") as f:
283
+ f.read()
284
+ except UnicodeDecodeError as e:
285
+ report_dict["utf8"] = (
286
+ f"File is not UTF-8 encoded. Error raised: {e}."
287
+ f"See https://docs.together.ai/docs/fine-tuning for more information."
288
+ )
289
+ report_dict["is_check_passed"] = False
290
+ return report_dict
291
+
272
292
  with open(file) as f:
293
+ # idx must be instantiated so decode errors (e.g. file is a tar) or empty files are caught
294
+ idx = -1
273
295
  try:
274
296
  for idx, line in enumerate(f):
275
297
  json_line = json.loads(line) # each line in jsonlines should be a json
@@ -316,14 +338,20 @@ def check_json(
316
338
  report_dict["num_samples"] = idx + 1
317
339
 
318
340
  except ValueError:
319
- report_dict["load_json"] = (
320
- f"File should be a valid jsonlines (.jsonl) with a json in each line."
321
- 'Example of valid json: {"text":"my sample string"}'
322
- "Valid json not found in one or more lines in file."
323
- "see https://docs.together.ai/docs/fine-tuning."
324
- f"The first line where this occur is line {idx+1}, where 1 is the first line."
325
- f"{str(line)}"
326
- )
341
+ if idx < 0:
342
+ report_dict["load_json"] = (
343
+ "Unable to decode file. "
344
+ "File may be empty or in an unsupported format."
345
+ )
346
+ else:
347
+ report_dict["load_json"] = (
348
+ f"File should be a valid jsonlines (.jsonl) with a json in each line."
349
+ 'Example of valid json: {"text":"my sample string"}'
350
+ "Valid json not found in one or more lines in file."
351
+ "see https://docs.together.ai/docs/fine-tuning."
352
+ f"The first line where this occur is line {idx+1}, where 1 is the first line."
353
+ f"{str(line)}"
354
+ )
327
355
  report_dict["is_check_passed"] = False
328
356
 
329
357
  return report_dict
@@ -75,6 +75,27 @@ def parse_timestamp(timestamp: str) -> datetime:
75
75
  raise ValueError("Timestamp does not match any expected format")
76
76
 
77
77
 
78
+ def response_status_exception(response: requests.Response) -> None:
79
+ if response.status_code == 429:
80
+ raise together.RateLimitError(
81
+ message="Too many requests received. Please pace your requests."
82
+ )
83
+ elif response.status_code == 500:
84
+ raise Exception("server encountered an unexpected condition")
85
+ elif response.status_code == 401:
86
+ raise Exception("invalid authentication credentials")
87
+ response.raise_for_status()
88
+
89
+
90
+ def get_headers() -> Dict[str, str]:
91
+ headers = {
92
+ "Authorization": f"Bearer {together.api_key}",
93
+ "Content-Type": "application/json",
94
+ "User-Agent": together.user_agent,
95
+ }
96
+ return headers
97
+
98
+
78
99
  def create_post_request(
79
100
  url: str,
80
101
  headers: Optional[Dict[Any, Any]] = None,
@@ -87,11 +108,7 @@ def create_post_request(
87
108
  verify_api_key()
88
109
 
89
110
  if not headers:
90
- headers = {
91
- "Authorization": f"Bearer {api_key or together.api_key}",
92
- "Content-Type": "application/json",
93
- "User-Agent": together.user_agent,
94
- }
111
+ headers = get_headers()
95
112
 
96
113
  # send request
97
114
  try:
@@ -99,15 +116,7 @@ def create_post_request(
99
116
  except requests.exceptions.RequestException as e:
100
117
  raise together.ResponseError(e)
101
118
 
102
- if response.status_code == 429:
103
- raise together.RateLimitError(
104
- message="Too many requests received. Please pace your requests."
105
- )
106
- elif response.status_code == 500:
107
- raise Exception("Invalid API key supplied.")
108
- elif response.status_code == 401:
109
- raise Exception("API Key not supplied")
110
- response.raise_for_status()
119
+ response_status_exception(response)
111
120
 
112
121
  return response
113
122
 
@@ -127,11 +136,7 @@ def create_get_request(
127
136
  verify_api_key()
128
137
 
129
138
  if not headers:
130
- headers = {
131
- "Authorization": f"Bearer {together.api_key}",
132
- "Content-Type": "application/json",
133
- "User-Agent": together.user_agent,
134
- }
139
+ headers = get_headers()
135
140
 
136
141
  # send request
137
142
  try:
@@ -139,15 +144,7 @@ def create_get_request(
139
144
  except requests.exceptions.RequestException as e:
140
145
  raise together.ResponseError(e)
141
146
 
142
- if response.status_code == 429:
143
- raise together.RateLimitError(
144
- message="Too many requests received. Please pace your requests."
145
- )
146
- elif response.status_code == 500:
147
- raise Exception("Invalid API key supplied.")
148
- elif response.status_code == 401:
149
- raise Exception("API Key not supplied")
150
- response.raise_for_status()
147
+ response_status_exception(response)
151
148
 
152
149
  return response
153
150
 
File without changes
File without changes