Name | agent-rails JSON |
Version |
0.1.1
JSON |
| download |
home_page | None |
Summary | put your agents on guardrails |
upload_time | 2025-08-29 15:25:39 |
maintainer | None |
docs_url | None |
author | None |
requires_python | >=3.11 |
license | MIT |
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"
}