Text Generation
Safetensors
GGUF
Italian
physisml
curriculum-learning
from-scratch
italian
research
experimental
Instructions to use speleoalex/physisml-it-preview with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Notebooks
- Google Colab
- Kaggle
- Local Apps Settings
- llama.cpp
How to use speleoalex/physisml-it-preview with llama.cpp:
Install (macOS, Linux)
curl -LsSf https://llama.app/install.sh | sh # Start a local OpenAI-compatible server with a web UI: llama serve -hf speleoalex/physisml-it-preview # Run inference directly in the terminal: llama cli -hf speleoalex/physisml-it-preview
Install from WinGet (Windows)
winget install llama.cpp # Start a local OpenAI-compatible server with a web UI: llama serve -hf speleoalex/physisml-it-preview # Run inference directly in the terminal: llama cli -hf speleoalex/physisml-it-preview
Build from source code
git clone https://github.com/ggml-org/llama.cpp.git cd llama.cpp cmake -B build cmake --build build -j --target llama-app # Start a local OpenAI-compatible server with a web UI: ./build/bin/llama serve -hf speleoalex/physisml-it-preview # Run inference directly in the terminal: ./build/bin/llama cli -hf speleoalex/physisml-it-preview
Use Docker
docker model run hf.co/speleoalex/physisml-it-preview
- Llama new
- LM Studio
- Jan
- vLLM
How to use speleoalex/physisml-it-preview with vLLM:
Install from pip and serve model
# Install vLLM from pip: pip install vllm # Start the vLLM server: vllm serve "speleoalex/physisml-it-preview" # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:8000/v1/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "speleoalex/physisml-it-preview", "prompt": "Once upon a time,", "max_tokens": 512, "temperature": 0.5 }'Use Docker
docker model run hf.co/speleoalex/physisml-it-preview
- Ollama
How to use speleoalex/physisml-it-preview with Ollama:
ollama run hf.co/speleoalex/physisml-it-preview
- Unsloth Desktop
- Docker Model Runner
How to use speleoalex/physisml-it-preview with Docker Model Runner:
docker model run hf.co/speleoalex/physisml-it-preview
- Lemonade
How to use speleoalex/physisml-it-preview with Lemonade:
Pull the model
# Download Lemonade from https://lemonade-server.ai/ lemonade pull speleoalex/physisml-it-preview
Run and chat with the model
lemonade run user.physisml-it-preview-{{QUANT_TAG}}List all available models
lemonade list
- Atomic Chat
Ship the missing generation module and drop the stale bytecode
Browse filesgenerate.py imported physisml.generation, which was never uploaded: every download failed with ModuleNotFoundError. The file is byte-identical to the one already published in physisml-en-preview. The six __pycache__ entries were build residue and are removed.
- physisml/__pycache__/__init__.cpython-312.pyc +0 -0
- physisml/__pycache__/affect_state.cpython-312.pyc +0 -0
- physisml/__pycache__/modulator.cpython-312.pyc +0 -0
- physisml/__pycache__/tokenizer.cpython-312.pyc +0 -0
- physisml/__pycache__/torch_model.cpython-312.pyc +0 -0
- physisml/__pycache__/utils.cpython-312.pyc +0 -0
- physisml/generation.py +148 -0
physisml/__pycache__/__init__.cpython-312.pyc
DELETED
|
Binary file (367 Bytes)
|
|
|
physisml/__pycache__/affect_state.cpython-312.pyc
DELETED
|
Binary file (19.9 kB)
|
|
|
physisml/__pycache__/modulator.cpython-312.pyc
DELETED
|
Binary file (9.55 kB)
|
|
|
physisml/__pycache__/tokenizer.cpython-312.pyc
DELETED
|
Binary file (15.9 kB)
|
|
|
physisml/__pycache__/torch_model.cpython-312.pyc
DELETED
|
Binary file (15.6 kB)
|
|
|
physisml/__pycache__/utils.cpython-312.pyc
DELETED
|
Binary file (5.65 kB)
|
|
|
physisml/generation.py
ADDED
|
@@ -0,0 +1,148 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
Shared generation path for PhysisML checkpoints.
|
| 3 |
+
|
| 4 |
+
This is the code that produces the answers quoted in the README, extracted so
|
| 5 |
+
that the repository's chat script (`standalone/chat.py`) and the script shipped
|
| 6 |
+
with the published weights (`huggingface/generate.py`) cannot drift apart. It
|
| 7 |
+
is a port of the generation path used during training
|
| 8 |
+
(`dynamic_model/exp_b/trainer.py`) and keeps the parts that shape the answers:
|
| 9 |
+
|
| 10 |
+
* optional affective modulation — confidence / pleasure / pain / fear shift
|
| 11 |
+
the logits at every step
|
| 12 |
+
* EOS suppressed before `min_tokens`, otherwise the model answers with a bare
|
| 13 |
+
end-of-sequence token
|
| 14 |
+
* soft stop on sentence-ending punctuation, tested on the DECODED token
|
| 15 |
+
because the vocabulary has multi-character tokens carrying the terminator
|
| 16 |
+
* output slots the tokenizer cannot decode are masked out
|
| 17 |
+
|
| 18 |
+
One thing this path cannot reproduce: the published exact-match scores were
|
| 19 |
+
measured with the stop threshold set from the length of the gold answer, which
|
| 20 |
+
is information a free-form prompt does not have. Expect the model to keep
|
| 21 |
+
talking past its answer more often here.
|
| 22 |
+
"""
|
| 23 |
+
import re
|
| 24 |
+
|
| 25 |
+
import torch
|
| 26 |
+
|
| 27 |
+
from .utils import sample_top_k
|
| 28 |
+
|
| 29 |
+
|
| 30 |
+
def undecodable_mask(model, tok):
|
| 31 |
+
"""
|
| 32 |
+
Mask over the output layer marking ids the tokenizer cannot turn back into
|
| 33 |
+
bytes. vocab_size is rounded up past the tokenizer's real size, so several
|
| 34 |
+
hundred slots decode to nothing; sampling one derails the whole answer.
|
| 35 |
+
"""
|
| 36 |
+
vocab = getattr(tok, "vocab", None)
|
| 37 |
+
if not vocab:
|
| 38 |
+
return None
|
| 39 |
+
missing = [i for i in range(model.vocab_size) if i not in vocab]
|
| 40 |
+
if not missing:
|
| 41 |
+
return None
|
| 42 |
+
mask = torch.zeros(model.vocab_size, dtype=torch.bool)
|
| 43 |
+
mask[torch.tensor(missing, dtype=torch.long)] = True
|
| 44 |
+
return mask
|
| 45 |
+
|
| 46 |
+
|
| 47 |
+
def clean_response(text: str) -> str:
|
| 48 |
+
"""
|
| 49 |
+
Strip the two noise artifacts the model learned: a leading '!' (it picked
|
| 50 |
+
up the terminator as a start token) and an echoed 'di:' from the prompt.
|
| 51 |
+
Conservative — leading noise only, never content.
|
| 52 |
+
"""
|
| 53 |
+
t = re.sub(r"^[!\s]+", "", text.strip()).strip()
|
| 54 |
+
t = re.sub(r"(?:il|la|lo|le|gli|un|una)\s+di:\s*", "", t,
|
| 55 |
+
flags=re.IGNORECASE).strip()
|
| 56 |
+
t = re.sub(r"di:\s*", "", t, flags=re.IGNORECASE).strip()
|
| 57 |
+
t = re.sub(r"\s{2,}", " ", t).strip()
|
| 58 |
+
return t or text.strip()
|
| 59 |
+
|
| 60 |
+
|
| 61 |
+
def generate(model, tok, prompt: str, *, max_tokens: int, temperature: float,
|
| 62 |
+
top_k: int, min_tokens: int, stop_after: int, context: int,
|
| 63 |
+
modulator=None, affect=None, mask=None) -> str:
|
| 64 |
+
ids = tok.encode(prompt)[-(model.max_seq_len - 1):]
|
| 65 |
+
if not ids:
|
| 66 |
+
return ""
|
| 67 |
+
n_prompt = len(ids)
|
| 68 |
+
eos_id = tok.get_special_id(tok.EOS_TOKEN)
|
| 69 |
+
|
| 70 |
+
with torch.no_grad():
|
| 71 |
+
for step in range(max_tokens):
|
| 72 |
+
ctx = torch.tensor(ids[-context:], dtype=torch.long)
|
| 73 |
+
logits = model.forward(ctx)[-1]
|
| 74 |
+
|
| 75 |
+
if modulator is not None:
|
| 76 |
+
affect.update_from_logits(logits, model.vocab_size)
|
| 77 |
+
# The modulator applies the temperature itself, so the sampler
|
| 78 |
+
# is called with 1.0 afterwards. Greedy therefore cannot be
|
| 79 |
+
# expressed as temperature 0 here: it is top_k = 1, exactly as
|
| 80 |
+
# the training repo's own evaluation harness does it. Passing
|
| 81 |
+
# temperature 0 and leaving top_k at 40 sampled instead —
|
| 82 |
+
# 'cos è un cane?' answered animale, persona and luce on three
|
| 83 |
+
# consecutive runs of what claimed to be greedy decoding.
|
| 84 |
+
logits = modulator.modulate(
|
| 85 |
+
logits, temperature if temperature > 0 else 1.0,
|
| 86 |
+
response_len = len(ids) - n_prompt,
|
| 87 |
+
eos_min_len = min_tokens + 1,
|
| 88 |
+
)
|
| 89 |
+
sample_temperature = 1.0
|
| 90 |
+
sample_k = 1 if temperature <= 0 else top_k
|
| 91 |
+
else:
|
| 92 |
+
sample_temperature = temperature
|
| 93 |
+
sample_k = top_k
|
| 94 |
+
|
| 95 |
+
logits = logits.clone()
|
| 96 |
+
if eos_id is not None and (len(ids) - n_prompt) < min_tokens \
|
| 97 |
+
and eos_id < logits.shape[0]:
|
| 98 |
+
logits[eos_id] = float("-inf")
|
| 99 |
+
if mask is not None:
|
| 100 |
+
logits[mask] = float("-inf")
|
| 101 |
+
|
| 102 |
+
if sample_temperature <= 0 or sample_k == 1:
|
| 103 |
+
next_id = int(torch.argmax(logits).item())
|
| 104 |
+
else:
|
| 105 |
+
next_id = int(sample_top_k(logits.numpy(), k=sample_k,
|
| 106 |
+
temperature=sample_temperature))
|
| 107 |
+
ids.append(next_id)
|
| 108 |
+
|
| 109 |
+
if eos_id is not None and next_id == eos_id and step >= 1:
|
| 110 |
+
break
|
| 111 |
+
|
| 112 |
+
# Soft stop on sentence-ending punctuation. Test the decoded token:
|
| 113 |
+
# multi-character tokens ('la!', 'no!') carry the terminator too.
|
| 114 |
+
if step >= stop_after:
|
| 115 |
+
try:
|
| 116 |
+
tail = tok.decode([next_id]).rstrip()
|
| 117 |
+
except Exception:
|
| 118 |
+
tail = "!" if next_id in (33, 46, 63) else ""
|
| 119 |
+
if tail.endswith(("!", ".", "?")) and (step >= 1 or len(tail) > 1):
|
| 120 |
+
break
|
| 121 |
+
|
| 122 |
+
# The EOS token that ended the loop is inside ids: decode() renders it as
|
| 123 |
+
# the literal '<|EOS|>', so drop every trailing special slot first.
|
| 124 |
+
out = ids[n_prompt:]
|
| 125 |
+
while out and tok.is_special(out[-1]):
|
| 126 |
+
out.pop()
|
| 127 |
+
return clean_response(tok.decode(out))
|
| 128 |
+
|
| 129 |
+
|
| 130 |
+
def load_affect(tok):
|
| 131 |
+
"""
|
| 132 |
+
Build the affective modulator if the modules are available. They live in
|
| 133 |
+
dynamic_model/exp_b/ in the repository and are copied into this package by
|
| 134 |
+
scripts/export_hf.py, so both layouts are tried. Returns (modulator,
|
| 135 |
+
affect), or (None, None) when neither is importable.
|
| 136 |
+
"""
|
| 137 |
+
try:
|
| 138 |
+
try:
|
| 139 |
+
from .affect_state import AffectState # published package
|
| 140 |
+
from .modulator import AffectModulator
|
| 141 |
+
except ImportError:
|
| 142 |
+
from dynamic_model.exp_b.affect_state import AffectState
|
| 143 |
+
from dynamic_model.exp_b.modulator import AffectModulator
|
| 144 |
+
except ImportError:
|
| 145 |
+
return None, None
|
| 146 |
+
affect = AffectState()
|
| 147 |
+
return AffectModulator(
|
| 148 |
+
affect, eos_token_id=tok.get_special_id(tok.EOS_TOKEN)), affect
|