rhylthyme-cli-runner 0.2.5a0__tar.gz → 0.2.7a0__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.
- {rhylthyme_cli_runner-0.2.5a0/src/rhylthyme_cli_runner.egg-info → rhylthyme_cli_runner-0.2.7a0}/PKG-INFO +12 -3
- {rhylthyme_cli_runner-0.2.5a0 → rhylthyme_cli_runner-0.2.7a0}/README.md +11 -2
- {rhylthyme_cli_runner-0.2.5a0 → rhylthyme_cli_runner-0.2.7a0}/setup.py +1 -1
- {rhylthyme_cli_runner-0.2.5a0 → rhylthyme_cli_runner-0.2.7a0}/src/rhylthyme_cli_runner/__init__.py +1 -1
- {rhylthyme_cli_runner-0.2.5a0 → rhylthyme_cli_runner-0.2.7a0}/src/rhylthyme_cli_runner/import_cli.py +105 -3
- {rhylthyme_cli_runner-0.2.5a0 → rhylthyme_cli_runner-0.2.7a0}/src/rhylthyme_cli_runner/remote/checks.py +1 -1
- {rhylthyme_cli_runner-0.2.5a0 → rhylthyme_cli_runner-0.2.7a0/src/rhylthyme_cli_runner.egg-info}/PKG-INFO +12 -3
- {rhylthyme_cli_runner-0.2.5a0 → rhylthyme_cli_runner-0.2.7a0}/tests/test_import_cli.py +71 -0
- {rhylthyme_cli_runner-0.2.5a0 → rhylthyme_cli_runner-0.2.7a0}/LICENSE +0 -0
- {rhylthyme_cli_runner-0.2.5a0 → rhylthyme_cli_runner-0.2.7a0}/pyproject.toml +0 -0
- {rhylthyme_cli_runner-0.2.5a0 → rhylthyme_cli_runner-0.2.7a0}/setup.cfg +0 -0
- {rhylthyme_cli_runner-0.2.5a0 → rhylthyme_cli_runner-0.2.7a0}/src/rhylthyme_cli_runner/cli.py +0 -0
- {rhylthyme_cli_runner-0.2.5a0 → rhylthyme_cli_runner-0.2.7a0}/src/rhylthyme_cli_runner/environment_icons.py +0 -0
- {rhylthyme_cli_runner-0.2.5a0 → rhylthyme_cli_runner-0.2.7a0}/src/rhylthyme_cli_runner/environment_loader.py +0 -0
- {rhylthyme_cli_runner-0.2.5a0 → rhylthyme_cli_runner-0.2.7a0}/src/rhylthyme_cli_runner/environment_schemas.py +0 -0
- {rhylthyme_cli_runner-0.2.5a0 → rhylthyme_cli_runner-0.2.7a0}/src/rhylthyme_cli_runner/eval/__init__.py +0 -0
- {rhylthyme_cli_runner-0.2.5a0 → rhylthyme_cli_runner-0.2.7a0}/src/rhylthyme_cli_runner/eval/cli.py +0 -0
- {rhylthyme_cli_runner-0.2.5a0 → rhylthyme_cli_runner-0.2.7a0}/src/rhylthyme_cli_runner/eval/compare.py +0 -0
- {rhylthyme_cli_runner-0.2.5a0 → rhylthyme_cli_runner-0.2.7a0}/src/rhylthyme_cli_runner/eval/gold.py +0 -0
- {rhylthyme_cli_runner-0.2.5a0 → rhylthyme_cli_runner-0.2.7a0}/src/rhylthyme_cli_runner/eval/harness.py +0 -0
- {rhylthyme_cli_runner-0.2.5a0 → rhylthyme_cli_runner-0.2.7a0}/src/rhylthyme_cli_runner/eval/llm.py +0 -0
- {rhylthyme_cli_runner-0.2.5a0 → rhylthyme_cli_runner-0.2.7a0}/src/rhylthyme_cli_runner/eval/matcher.py +0 -0
- {rhylthyme_cli_runner-0.2.5a0 → rhylthyme_cli_runner-0.2.7a0}/src/rhylthyme_cli_runner/eval/metrics.py +0 -0
- {rhylthyme_cli_runner-0.2.5a0 → rhylthyme_cli_runner-0.2.7a0}/src/rhylthyme_cli_runner/eval/patterns/__init__.py +0 -0
- {rhylthyme_cli_runner-0.2.5a0 → rhylthyme_cli_runner-0.2.7a0}/src/rhylthyme_cli_runner/eval/patterns/authoring_guide.md +0 -0
- {rhylthyme_cli_runner-0.2.5a0 → rhylthyme_cli_runner-0.2.7a0}/src/rhylthyme_cli_runner/eval/patterns/baseline.md +0 -0
- {rhylthyme_cli_runner-0.2.5a0 → rhylthyme_cli_runner-0.2.7a0}/src/rhylthyme_cli_runner/eval/patterns/baseline.py +0 -0
- {rhylthyme_cli_runner-0.2.5a0 → rhylthyme_cli_runner-0.2.7a0}/src/rhylthyme_cli_runner/eval/patterns/extract.py +0 -0
- {rhylthyme_cli_runner-0.2.5a0 → rhylthyme_cli_runner-0.2.7a0}/src/rhylthyme_cli_runner/eval/patterns/four_turn.py +0 -0
- {rhylthyme_cli_runner-0.2.5a0 → rhylthyme_cli_runner-0.2.7a0}/src/rhylthyme_cli_runner/eval/report.py +0 -0
- {rhylthyme_cli_runner-0.2.5a0 → rhylthyme_cli_runner-0.2.7a0}/src/rhylthyme_cli_runner/expand_replicates.py +0 -0
- {rhylthyme_cli_runner-0.2.5a0 → rhylthyme_cli_runner-0.2.7a0}/src/rhylthyme_cli_runner/history/__init__.py +0 -0
- {rhylthyme_cli_runner-0.2.5a0 → rhylthyme_cli_runner-0.2.7a0}/src/rhylthyme_cli_runner/history/calibrate.py +0 -0
- {rhylthyme_cli_runner-0.2.5a0 → rhylthyme_cli_runner-0.2.7a0}/src/rhylthyme_cli_runner/history/calibrate_cli.py +0 -0
- {rhylthyme_cli_runner-0.2.5a0 → rhylthyme_cli_runner-0.2.7a0}/src/rhylthyme_cli_runner/history/cli.py +0 -0
- {rhylthyme_cli_runner-0.2.5a0 → rhylthyme_cli_runner-0.2.7a0}/src/rhylthyme_cli_runner/history/evaluate.py +0 -0
- {rhylthyme_cli_runner-0.2.5a0 → rhylthyme_cli_runner-0.2.7a0}/src/rhylthyme_cli_runner/history/evaluate_cli.py +0 -0
- {rhylthyme_cli_runner-0.2.5a0 → rhylthyme_cli_runner-0.2.7a0}/src/rhylthyme_cli_runner/history/factors.py +0 -0
- {rhylthyme_cli_runner-0.2.5a0 → rhylthyme_cli_runner-0.2.7a0}/src/rhylthyme_cli_runner/history/hash.py +0 -0
- {rhylthyme_cli_runner-0.2.5a0 → rhylthyme_cli_runner-0.2.7a0}/src/rhylthyme_cli_runner/history/predict.py +0 -0
- {rhylthyme_cli_runner-0.2.5a0 → rhylthyme_cli_runner-0.2.7a0}/src/rhylthyme_cli_runner/history/recorder.py +0 -0
- {rhylthyme_cli_runner-0.2.5a0 → rhylthyme_cli_runner-0.2.7a0}/src/rhylthyme_cli_runner/history/render.py +0 -0
- {rhylthyme_cli_runner-0.2.5a0 → rhylthyme_cli_runner-0.2.7a0}/src/rhylthyme_cli_runner/history/replay.py +0 -0
- {rhylthyme_cli_runner-0.2.5a0 → rhylthyme_cli_runner-0.2.7a0}/src/rhylthyme_cli_runner/history/report.py +0 -0
- {rhylthyme_cli_runner-0.2.5a0 → rhylthyme_cli_runner-0.2.7a0}/src/rhylthyme_cli_runner/history/report_cli.py +0 -0
- {rhylthyme_cli_runner-0.2.5a0 → rhylthyme_cli_runner-0.2.7a0}/src/rhylthyme_cli_runner/history/store.py +0 -0
- {rhylthyme_cli_runner-0.2.5a0 → rhylthyme_cli_runner-0.2.7a0}/src/rhylthyme_cli_runner/history/synth.py +0 -0
- {rhylthyme_cli_runner-0.2.5a0 → rhylthyme_cli_runner-0.2.7a0}/src/rhylthyme_cli_runner/history/usable.py +0 -0
- {rhylthyme_cli_runner-0.2.5a0 → rhylthyme_cli_runner-0.2.7a0}/src/rhylthyme_cli_runner/instance_checks.py +0 -0
- {rhylthyme_cli_runner-0.2.5a0 → rhylthyme_cli_runner-0.2.7a0}/src/rhylthyme_cli_runner/program_planner.py +0 -0
- {rhylthyme_cli_runner-0.2.5a0 → rhylthyme_cli_runner-0.2.7a0}/src/rhylthyme_cli_runner/program_runner.py +0 -0
- {rhylthyme_cli_runner-0.2.5a0 → rhylthyme_cli_runner-0.2.7a0}/src/rhylthyme_cli_runner/remote/__init__.py +0 -0
- {rhylthyme_cli_runner-0.2.5a0 → rhylthyme_cli_runner-0.2.7a0}/src/rhylthyme_cli_runner/remote/auth.py +0 -0
- {rhylthyme_cli_runner-0.2.5a0 → rhylthyme_cli_runner-0.2.7a0}/src/rhylthyme_cli_runner/remote/cli.py +0 -0
- {rhylthyme_cli_runner-0.2.5a0 → rhylthyme_cli_runner-0.2.7a0}/src/rhylthyme_cli_runner/remote/mcp_client.py +0 -0
- {rhylthyme_cli_runner-0.2.5a0 → rhylthyme_cli_runner-0.2.7a0}/src/rhylthyme_cli_runner/render_cli.py +0 -0
- {rhylthyme_cli_runner-0.2.5a0 → rhylthyme_cli_runner-0.2.7a0}/src/rhylthyme_cli_runner/validate_program.py +0 -0
- {rhylthyme_cli_runner-0.2.5a0 → rhylthyme_cli_runner-0.2.7a0}/src/rhylthyme_cli_runner.egg-info/SOURCES.txt +0 -0
- {rhylthyme_cli_runner-0.2.5a0 → rhylthyme_cli_runner-0.2.7a0}/src/rhylthyme_cli_runner.egg-info/dependency_links.txt +0 -0
- {rhylthyme_cli_runner-0.2.5a0 → rhylthyme_cli_runner-0.2.7a0}/src/rhylthyme_cli_runner.egg-info/entry_points.txt +0 -0
- {rhylthyme_cli_runner-0.2.5a0 → rhylthyme_cli_runner-0.2.7a0}/src/rhylthyme_cli_runner.egg-info/requires.txt +0 -0
- {rhylthyme_cli_runner-0.2.5a0 → rhylthyme_cli_runner-0.2.7a0}/src/rhylthyme_cli_runner.egg-info/top_level.txt +0 -0
- {rhylthyme_cli_runner-0.2.5a0 → rhylthyme_cli_runner-0.2.7a0}/tests/test_calibrate.py +0 -0
- {rhylthyme_cli_runner-0.2.5a0 → rhylthyme_cli_runner-0.2.7a0}/tests/test_cli.py +0 -0
- {rhylthyme_cli_runner-0.2.5a0 → rhylthyme_cli_runner-0.2.7a0}/tests/test_eval_baseline.py +0 -0
- {rhylthyme_cli_runner-0.2.5a0 → rhylthyme_cli_runner-0.2.7a0}/tests/test_eval_harness.py +0 -0
- {rhylthyme_cli_runner-0.2.5a0 → rhylthyme_cli_runner-0.2.7a0}/tests/test_eval_providers.py +0 -0
- {rhylthyme_cli_runner-0.2.5a0 → rhylthyme_cli_runner-0.2.7a0}/tests/test_eval_scorer.py +0 -0
- {rhylthyme_cli_runner-0.2.5a0 → rhylthyme_cli_runner-0.2.7a0}/tests/test_evaluate.py +0 -0
- {rhylthyme_cli_runner-0.2.5a0 → rhylthyme_cli_runner-0.2.7a0}/tests/test_example_validation.py +0 -0
- {rhylthyme_cli_runner-0.2.5a0 → rhylthyme_cli_runner-0.2.7a0}/tests/test_examples_integration.py +0 -0
- {rhylthyme_cli_runner-0.2.5a0 → rhylthyme_cli_runner-0.2.7a0}/tests/test_expand_replicates.py +0 -0
- {rhylthyme_cli_runner-0.2.5a0 → rhylthyme_cli_runner-0.2.7a0}/tests/test_hash_parity.py +0 -0
- {rhylthyme_cli_runner-0.2.5a0 → rhylthyme_cli_runner-0.2.7a0}/tests/test_history_report.py +0 -0
- {rhylthyme_cli_runner-0.2.5a0 → rhylthyme_cli_runner-0.2.7a0}/tests/test_mcp_checks.py +0 -0
- {rhylthyme_cli_runner-0.2.5a0 → rhylthyme_cli_runner-0.2.7a0}/tests/test_planner_time_strings.py +0 -0
- {rhylthyme_cli_runner-0.2.5a0 → rhylthyme_cli_runner-0.2.7a0}/tests/test_predict.py +0 -0
- {rhylthyme_cli_runner-0.2.5a0 → rhylthyme_cli_runner-0.2.7a0}/tests/test_program_runner.py +0 -0
- {rhylthyme_cli_runner-0.2.5a0 → rhylthyme_cli_runner-0.2.7a0}/tests/test_remote.py +0 -0
- {rhylthyme_cli_runner-0.2.5a0 → rhylthyme_cli_runner-0.2.7a0}/tests/test_render_cli.py +0 -0
- {rhylthyme_cli_runner-0.2.5a0 → rhylthyme_cli_runner-0.2.7a0}/tests/test_replay.py +0 -0
- {rhylthyme_cli_runner-0.2.5a0 → rhylthyme_cli_runner-0.2.7a0}/tests/test_run_recorder.py +0 -0
- {rhylthyme_cli_runner-0.2.5a0 → rhylthyme_cli_runner-0.2.7a0}/tests/test_skill.py +0 -0
- {rhylthyme_cli_runner-0.2.5a0 → rhylthyme_cli_runner-0.2.7a0}/tests/test_synth.py +0 -0
- {rhylthyme_cli_runner-0.2.5a0 → rhylthyme_cli_runner-0.2.7a0}/tests/test_usable.py +0 -0
- {rhylthyme_cli_runner-0.2.5a0 → rhylthyme_cli_runner-0.2.7a0}/tests/test_validate_examples_ci.py +0 -0
- {rhylthyme_cli_runner-0.2.5a0 → rhylthyme_cli_runner-0.2.7a0}/tests/test_validate_program.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: rhylthyme-cli-runner
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.7a0
|
|
4
4
|
Summary: CLI runner for Rhylthyme real-time program schedules
|
|
5
5
|
Home-page: https://github.com/rhylthyme/rhylthyme-cli-runner
|
|
6
6
|
Author: Rhylthyme Team
|
|
@@ -561,8 +561,17 @@ rhylthyme search "pad thai" -i spoonacular # then impo
|
|
|
561
561
|
```
|
|
562
562
|
|
|
563
563
|
**Options:** `-i IMPORTER` (default: chosen from the URL), `-o PATH`,
|
|
564
|
-
`--stdout`, `--publish` (and `--open`), `--no-validate`. `-` reads
|
|
565
|
-
source from stdin (with `-i`).
|
|
564
|
+
`--stdout`, `--publish` (and `--open`), `--no-validate`, `--review`. `-` reads
|
|
565
|
+
pasted source from stdin (with `-i`).
|
|
566
|
+
|
|
567
|
+
`--review` sends the import and its source to a model on the server (GPT-5.6
|
|
568
|
+
Luna, the model that related steps best in the paper's evaluation) and prints
|
|
569
|
+
what looks wrong: a default duration on "simmer until thick", a step the
|
|
570
|
+
parser dropped, steps chained that the source runs in parallel, a total that
|
|
571
|
+
disagrees with the source. It needs `rhylthyme login`, spends one model call
|
|
572
|
+
(capped per day), and changes nothing: the findings are yours to apply. On
|
|
573
|
+
rhylthyme.com the same review runs automatically after every import when you
|
|
574
|
+
are signed in.
|
|
566
575
|
|
|
567
576
|
### `rhylthyme render`
|
|
568
577
|
|
|
@@ -501,8 +501,17 @@ rhylthyme search "pad thai" -i spoonacular # then impo
|
|
|
501
501
|
```
|
|
502
502
|
|
|
503
503
|
**Options:** `-i IMPORTER` (default: chosen from the URL), `-o PATH`,
|
|
504
|
-
`--stdout`, `--publish` (and `--open`), `--no-validate`. `-` reads
|
|
505
|
-
source from stdin (with `-i`).
|
|
504
|
+
`--stdout`, `--publish` (and `--open`), `--no-validate`, `--review`. `-` reads
|
|
505
|
+
pasted source from stdin (with `-i`).
|
|
506
|
+
|
|
507
|
+
`--review` sends the import and its source to a model on the server (GPT-5.6
|
|
508
|
+
Luna, the model that related steps best in the paper's evaluation) and prints
|
|
509
|
+
what looks wrong: a default duration on "simmer until thick", a step the
|
|
510
|
+
parser dropped, steps chained that the source runs in parallel, a total that
|
|
511
|
+
disagrees with the source. It needs `rhylthyme login`, spends one model call
|
|
512
|
+
(capped per day), and changes nothing: the findings are yours to apply. On
|
|
513
|
+
rhylthyme.com the same review runs automatically after every import when you
|
|
514
|
+
are signed in.
|
|
506
515
|
|
|
507
516
|
### `rhylthyme render`
|
|
508
517
|
|
|
@@ -19,7 +19,7 @@ def read_readme():
|
|
|
19
19
|
|
|
20
20
|
setup(
|
|
21
21
|
name="rhylthyme-cli-runner",
|
|
22
|
-
version="0.2.
|
|
22
|
+
version="0.2.7a0",
|
|
23
23
|
description="CLI runner for Rhylthyme real-time program schedules",
|
|
24
24
|
long_description=read_readme(),
|
|
25
25
|
long_description_content_type="text/markdown",
|
{rhylthyme_cli_runner-0.2.5a0 → rhylthyme_cli_runner-0.2.7a0}/src/rhylthyme_cli_runner/__init__.py
RENAMED
|
@@ -6,7 +6,7 @@ This package provides the command-line interface for running and validating
|
|
|
6
6
|
Rhylthyme real-time program schedules.
|
|
7
7
|
"""
|
|
8
8
|
|
|
9
|
-
__version__ = "0.2.
|
|
9
|
+
__version__ = "0.2.7a0"
|
|
10
10
|
__author__ = "Rhylthyme Team"
|
|
11
11
|
__description__ = "CLI runner for Rhylthyme real-time program schedules"
|
|
12
12
|
|
{rhylthyme_cli_runner-0.2.5a0 → rhylthyme_cli_runner-0.2.7a0}/src/rhylthyme_cli_runner/import_cli.py
RENAMED
|
@@ -74,6 +74,10 @@ def _import_one(importer, source: str, text: Optional[str]):
|
|
|
74
74
|
if hasattr(importer, "allow_local_files"):
|
|
75
75
|
importer.allow_local_files = True
|
|
76
76
|
return importer.import_from_url(str(path))
|
|
77
|
+
if not source.startswith(("http://", "https://")) and (
|
|
78
|
+
path.suffix or "/" in source
|
|
79
|
+
):
|
|
80
|
+
raise click.ClickException(f"No such file: {source}")
|
|
77
81
|
return importer.import_from_url(source)
|
|
78
82
|
|
|
79
83
|
|
|
@@ -130,8 +134,21 @@ def _validation(program: dict):
|
|
|
130
134
|
@click.option(
|
|
131
135
|
"--no-validate", is_flag=True, help="Skip validation of the imported program."
|
|
132
136
|
)
|
|
137
|
+
@click.option(
|
|
138
|
+
"--review",
|
|
139
|
+
"do_review",
|
|
140
|
+
is_flag=True,
|
|
141
|
+
help="Have a model read the import against its source and report what looks wrong (needs `rhylthyme login`).",
|
|
142
|
+
)
|
|
133
143
|
def import_command(
|
|
134
|
-
source,
|
|
144
|
+
source,
|
|
145
|
+
importer_name,
|
|
146
|
+
output,
|
|
147
|
+
to_stdout,
|
|
148
|
+
do_publish,
|
|
149
|
+
open_url,
|
|
150
|
+
no_validate,
|
|
151
|
+
do_review,
|
|
135
152
|
):
|
|
136
153
|
"""Import a recipe, protocol or deck as a program.
|
|
137
154
|
|
|
@@ -141,6 +158,7 @@ def import_command(
|
|
|
141
158
|
.cook or .pptx file. `-` reads the source text from stdin (with -i).
|
|
142
159
|
rhylthyme import https://www.seriouseats.com/the-best-chili-recipe
|
|
143
160
|
rhylthyme import 52772 -i themealdb --publish
|
|
161
|
+
rhylthyme import recipe.cook -i cooklang --review # a second opinion on durations, order, gaps
|
|
144
162
|
rhylthyme import https://raw.githubusercontent.com/Opentrons/Protocols/develop/protocols/007992/rna_isolation.ot2.apiv2.py
|
|
145
163
|
rhylthyme importers # what is installed
|
|
146
164
|
"""
|
|
@@ -153,8 +171,22 @@ def import_command(
|
|
|
153
171
|
)
|
|
154
172
|
text = sys.stdin.read()
|
|
155
173
|
importer = _pick(registry, source, importer_name)
|
|
174
|
+
token = None
|
|
175
|
+
if do_review:
|
|
176
|
+
# Before any work: the review needs an account, say so now.
|
|
177
|
+
from .remote import auth
|
|
178
|
+
|
|
179
|
+
try:
|
|
180
|
+
token = auth.access_token()
|
|
181
|
+
except auth.AuthError as e:
|
|
182
|
+
raise click.ClickException(
|
|
183
|
+
f"{e} (--review sends the import to a model on the server, which needs your account.)"
|
|
184
|
+
)
|
|
156
185
|
click.echo(f"Importing with {importer.name}…", err=True)
|
|
157
186
|
result = _import_one(importer, source, text)
|
|
187
|
+
source_text = (
|
|
188
|
+
text if text is not None else _source_text_for_review(importer, source)
|
|
189
|
+
)
|
|
158
190
|
if not result.success:
|
|
159
191
|
raise click.ClickException(result.error or "import failed")
|
|
160
192
|
program = result.program
|
|
@@ -163,9 +195,10 @@ def import_command(
|
|
|
163
195
|
v = _validation(program)
|
|
164
196
|
errors = v.get("errors") or v.get("logic_errors") or []
|
|
165
197
|
if not v.get("is_valid", v.get("valid", True)):
|
|
166
|
-
for
|
|
198
|
+
for finding in errors[:8]:
|
|
167
199
|
click.echo(
|
|
168
|
-
f" ✗ {
|
|
200
|
+
f" ✗ {finding if isinstance(finding, str) else finding.get('message', finding)}",
|
|
201
|
+
err=True,
|
|
169
202
|
)
|
|
170
203
|
raise click.ClickException(
|
|
171
204
|
"The imported program does not validate; not published. Re-run with --no-validate to keep it anyway."
|
|
@@ -187,6 +220,14 @@ def import_command(
|
|
|
187
220
|
target.write_text(payload + "\n")
|
|
188
221
|
click.echo(f"Saved {target}", err=True)
|
|
189
222
|
|
|
223
|
+
if do_review:
|
|
224
|
+
_review(
|
|
225
|
+
program,
|
|
226
|
+
token,
|
|
227
|
+
source_text=source_text,
|
|
228
|
+
source_url=source if source.startswith(("http://", "https://")) else None,
|
|
229
|
+
)
|
|
230
|
+
|
|
190
231
|
if do_publish:
|
|
191
232
|
from .remote.cli import publish as publish_command
|
|
192
233
|
|
|
@@ -208,6 +249,67 @@ def import_command(
|
|
|
208
249
|
Path(tmp_path).unlink(missing_ok=True)
|
|
209
250
|
|
|
210
251
|
|
|
252
|
+
def _source_text_for_review(importer, source: str) -> Optional[str]:
|
|
253
|
+
"""The text the import came from, when the CLI has it: a local file, or
|
|
254
|
+
a URL the CLI fetched for a text importer. Recipe-site imports do not
|
|
255
|
+
keep the page, so the reviewer gets the URL instead."""
|
|
256
|
+
path = Path(source)
|
|
257
|
+
if path.is_file() and path.suffix.lower() in (".cook", ".py", ".txt", ".md"):
|
|
258
|
+
try:
|
|
259
|
+
return path.read_text()
|
|
260
|
+
except OSError:
|
|
261
|
+
return None
|
|
262
|
+
if (
|
|
263
|
+
source.startswith(("http://", "https://"))
|
|
264
|
+
and hasattr(importer, "import_from_source")
|
|
265
|
+
and not getattr(importer, "supported_domains", None)
|
|
266
|
+
):
|
|
267
|
+
try:
|
|
268
|
+
from .remote.cli import _fetch_bytes
|
|
269
|
+
|
|
270
|
+
return _fetch_bytes(source).decode("utf-8")
|
|
271
|
+
except Exception: # noqa: BLE001
|
|
272
|
+
return None
|
|
273
|
+
return None
|
|
274
|
+
|
|
275
|
+
|
|
276
|
+
def _review(
|
|
277
|
+
program: dict, token: str, *, source_text: Optional[str], source_url: Optional[str]
|
|
278
|
+
) -> None:
|
|
279
|
+
from .remote import mcp_client as MC
|
|
280
|
+
|
|
281
|
+
click.echo("Reviewing…", err=True)
|
|
282
|
+
client = MC.McpClient(url=MC.endpoint_for(None))
|
|
283
|
+
args = {"program": program, "token": token}
|
|
284
|
+
if source_text:
|
|
285
|
+
args["source_text"] = source_text
|
|
286
|
+
elif source_url:
|
|
287
|
+
args["source_url"] = source_url
|
|
288
|
+
try:
|
|
289
|
+
result = client.call_tool("review_program", args, timeout=150)
|
|
290
|
+
except MC.ToolError as e:
|
|
291
|
+
raise click.ClickException(f"Review failed: {e.text}")
|
|
292
|
+
except MC.McpError as e:
|
|
293
|
+
raise click.ClickException(f"Review failed: {e}")
|
|
294
|
+
review = result.structured or {}
|
|
295
|
+
findings = review.get("findings") or []
|
|
296
|
+
mark = {"error": "✗", "warning": "!", "note": "·"}
|
|
297
|
+
click.echo("")
|
|
298
|
+
click.echo(
|
|
299
|
+
review.get("summary")
|
|
300
|
+
or ("Review: no problems found." if not findings else "Review:")
|
|
301
|
+
)
|
|
302
|
+
for f in findings:
|
|
303
|
+
where = f" [{f['stepId']}]" if f.get("stepId") else ""
|
|
304
|
+
click.echo(f" {mark.get(f.get('severity'), '·')}{where} {f.get('message')}")
|
|
305
|
+
if f.get("suggestion"):
|
|
306
|
+
click.echo(f" → {f['suggestion']}")
|
|
307
|
+
if findings:
|
|
308
|
+
click.echo(
|
|
309
|
+
"\nThe file is unchanged; edit it and `rhylthyme validate` again.", err=True
|
|
310
|
+
)
|
|
311
|
+
|
|
312
|
+
|
|
211
313
|
@click.command("search")
|
|
212
314
|
@click.argument("query")
|
|
213
315
|
@click.option(
|
|
@@ -252,7 +252,7 @@ def check_initialize(client, endpoint, opts, state) -> str:
|
|
|
252
252
|
return f"{info['name']} {info.get('version', '?')}, protocol {result['protocolVersion']}"
|
|
253
253
|
|
|
254
254
|
|
|
255
|
-
TOOL_DEFINITION_BUDGET_CHARS =
|
|
255
|
+
TOOL_DEFINITION_BUDGET_CHARS = 17000
|
|
256
256
|
|
|
257
257
|
|
|
258
258
|
def check_tools(client, endpoint, opts, state) -> str:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: rhylthyme-cli-runner
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.7a0
|
|
4
4
|
Summary: CLI runner for Rhylthyme real-time program schedules
|
|
5
5
|
Home-page: https://github.com/rhylthyme/rhylthyme-cli-runner
|
|
6
6
|
Author: Rhylthyme Team
|
|
@@ -561,8 +561,17 @@ rhylthyme search "pad thai" -i spoonacular # then impo
|
|
|
561
561
|
```
|
|
562
562
|
|
|
563
563
|
**Options:** `-i IMPORTER` (default: chosen from the URL), `-o PATH`,
|
|
564
|
-
`--stdout`, `--publish` (and `--open`), `--no-validate`. `-` reads
|
|
565
|
-
source from stdin (with `-i`).
|
|
564
|
+
`--stdout`, `--publish` (and `--open`), `--no-validate`, `--review`. `-` reads
|
|
565
|
+
pasted source from stdin (with `-i`).
|
|
566
|
+
|
|
567
|
+
`--review` sends the import and its source to a model on the server (GPT-5.6
|
|
568
|
+
Luna, the model that related steps best in the paper's evaluation) and prints
|
|
569
|
+
what looks wrong: a default duration on "simmer until thick", a step the
|
|
570
|
+
parser dropped, steps chained that the source runs in parallel, a total that
|
|
571
|
+
disagrees with the source. It needs `rhylthyme login`, spends one model call
|
|
572
|
+
(capped per day), and changes nothing: the findings are yours to apply. On
|
|
573
|
+
rhylthyme.com the same review runs automatically after every import when you
|
|
574
|
+
are signed in.
|
|
566
575
|
|
|
567
576
|
### `rhylthyme render`
|
|
568
577
|
|
|
@@ -240,3 +240,74 @@ def test_a_text_importer_gets_a_urls_body(fake, monkeypatch):
|
|
|
240
240
|
)
|
|
241
241
|
assert r.exit_code == 0, r.output
|
|
242
242
|
assert fake.calls[-1] == ("text", "def run(protocol): pass\n", None)
|
|
243
|
+
|
|
244
|
+
|
|
245
|
+
def test_a_missing_local_file_says_so(fake, tmp_path):
|
|
246
|
+
r = CliRunner().invoke(
|
|
247
|
+
cli, ["import", str(tmp_path / "Neapolitan Pizza.cook"), "-i", "fake"]
|
|
248
|
+
)
|
|
249
|
+
assert r.exit_code != 0 and "No such file" in r.output
|
|
250
|
+
assert fake.calls == []
|
|
251
|
+
|
|
252
|
+
|
|
253
|
+
def test_review_needs_a_login_before_any_work(fake, monkeypatch):
|
|
254
|
+
monkeypatch.delenv("RHYLTHYME_TOKEN", raising=False)
|
|
255
|
+
from rhylthyme_cli_runner.remote import auth
|
|
256
|
+
|
|
257
|
+
monkeypatch.setattr(auth, "load_credentials", lambda path=None: None)
|
|
258
|
+
r = CliRunner().invoke(
|
|
259
|
+
cli, ["import", "https://fake.example/dinner", "--review", "--stdout"]
|
|
260
|
+
)
|
|
261
|
+
assert r.exit_code != 0 and "rhylthyme login" in r.output
|
|
262
|
+
assert fake.calls == [], "nothing was imported"
|
|
263
|
+
|
|
264
|
+
|
|
265
|
+
def test_review_sends_the_program_and_source_and_prints_findings(
|
|
266
|
+
fake, monkeypatch, tmp_path
|
|
267
|
+
):
|
|
268
|
+
monkeypatch.setenv("RHYLTHYME_TOKEN", "tok-1")
|
|
269
|
+
from rhylthyme_cli_runner.remote import mcp_client as MC
|
|
270
|
+
|
|
271
|
+
seen = {}
|
|
272
|
+
|
|
273
|
+
class FakeMcp:
|
|
274
|
+
def __init__(self, url=None, **kw):
|
|
275
|
+
seen["url"] = url
|
|
276
|
+
|
|
277
|
+
def call_tool(self, name, args, timeout=None, **kw):
|
|
278
|
+
seen["tool"], seen["args"] = name, args
|
|
279
|
+
return MC.ToolResult(
|
|
280
|
+
content=[{"type": "text", "text": "x"}],
|
|
281
|
+
structured={
|
|
282
|
+
"summary": "One problem.",
|
|
283
|
+
"usable": False,
|
|
284
|
+
"findings": [
|
|
285
|
+
{
|
|
286
|
+
"severity": "error",
|
|
287
|
+
"stepId": "s1",
|
|
288
|
+
"message": "Too short.",
|
|
289
|
+
"suggestion": "90m",
|
|
290
|
+
},
|
|
291
|
+
{"severity": "note", "message": "Fine."},
|
|
292
|
+
],
|
|
293
|
+
},
|
|
294
|
+
)
|
|
295
|
+
|
|
296
|
+
monkeypatch.setattr(MC, "McpClient", FakeMcp)
|
|
297
|
+
src = tmp_path / "dinner.cook"
|
|
298
|
+
src.write_text("Simmer @sauce for ~{90%minutes}.\n")
|
|
299
|
+
fake.supported_domains = []
|
|
300
|
+
r = CliRunner().invoke(
|
|
301
|
+
cli,
|
|
302
|
+
["import", str(src), "-i", "fake", "--review", "-o", str(tmp_path / "d.json")],
|
|
303
|
+
)
|
|
304
|
+
assert r.exit_code == 0, r.output
|
|
305
|
+
assert seen["tool"] == "review_program"
|
|
306
|
+
assert seen["args"]["token"] == "tok-1"
|
|
307
|
+
assert seen["args"]["program"]["programId"] == "fake-dinner"
|
|
308
|
+
assert seen["args"]["source_text"].startswith("Simmer @sauce")
|
|
309
|
+
assert (
|
|
310
|
+
"One problem." in r.output
|
|
311
|
+
and "✗ [s1] Too short." in r.output
|
|
312
|
+
and "→ 90m" in r.output
|
|
313
|
+
)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{rhylthyme_cli_runner-0.2.5a0 → rhylthyme_cli_runner-0.2.7a0}/src/rhylthyme_cli_runner/cli.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{rhylthyme_cli_runner-0.2.5a0 → rhylthyme_cli_runner-0.2.7a0}/src/rhylthyme_cli_runner/eval/cli.py
RENAMED
|
File without changes
|
|
File without changes
|
{rhylthyme_cli_runner-0.2.5a0 → rhylthyme_cli_runner-0.2.7a0}/src/rhylthyme_cli_runner/eval/gold.py
RENAMED
|
File without changes
|
|
File without changes
|
{rhylthyme_cli_runner-0.2.5a0 → rhylthyme_cli_runner-0.2.7a0}/src/rhylthyme_cli_runner/eval/llm.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{rhylthyme_cli_runner-0.2.5a0 → rhylthyme_cli_runner-0.2.7a0}/src/rhylthyme_cli_runner/remote/cli.py
RENAMED
|
File without changes
|
|
File without changes
|
{rhylthyme_cli_runner-0.2.5a0 → rhylthyme_cli_runner-0.2.7a0}/src/rhylthyme_cli_runner/render_cli.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{rhylthyme_cli_runner-0.2.5a0 → rhylthyme_cli_runner-0.2.7a0}/tests/test_example_validation.py
RENAMED
|
File without changes
|
{rhylthyme_cli_runner-0.2.5a0 → rhylthyme_cli_runner-0.2.7a0}/tests/test_examples_integration.py
RENAMED
|
File without changes
|
{rhylthyme_cli_runner-0.2.5a0 → rhylthyme_cli_runner-0.2.7a0}/tests/test_expand_replicates.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{rhylthyme_cli_runner-0.2.5a0 → rhylthyme_cli_runner-0.2.7a0}/tests/test_planner_time_strings.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{rhylthyme_cli_runner-0.2.5a0 → rhylthyme_cli_runner-0.2.7a0}/tests/test_validate_examples_ci.py
RENAMED
|
File without changes
|
{rhylthyme_cli_runner-0.2.5a0 → rhylthyme_cli_runner-0.2.7a0}/tests/test_validate_program.py
RENAMED
|
File without changes
|