Built-in Routines¶
Routilux provides a comprehensive set of built-in routines that are generic and reusable across different business domains. These routines are organized by category and can be easily integrated into your workflows.
Overview¶
All built-in routines inherit from the Routine base class and provide:
Input Data Extraction: Automatic handling of various input patterns using
_extract_input_data()Operation Tracking: Consistent statistics tracking using
_track_operation()Configuration Management: Flexible configuration via
_configdictionaryError Handling: Robust error handling and recovery mechanisms
Each routine package is self-contained with its own tests and documentation, making it easy to copy and use in other projects.
Text Processing Routines¶
Text processing routines handle text manipulation, formatting, and extraction.
TextClipper¶
Clips text to a maximum length while preserving important information like tracebacks.
Features: * Preserves tracebacks completely * Clips text line by line * Provides informative truncation messages * Configurable maximum length
Usage:
from routilux.builtin_routines.text_processing import TextClipper
from routilux import Flow
clipper = TextClipper()
clipper.set_config(max_length=1000, preserve_tracebacks=True)
flow = Flow()
flow.add_routine(clipper, "clipper")
Configuration:
* max_length (int): Maximum text length (default: 1000)
* preserve_tracebacks (bool): Whether to preserve tracebacks (default: True)
* truncation_message (str): Message to append when text is clipped
Input:
* text (str): Text to clip
Output:
* clipped_text (str): Clipped text
* was_clipped (bool): Whether text was clipped
* original_length (int): Original text length
TextRenderer¶
Renders objects into formatted text with XML-like tags or markdown format.
Features: * Renders dictionaries, lists, and nested structures * Supports XML and markdown formats * Configurable indentation * Handles circular references safely
Usage:
from routilux.builtin_routines.text_processing import TextRenderer
renderer = TextRenderer()
renderer.set_config(tag_format="xml", indent=" ")
flow = Flow()
flow.add_routine(renderer, "renderer")
Configuration:
* tag_format (str): Format type - “xml” or “markdown” (default: “xml”)
* indent (str): Indentation string (default: “ “)
Input:
* data (Any): Data to render
Output:
* rendered_text (str): Formatted text
* original_type (str): Original data type
ResultExtractor¶
Extracts and formats results from various output formats with extensible architecture.
Features: * Extracts JSON from code blocks and plain strings * Extracts code blocks of various languages * Formats interpreter output * Supports custom extractors * Multiple extraction strategies
Usage:
from routilux.builtin_routines.text_processing import ResultExtractor
extractor = ResultExtractor()
extractor.set_config(
strategy="auto",
extract_json_blocks=True,
extract_code_blocks=True
)
flow = Flow()
flow.add_routine(extractor, "extractor")
Configuration:
* strategy (str): Extraction strategy - “auto”, “first_match”, “all”, “priority” (default: “auto”)
* extract_json_blocks (bool): Extract JSON from code blocks (default: True)
* extract_code_blocks (bool): Extract code blocks (default: True)
* format_interpreter_output (bool): Format interpreter output (default: True)
* continue_on_error (bool): Continue on extraction errors (default: True)
* return_original_on_failure (bool): Return original data on failure (default: True)
Input:
* data (Any): Data to extract from
Output:
* extracted_result (Any): Extracted result
* format (str): Detected format type
* metadata (dict): Extraction metadata
* confidence (float): Confidence score (0.0-1.0)
* extraction_path (list): Path of extraction methods used
Utility Routines¶
Utility routines provide general-purpose functionality for common operations.
TimeProvider¶
Provides current time in various formats (ISO, formatted, timestamp, custom).
Usage:
from routilux.builtin_routines.utils import TimeProvider
from routilux import Flow
time_provider = TimeProvider()
time_provider.set_config(format="iso", locale="zh_CN")
flow = Flow()
flow.add_routine(time_provider, "time_provider")
Configuration:
* format (str): Format type - “iso”, “formatted”, “timestamp”, “custom” (default: “iso”)
* locale (str): Locale for formatted output (default: “zh_CN”)
* custom_format (str): Custom format string for “custom” format (default: “%Y-%m-%d %H:%M:%S”)
* include_weekday (bool): Include weekday in formatted output (default: True)
Input:
* Trigger via trigger_slot.receive({})
Output:
* time_string (str): Formatted time string
* timestamp (float): Unix timestamp
* datetime (str): ISO datetime string
* formatted (str): Custom formatted string
DataFlattener¶
Flattens nested data structures into flat dictionaries.
Usage:
from routilux.builtin_routines.utils import DataFlattener
flattener = DataFlattener()
flattener.set_config(separator=".", max_depth=100, preserve_lists=True)
flow = Flow()
flow.add_routine(flattener, "flattener")
Configuration:
* separator (str): Key separator for nested keys (default: “.”)
* max_depth (int): Maximum nesting depth (default: 100)
* preserve_lists (bool): Preserve list structures (default: True)
Input:
* data (Any): Data to flatten
Output:
* flattened_data (dict): Flattened dictionary
* original_type (str): Original data type
* depth (int): Maximum depth reached
Data Processing Routines¶
Data processing routines handle data transformation, validation, and manipulation.
DataTransformer¶
Transforms data using configurable transformation functions.
Usage:
from routilux.builtin_routines.data_processing import DataTransformer
transformer = DataTransformer()
transformer.set_config(transformations=["lowercase", "strip"])
flow = Flow()
flow.add_routine(transformer, "transformer")
Configuration:
* transformations (list): List of transformation names to apply
* transformation_map (dict): Custom transformation functions
Built-in Transformations:
* lowercase: Convert string to lowercase
* uppercase: Convert string to uppercase
* strip: Strip whitespace
* to_string: Convert to string
* to_int: Convert to integer
* to_float: Convert to float
* remove_none: Remove None values from dict
Input:
* data (Any): Data to transform
* transformations (list, optional): Override config transformations
Output:
* transformed_data (Any): Transformed data
* transformation_applied (list): List of transformations applied
* errors (list): List of errors if any
DataValidator¶
Validates data against schemas or validation rules.
Usage:
from routilux.builtin_routines.data_processing import DataValidator
validator = DataValidator()
validator.set_config(
rules={"age": "is_int", "name": "is_string"},
required_fields=["name", "age"],
strict_mode=False
)
flow = Flow()
flow.add_routine(validator, "validator")
Configuration:
* rules (dict): Validation rules mapping field names to validators
* required_fields (list): List of required field names
* strict_mode (bool): Reject extra fields not in rules (default: False)
* allow_extra_fields (bool): Allow extra fields (default: True)
Built-in Validators:
* is_string: Check if value is string
* is_int: Check if value is integer
* is_float: Check if value is float
* is_dict: Check if value is dictionary
* is_list: Check if value is list
* not_empty: Check if value is not empty
* is_positive: Check if value is positive number
* is_non_negative: Check if value is non-negative number
Input:
* data (Any): Data to validate
* rules (dict, optional): Override config rules
Output:
Emits to
validevent if valid:validated_data(Any): Validated data
Emits to
invalidevent if invalid:errors(list): List of validation errorsdata(Any): Original data
Control Flow Routines¶
Control flow routines handle flow control, routing, and conditional execution.
ConditionalRouter¶
Routes data to different outputs based on conditions.
Usage:
String expressions (recommended):
from routilux.builtin_routines.control_flow import ConditionalRouter
from routilux import Flow
router = ConditionalRouter()
router.set_config(
routes=[
("high", "data.get('priority') == 'high'"),
("low", "isinstance(data, dict) and data.get('priority') == 'low'"),
],
default_route="normal"
)
flow = Flow()
flow.add_routine(router, "router")
# Use router.input_slot.receive({"data": {...}}) to route data
Dictionary conditions:
router = ConditionalRouter()
router.set_config(
routes=[
("high", {"priority": "high"}),
("low", {"priority": "low"}),
]
)
Configuration:
* routes (list): List of (route_name, condition) tuples. Condition can be:
String expression (recommended):
"data.get('priority') == 'high'"- Fully serializable. Can accessdata,config(routine’s_config), andstats(routine’s_stats). Example:"data.get('value', 0) > config.get('threshold', 0)"Function reference: A callable function - Serializable if function is in a module. Can accept
data,config, andstatsas parameters.Dictionary: Field matching condition - Fully serializable. Example:
{"priority": "high"}Lambda function:
lambda x: x.get('priority') == 'high'- Can be used at runtime. May be converted to string expression during serialization if source code is available. Can access external variables via closure, but closure variables are lost during serialization.
default_route(str): Default route name if no condition matchesroute_priority(str): Priority strategy - “first_match” or “all_matches” (default: “first_match”)
Input:
* data (Any): Data to route
Output:
* Emits to route event (e.g., “high”, “low”, “normal”)
* data (Any): Original data
* route (str): Route name that matched
Serialization:
ConditionalRouter supports full serialization/deserialization. All condition types have been tested and verified:
✅ String expressions:
"data.get('priority') == 'high'"- Always serializable, can access config and stats✅ Dictionary conditions:
{"priority": "high"}- Always serializable✅ Module-level functions: Functions defined at module level are fully serializable and can accept
data,config, andstatsparameters⚠️ Lambda functions: Can be used at runtime. Simple lambdas may be converted to string expressions during serialization (if source code is available via
inspect.getsource()). Closure variables are lost during serialization, so prefer string expressions for config/stats access.
Serialization Testing:
All condition types have been thoroughly tested: * ✅ Lambda conditions: Serialize/deserialize successfully (converted to string expressions) * ✅ Function conditions: Serialize/deserialize successfully (module-level functions) * ✅ Flow-level serialization: Works with both lambda and function conditions * ✅ JSON roundtrip: Both lambda and function conditions work through JSON serialization
Accessing Config and Stats in Conditions:
String expressions can access the routine’s configuration and statistics:
router = ConditionalRouter()
router.set_config(
routes=[
# Access config
("high", "data.get('value', 0) > config.get('threshold', 0)"),
# Access stats
("active", "stats.get('count', 0) < 10"),
],
threshold=10
)
router.set_stat("count", 5)
router.input_slot.receive({"data": {"value": 15}}) # Routes to "high"
Examples:
String expression (recommended):
router = ConditionalRouter()
router.set_config(
routes=[
("high", "data.get('priority') == 'high'"),
("low", "isinstance(data, dict) and data.get('priority') == 'low'"),
],
default_route="normal"
)
Dictionary condition:
router = ConditionalRouter()
router.set_config(
routes=[
("high", {"priority": "high"}),
("low", {"priority": "low"}),
]
)
Code Quality and Testing¶
All built-in routines have been reviewed and improved to follow Python best practices:
Circular Reference Protection: Routines that process recursive data structures (TextRenderer, DataFlattener) include protection against circular references
Timezone Handling: TimeProvider uses proper timezone-aware timestamp conversion
Type Hints: Complete type annotations for better IDE support and static analysis
Error Handling: Comprehensive error handling and recovery mechanisms
Test Coverage: 100% test coverage with 104 test cases covering all functionality
Test Coverage¶
All built-in routines have comprehensive test coverage:
Text Processing: 23 tests
Utils: 9 tests
Data Processing: 9 tests
Control Flow: 9 tests
Integration Tests: 2 tests
Total: 104 test cases, all passing ✅
Each routine package includes its own test directory for easy maintenance and standalone usage.