labnotes 0.0.17__tar.gz → 0.0.19__tar.gz

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.
Files changed (90) hide show
  1. {labnotes-0.0.17 → labnotes-0.0.19}/PKG-INFO +8 -1
  2. {labnotes-0.0.17 → labnotes-0.0.19}/README.md +7 -0
  3. {labnotes-0.0.17 → labnotes-0.0.19}/src/labnotes/context.md +7 -0
  4. {labnotes-0.0.17 → labnotes-0.0.19}/src/labnotes/count_runs.py +3 -3
  5. labnotes-0.0.19/src/labnotes/create_experiment.py +32 -0
  6. {labnotes-0.0.17 → labnotes-0.0.19}/src/labnotes/experiments.py +15 -2
  7. {labnotes-0.0.17 → labnotes-0.0.19}/src/labnotes/list_runs.py +5 -4
  8. {labnotes-0.0.17 → labnotes-0.0.19}/src/labnotes/load_experiment.py +4 -1
  9. {labnotes-0.0.17 → labnotes-0.0.19}/src/labnotes/runner/run_experiment.py +5 -1
  10. {labnotes-0.0.17 → labnotes-0.0.19}/src/labnotes/study_progress.py +4 -2
  11. labnotes-0.0.19/src/labnotes/study_runs.py +5 -0
  12. {labnotes-0.0.17 → labnotes-0.0.19}/src/labnotes/tables/list_experiment_runs.py +2 -1
  13. {labnotes-0.0.17 → labnotes-0.0.19}/src/labnotes/tables/open_tables.py +2 -1
  14. {labnotes-0.0.17 → labnotes-0.0.19}/src/labnotes/tables/read_run.py +2 -0
  15. {labnotes-0.0.17 → labnotes-0.0.19}/.gitignore +0 -0
  16. {labnotes-0.0.17 → labnotes-0.0.19}/CHANGELOG.md +0 -0
  17. {labnotes-0.0.17 → labnotes-0.0.19}/MIGRATIONS.md +0 -0
  18. {labnotes-0.0.17 → labnotes-0.0.19}/pyproject.toml +0 -0
  19. {labnotes-0.0.17 → labnotes-0.0.19}/src/labnotes/__init__.py +0 -0
  20. {labnotes-0.0.17 → labnotes-0.0.19}/src/labnotes/asset_url.py +0 -0
  21. {labnotes-0.0.17 → labnotes-0.0.19}/src/labnotes/cell_progress.py +0 -0
  22. {labnotes-0.0.17 → labnotes-0.0.19}/src/labnotes/cli.py +0 -0
  23. {labnotes-0.0.17 → labnotes-0.0.19}/src/labnotes/context_guide.py +0 -0
  24. {labnotes-0.0.17 → labnotes-0.0.19}/src/labnotes/create_app.py +0 -0
  25. {labnotes-0.0.17 → labnotes-0.0.19}/src/labnotes/data_html.py +0 -0
  26. {labnotes-0.0.17 → labnotes-0.0.19}/src/labnotes/default_config_text.py +0 -0
  27. {labnotes-0.0.17 → labnotes-0.0.19}/src/labnotes/drain_watch.py +0 -0
  28. {labnotes-0.0.17 → labnotes-0.0.19}/src/labnotes/format_value.py +0 -0
  29. {labnotes-0.0.17 → labnotes-0.0.19}/src/labnotes/init.py +0 -0
  30. {labnotes-0.0.17 → labnotes-0.0.19}/src/labnotes/list_experiments.py +0 -0
  31. {labnotes-0.0.17 → labnotes-0.0.19}/src/labnotes/load_config.py +0 -0
  32. {labnotes-0.0.17 → labnotes-0.0.19}/src/labnotes/models.py +0 -0
  33. {labnotes-0.0.17 → labnotes-0.0.19}/src/labnotes/notebook_kernel.py +0 -0
  34. {labnotes-0.0.17 → labnotes-0.0.19}/src/labnotes/open_experiments.py +0 -0
  35. {labnotes-0.0.17 → labnotes-0.0.19}/src/labnotes/open_runs.py +0 -0
  36. {labnotes-0.0.17 → labnotes-0.0.19}/src/labnotes/output_html.py +0 -0
  37. {labnotes-0.0.17 → labnotes-0.0.19}/src/labnotes/parse_run_view.py +0 -0
  38. {labnotes-0.0.17 → labnotes-0.0.19}/src/labnotes/pivot_cells.py +0 -0
  39. {labnotes-0.0.17 → labnotes-0.0.19}/src/labnotes/print_rows.py +0 -0
  40. {labnotes-0.0.17 → labnotes-0.0.19}/src/labnotes/python_blocks.py +0 -0
  41. {labnotes-0.0.17 → labnotes-0.0.19}/src/labnotes/read_config.py +0 -0
  42. {labnotes-0.0.17 → labnotes-0.0.19}/src/labnotes/read_design.py +0 -0
  43. {labnotes-0.0.17 → labnotes-0.0.19}/src/labnotes/read_note.py +0 -0
  44. {labnotes-0.0.17 → labnotes-0.0.19}/src/labnotes/read_path.py +0 -0
  45. {labnotes-0.0.17 → labnotes-0.0.19}/src/labnotes/render_markdown.py +0 -0
  46. {labnotes-0.0.17 → labnotes-0.0.19}/src/labnotes/run_columns.py +0 -0
  47. {labnotes-0.0.17 → labnotes-0.0.19}/src/labnotes/runner/__init__.py +0 -0
  48. {labnotes-0.0.17 → labnotes-0.0.19}/src/labnotes/runner/describe_event.py +0 -0
  49. {labnotes-0.0.17 → labnotes-0.0.19}/src/labnotes/runner/execute_run.py +0 -0
  50. {labnotes-0.0.17 → labnotes-0.0.19}/src/labnotes/runner/lock_path.py +0 -0
  51. {labnotes-0.0.17 → labnotes-0.0.19}/src/labnotes/runner/new_run_id.py +0 -0
  52. {labnotes-0.0.17 → labnotes-0.0.19}/src/labnotes/runner/param_args.py +0 -0
  53. {labnotes-0.0.17 → labnotes-0.0.19}/src/labnotes/runner/param_grid.py +0 -0
  54. {labnotes-0.0.17 → labnotes-0.0.19}/src/labnotes/runner/plan_runs.py +0 -0
  55. {labnotes-0.0.17 → labnotes-0.0.19}/src/labnotes/runner/read_experiment_config.py +0 -0
  56. {labnotes-0.0.17 → labnotes-0.0.19}/src/labnotes/runner/run_folder.py +0 -0
  57. {labnotes-0.0.17 → labnotes-0.0.19}/src/labnotes/runner/run_headless.py +0 -0
  58. {labnotes-0.0.17 → labnotes-0.0.19}/src/labnotes/runner/shell_command.py +0 -0
  59. {labnotes-0.0.17 → labnotes-0.0.19}/src/labnotes/runner/start_experiment.py +0 -0
  60. {labnotes-0.0.17 → labnotes-0.0.19}/src/labnotes/runner/stop_then_kill.py +0 -0
  61. {labnotes-0.0.17 → labnotes-0.0.19}/src/labnotes/runner/write_run_json.py +0 -0
  62. {labnotes-0.0.17 → labnotes-0.0.19}/src/labnotes/saved_outputs.py +0 -0
  63. {labnotes-0.0.17 → labnotes-0.0.19}/src/labnotes/serve_app.py +0 -0
  64. {labnotes-0.0.17 → labnotes-0.0.19}/src/labnotes/server.py +0 -0
  65. {labnotes-0.0.17 → labnotes-0.0.19}/src/labnotes/sort_href.py +0 -0
  66. {labnotes-0.0.17 → labnotes-0.0.19}/src/labnotes/sql_quote.py +0 -0
  67. {labnotes-0.0.17 → labnotes-0.0.19}/src/labnotes/static/labnotes.css +0 -0
  68. {labnotes-0.0.17 → labnotes-0.0.19}/src/labnotes/static/row-link.js +0 -0
  69. {labnotes-0.0.17 → labnotes-0.0.19}/src/labnotes/static/run-cell.js +0 -0
  70. {labnotes-0.0.17 → labnotes-0.0.19}/src/labnotes/static/view-toggle.js +0 -0
  71. {labnotes-0.0.17 → labnotes-0.0.19}/src/labnotes/stop_idle_kernels.py +0 -0
  72. {labnotes-0.0.17 → labnotes-0.0.19}/src/labnotes/summarize_design.py +0 -0
  73. {labnotes-0.0.17 → labnotes-0.0.19}/src/labnotes/tables/__init__.py +0 -0
  74. {labnotes-0.0.17 → labnotes-0.0.19}/src/labnotes/tables/experiment_rows.py +0 -0
  75. {labnotes-0.0.17 → labnotes-0.0.19}/src/labnotes/tables/file_prefix.py +0 -0
  76. {labnotes-0.0.17 → labnotes-0.0.19}/src/labnotes/tables/iso_time.py +0 -0
  77. {labnotes-0.0.17 → labnotes-0.0.19}/src/labnotes/tables/path_glob.py +0 -0
  78. {labnotes-0.0.17 → labnotes-0.0.19}/src/labnotes/tables/path_pattern.py +0 -0
  79. {labnotes-0.0.17 → labnotes-0.0.19}/src/labnotes/tables/read_experiment.py +0 -0
  80. {labnotes-0.0.17 → labnotes-0.0.19}/src/labnotes/tables/read_file.py +0 -0
  81. {labnotes-0.0.17 → labnotes-0.0.19}/src/labnotes/tables/summarize_experiments.py +0 -0
  82. {labnotes-0.0.17 → labnotes-0.0.19}/src/labnotes/templates/base.html +0 -0
  83. {labnotes-0.0.17 → labnotes-0.0.19}/src/labnotes/templates/experiment.html +0 -0
  84. {labnotes-0.0.17 → labnotes-0.0.19}/src/labnotes/templates/index.html +0 -0
  85. {labnotes-0.0.17 → labnotes-0.0.19}/src/labnotes/templates/macros.html +0 -0
  86. {labnotes-0.0.17 → labnotes-0.0.19}/src/labnotes/tui/__init__.py +0 -0
  87. {labnotes-0.0.17 → labnotes-0.0.19}/src/labnotes/tui/format_elapsed.py +0 -0
  88. {labnotes-0.0.17 → labnotes-0.0.19}/src/labnotes/tui/run_app.py +0 -0
  89. {labnotes-0.0.17 → labnotes-0.0.19}/src/labnotes/tui/run_tui.py +0 -0
  90. {labnotes-0.0.17 → labnotes-0.0.19}/uv.lock +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: labnotes
3
- Version: 0.0.17
3
+ Version: 0.0.19
4
4
  Summary: Browse experiment designs, notes, and run data from a folder.
5
5
  Project-URL: Repository, https://github.com/thekevinscott/labnotes
6
6
  Author-email: Kevin Scott <me@thekevinscott.com>
@@ -69,6 +69,10 @@ labnotes experiments foo runs # foo's runs, oldest first
69
69
 
70
70
  Each takes `--format table` (the default) or `--format json`.
71
71
 
72
+ `labnotes experiments new [NAME]` creates an experiment folder with a starter
73
+ `experiment.toml`, picking a random name if you give none. `new` itself can't
74
+ be an experiment name.
75
+
72
76
  `labnotes experiments foo run` runs `foo` from its `experiment.toml`, in the
73
77
  experiments folder:
74
78
 
@@ -94,6 +98,9 @@ Each param is appended as `--name value`, with the name as written (use
94
98
  `source-language` for a `--source-language` flag), booleans as 0 or 1, and lists
95
99
  and tables as JSON. labnotes writes `run.json` (params, created, ended, exit
96
100
  code), `stdout.log` and `stderr.log` to the run folder.
101
+ To exclude a run, add `"excluded": true` and an optional `"reason"` to its
102
+ `run.json`. An excluded run doesn't count toward `repeats`, so the next `run`
103
+ replaces it; both show as columns in `runs`.
97
104
 
98
105
  In a terminal, `run` shows a live table of runs (status and time) and stays
99
106
  open when they finish; `q` quits. Elsewhere, or with `--no-tui`, it prints a
@@ -46,6 +46,10 @@ labnotes experiments foo runs # foo's runs, oldest first
46
46
 
47
47
  Each takes `--format table` (the default) or `--format json`.
48
48
 
49
+ `labnotes experiments new [NAME]` creates an experiment folder with a starter
50
+ `experiment.toml`, picking a random name if you give none. `new` itself can't
51
+ be an experiment name.
52
+
49
53
  `labnotes experiments foo run` runs `foo` from its `experiment.toml`, in the
50
54
  experiments folder:
51
55
 
@@ -71,6 +75,9 @@ Each param is appended as `--name value`, with the name as written (use
71
75
  `source-language` for a `--source-language` flag), booleans as 0 or 1, and lists
72
76
  and tables as JSON. labnotes writes `run.json` (params, created, ended, exit
73
77
  code), `stdout.log` and `stderr.log` to the run folder.
78
+ To exclude a run, add `"excluded": true` and an optional `"reason"` to its
79
+ `run.json`. An excluded run doesn't count toward `repeats`, so the next `run`
80
+ replaces it; both show as columns in `runs`.
74
81
 
75
82
  In a terminal, `run` shows a live table of runs (status and time) and stays
76
83
  open when they finish; `q` quits. Elsewhere, or with `--no-tui`, it prints a
@@ -36,6 +36,10 @@ labnotes experiments foo runs # foo's runs, oldest first
36
36
 
37
37
  Each takes `--format table` (the default) or `--format json`; use `--format json` when you parse the output.
38
38
 
39
+ `labnotes experiments new [NAME]` creates an experiment folder with a starter
40
+ `experiment.toml`, picking a random name if you give none. `new` itself can't
41
+ be an experiment name.
42
+
39
43
  `labnotes experiments foo run` runs `foo` from its `experiment.toml`, in the
40
44
  experiments folder:
41
45
 
@@ -61,6 +65,9 @@ Each param is appended as `--name value`, with the name as written (use
61
65
  `source-language` for a `--source-language` flag), booleans as 0 or 1, and lists
62
66
  and tables as JSON. labnotes writes `run.json` (params, created, ended, exit
63
67
  code), `stdout.log` and `stderr.log` to the run folder.
68
+ To exclude a run, add `"excluded": true` and an optional `"reason"` to its
69
+ `run.json`. An excluded run doesn't count toward `repeats`, so the next `run`
70
+ replaces it; both show as columns in `runs`.
64
71
 
65
72
  A script run by labnotes should:
66
73
 
@@ -1,10 +1,10 @@
1
1
  from dirsql import DirSQL
2
2
 
3
- from labnotes.sql_quote import quote_identifier, quote_string
3
+ from labnotes.sql_quote import quote_identifier
4
4
 
5
5
 
6
6
  async def count_runs(
7
- runs: DirSQL, study: str, completed_run: str, group_by: list[str] | None = None
7
+ runs: DirSQL, source: str, completed_run: str, group_by: list[str] | None = None
8
8
  ) -> list[dict[str, object]]:
9
9
  keys = [quote_identifier(key) for key in group_by or []]
10
10
  columns = "".join(f"{key}, " for key in keys)
@@ -12,5 +12,5 @@ async def count_runs(
12
12
  return await runs.query(
13
13
  f"SELECT {columns}COUNT(*) AS runs,"
14
14
  f" COALESCE(SUM(CASE WHEN ({completed_run}) THEN 1 ELSE 0 END), 0) AS completed"
15
- f" FROM runs WHERE study = {quote_string(study)}{group}"
15
+ f" FROM {source}{group}"
16
16
  )
@@ -0,0 +1,32 @@
1
+ from pathlib import Path
2
+
3
+ from labnotes.models import LabnotesConfig
4
+ from labnotes.runner.new_run_id import new_run_id
5
+
6
+ STARTER = """\
7
+ # Runs from the project root. {run_dir} becomes the run's folder.
8
+ command = "uv run script.py --output {run_dir}"
9
+ repeats = 1
10
+
11
+ # Each combination runs `repeats` times, passed as --name value.
12
+ [params]
13
+ # model = ["opus", "sonnet"]
14
+ """
15
+
16
+
17
+ def create_experiment(config: LabnotesConfig, name: str | None) -> Path:
18
+ def folder(candidate: str) -> Path:
19
+ return config.root / config.experiments.replace("{experiment}", candidate)
20
+
21
+ if name is None:
22
+ name = new_run_id(lambda candidate: folder(candidate).exists())
23
+ if name == "new":
24
+ raise ValueError("new is reserved; pick another name.")
25
+ if "/" in name or name.startswith("."):
26
+ raise ValueError(f"{name} can't be an experiment name.")
27
+ path = folder(name)
28
+ if path.exists():
29
+ raise ValueError(f"{name} already exists at {path}.")
30
+ path.mkdir(parents=True)
31
+ (path / "experiment.toml").write_text(STARTER, encoding="utf-8")
32
+ return path
@@ -4,6 +4,7 @@ from pathlib import Path
4
4
 
5
5
  import click
6
6
 
7
+ from labnotes.create_experiment import create_experiment
7
8
  from labnotes.load_config import load_config
8
9
  from labnotes.print_rows import print_rows
9
10
  from labnotes.read_config import CONFIG_FILE
@@ -13,9 +14,12 @@ from labnotes.tables.experiment_rows import experiment_rows
13
14
  from labnotes.tui.run_tui import run_tui
14
15
 
15
16
 
16
- @click.command(help="List experiments, show one, list its runs, or run it.")
17
+ @click.command(
18
+ help="List experiments, show one, list its runs, or run it."
19
+ " `new [NAME]` creates one."
20
+ )
17
21
  @click.argument("name", required=False)
18
- @click.argument("action", required=False, type=click.Choice(["runs", "run"]))
22
+ @click.argument("action", required=False, metavar="[runs|run]")
19
23
  @click.option(
20
24
  "--format",
21
25
  "output_format",
@@ -57,6 +61,13 @@ def experiments(
57
61
  f"No {CONFIG_FILE} here. Run labnotes init, or pass -c PATH."
58
62
  )
59
63
  match name, action:
64
+ case "new", _:
65
+ try:
66
+ path = create_experiment(config, action)
67
+ except ValueError as error:
68
+ raise click.ClickException(str(error)) from error
69
+ click.echo(f"Created {path / 'experiment.toml'}")
70
+ return
60
71
  case str(), "run":
61
72
  live = sys.stdout.isatty() and sys.stderr.isatty() and not no_tui
62
73
  drive = run_tui if live else lambda start, title: run_headless(start)
@@ -69,6 +80,8 @@ def experiments(
69
80
  failed = sum(result["exit_code"] != 0 for result in results)
70
81
  click.echo(f"{len(results) - failed} passed, {failed} failed.")
71
82
  ctx.exit(1 if failed else 0)
83
+ if action not in (None, "runs"):
84
+ raise click.UsageError(f"Unknown action {action}. Use runs or run.")
72
85
  rows = asyncio.run(experiment_rows(config, name, action))
73
86
  if rows is None:
74
87
  raise click.ClickException(f"No experiment named {name}.")
@@ -5,9 +5,9 @@ from labnotes.sql_quote import quote_identifier, quote_string
5
5
 
6
6
 
7
7
  async def list_runs(
8
- runs: DirSQL, study: str, completed_run: str, view: RunView
8
+ runs: DirSQL, source: str, completed_run: str, view: RunView
9
9
  ) -> list[dict[str, object]]:
10
- where = [f"study = {quote_string(study)}"]
10
+ where = []
11
11
  if view.status:
12
12
  negate = {"complete": "", "incomplete": "NOT "}[view.status]
13
13
  where.append(f"{negate}({completed_run})")
@@ -16,6 +16,7 @@ async def list_runs(
16
16
  f" lower({quote_string(value)})) > 0"
17
17
  for column, value in view.filters.items()
18
18
  ]
19
+ filtered = f" WHERE {' AND '.join(where)}" if where else ""
19
20
  direction = "DESC" if view.descending else "ASC"
20
21
  if view.sort == "status":
21
22
  order = f"labnotes_completed {direction}"
@@ -24,6 +25,6 @@ async def list_runs(
24
25
  else:
25
26
  order = "2 DESC"
26
27
  return await runs.query(
27
- f"SELECT ({completed_run}) AS labnotes_completed, * FROM runs"
28
- f" WHERE {' AND '.join(where)} ORDER BY {order}"
28
+ f"SELECT ({completed_run}) AS labnotes_completed, * FROM {source}"
29
+ f"{filtered} ORDER BY {order}"
29
30
  )
@@ -12,6 +12,7 @@ from labnotes.run_columns import run_columns
12
12
  from labnotes.sort_href import sort_href
13
13
  from labnotes.sql_quote import quote_string
14
14
  from labnotes.study_progress import study_progress
15
+ from labnotes.study_runs import study_runs
15
16
  from labnotes.summarize_design import summarize_design
16
17
 
17
18
 
@@ -46,7 +47,9 @@ async def load_experiment(
46
47
  listed = (
47
48
  []
48
49
  if study is None
49
- else await list_runs(runs, study, str(summary["completed_run"]), view)
50
+ else await list_runs(
51
+ runs, study_runs(study), str(summary["completed_run"]), view
52
+ )
50
53
  )
51
54
  return {
52
55
  "name": row["name"],
@@ -23,7 +23,11 @@ async def run_experiment(
23
23
  listen: Listen,
24
24
  ) -> list[dict[str, object]]:
25
25
  runs = await list_experiment_runs(open_tables(config), name)
26
- finished = [json.loads(str(run["params"])) for run in runs if run["exit_code"] == 0]
26
+ finished = [
27
+ json.loads(str(run["params"]))
28
+ for run in runs
29
+ if run["exit_code"] == 0 and not run["excluded"]
30
+ ]
27
31
  plan = plan_runs(param_grid(experiment.params), experiment.repeats, finished)
28
32
  report({"event": "planned", "runs": len(plan), "concurrency": concurrency})
29
33
  running: set[asyncio.subprocess.Process] = set()
@@ -4,6 +4,7 @@ from dirsql import DirSQL
4
4
 
5
5
  from labnotes.cell_progress import cell_progress
6
6
  from labnotes.count_runs import count_runs
7
+ from labnotes.study_runs import study_runs
7
8
 
8
9
 
9
10
  async def study_progress(
@@ -22,11 +23,12 @@ async def study_progress(
22
23
  }
23
24
  if study is None:
24
25
  return progress
25
- [totals] = await count_runs(runs, study, completed_run)
26
+ source = study_runs(study)
27
+ [totals] = await count_runs(runs, source, completed_run)
26
28
  progress.update(totals)
27
29
  if missing:
28
30
  return progress
29
- grouped = await count_runs(runs, study, completed_run, group_by=list(varies))
31
+ grouped = await count_runs(runs, source, completed_run, group_by=list(varies))
30
32
  cells = cell_progress(varies, grouped)
31
33
  progress["cells"] = cells
32
34
  if isinstance(runs_per_cell, int):
@@ -0,0 +1,5 @@
1
+ from labnotes.sql_quote import quote_string
2
+
3
+
4
+ def study_runs(study: str) -> str:
5
+ return f"(SELECT * FROM runs WHERE study = {quote_string(study)})"
@@ -5,6 +5,7 @@ from labnotes.sql_quote import quote_string
5
5
 
6
6
  async def list_experiment_runs(tables: DirSQL, name: str) -> list[dict[str, object]]:
7
7
  return await tables.query(
8
- "SELECT id, created, ended, exit_code, error, params, folder FROM runs"
8
+ "SELECT id, created, ended, exit_code, error, excluded, reason, params, folder"
9
+ " FROM runs"
9
10
  f" WHERE experiment = {quote_string(name)} ORDER BY created, id"
10
11
  )
@@ -31,7 +31,8 @@ CREATE TABLE files (
31
31
  RUNS = """
32
32
  CREATE TABLE runs (
33
33
  experiment TEXT, id TEXT, folder TEXT, created TEXT, ended TEXT,
34
- exit_code INTEGER, error TEXT, stdout TEXT, stderr TEXT, params TEXT,
34
+ exit_code INTEGER, error TEXT, excluded INTEGER, reason TEXT,
35
+ stdout TEXT, stderr TEXT, params TEXT,
35
36
  PRIMARY KEY (experiment, id)
36
37
  )
37
38
  """
@@ -20,6 +20,8 @@ def read_run(root: Path, template: str, path: str) -> list[dict[str, object]]:
20
20
  "ended": run.get("ended"),
21
21
  "exit_code": run.get("exit_code"),
22
22
  "error": run.get("error"),
23
+ "excluded": run.get("excluded") is True,
24
+ "reason": run.get("reason"),
23
25
  "stdout": f"{folder}/stdout.log",
24
26
  "stderr": f"{folder}/stderr.log",
25
27
  "params": json.dumps(run.get("params", {})),
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes