wayneworkman2012 commited on
Commit
00bad86
·
verified ·
1 Parent(s): 912b344

Add wayneworkman2012/peacebell-v1-148M (WWII domain model, greedy; additive Peacebell-only branch)

Browse files

Adds **wayneworkman2012/peacebell-v1-148M** to the main (chat) arena: https://huggingface.co/wayneworkman2012/peacebell-v1-148M

148.55M parameters, trained from scratch, Apache-2.0. It is a single-domain model: it only knows World War II, and it redirects most other topics back to the war. It will lose plenty of general battles and that is fine; it is a first version and I would like it out there.

**Why this is a PR and not just a model id**

I replayed this Space's own `load_models` / `build_inputs` / `generate_for_model` code against the model locally. It loads fine with your generic loader (`AutoTokenizer` + `AutoModelForCausalLM`, `trust_remote_code=True`, float32), but every reply came back as `[Error generating ... 'GPTForCausalLM' object has no attribute 'generate']`. Two reasons, both in my repo's design:
- the model ships its own KV-cached decoder (`chat_generate`) instead of HF `generate()`;
- it has no Jinja `chat_template` on purpose: the ChatML control tokens are injected by id exactly as at training time (`tokenizer.build_chatml_ids`), which a string template cannot reproduce with SentencePiece.

So it needs a small special case, the same way MetaDiffusion has one.

**What this PR changes** (`app.py`: 20 lines added, 0 removed; `requirements.txt`: 1 line added)
- one entry each in `MODEL_IDS`, `MODEL_DISPLAY`, `MODEL_PARAMS` (148.55e6), `GEN_DEFAULTS`, `MODEL_CONTEXT` (16384);
- `is_peacebell_model()` + `generate_peacebell()`, and a two-line branch in `generate_for_model` placed right after the diffusion branch. It honours the same `max_new` the slider gives every other model;
- `sentencepiece>=0.2.0` in `requirements.txt` (the model's tokenizer imports it).

**Decoding: greedy on purpose.** Peacebell is trained and validated at temperature 0, with no repetition penalty and no n-gram block, so that is how it is wired (`temperature=0.0`, `no_repeat_ngram_size=0`). Its answers are deterministic.

**Nothing else is affected.** No existing line is modified; other models never enter the new branch. What I checked locally (transformers 5.14.1, your pinned version):
- a control entrant, `HuggingFaceTB/SmolLM2-135M-Instruct`, run through the original and the patched `app.py` with the same seeds over 8 prompts: output SHA-256 identical (`d45c0901...c02f9f` both ways);
- because `sentencepiece` is a new dependency, I loaded the tokenizer of every model in `MODEL_IDS` + `BASE_MODEL_IDS` in a clean environment without and then with `sentencepiece` installed: tokenizer class, vocabulary size, special tokens, encodings of three test strings and chat-template output are identical for all of them;
- peak GPU memory for Peacebell in float32: 0.59 GiB. The same modeling code already runs on ZeroGPU in my own demo Space.

Unrelated, in case it helps: `DedeProGames/Kiyo-135M` currently returns 404 on the Hub (it failed to load in both of my runs), so that entry is probably erroring in battles. I did not touch it.

Sample replies through the patched code, 128 new tokens:
- "When did World War II begin and end?" -> "World War II began in 1939 and ended in 1945."
- "Who commanded the Allied forces on D-Day?" -> "General Dwight D. Eisenhower commanded the Allied forces on D-Day."
- "Tell me a joke." -> "I won't do that. We can go over World War II together, if you'd like to explore it."

Thanks for running the arena.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

https://claude.ai/code/session_01Kpv2a5EA4htxnrqzE1VdGG

Files changed (2) hide show
  1. app.py +20 -0
  2. requirements.txt +2 -1
app.py CHANGED
@@ -39,6 +39,7 @@ MODEL_IDS: List[str] = [
39
  "BananaMind/BananaMind-2-Pro-Preview-Chat",
40
  "BananaMind/BananaMind-2-Nano-Chat",
41
  "BananaMind/BananaMind-2-Mini-Chat",
 
42
  ]
43
 
44
  MODEL_DISPLAY: Dict[str, str] = {
@@ -53,6 +54,7 @@ MODEL_DISPLAY: Dict[str, str] = {
53
  "BananaMind/BananaMind-2-Pro-Preview-Chat": "BananaMind-2-Pro-Preview-Chat",
54
  "BananaMind/BananaMind-2-Nano-Chat": "BananaMind-2-Nano-Chat",
55
  "BananaMind/BananaMind-2-Mini-Chat": "BananaMind-2-Mini-Chat",
 
56
  }
57
 
58
  BASE_MODEL_IDS: List[str] = [
@@ -121,6 +123,7 @@ MODEL_PARAMS: Dict[str, float] = {
121
  "DedeProGames/Kiyo-230M-Preview": 229.7e6,
122
  "DedeProGames/Kiyo-135M": 134.5e6,
123
  "DedeProGames/Kiyo-65M": 65.0e6,
 
124
  }
125
 
126
  FALLBACK_IDS: Dict[str, str] = {}
@@ -218,6 +221,8 @@ GEN_DEFAULTS: Dict[str, dict] = {
218
  "DedeProGames/Kiyo-230M-Preview": {"max_new_tokens": 64, "temperature": 0.8, "top_p": 0.95, "repetition_penalty": 1.1, "do_sample": True},
219
  "DedeProGames/Kiyo-135M": {"max_new_tokens": 64, "temperature": 0.8, "top_p": 0.95, "repetition_penalty": 1.1, "do_sample": True},
220
  "DedeProGames/Kiyo-65M": {"max_new_tokens": 64, "temperature": 0.8, "top_p": 0.95, "repetition_penalty": 1.1, "do_sample": True},
 
 
221
  }
222
 
223
  MODEL_CONTEXT: Dict[str, int] = {
@@ -248,6 +253,7 @@ MODEL_CONTEXT: Dict[str, int] = {
248
  "DedeProGames/Kiyo-230M-Preview": 2048,
249
  "DedeProGames/Kiyo-135M": 8192,
250
  "DedeProGames/Kiyo-65M": 2048,
 
251
  }
252
 
253
 
@@ -894,6 +900,18 @@ def build_inputs(tokenizer, model_id: str, prompt: str):
894
  def is_diffusion_model(model_id: str) -> bool:
895
  return "metadiffusion" in model_id.lower()
896
 
 
 
 
 
 
 
 
 
 
 
 
 
897
  def generate_for_model(model_id: str, prompt: str, max_new_tokens: int = 0) -> str:
898
  ensure_models_loaded()
899
  if model_id not in models or model_id not in tokenizers:
@@ -910,6 +928,8 @@ def generate_for_model(model_id: str, prompt: str, max_new_tokens: int = 0) -> s
910
  try:
911
  if is_diffusion_model(model_id):
912
  return generate_diffusion(model, tokenizer, prompt, cfg) # type: ignore
 
 
913
  inputs = build_inputs(tokenizer, model_id, prompt)
914
  input_len = inputs["input_ids"].shape[1]
915
  gen_kwargs = {
 
39
  "BananaMind/BananaMind-2-Pro-Preview-Chat",
40
  "BananaMind/BananaMind-2-Nano-Chat",
41
  "BananaMind/BananaMind-2-Mini-Chat",
42
+ "wayneworkman2012/peacebell-v1-148M",
43
  ]
44
 
45
  MODEL_DISPLAY: Dict[str, str] = {
 
54
  "BananaMind/BananaMind-2-Pro-Preview-Chat": "BananaMind-2-Pro-Preview-Chat",
55
  "BananaMind/BananaMind-2-Nano-Chat": "BananaMind-2-Nano-Chat",
56
  "BananaMind/BananaMind-2-Mini-Chat": "BananaMind-2-Mini-Chat",
57
+ "wayneworkman2012/peacebell-v1-148M": "peacebell-v1-148M",
58
  }
59
 
60
  BASE_MODEL_IDS: List[str] = [
 
123
  "DedeProGames/Kiyo-230M-Preview": 229.7e6,
124
  "DedeProGames/Kiyo-135M": 134.5e6,
125
  "DedeProGames/Kiyo-65M": 65.0e6,
126
+ "wayneworkman2012/peacebell-v1-148M": 148.55e6,
127
  }
128
 
129
  FALLBACK_IDS: Dict[str, str] = {}
 
221
  "DedeProGames/Kiyo-230M-Preview": {"max_new_tokens": 64, "temperature": 0.8, "top_p": 0.95, "repetition_penalty": 1.1, "do_sample": True},
222
  "DedeProGames/Kiyo-135M": {"max_new_tokens": 64, "temperature": 0.8, "top_p": 0.95, "repetition_penalty": 1.1, "do_sample": True},
223
  "DedeProGames/Kiyo-65M": {"max_new_tokens": 64, "temperature": 0.8, "top_p": 0.95, "repetition_penalty": 1.1, "do_sample": True},
224
+ # Peacebell: greedy (temperature 0), no repetition penalty, no n-gram block; decoded by its own ChatML path (generate_peacebell)
225
+ "wayneworkman2012/peacebell-v1-148M": {"max_new_tokens": 64, "temperature": 0.0, "do_sample": False, "repetition_penalty": 1.0},
226
  }
227
 
228
  MODEL_CONTEXT: Dict[str, int] = {
 
253
  "DedeProGames/Kiyo-230M-Preview": 2048,
254
  "DedeProGames/Kiyo-135M": 8192,
255
  "DedeProGames/Kiyo-65M": 2048,
256
+ "wayneworkman2012/peacebell-v1-148M": 16384,
257
  }
258
 
259
 
 
900
  def is_diffusion_model(model_id: str) -> bool:
901
  return "metadiffusion" in model_id.lower()
902
 
903
+ def is_peacebell_model(model_id: str) -> bool:
904
+ return model_id.lower().startswith("wayneworkman2012/peacebell-")
905
+
906
+ def generate_peacebell(model, tokenizer, prompt: str, max_new: int) -> str:
907
+ """Peacebell ships its own ChatML encoder + KV-cached greedy decoder (no HF generate(), no Jinja chat_template):
908
+ special-token ids are injected by id exactly as at training time. Same path as its demo Space. Temperature 0."""
909
+ ids = tokenizer.build_chatml_ids([{"role": "user", "content": prompt}], add_generation_prompt=True)
910
+ input_ids = torch.tensor(ids, dtype=torch.long, device=DEVICE)
911
+ out_ids = model.chat_generate(input_ids, max_new_tokens=int(max_new), temperature=0.0, no_repeat_ngram_size=0)
912
+ text = tokenizer.decode_response(out_ids)
913
+ return text if text else "[Empty response]"
914
+
915
  def generate_for_model(model_id: str, prompt: str, max_new_tokens: int = 0) -> str:
916
  ensure_models_loaded()
917
  if model_id not in models or model_id not in tokenizers:
 
928
  try:
929
  if is_diffusion_model(model_id):
930
  return generate_diffusion(model, tokenizer, prompt, cfg) # type: ignore
931
+ if is_peacebell_model(model_id):
932
+ return generate_peacebell(model, tokenizer, prompt, max_new)
933
  inputs = build_inputs(tokenizer, model_id, prompt)
934
  input_len = inputs["input_ids"].shape[1]
935
  gen_kwargs = {
requirements.txt CHANGED
@@ -7,4 +7,5 @@ huggingface_hub==1.22.0
7
  accelerate==1.14.0
8
  numpy==2.2.6
9
  plotly>=5.18.0
10
- tiktoken>=0.8.0
 
 
7
  accelerate==1.14.0
8
  numpy==2.2.6
9
  plotly>=5.18.0
10
+ tiktoken>=0.8.0
11
+ sentencepiece>=0.2.0