Generator Pattern¶
DeepFabric uses a Generator Pattern to provide clean separation between core logic and user interface concerns, enabling flexible integration into different applications and workflows.
How Generators Work¶
The generator pattern allows DeepFabric's core components to yield events during processing, letting you handle progress monitoring, logging, and user interface updates as needed:
import asyncio
async def build_tree(tree):
async for event in tree.build_async(): # Core yields events, caller handles UI
if event['event'] == 'build_complete':
print(f"Done! {event['total_paths']} paths")
asyncio.run(build_tree(tree))
This approach enables using DeepFabric as a library without any UI dependencies:
import asyncio
from deepfabric import Tree, Graph, DataSetGenerator
# Silent usage - just consume the async generator
tree = Tree(topic_prompt="AI Ethics", provider="ollama", model_name="qwen3:8b")
async def build_silently() -> None:
async for _ in tree.build_async():
pass
asyncio.run(build_silently())
tree.save("ai_ethics.jsonl")
Custom Progress Monitoring¶
Create your own progress handling:
import asyncio
import logging
async def build_with_logging(tree):
"""Build tree with custom logging."""
logger = logging.getLogger(__name__)
async for event in tree.build_async():
if event['event'] == 'subtopics_generated':
logger.info(f"Generated {event['count']} subtopics")
elif event['event'] == 'build_complete':
logger.info(f"Build complete: {event['total_paths']} paths")
async def build_with_metrics(graph):
"""Build graph with metrics collection."""
metrics = {'nodes_created': 0, 'failures': 0}
async for event in graph.build_async():
if event['event'] == 'node_expanded':
metrics['nodes_created'] += event['subtopics_added']
elif event['event'] == 'build_complete':
metrics['failures'] = event.get('failed_generations', 0)
return metrics
# Execute helpers (assuming `tree` and `graph` have been created earlier)
asyncio.run(build_with_logging(tree))
metrics = asyncio.run(build_with_metrics(graph))
print(metrics)
Easy Testing¶
Test core logic without mocking UI:
import asyncio
async def collect_events(tree):
return [event async for event in tree.build_async()]
async def test_tree_generation():
tree = Tree(topic_prompt="Test", provider="ollama", model_name="test")
# Collect all events
events = await collect_events(tree)
# Assert on specific events
start_events = [e for e in events if e['event'] == 'build_start']
assert len(start_events) == 1
complete_events = [e for e in events if e['event'] == 'build_complete']
assert len(complete_events) == 1
assert complete_events[0]['total_paths'] > 0
asyncio.run(test_tree_generation())
Event Types¶
Tree Events¶
| Event Type | Description | Key Fields |
|---|---|---|
build_start |
Build initialization | model_name, depth, degree |
subtree_start |
Beginning subtree generation | node_path, depth |
subtopics_generated |
Subtopic generation result | parent_path, count, success |
leaf_reached |
Path reached maximum depth | path |
build_complete |
Build finished | total_paths, failed_generations |
error |
Build error occurred | error |
Graph Events¶
| Event Type | Description | Key Fields |
|---|---|---|
depth_start |
Beginning depth level | depth, leaf_count |
node_expanded |
Node expansion completed | node_topic, subtopics_added, connections_added |
depth_complete |
Depth level finished | depth |
build_complete |
Graph construction finished | nodes_count, failed_generations |
error |
Build error occurred | error |
Usage Patterns¶
Pattern 1: Silent Consumption¶
import asyncio
async def build_all() -> None:
async for _ in tree.build_async():
pass
async for _ in graph.build_async():
pass
asyncio.run(build_all())
Pattern 2: Progress Monitoring¶
import asyncio
async def monitor_tree() -> None:
async for event in tree.build_async():
if event['event'] == 'build_complete':
print(f"✅ Complete: {event['total_paths']} paths")
asyncio.run(monitor_tree())
Pattern 3: Event Collection¶
import asyncio
async def collect_graph_events():
return [event async for event in graph.build_async()]
async def analyze_graph():
events = await collect_graph_events()
failed_count = sum(1 for e in events if e['event'] == 'error')
node_expansions = [e for e in events if e['event'] == 'node_expanded']
return events, failed_count, node_expansions
EVENTS, FAILED_COUNT, NODE_EXPANSIONS = asyncio.run(analyze_graph())
Pattern 4: Real-time Streaming¶
import asyncio
async def process_build_events(generator):
async for event in generator:
# Send to monitoring system
metrics_client.send_event(event)
# Log important events
if event['event'] in ['error', 'build_complete']:
logger.info(f"Build event: {event}")
asyncio.run(process_build_events(tree.build_async()))
CLI Integration¶
The CLI uses adapter functions to bridge generators to TUI components:
# cli.py - Adapts generator events to TUI
import asyncio
async def handle_tree_events_async(tree, show_progress=True):
if show_progress:
tui = get_tree_tui()
async for event in tree.build_async():
if show_progress:
if event['event'] == 'build_start':
tui.start_building(event['model_name'], event['depth'], event['degree'])
elif event['event'] == 'build_complete':
tui.finish_building(event['total_paths'], event['failed_generations'])
return event # Return final event
# Synchronous entry point for CLI commands
def handle_tree_events(tree, show_progress=True):
return asyncio.run(handle_tree_events_async(tree, show_progress=show_progress))
This approach maintains clean separation between core logic and user interface concerns while providing rich interactive experiences when needed.