Skip to content

Commit e81dce4

Browse files
stainless-app[bot]dulaj-meblainekasten
authored
release: 2.3.1 (#288)
* codegen metadata * codegen metadata * chore(ci): skip uploading artifacts on stainless-internal branches * chore: update placeholder string * fix(jig): compile cache should be owned by current user instead of root so cleanup works * fix: Improve multipart file uploads (#290) * release: 2.3.1 --------- Co-authored-by: stainless-app[bot] <142633134+stainless-app[bot]@users.noreply.github.com> Co-authored-by: Dulaj Disanayaka <8828757+dulaj-me@users.noreply.github.com> Co-authored-by: Blaine Kasten <blainekasten@gmail.com>
1 parent dbad11f commit e81dce4

File tree

13 files changed

+65
-33
lines changed

13 files changed

+65
-33
lines changed

.github/workflows/ci.yml

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,14 +58,18 @@ jobs:
5858
run: uv build
5959

6060
- name: Get GitHub OIDC Token
61-
if: github.repository == 'stainless-sdks/togetherai-python'
61+
if: |-
62+
github.repository == 'stainless-sdks/togetherai-python' &&
63+
!startsWith(github.ref, 'refs/heads/stl/')
6264
id: github-oidc
6365
uses: actions/github-script@v8
6466
with:
6567
script: core.setOutput('github_token', await core.getIDToken());
6668

6769
- name: Upload tarball
68-
if: github.repository == 'stainless-sdks/togetherai-python'
70+
if: |-
71+
github.repository == 'stainless-sdks/togetherai-python' &&
72+
!startsWith(github.ref, 'refs/heads/stl/')
6973
env:
7074
URL: https://pkg.stainless.com/s
7175
AUTH: ${{ steps.github-oidc.outputs.github_token }}

.release-please-manifest.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
{
2-
".": "2.3.0"
2+
".": "2.3.1"
33
}

.stats.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
configured_endpoints: 74
2-
openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/togetherai%2Ftogetherai-af3c0c80dd8e6f0908da6156c9e213f6e8f801c1b96d34fe892814fd12d25d49.yml
3-
openapi_spec_hash: 9f4213309ef450d7a4416efba1532c9a
2+
openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/togetherai%2Ftogetherai-7a33f9086abc839141dcbfef36fc505b7c2618dc893a24e11a09e5bffe976bcf.yml
3+
openapi_spec_hash: dba6fe0b4f5f10181628be7e93718d3a
44
config_hash: b66198d27b4d5c152688ff6cccfdeab5

CHANGELOG.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,20 @@
11
# Changelog
22

3+
## 2.3.1 (2026-03-09)
4+
5+
Full Changelog: [v2.3.0...v2.3.1](https://github.com/togethercomputer/together-py/compare/v2.3.0...v2.3.1)
6+
7+
### Bug Fixes
8+
9+
* Improve multipart file uploads ([#290](https://github.com/togethercomputer/together-py/issues/290)) ([b27d19c](https://github.com/togethercomputer/together-py/commit/b27d19c7d789fbdcd54a5aa47872a8c425ea3e47))
10+
* **jig:** compile cache should be owned by current user instead of root so cleanup works ([504717e](https://github.com/togethercomputer/together-py/commit/504717e21983ba7523326b7dafa64dd6b6fecb8d))
11+
12+
13+
### Chores
14+
15+
* **ci:** skip uploading artifacts on stainless-internal branches ([e7d23c3](https://github.com/togethercomputer/together-py/commit/e7d23c36df459a47276f33b615301e8160231c32))
16+
* update placeholder string ([9b0a5b9](https://github.com/togethercomputer/together-py/commit/9b0a5b984f4fe74ab8967e0a6dfe620b203b309a))
17+
318
## 2.3.0 (2026-03-05)
419

520
Full Changelog: [v2.2.0...v2.3.0](https://github.com/togethercomputer/together-py/compare/v2.2.0...v2.3.0)

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[project]
22
name = "together"
3-
version = "2.3.0"
3+
version = "2.3.1"
44
description = "The official Python library for the together API"
55
dynamic = ["readme"]
66
license = "Apache-2.0"

src/together/_version.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
22

33
__title__ = "together"
4-
__version__ = "2.3.0" # x-release-please-version
4+
__version__ = "2.3.1" # x-release-please-version

src/together/lib/cli/api/_utils.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
from __future__ import annotations
22

3+
import os
34
import re
45
import sys
56
import math
@@ -190,6 +191,9 @@ def wrapper(*args: Any, **kwargs: Any) -> Any:
190191
click.echo(prefix_styled + click.style(error_msg, fg="red"))
191192
sys.exit(1)
192193
except Exception as e:
194+
if os.getenv("TOGETHER_LOG", "").lower() == "debug":
195+
# Raise the error with the full traceback
196+
raise
193197
click.echo(prefix_styled + click.style("Failed", fg="red"))
194198
click.echo(prefix_styled + click.style(f"An unexpected error occurred - {str(e)}", fg="red"))
195199
sys.exit(1)

src/together/lib/cli/api/beta/jig/jig.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -420,18 +420,17 @@ def _build_warm_image(base_image: str) -> None:
420420
"""
421421
cache_dir = Path(WARMUP_DEST)
422422
# clean any existing cache
423-
try:
423+
if cache_dir.exists():
424424
shutil.rmtree(cache_dir)
425-
except FileNotFoundError:
426-
pass
427425
cache_dir.mkdir(exist_ok=True)
428426

429427
echo("\N{FIRE} Running warmup to generate compile cache...")
430428

431429
# run container with GPU and RUN_AND_EXIT=1
432430
# mount current dir as /app so warmup_inputs can reference local weights
433431
# mount cache dir for compile artifacts
434-
cmd = ["docker", "run", "--rm", "--gpus", "all", "-e", "RUN_AND_EXIT=1"]
432+
# run as current user so cache files on the bind mount are not owned by root
433+
cmd = ["docker", "run", "--rm", "--gpus", "all", "--user", f"{os.getuid()}:{os.getgid()}", "-e", "RUN_AND_EXIT=1"]
435434
cmd.extend(["-e", f"{WARMUP_ENV_NAME}=/app/{WARMUP_DEST}"])
436435
cmd.extend(["-v", f"{Path.cwd()}:/app"])
437436
# if MODEL_PRELOAD_PATH is set, also mount that (e.g. ~/.cache/huggingface)

src/together/lib/constants.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,8 @@
2525
MIN_PART_SIZE_MB = 5 # Minimum part size (S3 requirement)
2626
TARGET_PART_SIZE_MB = 250 # Target part size for optimal performance
2727
MAX_MULTIPART_PARTS = 250 # Maximum parts per upload (S3 limit)
28-
MULTIPART_UPLOAD_TIMEOUT = 300 # Timeout in seconds for uploading each part
28+
MULTIPART_UPLOAD_TIMEOUT = 300 # Connect/read timeout in seconds for each part
29+
MULTIPART_UPLOAD_WRITE_TIMEOUT = 3600 # Write timeout in seconds (1h) for large part uploads
2930
MULTIPART_THRESHOLD_GB = 5.0 # threshold for switching to multipart upload
3031

3132
# Minimum number of samples required for fine-tuning file

src/together/lib/resources/files.py

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
DOWNLOAD_MAX_RETRY_DELAY,
3737
MULTIPART_UPLOAD_TIMEOUT,
3838
DOWNLOAD_INITIAL_RETRY_DELAY,
39+
MULTIPART_UPLOAD_WRITE_TIMEOUT,
3940
)
4041
from ..._resource import SyncAPIResource, AsyncAPIResource
4142
from ..types.error import DownloadError, FileTypeError
@@ -602,11 +603,15 @@ def _upload_single_part(self, part_info: Dict[str, Any], part_data: bytes) -> st
602603

603604
part_headers = part_info.get("Headers", {})
604605

606+
timeout = httpx.Timeout(
607+
MULTIPART_UPLOAD_TIMEOUT,
608+
write=MULTIPART_UPLOAD_WRITE_TIMEOUT,
609+
)
605610
response = self._client._client.put(
606611
url=upload_url,
607612
content=part_data,
608613
headers=part_headers,
609-
timeout=MULTIPART_UPLOAD_TIMEOUT,
614+
timeout=timeout,
610615
)
611616
response.raise_for_status()
612617

@@ -670,7 +675,7 @@ def _abort_upload(self, url: str, upload_id: str, file_id: str) -> None:
670675

671676
self._client.post(
672677
path=f"{url}/multipart/abort",
673-
cast_to=dict,
678+
cast_to=httpx.Response,
674679
body=payload,
675680
options={"headers": {"Content-Type": "application/json"}},
676681
)
@@ -1003,12 +1008,16 @@ def _upload_single_part_sync(self, part_info: Dict[str, Any], part_data: bytes)
10031008

10041009
part_headers = part_info.get("Headers", {})
10051010

1011+
timeout = httpx.Timeout(
1012+
MULTIPART_UPLOAD_TIMEOUT,
1013+
write=MULTIPART_UPLOAD_WRITE_TIMEOUT,
1014+
)
10061015
with httpx.Client() as client:
10071016
response = client.put(
10081017
url=upload_url,
10091018
content=part_data,
10101019
headers=part_headers,
1011-
timeout=MULTIPART_UPLOAD_TIMEOUT,
1020+
timeout=timeout,
10121021
)
10131022
response.raise_for_status()
10141023

@@ -1068,7 +1077,7 @@ async def _abort_upload(self, url: str, upload_id: str, file_id: str) -> None:
10681077

10691078
await self._client.post(
10701079
path=f"{url}/multipart/abort",
1071-
cast_to=dict,
1080+
cast_to=httpx.Response,
10721081
body=payload,
10731082
options={"headers": {"Content-Type": "application/json"}},
10741083
)

0 commit comments

Comments
 (0)