together 2.0.0a18__py3-none-any.whl → 2.0.0a19__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/_client.py +0 -38
- together/_version.py +1 -1
- together/lib/cli/api/beta/__init__.py +2 -0
- together/lib/cli/api/beta/jig/__init__.py +52 -0
- together/lib/cli/api/beta/jig/_config.py +170 -0
- together/lib/cli/api/beta/jig/jig.py +664 -0
- together/lib/cli/api/beta/jig/secrets.py +138 -0
- together/lib/cli/api/beta/jig/volumes.py +509 -0
- together/lib/cli/api/endpoints/hardware.py +3 -3
- together/lib/cli/api/models/upload.py +1 -1
- together/resources/__init__.py +0 -14
- together/resources/beta/jig/jig.py +8 -28
- together/resources/endpoints.py +103 -1
- together/types/__init__.py +2 -2
- together/types/beta/jig_deploy_params.py +1 -1
- together/types/beta/jig_retrieve_logs_params.py +0 -3
- together/types/{hardware_list_params.py → endpoint_list_hardware_params.py} +2 -2
- together/types/{hardware_list_response.py → endpoint_list_hardware_response.py} +2 -2
- {together-2.0.0a18.dist-info → together-2.0.0a19.dist-info}/METADATA +5 -1
- {together-2.0.0a18.dist-info → together-2.0.0a19.dist-info}/RECORD +23 -19
- together/resources/hardware.py +0 -181
- {together-2.0.0a18.dist-info → together-2.0.0a19.dist-info}/WHEEL +0 -0
- {together-2.0.0a18.dist-info → together-2.0.0a19.dist-info}/entry_points.txt +0 -0
- {together-2.0.0a18.dist-info → together-2.0.0a19.dist-info}/licenses/LICENSE +0 -0
together/_client.py
CHANGED
|
@@ -45,7 +45,6 @@ if TYPE_CHECKING:
|
|
|
45
45
|
rerank,
|
|
46
46
|
videos,
|
|
47
47
|
batches,
|
|
48
|
-
hardware,
|
|
49
48
|
endpoints,
|
|
50
49
|
embeddings,
|
|
51
50
|
completions,
|
|
@@ -58,7 +57,6 @@ if TYPE_CHECKING:
|
|
|
58
57
|
from .resources.rerank import RerankResource, AsyncRerankResource
|
|
59
58
|
from .resources.videos import VideosResource, AsyncVideosResource
|
|
60
59
|
from .resources.batches import BatchesResource, AsyncBatchesResource
|
|
61
|
-
from .resources.hardware import HardwareResource, AsyncHardwareResource
|
|
62
60
|
from .resources.beta.beta import BetaResource, AsyncBetaResource
|
|
63
61
|
from .resources.chat.chat import ChatResource, AsyncChatResource
|
|
64
62
|
from .resources.endpoints import EndpointsResource, AsyncEndpointsResource
|
|
@@ -213,12 +211,6 @@ class Together(SyncAPIClient):
|
|
|
213
211
|
|
|
214
212
|
return EndpointsResource(self)
|
|
215
213
|
|
|
216
|
-
@cached_property
|
|
217
|
-
def hardware(self) -> HardwareResource:
|
|
218
|
-
from .resources.hardware import HardwareResource
|
|
219
|
-
|
|
220
|
-
return HardwareResource(self)
|
|
221
|
-
|
|
222
214
|
@cached_property
|
|
223
215
|
def rerank(self) -> RerankResource:
|
|
224
216
|
from .resources.rerank import RerankResource
|
|
@@ -484,12 +476,6 @@ class AsyncTogether(AsyncAPIClient):
|
|
|
484
476
|
|
|
485
477
|
return AsyncEndpointsResource(self)
|
|
486
478
|
|
|
487
|
-
@cached_property
|
|
488
|
-
def hardware(self) -> AsyncHardwareResource:
|
|
489
|
-
from .resources.hardware import AsyncHardwareResource
|
|
490
|
-
|
|
491
|
-
return AsyncHardwareResource(self)
|
|
492
|
-
|
|
493
479
|
@cached_property
|
|
494
480
|
def rerank(self) -> AsyncRerankResource:
|
|
495
481
|
from .resources.rerank import AsyncRerankResource
|
|
@@ -701,12 +687,6 @@ class TogetherWithRawResponse:
|
|
|
701
687
|
|
|
702
688
|
return EndpointsResourceWithRawResponse(self._client.endpoints)
|
|
703
689
|
|
|
704
|
-
@cached_property
|
|
705
|
-
def hardware(self) -> hardware.HardwareResourceWithRawResponse:
|
|
706
|
-
from .resources.hardware import HardwareResourceWithRawResponse
|
|
707
|
-
|
|
708
|
-
return HardwareResourceWithRawResponse(self._client.hardware)
|
|
709
|
-
|
|
710
690
|
@cached_property
|
|
711
691
|
def rerank(self) -> rerank.RerankResourceWithRawResponse:
|
|
712
692
|
from .resources.rerank import RerankResourceWithRawResponse
|
|
@@ -804,12 +784,6 @@ class AsyncTogetherWithRawResponse:
|
|
|
804
784
|
|
|
805
785
|
return AsyncEndpointsResourceWithRawResponse(self._client.endpoints)
|
|
806
786
|
|
|
807
|
-
@cached_property
|
|
808
|
-
def hardware(self) -> hardware.AsyncHardwareResourceWithRawResponse:
|
|
809
|
-
from .resources.hardware import AsyncHardwareResourceWithRawResponse
|
|
810
|
-
|
|
811
|
-
return AsyncHardwareResourceWithRawResponse(self._client.hardware)
|
|
812
|
-
|
|
813
787
|
@cached_property
|
|
814
788
|
def rerank(self) -> rerank.AsyncRerankResourceWithRawResponse:
|
|
815
789
|
from .resources.rerank import AsyncRerankResourceWithRawResponse
|
|
@@ -907,12 +881,6 @@ class TogetherWithStreamedResponse:
|
|
|
907
881
|
|
|
908
882
|
return EndpointsResourceWithStreamingResponse(self._client.endpoints)
|
|
909
883
|
|
|
910
|
-
@cached_property
|
|
911
|
-
def hardware(self) -> hardware.HardwareResourceWithStreamingResponse:
|
|
912
|
-
from .resources.hardware import HardwareResourceWithStreamingResponse
|
|
913
|
-
|
|
914
|
-
return HardwareResourceWithStreamingResponse(self._client.hardware)
|
|
915
|
-
|
|
916
884
|
@cached_property
|
|
917
885
|
def rerank(self) -> rerank.RerankResourceWithStreamingResponse:
|
|
918
886
|
from .resources.rerank import RerankResourceWithStreamingResponse
|
|
@@ -1010,12 +978,6 @@ class AsyncTogetherWithStreamedResponse:
|
|
|
1010
978
|
|
|
1011
979
|
return AsyncEndpointsResourceWithStreamingResponse(self._client.endpoints)
|
|
1012
980
|
|
|
1013
|
-
@cached_property
|
|
1014
|
-
def hardware(self) -> hardware.AsyncHardwareResourceWithStreamingResponse:
|
|
1015
|
-
from .resources.hardware import AsyncHardwareResourceWithStreamingResponse
|
|
1016
|
-
|
|
1017
|
-
return AsyncHardwareResourceWithStreamingResponse(self._client.hardware)
|
|
1018
|
-
|
|
1019
981
|
@cached_property
|
|
1020
982
|
def rerank(self) -> rerank.AsyncRerankResourceWithStreamingResponse:
|
|
1021
983
|
from .resources.rerank import AsyncRerankResourceWithStreamingResponse
|
together/_version.py
CHANGED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
"""Jig CLI - deployment tool for Together AI."""
|
|
2
|
+
|
|
3
|
+
import click
|
|
4
|
+
|
|
5
|
+
from together.lib.cli.api.beta.jig.jig import (
|
|
6
|
+
init,
|
|
7
|
+
logs,
|
|
8
|
+
push,
|
|
9
|
+
build,
|
|
10
|
+
deploy,
|
|
11
|
+
status,
|
|
12
|
+
endpoint,
|
|
13
|
+
submit,
|
|
14
|
+
destroy,
|
|
15
|
+
dockerfile,
|
|
16
|
+
job_status,
|
|
17
|
+
queue_status,
|
|
18
|
+
list_deployments,
|
|
19
|
+
)
|
|
20
|
+
from together.lib.cli.api.beta.jig.secrets import secrets
|
|
21
|
+
from together.lib.cli.api.beta.jig.volumes import volumes
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
@click.group()
|
|
25
|
+
@click.pass_context
|
|
26
|
+
def jig(ctx: click.Context) -> None:
|
|
27
|
+
"""Jig commands - deploy and manage containers"""
|
|
28
|
+
pass
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def add_commands(parent: click.Group):
|
|
32
|
+
# Add subgroups
|
|
33
|
+
parent.add_command(secrets)
|
|
34
|
+
parent.add_command(volumes)
|
|
35
|
+
|
|
36
|
+
# Add main commands
|
|
37
|
+
parent.add_command(init)
|
|
38
|
+
parent.add_command(dockerfile)
|
|
39
|
+
parent.add_command(build)
|
|
40
|
+
parent.add_command(push)
|
|
41
|
+
parent.add_command(deploy)
|
|
42
|
+
parent.add_command(status)
|
|
43
|
+
parent.add_command(endpoint)
|
|
44
|
+
parent.add_command(logs)
|
|
45
|
+
parent.add_command(destroy)
|
|
46
|
+
parent.add_command(submit)
|
|
47
|
+
parent.add_command(job_status)
|
|
48
|
+
parent.add_command(queue_status)
|
|
49
|
+
parent.add_command(list_deployments)
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
add_commands(jig)
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
"""Configuration and state management for jig CLI."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import os
|
|
6
|
+
import sys
|
|
7
|
+
import json
|
|
8
|
+
from typing import TYPE_CHECKING, Any, Optional
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
from dataclasses import field, asdict, dataclass
|
|
11
|
+
|
|
12
|
+
import click
|
|
13
|
+
|
|
14
|
+
if TYPE_CHECKING:
|
|
15
|
+
import tomli as tomllib
|
|
16
|
+
else:
|
|
17
|
+
try:
|
|
18
|
+
import tomllib
|
|
19
|
+
except ImportError:
|
|
20
|
+
import tomli as tomllib
|
|
21
|
+
|
|
22
|
+
# --- Environment Configuration ---
|
|
23
|
+
|
|
24
|
+
DEBUG = os.getenv("TOGETHER_DEBUG", "").strip()[:1] in ("y", "1", "t")
|
|
25
|
+
|
|
26
|
+
UPLOAD_CONCURRENCY_LIMIT = int(os.getenv("TOGETHER_UPLOAD_CONCURRENCY", "15"))
|
|
27
|
+
MULTIPART_CHUNK_SIZE_MB = int(os.getenv("TOGETHER_MULTIPART_CHUNK_SIZE_MB", "20"))
|
|
28
|
+
MULTIPART_THRESHOLD_MB = int(os.getenv("TOGETHER_MULTIPART_THRESHOLD_MB", "100"))
|
|
29
|
+
MAX_UPLOAD_RETRIES = 3
|
|
30
|
+
|
|
31
|
+
# Warmup configuration (for torch compile cache)
|
|
32
|
+
WARMUP_ENV_NAME = os.getenv("WARMUP_ENV_NAME", "TORCHINDUCTOR_CACHE_DIR")
|
|
33
|
+
WARMUP_DEST = os.getenv("WARMUP_DEST", "torch_cache")
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
# --- Configuration Dataclasses ---
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
@dataclass
|
|
40
|
+
class ImageConfig:
|
|
41
|
+
"""Container image configuration from pyproject.toml"""
|
|
42
|
+
|
|
43
|
+
python_version: str = "3.11"
|
|
44
|
+
system_packages: list[str] = field(default_factory=list[str])
|
|
45
|
+
environment: dict[str, str] = field(default_factory=dict[str, str])
|
|
46
|
+
run: list[str] = field(default_factory=list[str])
|
|
47
|
+
cmd: str = "python app.py"
|
|
48
|
+
copy: list[str] = field(default_factory=list[str])
|
|
49
|
+
auto_include_git: bool = False
|
|
50
|
+
|
|
51
|
+
@classmethod
|
|
52
|
+
def from_dict(cls, data: dict[str, Any]) -> ImageConfig:
|
|
53
|
+
return cls(**{k: v for k, v in data.items() if k in cls.__annotations__})
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
@dataclass
|
|
57
|
+
class DeployConfig:
|
|
58
|
+
"""Deployment configuration"""
|
|
59
|
+
|
|
60
|
+
description: str = ""
|
|
61
|
+
gpu_type: str = "h100-80gb"
|
|
62
|
+
gpu_count: int = 1
|
|
63
|
+
cpu: float = 1
|
|
64
|
+
memory: float = 8
|
|
65
|
+
storage: int = 100
|
|
66
|
+
min_replicas: int = 1
|
|
67
|
+
max_replicas: int = 1
|
|
68
|
+
port: int = 8000
|
|
69
|
+
environment_variables: dict[str, str] = field(default_factory=dict[str, str])
|
|
70
|
+
command: Optional[list[str]] = None
|
|
71
|
+
autoscaling: dict[str, str] = field(default_factory=dict[str, str])
|
|
72
|
+
health_check_path: str = "/health"
|
|
73
|
+
termination_grace_period_seconds: int = 300
|
|
74
|
+
|
|
75
|
+
@classmethod
|
|
76
|
+
def from_dict(cls, data: dict[str, Any]) -> DeployConfig:
|
|
77
|
+
return cls(**{k: v for k, v in data.items() if k in cls.__annotations__})
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
@dataclass
|
|
81
|
+
class Config:
|
|
82
|
+
"""Main configuration from jig.toml or pyproject.toml"""
|
|
83
|
+
|
|
84
|
+
model_name: str = ""
|
|
85
|
+
dockerfile: str = "Dockerfile"
|
|
86
|
+
image: ImageConfig = field(default_factory=ImageConfig)
|
|
87
|
+
deploy: DeployConfig = field(default_factory=DeployConfig)
|
|
88
|
+
_path: Path = field(default_factory=lambda: Path("pyproject.toml"))
|
|
89
|
+
|
|
90
|
+
@classmethod
|
|
91
|
+
def find(cls, config_path: Optional[str] = None, init: bool = False) -> Config:
|
|
92
|
+
"""Find specified config_path, pyproject.toml, or jig.toml"""
|
|
93
|
+
if config_path:
|
|
94
|
+
found_path = Path(config_path)
|
|
95
|
+
if not found_path.exists():
|
|
96
|
+
click.echo(f"ERROR: Configuration file not found: {config_path}", err=True)
|
|
97
|
+
sys.exit(1)
|
|
98
|
+
return cls.load(tomllib.load(found_path.open("rb")), found_path)
|
|
99
|
+
|
|
100
|
+
if (jigfile := Path("jig.toml")).exists():
|
|
101
|
+
return cls.load(tomllib.load(jigfile.open("rb")), jigfile)
|
|
102
|
+
|
|
103
|
+
if (pyproject_path := Path("pyproject.toml")).exists():
|
|
104
|
+
data = tomllib.load(pyproject_path.open("rb"))
|
|
105
|
+
if "tool" in data and "jig" in data["tool"]:
|
|
106
|
+
return cls.load(data, pyproject_path)
|
|
107
|
+
|
|
108
|
+
if init:
|
|
109
|
+
return cls()
|
|
110
|
+
click.echo(
|
|
111
|
+
"ERROR: No pyproject.toml or jig.toml found, use --config to specify a config path.",
|
|
112
|
+
err=True,
|
|
113
|
+
)
|
|
114
|
+
sys.exit(1)
|
|
115
|
+
|
|
116
|
+
@classmethod
|
|
117
|
+
def load(cls, data: dict[str, Any], path: Path) -> Config:
|
|
118
|
+
"""Load configuration from parsed TOML data"""
|
|
119
|
+
is_pyproject = path.name == "pyproject.toml"
|
|
120
|
+
|
|
121
|
+
jig_config = data.get("tool", {}).get("jig", {}) if is_pyproject else data
|
|
122
|
+
|
|
123
|
+
name = jig_config.get("name")
|
|
124
|
+
if name is None:
|
|
125
|
+
if is_pyproject:
|
|
126
|
+
name = data.get("project", {}).get("name", "")
|
|
127
|
+
else:
|
|
128
|
+
name = path.resolve().parent.name
|
|
129
|
+
click.echo(f"\N{PACKAGE} Name not set in config file or pyproject.toml - defaulting to {name}")
|
|
130
|
+
|
|
131
|
+
if autoscaling := jig_config.get("autoscaling", {}):
|
|
132
|
+
autoscaling["model"] = name
|
|
133
|
+
jig_config["deploy"]["autoscaling"] = autoscaling
|
|
134
|
+
|
|
135
|
+
return cls(
|
|
136
|
+
image=ImageConfig.from_dict(jig_config.get("image", {})),
|
|
137
|
+
deploy=DeployConfig.from_dict(jig_config.get("deploy", {})),
|
|
138
|
+
dockerfile=jig_config.get("dockerfile", "Dockerfile"),
|
|
139
|
+
model_name=name,
|
|
140
|
+
_path=path,
|
|
141
|
+
)
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
# --- State Management ---
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
@dataclass
|
|
148
|
+
class State:
|
|
149
|
+
"""Persistent state stored in .jig.json"""
|
|
150
|
+
|
|
151
|
+
_config_dir: Path
|
|
152
|
+
registry_base_path: str = ""
|
|
153
|
+
secrets: dict[str, str] = field(default_factory=dict[str, str])
|
|
154
|
+
volumes: dict[str, str] = field(default_factory=dict[str, str])
|
|
155
|
+
|
|
156
|
+
@classmethod
|
|
157
|
+
def load(cls, config_dir: Path) -> State:
|
|
158
|
+
path = config_dir / ".jig.json"
|
|
159
|
+
try:
|
|
160
|
+
with open(path) as f:
|
|
161
|
+
data = {k: v for k, v in json.load(f).items() if k in cls.__annotations__ and not k.startswith("_")}
|
|
162
|
+
return cls(_config_dir=config_dir, **data)
|
|
163
|
+
except FileNotFoundError:
|
|
164
|
+
return cls(_config_dir=config_dir)
|
|
165
|
+
|
|
166
|
+
def save(self) -> None:
|
|
167
|
+
path = self._config_dir / ".jig.json"
|
|
168
|
+
data = {k: v for k, v in asdict(self).items() if not k.startswith("_")}
|
|
169
|
+
with open(path, "w") as f:
|
|
170
|
+
json.dump(data, f, indent=2)
|