hkblockchaintechnology commited on
Commit
57356d8
·
verified ·
1 Parent(s): 29b362f

Publish tiny_computer_agent/ from marimo

Browse files
tiny_computer_agent/__main__.py CHANGED
@@ -80,6 +80,10 @@ def build_parser() -> argparse.ArgumentParser:
80
  help="skip OCR and index a numbered grid instead")
81
  parser.add_argument("--max-width", type=int, default=1600,
82
  help="downscale captures wider than this before indexing")
 
 
 
 
83
  parser.add_argument("--allow-grid", action="store_true",
84
  help="permit clicks on numbered grid cells with --real-input")
85
  parser.add_argument("--kill-switch", default=DEFAULT_KILL_SWITCH,
@@ -215,6 +219,13 @@ def main(argv: list[str] | None = None) -> int:
215
 
216
  warned = False
217
 
 
 
 
 
 
 
 
218
  def on_step(step: Step) -> None:
219
  nonlocal warned
220
  elements = step.observation.elements
@@ -228,7 +239,7 @@ def main(argv: list[str] | None = None) -> int:
228
 
229
  try:
230
  episode = AgentLoop(screen=screen, brain=brain, executor=executor,
231
- policy=policy, on_step=on_step).run(args.goal)
232
  except ScreenUnavailable as exc:
233
  print(f"cannot see the screen: {exc}", file=sys.stderr)
234
  print("run `python -m tiny_computer_agent --doctor --live` for details.", file=sys.stderr)
 
80
  help="skip OCR and index a numbered grid instead")
81
  parser.add_argument("--max-width", type=int, default=1600,
82
  help="downscale captures wider than this before indexing")
83
+ parser.add_argument("--verify-text", default=None, metavar="TEXT",
84
+ help="stop with success once this text appears on screen "
85
+ "(otherwise a successful action that removes its own "
86
+ "target is indistinguishable from a failure)")
87
  parser.add_argument("--allow-grid", action="store_true",
88
  help="permit clicks on numbered grid cells with --real-input")
89
  parser.add_argument("--kill-switch", default=DEFAULT_KILL_SWITCH,
 
219
 
220
  warned = False
221
 
222
+ verify = None
223
+ if args.verify_text:
224
+ _needle = args.verify_text.lower()
225
+
226
+ def verify(observation) -> bool: # noqa: F811 - the loop calls this
227
+ return _needle in observation.text.lower()
228
+
229
  def on_step(step: Step) -> None:
230
  nonlocal warned
231
  elements = step.observation.elements
 
239
 
240
  try:
241
  episode = AgentLoop(screen=screen, brain=brain, executor=executor,
242
+ policy=policy, on_step=on_step, verify=verify).run(args.goal)
243
  except ScreenUnavailable as exc:
244
  print(f"cannot see the screen: {exc}", file=sys.stderr)
245
  print("run `python -m tiny_computer_agent --doctor --live` for details.", file=sys.stderr)
tiny_computer_agent/act.py CHANGED
@@ -99,9 +99,9 @@ class PyAutoGuiExecutor:
99
  def _pyautogui():
100
  try:
101
  import pyautogui
102
- except ImportError as exc: # pragma: no cover - depends on the machine
103
  raise InputUnavailable(
104
- "real input needs pyautogui: pip install \'tiny-computer-agent[desktop]\'"
105
  ) from exc
106
  return pyautogui
107
 
 
99
  def _pyautogui():
100
  try:
101
  import pyautogui
102
+ except Exception as exc: # pragma: no cover - depends on the machine
103
  raise InputUnavailable(
104
+ "real input needs pyautogui and a live display: pip install \'tiny-computer-agent[desktop]\'"
105
  ) from exc
106
  return pyautogui
107
 
tiny_computer_agent/loop.py CHANGED
@@ -83,7 +83,11 @@ class AgentLoop:
83
  def __init__(self, *, screen: ScreenSource, brain: Brain,
84
  executor: Executor | None = None, policy: Policy | None = None,
85
  on_step: Callable[[Step], None] | None = None,
86
- max_repeats: int = 1) -> None:
 
 
 
 
87
  self.screen = screen
88
  self.brain = brain
89
  self.policy = policy or Policy()
@@ -91,6 +95,7 @@ class AgentLoop:
91
  executor = DryRunExecutor() if self.policy.dry_run else PyAutoGuiExecutor()
92
  self.executor = executor
93
  self.on_step = on_step
 
94
  self.max_repeats = max_repeats
95
 
96
  def run(self, goal: str, *, max_steps: int | None = None) -> Episode:
@@ -177,8 +182,20 @@ class AgentLoop:
177
  episode.stop_reason = "perception failed mid-episode"
178
  break
179
 
 
 
 
 
 
180
  return episode
181
 
 
 
 
 
 
 
 
182
  @staticmethod
183
  def _fingerprint(observation: Observation) -> tuple:
184
  """What the agent can see: enough to tell "nothing changed" from "it worked"."""
 
83
  def __init__(self, *, screen: ScreenSource, brain: Brain,
84
  executor: Executor | None = None, policy: Policy | None = None,
85
  on_step: Callable[[Step], None] | None = None,
86
+ max_repeats: int = 1,
87
+ verify: Callable[[Observation], bool] | None = None) -> None:
88
+ """``verify`` runs after every action: the harness, not the model, decides
89
+ whether the goal was reached. A brain saying "done" is a claim; a verifier
90
+ looking at the screen is evidence."""
91
  self.screen = screen
92
  self.brain = brain
93
  self.policy = policy or Policy()
 
95
  executor = DryRunExecutor() if self.policy.dry_run else PyAutoGuiExecutor()
96
  self.executor = executor
97
  self.on_step = on_step
98
+ self.verify = verify
99
  self.max_repeats = max_repeats
100
 
101
  def run(self, goal: str, *, max_steps: int | None = None) -> Episode:
 
182
  episode.stop_reason = "perception failed mid-episode"
183
  break
184
 
185
+ if self.verify is not None and self._verified(observation):
186
+ episode.success = True
187
+ episode.stop_reason = f"verified after {index + 1} action(s)"
188
+ break
189
+
190
  return episode
191
 
192
+ def _verified(self, observation: Observation) -> bool:
193
+ """A broken verifier must never fake a success."""
194
+ try:
195
+ return bool(self.verify(observation))
196
+ except Exception:
197
+ return False
198
+
199
  @staticmethod
200
  def _fingerprint(observation: Observation) -> tuple:
201
  """What the agent can see: enough to tell "nothing changed" from "it worked"."""