Download tests/test_stepwise.py from hkblockchaintechnology/tiny-computer-agent: direct link, hf CLI and curl.
- Browser
- Download file 6.21 kB
-
https://huggingface.co/hkblockchaintechnology/tiny-computer-agent/resolve/main/tests/test_stepwise.py
- Command line
-
hf download hf://hkblockchaintechnology/tiny-computer-agent/tests/test_stepwise.py
-
curl -L -o test_stepwise.py https://huggingface.co/hkblockchaintechnology/tiny-computer-agent/resolve/main/tests/test_stepwise.py
6.21 kB
| """The stepwise API: a console needs to run the loop one turn at a time. | |
| `run()` is the batch form. A live console needs the opposite - the caller owns the | |
| loop, so a button press advances exactly one step and a pause switch stops between | |
| them. Both are the same rules; these tests pin that down, including an | |
| equivalence check so the two forms cannot drift apart. | |
| """ | |
| import pytest | |
| from tiny_computer_agent import ( | |
| AgentLoop, Element, FakeScreen, MockBrain, PlannerBrain, Policy, | |
| ) | |
| def script(): | |
| return MockBrain([ | |
| {"kind": "wait", "seconds": 0.1}, | |
| {"kind": "click", "label": "Save"}, | |
| {"kind": "key", "keys": "ctrl+s"}, | |
| {"kind": "done", "reason": "saved"}, | |
| ]) | |
| def screen(): | |
| return FakeScreen([ | |
| (Element("Files", x=10, y=6, width=60, height=20),), | |
| (Element("Save", x=320, y=120, width=80, height=24),), | |
| ]) | |
| def shape(episode): | |
| """Everything about an episode that a user could notice.""" | |
| return ( | |
| episode.stop_reason, | |
| episode.success, | |
| [ | |
| (s.index, | |
| s.action.describe() if s.action else None, | |
| s.error, | |
| s.result.summary() if s.result else None) | |
| for s in episode.steps | |
| ], | |
| ) | |
| # -- stepping --------------------------------------------------------------- | |
| def test_begin_reads_the_screen_once_and_step_once_advances_one_turn(): | |
| screen_ = screen() | |
| loop = AgentLoop(screen=screen_, brain=script()).begin("save") | |
| assert screen_.captures == 1 # one read, before the first decision | |
| assert loop.finished is False | |
| assert loop.episode.steps == [] | |
| first = loop.step_once() | |
| assert first.index == 0 and first.action.kind == "wait" | |
| assert len(loop.episode.steps) == 1 | |
| assert loop.finished is False # three turns still to go | |
| loop.step_once() | |
| assert [s.action.kind for s in loop.episode.steps] == ["wait", "click"] | |
| def test_stepping_past_the_end_returns_none_instead_of_raising(): | |
| loop = AgentLoop(screen=screen(), brain=script()).begin("save") | |
| while loop.step_once() is not None: | |
| pass | |
| assert loop.finished is True | |
| assert loop.episode.success is True | |
| assert loop.step_once() is None | |
| assert loop.step_once() is None | |
| def test_step_once_before_begin_does_nothing(): | |
| loop = AgentLoop(screen=screen(), brain=script()) | |
| assert loop.step_once() is None | |
| assert loop.episode is None | |
| def test_the_budget_is_enforced_one_step_at_a_time(): | |
| loop = AgentLoop( | |
| screen=screen(), | |
| brain=MockBrain([{"kind": "wait", "seconds": 0.2 + i} for i in range(10)]), | |
| policy=Policy(step_budget=3), | |
| ).begin("save") | |
| taken = 0 | |
| while loop.step_once() is not None: | |
| taken += 1 | |
| assert taken == 3 | |
| assert "budget of 3" in loop.episode.stop_reason | |
| # -- pause and resume ------------------------------------------------------- | |
| def test_pause_stops_between_steps_and_leaves_the_episode_open(): | |
| loop = AgentLoop(screen=screen(), brain=script()).begin("save") | |
| loop.step_once() | |
| loop.pause() | |
| episode = loop.run() # returns immediately | |
| assert len(episode.steps) == 1 | |
| assert loop.finished is False # not finished, only paused | |
| assert loop.paused is True | |
| def test_resume_continues_the_same_episode_to_the_same_end(): | |
| loop = AgentLoop(screen=screen(), brain=script()).begin("save") | |
| loop.step_once() | |
| loop.pause() | |
| loop.run() | |
| loop.resume() | |
| episode = loop.run() | |
| assert episode.success is True | |
| assert [s.action.kind for s in episode.steps] == ["wait", "click", "key", "done"] | |
| assert loop.paused is False | |
| def test_a_paused_run_is_equivalent_to_an_uninterrupted_one(): | |
| straight = AgentLoop(screen=screen(), brain=script()).run("save") | |
| interrupted = AgentLoop(screen=screen(), brain=script()).begin("save") | |
| interrupted.step_once() | |
| interrupted.pause() | |
| interrupted.run() | |
| interrupted.resume() | |
| interrupted.run() | |
| assert shape(interrupted.episode) == shape(straight) | |
| # -- the two forms cannot drift apart --------------------------------------- | |
| def test_run_is_exactly_begin_plus_step_once_in_a_loop(): | |
| batch = AgentLoop(screen=screen(), brain=PlannerBrain()).run("click 'Save'") | |
| stepwise = AgentLoop(screen=screen(), brain=PlannerBrain()).begin("click 'Save'") | |
| while stepwise.step_once() is not None: | |
| pass | |
| assert shape(stepwise.episode) == shape(batch) | |
| def test_verify_still_owns_success_when_stepping(): | |
| texts = ["Unsaved changes", "before", "Saved"] | |
| class TextScreen: | |
| def __init__(self): | |
| self.n = 0 | |
| def capture(self): | |
| from tiny_computer_agent import Observation | |
| text = texts[min(self.n, len(texts) - 1)] | |
| self.n += 1 | |
| return Observation(index=self.n - 1, elements=(Element("Save"),), text=text) | |
| loop = AgentLoop( | |
| screen=TextScreen(), | |
| brain=MockBrain([{"kind": "wait", "seconds": 0.1}] * 4), | |
| verify=lambda observation: "saved" in observation.text.lower(), | |
| ).begin("save") | |
| while loop.step_once() is not None: | |
| pass | |
| assert loop.episode.success is True | |
| assert loop.episode.stop_reason == "verified after 2 action(s)" | |
| def test_a_kill_switch_stops_a_stepwise_run_before_the_first_action(tmp_path): | |
| marker = tmp_path / "STOP" | |
| marker.write_text("stop") | |
| loop = AgentLoop( | |
| screen=screen(), | |
| brain=MockBrain([{"kind": "click", "label": "Save"}]), | |
| policy=Policy(kill_switch_path=str(marker)), | |
| ).begin("save") | |
| assert loop.step_once() is None | |
| assert loop.episode.steps == [] | |
| assert loop.episode.stop_reason == "kill switch engaged" | |
| assert loop.finished is True | |
| def test_a_dead_screen_finishes_the_episode_at_begin(): | |
| class Blind: | |
| def capture(self): | |
| raise RuntimeError("X connection failed") | |
| loop = AgentLoop(screen=Blind(), brain=script()).begin("anything") | |
| assert loop.finished is True | |
| assert loop.episode.steps == [] | |
| assert loop.episode.stop_reason.startswith("capture failed: RuntimeError") | |
| assert loop.step_once() is None | |