426e9eeabd
Voice Workbench / headless workbench (mocked backends) (push) Has been cancelled
Voice Workbench / real acoustic lane (nightly, provisioned only) (push) Has been cancelled
ci / test (push) Has been cancelled
ci / lint-and-format (push) Has been cancelled
ci / build (push) Has been cancelled
ci / dev-startup (push) Has been cancelled
gitleaks / gitleaks (push) Has been cancelled
Markdown Links / Relative Markdown Links (push) Has been cancelled
Quality (Extended) / Homepage Build (PR smoke) (push) Has been cancelled
Quality (Extended) / Comment-only diff guard (push) Has been cancelled
Quality (Extended) / Format + Type Safety Ratchet (push) Has been cancelled
Quality (Extended) / Develop Gate (secret scan + UI determinism) (push) Has been cancelled
Quality (Extended) / Develop Gate (lint) (push) Has been cancelled
Chat shell gestures / Chat shell gesture + parity e2e (push) Has been cancelled
Cloud Gateway Discord / Test (push) Has been cancelled
Benchmark Bridge Tests / benchmark (bunx @biomejs/biome check packages/lifeops-bench/src, benchmark-lint) (push) Has been cancelled
Benchmark Bridge Tests / benchmark (bunx vitest run --config packages/lifeops-bench/vitest.config.ts --root packages/lifeops-bench --passWithNoTests, benchmark-tests) (push) Has been cancelled
Build Agent Image / build-and-push (push) Has been cancelled
Dev Smoke / bun run dev onboarding chat (push) Has been cancelled
Dev Smoke / Vite HMR dependency-level smoke (push) Has been cancelled
Electrobun Submodule Guard / electrobun gitlink is fetchable (push) Has been cancelled
Publish @elizaos/example-code / check_npm (push) Has been cancelled
Publish @elizaos/example-code / publish_npm (push) Has been cancelled
Publish @elizaos/plugin-elizacloud / verify_version (push) Has been cancelled
Publish @elizaos/plugin-elizacloud / publish_npm (push) Has been cancelled
Sandbox Live Smoke / Sandbox live smoke (push) Has been cancelled
Snap Build & Test / Build Snap (amd64) (push) Has been cancelled
Snap Build & Test / Build Snap (arm64) (push) Has been cancelled
Test Packaging / elizaos CLI global-install smoke (node + bun) (push) Has been cancelled
Cloud Gateway Webhook / Test (push) Has been cancelled
Cloud Tests / lint-and-types (push) Has been cancelled
Cloud Tests / unit-tests (push) Has been cancelled
Cloud Tests / integration-tests (push) Has been cancelled
Cloud Tests / e2e-tests (push) Has been cancelled
CodeQL Advanced / Analyze (javascript-typescript) (push) Has been cancelled
Deploy Apps Worker (Product 2) / Determine environment (push) Has been cancelled
Deploy Apps Worker (Product 2) / Deploy apps worker to apps-control host (${{ needs.determine-env.outputs.environment }}) (push) Has been cancelled
Deploy Eliza Provisioning Worker / Determine environment (push) Has been cancelled
Deploy Eliza Provisioning Worker / Deploy worker to Hetzner host (${{ needs.determine-env.outputs.environment }} @ ${{ needs.determine-env.outputs.deployment_sha }}) (push) Has been cancelled
Dev Smoke / Classify changed paths (push) Has been cancelled
supply-chain / sbom (push) Has been cancelled
supply-chain / vulnerability-scan (push) Has been cancelled
Build, Push & Deploy to Phala Cloud / build-and-push (push) Has been cancelled
Test Packaging / Validate Packaging Configs (push) Has been cancelled
Test Packaging / Build & Test PyPI Package (push) Has been cancelled
Test Packaging / PyPI on Python ${{ matrix.python }} (push) Has been cancelled
Test Packaging / Pack & Test JS Tarballs (push) Has been cancelled
UI Fixture E2E / ui-fixture-e2e (push) Has been cancelled
UI Fixture E2E / fixture-e2e (push) Has been cancelled
UI Story Gate / story-gate (push) Has been cancelled
vault-ci / test (macos-latest) (push) Has been cancelled
vault-ci / test (ubuntu-latest) (push) Has been cancelled
vault-ci / test (windows-latest) (push) Has been cancelled
vault-ci / app-core wiring tests (push) Has been cancelled
verify-patches / verify patches/CHECKSUMS.sha256 (push) Has been cancelled
Voice Benchmark Smoke / voice-emotion fixture smoke (push) Has been cancelled
Voice Benchmark Smoke / voiceagentbench fixture smoke (push) Has been cancelled
Voice Benchmark Smoke / voicebench-quality unit smoke (push) Has been cancelled
Voice Benchmark Smoke / voicebench TypeScript unit (no audio) (push) Has been cancelled
Voice Benchmark Smoke / voice bench smoke summary (push) Has been cancelled
Windows CI / windows ([bun run --cwd packages/app-core test bun run --cwd packages/elizaos test bun run --cwd packages/cloud/shared test], app-and-cli) (push) Has been cancelled
Windows CI / windows ([bun run --cwd packages/scenario-runner test bun run --cwd packages/vault test bun run --cwd packages/security test bun run --cwd plugins/plugin-coding-tools test], framework-packages) (push) Has been cancelled
Windows CI / windows ([bun run --cwd plugins/plugin-elizacloud test bun run --cwd plugins/plugin-discord test bun run --cwd plugins/plugin-anthropic test bun run --cwd plugins/plugin-openai test bun run --cwd plugins/plugin-app-control test bun run --cwd plugins/pl… (push) Has been cancelled
Windows CI / windows ([node packages/scripts/run-turbo.mjs run build --filter=@elizaos/core --filter=@elizaos/shared --filter=@elizaos/agent --concurrency=4 node packages/scripts/run-bash-linux-only.mjs scripts/verify-riscv64-buildpaths.sh node packages/scripts/run… (push) Has been cancelled
Windows CI / windows ([node packages/scripts/run-turbo.mjs run typecheck --filter=@elizaos/core --filter=@elizaos/shared --filter=@elizaos/cloud-shared --concurrency=4 bun run --cwd packages/core test bun run --cwd packages/shared test], core-runtime, 75) (push) Has been cancelled
173 lines
6.1 KiB
Python
173 lines
6.1 KiB
Python
"""Apply TurboQuant KV-cache quantization to a fine-tuned Eliza-1/Gemma checkpoint.
|
|
|
|
TurboQuant
|
|
Zandieh, Daliri, Hadian, Mirrokni. *TurboQuant: Online Random
|
|
Rotations for KV-Cache Quantization*. arXiv:2504.19874, ICLR 2026.
|
|
PyPI: ``turbokv`` (import name: ``turboquant``).
|
|
|
|
This is a runtime KV-cache compressor. The on-disk safetensors are
|
|
unchanged. We:
|
|
|
|
1. Load the model (merging a LoRA adapter if ``--model`` is one).
|
|
2. Optionally calibrate ``skip_layers`` from a JSONL of prompts.
|
|
3. Save the (unchanged) merged weights and a ``turboquant.json``
|
|
sidecar with the quantizer config so downstream loaders can
|
|
reconstruct ``TurboQuantCache`` deterministically.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import argparse
|
|
import json
|
|
import logging
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
import torch.nn as nn
|
|
from transformers.tokenization_utils_base import PreTrainedTokenizerBase
|
|
|
|
_HERE = Path(__file__).resolve().parent
|
|
if str(_HERE) not in sys.path:
|
|
sys.path.insert(0, str(_HERE))
|
|
|
|
from _common import ( # noqa: E402
|
|
add_quantization_cli_args,
|
|
get_text_config,
|
|
head_dim_of,
|
|
kernel_manifest_fragment,
|
|
load_calibration_prompts,
|
|
load_model_and_tokenizer,
|
|
save_model,
|
|
validate_quantization_args,
|
|
write_sidecar,
|
|
)
|
|
|
|
logging.basicConfig(
|
|
level=logging.INFO, format="%(asctime)s [%(levelname)s] %(message)s"
|
|
)
|
|
log = logging.getLogger("turboquant_apply")
|
|
|
|
|
|
def calibrate_skip_layers(
|
|
model: nn.Module,
|
|
tokenizer: PreTrainedTokenizerBase,
|
|
prompts: list[str],
|
|
norm_threshold: float = 5.0,
|
|
) -> list[int]:
|
|
"""Union ``TurboQuantCache.calibrate_skip_layers`` results across prompts.
|
|
|
|
The library helper inspects one calibration string at a time; we union
|
|
its skip-sets to be conservative.
|
|
"""
|
|
from turboquant import TurboQuantCache
|
|
|
|
skip: set[int] = set()
|
|
for i, prompt in enumerate(prompts):
|
|
s = TurboQuantCache.calibrate_skip_layers(
|
|
model, tokenizer, calibration_text=prompt, norm_threshold=norm_threshold
|
|
)
|
|
log.info("calibration prompt %d/%d -> skip %s", i + 1, len(prompts), sorted(s))
|
|
skip |= s
|
|
return sorted(skip)
|
|
|
|
|
|
def main(argv: list[str] | None = None) -> int:
|
|
ap = argparse.ArgumentParser(description=__doc__.split("\n\n", 1)[0])
|
|
add_quantization_cli_args(ap)
|
|
# Recipe-specific knobs.
|
|
ap.add_argument("--nbits", type=int, default=4, choices=(2, 4))
|
|
ap.add_argument("--residual-length", type=int, default=128)
|
|
ap.add_argument("--base-seed", type=int, default=42)
|
|
ap.add_argument("--norm-threshold", type=float, default=5.0)
|
|
# Long-context / trellis path. Per packages/training/AGENTS.md §3,
|
|
# experiments that intentionally target extended context can route the
|
|
# K-cache through Trellis-Coded Quantization (`turbo3_tcq`) instead of
|
|
# plain `turbo3`/`turbo4`. The weights are still unchanged on disk — this
|
|
# only flips which KV cache type the runtime is told to use, recorded in
|
|
# the sidecar so the manifest builder + downloader pick `turbo3_tcq`.
|
|
ap.add_argument(
|
|
"--trellis",
|
|
action="store_true",
|
|
help=(
|
|
"Long-context path: record turbo3_tcq as the K-cache type in the "
|
|
"sidecar (use for verified long-context variants)."
|
|
),
|
|
)
|
|
ap.add_argument(
|
|
"--context-length",
|
|
type=int,
|
|
default=None,
|
|
help=(
|
|
"Trained/served context length for this variant. Recorded in the "
|
|
"sidecar. Implies --trellis when >= 65536."
|
|
),
|
|
)
|
|
args = ap.parse_args(argv)
|
|
if args.context_length is not None and args.context_length >= 65536:
|
|
args.trellis = True
|
|
|
|
validate_quantization_args(args)
|
|
|
|
if args.dry_run:
|
|
print(json.dumps(vars(args), indent=2, default=str))
|
|
return 0
|
|
|
|
out_dir = Path(args.output)
|
|
model, tok = load_model_and_tokenizer(args.model, device_map=args.device)
|
|
|
|
if args.calibration:
|
|
prompts = load_calibration_prompts(args.calibration, n=args.calibration_samples)
|
|
log.info("calibrating with %d prompts", len(prompts))
|
|
skip_layers = calibrate_skip_layers(
|
|
model, tok, prompts, norm_threshold=args.norm_threshold
|
|
)
|
|
else:
|
|
log.info("no calibration; defaulting skip_layers to [0]")
|
|
skip_layers = [0]
|
|
|
|
save_model(model, tok, out_dir)
|
|
|
|
text_cfg = get_text_config(model.config)
|
|
head_dim = head_dim_of(text_cfg)
|
|
|
|
# Which KV cache type the runtime uses for K. Long-context variants take
|
|
# the trellis path (turbo3_tcq); everything else uses the per-block
|
|
# turbo3/turbo4 layout selected by --nbits.
|
|
cache_type_k = "turbo3_tcq" if args.trellis else f"turbo{args.nbits}_0"
|
|
|
|
sidecar_payload = {
|
|
"method": "turboquant",
|
|
"paper": "arXiv:2504.19874",
|
|
"library": "turbokv (import: turboquant) v0.1.0",
|
|
"source_model": args.model,
|
|
"nbits": args.nbits,
|
|
"residual_length": args.residual_length,
|
|
"base_seed": args.base_seed,
|
|
"skip_layers": skip_layers,
|
|
"head_dim": head_dim,
|
|
"num_hidden_layers": int(text_cfg.num_hidden_layers),
|
|
"trellis": bool(args.trellis),
|
|
"context_length": (
|
|
int(args.context_length) if args.context_length is not None else None
|
|
),
|
|
"cache_type_k": cache_type_k,
|
|
"calibration_file": str(args.calibration) if args.calibration else None,
|
|
"calibration_samples": args.calibration_samples if args.calibration else 0,
|
|
"norm_threshold": args.norm_threshold,
|
|
"kernel_manifest": kernel_manifest_fragment("turboquant"),
|
|
"notes": (
|
|
"TurboQuant is a runtime KV-cache compressor. The weights in "
|
|
"this directory are unchanged. To use the quantized cache, "
|
|
"construct turboquant.TurboQuantCache(model.config, nbits=..., "
|
|
"base_seed=..., skip_layers=set(skip_layers)) and pass it to "
|
|
"model.generate(past_key_values=cache)."
|
|
),
|
|
}
|
|
sidecar_path = write_sidecar(out_dir, "turboquant.json", sidecar_payload)
|
|
log.info("wrote %s", sidecar_path)
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
raise SystemExit(main())
|