Blocks in Legion
Blocks are enhanced functions that provide a foundation for building complex workflows in Legion. They offer built-in features for validation, monitoring, and chain integration.
What are Blocks?
Blocks are decorated functions that provide:
- Input/output validation using Pydantic models
- Execution monitoring and logging
- Async compatibility
- Chain integration
- Tagging for organization
Creating Blocks
Here's how to create and use blocks:
import logging from typing import Any from pydantic import BaseModel, Field from legion import block, chain # Set up logging logging.basicConfig(level=logging.INFO) logger = logging.getLogger(__name__) # Define input/output schemas for type safety class TextInput(BaseModel): text: str = Field(description="Input text to process") class WordCountOutput(BaseModel): word_count: int = Field(description="Number of words in text") char_count: int = Field(description="Number of characters in text") # Create a block using decorator syntax @block( input_schema=TextInput, output_schema=WordCountOutput, tags=["text", "analysis"] ) def count_words(input_data: TextInput) -> WordCountOutput: """Count words and characters in text.""" text = input_data.text words = len(text.split()) chars = len(text) return WordCountOutput(word_count=words, char_count=chars)
Block Features
- Schema Validation: Input and output validation using Pydantic
- Tagging: Organize blocks by functionality with tags
- Monitoring: Track execution and performance
- Chain Integration: Easily combine blocks into processing chains
Using Blocks
Blocks can be used individually or as part of chains:
# Using a block individually input_data = TextInput(text="This is a test sentence.") result = await count_words(input_data) print(f"Word count: {result.word_count}, Character count: {result.char_count}")
Using Blocks in Chains
Blocks can be combined into chains for complex workflows:
# Define another block for sentiment analysis class SentimentOutput(BaseModel): sentiment: str = Field(description="Detected sentiment (positive/negative/neutral)") confidence: float = Field(description="Confidence score of sentiment") @block( input_schema=TextInput, output_schema=SentimentOutput, tags=["text", "sentiment", "nlp"] ) async def analyze_sentiment(input_data: TextInput) -> SentimentOutput: """Analyze sentiment of text.""" # Implementation details... return SentimentOutput(sentiment="positive", confidence=0.8) # Create a chain that combines blocks @chain class TextAnalysisChain: """A chain that analyzes text by counting words and determining sentiment.""" # List the blocks in processing order members = [ count_words, analyze_sentiment ] # Use the chain text_analysis_chain = TextAnalysisChain() chain_result = await text_analysis_chain.aprocess( TextInput(text="This is a great example!") ) print(f"Chain results: {chain_result}")
Best Practices
- Define clear input and output schemas
- Use descriptive tags for organization
- Keep blocks focused on single responsibilities
- Use chains to combine blocks into workflows
- Leverage async for performance-critical operations