abyss-api / ml /chat.py
KyleEastwood's picture
Upload folder using huggingface_hub
cc15101 verified
Raw History Blame Contribute Delete
5.47 kB
"""
Phase N - Interactive multi-turn chat REPL for The Abyss-1 (SFT Checkpoint)
Builds The Abyss chat template on every turn, streams tokesn as they
generate, and stops cleanly at <|endofturn|> or <|endoftext|>
Usage (from the_final_product/):
python -m ml.chat
python -m ml.chat --checkpoint ml/checkpoints/sft_ckpt.pt
python -m ml.chat --temperature 0.7 --max_tokens 512
Commands during chat:
reset - clear conversation history
quit - exit
"""
import argparse
import sys
import torch
import torch.nn.functional as F
from ml.model import TheAbyssModel
from ml.tokenizer.tokenizer import AbyssTokenizer
TOKENIZER_DIR = "ml/tokenizer/vocab"
SYSTEM_TEXT = "You are The Abyss, and empty void of nothingness"
@torch.no_grad()
def respond(model, tok, history, sys_ids, cfg,
max_new_tokens, temperature, top_k, top_p,
EOS, EOT, USR, AST, device):
"""
Build the full prompt from history, sample until EOT/EOS,
stream each token to stdout. Returns list of response token ids.
"""
# ----- BUILD PROMPT -----
prompt = list(sys_ids)
for turn in history:
prompt += turn
prompt += [AST]
# If prompt is too long, drop oldest turns (keep system + recent turns)
max_prompt = cfg.block_size - max_new_tokens - 1
if len(prompt) > max_prompt:
prompt = list(sys_ids)
tail = []
for turn in reversed(history):
if len(sys_ids) + len(turn) + len(tail) + 1 > max_prompt:
break
tail = list(turn) + tail
prompt = list(sys_ids) + tail + [AST]
idx = torch.tensor([prompt], dtype=torch.long, device=device)
# ----- SAMPLE -----
response_ids = []
for _ in range(max_new_tokens):
idx_cond = idx[:, -cfg.block_size:]
logits, _ = model(idx_cond)
logits = logits[0, -1, :] / temperature # (V,) — 1D
# top_k
if top_k > 0:
k = min(top_k, logits.size(-1))
kth = torch.topk(logits, k).values[-1]
logits = logits.masked_fill(logits < kth, float("-inf"))
# top_p (nucleus)
if 0.0 < top_p < 1.0:
s_logits, s_idx = torch.sort(logits, descending=True)
cum = torch.cumsum(F.softmax(s_logits, dim=-1), dim=-1)
mask = (cum - F.softmax(s_logits, dim=-1)) > top_p
s_logits = s_logits.masked_fill(mask, float("-inf"))
logits = torch.zeros_like(logits).scatter_(0, s_idx, s_logits)
next_tok = torch.multinomial(F.softmax(logits, dim=-1), 1) # (1,)
tok_id = next_tok.item()
# STOP ON END-OF-TURN or EOS
if tok_id == EOT or tok_id == EOS:
break
response_ids.append(tok_id)
sys.stdout.write(tok.decode([tok_id]))
sys.stdout.flush()
idx = torch.cat([idx, next_tok.view(1, 1)], dim=1)
print()
return response_ids
def main():
parser = argparse.ArgumentParser(description="Chat with The Abyss-1")
parser.add_argument("--checkpoint", default="ml/checkpoints/sft_ckpt.pt")
parser.add_argument("--max_tokens", type=int, default=256)
parser.add_argument("--temperature", type=float, default=0.8)
parser.add_argument("--top_k", type=int, default=50)
parser.add_argument("--top_p", type=float, default=0.95)
parser.add_argument("--device",
default="cuda" if torch.cuda.is_available() else "cpu")
args = parser.parse_args()
# ----- LOAD MODEL -----
print(f"Loading {args.checkpoint} ...", flush=True)
ck = torch.load(args.checkpoint, map_location=args.device, weights_only=False)
cfg = ck["config"]
model = TheAbyssModel(cfg).to(args.device)
model.load_state_dict(ck["model"])
model.eval()
print(f" iter={ck.get('iter', '?')} params={model.num_params():,}\n", flush=True)
# ----- LOAD TOKENIZER -----
tok = AbyssTokenizer.load(TOKENIZER_DIR)
EOS = tok.encode("<|endoftext|>")[0]
USR = tok.encode("<|user|>")[0]
AST = tok.encode("<|assistant|>")[0]
EOT = tok.encode("<|endofturn|>")[0]
SYS = tok.encode("<|system|>")[0]
sys_ids = [SYS] + tok.encode(SYSTEM_TEXT) + [EOT]
print("The Abyss Chat - type 'reset' to clear history, 'quit' to exit\n")
print("-" *50)
history = []
while True:
try:
user_input = input("\nYou: ").strip()
except (EOFError, KeyboardInterrupt):
print("\nBye.")
break
if not user_input:
continue
if user_input.lower() in ("quit", "exit"):
print("Bye.")
break
if user_input.lower() == "reset":
history.clear()
print(" [history cleared]")
continue
# ENCODE AND STORE USER TURN
user_turn = [USR] + tok.encode(user_input) + [EOT]
history.append(user_turn)
print("\nThe Abyss: ", end="", flush=True)
response_ids = respond(
model, tok, history, sys_ids, cfg,
args.max_tokens, args.temperature, args.top_k, args.top_p,
EOS, EOT, USR, AST, args.device
)
# STORE ASSISTANT TURN
history.append([AST] + response_ids + [EOT])
if __name__ == "__main__":
main()