-
Notifications
You must be signed in to change notification settings - Fork 7
Add faithful private Tinker checkpoint serving #464
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
lluisinthedesert
wants to merge
18
commits into
main
Choose a base branch
from
yolo/cedar-seed37-serving
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
18 commits
Select commit
Hold shift + click to select a range
73c2b63
feat: add faithful Tinker serving compatibility gate
lluisinthedesert f8d07a9
feat: serve multiple Tinker checkpoints privately on Modal
lluisinthedesert 618a894
fix: enforce Modal proxy auth on Tinker serving
lluisinthedesert d58ca88
fix: pin Tinker serving to training versions
lluisinthedesert 1fea71a
fix: preserve Tinker tool-call roundtrips
lluisinthedesert e5a69ae
fix: preserve deterministic sampler error semantics
lluisinthedesert 5302120
ops: isolate Cedar seed37 checkpoint serving
lluisinthedesert 92d8785
ops: surface bounded Tinker shim diagnostics
lluisinthedesert 0f9a80d
fix: accept OpenAI text content arrays in Tinker shim
lluisinthedesert 8fe682a
fix: emit complete OpenAI chat completion shape
lluisinthedesert 2d85442
test: require complete token usage shape
lluisinthedesert 4249551
Fail closed on malformed shim requests
lluisinthedesert 917e7e1
Harden Tinker serving contract
lluisinthedesert c111e3a
Allow isolated Tinker serving deployments
lluisinthedesert 73680ae
test: lock private Tinker shim safety invariants
lluisinthedesert df42aad
feat: isolate private checkpoint registry secrets
lluisinthedesert c424d4b
docs: explain private registry secret deployment
lluisinthedesert a4576ce
fix: make Modal secret dependencies deterministic
lluisinthedesert File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,24 @@ | ||
| # Adapter serving compatibility is a pre-training gate | ||
|
|
||
| An exported LoRA file is not proof that a serving runtime can reproduce the | ||
| trained model. Training and evaluation plans must name the intended serving | ||
| runtime and run `scripts/adapter-serving-compat.py` before paid training and | ||
| again against the frozen adapter receipt before evaluation. | ||
|
|
||
| For Nemotron-H, a Tinker adapter trained with `target_modules: "all-linear"` | ||
| is not faithfully portable to vLLM. It contains separate Mamba projections and | ||
| routed-MoE factors that the vLLM Nemotron-H LoRA surface cannot represent. | ||
| Dropping or remapping those weights may test plumbing, but the result is a | ||
| different model and must never support a quality claim. | ||
|
|
||
| Use one of two truthful paths: | ||
|
|
||
| 1. Serve an existing all-linear checkpoint through Tinker's native sampling | ||
| client and put the authenticated OpenAI-compatible shim behind Understudy | ||
| Gateway. | ||
| 2. If vLLM deployment is required, constrain training targets up front to the | ||
| supported projection set and hash-bind that choice into the training | ||
| manifest. | ||
|
|
||
| The preflight emits a JSON receipt and exits non-zero for incompatible or | ||
| unknown combinations. Unknown is deliberately not equivalent to compatible. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,111 @@ | ||
| #!/usr/bin/env python3 | ||
| """Fail-closed LoRA training/serving compatibility preflight.""" | ||
| from __future__ import annotations | ||
|
|
||
| import argparse | ||
| import hashlib | ||
| import json | ||
| from pathlib import Path | ||
|
|
||
| NEMOTRON_H_MARKERS = ("NVIDIA-Nemotron-3-Nano", "Nemotron-3-Nano") | ||
| VLLM_NEMOTRON_H_TARGETS = frozenset({ | ||
| "q_proj", "k_proj", "v_proj", "o_proj", "out_proj", | ||
| "up_proj", "down_proj", "lm_head", | ||
| }) | ||
|
|
||
|
|
||
| def _sha256(path: Path) -> str: | ||
| digest = hashlib.sha256() | ||
| with path.open("rb") as stream: | ||
| for chunk in iter(lambda: stream.read(1024 * 1024), b""): | ||
| digest.update(chunk) | ||
| return digest.hexdigest() | ||
|
|
||
|
|
||
| def _normalize_targets(value: object) -> tuple[list[str], str | None]: | ||
| if isinstance(value, str): | ||
| return [value], value if value in {"all-linear", "all_linear"} else None | ||
| if isinstance(value, list) and all(isinstance(item, str) for item in value): | ||
| targets = sorted(set(value)) | ||
| wildcard = next((item for item in targets if item in {"all-linear", "all_linear"}), None) | ||
| return targets, wildcard | ||
| return [], None | ||
|
|
||
|
|
||
| def assess(config_path: Path, runtime: str, base_model: str) -> dict: | ||
| config = json.loads(config_path.read_text(encoding="utf-8")) | ||
| targets, wildcard = _normalize_targets(config.get("target_modules")) | ||
| receipt = { | ||
| "schema_version": 1, | ||
| "adapter_config": str(config_path), | ||
| "adapter_config_sha256": _sha256(config_path), | ||
| "runtime": runtime, | ||
| "base_model": base_model, | ||
| "training_target_modules": targets, | ||
| "compatibility": "unknown", | ||
| "faithful": False, | ||
| "unsupported_target_modules": [], | ||
| "reason": "unsupported runtime or model; no faithful compatibility claim is available", | ||
| } | ||
| if runtime == "tinker-sampling": | ||
| receipt.update( | ||
| compatibility="faithful", | ||
| faithful=True, | ||
| reason="Tinker sampling serves the checkpoint through its native trained-weight path", | ||
| ) | ||
| return receipt | ||
|
|
||
| is_nemotron_h = any(marker in base_model for marker in NEMOTRON_H_MARKERS) | ||
| if runtime != "vllm-nemotron-h" or not is_nemotron_h: | ||
| return receipt | ||
| if wildcard: | ||
| receipt.update( | ||
| compatibility="incompatible", | ||
| reason=( | ||
| "wildcard all-linear training includes Nemotron-H Mamba and routed-MoE " | ||
| "targets that vLLM cannot faithfully represent" | ||
| ), | ||
| unsupported_target_modules=[wildcard], | ||
| ) | ||
| return receipt | ||
| if not targets: | ||
| receipt.update( | ||
| compatibility="incompatible", | ||
| reason="adapter target_modules is missing or invalid; compatibility must fail closed", | ||
| unsupported_target_modules=["missing_or_invalid"], | ||
| ) | ||
| return receipt | ||
| unsupported = sorted(set(targets) - VLLM_NEMOTRON_H_TARGETS) | ||
| if unsupported: | ||
| receipt.update( | ||
| compatibility="incompatible", | ||
| reason="one or more trained targets are outside the faithful vLLM Nemotron-H surface", | ||
| unsupported_target_modules=unsupported, | ||
| ) | ||
| return receipt | ||
| receipt.update( | ||
| compatibility="faithful", | ||
| faithful=True, | ||
| reason="all declared training targets are within the supported vLLM Nemotron-H surface", | ||
| ) | ||
| return receipt | ||
|
|
||
|
|
||
| def main() -> int: | ||
| parser = argparse.ArgumentParser() | ||
| parser.add_argument("--adapter-config", type=Path, required=True) | ||
| parser.add_argument("--runtime", choices=("tinker-sampling", "vllm-nemotron-h"), required=True) | ||
| parser.add_argument("--base-model", required=True) | ||
| parser.add_argument("--receipt", type=Path) | ||
| args = parser.parse_args() | ||
| result = assess(args.adapter_config, args.runtime, args.base_model) | ||
| encoded = json.dumps(result, indent=2, sort_keys=True) + "\n" | ||
| if args.receipt: | ||
| args.receipt.parent.mkdir(parents=True, exist_ok=True) | ||
| args.receipt.write_text(encoded, encoding="utf-8") | ||
| print(encoded, end="") | ||
| return 0 if result["faithful"] else 2 | ||
|
|
||
|
|
||
| if __name__ == "__main__": | ||
| raise SystemExit(main()) | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,52 @@ | ||
| #!/usr/bin/env python3 | ||
| """Provider-free regression tests for adapter-serving-compat.py.""" | ||
| from __future__ import annotations | ||
|
|
||
| import importlib.util | ||
| import json | ||
| import tempfile | ||
| from pathlib import Path | ||
|
|
||
| SCRIPT = Path(__file__).with_name("adapter-serving-compat.py") | ||
| SPEC = importlib.util.spec_from_file_location("adapter_serving_compat", SCRIPT) | ||
| MODULE = importlib.util.module_from_spec(SPEC) | ||
| assert SPEC.loader | ||
| SPEC.loader.exec_module(MODULE) | ||
| MODEL = "nvidia/NVIDIA-Nemotron-3-Nano-30B-A3B-BF16" | ||
|
|
||
|
|
||
| def config(root: Path, targets: object) -> Path: | ||
| path = root / "adapter_config.json" | ||
| path.write_text(json.dumps({"target_modules": targets}) + "\n", encoding="utf-8") | ||
| return path | ||
|
|
||
|
|
||
| def main() -> None: | ||
| with tempfile.TemporaryDirectory() as raw: | ||
| root = Path(raw) | ||
| all_linear = MODULE.assess(config(root, "all-linear"), "vllm-nemotron-h", MODEL) | ||
| assert all_linear["compatibility"] == "incompatible" | ||
| assert all_linear["faithful"] is False | ||
| supported = MODULE.assess( | ||
| config(root, ["q_proj", "k_proj", "v_proj", "o_proj", "up_proj", "down_proj"]), | ||
| "vllm-nemotron-h", MODEL, | ||
| ) | ||
| assert supported["compatibility"] == "faithful" | ||
| unsupported = MODULE.assess( | ||
| config(root, ["q_proj", "gate_proj", "experts.w1"]), "vllm-nemotron-h", MODEL | ||
| ) | ||
| assert unsupported["unsupported_target_modules"] == ["experts.w1", "gate_proj"] | ||
| native = MODULE.assess(config(root, "all-linear"), "tinker-sampling", MODEL) | ||
| assert native["compatibility"] == "faithful" | ||
| unknown = MODULE.assess(config(root, ["q_proj"]), "vllm-nemotron-h", "some/other-model") | ||
| assert unknown["compatibility"] == "unknown" | ||
| assert unknown["faithful"] is False | ||
| missing = MODULE.assess(config(root, None), "vllm-nemotron-h", MODEL) | ||
| assert missing["compatibility"] == "incompatible" | ||
| assert missing["unsupported_target_modules"] == ["missing_or_invalid"] | ||
| assert "missing or invalid" in missing["reason"] | ||
| print("ALL ADAPTER SERVING COMPAT TESTS PASSED") | ||
|
|
||
|
|
||
| if __name__ == "__main__": | ||
| main() |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,100 @@ | ||
| """Private multi-checkpoint Tinker sampling bridge for Understudy Gateway. | ||
|
|
||
| Deploy with a Modal secret containing TINKER_API_KEY and | ||
| TINKER_MODEL_REGISTRY_JSON. Set TINKER_SERVING_APP_NAME and | ||
| TINKER_SERVING_SECRET_NAME at deploy time to isolate checkpoint lineages. | ||
|
|
||
| For a new private checkpoint, reuse an existing API-key secret without reading | ||
| it back by setting TINKER_SERVING_API_SECRET_NAME, and place the checkpoint-only | ||
| registry in a second named secret selected with | ||
| TINKER_SERVING_REGISTRY_SECRET_NAME. Both dependencies are declared | ||
| unconditionally because Modal imports this module again in the remote runtime; | ||
| conditional Secret objects produce a local/remote dependency-count mismatch. | ||
| Modal proxy authentication is required before requests reach the shim. | ||
| """ | ||
| from __future__ import annotations | ||
|
|
||
| import json | ||
| import os | ||
| import subprocess | ||
| from pathlib import Path | ||
|
|
||
| import modal | ||
|
|
||
| APP_NAME = os.environ.get( | ||
| "TINKER_SERVING_APP_NAME", "understudy-tinker-cedar-seed37-serving" | ||
| ) | ||
| PORT = 8099 | ||
| SECRET_NAME = os.environ.get( | ||
| "TINKER_SERVING_SECRET_NAME", "understudy-tinker-serving-seed37" | ||
| ) | ||
| API_SECRET_NAME = os.environ.get("TINKER_SERVING_API_SECRET_NAME", SECRET_NAME) | ||
| REGISTRY_SECRET_NAME = os.environ.get( | ||
| "TINKER_SERVING_REGISTRY_SECRET_NAME", SECRET_NAME | ||
| ) | ||
| # Keep this list structurally identical in the deploy process and the remote | ||
| # container import. Values stay in Modal's encrypted secret plane. | ||
| runtime_secrets = [ | ||
| modal.Secret.from_name(API_SECRET_NAME), | ||
| modal.Secret.from_name(REGISTRY_SECRET_NAME), | ||
| ] | ||
| BASE_MODEL = "nvidia/NVIDIA-Nemotron-3-Nano-30B-A3B-BF16" | ||
| TINKER_VERSION = "0.24.0" | ||
| COOKBOOK_VERSION = "0.5.3" | ||
|
|
||
| app = modal.App(APP_NAME) | ||
| image = ( | ||
| modal.Image.debian_slim(python_version="3.11") | ||
| .apt_install("git") | ||
| .pip_install( | ||
| f"tinker=={TINKER_VERSION}", | ||
| f"tinker-cookbook=={COOKBOOK_VERSION}", | ||
| "httpx>=0.27,<1", | ||
| ) | ||
| .add_local_file("scripts/tinker-openai-shim.py", "/opt/understudy/tinker-openai-shim.py") | ||
| .add_local_file("scripts/tinker_openai_compat.py", "/opt/understudy/tinker_openai_compat.py") | ||
| .add_local_file("scripts/tinker_renderer_compat.py", "/opt/understudy/tinker_renderer_compat.py") | ||
| ) | ||
|
|
||
|
|
||
| @app.function( | ||
| image=image, | ||
| secrets=runtime_secrets, | ||
| timeout=60 * 60, | ||
| scaledown_window=300, | ||
| max_containers=4, | ||
| ) | ||
| @modal.concurrent(max_inputs=64) | ||
| @modal.web_server(PORT, startup_timeout=10 * 60, requires_proxy_auth=True) | ||
| def serve() -> None: | ||
| registry = json.loads( | ||
| os.environ.get("TINKER_MODEL_REGISTRY_JSON_OVERRIDE") | ||
| or os.environ["TINKER_MODEL_REGISTRY_JSON"] | ||
| ) | ||
| if not isinstance(registry, dict) or not registry: | ||
| raise RuntimeError("TINKER_MODEL_REGISTRY_JSON must be a non-empty object") | ||
| registry_path = Path("/tmp/tinker-model-registry.json") | ||
| registry_path.write_text(json.dumps(registry, sort_keys=True), encoding="utf-8") | ||
| registry_path.chmod(0o600) | ||
| subprocess.Popen( | ||
| [ | ||
| "python", | ||
| "/opt/understudy/tinker-openai-shim.py", | ||
| "--model-registry-file", | ||
| str(registry_path), | ||
| "--tokenizer-model", | ||
| BASE_MODEL, | ||
| "--renderer", | ||
| "nemotron3_disable_thinking", | ||
| "--host", | ||
| "0.0.0.0", | ||
| "--trusted-proxy-auth", | ||
| "--port", | ||
| str(PORT), | ||
| "--max-workers", | ||
| "64", | ||
| "--max-tokens", | ||
| "2048", | ||
| ], | ||
| env={**os.environ, "TINKER_TRUSTED_PROXY_AUTH": "modal"}, | ||
| ) |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🟡 Compatibility receipt blames a wildcard setting when the adapter config is actually missing or malformed
A configuration whose module list is absent or of the wrong type is described in the receipt as wildcard all-linear training (
if wildcard:atscripts/adapter-serving-compat.py:61) instead of as missing/invalid, so the emitted reason misstates why the check failed.Impact: Operators reading the failure receipt get a misleading explanation and may chase a training-target problem that does not exist.
Sentinel string is returned in the wildcard slot
_normalize_targets(scripts/adapter-serving-compat.py:25-32) returns([], "missing_or_invalid")for any non-str, non-list-of-str value (e.g.null, a dict, or a mixed list). Inassess, theif wildcard:branch at line 61 is evaluated before theif not targets:branch at line 71, so the truthy sentinel triggers the wildcard message: reason "wildcard all-linear training includes Nemotron-H Mamba and routed-MoE targets…" withunsupported_target_modules=["missing_or_invalid"]. The intended branch at lines 71-77 is unreachable for those inputs. Both outcomes areincompatible, so the exit code is right, but the recorded reason is wrong.Fix by returning
Nonefor the wildcard in the invalid case, or checkingnot targetsbefore the wildcard branch.Was this helpful? React with 👍 or 👎 to provide feedback.