Skip to content

Commit 894afce

Browse files
committed
Merge branch 'main' into split-cli-jobs
2 parents dcdd9ce + 207f3f3 commit 894afce

File tree

6 files changed

+179
-0
lines changed

6 files changed

+179
-0
lines changed

.github/workflows/ci.yml

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -465,10 +465,27 @@ jobs:
465465
fi
466466
467467
echo "Running CI Operator Test"
468+
dora build examples/python-operator-dataflow/dataflow.yml --uv
468469
dora start examples/python-operator-dataflow/dataflow.yml --name ci-python-operator --detach --uv
469470
sleep 10
470471
echo "Running dora stop"
471472
dora stop --name ci-python-operator --grace-duration 30s
473+
474+
- name: "Test CLI (Python): Object Detection with external deps"
475+
timeout-minutes: 30
476+
# fail-fast by using bash shell explicitly
477+
shell: bash
478+
run: |
479+
if [ "$RUNNER_OS" != "Windows" ]; then
480+
source .venv/bin/activate
481+
else
482+
source .venv/Scripts/activate
483+
fi
484+
echo "Running Python object detection example with external deps"
485+
dora build examples/python-dataflow/dataflow-object-detection.yml --uv
486+
dora start examples/python-dataflow/dataflow-object-detection.yml --name ci-python-obj-det --detach --uv
487+
sleep 10
488+
dora stop --name ci-python-obj-det --grace-duration 30s
472489
- name: "Test CLI (Python): Dora destroy"
473490
timeout-minutes: 5
474491
# fail-fast by using bash shell explicitly

.github/workflows/claude-code.yml

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
name: Claude Code
2+
3+
on:
4+
issue_comment:
5+
types: [created]
6+
pull_request_review_comment:
7+
types: [created]
8+
issues:
9+
types: [opened, assigned]
10+
pull_request_review:
11+
types: [submitted]
12+
13+
jobs:
14+
claude:
15+
if: |
16+
(github.event_name == 'issue_comment' && contains(github.event.comment.body, '@claude')) ||
17+
(github.event_name == 'pull_request_review_comment' && contains(github.event.comment.body, '@claude')) ||
18+
(github.event_name == 'pull_request_review' && contains(github.event.review.body, '@claude')) ||
19+
(github.event_name == 'issues' && (contains(github.event.issue.body, '@claude') || contains(github.event.issue.title, '@claude')))
20+
runs-on: ubuntu-latest
21+
permissions:
22+
contents: write
23+
pull-requests: write
24+
issues: write
25+
id-token: write
26+
actions: read
27+
steps:
28+
- name: Checkout repository
29+
uses: actions/checkout@v4
30+
with:
31+
fetch-depth: 1
32+
33+
- name: Run Claude Code
34+
id: claude
35+
uses: anthropics/claude-code-action@v1
36+
with:
37+
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
# Fast Multi-Array Messaging with Dora
2+
3+
Hi there! This example shows you how to send multiple numpy arrays (like several sensor readings or images) in a single Dora message with lightning-fast performance.
4+
5+
## Why this matters
6+
You might have noticed that using `numpy_array.tolist()` to package data is... well, pretty slow. That's because it converts every single pixel or number into a Python object, which is heavy work for the CPU.
7+
8+
## The Secret Sauce
9+
The trick is to keep things in binary format! We use `numpy_array.ravel()` to flatten the arrays efficiently and pass them straight to `pyarrow`. This lets us achieve near zero-copy performance.
10+
11+
On the receiving end, we simply convert back to numpy and reshape. Easy peasy!
12+
13+
## Give it a spin
14+
15+
1. **Get Set Up**: Make sure you have `dora` installed and the binary in your PATH.
16+
2. **Install the goods**:
17+
```bash
18+
pip install numpy pyarrow
19+
```
20+
3. **Run it**:
21+
```bash
22+
dora up
23+
dora start dataflow.yml
24+
```
25+
26+
You should see something awesome like this in your terminal:
27+
```
28+
Sent message with 3 arrays. Encoding time: 0.000345s
29+
Received and decoded. Shape1: (480, 640, 3), Shape2: (480, 640, 3), State: (1, 6). Time: 0.000210s
30+
```
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
nodes:
2+
- id: sender
3+
path: sender.py
4+
outputs:
5+
- multi_array_msg
6+
inputs:
7+
tick: dora/timer/millis/1000
8+
9+
- id: receiver
10+
path: receiver.py
11+
inputs:
12+
multi_array_msg: sender/multi_array_msg
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import numpy as np
2+
from dora import Node
3+
import time
4+
5+
def main():
6+
node = Node()
7+
8+
for event in node:
9+
if event["type"] == "INPUT":
10+
if event["id"] == "multi_array_msg":
11+
t0 = time.time()
12+
13+
# The event value is our PyArrow StructArray.
14+
packed_data = event["value"]
15+
16+
# We can access the columns by name.
17+
# packed_data.field('image1') gives us the column for image1.
18+
19+
# Grab the arrow arrays for each field.
20+
col_image1 = packed_data.field('image1')
21+
col_image2 = packed_data.field('image2')
22+
col_state = packed_data.field('state')
23+
24+
# Now we extract the values and convert back to numpy.
25+
# We take the first element since we know we sent a single batch.
26+
27+
arr_image1_flat = col_image1[0].values.to_numpy(zero_copy_only=False)
28+
# Reshape it back to the original image dimensions.
29+
# (Ideally, you'd pass metadata for dynamic shapes, but we'll keep it simple here).
30+
image1 = arr_image1_flat.view(np.uint8).reshape((480, 640, 3))
31+
32+
arr_image2_flat = col_image2[0].values.to_numpy(zero_copy_only=False)
33+
image2 = arr_image2_flat.view(np.uint8).reshape((480, 640, 3))
34+
35+
arr_state_flat = col_state[0].values.to_numpy(zero_copy_only=False)
36+
state = arr_state_flat.view(np.float32).reshape((1, 6))
37+
38+
proc_time = time.time() - t0
39+
print(f"Received and decoded. Shape1: {image1.shape}, Shape2: {image2.shape}, State: {state.shape}. Time: {proc_time:.6f}s", flush=True)
40+
41+
if __name__ == "__main__":
42+
main()
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import numpy as np
2+
import pyarrow as pa
3+
from dora import Node
4+
import time
5+
6+
def main():
7+
node = Node()
8+
9+
# Initialize some dummy data
10+
# Let's create some dummy data to send.
11+
# We have two images and a state vector, just like in your robotics setup.
12+
image1 = np.zeros((480, 640, 3), dtype=np.uint8)
13+
image2 = np.ones((480, 640, 3), dtype=np.uint8) * 255
14+
state = np.array([[1.0, 2.0, 3.0, 4.0, 5.0, 6.0]], dtype=np.float32)
15+
16+
for event in node:
17+
if event["type"] == "INPUT":
18+
if event["id"] == "tick":
19+
# Efficiently package the arrays using PyArrow.
20+
# We flatten the arrays with .ravel() which is super fast and avoids expensive logic.
21+
# Then we wrap them in a list of length 1 to create the arrow columns.
22+
23+
t0 = time.time()
24+
25+
# Fast path!
26+
encoded_image1 = pa.array([image1.ravel()])
27+
encoded_image2 = pa.array([image2.ravel()])
28+
encoded_state = pa.array([state.ravel()])
29+
30+
struct_data = pa.StructArray.from_arrays(
31+
[encoded_image1, encoded_image2, encoded_state],
32+
names=['image1', 'image2', 'state']
33+
)
34+
35+
node.send_output("multi_array_msg", struct_data)
36+
37+
proc_time = time.time() - t0
38+
print(f"Sent message with 3 arrays. Encoding time: {proc_time:.6f}s", flush=True)
39+
40+
if __name__ == "__main__":
41+
main()

0 commit comments

Comments
 (0)