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 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
@@ -1,4 +1,4 @@
1
1
  # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
2
2
 
3
3
  __title__ = "together"
4
- __version__ = "2.0.0-alpha.18" # x-release-please-version
4
+ __version__ = "2.0.0-alpha.19" # x-release-please-version
@@ -1,5 +1,6 @@
1
1
  import click
2
2
 
3
+ from together.lib.cli.api.beta.jig import jig
3
4
  from together.lib.cli.api.beta.clusters import clusters
4
5
 
5
6
 
@@ -10,3 +11,4 @@ def beta() -> None:
10
11
 
11
12
 
12
13
  beta.add_command(clusters)
14
+ beta.add_command(jig)
@@ -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)