together 1.5.31__tar.gz → 1.5.33__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.
- {together-1.5.31 → together-1.5.33}/PKG-INFO +30 -2
- {together-1.5.31 → together-1.5.33}/README.md +28 -0
- {together-1.5.31 → together-1.5.33}/pyproject.toml +2 -2
- together-1.5.33/src/together/__init__.py +123 -0
- {together-1.5.31 → together-1.5.33}/src/together/cli/api/finetune.py +52 -6
- together-1.5.33/src/together/cli/api/utils.py +139 -0
- {together-1.5.31 → together-1.5.33}/src/together/resources/finetune.py +204 -1
- {together-1.5.31 → together-1.5.33}/src/together/types/__init__.py +4 -0
- {together-1.5.31 → together-1.5.33}/src/together/types/finetune.py +45 -0
- together-1.5.31/src/together/__init__.py +0 -72
- together-1.5.31/src/together/cli/api/utils.py +0 -51
- {together-1.5.31 → together-1.5.33}/LICENSE +0 -0
- {together-1.5.31 → together-1.5.33}/src/together/abstract/__init__.py +0 -0
- {together-1.5.31 → together-1.5.33}/src/together/abstract/api_requestor.py +0 -0
- {together-1.5.31 → together-1.5.33}/src/together/cli/__init__.py +0 -0
- {together-1.5.31 → together-1.5.33}/src/together/cli/api/__init__.py +0 -0
- {together-1.5.31 → together-1.5.33}/src/together/cli/api/chat.py +0 -0
- {together-1.5.31 → together-1.5.33}/src/together/cli/api/completions.py +0 -0
- {together-1.5.31 → together-1.5.33}/src/together/cli/api/endpoints.py +0 -0
- {together-1.5.31 → together-1.5.33}/src/together/cli/api/evaluation.py +0 -0
- {together-1.5.31 → together-1.5.33}/src/together/cli/api/files.py +0 -0
- {together-1.5.31 → together-1.5.33}/src/together/cli/api/images.py +0 -0
- {together-1.5.31 → together-1.5.33}/src/together/cli/api/models.py +0 -0
- {together-1.5.31 → together-1.5.33}/src/together/cli/cli.py +0 -0
- {together-1.5.31 → together-1.5.33}/src/together/client.py +0 -0
- {together-1.5.31 → together-1.5.33}/src/together/constants.py +0 -0
- {together-1.5.31 → together-1.5.33}/src/together/error.py +0 -0
- {together-1.5.31 → together-1.5.33}/src/together/filemanager.py +0 -0
- {together-1.5.31 → together-1.5.33}/src/together/legacy/__init__.py +0 -0
- {together-1.5.31 → together-1.5.33}/src/together/legacy/base.py +0 -0
- {together-1.5.31 → together-1.5.33}/src/together/legacy/complete.py +0 -0
- {together-1.5.31 → together-1.5.33}/src/together/legacy/embeddings.py +0 -0
- {together-1.5.31 → together-1.5.33}/src/together/legacy/files.py +0 -0
- {together-1.5.31 → together-1.5.33}/src/together/legacy/finetune.py +0 -0
- {together-1.5.31 → together-1.5.33}/src/together/legacy/images.py +0 -0
- {together-1.5.31 → together-1.5.33}/src/together/legacy/models.py +0 -0
- {together-1.5.31 → together-1.5.33}/src/together/resources/__init__.py +0 -0
- {together-1.5.31 → together-1.5.33}/src/together/resources/audio/__init__.py +0 -0
- {together-1.5.31 → together-1.5.33}/src/together/resources/audio/speech.py +0 -0
- {together-1.5.31 → together-1.5.33}/src/together/resources/audio/transcriptions.py +0 -0
- {together-1.5.31 → together-1.5.33}/src/together/resources/audio/translations.py +0 -0
- {together-1.5.31 → together-1.5.33}/src/together/resources/audio/voices.py +0 -0
- {together-1.5.31 → together-1.5.33}/src/together/resources/batch.py +0 -0
- {together-1.5.31 → together-1.5.33}/src/together/resources/chat/__init__.py +0 -0
- {together-1.5.31 → together-1.5.33}/src/together/resources/chat/completions.py +0 -0
- {together-1.5.31 → together-1.5.33}/src/together/resources/code_interpreter.py +0 -0
- {together-1.5.31 → together-1.5.33}/src/together/resources/completions.py +0 -0
- {together-1.5.31 → together-1.5.33}/src/together/resources/embeddings.py +0 -0
- {together-1.5.31 → together-1.5.33}/src/together/resources/endpoints.py +0 -0
- {together-1.5.31 → together-1.5.33}/src/together/resources/evaluation.py +0 -0
- {together-1.5.31 → together-1.5.33}/src/together/resources/files.py +0 -0
- {together-1.5.31 → together-1.5.33}/src/together/resources/images.py +0 -0
- {together-1.5.31 → together-1.5.33}/src/together/resources/models.py +0 -0
- {together-1.5.31 → together-1.5.33}/src/together/resources/rerank.py +0 -0
- {together-1.5.31 → together-1.5.33}/src/together/resources/videos.py +0 -0
- {together-1.5.31 → together-1.5.33}/src/together/together_response.py +0 -0
- {together-1.5.31 → together-1.5.33}/src/together/types/abstract.py +0 -0
- {together-1.5.31 → together-1.5.33}/src/together/types/audio_speech.py +0 -0
- {together-1.5.31 → together-1.5.33}/src/together/types/batch.py +0 -0
- {together-1.5.31 → together-1.5.33}/src/together/types/chat_completions.py +0 -0
- {together-1.5.31 → together-1.5.33}/src/together/types/code_interpreter.py +0 -0
- {together-1.5.31 → together-1.5.33}/src/together/types/common.py +0 -0
- {together-1.5.31 → together-1.5.33}/src/together/types/completions.py +0 -0
- {together-1.5.31 → together-1.5.33}/src/together/types/embeddings.py +0 -0
- {together-1.5.31 → together-1.5.33}/src/together/types/endpoints.py +0 -0
- {together-1.5.31 → together-1.5.33}/src/together/types/error.py +0 -0
- {together-1.5.31 → together-1.5.33}/src/together/types/evaluation.py +0 -0
- {together-1.5.31 → together-1.5.33}/src/together/types/files.py +0 -0
- {together-1.5.31 → together-1.5.33}/src/together/types/images.py +0 -0
- {together-1.5.31 → together-1.5.33}/src/together/types/models.py +0 -0
- {together-1.5.31 → together-1.5.33}/src/together/types/rerank.py +0 -0
- {together-1.5.31 → together-1.5.33}/src/together/types/videos.py +0 -0
- {together-1.5.31 → together-1.5.33}/src/together/utils/__init__.py +0 -0
- {together-1.5.31 → together-1.5.33}/src/together/utils/_log.py +0 -0
- {together-1.5.31 → together-1.5.33}/src/together/utils/api_helpers.py +0 -0
- {together-1.5.31 → together-1.5.33}/src/together/utils/files.py +0 -0
- {together-1.5.31 → together-1.5.33}/src/together/utils/tools.py +0 -0
- {together-1.5.31 → together-1.5.33}/src/together/version.py +0 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: together
|
|
3
|
-
Version: 1.5.
|
|
4
|
-
Summary: Python client for Together's Cloud Platform!
|
|
3
|
+
Version: 1.5.33
|
|
4
|
+
Summary: Python client for Together's Cloud Platform! Note: SDK 2.0 is now available at https://github.com/togethercomputer/together-py
|
|
5
5
|
License: Apache-2.0
|
|
6
6
|
License-File: LICENSE
|
|
7
7
|
Author: Together AI
|
|
@@ -42,6 +42,34 @@ Description-Content-Type: text/markdown
|
|
|
42
42
|
</a>
|
|
43
43
|
</div>
|
|
44
44
|
|
|
45
|
+
> [!NOTE]
|
|
46
|
+
> ## 🚀 Together Python SDK 2.0 is now available!
|
|
47
|
+
>
|
|
48
|
+
> Check out the new SDK: **[together-py](https://github.com/togethercomputer/together-py)**
|
|
49
|
+
>
|
|
50
|
+
> 📖 **Migration Guide:** [https://docs.together.ai/docs/pythonv2-migration-guide](https://docs.together.ai/docs/pythonv2-migration-guide)
|
|
51
|
+
>
|
|
52
|
+
> ### Install the Beta
|
|
53
|
+
>
|
|
54
|
+
> **Using uv (Recommended):**
|
|
55
|
+
> ```bash
|
|
56
|
+
> # Install uv if you haven't already
|
|
57
|
+
> curl -LsSf https://astral.sh/uv/install.sh | sh
|
|
58
|
+
>
|
|
59
|
+
> # Install together python SDK
|
|
60
|
+
> uv add together --prerelease allow
|
|
61
|
+
>
|
|
62
|
+
> # Or upgrade an existing installation
|
|
63
|
+
> uv sync --upgrade-package together --prerelease allow
|
|
64
|
+
> ```
|
|
65
|
+
>
|
|
66
|
+
> **Using pip:**
|
|
67
|
+
> ```bash
|
|
68
|
+
> pip install --pre together
|
|
69
|
+
> ```
|
|
70
|
+
>
|
|
71
|
+
> This package will be maintained until January 2026.
|
|
72
|
+
|
|
45
73
|
# Together Python API library
|
|
46
74
|
|
|
47
75
|
[](https://pypi.org/project/together/)
|
|
@@ -4,6 +4,34 @@
|
|
|
4
4
|
</a>
|
|
5
5
|
</div>
|
|
6
6
|
|
|
7
|
+
> [!NOTE]
|
|
8
|
+
> ## 🚀 Together Python SDK 2.0 is now available!
|
|
9
|
+
>
|
|
10
|
+
> Check out the new SDK: **[together-py](https://github.com/togethercomputer/together-py)**
|
|
11
|
+
>
|
|
12
|
+
> 📖 **Migration Guide:** [https://docs.together.ai/docs/pythonv2-migration-guide](https://docs.together.ai/docs/pythonv2-migration-guide)
|
|
13
|
+
>
|
|
14
|
+
> ### Install the Beta
|
|
15
|
+
>
|
|
16
|
+
> **Using uv (Recommended):**
|
|
17
|
+
> ```bash
|
|
18
|
+
> # Install uv if you haven't already
|
|
19
|
+
> curl -LsSf https://astral.sh/uv/install.sh | sh
|
|
20
|
+
>
|
|
21
|
+
> # Install together python SDK
|
|
22
|
+
> uv add together --prerelease allow
|
|
23
|
+
>
|
|
24
|
+
> # Or upgrade an existing installation
|
|
25
|
+
> uv sync --upgrade-package together --prerelease allow
|
|
26
|
+
> ```
|
|
27
|
+
>
|
|
28
|
+
> **Using pip:**
|
|
29
|
+
> ```bash
|
|
30
|
+
> pip install --pre together
|
|
31
|
+
> ```
|
|
32
|
+
>
|
|
33
|
+
> This package will be maintained until January 2026.
|
|
34
|
+
|
|
7
35
|
# Together Python API library
|
|
8
36
|
|
|
9
37
|
[](https://pypi.org/project/together/)
|
|
@@ -12,9 +12,9 @@ build-backend = "poetry.masonry.api"
|
|
|
12
12
|
|
|
13
13
|
[tool.poetry]
|
|
14
14
|
name = "together"
|
|
15
|
-
version = "1.5.
|
|
15
|
+
version = "1.5.33"
|
|
16
16
|
authors = ["Together AI <support@together.ai>"]
|
|
17
|
-
description = "Python client for Together's Cloud Platform!"
|
|
17
|
+
description = "Python client for Together's Cloud Platform! Note: SDK 2.0 is now available at https://github.com/togethercomputer/together-py"
|
|
18
18
|
readme = "README.md"
|
|
19
19
|
license = "Apache-2.0"
|
|
20
20
|
classifiers = [
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
import sys
|
|
5
|
+
|
|
6
|
+
# =============================================================================
|
|
7
|
+
# SDK 2.0 ANNOUNCEMENT
|
|
8
|
+
# =============================================================================
|
|
9
|
+
_ANNOUNCEMENT_MESSAGE = """
|
|
10
|
+
================================================================================
|
|
11
|
+
Together Python SDK 2.0 is now available!
|
|
12
|
+
|
|
13
|
+
Install: pip install --pre together
|
|
14
|
+
New SDK: https://github.com/togethercomputer/together-py
|
|
15
|
+
Migration guide: https://docs.together.ai/docs/pythonv2-migration-guide
|
|
16
|
+
|
|
17
|
+
This package will be maintained until January 2026.
|
|
18
|
+
================================================================================
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
# Show info banner (unless suppressed)
|
|
22
|
+
if not os.environ.get("TOGETHER_NO_BANNER"):
|
|
23
|
+
try:
|
|
24
|
+
from rich.console import Console
|
|
25
|
+
from rich.panel import Panel
|
|
26
|
+
|
|
27
|
+
console = Console(stderr=True)
|
|
28
|
+
console.print(
|
|
29
|
+
Panel(
|
|
30
|
+
"[bold cyan]Together Python SDK 2.0 is now available![/bold cyan]\n\n"
|
|
31
|
+
"Install the beta:\n"
|
|
32
|
+
"[green]pip install --pre together[/green] or "
|
|
33
|
+
"[green]uv add together --prerelease allow[/green]\n\n"
|
|
34
|
+
"New SDK: [link=https://github.com/togethercomputer/together-py]"
|
|
35
|
+
"https://github.com/togethercomputer/together-py[/link]\n"
|
|
36
|
+
"Migration guide: [link=https://docs.together.ai/docs/pythonv2-migration-guide]"
|
|
37
|
+
"https://docs.together.ai/docs/pythonv2-migration-guide[/link]\n\n"
|
|
38
|
+
"[dim]This package will be maintained until January 2026.\n"
|
|
39
|
+
"Set TOGETHER_NO_BANNER=1 to hide this message.[/dim]",
|
|
40
|
+
title="🚀 New SDK Available",
|
|
41
|
+
border_style="cyan",
|
|
42
|
+
)
|
|
43
|
+
)
|
|
44
|
+
except Exception:
|
|
45
|
+
# Fallback for any error (ImportError, OSError in daemons, rich errors, etc.)
|
|
46
|
+
# Banner display should never break module imports
|
|
47
|
+
try:
|
|
48
|
+
print(_ANNOUNCEMENT_MESSAGE, file=sys.stderr)
|
|
49
|
+
except Exception:
|
|
50
|
+
pass # Silently ignore if even stderr is unavailable
|
|
51
|
+
|
|
52
|
+
# =============================================================================
|
|
53
|
+
|
|
54
|
+
from contextvars import ContextVar
|
|
55
|
+
from typing import TYPE_CHECKING, Callable
|
|
56
|
+
|
|
57
|
+
from together import (
|
|
58
|
+
abstract,
|
|
59
|
+
client,
|
|
60
|
+
constants,
|
|
61
|
+
error,
|
|
62
|
+
filemanager,
|
|
63
|
+
resources,
|
|
64
|
+
together_response,
|
|
65
|
+
types,
|
|
66
|
+
utils,
|
|
67
|
+
)
|
|
68
|
+
from together.version import VERSION
|
|
69
|
+
|
|
70
|
+
from together.legacy.complete import AsyncComplete, Complete, Completion
|
|
71
|
+
from together.legacy.embeddings import Embeddings
|
|
72
|
+
from together.legacy.files import Files
|
|
73
|
+
from together.legacy.finetune import Finetune
|
|
74
|
+
from together.legacy.images import Image
|
|
75
|
+
from together.legacy.models import Models
|
|
76
|
+
|
|
77
|
+
version = VERSION
|
|
78
|
+
|
|
79
|
+
log: str | None = None # Set to either 'debug' or 'info', controls console logging
|
|
80
|
+
|
|
81
|
+
if TYPE_CHECKING:
|
|
82
|
+
import requests
|
|
83
|
+
from aiohttp import ClientSession
|
|
84
|
+
|
|
85
|
+
requestssession: "requests.Session" | Callable[[], "requests.Session"] | None = None
|
|
86
|
+
|
|
87
|
+
aiosession: ContextVar["ClientSession" | None] = ContextVar(
|
|
88
|
+
"aiohttp-session", default=None
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
from together.client import AsyncClient, AsyncTogether, Client, Together
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
api_key: str | None = None # To be deprecated in the next major release
|
|
95
|
+
|
|
96
|
+
# Legacy functions
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
__all__ = [
|
|
100
|
+
"aiosession",
|
|
101
|
+
"constants",
|
|
102
|
+
"version",
|
|
103
|
+
"Together",
|
|
104
|
+
"AsyncTogether",
|
|
105
|
+
"Client",
|
|
106
|
+
"AsyncClient",
|
|
107
|
+
"resources",
|
|
108
|
+
"types",
|
|
109
|
+
"abstract",
|
|
110
|
+
"filemanager",
|
|
111
|
+
"error",
|
|
112
|
+
"together_response",
|
|
113
|
+
"client",
|
|
114
|
+
"utils",
|
|
115
|
+
"Complete",
|
|
116
|
+
"AsyncComplete",
|
|
117
|
+
"Completion",
|
|
118
|
+
"Embeddings",
|
|
119
|
+
"Files",
|
|
120
|
+
"Finetune",
|
|
121
|
+
"Image",
|
|
122
|
+
"Models",
|
|
123
|
+
]
|
|
@@ -9,14 +9,17 @@ from typing import Any, Literal
|
|
|
9
9
|
import click
|
|
10
10
|
from click.core import ParameterSource # type: ignore[attr-defined]
|
|
11
11
|
from rich import print as rprint
|
|
12
|
+
from rich.json import JSON
|
|
12
13
|
from tabulate import tabulate
|
|
13
14
|
|
|
14
15
|
from together import Together
|
|
15
|
-
from together.cli.api.utils import BOOL_WITH_AUTO, INT_WITH_MAX
|
|
16
|
+
from together.cli.api.utils import BOOL_WITH_AUTO, INT_WITH_MAX, generate_progress_bar
|
|
16
17
|
from together.types.finetune import (
|
|
17
18
|
DownloadCheckpointType,
|
|
18
19
|
FinetuneEventType,
|
|
19
20
|
FinetuneTrainingLimits,
|
|
21
|
+
FullTrainingType,
|
|
22
|
+
LoRATrainingType,
|
|
20
23
|
)
|
|
21
24
|
from together.utils import (
|
|
22
25
|
finetune_price_to_dollars,
|
|
@@ -29,13 +32,21 @@ from together.utils import (
|
|
|
29
32
|
|
|
30
33
|
_CONFIRMATION_MESSAGE = (
|
|
31
34
|
"You are about to create a fine-tuning job. "
|
|
32
|
-
"The
|
|
35
|
+
"The estimated price of this job is {price}. "
|
|
36
|
+
"The actual cost of your job will be determined by the model size, the number of tokens "
|
|
33
37
|
"in the training file, the number of tokens in the validation file, the number of epochs, and "
|
|
34
|
-
"the number of evaluations. Visit https://www.together.ai/pricing to
|
|
38
|
+
"the number of evaluations. Visit https://www.together.ai/pricing to learn more about fine-tuning pricing.\n"
|
|
39
|
+
"{warning}"
|
|
35
40
|
"You can pass `-y` or `--confirm` to your command to skip this message.\n\n"
|
|
36
41
|
"Do you want to proceed?"
|
|
37
42
|
)
|
|
38
43
|
|
|
44
|
+
_WARNING_MESSAGE_INSUFFICIENT_FUNDS = (
|
|
45
|
+
"The estimated price of this job is significantly greater than your current credit limit and balance combined. "
|
|
46
|
+
"It will likely get cancelled due to insufficient funds. "
|
|
47
|
+
"Consider increasing your credit limit at https://api.together.xyz/settings/profile\n"
|
|
48
|
+
)
|
|
49
|
+
|
|
39
50
|
|
|
40
51
|
class DownloadCheckpointTypeChoice(click.Choice):
|
|
41
52
|
def __init__(self) -> None:
|
|
@@ -357,12 +368,36 @@ def create(
|
|
|
357
368
|
"You have specified a number of evaluation loops but no validation file."
|
|
358
369
|
)
|
|
359
370
|
|
|
360
|
-
|
|
371
|
+
finetune_price_estimation_result = client.fine_tuning.estimate_price(
|
|
372
|
+
training_file=training_file,
|
|
373
|
+
validation_file=validation_file,
|
|
374
|
+
model=model,
|
|
375
|
+
n_epochs=n_epochs,
|
|
376
|
+
n_evals=n_evals,
|
|
377
|
+
training_type="lora" if lora else "full",
|
|
378
|
+
training_method=training_method,
|
|
379
|
+
)
|
|
380
|
+
|
|
381
|
+
price = click.style(
|
|
382
|
+
f"${finetune_price_estimation_result.estimated_total_price:.2f}",
|
|
383
|
+
bold=True,
|
|
384
|
+
)
|
|
385
|
+
|
|
386
|
+
if not finetune_price_estimation_result.allowed_to_proceed:
|
|
387
|
+
warning = click.style(_WARNING_MESSAGE_INSUFFICIENT_FUNDS, fg="red", bold=True)
|
|
388
|
+
else:
|
|
389
|
+
warning = ""
|
|
390
|
+
|
|
391
|
+
confirmation_message = _CONFIRMATION_MESSAGE.format(
|
|
392
|
+
price=price,
|
|
393
|
+
warning=warning,
|
|
394
|
+
)
|
|
395
|
+
|
|
396
|
+
if confirm or click.confirm(confirmation_message, default=True, show_default=True):
|
|
361
397
|
response = client.fine_tuning.create(
|
|
362
398
|
**training_args,
|
|
363
399
|
verbose=True,
|
|
364
400
|
)
|
|
365
|
-
|
|
366
401
|
report_string = f"Successfully submitted a fine-tuning job {response.id}"
|
|
367
402
|
if response.created_at is not None:
|
|
368
403
|
created_time = datetime.strptime(
|
|
@@ -401,6 +436,9 @@ def list(ctx: click.Context) -> None:
|
|
|
401
436
|
"Price": f"""${
|
|
402
437
|
finetune_price_to_dollars(float(str(i.total_price)))
|
|
403
438
|
}""", # convert to string for mypy typing
|
|
439
|
+
"Progress": generate_progress_bar(
|
|
440
|
+
i, datetime.now().astimezone(), use_rich=False
|
|
441
|
+
),
|
|
404
442
|
}
|
|
405
443
|
)
|
|
406
444
|
table = tabulate(display_list, headers="keys", tablefmt="grid", showindex=True)
|
|
@@ -420,7 +458,15 @@ def retrieve(ctx: click.Context, fine_tune_id: str) -> None:
|
|
|
420
458
|
# remove events from response for cleaner output
|
|
421
459
|
response.events = None
|
|
422
460
|
|
|
423
|
-
|
|
461
|
+
rprint(JSON.from_data(response.model_dump(exclude_none=True)))
|
|
462
|
+
progress_text = generate_progress_bar(
|
|
463
|
+
response, datetime.now().astimezone(), use_rich=True
|
|
464
|
+
)
|
|
465
|
+
status = "Unknown"
|
|
466
|
+
if response.status is not None:
|
|
467
|
+
status = response.status.value
|
|
468
|
+
prefix = f"Status: [bold]{status}[/bold],"
|
|
469
|
+
rprint(f"{prefix} {progress_text}")
|
|
424
470
|
|
|
425
471
|
|
|
426
472
|
@fine_tuning.command()
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import math
|
|
4
|
+
import re
|
|
5
|
+
from gettext import gettext as _
|
|
6
|
+
from typing import Literal
|
|
7
|
+
from datetime import datetime
|
|
8
|
+
|
|
9
|
+
import click
|
|
10
|
+
|
|
11
|
+
from together.types.finetune import FinetuneResponse, COMPLETED_STATUSES
|
|
12
|
+
|
|
13
|
+
_PROGRESS_BAR_WIDTH = 40
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class AutoIntParamType(click.ParamType):
|
|
17
|
+
name = "integer_or_max"
|
|
18
|
+
_number_class = int
|
|
19
|
+
|
|
20
|
+
def convert(
|
|
21
|
+
self, value: str, param: click.Parameter | None, ctx: click.Context | None
|
|
22
|
+
) -> int | Literal["max"] | None:
|
|
23
|
+
if value == "max":
|
|
24
|
+
return "max"
|
|
25
|
+
try:
|
|
26
|
+
return int(value)
|
|
27
|
+
except ValueError:
|
|
28
|
+
self.fail(
|
|
29
|
+
_("{value!r} is not a valid {number_type}.").format(
|
|
30
|
+
value=value, number_type=self.name
|
|
31
|
+
),
|
|
32
|
+
param,
|
|
33
|
+
ctx,
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class BooleanWithAutoParamType(click.ParamType):
|
|
38
|
+
name = "boolean_or_auto"
|
|
39
|
+
|
|
40
|
+
def convert(
|
|
41
|
+
self, value: str, param: click.Parameter | None, ctx: click.Context | None
|
|
42
|
+
) -> bool | Literal["auto"] | None:
|
|
43
|
+
if value == "auto":
|
|
44
|
+
return "auto"
|
|
45
|
+
try:
|
|
46
|
+
return bool(value)
|
|
47
|
+
except ValueError:
|
|
48
|
+
self.fail(
|
|
49
|
+
_("{value!r} is not a valid {type}.").format(
|
|
50
|
+
value=value, type=self.name
|
|
51
|
+
),
|
|
52
|
+
param,
|
|
53
|
+
ctx,
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
INT_WITH_MAX = AutoIntParamType()
|
|
58
|
+
BOOL_WITH_AUTO = BooleanWithAutoParamType()
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def _human_readable_time(timedelta: float) -> str:
|
|
62
|
+
"""Convert a timedelta to a compact human-readble string
|
|
63
|
+
Examples:
|
|
64
|
+
00:00:10 -> 10s
|
|
65
|
+
01:23:45 -> 1h 23min 45s
|
|
66
|
+
1 Month 23 days 04:56:07 -> 1month 23d 4h 56min 7s
|
|
67
|
+
Args:
|
|
68
|
+
timedelta (float): The timedelta in seconds to convert.
|
|
69
|
+
Returns:
|
|
70
|
+
A string representing the timedelta in a human-readable format.
|
|
71
|
+
"""
|
|
72
|
+
units = [
|
|
73
|
+
(30 * 24 * 60 * 60, "month"), # 30 days
|
|
74
|
+
(24 * 60 * 60, "d"),
|
|
75
|
+
(60 * 60, "h"),
|
|
76
|
+
(60, "min"),
|
|
77
|
+
(1, "s"),
|
|
78
|
+
]
|
|
79
|
+
|
|
80
|
+
total_seconds = int(timedelta)
|
|
81
|
+
parts = []
|
|
82
|
+
|
|
83
|
+
for unit_seconds, unit_name in units:
|
|
84
|
+
if total_seconds >= unit_seconds:
|
|
85
|
+
value = total_seconds // unit_seconds
|
|
86
|
+
total_seconds %= unit_seconds
|
|
87
|
+
parts.append(f"{value}{unit_name}")
|
|
88
|
+
|
|
89
|
+
return " ".join(parts) if parts else "0s"
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def generate_progress_bar(
|
|
93
|
+
finetune_job: FinetuneResponse, current_time: datetime, use_rich: bool = False
|
|
94
|
+
) -> str:
|
|
95
|
+
"""Generate a progress bar for a finetune job.
|
|
96
|
+
Args:
|
|
97
|
+
finetune_job: The finetune job to generate a progress bar for.
|
|
98
|
+
current_time: The current time.
|
|
99
|
+
use_rich: Whether to use rich formatting.
|
|
100
|
+
Returns:
|
|
101
|
+
A string representing the progress bar.
|
|
102
|
+
"""
|
|
103
|
+
progress = "Progress: [bold red]unavailable[/bold red]"
|
|
104
|
+
if finetune_job.status in COMPLETED_STATUSES:
|
|
105
|
+
progress = "Progress: [bold green]completed[/bold green]"
|
|
106
|
+
elif finetune_job.updated_at is not None:
|
|
107
|
+
# Replace 'Z' with '+00:00' for Python 3.10 compatibility
|
|
108
|
+
updated_at_str = finetune_job.updated_at.replace("Z", "+00:00")
|
|
109
|
+
update_at = datetime.fromisoformat(updated_at_str).astimezone()
|
|
110
|
+
|
|
111
|
+
if finetune_job.progress is not None:
|
|
112
|
+
if current_time < update_at:
|
|
113
|
+
return progress
|
|
114
|
+
|
|
115
|
+
if not finetune_job.progress.estimate_available:
|
|
116
|
+
return progress
|
|
117
|
+
|
|
118
|
+
if finetune_job.progress.seconds_remaining <= 0:
|
|
119
|
+
return progress
|
|
120
|
+
|
|
121
|
+
elapsed_time = (current_time - update_at).total_seconds()
|
|
122
|
+
ratio_filled = min(
|
|
123
|
+
elapsed_time / finetune_job.progress.seconds_remaining, 1.0
|
|
124
|
+
)
|
|
125
|
+
percentage = ratio_filled * 100
|
|
126
|
+
filled = math.ceil(ratio_filled * _PROGRESS_BAR_WIDTH)
|
|
127
|
+
bar = "█" * filled + "░" * (_PROGRESS_BAR_WIDTH - filled)
|
|
128
|
+
time_left = "N/A"
|
|
129
|
+
if finetune_job.progress.seconds_remaining > elapsed_time:
|
|
130
|
+
time_left = _human_readable_time(
|
|
131
|
+
finetune_job.progress.seconds_remaining - elapsed_time
|
|
132
|
+
)
|
|
133
|
+
time_text = f"{time_left} left"
|
|
134
|
+
progress = f"Progress: {bar} [bold]{percentage:>3.0f}%[/bold] [yellow]{time_text}[/yellow]"
|
|
135
|
+
|
|
136
|
+
if use_rich:
|
|
137
|
+
return progress
|
|
138
|
+
|
|
139
|
+
return re.sub(r"\[/?[^\]]+\]", "", progress)
|
|
@@ -20,6 +20,8 @@ from together.types import (
|
|
|
20
20
|
FinetuneLRScheduler,
|
|
21
21
|
FinetuneRequest,
|
|
22
22
|
FinetuneResponse,
|
|
23
|
+
FinetunePriceEstimationRequest,
|
|
24
|
+
FinetunePriceEstimationResponse,
|
|
23
25
|
FinetuneTrainingLimits,
|
|
24
26
|
FullTrainingType,
|
|
25
27
|
LinearLRScheduler,
|
|
@@ -31,7 +33,7 @@ from together.types import (
|
|
|
31
33
|
TrainingMethodSFT,
|
|
32
34
|
TrainingType,
|
|
33
35
|
)
|
|
34
|
-
from together.types.finetune import DownloadCheckpointType
|
|
36
|
+
from together.types.finetune import DownloadCheckpointType, TrainingMethod
|
|
35
37
|
from together.utils import log_warn_once, normalize_key
|
|
36
38
|
|
|
37
39
|
|
|
@@ -42,6 +44,12 @@ AVAILABLE_TRAINING_METHODS = {
|
|
|
42
44
|
TrainingMethodSFT().method,
|
|
43
45
|
TrainingMethodDPO().method,
|
|
44
46
|
}
|
|
47
|
+
_WARNING_MESSAGE_INSUFFICIENT_FUNDS = (
|
|
48
|
+
"The estimated price of the fine-tuning job is {} which is significantly "
|
|
49
|
+
"greater than your current credit limit and balance combined. "
|
|
50
|
+
"It will likely get cancelled due to insufficient funds. "
|
|
51
|
+
"Proceed at your own risk."
|
|
52
|
+
)
|
|
45
53
|
|
|
46
54
|
|
|
47
55
|
def create_finetune_request(
|
|
@@ -473,12 +481,34 @@ class FineTuning:
|
|
|
473
481
|
hf_api_token=hf_api_token,
|
|
474
482
|
hf_output_repo_name=hf_output_repo_name,
|
|
475
483
|
)
|
|
484
|
+
if from_checkpoint is None and from_hf_model is None:
|
|
485
|
+
price_estimation_result = self.estimate_price(
|
|
486
|
+
training_file=training_file,
|
|
487
|
+
validation_file=validation_file,
|
|
488
|
+
model=model_name,
|
|
489
|
+
n_epochs=finetune_request.n_epochs,
|
|
490
|
+
n_evals=finetune_request.n_evals,
|
|
491
|
+
training_type="lora" if lora else "full",
|
|
492
|
+
training_method=training_method,
|
|
493
|
+
)
|
|
494
|
+
price_limit_passed = price_estimation_result.allowed_to_proceed
|
|
495
|
+
else:
|
|
496
|
+
# unsupported case
|
|
497
|
+
price_limit_passed = True
|
|
476
498
|
|
|
477
499
|
if verbose:
|
|
478
500
|
rprint(
|
|
479
501
|
"Submitting a fine-tuning job with the following parameters:",
|
|
480
502
|
finetune_request,
|
|
481
503
|
)
|
|
504
|
+
if not price_limit_passed:
|
|
505
|
+
rprint(
|
|
506
|
+
"[red]"
|
|
507
|
+
+ _WARNING_MESSAGE_INSUFFICIENT_FUNDS.format(
|
|
508
|
+
price_estimation_result.estimated_total_price
|
|
509
|
+
)
|
|
510
|
+
+ "[/red]",
|
|
511
|
+
)
|
|
482
512
|
parameter_payload = finetune_request.model_dump(exclude_none=True)
|
|
483
513
|
|
|
484
514
|
response, _, _ = requestor.request(
|
|
@@ -493,6 +523,81 @@ class FineTuning:
|
|
|
493
523
|
|
|
494
524
|
return FinetuneResponse(**response.data)
|
|
495
525
|
|
|
526
|
+
def estimate_price(
|
|
527
|
+
self,
|
|
528
|
+
*,
|
|
529
|
+
training_file: str,
|
|
530
|
+
model: str,
|
|
531
|
+
validation_file: str | None = None,
|
|
532
|
+
n_epochs: int | None = 1,
|
|
533
|
+
n_evals: int | None = 0,
|
|
534
|
+
training_type: str = "lora",
|
|
535
|
+
training_method: str = "sft",
|
|
536
|
+
) -> FinetunePriceEstimationResponse:
|
|
537
|
+
"""
|
|
538
|
+
Estimates the price of a fine-tuning job
|
|
539
|
+
|
|
540
|
+
Args:
|
|
541
|
+
training_file (str): File-ID of a file uploaded to the Together API
|
|
542
|
+
model (str): Name of the base model to run fine-tune job on
|
|
543
|
+
validation_file (str, optional): File ID of a file uploaded to the Together API for validation.
|
|
544
|
+
n_epochs (int, optional): Number of epochs for fine-tuning. Defaults to 1.
|
|
545
|
+
n_evals (int, optional): Number of evaluation loops to run. Defaults to 0.
|
|
546
|
+
training_type (str, optional): Training type. Defaults to "lora".
|
|
547
|
+
training_method (str, optional): Training method. Defaults to "sft".
|
|
548
|
+
|
|
549
|
+
Returns:
|
|
550
|
+
FinetunePriceEstimationResponse: Object containing the price estimation result.
|
|
551
|
+
"""
|
|
552
|
+
training_type_cls: TrainingType
|
|
553
|
+
training_method_cls: TrainingMethod
|
|
554
|
+
|
|
555
|
+
if training_method == "sft":
|
|
556
|
+
training_method_cls = TrainingMethodSFT(method="sft")
|
|
557
|
+
elif training_method == "dpo":
|
|
558
|
+
training_method_cls = TrainingMethodDPO(method="dpo")
|
|
559
|
+
else:
|
|
560
|
+
raise ValueError(f"Unknown training method: {training_method}")
|
|
561
|
+
|
|
562
|
+
if training_type.lower() == "lora":
|
|
563
|
+
# parameters of lora are unused in price estimation
|
|
564
|
+
# but we need to set them to valid values
|
|
565
|
+
training_type_cls = LoRATrainingType(
|
|
566
|
+
type="Lora",
|
|
567
|
+
lora_r=16,
|
|
568
|
+
lora_alpha=16,
|
|
569
|
+
lora_dropout=0.0,
|
|
570
|
+
lora_trainable_modules="all-linear",
|
|
571
|
+
)
|
|
572
|
+
elif training_type.lower() == "full":
|
|
573
|
+
training_type_cls = FullTrainingType(type="Full")
|
|
574
|
+
else:
|
|
575
|
+
raise ValueError(f"Unknown training type: {training_type}")
|
|
576
|
+
|
|
577
|
+
request = FinetunePriceEstimationRequest(
|
|
578
|
+
training_file=training_file,
|
|
579
|
+
validation_file=validation_file,
|
|
580
|
+
model=model,
|
|
581
|
+
n_epochs=n_epochs,
|
|
582
|
+
n_evals=n_evals,
|
|
583
|
+
training_type=training_type_cls,
|
|
584
|
+
training_method=training_method_cls,
|
|
585
|
+
)
|
|
586
|
+
parameter_payload = request.model_dump(exclude_none=True)
|
|
587
|
+
requestor = api_requestor.APIRequestor(
|
|
588
|
+
client=self._client,
|
|
589
|
+
)
|
|
590
|
+
|
|
591
|
+
response, _, _ = requestor.request(
|
|
592
|
+
options=TogetherRequest(
|
|
593
|
+
method="POST", url="fine-tunes/estimate-price", params=parameter_payload
|
|
594
|
+
),
|
|
595
|
+
stream=False,
|
|
596
|
+
)
|
|
597
|
+
assert isinstance(response, TogetherResponse)
|
|
598
|
+
|
|
599
|
+
return FinetunePriceEstimationResponse(**response.data)
|
|
600
|
+
|
|
496
601
|
def list(self) -> FinetuneList:
|
|
497
602
|
"""
|
|
498
603
|
Lists fine-tune job history
|
|
@@ -941,11 +1046,34 @@ class AsyncFineTuning:
|
|
|
941
1046
|
hf_output_repo_name=hf_output_repo_name,
|
|
942
1047
|
)
|
|
943
1048
|
|
|
1049
|
+
if from_checkpoint is None and from_hf_model is None:
|
|
1050
|
+
price_estimation_result = await self.estimate_price(
|
|
1051
|
+
training_file=training_file,
|
|
1052
|
+
validation_file=validation_file,
|
|
1053
|
+
model=model_name,
|
|
1054
|
+
n_epochs=finetune_request.n_epochs,
|
|
1055
|
+
n_evals=finetune_request.n_evals,
|
|
1056
|
+
training_type="lora" if lora else "full",
|
|
1057
|
+
training_method=training_method,
|
|
1058
|
+
)
|
|
1059
|
+
price_limit_passed = price_estimation_result.allowed_to_proceed
|
|
1060
|
+
else:
|
|
1061
|
+
# unsupported case
|
|
1062
|
+
price_limit_passed = True
|
|
1063
|
+
|
|
944
1064
|
if verbose:
|
|
945
1065
|
rprint(
|
|
946
1066
|
"Submitting a fine-tuning job with the following parameters:",
|
|
947
1067
|
finetune_request,
|
|
948
1068
|
)
|
|
1069
|
+
if not price_limit_passed:
|
|
1070
|
+
rprint(
|
|
1071
|
+
"[red]"
|
|
1072
|
+
+ _WARNING_MESSAGE_INSUFFICIENT_FUNDS.format(
|
|
1073
|
+
price_estimation_result.estimated_total_price
|
|
1074
|
+
)
|
|
1075
|
+
+ "[/red]",
|
|
1076
|
+
)
|
|
949
1077
|
parameter_payload = finetune_request.model_dump(exclude_none=True)
|
|
950
1078
|
|
|
951
1079
|
response, _, _ = await requestor.arequest(
|
|
@@ -961,6 +1089,81 @@ class AsyncFineTuning:
|
|
|
961
1089
|
|
|
962
1090
|
return FinetuneResponse(**response.data)
|
|
963
1091
|
|
|
1092
|
+
async def estimate_price(
|
|
1093
|
+
self,
|
|
1094
|
+
*,
|
|
1095
|
+
training_file: str,
|
|
1096
|
+
model: str,
|
|
1097
|
+
validation_file: str | None = None,
|
|
1098
|
+
n_epochs: int | None = 1,
|
|
1099
|
+
n_evals: int | None = 0,
|
|
1100
|
+
training_type: str = "lora",
|
|
1101
|
+
training_method: str = "sft",
|
|
1102
|
+
) -> FinetunePriceEstimationResponse:
|
|
1103
|
+
"""
|
|
1104
|
+
Estimates the price of a fine-tuning job
|
|
1105
|
+
|
|
1106
|
+
Args:
|
|
1107
|
+
training_file (str): File-ID of a file uploaded to the Together API
|
|
1108
|
+
model (str): Name of the base model to run fine-tune job on
|
|
1109
|
+
validation_file (str, optional): File ID of a file uploaded to the Together API for validation.
|
|
1110
|
+
n_epochs (int, optional): Number of epochs for fine-tuning. Defaults to 1.
|
|
1111
|
+
n_evals (int, optional): Number of evaluation loops to run. Defaults to 0.
|
|
1112
|
+
training_type (str, optional): Training type. Defaults to "lora".
|
|
1113
|
+
training_method (str, optional): Training method. Defaults to "sft".
|
|
1114
|
+
|
|
1115
|
+
Returns:
|
|
1116
|
+
FinetunePriceEstimationResponse: Object containing the price estimation result.
|
|
1117
|
+
"""
|
|
1118
|
+
training_type_cls: TrainingType
|
|
1119
|
+
training_method_cls: TrainingMethod
|
|
1120
|
+
|
|
1121
|
+
if training_method == "sft":
|
|
1122
|
+
training_method_cls = TrainingMethodSFT(method="sft")
|
|
1123
|
+
elif training_method == "dpo":
|
|
1124
|
+
training_method_cls = TrainingMethodDPO(method="dpo")
|
|
1125
|
+
else:
|
|
1126
|
+
raise ValueError(f"Unknown training method: {training_method}")
|
|
1127
|
+
|
|
1128
|
+
if training_type.lower() == "lora":
|
|
1129
|
+
# parameters of lora are unused in price estimation
|
|
1130
|
+
# but we need to set them to valid values
|
|
1131
|
+
training_type_cls = LoRATrainingType(
|
|
1132
|
+
type="Lora",
|
|
1133
|
+
lora_r=16,
|
|
1134
|
+
lora_alpha=16,
|
|
1135
|
+
lora_dropout=0.0,
|
|
1136
|
+
lora_trainable_modules="all-linear",
|
|
1137
|
+
)
|
|
1138
|
+
elif training_type.lower() == "full":
|
|
1139
|
+
training_type_cls = FullTrainingType(type="Full")
|
|
1140
|
+
else:
|
|
1141
|
+
raise ValueError(f"Unknown training type: {training_type}")
|
|
1142
|
+
|
|
1143
|
+
request = FinetunePriceEstimationRequest(
|
|
1144
|
+
training_file=training_file,
|
|
1145
|
+
validation_file=validation_file,
|
|
1146
|
+
model=model,
|
|
1147
|
+
n_epochs=n_epochs,
|
|
1148
|
+
n_evals=n_evals,
|
|
1149
|
+
training_type=training_type_cls,
|
|
1150
|
+
training_method=training_method_cls,
|
|
1151
|
+
)
|
|
1152
|
+
parameter_payload = request.model_dump(exclude_none=True)
|
|
1153
|
+
requestor = api_requestor.APIRequestor(
|
|
1154
|
+
client=self._client,
|
|
1155
|
+
)
|
|
1156
|
+
|
|
1157
|
+
response, _, _ = await requestor.arequest(
|
|
1158
|
+
options=TogetherRequest(
|
|
1159
|
+
method="POST", url="fine-tunes/estimate-price", params=parameter_payload
|
|
1160
|
+
),
|
|
1161
|
+
stream=False,
|
|
1162
|
+
)
|
|
1163
|
+
assert isinstance(response, TogetherResponse)
|
|
1164
|
+
|
|
1165
|
+
return FinetunePriceEstimationResponse(**response.data)
|
|
1166
|
+
|
|
964
1167
|
async def list(self) -> FinetuneList:
|
|
965
1168
|
"""
|
|
966
1169
|
Async method to list fine-tune job history
|
|
@@ -54,6 +54,8 @@ from together.types.finetune import (
|
|
|
54
54
|
FinetuneListEvents,
|
|
55
55
|
FinetuneRequest,
|
|
56
56
|
FinetuneResponse,
|
|
57
|
+
FinetunePriceEstimationRequest,
|
|
58
|
+
FinetunePriceEstimationResponse,
|
|
57
59
|
FinetuneDeleteResponse,
|
|
58
60
|
FinetuneTrainingLimits,
|
|
59
61
|
FullTrainingType,
|
|
@@ -103,6 +105,8 @@ __all__ = [
|
|
|
103
105
|
"FinetuneDeleteResponse",
|
|
104
106
|
"FinetuneDownloadResult",
|
|
105
107
|
"FinetuneLRScheduler",
|
|
108
|
+
"FinetunePriceEstimationRequest",
|
|
109
|
+
"FinetunePriceEstimationResponse",
|
|
106
110
|
"LinearLRScheduler",
|
|
107
111
|
"LinearLRSchedulerArgs",
|
|
108
112
|
"CosineLRScheduler",
|
|
@@ -28,6 +28,14 @@ class FinetuneJobStatus(str, Enum):
|
|
|
28
28
|
STATUS_COMPLETED = "completed"
|
|
29
29
|
|
|
30
30
|
|
|
31
|
+
COMPLETED_STATUSES = [
|
|
32
|
+
FinetuneJobStatus.STATUS_ERROR,
|
|
33
|
+
FinetuneJobStatus.STATUS_USER_ERROR,
|
|
34
|
+
FinetuneJobStatus.STATUS_COMPLETED,
|
|
35
|
+
FinetuneJobStatus.STATUS_CANCELLED,
|
|
36
|
+
]
|
|
37
|
+
|
|
38
|
+
|
|
31
39
|
class FinetuneEventLevels(str, Enum):
|
|
32
40
|
"""
|
|
33
41
|
Fine-tune job event status levels
|
|
@@ -167,6 +175,15 @@ class TrainingMethodDPO(TrainingMethod):
|
|
|
167
175
|
simpo_gamma: float | None = None
|
|
168
176
|
|
|
169
177
|
|
|
178
|
+
class FinetuneProgress(BaseModel):
|
|
179
|
+
"""
|
|
180
|
+
Fine-tune job progress
|
|
181
|
+
"""
|
|
182
|
+
|
|
183
|
+
estimate_available: bool = False
|
|
184
|
+
seconds_remaining: float = 0
|
|
185
|
+
|
|
186
|
+
|
|
170
187
|
class FinetuneRequest(BaseModel):
|
|
171
188
|
"""
|
|
172
189
|
Fine-tune request type
|
|
@@ -297,6 +314,8 @@ class FinetuneResponse(BaseModel):
|
|
|
297
314
|
train_on_inputs: StrictBool | Literal["auto"] | None = "auto"
|
|
298
315
|
from_checkpoint: str | None = None
|
|
299
316
|
|
|
317
|
+
progress: FinetuneProgress | None = None
|
|
318
|
+
|
|
300
319
|
@field_validator("training_type")
|
|
301
320
|
@classmethod
|
|
302
321
|
def validate_training_type(cls, v: TrainingType) -> TrainingType:
|
|
@@ -308,6 +327,32 @@ class FinetuneResponse(BaseModel):
|
|
|
308
327
|
raise ValueError("Unknown training type")
|
|
309
328
|
|
|
310
329
|
|
|
330
|
+
class FinetunePriceEstimationRequest(BaseModel):
|
|
331
|
+
"""
|
|
332
|
+
Fine-tune price estimation request type
|
|
333
|
+
"""
|
|
334
|
+
|
|
335
|
+
training_file: str
|
|
336
|
+
validation_file: str | None = None
|
|
337
|
+
model: str
|
|
338
|
+
n_epochs: int
|
|
339
|
+
n_evals: int
|
|
340
|
+
training_type: LoRATrainingType | FullTrainingType
|
|
341
|
+
training_method: TrainingMethodSFT | TrainingMethodDPO
|
|
342
|
+
|
|
343
|
+
|
|
344
|
+
class FinetunePriceEstimationResponse(BaseModel):
|
|
345
|
+
"""
|
|
346
|
+
Fine-tune price estimation response type
|
|
347
|
+
"""
|
|
348
|
+
|
|
349
|
+
estimated_total_price: float
|
|
350
|
+
user_limit: float
|
|
351
|
+
estimated_train_token_count: int
|
|
352
|
+
estimated_eval_token_count: int
|
|
353
|
+
allowed_to_proceed: bool
|
|
354
|
+
|
|
355
|
+
|
|
311
356
|
class FinetuneList(BaseModel):
|
|
312
357
|
# object type
|
|
313
358
|
object: Literal["list"] | None = None
|
|
@@ -1,72 +0,0 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
|
|
3
|
-
from contextvars import ContextVar
|
|
4
|
-
from typing import TYPE_CHECKING, Callable
|
|
5
|
-
|
|
6
|
-
from together import (
|
|
7
|
-
abstract,
|
|
8
|
-
client,
|
|
9
|
-
constants,
|
|
10
|
-
error,
|
|
11
|
-
filemanager,
|
|
12
|
-
resources,
|
|
13
|
-
together_response,
|
|
14
|
-
types,
|
|
15
|
-
utils,
|
|
16
|
-
)
|
|
17
|
-
from together.version import VERSION
|
|
18
|
-
|
|
19
|
-
from together.legacy.complete import AsyncComplete, Complete, Completion
|
|
20
|
-
from together.legacy.embeddings import Embeddings
|
|
21
|
-
from together.legacy.files import Files
|
|
22
|
-
from together.legacy.finetune import Finetune
|
|
23
|
-
from together.legacy.images import Image
|
|
24
|
-
from together.legacy.models import Models
|
|
25
|
-
|
|
26
|
-
version = VERSION
|
|
27
|
-
|
|
28
|
-
log: str | None = None # Set to either 'debug' or 'info', controls console logging
|
|
29
|
-
|
|
30
|
-
if TYPE_CHECKING:
|
|
31
|
-
import requests
|
|
32
|
-
from aiohttp import ClientSession
|
|
33
|
-
|
|
34
|
-
requestssession: "requests.Session" | Callable[[], "requests.Session"] | None = None
|
|
35
|
-
|
|
36
|
-
aiosession: ContextVar["ClientSession" | None] = ContextVar(
|
|
37
|
-
"aiohttp-session", default=None
|
|
38
|
-
)
|
|
39
|
-
|
|
40
|
-
from together.client import AsyncClient, AsyncTogether, Client, Together
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
api_key: str | None = None # To be deprecated in the next major release
|
|
44
|
-
|
|
45
|
-
# Legacy functions
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
__all__ = [
|
|
49
|
-
"aiosession",
|
|
50
|
-
"constants",
|
|
51
|
-
"version",
|
|
52
|
-
"Together",
|
|
53
|
-
"AsyncTogether",
|
|
54
|
-
"Client",
|
|
55
|
-
"AsyncClient",
|
|
56
|
-
"resources",
|
|
57
|
-
"types",
|
|
58
|
-
"abstract",
|
|
59
|
-
"filemanager",
|
|
60
|
-
"error",
|
|
61
|
-
"together_response",
|
|
62
|
-
"client",
|
|
63
|
-
"utils",
|
|
64
|
-
"Complete",
|
|
65
|
-
"AsyncComplete",
|
|
66
|
-
"Completion",
|
|
67
|
-
"Embeddings",
|
|
68
|
-
"Files",
|
|
69
|
-
"Finetune",
|
|
70
|
-
"Image",
|
|
71
|
-
"Models",
|
|
72
|
-
]
|
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
|
|
3
|
-
from gettext import gettext as _
|
|
4
|
-
from typing import Literal
|
|
5
|
-
|
|
6
|
-
import click
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
class AutoIntParamType(click.ParamType):
|
|
10
|
-
name = "integer_or_max"
|
|
11
|
-
_number_class = int
|
|
12
|
-
|
|
13
|
-
def convert(
|
|
14
|
-
self, value: str, param: click.Parameter | None, ctx: click.Context | None
|
|
15
|
-
) -> int | Literal["max"] | None:
|
|
16
|
-
if value == "max":
|
|
17
|
-
return "max"
|
|
18
|
-
try:
|
|
19
|
-
return int(value)
|
|
20
|
-
except ValueError:
|
|
21
|
-
self.fail(
|
|
22
|
-
_("{value!r} is not a valid {number_type}.").format(
|
|
23
|
-
value=value, number_type=self.name
|
|
24
|
-
),
|
|
25
|
-
param,
|
|
26
|
-
ctx,
|
|
27
|
-
)
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
class BooleanWithAutoParamType(click.ParamType):
|
|
31
|
-
name = "boolean_or_auto"
|
|
32
|
-
|
|
33
|
-
def convert(
|
|
34
|
-
self, value: str, param: click.Parameter | None, ctx: click.Context | None
|
|
35
|
-
) -> bool | Literal["auto"] | None:
|
|
36
|
-
if value == "auto":
|
|
37
|
-
return "auto"
|
|
38
|
-
try:
|
|
39
|
-
return bool(value)
|
|
40
|
-
except ValueError:
|
|
41
|
-
self.fail(
|
|
42
|
-
_("{value!r} is not a valid {type}.").format(
|
|
43
|
-
value=value, type=self.name
|
|
44
|
-
),
|
|
45
|
-
param,
|
|
46
|
-
ctx,
|
|
47
|
-
)
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
INT_WITH_MAX = AutoIntParamType()
|
|
51
|
-
BOOL_WITH_AUTO = BooleanWithAutoParamType()
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|