diff --git a/README.md b/README.md index 0ad5a0bc..ec773b7f 100644 --- a/README.md +++ b/README.md @@ -71,6 +71,15 @@ This allows developers to deploy production agents that can scale beautifully to ).start() ``` + Note: `Runtime.start()` will block the main thread in normal scripts (no running event loop). In interactive environments with an active loop (e.g., Jupyter), it returns an `asyncio.Task` and does not block. For non-blocking usage from a sync script, you can run it in a background thread: + + ```python + from threading import Thread + + runtime = Runtime(name="my-first-runtime", namespace="hello-world", nodes=[MyFirstNode]) + Thread(target=runtime.start, daemon=True).start() + ``` + - ### Define your first graph Graphs are then described connecting nodes with relationships in json objects. Exosphere runs graph as per defined trigger conditions. See [Graph definitions](https://docs.exosphere.host/exosphere/create-graph/) to see more examples. diff --git a/docs/docs/getting-started.md b/docs/docs/getting-started.md index 7ab29670..a668a902 100644 --- a/docs/docs/getting-started.md +++ b/docs/docs/getting-started.md @@ -86,6 +86,39 @@ Runtime( ).start() ``` +### Note on blocking behavior of `Runtime.start()` + +By design, `Runtime.start()` runs the runtime loop indefinitely and will block the main thread when no asyncio event loop is running (e.g., normal Python scripts). In interactive environments that already have an event loop (like Jupyter notebooks), `Runtime.start()` returns an `asyncio.Task` and does not block. + +- If you're in an async/interactive environment (e.g., Jupyter/REPL with a running loop): + + ```python + # Jupyter/async environment + runtime = Runtime(namespace="MyProject", name="DataProcessor", nodes=[SampleNode]) + task = runtime.start() # task is an asyncio.Task running in the background + # You can continue interacting, and optionally await/cancel the task later + # await task # if you want to wait on it + ``` + +- If you need a non-blocking start from a regular sync script, run it in a background thread: + + ```python + from threading import Thread + + runtime = Runtime(namespace="MyProject", name="DataProcessor", nodes=[SampleNode]) + Thread(target=runtime.start, daemon=True).start() + # continue with other work in the main thread + ``` + +- Alternatively, from an async context you can offload to a thread: + + ```python + import asyncio + + runtime = Runtime(namespace="MyProject", name="DataProcessor", nodes=[SampleNode]) + await asyncio.to_thread(runtime.start) + ``` + ## Next Steps Now that you have the basics, explore: diff --git a/python-sdk/README.md b/python-sdk/README.md index 7df6c3c2..c7f68168 100644 --- a/python-sdk/README.md +++ b/python-sdk/README.md @@ -59,6 +59,36 @@ Runtime( ).start() ``` +### Note on blocking behavior of `Runtime.start()` + +`Runtime.start()` blocks the main thread when no asyncio event loop is running (typical Python scripts). In environments with an active event loop (e.g., Jupyter), it returns an `asyncio.Task` and does not block. + +- Jupyter/interactive (non-blocking): + + ```python + runtime = Runtime(namespace="MyProject", name="DataProcessor", nodes=[SampleNode]) + task = runtime.start() # background asyncio.Task + # await task # optionally wait on it later + ``` + +- Regular sync script (non-blocking via thread): + + ```python + from threading import Thread + + runtime = Runtime(namespace="MyProject", name="DataProcessor", nodes=[SampleNode]) + Thread(target=runtime.start, daemon=True).start() + ``` + +- From async code (offload to a thread): + + ```python + import asyncio + + runtime = Runtime(namespace="MyProject", name="DataProcessor", nodes=[SampleNode]) + await asyncio.to_thread(runtime.start) + ``` + ## Environment Configuration The SDK requires the following environment variables for authentication with ExosphereHost: