Michael Stattelman commited on
Commit
6012dcc
·
1 Parent(s): 43c3b3c

Add application file

Browse files
README.md CHANGED
@@ -14,9 +14,10 @@ pinned: false
14
 
15
  A self-hosted web lab that puts decision models on the same decisions and compares their answers, confidence and speed:
16
 
17
- - **Three models from the Hugging Face Hub**, in this order:
18
  - **LightDec_Arthur**: [Falconsai/LightDec_Arthur](https://huggingface.co/Falconsai/LightDec_Arthur), the byte-level Arthur model (about 12M parameters). Only its `config.json` and `model.safetensors` are downloaded; the code that runs it ships with DecisionLab, copied verbatim from the arthur_v0_8_0 notebook.
19
  - **LightDec_V2**: [Falconsai/LightDec_V2](https://huggingface.co/Falconsai/LightDec_V2), FalconDec on the Ettin-150M encoder (about 160M parameters). Its `falcondec_modeling.py` runs only if its sha256 is on the allowlist; otherwise its card shows the hash and how to trust it.
 
20
  - **Laya**: [convaiinnovations/laya](https://huggingface.co/convaiinnovations/laya), by Convai Innovations, ModernBERT-large, 421M parameters, Apache-2.0.
21
  - **Every model in the `models` folder**, added after those and marked "(local)":
22
  - **LightDec (FalconDec) models**: a folder with a `falcondec_config.json`, named from that config.
@@ -52,7 +53,7 @@ docker build -t decisionlab .
52
  docker run -d --name DecisionLab -p 9910:9910 -v decisionlab-hf:/data/hf -v "${PWD}\models:/models:ro" decisionlab
53
  ```
54
 
55
- The first start downloads the three Hub models (about 1.2 GB) into the `decisionlab-hf` volume. The page shows each model's loading progress, and later starts load from the cache in seconds.
56
 
57
  ### NVIDIA GPU
58
 
@@ -80,11 +81,11 @@ cd <your-space>
80
  git lfs install
81
  # copy the contents of this DecisionLab folder into it (not the folder itself), then:
82
  git add .
83
- git commit -m "DecisionLab 2.1.2"
84
  git push
85
  ```
86
 
87
- The Space downloads the three Hub models on first start. Model folders you put in `models/` are loaded too; their weights go through Git LFS (`.gitattributes`).
88
 
89
  Call it from Python:
90
 
@@ -111,6 +112,7 @@ The plain HTTP API works on the Space too, at `https://<your-user>-<your-space>.
111
  | `LIGHTDEC_VARIANT` | `fp16` | `fp16` (each model folder itself) or `int8` (its `compact-int8` subfolder, dequantised on load), for every discovered model |
112
  | `LIGHTDEC_ARTHUR_REPO` | `Falconsai/LightDec_Arthur` | Hub repo shown as LightDec_Arthur |
113
  | `LIGHTDEC_V2_REPO` | `Falconsai/LightDec_V2` | Hub repo shown as LightDec_V2 |
 
114
  | `LAYA_REPO` | `convaiinnovations/laya` | Laya checkpoint loaded with `laya.load()` |
115
  | `LAYA_FALLBACK_REPO` | – | Optional second Laya repo, tried if the first fails |
116
  | `LOAD_ORDER` | comparison order | Which models to load at start, in order, by key (see `/api/status`) |
 
14
 
15
  A self-hosted web lab that puts decision models on the same decisions and compares their answers, confidence and speed:
16
 
17
+ - **Four models from the Hugging Face Hub**, in this order:
18
  - **LightDec_Arthur**: [Falconsai/LightDec_Arthur](https://huggingface.co/Falconsai/LightDec_Arthur), the byte-level Arthur model (about 12M parameters). Only its `config.json` and `model.safetensors` are downloaded; the code that runs it ships with DecisionLab, copied verbatim from the arthur_v0_8_0 notebook.
19
  - **LightDec_V2**: [Falconsai/LightDec_V2](https://huggingface.co/Falconsai/LightDec_V2), FalconDec on the Ettin-150M encoder (about 160M parameters). Its `falcondec_modeling.py` runs only if its sha256 is on the allowlist; otherwise its card shows the hash and how to trust it.
20
+ - **Enterprise Reflux Laya V2.1**: [yasserrmd/enterprise-reflux-laya-v21](https://huggingface.co/yasserrmd/enterprise-reflux-laya-v21), a Laya fine-tune for ranking enterprise actions (ModernBERT-large, like Laya), loaded with the same `laya` runtime. Its model card marks it a research prototype, not for production.
21
  - **Laya**: [convaiinnovations/laya](https://huggingface.co/convaiinnovations/laya), by Convai Innovations, ModernBERT-large, 421M parameters, Apache-2.0.
22
  - **Every model in the `models` folder**, added after those and marked "(local)":
23
  - **LightDec (FalconDec) models**: a folder with a `falcondec_config.json`, named from that config.
 
53
  docker run -d --name DecisionLab -p 9910:9910 -v decisionlab-hf:/data/hf -v "${PWD}\models:/models:ro" decisionlab
54
  ```
55
 
56
+ The first start downloads the four Hub models (about 2 GB) into the `decisionlab-hf` volume. The page shows each model's loading progress, and later starts load from the cache in seconds.
57
 
58
  ### NVIDIA GPU
59
 
 
81
  git lfs install
82
  # copy the contents of this DecisionLab folder into it (not the folder itself), then:
83
  git add .
84
+ git commit -m "DecisionLab 2.2.0"
85
  git push
86
  ```
87
 
88
+ The Space downloads the four Hub models on first start. Model folders you put in `models/` are loaded too; their weights go through Git LFS (`.gitattributes`).
89
 
90
  Call it from Python:
91
 
 
112
  | `LIGHTDEC_VARIANT` | `fp16` | `fp16` (each model folder itself) or `int8` (its `compact-int8` subfolder, dequantised on load), for every discovered model |
113
  | `LIGHTDEC_ARTHUR_REPO` | `Falconsai/LightDec_Arthur` | Hub repo shown as LightDec_Arthur |
114
  | `LIGHTDEC_V2_REPO` | `Falconsai/LightDec_V2` | Hub repo shown as LightDec_V2 |
115
+ | `REFLUX_LAYA_REPO` | `yasserrmd/enterprise-reflux-laya-v21` | Hub repo shown as Enterprise Reflux Laya V2.1 (a Laya checkpoint) |
116
  | `LAYA_REPO` | `convaiinnovations/laya` | Laya checkpoint loaded with `laya.load()` |
117
  | `LAYA_FALLBACK_REPO` | – | Optional second Laya repo, tried if the first fails |
118
  | `LOAD_ORDER` | comparison order | Which models to load at start, in order, by key (see `/api/status`) |
app/main.py CHANGED
@@ -27,7 +27,7 @@ MAX_BODY_BYTES = int(os.getenv("MAX_BODY_BYTES", str(256 * 1024)))
27
  DECIDE_SLOTS = Gate(int(os.getenv("MAX_PENDING_DECIDES", "4"))) # DL-SA-004
28
 
29
  # DL-SA-007: no /docs, /redoc or /openapi.json
30
- VERSION = "2.1.2"
31
  PAGE = stamp_assets((STATIC / "index.html").read_text(encoding="utf-8"), VERSION)
32
 
33
 
 
27
  DECIDE_SLOTS = Gate(int(os.getenv("MAX_PENDING_DECIDES", "4"))) # DL-SA-004
28
 
29
  # DL-SA-007: no /docs, /redoc or /openapi.json
30
+ VERSION = "2.2.0"
31
  PAGE = stamp_assets((STATIC / "index.html").read_text(encoding="utf-8"), VERSION)
32
 
33
 
app/registry.py CHANGED
@@ -1,8 +1,9 @@
1
  """Which models DecisionLab compares.
2
 
3
- First the operator's three Hugging Face models, in this order (ruling 2026-09-28):
4
  LightDec_Arthur Falconsai/LightDec_Arthur (Arthur; code ships with DecisionLab)
5
  LightDec_V2 Falconsai/LightDec_V2 (FalconDec; its modeling code must be on the allowlist)
 
6
  Laya convaiinnovations/laya
7
  Then every model folder found in the models directory, named "... (local)".
8
 
@@ -83,19 +84,21 @@ def discover(models_dir: str) -> list[tuple[Path, str]]:
83
 
84
 
85
  def model_specs(env: Mapping[str, str]) -> list[dict]:
86
- """One dict per model, in the order the UI shows them: the three Hub models, then the models folder."""
87
  variant = _variant(env, "LIGHTDEC_VARIANT")
88
  specs = [
89
  {"key": "lightdec_arthur", "name": "LightDec_Arthur", "kind": "arthur", "side": PALETTE[0], "source": "hub",
90
  "repo": env.get("LIGHTDEC_ARTHUR_REPO") or "Falconsai/LightDec_Arthur"},
91
  {"key": "lightdec_v2", "name": "LightDec_V2", "kind": "lightdec", "side": PALETTE[1], "source": "hub",
92
  "repo": env.get("LIGHTDEC_V2_REPO") or "Falconsai/LightDec_V2", "revision": None, "variant": variant},
 
 
93
  {"key": "laya", "name": "Laya", "kind": "laya", "side": "laya",
94
  "repo": env.get("LAYA_REPO") or "convaiinnovations/laya",
95
  "fallback_repo": env.get("LAYA_FALLBACK_REPO", "")},
96
  ]
97
  used = {s["key"] for s in specs}
98
- for i, (folder, kind) in enumerate(discover(env.get("MODELS_DIR") or DEFAULT_MODELS_DIR), start=2):
99
  key, n = _slug(folder.name), 2
100
  while key in used:
101
  key, n = f"{_slug(folder.name)}_{n}", n + 1
 
1
  """Which models DecisionLab compares.
2
 
3
+ First the operator's Hugging Face models, in this order (rulings 2026-09-28 and 2026-09-30):
4
  LightDec_Arthur Falconsai/LightDec_Arthur (Arthur; code ships with DecisionLab)
5
  LightDec_V2 Falconsai/LightDec_V2 (FalconDec; its modeling code must be on the allowlist)
6
+ Enterprise Reflux Laya V2.1 yasserrmd/enterprise-reflux-laya-v21 (a Laya fine-tune; loaded with laya 0.3.20)
7
  Laya convaiinnovations/laya
8
  Then every model folder found in the models directory, named "... (local)".
9
 
 
84
 
85
 
86
  def model_specs(env: Mapping[str, str]) -> list[dict]:
87
+ """One dict per model, in the order the UI shows them: the four Hub models, then the models folder."""
88
  variant = _variant(env, "LIGHTDEC_VARIANT")
89
  specs = [
90
  {"key": "lightdec_arthur", "name": "LightDec_Arthur", "kind": "arthur", "side": PALETTE[0], "source": "hub",
91
  "repo": env.get("LIGHTDEC_ARTHUR_REPO") or "Falconsai/LightDec_Arthur"},
92
  {"key": "lightdec_v2", "name": "LightDec_V2", "kind": "lightdec", "side": PALETTE[1], "source": "hub",
93
  "repo": env.get("LIGHTDEC_V2_REPO") or "Falconsai/LightDec_V2", "revision": None, "variant": variant},
94
+ {"key": "enterprise_reflux_laya_v21", "name": "Enterprise Reflux Laya V2.1", "kind": "laya", "side": PALETTE[2],
95
+ "repo": env.get("REFLUX_LAYA_REPO") or "yasserrmd/enterprise-reflux-laya-v21", "fallback_repo": ""},
96
  {"key": "laya", "name": "Laya", "kind": "laya", "side": "laya",
97
  "repo": env.get("LAYA_REPO") or "convaiinnovations/laya",
98
  "fallback_repo": env.get("LAYA_FALLBACK_REPO", "")},
99
  ]
100
  used = {s["key"] for s in specs}
101
+ for i, (folder, kind) in enumerate(discover(env.get("MODELS_DIR") or DEFAULT_MODELS_DIR), start=3):
102
  key, n = _slug(folder.name), 2
103
  while key in used:
104
  key, n = f"{_slug(folder.name)}_{n}", n + 1
app/static/index.html CHANGED
@@ -42,7 +42,7 @@
42
  <header class="section-head">
43
  <p class="step">00 / setup</p>
44
  <h2 data-n-models="{N} models load when the lab starts">Models load when the lab starts</h2>
45
- <p class="section-note">The first start downloads LightDec_Arthur, LightDec_V2 and Laya from Hugging Face (about 1.2 GB) into the cache volume. Every LightDec (FalconDec) and Arthur model in the local <code>models</code> folder is found at start-up and loaded from there. Nothing you type leaves this server.</p>
46
  <p class="section-note" id="models-found"></p>
47
  </header>
48
  <div class="models" id="models">
@@ -196,6 +196,7 @@
196
  <h3>The models</h3>
197
  <p><a href="https://huggingface.co/Falconsai/LightDec_Arthur" target="_blank" rel="noopener noreferrer">LightDec_Arthur</a>: byte-level Arthur model, about 12M parameters; the code that runs it ships with DecisionLab.</p>
198
  <p><a href="https://huggingface.co/Falconsai/LightDec_V2" target="_blank" rel="noopener noreferrer">LightDec_V2</a>: FalconDec on the Ettin-150M encoder, 2,048-token window, Apache-2.0.</p>
 
199
  <p><a href="https://huggingface.co/convaiinnovations/laya" target="_blank" rel="noopener noreferrer">Laya</a>: by Convai Innovations, ModernBERT-large, Apache-2.0.</p>
200
  <p>Models in the mounted <code>models</code> folder are added after these, marked "(local)": a LightDec folder has a <code>falcondec_config.json</code>, an Arthur folder a <code>config.json</code> and <code>model.safetensors</code>.</p>
201
  <p>None of the models writes text. Each only ranks the options you give it.</p>
 
42
  <header class="section-head">
43
  <p class="step">00 / setup</p>
44
  <h2 data-n-models="{N} models load when the lab starts">Models load when the lab starts</h2>
45
+ <p class="section-note">The first start downloads LightDec_Arthur, LightDec_V2, Enterprise Reflux Laya V2.1 and Laya from Hugging Face (about 2 GB) into the cache volume. Every LightDec (FalconDec) and Arthur model in the local <code>models</code> folder is found at start-up and loaded from there. Nothing you type leaves this server.</p>
46
  <p class="section-note" id="models-found"></p>
47
  </header>
48
  <div class="models" id="models">
 
196
  <h3>The models</h3>
197
  <p><a href="https://huggingface.co/Falconsai/LightDec_Arthur" target="_blank" rel="noopener noreferrer">LightDec_Arthur</a>: byte-level Arthur model, about 12M parameters; the code that runs it ships with DecisionLab.</p>
198
  <p><a href="https://huggingface.co/Falconsai/LightDec_V2" target="_blank" rel="noopener noreferrer">LightDec_V2</a>: FalconDec on the Ettin-150M encoder, 2,048-token window, Apache-2.0.</p>
199
+ <p><a href="https://huggingface.co/yasserrmd/enterprise-reflux-laya-v21" target="_blank" rel="noopener noreferrer">Enterprise Reflux Laya V2.1</a>: a fine-tune of Laya for ranking enterprise actions, by Mohamed Yasser; its model card marks it a research prototype, not for production.</p>
200
  <p><a href="https://huggingface.co/convaiinnovations/laya" target="_blank" rel="noopener noreferrer">Laya</a>: by Convai Innovations, ModernBERT-large, Apache-2.0.</p>
201
  <p>Models in the mounted <code>models</code> folder are added after these, marked "(local)": a LightDec folder has a <code>falcondec_config.json</code>, an Arthur folder a <code>config.json</code> and <code>model.safetensors</code>.</p>
202
  <p>None of the models writes text. Each only ranks the options you give it.</p>
docker-compose.yml CHANGED
@@ -16,6 +16,7 @@ services:
16
  # The three Hub models (shown in this order, then any models in ./models, marked "(local)"):
17
  LIGHTDEC_ARTHUR_REPO: Falconsai/LightDec_Arthur
18
  LIGHTDEC_V2_REPO: Falconsai/LightDec_V2
 
19
  LAYA_REPO: convaiinnovations/laya
20
  MODELS_DIR: /models # LightDec and Arthur folders in ./models (mounted below) are added too
21
  LIGHTDEC_VARIANT: fp16 # fp16 | int8 (compact-int8 folder), for every LightDec model
 
16
  # The three Hub models (shown in this order, then any models in ./models, marked "(local)"):
17
  LIGHTDEC_ARTHUR_REPO: Falconsai/LightDec_Arthur
18
  LIGHTDEC_V2_REPO: Falconsai/LightDec_V2
19
+ REFLUX_LAYA_REPO: yasserrmd/enterprise-reflux-laya-v21
20
  LAYA_REPO: convaiinnovations/laya
21
  MODELS_DIR: /models # LightDec and Arthur folders in ./models (mounted below) are added too
22
  LIGHTDEC_VARIANT: fp16 # fp16 | int8 (compact-int8 folder), for every LightDec model
tests/test_api.py CHANGED
@@ -29,12 +29,15 @@ class DecideEndpointTest(unittest.TestCase):
29
  from pathlib import Path
30
  from app.registry import discover
31
  st = self.main.status()
32
- self.assertEqual(st["order"][:3], ["lightdec_arthur", "lightdec_v2", "laya"])
33
- self.assertEqual([st["models"][k]["name"] for k in st["order"][:3]], ["LightDec_Arthur", "LightDec_V2", "Laya"])
34
- self.assertEqual([st["models"][k]["repo"] for k in st["order"][:3]],
35
- ["Falconsai/LightDec_Arthur", "Falconsai/LightDec_V2", "convaiinnovations/laya"])
 
 
 
36
  found = [folder.name for folder, _ in discover(os.environ.get("MODELS_DIR") or "/models")]
37
- self.assertEqual([Path(st["models"][k]["path"]).name for k in st["order"][3:]], found)
38
 
39
  def test_importing_the_app_raises_no_deprecation_warning_of_its_own(self):
40
  import subprocess
 
29
  from pathlib import Path
30
  from app.registry import discover
31
  st = self.main.status()
32
+ hub = ["lightdec_arthur", "lightdec_v2", "enterprise_reflux_laya_v21", "laya"]
33
+ self.assertEqual(st["order"][:4], hub)
34
+ self.assertEqual([st["models"][k]["name"] for k in hub],
35
+ ["LightDec_Arthur", "LightDec_V2", "Enterprise Reflux Laya V2.1", "Laya"])
36
+ self.assertEqual([st["models"][k]["repo"] for k in hub],
37
+ ["Falconsai/LightDec_Arthur", "Falconsai/LightDec_V2", "yasserrmd/enterprise-reflux-laya-v21",
38
+ "convaiinnovations/laya"])
39
  found = [folder.name for folder, _ in discover(os.environ.get("MODELS_DIR") or "/models")]
40
+ self.assertEqual([Path(st["models"][k]["path"]).name for k in st["order"][4:]], found)
41
 
42
  def test_importing_the_app_raises_no_deprecation_warning_of_its_own(self):
43
  import subprocess
tests/test_links.py CHANGED
@@ -26,9 +26,10 @@ class ExternalLinksTest(unittest.TestCase):
26
  def test_links_the_script_builds(self):
27
  self.check("app.js")
28
 
29
- def test_page_credits_the_three_models(self):
30
  html = (STATIC / "index.html").read_text(encoding="utf-8")
31
- for repo in ("Falconsai/LightDec_Arthur", "Falconsai/LightDec_V2", "convaiinnovations/laya"):
 
32
  with self.subTest(repo=repo):
33
  self.assertIn(f'href="https://huggingface.co/{repo}"', html)
34
  self.assertNotIn("laya-v796", html)
 
26
  def test_links_the_script_builds(self):
27
  self.check("app.js")
28
 
29
+ def test_page_credits_the_hub_models(self):
30
  html = (STATIC / "index.html").read_text(encoding="utf-8")
31
+ for repo in ("Falconsai/LightDec_Arthur", "Falconsai/LightDec_V2", "yasserrmd/enterprise-reflux-laya-v21",
32
+ "convaiinnovations/laya"):
33
  with self.subTest(repo=repo):
34
  self.assertIn(f'href="https://huggingface.co/{repo}"', html)
35
  self.assertNotIn("laya-v796", html)
tests/test_registry.py CHANGED
@@ -6,7 +6,7 @@ from pathlib import Path
6
 
7
  from app.registry import load_order, model_specs, resolve_local_dir
8
 
9
- HUB_KEYS = ["lightdec_arthur", "lightdec_v2", "laya"]
10
 
11
 
12
  def folder_models(specs):
@@ -33,16 +33,17 @@ class DiscoveryTest(unittest.TestCase):
33
  make_model(self.root, "LightDec_V2_Long-v1.0.0", {"name": "LightDec_V2_Long", "version": "1.0.0"})
34
  make_model(self.root, "LightDec-v1.0.2", {"name": "LightDec", "version": "1.0.2"})
35
  specs = model_specs(self.env)
36
- self.assertEqual([s["key"] for s in specs[:3]], HUB_KEYS)
37
  self.assertEqual([s["path"] for s in folder_models(specs)],
38
  [str(self.root / "LightDec-v1.0.2"), str(self.root / "LightDec_V2_Long-v1.0.0")])
39
- self.assertEqual(specs[2]["kind"], "laya")
40
 
41
  def test_names_come_from_each_config(self):
42
  make_model(self.root, "a", {"name": "LightDec", "version": "1.0.2"})
43
  make_model(self.root, "b", {"name": "LightDec_V2_Long", "version": "1.0.0"})
44
  self.assertEqual([s["name"] for s in model_specs(self.env)],
45
- ["LightDec_Arthur", "LightDec_V2", "Laya", "LightDec 1.0.2 (local)", "LightDec_V2_Long 1.0.0 (local)"])
 
46
 
47
  def test_unreadable_config_falls_back_to_the_folder_name(self):
48
  make_model(self.root, "Broken-Model", raw="{not json")
@@ -72,9 +73,9 @@ class DiscoveryTest(unittest.TestCase):
72
  for i in range(6):
73
  make_model(self.root, f"m{i}", {})
74
  sides = [s["side"] for s in model_specs(self.env)]
75
- self.assertEqual(len(sides), 9)
76
- self.assertEqual(len(set(sides)), 9)
77
- self.assertEqual(sides[2], "laya")
78
 
79
  def test_discovered_models_are_local_lightdec_backends(self):
80
  make_model(self.root, "m", {})
@@ -93,13 +94,13 @@ class DiscoveryTest(unittest.TestCase):
93
  self.assertEqual(str(ctx.exception), "LIGHTDEC_VARIANT must be fp16 or int8 (got 'fp8').")
94
 
95
  def test_laya_settings(self):
96
- laya = model_specs(dict(self.env, LAYA_REPO="me/laya", LAYA_FALLBACK_REPO=""))[2]
97
  self.assertEqual((laya["name"], laya["repo"], laya["fallback_repo"]), ("Laya", "me/laya", ""))
98
- laya = model_specs(self.env)[2]
99
  self.assertEqual((laya["repo"], laya["fallback_repo"]), ("convaiinnovations/laya", ""))
100
 
101
  def test_default_models_directory_is_slash_models(self):
102
- self.assertEqual([s["key"] for s in model_specs({"MODELS_DIR": ""})][:3], HUB_KEYS) # empty setting falls back to /models
103
 
104
 
105
  class LoadOrderTest(unittest.TestCase):
@@ -226,9 +227,9 @@ class ArthurDiscoveryTest(unittest.TestCase):
226
  self.arthur("arthur-base")
227
  make_model(self.root, "LightDec-v1.0.2", {"name": "LightDec", "version": "1.0.2"})
228
  specs = model_specs(self.env)
229
- self.assertEqual([(s["key"], s["kind"], s["name"]) for s in specs][3:],
230
  [("lightdec_v1_0_2", "lightdec", "LightDec 1.0.2 (local)"), ("arthur_base", "arthur", "Arthur base (local)")])
231
- self.assertEqual(specs[4]["path"], str(self.root / "arthur-base"))
232
 
233
  def test_pretrained_run_is_named_so(self):
234
  self.arthur("arthur-base-pt", dict(ARTHUR_CFG, pretrain={"epochs": 1}))
@@ -249,7 +250,7 @@ class ArthurDiscoveryTest(unittest.TestCase):
249
  def test_colours_continue_across_kinds(self):
250
  self.arthur("b-arthur")
251
  make_model(self.root, "a-lightdec", {})
252
- self.assertEqual([s["side"] for s in model_specs(self.env)], ["c0", "c1", "laya", "c2", "c3"])
253
 
254
 
255
  class HubModelsTest(unittest.TestCase):
@@ -262,14 +263,24 @@ class HubModelsTest(unittest.TestCase):
262
  self.assertEqual([(s["key"], s["name"], s["repo"]) for s in self.specs],
263
  [("lightdec_arthur", "LightDec_Arthur", "Falconsai/LightDec_Arthur"),
264
  ("lightdec_v2", "LightDec_V2", "Falconsai/LightDec_V2"),
 
265
  ("laya", "Laya", "convaiinnovations/laya")])
266
 
267
  def test_kinds_and_sources(self):
268
  self.assertEqual([(s["kind"], s.get("source")) for s in self.specs],
269
- [("arthur", "hub"), ("lightdec", "hub"), ("laya", None)])
 
 
 
 
 
 
 
 
 
270
 
271
  def test_laya_has_no_fallback(self):
272
- self.assertEqual(self.specs[2]["fallback_repo"], "")
273
 
274
  def test_hub_repos_can_be_overridden(self):
275
  specs = model_specs({"MODELS_DIR": "/x", "LIGHTDEC_ARTHUR_REPO": "me/a", "LIGHTDEC_V2_REPO": "me/b"})
@@ -279,7 +290,7 @@ class HubModelsTest(unittest.TestCase):
279
  with tempfile.TemporaryDirectory() as d:
280
  make_model(Path(d), "LightDec_V2", {"name": "LightDec_V2_Long", "version": "1.0.0"})
281
  specs = model_specs({"MODELS_DIR": d})
282
- self.assertEqual([(s["key"], s["name"]) for s in specs][3:], [("lightdec_v2_2", "LightDec_V2_Long 1.0.0 (local)")])
283
 
284
 
285
  from app.registry import warmup_enabled
 
6
 
7
  from app.registry import load_order, model_specs, resolve_local_dir
8
 
9
+ HUB_KEYS = ["lightdec_arthur", "lightdec_v2", "enterprise_reflux_laya_v21", "laya"]
10
 
11
 
12
  def folder_models(specs):
 
33
  make_model(self.root, "LightDec_V2_Long-v1.0.0", {"name": "LightDec_V2_Long", "version": "1.0.0"})
34
  make_model(self.root, "LightDec-v1.0.2", {"name": "LightDec", "version": "1.0.2"})
35
  specs = model_specs(self.env)
36
+ self.assertEqual([s["key"] for s in specs[:4]], HUB_KEYS)
37
  self.assertEqual([s["path"] for s in folder_models(specs)],
38
  [str(self.root / "LightDec-v1.0.2"), str(self.root / "LightDec_V2_Long-v1.0.0")])
39
+ self.assertEqual(specs[3]["kind"], "laya")
40
 
41
  def test_names_come_from_each_config(self):
42
  make_model(self.root, "a", {"name": "LightDec", "version": "1.0.2"})
43
  make_model(self.root, "b", {"name": "LightDec_V2_Long", "version": "1.0.0"})
44
  self.assertEqual([s["name"] for s in model_specs(self.env)],
45
+ ["LightDec_Arthur", "LightDec_V2", "Enterprise Reflux Laya V2.1", "Laya",
46
+ "LightDec 1.0.2 (local)", "LightDec_V2_Long 1.0.0 (local)"])
47
 
48
  def test_unreadable_config_falls_back_to_the_folder_name(self):
49
  make_model(self.root, "Broken-Model", raw="{not json")
 
73
  for i in range(6):
74
  make_model(self.root, f"m{i}", {})
75
  sides = [s["side"] for s in model_specs(self.env)]
76
+ self.assertEqual(len(sides), 10)
77
+ self.assertEqual(len(set(sides)), 9) # 8 palette colours + Laya
78
+ self.assertEqual(sides[3], "laya")
79
 
80
  def test_discovered_models_are_local_lightdec_backends(self):
81
  make_model(self.root, "m", {})
 
94
  self.assertEqual(str(ctx.exception), "LIGHTDEC_VARIANT must be fp16 or int8 (got 'fp8').")
95
 
96
  def test_laya_settings(self):
97
+ laya = model_specs(dict(self.env, LAYA_REPO="me/laya", LAYA_FALLBACK_REPO=""))[3]
98
  self.assertEqual((laya["name"], laya["repo"], laya["fallback_repo"]), ("Laya", "me/laya", ""))
99
+ laya = model_specs(self.env)[3]
100
  self.assertEqual((laya["repo"], laya["fallback_repo"]), ("convaiinnovations/laya", ""))
101
 
102
  def test_default_models_directory_is_slash_models(self):
103
+ self.assertEqual([s["key"] for s in model_specs({"MODELS_DIR": ""})][:4], HUB_KEYS) # empty setting falls back to /models
104
 
105
 
106
  class LoadOrderTest(unittest.TestCase):
 
227
  self.arthur("arthur-base")
228
  make_model(self.root, "LightDec-v1.0.2", {"name": "LightDec", "version": "1.0.2"})
229
  specs = model_specs(self.env)
230
+ self.assertEqual([(s["key"], s["kind"], s["name"]) for s in specs][4:],
231
  [("lightdec_v1_0_2", "lightdec", "LightDec 1.0.2 (local)"), ("arthur_base", "arthur", "Arthur base (local)")])
232
+ self.assertEqual(specs[5]["path"], str(self.root / "arthur-base"))
233
 
234
  def test_pretrained_run_is_named_so(self):
235
  self.arthur("arthur-base-pt", dict(ARTHUR_CFG, pretrain={"epochs": 1}))
 
250
  def test_colours_continue_across_kinds(self):
251
  self.arthur("b-arthur")
252
  make_model(self.root, "a-lightdec", {})
253
+ self.assertEqual([s["side"] for s in model_specs(self.env)], ["c0", "c1", "c2", "laya", "c3", "c4"])
254
 
255
 
256
  class HubModelsTest(unittest.TestCase):
 
263
  self.assertEqual([(s["key"], s["name"], s["repo"]) for s in self.specs],
264
  [("lightdec_arthur", "LightDec_Arthur", "Falconsai/LightDec_Arthur"),
265
  ("lightdec_v2", "LightDec_V2", "Falconsai/LightDec_V2"),
266
+ ("enterprise_reflux_laya_v21", "Enterprise Reflux Laya V2.1", "yasserrmd/enterprise-reflux-laya-v21"),
267
  ("laya", "Laya", "convaiinnovations/laya")])
268
 
269
  def test_kinds_and_sources(self):
270
  self.assertEqual([(s["kind"], s.get("source")) for s in self.specs],
271
+ [("arthur", "hub"), ("lightdec", "hub"), ("laya", None), ("laya", None)])
272
+
273
+ def test_reflux_is_a_laya_checkpoint_with_no_fallback_and_its_own_colour(self):
274
+ reflux = self.specs[2]
275
+ self.assertEqual((reflux["kind"], reflux["fallback_repo"]), ("laya", ""))
276
+ self.assertEqual(len({s["side"] for s in self.specs}), 4)
277
+
278
+ def test_reflux_repo_can_be_overridden(self):
279
+ specs = model_specs({"MODELS_DIR": "/x", "REFLUX_LAYA_REPO": "me/reflux"})
280
+ self.assertEqual(specs[2]["repo"], "me/reflux")
281
 
282
  def test_laya_has_no_fallback(self):
283
+ self.assertEqual(self.specs[3]["fallback_repo"], "")
284
 
285
  def test_hub_repos_can_be_overridden(self):
286
  specs = model_specs({"MODELS_DIR": "/x", "LIGHTDEC_ARTHUR_REPO": "me/a", "LIGHTDEC_V2_REPO": "me/b"})
 
290
  with tempfile.TemporaryDirectory() as d:
291
  make_model(Path(d), "LightDec_V2", {"name": "LightDec_V2_Long", "version": "1.0.0"})
292
  specs = model_specs({"MODELS_DIR": d})
293
+ self.assertEqual([(s["key"], s["name"]) for s in specs][4:], [("lightdec_v2_2", "LightDec_V2_Long 1.0.0 (local)")])
294
 
295
 
296
  from app.registry import warmup_enabled