agent-rails


Nameagent-rails JSON
Version 0.1.1 PyPI version JSON
download
home_pageNone
Summaryput your agents on guardrails
upload_time2025-08-29 15:25:39
maintainerNone
docs_urlNone
authorNone
requires_python>=3.11
licenseMIT
keywords ai agents lifecycle management
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            # Rails | Put your agents on rails

**Production-grade lifecycle management for AI agents - inject context and execute workflows when conditions are met**

Rails provides a comprehensive, framework-agnostic system for surgical control over AI agent behavior. Inject messages, execute workflows, compose lifecycle functions, and orchestrate complex patterns - all triggered by your custom conditions. No forced lifecycle phases, maximum flexibility.

```bash
pip install agent-rails
# or
pdm add agent-rails
```

**Built by: [Rizome Labs](https://rizome.dev) | Contact: [hi@rizome.dev](mailto:hi@rizome.dev)**

## Quick Start

```python
import asyncio
from rails import Rails, current_rails, lifecycle_function

# Define a tool that accesses Rails
def api_tool(data):
    rails = current_rails()  # Access Rails from within tools
    rails.store.increment_sync('api_calls')
    return {"processed": True, "calls": rails.store.get_counter_sync('api_calls')}

# Define composable lifecycle function
@lifecycle_function(priority=10)
async def setup_monitoring(rails):
    rails.store.set_sync('monitoring_active', True)
    print("🔧 Monitoring started")
    yield  # Main execution happens here
    print("🔧 Monitoring stopped")

# Define workflow function
async def error_recovery(rails):
    print("🔄 Running error recovery workflow")
    rails.store.set_counter_sync('errors', 0)

async def main():
    # Create Rails with lifecycle functions
    rails = Rails().with_lifecycle(setup_monitoring)
    
    # Message injection: When errors occur, inject helpful message
    rails.when(lambda s: s.get_counter_sync('errors') >= 2).inject({
        "role": "system", 
        "content": "Multiple errors detected. Switching to recovery mode."
    })
    
    # Workflow execution: When API calls exceed limit, run background optimization
    rails.when(lambda s: s.get_counter_sync('api_calls') >= 5).then(
        lambda r: print("⚡ Running optimization..."), background=True
    )
    
    # Error recovery workflow
    rails.when(lambda s: s.get_counter_sync('errors') >= 3).then(error_recovery)
    
    # Use Rails with automatic lifecycle management
    async with rails:
        messages = [{"role": "user", "content": "Help me process data"}]
        
        # Simulate tool calls and errors
        for i in range(6):
            result = api_tool({"item": i})
            if i > 3: rails.store.increment_sync('errors')
            
            # Check conditions - both inject messages AND execute workflows
            messages = await rails.check(messages)
            
            # Display any Rails messages
            for msg in messages:
                if "system" in msg.get("role", ""):
                    print(f"💬 Rails: {msg['content']}")

asyncio.run(main())
```

# Documentation

## Core Features

### 1. Message Injection - `when().inject()`

Conditionally inject messages into agent conversations:

```python
from rails import Rails, CounterCondition, StateCondition

rails = Rails()

# Lambda conditions for custom logic
rails.when(lambda s: s.get_counter_sync('errors') >= 3).inject({
    "role": "system",
    "content": "Multiple errors detected. Switching to recovery mode."
})

# Built-in condition types for common patterns
rails.when(CounterCondition('attempts', 5, '>=')).inject(retry_message)
rails.when(StateCondition('mode', 'expert')).inject(expert_guidance)

# Multiple injection strategies
rails.when(condition).inject(message)  # append (default)
rails.when(condition).inject(message, strategy='prepend')  # add to start
rails.when(condition).inject(message, strategy='replace_last')  # replace last

# Convenience methods
rails.on_counter('turns', 10, stop_message)
rails.on_state('debug_mode', True, debug_message)

# Apply all conditions and get enhanced messages
enhanced_messages = await rails.check(original_messages)
```

### 2. Workflow Execution - `when().then()`

Execute functions and workflows when conditions are met:

```python
from rails import Rails

rails = Rails()

# Execute function when condition met
async def error_recovery(rails):
    print("Running error recovery...")
    rails.store.set_counter_sync('errors', 0)
    
rails.when(lambda s: s.get_counter_sync('errors') >= 3).then(error_recovery)

# Execute in background (non-blocking)
rails.when(condition).then(
    optimization_workflow, 
    background=True,  # runs asynchronously
    name="background_optimizer"
)

# Pass additional arguments
rails.when(condition).then(
    custom_workflow, 
    arg1, arg2,  # positional args
    param1="value",  # keyword args
    background=False
)

# Lambda functions for simple workflows
rails.when(condition).then(lambda r: r.store.set('recovery_mode', True))
```

### 3. Composable Lifecycle Functions - `@lifecycle_function`

Create modular, reusable lifecycle components:

```python
from rails import Rails, lifecycle_function

@lifecycle_function(name="database", priority=10)
async def database_lifecycle(rails):
    # Setup phase
    connection = await create_db_connection()
    rails.store.set_sync('db_connection', connection)
    print("🔧 Database connected")
    
    yield  # Main execution happens here
    
    # Cleanup phase
    await connection.close()
    print("🔧 Database disconnected")

@lifecycle_function(name="monitoring", priority=5)
async def monitoring_lifecycle(rails):
    # Setup monitoring
    rails.store.set_sync('monitoring_active', True)
    
    # Add conditional rules during setup
    rails.when(lambda s: s.get_counter_sync('errors') >= 5).inject({
        "role": "system",
        "content": "🚨 High error rate detected!"
    })
    
    yield
    
    # Cleanup monitoring
    rails.store.set_sync('monitoring_active', False)

# Compose multiple lifecycle functions
rails = Rails().with_lifecycle('database', 'monitoring', custom_lifecycle)

# Or compose with function references
rails = Rails().with_lifecycle(database_lifecycle, monitoring_lifecycle)
```

### 4. Global Tool Access - `current_rails()`

Tools can access and manipulate the Rails instance they're running within:

```python
from rails import current_rails

def api_client_tool(endpoint, data):
    """Tool that tracks usage and adds conditional behaviors."""
    rails = current_rails()  # Access the active Rails instance
    
    # Track API usage
    rails.store.increment_sync('api_calls')
    rails.store.increment_sync(f'api_calls_{endpoint}')
    
    # Tool can add conditional rules based on its state
    if rails.store.get_counter_sync('api_calls') >= 8:
        rails.when(lambda s: s.get_counter_sync('api_calls') >= 10).inject({
            "role": "system",
            "content": "⚠️ API rate limit approaching. Consider throttling."
        })
    
    # Simulate API call
    result = call_external_api(endpoint, data)
    
    # Track errors
    if not result.get('success'):
        rails.store.increment_sync('api_errors')
    
    return result

# Tools automatically access Rails when called within Rails context
async with Rails() as rails:
    result = api_client_tool('process', {'data': 'value'})  # Works seamlessly
```

## Advanced Features

### 5. Background Execution & Orchestration

Execute complex workflows with proper concurrency and orchestration:

```python
from rails import Rails, WorkflowOrchestrator, execute_background_workflow

async def complex_data_processing(rails):
    # Long-running data processing
    await process_large_dataset()
    return {"processed": True}

async def quick_validation(rails):  
    # Quick validation task
    return {"valid": True}

# Background execution
rails = Rails()

async with rails:
    # Execute single workflow in background
    task_id = await execute_background_workflow(
        complex_data_processing, 
        rails_instance=rails,
        task_id="data_proc_001"
    )
    
    # Continue other work while background task runs
    messages = await rails.check(messages)
    
    # Advanced orchestration
    orchestrator = WorkflowOrchestrator(rails, max_concurrent=3)
    
    async with orchestrator.orchestration_context():
        # Conditional pipeline - steps run based on conditions
        pipeline_steps = [
            (lambda s: True, initialize_system),
            (lambda s: s.get_counter_sync('items') > 0, process_items),
            (lambda s: s.get_sync('validation_required'), validate_results),
            (lambda s: True, finalize_results)
        ]
        
        pipeline_results = await orchestrator.execute_conditional_pipeline(pipeline_steps)
        
        # Parallel execution
        parallel_workflows = [complex_data_processing, quick_validation, cleanup_temp_data]
        parallel_results = await orchestrator.execute_parallel_workflows(
            parallel_workflows, 
            wait_all=True
        )
```

### 6. Built-in Condition Types

Rails provides powerful condition primitives:

```python
from rails import (
    CounterCondition, StateCondition, LambdaCondition,
    AndCondition, OrCondition, NotCondition
)

# Counter conditions with operators
rails.when(CounterCondition('api_calls', 10, '>=')).inject(rate_limit_msg)
rails.when(CounterCondition('errors', 0, '==')).inject(success_msg)

# State conditions
rails.when(StateCondition('mode', 'production')).inject(prod_warning)
rails.when(StateCondition('user_tier', 'premium')).inject(premium_features)

# Logical combinations
complex_condition = AndCondition([
    CounterCondition('attempts', 3, '>='),
    StateCondition('auto_retry', True)
])
rails.when(complex_condition).then(auto_retry_workflow)

# Custom logic with lambda conditions
rails.when(LambdaCondition(lambda s: 
    s.get_counter_sync('success_rate') / s.get_counter_sync('total_attempts') < 0.5
)).inject(low_success_warning)
```

## Framework Integration

### 7. Framework Adapters

Rails includes adapters for seamless integration with popular agent frameworks:

```python
from rails.adapters import create_adapter, BaseRailsAdapter

# Generic adapter with any processing function
def my_agent_processor(messages):
    # Your agent processing logic here
    return {"role": "assistant", "content": "Processed with Rails!"}

adapter = create_adapter(rails, my_agent_processor)

# Context manager handles Rails lifecycle automatically
async with adapter as active_adapter:
    result = await active_adapter.run(messages)
```

#### LangChain Integration

```python
from rails.adapters import LangChainAdapter
from langchain.chat_models import ChatOpenAI
from langchain.schema import HumanMessage

# Set up Rails with conditions
rails = Rails()
rails.when(lambda s: s.get_counter_sync('messages') >= 3).inject({
    "role": "system",
    "content": "This conversation is getting long. Let me summarize..."
})

# Create LangChain adapter
llm = ChatOpenAI(model="gpt-4")
adapter = LangChainAdapter(rails, llm)

# Rails automatically applies before LangChain processing
messages = [HumanMessage(content="Help me debug this code")]
result = await adapter.run(messages)
```

#### SmolAgents Integration

```python
from rails.adapters import SmolAgentsAdapter
from smolagents import Agent

# Set up Rails with agent-specific conditions
rails = Rails()
rails.when(lambda s: s.get_counter_sync('tool_calls') >= 5).inject({
    "role": "system", 
    "content": "I notice I'm using many tools. Let me focus on the core task."
})

# Create SmolAgents adapter  
agent = Agent(model="gpt-4", tools=[web_search, calculator])
adapter = SmolAgentsAdapter(rails, agent)

result = await adapter.run("Research the latest AI developments and calculate ROI")
```

#### Custom Framework Adapter

```python
from rails.adapters import BaseRailsAdapter

class MyFrameworkAdapter(BaseRailsAdapter):
    def __init__(self, rails, my_agent):
        super().__init__(rails)
        self.agent = my_agent
    
    async def process_messages(self, messages, **kwargs):
        # Convert Rails messages to your framework format
        framework_messages = self.convert_messages(messages)
        
        # Process with your framework
        result = await self.agent.process(framework_messages, **kwargs)
        
        # Update Rails state based on result
        await self.rails.store.increment('framework_calls')
        
        return result
    
    async def update_rails_state(self, original_messages, modified_messages, result):
        # Custom state updates beyond the default
        await super().update_rails_state(original_messages, modified_messages, result)
        
        # Track framework-specific metrics
        if result.get('tool_used'):
            await self.rails.store.increment('tool_usage')

# Usage
adapter = MyFrameworkAdapter(rails, my_custom_agent)
result = await adapter.run(messages)
```

### 8. State Management

Rails provides comprehensive thread-safe state management:

```python
from rails import Rails

async with Rails() as rails:
    # COUNTERS - for tracking numeric values
    rails.store.increment_sync('api_calls')  # increment by 1
    rails.store.increment_sync('errors', 5)  # increment by custom amount
    rails.store.set_counter_sync('retries', 0)  # set to specific value
    
    current_calls = rails.store.get_counter_sync('api_calls', default=0)
    has_errors = rails.store.get_counter_sync('errors') > 0
    
    # STATE VALUES - for storing arbitrary data  
    rails.store.set_sync('user_tier', 'premium')
    rails.store.set_sync('config', {'debug': True, 'retries': 3})
    rails.store.set_sync('last_error', None)
    
    user_tier = rails.store.get_sync('user_tier', 'standard')
    config = rails.store.get_sync('config', {})
    has_config = rails.store.exists_sync('config')
    
    # ASYNC VERSIONS - for use in async contexts
    await rails.store.increment('async_counter')
    await rails.store.set('async_state', 'value')
    value = await rails.store.get('async_state')
    
    # BULK OPERATIONS
    await rails.store.clear()  # clear all state
    rails.store.delete_sync('old_key')  # remove specific key
```

### 9. Context Manager & Lifecycle

Rails supports both manual and automatic lifecycle management:

```python
# Automatic lifecycle with context manager (recommended)
async with Rails() as rails:
    # Rails instance available globally via current_rails()
    rails.when(condition).inject(message)
    rails.when(condition).then(workflow)
    result = await rails.check(messages)
    # Cleanup handled automatically on exit

# Manual lifecycle management
rails = Rails()
try:
    rails_instance = await rails.__aenter__()  # Manual setup
    # Use rails...
    result = await rails.check(messages)
finally:
    await rails.__aexit__(None, None, None)  # Manual cleanup

# With lifecycle functions
async with Rails().with_lifecycle('database', 'monitoring') as rails:
    # All lifecycle functions activated automatically
    result = await rails.check(messages)
    # Lifecycle functions cleaned up in reverse order
```

# Installation & Development

## Installation

```bash
# Core Rails package
pip install agent-rails

# Or with PDM
pdm add agent-rails

# With optional framework dependencies
pip install agent-rails[adapters]  # includes langchain, smolagents
pip install agent-rails[dev]       # includes development tools

# Framework-specific installation
pip install "agent-rails[adapters]" langchain
pip install "agent-rails[adapters]" smolagents
```

## Development

```bash
# Clone and set up development environment
git clone https://github.com/rizome-dev/rails
cd rails
pdm install --dev

# Run tests
pdm run test
pdm run test-cov  # with coverage report

# Code quality
pdm run lint      # ruff linting
pdm run format    # black formatting  
pdm run typecheck # mypy type checking

# Build and publish (maintainers only)
pdm run build    # build wheel and sdist
pdm run check    # verify built packages
```

---

## Examples & Community  

- **Enhanced Example**: [`enhanced_example.py`](https://github.com/rizome-dev/rails/blob/main/examples/enhanced_example.py) - Full demonstration of all capabilities
- **Adapter Examples**: [`adapter_example.py`](https://github.com/rizome-dev/rails/blob/main/examples/adapter_example.py) - Framework integration patterns  
- **Documentation**: [GitHub Repository](https://github.com/rizome-dev/rails)
- **Issues & Feature Requests**: [GitHub Issues](https://github.com/rizome-dev/rails/issues)

**Built with ❤️ by [Rizome Labs, Inc.](https://rizome.dev)**

            

Raw data

            {
    "_id": null,
    "home_page": null,
    "name": "agent-rails",
    "maintainer": null,
    "docs_url": null,
    "requires_python": ">=3.11",
    "maintainer_email": null,
    "keywords": "ai, agents, lifecycle, management",
    "author": null,
    "author_email": "samjtro <hi@samjtro.com>",
    "download_url": "https://files.pythonhosted.org/packages/ee/b9/912d0608a60cb56a30ec9e177e6be9e0af38b174af47f0d0708bb8661cb9/agent_rails-0.1.1.tar.gz",
    "platform": null,
    "description": "# Rails | Put your agents on rails\n\n**Production-grade lifecycle management for AI agents - inject context and execute workflows when conditions are met**\n\nRails provides a comprehensive, framework-agnostic system for surgical control over AI agent behavior. Inject messages, execute workflows, compose lifecycle functions, and orchestrate complex patterns - all triggered by your custom conditions. No forced lifecycle phases, maximum flexibility.\n\n```bash\npip install agent-rails\n# or\npdm add agent-rails\n```\n\n**Built by: [Rizome Labs](https://rizome.dev) | Contact: [hi@rizome.dev](mailto:hi@rizome.dev)**\n\n## Quick Start\n\n```python\nimport asyncio\nfrom rails import Rails, current_rails, lifecycle_function\n\n# Define a tool that accesses Rails\ndef api_tool(data):\n    rails = current_rails()  # Access Rails from within tools\n    rails.store.increment_sync('api_calls')\n    return {\"processed\": True, \"calls\": rails.store.get_counter_sync('api_calls')}\n\n# Define composable lifecycle function\n@lifecycle_function(priority=10)\nasync def setup_monitoring(rails):\n    rails.store.set_sync('monitoring_active', True)\n    print(\"\ud83d\udd27 Monitoring started\")\n    yield  # Main execution happens here\n    print(\"\ud83d\udd27 Monitoring stopped\")\n\n# Define workflow function\nasync def error_recovery(rails):\n    print(\"\ud83d\udd04 Running error recovery workflow\")\n    rails.store.set_counter_sync('errors', 0)\n\nasync def main():\n    # Create Rails with lifecycle functions\n    rails = Rails().with_lifecycle(setup_monitoring)\n    \n    # Message injection: When errors occur, inject helpful message\n    rails.when(lambda s: s.get_counter_sync('errors') >= 2).inject({\n        \"role\": \"system\", \n        \"content\": \"Multiple errors detected. Switching to recovery mode.\"\n    })\n    \n    # Workflow execution: When API calls exceed limit, run background optimization\n    rails.when(lambda s: s.get_counter_sync('api_calls') >= 5).then(\n        lambda r: print(\"\u26a1 Running optimization...\"), background=True\n    )\n    \n    # Error recovery workflow\n    rails.when(lambda s: s.get_counter_sync('errors') >= 3).then(error_recovery)\n    \n    # Use Rails with automatic lifecycle management\n    async with rails:\n        messages = [{\"role\": \"user\", \"content\": \"Help me process data\"}]\n        \n        # Simulate tool calls and errors\n        for i in range(6):\n            result = api_tool({\"item\": i})\n            if i > 3: rails.store.increment_sync('errors')\n            \n            # Check conditions - both inject messages AND execute workflows\n            messages = await rails.check(messages)\n            \n            # Display any Rails messages\n            for msg in messages:\n                if \"system\" in msg.get(\"role\", \"\"):\n                    print(f\"\ud83d\udcac Rails: {msg['content']}\")\n\nasyncio.run(main())\n```\n\n# Documentation\n\n## Core Features\n\n### 1. Message Injection - `when().inject()`\n\nConditionally inject messages into agent conversations:\n\n```python\nfrom rails import Rails, CounterCondition, StateCondition\n\nrails = Rails()\n\n# Lambda conditions for custom logic\nrails.when(lambda s: s.get_counter_sync('errors') >= 3).inject({\n    \"role\": \"system\",\n    \"content\": \"Multiple errors detected. Switching to recovery mode.\"\n})\n\n# Built-in condition types for common patterns\nrails.when(CounterCondition('attempts', 5, '>=')).inject(retry_message)\nrails.when(StateCondition('mode', 'expert')).inject(expert_guidance)\n\n# Multiple injection strategies\nrails.when(condition).inject(message)  # append (default)\nrails.when(condition).inject(message, strategy='prepend')  # add to start\nrails.when(condition).inject(message, strategy='replace_last')  # replace last\n\n# Convenience methods\nrails.on_counter('turns', 10, stop_message)\nrails.on_state('debug_mode', True, debug_message)\n\n# Apply all conditions and get enhanced messages\nenhanced_messages = await rails.check(original_messages)\n```\n\n### 2. Workflow Execution - `when().then()`\n\nExecute functions and workflows when conditions are met:\n\n```python\nfrom rails import Rails\n\nrails = Rails()\n\n# Execute function when condition met\nasync def error_recovery(rails):\n    print(\"Running error recovery...\")\n    rails.store.set_counter_sync('errors', 0)\n    \nrails.when(lambda s: s.get_counter_sync('errors') >= 3).then(error_recovery)\n\n# Execute in background (non-blocking)\nrails.when(condition).then(\n    optimization_workflow, \n    background=True,  # runs asynchronously\n    name=\"background_optimizer\"\n)\n\n# Pass additional arguments\nrails.when(condition).then(\n    custom_workflow, \n    arg1, arg2,  # positional args\n    param1=\"value\",  # keyword args\n    background=False\n)\n\n# Lambda functions for simple workflows\nrails.when(condition).then(lambda r: r.store.set('recovery_mode', True))\n```\n\n### 3. Composable Lifecycle Functions - `@lifecycle_function`\n\nCreate modular, reusable lifecycle components:\n\n```python\nfrom rails import Rails, lifecycle_function\n\n@lifecycle_function(name=\"database\", priority=10)\nasync def database_lifecycle(rails):\n    # Setup phase\n    connection = await create_db_connection()\n    rails.store.set_sync('db_connection', connection)\n    print(\"\ud83d\udd27 Database connected\")\n    \n    yield  # Main execution happens here\n    \n    # Cleanup phase\n    await connection.close()\n    print(\"\ud83d\udd27 Database disconnected\")\n\n@lifecycle_function(name=\"monitoring\", priority=5)\nasync def monitoring_lifecycle(rails):\n    # Setup monitoring\n    rails.store.set_sync('monitoring_active', True)\n    \n    # Add conditional rules during setup\n    rails.when(lambda s: s.get_counter_sync('errors') >= 5).inject({\n        \"role\": \"system\",\n        \"content\": \"\ud83d\udea8 High error rate detected!\"\n    })\n    \n    yield\n    \n    # Cleanup monitoring\n    rails.store.set_sync('monitoring_active', False)\n\n# Compose multiple lifecycle functions\nrails = Rails().with_lifecycle('database', 'monitoring', custom_lifecycle)\n\n# Or compose with function references\nrails = Rails().with_lifecycle(database_lifecycle, monitoring_lifecycle)\n```\n\n### 4. Global Tool Access - `current_rails()`\n\nTools can access and manipulate the Rails instance they're running within:\n\n```python\nfrom rails import current_rails\n\ndef api_client_tool(endpoint, data):\n    \"\"\"Tool that tracks usage and adds conditional behaviors.\"\"\"\n    rails = current_rails()  # Access the active Rails instance\n    \n    # Track API usage\n    rails.store.increment_sync('api_calls')\n    rails.store.increment_sync(f'api_calls_{endpoint}')\n    \n    # Tool can add conditional rules based on its state\n    if rails.store.get_counter_sync('api_calls') >= 8:\n        rails.when(lambda s: s.get_counter_sync('api_calls') >= 10).inject({\n            \"role\": \"system\",\n            \"content\": \"\u26a0\ufe0f API rate limit approaching. Consider throttling.\"\n        })\n    \n    # Simulate API call\n    result = call_external_api(endpoint, data)\n    \n    # Track errors\n    if not result.get('success'):\n        rails.store.increment_sync('api_errors')\n    \n    return result\n\n# Tools automatically access Rails when called within Rails context\nasync with Rails() as rails:\n    result = api_client_tool('process', {'data': 'value'})  # Works seamlessly\n```\n\n## Advanced Features\n\n### 5. Background Execution & Orchestration\n\nExecute complex workflows with proper concurrency and orchestration:\n\n```python\nfrom rails import Rails, WorkflowOrchestrator, execute_background_workflow\n\nasync def complex_data_processing(rails):\n    # Long-running data processing\n    await process_large_dataset()\n    return {\"processed\": True}\n\nasync def quick_validation(rails):  \n    # Quick validation task\n    return {\"valid\": True}\n\n# Background execution\nrails = Rails()\n\nasync with rails:\n    # Execute single workflow in background\n    task_id = await execute_background_workflow(\n        complex_data_processing, \n        rails_instance=rails,\n        task_id=\"data_proc_001\"\n    )\n    \n    # Continue other work while background task runs\n    messages = await rails.check(messages)\n    \n    # Advanced orchestration\n    orchestrator = WorkflowOrchestrator(rails, max_concurrent=3)\n    \n    async with orchestrator.orchestration_context():\n        # Conditional pipeline - steps run based on conditions\n        pipeline_steps = [\n            (lambda s: True, initialize_system),\n            (lambda s: s.get_counter_sync('items') > 0, process_items),\n            (lambda s: s.get_sync('validation_required'), validate_results),\n            (lambda s: True, finalize_results)\n        ]\n        \n        pipeline_results = await orchestrator.execute_conditional_pipeline(pipeline_steps)\n        \n        # Parallel execution\n        parallel_workflows = [complex_data_processing, quick_validation, cleanup_temp_data]\n        parallel_results = await orchestrator.execute_parallel_workflows(\n            parallel_workflows, \n            wait_all=True\n        )\n```\n\n### 6. Built-in Condition Types\n\nRails provides powerful condition primitives:\n\n```python\nfrom rails import (\n    CounterCondition, StateCondition, LambdaCondition,\n    AndCondition, OrCondition, NotCondition\n)\n\n# Counter conditions with operators\nrails.when(CounterCondition('api_calls', 10, '>=')).inject(rate_limit_msg)\nrails.when(CounterCondition('errors', 0, '==')).inject(success_msg)\n\n# State conditions\nrails.when(StateCondition('mode', 'production')).inject(prod_warning)\nrails.when(StateCondition('user_tier', 'premium')).inject(premium_features)\n\n# Logical combinations\ncomplex_condition = AndCondition([\n    CounterCondition('attempts', 3, '>='),\n    StateCondition('auto_retry', True)\n])\nrails.when(complex_condition).then(auto_retry_workflow)\n\n# Custom logic with lambda conditions\nrails.when(LambdaCondition(lambda s: \n    s.get_counter_sync('success_rate') / s.get_counter_sync('total_attempts') < 0.5\n)).inject(low_success_warning)\n```\n\n## Framework Integration\n\n### 7. Framework Adapters\n\nRails includes adapters for seamless integration with popular agent frameworks:\n\n```python\nfrom rails.adapters import create_adapter, BaseRailsAdapter\n\n# Generic adapter with any processing function\ndef my_agent_processor(messages):\n    # Your agent processing logic here\n    return {\"role\": \"assistant\", \"content\": \"Processed with Rails!\"}\n\nadapter = create_adapter(rails, my_agent_processor)\n\n# Context manager handles Rails lifecycle automatically\nasync with adapter as active_adapter:\n    result = await active_adapter.run(messages)\n```\n\n#### LangChain Integration\n\n```python\nfrom rails.adapters import LangChainAdapter\nfrom langchain.chat_models import ChatOpenAI\nfrom langchain.schema import HumanMessage\n\n# Set up Rails with conditions\nrails = Rails()\nrails.when(lambda s: s.get_counter_sync('messages') >= 3).inject({\n    \"role\": \"system\",\n    \"content\": \"This conversation is getting long. Let me summarize...\"\n})\n\n# Create LangChain adapter\nllm = ChatOpenAI(model=\"gpt-4\")\nadapter = LangChainAdapter(rails, llm)\n\n# Rails automatically applies before LangChain processing\nmessages = [HumanMessage(content=\"Help me debug this code\")]\nresult = await adapter.run(messages)\n```\n\n#### SmolAgents Integration\n\n```python\nfrom rails.adapters import SmolAgentsAdapter\nfrom smolagents import Agent\n\n# Set up Rails with agent-specific conditions\nrails = Rails()\nrails.when(lambda s: s.get_counter_sync('tool_calls') >= 5).inject({\n    \"role\": \"system\", \n    \"content\": \"I notice I'm using many tools. Let me focus on the core task.\"\n})\n\n# Create SmolAgents adapter  \nagent = Agent(model=\"gpt-4\", tools=[web_search, calculator])\nadapter = SmolAgentsAdapter(rails, agent)\n\nresult = await adapter.run(\"Research the latest AI developments and calculate ROI\")\n```\n\n#### Custom Framework Adapter\n\n```python\nfrom rails.adapters import BaseRailsAdapter\n\nclass MyFrameworkAdapter(BaseRailsAdapter):\n    def __init__(self, rails, my_agent):\n        super().__init__(rails)\n        self.agent = my_agent\n    \n    async def process_messages(self, messages, **kwargs):\n        # Convert Rails messages to your framework format\n        framework_messages = self.convert_messages(messages)\n        \n        # Process with your framework\n        result = await self.agent.process(framework_messages, **kwargs)\n        \n        # Update Rails state based on result\n        await self.rails.store.increment('framework_calls')\n        \n        return result\n    \n    async def update_rails_state(self, original_messages, modified_messages, result):\n        # Custom state updates beyond the default\n        await super().update_rails_state(original_messages, modified_messages, result)\n        \n        # Track framework-specific metrics\n        if result.get('tool_used'):\n            await self.rails.store.increment('tool_usage')\n\n# Usage\nadapter = MyFrameworkAdapter(rails, my_custom_agent)\nresult = await adapter.run(messages)\n```\n\n### 8. State Management\n\nRails provides comprehensive thread-safe state management:\n\n```python\nfrom rails import Rails\n\nasync with Rails() as rails:\n    # COUNTERS - for tracking numeric values\n    rails.store.increment_sync('api_calls')  # increment by 1\n    rails.store.increment_sync('errors', 5)  # increment by custom amount\n    rails.store.set_counter_sync('retries', 0)  # set to specific value\n    \n    current_calls = rails.store.get_counter_sync('api_calls', default=0)\n    has_errors = rails.store.get_counter_sync('errors') > 0\n    \n    # STATE VALUES - for storing arbitrary data  \n    rails.store.set_sync('user_tier', 'premium')\n    rails.store.set_sync('config', {'debug': True, 'retries': 3})\n    rails.store.set_sync('last_error', None)\n    \n    user_tier = rails.store.get_sync('user_tier', 'standard')\n    config = rails.store.get_sync('config', {})\n    has_config = rails.store.exists_sync('config')\n    \n    # ASYNC VERSIONS - for use in async contexts\n    await rails.store.increment('async_counter')\n    await rails.store.set('async_state', 'value')\n    value = await rails.store.get('async_state')\n    \n    # BULK OPERATIONS\n    await rails.store.clear()  # clear all state\n    rails.store.delete_sync('old_key')  # remove specific key\n```\n\n### 9. Context Manager & Lifecycle\n\nRails supports both manual and automatic lifecycle management:\n\n```python\n# Automatic lifecycle with context manager (recommended)\nasync with Rails() as rails:\n    # Rails instance available globally via current_rails()\n    rails.when(condition).inject(message)\n    rails.when(condition).then(workflow)\n    result = await rails.check(messages)\n    # Cleanup handled automatically on exit\n\n# Manual lifecycle management\nrails = Rails()\ntry:\n    rails_instance = await rails.__aenter__()  # Manual setup\n    # Use rails...\n    result = await rails.check(messages)\nfinally:\n    await rails.__aexit__(None, None, None)  # Manual cleanup\n\n# With lifecycle functions\nasync with Rails().with_lifecycle('database', 'monitoring') as rails:\n    # All lifecycle functions activated automatically\n    result = await rails.check(messages)\n    # Lifecycle functions cleaned up in reverse order\n```\n\n# Installation & Development\n\n## Installation\n\n```bash\n# Core Rails package\npip install agent-rails\n\n# Or with PDM\npdm add agent-rails\n\n# With optional framework dependencies\npip install agent-rails[adapters]  # includes langchain, smolagents\npip install agent-rails[dev]       # includes development tools\n\n# Framework-specific installation\npip install \"agent-rails[adapters]\" langchain\npip install \"agent-rails[adapters]\" smolagents\n```\n\n## Development\n\n```bash\n# Clone and set up development environment\ngit clone https://github.com/rizome-dev/rails\ncd rails\npdm install --dev\n\n# Run tests\npdm run test\npdm run test-cov  # with coverage report\n\n# Code quality\npdm run lint      # ruff linting\npdm run format    # black formatting  \npdm run typecheck # mypy type checking\n\n# Build and publish (maintainers only)\npdm run build    # build wheel and sdist\npdm run check    # verify built packages\n```\n\n---\n\n## Examples & Community  \n\n- **Enhanced Example**: [`enhanced_example.py`](https://github.com/rizome-dev/rails/blob/main/examples/enhanced_example.py) - Full demonstration of all capabilities\n- **Adapter Examples**: [`adapter_example.py`](https://github.com/rizome-dev/rails/blob/main/examples/adapter_example.py) - Framework integration patterns  \n- **Documentation**: [GitHub Repository](https://github.com/rizome-dev/rails)\n- **Issues & Feature Requests**: [GitHub Issues](https://github.com/rizome-dev/rails/issues)\n\n**Built with \u2764\ufe0f by [Rizome Labs, Inc.](https://rizome.dev)**\n",
    "bugtrack_url": null,
    "license": "MIT",
    "summary": "put your agents on guardrails",
    "version": "0.1.1",
    "project_urls": {
        "Changelog": "https://github.com/rizome-dev/rails/blob/main/CHANGELOG.md",
        "Documentation": "https://github.com/rizome-dev/rails#readme",
        "Homepage": "https://github.com/rizome-dev/rails",
        "Issue Tracker": "https://github.com/rizome-dev/rails/issues",
        "Repository": "https://github.com/rizome-dev/rails"
    },
    "split_keywords": [
        "ai",
        " agents",
        " lifecycle",
        " management"
    ],
    "urls": [
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "268033a1e5a32a68847f25b470eae338e07ecf2458846786f4fdc998a3a0c073",
                "md5": "66270f282fd22da3f658f9a127626ffe",
                "sha256": "979916bcf29d5d7660397d7496e1f01b1ada4ccae19407fdfdf2ff920352420d"
            },
            "downloads": -1,
            "filename": "agent_rails-0.1.1-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "66270f282fd22da3f658f9a127626ffe",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": ">=3.11",
            "size": 32450,
            "upload_time": "2025-08-29T15:25:37",
            "upload_time_iso_8601": "2025-08-29T15:25:37.536513Z",
            "url": "https://files.pythonhosted.org/packages/26/80/33a1e5a32a68847f25b470eae338e07ecf2458846786f4fdc998a3a0c073/agent_rails-0.1.1-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "eeb9912d0608a60cb56a30ec9e177e6be9e0af38b174af47f0d0708bb8661cb9",
                "md5": "6845e037f1f0e16eb1cf961b5b6b1d82",
                "sha256": "72d504fc3795227ead8cb6d6f858153a0496663d0c43f8ef0201385b9c48dcac"
            },
            "downloads": -1,
            "filename": "agent_rails-0.1.1.tar.gz",
            "has_sig": false,
            "md5_digest": "6845e037f1f0e16eb1cf961b5b6b1d82",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.11",
            "size": 33781,
            "upload_time": "2025-08-29T15:25:39",
            "upload_time_iso_8601": "2025-08-29T15:25:39.056140Z",
            "url": "https://files.pythonhosted.org/packages/ee/b9/912d0608a60cb56a30ec9e177e6be9e0af38b174af47f0d0708bb8661cb9/agent_rails-0.1.1.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2025-08-29 15:25:39",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "rizome-dev",
    "github_project": "rails",
    "travis_ci": false,
    "coveralls": false,
    "github_actions": true,
    "lcname": "agent-rails"
}
        
Elapsed time: 1.58668s