feat: add new examples from pocketflow-academy

This commit is contained in:
Alan ALves
2025-03-19 10:31:04 -03:00
parent 84720ceebd
commit 557a14f695
129 changed files with 13455 additions and 0 deletions
@@ -0,0 +1,105 @@
# PocketFlow Parallel Batch Node Example
This example demonstrates parallel processing using AsyncParallelBatchNode to summarize multiple news articles concurrently. It shows how to:
1. Process multiple items in parallel
2. Handle I/O-bound tasks efficiently
3. Manage rate limits with throttling
## What this Example Does
When you run the example:
1. It loads multiple news articles from a data directory
2. Processes them in parallel using AsyncParallelBatchNode
3. For each article:
- Extracts key information
- Generates a summary using an LLM
- Saves the results
4. Combines all summaries into a final report
## How it Works
The example uses AsyncParallelBatchNode to process articles in parallel:
```python
class ParallelSummarizer(AsyncParallelBatchNode):
async def prep_async(self, shared):
# Return list of articles to process
return shared["articles"]
async def exec_async(self, article):
# Process single article (called in parallel)
summary = await call_llm_async(f"Summarize: {article}")
return summary
async def post_async(self, shared, prep_res, summaries):
# Combine all summaries
shared["summaries"] = summaries
return "default"
```
Key features demonstrated:
- Parallel execution of `exec_async`
- Rate limiting with semaphores
- Error handling for failed requests
- Progress tracking for parallel tasks
## Project Structure
```
pocketflow-parallel-batch-node/
├── README.md
├── requirements.txt
├── data/
│ ├── article1.txt
│ ├── article2.txt
│ └── article3.txt
├── main.py
├── flow.py
├── nodes.py
└── utils.py
```
## Running the Example
```bash
# Install dependencies
pip install -r requirements.txt
# Run the example
python main.py
```
## Sample Output
```
Loading articles...
Found 3 articles to process
Processing in parallel...
[1/3] Processing article1.txt...
[2/3] Processing article2.txt...
[3/3] Processing article3.txt...
Summaries generated:
1. First article summary...
2. Second article summary...
3. Third article summary...
Final report saved to: summaries.txt
```
## Key Concepts
1. **Parallel Processing**
- Using AsyncParallelBatchNode for concurrent execution
- Managing parallel tasks efficiently
2. **Rate Limiting**
- Using semaphores to control concurrent requests
- Avoiding API rate limits
3. **Error Handling**
- Graceful handling of failed requests
- Retrying failed tasks
4. **Progress Tracking**
- Monitoring parallel task progress
- Providing user feedback
@@ -0,0 +1 @@
Article 1: AI advances in 2024...
@@ -0,0 +1 @@
Article 2: New quantum computing breakthrough...
@@ -0,0 +1 @@
Article 3: Latest developments in robotics...
@@ -0,0 +1,3 @@
1. Summary of: Article 1: AI advances in 2024...
2. Summary of: Article 2: New quantum computi...
3. Summary of: Article 3: Latest developments...
@@ -0,0 +1,24 @@
"""AsyncFlow implementation for parallel article processing."""
from pocketflow import AsyncFlow, Node
from nodes import LoadArticles, ParallelSummarizer
class NoOp(Node):
"""Node that does nothing, used to properly end the flow."""
pass
def create_flow():
"""Create and connect nodes into a flow."""
# Create nodes
loader = LoadArticles()
summarizer = ParallelSummarizer()
end = NoOp()
# Connect nodes
loader - "process" >> summarizer
summarizer - "default" >> end # Properly end the flow
# Create flow starting with loader
flow = AsyncFlow(start=loader)
return flow
@@ -0,0 +1,19 @@
import asyncio
from flow import create_flow
async def main():
"""Run the parallel processing flow."""
# Create flow
flow = create_flow()
# Create shared store
shared = {}
# Run flow
print("\nParallel Article Summarizer")
print("-------------------------")
await flow.run_async(shared)
if __name__ == "__main__":
# Run the async main function
asyncio.run(main())
@@ -0,0 +1,48 @@
"""AsyncParallelBatchNode implementation for article summarization."""
from pocketflow import AsyncParallelBatchNode, AsyncNode
from utils import call_llm_async, load_articles, save_summaries
class LoadArticles(AsyncNode):
"""Node that loads articles to process."""
async def prep_async(self, shared):
"""Load articles from data directory."""
print("\nLoading articles...")
articles = await load_articles()
return articles
async def exec_async(self, articles):
"""No processing needed."""
return articles
async def post_async(self, shared, prep_res, exec_res):
"""Store articles in shared store."""
shared["articles"] = exec_res
print(f"Found {len(exec_res)} articles to process")
return "process"
class ParallelSummarizer(AsyncParallelBatchNode):
"""Node that summarizes articles in parallel."""
async def prep_async(self, shared):
"""Get articles from shared store."""
print("\nProcessing in parallel...")
return shared["articles"]
async def exec_async(self, article):
"""Summarize a single article (called in parallel)."""
summary = await call_llm_async(article)
return summary
async def post_async(self, shared, prep_res, summaries):
"""Store summaries and save to file."""
shared["summaries"] = summaries
print("\nSummaries generated:")
for i, summary in enumerate(summaries, 1):
print(f"{i}. {summary}")
save_summaries(summaries)
print("\nFinal report saved to: summaries.txt")
return "default"
@@ -0,0 +1,4 @@
pocketflow
aiohttp>=3.8.0 # For async HTTP requests
openai>=1.0.0 # For async LLM calls
tqdm>=4.65.0 # For progress bars
@@ -0,0 +1,53 @@
"""Utility functions for parallel processing."""
import os
import asyncio
import aiohttp
from openai import AsyncOpenAI
from tqdm import tqdm
# Semaphore to limit concurrent API calls
MAX_CONCURRENT_CALLS = 3
semaphore = asyncio.Semaphore(MAX_CONCURRENT_CALLS)
async def call_llm_async(prompt):
"""Make async LLM call with rate limiting."""
async with semaphore: # Limit concurrent calls
print(f"\nProcessing: {prompt[:50]}...")
# Simulate API call with delay
await asyncio.sleep(1)
# Mock LLM response (in real app, would call OpenAI)
summary = f"Summary of: {prompt[:30]}..."
return summary
async def load_articles():
"""Load articles from data directory."""
# For demo, generate mock articles
articles = [
"Article 1: AI advances in 2024...",
"Article 2: New quantum computing breakthrough...",
"Article 3: Latest developments in robotics..."
]
# Create data directory if it doesn't exist
data_dir = "data"
os.makedirs(data_dir, exist_ok=True)
# Save mock articles to files
for i, content in enumerate(articles, 1):
with open(os.path.join(data_dir, f"article{i}.txt"), "w") as f:
f.write(content)
return articles
def save_summaries(summaries):
"""Save summaries to output file."""
# Create data directory if it doesn't exist
data_dir = "data"
os.makedirs(data_dir, exist_ok=True)
with open(os.path.join(data_dir, "summaries.txt"), "w") as f:
for i, summary in enumerate(summaries, 1):
f.write(f"{i}. {summary}\n")