Agentic Architecture Pattern: Partitioning

Written by

in


    """
Pattern 170: Partitioning Pattern with LangChain/LangGraph

Purpose: Divide tasks/data into logical partitions for parallel processing with intelligent strategies.
Different from sharding - partitions can be dynamic and application-specific.
"""

from typing import Dict, Any, List, Optional, Callable, TypedDict
from dataclasses import dataclass, field
from enum import Enum
from langchain_core.prompts import ChatPromptTemplate
from langchain_openai import ChatOpenAI
from langgraph.graph import StateGraph, END
import statistics
import hashlib


class PartitionStrategy(Enum):
    """Partitioning strategies."""
    BY_SIZE = "by_size"
    BY_KEY = "by_key"
    BY_TYPE = "by_type"
    BY_PRIORITY = "by_priority"
    BY_HASH = "by_hash"
    BY_RANGE = "by_range"
    ADAPTIVE = "adaptive"


class LoadBalanceStrategy(Enum):
    """Load balancing strategies."""
    ROUND_ROBIN = "round_robin"
    LEAST_LOADED = "least_loaded"
    WEIGHTED = "weighted"
    RANDOM = "random"


class PartitionHealth(Enum):
    """Partition health status."""
    BALANCED = "balanced"
    SKEWED = "skewed"
    OVERLOADED = "overloaded"
    UNDERUTILIZED = "underutilized"


@dataclass
class Partition:
    """Logical partition of data/tasks with metadata."""
    id: str
    strategy: PartitionStrategy
    criteria: Any
    items: List[Any] = field(default_factory=list)
    metadata: Dict[str, Any] = field(default_factory=dict)
    weight: float = 1.0
    
    def size(self) -> int:
        """Get partition size."""
        return len(self.items)


@dataclass
class PartitionAssignment:
    """Assignment of partition to agent."""
    partition_id: str
    agent_id: str
    assigned_at: float
    weight: float = 1.0


@dataclass
class PartitionMetrics:
    """Metrics for a partition."""
    partition_id: str
    item_count: int
    avg_item_size: float
    load_factor: float
    health: PartitionHealth


@dataclass
class RebalanceResult:
    """Result of rebalancing operation."""
    partitions_moved: int
    items_redistributed: int
    balance_improvement: float
    new_partition_count: int


class PartitioningState(TypedDict):
    """State for partitioning workflow."""
    items: List[Any]
    strategy: str
    partition_count: int
    partitions: List[Dict[str, Any]]
    metrics: Dict[str, Any]
    recommendations: List[str]
    balance_score: float


class IntelligentPartitioner:
    """
    Intelligent partitioner with LangChain/LangGraph integration.
    """
    
    def __init__(self, model_name: str = "gpt-4", temperature: float = 0.3):
        self.llm = ChatOpenAI(model=model_name, temperature=temperature)
        self.partitions: Dict[str, Partition] = {}
        self.assignments: Dict[str, PartitionAssignment] = {}
        self.workflow = self._build_partitioning_workflow()
        
        # Statistics
        self.stats = {
            "total_partitions_created": 0,
            "total_items_partitioned": 0,
            "rebalance_operations": 0,
            "assignments_made": 0,
            "partitions_by_strategy": {strategy: 0 for strategy in PartitionStrategy}
        }
    
    def _build_partitioning_workflow(self) -> StateGraph:
        """Build LangGraph workflow for intelligent partitioning."""
        workflow = StateGraph(PartitioningState)
        
        # Add nodes
        workflow.add_node("analyze_data", self._analyze_data_node)
        workflow.add_node("select_strategy", self._select_strategy_node)
        workflow.add_node("create_partitions", self._create_partitions_node)
        workflow.add_node("evaluate_balance", self._evaluate_balance_node)
        workflow.add_node("provide_recommendations", self._provide_recommendations_node)
        
        # Define edges
        workflow.set_entry_point("analyze_data")
        workflow.add_edge("analyze_data", "select_strategy")
        workflow.add_edge("select_strategy", "create_partitions")
        workflow.add_edge("create_partitions", "evaluate_balance")
        workflow.add_edge("evaluate_balance", "provide_recommendations")
        workflow.add_edge("provide_recommendations", END)
        
        return workflow.compile()
    
    def _analyze_data_node(self, state: PartitioningState) -> PartitioningState:
        """Analyze data characteristics."""
        items = state["items"]
        
        metrics = {
            "total_items": len(items),
            "has_keys": all(isinstance(item, dict) for item in items),
            "item_types": len(set(type(item).__name__ for item in items)),
        }
        
        state["metrics"] = metrics
        return state
    
    def _select_strategy_node(self, state: PartitioningState) -> PartitioningState:
        """Select optimal partitioning strategy using LLM."""
        metrics = state["metrics"]
        requested_strategy = state.get("strategy", "")
        
        if requested_strategy:
            state["strategy"] = requested_strategy
        else:
            # Use LLM to recommend strategy
            prompt = ChatPromptTemplate.from_template(
                "Based on the data characteristics, recommend a partitioning strategy:\n"
                "Total Items: {total_items}\n"
                "Has Dictionary Keys: {has_keys}\n"
                "Item Type Diversity: {item_types}\n\n"
                "Choose from: by_size, by_key, by_type, by_priority, by_hash, by_range, adaptive\n"
                "Provide just the strategy name."
            )
            
            chain = prompt | self.llm
            response = chain.invoke(metrics)
            
            # Extract strategy from response
            strategy_str = str(response.content).lower().strip()
            if "size" in strategy_str:
                state["strategy"] = PartitionStrategy.BY_SIZE.value
            elif "key" in strategy_str:
                state["strategy"] = PartitionStrategy.BY_KEY.value
            elif "hash" in strategy_str:
                state["strategy"] = PartitionStrategy.BY_HASH.value
            else:
                state["strategy"] = PartitionStrategy.ADAPTIVE.value
        
        return state
    
    def _create_partitions_node(self, state: PartitioningState) -> PartitioningState:
        """Create partitions based on selected strategy."""
        items = state["items"]
        strategy = state["strategy"]
        partition_count = state.get("partition_count", 4)
        
        partitions_data = []
        
        if strategy == PartitionStrategy.BY_SIZE.value:
            size = max(1, len(items) // partition_count)
            for i in range(0, len(items), size):
                partitions_data.append({
                    "id": f"size_partition_{len(partitions_data)}",
                    "strategy": strategy,
                    "item_count": min(size, len(items) - i)
                })
        else:
            # Default partitioning
            for i in range(partition_count):
                partitions_data.append({
                    "id": f"partition_{i}",
                    "strategy": strategy,
                    "item_count": 0
                })
        
        state["partitions"] = partitions_data
        return state
    
    def _evaluate_balance_node(self, state: PartitioningState) -> PartitioningState:
        """Evaluate partition balance."""
        partitions = state["partitions"]
        
        if not partitions:
            state["balance_score"] = 0.0
            return state
        
        # Calculate balance score
        sizes = [p["item_count"] for p in partitions]
        if sizes and max(sizes) > 0:
            avg_size = statistics.mean(sizes)
            variance = statistics.variance(sizes) if len(sizes) > 1 else 0
            balance_score = 1.0 / (1.0 + variance / (avg_size ** 2) if avg_size > 0 else 1.0)
        else:
            balance_score = 1.0
        
        state["balance_score"] = balance_score
        return state
    
    def _provide_recommendations_node(self, state: PartitioningState) -> PartitioningState:
        """Provide recommendations for partitioning."""
        balance_score = state.get("balance_score", 0.0)
        partitions = state["partitions"]
        
        recommendations = []
        
        if balance_score < 0.7:
            recommendations.append("Partitions are unbalanced - consider rebalancing")
        
        if len(partitions)  state["metrics"]["total_items"]:
            recommendations.append("Too many partitions - some will be empty")
        
        state["recommendations"] = recommendations
        return state
    
    def partition_by_size(
        self,
        items: List[Any],
        partition_size: int,
        prefix: str = "size"
    ) -> List[Partition]:
        """Partition by fixed size."""
        partitions = []
        
        for i in range(0, len(items), partition_size):
            partition_id = f"{prefix}_partition_{len(partitions)}"
            partition = Partition(
                id=partition_id,
                strategy=PartitionStrategy.BY_SIZE,
                criteria=partition_size,
                items=items[i:i+partition_size]
            )
            partitions.append(partition)
            self.partitions[partition_id] = partition
            self.stats["total_partitions_created"] += 1
        
        self.stats["total_items_partitioned"] += len(items)
        self.stats["partitions_by_strategy"][PartitionStrategy.BY_SIZE] += len(partitions)
        
        return partitions
    
    def partition_by_key(
        self,
        items: List[Dict],
        key: str,
        prefix: str = "key"
    ) -> List[Partition]:
        """Partition by key value."""
        groups: Dict[Any, List[Dict]] = {}
        
        for item in items:
            key_value = item.get(key)
            if key_value not in groups:
                groups[key_value] = []
            groups[key_value].append(item)
        
        partitions = []
        for key_value, group_items in groups.items():
            partition_id = f"{prefix}_{key}_{key_value}"
            partition = Partition(
                id=partition_id,
                strategy=PartitionStrategy.BY_KEY,
                criteria=key_value,
                items=group_items,
                metadata={"key": key, "value": key_value}
            )
            partitions.append(partition)
            self.partitions[partition_id] = partition
            self.stats["total_partitions_created"] += 1
        
        self.stats["total_items_partitioned"] += len(items)
        self.stats["partitions_by_strategy"][PartitionStrategy.BY_KEY] += len(partitions)
        
        return partitions
    
    def partition_by_hash(
        self,
        items: List[Any],
        num_partitions: int,
        hash_func: Optional[Callable[[Any], str]] = None
    ) -> List[Partition]:
        """Partition using consistent hashing."""
        if not hash_func:
            hash_func = lambda x: hashlib.md5(str(x).encode()).hexdigest()
        
        # Initialize partitions
        partition_map: Dict[int, List[Any]] = {i: [] for i in range(num_partitions)}
        
        for item in items:
            hash_value = hash_func(item)
            partition_idx = int(hash_value, 16) % num_partitions
            partition_map[partition_idx].append(item)
        
        partitions = []
        for idx, partition_items in partition_map.items():
            partition_id = f"hash_partition_{idx}"
            partition = Partition(
                id=partition_id,
                strategy=PartitionStrategy.BY_HASH,
                criteria=idx,
                items=partition_items
            )
            partitions.append(partition)
            self.partitions[partition_id] = partition
            self.stats["total_partitions_created"] += 1
        
        self.stats["total_items_partitioned"] += len(items)
        self.stats["partitions_by_strategy"][PartitionStrategy.BY_HASH] += len(partitions)
        
        return partitions
    
    def partition_by_predicate(
        self,
        items: List[Any],
        predicates: Dict[str, Callable[[Any], bool]]
    ) -> List[Partition]:
        """Partition using custom predicates."""
        partitions = []
        
        for name, predicate in predicates.items():
            matching_items = [item for item in items if predicate(item)]
            partition_id = f"predicate_{name}"
            partition = Partition(
                id=partition_id,
                strategy=PartitionStrategy.BY_TYPE,
                criteria=name,
                items=matching_items,
                metadata={"predicate": name}
            )
            partitions.append(partition)
            self.partitions[partition_id] = partition
            self.stats["total_partitions_created"] += 1
        
        self.stats["total_items_partitioned"] += sum(len(p.items) for p in partitions)
        self.stats["partitions_by_strategy"][PartitionStrategy.BY_TYPE] += len(partitions)
        
        return partitions
    
    def intelligent_partition(
        self,
        items: List[Any],
        target_partitions: int = 4
    ) -> Dict[str, Any]:
        """Use workflow to intelligently partition data."""
        initial_state: PartitioningState = {
            "items": items,
            "strategy": "",
            "partition_count": target_partitions,
            "partitions": [],
            "metrics": {},
            "recommendations": [],
            "balance_score": 0.0
        }
        
        # Run workflow
        result = self.workflow.invoke(initial_state)
        
        return {
            "strategy": result["strategy"],
            "partitions": result["partitions"],
            "balance_score": result["balance_score"],
            "recommendations": result["recommendations"]
        }
    
    def assign_partition(
        self,
        partition_id: str,
        agent_id: str,
        weight: float = 1.0
    ) -> PartitionAssignment:
        """Assign partition to agent."""
        import time
        
        assignment = PartitionAssignment(
            partition_id=partition_id,
            agent_id=agent_id,
            assigned_at=time.time(),
            weight=weight
        )
        
        self.assignments[partition_id] = assignment
        self.stats["assignments_made"] += 1
        
        return assignment
    
    def get_agent_workload(self, agent_id: str) -> List[Partition]:
        """Get all partitions assigned to agent."""
        assigned_partition_ids = [
            pid for pid, assignment in self.assignments.items()
            if assignment.agent_id == agent_id
        ]
        
        return [
            self.partitions[pid]
            for pid in assigned_partition_ids
            if pid in self.partitions
        ]
    
    def get_partition_metrics(self, partition_id: str) -> Optional[PartitionMetrics]:
        """Get metrics for a partition."""
        partition = self.partitions.get(partition_id)
        if not partition:
            return None
        
        item_count = len(partition.items)
        avg_item_size = statistics.mean(
            [len(str(item)) for item in partition.items]
        ) if partition.items else 0.0
        
        # Calculate load factor relative to other partitions
        all_sizes = [len(p.items) for p in self.partitions.values()]
        avg_size = statistics.mean(all_sizes) if all_sizes else 0
        load_factor = item_count / avg_size if avg_size > 0 else 1.0
        
        # Determine health
        if load_factor > 1.5:
            health = PartitionHealth.OVERLOADED
        elif load_factor  0.2:
            health = PartitionHealth.SKEWED
        else:
            health = PartitionHealth.BALANCED
        
        return PartitionMetrics(
            partition_id=partition_id,
            item_count=item_count,
            avg_item_size=avg_item_size,
            load_factor=load_factor,
            health=health
        )
    
    def rebalance(
        self,
        target_partition_count: Optional[int] = None
    ) -> RebalanceResult:
        """Rebalance partitions by redistributing items."""
        # Collect all items
        all_items = []
        old_partition_count = len(self.partitions)
        
        for partition in self.partitions.values():
            all_items.extend(partition.items)
        
        # Clear partitions and assignments
        self.partitions.clear()
        self.assignments.clear()
        
        # Determine new partition count
        if target_partition_count is None:
            target_partition_count = max(1, old_partition_count)
        
        # Create new balanced partitions
        items_per_partition = max(1, len(all_items) // target_partition_count)
        new_partitions = self.partition_by_size(all_items, items_per_partition, "rebalanced")
        
        self.stats["rebalance_operations"] += 1
        
        return RebalanceResult(
            partitions_moved=old_partition_count,
            items_redistributed=len(all_items),
            balance_improvement=0.5,  # Simplified
            new_partition_count=len(new_partitions)
        )
    
    def get_statistics(self) -> Dict[str, Any]:
        """Get comprehensive partitioning statistics."""
        partition_sizes = [len(p.items) for p in self.partitions.values()]
        
        return {
            "total_partitions_created": self.stats["total_partitions_created"],
            "active_partitions": len(self.partitions),
            "total_items_partitioned": self.stats["total_items_partitioned"],
            "items_in_partitions": sum(partition_sizes),
            "rebalance_operations": self.stats["rebalance_operations"],
            "assignments_made": self.stats["assignments_made"],
            "active_assignments": len(self.assignments),
            "avg_partition_size": statistics.mean(partition_sizes) if partition_sizes else 0.0,
            "min_partition_size": min(partition_sizes) if partition_sizes else 0,
            "max_partition_size": max(partition_sizes) if partition_sizes else 0,
            "partitions_by_strategy": {
                strategy.value: count
                for strategy, count in self.stats["partitions_by_strategy"].items()
                if count > 0
            }
        }


class PartitioningPattern:
    """
    Comprehensive partitioning pattern coordinator with LangChain/LangGraph.
    """
    
    def __init__(self, model_name: str = "gpt-4"):
        self.partitioner = IntelligentPartitioner(model_name=model_name)
    
    def partition(
        self,
        items: List[Any],
        strategy: PartitionStrategy = PartitionStrategy.BY_SIZE,
        **kwargs
    ) -> List[Partition]:
        """Partition items using specified strategy."""
        if strategy == PartitionStrategy.BY_SIZE:
            size = kwargs.get("size", 10)
            return self.partitioner.partition_by_size(items, size)
        elif strategy == PartitionStrategy.BY_KEY:
            key = kwargs.get("key", "id")
            return self.partitioner.partition_by_key(items, key)
        elif strategy == PartitionStrategy.BY_HASH:
            num_partitions = kwargs.get("num_partitions", 4)
            return self.partitioner.partition_by_hash(items, num_partitions)
        else:
            return self.partitioner.partition_by_size(items, 10)
    
    def smart_partition(
        self,
        items: List[Any],
        target_partitions: int = 4
    ) -> Dict[str, Any]:
        """Use AI to determine optimal partitioning."""
        return self.partitioner.intelligent_partition(items, target_partitions)
    
    def assign(self, partition_id: str, agent_id: str) -> PartitionAssignment:
        """Assign partition to agent."""
        return self.partitioner.assign_partition(partition_id, agent_id)
    
    def get_workload(self, agent_id: str) -> List[Partition]:
        """Get agent's workload."""
        return self.partitioner.get_agent_workload(agent_id)
    
    def rebalance(self, target_count: Optional[int] = None) -> RebalanceResult:
        """Rebalance partitions."""
        return self.partitioner.rebalance(target_count)
    
    def get_statistics(self) -> Dict[str, Any]:
        """Get statistics."""
        return self.partitioner.get_statistics()



if __name__ == "__main__":
    print("=== Partitioning Pattern with LangChain/LangGraph Demo ===\n")
    
    # Note: This demo shows the structure. In production, you'd need:
    # - export OPENAI_API_KEY="your-key-here"
    # - Actual LLM calls would be made
    
    print("Scenario 1: Basic Partitioning Strategies")
    print("-" * 50)
    partitioner = PartitioningPattern(model_name="gpt-4")
    
    # Sample dataset
    items = [
        {"id": i, "type": "A" if i % 2 == 0 else "B", "priority": i % 3, "size": i * 10}
        for i in range(50)
    ]
    
    print(f"Dataset: {len(items)} items")
    
    # Partition by size
    print("\n1. Partitioning by size (10 items per partition):")
    size_partitions = partitioner.partition(items, PartitionStrategy.BY_SIZE, size=10)
    for p in size_partitions[:3]:
        print(f"  {p.id}: {p.size()} items")
    print(f"  ... ({len(size_partitions)} total partitions)")
    
    # Partition by key
    print("\n2. Partitioning by key (type):")
    key_partitions = partitioner.partition(items, PartitionStrategy.BY_KEY, key="type")
    for p in key_partitions:
        print(f"  {p.id}: {p.size()} items (criteria: {p.criteria})")
    
    # Partition by hash
    print("\n3. Partitioning by hash (4 partitions):")
    hash_partitions = partitioner.partition(items, PartitionStrategy.BY_HASH, num_partitions=4)
    for p in hash_partitions:
        print(f"  {p.id}: {p.size()} items")
    
    print("\nScenario 2: Intelligent Partitioning")
    print("-" * 50)
    
    # Use AI to determine optimal strategy
    print("Using LLM to determine optimal partitioning strategy...")
    smart_result = partitioner.smart_partition(items, target_partitions=5)
    
    print(f"\nRecommended Strategy: {smart_result['strategy']}")
    print(f"Balance Score: {smart_result['balance_score']:.2f}")
    print(f"Partitions Created: {len(smart_result['partitions'])}")
    
    if smart_result['recommendations']:
        print("\nRecommendations:")
        for rec in smart_result['recommendations']:
            print(f"  - {rec}")
    
    print("\nScenario 3: Partition Assignment")
    print("-" * 50)
    
    # Assign partitions to agents
    agents = ["agent-001", "agent-002", "agent-003", "agent-004"]
    
    print("Assigning partitions to agents:")
    for i, partition in enumerate(hash_partitions):
        agent_id = agents[i % len(agents)]
        partitioner.assign(partition.id, agent_id)
        print(f"  {partition.id} → {agent_id}")
    
    # Get workload for each agent
    print("\nAgent workloads:")
    for agent_id in agents:
        workload = partitioner.get_workload(agent_id)
        total_items = sum(p.size() for p in workload)
        print(f"  {agent_id}: {len(workload)} partitions, {total_items} items")
    
    print("\nScenario 4: Partition Metrics")
    print("-" * 50)
    
    # Get metrics for partitions
    print("Partition health metrics:")
    for partition in hash_partitions[:3]:
        metrics = partitioner.partitioner.get_partition_metrics(partition.id)
        if metrics:
            print(f"\n  {metrics.partition_id}:")
            print(f"    Item Count: {metrics.item_count}")
            print(f"    Avg Item Size: {metrics.avg_item_size:.1f} chars")
            print(f"    Load Factor: {metrics.load_factor:.2f}")
            print(f"    Health: {metrics.health.value}")
    
    print("\nScenario 5: Dynamic Rebalancing")
    print("-" * 50)
    
    # Create imbalanced partitions
    imbalanced_items = items[:30]
    partitioner2 = PartitioningPattern(model_name="gpt-4")
    
    # Create partitions with different sizes
    partitioner2.partitioner.partition_by_size(imbalanced_items[:5], 5)
    partitioner2.partitioner.partition_by_size(imbalanced_items[5:25], 20)
    
    print(f"Before rebalancing: {len(partitioner2.partitioner.partitions)} partitions")
    
    # Show imbalance
    sizes_before = [p.size() for p in partitioner2.partitioner.partitions.values()]
    print(f"  Partition sizes: {sizes_before}")
    print(f"  Min: {min(sizes_before)}, Max: {max(sizes_before)}, Avg: {statistics.mean(sizes_before):.1f}")
    
    # Rebalance
    print("\nRebalancing partitions...")
    result = partitioner2.rebalance(target_count=4)
    
    print(f"\nRebalance Results:")
    print(f"  Partitions Moved: {result.partitions_moved}")
    print(f"  Items Redistributed: {result.items_redistributed}")
    print(f"  New Partition Count: {result.new_partition_count}")
    
    sizes_after = [p.size() for p in partitioner2.partitioner.partitions.values()]
    print(f"\nAfter rebalancing:")
    print(f"  Partition sizes: {sizes_after}")
    print(f"  Min: {min(sizes_after)}, Max: {max(sizes_after)}, Avg: {statistics.mean(sizes_after):.1f}")
    
    print("\nScenario 6: Custom Predicate Partitioning")
    print("-" * 50)
    
    # Partition by custom predicates
    predicates = {
        "high_priority": lambda x: x.get("priority") == 0,
        "medium_priority": lambda x: x.get("priority") == 1,
        "low_priority": lambda x: x.get("priority") == 2,
        "large_items": lambda x: x.get("size", 0) > 250
    }
    
    print("Partitioning by custom predicates:")
    predicate_partitions = partitioner.partitioner.partition_by_predicate(items, predicates)
    
    for p in predicate_partitions:
        print(f"  {p.metadata.get('predicate', 'unknown')}: {p.size()} items")
    
    print("\nScenario 7: Statistics")
    print("-" * 50)
    
    stats = partitioner.get_statistics()
    print(f"Total Partitions Created: {stats['total_partitions_created']}")
    print(f"Active Partitions: {stats['active_partitions']}")
    print(f"Total Items Partitioned: {stats['total_items_partitioned']}")
    print(f"Items in Partitions: {stats['items_in_partitions']}")
    print(f"Rebalance Operations: {stats['rebalance_operations']}")
    print(f"Assignments Made: {stats['assignments_made']}")
    print(f"Active Assignments: {stats['active_assignments']}")
    
    print(f"\nPartition Size Statistics:")
    print(f"  Average: {stats['avg_partition_size']:.1f} items")
    print(f"  Min: {stats['min_partition_size']} items")
    print(f"  Max: {stats['max_partition_size']} items")
    
    if stats['partitions_by_strategy']:
        print(f"\nPartitions by Strategy:")
        for strategy, count in stats['partitions_by_strategy'].items():
            print(f"  {strategy}: {count}")
    
    print("\n" + "=" * 50)
    print("Partitioning pattern demonstration complete!")
    print("=" * 50)

    

Agentic Architecture Pattern: Partitioning

Pattern 170: 170 Partitioning – Line-by-Line Explanation

Overview

Pattern 170: Partitioning Pattern with LangChain/LangGraph

Purpose: Divide tasks/data into logical partitions for parallel processing with intelligent strategies. Different from sharding – partitions can be dynamic and application-specific.


Code Structure Analysis

File Information

  • Pattern Number: 170
  • Pattern Name: 170 Partitioning
  • File Path: patterns\scaling\170_partitioning.py
  • Total Lines: 741

Detailed Code Explanation

Module Docstring

"""
Pattern 170: Partitioning Pattern with LangChain/LangGraph

Purpose: Divide tasks/data into logical partitions for parallel processing with intelligent strategies.
Different from sharding - partitions can be dynamic and application-specific.
"""

Explanation: This module docstring describes the purpose and key features of the 170 Partitioning pattern.


Imports Section

from typing import Dict, Any, List, Optional, Callable, TypedDict
from dataclasses import dataclass, field
from enum import Enum
from langchain_core.prompts import ChatPromptTemplate
from langchain_openai import ChatOpenAI
from langgraph.graph import StateGraph, END
import statistics
import hashlib
        import time  # First 20 imports shown

Explanation: The module imports necessary dependencies: – from typing import Dict, Any, List, Optional, Callable, TypedDict: Type hints for better code documentation – from dataclasses import dataclass, field: Data class decorators for clean data structures – from enum import Enum: Enumeration support for fixed sets of values – from langchain_core.prompts import ChatPromptTemplate: LangChain framework integration – from langchain_openai import ChatOpenAI: LangChain framework integration – from langgraph.graph import StateGraph, END: LangGraph for workflow orchestration


Enumerations

PartitionStrategy (Line 18)

class PartitionStrategy(Enum):
    BY_SIZE
    BY_KEY
    BY_TYPE
    BY_PRIORITY
    BY_HASH
    BY_RANGE
    ADAPTIVE

Purpose: Partitioning strategies.

Values: – BY_SIZE: Represents by size – BY_KEY: Represents by key – BY_TYPE: Represents by type – BY_PRIORITY: Represents by priority – BY_HASH: Represents by hash – BY_RANGE: Represents by range – ADAPTIVE: Represents adaptive

LoadBalanceStrategy (Line 29)

class LoadBalanceStrategy(Enum):
    ROUND_ROBIN
    LEAST_LOADED
    WEIGHTED
    RANDOM

Purpose: Load balancing strategies.

Values: – ROUND_ROBIN: Represents round robin – LEAST_LOADED: Represents least loaded – WEIGHTED: Represents weighted – RANDOM: Represents random

PartitionHealth (Line 37)

class PartitionHealth(Enum):
    BALANCED
    SKEWED
    OVERLOADED
    UNDERUTILIZED

Purpose: Partition health status.

Values: – BALANCED: Represents balanced – SKEWED: Represents skewed – OVERLOADED: Represents overloaded – UNDERUTILIZED: Represents underutilized


Classes

PartitionStrategy (Line 18)

Description: Partitioning strategies.

LoadBalanceStrategy (Line 29)

Description: Load balancing strategies.

PartitionHealth (Line 37)

Description: Partition health status.

Partition (Line 46)

Description: Logical partition of data/tasks with metadata.

Methods:

size (Line 55)
def size(self):
    """Get partition size."""

Purpose: Get partition size.

Parameters: – self: Instance reference

PartitionAssignment (Line 61)

Description: Assignment of partition to agent.

PartitionMetrics (Line 70)

Description: Metrics for a partition.

RebalanceResult (Line 80)

Description: Result of rebalancing operation.

PartitioningState (Line 88)

Description: State for partitioning workflow.

IntelligentPartitioner (Line 99)

Description: Intelligent partitioner with LangChain/LangGraph integration.

Methods:

__init__ (Line 104)
def __init__(self, model_name, temperature):
    """No method docstring."""

Purpose: No method docstring.

Parameters: – self: Instance reference – model_name: LLM model to use (e.g., “gpt-4”) – temperature: Randomness in LLM responses (0.0-1.0)

_build_partitioning_workflow (Line 119)
def _build_partitioning_workflow(self):
    """Build LangGraph workflow for intelligent partitioning."""

Purpose: Build LangGraph workflow for intelligent partitioning.

Parameters: – self: Instance reference

_analyze_data_node (Line 140)
def _analyze_data_node(self, state):
    """Analyze data characteristics."""

Purpose: Analyze data characteristics.

Parameters: – self: Instance reference – state: Current workflow state

_select_strategy_node (Line 153)
def _select_strategy_node(self, state):
    """Select optimal partitioning strategy using LLM."""

Purpose: Select optimal partitioning strategy using LLM.

Parameters: – self: Instance reference – state: Current workflow state

_create_partitions_node (Line 187)
def _create_partitions_node(self, state):
    """Create partitions based on selected strategy."""

Purpose: Create partitions based on selected strategy.

Parameters: – self: Instance reference – state: Current workflow state

_evaluate_balance_node (Line 215)
def _evaluate_balance_node(self, state):
    """Evaluate partition balance."""

Purpose: Evaluate partition balance.

Parameters: – self: Instance reference – state: Current workflow state

_provide_recommendations_node (Line 235)
def _provide_recommendations_node(self, state):
    """Provide recommendations for partitioning."""

Purpose: Provide recommendations for partitioning.

Parameters: – self: Instance reference – state: Current workflow state

partition_by_size (Line 254)
def partition_by_size(self, items, partition_size, prefix):
    """Partition by fixed size."""

Purpose: Partition by fixed size.

Parameters: – self: Instance reference – items: Parameter for items – partition_size: Parameter for partition_size – prefix: Parameter for prefix

partition_by_key (Line 280)
def partition_by_key(self, items, key, prefix):
    """Partition by key value."""

Purpose: Partition by key value.

Parameters: – self: Instance reference – items: Parameter for items – key: Parameter for key – prefix: Parameter for prefix

partition_by_hash (Line 314)
def partition_by_hash(self, items, num_partitions, hash_func):
    """Partition using consistent hashing."""

Purpose: Partition using consistent hashing.

Parameters: – self: Instance reference – items: Parameter for items – num_partitions: Parameter for num_partitions – hash_func: Parameter for hash_func

partition_by_predicate (Line 350)
def partition_by_predicate(self, items, predicates):
    """Partition using custom predicates."""

Purpose: Partition using custom predicates.

Parameters: – self: Instance reference – items: Parameter for items – predicates: Parameter for predicates

intelligent_partition (Line 377)
def intelligent_partition(self, items, target_partitions):
    """Use workflow to intelligently partition data."""

Purpose: Use workflow to intelligently partition data.

Parameters: – self: Instance reference – items: Parameter for items – target_partitions: Parameter for target_partitions

assign_partition (Line 403)
def assign_partition(self, partition_id, agent_id, weight):
    """Assign partition to agent."""

Purpose: Assign partition to agent.

Parameters: – self: Instance reference – partition_id: Parameter for partition_id – agent_id: Parameter for agent_id – weight: Parameter for weight

get_agent_workload (Line 424)
def get_agent_workload(self, agent_id):
    """Get all partitions assigned to agent."""

Purpose: Get all partitions assigned to agent.

Parameters: – self: Instance reference – agent_id: Parameter for agent_id

get_partition_metrics (Line 437)
def get_partition_metrics(self, partition_id):
    """Get metrics for a partition."""

Purpose: Get metrics for a partition.

Parameters: – self: Instance reference – partition_id: Parameter for partition_id

…and 2 more methods

PartitioningPattern (Line 527)

Description: Comprehensive partitioning pattern coordinator with LangChain/LangGraph.

Methods:

__init__ (Line 532)
def __init__(self, model_name):
    """No method docstring."""

Purpose: No method docstring.

Parameters: – self: Instance reference – model_name: LLM model to use (e.g., “gpt-4”)

partition (Line 535)
def partition(self, items, strategy):
    """Partition items using specified strategy."""

Purpose: Partition items using specified strategy.

Parameters: – self: Instance reference – items: Parameter for items – strategy: Parameter for strategy

smart_partition (Line 554)
def smart_partition(self, items, target_partitions):
    """Use AI to determine optimal partitioning."""

Purpose: Use AI to determine optimal partitioning.

Parameters: – self: Instance reference – items: Parameter for items – target_partitions: Parameter for target_partitions

assign (Line 562)
def assign(self, partition_id, agent_id):
    """Assign partition to agent."""

Purpose: Assign partition to agent.

Parameters: – self: Instance reference – partition_id: Parameter for partition_id – agent_id: Parameter for agent_id

get_workload (Line 566)
def get_workload(self, agent_id):
    """Get agent's workload."""

Purpose: Get agent’s workload.

Parameters: – self: Instance reference – agent_id: Parameter for agent_id

rebalance (Line 570)
def rebalance(self, target_count):
    """Rebalance partitions."""

Purpose: Rebalance partitions.

Parameters: – self: Instance reference – target_count: Parameter for target_count

get_statistics (Line 574)
def get_statistics(self):
    """Get statistics."""

Purpose: Get statistics.

Parameters: – self: Instance reference


Design Patterns Used

  • Strategy Pattern: Different execution strategies for various scenarios
  • State Pattern: Managing different states in the workflow
  • Chain of Responsibility: Sequential processing through workflow nodes
  • Observer Pattern: Tracking and monitoring pattern execution
  • Factory Pattern: Creating instances with proper configuration

Usage Example

# Initialize the pattern
pattern = PartitionStrategy(name="example", model_name="gpt-4")

# Configure and execute
result = pattern.execute(data={"key": "value"})

# Check results
print(result)

Key Concepts

  • LLM Integration: Uses LangChain/OpenAI for intelligent decision-making
  • Workflow Orchestration: LangGraph manages execution flow through nodes
  • State Management: TypedDict defines workflow state structure
  • Error Handling: Comprehensive error handling and fallback mechanisms
  • Extensibility: Designed for easy customization and extension

Extension Points

This pattern can be extended by: 1. Adding custom workflow nodes 2. Implementing additional validation logic 3. Integrating with external services 4. Customizing LLM prompts and responses 5. Adding monitoring and logging capabilities

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *