Scrypt / finetune /nemo /warden_processor.py
IMJONEZZ's picture
SCRYPT: initial commit — game, sandbox, Warden, Space web layer
9fca766
Raw
History Blame Contribute Delete
2.25 kB
"""Convert our `{"messages": [...]}` chat rows into the (input, output) pair
the Megatron-Bridge SFT builder expects.
The builder combines them as `"{input} {output}"` + EOS and masks the loss to
the `output` span (sft.py: prompt_template, add_eos=True). So:
input = the conversation up to (but not including) the final assistant turn,
rendered through the model's own chat template with a generation
prompt appended -> exactly what the game feeds the model at play time.
output = the final assistant turn's content (the Warden's line, or a tool-call
JSON string for director examples).
Rendering through the real chat template keeps train == play: the live game
prompts the GGUF with the same Jinja template via llama.cpp.
"""
from typing import Any, Optional
from megatron.bridge.data.builders.hf_dataset import ProcessExampleOutput
from megatron.bridge.training.tokenizers.tokenizer import MegatronTokenizer
def _hf_tokenizer(tokenizer):
"""Reach the underlying HF tokenizer that owns apply_chat_template."""
for attr in ("apply_chat_template",):
if hasattr(tokenizer, attr):
return tokenizer
for attr in ("_tokenizer", "tokenizer", "hf_tokenizer"):
inner = getattr(tokenizer, attr, None)
if inner is not None and hasattr(inner, "apply_chat_template"):
return inner
raise RuntimeError("could not find an apply_chat_template-capable tokenizer")
def process_warden_example(
example: dict[str, Any], tokenizer: Optional[MegatronTokenizer] = None
) -> ProcessExampleOutput:
messages = example["messages"]
if not messages or messages[-1]["role"] != "assistant":
raise ValueError("every training row must end with an assistant turn")
prompt_messages = messages[:-1]
target = messages[-1]["content"]
tok = _hf_tokenizer(tokenizer)
_input = tok.apply_chat_template(
prompt_messages,
tokenize=False,
add_generation_prompt=True,
)
# The builder inserts a single space before {output}; trim a trailing space
# on the rendered prompt so we don't double it.
_input = _input.rstrip(" ")
return ProcessExampleOutput(input=_input, output=target, original_answers=[target])