Instructions to use handshake-ai-research/cue with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Transformers
How to use handshake-ai-research/cue with Transformers:
# Use a pipeline as a high-level helper from transformers import pipeline pipe = pipeline("feature-extraction", model="handshake-ai-research/cue", trust_remote_code=True)# pip install -U transformers accelerate # Load model directly from transformers import AutoModel model = AutoModel.from_pretrained("handshake-ai-research/cue", trust_remote_code=True, device_map="auto") - Notebooks
- Google Colab
- Kaggle
Upload folder using huggingface_hub
Browse files- checkpoint.py +7 -18
- configuration_cue.py +5 -2
- modeling_cue.py +9 -3
- sampler_sample.py +45 -0
checkpoint.py
CHANGED
|
@@ -13,14 +13,8 @@ from .configuration_cue import CueConfig
|
|
| 13 |
JOINT_CONFIG_NAME = "unified_config.json"
|
| 14 |
JOINT_WEIGHTS_NAME = "model.pt"
|
| 15 |
REFINED_DECODER_STATE = "decoder_state.pt"
|
| 16 |
-
|
| 17 |
-
|
| 18 |
-
"refinement_config.json",
|
| 19 |
-
"decoder_refinement_config.json",
|
| 20 |
-
"stage2_config.json",
|
| 21 |
-
)
|
| 22 |
-
# Newest first; `stage1_checkpoint` predates the rename.
|
| 23 |
-
JOINT_CHECKPOINT_KEYS = ("joint_training_checkpoint", "stage1_checkpoint")
|
| 24 |
|
| 25 |
LAYOUT_NATIVE = "native"
|
| 26 |
LAYOUT_JOINT = "joint"
|
|
@@ -55,16 +49,13 @@ def detect_layout(directory: str | Path) -> str:
|
|
| 55 |
return LAYOUT_NATIVE
|
| 56 |
raise ValueError(
|
| 57 |
f"{directory} is not a cue-hf, joint training, or refinement checkpoint "
|
| 58 |
-
f"(looked for config.json, {JOINT_CONFIG_NAME}, {
|
| 59 |
)
|
| 60 |
|
| 61 |
|
| 62 |
def _refined_config_path(directory: Path) -> Path | None:
|
| 63 |
-
|
| 64 |
-
|
| 65 |
-
if path.is_file():
|
| 66 |
-
return path
|
| 67 |
-
return None
|
| 68 |
|
| 69 |
|
| 70 |
def config_from_joint_dir(directory: str | Path) -> CueConfig:
|
|
@@ -91,12 +82,10 @@ def resolve_refined(directory: str | Path) -> tuple[Path, Path]:
|
|
| 91 |
if config_path is None:
|
| 92 |
raise ValueError(f"{directory} has no refinement config")
|
| 93 |
payload = json.loads(config_path.read_text(encoding="utf-8"))
|
| 94 |
-
joint =
|
| 95 |
-
(payload[key] for key in JOINT_CHECKPOINT_KEYS if payload.get(key)), None
|
| 96 |
-
)
|
| 97 |
if not joint:
|
| 98 |
raise ValueError(
|
| 99 |
-
f"{config_path} has
|
| 100 |
"joint training checkpoint holding the encoder"
|
| 101 |
)
|
| 102 |
decoder_state = directory / REFINED_DECODER_STATE
|
|
|
|
| 13 |
JOINT_CONFIG_NAME = "unified_config.json"
|
| 14 |
JOINT_WEIGHTS_NAME = "model.pt"
|
| 15 |
REFINED_DECODER_STATE = "decoder_state.pt"
|
| 16 |
+
REFINED_CONFIG_NAME = "refinement_config.json"
|
| 17 |
+
JOINT_CHECKPOINT_KEY = "joint_training_checkpoint"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 18 |
|
| 19 |
LAYOUT_NATIVE = "native"
|
| 20 |
LAYOUT_JOINT = "joint"
|
|
|
|
| 49 |
return LAYOUT_NATIVE
|
| 50 |
raise ValueError(
|
| 51 |
f"{directory} is not a cue-hf, joint training, or refinement checkpoint "
|
| 52 |
+
f"(looked for config.json, {JOINT_CONFIG_NAME}, {REFINED_CONFIG_NAME})"
|
| 53 |
)
|
| 54 |
|
| 55 |
|
| 56 |
def _refined_config_path(directory: Path) -> Path | None:
|
| 57 |
+
path = directory / REFINED_CONFIG_NAME
|
| 58 |
+
return path if path.is_file() else None
|
|
|
|
|
|
|
|
|
|
| 59 |
|
| 60 |
|
| 61 |
def config_from_joint_dir(directory: str | Path) -> CueConfig:
|
|
|
|
| 82 |
if config_path is None:
|
| 83 |
raise ValueError(f"{directory} has no refinement config")
|
| 84 |
payload = json.loads(config_path.read_text(encoding="utf-8"))
|
| 85 |
+
joint = payload.get(JOINT_CHECKPOINT_KEY)
|
|
|
|
|
|
|
| 86 |
if not joint:
|
| 87 |
raise ValueError(
|
| 88 |
+
f"{config_path} has no {JOINT_CHECKPOINT_KEY}; cannot find the "
|
| 89 |
"joint training checkpoint holding the encoder"
|
| 90 |
)
|
| 91 |
decoder_state = directory / REFINED_DECODER_STATE
|
configuration_cue.py
CHANGED
|
@@ -12,6 +12,7 @@ from .encoder_model import (
|
|
| 12 |
DEFAULT_PASSAGE_PREFIX,
|
| 13 |
DEFAULT_QUERY_PREFIX,
|
| 14 |
)
|
|
|
|
| 15 |
|
| 16 |
# Defaults mirror the final CUE joint-training config (ModernBERT turn encoder).
|
| 17 |
DEFAULT_ENCODER_CONFIG: dict[str, Any] = {
|
|
@@ -59,7 +60,7 @@ class CueConfig(PretrainedConfig):
|
|
| 59 |
style_command_slots: int = 4,
|
| 60 |
slot_embedding_dim: int = 64,
|
| 61 |
target_max_tokens: int = 2048,
|
| 62 |
-
session_preprocess: str = "full",
|
| 63 |
decode_temperature: float = 0.0,
|
| 64 |
decode_max_new_tokens: int = 2048,
|
| 65 |
slot_dedup_jaccard: float = 0.8,
|
|
@@ -81,7 +82,9 @@ class CueConfig(PretrainedConfig):
|
|
| 81 |
self.style_command_slots = int(style_command_slots)
|
| 82 |
self.slot_embedding_dim = int(slot_embedding_dim)
|
| 83 |
self.target_max_tokens = int(target_max_tokens)
|
| 84 |
-
|
|
|
|
|
|
|
| 85 |
self.decode_temperature = float(decode_temperature)
|
| 86 |
self.decode_max_new_tokens = int(decode_max_new_tokens)
|
| 87 |
self.slot_dedup_jaccard = float(slot_dedup_jaccard)
|
|
|
|
| 12 |
DEFAULT_PASSAGE_PREFIX,
|
| 13 |
DEFAULT_QUERY_PREFIX,
|
| 14 |
)
|
| 15 |
+
from .session_preprocess import normalize_session_preprocess
|
| 16 |
|
| 17 |
# Defaults mirror the final CUE joint-training config (ModernBERT turn encoder).
|
| 18 |
DEFAULT_ENCODER_CONFIG: dict[str, Any] = {
|
|
|
|
| 60 |
style_command_slots: int = 4,
|
| 61 |
slot_embedding_dim: int = 64,
|
| 62 |
target_max_tokens: int = 2048,
|
| 63 |
+
session_preprocess: str | None = "full",
|
| 64 |
decode_temperature: float = 0.0,
|
| 65 |
decode_max_new_tokens: int = 2048,
|
| 66 |
slot_dedup_jaccard: float = 0.8,
|
|
|
|
| 82 |
self.style_command_slots = int(style_command_slots)
|
| 83 |
self.slot_embedding_dim = int(slot_embedding_dim)
|
| 84 |
self.target_max_tokens = int(target_max_tokens)
|
| 85 |
+
# A config exported without an explicit mode carries null, and str(None) would reach
|
| 86 |
+
# the encoder as the literal "None".
|
| 87 |
+
self.session_preprocess = normalize_session_preprocess(session_preprocess)
|
| 88 |
self.decode_temperature = float(decode_temperature)
|
| 89 |
self.decode_max_new_tokens = int(decode_max_new_tokens)
|
| 90 |
self.slot_dedup_jaccard = float(slot_dedup_jaccard)
|
modeling_cue.py
CHANGED
|
@@ -23,7 +23,7 @@ from .decoder_model import CueDecoder
|
|
| 23 |
from .encoder_model import CueEncoder
|
| 24 |
from .example_pool import META_NAME, POOL_DIRNAME, ExamplePool, inject_examples
|
| 25 |
from .log import log, warn
|
| 26 |
-
from .sampler_sample import CueSampler, load_sampler
|
| 27 |
from .schema import (
|
| 28 |
COMMAND_BLOCK_SENTINEL,
|
| 29 |
COMMAND_SLOT_SENTINEL,
|
|
@@ -156,8 +156,14 @@ class CueModel(PreTrainedModel):
|
|
| 156 |
def attach_sampler(self, checkpoint: str | Path, **kwargs: Any) -> CueSampler:
|
| 157 |
"""Load a diffusion prior (``sampler.pt``) from a directory or Hub repo."""
|
| 158 |
|
| 159 |
-
|
| 160 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 161 |
return self._sampler
|
| 162 |
|
| 163 |
def attach_example_pool(self, source: str | Path, *, split: str = "train") -> ExamplePool:
|
|
|
|
| 23 |
from .encoder_model import CueEncoder
|
| 24 |
from .example_pool import META_NAME, POOL_DIRNAME, ExamplePool, inject_examples
|
| 25 |
from .log import log, warn
|
| 26 |
+
from .sampler_sample import SAMPLER_NAME, CueSampler, load_sampler
|
| 27 |
from .schema import (
|
| 28 |
COMMAND_BLOCK_SENTINEL,
|
| 29 |
COMMAND_SLOT_SENTINEL,
|
|
|
|
| 156 |
def attach_sampler(self, checkpoint: str | Path, **kwargs: Any) -> CueSampler:
|
| 157 |
"""Load a diffusion prior (``sampler.pt``) from a directory or Hub repo."""
|
| 158 |
|
| 159 |
+
source = Path(checkpoint)
|
| 160 |
+
if not source.is_dir():
|
| 161 |
+
from huggingface_hub import hf_hub_download
|
| 162 |
+
|
| 163 |
+
# Refined repos point sampler_id at the joint repo, so snapshotting it would
|
| 164 |
+
# pull that model's weights too: fetch the one file instead.
|
| 165 |
+
source = Path(hf_hub_download(str(checkpoint), SAMPLER_NAME, **kwargs))
|
| 166 |
+
self._sampler = load_sampler(source, device=str(self.device))
|
| 167 |
return self._sampler
|
| 168 |
|
| 169 |
def attach_example_pool(self, source: str | Path, *, split: str = "train") -> ExamplePool:
|
sampler_sample.py
CHANGED
|
@@ -229,6 +229,51 @@ def load_sampler(
|
|
| 229 |
)
|
| 230 |
|
| 231 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 232 |
def as_condition_tensor(
|
| 233 |
condition: np.ndarray | torch.Tensor | list,
|
| 234 |
*,
|
|
|
|
| 229 |
)
|
| 230 |
|
| 231 |
|
| 232 |
+
"""Keys ``load_sampler`` reads, plus provenance scalars. Everything else is training state."""
|
| 233 |
+
INFERENCE_KEYS = (
|
| 234 |
+
"schedule",
|
| 235 |
+
"mu",
|
| 236 |
+
"sigma",
|
| 237 |
+
"layernorm_weight",
|
| 238 |
+
"layernorm_bias",
|
| 239 |
+
"dim",
|
| 240 |
+
"config",
|
| 241 |
+
"checkpoint_hash",
|
| 242 |
+
"step",
|
| 243 |
+
)
|
| 244 |
+
|
| 245 |
+
|
| 246 |
+
def slim_sampler_payload(payload: dict[str, Any]) -> dict[str, Any]:
|
| 247 |
+
"""Drop optimizer/scheduler/scaler state and the duplicate weight copy.
|
| 248 |
+
|
| 249 |
+
Training checkpoints hold ``model``, an EMA shadow, and optimizer moments. Inference
|
| 250 |
+
uses the EMA weights, so those are promoted into ``model`` and the rest is dropped:
|
| 251 |
+
one copy that loads identically whether or not ``use_ema`` is set.
|
| 252 |
+
"""
|
| 253 |
+
|
| 254 |
+
ema = payload.get("ema")
|
| 255 |
+
if isinstance(ema, dict) and "shadow" in ema:
|
| 256 |
+
weights = ema["shadow"]
|
| 257 |
+
elif "model" in payload:
|
| 258 |
+
weights = payload["model"]
|
| 259 |
+
else:
|
| 260 |
+
raise KeyError("sampler payload has neither ema['shadow'] nor model weights")
|
| 261 |
+
slim: dict[str, Any] = {"model": weights}
|
| 262 |
+
slim.update({key: payload[key] for key in INFERENCE_KEYS if key in payload})
|
| 263 |
+
return slim
|
| 264 |
+
|
| 265 |
+
|
| 266 |
+
def slim_sampler_file(source: str | Path, destination: str | Path) -> Path:
|
| 267 |
+
"""Rewrite a ``sampler.pt`` with only the tensors inference needs."""
|
| 268 |
+
|
| 269 |
+
source, destination = Path(source), Path(destination)
|
| 270 |
+
if source.is_dir():
|
| 271 |
+
source = source / SAMPLER_NAME
|
| 272 |
+
payload = torch.load(source, map_location="cpu", mmap=True, weights_only=False)
|
| 273 |
+
torch.save(slim_sampler_payload(payload), destination)
|
| 274 |
+
return destination
|
| 275 |
+
|
| 276 |
+
|
| 277 |
def as_condition_tensor(
|
| 278 |
condition: np.ndarray | torch.Tensor | list,
|
| 279 |
*,
|