tuft 0.1.0__py3-none-any.whl → 0.1.2__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.
- tuft/__init__.py +5 -2
- tuft/__main__.py +7 -0
- tuft/auth.py +35 -0
- tuft/backend.py +254 -0
- tuft/backends/__init__.py +10 -0
- tuft/backends/base_backend.py +112 -0
- tuft/backends/hf_training_model.py +404 -0
- tuft/backends/sampling_backend.py +253 -0
- tuft/backends/training_backend.py +327 -0
- tuft/checkpoints.py +193 -0
- tuft/cli.py +124 -0
- tuft/config.py +123 -0
- tuft/exceptions.py +138 -0
- tuft/futures.py +431 -0
- tuft/loss_fn/__init__.py +48 -0
- tuft/loss_fn/cispo.py +40 -0
- tuft/loss_fn/cross_entropy.py +26 -0
- tuft/loss_fn/dro.py +37 -0
- tuft/loss_fn/importance_sampling.py +33 -0
- tuft/loss_fn/ppo.py +43 -0
- tuft/persistence/__init__.py +32 -0
- tuft/persistence/file_redis.py +268 -0
- tuft/persistence/redis_store.py +488 -0
- tuft/sampling_controller.py +368 -0
- tuft/server.py +720 -0
- tuft/state.py +352 -0
- tuft/telemetry/__init__.py +17 -0
- tuft/telemetry/metrics.py +335 -0
- tuft/telemetry/provider.py +198 -0
- tuft/telemetry/tracing.py +43 -0
- tuft/training_controller.py +728 -0
- tuft-0.1.2.dist-info/METADATA +633 -0
- tuft-0.1.2.dist-info/RECORD +36 -0
- {tuft-0.1.0.dist-info → tuft-0.1.2.dist-info}/WHEEL +1 -2
- tuft-0.1.2.dist-info/entry_points.txt +2 -0
- {tuft-0.1.0.dist-info → tuft-0.1.2.dist-info}/licenses/LICENSE +2 -2
- tuft-0.1.0.dist-info/METADATA +0 -77
- tuft-0.1.0.dist-info/RECORD +0 -6
- tuft-0.1.0.dist-info/top_level.txt +0 -1
|
@@ -0,0 +1,633 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: tuft
|
|
3
|
+
Version: 0.1.2
|
|
4
|
+
Summary: A multi-tenant fine-tuning platform for LLMs with Tinker-compatible API
|
|
5
|
+
Author-email: TuFT Developers <tuft@list.alibaba-inc.com>
|
|
6
|
+
License: MIT License
|
|
7
|
+
|
|
8
|
+
Copyright (c) 2026 agentscope-ai
|
|
9
|
+
|
|
10
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
11
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
12
|
+
in the Software without restriction, including without limitation the rights
|
|
13
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
14
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
15
|
+
furnished to do so, subject to the following conditions:
|
|
16
|
+
|
|
17
|
+
The above copyright notice and this permission notice shall be included in all
|
|
18
|
+
copies or substantial portions of the Software.
|
|
19
|
+
|
|
20
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
21
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
22
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
23
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
24
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
25
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
26
|
+
SOFTWARE.
|
|
27
|
+
License-File: LICENSE
|
|
28
|
+
Requires-Python: >=3.11
|
|
29
|
+
Requires-Dist: fastapi>=0.125.0
|
|
30
|
+
Requires-Dist: httpx>=0.28.1
|
|
31
|
+
Requires-Dist: numpy<2.0.0
|
|
32
|
+
Requires-Dist: nvidia-ml-py>=13.0.0
|
|
33
|
+
Requires-Dist: omegaconf>=2.3.0
|
|
34
|
+
Requires-Dist: opentelemetry-api>=1.20.0
|
|
35
|
+
Requires-Dist: opentelemetry-exporter-otlp>=1.20.0
|
|
36
|
+
Requires-Dist: opentelemetry-instrumentation-fastapi>=0.41b0
|
|
37
|
+
Requires-Dist: opentelemetry-instrumentation-logging>=0.41b0
|
|
38
|
+
Requires-Dist: opentelemetry-sdk>=1.20.0
|
|
39
|
+
Requires-Dist: psutil>=5.9.0
|
|
40
|
+
Requires-Dist: ray>=2.50.0
|
|
41
|
+
Requires-Dist: tinker>=0.7.0
|
|
42
|
+
Requires-Dist: transformers<5.0.0,>=4.57.3
|
|
43
|
+
Requires-Dist: typer>=0.20.1
|
|
44
|
+
Requires-Dist: uvicorn[standard]>=0.38.0
|
|
45
|
+
Provides-Extra: backend
|
|
46
|
+
Requires-Dist: datasets>=4.0.0; extra == 'backend'
|
|
47
|
+
Requires-Dist: huggingface-hub<1.0,>=0.20.0; extra == 'backend'
|
|
48
|
+
Requires-Dist: peft>=0.18.0; extra == 'backend'
|
|
49
|
+
Requires-Dist: trinity-rft[vllm]>=0.4.1; extra == 'backend'
|
|
50
|
+
Provides-Extra: dev
|
|
51
|
+
Requires-Dist: detect-secrets>=1.5.0; extra == 'dev'
|
|
52
|
+
Requires-Dist: nbqa>=1.9.1; extra == 'dev'
|
|
53
|
+
Requires-Dist: pre-commit>=4.5.1; extra == 'dev'
|
|
54
|
+
Requires-Dist: pyright>=1.1.408; extra == 'dev'
|
|
55
|
+
Requires-Dist: pytest-asyncio>=1.3.0; extra == 'dev'
|
|
56
|
+
Requires-Dist: pytest-json-ctrf; extra == 'dev'
|
|
57
|
+
Requires-Dist: pytest>=9.0.2; extra == 'dev'
|
|
58
|
+
Requires-Dist: ruff>=0.14.14; extra == 'dev'
|
|
59
|
+
Provides-Extra: persistence
|
|
60
|
+
Requires-Dist: redis>=5.0.0; extra == 'persistence'
|
|
61
|
+
Description-Content-Type: text/markdown
|
|
62
|
+
|
|
63
|
+
<div align="center">
|
|
64
|
+
<img src="assets/tuft-logo-colorful.svg" alt="TuFT Logo" width="400"/>
|
|
65
|
+
</div>
|
|
66
|
+
|
|
67
|
+
TuFT (**T**enant-**u**nified **F**ine**T**uning) is a multi-tenant platform that lets multiple users fine-tune LLMs on shared infrastructure through a unified API. Access it via the Tinker SDK or compatible clients.
|
|
68
|
+
|
|
69
|
+
Check out our [roadmap](#roadmap) to see what we're building next.
|
|
70
|
+
|
|
71
|
+
We're open source and welcome contributions! Join the community:
|
|
72
|
+
- [DingTalk Group](https://qr.dingtalk.com/action/joingroup?code=v1,k1,UWvzO6HHSeuvRQ5WXCOMJEijadQV+hDjhMIpiVr8qCs=&_dt_no_comment=1&origin=11?)
|
|
73
|
+
- [Discord](https://discord.gg/wEahC7ZJ)
|
|
74
|
+
|
|
75
|
+
## Table of Contents
|
|
76
|
+
|
|
77
|
+
- [Quick Install](#quick-install)
|
|
78
|
+
- [Quick Start Example](#quick-start-example)
|
|
79
|
+
- [Installation](#installation)
|
|
80
|
+
- [Use the Pre-built Docker Image](#use-the-pre-built-docker-image)
|
|
81
|
+
- [User Guide](#user-guide)
|
|
82
|
+
- [Persistence](#persistence)
|
|
83
|
+
- [Observability (OpenTelemetry)](#observability-opentelemetry)
|
|
84
|
+
- [Architecture](#architecture)
|
|
85
|
+
- [Roadmap](#roadmap)
|
|
86
|
+
- [Development](#development)
|
|
87
|
+
|
|
88
|
+
## Quick Install
|
|
89
|
+
|
|
90
|
+
> **Note**: This script supports unix platforms. For other platforms, see [Installation](#installation).
|
|
91
|
+
|
|
92
|
+
Install TuFT with a single command:
|
|
93
|
+
|
|
94
|
+
```bash
|
|
95
|
+
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/agentscope-ai/tuft/main/scripts/install.sh)"
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
This installs TuFT with full backend support (GPU dependencies, persistence, flash-attn) and a bundled Python environment to `~/.tuft`. After installation, restart your terminal and run:
|
|
99
|
+
|
|
100
|
+
```bash
|
|
101
|
+
tuft
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
## Quick Start Example
|
|
105
|
+
|
|
106
|
+
This example demonstrates how to use TuFT for training and sampling with the [Tinker SDK](https://pypi.org/project/tinker/).
|
|
107
|
+
Make sure the server is running on port 10610 before running the code. See the [Run the server](#run-the-server) section below for instructions on starting the server.
|
|
108
|
+
|
|
109
|
+
### 1. Data Preparation
|
|
110
|
+
|
|
111
|
+
Prepare your training data in the format expected by TuFT:
|
|
112
|
+
|
|
113
|
+
```python
|
|
114
|
+
import tinker
|
|
115
|
+
from tinker import types
|
|
116
|
+
|
|
117
|
+
# Connect to the running TuFT server
|
|
118
|
+
client = tinker.ServiceClient(base_url="http://localhost:10610", api_key="local-dev-key")
|
|
119
|
+
|
|
120
|
+
# Discover available base models
|
|
121
|
+
capabilities = client.get_server_capabilities()
|
|
122
|
+
base_model = capabilities.supported_models[0].model_name
|
|
123
|
+
|
|
124
|
+
print("Supported models:")
|
|
125
|
+
for model in capabilities.supported_models:
|
|
126
|
+
print("-", model.model_name or "(unknown)")
|
|
127
|
+
|
|
128
|
+
# Prepare training data
|
|
129
|
+
# In practice, you would use a tokenizer:
|
|
130
|
+
# tokenizer = training.get_tokenizer()
|
|
131
|
+
# prompt_tokens = tokenizer.encode("Hello from TuFT")
|
|
132
|
+
# target_tokens = tokenizer.encode(" Generalizing beyond the prompt")
|
|
133
|
+
|
|
134
|
+
# For this example, we use fake token IDs
|
|
135
|
+
prompt_tokens = [101, 42, 37, 102]
|
|
136
|
+
target_tokens = [101, 99, 73, 102]
|
|
137
|
+
|
|
138
|
+
datum = types.Datum(
|
|
139
|
+
model_input=types.ModelInput.from_ints(prompt_tokens),
|
|
140
|
+
loss_fn_inputs={
|
|
141
|
+
"target_tokens": types.TensorData(
|
|
142
|
+
data=target_tokens,
|
|
143
|
+
dtype="int64",
|
|
144
|
+
shape=[len(target_tokens)]
|
|
145
|
+
),
|
|
146
|
+
"weights": types.TensorData(data=[1.0, 1.0, 1.0, 1.0], dtype="float32", shape=[4])
|
|
147
|
+
},
|
|
148
|
+
)
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
**Example Output:**
|
|
152
|
+
```
|
|
153
|
+
Supported models:
|
|
154
|
+
- Qwen/Qwen3-4B
|
|
155
|
+
- Qwen/Qwen3-8B
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
### 2. Training
|
|
159
|
+
|
|
160
|
+
Create a LoRA training client and perform forward/backward passes with optimizer steps:
|
|
161
|
+
|
|
162
|
+
```python
|
|
163
|
+
# Create a LoRA training client
|
|
164
|
+
training = client.create_lora_training_client(base_model=base_model, rank=8)
|
|
165
|
+
|
|
166
|
+
# Run forward/backward pass
|
|
167
|
+
fwdbwd = training.forward_backward([datum], "cross_entropy").result(timeout=30)
|
|
168
|
+
print("Loss metrics:", fwdbwd.metrics)
|
|
169
|
+
|
|
170
|
+
# Apply optimizer update
|
|
171
|
+
optim = training.optim_step(types.AdamParams(learning_rate=1e-4)).result(timeout=30)
|
|
172
|
+
print("Optimizer metrics:", optim.metrics)
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
**Example Output:**
|
|
176
|
+
```
|
|
177
|
+
Loss metrics: {'loss:sum': 2.345, 'step:max': 0.0, 'grad_norm:mean': 0.123}
|
|
178
|
+
Optimizer metrics: {'learning_rate:mean': 0.0001, 'step:max': 1.0, 'update_norm:mean': 0.045}
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
### 3. Save Checkpoint
|
|
182
|
+
|
|
183
|
+
Save the trained model checkpoint and sampler weights:
|
|
184
|
+
|
|
185
|
+
```python
|
|
186
|
+
# Save checkpoint for training resumption
|
|
187
|
+
checkpoint = training.save_state("demo-checkpoint").result(timeout=60)
|
|
188
|
+
print("Checkpoint saved to:", checkpoint.path)
|
|
189
|
+
|
|
190
|
+
# Save sampler weights for inference
|
|
191
|
+
sampler_weights = training.save_weights_for_sampler("demo-sampler").result(timeout=60)
|
|
192
|
+
print("Sampler weights saved to:", sampler_weights.path)
|
|
193
|
+
|
|
194
|
+
# Inspect session information
|
|
195
|
+
rest = client.create_rest_client()
|
|
196
|
+
session_id = client.holder.get_session_id()
|
|
197
|
+
session_info = rest.get_session(session_id).result(timeout=30)
|
|
198
|
+
print("Session contains training runs:", session_info.training_run_ids)
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
**Example Output:**
|
|
202
|
+
```
|
|
203
|
+
Checkpoint saved to: tinker://550e8400-e29b-41d4-a716-446655440000/weights/checkpoint-001
|
|
204
|
+
Sampler weights saved to: tinker://550e8400-e29b-41d4-a716-446655440000/sampler_weights/sampler-001
|
|
205
|
+
Session contains training runs: ['550e8400-e29b-41d4-a716-446655440000']
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
### 4. Sampling
|
|
209
|
+
|
|
210
|
+
Load the saved weights and generate tokens:
|
|
211
|
+
|
|
212
|
+
```python
|
|
213
|
+
# Create a sampling client with saved weights
|
|
214
|
+
sampling = client.create_sampling_client(model_path=sampler_weights.path)
|
|
215
|
+
|
|
216
|
+
# Prepare prompt for sampling
|
|
217
|
+
# sample_prompt = tokenizer.encode("Tell me something inspiring.")
|
|
218
|
+
sample_prompt = [101, 57, 12, 7, 102]
|
|
219
|
+
|
|
220
|
+
# Generate tokens
|
|
221
|
+
sample = sampling.sample(
|
|
222
|
+
prompt=types.ModelInput.from_ints(sample_prompt),
|
|
223
|
+
num_samples=1,
|
|
224
|
+
sampling_params=types.SamplingParams(max_tokens=5, temperature=0.5),
|
|
225
|
+
).result(timeout=30)
|
|
226
|
+
|
|
227
|
+
if sample.sequences:
|
|
228
|
+
print("Sample tokens:", sample.sequences[0].tokens)
|
|
229
|
+
# Decode tokens to text:
|
|
230
|
+
# sample_text = tokenizer.decode(sample.sequences[0].tokens)
|
|
231
|
+
# print("Generated text:", sample_text)
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
**Example Output:**
|
|
235
|
+
```
|
|
236
|
+
Sample tokens: [101, 57, 12, 7, 42, 102]
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
> **Note**: Replace fake token IDs with actual tokenizer calls when you have a tokenizer available locally.
|
|
240
|
+
|
|
241
|
+
## Installation
|
|
242
|
+
|
|
243
|
+
> **Tip**: For a quick one-command setup, see [Quick Install](#quick-install). This section is for users who prefer to manage their own Python environment or need more control over the installation.
|
|
244
|
+
|
|
245
|
+
We recommend using [uv](https://github.com/astral-sh/uv) for dependency management.
|
|
246
|
+
|
|
247
|
+
### Install from Source Code
|
|
248
|
+
|
|
249
|
+
1. Clone the repository:
|
|
250
|
+
|
|
251
|
+
```bash
|
|
252
|
+
git clone https://github.com/agentscope-ai/TuFT
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
2. Create a virtual environment:
|
|
256
|
+
|
|
257
|
+
```bash
|
|
258
|
+
cd TuFT
|
|
259
|
+
uv venv --python 3.12
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
3. Activate environment:
|
|
263
|
+
|
|
264
|
+
```bash
|
|
265
|
+
source .venv/bin/activate
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
4. Install dependencies:
|
|
269
|
+
|
|
270
|
+
```bash
|
|
271
|
+
# Install minimal dependencies for non-development installs
|
|
272
|
+
uv sync
|
|
273
|
+
|
|
274
|
+
# If you need to develop or run tests, install dev dependencies
|
|
275
|
+
uv sync --extra dev
|
|
276
|
+
|
|
277
|
+
# If you want to run the full feature set (e.g., model serving, persistence),
|
|
278
|
+
# please install all dependencies
|
|
279
|
+
uv sync --all-extras
|
|
280
|
+
python scripts/install_flash_attn.py
|
|
281
|
+
# If you face issues with flash-attn installation, you can try installing it manually:
|
|
282
|
+
# uv pip install flash-attn --no-build-isolation
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
|
|
286
|
+
|
|
287
|
+
### Install via PyPI
|
|
288
|
+
|
|
289
|
+
You can also install TuFT directly from PyPI:
|
|
290
|
+
|
|
291
|
+
```bash
|
|
292
|
+
uv pip install tuft
|
|
293
|
+
|
|
294
|
+
# Install optional dependencies as needed
|
|
295
|
+
uv pip install "tuft[dev,backend,persistence]"
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
### Run the server
|
|
299
|
+
|
|
300
|
+
The CLI starts a FastAPI server:
|
|
301
|
+
|
|
302
|
+
```bash
|
|
303
|
+
tuft --port 10610 --config /path/to/tuft_config.yaml
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
The config file `tuft_config.yaml` specifies server settings including available base models, authentication, persistence, and telemetry. Below is a minimal example.
|
|
307
|
+
|
|
308
|
+
```yaml
|
|
309
|
+
supported_models:
|
|
310
|
+
- model_name: Qwen/Qwen3-4B
|
|
311
|
+
model_path: Qwen/Qwen3-4B
|
|
312
|
+
max_model_len: 32768
|
|
313
|
+
tensor_parallel_size: 1
|
|
314
|
+
- model_name: Qwen/Qwen3-8B
|
|
315
|
+
model_path: Qwen/Qwen3-8B
|
|
316
|
+
max_model_len: 32768
|
|
317
|
+
tensor_parallel_size: 1
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
See [`config/tuft_config.example.yaml`](config/tuft_config.example.yaml) for a complete example configuration with all available options.
|
|
321
|
+
|
|
322
|
+
## Use the Pre-built Docker Image
|
|
323
|
+
|
|
324
|
+
If you face issues with local installation or want to get started quickly,
|
|
325
|
+
you can use the pre-built Docker image.
|
|
326
|
+
|
|
327
|
+
1. Pull the latest image from GitHub Container Registry:
|
|
328
|
+
|
|
329
|
+
```bash
|
|
330
|
+
docker pull ghcr.io/agentscope-ai/tuft:latest
|
|
331
|
+
```
|
|
332
|
+
|
|
333
|
+
2. Run the Docker container and start the TuFT server on port 10610:
|
|
334
|
+
|
|
335
|
+
```bash
|
|
336
|
+
docker run -it \
|
|
337
|
+
--gpus all \
|
|
338
|
+
--shm-size="128g" \
|
|
339
|
+
--rm \
|
|
340
|
+
-p 10610:10610 \
|
|
341
|
+
-v <host_dir>:/data \
|
|
342
|
+
ghcr.io/agentscope-ai/tuft:latest \
|
|
343
|
+
tuft --port 10610 --config /data/tuft_config.yaml
|
|
344
|
+
```
|
|
345
|
+
|
|
346
|
+
Please replace `<host_dir>` with a directory on your host machine where you want to store model checkpoints and other data.
|
|
347
|
+
Suppose you have the following structure on your host machine:
|
|
348
|
+
|
|
349
|
+
```plaintext
|
|
350
|
+
<host_dir>/
|
|
351
|
+
├── checkpoints/
|
|
352
|
+
├── Qwen3-4B/
|
|
353
|
+
├── Qwen3-8B/
|
|
354
|
+
└── tuft_config.yaml
|
|
355
|
+
```
|
|
356
|
+
|
|
357
|
+
The `tuft_config.yaml` file defines the server configuration, for example:
|
|
358
|
+
```yaml
|
|
359
|
+
supported_models:
|
|
360
|
+
- model_name: Qwen/Qwen3-4B
|
|
361
|
+
model_path: /data/Qwen3-4B
|
|
362
|
+
max_model_len: 32768
|
|
363
|
+
tensor_parallel_size: 1
|
|
364
|
+
- model_name: Qwen/Qwen3-8B
|
|
365
|
+
model_path: /data/Qwen3-8B
|
|
366
|
+
max_model_len: 32768
|
|
367
|
+
tensor_parallel_size: 1
|
|
368
|
+
```
|
|
369
|
+
|
|
370
|
+
## User Guide
|
|
371
|
+
We provide practical examples to demonstrate how to use TuFT for training and sampling. The guides below cover both Supervised Fine-Tuning and Reinforcement Learning workflows, with links to runnable notebooks.
|
|
372
|
+
|
|
373
|
+
| Dataset | Task | Guide | Example |
|
|
374
|
+
|---|---|---|---|
|
|
375
|
+
| [no_robots](https://huggingface.co/datasets/HuggingFaceH4/no_robots) | Supervised Fine-Tuning (SFT) | [chat_sft.md](docs/chat_sft.md) | [chat_sft.ipynb](examples/chat_sft.ipynb) |
|
|
376
|
+
| [Countdown](https://huggingface.co/datasets/Jiayi-Pan/Countdown-Tasks-3to4) | Reinforcement Learning (RL) | [countdown_rl.md](docs/countdown_rl.md) | [countdown_rl.ipynb](examples/countdown_rl.ipynb) |
|
|
377
|
+
|
|
378
|
+
|
|
379
|
+
## Persistence
|
|
380
|
+
|
|
381
|
+
TuFT supports optional Redis-based persistence for server state. When enabled,
|
|
382
|
+
the server can recover sessions, training runs, and pending futures after a restart.
|
|
383
|
+
|
|
384
|
+
To use persistence, install the optional dependency:
|
|
385
|
+
|
|
386
|
+
```bash
|
|
387
|
+
uv pip install tuft[persistence]
|
|
388
|
+
```
|
|
389
|
+
|
|
390
|
+
### Persistence Modes
|
|
391
|
+
|
|
392
|
+
TuFT provides three persistence modes:
|
|
393
|
+
|
|
394
|
+
| Mode | Description | Use Case |
|
|
395
|
+
|------|-------------|----------|
|
|
396
|
+
| `disabled` | No persistence, data in-memory only | Development, testing without state recovery |
|
|
397
|
+
| `redis_url` | External Redis server | Production, multi-instance deployments |
|
|
398
|
+
| `file_redis` | File-backed store | Demos, small-scale testing |
|
|
399
|
+
|
|
400
|
+
### Configuration
|
|
401
|
+
|
|
402
|
+
Add a `persistence` section to your `tuft_config.yaml` configuration file and choose one of the following modes.
|
|
403
|
+
|
|
404
|
+
#### Mode 1: Disabled (Default)
|
|
405
|
+
|
|
406
|
+
No configuration needed. All data is stored in memory and lost on restart.
|
|
407
|
+
|
|
408
|
+
```yaml
|
|
409
|
+
# tuft_config.yaml
|
|
410
|
+
persistence:
|
|
411
|
+
mode: disabled
|
|
412
|
+
```
|
|
413
|
+
|
|
414
|
+
#### Mode 2: External Redis Server
|
|
415
|
+
|
|
416
|
+
Use an external Redis server for production deployments:
|
|
417
|
+
|
|
418
|
+
```yaml
|
|
419
|
+
# tuft_config.yaml
|
|
420
|
+
persistence:
|
|
421
|
+
mode: redis_url
|
|
422
|
+
redis_url: "redis://localhost:6379/0"
|
|
423
|
+
namespace: "tuft"
|
|
424
|
+
```
|
|
425
|
+
|
|
426
|
+
You can start a local Redis instance using Docker:
|
|
427
|
+
|
|
428
|
+
```bash
|
|
429
|
+
docker run -d --name TuFT-redis -p 6379:6379 redis:7-alpine
|
|
430
|
+
```
|
|
431
|
+
|
|
432
|
+
#### Mode 3: File-backed Store
|
|
433
|
+
|
|
434
|
+
Use the file-backed store for demos or small-scale testing:
|
|
435
|
+
|
|
436
|
+
```yaml
|
|
437
|
+
# tuft_config.yaml
|
|
438
|
+
persistence:
|
|
439
|
+
mode: file_redis
|
|
440
|
+
file_path: "~/.cache/tuft/file_redis.json"
|
|
441
|
+
namespace: "tuft"
|
|
442
|
+
```
|
|
443
|
+
|
|
444
|
+
## Observability (OpenTelemetry)
|
|
445
|
+
|
|
446
|
+
TuFT supports optional OpenTelemetry integration for distributed tracing, metrics, and logging.
|
|
447
|
+
This allows you to monitor your TuFT server using observability tools like SigNoz, Jaeger, or Grafana.
|
|
448
|
+
|
|
449
|
+
### Configuration
|
|
450
|
+
|
|
451
|
+
Add the following `telemetry` section to your `tuft_config.yaml` configuration file:
|
|
452
|
+
|
|
453
|
+
```yaml
|
|
454
|
+
# tuft_config.yaml
|
|
455
|
+
telemetry:
|
|
456
|
+
enabled: true
|
|
457
|
+
service_name: tuft
|
|
458
|
+
otlp_endpoint: http://localhost:4317 # Your OTLP collector endpoint
|
|
459
|
+
resource_attributes: {}
|
|
460
|
+
# example:
|
|
461
|
+
# deployment.environment: production
|
|
462
|
+
# service.version: 1.0.0
|
|
463
|
+
# service.namespace: my-namespace
|
|
464
|
+
```
|
|
465
|
+
|
|
466
|
+
Alternatively, use environment variables:
|
|
467
|
+
|
|
468
|
+
```bash
|
|
469
|
+
export TUFT_OTLP_ENDPOINT=http://localhost:4317
|
|
470
|
+
export TUFT_OTEL_DEBUG=1 # Enable console exporter for debugging
|
|
471
|
+
```
|
|
472
|
+
|
|
473
|
+
## Architecture
|
|
474
|
+
|
|
475
|
+
TuFT provides a unified service API for agentic model training and sampling. The system supports multiple LoRA adapters per base model and checkpoint management.
|
|
476
|
+
|
|
477
|
+
```mermaid
|
|
478
|
+
graph TB
|
|
479
|
+
subgraph Client["Client Layer"]
|
|
480
|
+
SDK[Tinker SDK Client]
|
|
481
|
+
end
|
|
482
|
+
|
|
483
|
+
subgraph API["TuFT Service API"]
|
|
484
|
+
REST[Service API<br/>REST/HTTP]
|
|
485
|
+
Session[Session Management]
|
|
486
|
+
end
|
|
487
|
+
|
|
488
|
+
subgraph Backend["Backend Layer"]
|
|
489
|
+
Training[Training Backend<br/>Forward/Backward/Optim Step]
|
|
490
|
+
Sampling[Sampling Backend<br/>Token Generation]
|
|
491
|
+
end
|
|
492
|
+
|
|
493
|
+
subgraph Models["Model Layer"]
|
|
494
|
+
BaseModel[Base LLM Model]
|
|
495
|
+
LoRA[LoRA Adapters<br/>Multiple per Base Model]
|
|
496
|
+
end
|
|
497
|
+
|
|
498
|
+
subgraph Storage["Storage"]
|
|
499
|
+
Checkpoint[Model Checkpoints<br/>& LoRA Weights]
|
|
500
|
+
end
|
|
501
|
+
|
|
502
|
+
SDK --> REST
|
|
503
|
+
REST --> Session
|
|
504
|
+
Session --> Training
|
|
505
|
+
Session --> Sampling
|
|
506
|
+
Training --> BaseModel
|
|
507
|
+
Training --> LoRA
|
|
508
|
+
Sampling --> BaseModel
|
|
509
|
+
Sampling --> LoRA
|
|
510
|
+
Training --> Checkpoint
|
|
511
|
+
Sampling --> Checkpoint
|
|
512
|
+
```
|
|
513
|
+
|
|
514
|
+
### Key Components
|
|
515
|
+
|
|
516
|
+
- **Service API**: RESTful interface for training and sampling operations
|
|
517
|
+
- **Training Backend**: Handles forward/backward passes and optimizer steps for LoRA fine-tuning
|
|
518
|
+
- **Sampling Backend**: Generates tokens from trained models
|
|
519
|
+
- **Checkpoint Storage**: Manages model checkpoints and LoRA weights
|
|
520
|
+
|
|
521
|
+
|
|
522
|
+
|
|
523
|
+
## Roadmap
|
|
524
|
+
|
|
525
|
+
### Core Focus: Post-Training for Agent Scenarios
|
|
526
|
+
|
|
527
|
+
We focus on post-training for agentic models. The rollout phase in RL training involves reasoning, multi-turn conversations, and tool use, which tends to be asynchronous relative to the training phase. We aim to improve the throughput and resource efficiency of the overall system, building tools that are easy to use and integrate into existing workflows.
|
|
528
|
+
|
|
529
|
+
### Architecture & Positioning
|
|
530
|
+
|
|
531
|
+
- **Horizontal platform**: Not a vertically integrated fine-tuning solution, but a flexible platform that plugs into different training frameworks and compute infrastructures
|
|
532
|
+
- **Code-first API**: Connects agentic training workflows with compute infrastructure through programmatic interfaces
|
|
533
|
+
- **Layer in AI stack**: Sits above the infrastructure layer (Kubernetes, cloud platforms, GPU clusters), integrating with training frameworks (PeFT, FSDP, vLLM, DeepSpeed) as implementation dependencies
|
|
534
|
+
- **Integration approach**: Works with existing ecosystems rather than replacing them
|
|
535
|
+
|
|
536
|
+
### Near-Term (3 months)
|
|
537
|
+
|
|
538
|
+
- **Multi-machine, multi-GPU training**: Support distributed architectures using PeFT, FSDP, vLLM, DeepSpeed, etc.
|
|
539
|
+
- **Cloud-native deployment**: Integration with AWS, Alibaba Cloud, GCP, Azure and Kubernetes orchestration
|
|
540
|
+
- **Observability**: Monitoring system with real-time logs, GPU metrics, training progress, and debugging tools
|
|
541
|
+
- **Serverless GPU**: Lightweight runtime for diverse deployment scenarios, with multi-user and multi-tenant GPU resource sharing to improve utilization efficiency
|
|
542
|
+
|
|
543
|
+
### Long-Term (6 months)
|
|
544
|
+
|
|
545
|
+
- **Environment-driven learning loop**: Standardized interfaces with WebShop, MiniWob++, BrowserEnv, Voyager and other agent training environments
|
|
546
|
+
- **Automated pipeline**: Task execution → feedback collection → data generation → model updates
|
|
547
|
+
- **Advanced RL paradigms**: RLAIF, Error Replay, and environment feedback mechanisms
|
|
548
|
+
- **Simulation sandboxes**: Lightweight local environments for rapid experimentation
|
|
549
|
+
|
|
550
|
+
### Open Collaboration: We are Looking for Collaborators
|
|
551
|
+
|
|
552
|
+
This roadmap is not fixed, but rather a starting point for our journey with the open source community. Every feature design will be implemented through GitHub Issue discussions, PRs, and prototype validation. We sincerely welcome you to propose real-world use cases, performance bottlenecks, or innovative ideas—it is these voices that will collectively define the future of Agent post-training.
|
|
553
|
+
|
|
554
|
+
We welcome suggestions and contributions from the community! Join us on:
|
|
555
|
+
- [DingTalk Group](https://qr.dingtalk.com/action/joingroup?code=v1,k1,UWvzO6HHSeuvRQ5WXCOMJEijadQV+hDjhMIpiVr8qCs=&_dt_no_comment=1&origin=11?)
|
|
556
|
+
- [Discord](https://discord.gg/wEahC7ZJ) (on AgentScope's Server)
|
|
557
|
+
|
|
558
|
+
## Development
|
|
559
|
+
|
|
560
|
+
### Setup Development Environment
|
|
561
|
+
|
|
562
|
+
1. Install [uv](https://github.com/astral-sh/uv) if you haven't already:
|
|
563
|
+
|
|
564
|
+
```bash
|
|
565
|
+
curl -LsSf https://astral.sh/uv/install.sh | sh
|
|
566
|
+
```
|
|
567
|
+
|
|
568
|
+
2. Install dev dependencies:
|
|
569
|
+
|
|
570
|
+
```bash
|
|
571
|
+
uv sync --extra dev
|
|
572
|
+
```
|
|
573
|
+
|
|
574
|
+
3. Set up pre-commit hooks:
|
|
575
|
+
|
|
576
|
+
```bash
|
|
577
|
+
uv run pre-commit install
|
|
578
|
+
```
|
|
579
|
+
|
|
580
|
+
### Running Tests
|
|
581
|
+
|
|
582
|
+
```bash
|
|
583
|
+
uv run pytest
|
|
584
|
+
```
|
|
585
|
+
|
|
586
|
+
To skip integration tests:
|
|
587
|
+
|
|
588
|
+
```bash
|
|
589
|
+
uv run pytest -m "not integration"
|
|
590
|
+
```
|
|
591
|
+
|
|
592
|
+
For detailed testing instructions, including GPU tests, persistence testing, and writing new tests, see the [Testing Guide](docs/how_to_write_tests.md).
|
|
593
|
+
|
|
594
|
+
### Linting and Type Checking
|
|
595
|
+
|
|
596
|
+
Run the linter:
|
|
597
|
+
|
|
598
|
+
```bash
|
|
599
|
+
uv run ruff check .
|
|
600
|
+
uv run ruff format .
|
|
601
|
+
```
|
|
602
|
+
|
|
603
|
+
Run the type checker:
|
|
604
|
+
|
|
605
|
+
```bash
|
|
606
|
+
uv run pyright
|
|
607
|
+
```
|
|
608
|
+
|
|
609
|
+
### Notebook Linting
|
|
610
|
+
|
|
611
|
+
For Jupyter notebooks:
|
|
612
|
+
|
|
613
|
+
```bash
|
|
614
|
+
uv run nbqa ruff notebooks/
|
|
615
|
+
```
|
|
616
|
+
|
|
617
|
+
### Secret Detection
|
|
618
|
+
|
|
619
|
+
Scan and update the secrets baseline:
|
|
620
|
+
|
|
621
|
+
```bash
|
|
622
|
+
uv run detect-secrets scan > .secrets.baseline
|
|
623
|
+
```
|
|
624
|
+
|
|
625
|
+
Audit detected secrets to mark false positives:
|
|
626
|
+
|
|
627
|
+
```bash
|
|
628
|
+
uv run detect-secrets audit .secrets.baseline
|
|
629
|
+
```
|
|
630
|
+
|
|
631
|
+
### Contributing
|
|
632
|
+
|
|
633
|
+
Please ensure all tests pass and pre-commit hooks succeed before creating new PRs.
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
tuft/__init__.py,sha256=BJu6iJ_QGwcJXRXDgR1LjV25KgM6sVd7_WqIXVTEuVM,97
|
|
2
|
+
tuft/__main__.py,sha256=MPhC9msQXf9py5xkLPQ4JoqrvCpL_qXVwksasNUj7ig,131
|
|
3
|
+
tuft/auth.py,sha256=2Wk9ATXlAiGm1Irpj66CfIyORuHzciSNAOzVwM8PeO0,1071
|
|
4
|
+
tuft/backend.py,sha256=ftiaaNds2MXroszZW8l6DEq515qiw1KmrODI3x6AHE4,10254
|
|
5
|
+
tuft/checkpoints.py,sha256=bObo2NzDrfzp5BiS6I_FIA3frLFic_sT4o4c-PEzfpk,6917
|
|
6
|
+
tuft/cli.py,sha256=-WhmHGIHmWtL46LvXRlhTPVPhBUjZHVLJi0nYR_pqoE,4024
|
|
7
|
+
tuft/config.py,sha256=bX6NuSora0Wqhk5Q5lsnc0lojeevxnLHfiijJHMdtVg,4380
|
|
8
|
+
tuft/exceptions.py,sha256=_xdsL8bx3Y6jvC5VYHVCa73uAEWXxcl2YwVc09lJXFk,4088
|
|
9
|
+
tuft/futures.py,sha256=0gRLgDJJQRGGmULYsKdUs3VDsrLN8QfuFfXV00kxHO4,16375
|
|
10
|
+
tuft/sampling_controller.py,sha256=c02VQ6Qww9IQC9VJYzQO9Z9v45kK2QeaOKlknYWjSI4,15250
|
|
11
|
+
tuft/server.py,sha256=NUapRGdQbQH6PbuCfMZeMVi_7vM6nM7xmxepCPkgyko,24996
|
|
12
|
+
tuft/state.py,sha256=J9R5Wd9JlMtpYcaY_6t5RvgJbY3EX5ZJTZfoQhwZ9hU,12853
|
|
13
|
+
tuft/training_controller.py,sha256=V4JMgyEnf4wYGrk72AR5rHH1iYl488vt7d0c-ubTrO0,30008
|
|
14
|
+
tuft/backends/__init__.py,sha256=7A6Pu-vEMbcMWapAh-zkI1O5WtBHO0OxwED8qAy9kAQ,262
|
|
15
|
+
tuft/backends/base_backend.py,sha256=bdlx3hRyEj00GKFlh2fAczn7h4zANz7bdKgXb_F18y4,3462
|
|
16
|
+
tuft/backends/hf_training_model.py,sha256=XQa598SpY7DnYYU0rTaHjlh-5dRCPueFtcdxrcjXWIc,16993
|
|
17
|
+
tuft/backends/sampling_backend.py,sha256=gf5laCMGbk9CrFuEJB0udKywVIimyU9-lqlwKok6j_w,10178
|
|
18
|
+
tuft/backends/training_backend.py,sha256=p1w-1i9-vxlocr97eumB46WZS5LxrTnxz4y86mKA950,13149
|
|
19
|
+
tuft/loss_fn/__init__.py,sha256=l6wNbeqV6_WCs0jIg3H89eTUUpTf50aitLnDb9lRdM4,1620
|
|
20
|
+
tuft/loss_fn/cispo.py,sha256=L8HhqJ0rJcfgqfEkk445bvaKsZNWNAqwqm_47M9SB1Y,1598
|
|
21
|
+
tuft/loss_fn/cross_entropy.py,sha256=e9D2U_G8TNXOlOvEw7OQj-YE1H5DldzG2HS2QjKBfe8,935
|
|
22
|
+
tuft/loss_fn/dro.py,sha256=6d3jDK1OybcoFjq5vDwiUrURyaV-EajGLMECyF_2mjE,1315
|
|
23
|
+
tuft/loss_fn/importance_sampling.py,sha256=MTxO63LBhghVCEyDQYptaziFmVvEiSTE9xeGoyo20wc,1090
|
|
24
|
+
tuft/loss_fn/ppo.py,sha256=YpIYWNWqv9Asr3tV8iq5erSDHlZD8VmgOmDJQFHVWSo,1678
|
|
25
|
+
tuft/persistence/__init__.py,sha256=U-yEVEgikbrTMLdwPS2S9GUwuQV-1Fnt9Y5key0r9bA,615
|
|
26
|
+
tuft/persistence/file_redis.py,sha256=hLGClNhd9OID9JZMP-RZTisyoXOvQ0ctv3czj01dgIY,8091
|
|
27
|
+
tuft/persistence/redis_store.py,sha256=9z1zbtUSXzaQP3bAH18eLd4OQ8YiJtFo4TgsZoGsGX4,15904
|
|
28
|
+
tuft/telemetry/__init__.py,sha256=dlSGiJ_pMElhwEe31olGg88ZrjoBeGUBn2P17qFNymM,336
|
|
29
|
+
tuft/telemetry/metrics.py,sha256=Yz6s2AQ5CptFXvEm-PbO-Ib17-aF0rnoG8vZxH-Pawo,11538
|
|
30
|
+
tuft/telemetry/provider.py,sha256=jGKqTMsP-WekKGCMN9QHwt-g_1Lk1xUOy1BO-__xG5I,6700
|
|
31
|
+
tuft/telemetry/tracing.py,sha256=GL-wEEQtzM1ycgfI4sMsHUeIC7qj5MyOH-sBwHihbsE,957
|
|
32
|
+
tuft-0.1.2.dist-info/METADATA,sha256=UlTE_gR3cPFLzV69GyIHD6TOm-dHSmSM5NcEHt8L0Pg,20381
|
|
33
|
+
tuft-0.1.2.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
34
|
+
tuft-0.1.2.dist-info/entry_points.txt,sha256=T48zU7Vdi2ZsARDeOZ9jK6XGuYNaCbSaUTd5POouLms,39
|
|
35
|
+
tuft-0.1.2.dist-info/licenses/LICENSE,sha256=fJHdoqbikZ-GATzLNmixfKDot1w_cJuHKY3mH4qSmYs,1069
|
|
36
|
+
tuft-0.1.2.dist-info/RECORD,,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
MIT License
|
|
2
2
|
|
|
3
|
-
Copyright (c) 2026
|
|
3
|
+
Copyright (c) 2026 agentscope-ai
|
|
4
4
|
|
|
5
5
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
6
|
of this software and associated documentation files (the "Software"), to deal
|
|
@@ -18,4 +18,4 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
|
18
18
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
19
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
20
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
-
SOFTWARE.
|
|
21
|
+
SOFTWARE.
|