A Python package
Project description
DAG-based Gradio workflows!
DAG-based Gradio workflows!
daggr is a Python library for building AI workflows that connect Gradio Spaces, ML models (through Hugging Face Inference Providers), and custom functions. It automatically generates a visual canvas or your workflow allowing you to inspect intermediate outputs, rerun any step, and preserves state for complex workflows.
https://github.com/user-attachments/assets/2cfe49c0-3118-4570-b2bd-f87c333836b5
Installation
pip install daggr
Quick Start
import gradio as gr
from daggr import Graph, FnNode, GradioNode
# 1. Define nodes with inputs and outputs
voice = GradioNode(
space_or_url="abidlabs/tts",
api_name="/generate_voice_design",
inputs={
"voice_description": gr.Textbox(label="Voice", value="Professional voice..."),
"language": "Auto", # Fixed value (no UI)
"text": "Hello world!",
},
outputs={
"audio": gr.Audio(label="Generated Voice"),
},
)
def process_audio(audio: str) -> str:
return audio # Your processing logic here
processor = FnNode(
fn=process_audio,
inputs={"audio": voice.audio}, # Connect to voice node's output
outputs={"audio": gr.Audio(label="Processed Audio")},
)
# 2. Create graph and launch
graph = Graph(name="Audio Pipeline", nodes=[voice, processor])
graph.launch()
How It Works
Input Types
Each node's inputs dict accepts three types of values:
| Type | Example | Result |
|---|---|---|
| Gradio component | gr.Textbox(label="Topic") |
Creates UI input |
| Port reference | other_node.output_name |
Connects nodes |
| Fixed value | "Auto" or 42 |
Constant, no UI |
Node Types
GradioNode: Calls a Gradio Space API endpointFnNode: Runs a Python function
Scatter / Gather (Map over lists)
When a node outputs a list and you want to process each item individually, use .each to scatter and .all() to gather:
def generate_script(topic: str) -> list[dict]:
# Returns a list of dialogue lines
return [
{"speaker": "host", "text": "Welcome!"},
{"speaker": "guest", "text": "Thanks for having me!"},
]
script = FnNode(
fn=generate_script,
inputs={"topic": gr.Textbox(label="Topic")},
outputs={"lines": gr.JSON()},
)
def text_to_speech(text: str, speaker: str) -> str:
# Process single item
return f"audio_for_{speaker}.mp3"
# .each["key"] - scatter: run once per item, extracting "key" from each
tts = FnNode(
fn=text_to_speech,
inputs={
"text": script.lines.each["text"], # Each item's "text" field
"speaker": script.lines.each["speaker"], # Each item's "speaker" field
},
outputs={"audio": gr.Audio()},
)
def combine_audio(audio_files: list[str]) -> str:
# Combine all audio files
return "combined.mp3"
# .all() - gather: collect all outputs back into a list
final = FnNode(
fn=combine_audio,
inputs={"audio_files": tts.audio.all()}, # Gathers all audio outputs
outputs={"audio": gr.Audio(label="Final Audio")},
)
graph = Graph(nodes=[script, tts, final])
Visual indicator: Scatter edges show as forked lines (→⟨) and gather edges show as converging lines (⟩→) in the canvas UI.
Full Example: Podcast Generator
import gradio as gr
from daggr import FnNode, GradioNode, Graph
# Generate voice profiles
host_voice = GradioNode(
space_or_url="abidlabs/tts",
api_name="/generate_voice_design",
inputs={
"voice_description": gr.Textbox(label="Host Voice", value="Deep British voice..."),
"language": "Auto",
"text": "Hi! I'm the host.",
},
outputs={"audio": gr.Audio(label="Host Voice")},
)
guest_voice = GradioNode(
space_or_url="abidlabs/tts",
api_name="/generate_voice_design",
inputs={
"voice_description": gr.Textbox(label="Guest Voice", value="Friendly American voice..."),
"language": "Auto",
"text": "Hi! I'm the guest.",
},
outputs={"audio": gr.Audio(label="Guest Voice")},
)
# Generate dialogue (would be an LLM call in production)
def generate_dialogue(topic: str, host_voice: str, guest_voice: str):
dialogue = [
{"voice": host_voice, "text": "Hello, how are you?"},
{"voice": guest_voice, "text": "I'm great, thanks!"},
]
html = "<b>Host:</b> Hello!<br><b>Guest:</b> I'm great!"
return dialogue, html
dialogue = FnNode(
fn=generate_dialogue,
inputs={
"topic": gr.Textbox(label="Topic", value="AI"),
"host_voice": host_voice.audio,
"guest_voice": guest_voice.audio,
},
outputs={
"json": gr.JSON(visible=False),
"html": gr.HTML(label="Script"),
},
)
# Generate audio for each line (scatter)
def text_to_speech(text: str, audio: str) -> str:
return audio # Would call TTS model in production
samples = FnNode(
fn=text_to_speech,
inputs={
"text": dialogue.json.each["text"],
"audio": dialogue.json.each["voice"],
},
outputs={"audio": gr.Audio(label="Sample")},
)
# Combine all audio (gather)
def combine_audio(audio_files: list[str]) -> str:
from pydub import AudioSegment
combined = AudioSegment.empty()
for path in audio_files:
combined += AudioSegment.from_file(path)
combined.export("output.mp3", format="mp3")
return "output.mp3"
final = FnNode(
fn=combine_audio,
inputs={"audio_files": samples.audio.all()},
outputs={"audio": gr.Audio(label="Full Podcast")},
)
graph = Graph(name="Podcast Generator", nodes=[host_voice, guest_voice, dialogue, samples, final])
graph.launch()
Development
pip install -e ".[dev]"
ruff check --fix --select I && ruff format
License
MIT License
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distributions
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file daggr-0.2.1-py3-none-any.whl.
File metadata
- Download URL: daggr-0.2.1-py3-none-any.whl
- Upload date:
- Size: 456.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.2.0 CPython/3.10.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
2d56ce3b9b119a0cb3c63c5bb9f5aaa28c7acb7b98b835a91dc282cb1d70dccc
|
|
| MD5 |
738bb229ac231dba870939b0666b1a1c
|
|
| BLAKE2b-256 |
5d9f86da6b9ea696ecdd8485735c06e4fc754b4932db5757375332a6da4bc235
|
Provenance
The following attestation bundles were made for daggr-0.2.1-py3-none-any.whl:
Publisher:
publish.yml on abidlabs/daggr
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
daggr-0.2.1-py3-none-any.whl -
Subject digest:
2d56ce3b9b119a0cb3c63c5bb9f5aaa28c7acb7b98b835a91dc282cb1d70dccc - Sigstore transparency entry: 849460244
- Sigstore integration time:
-
Permalink:
abidlabs/daggr@c735995c501c4733fdbadc016af848bab9a09e05 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/abidlabs
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@c735995c501c4733fdbadc016af848bab9a09e05 -
Trigger Event:
push
-
Statement type: