Skip to content
Open
Show file tree
Hide file tree
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 Aug 4, 2026
f8d07a9
feat: serve multiple Tinker checkpoints privately on Modal
lluisinthedesert Aug 4, 2026
618a894
fix: enforce Modal proxy auth on Tinker serving
lluisinthedesert Aug 4, 2026
d58ca88
fix: pin Tinker serving to training versions
lluisinthedesert Aug 4, 2026
1fea71a
fix: preserve Tinker tool-call roundtrips
lluisinthedesert Aug 4, 2026
e5a69ae
fix: preserve deterministic sampler error semantics
lluisinthedesert Aug 4, 2026
5302120
ops: isolate Cedar seed37 checkpoint serving
lluisinthedesert Aug 4, 2026
92d8785
ops: surface bounded Tinker shim diagnostics
lluisinthedesert Aug 4, 2026
0f9a80d
fix: accept OpenAI text content arrays in Tinker shim
lluisinthedesert Aug 4, 2026
8fe682a
fix: emit complete OpenAI chat completion shape
lluisinthedesert Aug 4, 2026
2d85442
test: require complete token usage shape
lluisinthedesert Aug 4, 2026
4249551
Fail closed on malformed shim requests
lluisinthedesert Aug 4, 2026
917e7e1
Harden Tinker serving contract
lluisinthedesert Aug 4, 2026
c111e3a
Allow isolated Tinker serving deployments
lluisinthedesert Aug 4, 2026
73680ae
test: lock private Tinker shim safety invariants
lluisinthedesert Aug 5, 2026
df42aad
feat: isolate private checkpoint registry secrets
lluisinthedesert Aug 5, 2026
c424d4b
docs: explain private registry secret deployment
lluisinthedesert Aug 5, 2026
a4576ce
fix: make Modal secret dependencies deterministic
lluisinthedesert Aug 5, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions docs/adapter-serving-compatibility.md
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.
111 changes: 111 additions & 0 deletions scripts/adapter-serving-compat.py
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
Comment on lines +61 to +77

Copy link
Copy Markdown
Contributor

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: at scripts/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). In assess, the if wildcard: branch at line 61 is evaluated before the if 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…" with unsupported_target_modules=["missing_or_invalid"]. The intended branch at lines 71-77 is unreachable for those inputs. Both outcomes are incompatible, so the exit code is right, but the recorded reason is wrong.

Fix by returning None for the wildcard in the invalid case, or checking not targets before the wildcard branch.

Suggested change
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
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
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
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

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())
52 changes: 52 additions & 0 deletions scripts/adapter_serving_compat_test.py
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()
100 changes: 100 additions & 0 deletions scripts/modal-tinker-openai-shim.py
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"},
)
Loading