@tuned-tensor/cli 0.15.0 → 0.16.0
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.
- package/README.md +110 -42
- package/dist/{chunk-YGN26POJ.js → chunk-6RLNBTM6.js} +377 -99
- package/dist/chunk-6RLNBTM6.js.map +1 -0
- package/dist/index.js +58 -142
- package/dist/index.js.map +1 -1
- package/dist/local-runtime.js +143 -32
- package/dist/local-runtime.js.map +1 -1
- package/docs/local-runtime/architecture.md +17 -6
- package/docs/local-runtime/aws-gpu.md +131 -0
- package/docs/local-runtime/foundation-long-runs.md +21 -5
- package/docs/local-runtime/serving.md +11 -1
- package/examples/local-runtime/aws-runner.json +13 -0
- package/package.json +9 -6
- package/training/adapter/src/foundation_export.py +81 -0
- package/training/adapter/src/serve.py +12 -5
- package/training/adapter/src/train.py +4 -0
- package/training/foundation/pyproject.toml +4 -0
- package/training/foundation/src/model.py +2 -0
- package/training/foundation/uv.lock +225 -0
- package/dist/chunk-YGN26POJ.js.map +0 -1
|
@@ -127,17 +127,28 @@ hide a valid artifact. Serving verifies the manifest again.
|
|
|
127
127
|
|
|
128
128
|
## Configuration
|
|
129
129
|
|
|
130
|
-
The runner config controls
|
|
130
|
+
The runner config controls:
|
|
131
131
|
|
|
132
132
|
- state, artifact, and Hugging Face cache paths;
|
|
133
|
+
- optional AWS instance lookup, SSH access, and a remote process deadline;
|
|
133
134
|
- CUDA or CPU evaluation, with local serving through the CUDA vLLM runtime;
|
|
134
135
|
- deterministic generation and scoring limits.
|
|
135
136
|
|
|
136
|
-
The training project, Python entrypoints, working directory, and
|
|
137
|
-
|
|
138
|
-
included in the npm package.
|
|
139
|
-
`~/.tuned-tensor/cache`, keyed by the project and lockfile content, so
|
|
140
|
-
read-only global npm install remains runnable.
|
|
137
|
+
The training project, Python entrypoints, working directory, and forwarded
|
|
138
|
+
runtime settings are internal and fixed. Every stage uses the locked `uv`
|
|
139
|
+
project included in the npm package. Locally, the mutable virtual environment
|
|
140
|
+
lives in `~/.tuned-tensor/cache`, keyed by the project and lockfile content, so
|
|
141
|
+
a read-only global npm install remains runnable.
|
|
142
|
+
|
|
143
|
+
With [`gpu` configured](aws-gpu.md), the same orchestrator dispatches adapter
|
|
144
|
+
training and CUDA inference, or foundation stages after tokenization, over SSH
|
|
145
|
+
to an existing EC2 instance. It stages the bundled Python source, lockfile and
|
|
146
|
+
declared inputs in a private remote directory, creates the runtime environment
|
|
147
|
+
there, and retrieves outputs before cleanup. Dataset compilation, adapter
|
|
148
|
+
scoring/comparison, artifact validation and run state remain local. Foundation
|
|
149
|
+
tokenization also stays local. Serving and `tt hardware` inspect/use the local
|
|
150
|
+
host. AWS credentials authorize instance lookup locally; they are not forwarded
|
|
151
|
+
to GPU processes. TT does not provision or manage instance capacity.
|
|
141
152
|
|
|
142
153
|
Schemas are strict. Misspelled or obsolete fields fail validation instead of
|
|
143
154
|
silently reverting to defaults. Training itself is CUDA-only and fails fast
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
# Use your AWS GPU with the local pipeline
|
|
2
|
+
|
|
3
|
+
TT runs one pipeline on your laptop. Add `gpu` to `local-runner.json` to send
|
|
4
|
+
adapter training and CUDA evaluation, or foundation pretraining, fine-tuning,
|
|
5
|
+
RL and evaluation, to an existing EC2 instance in your account. Foundation
|
|
6
|
+
tokenization, dataset preparation, adapter scoring/comparisons, run history,
|
|
7
|
+
and artifact validation stay local. Results return to the usual local
|
|
8
|
+
directories. The laptop does not need an NVIDIA GPU.
|
|
9
|
+
|
|
10
|
+
Keep the existing spec and pipeline. Save this config beside `tunedtensor.json`,
|
|
11
|
+
replacing the example values with your own. You can also copy
|
|
12
|
+
[`examples/local-runtime/aws-runner.json`](../../examples/local-runtime/aws-runner.json).
|
|
13
|
+
All runner paths refer to the laptop; TT translates the paths needed remotely.
|
|
14
|
+
|
|
15
|
+
```json
|
|
16
|
+
{
|
|
17
|
+
"gpu": {
|
|
18
|
+
"provider": "aws",
|
|
19
|
+
"profile": "research",
|
|
20
|
+
"region": "eu-west-1",
|
|
21
|
+
"instanceId": "i-0123456789abcdef0",
|
|
22
|
+
"user": "ubuntu",
|
|
23
|
+
"identityFile": "~/.ssh/research-gpu.pem",
|
|
24
|
+
"maxSeconds": 86400
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
The laptop needs AWS CLI v2, OpenSSH, rsync 3.2+, and uv. On macOS, install
|
|
30
|
+
a current rsync (for example with Homebrew); the system version is too old.
|
|
31
|
+
Each machine must find its required programs through `PATH`; on the instance,
|
|
32
|
+
this includes noninteractive SSH sessions. The instance needs Linux, a compatible NVIDIA
|
|
33
|
+
GPU/driver, uv, rsync 3.2+, bash, setsid, and GNU timeout. TT
|
|
34
|
+
copies the bundled Python source and lockfile and runs that exact runtime
|
|
35
|
+
with `uv run --frozen`; there is no separate training image or Step Functions
|
|
36
|
+
pipeline. Internet access on the instance is needed to install the locked
|
|
37
|
+
runtime. No TT token is needed for training.
|
|
38
|
+
|
|
39
|
+
`profile` and `region` are optional. AWS CLI uses its normal credential chain,
|
|
40
|
+
including environment credentials, configured profiles and SSO. For SSO, log
|
|
41
|
+
in first with `aws sso login --profile research`. The selected identity needs
|
|
42
|
+
`ec2:DescribeInstances`. TT resolves the instance using
|
|
43
|
+
[AWS DescribeInstances](https://docs.aws.amazon.com/cli/latest/reference/ec2/describe-instances.html).
|
|
44
|
+
An AWS profile authorizes the lookup; SSH access to the machine is also required.
|
|
45
|
+
The selected instance must already be running in the selected region and have
|
|
46
|
+
enough GPU memory and free disk for the model, runtime, inputs and outputs.
|
|
47
|
+
|
|
48
|
+
Verify the instance's SSH host key for the selected IP address and connect
|
|
49
|
+
once yourself before using TT. TT requires a known host key and noninteractive
|
|
50
|
+
SSH authentication. Omit
|
|
51
|
+
`identityFile` to use your SSH agent or SSH configuration. Relative identity
|
|
52
|
+
file paths resolve beside the runner config; `~` expands locally. Set
|
|
53
|
+
`privateIp: true` when connected to the instance's VPC through a VPN or other
|
|
54
|
+
private network. The default uses its public IPv4 address. Security groups and
|
|
55
|
+
network routes must allow the laptop to reach that address over SSH.
|
|
56
|
+
TT addresses the instance by IP, so any `Host` rules in your SSH configuration
|
|
57
|
+
must match that IP. Remote Python commands run through `bash -lc`, which loads
|
|
58
|
+
the login profile so a normal uv installation can be found.
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
# Adapter workflow: fetch the base model locally first.
|
|
62
|
+
tt validate tunedtensor.json
|
|
63
|
+
tt models prefetch tunedtensor.json --config local-runner.json
|
|
64
|
+
tt doctor tunedtensor.json --config local-runner.json
|
|
65
|
+
tt pipeline run --spec tunedtensor.json --config local-runner.json --dry-run
|
|
66
|
+
tt pipeline run --spec tunedtensor.json --config local-runner.json
|
|
67
|
+
|
|
68
|
+
# Foundation workflow: no base-model prefetch is needed.
|
|
69
|
+
tt doctor tunedtensor.json --config local-runner.json
|
|
70
|
+
tt pipeline run --spec tunedtensor.json --config local-runner.json \
|
|
71
|
+
--resume /absolute/path/to/foundation-run
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
Choose the command block for your spec's engine. `tt doctor` connects to the
|
|
75
|
+
instance and prepares/probes its locked Python/CUDA runtime, which may take
|
|
76
|
+
time on first use. It does not start training or reserve GPU capacity.
|
|
77
|
+
`tt hardware` reports the laptop's hardware; it does not inspect the AWS
|
|
78
|
+
instance. `tt pipeline run` discovers `local-runner.json` beside the selected
|
|
79
|
+
spec when `--config` is omitted.
|
|
80
|
+
|
|
81
|
+
Keep pipeline step targets set to `local`: the target describes the
|
|
82
|
+
orchestrator, and `gpu` selects where its GPU processes execute. CPU adapter
|
|
83
|
+
evaluation (`evaluation.inference.device: "cpu"`) stays on the laptop.
|
|
84
|
+
Remove `gpu` to execute everything locally. `--dry-run` or `dryRun: true`
|
|
85
|
+
does not connect to AWS or copy files.
|
|
86
|
+
|
|
87
|
+
TT copies only each process's declared data, model snapshot, and runtime files,
|
|
88
|
+
not the workspace or laptop credentials. Adapter runs use an immutable local
|
|
89
|
+
model snapshot; the Hugging Face credential/cache root is never uploaded.
|
|
90
|
+
GPU processes inherit the remote machine's environment plus a small set of
|
|
91
|
+
explicit runtime settings. Gated-model authentication happens during local
|
|
92
|
+
prefetch. Logs stream to the laptop; models and foundation checkpoints are
|
|
93
|
+
downloaded when the process ends, before temporary remote files are removed.
|
|
94
|
+
Staging paths are stable for checkpoint resume and reserved exclusively; an
|
|
95
|
+
existing directory blocks a new process until its previous work or artifacts
|
|
96
|
+
are recovered. Inputs and runtime files are staged for each process, so large
|
|
97
|
+
models incur transfer time and require space on both
|
|
98
|
+
machines, including the instance's `/tmp`. Serving remains local in this version.
|
|
99
|
+
|
|
100
|
+
Foundation `checkpoint_backup_dir` is also a local path that TT stages remotely.
|
|
101
|
+
Keep it outside the foundation run directory; overlapping input, output,
|
|
102
|
+
recovery or backup paths are rejected before transfer.
|
|
103
|
+
During pretraining, both rolling checkpoints and this backup are written under
|
|
104
|
+
the instance's staging directory. They return to the laptop only after the
|
|
105
|
+
process ends or is cancelled; there is no continuous backup to the laptop or
|
|
106
|
+
to a separate remote disk. Protect and recover that staging directory if the
|
|
107
|
+
connection fails. See [long foundation runs](foundation-long-runs.md) for the
|
|
108
|
+
checkpoint contents and resume contract.
|
|
109
|
+
|
|
110
|
+
Cancellation signals the remote process group and retrieves available
|
|
111
|
+
outputs while SSH remains reachable. If SSH or artifact retrieval fails, the
|
|
112
|
+
error includes the remote staging directory for recovery; keep it until you have recovered
|
|
113
|
+
your files. A remote timeout (24 hours by default, configurable from 60 seconds
|
|
114
|
+
to 7 days per process) limits work after a lost connection or laptop failure.
|
|
115
|
+
Adapter evaluation also retains `evaluation.timeoutMs` (30 minutes by default),
|
|
116
|
+
and individual transfer/setup commands have a 30-minute timeout. Increasing
|
|
117
|
+
`gpu.maxSeconds` does not extend those limits.
|
|
118
|
+
The pipeline needs the laptop to stay running; it is not a detached scheduler.
|
|
119
|
+
Foundation pretraining can resume from returned checkpoints using its existing
|
|
120
|
+
`--resume` command. Use the same laptop run directory and unchanged spec/data
|
|
121
|
+
so checkpoint identities still match. Adapter training keeps its existing
|
|
122
|
+
restart behavior.
|
|
123
|
+
|
|
124
|
+
You provide capacity and pay AWS directly. TT does not create, start, stop,
|
|
125
|
+
terminate or resize instances, request quotas, choose fallback instances or
|
|
126
|
+
fall back to another account. An AWS profile alone cannot create capacity.
|
|
127
|
+
The instance remains running after the job; its lifecycle is yours to manage.
|
|
128
|
+
|
|
129
|
+
Hosted training submission (`tt cloud runs start` and `estimate`) is retired.
|
|
130
|
+
Existing cloud reports and cancellation of older hosted jobs remain available.
|
|
131
|
+
This CLI change does not delete deployed AWS infrastructure or historical data.
|
|
@@ -4,6 +4,10 @@ The foundation engine can run recoverable, single-GPU pretraining from `.txt`
|
|
|
4
4
|
files or JSONL shards whose rows contain a string `text` field. Keep training
|
|
5
5
|
and validation data in different files or directories.
|
|
6
6
|
|
|
7
|
+
The commands below also support an [AWS GPU](aws-gpu.md) configured in the
|
|
8
|
+
adjacent `local-runner.json`. The CLI and tokenizer still run locally; the
|
|
9
|
+
local machine must stay awake and connected throughout the pipeline.
|
|
10
|
+
|
|
7
11
|
## Spec
|
|
8
12
|
|
|
9
13
|
The long-run fields live under `foundation` in `tunedtensor.json`:
|
|
@@ -69,11 +73,22 @@ SIGINT or SIGTERM requests a step-boundary checkpoint. The CLI allows up to two
|
|
|
69
73
|
minutes for that checkpoint before forcibly stopping the process. SIGKILL or a
|
|
70
74
|
power loss falls back to the most recent periodic checkpoint.
|
|
71
75
|
|
|
76
|
+
With AWS execution, checkpoints remain in remote staging during pretraining
|
|
77
|
+
and are downloaded when the process ends or cancellation succeeds. The
|
|
78
|
+
configured `checkpoint_backup_dir` is mapped into that same remote staging
|
|
79
|
+
tree, then copied back to its local destination; it is not a live backup to
|
|
80
|
+
another disk or to the laptop. Keep the backup directory outside the run
|
|
81
|
+
directory; AWS execution rejects overlapping paths. An interrupted connection
|
|
82
|
+
may need manual recovery from the remote path printed in the error before
|
|
83
|
+
`--resume` can run.
|
|
84
|
+
Set `gpu.maxSeconds` to cover the intended pretrain stage (default 24 hours,
|
|
85
|
+
maximum 7 days); reaching the deadline ends that process.
|
|
86
|
+
|
|
72
87
|
## User service
|
|
73
88
|
|
|
74
|
-
For unattended operation
|
|
75
|
-
service. Replace every path with an absolute path
|
|
76
|
-
path reported by `command -v tt`:
|
|
89
|
+
For unattended operation on a Linux machine running the CLI, run the resumable
|
|
90
|
+
command under a user systemd service. Replace every path with an absolute path
|
|
91
|
+
and use the installed `tt` path reported by `command -v tt`:
|
|
77
92
|
|
|
78
93
|
```ini
|
|
79
94
|
[Unit]
|
|
@@ -102,7 +117,8 @@ journalctl --user -fu tt-foundation.service
|
|
|
102
117
|
```
|
|
103
118
|
|
|
104
119
|
Enable user lingering if the job must survive logout. Use a UPS for power-loss
|
|
105
|
-
protection,
|
|
106
|
-
remote mounted filesystem.
|
|
120
|
+
protection. With a local GPU, put `checkpoint_backup_dir` on a different
|
|
121
|
+
physical disk or remote mounted filesystem. AWS staging does not provide that
|
|
122
|
+
separate-disk guarantee. Before a multi-day run, complete a full-load soak,
|
|
107
123
|
stop the service during pretraining, and verify the journal reports a resumed
|
|
108
124
|
checkpoint after restart.
|
|
@@ -13,11 +13,21 @@ not mutate the training runtime.
|
|
|
13
13
|
serving is not supported by this runtime; CLI/package commands still work
|
|
14
14
|
there. Unsupported hardware or model/adapter combinations fail rather than
|
|
15
15
|
silently falling back to the old server.
|
|
16
|
+
- `gpu` in `local-runner.json` applies to training and evaluation. It does not
|
|
17
|
+
move `tt serve` to AWS; returned artifacts still need a Linux/CUDA machine
|
|
18
|
+
running the serving command.
|
|
16
19
|
- Install `uv` and prefetch the certified model with `tt models prefetch`.
|
|
17
20
|
First launch installs the locked serving dependencies and may compile kernels;
|
|
18
21
|
it needs substantially more time and disk than a warm request. CUDA JIT may
|
|
19
22
|
require a working CUDA toolkit/C++ toolchain. Model loading remains offline.
|
|
20
|
-
- Foundation
|
|
23
|
+
- Foundation model directories are supported through a temporary GPT-2 export;
|
|
24
|
+
pass `tt serve foundation --checkpoint <model-dir> --tokenizer <tokenizer.json>`.
|
|
25
|
+
The tokenizer must be the one used during training. Export checks vocabulary
|
|
26
|
+
size and required special tokens, not tokenizer identity: a different tokenizer
|
|
27
|
+
with the same vocabulary size can pass these checks and produce incorrect text.
|
|
28
|
+
Keep each model paired with its original training tokenizer. Context defaults to
|
|
29
|
+
the checkpoint limit. Text completions and chat are supported; foundation tool-call
|
|
30
|
+
parsing and image inputs are not supported.
|
|
21
31
|
- Qwen3.5-2B base and a one-step, rank-16 `all-linear` TT-shaped adapter have been
|
|
22
32
|
exercised on GB10. The registry's Nemotron and Muse Glimmer architectures have
|
|
23
33
|
upstream support, but their exact adapters/hardware combinations are **not
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{
|
|
2
|
+
"artifactRoot": ".tuned-tensor/artifacts",
|
|
3
|
+
"storeRoot": ".tuned-tensor/store",
|
|
4
|
+
"gpu": {
|
|
5
|
+
"provider": "aws",
|
|
6
|
+
"profile": "research",
|
|
7
|
+
"region": "eu-west-1",
|
|
8
|
+
"instanceId": "i-0123456789abcdef0",
|
|
9
|
+
"user": "ubuntu",
|
|
10
|
+
"identityFile": "~/.ssh/research-gpu.pem",
|
|
11
|
+
"maxSeconds": 86400
|
|
12
|
+
}
|
|
13
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tuned-tensor/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.16.0",
|
|
4
4
|
"description": "Local TT agent and CUDA fine-tuning CLI for Tuned Tensor workflows",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -13,12 +13,15 @@
|
|
|
13
13
|
"scripts": {
|
|
14
14
|
"build": "tsup",
|
|
15
15
|
"dev": "tsup --watch",
|
|
16
|
-
"typecheck": "tsc --noEmit",
|
|
16
|
+
"typecheck": "tsc --noEmit && tsc --noEmit -p tsconfig.scripts.json",
|
|
17
17
|
"test": "npm run test:cli && npm run test:local-runtime && npm run test:python",
|
|
18
18
|
"test:cli": "vitest run",
|
|
19
|
+
"test:workflows": "vitest run src/__tests__/workflows",
|
|
20
|
+
"eval:agent": "tsx scripts/eval-agent.ts",
|
|
19
21
|
"test:local-runtime": "node --import tsx --test \"test/local-runtime/**/*.test.ts\"",
|
|
20
|
-
"test:python": "python3 -m unittest discover -s training/adapter/tests -p 'test_*.py' && uv run --frozen --project training/foundation python -m unittest discover -s training/foundation/tests -p 'test_*.py'",
|
|
21
|
-
"test:watch": "vitest"
|
|
22
|
+
"test:python": "python3 -m unittest discover -s training/adapter/tests -p 'test_*.py' && uv run --frozen --project training/foundation --group test python -m unittest discover -s training/foundation/tests -p 'test_*.py'",
|
|
23
|
+
"test:watch": "vitest",
|
|
24
|
+
"check": "npm run typecheck && npm test && npm run build"
|
|
22
25
|
},
|
|
23
26
|
"files": [
|
|
24
27
|
"dist",
|
|
@@ -50,9 +53,9 @@
|
|
|
50
53
|
"@earendil-works/pi-agent-core": "0.84.1",
|
|
51
54
|
"@earendil-works/pi-coding-agent": "0.84.1",
|
|
52
55
|
"@tuned-tensor/pipeline-contract": "1.1.0",
|
|
53
|
-
"chalk": "^
|
|
56
|
+
"chalk": "^6.0.0",
|
|
54
57
|
"cli-table3": "^0.6.5",
|
|
55
|
-
"commander": "^
|
|
58
|
+
"commander": "^15.0.0",
|
|
56
59
|
"open": "^10.1.0",
|
|
57
60
|
"ora": "^9.3.0",
|
|
58
61
|
"typebox": "1.3.7",
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
"""Export TT FoundationGPT weights to vLLM's existing GPT-2 implementation."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
import json
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
|
|
7
|
+
CHAT_TEMPLATE = """{%- if not messages or messages[0]['role'] != 'system' -%}
|
|
8
|
+
{{- '<|system|>You are a helpful assistant.<|end|>' -}}
|
|
9
|
+
{%- endif -%}
|
|
10
|
+
{%- for message in messages -%}
|
|
11
|
+
{%- set content = message['content'] -%}
|
|
12
|
+
{%- if message['role'] == 'system' -%}
|
|
13
|
+
{%- set content = (content | trim) or 'You are a helpful assistant.' -%}
|
|
14
|
+
{%- endif -%}
|
|
15
|
+
{%- if message['role'] not in ['system', 'user', 'assistant'] -%}
|
|
16
|
+
{{- raise_exception('Foundation chat supports system, user and assistant messages only.') -}}
|
|
17
|
+
{%- endif -%}
|
|
18
|
+
{{- '<|' + message['role'] + '|>' + content + '<|end|>' -}}
|
|
19
|
+
{%- endfor -%}
|
|
20
|
+
{%- if add_generation_prompt -%}{{- '<|assistant|>' -}}{%- endif -%}"""
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def export_foundation(checkpoint: str, tokenizer_path: str, destination: Path) -> str:
|
|
24
|
+
import torch
|
|
25
|
+
from safetensors.torch import load_file, save_file
|
|
26
|
+
from tokenizers import Tokenizer
|
|
27
|
+
from transformers import GPT2Config, PreTrainedTokenizerFast
|
|
28
|
+
|
|
29
|
+
source = Path(checkpoint)
|
|
30
|
+
config = json.loads((source / 'config.json').read_text())
|
|
31
|
+
for key in ('depth', 'width', 'heads', 'vocab_size', 'sequence_length'):
|
|
32
|
+
if type(config.get(key)) is not int or config[key] <= 0:
|
|
33
|
+
raise ValueError(f'Invalid foundation config: {key}')
|
|
34
|
+
if config['width'] % config['heads']:
|
|
35
|
+
raise ValueError('Foundation width must be divisible by heads.')
|
|
36
|
+
raw_tokenizer = Tokenizer.from_file(tokenizer_path)
|
|
37
|
+
special_tokens = ['<unk>', '<pad>', '<|system|>', '<|user|>', '<|assistant|>', '<|end|>']
|
|
38
|
+
if any(raw_tokenizer.token_to_id(token) is None for token in special_tokens):
|
|
39
|
+
raise ValueError('Foundation tokenizer is missing required special tokens.')
|
|
40
|
+
tokenizer = PreTrainedTokenizerFast(tokenizer_object=raw_tokenizer,
|
|
41
|
+
unk_token='<unk>', pad_token='<pad>', eos_token='<|end|>',
|
|
42
|
+
additional_special_tokens=['<|system|>', '<|user|>', '<|assistant|>'],
|
|
43
|
+
model_max_length=config['sequence_length'], chat_template=CHAT_TEMPLATE)
|
|
44
|
+
if len(tokenizer) != config['vocab_size']:
|
|
45
|
+
raise ValueError('Foundation tokenizer vocabulary does not match the checkpoint.')
|
|
46
|
+
destination.mkdir(parents=True, exist_ok=True)
|
|
47
|
+
tokenizer.save_pretrained(destination)
|
|
48
|
+
GPT2Config(n_layer=config['depth'], n_embd=config['width'], n_head=config['heads'],
|
|
49
|
+
n_positions=config['sequence_length'], vocab_size=config['vocab_size'],
|
|
50
|
+
activation_function='gelu', layer_norm_epsilon=1e-5,
|
|
51
|
+
resid_pdrop=0.0, embd_pdrop=0.0, attn_pdrop=0.0,
|
|
52
|
+
bos_token_id=None, eos_token_id=tokenizer.eos_token_id,
|
|
53
|
+
pad_token_id=tokenizer.pad_token_id, architectures=['GPT2LMHeadModel'],
|
|
54
|
+
tie_word_embeddings=True).save_pretrained(destination)
|
|
55
|
+
weights = load_file(str(source / 'model.safetensors'))
|
|
56
|
+
exported = {}
|
|
57
|
+
def move(old, new, transpose=False):
|
|
58
|
+
tensor = weights.pop(old)
|
|
59
|
+
exported[new] = tensor.t().contiguous() if transpose else tensor
|
|
60
|
+
move('tok_emb.weight', 'transformer.wte.weight')
|
|
61
|
+
move('pos_emb.weight', 'transformer.wpe.weight')
|
|
62
|
+
head = weights.pop('lm_head.weight')
|
|
63
|
+
if not torch.equal(head, exported['transformer.wte.weight']):
|
|
64
|
+
raise ValueError('Foundation checkpoint must have tied embedding weights.')
|
|
65
|
+
for suffix in ('weight', 'bias'):
|
|
66
|
+
move('ln_f.' + suffix, 'transformer.ln_f.' + suffix)
|
|
67
|
+
for i in range(config['depth']):
|
|
68
|
+
old, new = f'blocks.{i}.', f'transformer.h.{i}.'
|
|
69
|
+
for src, dst in [('ln1', 'ln_1'), ('ln2', 'ln_2'),
|
|
70
|
+
('mlp.0', 'mlp.c_fc'), ('mlp.2', 'mlp.c_proj')]:
|
|
71
|
+
for suffix in ('weight', 'bias'):
|
|
72
|
+
move(old + src + '.' + suffix, new + dst + '.' + suffix,
|
|
73
|
+
transpose=src.startswith('mlp') and suffix == 'weight')
|
|
74
|
+
for src, dst in [('attn.qkv', 'attn.c_attn'), ('attn.proj', 'attn.c_proj')]:
|
|
75
|
+
move(old + src + '.weight', new + dst + '.weight', transpose=True)
|
|
76
|
+
weight = exported[new + dst + '.weight']
|
|
77
|
+
exported[new + dst + '.bias'] = torch.zeros(weight.shape[1], dtype=weight.dtype)
|
|
78
|
+
if weights:
|
|
79
|
+
raise ValueError(f'Unexpected foundation weights: {sorted(weights)}')
|
|
80
|
+
save_file(exported, str(destination / 'model.safetensors'), metadata={'format': 'pt'})
|
|
81
|
+
return str(destination)
|
|
@@ -64,9 +64,10 @@ def build_vllm_args(model_source: str, adapter_path: str | None, temp_dir: Path)
|
|
|
64
64
|
"Qwen/Qwen3.5-2B": "qwen3_xml",
|
|
65
65
|
"nvidia/NVIDIA-Nemotron-3.5-Lightning-30B-A3B-BF16": "qwen3_coder",
|
|
66
66
|
"meta-models/Muse-Glimmer-30B": "muse_glimmer",
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
|
|
67
|
+
}.get(base)
|
|
68
|
+
if parser:
|
|
69
|
+
args += ["--enable-auto-tool-choice", "--tool-call-parser", parser]
|
|
70
|
+
if parser and base != "meta-models/Muse-Glimmer-30B":
|
|
70
71
|
args += ["--default-chat-template-kwargs", '{"enable_thinking":false}']
|
|
71
72
|
if adapter_path:
|
|
72
73
|
config = json.loads((Path(adapter_path) / "adapter_config.json").read_text())
|
|
@@ -124,11 +125,17 @@ def run_server() -> None:
|
|
|
124
125
|
family = socket.AF_INET6 if ":" in host else socket.AF_INET
|
|
125
126
|
with socket.socket(family, socket.SOCK_STREAM) as probe:
|
|
126
127
|
probe.bind((host, port))
|
|
127
|
-
model_source = prepare_model_source()
|
|
128
128
|
if os.environ.get("TT_API_KEY"):
|
|
129
129
|
os.environ["VLLM_API_KEY"] = os.environ["TT_API_KEY"]
|
|
130
130
|
with TemporaryDirectory(prefix="tt-serve-") as temp:
|
|
131
|
-
|
|
131
|
+
if os.environ.get("TT_FOUNDATION_CHECKPOINT"):
|
|
132
|
+
from foundation_export import export_foundation
|
|
133
|
+
model_source = export_foundation(os.environ["TT_FOUNDATION_CHECKPOINT"],
|
|
134
|
+
os.environ["TT_FOUNDATION_TOKENIZER"], Path(temp) / "foundation")
|
|
135
|
+
adapter = None
|
|
136
|
+
else:
|
|
137
|
+
model_source = prepare_model_source()
|
|
138
|
+
adapter = resolve_adapter_path(os.environ.get("TT_MODEL_ARTIFACT"), Path(temp))
|
|
132
139
|
args = build_vllm_args(model_source, adapter, Path(temp))
|
|
133
140
|
from vllm.entrypoints.cli.main import main
|
|
134
141
|
previous = sys.argv
|
|
@@ -282,6 +282,10 @@ def run_training(rows: list[dict[str, Any]], model_source: str) -> tuple[dict[st
|
|
|
282
282
|
)
|
|
283
283
|
result = trainer.train()
|
|
284
284
|
|
|
285
|
+
# Saved adapters must remain loadable after a local or remote staging directory is removed.
|
|
286
|
+
for adapter_config in model.peft_config.values():
|
|
287
|
+
adapter_config.base_model_name_or_path = str(hp("base_model", CERTIFIED_BASE_MODEL))
|
|
288
|
+
adapter_config.revision = hp("base_model_revision")
|
|
285
289
|
model.save_pretrained(MODEL_DIR)
|
|
286
290
|
tokenizer.save_pretrained(MODEL_DIR)
|
|
287
291
|
return result.metrics, dtype
|
|
@@ -10,6 +10,10 @@ dependencies = [
|
|
|
10
10
|
"torch>=2.11.0",
|
|
11
11
|
]
|
|
12
12
|
|
|
13
|
+
[dependency-groups]
|
|
14
|
+
# Match the pinned serving runtime for CPU export parity tests.
|
|
15
|
+
test = ["transformers==5.16.1"]
|
|
16
|
+
|
|
13
17
|
[tool.uv.sources]
|
|
14
18
|
torch = [
|
|
15
19
|
{ index = "pytorch-cu130", marker = "sys_platform == 'linux'" },
|
|
@@ -132,6 +132,8 @@ def generate(
|
|
|
132
132
|
def save_model(model: FoundationGPT, directory: str | Path) -> None:
|
|
133
133
|
destination = ensure_private_directory(directory)
|
|
134
134
|
tensors = {key: value.detach().cpu().contiguous() for key, value in model.state_dict().items()}
|
|
135
|
+
# CPU tensors can still alias the tied embedding after .cpu().
|
|
136
|
+
tensors["lm_head.weight"] = tensors["lm_head.weight"].clone()
|
|
135
137
|
model_path = destination / "model.safetensors"
|
|
136
138
|
save_file(tensors, str(model_path))
|
|
137
139
|
make_private_file(model_path)
|