evals-lab 0.3.0 → 0.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lab/server.py CHANGED
@@ -1356,23 +1356,25 @@ class Sources:
1356
1356
  # A lab from before this held a one-time import's `meta` row saying it ran;
1357
1357
  # it is left where it is, and nothing reads it.
1358
1358
 
1359
- DATASET_FIELDS = ("version", "source", "cases")
1360
- # A body's own version: evals-core.ts's DATASET_BODY_VERSION. Version 5 was
1361
- # told by its `source` alone, and earlier ones by neither.
1362
- DATASET_BODY_VERSION = 6
1359
+ DATASET_FIELDS = ("version", "source", "scoring", "grader", "every", "run", "cases")
1360
+ # A body's own version: evals-core.ts's DATASET_BODY_VERSION. Version 7 is an
1361
+ # eval group (docs/pipeline-model.md §17), version 6 its cases alone; version
1362
+ # 5 was told by its `source` alone, and earlier ones by neither.
1363
+ DATASET_BODY_VERSION = 7
1363
1364
  DATASET_NAME_MAX = 80
1364
- # The file forms Export writes and Import reads. Export writes version 6;
1365
- # Import reads it and versions 1 to 5, upgraded, and refuses anything else,
1365
+ # The file forms Export writes and Import reads. Export writes version 7;
1366
+ # Import reads it and versions 1 to 6, upgraded, and refuses anything else,
1366
1367
  # as a pipeline of another version is refused. Versions 1 to 3 carried a
1367
1368
  # prompt, which an import gives to the Prompt library.
1368
1369
  EXPORT_ONE = "evals-lab/dataset"
1369
1370
  EXPORT_ALL = "evals-lab/datasets"
1370
- EXPORT_VERSION = 6
1371
- IMPORT_VERSIONS = (1, 2, 3, 4, 5, 6)
1371
+ EXPORT_VERSION = 7
1372
+ IMPORT_VERSIONS = (1, 2, 3, 4, 5, 6, 7)
1373
+ SCORING_MODES = ("all", "weighted")
1372
1374
 
1373
1375
 
1374
1376
  def blank_dataset() -> dict:
1375
- return {"version": DATASET_BODY_VERSION, "source": None, "cases": []}
1377
+ return group_of_v6({"source": None, "cases": []})
1376
1378
 
1377
1379
 
1378
1380
  # vocab: the names older versions gave a case's fields
@@ -1400,7 +1402,7 @@ def _term_in(items, term) -> bool:
1400
1402
  def case_metrics(c: dict) -> list:
1401
1403
  """A version-4 case's expectations as the metrics that say the same:
1402
1404
  evals-core.ts's caseMetrics, in Python, and held to it by proxy-check.py
1403
- through fixtures/dataset-v6.json."""
1405
+ through fixtures/dataset-v7.json."""
1404
1406
  def strs(v):
1405
1407
  return [x for x in v if isinstance(x, str)] if isinstance(v, list) else []
1406
1408
  if c.get("discarded") is True:
@@ -1466,8 +1468,18 @@ def case_of_v5(c):
1466
1468
  else m for m in c["metrics"]]}
1467
1469
 
1468
1470
 
1471
+ def group_of_v6(body: dict) -> dict:
1472
+ """A version-6 body as a version-7 eval group: evals-core.ts's
1473
+ groupOfV6. Scored All, the lab's grader, and no metrics of its own for
1474
+ every item or the whole run -- what a Metrics eval naming the dataset with
1475
+ none of its own graded."""
1476
+ rest = {k: v for k, v in body.items() if k != "version"}
1477
+ return {"version": DATASET_BODY_VERSION, "source": None, "scoring": {"mode": "all", "threshold": None},
1478
+ "grader": None, "every": [], "run": [], **rest}
1479
+
1480
+
1469
1481
  def upgrade_body(body):
1470
- """An earlier body as today's (version 6): evals-core.ts's
1482
+ """An earlier body as today's (version 7): evals-core.ts's
1471
1483
  upgradeDatasetBody, in Python. Version 1's `imageCases` are `cases`, and
1472
1484
  its `replays` and `conformance` go (fixtures/replays.json holds the
1473
1485
  parser's tests). Version 2's `rules` go -- they clean a job's answer, so
@@ -1477,19 +1489,25 @@ def upgrade_body(body):
1477
1489
  `note`, `traits` go, and the body names no Source yet. A caller that
1478
1490
  needs the rules or the prompt takes them first (`body_rules`,
1479
1491
  `body_prompt`). A body naming its Source is version 5, whose Contains
1480
- metrics each come to say Ignore case (`case_of_v5`). A body saying it is
1481
- version 6 comes back as it was; so does anything that is not a body."""
1492
+ metrics each come to say Ignore case (`case_of_v5`). Version 6 gains a
1493
+ group's scoring, grader, Every item and Whole run (`group_of_v6`). A body
1494
+ saying it is version 7 comes back as it was; so does anything that is
1495
+ not a body."""
1482
1496
  if not isinstance(body, dict) or body.get("version") == DATASET_BODY_VERSION:
1483
1497
  return body
1498
+ # A body saying any other version is one this lab does not read, and is
1499
+ # left for dataset_problem to refuse.
1500
+ if "version" in body:
1501
+ return group_of_v6(body) if body["version"] == 6 else body
1484
1502
  if "source" in body:
1485
- up = {"version": DATASET_BODY_VERSION, **body}
1503
+ up = dict(body)
1486
1504
  if isinstance(body.get("cases"), list):
1487
1505
  up["cases"] = [case_of_v5(c) for c in body["cases"]]
1488
- return up
1506
+ return group_of_v6(up)
1489
1507
  cases = body.get("cases") if isinstance(body.get("cases"), list) else body.get("imageCases")
1490
1508
  if not isinstance(cases, list):
1491
1509
  return body
1492
- return {"version": DATASET_BODY_VERSION, "source": None, "cases": [case_of_v5(case_of_v4(c)) for c in cases]}
1510
+ return group_of_v6({"source": None, "cases": [case_of_v5(case_of_v4(c)) for c in cases]})
1493
1511
 
1494
1512
 
1495
1513
  def body_prompt(body):
@@ -1518,10 +1536,25 @@ def dataset_problem(body) -> str:
1518
1536
  return f"a dataset's body is version {DATASET_BODY_VERSION}"
1519
1537
  if not isinstance(body["cases"], list) or not all(isinstance(c, dict) for c in body["cases"]):
1520
1538
  return "cases has to be a list of cases"
1521
- src = body["source"]
1522
- if src is not None and not (isinstance(src, dict) and isinstance(src.get("id"), str)
1523
- and isinstance(src.get("name"), str)):
1539
+ def is_ref(v):
1540
+ return isinstance(v, dict) and isinstance(v.get("id"), str) and isinstance(v.get("name"), str)
1541
+ if body["source"] is not None and not is_ref(body["source"]):
1524
1542
  return "a dataset names its Source as { id, name }, or null"
1543
+ # A group's own sections: their metrics are evals-core.ts's validateEvals'
1544
+ # to judge, as a case's are; the shape is the server's.
1545
+ sc = body["scoring"]
1546
+ if not isinstance(sc, dict) or sc.get("mode") not in SCORING_MODES:
1547
+ return "a group is scored all or weighted"
1548
+ number = lambda v: type(v) in (int, float)
1549
+ if sc["mode"] == "weighted" and not number(sc.get("threshold")):
1550
+ return "a group scored in points needs Pass at: the points an item has to reach"
1551
+ if sc.get("threshold") is not None and not number(sc.get("threshold")):
1552
+ return "a group's Pass at has to be a number"
1553
+ if body["grader"] is not None and not is_ref(body["grader"]):
1554
+ return "a group names its grader as { id, name }, or null"
1555
+ for k, label in (("every", "Every item"), ("run", "Whole run")):
1556
+ if not isinstance(body[k], list) or not all(isinstance(m, dict) for m in body[k]):
1557
+ return f"{label} has to be a list of metrics"
1525
1558
  return ""
1526
1559
 
1527
1560
 
@@ -1926,6 +1959,10 @@ class Datasets:
1926
1959
  given = False
1927
1960
  for did, name, version, raw in rows:
1928
1961
  body = json.loads(raw)
1962
+ # A version-6 body is read as version 7 (`_doc`) and saved as
1963
+ # one at its next edit, never rewritten here (§17).
1964
+ if isinstance(body, dict) and body.get("version") == 6:
1965
+ continue
1929
1966
  up = upgrade_body(body)
1930
1967
  if up is not body:
1931
1968
  self._archive(db, did, body)
@@ -1956,8 +1993,9 @@ class Datasets:
1956
1993
 
1957
1994
  @staticmethod
1958
1995
  def _doc(r, body=True):
1959
- """A row as the API answers it: a DatasetSummary, and its body with it."""
1960
- parsed = json.loads(r["body"])
1996
+ """A row as the API answers it: a DatasetSummary, and its body with it,
1997
+ read as today's version."""
1998
+ parsed = upgrade_body(json.loads(r["body"]))
1961
1999
  out = {"id": r["id"], "name": r["name"], "cases": len(parsed.get("cases") or []),
1962
2000
  "version": r["version"], "updated": r["updated_at"]}
1963
2001
  if body:
@@ -2101,7 +2139,7 @@ class Datasets:
2101
2139
  rows = db.execute("SELECT * FROM datasets WHERE trash IS NULL "
2102
2140
  "ORDER BY name COLLATE NOCASE, id").fetchall()
2103
2141
  return {"format": EXPORT_ALL, "version": EXPORT_VERSION,
2104
- "datasets": [{"name": r["name"], "body": json.loads(r["body"])} for r in rows]}
2142
+ "datasets": [{"name": r["name"], "body": upgrade_body(json.loads(r["body"]))} for r in rows]}
2105
2143
 
2106
2144
  def import_file(self, doc):
2107
2145
  """Either export's file, as new datasets: import always creates, ids
@@ -3323,11 +3361,22 @@ class Queue:
3323
3361
  "dataset TEXT)")
3324
3362
  # A store from before runs kept their dataset gains the column;
3325
3363
  # its rows have none, and are pinned when first they need one.
3326
- if "dataset" not in [c[1] for c in db.execute("PRAGMA table_info(queue)")]:
3364
+ cols = [c[1] for c in db.execute("PRAGMA table_info(queue)")]
3365
+ if "dataset" not in cols:
3327
3366
  db.execute("ALTER TABLE queue ADD COLUMN dataset TEXT")
3367
+ # The run a re-run was queued from (#245); every earlier row is
3368
+ # one of its own.
3369
+ if "rerun_of" not in cols:
3370
+ db.execute("ALTER TABLE queue ADD COLUMN rerun_of TEXT")
3328
3371
 
3329
3372
  # ---- rows -----------------------------------------------------------
3330
3373
 
3374
+ # A row read with the submit time of the run it re-runs, if any: the
3375
+ # page names a run by that time (History's Run ID), so a "Re-run of"
3376
+ # note reads without fetching the original.
3377
+ SELECT = ("SELECT q.*, o.submitted_at FROM queue q "
3378
+ "LEFT JOIN queue o ON o.id = q.rerun_of")
3379
+
3331
3380
  @staticmethod
3332
3381
  def _row(r):
3333
3382
  if r is None:
@@ -3338,6 +3387,8 @@ class Queue:
3338
3387
  "snapshot": json.loads(r[6]), "results": json.loads(r[7]),
3339
3388
  "progress": json.loads(r[8]), "totals": json.loads(r[9]),
3340
3389
  "error": r[10],
3390
+ "rerunOf": r[12] if len(r) > 12 else None,
3391
+ "rerunOfAt": r[13] if len(r) > 13 else None,
3341
3392
  }
3342
3393
 
3343
3394
  # A row from before run documents has no version, and nothing here can
@@ -3351,12 +3402,12 @@ class Queue:
3351
3402
  return row is not None and (row["snapshot"] or {}).get("version") in READABLE_VERSIONS
3352
3403
 
3353
3404
  def _all(self, db):
3354
- return [row for row in (self._row(r) for r in db.execute("SELECT * FROM queue"))
3405
+ return [row for row in (self._row(r) for r in db.execute(self.SELECT))
3355
3406
  if self._readable(row)]
3356
3407
 
3357
3408
  def get(self, rid):
3358
3409
  with self.lock, closing(sqlite3.connect(self.store.path)) as db:
3359
- row = self._row(db.execute("SELECT * FROM queue WHERE id = ?",
3410
+ row = self._row(db.execute(self.SELECT + " WHERE q.id = ?",
3360
3411
  (rid,)).fetchone())
3361
3412
  return row if self._readable(row) else None
3362
3413
 
@@ -3379,13 +3430,13 @@ class Queue:
3379
3430
 
3380
3431
  # ---- submit ---------------------------------------------------------
3381
3432
 
3382
- def submit(self, run: dict, dataset=None):
3433
+ def submit(self, run: dict, dataset=None, rerun_of=None):
3383
3434
  """
3384
3435
  A new queued run. `run` is the run document (docs/pipeline-model.md
3385
3436
  §5): the pipeline, the profiles it resolved to without their keys, its
3386
3437
  content's file list in order and with its repeats, and the dataset's
3387
- version; `dataset` is that version's body, for a graded run. Returns
3388
- the row. Its items are that list, or the one inline text, each through
3438
+ version; `dataset` is that version's body, for a graded run;
3439
+ `rerun_of` is the run a re-run was queued from. Returns the row. Its items are that list, or the one inline text, each through
3389
3440
  every scenario -- so the total is the list's length, repeats and all,
3390
3441
  the same count the runner and the page make.
3391
3442
  """
@@ -3394,11 +3445,12 @@ class Queue:
3394
3445
  total = len(run_items(run))
3395
3446
  with self.lock, closing(sqlite3.connect(self.store.path)) as db, db:
3396
3447
  db.execute("INSERT INTO queue (id, status, cancel, submitted_at, "
3397
- "snapshot, results, progress, totals, dataset) VALUES (?, ?, 0, ?, ?, ?, ?, ?, ?)",
3448
+ "snapshot, results, progress, totals, dataset, rerun_of) "
3449
+ "VALUES (?, ?, 0, ?, ?, ?, ?, ?, ?, ?)",
3398
3450
  (rid, "queued", now, json.dumps(run), "[]",
3399
3451
  json.dumps({"current": None, "n": 0, "total": total}),
3400
3452
  json.dumps({"ran": 0, "passed": 0, "found": 0, "of": 0}),
3401
- None if dataset is None else json.dumps(dataset)))
3453
+ None if dataset is None else json.dumps(dataset), rerun_of))
3402
3454
  # Its prompts' uses, in the same transaction: a run is in the
3403
3455
  # library the moment it is queued, or not queued at all.
3404
3456
  if self.prompts is not None:
@@ -3406,7 +3458,7 @@ class Queue:
3406
3458
  # Read back under the lock the worker dequeues under, so the
3407
3459
  # answer is the run as it was queued: an idle worker can take it
3408
3460
  # the moment the lock is let go.
3409
- return self._row(db.execute("SELECT * FROM queue WHERE id = ?", (rid,)).fetchone())
3461
+ return self._row(db.execute(self.SELECT + " WHERE q.id = ?", (rid,)).fetchone())
3410
3462
 
3411
3463
  def dataset(self, rid, raw=False):
3412
3464
  """The dataset body a readable run was submitted against, or None --
@@ -3525,6 +3577,46 @@ class Queue:
3525
3577
  shutil.rmtree(d, ignore_errors=True)
3526
3578
  return n
3527
3579
 
3580
+ def rerun(self, rid):
3581
+ """
3582
+ A new run from a finished one's document (#245): a new id and submit
3583
+ time, the same document -- the file revisions and the plugins it
3584
+ pinned -- and the dataset body it was graded by, so a re-run measures
3585
+ the model again rather than changed data. The original is left as it
3586
+ was; the new row names it in `rerunOf`. Refused, naming what is
3587
+ missing, when a pinned file or dataset version is no longer kept. A
3588
+ Target's key is read from its profile now, at dequeue, as for any
3589
+ run: none is ever stored in one.
3590
+ """
3591
+ run = self.get(rid)
3592
+ if run is None:
3593
+ return None, (404, "no such run")
3594
+ if run["status"] in ("queued", "running"):
3595
+ return None, (409, "a run still in progress cannot be re-run")
3596
+ snap = run["snapshot"]
3597
+ _, err = self._pinned_files(snap)
3598
+ if err:
3599
+ return None, (409, err)
3600
+ ref = evals_dataset(snap)
3601
+ body = None
3602
+ if ref is not None:
3603
+ body = self.dataset(rid, raw=True)
3604
+ if body is None:
3605
+ # A run that never started kept no body: the dataset's, if it
3606
+ # still reads as the version the run was submitted against.
3607
+ now = DATASETS.snapshot(ref.get("id")) if DATASETS is not None else None
3608
+ if now is None or not ref.get("version") or now[1] != ref.get("version"):
3609
+ return None, (409, f"the version of the dataset {ref.get('name') or ref.get('id')!r} "
3610
+ "this run was submitted against is no longer kept")
3611
+ body = now[0]
3612
+ _, err = self._plugin_args(run)
3613
+ if err:
3614
+ return None, (409, err)
3615
+ _, err = worker_destinations(snap)
3616
+ if err:
3617
+ return None, (403, err)
3618
+ return self.submit(snap, body, rerun_of=rid), None
3619
+
3528
3620
  def rerun_item(self, rid, index):
3529
3621
  """
3530
3622
  One item against the run's snapshot, by its index, updating the row in
@@ -3671,34 +3763,48 @@ class Queue:
3671
3763
  has gone fails the run with the reason stated. Returns the run dir,
3672
3764
  or (None, error)."""
3673
3765
  snap = run["snapshot"]
3674
- content = content_of(snap) or {}
3766
+ files, err = self._pinned_files(snap)
3767
+ if err:
3768
+ return None, err
3675
3769
  rundir = self.dir / run["id"]
3676
3770
  files_dir = rundir / "files"
3677
3771
  shutil.rmtree(rundir, ignore_errors=True)
3678
3772
  files_dir.mkdir(parents=True)
3679
- if content.get("type") == "source":
3680
- ref = content.get("ref") or {}
3681
- label = ref.get("name") or ref.get("id")
3682
- if SOURCES.get(ref.get("id")) is None:
3683
- return None, f"the source {label!r} is gone"
3684
- revs = content.get("revs") if isinstance(content.get("revs"), dict) else None
3685
- changed = []
3686
- for name in dict.fromkeys(content.get("files") or []):
3687
- path = SOURCES.file_path(ref["id"], name)
3688
- if path is None or not path.is_file():
3689
- return None, f"{name!r} is gone from the source {label!r}"
3690
- if revs is not None:
3691
- path = SOURCES.rev_path(ref["id"], name, revs.get(name))
3692
- if path is None:
3693
- changed.append(repr(name))
3694
- continue
3695
- shutil.copy2(path, files_dir / name)
3696
- if changed:
3697
- return None, (f"{', '.join(changed)} {'has' if len(changed) == 1 else 'have'} "
3698
- f"changed in the source {label!r} since this run was queued")
3773
+ for name, path in files:
3774
+ shutil.copy2(path, files_dir / name)
3699
3775
  (rundir / "run.json").write_text(json.dumps(snap))
3700
3776
  return rundir, None
3701
3777
 
3778
+ @staticmethod
3779
+ def _pinned_files(snap):
3780
+ """Each file a run document reads, by name, and the path holding the
3781
+ bytes it pinned at submit -- or (None, why), naming the Source that
3782
+ has gone, or the files gone from it or changed since. A document
3783
+ with no Source reads no files."""
3784
+ content = content_of(snap) or {}
3785
+ if content.get("type") != "source":
3786
+ return [], None
3787
+ ref = content.get("ref") or {}
3788
+ label = ref.get("name") or ref.get("id")
3789
+ if SOURCES.get(ref.get("id")) is None:
3790
+ return None, f"the source {label!r} is gone"
3791
+ revs = content.get("revs") if isinstance(content.get("revs"), dict) else None
3792
+ files, changed = [], []
3793
+ for name in dict.fromkeys(content.get("files") or []):
3794
+ path = SOURCES.file_path(ref["id"], name)
3795
+ if path is None or not path.is_file():
3796
+ return None, f"{name!r} is gone from the source {label!r}"
3797
+ if revs is not None:
3798
+ path = SOURCES.rev_path(ref["id"], name, revs.get(name))
3799
+ if path is None:
3800
+ changed.append(repr(name))
3801
+ continue
3802
+ files.append((name, path))
3803
+ if changed:
3804
+ return None, (f"{', '.join(changed)} {'has' if len(changed) == 1 else 'have'} "
3805
+ f"changed in the source {label!r} since this run was queued")
3806
+ return files, None
3807
+
3702
3808
  def _execute(self, run):
3703
3809
  """One run, FIFO. Never more than one of these at a time: the worker
3704
3810
  is a single thread, so the queue is single-flight by construction."""
@@ -5199,6 +5305,9 @@ class Handler(BaseHTTPRequestHandler):
5199
5305
  return self._send(404, b"not found", "text/plain")
5200
5306
 
5201
5307
  def _queue_action(self, path):
5308
+ # Drained, so a keep-alive connection is not left holding the
5309
+ # page's `{}` in front of its next request.
5310
+ self._payload()
5202
5311
  parts = path.split("/")
5203
5312
  rid = parts[3]
5204
5313
  action = parts[4] if len(parts) > 4 else ""
@@ -5206,6 +5315,10 @@ class Handler(BaseHTTPRequestHandler):
5206
5315
  run, err = QUEUE.cancel(rid)
5207
5316
  elif action == "resume":
5208
5317
  run, err = QUEUE.resume(rid)
5318
+ elif action == "rerun" and len(parts) == 5:
5319
+ run, err = QUEUE.rerun(rid)
5320
+ if not err:
5321
+ return self._json(201, {"run": run})
5209
5322
  elif action == "items" and len(parts) == 6:
5210
5323
  run, err = QUEUE.rerun_item(rid, urllib.parse.unquote(parts[5]))
5211
5324
  elif action == "rescore" and len(parts) == 6:
@@ -1,3 +1,3 @@
1
- import{$ as e,C as t,H as n,O as r,S as i,b as a,c as o,d as s,et as c,f as l,h as u,k as d,m as f,n as p,p as m,r as h,v as g,x as _,y as v}from"./tokens-iGpvbD5R.js";var y=c(),b=e(),x=[{id:`text`,label:`Plain text`,settings:[]},{id:`list`,label:`List`,settings:[{key:`parse`,label:`Parse as`,type:`select`,choices:[{value:`csv`,label:`CSV`},{value:`lines`,label:`One a line`},{value:`json`,label:`JSON array`}]},{key:`preamble`,label:`Ignore preamble`,type:`checkbox`}]}],S=[{id:`case`,label:`Case`,accepts:[`list`],options:[{key:`mode`,label:`Mode`,type:`select`,choices:[{value:`LOWER`,label:`lower`},{value:`SENTENCE`,label:`Sentence`},{value:`TITLE`,label:`Title`},{value:`UPPER`,label:`UPPER`},{value:`NONE`,label:`As replied`}]}],defaults:()=>({type:`case`,mode:`LOWER`})},{id:`join`,label:`Join`,accepts:[`list`],options:[{key:`with`,label:`Separator`,type:`text`}],defaults:()=>({type:`join`,with:`, `})}],C=[{name:`language`,type:`value`,value:`English UK`},{name:`geo-location`,type:`value`,value:`Hamilton, Waikato, New Zealand`},{name:`geo`,type:`block`,enabled:!1}],w=[{path:`group/open`,title:`Group, open`},{path:`group/closed`,title:`Group, closed with a note`},{path:`group/no-menu`,title:`Group without actions`},{path:`group/sub`,title:`Sub-group, level 3`},{path:`group/depth`,title:`Group, three levels deep`},{path:`group/renaming`,title:`Group, renaming`},{path:`menu/disabled`,title:`Menu with a disabled item`},{path:`header/pipeline`,title:`Context header, a Pipeline`},{path:`header/dataset`,title:`Context header, a Dataset`},{path:`header/source`,title:`Context header, a Source with its crumb`},{path:`header/long`,title:`Context header, a long name`},{path:`menu/busy`,title:`Menu with a busy item`},{path:`loading/group`,title:`Loading, inside a group`},{path:`loading/error`,title:`Loading failed, with Retry`},{path:`loading/busy-button`,title:`Busy buttons`},{path:`repeater/empty`,title:`Repeater, empty`},{path:`repeater/min`,title:`Repeater at min 1`},{path:`repeater/between`,title:`Repeater, two of four`},{path:`repeater/max`,title:`Repeater at max`},{path:`repeater/locked`,title:`Repeater, a locked row`},{path:`props/controls`,title:`Property table, every control`},{path:`props/required`,title:`Property table, required only`},{path:`props/long`,title:`Property table, long values`},{path:`props/widths`,title:`Property table, a width per value`},{path:`props/secret`,title:`Property table, a key held and not held`},{path:`props/code`,title:`Property table, an endpoint and a payload`},{path:`table/sortable`,title:`Table, sortable headers and a row menu`},{path:`table/paged`,title:`Table, paged ten at a time`},{path:`table/filter`,title:`Table with a filter bar`},{path:`table/filter-on`,title:`Table, filtered, with a scope`},{path:`edit/form`,title:`Edit modal, a profile`},{path:`edit/plain`,title:`Edit modal, one section and no menu`},{path:`edit/long`,title:`Edit modal, a form that scrolls`},{path:`edit/status`,title:`Edit modal, a status that is a refusal`},{path:`view/tokens`,title:`Token mappings`},{path:`view/output`,title:`Output with Case`},{path:`view/output-text`,title:`Output, plain text`},{path:`view/output-two`,title:`Output with two modifiers`},{path:`view/output-twice`,title:`Output with the same modifier twice`},{path:`view/output-none`,title:`Responses, read as text`},{path:`view/output-flow`,title:`Responses with Read as`}],T=n(),E=()=>{},D=(...e)=>e.map(e=>({label:e,icon:e===`Remove`?i.remove:e===`Clone`?i.clone:i.rename,onSelect:E,destructive:e===`Remove`}));function O({start:e,min:t,max:n,locked:r}){let[i,a]=(0,y.useState)(e);return(0,T.jsx)(f,{noun:`scenario`,items:i,...t===void 0?{}:{min:t},...n===void 0?{}:{max:n},...r?{locked:r}:{},onAdd:()=>a([...i,`Target ${i.length+1}`]),onRemove:e=>a(i.filter((t,n)=>n!==e)),onClone:e=>a([...i.slice(0,e+1),`${i[e]} copy`,...i.slice(e+1)]),onRename:E,empty:`No targets yet.`,renderItem:(e,t,n)=>(0,T.jsx)(g,{id:`scenario-${t}`,title:e,level:3,actions:n,children:(0,T.jsx)(`p`,{className:`note`,children:`A Target profile and a prompt for each job.`})},`${e}-${t}`)})}function k(){let[e,t]=(0,y.useState)(C),n=(n,r)=>t(e.map(e=>e.name===n?{...e,...r}:e));return(0,T.jsx)(p,{mappings:e,onValue:(e,t)=>n(e,{value:t}),onEnabled:(e,t)=>n(e,{enabled:t}),onEdit:E,onRemove:n=>t(e.filter(e=>e.name!==n)),onAdd:()=>t([...e,{name:`token${e.length+1}`,type:`value`,value:``}])})}function A({start:e,format:t=!0,readAs:n}){let[r,i]=(0,y.useState)(e),[a,o]=(0,y.useState)(t),[s,c]=(0,y.useState)(n);return(0,T.jsx)(h,{id:`output`,out:r,format:a,readAs:s,outputKinds:x,modifiers:S,onAddFormat:()=>o(!0),onRemoveFormat:()=>{o(!1),i({kind:`text`,modifiers:[]})},onReadAs:c,onKind:e=>i({kind:e,modifiers:r.modifiers.filter(t=>S.find(e=>e.id===t.type)?.accepts.includes(e))}),onModifier:(e,t)=>i({...r,modifiers:r.modifiers.map((n,r)=>r===e?t:n)}),onAddModifier:e=>i({...r,modifiers:[...r.modifiers,e]}),onRemoveModifier:e=>i({...r,modifiers:r.modifiers.filter((t,n)=>n!==e)})})}function j(){let[e,t]=(0,y.useState)(`Job 1`),[n,r]=(0,y.useState)(!0);return(0,T.jsx)(g,{id:`renaming`,title:e,actions:[{label:`Rename`,icon:i.rename,onSelect:()=>r(!0)}],...n?{rename:{value:e,onCommit:n=>{t(n||e),r(!1)},onCancel:()=>r(!1)}}:{},children:(0,T.jsx)(`p`,{className:`note`,children:`Enter or leaving keeps the name; Escape cancels.`})})}function M({sections:e,menu:t,status:n,statusBad:r,outcome:i}){return(0,T.jsx)(s,{title:`Lab Ollama`,...n?{status:n}:{},...r?{statusBad:!0}:{},...t?{menu:t}:{},sections:e,actions:[{label:`Test connection`,icon:d,onSelect:E,outcome:i?{label:i,onOpen:E}:void 0}],onClose:E})}var N=()=>[{key:`name`,label:`Name`,required:!0,control:{type:`text`,value:`Lab Ollama`,onChange:E}},{key:`type`,label:`Type`,required:!0,control:{type:`select`,value:`ollama`,onChange:E,options:[{value:`ollama`,label:`Ollama (Local)`},{value:`anthropic`,label:`Anthropic`}]}},{key:`url`,label:`Address`,control:{type:`text`,value:``,placeholder:`the lab's Ollama`,onChange:E}},{key:`key`,label:`Key`,control:{type:`secret`,value:``,onChange:E}},{key:`model`,label:`Model`,control:{type:`text`,value:`minicpm-v4.6:latest`,onChange:E}}],P=()=>[{key:`seed`,label:`Seed`,control:{type:`text`,value:``,placeholder:`1234`,marker:`default`,onChange:E}},{key:`nPredict`,label:`Reply tokens`,control:{type:`text`,value:``,size:`num`,marker:`server`,onChange:E}},{key:`temperature`,label:`Temperature`,control:{type:`text`,value:`0.3`,onChange:E}},{key:`repeatPenalty`,label:`Repeat penalty`,control:{type:`text`,value:``,size:`num`,marker:`server`,onChange:E}}],F=()=>[{key:`endpoint`,label:`Endpoint`,required:!0,control:{type:`code`,text:`http://127.0.0.1:11434/api/chat`}},{key:`prompt`,label:`Prompt`,required:!0,control:{type:`static`,text:`(prompt)`}},{key:`model`,label:`model`,control:{type:`code`,text:`minicpm-v4.6:latest`}},{key:`options`,label:`options`,control:{type:`code`,text:`{"seed":1234,"temperature":0.3}`}}],I={key:`kind`,label:`Response Format`,required:!0,control:{type:`select`,value:`list`,onChange:E,options:x.map(e=>({value:e.id,label:e.label}))}},L=({children:e})=>(0,T.jsx)(`div`,{style:{borderTop:`1px solid var(--line)`,paddingTop:`var(--s4)`},children:e}),R=(...e)=>e.map(e=>({label:e,icon:e===`Delete`?i.remove:e===`Clone`?i.clone:i.rename,onSelect:E,destructive:e===`Delete`})),z={"header/pipeline":()=>(0,T.jsx)(L,{children:(0,T.jsx)(u,{kind:`Pipeline`,name:`Pipeline 1`,onPick:E,meta:`2 jobs · 3 Targets`,primary:{label:`New pipeline`,icon:r,onSelect:E},actions:R(`Rename`,`Clone`,`Export YAML`,`Import YAML…`,`Restore…`,`Delete`)})}),"header/dataset":()=>(0,T.jsx)(L,{children:(0,T.jsx)(u,{kind:`Dataset`,name:`Demo 1`,onPick:E,meta:`62 cases · 19 graded · Source: Demo 1 images`,primary:{label:`New dataset`,icon:r,onSelect:E},actions:R(`Rename`,`Clone`,`Import JSON…`,`Export JSON`,`Restore…`,`Delete`)})}),"header/source":()=>(0,T.jsx)(L,{children:(0,T.jsx)(u,{kind:`Source`,name:`Demo 1 images`,crumb:{label:`Sources`,onClick:E},onPick:E,meta:`File Library · 63 files · 48 MB`,primary:{label:`Upload`,icon:t,onSelect:E},actions:R(`Rename`,`Delete`)})}),"header/long":()=>(0,T.jsx)(L,{children:(0,T.jsx)(u,{kind:`Pipeline`,name:`Receipts and handwritten menus, second pass with the tighter prompt and a Contains metric`,onPick:E,meta:`12 jobs · 8 Targets`,primary:{label:`New pipeline`,icon:r,onSelect:E},actions:R(`Rename`,`Clone`,`Delete`)})}),"group/open":()=>(0,T.jsx)(g,{id:`g`,title:`Evals`,actions:D(`Change`,`Remove`),children:(0,T.jsx)(`p`,{className:`note`,children:`The graded set.`})}),"group/closed":()=>(0,T.jsx)(g,{id:`g`,title:`Content`,open:!1,note:`Sample library, 63 files`,actions:D(`Change`,`Remove`)}),"group/no-menu":()=>(0,T.jsx)(g,{id:`g`,title:`Mapping`,children:(0,T.jsx)(`p`,{className:`note`,children:`Nothing to do to it.`})}),"group/sub":()=>(0,T.jsx)(g,{id:`g`,title:`Job 1`,actions:D(`Rename`,`Remove`),children:(0,T.jsx)(g,{id:`g-sub`,title:`Scenarios`,level:3,actions:D(`Clear`),children:(0,T.jsx)(`p`,{className:`note`,children:`Inside a pane.`})})}),"group/depth":()=>(0,T.jsxs)(g,{id:`g`,title:`Job 1`,actions:D(`Rename`,`Remove`),children:[(0,T.jsxs)(g,{id:`g-scenarios`,title:`Scenarios`,level:3,children:[(0,T.jsx)(g,{id:`g-plain`,title:`Plain`,level:3,actions:D(`Rename`,`Remove`),children:(0,T.jsx)(`p`,{className:`note`,children:`A pane, a section of it, and a thing with a name inside that section.`})}),(0,T.jsx)(g,{id:`g-strict`,title:`Strict`,level:3,actions:D(`Rename`,`Remove`)})]}),(0,T.jsx)(g,{id:`g-output`,title:`Output`,level:3,children:(0,T.jsx)(`p`,{className:`note`,children:`The next section of the same pane.`})})]}),"group/renaming":()=>(0,T.jsx)(j,{}),"menu/disabled":()=>(0,T.jsx)(g,{id:`g`,title:`Target 4`,actions:[{label:`Rename`,icon:i.rename,onSelect:E},{label:`Clone`,icon:i.clone,onSelect:E,disabled:!0},{label:`Remove`,icon:i.remove,onSelect:E,destructive:!0}]}),"menu/busy":()=>(0,T.jsx)(g,{id:`g`,title:`History`,actions:[{label:`Export JSON`,icon:i.export,onSelect:E,busy:!0},{label:`Clear`,icon:i.clear,onSelect:E,destructive:!0}]}),"loading/group":()=>(0,T.jsx)(g,{id:`g`,title:`History`,actions:D(`Rename`),children:(0,T.jsx)(a,{label:`Runs`,delay:0})}),"loading/error":()=>(0,T.jsx)(g,{id:`g`,title:`History`,children:(0,T.jsx)(a,{label:`Runs`,error:`Runs not loaded: server unreachable.`,onRetry:E})}),"loading/busy-button":()=>(0,T.jsx)(g,{id:`g`,title:`Run`,children:(0,T.jsxs)(`div`,{className:`gallery-row`,children:[(0,T.jsxs)(`button`,{type:`button`,className:`run`,disabled:!0,"aria-busy":!0,children:[(0,T.jsx)(_,{size:16}),`Run`]}),(0,T.jsxs)(`button`,{type:`button`,className:`ghost`,disabled:!0,"aria-busy":!0,children:[(0,T.jsx)(_,{}),`Older`]})]})}),"repeater/empty":()=>(0,T.jsx)(O,{start:[]}),"repeater/min":()=>(0,T.jsx)(O,{start:[`Target 1`],min:1}),"repeater/between":()=>(0,T.jsx)(O,{start:[`Target 1`,`Target 2`],min:1,max:4}),"repeater/max":()=>(0,T.jsx)(O,{start:[`Target 1`,`Target 2`,`Target 3`,`Target 4`],min:1,max:4}),"repeater/locked":()=>(0,T.jsx)(O,{start:[`Reply`,`Target 1`],locked:(e,t)=>t===0}),"props/controls":()=>(0,T.jsx)(l,{label:`Controls`,onRename:E,onRemove:E,onAdd:E,addLabel:`Add row`,rows:[I,{key:`language`,label:`{language}`,code:!0,control:{type:`text`,value:`English UK`,onChange:E}},{key:`geo`,label:`{geo}…{/geo}`,code:!0,control:{type:`static`,text:`Block`},enabled:{checked:!0,onChange:E}},{key:`image`,label:`Image`,control:{type:`checkbox`,checked:!0,label:`Send`,onChange:E}}]}),"props/required":()=>(0,T.jsx)(l,{label:`Output`,onRemove:E,rows:[I]}),"props/long":()=>(0,T.jsx)(l,{label:`Long`,onRemove:E,rows:[{key:`geo-location`,label:`{geo-location}`,code:!0,control:{type:`text`,value:`Hamilton, Waikato, New Zealand, Southern Hemisphere, Earth`,onChange:E}},{key:`averylongtokennamethatdoesnotbreak`,label:`{averylongtokennamethatdoesnotbreak}`,code:!0,control:{type:`text`,value:``,onChange:E}}]}),"props/widths":()=>(0,T.jsx)(l,{label:`Widths`,rows:[{key:`type`,label:`Type`,required:!0,control:{type:`select`,value:`ollama`,onChange:E,options:[{value:`ollama`,label:`Ollama (Local)`},{value:`anthropic`,label:`Anthropic`}]}},{key:`url`,label:`Address`,control:{type:`text`,value:``,placeholder:`the lab's Ollama`,onChange:E}},{key:`model`,label:`Model`,control:{type:`text`,value:`minicpm-v4.6:latest`,onChange:E}},{key:`seed`,label:`Seed`,control:{type:`text`,value:``,placeholder:`1234`,marker:`default`,onChange:E}},{key:`nPredict`,label:`Reply tokens`,control:{type:`text`,value:``,size:`num`,marker:`server`,onChange:E}},{key:`temperature`,label:`Temperature`,control:{type:`text`,value:`0.2`,onChange:E}},{key:`grammar`,label:`Grammar`,control:{type:`textarea`,value:`root ::= item (", " item)*
1
+ import{$ as e,C as t,H as n,O as r,S as i,b as a,c as o,d as s,et as c,f as l,h as u,k as d,m as f,n as p,p as m,r as h,v as g,x as _,y as v}from"./tokens-DLRdTFGY.js";var y=c(),b=e(),x=[{id:`text`,label:`Plain text`,settings:[]},{id:`list`,label:`List`,settings:[{key:`parse`,label:`Parse as`,type:`select`,choices:[{value:`csv`,label:`CSV`},{value:`lines`,label:`One a line`},{value:`json`,label:`JSON array`}]},{key:`preamble`,label:`Ignore preamble`,type:`checkbox`}]}],S=[{id:`case`,label:`Case`,accepts:[`list`],options:[{key:`mode`,label:`Mode`,type:`select`,choices:[{value:`LOWER`,label:`lower`},{value:`SENTENCE`,label:`Sentence`},{value:`TITLE`,label:`Title`},{value:`UPPER`,label:`UPPER`},{value:`NONE`,label:`As replied`}]}],defaults:()=>({type:`case`,mode:`LOWER`})},{id:`join`,label:`Join`,accepts:[`list`],options:[{key:`with`,label:`Separator`,type:`text`}],defaults:()=>({type:`join`,with:`, `})}],C=[{name:`language`,type:`value`,value:`English UK`},{name:`geo-location`,type:`value`,value:`Hamilton, Waikato, New Zealand`},{name:`geo`,type:`block`,enabled:!1}],w=[{path:`group/open`,title:`Group, open`},{path:`group/closed`,title:`Group, closed with a note`},{path:`group/no-menu`,title:`Group without actions`},{path:`group/sub`,title:`Sub-group, level 3`},{path:`group/depth`,title:`Group, three levels deep`},{path:`group/renaming`,title:`Group, renaming`},{path:`menu/disabled`,title:`Menu with a disabled item`},{path:`header/pipeline`,title:`Context header, a Pipeline`},{path:`header/dataset`,title:`Context header, a Dataset`},{path:`header/source`,title:`Context header, a Source with its crumb`},{path:`header/long`,title:`Context header, a long name`},{path:`menu/busy`,title:`Menu with a busy item`},{path:`loading/group`,title:`Loading, inside a group`},{path:`loading/error`,title:`Loading failed, with Retry`},{path:`loading/busy-button`,title:`Busy buttons`},{path:`repeater/empty`,title:`Repeater, empty`},{path:`repeater/min`,title:`Repeater at min 1`},{path:`repeater/between`,title:`Repeater, two of four`},{path:`repeater/max`,title:`Repeater at max`},{path:`repeater/locked`,title:`Repeater, a locked row`},{path:`props/controls`,title:`Property table, every control`},{path:`props/required`,title:`Property table, required only`},{path:`props/long`,title:`Property table, long values`},{path:`props/widths`,title:`Property table, a width per value`},{path:`props/secret`,title:`Property table, a key held and not held`},{path:`props/code`,title:`Property table, an endpoint and a payload`},{path:`table/sortable`,title:`Table, sortable headers and a row menu`},{path:`table/paged`,title:`Table, paged ten at a time`},{path:`table/filter`,title:`Table with a filter bar`},{path:`table/filter-on`,title:`Table, filtered, with a scope`},{path:`edit/form`,title:`Edit modal, a profile`},{path:`edit/plain`,title:`Edit modal, one section and no menu`},{path:`edit/long`,title:`Edit modal, a form that scrolls`},{path:`edit/status`,title:`Edit modal, a status that is a refusal`},{path:`view/tokens`,title:`Token mappings`},{path:`view/output`,title:`Output with Case`},{path:`view/output-text`,title:`Output, plain text`},{path:`view/output-two`,title:`Output with two modifiers`},{path:`view/output-twice`,title:`Output with the same modifier twice`},{path:`view/output-none`,title:`Responses, read as text`},{path:`view/output-flow`,title:`Responses with Read as`}],T=n(),E=()=>{},D=(...e)=>e.map(e=>({label:e,icon:e===`Remove`?i.remove:e===`Clone`?i.clone:i.rename,onSelect:E,destructive:e===`Remove`}));function O({start:e,min:t,max:n,locked:r}){let[i,a]=(0,y.useState)(e);return(0,T.jsx)(f,{noun:`scenario`,items:i,...t===void 0?{}:{min:t},...n===void 0?{}:{max:n},...r?{locked:r}:{},onAdd:()=>a([...i,`Target ${i.length+1}`]),onRemove:e=>a(i.filter((t,n)=>n!==e)),onClone:e=>a([...i.slice(0,e+1),`${i[e]} copy`,...i.slice(e+1)]),onRename:E,empty:`No targets yet.`,renderItem:(e,t,n)=>(0,T.jsx)(g,{id:`scenario-${t}`,title:e,level:3,actions:n,children:(0,T.jsx)(`p`,{className:`note`,children:`A Target profile and a prompt for each job.`})},`${e}-${t}`)})}function k(){let[e,t]=(0,y.useState)(C),n=(n,r)=>t(e.map(e=>e.name===n?{...e,...r}:e));return(0,T.jsx)(p,{mappings:e,onValue:(e,t)=>n(e,{value:t}),onEnabled:(e,t)=>n(e,{enabled:t}),onEdit:E,onRemove:n=>t(e.filter(e=>e.name!==n)),onAdd:()=>t([...e,{name:`token${e.length+1}`,type:`value`,value:``}])})}function A({start:e,format:t=!0,readAs:n}){let[r,i]=(0,y.useState)(e),[a,o]=(0,y.useState)(t),[s,c]=(0,y.useState)(n);return(0,T.jsx)(h,{id:`output`,out:r,format:a,readAs:s,outputKinds:x,modifiers:S,onAddFormat:()=>o(!0),onRemoveFormat:()=>{o(!1),i({kind:`text`,modifiers:[]})},onReadAs:c,onKind:e=>i({kind:e,modifiers:r.modifiers.filter(t=>S.find(e=>e.id===t.type)?.accepts.includes(e))}),onModifier:(e,t)=>i({...r,modifiers:r.modifiers.map((n,r)=>r===e?t:n)}),onAddModifier:e=>i({...r,modifiers:[...r.modifiers,e]}),onRemoveModifier:e=>i({...r,modifiers:r.modifiers.filter((t,n)=>n!==e)})})}function j(){let[e,t]=(0,y.useState)(`Job 1`),[n,r]=(0,y.useState)(!0);return(0,T.jsx)(g,{id:`renaming`,title:e,actions:[{label:`Rename`,icon:i.rename,onSelect:()=>r(!0)}],...n?{rename:{value:e,onCommit:n=>{t(n||e),r(!1)},onCancel:()=>r(!1)}}:{},children:(0,T.jsx)(`p`,{className:`note`,children:`Enter or leaving keeps the name; Escape cancels.`})})}function M({sections:e,menu:t,status:n,statusBad:r,outcome:i}){return(0,T.jsx)(s,{title:`Lab Ollama`,...n?{status:n}:{},...r?{statusBad:!0}:{},...t?{menu:t}:{},sections:e,actions:[{label:`Test connection`,icon:d,onSelect:E,outcome:i?{label:i,onOpen:E}:void 0}],onClose:E})}var N=()=>[{key:`name`,label:`Name`,required:!0,control:{type:`text`,value:`Lab Ollama`,onChange:E}},{key:`type`,label:`Type`,required:!0,control:{type:`select`,value:`ollama`,onChange:E,options:[{value:`ollama`,label:`Ollama (Local)`},{value:`anthropic`,label:`Anthropic`}]}},{key:`url`,label:`Address`,control:{type:`text`,value:``,placeholder:`the lab's Ollama`,onChange:E}},{key:`key`,label:`Key`,control:{type:`secret`,value:``,onChange:E}},{key:`model`,label:`Model`,control:{type:`text`,value:`minicpm-v4.6:latest`,onChange:E}}],P=()=>[{key:`seed`,label:`Seed`,control:{type:`text`,value:``,placeholder:`1234`,marker:`default`,onChange:E}},{key:`nPredict`,label:`Reply tokens`,control:{type:`text`,value:``,size:`num`,marker:`server`,onChange:E}},{key:`temperature`,label:`Temperature`,control:{type:`text`,value:`0.3`,onChange:E}},{key:`repeatPenalty`,label:`Repeat penalty`,control:{type:`text`,value:``,size:`num`,marker:`server`,onChange:E}}],F=()=>[{key:`endpoint`,label:`Endpoint`,required:!0,control:{type:`code`,text:`http://127.0.0.1:11434/api/chat`}},{key:`prompt`,label:`Prompt`,required:!0,control:{type:`static`,text:`(prompt)`}},{key:`model`,label:`model`,control:{type:`code`,text:`minicpm-v4.6:latest`}},{key:`options`,label:`options`,control:{type:`code`,text:`{"seed":1234,"temperature":0.3}`}}],I={key:`kind`,label:`Response Format`,required:!0,control:{type:`select`,value:`list`,onChange:E,options:x.map(e=>({value:e.id,label:e.label}))}},L=({children:e})=>(0,T.jsx)(`div`,{style:{borderTop:`1px solid var(--line)`,paddingTop:`var(--s4)`},children:e}),R=(...e)=>e.map(e=>({label:e,icon:e===`Delete`?i.remove:e===`Clone`?i.clone:i.rename,onSelect:E,destructive:e===`Delete`})),z={"header/pipeline":()=>(0,T.jsx)(L,{children:(0,T.jsx)(u,{kind:`Pipeline`,name:`Pipeline 1`,onPick:E,meta:`2 jobs · 3 Targets`,primary:{label:`New pipeline`,icon:r,onSelect:E},actions:R(`Rename`,`Clone`,`Export YAML`,`Import YAML…`,`Restore…`,`Delete`)})}),"header/dataset":()=>(0,T.jsx)(L,{children:(0,T.jsx)(u,{kind:`Dataset`,name:`Demo 1`,onPick:E,meta:`62 cases · 19 graded · Source: Demo 1 images`,primary:{label:`New dataset`,icon:r,onSelect:E},actions:R(`Rename`,`Clone`,`Import JSON…`,`Export JSON`,`Restore…`,`Delete`)})}),"header/source":()=>(0,T.jsx)(L,{children:(0,T.jsx)(u,{kind:`Source`,name:`Demo 1 images`,crumb:{label:`Sources`,onClick:E},onPick:E,meta:`File Library · 63 files · 48 MB`,primary:{label:`Upload`,icon:t,onSelect:E},actions:R(`Rename`,`Delete`)})}),"header/long":()=>(0,T.jsx)(L,{children:(0,T.jsx)(u,{kind:`Pipeline`,name:`Receipts and handwritten menus, second pass with the tighter prompt and a Contains metric`,onPick:E,meta:`12 jobs · 8 Targets`,primary:{label:`New pipeline`,icon:r,onSelect:E},actions:R(`Rename`,`Clone`,`Delete`)})}),"group/open":()=>(0,T.jsx)(g,{id:`g`,title:`Evals`,actions:D(`Change`,`Remove`),children:(0,T.jsx)(`p`,{className:`note`,children:`The graded set.`})}),"group/closed":()=>(0,T.jsx)(g,{id:`g`,title:`Content`,open:!1,note:`Sample library, 63 files`,actions:D(`Change`,`Remove`)}),"group/no-menu":()=>(0,T.jsx)(g,{id:`g`,title:`Mapping`,children:(0,T.jsx)(`p`,{className:`note`,children:`Nothing to do to it.`})}),"group/sub":()=>(0,T.jsx)(g,{id:`g`,title:`Job 1`,actions:D(`Rename`,`Remove`),children:(0,T.jsx)(g,{id:`g-sub`,title:`Scenarios`,level:3,actions:D(`Clear`),children:(0,T.jsx)(`p`,{className:`note`,children:`Inside a pane.`})})}),"group/depth":()=>(0,T.jsxs)(g,{id:`g`,title:`Job 1`,actions:D(`Rename`,`Remove`),children:[(0,T.jsxs)(g,{id:`g-scenarios`,title:`Scenarios`,level:3,children:[(0,T.jsx)(g,{id:`g-plain`,title:`Plain`,level:3,actions:D(`Rename`,`Remove`),children:(0,T.jsx)(`p`,{className:`note`,children:`A pane, a section of it, and a thing with a name inside that section.`})}),(0,T.jsx)(g,{id:`g-strict`,title:`Strict`,level:3,actions:D(`Rename`,`Remove`)})]}),(0,T.jsx)(g,{id:`g-output`,title:`Output`,level:3,children:(0,T.jsx)(`p`,{className:`note`,children:`The next section of the same pane.`})})]}),"group/renaming":()=>(0,T.jsx)(j,{}),"menu/disabled":()=>(0,T.jsx)(g,{id:`g`,title:`Target 4`,actions:[{label:`Rename`,icon:i.rename,onSelect:E},{label:`Clone`,icon:i.clone,onSelect:E,disabled:!0},{label:`Remove`,icon:i.remove,onSelect:E,destructive:!0}]}),"menu/busy":()=>(0,T.jsx)(g,{id:`g`,title:`History`,actions:[{label:`Export JSON`,icon:i.export,onSelect:E,busy:!0},{label:`Clear`,icon:i.clear,onSelect:E,destructive:!0}]}),"loading/group":()=>(0,T.jsx)(g,{id:`g`,title:`History`,actions:D(`Rename`),children:(0,T.jsx)(a,{label:`Runs`,delay:0})}),"loading/error":()=>(0,T.jsx)(g,{id:`g`,title:`History`,children:(0,T.jsx)(a,{label:`Runs`,error:`Runs not loaded: server unreachable.`,onRetry:E})}),"loading/busy-button":()=>(0,T.jsx)(g,{id:`g`,title:`Run`,children:(0,T.jsxs)(`div`,{className:`gallery-row`,children:[(0,T.jsxs)(`button`,{type:`button`,className:`run`,disabled:!0,"aria-busy":!0,children:[(0,T.jsx)(_,{size:16}),`Run`]}),(0,T.jsxs)(`button`,{type:`button`,className:`ghost`,disabled:!0,"aria-busy":!0,children:[(0,T.jsx)(_,{}),`Older`]})]})}),"repeater/empty":()=>(0,T.jsx)(O,{start:[]}),"repeater/min":()=>(0,T.jsx)(O,{start:[`Target 1`],min:1}),"repeater/between":()=>(0,T.jsx)(O,{start:[`Target 1`,`Target 2`],min:1,max:4}),"repeater/max":()=>(0,T.jsx)(O,{start:[`Target 1`,`Target 2`,`Target 3`,`Target 4`],min:1,max:4}),"repeater/locked":()=>(0,T.jsx)(O,{start:[`Reply`,`Target 1`],locked:(e,t)=>t===0}),"props/controls":()=>(0,T.jsx)(l,{label:`Controls`,onRename:E,onRemove:E,onAdd:E,addLabel:`Add row`,rows:[I,{key:`language`,label:`{language}`,code:!0,control:{type:`text`,value:`English UK`,onChange:E}},{key:`geo`,label:`{geo}…{/geo}`,code:!0,control:{type:`static`,text:`Block`},enabled:{checked:!0,onChange:E}},{key:`image`,label:`Image`,control:{type:`checkbox`,checked:!0,label:`Send`,onChange:E}}]}),"props/required":()=>(0,T.jsx)(l,{label:`Output`,onRemove:E,rows:[I]}),"props/long":()=>(0,T.jsx)(l,{label:`Long`,onRemove:E,rows:[{key:`geo-location`,label:`{geo-location}`,code:!0,control:{type:`text`,value:`Hamilton, Waikato, New Zealand, Southern Hemisphere, Earth`,onChange:E}},{key:`averylongtokennamethatdoesnotbreak`,label:`{averylongtokennamethatdoesnotbreak}`,code:!0,control:{type:`text`,value:``,onChange:E}}]}),"props/widths":()=>(0,T.jsx)(l,{label:`Widths`,rows:[{key:`type`,label:`Type`,required:!0,control:{type:`select`,value:`ollama`,onChange:E,options:[{value:`ollama`,label:`Ollama (Local)`},{value:`anthropic`,label:`Anthropic`}]}},{key:`url`,label:`Address`,control:{type:`text`,value:``,placeholder:`the lab's Ollama`,onChange:E}},{key:`model`,label:`Model`,control:{type:`text`,value:`minicpm-v4.6:latest`,onChange:E}},{key:`seed`,label:`Seed`,control:{type:`text`,value:``,placeholder:`1234`,marker:`default`,onChange:E}},{key:`nPredict`,label:`Reply tokens`,control:{type:`text`,value:``,size:`num`,marker:`server`,onChange:E}},{key:`temperature`,label:`Temperature`,control:{type:`text`,value:`0.2`,onChange:E}},{key:`grammar`,label:`Grammar`,control:{type:`textarea`,value:`root ::= item (", " item)*
2
2
  item ::= [a-z ]+`,onChange:E}}]}),"props/secret":()=>(0,T.jsx)(l,{label:`Keys`,rows:[{key:`held`,label:`Key`,control:{type:`secret`,value:`sk-not-a-real-key-0000`,onChange:E}},{key:`empty`,label:`Key`,control:{type:`secret`,value:``,onChange:E}}]}),"props/code":()=>(0,T.jsx)(l,{label:`Debug`,rows:[{key:`endpoint`,label:`Endpoint`,required:!0,control:{type:`code`,text:`https://api.anthropic.com/v1/messages`}},{key:`prompt`,label:`Prompt`,required:!0,control:{type:`static`,text:`(prompt)`}},{key:`model`,label:`model`,control:{type:`code`,text:`claude-sonnet-4-5-20250929`}},{key:`options`,label:`options`,control:{type:`code`,text:`{"seed":1234,"temperature":0}`}},{key:`stream`,label:`stream`,control:{type:`code`,text:`false`}}]}),"table/sortable":()=>(0,T.jsx)(U,{count:4}),"table/paged":()=>(0,T.jsx)(U,{count:35,pageSize:10}),"table/filter":()=>(0,T.jsx)(H,{}),"table/filter-on":()=>(0,T.jsx)(H,{search:`IMG_40`,size:`large`}),"edit/form":()=>(0,T.jsx)(M,{outcome:`Connected`,menu:[{label:`Restore…`,icon:i.restore,onSelect:E},{label:`Apply preset: repeat 1.3`,icon:i.preset,onSelect:E}],sections:[{key:`identity`,rows:N()},{key:`settings`,title:`Settings`,rows:P()},{key:`debug`,title:`Debug`,rows:F(),folded:!0}]}),"edit/plain":()=>(0,T.jsx)(s,{title:`A mapping`,sections:[{key:`one`,rows:[I,{key:`with`,label:`Separator`,control:{type:`text`,value:`, `,onChange:E}}]}],onClose:E}),"edit/long":()=>(0,T.jsx)(M,{menu:[{label:`Restore…`,icon:i.restore,onSelect:E,disabled:!0}],sections:[{key:`identity`,rows:N()},{key:`settings`,title:`Settings`,rows:[...P(),{key:`grammar`,label:`Grammar`,control:{type:`textarea`,value:`root ::= item (", " item)*
3
3
  item ::= [a-z ]+`,onChange:E}},{key:`imagePosition`,label:`Image position`,control:{type:`select`,value:`after`,onChange:E,options:[{value:`before`,label:`before the prompt`},{value:`after`,label:`after the prompt`}]}},{key:`cachePrompt`,label:`Cache prompt`,control:{type:`select`,value:`off`,onChange:E,options:[{value:`on`,label:`on`},{value:`off`,label:`off`}]}}]},{key:`debug`,title:`Debug`,rows:F(),folded:!0}]}),"edit/status":()=>(0,T.jsx)(M,{status:`the relay could not reach http://127.0.0.1:11434`,statusBad:!0,sections:[{key:`identity`,rows:N()}]}),"view/tokens":()=>(0,T.jsx)(g,{id:`g`,title:`Token mappings`,level:3,children:(0,T.jsx)(k,{})}),"view/output":()=>(0,T.jsx)(A,{start:{kind:`list`,parse:`csv`,modifiers:[{type:`case`,mode:`LOWER`}]}}),"view/output-text":()=>(0,T.jsx)(A,{start:{kind:`text`,modifiers:[]}}),"view/output-two":()=>(0,T.jsx)(A,{start:{kind:`list`,parse:`csv`,modifiers:[{type:`case`,mode:`TITLE`},{type:`join`,with:` | `}]}}),"view/output-twice":()=>(0,T.jsx)(A,{start:{kind:`list`,parse:`csv`,modifiers:[{type:`case`,mode:`TITLE`},{type:`case`,mode:`LOWER`}]}}),"view/output-none":()=>(0,T.jsx)(A,{start:{kind:`text`,modifiers:[]},format:!1}),"view/output-flow":()=>(0,T.jsx)(A,{start:{kind:`text`,modifiers:[]},readAs:`@body('Ask')?['content']?[0]?['text']`})},B=w.map(e=>({...e,render:z[e.path]})),V=e=>Array.from({length:e},(e,t)=>({name:`IMG_${String(4e3+t*7919%97).padStart(4,`0`)}.jpg`,kb:t*37%900+40}));function H({search:e=``,size:t=`all`}){let[n,r]=(0,y.useState)(e),[i,a]=(0,y.useState)(t),[s,c]=(0,y.useState)(`shoot`),l=V(12),u=l.filter(e=>e.name.toLowerCase().includes(n.trim().toLowerCase())&&(i===`all`||i===`large`==e.kb>=400));return(0,T.jsxs)(g,{id:`files`,title:`Files`,children:[(0,T.jsx)(m,{noun:`files`,search:{label:`Name`,placeholder:`Search names`,value:n,onChange:r},count:{shown:u.length,total:l.length},filters:[{key:`source`,label:`Source`,value:s,neutral:null,onChange:c,options:[{value:`shoot`,label:`Tuesday shoot`},{value:`samples`,label:`Sample library`}]},{key:`size`,label:`Size`,value:i,onChange:a,options:[{value:`all`,label:`All`},{value:`small`,label:`Small`},{value:`large`,label:`Large`}]}]}),u.length?(0,T.jsx)(o,{label:`Files`,rows:u,rowKey:e=>e.name,columns:[{key:`name`,header:`Name`,cell:e=>e.name},{key:`size`,header:`Size`,sort:e=>e.kb,cell:e=>`${e.kb} KB`}]}):(0,T.jsx)(`p`,{className:`note`,children:`No files match these filters.`})]})}function U({count:e,pageSize:t}){let n=V(e);return(0,T.jsx)(g,{id:`files`,title:`Files`,children:(0,T.jsx)(o,{label:`Files`,rows:n,rowKey:e=>e.name,pageSize:t??null,columns:[{key:`name`,header:`Name`,cell:e=>e.name},{key:`size`,header:`Size`,sort:e=>e.kb,cell:e=>`${e.kb} KB`},{key:`menu`,header:``,sort:null,cell:e=>(0,T.jsx)(v,{label:`${e.name} actions`,actions:[{label:`Rename`,icon:i.rename,onSelect:E},{label:`Remove`,icon:i.remove,destructive:!0,onSelect:E}]})}]})})}var W=()=>decodeURIComponent(location.hash.replace(/^#\/?/,``));function G(){let[e,t]=(0,y.useState)(W);(0,y.useEffect)(()=>{let e=()=>t(W());return window.addEventListener(`hashchange`,e),()=>window.removeEventListener(`hashchange`,e)},[]);let n=B.find(t=>t.path===e);return n?(0,T.jsxs)(`main`,{className:`gallery-one`,"data-route":n.path,children:[(0,T.jsxs)(`nav`,{className:`gallery-nav`,children:[(0,T.jsx)(`a`,{href:`#/`,children:`All`}),(0,T.jsx)(`span`,{children:n.title})]}),n.render()]}):(0,T.jsxs)(`main`,{className:`gallery-index`,"data-route":``,children:[(0,T.jsx)(`h1`,{children:`Components`}),(0,T.jsx)(`ul`,{children:B.map(e=>(0,T.jsxs)(`li`,{children:[(0,T.jsx)(`a`,{href:`#/${e.path}`,children:e.title}),` `,(0,T.jsx)(`code`,{children:e.path})]},e.path))})]})}(0,b.createRoot)(document.getElementById(`root`)).render((0,T.jsx)(y.StrictMode,{children:(0,T.jsx)(G,{})}));