Add fluksio status, and ask for a run's parameters at a terminal
Two halves of the same gap: the CLI could start work but not show you any. `fluksio status` draws the home screen's top half in a terminal — health and what is wrong with it, every flow with its state and node count, and the recent runs and failures under them. `--watch` keeps it there. Rich does the drawing; it was already installed under fastapi's own CLI, and is named now because a command depends on it. `fluksio run` with no parameters at a terminal asks for them, one line per declared input with its declared value in brackets — so Enter through the lot is what running the defaults looks like, and an artifact input takes the `@run:` spelling the engine now resolves. A scripted run is untouched: passing any parameter, or piping the command, skips the questions, as does --defaults. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019V5bsYGNxcgPs4xXmTPx69
This commit is contained in:
@@ -149,7 +149,9 @@ def made_artifact():
|
||||
|
||||
def test_a_run_reference_resolves_to_what_that_run_produced(made_artifact):
|
||||
run_id, reference = made_artifact
|
||||
resolved = resolve_references(artifact_flow(), {"dataset": f"@run:{run_id}.dataset"})
|
||||
resolved = resolve_references(
|
||||
artifact_flow(), {"dataset": f"@run:{run_id}.dataset"}
|
||||
)
|
||||
|
||||
# The producer's own reference, file name and all — not one rebuilt from
|
||||
# the row, which carries the message name instead.
|
||||
|
||||
@@ -314,9 +314,7 @@ def test_work_in_flight_is_touched_until_it_finishes(monkeypatch):
|
||||
consumer.assign_flow("f", "consumer")
|
||||
|
||||
queue = RecordingQueue()
|
||||
pipeline = Pipeline(
|
||||
nodes=[source, consumer], state=MemoryState(), work_queue=queue
|
||||
)
|
||||
pipeline = Pipeline(nodes=[source, consumer], state=MemoryState(), work_queue=queue)
|
||||
service = ExecutionService(queue)
|
||||
service.bind(pipeline)
|
||||
service.start()
|
||||
|
||||
@@ -443,4 +443,3 @@ def test_a_node_with_no_fingerprint_is_never_looked_up():
|
||||
assert calls == [1]
|
||||
assert cache.asked == []
|
||||
assert seen[0].cache_key == ""
|
||||
|
||||
|
||||
@@ -255,3 +255,53 @@ def test_ctrl_c_while_waiting_cancels_the_run(monkeypatch) -> None:
|
||||
args = _parser().parse_args(["run", "train", "--local", "--no-sync"])
|
||||
assert cli.cmd_run(args, []) == 130
|
||||
assert cancelled == ["run-1"]
|
||||
|
||||
|
||||
def test_an_artifact_input_may_be_named_rather_than_pasted() -> None:
|
||||
"""The engine resolves either spelling; the CLI just stops mangling them."""
|
||||
import json
|
||||
|
||||
from fluksio.sdk.cli import _coerce
|
||||
|
||||
assert _coerce("@run:123-abc.dataset", "artifact") == "@run:123-abc.dataset"
|
||||
digest = "sha256:" + "a1" * 32
|
||||
assert _coerce(digest, "artifact") == digest
|
||||
# A reference a script already holds still arrives as the object it is.
|
||||
reference = {"digest": digest, "size": 3}
|
||||
assert _coerce(json.dumps(reference), "artifact") == reference
|
||||
|
||||
|
||||
def test_the_run_prompt_keeps_declared_values_for_anything_left_blank() -> None:
|
||||
"""Enter through the lot is what running with the defaults looks like."""
|
||||
from unittest.mock import patch
|
||||
|
||||
from fluksio.sdk.cli import _ask_params
|
||||
|
||||
definition = {
|
||||
"inputs": [
|
||||
{"spec": {"name": "lr", "dtype": "float"}, "initial": 0.05},
|
||||
{"spec": {"name": "epochs", "dtype": "int"}, "initial": 10},
|
||||
]
|
||||
}
|
||||
|
||||
with patch("builtins.input", side_effect=["", ""]):
|
||||
# Nothing sent: an input the caller leaves out keeps what it declares,
|
||||
# which is the same contract the browser's dialog has.
|
||||
assert _ask_params(definition) == {}
|
||||
|
||||
with patch("builtins.input", side_effect=["0.01", "50"]):
|
||||
assert _ask_params(definition) == {"lr": 0.01, "epochs": 50}
|
||||
|
||||
|
||||
def test_the_run_prompt_names_an_answer_of_the_wrong_type() -> None:
|
||||
from unittest.mock import patch
|
||||
|
||||
import pytest
|
||||
|
||||
from fluksio.sdk import SyncError
|
||||
from fluksio.sdk.cli import _ask_params
|
||||
|
||||
definition = {"inputs": [{"spec": {"name": "lr", "dtype": "float"}}]}
|
||||
with patch("builtins.input", side_effect=["fast"]):
|
||||
with pytest.raises(SyncError, match="lr"):
|
||||
_ask_params(definition)
|
||||
|
||||
Reference in New Issue
Block a user