Spaces:
Sleeping
Sleeping
Fixed app.py
#2
by Maxilicious20 - opened
app.py
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
|
|
|
|
|
|
|
|
| 1 |
import json
|
| 2 |
import re
|
| 3 |
import shutil
|
|
@@ -11,18 +14,15 @@ import spaces
|
|
| 11 |
import torch
|
| 12 |
from huggingface_hub import HfApi, hf_hub_download
|
| 13 |
from safetensors.torch import load_file
|
| 14 |
-
from tokenizers import Tokenizer
|
| 15 |
|
| 16 |
-
TITLE = "Promethean Studios Model Playground"
|
| 17 |
|
|
|
|
| 18 |
MODELS = {
|
| 19 |
"Talos 10M": "PrometheanStudio/talos-10m",
|
| 20 |
"Styx 100M": "PrometheanStudio/styx-100m",
|
| 21 |
}
|
| 22 |
|
| 23 |
-
|
| 24 |
-
# Do not change this to "main": the repository's current main branch has a
|
| 25 |
-
# different layout and is not guaranteed to load these older checkpoints.
|
| 26 |
TALOS_COMMIT = "e230fed9c18a5467e69ed0f15778a5b06d59cb5a"
|
| 27 |
TALOS_URL = "https://github.com/Promethean-Studios/talos.git"
|
| 28 |
CACHE = Path("/tmp/promethean-playground")
|
|
@@ -32,8 +32,7 @@ CACHE.mkdir(parents=True, exist_ok=True)
|
|
| 32 |
MODEL_CACHE = {}
|
| 33 |
TOKENIZER_CACHE = {}
|
| 34 |
|
| 35 |
-
|
| 36 |
-
# not match its own weights. A candidate is accepted only after strict loading.
|
| 37 |
FALLBACK_CONFIGS = {
|
| 38 |
"PrometheanStudio/talos-10m": {
|
| 39 |
"vocab_size": 1024,
|
|
@@ -73,10 +72,7 @@ def repo_files(repo_id):
|
|
| 73 |
|
| 74 |
|
| 75 |
def find_tokenizer_file(files):
|
| 76 |
-
candidates = [
|
| 77 |
-
name for name in files
|
| 78 |
-
if Path(name).name.lower() == "tokenizer.json"
|
| 79 |
-
]
|
| 80 |
if not candidates:
|
| 81 |
raise FileNotFoundError("No tokenizer.json was found in the model repository.")
|
| 82 |
candidates.sort(
|
|
@@ -90,98 +86,26 @@ def find_tokenizer_file(files):
|
|
| 90 |
return candidates[0]
|
| 91 |
|
| 92 |
|
| 93 |
-
def load_tokenizer(repo_id, files):
|
| 94 |
-
if repo_id in TOKENIZER_CACHE:
|
| 95 |
-
return TOKENIZER_CACHE[repo_id]
|
| 96 |
-
|
| 97 |
-
filename = find_tokenizer_file(files)
|
| 98 |
-
downloaded = hf_hub_download(
|
| 99 |
-
repo_id=repo_id,
|
| 100 |
-
filename=filename,
|
| 101 |
-
repo_type="model",
|
| 102 |
-
)
|
| 103 |
-
|
| 104 |
-
try:
|
| 105 |
-
with open(downloaded, "r", encoding="utf-8") as handle:
|
| 106 |
-
payload = json.load(handle)
|
| 107 |
-
except Exception as exc:
|
| 108 |
-
raise RuntimeError(f"Could not read {filename}: {exc}") from exc
|
| 109 |
-
|
| 110 |
-
if not isinstance(payload, dict):
|
| 111 |
-
raise RuntimeError(f"{filename} must contain a JSON object.")
|
| 112 |
-
|
| 113 |
-
# Do not blindly coerce the tokenizer schema. The previous version of
|
| 114 |
-
# this app changed numeric versions into strings, which can itself trigger
|
| 115 |
-
# a Rust deserialization error depending on the uploaded file/schema.
|
| 116 |
-
# Try the original file first, then only try the alternate version type
|
| 117 |
-
# when the parser specifically rejects the original serialization.
|
| 118 |
-
tokenizer = None
|
| 119 |
-
parser_errors = []
|
| 120 |
-
candidates = [("original", downloaded, payload)]
|
| 121 |
-
version = payload.get("version")
|
| 122 |
-
|
| 123 |
-
if isinstance(version, str):
|
| 124 |
-
try:
|
| 125 |
-
numeric_version = float(version)
|
| 126 |
-
if numeric_version.is_integer():
|
| 127 |
-
numeric_version = int(numeric_version)
|
| 128 |
-
alternate = dict(payload)
|
| 129 |
-
alternate["version"] = numeric_version
|
| 130 |
-
candidates.append(("numeric-version", None, alternate))
|
| 131 |
-
except (ValueError, OverflowError):
|
| 132 |
-
pass
|
| 133 |
-
elif isinstance(version, (int, float)) and not isinstance(version, bool):
|
| 134 |
-
alternate = dict(payload)
|
| 135 |
-
alternate["version"] = f"{version:.1f}" if isinstance(version, float) else f"{version}.0"
|
| 136 |
-
candidates.append(("string-version", None, alternate))
|
| 137 |
-
|
| 138 |
-
for candidate_name, candidate_path, candidate_payload in candidates:
|
| 139 |
-
try:
|
| 140 |
-
if candidate_path is not None:
|
| 141 |
-
tokenizer = Tokenizer.from_file(str(candidate_path))
|
| 142 |
-
else:
|
| 143 |
-
candidate_path = CACHE / (
|
| 144 |
-
f"{repo_id.replace('/', '_')}_tokenizer_{candidate_name}.json"
|
| 145 |
-
)
|
| 146 |
-
with open(candidate_path, "w", encoding="utf-8") as handle:
|
| 147 |
-
json.dump(candidate_payload, handle, ensure_ascii=False)
|
| 148 |
-
tokenizer = Tokenizer.from_file(str(candidate_path))
|
| 149 |
-
print(f"Tokenizer loaded using {candidate_name} serialization")
|
| 150 |
-
break
|
| 151 |
-
except Exception as exc:
|
| 152 |
-
parser_errors.append(f"{candidate_name}: {type(exc).__name__}: {exc}")
|
| 153 |
-
|
| 154 |
-
if tokenizer is None:
|
| 155 |
-
preview = json.dumps(payload, ensure_ascii=False)[:240]
|
| 156 |
-
raise RuntimeError(
|
| 157 |
-
f"Tokenizer parser rejected {repo_id}/{filename}. "
|
| 158 |
-
f"Top-level version={version!r}; beginning of parsed JSON={preview!r}. "
|
| 159 |
-
+ " | ".join(parser_errors)
|
| 160 |
-
)
|
| 161 |
-
|
| 162 |
-
TOKENIZER_CACHE[repo_id] = tokenizer
|
| 163 |
-
print(
|
| 164 |
-
f"Tokenizer ready: {repo_id}/{filename}; "
|
| 165 |
-
f"vocabulary entries={tokenizer.get_vocab_size(with_added_tokens=True)}"
|
| 166 |
-
)
|
| 167 |
-
return tokenizer
|
| 168 |
-
|
| 169 |
-
|
| 170 |
def ensure_talos_source():
|
| 171 |
-
|
| 172 |
marker = SOURCE / ".talos_commit"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 173 |
source_is_ready = (
|
| 174 |
-
marker.
|
| 175 |
and marker.read_text(encoding="utf-8").strip() == TALOS_COMMIT
|
| 176 |
-
and (SOURCE /
|
| 177 |
-
and (SOURCE / "model" / "config.py").is_file()
|
| 178 |
)
|
| 179 |
|
| 180 |
if not source_is_ready:
|
| 181 |
if SOURCE.exists():
|
| 182 |
shutil.rmtree(SOURCE)
|
| 183 |
SOURCE.mkdir(parents=True, exist_ok=True)
|
| 184 |
-
|
| 185 |
commands = [
|
| 186 |
["git", "init", str(SOURCE)],
|
| 187 |
["git", "-C", str(SOURCE), "remote", "add", "origin", TALOS_URL],
|
|
@@ -189,42 +113,81 @@ def ensure_talos_source():
|
|
| 189 |
["git", "-C", str(SOURCE), "checkout", "--detach", "FETCH_HEAD"],
|
| 190 |
]
|
| 191 |
for command in commands:
|
| 192 |
-
|
| 193 |
-
|
| 194 |
-
|
| 195 |
-
capture_output=True,
|
| 196 |
-
text=True,
|
| 197 |
-
timeout=240,
|
| 198 |
-
)
|
| 199 |
-
if result.returncode != 0:
|
| 200 |
-
details = (result.stderr or result.stdout or "No git output").strip()
|
| 201 |
-
raise RuntimeError(
|
| 202 |
-
f"Could not prepare Talos source ({' '.join(command)}): {details[-1200:]}"
|
| 203 |
)
|
| 204 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 205 |
marker.write_text(TALOS_COMMIT, encoding="utf-8")
|
| 206 |
-
print(f"Talos
|
| 207 |
|
| 208 |
-
# Do this even when source files were cached from a prior app process:
|
| 209 |
-
# /tmp may survive a restart while Python's sys.path does not.
|
| 210 |
source_prefix = str(SOURCE.resolve())
|
| 211 |
-
sys.path[:] = [
|
|
|
|
|
|
|
|
|
|
| 212 |
sys.path.insert(0, source_prefix)
|
| 213 |
|
| 214 |
-
|
| 215 |
-
# Never unload modules already coming from this pinned checkout.
|
| 216 |
for name, module in list(sys.modules.items()):
|
| 217 |
-
if not (
|
|
|
|
|
|
|
|
|
|
| 218 |
continue
|
| 219 |
module_file = getattr(module, "__file__", None)
|
| 220 |
try:
|
| 221 |
-
belongs_to_source =
|
|
|
|
|
|
|
| 222 |
except (OSError, ValueError):
|
| 223 |
belongs_to_source = False
|
| 224 |
if not belongs_to_source:
|
| 225 |
del sys.modules[name]
|
| 226 |
|
| 227 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 228 |
def config_dict_from(value):
|
| 229 |
if isinstance(value, dict):
|
| 230 |
for nested_key in ("model_config", "config", "model"):
|
|
@@ -240,11 +203,13 @@ def config_dict_from(value):
|
|
| 240 |
|
| 241 |
|
| 242 |
def config_candidates(repo_id, files, checkpoint):
|
| 243 |
-
# Only pass actual ModelConfig fields to the constructor. Hugging Face
|
| 244 |
-
# metadata such as "model_type" is not part of Talos ModelConfig.
|
| 245 |
from model.config import ModelConfig
|
| 246 |
|
| 247 |
allowed = {item.name for item in fields(ModelConfig)}
|
|
|
|
|
|
|
|
|
|
|
|
|
| 248 |
candidates = []
|
| 249 |
|
| 250 |
if "config.json" in files:
|
|
@@ -254,35 +219,24 @@ def config_candidates(repo_id, files, checkpoint):
|
|
| 254 |
raw = json.load(handle)
|
| 255 |
normalized = config_dict_from(raw)
|
| 256 |
if normalized:
|
| 257 |
-
clean = {key:
|
| 258 |
-
required = {
|
| 259 |
-
"vocab_size", "max_seq_len", "hidden_size", "num_layers",
|
| 260 |
-
"num_attention_heads", "num_kv_heads",
|
| 261 |
-
}
|
| 262 |
if required.issubset(clean):
|
| 263 |
candidates.append(("repository config.json", clean))
|
| 264 |
except Exception as exc:
|
| 265 |
-
print(f"
|
| 266 |
|
| 267 |
if isinstance(checkpoint, dict):
|
| 268 |
for key in ("config", "model_config"):
|
| 269 |
raw = config_dict_from(checkpoint.get(key))
|
| 270 |
if raw:
|
| 271 |
-
clean = {name:
|
| 272 |
-
required = {
|
| 273 |
-
"vocab_size", "max_seq_len", "hidden_size", "num_layers",
|
| 274 |
-
"num_attention_heads", "num_kv_heads",
|
| 275 |
-
}
|
| 276 |
if required.issubset(clean):
|
| 277 |
candidates.append((f"checkpoint {key}", clean))
|
| 278 |
|
| 279 |
-
|
| 280 |
-
|
| 281 |
-
candidates.append(("known model preset", fallback))
|
| 282 |
|
| 283 |
-
|
| 284 |
-
unique = []
|
| 285 |
-
seen = set()
|
| 286 |
for source, cfg in candidates:
|
| 287 |
signature = json.dumps(cfg, sort_keys=True, default=str)
|
| 288 |
if signature not in seen:
|
|
@@ -296,7 +250,7 @@ def read_weights(repo_id, files):
|
|
| 296 |
if safetensors_files:
|
| 297 |
safetensors_files.sort(
|
| 298 |
key=lambda name: (
|
| 299 |
-
0 if
|
| 300 |
len(name),
|
| 301 |
name,
|
| 302 |
)
|
|
@@ -306,14 +260,11 @@ def read_weights(repo_id, files):
|
|
| 306 |
print(f"Loading inference weights: {repo_id}/{filename}")
|
| 307 |
return load_file(path, device="cpu"), None
|
| 308 |
|
| 309 |
-
checkpoint_files = [
|
| 310 |
-
name for name in files
|
| 311 |
-
if name.endswith((".pt", ".pth", ".bin"))
|
| 312 |
-
]
|
| 313 |
if not checkpoint_files:
|
| 314 |
-
raise FileNotFoundError(f"No
|
| 315 |
|
| 316 |
-
def
|
| 317 |
basename = Path(name).name.lower()
|
| 318 |
match = re.search(r"step[-_](\d+)", basename)
|
| 319 |
step = int(match.group(1)) if match else -1
|
|
@@ -325,31 +276,27 @@ def read_weights(repo_id, files):
|
|
| 325 |
name,
|
| 326 |
)
|
| 327 |
|
| 328 |
-
checkpoint_files.sort(key=
|
| 329 |
filename = checkpoint_files[0]
|
| 330 |
path = hf_hub_download(repo_id=repo_id, filename=filename, repo_type="model")
|
| 331 |
print(f"Loading PyTorch checkpoint: {repo_id}/{filename}")
|
|
|
|
| 332 |
try:
|
| 333 |
checkpoint = torch.load(path, map_location="cpu", weights_only=True)
|
| 334 |
-
except TypeError:
|
| 335 |
-
# Older PyTorch releases do not expose the weights_only parameter.
|
| 336 |
-
checkpoint = torch.load(path, map_location="cpu")
|
| 337 |
except Exception as safe_exc:
|
| 338 |
-
|
| 339 |
-
|
| 340 |
-
# objects that weights_only=True rejects. Keep safetensors as the first
|
| 341 |
-
# choice; only use the legacy unpickler for these explicit repositories.
|
| 342 |
if repo_id not in MODELS.values():
|
| 343 |
raise RuntimeError(
|
| 344 |
-
f"Safe loading
|
| 345 |
-
"
|
| 346 |
) from safe_exc
|
|
|
|
| 347 |
try:
|
| 348 |
checkpoint = torch.load(path, map_location="cpu", weights_only=False)
|
| 349 |
except Exception as legacy_exc:
|
| 350 |
raise RuntimeError(
|
| 351 |
-
f"Could not load {filename}
|
| 352 |
-
f"{safe_exc}; legacy load failed with {type(legacy_exc).__name__}: {legacy_exc})."
|
| 353 |
) from legacy_exc
|
| 354 |
|
| 355 |
state = checkpoint
|
|
@@ -360,27 +307,46 @@ def read_weights(repo_id, files):
|
|
| 360 |
state = value
|
| 361 |
break
|
| 362 |
if not isinstance(state, dict) or not state or not all(torch.is_tensor(v) for v in state.values()):
|
| 363 |
-
raise RuntimeError(f"{filename} did not contain a
|
| 364 |
return state, checkpoint if isinstance(checkpoint, dict) else None
|
| 365 |
|
| 366 |
|
| 367 |
def normalize_state_dict(state):
|
| 368 |
-
# Strip common wrapper prefixes only when they exist on every tensor key.
|
| 369 |
for prefix in ("module.", "_orig_mod."):
|
| 370 |
if state and all(key.startswith(prefix) for key in state):
|
| 371 |
state = {key[len(prefix):]: value for key, value in state.items()}
|
| 372 |
return state
|
| 373 |
|
| 374 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 375 |
def load_model(repo_id):
|
| 376 |
if repo_id in MODEL_CACHE:
|
| 377 |
return MODEL_CACHE[repo_id]
|
| 378 |
|
| 379 |
files = repo_files(repo_id)
|
| 380 |
-
tokenizer = load_tokenizer(repo_id, files)
|
| 381 |
-
# Prepare the class modules before reading a legacy .pt checkpoint, which
|
| 382 |
-
# may contain a pickled ModelConfig object from this exact code revision.
|
| 383 |
ensure_talos_source()
|
|
|
|
|
|
|
| 384 |
state, checkpoint = read_weights(repo_id, files)
|
| 385 |
state = normalize_state_dict(state)
|
| 386 |
|
|
@@ -393,59 +359,59 @@ def load_model(repo_id):
|
|
| 393 |
config = ModelConfig(**raw_config).derive()
|
| 394 |
model = TalosGPT(config)
|
| 395 |
model.load_state_dict(state, strict=True)
|
|
|
|
| 396 |
model.eval()
|
| 397 |
-
|
| 398 |
-
vocab = tokenizer.get_vocab()
|
| 399 |
-
max_token_id = max(vocab.values(), default=-1)
|
| 400 |
-
if max_token_id >= config.vocab_size:
|
| 401 |
-
raise RuntimeError(
|
| 402 |
-
f"Tokenizer emits token id {max_token_id}, but model vocabulary has "
|
| 403 |
-
f"only {config.vocab_size} rows. This repository's tokenizer and weights are incompatible."
|
| 404 |
-
)
|
| 405 |
-
|
| 406 |
-
count = model.num_parameters()
|
| 407 |
print(
|
| 408 |
-
f"Loaded {repo_id}
|
| 409 |
-
f"
|
|
|
|
| 410 |
)
|
| 411 |
-
MODEL_CACHE[repo_id] = (model, tokenizer)
|
| 412 |
-
return
|
| 413 |
except Exception as exc:
|
| 414 |
attempts.append(
|
| 415 |
-
f"{source} ({raw_config.get('hidden_size')}
|
| 416 |
-
f"{raw_config.get('num_layers')}
|
|
|
|
| 417 |
)
|
| 418 |
|
| 419 |
raise RuntimeError(
|
| 420 |
-
f"The weights in {repo_id} did not strictly match
|
| 421 |
-
"This is a real checkpoint/config mismatch, not a Transformers model_type problem.\n"
|
| 422 |
+ "\n".join(attempts)
|
| 423 |
)
|
| 424 |
|
| 425 |
|
| 426 |
def get_model(label):
|
|
|
|
|
|
|
| 427 |
return load_model(MODELS[label])
|
| 428 |
|
| 429 |
|
| 430 |
-
def choose_next_token(scores, history, temperature, top_p, repetition_penalty):
|
| 431 |
scores = scores.float().clone()
|
| 432 |
|
| 433 |
if repetition_penalty > 1.0:
|
| 434 |
for token_id in set(history[-128:]):
|
| 435 |
-
if 0 <= token_id < scores.
|
| 436 |
if scores[token_id] < 0:
|
| 437 |
scores[token_id] *= repetition_penalty
|
| 438 |
else:
|
| 439 |
scores[token_id] /= repetition_penalty
|
| 440 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 441 |
if temperature <= 0:
|
| 442 |
return scores.argmax(dim=-1).view(1, 1)
|
| 443 |
|
| 444 |
probabilities = torch.softmax(scores / max(float(temperature), 1e-5), dim=-1)
|
|
|
|
|
|
|
|
|
|
| 445 |
sorted_probs, sorted_ids = torch.sort(probabilities, descending=True)
|
| 446 |
cumulative = torch.cumsum(sorted_probs, dim=-1)
|
| 447 |
-
|
| 448 |
-
# Keep the first token that crosses the nucleus threshold.
|
| 449 |
remove = cumulative > float(top_p)
|
| 450 |
remove[1:] = remove[:-1].clone()
|
| 451 |
remove[0] = False
|
|
@@ -453,8 +419,7 @@ def choose_next_token(scores, history, temperature, top_p, repetition_penalty):
|
|
| 453 |
total = sorted_probs.sum()
|
| 454 |
if not torch.isfinite(total) or total <= 0:
|
| 455 |
return scores.argmax(dim=-1).view(1, 1)
|
| 456 |
-
|
| 457 |
-
sampled_index = torch.multinomial(sorted_probs, num_samples=1)
|
| 458 |
return sorted_ids.gather(0, sampled_index).view(1, 1)
|
| 459 |
|
| 460 |
|
|
@@ -464,25 +429,26 @@ def generate(label, prompt, max_new_tokens, temperature, top_p, repetition_penal
|
|
| 464 |
raise gr.Error("Enter a prompt first.")
|
| 465 |
|
| 466 |
try:
|
| 467 |
-
model, tokenizer = get_model(label)
|
| 468 |
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
|
| 469 |
model = model.to(device).eval()
|
| 470 |
|
| 471 |
-
|
| 472 |
-
|
| 473 |
-
|
|
|
|
| 474 |
|
| 475 |
max_new_tokens = int(max_new_tokens)
|
| 476 |
max_seq_len = int(model.config.max_seq_len)
|
| 477 |
-
if
|
| 478 |
raise gr.Error(f"New-token count must be between 1 and {max_seq_len - 1}.")
|
|
|
|
|
|
|
|
|
|
| 479 |
|
| 480 |
-
prompt_ids = encoded.ids
|
| 481 |
-
# Reserve room for all requested output tokens, retaining the latest
|
| 482 |
-
# prompt tokens if the input is longer than the model's context window.
|
| 483 |
-
keep = max_seq_len - max_new_tokens
|
| 484 |
-
prompt_ids = prompt_ids[-keep:]
|
| 485 |
input_ids = torch.tensor([prompt_ids], dtype=torch.long, device=device)
|
|
|
|
|
|
|
| 486 |
history = list(prompt_ids)
|
| 487 |
generated_ids = []
|
| 488 |
|
|
@@ -498,18 +464,16 @@ def generate(label, prompt, max_new_tokens, temperature, top_p, repetition_penal
|
|
| 498 |
float(temperature),
|
| 499 |
float(top_p),
|
| 500 |
float(repetition_penalty),
|
|
|
|
| 501 |
)
|
| 502 |
token_id = int(next_token.item())
|
|
|
|
|
|
|
| 503 |
generated_ids.append(token_id)
|
| 504 |
history.append(token_id)
|
| 505 |
-
|
| 506 |
if step + 1 < max_new_tokens:
|
| 507 |
-
|
| 508 |
-
|
| 509 |
-
cache,
|
| 510 |
-
next_token.to(device),
|
| 511 |
-
position=input_ids.shape[1] + step,
|
| 512 |
-
)
|
| 513 |
|
| 514 |
return tokenizer.decode(generated_ids)
|
| 515 |
|
|
@@ -517,9 +481,7 @@ def generate(label, prompt, max_new_tokens, temperature, top_p, repetition_penal
|
|
| 517 |
raise
|
| 518 |
except Exception as exc:
|
| 519 |
print(f"Generation failure for {label}: {type(exc).__name__}: {exc}")
|
| 520 |
-
raise gr.Error(
|
| 521 |
-
f"Could not load or run {label}: {type(exc).__name__}: {exc}"
|
| 522 |
-
) from exc
|
| 523 |
|
| 524 |
|
| 525 |
with gr.Blocks(title=TITLE) as demo:
|
|
@@ -531,16 +493,10 @@ with gr.Blocks(title=TITLE) as demo:
|
|
| 531 |
|
| 532 |
with gr.Row():
|
| 533 |
model_choice = gr.Dropdown(
|
| 534 |
-
choices=list(MODELS.keys()),
|
| 535 |
-
value="Styx 100M",
|
| 536 |
-
label="Model",
|
| 537 |
)
|
| 538 |
max_tokens = gr.Slider(
|
| 539 |
-
minimum=16,
|
| 540 |
-
maximum=256,
|
| 541 |
-
value=96,
|
| 542 |
-
step=16,
|
| 543 |
-
label="New tokens",
|
| 544 |
)
|
| 545 |
|
| 546 |
prompt = gr.Textbox(
|
|
@@ -552,24 +508,14 @@ with gr.Blocks(title=TITLE) as demo:
|
|
| 552 |
with gr.Accordion("Generation settings", open=False):
|
| 553 |
with gr.Row():
|
| 554 |
temperature = gr.Slider(
|
| 555 |
-
minimum=0,
|
| 556 |
-
maximum=1.5,
|
| 557 |
-
value=0.8,
|
| 558 |
-
step=0.05,
|
| 559 |
label="Temperature (0 = greedy)",
|
| 560 |
)
|
| 561 |
top_p = gr.Slider(
|
| 562 |
-
minimum=0.1,
|
| 563 |
-
maximum=1.0,
|
| 564 |
-
value=0.9,
|
| 565 |
-
step=0.05,
|
| 566 |
-
label="Top-p",
|
| 567 |
)
|
| 568 |
repetition = gr.Slider(
|
| 569 |
-
minimum=1.0,
|
| 570 |
-
maximum=1.5,
|
| 571 |
-
value=1.1,
|
| 572 |
-
step=0.05,
|
| 573 |
label="Repetition penalty",
|
| 574 |
)
|
| 575 |
|
|
@@ -580,10 +526,10 @@ with gr.Blocks(title=TITLE) as demo:
|
|
| 580 |
prompt.submit(fn=generate, inputs=inputs, outputs=output)
|
| 581 |
|
| 582 |
gr.Markdown(
|
| 583 |
-
"**First use:** the Space downloads model files and pins the matching
|
| 584 |
-
"Generation quality is experimental."
|
| 585 |
)
|
| 586 |
|
| 587 |
|
| 588 |
if __name__ == "__main__":
|
| 589 |
-
demo.queue().launch()
|
|
|
|
| 1 |
+
|
| 2 |
+
|
| 3 |
+
|
| 4 |
import json
|
| 5 |
import re
|
| 6 |
import shutil
|
|
|
|
| 14 |
import torch
|
| 15 |
from huggingface_hub import HfApi, hf_hub_download
|
| 16 |
from safetensors.torch import load_file
|
|
|
|
| 17 |
|
|
|
|
| 18 |
|
| 19 |
+
TITLE = "Promethean Studios Model Playground"
|
| 20 |
MODELS = {
|
| 21 |
"Talos 10M": "PrometheanStudio/talos-10m",
|
| 22 |
"Styx 100M": "PrometheanStudio/styx-100m",
|
| 23 |
}
|
| 24 |
|
| 25 |
+
|
|
|
|
|
|
|
| 26 |
TALOS_COMMIT = "e230fed9c18a5467e69ed0f15778a5b06d59cb5a"
|
| 27 |
TALOS_URL = "https://github.com/Promethean-Studios/talos.git"
|
| 28 |
CACHE = Path("/tmp/promethean-playground")
|
|
|
|
| 32 |
MODEL_CACHE = {}
|
| 33 |
TOKENIZER_CACHE = {}
|
| 34 |
|
| 35 |
+
|
|
|
|
| 36 |
FALLBACK_CONFIGS = {
|
| 37 |
"PrometheanStudio/talos-10m": {
|
| 38 |
"vocab_size": 1024,
|
|
|
|
| 72 |
|
| 73 |
|
| 74 |
def find_tokenizer_file(files):
|
| 75 |
+
candidates = [name for name in files if Path(name).name.lower() == "tokenizer.json"]
|
|
|
|
|
|
|
|
|
|
| 76 |
if not candidates:
|
| 77 |
raise FileNotFoundError("No tokenizer.json was found in the model repository.")
|
| 78 |
candidates.sort(
|
|
|
|
| 86 |
return candidates[0]
|
| 87 |
|
| 88 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 89 |
def ensure_talos_source():
|
| 90 |
+
|
| 91 |
marker = SOURCE / ".talos_commit"
|
| 92 |
+
required_files = (
|
| 93 |
+
"model/gpt.py",
|
| 94 |
+
"model/config.py",
|
| 95 |
+
"inference/generate.py",
|
| 96 |
+
"tokenizer/tokenizer.py",
|
| 97 |
+
"tokenizer/vocab.py",
|
| 98 |
+
)
|
| 99 |
source_is_ready = (
|
| 100 |
+
marker.is_file()
|
| 101 |
and marker.read_text(encoding="utf-8").strip() == TALOS_COMMIT
|
| 102 |
+
and all((SOURCE / filename).is_file() for filename in required_files)
|
|
|
|
| 103 |
)
|
| 104 |
|
| 105 |
if not source_is_ready:
|
| 106 |
if SOURCE.exists():
|
| 107 |
shutil.rmtree(SOURCE)
|
| 108 |
SOURCE.mkdir(parents=True, exist_ok=True)
|
|
|
|
| 109 |
commands = [
|
| 110 |
["git", "init", str(SOURCE)],
|
| 111 |
["git", "-C", str(SOURCE), "remote", "add", "origin", TALOS_URL],
|
|
|
|
| 113 |
["git", "-C", str(SOURCE), "checkout", "--detach", "FETCH_HEAD"],
|
| 114 |
]
|
| 115 |
for command in commands:
|
| 116 |
+
try:
|
| 117 |
+
result = subprocess.run(
|
| 118 |
+
command, check=False, capture_output=True, text=True, timeout=240
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 119 |
)
|
| 120 |
+
except (OSError, subprocess.TimeoutExpired) as exc:
|
| 121 |
+
raise RuntimeError(f"Could not prepare Talos source: {exc}") from exc
|
| 122 |
+
if result.returncode != 0:
|
| 123 |
+
detail = (result.stderr or result.stdout or "No git output").strip()
|
| 124 |
+
raise RuntimeError(f"Talos git checkout failed: {detail[-1200:]}")
|
| 125 |
+
if not all((SOURCE / filename).is_file() for filename in required_files):
|
| 126 |
+
raise RuntimeError("The pinned Talos revision is missing expected source files.")
|
| 127 |
marker.write_text(TALOS_COMMIT, encoding="utf-8")
|
| 128 |
+
print(f"Talos source pinned to {TALOS_COMMIT}")
|
| 129 |
|
|
|
|
|
|
|
| 130 |
source_prefix = str(SOURCE.resolve())
|
| 131 |
+
sys.path[:] = [
|
| 132 |
+
entry for entry in sys.path
|
| 133 |
+
if str(Path(entry or ".").resolve()) != source_prefix
|
| 134 |
+
]
|
| 135 |
sys.path.insert(0, source_prefix)
|
| 136 |
|
| 137 |
+
|
|
|
|
| 138 |
for name, module in list(sys.modules.items()):
|
| 139 |
+
if not (
|
| 140 |
+
name in ("model", "inference", "tokenizer")
|
| 141 |
+
or name.startswith(("model.", "inference.", "tokenizer."))
|
| 142 |
+
):
|
| 143 |
continue
|
| 144 |
module_file = getattr(module, "__file__", None)
|
| 145 |
try:
|
| 146 |
+
belongs_to_source = bool(
|
| 147 |
+
module_file and Path(module_file).resolve().is_relative_to(SOURCE.resolve())
|
| 148 |
+
)
|
| 149 |
except (OSError, ValueError):
|
| 150 |
belongs_to_source = False
|
| 151 |
if not belongs_to_source:
|
| 152 |
del sys.modules[name]
|
| 153 |
|
| 154 |
|
| 155 |
+
def load_tokenizer(repo_id, files):
|
| 156 |
+
|
| 157 |
+
if repo_id in TOKENIZER_CACHE:
|
| 158 |
+
return TOKENIZER_CACHE[repo_id]
|
| 159 |
+
|
| 160 |
+
ensure_talos_source()
|
| 161 |
+
from tokenizer.tokenizer import ByteLevelBPETokenizer
|
| 162 |
+
|
| 163 |
+
filename = find_tokenizer_file(files)
|
| 164 |
+
downloaded = hf_hub_download(repo_id=repo_id, filename=filename, repo_type="model")
|
| 165 |
+
|
| 166 |
+
with open(downloaded, "r", encoding="utf-8") as handle:
|
| 167 |
+
payload = json.load(handle)
|
| 168 |
+
if not isinstance(payload, dict) or payload.get("type") != "ByteLevelBPE":
|
| 169 |
+
raise RuntimeError(
|
| 170 |
+
f"{repo_id}/{filename} is not the native Talos ByteLevelBPE format. "
|
| 171 |
+
"A different tokenizer loader is required."
|
| 172 |
+
)
|
| 173 |
+
if payload.get("version") != 1:
|
| 174 |
+
raise RuntimeError(
|
| 175 |
+
f"Unsupported Talos tokenizer version {payload.get('version')!r} "
|
| 176 |
+
f"in {repo_id}/{filename}."
|
| 177 |
+
)
|
| 178 |
+
|
| 179 |
+
tokenizer = ByteLevelBPETokenizer.from_file(str(downloaded))
|
| 180 |
+
|
| 181 |
+
print(
|
| 182 |
+
f"Tokenizer ready: {repo_id}/{filename}; "
|
| 183 |
+
f"defined tokens={tokenizer.vocab_size}, "
|
| 184 |
+
f"configured vocabulary={tokenizer.config.vocab_size}, "
|
| 185 |
+
f"merges={tokenizer.merge_count}"
|
| 186 |
+
)
|
| 187 |
+
TOKENIZER_CACHE[repo_id] = tokenizer
|
| 188 |
+
return tokenizer
|
| 189 |
+
|
| 190 |
+
|
| 191 |
def config_dict_from(value):
|
| 192 |
if isinstance(value, dict):
|
| 193 |
for nested_key in ("model_config", "config", "model"):
|
|
|
|
| 203 |
|
| 204 |
|
| 205 |
def config_candidates(repo_id, files, checkpoint):
|
|
|
|
|
|
|
| 206 |
from model.config import ModelConfig
|
| 207 |
|
| 208 |
allowed = {item.name for item in fields(ModelConfig)}
|
| 209 |
+
required = {
|
| 210 |
+
"vocab_size", "max_seq_len", "hidden_size", "num_layers",
|
| 211 |
+
"num_attention_heads", "num_kv_heads",
|
| 212 |
+
}
|
| 213 |
candidates = []
|
| 214 |
|
| 215 |
if "config.json" in files:
|
|
|
|
| 219 |
raw = json.load(handle)
|
| 220 |
normalized = config_dict_from(raw)
|
| 221 |
if normalized:
|
| 222 |
+
clean = {key: value for key, value in normalized.items() if key in allowed}
|
|
|
|
|
|
|
|
|
|
|
|
|
| 223 |
if required.issubset(clean):
|
| 224 |
candidates.append(("repository config.json", clean))
|
| 225 |
except Exception as exc:
|
| 226 |
+
print(f"Could not use config.json for {repo_id}: {exc}")
|
| 227 |
|
| 228 |
if isinstance(checkpoint, dict):
|
| 229 |
for key in ("config", "model_config"):
|
| 230 |
raw = config_dict_from(checkpoint.get(key))
|
| 231 |
if raw:
|
| 232 |
+
clean = {name: value for name, value in raw.items() if name in allowed}
|
|
|
|
|
|
|
|
|
|
|
|
|
| 233 |
if required.issubset(clean):
|
| 234 |
candidates.append((f"checkpoint {key}", clean))
|
| 235 |
|
| 236 |
+
if repo_id in FALLBACK_CONFIGS:
|
| 237 |
+
candidates.append(("known model preset", FALLBACK_CONFIGS[repo_id]))
|
|
|
|
| 238 |
|
| 239 |
+
unique, seen = [], set()
|
|
|
|
|
|
|
| 240 |
for source, cfg in candidates:
|
| 241 |
signature = json.dumps(cfg, sort_keys=True, default=str)
|
| 242 |
if signature not in seen:
|
|
|
|
| 250 |
if safetensors_files:
|
| 251 |
safetensors_files.sort(
|
| 252 |
key=lambda name: (
|
| 253 |
+
0 if name == "model.safetensors" else 1,
|
| 254 |
len(name),
|
| 255 |
name,
|
| 256 |
)
|
|
|
|
| 260 |
print(f"Loading inference weights: {repo_id}/{filename}")
|
| 261 |
return load_file(path, device="cpu"), None
|
| 262 |
|
| 263 |
+
checkpoint_files = [name for name in files if name.endswith((".pt", ".pth", ".bin"))]
|
|
|
|
|
|
|
|
|
|
| 264 |
if not checkpoint_files:
|
| 265 |
+
raise FileNotFoundError(f"No model weights found in {repo_id}.")
|
| 266 |
|
| 267 |
+
def priority(name):
|
| 268 |
basename = Path(name).name.lower()
|
| 269 |
match = re.search(r"step[-_](\d+)", basename)
|
| 270 |
step = int(match.group(1)) if match else -1
|
|
|
|
| 276 |
name,
|
| 277 |
)
|
| 278 |
|
| 279 |
+
checkpoint_files.sort(key=priority)
|
| 280 |
filename = checkpoint_files[0]
|
| 281 |
path = hf_hub_download(repo_id=repo_id, filename=filename, repo_type="model")
|
| 282 |
print(f"Loading PyTorch checkpoint: {repo_id}/{filename}")
|
| 283 |
+
|
| 284 |
try:
|
| 285 |
checkpoint = torch.load(path, map_location="cpu", weights_only=True)
|
|
|
|
|
|
|
|
|
|
| 286 |
except Exception as safe_exc:
|
| 287 |
+
|
| 288 |
+
|
|
|
|
|
|
|
| 289 |
if repo_id not in MODELS.values():
|
| 290 |
raise RuntimeError(
|
| 291 |
+
f"Safe checkpoint loading failed: {safe_exc}. "
|
| 292 |
+
"Publish a model.safetensors file instead."
|
| 293 |
) from safe_exc
|
| 294 |
+
print(f"Safe loading failed for {filename}; trying legacy checkpoint reader")
|
| 295 |
try:
|
| 296 |
checkpoint = torch.load(path, map_location="cpu", weights_only=False)
|
| 297 |
except Exception as legacy_exc:
|
| 298 |
raise RuntimeError(
|
| 299 |
+
f"Could not load {filename}: safe={safe_exc}; legacy={legacy_exc}"
|
|
|
|
| 300 |
) from legacy_exc
|
| 301 |
|
| 302 |
state = checkpoint
|
|
|
|
| 307 |
state = value
|
| 308 |
break
|
| 309 |
if not isinstance(state, dict) or not state or not all(torch.is_tensor(v) for v in state.values()):
|
| 310 |
+
raise RuntimeError(f"{filename} did not contain a model state dictionary.")
|
| 311 |
return state, checkpoint if isinstance(checkpoint, dict) else None
|
| 312 |
|
| 313 |
|
| 314 |
def normalize_state_dict(state):
|
|
|
|
| 315 |
for prefix in ("module.", "_orig_mod."):
|
| 316 |
if state and all(key.startswith(prefix) for key in state):
|
| 317 |
state = {key[len(prefix):]: value for key, value in state.items()}
|
| 318 |
return state
|
| 319 |
|
| 320 |
|
| 321 |
+
def allowed_token_ids(tokenizer, model_vocab_size):
|
| 322 |
+
|
| 323 |
+
|
| 324 |
+
text_ids = set(tokenizer.vocab.id_to_bytes.keys())
|
| 325 |
+
valid_ids = text_ids | {int(tokenizer.eos_id)}
|
| 326 |
+
if not valid_ids or min(valid_ids) < 0 or max(valid_ids) >= model_vocab_size:
|
| 327 |
+
raise RuntimeError(
|
| 328 |
+
"Tokenizer contains IDs outside the model vocabulary: "
|
| 329 |
+
f"largest={max(valid_ids, default=-1)}, model_vocab={model_vocab_size}"
|
| 330 |
+
)
|
| 331 |
+
|
| 332 |
+
|
| 333 |
+
all_ids = text_ids | set(tokenizer.special_id_map().values())
|
| 334 |
+
if min(all_ids, default=0) < 0 or max(all_ids, default=-1) >= model_vocab_size:
|
| 335 |
+
raise RuntimeError(
|
| 336 |
+
"Tokenizer special-token IDs exceed the model vocabulary: "
|
| 337 |
+
f"largest={max(all_ids)}, model_vocab={model_vocab_size}"
|
| 338 |
+
)
|
| 339 |
+
return frozenset(valid_ids)
|
| 340 |
+
|
| 341 |
+
|
| 342 |
def load_model(repo_id):
|
| 343 |
if repo_id in MODEL_CACHE:
|
| 344 |
return MODEL_CACHE[repo_id]
|
| 345 |
|
| 346 |
files = repo_files(repo_id)
|
|
|
|
|
|
|
|
|
|
| 347 |
ensure_talos_source()
|
| 348 |
+
tokenizer = load_tokenizer(repo_id, files)
|
| 349 |
+
|
| 350 |
state, checkpoint = read_weights(repo_id, files)
|
| 351 |
state = normalize_state_dict(state)
|
| 352 |
|
|
|
|
| 359 |
config = ModelConfig(**raw_config).derive()
|
| 360 |
model = TalosGPT(config)
|
| 361 |
model.load_state_dict(state, strict=True)
|
| 362 |
+
valid_ids = allowed_token_ids(tokenizer, config.vocab_size)
|
| 363 |
model.eval()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 364 |
print(
|
| 365 |
+
f"Loaded {repo_id} from {source}: "
|
| 366 |
+
f"{model.num_parameters():,} parameters, "
|
| 367 |
+
f"context={config.max_seq_len}, generatable_ids={len(valid_ids)}"
|
| 368 |
)
|
| 369 |
+
MODEL_CACHE[repo_id] = (model, tokenizer, valid_ids)
|
| 370 |
+
return MODEL_CACHE[repo_id]
|
| 371 |
except Exception as exc:
|
| 372 |
attempts.append(
|
| 373 |
+
f"{source} (hidden={raw_config.get('hidden_size')}, "
|
| 374 |
+
f"layers={raw_config.get('num_layers')}): "
|
| 375 |
+
f"{type(exc).__name__}: {str(exc)[:700]}"
|
| 376 |
)
|
| 377 |
|
| 378 |
raise RuntimeError(
|
| 379 |
+
f"The weights in {repo_id} did not strictly match any Talos config.\n"
|
|
|
|
| 380 |
+ "\n".join(attempts)
|
| 381 |
)
|
| 382 |
|
| 383 |
|
| 384 |
def get_model(label):
|
| 385 |
+
if label not in MODELS:
|
| 386 |
+
raise ValueError(f"Unknown model selection: {label!r}")
|
| 387 |
return load_model(MODELS[label])
|
| 388 |
|
| 389 |
|
| 390 |
+
def choose_next_token(scores, history, temperature, top_p, repetition_penalty, allowed_mask):
|
| 391 |
scores = scores.float().clone()
|
| 392 |
|
| 393 |
if repetition_penalty > 1.0:
|
| 394 |
for token_id in set(history[-128:]):
|
| 395 |
+
if 0 <= token_id < scores.numel():
|
| 396 |
if scores[token_id] < 0:
|
| 397 |
scores[token_id] *= repetition_penalty
|
| 398 |
else:
|
| 399 |
scores[token_id] /= repetition_penalty
|
| 400 |
|
| 401 |
+
scores = scores.masked_fill(~allowed_mask, float("-inf"))
|
| 402 |
+
scores = torch.where(torch.isnan(scores), -torch.inf, scores)
|
| 403 |
+
if not torch.isfinite(scores).any():
|
| 404 |
+
raise RuntimeError("The model produced no finite logits for decodable tokens.")
|
| 405 |
+
|
| 406 |
if temperature <= 0:
|
| 407 |
return scores.argmax(dim=-1).view(1, 1)
|
| 408 |
|
| 409 |
probabilities = torch.softmax(scores / max(float(temperature), 1e-5), dim=-1)
|
| 410 |
+
if not torch.isfinite(probabilities).all():
|
| 411 |
+
raise RuntimeError("Model output probabilities contain NaN or infinity.")
|
| 412 |
+
|
| 413 |
sorted_probs, sorted_ids = torch.sort(probabilities, descending=True)
|
| 414 |
cumulative = torch.cumsum(sorted_probs, dim=-1)
|
|
|
|
|
|
|
| 415 |
remove = cumulative > float(top_p)
|
| 416 |
remove[1:] = remove[:-1].clone()
|
| 417 |
remove[0] = False
|
|
|
|
| 419 |
total = sorted_probs.sum()
|
| 420 |
if not torch.isfinite(total) or total <= 0:
|
| 421 |
return scores.argmax(dim=-1).view(1, 1)
|
| 422 |
+
sampled_index = torch.multinomial(sorted_probs / total, num_samples=1)
|
|
|
|
| 423 |
return sorted_ids.gather(0, sampled_index).view(1, 1)
|
| 424 |
|
| 425 |
|
|
|
|
| 429 |
raise gr.Error("Enter a prompt first.")
|
| 430 |
|
| 431 |
try:
|
| 432 |
+
model, tokenizer, valid_ids = get_model(label)
|
| 433 |
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
|
| 434 |
model = model.to(device).eval()
|
| 435 |
|
| 436 |
+
|
| 437 |
+
prompt_ids = tokenizer.encode(prompt)
|
| 438 |
+
if not prompt_ids:
|
| 439 |
+
raise gr.Error("The tokenizer returned no tokens for the prompt.")
|
| 440 |
|
| 441 |
max_new_tokens = int(max_new_tokens)
|
| 442 |
max_seq_len = int(model.config.max_seq_len)
|
| 443 |
+
if not 1 <= max_new_tokens < max_seq_len:
|
| 444 |
raise gr.Error(f"New-token count must be between 1 and {max_seq_len - 1}.")
|
| 445 |
+
prompt_ids = prompt_ids[-(max_seq_len - max_new_tokens):]
|
| 446 |
+
if min(prompt_ids) < 0 or max(prompt_ids) >= model.config.vocab_size:
|
| 447 |
+
raise RuntimeError("Encoded prompt contains token IDs outside the model vocabulary.")
|
| 448 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 449 |
input_ids = torch.tensor([prompt_ids], dtype=torch.long, device=device)
|
| 450 |
+
allowed_mask = torch.zeros(model.config.vocab_size, dtype=torch.bool, device=device)
|
| 451 |
+
allowed_mask[list(valid_ids)] = True
|
| 452 |
history = list(prompt_ids)
|
| 453 |
generated_ids = []
|
| 454 |
|
|
|
|
| 464 |
float(temperature),
|
| 465 |
float(top_p),
|
| 466 |
float(repetition_penalty),
|
| 467 |
+
allowed_mask,
|
| 468 |
)
|
| 469 |
token_id = int(next_token.item())
|
| 470 |
+
if token_id == tokenizer.eos_id:
|
| 471 |
+
break
|
| 472 |
generated_ids.append(token_id)
|
| 473 |
history.append(token_id)
|
|
|
|
| 474 |
if step + 1 < max_new_tokens:
|
| 475 |
+
|
| 476 |
+
logits = decode_step(model, cache, next_token, position=cache.length)
|
|
|
|
|
|
|
|
|
|
|
|
|
| 477 |
|
| 478 |
return tokenizer.decode(generated_ids)
|
| 479 |
|
|
|
|
| 481 |
raise
|
| 482 |
except Exception as exc:
|
| 483 |
print(f"Generation failure for {label}: {type(exc).__name__}: {exc}")
|
| 484 |
+
raise gr.Error(f"Could not load or run {label}: {type(exc).__name__}: {exc}") from exc
|
|
|
|
|
|
|
| 485 |
|
| 486 |
|
| 487 |
with gr.Blocks(title=TITLE) as demo:
|
|
|
|
| 493 |
|
| 494 |
with gr.Row():
|
| 495 |
model_choice = gr.Dropdown(
|
| 496 |
+
choices=list(MODELS.keys()), value="Styx 100M", label="Model"
|
|
|
|
|
|
|
| 497 |
)
|
| 498 |
max_tokens = gr.Slider(
|
| 499 |
+
minimum=16, maximum=256, value=96, step=16, label="New tokens"
|
|
|
|
|
|
|
|
|
|
|
|
|
| 500 |
)
|
| 501 |
|
| 502 |
prompt = gr.Textbox(
|
|
|
|
| 508 |
with gr.Accordion("Generation settings", open=False):
|
| 509 |
with gr.Row():
|
| 510 |
temperature = gr.Slider(
|
| 511 |
+
minimum=0, maximum=1.5, value=0.8, step=0.05,
|
|
|
|
|
|
|
|
|
|
| 512 |
label="Temperature (0 = greedy)",
|
| 513 |
)
|
| 514 |
top_p = gr.Slider(
|
| 515 |
+
minimum=0.1, maximum=1.0, value=0.9, step=0.05, label="Top-p"
|
|
|
|
|
|
|
|
|
|
|
|
|
| 516 |
)
|
| 517 |
repetition = gr.Slider(
|
| 518 |
+
minimum=1.0, maximum=1.5, value=1.1, step=0.05,
|
|
|
|
|
|
|
|
|
|
| 519 |
label="Repetition penalty",
|
| 520 |
)
|
| 521 |
|
|
|
|
| 526 |
prompt.submit(fn=generate, inputs=inputs, outputs=output)
|
| 527 |
|
| 528 |
gr.Markdown(
|
| 529 |
+
"**First use:** the Space downloads model files and pins the matching "
|
| 530 |
+
"Talos source. Generation quality is experimental."
|
| 531 |
)
|
| 532 |
|
| 533 |
|
| 534 |
if __name__ == "__main__":
|
| 535 |
+
demo.queue().launch()
|