Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 23 additions & 1 deletion .github/workflows/node-hub-ci-cd.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,34 @@ jobs:
outputs:
folders: ${{ steps.jobs.outputs.folders }}
steps:
- uses: actions/checkout@v1
- uses: actions/checkout@v4

- id: jobs
uses: kmanimaran/list-folder-action@v4
with:
path: ./node-hub

lint:
runs-on: ubuntu-24.04
needs: [find-jobs]
name: Lint
strategy:
fail-fast: ${{ github.event_name != 'workflow_dispatch' && !(github.event_name == 'release' && startsWith(github.ref, 'refs/tags/')) }}
matrix:
folder: ${{ fromJson(needs.find-jobs.outputs.folders )}}
steps:
- uses: actions/checkout@v4

- name: Install the latest version of uv
uses: astral-sh/setup-uv@v5
with:
enable-cache: true

- name: Run linting
working-directory: node-hub/${{ matrix.folder }}
run: |
uv tool install ruff
ruff check .
Comment on lines +27 to +48
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FYI, this is not necessary as we already have it in our node hub script:

uv run ruff check .

:)


ci:
runs-on: ${{ matrix.platform }}
Expand Down
3 changes: 2 additions & 1 deletion benches/mllm/benchmark_script.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,8 @@ def write_to_csv(filename, header, row):


def main():
# Handle dynamic nodes, ask for the name of the node in the dataflow, and the same values as the ENV variables.
# Handle dynamic nodes, ask for the name of the node in the dataflow, and the same
# values as the ENV variables.
"""TODO: Add docstring."""
parser = argparse.ArgumentParser(description="Simple arrow sender")

Expand Down
3 changes: 2 additions & 1 deletion node-hub/dora-argotranslate/dora_argotranslate/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@
available_packages = argostranslate.package.get_available_packages()
package_to_install = next(
filter(
lambda x: x.from_code == from_code and x.to_code == to_code, available_packages,
lambda x: x.from_code == from_code and x.to_code == to_code,
available_packages,
),
)
argostranslate.package.install_from_path(package_to_install.download())
Expand Down
1 change: 1 addition & 0 deletions node-hub/dora-argotranslate/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -29,4 +29,5 @@ extend-select = [
"NPY", # Ruff's NPY rule
"N", # Ruff's N rule
"I", # Ruff's I rule
"E", # Ruff's E rule
]
3 changes: 2 additions & 1 deletion node-hub/dora-argotranslate/tests/test_translate.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ def test_import_main():
"""TODO: Add docstring."""
from dora_argotranslate.main import main

# Check that everything is working, and catch dora Runtime Exception as we're not running in a dora dataflow.
# Check that everything is working, and catch dora Runtime Exception as we're not
# running in a dora dataflow.
with pytest.raises(RuntimeError):
main()
3 changes: 2 additions & 1 deletion node-hub/dora-cotracker/tests/test_dora_cotracker.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
def test_import_main():
from dora_cotracker.main import main

# Check that everything is working, and catch dora Runtime Exception as we're not running in a dora dataflow.
# Check that everything is working, and catch dora Runtime Exception as we're not
# running in a dora dataflow.
with pytest.raises(RuntimeError):
main()
1 change: 1 addition & 0 deletions node-hub/dora-dav1d/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,5 @@ extend-select = [
"NPY", # Ruff's NPY rule
"N", # Ruff's N rule
"I", # Ruff's I rule
"E", # Ruff's E rule
]
10 changes: 7 additions & 3 deletions node-hub/dora-distil-whisper/dora_distil_whisper/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,11 @@ def remove_text_noise(text: str, text_noise="") -> str:
text # Return the original text if text_noise is empty or just whitespace
)

# Helper function to normalize text (remove punctuation, make lowercase, and handle hyphens)
# Helper function to normalize text (remove punctuation, make lowercase, and handle
# hyphens)
def normalize(s):
# Replace hyphens with spaces to treat "Notre-Dame" and "notre dame" as equivalent
# Replace hyphens with spaces to treat "Notre-Dame" and "notre dame" as
# equivalent
s = re.sub(r"-", " ", s)
# Remove other punctuation and convert to lowercase
return re.sub(r"[^\w\s]", "", s).lower()
Expand Down Expand Up @@ -236,5 +238,7 @@ def main():
if text.strip() == "" or text.strip() == ".":
continue
node.send_output(
"text", pa.array([text]), {"language": TARGET_LANGUAGE},
"text",
pa.array([text]),
{"language": TARGET_LANGUAGE},
)
1 change: 1 addition & 0 deletions node-hub/dora-distil-whisper/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -37,4 +37,5 @@ extend-select = [
"NPY", # Ruff's NPY rule
"N", # Ruff's N rule
"I", # Ruff's I rule
"E", # Ruff's E rule
]
3 changes: 2 additions & 1 deletion node-hub/dora-distil-whisper/tests/test_distil_whisper.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ def test_import_main():
"""TODO: Add docstring."""
from dora_distil_whisper.main import main

# Check that everything is working, and catch dora Runtime Exception as we're not running in a dora dataflow.
# Check that everything is working, and catch dora Runtime Exception as we're not
# running in a dora dataflow.
with pytest.raises(RuntimeError):
main()
3 changes: 2 additions & 1 deletion node-hub/dora-echo/dora_echo/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@


def main():
# Handle dynamic nodes, ask for the name of the node in the dataflow, and the same values as the ENV variables.
# Handle dynamic nodes, ask for the name of the node in the dataflow, and the same
# values as the ENV variables.
"""TODO: Add docstring."""
parser = argparse.ArgumentParser(description="Simple arrow sender")

Expand Down
1 change: 1 addition & 0 deletions node-hub/dora-echo/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,5 @@ extend-select = [
"NPY", # Ruff's NPY rule
"N", # Ruff's N rule
"I", # Ruff's I rule
"E", # Ruff's E rule
]
3 changes: 2 additions & 1 deletion node-hub/dora-echo/tests/test_dora_echo.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ def test_import_main():
"""TODO: Add docstring."""
from dora_echo.main import main

# Check that everything is working, and catch dora Runtime Exception as we're not running in a dora dataflow.
# Check that everything is working, and catch dora Runtime Exception as we're not
# running in a dora dataflow.
with pytest.raises(RuntimeError):
main()
79 changes: 48 additions & 31 deletions node-hub/dora-gradio/dora_gradio/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)


class DoraGradioUI:
def __init__(self):
self.node = Node()
Expand All @@ -33,28 +34,32 @@ def handle_audio_stream(self, stream, new_chunk, text_state):
if new_chunk is not None:
try:
sr, y = new_chunk

# Convert to mono and normalize like microphone node
if y.ndim > 1:
y = y[:, 0]
y = y.astype(np.float32)
if np.abs(y).max() > 1.0:
y = y / 32768.0 # For int16 input

# Resample to 16kHz if needed
if sr != self.sample_rate:
y = librosa.resample(y, orig_sr=sr, target_sr=self.sample_rate)

self.buffer.extend(y)
current_time = time.time()
if current_time - self.start_recording_time > self.max_duration:
audio_data = np.array(self.buffer, dtype=np.float32).ravel()
audio_data = np.clip(audio_data, -1.0, 1.0)
self.node.send_output("audio", pa.array(audio_data), {
"sample_rate": self.sample_rate,
"channels": 1,
"timestamp": int(time.time() * 1_000_000_000)
})
self.node.send_output(
"audio",
pa.array(audio_data),
{
"sample_rate": self.sample_rate,
"channels": 1,
"timestamp": int(time.time() * 1_000_000_000),
},
)
self.buffer = []
self.start_recording_time = current_time
return stream, "", "✓ Streaming audio..."
Expand All @@ -71,20 +76,24 @@ def handle_video_stream(self, frame):
if frame.shape[1] != 640 or frame.shape[0] != 480:
frame = cv2.resize(frame, (640, 480))
timestamp = int(time.time() * 1_000_000_000)
self.node.send_output("image", pa.array(frame.ravel()), {
"encoding": "bgr8",
"width": frame.shape[1],
"height": frame.shape[0],
"timestamp": timestamp,
"_time": timestamp
})

self.node.send_output(
"image",
pa.array(frame.ravel()),
{
"encoding": "bgr8",
"width": frame.shape[1],
"height": frame.shape[0],
"timestamp": timestamp,
"_time": timestamp,
},
)

return frame
except Exception as e:
logger.error(f"Error in video stream: {e}")
return np.zeros((480, 640, 3), dtype=np.uint8)
return None

def create_interface(self):
with gr.Blocks(theme=self.theme, title="Dora Input Interface") as interface:
gr.Markdown("## Dora Input Interface")
Expand All @@ -95,19 +104,17 @@ def create_interface(self):
modality="video",
mode="send-receive",
rtc_configuration={
"iceServers": [
{"urls": ["stun:stun.l.google.com:19302"]}
]
}
"iceServers": [{"urls": ["stun:stun.l.google.com:19302"]}]
},
)
with gr.Tab("Audio and Text Input"):
with gr.Group():
gr.Markdown("### Audio Input")
audio_state = gr.State(None)
text_state = gr.State("")
audio_input = gr.Audio(
sources=["microphone"],
streaming=True,
sources=["microphone"],
streaming=True,
type="numpy",
)
audio_status = gr.Markdown("Status: Ready")
Expand All @@ -117,19 +124,27 @@ def create_interface(self):
chatbot = gr.Chatbot(show_label=False, height=200)
with gr.Row():
text_input = gr.Textbox(
placeholder="Type your message here...",
placeholder="Type your message here...",
show_label=False,
scale=4
scale=4,
)
text_send = gr.Button("Send Text", scale=1)

with gr.Row():
stop_button = gr.Button("Stop Server", variant="stop", scale=0.5)

# Event handlers
text_input.submit(self.handle_text_input, [text_input, chatbot], [text_input, chatbot])
text_send.click(self.handle_text_input, [text_input, chatbot], [text_input, chatbot])
audio_input.stream(self.handle_audio_stream, [audio_state, audio_input, text_state], [audio_state, text_state, audio_status])
text_input.submit(
self.handle_text_input, [text_input, chatbot], [text_input, chatbot]
)
text_send.click(
self.handle_text_input, [text_input, chatbot], [text_input, chatbot]
)
audio_input.stream(
self.handle_audio_stream,
[audio_state, audio_input, text_state],
[audio_state, text_state, audio_status],
)
stop_button.click(self.kill_server)

return interface
Expand All @@ -140,11 +155,11 @@ def launch(self):
# import subprocess
# subprocess.run('lsof -ti :7860 | xargs kill -9', shell=True, stderr=subprocess.DEVNULL)
interface = self.create_interface()

def cleanup(signum, frame):
interface.close()
os._exit(0)

signal.signal(signal.SIGINT, cleanup)
signal.signal(signal.SIGTERM, cleanup)
interface.launch(server_name="0.0.0.0", server_port=7860, quiet=False)
Expand All @@ -158,9 +173,11 @@ def kill_server(self):
logger.info("Manually stopping server...")
os._exit(0)


def main():
ui = DoraGradioUI()
ui.launch()


if __name__ == "__main__":
main()
main()
3 changes: 2 additions & 1 deletion node-hub/dora-gradio/tests/test_dora_gradio.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
def test_import_main():
from dora_gradio.main import main

# Check that everything is working, and catch dora Runtime Exception as we're not running in a dora dataflow.
# Check that everything is working, and catch dora Runtime Exception as we're not
# running in a dora dataflow.
with pytest.raises(RuntimeError):
main()
Loading
Loading