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/CHANGELOG.md +36 -0
- package/README.md +68 -141
- package/lab/VERSION +1 -1
- package/lab/evals-core.mjs +90 -19
- package/lab/run-evals.js +2 -2
- package/lab/server.py +165 -52
- package/lab/web/dist/assets/{gallery-DRBlZ8mP.js → gallery-B-7oyY37.js} +1 -1
- package/lab/web/dist/assets/main-BkZTEix2.js +21 -0
- package/lab/web/dist/assets/{main-wfqC6HcM.css → main-C_b7QoTv.css} +1 -1
- package/lab/web/dist/assets/tokens-DLRdTFGY.js +55 -0
- package/lab/web/dist/gallery.html +2 -2
- package/lab/web/dist/index.html +3 -3
- package/package.json +1 -1
- package/lab/web/dist/assets/main-DMpQmW8l.js +0 -21
- package/lab/web/dist/assets/tokens-iGpvbD5R.js +0 -55
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
|
|
1361
|
-
#
|
|
1362
|
-
|
|
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
|
|
1365
|
-
# Import reads it and versions 1 to
|
|
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 =
|
|
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 {"
|
|
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-
|
|
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
|
|
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`).
|
|
1481
|
-
|
|
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 =
|
|
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 {"
|
|
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
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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(
|
|
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(
|
|
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
|
|
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
|
|
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(
|
|
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
|
-
|
|
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
|
-
|
|
3680
|
-
|
|
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,{})}));
|