Repository: open-world-agents/open-world-agents
Pull request #32: docs: fix outdated code, add example scripts
Author: login: haebin-seong
Created: 2025-03-25T04:38:14Z | Merged: 2025-03-26T02:06:05Z



Diff:
diff --git a/docs/env/guide.md b/docs/env/guide.md
index 55c975d60..6d9b27eda 100644
--- a/docs/env/guide.md
+++ b/docs/env/guide.md
@@ -77,7 +77,7 @@ print(CALLABLES, LISTENERS) # {'clock.time_ns': <built-in function time_ns>} {'c
 tick = LISTENERS["clock/tick"]().configure(callback=lambda: print(CALLABLES["clock.time_ns"]()), interval=1)
 tick.start()
 
-time.sleep(1)  # The listener prints the current time in nanoseconds a few times
+time.sleep(2)  # The listener prints the current time in nanoseconds a few times
 
 tick.stop(), tick.join()
 ```
@@ -88,7 +88,7 @@ Instead of manual `start-stop-join` procedure, you may utilize context manager:
 
 ```python
 with tick.session:
-    time.sleep(1)
+    time.sleep(2)
 ```
 
 ### Desktop Environment (`owa.env.desktop`)
@@ -96,13 +96,17 @@ with tick.session:
 *The desktop environment module provides capabilities for UI interaction and input handling.*
 
 ```python
+import time
+from owa.core.registry import CALLABLES, LISTENERS, activate_module
+from owa.env.desktop.msg import KeyboardEvent
+
 # Activate the desktop module to enable UI and input capabilities
 activate_module("owa.env.desktop")
 
 # Using screen capture and window management features
-print(CALLABLES["screen.capture"]().shape)  # Example output: (1080, 1920, 3)
-print(CALLABLES["window.get_active_window"])()
-print(CALLABLES["window.get_window_by_title"]("open-world-agents"))
+print(f"{CALLABLES['screen.capture']().shape=}")  # Example output: (1080, 1920, 3)
+print(f"{CALLABLES['window.get_active_window']()=}")
+print(f"{CALLABLES['window.get_window_by_title']('open-world-agents')=}")
 
 # Simulating a mouse click (left button, double click)
 mouse_click = CALLABLES["mouse.click"]
@@ -110,8 +114,10 @@ mouse_click("left", 2)
 
 # Configuring a keyboard listener
 
-def on_keyboard_event(event_type, key):
-    print(f"Keyboard event: {event_type}, {key}")
+
+def on_keyboard_event(keyboard_event: KeyboardEvent):
+    print(f"Keyboard event: {keyboard_event.event_type=}, {keyboard_event.vk=}")
+
 
 keyboard_listener = LISTENERS["keyboard"]().configure(callback=on_keyboard_event)
 keyboard_listener.start()
diff --git a/docs/env/plugins/desktop_env.md b/docs/env/plugins/desktop_env.md
index 372548381..2bee7598d 100644
--- a/docs/env/plugins/desktop_env.md
+++ b/docs/env/plugins/desktop_env.md
@@ -20,7 +20,7 @@ After activation, you can access desktop functionalities via the global registri
 
 ```python
 print(CALLABLES["screen.capture"]().shape)  # Capture and display screen dimensions
-print(CALLABLES["window.get_active_window"])()  # Retrieve the active window
+print(CALLABLES["window.get_active_window"]())  # Retrieve the active window
 ```
 
 This module is essential for applications that require integration with desktop UI elements and user input simulation.
diff --git a/docs/index.md b/docs/index.md
index 3e9c0fc69..32f1471bf 100644
--- a/docs/index.md
+++ b/docs/index.md
@@ -49,11 +49,9 @@ Here's what we've got in store for you!
         time_ns = CALLABLES["clock.time_ns"]()
         print(f"Current time in nanoseconds: {time_ns}")
 
-    # Create a listener for clock/tick event
-    tick = LISTENERS["clock/tick"]().configure(callback=callback)
+    # Create a listener for clock/tick event, Set listener to trigger every 1 second
+    tick = LISTENERS["clock/tick"]().configure(callback=callback, interval=1)
 
-    # Set listener to trigger every 1 second
-    tick.configure(interval=1)
     # Start the listener
     tick.start()
 
@@ -62,6 +60,7 @@ Here's what we've got in store for you!
 
     # Stop the listener and wait for it to finish
     tick.stop(), tick.join()
+
     ```
 
 - Record your own desktop usage data by just running `owl mcap record your-filename.mcap`. [Learn more...](data/recorder/install_and_usage.md)
diff --git a/projects/owa-core/owa/core/listener.py b/projects/owa-core/owa/core/listener.py
index f01083d6b..21990eef9 100644
--- a/projects/owa-core/owa/core/listener.py
+++ b/projects/owa-core/owa/core/listener.py
@@ -179,7 +179,7 @@ def run(self):
         """
         if not getattr(self, "_configured", False):
             raise RuntimeError(
-                "RunnableThread is not configured. Call configure() before start(). Or you may have overriden the configure method, not on_configure."
+                "RunnableProcess is not configured. Call configure() before start(). Or you may have overriden the configure method, not on_configure."
             )
 
         kwargs = {}
@@ -190,10 +190,10 @@ def run(self):
     @abstractmethod
     def loop(self, stop_event: mpEvent, callback: Callable):
         """
-        Main thread execution loop. Must be implemented by subclasses.
+        Main process execution loop. Must be implemented by subclasses.
 
         Args:
-            stop_event (threading.Event): An event that will be set when the thread should stop.
+            stop_event (multiprocessing.Event): An event that will be set when the process should stop.
                                         Check this event regularly and exit when it's set.
                                         If this argument is not present, the loop will be called without it.
             callback (Callable): The function to call when an event is detected.
diff --git a/scripts/examples/env_clock.py b/scripts/examples/env_clock.py
new file mode 100644
index 000000000..c59fe5233
--- /dev/null
+++ b/scripts/examples/env_clock.py
@@ -0,0 +1,21 @@
+import time
+from owa.core.registry import CALLABLES, LISTENERS, activate_module
+
+# Initial registry state (empty)
+print(CALLABLES, LISTENERS)  # {}, {}
+
+# Activate the standard module to register clock functionalities
+activate_module("owa.env.std")
+print(
+    CALLABLES, LISTENERS
+)  # {'clock.time_ns': <built-in function time_ns>} {'clock/tick': <class 'owa.env.std.clock.ClockTickListener'>}
+
+# Testing the clock/tick listener
+tick = LISTENERS["clock/tick"]().configure(
+    callback=lambda: print(CALLABLES["clock.time_ns"]()), interval=1
+)
+tick.start()
+
+time.sleep(2)  # The listener prints the current time in nanoseconds a few times
+
+tick.stop(), tick.join()
diff --git a/scripts/examples/env_keyboard.py b/scripts/examples/env_keyboard.py
new file mode 100644
index 000000000..2bb2fa82d
--- /dev/null
+++ b/scripts/examples/env_keyboard.py
@@ -0,0 +1,28 @@
+import time
+from owa.core.registry import CALLABLES, LISTENERS, activate_module
+from owa.env.desktop.msg import KeyboardEvent
+
+# Activate the desktop module to enable UI and input capabilities
+activate_module("owa.env.desktop")
+
+# Using screen capture and window management features
+print(f"{CALLABLES['screen.capture']().shape=}")  # Example output: (1080, 1920, 3)
+print(f"{CALLABLES['window.get_active_window']()=}")
+print(f"{CALLABLES['window.get_window_by_title']('open-world-agents')=}")
+
+# Simulating a mouse click (left button, double click)
+mouse_click = CALLABLES["mouse.click"]
+mouse_click("left", 2)
+
+# Configuring a keyboard listener
+
+
+def on_keyboard_event(keyboard_event: KeyboardEvent):
+    print(f"Keyboard event: {keyboard_event.event_type=}, {keyboard_event.vk=}")
+
+
+keyboard_listener = LISTENERS["keyboard"]().configure(callback=on_keyboard_event)
+keyboard_listener.start()
+
+time.sleep(5)
+keyboard_listener.stop(), keyboard_listener.join()


Reviews:
user: login: graphite-app[bot]
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-25T04:39:29Z
user: login: graphite-app[bot]
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-25T04:44:34Z
user: login: alohays
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-25T07:06:19Z

Review comments:
diff_hunk: @@ -0,0 +1,17 @@
+import time
+from owa.core.registry import CALLABLES, LISTENERS, activate_module
+
+# Initial registry state (empty)
+print(CALLABLES, LISTENERS)  # {}, {}
+
+# Activate the standard module to register clock functionalities
+activate_module("owa.env.std")
+print(CALLABLES, LISTENERS) # {'clock.time_ns': <built-in function time_ns>} {'clock/tick': <class 'owa.env.std.clock.ClockTickListener'>}
+
+# Testing the clock/tick listener
+tick = LISTENERS["clock/tick"]().configure(callback=lambda: print(CALLABLES["clock.time_ns"]()), interval=1)
+tick.start()
+
+time.sleep(2)  # The listener prints the current time in nanoseconds a few times
+
+tick.stop(), tick.join()
path: scripts/env_clock.py
user: login: graphite-app[bot]
body: The file is missing a newline at the end. Standard practice in most coding style guides recommends ending files with a newline character to avoid potential issues with certain tools and to maintain consistency. Please add a newline at the end of this file.
```suggestion
tick.stop(), tick.join()
```
  

*Spotted by [Diamond](https://app.graphite.dev/diamond/?org=open-world-agents&ref=ai-review-comment) (based on [custom rules](https://app.graphite.dev/diamond/?org=open-world-agents&ref=ai-review-comment&view=settings))*<i class='graphite__hidden'><br /><br />Is this helpful? React 👍 or 👎 to let us know.</i>
created_at: 2025-03-25T04:39:28Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,21 @@
+import time
+from owa.core.registry import CALLABLES, LISTENERS, activate_module
path: scripts/env_clock.py
user: login: graphite-app[bot]
body: According to the project's style guidelines, each import should be on its own line. Please modify this import statement to follow this pattern:

```python
from owa.core.registry import CALLABLES
from owa.core.registry import LISTENERS
from owa.core.registry import activate_module
```

This makes imports more readable and easier to manage when reviewing changes.
```suggestion
from owa.core.registry import CALLABLES
from owa.core.registry import LISTENERS
from owa.core.registry import activate_module
```
  

*Spotted by [Diamond](https://app.graphite.dev/diamond/?org=open-world-agents&ref=ai-review-comment) (based on [custom rules](https://app.graphite.dev/diamond/?org=open-world-agents&ref=ai-review-comment&view=settings))*<i class='graphite__hidden'><br /><br />Is this helpful? React 👍 or 👎 to let us know.</i>
created_at: 2025-03-25T04:44:33Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,21 @@
+import time
+from owa.core.registry import CALLABLES, LISTENERS, activate_module
path: scripts/env_clock.py
user: login: alohays
body: It seems like the Graphite agent has misunderstood the "one import per line" rule. I think we can disregard this.
created_at: 2025-03-25T07:06:18Z
author_association: CONTRIBUTOR

PR comments:
user: login: alohays
created_at: 2025-03-25T07:11:28Z
body: Please create a PR overview. If this is still in progress, please convert it to draft status.
author_association: CONTRIBUTOR
user: login: haebin-seong
created_at: 2025-03-25T07:16:56Z
body: Oh yeah, I might fix some more and let you know if I'm done. I'll convert it to draft for now!
author_association: MEMBER
user: login: MilkClouds
created_at: 2025-03-25T12:30:24Z
body: LGTM, but added scripts in `scripts` folder look better to be placed in separate folder that notify users that it is example. e.g. `scripts/examples`.
author_association: MEMBER